[关闭]
@Channelchan 2018-11-28T18:09:02.000000Z 字数 20120 阅读 63329

仓位管理-pyramid

1、金字塔加仓法

2、 金字塔加仓法优化


1.金字塔加仓法

金字塔加仓法是一种逐级加仓模式,其加仓数量与盈利(亏损)幅度成反比,加仓手数如图,图中数值分别为每次加仓手数占总仓位百分比:
金字塔.png-99.4kB

见 onBar ‘金字塔加仓模块’以及on30MinBar‘进出场逻辑模块’

用参数 n 控制加仓次数,参数 Ratio 控制加仓的进场位置,参数fixsize控制最大仓位手数

则第一次开仓到第五次加满仓的手数分别为fixsize*0.3,fixsize*0.25,fixsize*0.20,fixsize*0.15,fixsize*0.10


算法如下:

if 持多头仓位 and 当前加仓次数 nPos < 3:

if 亏损达到Ratio/100:
    nPos = nPos+1

    加仓fixsize*(0.3-0.05*nPos)手数

elif 持空头仓位 and 当前加仓次数 nPos < 3:

if 亏损达到Ratio:
    nPos = nPos+1

    加仓fixsize*(0.3-0.05*nPos)手数

当执行平仓信号,将 nPos 重置为 0

if 持有多头仓位 and 死叉:

nPos = 0

elif 持有空头仓位 and 金叉:

nPos = 0
  1. ## 大的价格除以小的价格减1
  2. lastOrder=self.transactionPrice[symbol]
  3. ## 多头亏损大于一个百分比
  4. (self.transactionPrice[symbol] - bar.close)/bar.close
  5. = lastOrder/bar.close-1
  6. ## 多头盈利大于一个百分比
  7. (bar.close-self.transactionPrice[symbol])/self.transactionPrice[symbol]
  8. = bar.close/lastOrder-1
  9. ## 空头亏损大于一个百分比
  10. (bar.close - self.transactionPrice[symbol])/self.transactionPrice[symbol]
  11. = bar.close/lastOrder-1
  12. ## 空头盈利大于一个百分比
  13. (self.transactionPrice[symbol] - bar.close)/bar.close
  14. = lastOrder/bar.close-1
  1. # 设置参数
  2. nPos = 0
  3. fixsize = 100
  4. transactionPrice = {}
  5. Ratio = 0.02
  6. # 设置变量
  7. self.transactionPrice = {s: 0 for s in self.symbolList}
  8. #----------------------------------------------------------------------
  9. def onBar(self, bar):
  10. """收到Bar推送"""
  11. symbol = bar.vtSymbol
  12. lastOrder=self.transactionPrice[symbol]
  13. # 金字塔加仓模块______________________________________
  14. if (self.posDict[symbol+'_LONG']!=0 and self.nPos < 3): # 持有多头仓位并且加仓次数不超过3次
  15. if bar.close/lastOrder-1>= self.Ratio: # 计算盈利比例,达到2%
  16. self.nPos += 1 # 加仓次数减少 1 次
  17. self.buy(symbol,bar.close*1.02,self.fixsize*(0.3-0.05*self.nPos)) # 目标仓位100手,分别加仓25手、20手、15手,10手
  18. elif (self.posDict[symbol + "_SHORT"] != 0 and self.nPos < 3): # 持有空头仓位并且加仓次数不超过3次
  19. if lastOrder/bar.close-1 >= self.Ratio: # 计算盈利比例,达到2%
  20. self.nPos += 1 # 加仓次数减少 1 次
  21. self.short(symbol,bar.close*0.98,self.fixsize*(0.3-0.05*self.nPos)) # 目标仓位100手,分别加仓25手、20手、15手,10手
  22. # 发出状态更新事件
  23. self.putEvent()
  24. def on30MinBar(self, bar):
  25. """30分钟K线推送"""
  26. symbol = bar.vtSymbol
  27. am30 = self.getArrayManager(symbol, "30m")
  28. if not am30.inited:
  29. return
  30. # 计算策略需要的信号-------------------------------------------------
  31. fastMa = ta.EMA(am30.close, self.fastWindow)
  32. slowMa = ta.EMA(am30.close, self.slowWindow)
  33. crossOver = fastMa[-1]>slowMa[-1] and fastMa[-2]<=slowMa[-2] # 金叉上穿
  34. crossBelow = fastMa[-1]<slowMa[-1] and fastMa[-2]>=slowMa[-2] # 死叉下穿
  35. if crossOver:
  36. print('crossOver:',crossOver)
  37. elif crossBelow:
  38. print('crossBelow:',crossBelow)
  39. # 构建进出场逻辑-------------------------------------------------
  40. # 金叉和死叉的条件是互斥
  41. if crossOver:
  42. # 如果金叉时手头没有持仓,则直接做多
  43. if (self.posDict[symbol+'_LONG']==0) and (self.posDict[symbol+'_SHORT']==0):
  44. self.buy(symbol, bar.close*1.02, self.fixsize*0.3)
  45. # 如果有空头持仓,则先平空,再做多
  46. elif self.posDict[symbol+'_SHORT'] >0:
  47. self.cancelAll()
  48. self.cover(symbol,bar.close*1.02, self.posDict[symbol+'_SHORT'])
  49. self.nPos = 0
  50. self.buy(symbol,bar.close*1.02, self.fixsize*0.3)
  51. # 死叉和金叉相反
  52. elif crossBelow :
  53. if (self.posDict[symbol+'_LONG']==0) and (self.posDict[symbol+'_SHORT']==0):
  54. self.short(symbol,bar.close*0.98, self.fixsize*0.3)
  55. elif self.posDict[symbol+'_LONG'] >0:
  56. self.cancelAll()
  57. self.sell(symbol,bar.close*0.98, self.posDict[symbol+'_LONG'])
  58. self.nPos = 0
  59. self.short(symbol,bar.close*0.98, self.fixsize*0.3)
  60. # 发出状态更新事件
  61. self.putEvent()

