了解BigQuant策略研究平台回测机制

# 了解Bigquant策略研究平台回测机制

先看BigQuant策略研究平台回测机制的概览图:

BigQuant策略研究平台回测主体有两个使用频率很高的函数:initialize函数和handle_data函数,理解了这两个函数开发策略就再也不是什么难事了,结合上面K线图来理解这两个函数。

从图中可以看出,其实一共有26个事件,即26根K线,第一根K线既对应黑色箭头,又对应灰色箭头,其余都只对应灰色箭头。回测主体函数的initialize函数(在可视化模块中称为初始化函数)只在第一个事件上调用,即第一根K线,因此很多初始设置可以放在initialize函数里面。每个K线都对应灰色箭头,表示每个事件都会调用handle_data函数(在可视化模块中称为主函数),即从第一根K线到最后一根K线都会运行handle_data一次,于是很多策略逻辑部分就可以放在handle_data里。

从概览图可以了解整个回测机制的原理但仍不知道每根K线是如何处理的,为便于理解每根K线回调的细节,见下图:

在回测过程中,我们在调用handle_data函数时需要生成交易信号和下单。以双均线策略举例说明,代码如下所示:

示例代码

def initialize(context):
    # 初始化策略基本参数,比如双均线策略中短期均线周期和长期均线周期
    context.short_length = 7
    context.long_length = 21

def handle_data(context, data):
    sid = symbol('000002.SZA')
    # 计算短周期均线
    short_ma = data.history(sid, 'close', 5, '1d').mean()
    # 计算长周期均线
    long_ma = data.history(sid, 'close', 20, '1d').mean()
    # 持仓
    cur_position = context.portfolio.positions[sid].amount 

    if short_ma > long_ma and cur_position == 0:
        context.order(sid, 100)
    elif short_ma < long_ma and cur_position > 0:
        context.order_target_percent(sid, 0)

上述的策略编写方式比较直观,但存在两个弊端:一是有多少根K线就会调用多少次handle_data回调函数,每次在handle_data都获取数据生成交易信号的方式会使得回测性能偏低,日线级别的K线回测关系不大,但若是分钟级别的K线回测,等待一个策略回测完成的时间就会很长;二是在handle_data内通过data.history方法只能读取价量数据,而量化策略开发会涉及到各种数据,譬如财务数据、宏观经济数据、公司基本面数据等。因此,最理想的方式是在handle_data回调函数之外批量生成交易信号,在handle_data回调函数内部根据K线时间读取交易信号,然后下单,代码见下:

示例代码


df = DataSource('bar1d_CN_STOCK_A').read(instruments=['000002.SZA'],
     start_date='2018-01-01', end_date='2018-09-01', fields=['close'])
df['short_ma'] = df['close'].rolling(5).mean()
df['long_ma'] = df['close'].rolling(20).mean()
df['date'] = df['date'].map(lambda x:x.strftime('%Y-%m-%d'))
df.set_index('date', inplace=True) 
df['signal'] = np.where(df['short_ma']>df['long_ma'], 'long', 'short')
print('交易信号数据: ', df)

def initialize(context):
    context.mydata = df # 将交易信号数据赋值给context对象

def handle_data(context, data):
    if context.trading_day_index < 20:   
        return 
    sid = context.symbol('000002.SZA')
    today = data.current_dt.strftime('%Y-%m-%d')
    signal = context.mydata[today]['signal'] # 通过当根K线的时间调取该K线对应的交易信号

    # 持仓
    cur_position = context.portfolio.positions[sid].amount 

    if signal == 'long' and cur_position == 0:
        context.order(sid, 100)
    elif signal == 'short' and cur_position > 0:
        context.order_target_percent(sid, 0)

# 简单策略开发

在首页点击“我的策略”按钮就进入个人账户页面,然后点击左上角的添加按钮(“+”)新建notebook。

策略的编写主要包含三个步骤:

1.确定策略参数

2.编写策略主体函数

3.调用回测接口

# 回测结果解读

当我们完成一个策略回测时,我们会得到如下的一个图形,得到回测结果的详细指标:

上图为策略回测结果图,红色矩形标记部分包含了策略的主要信息,包括 收益概况、交易详情、每日持仓及收益、输出日志 。接下来,我们详细介绍这几个部分。

# 收益概况

收益概况以折线图的方式显示了策略在时间序列上的收益率,黄色曲线为策略收益率。同时也显示了沪深300收益率曲线作为比较基准,蓝色曲线为基准收益率。同时,最下面的绿色曲线为持仓占比,持仓占比即仓位,10%的持仓占比表示账户里股票价值只占10%。相对收益率的曲线并没有直接绘制在图上,点击图例 相对收益率(如下图所示),就可以将其绘制出来。

不仅如此,衡量一个策略好坏的关键指标在收益概览页面也得到展示。

  • 收益率:策略整个回测时间段上的总收益率。比如,如果收益率为30%,表明起始时间是1万的本金,结束时间本金就变成1.3万了,一共赚了3000元。

  • 年化收益率:该策略每一年的收益率。比如,如果回测时间段为2年,总收益率为30%,那么每年的年化收益率就在15附近(不考虑复利)。

  • 基准收益率:策略需要有一个比较基准,比较基准为沪深300。若基准收益率为15%,表明在整个回测时间段,大盘本身就上涨了15%,如果策略收益率小于基准收益率,说明策略表现并不好,连大盘都没有跑赢。

  • 阿尔法:衡量策略的一个重要指标,该值越大越好。

  • 贝塔:衡量策略的一个重要指标,该值越小越好。

  • 夏普比率:衡量策略最重要的一个指标,该指标的计算不仅考虑收益率,还考虑了风险,因此比较具有参考价值,可以理解为经过风险调整后的收益率。

  • 胜率:衡量策略盈利一指标,胜率越大越好。比如10次投资中有8次获利,胜率就是80%。

  • 盈亏比:衡量策略盈亏能力大小比较,盈亏比越大越好。比如投资盈利时,平均每次盈利4元,亏损时,平均每次亏损2元,那么此时的盈亏比为2。

  • 收益波动率:收益率的标准差,是风险的一个指标。

  • 最大回撤:策略在整个时间段上亏损最严重的时候相比净值最高值下跌的百分比。如果最大回撤为20%,表明策略在某个时间点上,相比之前的净值最高点下降了20%。最大回撤是策略评估时非常关键的一个指标,通常与风险承受能力相关。

  • 信息比率:信息比率也是一个常用的策略评价指标。

交易详情

交易详情主要显示了策略在整个回测过程中每个交易日的买卖信息。包括买卖时间、股票代码、交易方向、交易数量、成交价格、交易成本。

每日持仓及收益

每日持仓及收益主要呈现每日持有股票代码、当日收盘价、持仓股票数量、持仓金额、收益等指标。

输出日志

输出日志主要为策略运行过程中的一些日志。包括涨跌停股票不能交易、停牌估计不能交易等。该日志可以便于我们检查回测结果的正确性。