vn.py量化社区
By Traders, For Traders.
Administrator
avatar
加入于:
帖子: 158
声望: 17

作者:爱茶语 ;来源:维恩的派论坛

 

  • 原文测试时间区间是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)   # 设置回测本金

 
 

回测效果

 
enter image description here

 
 
策略代码如下
 

# 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
Member
avatar
加入于:
帖子: 2
声望: 0

不错,支持楼主,很全面

Member
avatar
加入于:
帖子: 11
声望: 1

请问您是怎么办判断模型过拟合的?

Member
avatar
加入于:
帖子: 58
声望: 7

yangliu wrote:

请问您是怎么办判断模型过拟合的?
看收益率标准差,收益率峰度,当初也是写着玩,写的时候回测还可以......

Member
avatar
加入于:
帖子: 12
声望: 0

初始资金20万,没看见策略里乘保证金比例,10手已经超过20万了?

Member
avatar
加入于:
帖子: 73
声望: 0

这个策略是不是就是简单的双均线交叉😂,20和200的,在三十分钟周期的

Member
avatar
加入于:
帖子: 72
声望: 1

请问一下为什么显示 总手续费为0?

Member
avatar
加入于:
帖子: 50
声望: 0

楼主,请问回测的tick级别数据库如何设置啊,我搞了好久都没有搞定,有没有什么注意的地方啊

Member
avatar
加入于:
帖子: 50
声望: 0

engine.setDatabase(MINUTE_DB_NAME, 'RB99') # RQDATA一分钟期货指数数据
我看您回测用的还是分钟级别的数据库啊,然后这个RQDATA的数据是存到数据库还是直接调用的呢?谢谢

© 2015-2019 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号-3