金字塔加仓法案例

  1. """
  2. 这里的Demo是一个最简单的双均线策略实现
  3. """
  4. from __future__ import division
  5. from vnpy.trader.vtConstant import *
  6. from vnpy.trader.app.ctaStrategy.ctaBarManager import CtaTemplate
  7. import numpy as np
  8. import talib as ta
  9. from datetime import timedelta
  10. ########################################################################
  11. # 策略继承CtaTemplate
  12. class DoubleMaStrategy(CtaTemplate):
  13. """双指数均线策略Demo"""
  14. className = 'DoubleMaStrategy'
  15. author = 'ChannelCMT'
  16. # 策略参数
  17. barPeriod = 200
  18. fastWindow = 60 # 快速均线参数
  19. slowWindow = 120 # 慢速均线参数
  20. # 参数列表,保存了参数的名称
  21. paramList = ['name',
  22. 'className',
  23. 'author',
  24. 'fastWindow',
  25. 'slowWindow']
  26. # 变量列表,保存了变量的名称
  27. varList = ['barPeriod']
  28. nPos = 0
  29. fixsize = 100
  30. transactionPrice = {}
  31. Ratio = 0.02
  32. # 同步列表,保存了需要保存到数据库的变量名称
  33. syncList = ['posDict', 'eveningDict']
  34. #----------------------------------------------------------------------
  35. def __init__(self, ctaEngine, setting):
  36. # 首先找到策略的父类(就是类CtaTemplate),然后把DoubleMaStrategy的对象转换为类CtaTemplate的对象
  37. super().__init__(ctaEngine, setting)
  38. #----------------------------------------------------------------------
  39. def onInit(self):
  40. """初始化策略(必须由用户继承实现)"""
  41. self.writeCtaLog(u'双EMA演示策略初始化')
  42. # 生成Bar数组
  43. self.setArrayManagerSize(self.barPeriod)
  44. self.transactionPrice = {s: 0 for s in self.symbolList}
  45. self.mail("chushihuaaaaaaaaaaaaaaaaaaaaaaaaa")
  46. self.putEvent()
  47. #----------------------------------------------------------------------
  48. def onStart(self):
  49. """启动策略(必须由用户继承实现)"""
  50. self.writeCtaLog(u'双EMA演示策略启动')
  51. self.putEvent()
  52. #----------------------------------------------------------------------
  53. def onStop(self):
  54. """停止策略(必须由用户继承实现)"""
  55. self.writeCtaLog(u'策略停止')
  56. self.putEvent()
  57. #----------------------------------------------------------------------
  58. def onTick(self, tick):
  59. """收到行情TICK推送(必须由用户继承实现)"""
  60. pass
  61. #----------------------------------------------------------------------
  62. def onBar(self, bar):
  63. """收到Bar推送"""
  64. symbol = bar.vtSymbol
  65. lastOrder=self.transactionPrice[symbol]
  66. # 金字塔加仓模块______________________________________
  67. if (self.posDict[symbol+'_LONG']!=0 and self.nPos < 3): # 持有多头仓位并且加仓次数不超过3次
  68. if bar.close/lastOrder-1>= self.Ratio: # 计算盈利比例,达到2%
  69. self.nPos += 1 # 加仓次数减少 1 次
  70. self.buy(symbol,bar.close*1.02,self.fixsize*(0.3-0.05*self.nPos)) # 目标仓位100手,分别加仓25手、20手、15手,10手
  71. elif (self.posDict[symbol + "_SHORT"] != 0 and self.nPos < 3): # 持有空头仓位并且加仓次数不超过3次
  72. if lastOrder/bar.close-1 >= self.Ratio: # 计算盈利比例,达到2%
  73. self.nPos += 1 # 加仓次数减少 1 次
  74. self.short(symbol,bar.close*0.98,self.fixsize*(0.3-0.05*self.nPos)) # 目标仓位100手,分别加仓25手、20手、15手,10手
  75. # 发出状态更新事件
  76. self.putEvent()
  77. def on30MinBar(self, bar):
  78. """30分钟K线推送"""
  79. symbol = bar.vtSymbol
  80. am30 = self.getArrayManager(symbol, "30m")
  81. if not am30.inited:
  82. return
  83. # 计算策略需要的信号-------------------------------------------------
  84. fastMa = ta.EMA(am30.close, self.fastWindow)
  85. slowMa = ta.EMA(am30.close, self.slowWindow)
  86. crossOver = fastMa[-1]>slowMa[-1] and fastMa[-2]<=slowMa[-2] # 金叉上穿
  87. crossBelow = fastMa[-1]<slowMa[-1] and fastMa[-2]>=slowMa[-2] # 死叉下穿
  88. if crossOver:
  89. print('crossOver:',crossOver)
  90. elif crossBelow:
  91. print('crossBelow:',crossBelow)
  92. # 构建进出场逻辑-------------------------------------------------
  93. # 金叉和死叉的条件是互斥
  94. if crossOver:
  95. # 如果金叉时手头没有持仓,则直接做多
  96. if (self.posDict[symbol+'_LONG']==0) and (self.posDict[symbol+'_SHORT']==0):
  97. self.buy(symbol, bar.close*1.02, self.fixsize*0.3)
  98. # 如果有空头持仓,则先平空,再做多
  99. elif self.posDict[symbol+'_SHORT'] >0:
  100. self.cancelAll()
  101. self.cover(symbol,bar.close*1.02, self.posDict[symbol+'_SHORT'])
  102. self.nPos = 0
  103. self.buy(symbol,bar.close*1.02, self.fixsize*0.3)
  104. # 死叉和金叉相反
  105. elif crossBelow :
  106. if (self.posDict[symbol+'_LONG']==0) and (self.posDict[symbol+'_SHORT']==0):
  107. self.short(symbol,bar.close*0.98, self.fixsize*0.3)
  108. elif self.posDict[symbol+'_LONG'] >0:
  109. self.cancelAll()
  110. self.sell(symbol,bar.close*0.98, self.posDict[symbol+'_LONG'])
  111. self.nPos = 0
  112. self.short(symbol,bar.close*0.98, self.fixsize*0.3)
  113. # 发出状态更新事件
  114. self.putEvent()
  115. #----------------------------------------------------------------------
  116. def onOrder(self, order):
  117. """收到委托变化推送(必须由用户继承实现)"""
  118. # 对于无需做细粒度委托控制的策略,可以忽略onOrder
  119. # print(u'出现未知订单,需要策略师外部干预,ID:%s, symbol:%s,direction:%s,offset:%s'% (order.vtOrderID, order.vtSymbol, order.direction, order.offset))
  120. pass
  121. #----------------------------------------------------------------------
  122. def onTrade(self, trade):
  123. """收到成交推送(必须由用户继承实现)"""
  124. symbol = trade.vtSymbol
  125. self.transactionPrice[symbol] = trade.price
  126. pass
  127. #----------------------------------------------------------------------
  128. def onStopOrder(self, so):
  129. """停止单推送"""
  130. pass

