作者:爱茶语 ; 来源:维恩的派论坛
- 原文测试时间区间是20120111--20171117,样本内夏普比率达1.35。
- 今进行样本外测试,时间区间20130111--20190102,夏普比率为0.78。
结果显示尽管参数不多,但是模型还是过拟合了。但是在策略内实现Tick数据聚合成X分钟K线还是值得学习一下
回测设置
# 设置回测使用的数据
engine.setBacktestingMode(engine.BAR_MODE) # 设置引擎的回测模式为K线
engine.setDatabase(MINUTE_DB_NAME, 'RB99') # RQDATA一分钟期货指数数据
engine.setStartDate('20130101') # 设置回测用的数据起始日期
# 配置回测引擎参数
engine.setSlippage(1) # 设置滑点为1跳
engine.setRate(1/1000) # 设置手续费
engine.setSize(10) # 设置合约大小
engine.setPriceTick(1) # 设置最小价格变动
engine.setCapital(200000) # 设置回测本金
回测效果
策略代码如下:
# encoding: UTF-8
import talib
import numpy as np
from vnpy.trader.vtObject import VtBarData
from vnpy.trader.vtConstant import EMPTY_INT,EMPTY_STRING
from vnpy.trader.app.ctaStrategy.ctaTemplate import CtaTemplate
########################################################################
class RBMAStrategy(CtaTemplate):
"""结合MA的一个30分钟线交易策略"""
className = 'RBMAStrategy'
author = 'xldistance'
#策略参数
initDays = 33 # 初始化数据所用的天数默认35
open_pos = 10 #每次交易的手数
OCM = 30 #操作分钟周期(1,60)默认30
# 策略变量
bar = None # K线对象
barMinute = EMPTY_STRING # K线当前的分钟
minutebar = None # minuteK线对象
ma_windows1 = 20 #默认20
ma_windows2 = 200 #默认200
# 参数列表,保存了参数的名称
paramList = ['name',
'className',
'author',
'vtSymbol',
'open_pos']
# 变量列表,保存了变量的名称
varList = ['inited',
'trading',
'pos',
'OCM',
'ma20_value',
'ma200_value']
#----------------------------------------------------------------------
def __init__(self, ctaEngine, setting):
"""Constructor"""
super(RBMAStrategy, self).__init__(ctaEngine, setting)
"""
如果是多合约实例的话,变量需要放在__init__里面
"""
#self.orderList = []
self.barList = []
self.bufferSize = 201 # 需要缓存的数据的大小
self.bufferCount = 0 # 目前已经缓存了的数据的计数
self.highArray = np.zeros(self.bufferSize) # K线最高价的数组
self.lowArray = np.zeros(self.bufferSize) # K线最低价的数组
self.closeArray = np.zeros(self.bufferSize) # K线收盘价的数组
self.openArray = np.zeros(self.bufferSize) # K线开盘价的数组
self.LongEnterable = False
self.ShortEnterable = False
self.ma20_value = 0
self.ma200_value = 0
def onInit(self):
self.writeCtaLog('%s策略初始化' %self.name)
# 载入历史数据,并采用回放计算的方式初始化策略数值
initData = self.loadBar(self.initDays)
for bar in initData:
self.onBar(bar)
self.putEvent()
#----------------------------------------------------------------------
def onStart(self):
"""启动策略(必须由用户继承实现)"""
self.writeCtaLog('%s策略启动' %self.name)
self.putEvent()
#----------------------------------------------------------------------
def onStop(self):
"""停止策略(必须由用户继承实现)"""
self.writeCtaLog('%s策略停止' %self.name)
self.putEvent()
#----------------------------------------------------------------------
def onTick(self, tick):
"""收到行情TICK推送(必须由用户继承实现)"""
tickMinute = tick.datetime.minute
if tickMinute != self.barMinute:
if self.bar:
self.onBar(self.bar)
bar = VtBarData()
bar.vtSymbol = tick.vtSymbol
bar.symbol = tick.symbol
bar.exchange = tick.exchange
bar.open = tick.lastPrice
bar.high = tick.lastPrice
bar.low = tick.lastPrice
bar.close = tick.lastPrice
bar.date = tick.date
bar.time = tick.time
bar.datetime = tick.datetime # K线的时间设为第一个Tick的时间
self.bar = bar # 这种写法为了减少一层访问,加快速度
self.barMinute = tickMinute # 更新当前的分钟
else: # 否则继续累加新的K线
bar = self.bar # 写法同样为了加快速度
bar.high = max(bar.high, tick.lastPrice)
bar.low = min(bar.low, tick.lastPrice)
bar.close = tick.lastPrice
#----------------------------------------------------------------------
def onBar(self, bar):
"""收到Bar推送(必须由用户继承实现)"""
if self.LongEnterable:
if self.pos == 0:# and bar.close > self.dayOpen
self.buy(bar.close,self.open_pos,True)
elif self.pos < 0 :
self.cover(bar.close,abs(self.pos),True)
if self.ShortEnterable:
if self.pos ==0:#and bar.close < self.dayOpen
self.short(bar.close,self.open_pos,True)
elif self.pos > 0:
self.sell(bar.close,abs(self.pos),True)
if bar.datetime.minute % self.OCM == 0:
# 如果已经有聚合minuteK线
if self.minutebar:
# 将最新分钟的数据更新到目前minute线中
minutebar = self.minutebar
minutebar.high = max(minutebar.high, bar.high)
minutebar.low = min(minutebar.low, bar.low)
minutebar.close = bar.close
# 推送minute线数据
self.onminutebar(minutebar)
# 清空minute线数据缓存
self.minutebar = None
else:
# 如果没有缓存则新建
if not self.minutebar:
minutebar = VtBarData()
minutebar.vtSymbol = bar.vtSymbol
minutebar.symbol = bar.symbol
minutebar.exchange = bar.exchange
minutebar.open = bar.open
minutebar.high = bar.high
minutebar.low = bar.low
minutebar.close = bar.close
minutebar.date = bar.date
minutebar.time = bar.time
minutebar.datetime = bar.datetime
self.minutebar = minutebar
else:
minutebar = self.minutebar
minutebar.high = max(minutebar.high, bar.high)
minutebar.low = min(minutebar.low, bar.low)
minutebar.close = bar.close
# 发出状态更新事件
self.putEvent()
#----------------------------------------------------------------------
def onminutebar(self,bar):
"""收到Bar推送(必须由用户继承实现)"""
# 撤销之前发出的尚未成交的委托(包括限价单和停止单)
#for orderID in self.orderList:
#self.cancelOrder(orderID)
#self.orderList = []
# 保存K线数据
self.closeArray[0:self.bufferSize-1] = self.closeArray[1:self.bufferSize]
self.highArray[0:self.bufferSize-1] = self.highArray[1:self.bufferSize]
self.lowArray[0:self.bufferSize-1] = self.lowArray[1:self.bufferSize]
self.openArray[0:self.bufferSize-1] = self.openArray[1:self.bufferSize]
self.closeArray[-1] = bar.close
self.highArray[-1] = bar.high
self.lowArray[-1] = bar.low
self.openArray[-1] = bar.open
self.bufferCount += 1
if self.bufferCount < self.bufferSize:
return
# 计算指标数值
ma_20 = talib.EMA(self.closeArray,timeperiod = self.ma_windows1)
ma_200 = talib.EMA(self.closeArray,timeperiod = self.ma_windows2)
self.ma20_value = ma_20[-1]
self.ma200_value = ma_200[-1]
self.LongEnterable = ma_20[-1] > ma_200[-1] and ma_20[-2] < ma_200[-2]
self.ShortEnterable = ma_20[-1] < ma_200[-1] and ma_20[-2] > ma_200[-2]
# 发出状态更新事件
self.putEvent()
#----------------------------------------------------------------------
def onOrder(self, order):
"""收到委托变化推送(必须由用户继承实现)"""
pass
#----------------------------------------------------------------------
def onTrade(self, trade):
# 发出状态更新事件
self.putEvent()
def onStopOrder(self, so):
"""停止单推送"""
pass
推荐阅读: