[关闭]
@Channelchan 2018-12-04T22:15:57.000000Z 字数 17363 阅读 59531

思路验证

通过vnpy回测策略思路得到回测绩效,验证策略可行性

clone vnpy from https://github.com/xingetouzi/vnpy_fxdayu and install it

查看example中的策略demo了解vnpy策略编写格式

(1)策略类名以Strategy结尾,如multiATR_Strategy
(2)super(multiATR_Strategy, self).init(ctaEngine, setting),继承CtaTemplate类模板
(3)onInit()函数初始化变量,self.setArrayManagerSize()设置数据推送长度
(4)保存在paramList和varList的参数和变量的值会在vnpy的策略界面显示,方便策略运行时查看
(5)onBar()函数编写一分钟周期策略逻辑,通常作为出场指标计算与风险控制
(6)on15MinBar()函数,在15分钟周期下编写交易逻辑,即每15分钟计算一次指标
(7)onOrder()和onTrade()分别为交易委托信息和交易成交信息推送
(8)在执行开平仓逻辑时候,使用bar.close*1.02或者bar.close*0.98是为了更快速成交,避免发出信号没平仓的情况
(9)仓位信息保存在posdict中,适用posdict[symbol+'LONG'] 和 posdict[symbol+'SHORT']读取
(10)self.writeCtaLog()在实盘交易中会输出日志并保存在temp文件中,方便策略复盘

对上一章节提出的假设进行回测