金字塔加仓模型绩效测试

  1. from vnpy.trader.app.ctaStrategy.ctaBarManager import BacktestingEngine
  2. import pandas as pd
  3. def runBacktesting(strategyClass, settingDict,
  4. startDate, endDate, size, slippage, rate):
  5. engine = BacktestingEngine()
  6. engine.setBacktestingMode(engine.BAR_MODE)
  7. engine.setDatabase('VnTrader_1Min_Db')
  8. engine.setStartDate(startDate, initHours=200)
  9. engine.setEndDate(endDate)
  10. engine.setSize(size)
  11. engine.setSlippage(slippage)
  12. engine.setRate(rate)
  13. engine.initStrategy(strategyClass, settingDict)
  14. engine.setCapital(100000)
  15. engine.setLog(True, 'E://log//')
  16. engine.runBacktesting()
  17. #显示逐日回测结果
  18. engine.showDailyResult()
  19. #显示逐笔回测结果
  20. engine.showBacktestingResult()
  21. # 计算回测结果
  22. perfromance = engine.calculateDailyResult()
  23. perfromanceDf , result = engine.calculateDailyStatistics(perfromance)
  24. tradeReport = pd.DataFrame([obj.__dict__ for obj in engine.tradeDict.values()])
  25. tradeDf = tradeReport.set_index('dt')
  26. return perfromanceDf, tradeDf
  27. if __name__ == '__main__':
  28. # 同时传入信号与执行的数据
  29. performanceReport, tradeReport = \
  30. runBacktesting(DoubleMaStrategy, {'symbolList': ['BTCUSDT:binance']},
  31. '20181001 12:00', '20181031 16:00', 100, 0, 5/10000)
  32. # tradeReport.to_excel('BBandMa5MinStrategyReport.xlsx')
