@Channelchan
2018-11-28T17:56:43.000000Z
字数 13691
阅读 61310
from vnpy.trader.app.ctaStrategy import BacktestingEngine
import pandas as pd
def runBacktesting(strategyClass, settingDict,
startDate, endDate, slippage, rate):
engine = BacktestingEngine()
engine.setBacktestingMode(engine.BAR_MODE) # 设置引擎的回测模式为K线
engine.setDatabase('VnTrader_1Min_Db') # 设置使用的历史数据库
engine.setStartDate(startDate, initHours=200) # 设置回测用的数据起始日期
engine.setEndDate(endDate) # 设置回测用的数据结束日期
engine.setSlippage(slippage) # 设置滑点
engine.setRate(rate) # 设置手续费万0.3
engine.initStrategy(strategyClass, settingDict)
engine.setCapital(100000) # 设置回测本金
engine.runBacktesting()
#显示逐日回测结果
engine.showDailyResult()
#显示逐笔回测结果
engine.showBacktestingResult()
# 计算回测结果
df = engine.calculateDailyResult()
return df
from vnpy.trader.vtConstant import *
from vnpy.trader.app.ctaStrategy import CtaTemplate
import talib as ta
########################################################################
# 策略继承CtaTemplate
class MultiFrameMaStrategy(CtaTemplate):
className = 'MultiFrameMaStrategy'
author = 'ChannelCMT'
# 策略参数
fastPeriod = 20; slowPeriod = 40
signalMaPeriod = 20
stopRatio = 0.04
lot = 1
# 策略变量
maTrend = {} # 记录趋势状态,多头1,空头-1
transactionPrice = {} # 记录成交价格
# 参数列表,保存了参数的名称
paramList = [
'fastPeriod',
'slowPeriod',
'signalMaPeriod',
'stopRatio'
]
# 变量列表,保存了变量的名称
varList = [
'maTrend',
'transactionPrice'
]
# 同步列表,保存了需要保存到数据库的变量名称
syncList = ['posDict', 'eveningDict']
#----------------------------------------------------------------------
def __init__(self, ctaEngine, setting):
super().__init__(ctaEngine, setting)
#----------------------------------------------------------------------
def onInit(self):
"""初始化策略"""
self.writeCtaLog(u'策略初始化')
self.transactionPrice = {s:0 for s in self.symbolList} # 生成成交价格的字典
self.maTrend = {s:0 for s in self.symbolList}
self.putEvent()
#----------------------------------------------------------------------
def onStart(self):
"""启动策略"""
self.writeCtaLog(u'策略启动')
self.putEvent()
#----------------------------------------------------------------------
def onStop(self):
"""停止策略"""
self.writeCtaLog(u'策略停止')
self.putEvent()
#----------------------------------------------------------------------
def onTick(self, tick):
"""收到行情TICK推送"""
pass
#----------------------------------------------------------------------
def onBar(self, bar):
"""收到Bar推送"""
self.onBarStopLoss(bar)
def onBarStopLoss(self, bar):
symbol = bar.vtSymbol
# 计算止损止盈价位
longStop = self.transactionPrice[symbol]*(1-self.stopRatio)
longProfit = self.transactionPrice[symbol]*(1+3*self.stopRatio)
shortStop = self.transactionPrice[symbol]*(1+self.stopRatio)
shortProfit = self.transactionPrice[symbol]*(1-3*self.stopRatio)
# 洗价器
if (self.posDict[symbol+'_LONG'] > 0):
if (bar.close < longStop):
print('LONG stopLoss')
self.cancelAll()
self.sell(symbol,bar.close*0.99, self.posDict[symbol+'_LONG'])
elif (bar.close > longProfit):
print('LONG takeProfit')
self.cancelAll()
self.sell(symbol,bar.close*0.99, self.posDict[symbol+'_LONG'])
elif (self.posDict[symbol+'_SHORT'] > 0):
if (bar.close > shortStop):
print('SHORT stopLoss')
self.cancelAll()
self.cover(symbol,bar.close*1.01, self.posDict[symbol+'_SHORT'])
elif (bar.close < shortProfit):
print('SHORT takeProfit')
self.cancelAll()
self.cover(symbol,bar.close*1.01, self.posDict[symbol+'_SHORT'])
#----------------------------------------------------------------------
def on60MinBar(self, bar):
"""收到60MinBar推送"""
symbol = bar.vtSymbol
am60 = self.getArrayManager(symbol, "60m")
if not am60.inited:
return
# 计算均线并判断趋势
fastMa = ta.MA(am60.close, self.fastPeriod)
slowMa = ta.MA(am60.close, self.slowPeriod)
if fastMa[-1] > slowMa[-1]:
self.maTrend[symbol] = 1
else:
self.maTrend[symbol] = -1
#----------------------------------------------------------------------
def on15MinBar(self, bar):
"""收到15MinBar推送"""
symbol = bar.vtSymbol
am15 = self.getArrayManager(symbol, "15m")
if not am15.inited:
return
signalMa = ta.EMA(am15.close, self.signalMaPeriod)
maUp = signalMa[-1]>signalMa[-3] # 均线上涨
maDn = signalMa[-1]<signalMa[-3] # 均线下跌
# 均线上涨, 趋势为多头, 多头没有持仓
if maUp and (self.maTrend[symbol]==1) and (self.posDict[symbol+'_LONG']==0):
if (self.posDict[symbol+'_SHORT']==0):
self.buy(symbol, bar.close*1.01, self.lot) # 成交价*1.01发送高价位的限价单,以最优市价买入进场
elif (self.posDict[symbol+'_SHORT'] > 0):
self.cancelAll() # 撤销挂单
self.cover(symbol, bar.close*1.01, self.posDict[symbol+'_SHORT'])
self.buy(symbol, bar.close*1.01, self.lot)
# 均线下跌, 趋势为空头, 空头没有持仓
if maDn and (self.maTrend[symbol]==-1) and (self.posDict[symbol+'_SHORT']==0):
if (self.posDict[symbol+'_LONG']==0):
self.short(symbol, bar.close*0.99, self.lot) # 成交价*0.99发送低价位的限价单,以最优市价卖出进场
elif (self.posDict[symbol+'_LONG'] > 0):
self.cancelAll() # 撤销挂单
self.sell(symbol, bar.close*0.99, self.posDict[symbol+'_LONG'])
self.short(symbol, bar.close*0.99, self.lot)
self.putEvent()
#----------------------------------------------------------------------
def onOrder(self, order):
"""收到委托变化推送(必须由用户继承实现)"""
# 对于无需做细粒度委托控制的策略,可以忽略onOrder
pass
#----------------------------------------------------------------------
def onTrade(self, trade):
"""收到成交推送(必须由用户继承实现)"""
symbol = trade.vtSymbol
if trade.offset == OFFSET_OPEN: # 判断成交订单类型
self.transactionPrice[symbol] = trade.price # 记录成交价格
print(trade.tradeTime, self.posDict)
#----------------------------------------------------------------------
def onStopOrder(self, so):
"""停止单推送"""
pass
MultiFrameMaDf = runBacktesting(MultiFrameMaStrategy, {'symbolList':['BTCUSDT:binance']} , '20180901 12:00', \
'20181126 12:00', 0.002, 5/10000)
2018-11-27 17:37:04.057980 计算按日统计结果
2018-11-27 17:37:04.080956 ------------------------------
2018-11-27 17:37:04.081955 首个交易日: 2018-09-01 00:00:00
2018-11-27 17:37:04.081955 最后交易日: 2018-11-26 00:00:00
2018-11-27 17:37:04.081955 总交易日: 87
2018-11-27 17:37:04.081955 盈利交易日 45
2018-11-27 17:37:04.081955 亏损交易日: 41
2018-11-27 17:37:04.081955 起始资金: 100000
2018-11-27 17:37:04.081955 结束资金: 101,987.91
2018-11-27 17:37:04.082954 总收益率: 1.99%
2018-11-27 17:37:04.082954 年化收益: 5.48%
2018-11-27 17:37:04.082954 总盈亏: 1,987.91
2018-11-27 17:37:04.082954 最大回撤: -408.37
2018-11-27 17:37:04.082954 百分比最大回撤: -0.41%
2018-11-27 17:37:04.082954 总手续费: 427.34
2018-11-27 17:37:04.082954 总滑点: 0.28
2018-11-27 17:37:04.082954 总成交金额: 854,686.82
2018-11-27 17:37:04.082954 总成交笔数: 139
2018-11-27 17:37:04.082954 日均盈亏: 22.85
2018-11-27 17:37:04.082954 日均手续费: 4.91
2018-11-27 17:37:04.082954 日均滑点: 0.0
2018-11-27 17:37:04.082954 日均成交金额: 9,823.99
2018-11-27 17:37:04.082954 日均成交笔数: 1.6
2018-11-27 17:37:04.082954 日均收益率: 0.02%
2018-11-27 17:37:04.082954 收益标准差: 0.14%
2018-11-27 17:37:04.082954 Sharpe Ratio: 2.3
2018-11-27 17:37:05.468537 计算回测结果
2018-11-27 17:37:05.474531 ------------------------------
2018-11-27 17:37:05.475529 第一笔交易: 2018-09-04 06:45:00
2018-11-27 17:37:05.475529 最后一笔交易: 2018-11-26 11:58:00
2018-11-27 17:37:05.475529 总交易次数: 70
2018-11-27 17:37:05.475529 总盈亏: 1,985.85
2018-11-27 17:37:05.475529 最大回撤: -964.09
2018-11-27 17:37:05.475529 平均每笔盈利: 28.37
2018-11-27 17:37:05.475529 平均每笔滑点: 0.0
2018-11-27 17:37:05.475529 平均每笔佣金: 6.13
2018-11-27 17:37:05.475529 胜率 25.71%
2018-11-27 17:37:05.475529 盈利交易平均值 317.25
2018-11-27 17:37:05.475529 亏损交易平均值 -71.63
2018-11-27 17:37:05.475529 盈亏比: 4.43
2018-11-27 17:37:06.443538 计算按日统计结果
Buy: RSI<30
Sell: RSI>70
from __future__ import division
from vnpy.trader.vtConstant import *
from vnpy.trader.app.ctaStrategy import CtaTemplate
import talib as ta
########################################################################
# 策略继承CtaTemplate
class RsiTrendStrategy(CtaTemplate):
className = 'RsiTrendStrategy'
author = 'ChannelCMT'
# 策略参数
fastPeriod = 30; slowPeriod = 60
signalMaPeriod = 20
stopRatio = 0.03
lot = 1
# 策略变量
maTrend = {} # 记录趋势状态,多头1,空头-1
transactionPrice = {} # 记录成交价格
# 参数列表,保存了参数的名称
paramList = [
'fastPeriod', 'slowPeriod',
'signalMaPeriod',
'stopRatio'
]
# 变量列表,保存了变量的名称
varList = [
'maTrend',
'transactionPrice'
]
# 同步列表,保存了需要保存到数据库的变量名称
syncList = ['posDict', 'eveningDict']
#----------------------------------------------------------------------
def __init__(self, ctaEngine, setting):
super().__init__(ctaEngine, setting)
#----------------------------------------------------------------------
def onInit(self):
"""初始化策略"""
self.writeCtaLog(u'策略初始化')
self.transactionPrice = {s:0 for s in self.symbolList} # 生成成交价格的字典
self.maTrend = {s:0 for s in self.symbolList}
self.putEvent()
#----------------------------------------------------------------------
def onStart(self):
"""启动策略"""
self.writeCtaLog(u'策略启动')
self.putEvent()
#----------------------------------------------------------------------
def onStop(self):
"""停止策略"""
self.writeCtaLog(u'策略停止')
self.putEvent()
#----------------------------------------------------------------------
def onTick(self, tick):
"""收到行情TICK推送"""
pass
#----------------------------------------------------------------------
def onBar(self, bar):
"""收到Bar推送"""
self.onBarStopLoss(bar)
def onBarStopLoss(self, bar):
symbol = bar.vtSymbol
# 计算止损止盈价位
longStop = self.transactionPrice[symbol]*(1-self.stopRatio)
longProfit = self.transactionPrice[symbol]*(1+3*self.stopRatio)
shortStop = self.transactionPrice[symbol]*(1+self.stopRatio)
shortProfit = self.transactionPrice[symbol]*(1-3*self.stopRatio)
# 洗价器
if (self.posDict[symbol+'_LONG'] > 0):
if (bar.close < longStop):
self.cancelAll()
self.sell(symbol,bar.close*0.99, self.posDict[symbol+'_LONG'])
elif (bar.close > longProfit):
self.cancelAll()
self.sell(symbol,bar.close*0.99, self.posDict[symbol+'_LONG'])
elif (self.posDict[symbol+'_SHORT'] > 0):
if (bar.close > shortStop):
self.cancelAll()
self.cover(symbol,bar.close*1.01, self.posDict[symbol+'_SHORT'])
elif (bar.close < shortProfit):
self.cancelAll()
self.cover(symbol,bar.close*1.01, self.posDict[symbol+'_SHORT'])
#----------------------------------------------------------------------
def on60MinBar(self, bar):
"""收到60MinBar推送"""
symbol = bar.vtSymbol
am60 = self.getArrayManager(symbol, "60m")
if not am60.inited:
return
# 计算均线并判断趋势
fastMa = ta.MA(am60.close, self.fastPeriod)
slowMa = ta.MA(am60.close, self.slowPeriod)
if fastMa[-1] > slowMa[-1]:
self.maTrend[symbol] = 1
else:
self.maTrend[symbol] = -1
#----------------------------------------------------------------------
def on15MinBar(self, bar):
"""收到15MinBar推送"""
symbol = bar.vtSymbol
am15 = self.getArrayManager(symbol, "15m")
if not am15.inited:
return
rsi = ta.RSI(am15.close, self.signalMaPeriod)
rsiOverbought = (rsi[-1]>70) and (rsi[-2]<=70) # 超买
rsiOversold = (rsi[-1]<30) and (rsi[-2]>=30) # 超卖
# 均线上涨, 趋势为多头, 多头没有持仓
if rsiOversold and (self.maTrend[symbol]==1) and (self.posDict[symbol+'_LONG']==0):
if (self.posDict[symbol+'_SHORT']==0):
self.buy(symbol, bar.close*1.01, self.lot) # 成交价*1.01发送高价位的限价单,以最优市价买入进场
elif (self.posDict[symbol+'_SHORT'] > 0):
self.cancelAll() # 撤销挂单
self.cover(symbol, bar.close*1.01, self.posDict[symbol+'_SHORT'])
self.buy(symbol, bar.close*1.01, self.lot)
# 均线下跌, 趋势为空头, 空头没有持仓
if rsiOverbought and (self.maTrend[symbol]==-1) and (self.posDict[symbol+'_SHORT']==0):
if (self.posDict[symbol+'_LONG']==0):
# self.cancelAll() # 撤销挂单
self.short(symbol, bar.close*0.99, self.lot) # 成交价*0.99发送低价位的限价单,以最优市价卖出进场
elif (self.posDict[symbol+'_LONG'] > 0):
self.cancelAll() # 撤销挂单
self.sell(symbol, bar.close*0.99, self.posDict[symbol+'_LONG'])
self.short(symbol, bar.close*0.99, self.lot)
self.putEvent()
#----------------------------------------------------------------------
def onOrder(self, order):
"""收到委托变化推送(必须由用户继承实现)"""
# 对于无需做细粒度委托控制的策略,可以忽略onOrder
pass
#----------------------------------------------------------------------
def onTrade(self, trade):
"""收到成交推送(必须由用户继承实现)"""
symbol = trade.vtSymbol
if trade.offset == OFFSET_OPEN: # 判断成交订单类型
self.transactionPrice[symbol] = trade.price # 记录成交价格
# print(trade.tradeTime, self.posDict)
#----------------------------------------------------------------------
def onStopOrder(self, so):
"""停止单推送"""
pass
rsiTrendDf = runBacktesting(RsiTrendStrategy, {'symbolList':['BTCUSDT:binance']} , '20180901 12:00', \
'20181126 12:00', 0.002, 5/10000)
2018-11-27 17:37:24.700101 计算按日统计结果
2018-11-27 17:37:24.717082 ------------------------------
2018-11-27 17:37:24.717082 首个交易日: 2018-09-01 00:00:00
2018-11-27 17:37:24.717082 最后交易日: 2018-11-26 00:00:00
2018-11-27 17:37:24.717082 总交易日: 87
2018-11-27 17:37:24.717082 盈利交易日 33
2018-11-27 17:37:24.717082 亏损交易日: 24
2018-11-27 17:37:24.717082 起始资金: 100000
2018-11-27 17:37:24.717082 结束资金: 100,411.64
2018-11-27 17:37:24.717082 总收益率: 0.41%
2018-11-27 17:37:24.717082 年化收益: 1.14%
2018-11-27 17:37:24.717082 总盈亏: 411.64
2018-11-27 17:37:24.717082 最大回撤: -602.46
2018-11-27 17:37:24.717082 百分比最大回撤: -0.6%
2018-11-27 17:37:24.717082 总手续费: 117.45
2018-11-27 17:37:24.717082 总滑点: 0.07
2018-11-27 17:37:24.717082 总成交金额: 234,902.46
2018-11-27 17:37:24.718082 总成交笔数: 37
2018-11-27 17:37:24.718082 日均盈亏: 4.73
2018-11-27 17:37:24.718082 日均手续费: 1.35
2018-11-27 17:37:24.718082 日均滑点: 0.0
2018-11-27 17:37:24.718082 日均成交金额: 2,700.03
2018-11-27 17:37:24.718082 日均成交笔数: 0.43
2018-11-27 17:37:24.718082 日均收益率: 0.0%
2018-11-27 17:37:24.718082 收益标准差: 0.09%
2018-11-27 17:37:24.718082 Sharpe Ratio: 0.8
2018-11-27 17:37:26.076690 计算回测结果
2018-11-27 17:37:26.078689 ------------------------------
2018-11-27 17:37:26.078689 第一笔交易: 2018-09-06 00:51:00
2018-11-27 17:37:26.078689 最后一笔交易: 2018-11-26 11:58:00
2018-11-27 17:37:26.078689 总交易次数: 19
2018-11-27 17:37:26.078689 总盈亏: 409.58
2018-11-27 17:37:26.078689 最大回撤: -597.16
2018-11-27 17:37:26.078689 平均每笔盈利: 21.56
2018-11-27 17:37:26.078689 平均每笔滑点: 0.0
2018-11-27 17:37:26.078689 平均每笔佣金: 6.29
2018-11-27 17:37:26.078689 胜率 63.16%
2018-11-27 17:37:26.078689 盈利交易平均值 138.08
2018-11-27 17:37:26.078689 亏损交易平均值 -178.19
2018-11-27 17:37:26.078689 盈亏比: 0.77
2018-11-27 17:37:26.712041 计算按日统计结果
MultiFrameMaDf.tail()
rsiTrendDf.tail()
PortfolioDf = MultiFrameMaDf+rsiTrendDf
PortfolioDf = PortfolioDf.dropna()
# 创建回测引擎,并设置组合回测初始资金后,显示结果
engine = BacktestingEngine()
engine.setCapital(1000000)
dfp, result = engine.calculateDailyStatistics(PortfolioDf)
result
{'annualizedReturn': 0.6619457544827818,
'dailyCommission': 6.262007356321838,
'dailyNetPnl': 27.581073103448254,
'dailyReturn': 0.0026295448011353366,
'dailySlippage': 0.004045977011494256,
'dailyTradeCount': 2.0229885057471266,
'dailyTurnover': 12524.014712643675,
'endBalance': 1002399.55336,
'endDate': Timestamp('2018-11-26 00:00:00'),
'lossDays': 30,
'maxDdPercent': -0.05138439214392886,
'maxDrawdown': -514.0053199999966,
'profitDays': 46,
'returnStd': 0.015721744941956703,
'sharpeRatio': 2.5911076055495266,
'startDate': Timestamp('2018-09-01 00:00:00'),
'totalCommission': 544.79464,
'totalDays': 87,
'totalNetPnl': 2399.553359999998,
'totalReturn': 0.2399553360000084,
'totalSlippage': 0.35200000000000026,
'totalTradeCount': 176,
'totalTurnover': 1089589.2799999998}
import matplotlib.pyplot as plt
plt.figure(figsize=(15, 7))
plt.plot(PortfolioDf['netPnl'].cumsum())
plt.show()