@Channelchan
2018-12-11T13:19:33.000000Z
字数 16334
阅读 63702
满足信号的个数越多,加仓越多。
见 onBar ‘多信号加仓模块’以及on15Bar‘信号计算与开仓逻辑’
用参数 n 控制加仓次数,参数fixsize为开仓手数以及每次加仓数量
计算3个指标分别为MACD,RSI,movement并计算信号值signal_1,signal_2,signal_3和signal. signal = signal_1+signal_2+signal_3
if not 空头信号 and signal == 1:
buy
elif not 多头信号 and signal == -1:
short
if 持多头仓位 and 当前加仓次数 nPos < 2:
if 出现空头信号:
sell 全部多头头寸
elif 没加过仓 and signal == 2:
加仓fixsize手
elif 加过一次仓 and signal == 3:
加仓fixsize手
elif 持空头仓位 and 当前加仓次数 nPos < 2:
if 出现多头信号:
cover 全部空头头寸
elif 没加过仓 and signal == -2:
加仓fixsize手
elif 加过一次仓 and signal == -3:
加仓fixsize手
if 持有多头仓位 and 死叉:
nPos = 0
elif 持有空头仓位 and 金叉:
nPos = 0
#策略参数barPeriod = 200fastWindow = 60 # 快速均线参数slowWindow = 120 # 慢速均线参数# 变量列表nPos = 0fixsize = 10trailingPercent = 4stopRatio = 0.02 # 止损百分比profitMultiplier = 6 # 止盈与止损比例transactionPrice = {} # 记录成交价格intraTradeHighDict = {}intraTradeLowDict = {}Ratio = 0.02signal_1 = 0signal_2 = 0signal_3 = 0signal = 0# 设置变量self.transactionPrice = {s: 0 for s in self.symbolList}self.intraTradeHighDict = {s: 0 for s in self.symbolList}self.intraTradeLowDict ={s: 0 for s in self.symbolList}#----------------------------------------------------------------------def onBar(self, bar):"""收到Bar推送(必须由用户继承实现)"""symbol = bar.vtSymbolif self.posDict[symbol+"_LONG"] == 0 and self.posDict[symbol+"_SHORT"] == 0:self.intraTradeHighDict[symbol] = 0self.intraTradeLowDict[symbol] = 999999# 洗价器# 多信号加仓模块,多空相反______________________________________elif self.posDict[symbol+"_LONG"]!=0 : # 持有多头仓位并且加仓次数不超过2次(共三个信号,允许加2次仓)self.intraTradeHighDict[symbol] = max(self.intraTradeHighDict[symbol], bar.high)self.intraTradeLowDict[symbol] = bar.lowself.longStop = self.intraTradeHighDict[symbol]*(1-self.trailingPercent/100)# print('最高价:%s'%self.intraTradeHighDict[symbol])# print('止损价格:%s'%self.longStop)# print('开仓价格:%s'%self.transactionPrice)if bar.close<=self.longStop or (bar.close > self.transactionPrice[symbol] * (1 + self.profitMultiplier * self.stopRatio)):self.cancelAll()self.sell(symbol, bar.close*0.98, self.posDict[symbol+"_LONG"])self.signal_1 = 0self.signal_2 = 0self.signal_3 = 0self.signal = 0elif self.signal_1 < 0 or self.signal_2 < 0 or self.signal_3 < 0: # 持有多仓情况下有一个信号转换为空头信号,则清仓出场self.sell(symbol,bar.close*0.98, self.posDict[symbol+"_LONG"]) # 全部头寸出场print('time:%s,on bar sell:%s'%(bar.datetime,bar.close))self.signal_1 = 0self.signal_2 = 0self.signal_3 = 0self.signal = 0elif self.nPos == 0 and self.signal == 2: # 第一次开仓是有一个信号满足,当另一个信号满足多头信号,则加仓self.nPos += 1 # 加仓次数减少 1 次self.buy(symbol,bar.close*1.02,self.fixsize) # 加仓数量固定fixsizeprint('time:%s,onbar_long_add:%s,n:%s'%(bar.datetime,bar.close,self.nPos))elif self.nPos == 1 and self.signal == 3 : # 建立在第一次加仓的基础上,再有信号满足多头信号,继续加仓self.buy(symbol,bar.close*1.02,self.fixsize) # 加仓数量固定fixsizeprint('time:%s,onbar_long_add2:%s,n:%s'%(bar.datetime,bar.close,self.nPos+1))elif self.posDict[symbol + "_SHORT"] != 0:self.intraTradeLowDict[symbol] = min(self.intraTradeLowDict[symbol], bar.low)self.intraTradeHighDict[symbol] = bar.highself.shortStop = self.intraTradeLowDict[symbol]*(1+self.trailingPercent/100)if (bar.close>=self.shortStop) or (bar.close < self.transactionPrice[symbol] * (1 - self.profitMultiplier * self.stopRatio)):self.cancelAll()self.cover(symbol, bar.close*1.02, self.posDict[symbol+"_SHORT"])print('time:%s,shortStop cover:%s'%(bar.datetime,bar.high))self.signal_1 = 0self.signal_2 = 0self.signal_3 = 0self.signal = 0elif self.signal_1 > 0 or self.signal_2 > 0 or self.signal_3 > 0: #self.cover(symbol,bar.close*1.02, self.posDict[symbol+"_SHORT"])self.signal_1 = 0self.signal_2 = 0self.signal_3 = 0self.signal = 0print('time:%s,onbar cover:%s'%(bar.datetime,bar.close))elif self.nPos == 0 and self.signal == -2 : # 计算盈利比例self.nPos += 1 # 加仓次数减少 1 次self.short(symbol,bar.close*0.98,self.fixsize) # 加仓数量固定fixsizeprint('time:%s,onbar_short_add:%s,n:%s'%(bar.datetime,bar.close,self.nPos))elif self.nPos == 1 and self.signal == -3 :self.short(symbol,bar.close*0.98,self.fixsize) # 加仓数量固定fixsizeprint('time:%s,onbar_short_add2:%s,n:%s'%(bar.datetime,bar.close,self.nPos+1))# 发出状态更新事件self.putEvent()def on15MinBar(self, bar):"""30分钟K线推送"""symbol = bar.vtSymbolam15 = self.getArrayManager(symbol, "15m")if not am15.inited:return# 计算策略需要的信号,共三个信号分别是MACD,rsi,动量指标-------------------------------------------------diff,dea,hist = ta.MACD(am15.close)if diff[-1]<0 and dea[-1]<0 and diff[-2]<dea[-2] and diff[-1]>dea[-1]:self.signal_1 = 1elif diff[-1]>0 and dea[-2]>0 and diff[-2]>dea[-2] and diff[-1]<dea[-1]:self.signal_1 = -1else:self.signal_1 = 0rsi = ta.RSI(am15.close)if rsi[-2]<38 and rsi[-1]>38:self.signal_2 = 1elif rsi[-2]>68 and rsi[-1]<68:self.signal_2 = -1else:self.signal_2 = 0movement = (am15.close[-1] - am15.close[-10])/am15.close[-10]if movement < -0.015:self.signal_3 = 1elif movement > 0.015:self.signal_3 = -1else:self.signal_3 = 0### 将三个信号加和self.signal = self.signal_1 + self.signal_2 + self.signal_3# 构建进出场逻辑-------------------------------------------------# 如果3个都不是空头信号,并且有一个信号满足多头信号,则开多仓if self.signal_1 >= 0 and self.signal_2 >= 0 and self.signal_3 >= 0 and self.signal == 1:# 如果金叉时手头没有持仓,则直接做多if (self.posDict[symbol+'_LONG']==0) and (self.posDict[symbol+'_SHORT']==0):self.buy(symbol, bar.close*1.02, self.fixsize*0.3)print('time:%s,nornal_buy:%s'%(bar.datetime,bar.close))# 如果有空头持仓,则先平空,再做多elif self.posDict[symbol+'_SHORT'] >0:#print('short_pos:%s'%self.posDict[symbol+'_SHORT'])self.cover(symbol,bar.close*1.02, self.posDict[symbol+'_SHORT'])print('time:%s,cover cause buy:%s'%(bar.datetime,bar.close))#print('short_pos:%s'%self.posDict[symbol+'_SHORT'])self.nPos = 0self.buy(symbol,bar.close*1.02, self.fixsize*0.3)print('time:%s,buy after cover:%s'%(bar.datetime,bar.close))# 如果3个都不是多头信号,并且有一个信号满足空头信号,则开空仓elif self.signal_1 <= 0 and self.signal_2 <= 0 and self.signal_3 <= 0 and self.signal == -1:if (self.posDict[symbol+'_LONG']==0) and (self.posDict[symbol+'_SHORT']==0):self.short(symbol,bar.close*0.98, self.fixsize*0.3)print('time:%s,normal short:%s'%(bar.datetime,bar.close))elif self.posDict[symbol+'_LONG'] >0:self.sell(symbol,bar.close*0.98, self.posDict[symbol+'_LONG'])print('time:%s,sell cause buy:%s'%(bar.datetime,bar.close))self.nPos = 0self.short(symbol,bar.close*0.98, self.fixsize*0.3)print('time:%s,short after sell:%s'%(bar.datetime,bar.close))if abs(self.signal) >=2:print('signal_1:%s,signal_2:%s,signal_3:%s,signal:%s'%(self.signal_1,self.signal_2,self.signal_3,self.signal))# 发出状态更新事件self.putEvent()
"""这里的Demo是一个最简单的双均线策略实现"""# coding: utf-8from __future__ import divisionfrom vnpy.trader.vtConstant import *from vnpy.trader.app.ctaStrategy import CtaTemplatefrom collections import defaultdictimport numpy as npimport talib as taimport pandas as pdfrom datetime import datetime######################################################################### 策略继承CtaTemplateclass multi_signal_Strategy(CtaTemplate):"""双指数均线策略Demo"""className = 'multi_signal_Strategy'author = 'ChannelCMT'# 策略参数barPeriod = 200fastWindow = 60 # 快速均线参数slowWindow = 120 # 慢速均线参数# 参数列表,保存了参数的名称paramList = ['name','className','author','fastWindow','slowWindow']# 变量列表,保存了变量的名称varList = ['barPeriod']nPos = 0fixsize = 10trailingPercent = 4stopRatio = 0.02 # 止损百分比profitMultiplier = 6 # 止盈与止损比例transactionPrice = {} # 记录成交价格intraTradeHighDict = {}intraTradeLowDict = {}Ratio = 0.02signal_1 = 0signal_2 = 0signal_3 = 0signal = 0# 同步列表,保存了需要保存到数据库的变量名称syncList = ['posDict', 'eveningDict']#----------------------------------------------------------------------def __init__(self, ctaEngine, setting):# 首先找到策略的父类(就是类CtaTemplate),然后把DoubleMaStrategy的对象转换为类CtaTemplate的对象super().__init__(ctaEngine, setting)#----------------------------------------------------------------------def onInit(self):"""初始化策略(必须由用户继承实现)"""self.writeCtaLog(u'双EMA演示策略初始化')# 生成Bar数组self.transactionPrice = {s: 0 for s in self.symbolList}self.intraTradeHighDict = {s: 0 for s in self.symbolList}self.intraTradeLowDict ={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.vtSymbolif self.posDict[symbol+"_LONG"] == 0 and self.posDict[symbol+"_SHORT"] == 0:self.intraTradeHighDict[symbol] = 0self.intraTradeLowDict[symbol] = 999999# 洗价器# 多信号加仓模块,多空相反______________________________________elif self.posDict[symbol+"_LONG"]!=0 : # 持有多头仓位并且加仓次数不超过2次(共三个信号,允许加2次仓)self.intraTradeHighDict[symbol] = max(self.intraTradeHighDict[symbol], bar.high)self.intraTradeLowDict[symbol] = bar.lowself.longStop = self.intraTradeHighDict[symbol]*(1-self.trailingPercent/100)# print('最高价:%s'%self.intraTradeHighDict[symbol])# print('止损价格:%s'%self.longStop)# print('开仓价格:%s'%self.transactionPrice)if bar.close<=self.longStop or (bar.close > self.transactionPrice[symbol] * (1 + self.profitMultiplier * self.stopRatio)):self.cancelAll()self.sell(symbol, bar.close*0.98, self.posDict[symbol+"_LONG"])self.signal_1 = 0self.signal_2 = 0self.signal_3 = 0self.signal = 0elif self.signal_1 < 0 or self.signal_2 < 0 or self.signal_3 < 0: # 持有多仓情况下有一个信号转换为空头信号,则清仓出场self.sell(symbol,bar.close*0.98, self.posDict[symbol+"_LONG"]) # 全部头寸出场print('time:%s,on bar sell:%s'%(bar.datetime,bar.close))self.signal_1 = 0self.signal_2 = 0self.signal_3 = 0self.signal = 0elif self.nPos == 0 and self.signal == 2: # 第一次开仓是有一个信号满足,当另一个信号满足多头信号,则加仓self.nPos += 1 # 加仓次数减少 1 次self.buy(symbol,bar.close*1.02,self.fixsize) # 加仓数量固定fixsizeprint('time:%s,onbar_long_add:%s,n:%s'%(bar.datetime,bar.close,self.nPos))elif self.nPos == 1 and self.signal == 3 : # 建立在第一次加仓的基础上,再有信号满足多头信号,继续加仓self.buy(symbol,bar.close*1.02,self.fixsize) # 加仓数量固定fixsizeprint('time:%s,onbar_long_add2:%s,n:%s'%(bar.datetime,bar.close,self.nPos+1))elif self.posDict[symbol + "_SHORT"] != 0:self.intraTradeLowDict[symbol] = min(self.intraTradeLowDict[symbol], bar.low)self.intraTradeHighDict[symbol] = bar.highself.shortStop = self.intraTradeLowDict[symbol]*(1+self.trailingPercent/100)if (bar.close>=self.shortStop) or (bar.close < self.transactionPrice[symbol] * (1 - self.profitMultiplier * self.stopRatio)):self.cancelAll()self.cover(symbol, bar.close*1.02, self.posDict[symbol+"_SHORT"])print('time:%s,shortStop cover:%s'%(bar.datetime,bar.high))self.signal_1 = 0self.signal_2 = 0self.signal_3 = 0self.signal = 0elif self.signal_1 > 0 or self.signal_2 > 0 or self.signal_3 > 0: #self.cover(symbol,bar.close*1.02, self.posDict[symbol+"_SHORT"])self.signal_1 = 0self.signal_2 = 0self.signal_3 = 0self.signal = 0print('time:%s,onbar cover:%s'%(bar.datetime,bar.close))elif self.nPos == 0 and self.signal == -2 : # 计算盈利比例self.nPos += 1 # 加仓次数减少 1 次self.short(symbol,bar.close*0.98,self.fixsize) # 加仓数量固定fixsizeprint('time:%s,onbar_short_add:%s,n:%s'%(bar.datetime,bar.close,self.nPos))elif self.nPos == 1 and self.signal == -3 :self.short(symbol,bar.close*0.98,self.fixsize) # 加仓数量固定fixsizeprint('time:%s,onbar_short_add2:%s,n:%s'%(bar.datetime,bar.close,self.nPos+1))# 发出状态更新事件self.putEvent()def on15MinBar(self, bar):"""30分钟K线推送"""symbol = bar.vtSymbolam15 = self.getArrayManager(symbol, "15m")if not am15.inited:return# 计算策略需要的信号,共三个信号分别是MACD,rsi,动量指标-------------------------------------------------diff,dea,hist = ta.MACD(am15.close)if diff[-1]<0 and dea[-1]<0 and diff[-2]<dea[-2] and diff[-1]>dea[-1]:self.signal_1 = 1elif diff[-1]>0 and dea[-2]>0 and diff[-2]>dea[-2] and diff[-1]<dea[-1]:self.signal_1 = -1else:self.signal_1 = 0rsi = ta.RSI(am15.close)if rsi[-2]<38 and rsi[-1]>38:self.signal_2 = 1elif rsi[-2]>68 and rsi[-1]<68:self.signal_2 = -1else:self.signal_2 = 0movement = (am15.close[-1] - am15.close[-10])/am15.close[-10]if movement < -0.015:self.signal_3 = 1elif movement > 0.015:self.signal_3 = -1else:self.signal_3 = 0### 将三个信号加和self.signal = self.signal_1 + self.signal_2 + self.signal_3# 构建进出场逻辑-------------------------------------------------# 如果3个都不是空头信号,并且有一个信号满足多头信号,则开多仓if self.signal_1 >= 0 and self.signal_2 >= 0 and self.signal_3 >= 0 and self.signal == 1:# 如果金叉时手头没有持仓,则直接做多if (self.posDict[symbol+'_LONG']==0) and (self.posDict[symbol+'_SHORT']==0):self.buy(symbol, bar.close*1.02, self.fixsize*0.3)print('time:%s,nornal_buy:%s'%(bar.datetime,bar.close))# 如果有空头持仓,则先平空,再做多elif self.posDict[symbol+'_SHORT'] >0:#print('short_pos:%s'%self.posDict[symbol+'_SHORT'])self.cover(symbol,bar.close*1.02, self.posDict[symbol+'_SHORT'])print('time:%s,cover cause buy:%s'%(bar.datetime,bar.close))#print('short_pos:%s'%self.posDict[symbol+'_SHORT'])self.nPos = 0self.buy(symbol,bar.close*1.02, self.fixsize*0.3)print('time:%s,buy after cover:%s'%(bar.datetime,bar.close))# 如果3个都不是多头信号,并且有一个信号满足空头信号,则开空仓elif self.signal_1 <= 0 and self.signal_2 <= 0 and self.signal_3 <= 0 and self.signal == -1:if (self.posDict[symbol+'_LONG']==0) and (self.posDict[symbol+'_SHORT']==0):self.short(symbol,bar.close*0.98, self.fixsize*0.3)print('time:%s,normal short:%s'%(bar.datetime,bar.close))elif self.posDict[symbol+'_LONG'] >0:self.sell(symbol,bar.close*0.98, self.posDict[symbol+'_LONG'])print('time:%s,sell cause buy:%s'%(bar.datetime,bar.close))self.nPos = 0self.short(symbol,bar.close*0.98, self.fixsize*0.3)print('time:%s,short after sell:%s'%(bar.datetime,bar.close))if abs(self.signal) >=2:print('signal_1:%s,signal_2:%s,signal_3:%s,signal:%s'%(self.signal_1,self.signal_2,self.signal_3,self.signal))# 发出状态更新事件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(multi_signal_Strategy, {'symbolList': ['BTCUSDT:binance']},'20181001 12:00', '20181031 16:00', 100, 0, 5/10000)# tradeReport.to_excel('BBandMa5MinStrategyReport.xlsx')
2018-11-15 22:45:13.426139 策略日志已生成
2018-11-15 22:45:13.426139 计算按日统计结果
2018-11-15 22:45:13.469112 ------------------------------
2018-11-15 22:45:13.469112 首个交易日: 2018-10-01 00:00:00
2018-11-15 22:45:13.469112 最后交易日: 2018-10-31 00:00:00
2018-11-15 22:45:13.469112 总交易日: 31
2018-11-15 22:45:13.469112 盈利交易日 12
2018-11-15 22:45:13.469112 亏损交易日: 19
2018-11-15 22:45:13.469112 起始资金: 100000
2018-11-15 22:45:13.469112 结束资金: 2,274,259.17
2018-11-15 22:45:13.469112 总收益率: 2,174.26%
2018-11-15 22:45:13.469112 年化收益: 16,832.97%
2018-11-15 22:45:13.469112 总盈亏: 2,174,259.17
2018-11-15 22:45:13.469112 最大回撤: -1,235,083.51
2018-11-15 22:45:13.469112 百分比最大回撤: -74.78%
2018-11-15 22:45:13.470116 总手续费: 2,006,450.82
2018-11-15 22:45:13.470116 总滑点: 0.0
2018-11-15 22:45:13.470116 总成交金额: 4,012,901,650.0
2018-11-15 22:45:13.470116 总成交笔数: 265
2018-11-15 22:45:13.470116 日均盈亏: 70,137.39
2018-11-15 22:45:13.470116 日均手续费: 64,724.22
2018-11-15 22:45:13.470116 日均滑点: 0.0
2018-11-15 22:45:13.470116 日均成交金额: 129,448,440.32
2018-11-15 22:45:13.471122 日均成交笔数: 8.55
2018-11-15 22:45:13.471122 日均收益率: 4.97%
2018-11-15 22:45:13.471122 收益标准差: 43.69%
2018-11-15 22:45:13.471122 Sharpe Ratio: 1.76
2018-11-15 22:45:14.410675 策略回测绩效图已保存

2018-11-15 22:45:15.192194 计算回测结果
2018-11-15 22:45:15.222174 交割单已生成
2018-11-15 22:45:15.223172 ------------------------------
2018-11-15 22:45:15.223172 第一笔交易: 2018-10-01 16:15:00
2018-11-15 22:45:15.223172 最后一笔交易: 2018-10-31 15:58:00
2018-11-15 22:45:15.223172 总交易次数: 180
2018-11-15 22:45:15.223172 总盈亏: 284,654.44
2018-11-15 22:45:15.223172 最大回撤: -6,423,167.65
2018-11-15 22:45:15.223172 平均每笔盈利: 1,581.41
2018-11-15 22:45:15.223172 平均每笔滑点: 0.0
2018-11-15 22:45:15.223172 平均每笔佣金: 21,644.75
2018-11-15 22:45:15.223172 胜率 45.56%
2018-11-15 22:45:15.224172 盈利交易平均值 203,081.68
2018-11-15 22:45:15.224172 亏损交易平均值 -167,020.85
2018-11-15 22:45:15.224172 盈亏比: 1.22
2018-11-15 22:45:16.140242 策略回测统计图已保存

2018-11-15 22:45:16.877789 计算按日统计结果
tradeReport