2018-11-15 17:09:14.809911  计算按日统计结果
2018-11-15 17:09:14.857881  ------------------------------
2018-11-15 17:09:14.857881  首个交易日:  2018-10-01 00:00:00
2018-11-15 17:09:14.857881  最后交易日:  2018-10-31 00:00:00
2018-11-15 17:09:14.857881  总交易日:   31
2018-11-15 17:09:14.857881  盈利交易日   18
2018-11-15 17:09:14.857881  亏损交易日:  13
2018-11-15 17:09:14.857881  起始资金:   100000
2018-11-15 17:09:14.857881  结束资金:   1,051,594.31
2018-11-15 17:09:14.857881  总收益率:   951.59%
2018-11-15 17:09:14.857881  年化收益:   7,367.18%
2018-11-15 17:09:14.857881  总盈亏:    951,594.31
2018-11-15 17:09:14.858881  最大回撤:   -3,153,690.0
2018-11-15 17:09:14.858881  百分比最大回撤: -165.86%
2018-11-15 17:09:14.858881  总手续费:   204,385.69
2018-11-15 17:09:14.858881  总滑点:    0.0
2018-11-15 17:09:14.858881  总成交金额:  408,771,380.0
2018-11-15 17:09:14.858881  总成交笔数:  19
2018-11-15 17:09:14.858881  日均盈亏:   30,696.59
2018-11-15 17:09:14.858881  日均手续费:  6,593.09
2018-11-15 17:09:14.858881  日均滑点:   0.0
2018-11-15 17:09:14.858881  日均成交金额: 13,186,173.55
2018-11-15 17:09:14.858881  日均成交笔数: 0.61
2018-11-15 17:09:14.858881  日均收益率:  0.63%
2018-11-15 17:09:14.858881  收益标准差:  130.5%
2018-11-15 17:09:14.859880  Sharpe Ratio:   0.07

2018-11-15 17:09:15.731470  策略回测绩效图已保存

output_8_3.png-40kB

2018-11-15 17:09:16.499001  计算回测结果
2018-11-15 17:09:16.507002  交割单已生成
2018-11-15 17:09:16.507992  ------------------------------
2018-11-15 17:09:16.507992  第一笔交易:  2018-10-04 22:30:00
2018-11-15 17:09:16.507992  最后一笔交易: 2018-10-31 15:58:00
2018-11-15 17:09:16.507992  总交易次数:  14
2018-11-15 17:09:16.507992  总盈亏:    934,175.86
2018-11-15 17:09:16.507992  最大回撤:   -570,710.25
2018-11-15 17:09:16.507992  平均每笔盈利: 66,726.85
2018-11-15 17:09:16.507992  平均每笔滑点: 0.0
2018-11-15 17:09:16.507992  平均每笔佣金: 15,843.15
2018-11-15 17:09:16.507992  胜率      50.0%
2018-11-15 17:09:16.507992  盈利交易平均值 320,543.11
2018-11-15 17:09:16.507992  亏损交易平均值 -187,089.42
2018-11-15 17:09:16.507992  盈亏比:    1.71
2018-11-15 17:09:17.148822  策略回测统计图已保存

output_8_5.png-34.2kB

2018-11-15 17:09:17.684494  计算按日统计结果

交易信息

  1. tradeReport

2.金字塔加仓法优化

金字塔加仓法缺点是第一次入场仓位较重,而后加仓手数较轻,则可以适当提高第一次开仓的要求,同时降低后面加仓的要求

见 onBar 和0n30MinBar‘优化金字塔加仓模块’

用参数 n 控制加仓次数,参数 Ratio 控制加仓的进场位置,参数fixsize控制最大仓位手数,参数condition和tmp作为临时变量控制加仓逻辑。


