VNPY应用入门/进行量化策略回测03(不谈理论,只谈应用)


(xmnz) #1

今天来到回测的第三篇详解:

上次我们讲到现在开始策略回测数据,进入到真正的策略模块,调用 def onFiveBar(self, bar)

for orderID in self.orderList:
        self.cancelOrder(orderID)
self.orderList = []

这里是将前面没有成交的委托单全部取消,可以根据自己的策略进行修改。

接着就是更新K线数据和计算指标数值:

    am = self.am
    am.updateBar(bar)
    if not am.inited:
        return
    self.kkUp, self.kkDown = am.keltner(self.kkLength, self.kkDev)

策略分为三种情况进行执行:无持仓/多仓/空仓

  • 无持仓 POS=0 时,发送OCO(One Cancel Other)委托单:
    self.intraTradeHigh = bar.high self.intraTradeLow = bar.low self.sendOcoOrder(self.kkUp, self.kkDown, self.fixedSize)

这里我们看看什么是OCO订单:

    self.buyOrderIDList = self.buy(buyPrice, volume, True)
    self.shortOrderIDList = self.short(shortPrice, volume, True)
    self.orderList.extend(self.buyOrderIDList)
    self.orderList.extend(self.shortOrderIDList)

同时发送做空和做多的订单,在满足条件的时候开仓。

  • 多仓 POS>0 时:
    elif self.pos > 0: self.intraTradeHigh = max(self.intraTradeHigh, bar.high) self.intraTradeLow = bar.low l = self.sell(self.intraTradeHigh*(1-self.trailingPrcnt/100), abs(self.pos), True) self.orderList.extend(l) 我们计算好出场的价格,发送sell订单。最后一种情况,当POS<0时,我们计算好平仓价格,发送cover订单。 整个策略模块就结束了,这里我们展开看一下在回测中的订单流情况,我们看看在多仓时是如何执行下单的:
    l = self.sell(self.intraTradeHigh*(1-self.trailingPrcnt/100), abs(self.pos), True)

这里调用了ctaTemplate.py的sell函数:

def sell(self, price, volume, stop=False):
    """卖平"""
    return self.sendOrder(CTAORDER_SELL, price, volume, stop)

接着我们发现它继续调用了sendOrder函数:

def sendOrder(self, orderType, price, volume, stop=False):
    """发送委托"""
    if self.trading:
        # 如果stop为True,则意味着发本地停止单
        if stop:
            vtOrderIDList = self.ctaEngine.sendStopOrder(self.vtSymbol, orderType, price, volume, self)
        else:
            vtOrderIDList = self.ctaEngine.sendOrder(self.vtSymbol, orderType, price, volume, self) 
        return vtOrderIDList
    else:
        # 交易停止时发单返回空字符串
        return []

这里分为两种情况:是停止单还是非停止单,接着分别调用sendStopOrder或者sendOrder,这里需要注意的是,分为两种引擎,回测和实盘,调用的发单函数是不一样的,现在回测中调用的是ctabacktesting里的下单函数,我们看一看:

def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy):
    """发停止单(本地实现)"""
    self.stopOrderCount += 1
    stopOrderID = STOPORDERPREFIX + str(self.stopOrderCount)
    so = StopOrder()
    so.vtSymbol = vtSymbol
    so.price = self.roundToPriceTick(price)
    so.volume = volume
    so.strategy = strategy
    so.status = STOPORDER_WAITING
    so.stopOrderID = stopOrderID  
    if orderType == CTAORDER_BUY:
        so.direction = DIRECTION_LONG
        so.offset = OFFSET_OPEN
    elif orderType == CTAORDER_SELL:
        so.direction = DIRECTION_SHORT
        so.offset = OFFSET_CLOSE
    elif orderType == CTAORDER_SHORT:
        so.direction = DIRECTION_SHORT
        so.offset = OFFSET_OPEN
    elif orderType == CTAORDER_COVER:
        so.direction = DIRECTION_LONG
        so.offset = OFFSET_CLOSE           
    # 保存stopOrder对象到字典中
    self.stopOrderDict[stopOrderID] = so
    self.workingStopOrderDict[stopOrderID] = so 
    # 推送停止单初始更新
    self.strategy.onStopOrder(so)          
    return [stopOrderID]

主要就是更新stopOrderDict和workingStopOrderDict。所有的策略逻辑及下单、撮合到这里就全部将完了,下面我们看看关于策略表现的代码块。 回到runbacktesting文件,执行最后一条语句:engine.showBacktestingResult() 调用BacktestingEngine的showBacktestingResult函数:

d = self.calculateBacktestingResult()

这个函数中就会配对计算,基本上不用做什么修改,照搬就行,不过还是需要大家仔细读一下代码,到底是如何计算的,不难,这里就不一一解释了。

最后我们看看如何对参数进行优化,打开runOptimization.py

setting = OptimizationSetting()                 # 新建一个优化任务设置对象
setting.setOptimizeTarget('capital')            # 设置优化排序的目标是策略净盈利
setting.addParameter('atrLength', 12, 20, 2)    # 增加第一个优化参数atrLength,起始12,结束20,步进2
setting.addParameter('atrMa', 20, 30, 5)        # 增加第二个优化参数atrMa,起始20,结束30,步进5
setting.addParameter('rsiLength', 5)            # 增加一个固定数值的参数

可以看到显示定义了一个优化对象setting,然后加入自己想优化的对象,大家可以执行一下默认的runOptimization.py