进场条件,当满足波动率的条件以及突破周期最高低价格时买入或者卖出
出场条件,使用吊灯止损以及固定止盈法平仓(加减仓以及止盈止损方法见大鱼学院仓位管理内容)

  1. #coding: utf-8
  2. from __future__ import division
  3. from vnpy.trader.vtConstant import *
  4. from vnpy.trader.app.ctaStrategy import CtaTemplate
  5. import warnings
  6. import numpy as np
  7. import talib as ta
  8. import pandas as pd
  9. from datetime import datetime
  10. warnings.filterwarnings('ignore')
  11. class multiATR_Strategy(CtaTemplate):
  12. className = 'multiATR_Strategy'
  13. author = 'Sky'
  14. #策略交易标的
  15. symbol = EMPTY_STRING
  16. posDict = {} # 仓位数据缓存
  17. eveningDict = {} # 可平仓量数据缓存
  18. bondDict = {} # 保证金数据缓存
  19. up_value = 16
  20. down_value = 16
  21. window1 = 18
  22. window2 = 28
  23. trailingPercent = 4
  24. fixsize = 1
  25. stopRatio = 0.02 # 止损百分比
  26. profitMultiplier = 6 # 止盈与止损比例
  27. transactionPrice = {} # 记录成交价格
  28. intraTradeHighDict = {}
  29. intraTradeLowDict = {}
  30. adx_con = {} # 均线趋势,多头1,空头-1
  31. atr_con = {}
  32. cross = {}
  33. Ma_exit = {}
  34. MA_CON = {}
  35. # 策略变量
  36. initbars = 100 # 获取历史数据的条数
  37. # 参数列表,保存了参数的名称
  38. paramList = ['name',
  39. 'className',
  40. 'author',
  41. 'Window1',
  42. 'Window2',
  43. 'trailingPercent',
  44. 'fixsize',
  45. 'stopRatio',
  46. 'profitMultiplier',
  47. 'up_value',
  48. 'down_value']
  49. # 变量列表,保存了变量的名称
  50. varList = ['inited',
  51. 'trading',
  52. 'posDict',
  53. 'atr_con',
  54. 'cross',
  55. 'MA_CON']
  56. # 同步列表,保存了需要保存到数据库的变量名称
  57. syncList = ['posDict',
  58. 'eveningDict',
  59. 'bondDict']
  60. # ----------------------------------------------------------------------
  61. def __init__(self, ctaEngine, setting):
  62. """Constructor"""
  63. super(multiATR_Strategy, self).__init__(ctaEngine, setting)
  64. # ----------------------------------------------------------------------
  65. def onInit(self):
  66. """初始化策略(必须由用户继承实现)"""
  67. self.writeCtaLog(u'策略%s:初始化' % self.className)
  68. self.adx_con = {s: 0 for s in self.symbolList}
  69. self.atr_con = {s: 0 for s in self.symbolList}
  70. self.cross = {s: 0 for s in self.symbolList}
  71. self.transactionPrice = {s: 0 for s in self.symbolList}
  72. self.Ma_exit = {s: 0 for s in self.symbolList}
  73. self.MA_CON = {s: 0 for s in self.symbolList}
  74. self.intraTradeHighDict = {s: 0 for s in self.symbolList}
  75. self.intraTradeLowDict ={s: 0 for s in self.symbolList}
  76. self.setArrayManagerSize(100)
  77. self.putEvent()
  78. '''
  79. 在点击初始化策略时触发,载入历史数据,会推送到onbar去执行updatebar,但此时ctaEngine下单逻辑为False,不会触发下单.
  80. '''
  81. # ----------------------------------------------------------------------
  82. def onStart(self):
  83. """启动策略(必须由用户继承实现)"""
  84. # self.writeCtaLog(u'策略%s:启动' % self.className)
  85. # self.ctaEngine.loadSyncData(self) # 加载当前正确的持仓
  86. self.putEvent()
  87. '''
  88. 在点击启动策略时触发,此时的ctaEngine会将下单逻辑改为True,此时开始推送到onbar的数据会触发下单.
  89. '''
  90. # ----------------------------------------------------------------------
  91. def onStop(self):
  92. """停止策略(必须由用户继承实现)"""
  93. # self.writeCtaLog(u'策略%s:停止' % self.className)
  94. self.putEvent()
  95. # ----------------------------------------------------------------------
  96. def onRestore(self):
  97. """从错误状态恢复策略(必须由用户集成实现)"""
  98. # self.writeCtaLog(u'策略%s:恢复策略状态成功' % self.Name)
  99. self.putEvent()
  100. # ----------------------------------------------------------------------
  101. def onTick(self, tick):
  102. """收到行情TICK推送"""
  103. pass
  104. # ----------------------------------------------------------------------
  105. def onBar(self, bar):
  106. """收到1分钟K线推送"""
  107. symbol = bar.vtSymbol
  108. # 无持仓状态,重置变量
  109. if self.posDict[symbol+"_LONG"] == 0 and self.posDict[symbol+"_SHORT"] == 0:
  110. self.intraTradeHighDict[symbol] = 0
  111. self.intraTradeLowDict[symbol] = 999999
  112. # 洗价器
  113. elif (self.posDict[symbol+"_LONG"] > 0):
  114. self.intraTradeHighDict[symbol] = max(self.intraTradeHighDict[symbol], bar.high)
  115. self.intraTradeLowDict[symbol] = bar.low
  116. self.longStop = self.intraTradeHighDict[symbol]*(1-self.trailingPercent/100)
  117. if bar.close<=self.longStop or (bar.close > self.transactionPrice[symbol] * (1 + self.profitMultiplier * self.stopRatio)):
  118. self.cancelAll()
  119. self.sell(symbol, bar.close*0.98, self.fixsize)
  120. elif (self.posDict[symbol+"_SHORT"] > 0):
  121. self.intraTradeLowDict[symbol] = min(self.intraTradeLowDict[symbol], bar.low)
  122. self.intraTradeHighDict[symbol] = bar.high
  123. self.shortStop = self.intraTradeLowDict[symbol]*(1+self.trailingPercent/100)
  124. if (bar.close>=self.shortStop) or (bar.close < self.transactionPrice[symbol] * (1 - self.profitMultiplier * self.stopRatio)):
  125. self.cancelAll()
  126. self.cover(symbol, bar.close*1.02, self.fixsize,priceType=PRICETYPE_LIMITPRICE,levelRate = 10)
  127. self.putEvent()
  128. # ----------------------------------------------------------------------
  129. def on15MinBar(self, bar):
  130. """60分钟K线推送"""
  131. symbol = bar.vtSymbol
  132. am15 = self.getArrayManager(symbol, "15m")
  133. if not am15.inited:
  134. return
  135. ### 计算波动率指标
  136. atr14 = ta.ATR(am15.high,am15.low,am15.close,14)
  137. atr66 = ta.ATR(am15.high,am15.low,am15.close,66)
  138. self.upband = ta.MAX(am15.close,self.up_value)
  139. self.downband = ta.MIN(am15.close,self.down_value)
  140. if am15.close[-2]<self.upband[-2] and am15.close[-1]>self.upband[-2]:
  141. self.cross[symbol] = 1
  142. elif am15.close[-2]>self.downband[-2] and am15.close[-1]<self.downband[-2]:
  143. self.cross[symbol] = -1
  144. else:
  145. self.cross[symbol] = 0
  146. if atr14[-1]>atr66[-1]:
  147. self.atr_con[symbol] = 1
  148. else:
  149. self.atr_con[symbol] = 0
  150. if (self.cross[symbol] == 1 and self.atr_con[symbol] == 1):
  151. # 如果满足开仓条件时手头没有持仓,则直接做多
  152. if (self.posDict[symbol+"_LONG"]==0) and (self.posDict[symbol+"_SHORT"]==0):
  153. self.buy(symbol,bar.close*1.02, self.fixsize)
  154. # 如果有空头持仓,则先平空,再做多
  155. elif self.posDict[symbol+"_SHORT"] == 1:
  156. self.cover(symbol,bar.close*1.02, self.fixsize)
  157. self.buy(symbol,bar.close*1.02, self.fixsize)
  158. # 空头开仓
  159. elif (self.cross[symbol] == -1 and self.atr_con[symbol] == 1) :
  160. if (self.posDict[symbol+"_LONG"]==0) and (self.posDict[symbol+"_SHORT"]==0):
  161. self.short(symbol,bar.close, self.fixsize)
  162. elif self.posDict[symbol+"_LONG"] == 1:
  163. self.sell(symbol,bar.close*0.98, self.fixsize)
  164. self.short(symbol,bar.close*0.98, self.fixsize)
  165. def onOrder(self, order):
  166. """收到委托变化推送(必须由用户继承实现)"""
  167. self.putEvent()
  168. # ----------------------------------------------------------------------
  169. def onTrade(self, trade):
  170. """收到成交信息变化推送"""
  171. symbol = trade.vtSymbol
  172. #print("\n\n\n\n stg onTrade", trade.vtSymbol)
  173. self.transactionPrice[symbol] = trade.price
  174. #print('trade direction',trade.direction,'offset',trade.offset,'price',trade.price, trade.dt)
  175. # self.writeCtaLog('onTrade price:%s'%trade.price)
  176. # self.saveSyncData()
  177. pass
  178. # ---------------------------------------------------------------------
  179. def onStopOrder(self, so):
  180. """停止单推送"""
  181. pass
  1. from __future__ import division
  2. from vnpy.trader.app.ctaStrategy import BacktestingEngine
  3. from vnpy.trader.app.ctaStrategy.ctaBase import *
  4. if __name__ == '__main__':
  5. # 创建回测引擎
  6. engine = BacktestingEngine()
  7. # 设置引擎的回测模式为K线
  8. engine.setBacktestingMode(engine.BAR_MODE)
  9. # 设置使用的历史数据库
  10. engine.setDatabase('VnTrader_1Min_Db')
  11. # 设置回测用的数据起始日期,initHours 默认值为 0
  12. engine.setStartDate('20180605 01:00', initHours=10)
  13. engine.setEndDate('20181030 12:00')
  14. # 设置产品相关参数
  15. engine.setCapital(100000) # 设置起始资金,默认值是1,000,000
  16. engine.setSlippage(0.002) # 股指1跳
  17. engine.setRate(0.3 / 10000) # 万0.3
  18. engine.setSize(300) # 股指合约大小
  19. engine.setPriceTick(0.002) # 股指最小价格变动
  20. # 策略报告默认不输出,默认文件夹生成于当前文件夹下
  21. engine.setLog(True, "D:\\log\\") # 设置是否输出日志和交割单, 默认值是不输出False
  22. engine.setCachePath("D:\\vnpy_data\\") # 设置本地数据缓存的路径,默认存在用户文件夹内
  23. # 在引擎中创建策略对象
  24. d = {'symbolList': ['EOSUSDT:binance']}
  25. #d = {'symbolList':['eos_quarter:OKEX']}
  26. engine.initStrategy(multiATR_Strategy, d)
  27. # 开始跑回测
  28. engine.runBacktesting()
  29. # 显示回测结果
  30. engine.showBacktestingResult()
  31. engine.showDailyResult()
