@Channelchan
2018-11-28T10:04:57.000000Z
字数 13317
阅读 63535
一、固定损失加仓法基础版
二、固定损失加仓法激进版
三、固定损失加仓法保守版
盈利加仓模型的一个缺点是加仓后把成本拉高,亏损几率增加。
(1)每次加仓都是固定仓位,为开仓的手数。
(2)第一次加仓后如果产生亏损并且总盈利变成0,则出场,停止这次交易,保证第一次加仓不会产生亏损
(3)若成功加仓,成功进行第二次加仓,使用第一次加仓与第二次加仓的总手数计算盈利率,如果等于0,则出场停止交 易,因为是固定手数,所以从第二次加仓开始,策略总能保持第一笔交易的盈利。
见 onBar ‘固定损失加仓模块’
用参数 nPos 控制加仓次数,参数 Ratio 控制加仓的进场位置,参数fixsize控制最大仓位手数
每次加仓手数为fixsize
if 持多头仓位 and 当前加仓次数 nPos < 3:
if 加仓次数大于等于1:
if 上一次加仓亏损1%:
sell 全部多头头寸
if 盈利率达到2%:
nPos = nPos+1
加仓fixsize手
elif 持空头仓位 and 当前加仓次数 nPos < 3:
if 加仓次数大于等于1:
if 上一次加仓亏损1%:
cover 全部空头头寸
if 盈利率达到2%:
nPos = nPos+1
加仓fixsize手
if 持有多头仓位 and 死叉:
nPos = 0
elif 持有空头仓位 and 金叉:
nPos = 0
## 大的价格除以小的价格减1lastOrder=self.transactionPrice[symbol]## 多头亏损大于一个百分比(self.transactionPrice[symbol] - bar.close)/bar.close= lastOrder/bar.close-1## 多头盈利大于一个百分比(bar.close-self.transactionPrice[symbol])/self.transactionPrice[symbol]= bar.close/lastOrder-1## 空头亏损大于一个百分比(bar.close - self.transactionPrice[symbol])/self.transactionPrice[symbol]= bar.close/lastOrder-1## 空头盈利大于一个百分比(self.transactionPrice[symbol] - bar.close)/bar.close= lastOrder/bar.close-1
# 设置参数nPos = 0fixsize = 100transactionPrice = {}Ratio = 0.02# 设置变量self.transactionPrice = {s: 0 for s in self.symbolList}#在onBar中添加:lastOrder=self.transactionPrice[symbol]# 固定损失加仓模块______________________________________if (self.posDict[symbol+'_LONG']!=0 and self.nPos < 3): # 持有多头仓位并且加仓次数不超过3次if self.nPos >= 1: # 上一次的加仓之后亏损达到原来盈利比例的一半if lastOrder/bar.close-1 >= self.Ratio/2:self.sell(symbol,bar.close*0.98, self.posDict[symbol+'_LONG']) # 全部头寸出场if bar.close/lastOrder-1>= self.Ratio: # 计算盈利比例,达到2%self.nPos += 1 # 加仓次数减少 1 次self.buy(symbol,bar.close*1.02,self.fixsize) # 加仓数量固定fixsizeelif (self.posDict[symbol + "_SHORT"] != 0 and self.nPos < 3): # 持有空头仓位并且加仓次数不超过3次if self.nPos >= 1: # 上一次的加仓之后亏损达到原来盈利比例的一半if bar.close/lastOrder-1>= self.Ratio/2:self.cover(symbol,bar.close*1.02, self.posDict[symbol+'_SHORT']) # 全部头寸出场if lastOrder/bar.close-1 >= self.Ratio: # 计算盈利比例,达到2%self.nPos += 1 # 加仓次数减少 1 次self.short(symbol,bar.close*0.98,self.fixsize) # 加仓数量固定fixsize
盈利加仓模型的一个缺点是加仓后把成本拉高,亏损几率增加。
(1)每次加仓都是当前仓位的手数,因此每次加仓后总仓位都变成加仓前的2倍(proportion倍)
(2)每一次加仓后如果产生亏损并且总盈利变成0,则出场,停止这次交易,保证加仓不会产生亏损,但有损失所有盈利的风险
见 onBar ‘固定损失加仓激进版模块’
用参数 nPos 控制加仓次数,参数 Ratio 控制加仓的进场位置,参数fixsize控制最大仓位手数,参数proportion控制每一次加仓的数量,即加仓后是加仓前的倍数,参数add_fixsize为每一次加仓的手数。
if 持多头仓位 and 当前加仓次数 nPos< 3:
if 加仓次数大于等于1:
if 所有盈利回测100%:
sell 全部多头头寸
if 盈利率达到2%:
nPos = nPos+1
add_fixsize(加仓手数) = fixsize*proportion(加仓后总仓位) - self.fixsize(当前仓位)
加仓add_fixsize手
self.fixsize(更新当前仓位) = add_fixsize + self.fixsize
elif 持空头仓位 and 当前加仓次数 nPos< 3:
if 加仓次数大于等于1:
if 所有盈利回测100%:
cover 全部多头头寸
if 盈利率达到2%:
nPos = nPos+1
add_fixsize(加仓手数) = fixsize*proportion(加仓后总仓位) - self.fixsize(当前仓位)
加仓add_fixsize手
self.fixsize(更新当前仓位) = add_fixsize + self.fixsize
if 持有多头仓位 and 死叉:
nPos = 0
elif 持有空头仓位 and 金叉:
nPos = 0
# 设置参数nPos = 0fixsize = 100transactionPrice = {}Ratio = 0.02proportion = 2# 设置变量self.transactionPrice = {s: 0 for s in self.symbolList}#在onBar中添加:lastOrder=self.transactionPrice[symbol]# 固定损失加仓激进版模块______________________________________if (self.posDict[symbol+'_LONG']!=0 and self.nPos < 3): # 持有多头仓位并且加仓次数不超过3次if self.nPos >= 1: # 上一次的加仓亏损达到原来盈利比例的 1/proportion 倍if lastOrder/bar.close-1 >= self.Ratio/self.proportion:self.sell(symbol,bar.close*0.98, self.posDict[symbol+'_LONG'])if bar.close/lastOrder-1>= self.Ratio: # 计算盈利比例,达到2%self.nPos += 1 # 加仓次数减少 1 次add_fixsize = self.fixsize*self.proportion - self.fixsizeself.buy(symbol,bar.close*1.02, add_fixsize) # 加仓数量self.fixsize = add_fixsize + self.fixsizeelif (self.posDict[symbol + "_SHORT"] != 0 and self.nPos < 3): # 持有空头仓位并且加仓次数不超过3次if self.nPos >= 1: # 上一次的加仓亏损达到原来盈利比例的 1/proportion 倍if bar.close/lastOrder-1>= self.Ratio/self.proportion:self.cover(symbol,bar.close*1.02, self.posDict[symbol+'_SHORT'])if lastOrder/bar.close-1 >= self.Ratio: # 计算盈利比例,达到2%self.nPos += 1 # 加仓次数减少 1 次add_fixsize = self.fixsize*self.proportion - self.fixsizeself.short(symbol,bar.close*0.98,self.fixsize) # 目标仓位100手,分别加仓25手、20手、15手,10手self.fixsize = add_fixsize + self.fixsize
固定损失加仓法激进版在加仓的同时有损失盈利的风险。优化版本如下
(1)在激进版的基础上,每一次正方向加仓同时,往反方向加2/1仓位,若发生回撤时,则反方向的仓位能盈利,若继续往正方向,则平掉反方向的头寸
见 onBar ‘固定损失加仓保守版模块’
用参数 nPos 控制加仓次数,参数 Ratio 控制加仓的进场位置,参数fixsize控制最大仓位手数,参数proportion控制每一次加仓的数量,即加仓后是加仓前的倍数,参数add_fixsize为每一次加仓的手数。
if 持多头仓位 and 当前加仓次数 nPos < 3:
if 加仓次数大于等于1:
if 所有盈利回测100%:
sell 全部多头头寸
cover 全部空头头寸
if 盈利率达到2%:
nPos = nPos+1
add_fixsize(加仓手数) = fixsize*proportion(加仓后总仓位) - self.fixsize(当前仓位)
多头加仓 add_fixsize 手
空头开仓 add_fixsize/2 手
self.fixsize(更新当前仓位) = add_fixsize + self.fixsize
elif 持空头仓位 and 当前加仓次数 < 3:
if 加仓次数大于等于1:
if 所有盈利回测100%:
cover 全部空头头寸
sell 全部多头头寸
if 盈利率达到2%:
nPos = nPos+1
add_fixsize(加仓手数) = fixsize*proportion(加仓后总仓位) - self.fixsize(当前仓位)
空头加仓 add_fixsize 手
多头开仓 add_fixsize/2 手
self.fixsize(更新当前仓位) = add_fixsize + self.fixsize
if 持有多头仓位 and 死叉:
nPos = 0
elif 持有空头仓位 and 金叉:
nPos = 0
# 设置参数nPos = 0fixsize = 100transactionPrice = {}Ratio = 0.02proportion = 2# 设置变量self.transactionPrice = {s: 0 for s in self.symbolList}#在onBar中添加:lastOrder=self.transactionPrice[symbol]# 固定损失加仓保守版模块______________________________________if (self.posDict[symbol+'_LONG']!=0 and self.nPos < 3): # 持有多头仓位并且加仓次数不超过3次if self.nPos >= 1: # 上一次的加仓亏损达到原来盈利比例的 1/proportion 倍if lastOrder/bar.close-1 >= self.Ratio/self.proportion:self.sell(symbol,bar.close*0.98, self.posDict[symbol+'_LONG'])self.cover(symbol,bar.close*1.02, self.posDict[symbol+'_SHORT'])elif bar.close/firstOrder-1>= self.Ratio/self.proportion:self.cover(symbol,bar.close*1.02, self.posDict[symbol+'_SHORT'])if bar.close/lastOrder-1>= self.Ratio: # 计算盈利比例,达到2%self.nPos += 1 # 加仓次数减少 1 次add_fixsize = self.fixsize*self.proportion - self.fixsizeself.buy(symbol,bar.close*1.02, add_fixsize) # 加仓数量为self.short(symbol,bar.close*0.98, add_fixsize/2)self.fixsize = add_fixsize + self.fixsizeelif (self.posDict[symbol + "_SHORT"] != 0 and self.nPos < 3): # 持有空头仓位并且加仓次数不超过3次if self.nPos >= 1: # 上一次的加仓亏损达到原来盈利比例的 1/proportion 倍if bar.close/lastOrder-1>= self.Ratio/self.proportion:self.cover(symbol,bar.close*1.02, self.posDict[symbol+'_SHORT'])self.sell(symbol,bar.close*0.98, self.posDict[symbol+'_LONG'])elif lastOrder/bar.close-1 >= self.Ratio/self.proportion:self.sell(symbol,bar.close*0.98, self.posDict[symbol+'_LONG'])if lastOrder/bar.close-1 >= self.Ratio: # 计算盈利比例,达到2%self.nPos += 1 # 加仓次数减少 1 次add_fixsize = self.fixsize*self.proportion - self.fixsizeself.short(symbol,bar.close*0.98,self.fixsize) # 目标仓位100手,分别加仓25手、20手、15手,10手self.buy(symbol,bar.close*1.02, add_fixsize)self.fixsize = add_fixsize + self.fixsize
"""固定损失加仓,这里的Demo是一个最简单的双均线策略实现"""from __future__ import divisionfrom vnpy.trader.vtConstant import *from vnpy.trader.app.ctaStrategy.ctaBarManager import CtaTemplateimport numpy as npimport talib as tafrom datetime import timedelta######################################################################### 策略继承CtaTemplateclass DoubleMaStrategy(CtaTemplate):"""双指数均线策略Demo"""className = 'DoubleMaStrategy'author = 'ChannelCMT'# 策略参数barPeriod = 200fastWindow = 60 # 快速均线参数slowWindow = 120 # 慢速均线参数# 参数列表,保存了参数的名称paramList = ['name','className','author','fastWindow','slowWindow']# 变量列表,保存了变量的名称varList = ['barPeriod']nPos = 0fixsize = 100transactionPrice = {}Ratio = 0.02# 同步列表,保存了需要保存到数据库的变量名称syncList = ['posDict', 'eveningDict']#----------------------------------------------------------------------def __init__(self, ctaEngine, setting):# 首先找到策略的父类(就是类CtaTemplate),然后把DoubleMaStrategy的对象转换为类CtaTemplate的对象super().__init__(ctaEngine, setting)#----------------------------------------------------------------------def onInit(self):"""初始化策略(必须由用户继承实现)"""self.writeCtaLog(u'双EMA演示策略初始化')# 生成Bar数组self.setArrayManagerSize(self.barPeriod)self.transactionPrice = {s: 0 for s in self.symbolList}self.mail("chushihuaaaaaaaaaaaaaaaaaaaaaaaaa")self.putEvent()#----------------------------------------------------------------------def onStart(self):"""启动策略(必须由用户继承实现)"""self.writeCtaLog(u'双EMA演示策略启动')self.putEvent()#----------------------------------------------------------------------def onStop(self):"""停止策略(必须由用户继承实现)"""self.writeCtaLog(u'策略停止')self.putEvent()#----------------------------------------------------------------------def onTick(self, tick):"""收到行情TICK推送(必须由用户继承实现)"""pass#----------------------------------------------------------------------def onBar(self, bar):"""收到Bar推送(必须由用户继承实现)"""symbol = bar.vtSymbollastOrder=self.transactionPrice[symbol]# 固定损失加仓模块______________________________________if (self.posDict[symbol+'_LONG']!=0 and self.nPos < 3): # 持有多头仓位并且加仓次数不超过3次if self.nPos >= 1: # 上一次的加仓之后亏损达到原来盈利比例的一半if lastOrder/bar.close-1 >= self.Ratio/2:self.sell(symbol,bar.close*0.98, self.posDict[symbol+'_LONG']) # 全部头寸出场if bar.close/lastOrder-1>= self.Ratio: # 计算盈利比例,达到2%self.nPos += 1 # 加仓次数减少 1 次self.buy(symbol,bar.close*1.02,self.fixsize) # 加仓数量固定fixsizeelif (self.posDict[symbol + "_SHORT"] != 0 and self.nPos < 3): # 持有空头仓位并且加仓次数不超过3次if self.nPos >= 1: # 上一次的加仓之后亏损达到原来盈利比例的一半if bar.close/lastOrder-1>= self.Ratio/2:self.cover(symbol,bar.close*1.02, self.posDict[symbol+'_SHORT']) # 全部头寸出场if lastOrder/bar.close-1 >= self.Ratio: # 计算盈利比例,达到2%self.nPos += 1 # 加仓次数减少 1 次self.short(symbol,bar.close*0.98,self.fixsize) # 加仓数量固定fixsize# 发出状态更新事件self.putEvent()def on30MinBar(self, bar):"""30分钟K线推送"""symbol = bar.vtSymbolam30 = self.getArrayManager(symbol, "30m")if not am30.inited:return# 计算策略需要的信号-------------------------------------------------fastMa = ta.EMA(am30.close, self.fastWindow)slowMa = ta.EMA(am30.close, self.slowWindow)crossOver = fastMa[-1]>slowMa[-1] and fastMa[-2]<=slowMa[-2] # 金叉上穿crossBelow = fastMa[-1]<slowMa[-1] and fastMa[-2]>=slowMa[-2] # 死叉下穿if crossOver:print('crossOver:',crossOver)elif crossBelow:print('crossBelow:',crossBelow)# 构建进出场逻辑-------------------------------------------------# 金叉和死叉的条件是互斥if crossOver:# 如果金叉时手头没有持仓,则直接做多if (self.posDict[symbol+'_LONG']==0) and (self.posDict[symbol+'_SHORT']==0):self.buy(symbol, bar.close*1.02, self.fixsize*0.3)# 如果有空头持仓,则先平空,再做多elif self.posDict[symbol+'_SHORT'] >0:self.cancelAll()self.cover(symbol,bar.close*1.02, self.posDict[symbol+'_SHORT'])self.nPos = 0self.buy(symbol,bar.close*1.02, self.fixsize*0.3)# 死叉和金叉相反elif crossBelow :if (self.posDict[symbol+'_LONG']==0) and (self.posDict[symbol+'_SHORT']==0):self.short(symbol,bar.close*0.98, self.fixsize*0.3)elif self.posDict[symbol+'_LONG'] >0:self.cancelAll()self.sell(symbol,bar.close*0.98, self.posDict[symbol+'_LONG'])self.nPos = 0self.short(symbol,bar.close*0.98, self.fixsize*0.3)# 发出状态更新事件self.putEvent()#----------------------------------------------------------------------def onOrder(self, order):"""收到委托变化推送(必须由用户继承实现)"""# 对于无需做细粒度委托控制的策略,可以忽略onOrder# print(u'出现未知订单,需要策略师外部干预,ID:%s, symbol:%s,direction:%s,offset:%s'% (order.vtOrderID, order.vtSymbol, order.direction, order.offset))pass#----------------------------------------------------------------------def onTrade(self, trade):"""收到成交推送(必须由用户继承实现)"""symbol = trade.vtSymbolself.transactionPrice[symbol] = trade.pricepass#----------------------------------------------------------------------def onStopOrder(self, so):"""停止单推送"""pass
from vnpy.trader.app.ctaStrategy.ctaBarManager import BacktestingEngineimport pandas as pddef runBacktesting(strategyClass, settingDict,startDate, endDate, size, slippage, rate):engine = BacktestingEngine()engine.setBacktestingMode(engine.BAR_MODE)engine.setDatabase('VnTrader_1Min_Db')engine.setStartDate(startDate, initHours=200)engine.setEndDate(endDate)engine.setSize(size)engine.setSlippage(slippage)engine.setRate(rate)engine.initStrategy(strategyClass, settingDict)engine.setCapital(100000)engine.setLog(True, 'E://log//')engine.runBacktesting()#显示逐日回测结果engine.showDailyResult()#显示逐笔回测结果engine.showBacktestingResult()# 计算回测结果perfromance = engine.calculateDailyResult()perfromanceDf , result = engine.calculateDailyStatistics(perfromance)tradeReport = pd.DataFrame([obj.__dict__ for obj in engine.tradeDict.values()])tradeDf = tradeReport.set_index('dt')return perfromanceDf, tradeDfif __name__ == '__main__':# 同时传入信号与执行的数据performanceReport, tradeReport = \runBacktesting(DoubleMaStrategy, {'symbolList': ['BTCUSDT:binance']},'20181001 12:00', '20181031 16:00', 100, 0, 5/10000)# tradeReport.to_excel('BBandMa5MinStrategyReport.xlsx')
2018-11-15 17:14:00.236010 计算按日统计结果
2018-11-15 17:14:00.273987 ------------------------------
2018-11-15 17:14:00.273987 首个交易日: 2018-10-01 00:00:00
2018-11-15 17:14:00.273987 最后交易日: 2018-10-31 00:00:00
2018-11-15 17:14:00.273987 总交易日: 31
2018-11-15 17:14:00.273987 盈利交易日 12
2018-11-15 17:14:00.273987 亏损交易日: 8
2018-11-15 17:14:00.273987 起始资金: 100000
2018-11-15 17:14:00.273987 结束资金: 1,455,216.18
2018-11-15 17:14:00.273987 总收益率: 1,355.22%
2018-11-15 17:14:00.273987 年化收益: 10,492.0%
2018-11-15 17:14:00.273987 总盈亏: 1,355,216.18
2018-11-15 17:14:00.274987 最大回撤: -496,430.72
2018-11-15 17:14:00.274987 百分比最大回撤: -201.73%
2018-11-15 17:14:00.274987 总手续费: 306,913.82
2018-11-15 17:14:00.274987 总滑点: 0.0
2018-11-15 17:14:00.274987 总成交金额: 613,827,650.0
2018-11-15 17:14:00.274987 总成交笔数: 18
2018-11-15 17:14:00.274987 日均盈亏: 43,716.65
2018-11-15 17:14:00.274987 日均手续费: 9,900.45
2018-11-15 17:14:00.274987 日均滑点: 0.0
2018-11-15 17:14:00.274987 日均成交金额: 19,800,891.94
2018-11-15 17:14:00.274987 日均成交笔数: 0.58
2018-11-15 17:14:00.274987 日均收益率: 7.79%
2018-11-15 17:14:00.274987 收益标准差: 88.92%
2018-11-15 17:14:00.275986 Sharpe Ratio: 1.36
2018-11-15 17:14:01.109477 策略回测绩效图已保存

2018-11-15 17:14:01.883788 计算回测结果
2018-11-15 17:14:01.890782 交割单已生成
2018-11-15 17:14:01.891780 ------------------------------
2018-11-15 17:14:01.891780 第一笔交易: 2018-10-04 22:30:00
2018-11-15 17:14:01.891780 最后一笔交易: 2018-10-31 15:58:00
2018-11-15 17:14:01.891780 总交易次数: 11
2018-11-15 17:14:01.891780 总盈亏: 1,314,045.31
2018-11-15 17:14:01.891780 最大回撤: -1,065,331.1
2018-11-15 17:14:01.891780 平均每笔盈利: 119,458.66
2018-11-15 17:14:01.891780 平均每笔滑点: 0.0
2018-11-15 17:14:01.891780 平均每笔佣金: 31,644.06
2018-11-15 17:14:01.891780 胜率 36.36%
2018-11-15 17:14:01.891780 盈利交易平均值 731,392.53
2018-11-15 17:14:01.891780 亏损交易平均值 -230,217.83
2018-11-15 17:14:01.891780 盈亏比: 3.18
2018-11-15 17:14:02.622515 策略回测统计图已保存

2018-11-15 17:14:03.262126 计算按日统计结果
tradeReport