1.加入更多的开仓条件,开仓算法如下:

if 金叉 and 多头排列:

开多单,仓位 = fixsize*0.3

elif 死叉 and 空头排列:

开空单,仓位 = fixsize*0.3

2.加仓依然使用金字塔加仓法,但修改加仓条件,算法如下:

if 持多头仓位 and 股价与短均线死叉:

tmp = 1

elif 持空头仓位 and 股价与短均线金叉:

tmp = -1

if tmp == 1 and 股价与短均线金叉:

condition = 1

elif tmp == -1 and 股价与短均线死叉:

condition = -1

if 持多头仓位 and 当前加仓次数 nPos< 5:

if 当前处于盈利状态 and condition == 1:

    nPos = nPos+1

    加仓fixsize*(0.3-0.05*nPos)手数

    tmp,condition = 0,0

elif 持空头仓位 and 当前加仓次数 nPos< 5:

if 当前处于盈利状态 and condition == -1:

    nPos= nPos+1

    加仓fixsize*(0.3-0.05*nPos)手数

    tmp,condition = 0,0

当执行平仓信号,将 nPos 重置为 0

if 持有多头仓位 and 死叉:

nPos = 0

elif 持有空头仓位 and 金叉:

nPos = 0
  1. # 设置参数
  2. nPos = 0
  3. fixsize = 100
  4. transactionPrice = {}
  5. Ratio = 100
  6. tmp = 0
  7. condition = 0
  8. # 设置变量
  9. self.transactionPrice = {s: 0 for s in self.symbolList}
  10. #----------------------------------------------------------------------
  11. def onBar(self, bar):
  12. """收到Bar推送(必须由用户继承实现)"""
  13. symbol = bar.vtSymbol
  14. am = self.getArrayManager(symbol, "1m")
  15. if not am.inited:
  16. return
  17. # 优化金字塔加仓模块________________________________________________
  18. if (self.posDict[symbol+'_LONG']!=0 and self.nPos < 5): # 持有多头仓位并且加仓次数不超过5次
  19. if self.transactionPrice[symbol]>bar.close and self.condition == 1: # 当前是盈利状况,并且满足上述加仓条件
  20. self.nPos += 1 # 加仓次数减少 1 次
  21. self.buy(symbol,bar.close*1.02,self.fixsize*(0.3-self.nPos*0.05)) # 目标加仓100手 分别为加仓 30手、25手、20手,15手,10手
  22. self.tmp = 0 # 置零两个临时的控制变量
  23. self.condition = 0
  24. elif (self.posDict[symbol + "_SHORT"] != 0 and self.nPos < 5): # 持有空头仓位并且加仓次数不超过5次
  25. if bar.close>self.transactionPrice[symbol] and self.condition == -1: # 当前是盈利状况,并且满足上述加仓条件
  26. self.nPos += 1 # 加仓次数减少 1 次
  27. self.short(symbol,bar.close*0.98,self.fixsize*(0.3-self.nPos*0.05)) # 目标加仓100手 分别为加仓 30手、25手、20手,15手,10手
  28. self.tmp = 0 # 置零两个临时的控制变量
  29. self.condition = 0
  30. # 发出状态更新事件
  31. self.putEvent()
  32. def on30MinBar(self, bar):
  33. """30分钟K线推送"""
  34. symbol = bar.vtSymbol
  35. am30 = self.getArrayManager(symbol, "30m")
  36. if not am30.inited:
  37. return
  38. # 计算策略需要的信号-------------------------------------------------
  39. fastMa = ta.EMA(am30.close, self.fastWindow)
  40. slowMa = ta.EMA(am30.close, self.slowWindow)
  41. # 优化金字塔加仓模块________________________________________________
  42. price_crossOver = am30.close[-1]>fastMa[-1] and am30.close[-2]<fastMa[-2] # 估价金叉短均线
  43. price_crossBelow = am30.close[-1]<fastMa[-1] and am30.close[-2]>fastMa[-2] # 估价死叉短均线
  44. if self.posDict[symbol+'_LONG']!=0 and price_crossBelow: # 持有多头仓位,并且估价死叉短均线
  45. self.tmp = 1 # 临时变量,记录死叉现象
  46. elif self.posDict[symbol + "_SHORT"] != 0 and price_crossOver: # 持有多头仓位,并且估价金叉短均线
  47. self.tmp = -1 # 临时变量,记录金叉现象
  48. if self.tmp == 1 and price_crossOver: # 多头持仓中,发生死叉后金叉,即回调后再次上次,满足加仓条件
  49. self.condition = 1
  50. elif self.tmp == -1 and price_crossBelow: # 空头持仓中,发生金叉后死叉,即回调后再次下跌,满足加仓条件
  51. self.condition = -1