2018-12-03 01:03:15.091338  第一笔交易:  2018-06-05 18:12:00
2018-12-03 01:03:15.091338  最后一笔交易: 2018-10-30 11:58:00
2018-12-03 01:03:15.091338  总交易次数:  260
2018-12-03 01:03:15.091338  总盈亏:    -320.8
2018-12-03 01:03:15.091338  最大回撤:   -1,163.69
2018-12-03 01:03:15.091338  平均每笔盈利: -1.23
2018-12-03 01:03:15.091338  平均每笔滑点: 1.2
2018-12-03 01:03:15.091338  平均每笔佣金: 0.12
2018-12-03 01:03:15.091338  胜率      33.46%
2018-12-03 01:03:15.091338  盈利交易平均值 76.12
2018-12-03 01:03:15.091338  亏损交易平均值 -40.13
2018-12-03 01:03:15.091338  盈亏比:    1.9
2018-12-03 01:03:16.138695  策略回测统计图已保存

output_4_3.png-55.5kB

2018-12-03 01:03:16.974330  计算按日统计结果
2018-12-03 01:03:17.017303  ------------------------------
2018-12-03 01:03:17.017303  首个交易日:  2018-06-05 00:00:00
2018-12-03 01:03:17.017303  最后交易日:  2018-10-30 00:00:00
2018-12-03 01:03:17.017303  总交易日:   148
2018-12-03 01:03:17.017303  盈利交易日   65
2018-12-03 01:03:17.017303  亏损交易日:  82
2018-12-03 01:03:17.017303  起始资金:   100000
2018-12-03 01:03:17.017303  结束资金:   99,679.85
2018-12-03 01:03:17.017303  总收益率:   -0.32%
2018-12-03 01:03:17.017303  年化收益:   -0.52%
2018-12-03 01:03:17.017303  总盈亏:    -320.15
2018-12-03 01:03:17.017303  最大回撤:   -1,057.75
2018-12-03 01:03:17.018302  百分比最大回撤: -1.05%
2018-12-03 01:03:17.018302  总手续费:   32.21
2018-12-03 01:03:17.018302  总滑点:    311.4
2018-12-03 01:03:17.018302  总成交金额:  1,073,736.36
2018-12-03 01:03:17.018302  总成交笔数:  519
2018-12-03 01:03:17.018302  日均盈亏:   -2.16
2018-12-03 01:03:17.018302  日均手续费:  0.22
2018-12-03 01:03:17.018302  日均滑点:   2.1
2018-12-03 01:03:17.018302  日均成交金额: 7,254.98
2018-12-03 01:03:17.018302  日均成交笔数: 3.51
2018-12-03 01:03:17.018302  日均收益率:  -0.0%
2018-12-03 01:03:17.019305  收益标准差:  0.11%
2018-12-03 01:03:17.019305  Sharpe Ratio:   -0.4
2018-12-03 01:03:19.177748  策略回测绩效图已保存

