[Add]新增完整海龟策略的例子examples/TurtleStrategy
This commit is contained in:
parent
aaa4b80a7c
commit
3d1048848b
172
examples/TurtleStrategy/.ipynb_checkpoints/run-checkpoint.ipynb
Normal file
172
examples/TurtleStrategy/.ipynb_checkpoints/run-checkpoint.ipynb
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 1,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import sys\n",
|
||||||
|
"reload(sys)\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"%matplotlib inline\n",
|
||||||
|
"\n",
|
||||||
|
"from datetime import datetime\n",
|
||||||
|
"import matplotlib.pyplot as plt\n",
|
||||||
|
"\n",
|
||||||
|
"from turtleEngine import BacktestingEngine"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 2,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"l = ['IF99'] #, 'CU99', 'I99', 'TA99']\n",
|
||||||
|
"engine = BacktestingEngine()\n",
|
||||||
|
"engine.setPeriod(datetime(2015, 1, 1), datetime(2018, 11, 9))\n",
|
||||||
|
"engine.initPortfolio(l)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 3,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"11:39:12.298000:IF99数据加载完成,总数据量:940\n",
|
||||||
|
"11:39:12.299000:全部数据加载完成\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"engine.loadData()\n",
|
||||||
|
"engine.runBacktesting()\n",
|
||||||
|
"engine.calculateResult()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 4,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"2015-06-08 00:00:00\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ename": "UnicodeDecodeError",
|
||||||
|
"evalue": "'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)",
|
||||||
|
"output_type": "error",
|
||||||
|
"traceback": [
|
||||||
|
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||||
|
"\u001b[1;31mUnicodeDecodeError\u001b[0m Traceback (most recent call last)",
|
||||||
|
"\u001b[1;32m<ipython-input-4-74c05e0f545f>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;32mprint\u001b[0m \u001b[0mdt\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mtrade\u001b[0m \u001b[1;32min\u001b[0m \u001b[0ml\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[1;32mprint\u001b[0m \u001b[0mtrade\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
|
||||||
|
"\u001b[1;32mC:\\Github\\vnpy\\examples\\TurtleStrategy\\turtleEngine.py\u001b[0m in \u001b[0;36m__str__\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 134\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0moffset\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'UTF-8'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 135\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mvolume\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 136\u001b[1;33m self.price)\n\u001b[0m\u001b[0;32m 137\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 138\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
|
||||||
|
"\u001b[1;31mUnicodeDecodeError\u001b[0m: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"for dt, l in engine.tradeDict.items():\n",
|
||||||
|
" print dt\n",
|
||||||
|
" for trade in l:\n",
|
||||||
|
" print trade"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"scrolled": false
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"for result in engine.resultList:\n",
|
||||||
|
" print result.date, result.totalPnl"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import numpy as np"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"l = [result.totalPnl for result in engine.resultList]\n",
|
||||||
|
"equity = np.cumsum(l)\n",
|
||||||
|
"plt.plot(equity)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"for data in engine.dataDict.values():\n",
|
||||||
|
" print data"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"print trade."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 2",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python2"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython2",
|
||||||
|
"version": "2.7.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 2
|
||||||
|
}
|
265
examples/TurtleStrategy/run.ipynb
Normal file
265
examples/TurtleStrategy/run.ipynb
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 1,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import sys\n",
|
||||||
|
"reload(sys)\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"%matplotlib inline\n",
|
||||||
|
"\n",
|
||||||
|
"from datetime import datetime\n",
|
||||||
|
"import matplotlib.pyplot as plt\n",
|
||||||
|
"\n",
|
||||||
|
"from turtleEngine import BacktestingEngine"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 2,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"l = ['IF99'] #, 'CU99', 'I99', 'TA99']\n",
|
||||||
|
"engine = BacktestingEngine()\n",
|
||||||
|
"engine.setPeriod(datetime(2015, 1, 1), datetime(2018, 11, 9))\n",
|
||||||
|
"engine.initPortfolio(l)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 3,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"11:39:12.298000:IF99数据加载完成,总数据量:940\n",
|
||||||
|
"11:39:12.299000:全部数据加载完成\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"engine.loadData()\n",
|
||||||
|
"engine.runBacktesting()\n",
|
||||||
|
"engine.calculateResult()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 5,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"2015-06-08 00:00:00\n",
|
||||||
|
"IF99 多 开仓 5370.6182 1\n",
|
||||||
|
"IF99 多 开仓 5370.6182 1\n",
|
||||||
|
"2015-06-17 00:00:00\n",
|
||||||
|
"IF99 空 平仓 4994.5359289007665 1\n",
|
||||||
|
"IF99 空 平仓 4994.5359289007665 1\n",
|
||||||
|
"2015-10-12 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3341.0982 1\n",
|
||||||
|
"2015-10-26 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3424.5987371468213 1\n",
|
||||||
|
"2015-11-04 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3508.099274293643 1\n",
|
||||||
|
"2015-11-05 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3591.5998114404642 1\n",
|
||||||
|
"2015-11-23 00:00:00\n",
|
||||||
|
"IF99 空 平仓 3603.8898 4\n",
|
||||||
|
"2015-12-21 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3786.5235 1\n",
|
||||||
|
"2015-12-23 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3834.280588057014 1\n",
|
||||||
|
"2015-12-28 00:00:00\n",
|
||||||
|
"IF99 空 平仓 3643.2522358289566 2\n",
|
||||||
|
"2016-03-07 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3027.3054 1\n",
|
||||||
|
"IF99 多 开仓 3074.3983619185256 1\n",
|
||||||
|
"2016-03-18 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3121.4913238370505 1\n",
|
||||||
|
"2016-03-21 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3168.584285755576 1\n",
|
||||||
|
"2016-04-20 00:00:00\n",
|
||||||
|
"IF99 空 平仓 3105.8006 4\n",
|
||||||
|
"2016-07-07 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3166.5061 1\n",
|
||||||
|
"2016-07-11 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3194.9341606344988 1\n",
|
||||||
|
"IF99 多 开仓 3192.2685 1\n",
|
||||||
|
"2016-07-12 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3223.3622212689975 1\n",
|
||||||
|
"2016-07-27 00:00:00\n",
|
||||||
|
"IF99 空 平仓 3173.5677 4\n",
|
||||||
|
"2016-08-11 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3245.6871 1\n",
|
||||||
|
"2016-08-12 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3268.4782362989677 1\n",
|
||||||
|
"IF99 多 开仓 3274.5309 1\n",
|
||||||
|
"2016-08-15 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3291.269372597935 1\n",
|
||||||
|
"2016-08-25 00:00:00\n",
|
||||||
|
"IF99 空 平仓 3251.8034639734847 4\n",
|
||||||
|
"2016-10-19 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3294.1809 1\n",
|
||||||
|
"2016-10-24 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3313.0011487826814 1\n",
|
||||||
|
"IF99 多 开仓 3331.821397565363 1\n",
|
||||||
|
"IF99 多 开仓 3350.6416463480446 1\n",
|
||||||
|
"2016-12-07 00:00:00\n",
|
||||||
|
"IF99 空 平仓 3432.9777 4\n",
|
||||||
|
"2017-03-24 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3468.6291 1\n",
|
||||||
|
"2017-03-30 00:00:00\n",
|
||||||
|
"IF99 空 平仓 3406.5561141656326 1\n",
|
||||||
|
"2017-04-05 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3474.3432 1\n",
|
||||||
|
"IF99 多 开仓 3491.1258202647214 1\n",
|
||||||
|
"IF99 多 开仓 3474.3432 1\n",
|
||||||
|
"IF99 多 开仓 3491.1258202647214 1\n",
|
||||||
|
"2017-04-17 00:00:00\n",
|
||||||
|
"IF99 空 平仓 3440.7779594705567 3\n",
|
||||||
|
"IF99 空 平仓 3440.7779594705567 1\n",
|
||||||
|
"2017-05-25 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3433.5201 1\n",
|
||||||
|
"IF99 多 开仓 3449.826458335901 1\n",
|
||||||
|
"IF99 多 开仓 3466.1328166718017 1\n",
|
||||||
|
"2017-05-26 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3482.4391750077025 1\n",
|
||||||
|
"2017-06-15 00:00:00\n",
|
||||||
|
"IF99 空 平仓 3498.115614786368 4\n",
|
||||||
|
"2017-06-22 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3579.0624 1\n",
|
||||||
|
"2017-06-26 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3597.53568462007 1\n",
|
||||||
|
"IF99 多 开仓 3616.0089692401393 1\n",
|
||||||
|
"IF99 多 开仓 3634.4822538602093 1\n",
|
||||||
|
"2017-08-11 00:00:00\n",
|
||||||
|
"IF99 空 平仓 3656.9743 4\n",
|
||||||
|
"2018-01-02 00:00:00\n",
|
||||||
|
"IF99 多 开仓 4105.3543 1\n",
|
||||||
|
"2018-01-03 00:00:00\n",
|
||||||
|
"IF99 多 开仓 4132.444330290369 1\n",
|
||||||
|
"2018-01-08 00:00:00\n",
|
||||||
|
"IF99 多 开仓 4159.534360580738 1\n",
|
||||||
|
"2018-01-09 00:00:00\n",
|
||||||
|
"IF99 多 开仓 4186.624390871108 1\n",
|
||||||
|
"2018-02-01 00:00:00\n",
|
||||||
|
"IF99 空 平仓 4253.1416 4\n",
|
||||||
|
"2018-07-24 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3507.8183 1\n",
|
||||||
|
"IF99 多 开仓 3547.101357149508 1\n",
|
||||||
|
"2018-08-02 00:00:00\n",
|
||||||
|
"IF99 空 平仓 3389.9691285514746 2\n",
|
||||||
|
"2018-09-21 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3407.2856 1\n",
|
||||||
|
"2018-09-26 00:00:00\n",
|
||||||
|
"IF99 多 开仓 3438.7157642602238 1\n",
|
||||||
|
"2018-10-08 00:00:00\n",
|
||||||
|
"IF99 空 平仓 3312.9951072193303 2\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"for dt, l in engine.tradeDict.items():\n",
|
||||||
|
" print dt\n",
|
||||||
|
" for trade in l:\n",
|
||||||
|
" print trade.vtSymbol, trade.direction, trade.offset, trade.price, trade.volume"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {
|
||||||
|
"scrolled": false
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"for result in engine.resultList:\n",
|
||||||
|
" print result.date, result.totalPnl"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import numpy as np"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"l = [result.totalPnl for result in engine.resultList]\n",
|
||||||
|
"equity = np.cumsum(l)\n",
|
||||||
|
"plt.plot(equity)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"for data in engine.dataDict.values():\n",
|
||||||
|
" print data"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"print trade."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 2",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python2"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython2",
|
||||||
|
"version": "2.7.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 2
|
||||||
|
}
|
199
examples/TurtleStrategy/turtleEngine.py
Normal file
199
examples/TurtleStrategy/turtleEngine.py
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from pymongo import MongoClient
|
||||||
|
from collections import OrderedDict, defaultdict
|
||||||
|
|
||||||
|
from vnpy.trader.vtObject import VtBarData
|
||||||
|
from vnpy.trader.vtConstant import DIRECTION_LONG, DIRECTION_SHORT
|
||||||
|
|
||||||
|
from turtleStrategy import TurtlePortfolio
|
||||||
|
|
||||||
|
|
||||||
|
DAILY_DB_NAME = 'VnTrader_Daily_Db'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class BacktestingEngine(object):
|
||||||
|
""""""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self):
|
||||||
|
"""Constructor"""
|
||||||
|
self.portfolio = None
|
||||||
|
self.vtSymbolList = []
|
||||||
|
|
||||||
|
self.startDt = None
|
||||||
|
self.endDt = None
|
||||||
|
self.currentDt = None
|
||||||
|
|
||||||
|
self.dataDict = OrderedDict()
|
||||||
|
self.tradeDict = OrderedDict()
|
||||||
|
|
||||||
|
self.result = None
|
||||||
|
self.resultList = []
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def setPeriod(self, startDt, endDt):
|
||||||
|
""""""
|
||||||
|
self.startDt = startDt
|
||||||
|
self.endDt = endDt
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def initPortfolio(self, vtSymbolList):
|
||||||
|
""""""
|
||||||
|
self.vtSymbolList = vtSymbolList
|
||||||
|
|
||||||
|
self.portfolio = TurtlePortfolio(self)
|
||||||
|
self.portfolio.init(vtSymbolList)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def loadData(self):
|
||||||
|
""""""
|
||||||
|
mc = MongoClient()
|
||||||
|
db = mc[DAILY_DB_NAME]
|
||||||
|
|
||||||
|
for vtSymbol in self.vtSymbolList:
|
||||||
|
flt = {'datetime':{'$gte':self.startDt,
|
||||||
|
'$lte':self.endDt}}
|
||||||
|
|
||||||
|
collection = db[vtSymbol]
|
||||||
|
cursor = collection.find(flt).sort('datetime')
|
||||||
|
|
||||||
|
for d in cursor:
|
||||||
|
bar = VtBarData()
|
||||||
|
bar.__dict__ = d
|
||||||
|
|
||||||
|
barDict = self.dataDict.setdefault(bar.datetime, OrderedDict())
|
||||||
|
barDict[bar.vtSymbol] = bar
|
||||||
|
|
||||||
|
self.writeLog(u'%s数据加载完成,总数据量:%s' %(vtSymbol, cursor.count()))
|
||||||
|
|
||||||
|
self.writeLog(u'全部数据加载完成')
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def runBacktesting(self):
|
||||||
|
""""""
|
||||||
|
for dt, barDict in self.dataDict.items():
|
||||||
|
self.currentDt = dt
|
||||||
|
|
||||||
|
result = DailyResult(dt)
|
||||||
|
result.updatePos(self.portfolio.posDict)
|
||||||
|
|
||||||
|
for bar in barDict.values():
|
||||||
|
self.portfolio.onBar(bar)
|
||||||
|
result.updateBar(bar)
|
||||||
|
|
||||||
|
if self.result:
|
||||||
|
result.updatePreviousClose(self.result.closeDict)
|
||||||
|
|
||||||
|
self.resultList.append(result)
|
||||||
|
self.result = result
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def calculateResult(self):
|
||||||
|
""""""
|
||||||
|
for result in self.resultList:
|
||||||
|
result.calculatePnl()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def sendOrder(self, vtSymbol, direction, offset, price, volume):
|
||||||
|
""""""
|
||||||
|
trade = TradeData(vtSymbol, direction, offset, price, volume)
|
||||||
|
l = self.tradeDict.setdefault(self.currentDt, [])
|
||||||
|
l.append(trade)
|
||||||
|
|
||||||
|
self.result.updateTrade(trade)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def writeLog(self, content):
|
||||||
|
""""""
|
||||||
|
print '%s:%s' %(datetime.now().strftime('%H:%M:%S.%f'), content)
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class TradeData(object):
|
||||||
|
""""""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self, vtSymbol, direction, offset, price, volume):
|
||||||
|
"""Constructor"""
|
||||||
|
self.vtSymbol = vtSymbol
|
||||||
|
self.direction = direction
|
||||||
|
self.offset = offset
|
||||||
|
self.price = price
|
||||||
|
self.volume = volume
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class DailyResult(object):
|
||||||
|
"""每日的成交记录"""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self, date):
|
||||||
|
"""Constructor"""
|
||||||
|
self.date = date
|
||||||
|
|
||||||
|
self.closeDict = {} # 收盘价字典
|
||||||
|
self.previousCloseDict = {} # 昨收盘字典
|
||||||
|
|
||||||
|
self.tradeDict = defaultdict(list) # 成交字典
|
||||||
|
self.posDict = {} # 持仓字典(开盘时)
|
||||||
|
|
||||||
|
self.tradingPnl = 0
|
||||||
|
self.holdingPnl = 0
|
||||||
|
self.totalPnl = 0
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def updateTrade(self, trade):
|
||||||
|
"""更新交易"""
|
||||||
|
l = self.tradeDict[trade.vtSymbol]
|
||||||
|
l.append(trade)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def updatePos(self, d):
|
||||||
|
"""更新昨持仓"""
|
||||||
|
self.posDict.update(d)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def updateBar(self, bar):
|
||||||
|
"""更新K线"""
|
||||||
|
self.closeDict[bar.vtSymbol] = bar.close
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def updatePreviousClose(self, d):
|
||||||
|
"""更新昨收盘"""
|
||||||
|
self.previousCloseDict.update(d)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def calculateTradingPnl(self):
|
||||||
|
"""计算当日交易盈亏"""
|
||||||
|
for vtSymbol, l in self.tradeDict.items():
|
||||||
|
close = self.closeDict[vtSymbol]
|
||||||
|
|
||||||
|
for trade in l:
|
||||||
|
if trade.direction == DIRECTION_LONG:
|
||||||
|
side = 1
|
||||||
|
else:
|
||||||
|
side = -1
|
||||||
|
pnl = (close - trade.price) * trade.volume * side
|
||||||
|
self.tradingPnl += pnl
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def calculateHoldingPnl(self):
|
||||||
|
"""计算当日持仓盈亏"""
|
||||||
|
for vtSymbol, pos in self.posDict.items():
|
||||||
|
previousClose = self.previousCloseDict.get(vtSymbol, 0)
|
||||||
|
close = self.closeDict[vtSymbol]
|
||||||
|
pnl = (close - previousClose) * pos
|
||||||
|
self.holdingPnl += pnl
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def calculatePnl(self):
|
||||||
|
"""计算总盈亏"""
|
||||||
|
self.calculateHoldingPnl()
|
||||||
|
self.calculateTradingPnl()
|
||||||
|
self.totalPnl = self.holdingPnl + self.tradingPnl
|
||||||
|
|
336
examples/TurtleStrategy/turtleStrategy.py
Normal file
336
examples/TurtleStrategy/turtleStrategy.py
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
from vnpy.trader.vtConstant import (DIRECTION_LONG, DIRECTION_SHORT,
|
||||||
|
OFFSET_OPEN, OFFSET_CLOSE)
|
||||||
|
from vnpy.trader.vtUtility import ArrayManager
|
||||||
|
|
||||||
|
|
||||||
|
MAX_PRODUCT_POS = 4
|
||||||
|
MAX_DIRECTION_POS = 10
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class TurtleResult(object):
|
||||||
|
"""一次完整的开平交易"""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self):
|
||||||
|
"""Constructor"""
|
||||||
|
self.pos = 0
|
||||||
|
self.entry = 0 # 开仓均价
|
||||||
|
self.exit = 0 # 平仓均价
|
||||||
|
self.pnl = 0 # 盈亏
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def open(self, price, change):
|
||||||
|
"""开仓或者加仓"""
|
||||||
|
cost = self.pos * self.entry # 计算之前的开仓成本
|
||||||
|
cost += change * price # 加上新仓位的成本
|
||||||
|
self.pos += change # 加上新仓位的数量
|
||||||
|
self.entry = cost / self.pos # 计算新的平均开仓成本
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def close(self, price):
|
||||||
|
"""平仓"""
|
||||||
|
self.exit = price
|
||||||
|
self.pnl = self.pos * (self.exit - self.entry)
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class TurtleSignal(object):
|
||||||
|
"""海龟信号"""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self, portfolio, vtSymbol,
|
||||||
|
entryWindow, exitWindow, atrWindow,
|
||||||
|
profitCheck=False):
|
||||||
|
"""Constructor"""
|
||||||
|
self.portfolio = portfolio # 投资组合
|
||||||
|
|
||||||
|
self.vtSymbol = vtSymbol # 合约代码
|
||||||
|
self.entryWindow = entryWindow # 入场通道周期数
|
||||||
|
self.exitWindow = exitWindow # 出场通道周期数
|
||||||
|
self.atrWindow = atrWindow # 计算ATR周期数
|
||||||
|
self.profitCheck = profitCheck # 是否检查上一笔盈利
|
||||||
|
|
||||||
|
self.am = ArrayManager() # K线容器
|
||||||
|
|
||||||
|
self.atrVolatility = 0 # ATR波动率
|
||||||
|
self.entryUp = 0 # 入场通道
|
||||||
|
self.entryDown = 0
|
||||||
|
self.exitUp = 0 # 出场通道
|
||||||
|
self.exitDown = 0
|
||||||
|
|
||||||
|
self.longEntry1 = 0 # 多头入场位
|
||||||
|
self.longEntry2 = 0
|
||||||
|
self.longEntry3 = 0
|
||||||
|
self.longEntry4 = 0
|
||||||
|
self.longStop = 0 # 多头止损位
|
||||||
|
|
||||||
|
self.shortEntry1 = 0 # 空头入场位
|
||||||
|
self.shortEntry2 = 0
|
||||||
|
self.shortEntry3 = 0
|
||||||
|
self.shortEntry4 = 0
|
||||||
|
self.shortStop = 0 # 空头止损位
|
||||||
|
|
||||||
|
self.pos = 0 # 信号持仓
|
||||||
|
self.result = None # 当前的交易
|
||||||
|
self.resultList = [] # 交易列表
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def onBar(self, bar):
|
||||||
|
""""""
|
||||||
|
self.am.updateBar(bar)
|
||||||
|
if not self.am.inited:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.generateSignal(bar)
|
||||||
|
self.calculateIndicator()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def generateSignal(self, bar):
|
||||||
|
"""
|
||||||
|
判断交易信号
|
||||||
|
要注意在任何一个数据点:buy/sell/short/cover只允许执行一类动作
|
||||||
|
"""
|
||||||
|
# 如果指标尚未初始化,则忽略
|
||||||
|
if not self.longEntry1:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 优先检查平仓
|
||||||
|
if self.pos > 0:
|
||||||
|
longExit = max(self.longStop, self.exitDown)
|
||||||
|
if bar.low <= longExit:
|
||||||
|
self.sell(longExit)
|
||||||
|
return
|
||||||
|
elif self.pos < 0:
|
||||||
|
shortExit = min(self.shortStop, self.exitUp)
|
||||||
|
if bar.high >= shortExit:
|
||||||
|
self.cover(shortExit)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 没有仓位或者持有多头仓位的时候,可以做多(加仓)
|
||||||
|
if self.pos >= 0:
|
||||||
|
trade = False
|
||||||
|
|
||||||
|
if bar.high >= self.longEntry1 and self.pos < 1:
|
||||||
|
self.buy(self.longEntry1, 1)
|
||||||
|
trade = True
|
||||||
|
|
||||||
|
if bar.high >= self.longEntry2 and self.pos < 2:
|
||||||
|
self.buy(self.longEntry2, 1)
|
||||||
|
trade = True
|
||||||
|
|
||||||
|
if bar.high >= self.longEntry3 and self.pos < 3:
|
||||||
|
self.buy(self.longEntry3, 1)
|
||||||
|
trade = True
|
||||||
|
|
||||||
|
if bar.high >= self.longEntry4 and self.pos < 4:
|
||||||
|
self.buy(self.longEntry4, 1)
|
||||||
|
trade = True
|
||||||
|
|
||||||
|
if trade:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 没有仓位或者持有空头仓位的时候,可以做空(加仓)
|
||||||
|
elif self.pos <= 0:
|
||||||
|
if bar.low <= self.shortEntry1 and self.pos > -1:
|
||||||
|
self.short(self.shortEntry1, 1)
|
||||||
|
|
||||||
|
if bar.low <= self.shortEntry2 and self.pos > -2:
|
||||||
|
self.short(self.shortEntry2, 1)
|
||||||
|
|
||||||
|
if bar.low <= self.shortEntry3 and self.pos > -3:
|
||||||
|
self.short(self.shortEntry3, 1)
|
||||||
|
|
||||||
|
if bar.low <= self.shortEntry4 and self.pos > -4:
|
||||||
|
self.short(self.shortEntry4, 1)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def calculateIndicator(self):
|
||||||
|
"""计算技术指标"""
|
||||||
|
self.entryUp, self.entryDown = self.am.donchian(self.entryWindow)
|
||||||
|
self.exitUp, self.exitDown = self.am.donchian(self.exitWindow)
|
||||||
|
|
||||||
|
if not self.pos:
|
||||||
|
self.atrVolatility = self.am.atr(self.atrWindow)
|
||||||
|
|
||||||
|
self.longEntry1 = self.entryUp
|
||||||
|
self.longEntry2 = self.entryUp + self.atrVolatility * 0.5
|
||||||
|
self.longEntry3 = self.entryUp + self.atrVolatility * 1
|
||||||
|
self.longEntry4 = self.entryUp + self.atrVolatility * 1.5
|
||||||
|
|
||||||
|
self.shortEntry1 = self.entryDown
|
||||||
|
self.shortEntry2 = self.entryDown - self.atrVolatility * 0.5
|
||||||
|
self.shortEntry3 = self.entryDown - self.atrVolatility * 1
|
||||||
|
self.shortEntry4 = self.entryDown - self.atrVolatility * 1.5
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def newSignal(self, direction, offset, price, volume):
|
||||||
|
""""""
|
||||||
|
self.portfolio.newSignal(self, direction, offset, price, volume)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def buy(self, price, volume):
|
||||||
|
"""买入开仓"""
|
||||||
|
self.open(price, volume)
|
||||||
|
self.newSignal(DIRECTION_LONG, OFFSET_OPEN, price, volume)
|
||||||
|
|
||||||
|
# 以最后一次加仓价格,加上两倍N计算止损
|
||||||
|
self.longStop = price - self.atrVolatility * 2
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def sell(self, price):
|
||||||
|
"""卖出平仓"""
|
||||||
|
volume = abs(self.pos)
|
||||||
|
self.close(price)
|
||||||
|
self.newSignal(DIRECTION_SHORT, OFFSET_CLOSE, price, volume)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def short(self, price, volume):
|
||||||
|
"""卖出开仓"""
|
||||||
|
self.open(price, -volume)
|
||||||
|
self.newSignal(DIRECTION_SHORT, OFFSET_OPEN, price, volume)
|
||||||
|
|
||||||
|
# 以最后一次加仓价格,加上两倍N计算止损
|
||||||
|
self.shortStop = price + self.atrVolatility * 2
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def cover(self, price):
|
||||||
|
"""买入平仓"""
|
||||||
|
volume = abs(self.pos)
|
||||||
|
self.close(price)
|
||||||
|
self.newSignal(DIRECTION_LONG, OFFSET_CLOSE, price, volume)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def open(self, price, change):
|
||||||
|
"""开仓"""
|
||||||
|
self.pos += change
|
||||||
|
|
||||||
|
if not self.result:
|
||||||
|
self.result = TurtleResult()
|
||||||
|
self.result.open(price, change)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def close(self, price):
|
||||||
|
"""平仓"""
|
||||||
|
self.pos = 0
|
||||||
|
|
||||||
|
self.result.close(price)
|
||||||
|
self.resultList.append(self.result)
|
||||||
|
self.result = None
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def lastPnl(self):
|
||||||
|
"""上一笔交易的盈亏"""
|
||||||
|
if not self.resultList:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
result = self.resultList[-1]
|
||||||
|
return result.pnl
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
class TurtlePortfolio(object):
|
||||||
|
"""海龟组合"""
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def __init__(self, engine):
|
||||||
|
"""Constructor"""
|
||||||
|
self.engine = engine
|
||||||
|
|
||||||
|
self.signalDict = defaultdict(list)
|
||||||
|
|
||||||
|
self.posDict = {} # 每个品种的持仓情况
|
||||||
|
self.totalLong = 0 # 总的多头持仓
|
||||||
|
self.totalShort = 0 # 总的空头持仓
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def init(self, vtSymbolList):
|
||||||
|
""""""
|
||||||
|
for vtSymbol in vtSymbolList:
|
||||||
|
signal1 = TurtleSignal(self, vtSymbol, 20, 10, 20, True)
|
||||||
|
signal2 = TurtleSignal(self, vtSymbol, 55, 20, 20, False)
|
||||||
|
|
||||||
|
l = self.signalDict[vtSymbol]
|
||||||
|
l.append(signal1)
|
||||||
|
l.append(signal2)
|
||||||
|
|
||||||
|
self.posDict[vtSymbol] = 0
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def onBar(self, bar):
|
||||||
|
""""""
|
||||||
|
for signal in self.signalDict[bar.vtSymbol]:
|
||||||
|
signal.onBar(bar)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def newSignal(self, signal, direction, offset, price, volume):
|
||||||
|
"""对交易信号进行过滤,符合条件的才发单执行"""
|
||||||
|
pos = self.posDict[signal.vtSymbol]
|
||||||
|
|
||||||
|
# 开仓
|
||||||
|
if offset == OFFSET_OPEN:
|
||||||
|
# 检查上一次是否为盈利
|
||||||
|
if signal.profitCheck:
|
||||||
|
pnl = signal.lastPnl()
|
||||||
|
if pnl > 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 买入
|
||||||
|
if direction == DIRECTION_LONG:
|
||||||
|
# 组合持仓不能超过上限
|
||||||
|
if self.totalLong >= MAX_DIRECTION_POS:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 单品种持仓不能超过上限
|
||||||
|
if self.posDict[signal.vtSymbol] >= MAX_PRODUCT_POS:
|
||||||
|
return
|
||||||
|
# 卖出
|
||||||
|
else:
|
||||||
|
if self.totalShort <= -MAX_DIRECTION_POS:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.posDict[signal.vtSymbol] <= -MAX_PRODUCT_POS:
|
||||||
|
return
|
||||||
|
# 平仓
|
||||||
|
else:
|
||||||
|
if direction == DIRECTION_LONG:
|
||||||
|
# 必须有空头持仓
|
||||||
|
if pos >= 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 平仓数量不能超过空头持仓
|
||||||
|
volume = min(volume, abs(pos))
|
||||||
|
else:
|
||||||
|
if pos <= 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
volume = min(volume, abs(pos))
|
||||||
|
|
||||||
|
self.sendOrder(signal.vtSymbol, direction, offset, price, volume)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
def sendOrder(self, vtSymbol, direction, offset, price, volume):
|
||||||
|
""""""
|
||||||
|
# 计算合约持仓
|
||||||
|
if direction == DIRECTION_LONG:
|
||||||
|
self.posDict[vtSymbol] += volume
|
||||||
|
else:
|
||||||
|
self.posDict[vtSymbol] -= volume
|
||||||
|
|
||||||
|
# 计算总持仓
|
||||||
|
self.totalLong = 0
|
||||||
|
self.totalShort = 0
|
||||||
|
|
||||||
|
for pos in self.posDict.values():
|
||||||
|
if pos > 0:
|
||||||
|
self.totalLong += pos
|
||||||
|
elif pos < 0:
|
||||||
|
self.totalShort += pos
|
||||||
|
|
||||||
|
# 向回测引擎中发单记录
|
||||||
|
self.engine.sendOrder(vtSymbol, direction, offset, price, volume)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user