金字塔加仓法优化案例

  1. """
  2. 这里的Demo是一个最简单的双均线策略实现
  3. """
  4. from __future__ import division
  5. from vnpy.trader.vtConstant import *
  6. from vnpy.trader.app.ctaStrategy.ctaBarManager import CtaTemplate
  7. import numpy as np
  8. import talib as ta
  9. from datetime import timedelta
  10. ########################################################################
  11. # 策略继承CtaTemplate
  12. class DoubleMaStrategy(CtaTemplate):
  13. """双指数均线策略Demo"""
  14. className = 'DoubleMaStrategy'
  15. author = 'ChannelCMT'
  16. # 策略参数
  17. barPeriod = 200
  18. fastWindow = 60 # 快速均线参数
  19. slowWindow = 120 # 慢速均线参数
  20. # 参数列表,保存了参数的名称
  21. paramList = ['name',
  22. 'className',
  23. 'author',
  24. 'fastWindow',
  25. 'slowWindow']
  26. # 变量列表,保存了变量的名称
  27. varList = ['barPeriod']
  28. nPos = 0
  29. fixsize = 100
  30. transactionPrice = {}
  31. Ratio = 100
  32. tmp = 0
  33. condition = 0
  34. # 同步列表,保存了需要保存到数据库的变量名称
  35. syncList = ['posDict', 'eveningDict']
  36. #----------------------------------------------------------------------
  37. def __init__(self, ctaEngine, setting):
  38. # 首先找到策略的父类(就是类CtaTemplate),然后把DoubleMaStrategy的对象转换为类CtaTemplate的对象
  39. super().__init__(ctaEngine, setting)
  40. #----------------------------------------------------------------------
  41. def onInit(self):
  42. """初始化策略(必须由用户继承实现)"""
  43. self.writeCtaLog(u'双EMA演示策略初始化')
  44. # 生成Bar数组
  45. self.setArrayManagerSize(self.barPeriod)
  46. self.transactionPrice = {s: 0 for s in self.symbolList}
  47. self.mail("chushihuaaaaaaaaaaaaaaaaaaaaaaaaa")
  48. self.putEvent()
  49. #----------------------------------------------------------------------
  50. def onStart(self):
  51. """启动策略(必须由用户继承实现)"""
  52. self.writeCtaLog(u'双EMA演示策略启动')
  53. self.putEvent()
  54. #----------------------------------------------------------------------
  55. def onStop(self):
  56. """停止策略(必须由用户继承实现)"""
  57. self.writeCtaLog(u'策略停止')
  58. self.putEvent()
  59. #----------------------------------------------------------------------
  60. def onTick(self, tick):
  61. """收到行情TICK推送(必须由用户继承实现)"""
  62. pass
  63. #----------------------------------------------------------------------
  64. def onBar(self, bar):
  65. """收到Bar推送"""
  66. symbol = bar.vtSymbol
  67. am = self.getArrayManager(symbol, "1m")
  68. if not am.inited:
  69. return
  70. # 优化金字塔加仓模块________________________________________________
  71. if (self.posDict[symbol+'_LONG']!=0 and self.nPos < 5): # 持有多头仓位并且加仓次数不超过5次
  72. if self.transactionPrice[symbol]>bar.close and self.condition == 1: # 当前是盈利状况,并且满足上述加仓条件
  73. self.nPos += 1 # 加仓次数减少 1 次
  74. self.buy(symbol,bar.close*1.02,self.fixsize*(0.3-self.nPos*0.05)) # 目标加仓100手 分别为加仓 30手、25手、20手,15手,10手
  75. self.tmp = 0 # 置零两个临时的控制变量
  76. self.condition = 0
  77. elif (self.posDict[symbol + "_SHORT"] != 0 and self.nPos < 5): # 持有空头仓位并且加仓次数不超过5次
  78. if bar.close>self.transactionPrice[symbol] and self.condition == -1: # 当前是盈利状况,并且满足上述加仓条件
  79. self.nPos += 1 # 加仓次数减少 1 次
  80. self.short(symbol,bar.close*0.98,self.fixsize*(0.3-self.nPos*0.05)) # 目标加仓100手 分别为加仓 30手、25手、20手,15手,10手
  81. self.tmp = 0 # 置零两个临时的控制变量
  82. self.condition = 0
  83. # 发出状态更新事件
  84. self.putEvent()
  85. def on30MinBar(self, bar):
  86. """30分钟K线推送"""
  87. symbol = bar.vtSymbol
  88. am30 = self.getArrayManager(symbol, "30m")
  89. if not am30.inited:
  90. return
  91. # 计算策略需要的信号-------------------------------------------------
  92. fastMa = ta.EMA(am30.close, self.fastWindow)
  93. slowMa = ta.EMA(am30.close, self.slowWindow)
  94. # 优化金字塔加仓模块________________________________________________
  95. price_crossOver = am30.close[-1]>fastMa[-1] and am30.close[-2]<fastMa[-2] # 估价金叉短均线
  96. price_crossBelow = am30.close[-1]<fastMa[-1] and am30.close[-2]>fastMa[-2] # 估价死叉短均线
  97. if self.posDict[symbol+'_LONG']!=0 and price_crossBelow: # 持有多头仓位,并且估价死叉短均线
  98. self.tmp = 1 # 临时变量,记录死叉现象
  99. elif self.posDict[symbol + "_SHORT"] != 0 and price_crossOver: # 持有多头仓位,并且估价金叉短均线
  100. self.tmp = -1 # 临时变量,记录金叉现象
  101. if self.tmp == 1 and price_crossOver: # 多头持仓中,发生死叉后金叉,即回调后再次上次,满足加仓条件
  102. self.condition = 1
  103. elif self.tmp == -1 and price_crossBelow: # 空头持仓中,发生金叉后死叉,即回调后再次下跌,满足加仓条件
  104. self.condition = -1
  105. crossOver = fastMa[-1]>slowMa[-1] and fastMa[-2]<=slowMa[-2] # 金叉上穿
  106. crossBelow = fastMa[-1]<slowMa[-1] and fastMa[-2]>=slowMa[-2] # 死叉下穿
  107. long = fastMa[-1]>fastMa[-2] and slowMa[-1]>slowMa[-2] # 多头排列
  108. short = fastMa[-1]<fastMa[-2] and slowMa[-1]<slowMa[-2] # 空头排列
  109. # 构建进出场逻辑-------------------------------------------------
  110. # 金叉和死叉的条件是互斥,多头和空头排列条件互斥
  111. if crossOver and long:
  112. # 如果金叉时手头没有持仓,则直接做多
  113. if (self.posDict[symbol+'_LONG']==0) and (self.posDict[symbol+'_SHORT']==0):
  114. self.buy(symbol, bar.close*1.02, self.fixsize*0.3)
  115. # 如果有空头持仓,则先平空,再做多
  116. elif self.posDict[symbol+'_SHORT'] >0:
  117. self.cancelAll()
  118. self.cover(symbol,bar.close*1.02, self.posDict[symbol+'_SHORT'])
  119. self.nPos = 0
  120. self.buy(symbol,bar.close*1.02, self.fixsize*0.3)
  121. # 死叉和金叉相反
  122. elif crossBelow and short:
  123. if (self.posDict[symbol+'_LONG']==0) and (self.posDict[symbol+'_SHORT']==0):
  124. self.short(symbol,bar.close*0.98, self.fixsize*0.3)
  125. elif self.posDict[symbol+'_LONG'] >0:
  126. self.cancelAll()
  127. self.sell(symbol,bar.close*0.98, self.posDict[symbol+'_LONG'])
  128. self.nPos = 0
  129. self.short(symbol,bar.close*0.98, self.fixsize*0.3)
  130. # 发出状态更新事件
  131. self.putEvent()
  132. #----------------------------------------------------------------------
  133. def onOrder(self, order):
  134. """收到委托变化推送(必须由用户继承实现)"""
  135. # 对于无需做细粒度委托控制的策略,可以忽略onOrder
  136. # print(u'出现未知订单,需要策略师外部干预,ID:%s, symbol:%s,direction:%s,offset:%s'% (order.vtOrderID, order.vtSymbol, order.direction, order.offset))
  137. pass
  138. #----------------------------------------------------------------------
  139. def onTrade(self, trade):
  140. """收到成交推送(必须由用户继承实现)"""
  141. symbol = trade.vtSymbol
  142. self.transactionPrice[symbol] = trade.price
  143. pass
  144. #----------------------------------------------------------------------
  145. def onStopOrder(self, so):
  146. """停止单推送"""
  147. pass

