[量化学堂-新手专区]BigQuant回测机制

事件驱动
回测
标签: #<Tag:0x00007f5bf280e328> #<Tag:0x00007f5bf280e198>

(iQuant) #1

导语:不熟悉BigQuant平台的回测机制,可能使刚接触BigQuant平台的小伙伴有些困惑,不知该如何编写策略。当使用某一回测平台时,如果不能对其回测处理机制了解清楚,我们很可能出现偷价漏价、未来函数等问题,这些问题对策略的影响是致命的。即使不出现这样的问题,很多时候,用户可能写的策略并没有达到预期的目的,因此了解回测机制非常重要。


作者:bigquant
阅读时间:10分钟
本文由BigQuant宽客学院推出,难度标签:☆☆☆

事件驱动机制

在策略回测中应用最为广泛的就是 事件驱动机制。先看定义:当某个新的事件被推送到程序中时,程序立即调用和这个事件相对应的处理函数进行相关的操作。 举个“栗子”让大家更好理解。

比如开发一个股指策略,交易程序对股指TICK数据进行监听,当没有新的行情过来时,程序保持监听状态不进行任何操作;当收到新的数据时,数据处理函数立即更新K线和其他技术指标,并检查是否满足策略的下单条件,如果满足条件就执行下单。

BigQuant平台的回测机制就是事件驱动机制。因为用户分析的数据是股票、期货等交易数据,这类数据的一个很大特点就是本身是标准化的时间序列数据,在各个交易软件上也是以K线的形式呈现,K线包含了交易的开盘价、最高价、最低价、收盘价。如图1所示:

$$图1 \ \ K线示意图$$

BigQuant平台回测机制是 把每一个K线当做一个事件,按照时间发生先后顺序,即从左往右依次运行。 在新的事件发生时,即出现了包含高开低收四个价格的K线,回测程序会调用这个K线的数据,如果没有触发策略信号,就什么也不做,如果触发了交易信号,则产生订单。

为了避免未来函数,即在产生订单的时候不使用未来数据,只能使用当前能够获得的数据,BigQuant平台进行了如下处理:当产生订单时,只能在下一个K线上完成订单成交,这样就能避免未来函数的问题,同时也能更加逼近现实真实情况,因为很多时候,当某根K线发出交易信号时,用户只能在下一个K线上成交订单。不仅如此,为了控制订单以某个预期的价格成交,还可以设置成交价格,最为常见的就是设置订单价格是下一个K线的开盘价。

了解BigQuant回测机制

先看BigQuant回测机制的概览图(图2):

$$图2 \ \ BigQuant回测机制概览图$$
BigQuant平台回测主体有两个使用频率很高的函数:initialize函数和handle_data函数,理解了这两个函数开发策略就再也不是什么难事了,结合上面K线图来理解这两个函数(如图3)。

$$图3 \ \ 两个函数与K线的关系示意图$$

从图中可以看出,其实一共有26个事件,即26根K线,第一根K线既对应黑色箭头,又对应灰色箭头,其余都只对应灰色箭头。回测主体函数的initialize函数只在第一个事件上调用,即第一根K线,因此很多初始设置可以放在initialize函数里面。每个K线都对应灰色箭头,表示每个事件都会调用handle_data函数,即从第一根K线到最后一根K线都会运行handle_data一次,于是很多策略逻辑部分就可以放在handle_data里。不知道这样解释大家是否能够更容易理解?

简单策略开发

由于BigQuant提供的是提供在线的Notebook云端研究平台,因此在首页点击“编写策略”按钮就进入个人账户页面,然后点击左上角的添加按钮(“+”)新建notebook。

策略的编写主要包含三个步骤。回测文档参考:链接

1.确定策略参数

在回测的时候,会面临选哪只股票,回测从什么日期开始,回测在哪天结束这样的问题,因此我们需要再回测前确定这几个策略参数

instrument = ['600519.SHA']
start_date = '2014-01-01'
end_date = '2017-12-31'

2.编写策略主体函数

def initialize(context):
    context.set_commission(PerDollar(0.0015)) 

def handle_data(context, data):

    sid = context.symbol(instrument[0])
    cur_position = context.portfolio.positions[sid].amount
    if cur_position == 0:
        order(sid, 100)
    elif cur_position > 0:
        order(sid, -100)

initialize函数里我们设置了手续费。handle_data函数里我们制定了策略逻辑:当没有持仓时,产生股票数量为100的买入订单;当持仓时,产生股票数量为100的卖出订单。

3.调用回测接口

两个主体函数构建完毕之后,再调用回测接口就完成策略开发了,回测接口如下:

m=M.trade.v3( 
    instruments=instrument,
    start_date=start_date,
    end_date=end_date,
    initialize=initialize,
    handle_data=handle_data,
    order_price_field_buy='open', # 买入订单成交价格为开盘价
    order_price_field_sell='open', # 卖出订单成交价格为开盘价
    capital_base=200000, # 初始资金
    benchmark='000300.INDX', # 基准是沪深300 指数
)

直接运行策略接口就可以让策略跑起来,回测速度非常快,直接输出回测结果,图表也是相当友好。当然了,这个简单策略没有一点实际价值,因此资金曲线也是有的“惨”啊:joy:

不知道大家现在对BigQuant的回测机制是不是更了解一些了,快克隆下策略自己试试吧。

小结:只有了解了平台的回测机制,才能写出更为灵活丰富的策略。


本文由BigQuant宽客学院推出,版权归BigQuant所有,转载请注明出处。

附件:一个简单的策略

克隆策略
In [1]:
instrument = ['000001.SZA']
start_date = '2014-01-01'
end_date = '2017-12-31'
In [ ]:
def initialize(context):
    context.set_commission(PerDollar(0.0015)) 

def handle_data(context, data):
    sid = context.symbol(instrument[0])
    cur_position = context.portfolio.positions[sid].amount
    if cur_position == 0:
        order(sid, 100)
    elif cur_position > 0:
        order(sid, -100)
In [ ]:
m=M.trade.v3( 
    instruments=instrument,
    start_date=start_date,
    end_date=end_date,
    initialize=initialize,
    handle_data=handle_data,
    order_price_field_buy='open', # 买入订单成交价格为开盘价
    order_price_field_sell='open', # 卖出订单成交价格为开盘价
    capital_base=200000, # 初始资金
    benchmark='000300.SHA', # 基准是沪深300 指数
)


【宽客学院】回测数据深入分析
【宽客学院】自定义买入卖出策略
社区干货与精选整理(持续更新中...)
【宽客学院】策略回测结果解读
【宽客学院】开发传统趋势策略
BigQuant AI策略详解
[量化学堂-新手专区]熟悉BigQuant策略开发环境
请教回测中历史数据context.start_date、end_date的理解
LSTM Networks应用于股票市场之Sequential Model
gs-策略交易引擎介绍
(vwvwvw) #2

解释得很清楚,应用也很方便。


(breezescut) #3

我使用 bigquant 提供的贵州茅台回测例子进行回测, 在 “每日持仓和收益” 那里发现茅台 2017-11-23 收盘价达到4506.3701171875, 这个价是不是有问题,还是有其他解读?


(iQuant) #4

你好,这是后复权价格。之所以采取后复权是为了考虑到分红派息等因素,尽可能逼近真实情况。


(xycoder) #5

有个问题啊, 我也跑了例子, 点击交易详情的话, 看到交易详情是这样的?
后复权难道不应该修正持仓嘛? 这个交易价格,以及成交股数都不对吧?
2017-12-29 09:30:00 600519.SHA 买入 28.68 5105.6 146431.31 219.65


(iQuant) #6

是的,后复权价格模式下回测,价格是后复权,比较大。
因为回测只是验证策略有效性,因此不需要对数量进行复权。这与实盘交易有一些区别。
如果要模拟交易和实盘的话,可以在真实交易价格模式下进行回测,这样与实盘交易就是一致的。


(xycoder) #7

正在测试还没验证, 想问一下,这是说设置成 price_type = ‘original’ 就可以得到和实盘交易完全一致的回测效果嘛?
谢谢


(小Q) #8

是的,如你所说。@xycoder


(Rhein) #9

而平台只支持日K,这是不是意味着:订单是在明日进行交易的,我的买入价格,差不多是明日的开盘价?


(iQuant) #10

目前,回测机制是事件驱动机制,正如本文所述,如果是日线级别的回测的话,你们就是当天下单,实际成交在下一个交易日,成交价格可以是开盘价也可以是收盘价,看你怎么设置。同理,过段时间分钟回测上线以后,那么就是当下分钟下单,实际成交在下一分钟。
不知道是否解答了您的疑问。


(Rhein) #11

明白了,谢谢。
还有个疑问,就是下单语句之后的代码,是否也等到明日下单语句执行完毕之后,再执行?

context.order_target_percent(context.symbol(stock), 0.15) #这是下单语句
####后面还有一些代码####


(iQuant) #12

运行到该代码的时候,就已经执行了。
执行后生成订单计划,明日的时候加载这个订单计划,实际撮合成交。
不过这些都是回测引擎内部完成的。


(Rhein) #13

好的,谢谢


(youke) #14

我想在回测的第一天买入股票建仓,在
def initialize(context):
context.set_commission(PerDollar(0.0015))
后面加上了这几句,出错
def initialize(context):
context.set_commission(PerDollar(0.0015))

context.ins  = instrument # 传入股票对

stock_1 = context.ins['y'] # 股票y
stock_2 = context.ins['x'] # 股票x

symbol_1 = context.symbol(stock_1) # 转换成回测引擎所需要的symbol格式
symbol_2 = context.symbol(stock_2)

context.order_target_percent(symbol_1,0.5)
context.order_target_percent(symbol_2,0.5)

最后2句错误提示:AttributeError: ‘TradingAlgorithm’ object has no attribute ‘trading_client’

要如何实现初始建仓(第一天就建仓)的功能?


(cd6003) #15

我感觉如果不能实现日内分钟交易的话,往往第二天开盘时已经买不到前天收盘时的价格了。(假设你的策略是准确的,同理这个市场中也会有同样的人看到这一机会,往往第二天竞价高开)
即使第二天低开,那么可能成交的价格又不是昨收的价格。
这样,回测的数据就很难保证准确。