output_4_5.png-53.7kB

回测绩效按策略类型不同观察的侧重点不同

(1)收益率等盈亏指标观察总体盈利情况
(2)总成交笔数观察交易频率
(3)最大回测以及日均盈亏等按日统计指标观察是否有一些黑天鹅事件等特殊风险
(4)通常夏普比率大于2以上是比较不错的策略
(5)engine.setLog(True, "D:\log\")获取策略回测绩效报告
(6)engine.setCachePath("D:\vnpy_data\")数据缓存地址

发现问题:开仓次数太频繁,4个月的时间开仓260次,而且胜率较低

解决方案:加入更大周期的均线作为过滤条件,回测如下

  1. #coding: utf-8
  2. from __future__ import division
  3. from vnpy.trader.vtConstant import *
  4. from vnpy.trader.app.ctaStrategy import CtaTemplate
  5. import warnings
  6. import numpy as np
  7. import talib as ta
  8. import pandas as pd
  9. from datetime import datetime
  10. warnings.filterwarnings('ignore')
  11. class multiATR_Strategy(CtaTemplate):
  12. className = 'multiATR_Strategy'
  13. author = 'Sky'
  14. #策略交易标的
  15. up_value = 16
  16. down_value = 16
  17. Window1 = 14
  18. Window2 = 66
  19. Window3 = 18
  20. Window4 = 28
  21. trailingPercent = 4
  22. fixsize = 1
  23. stopRatio = 0.02 # 止损百分比
  24. profitMultiplier = 6 # 止盈与止损比例
  25. transactionPrice = {} # 记录成交价格
  26. intraTradeHighDict = {}
  27. intraTradeLowDict = {}
  28. atr_con = {}
  29. cross = {}
  30. MA_CON = {}
  31. # 策略变量
  32. initbars = 100 # 获取历史数据的条数
  33. # 参数列表,保存了参数的名称
  34. paramList = ['name',
  35. 'className',
  36. 'author',
  37. 'Window1',
  38. 'Window2',
  39. 'trailingPercent',
  40. 'fixsize',
  41. 'stopRatio',
  42. 'profitMultiplier',
  43. 'up_value',
  44. 'down_value']
  45. # 变量列表,保存了变量的名称
  46. varList = ['inited',
  47. 'trading',
  48. 'posDict',
  49. 'atr_con',
  50. 'cross',
  51. 'MA_CON']
  52. # 同步列表,保存了需要保存到数据库的变量名称
  53. syncList = ['posDict',
  54. 'eveningDict',
  55. 'bondDict']
  56. # ----------------------------------------------------------------------
  57. def __init__(self, ctaEngine, setting):
  58. """Constructor"""
  59. super(multiATR_Strategy, self).__init__(ctaEngine, setting)
  60. # ----------------------------------------------------------------------
  61. def onInit(self):
  62. """初始化策略(必须由用户继承实现)"""
  63. self.writeCtaLog(u'策略%s:初始化' % self.className)
  64. self.adx_con = {s: 0 for s in self.symbolList}
  65. self.atr_con = {s: 0 for s in self.symbolList}
  66. self.cross = {s: 0 for s in self.symbolList}
  67. self.transactionPrice = {s: 0 for s in self.symbolList}
  68. self.Ma_exit = {s: 0 for s in self.symbolList}
  69. self.MA_CON = {s: 0 for s in self.symbolList}
  70. self.intraTradeHighDict = {s: 0 for s in self.symbolList}
  71. self.intraTradeLowDict ={s: 0 for s in self.symbolList}
  72. self.setArrayManagerSize(100)
  73. self.putEvent()
  74. '''
  75. 在点击初始化策略时触发,载入历史数据,会推送到onbar去执行updatebar,但此时ctaEngine下单逻辑为False,不会触发下单.
  76. '''
  77. # ----------------------------------------------------------------------
  78. def onStart(self):
  79. """启动策略(必须由用户继承实现)"""
  80. self.writeCtaLog(u'策略%s:启动' % self.className)
  81. # self.ctaEngine.loadSyncData(self) # 加载当前正确的持仓
  82. self.putEvent()
  83. '''
  84. 在点击启动策略时触发,此时的ctaEngine会将下单逻辑改为True,此时开始推送到onbar的数据会触发下单.
  85. '''
  86. # ----------------------------------------------------------------------
  87. def onStop(self):
  88. """停止策略(必须由用户继承实现)"""
  89. # self.writeCtaLog(u'策略%s:停止' % self.className)
  90. self.putEvent()
  91. # ----------------------------------------------------------------------
  92. def onRestore(self):
  93. """从错误状态恢复策略(必须由用户集成实现)"""
  94. # self.writeCtaLog(u'策略%s:恢复策略状态成功' % self.Name)
  95. self.putEvent()
  96. # ----------------------------------------------------------------------
  97. def onTick(self, tick):
  98. """收到行情TICK推送"""
  99. pass
  100. # ----------------------------------------------------------------------
  101. def onBar(self, bar):
  102. """收到1分钟K线推送"""
  103. symbol = bar.vtSymbol
  104. # 无持仓状态,重置变量
  105. if self.posDict[symbol+"_LONG"] == 0 and self.posDict[symbol+"_SHORT"] == 0:
  106. self.intraTradeHighDict[symbol] = 0
  107. self.intraTradeLowDict[symbol] = 999999
  108. # 洗价器
  109. elif (self.posDict[symbol+"_LONG"] > 0):
  110. self.intraTradeHighDict[symbol] = max(self.intraTradeHighDict[symbol], bar.high)
  111. self.intraTradeLowDict[symbol] = bar.low
  112. self.longStop = self.intraTradeHighDict[symbol]*(1-self.trailingPercent/100)
  113. if bar.close<=self.longStop or (bar.close > self.transactionPrice[symbol] * (1 + self.profitMultiplier * self.stopRatio)):
  114. self.cancelAll()
  115. self.sell(symbol, bar.close*0.98, self.fixsize)
  116. elif (self.posDict[symbol+"_SHORT"] > 0):
  117. self.intraTradeLowDict[symbol] = min(self.intraTradeLowDict[symbol], bar.low)
  118. self.intraTradeHighDict[symbol] = bar.high
  119. self.shortStop = self.intraTradeLowDict[symbol]*(1+self.trailingPercent/100)
  120. if (bar.close>=self.shortStop) or (bar.close < self.transactionPrice[symbol] * (1 - self.profitMultiplier * self.stopRatio)):
  121. self.cancelAll()
  122. self.cover(symbol, bar.close*1.02, self.fixsize)
  123. self.putEvent()
  124. # ----------------------------------------------------------------------
  125. def on15MinBar(self, bar):
  126. """60分钟K线推送"""
  127. symbol = bar.vtSymbol
  128. am15 = self.getArrayManager(symbol, "15m")
  129. if not am15.inited:
  130. return
  131. ### 计算波动率指标
  132. atr20 = ta.ATR(am15.high,am15.low,am15.close,self.Window1)
  133. atr50 = ta.ATR(am15.high,am15.low,am15.close,self.Window2)
  134. self.upband = ta.MAX(am15.close,self.up_value)
  135. self.downband = ta.MIN(am15.close,self.down_value)
  136. if am15.close[-2]<self.upband[-2] and am15.close[-1]>self.upband[-2]:
  137. self.cross[symbol] = 1
  138. elif am15.close[-2]>self.downband[-2] and am15.close[-1]<self.downband[-2]:
  139. self.cross[symbol] = -1
  140. else:
  141. self.cross[symbol] = 0
  142. if atr20[-1]>atr50[-1]:
  143. self.atr_con[symbol] = 1
  144. else:
  145. self.atr_con[symbol] = 0
  146. def on25MinBar(self, bar):
  147. """60分钟K线推送"""
  148. symbol = bar.vtSymbol
  149. am25 = self.getArrayManager(symbol, "25m")
  150. if not am25.inited:
  151. return
  152. MA6 = ta.MA(am25.close,self.Window3)
  153. MA22 = ta.MA(am25.close,self.Window4)
  154. #加入均线过滤条件
  155. if MA6[-1]>MA6[-2] and MA22[-1]>MA22[-2]:
  156. self.MA_CON[symbol] = 1
  157. elif MA6[-1]<MA6[-2] and MA22[-1]<MA22[-2]:
  158. self.MA_CON[symbol] = -1
  159. else:
  160. self.MA_CON[symbol] = 0
  161. #print('cross:%s,adx:%s,atr:%s,MACON:%s'%(self.cross[symbol],self.adx_con[symbol],self.atr_con[symbol],self.MA_CON[symbol]))
  162. if (self.cross[symbol] == 1 and self.atr_con[symbol] == 1 and self.MA_CON[symbol] == 1):
  163. # 如果金叉时手头没有持仓,则直接做多
  164. if (self.posDict[symbol+"_LONG"]==0) and (self.posDict[symbol+"_SHORT"]==0):
  165. self.buy(symbol,bar.close*1.015, self.fixsize)
  166. # 如果有空头持仓,则先平空,再做多
  167. elif self.posDict[symbol+"_SHORT"] == 1:
  168. self.cover(symbol,bar.close, self.fixsize)
  169. self.buy(symbol,bar.close, self.fixsize)
  170. # 空头开仓
  171. elif (self.cross[symbol] == -1 and self.atr_con[symbol] == 1 and self.MA_CON[symbol] == -1) :
  172. if (self.posDict[symbol+"_LONG"]==0) and (self.posDict[symbol+"_SHORT"]==0):
  173. self.short(symbol,bar.close, self.fixsize)
  174. elif self.posDict[symbol+"_LONG"] == 1:
  175. self.sell(symbol,bar.close, self.fixsize)
  176. self.short(symbol,bar.close, self.fixsize)
  177. def onOrder(self, order):
  178. """收到委托变化推送(必须由用户继承实现)"""
  179. self.putEvent()
  180. # ----------------------------------------------------------------------
  181. def onTrade(self, trade):
  182. """收到成交信息变化推送"""
  183. symbol = trade.vtSymbol
  184. #print("\n\n\n\n stg onTrade", trade.vtSymbol)
  185. self.transactionPrice[symbol] = trade.price
  186. #print('trade direction',trade.direction,'offset',trade.offset,'price',trade.price, trade.dt)
  187. # self.writeCtaLog('onTrade price:%s'%trade.price)
  188. # self.saveSyncData()
  189. pass
  190. # ---------------------------------------------------------------------
  191. def onStopOrder(self, so):
  192. """停止单推送"""
  193. pass
  1. from __future__ import division
  2. from vnpy.trader.app.ctaStrategy import BacktestingEngine
  3. from vnpy.trader.app.ctaStrategy.ctaBase import *
  4. if __name__ == '__main__':
  5. # 创建回测引擎
  6. engine = BacktestingEngine()
  7. # 设置引擎的回测模式为K线
  8. engine.setBacktestingMode(engine.BAR_MODE)
  9. # 设置使用的历史数据库
  10. engine.setDatabase('VnTrader_1Min_Db')
  11. # 设置回测用的数据起始日期,initHours 默认值为 0
  12. engine.setStartDate('20180605 01:00', initHours=10)
  13. engine.setEndDate('20181030 12:00')
  14. # 设置产品相关参数
  15. engine.setCapital(100000) # 设置起始资金,默认值是1,000,000
  16. engine.setSlippage(0.002) # 股指1跳
  17. engine.setRate(0.3 / 10000) # 万0.3
  18. engine.setSize(300) # 股指合约大小
  19. engine.setPriceTick(0.002) # 股指最小价格变动
  20. # 策略报告默认不输出,默认文件夹生成于当前文件夹下
  21. engine.setLog(True, "D:\\log\\") # 设置是否输出日志和交割单, 默认值是不输出False
  22. engine.setCachePath("D:\\vnpy_data\\") # 设置本地数据缓存的路径,默认存在用户文件夹内
  23. # 在引擎中创建策略对象
  24. d = {'symbolList': ['EOSUSDT:binance']}
  25. engine.initStrategy(multiATR_Strategy, d)
  26. # 开始跑回测
  27. engine.runBacktesting()
  28. # 显示回测结果
  29. engine.showBacktestingResult()
  30. engine.showDailyResult()
2018-12-03 01:12:51.063547  第一笔交易:  2018-06-05 18:12:00
2018-12-03 01:12:51.063547  最后一笔交易: 2018-10-30 11:58:00
2018-12-03 01:12:51.064546  总交易次数:  113
2018-12-03 01:12:51.064546  总盈亏:    767.36
2018-12-03 01:12:51.064546  最大回撤:   -541.01
2018-12-03 01:12:51.064546  平均每笔盈利: 6.79
2018-12-03 01:12:51.064546  平均每笔滑点: 1.2
2018-12-03 01:12:51.064546  平均每笔佣金: 0.13
2018-12-03 01:12:51.064546  胜率      39.82%
2018-12-03 01:12:51.064546  盈利交易平均值 98.0
2018-12-03 01:12:51.064546  亏损交易平均值 -53.57
2018-12-03 01:12:51.064546  盈亏比:    1.83
2018-12-03 01:12:51.856062  策略回测统计图已保存

output_8_3.png-40.2kB

2018-12-03 01:12:52.505662  计算按日统计结果
2018-12-03 01:12:52.548636  ------------------------------
2018-12-03 01:12:52.548636  首个交易日:  2018-06-05 00:00:00
2018-12-03 01:12:52.548636  最后交易日:  2018-10-30 00:00:00
2018-12-03 01:12:52.548636  总交易日:   148
2018-12-03 01:12:52.548636  盈利交易日   48
2018-12-03 01:12:52.548636  亏损交易日:  74
2018-12-03 01:12:52.548636  起始资金:   100000
2018-12-03 01:12:52.548636  结束资金:   100,768.01
2018-12-03 01:12:52.548636  总收益率:   0.77%
2018-12-03 01:12:52.548636  年化收益:   1.25%
2018-12-03 01:12:52.548636  总盈亏:    768.01
2018-12-03 01:12:52.548636  最大回撤:   -527.52
2018-12-03 01:12:52.548636  百分比最大回撤: -0.52%
2018-12-03 01:12:52.548636  总手续费:   14.78
2018-12-03 01:12:52.548636  总滑点:    135.0
2018-12-03 01:12:52.548636  总成交金额:  492,704.55
2018-12-03 01:12:52.548636  总成交笔数:  225
2018-12-03 01:12:52.549634  日均盈亏:   5.19
2018-12-03 01:12:52.549634  日均手续费:  0.1
2018-12-03 01:12:52.549634  日均滑点:   0.91
2018-12-03 01:12:52.549634  日均成交金额: 3,329.08
2018-12-03 01:12:52.549634  日均成交笔数: 1.52
2018-12-03 01:12:52.549634  日均收益率:  0.01%
2018-12-03 01:12:52.549634  收益标准差:  0.09%
2018-12-03 01:12:52.549634  Sharpe Ratio:   0.97
2018-12-03 01:12:54.623435  策略回测绩效图已保存

output_8_5.png-44.2kB

交易次数从260减少到113,整个绩效也普遍提高了

接着可以对参数进行优化,选择更优参数,参考example里的runOptimize.py脚本

1、target='sharpeRatio'为优化指标

2、setting.addParameter('Window1', 5, 20, 5)为优化的参数

优化结果得到排序结果如下:
下载.png-722.1kB

为避免过拟合情况通常优化结果只作为参考,需进一步测试策略和验证

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注