金字塔加仓法(优化)绩效测试

  1. from vnpy.trader.app.ctaStrategy.ctaBarManager import BacktestingEngine
  2. import pandas as pd
  3. def runBacktesting(strategyClass, settingDict,
  4. startDate, endDate, size, slippage, rate):
  5. engine = BacktestingEngine()
  6. engine.setBacktestingMode(engine.BAR_MODE)
  7. engine.setDatabase('VnTrader_1Min_Db')
  8. engine.setStartDate(startDate, initHours=200)
  9. engine.setEndDate(endDate)
  10. engine.setSize(size)
  11. engine.setSlippage(slippage)
  12. engine.setRate(rate)
  13. engine.initStrategy(strategyClass, settingDict)
  14. engine.setCapital(100000)
  15. engine.setLog(True, 'E://log//')
  16. engine.runBacktesting()
  17. #显示逐日回测结果
  18. engine.showDailyResult()
  19. #显示逐笔回测结果
  20. engine.showBacktestingResult()
  21. # 计算回测结果
  22. perfromance = engine.calculateDailyResult()
  23. perfromanceDf , result = engine.calculateDailyStatistics(perfromance)
  24. tradeReport = pd.DataFrame([obj.__dict__ for obj in engine.tradeDict.values()])
  25. tradeDf = tradeReport.set_index('dt')
  26. return perfromanceDf, tradeDf
  27. if __name__ == '__main__':
  28. # 同时传入信号与执行的数据
  29. performanceReport, tradeReport = \
  30. runBacktesting(DoubleMaStrategy, {'symbolList': ['BTCUSDT:binance']},
  31. '20181001 12:00', '20181031 16:00', 100, 0, 5/10000)
  32. # tradeReport.to_excel('BBandMa5MinStrategyReport.xlsx')
2018-11-15 17:27:50.932043  计算按日统计结果
2018-11-15 17:27:50.965024  ------------------------------
2018-11-15 17:27:50.965024  首个交易日:  2018-10-01 00:00:00
2018-11-15 17:27:50.965024  最后交易日:  2018-10-31 00:00:00
2018-11-15 17:27:50.965024  总交易日:   31
2018-11-15 17:27:50.965024  盈利交易日   18
2018-11-15 17:27:50.965024  亏损交易日:  13
2018-11-15 17:27:50.965024  起始资金:   100000
2018-11-15 17:27:50.965024  结束资金:   2,709,496.33
2018-11-15 17:27:50.965024  总收益率:   2,609.5%
2018-11-15 17:27:50.965024  年化收益:   20,202.55%
2018-11-15 17:27:50.965024  总盈亏:    2,609,496.33
2018-11-15 17:27:50.965024  最大回撤:   -2,628,075.0
2018-11-15 17:27:50.965024  百分比最大回撤: -123.29%
2018-11-15 17:27:50.965024  总手续费:   316,833.66
2018-11-15 17:27:50.965024  总滑点:    0.0
2018-11-15 17:27:50.966023  总成交金额:  633,667,330.0
2018-11-15 17:27:50.966023  总成交笔数:  30
2018-11-15 17:27:50.966023  日均盈亏:   84,177.3
2018-11-15 17:27:50.966023  日均手续费:  10,220.44
2018-11-15 17:27:50.966023  日均滑点:   0.0
2018-11-15 17:27:50.966023  日均成交金额: 20,440,881.61
2018-11-15 17:27:50.966023  日均成交笔数: 0.97
2018-11-15 17:27:50.966023  日均收益率:  3.73%
2018-11-15 17:27:50.966023  收益标准差:  134.65%
2018-11-15 17:27:50.966023  Sharpe Ratio:   0.43

2018-11-15 17:27:51.800197  策略回测绩效图已保存

output_16_3.png-41.8kB

2018-11-15 17:27:52.577746  计算回测结果
2018-11-15 17:27:52.586740  交割单已生成
2018-11-15 17:27:52.586740  ------------------------------
2018-11-15 17:27:52.586740  第一笔交易:  2018-10-04 22:30:00
2018-11-15 17:27:52.586740  最后一笔交易: 2018-10-31 15:58:00
2018-11-15 17:27:52.586740  总交易次数:  26
2018-11-15 17:27:52.586740  总盈亏:    2,592,077.89
2018-11-15 17:27:52.586740  最大回撤:   -807,531.68
2018-11-15 17:27:52.586740  平均每笔盈利: 99,695.3
2018-11-15 17:27:52.586740  平均每笔滑点: 0.0
2018-11-15 17:27:52.586740  平均每笔佣金: 12,855.85
2018-11-15 17:27:52.586740  胜率      42.31%
2018-11-15 17:27:52.586740  盈利交易平均值 314,165.61
2018-11-15 17:27:52.586740  亏损交易平均值 -57,582.92
2018-11-15 17:27:52.586740  盈亏比:    5.46
2018-11-15 17:27:53.246336  策略回测统计图已保存

output_16_5.png-32.8kB

2018-11-15 17:27:53.797042  计算按日统计结果

绩效报告

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