31

如何用WonderTrader开发商品套利策略

 3 years ago
source link: https://segmentfault.com/a/1190000039971950
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

image

  《WonderTrader架构详解》系列文章已经写了四篇了,对于笔者这样的懒人来说,实在是一个大工程。这次先暂时不介绍架构了,本文将给大家介绍一下如何在WonderTrader上编写一个跨品种套利策略,旨在给大家演示一下WonderTrader下多标的策略的一般写法,同时也介绍一下笔者在wtpy最新版本v0.6.3中最新发布的绩效分析模块

WonderTrader架构文章列表:

套利策略简介

  相信每一个做量化的人,都研究过套利策略。套利策略以其逻辑的相对简单、收益的相对稳定而得到多数人的青睐。同时,套利策略和趋势策略有较强的互补性,这就意味着,如果配合得当,对平滑收益曲线,降低绩效的波动,提高夏普有极大的促进作用。
  套利策略的分类就非常多了,笔者曾在《WonderTrader架构详解之四——浅谈平台对策略的支持》文中引用了丁鹏博士的《量化投资——策略与技术》对于策略的分类,里面提到的套利的模式就有很多种,例如:期现套、跨期套、跨品种套、配对交易等等。
  笔者为了开发该策略,查阅资料的时候,看到一个观点,深以为然:不管套利策略分类多么繁复,套利策略的本质是在不同合约之间的价差波动中寻找交易机会

套利策略实现

  鉴于笔者的量化策略的技能树,还没点亮,所以本文中的策略示例,是从互联网上学习到的,笔者在此由衷地感谢此策略的分享者。该策略基本设计如下:

  • 合约对选择螺纹钢主力连续合约SHFE.rb.HOT和铁矿石主力连续合约DCE.i.HOT
  • 回测数据为主力连续数据,进行了复权处理,防止主力合约换月不同步的时候引起的价差变化
  • 基于1分钟数据,对过去N分钟两组收盘价序列进行线性回归,得到一个系数beta、一个常量c和一个残差序列
  • 对残差序列进行ADF检验,如果残差序列是一个平稳序列,则进入信号计算逻辑
  • 根据得到的系数beta和常量c,以及最新的价格计算新的残差
  • 当新的残差超出一个阈值,就触发入场信号,当价差再回归到阈值以内,就触发出场信号
  • 参数设计
    根据上文的介绍,策略核心的参数如下:

    • code1:套利合约1,本例设置为SHFE.rb.HOT
    • code2:套利合约2,本例设置为DCE.i.HOT
    • period:数据周期,本例设置为m1,即一分钟线
    • threshold:阈值,本例设置为0.9
    • N:统计的K线条数,本例设置为360,即一个交易日的一分钟线
    • bar_cnt:策略读取的K线条数,大于N,主要考虑预留滚动计算的空间
    def __init__(self, name:str, code1:str, code2:str, bar_cnt:int, 
                            period:str, N:int, threshold:float=1):
        BaseCtaStrategy.__init__(self, name)
    
        self.__n__ = N
        self.__threshold__ = threshold
    
        self.__period__ = period
        self.__bar_cnt__ = bar_cnt
        self.__code_1__ = code1
        self.__code_2__ = code2
  • 协整检验
    策略核心的逻辑在于协整检验,只有协整检验通过了,才能进行信号触发。

    # 协整检验函数
    def cointegration_check(series01, series02):
        # 对两个序列分别进行ADF检验
        urt_1 = ts.adfuller(np.array(series01), 1)[1]
        urt_2 = ts.adfuller(np.array(series02), 1)[1]
    
        # 同时平稳或不平稳则差分再次检验
        if (urt_1 > 0.1 and urt_2 > 0.1) or (urt_1 < 0.1 and urt_2 < 0.1):
            urt_diff_1 = ts.adfuller(np.diff(np.array(series01)), 1)[1]
            urt_diff_2 = ts.adfuller(np.diff(np.array(series02), 1))[1]
    
            # 同时差分平稳进行OLS回归的残差平稳检验
            if urt_diff_1 < 0.1 and urt_diff_2 < 0.1:
                matrix = np.vstack([series02, np.ones(len(series02))]).T
                beta, c = np.linalg.lstsq(matrix, series01, rcond=None)[0]
                resid = series01 - beta * series02 - c
                # 最后对残差序列再进行ADF检验
                if ts.adfuller(np.array(resid), 1)[1] > 0.1:
                    result = False
                else:
                    result = True
                return beta, c, resid, result
            else:
                result = False
                return 0.0, 0.0, 0.0, result
    
        else:
            result = False
            return 0.0, 0.0, 0.0, result
  • 信号发出
    当残差超出上边界,说明残差正向扩大,根据残差均值回归的特性,这时就需要做空价差;相反的,当残差超出下边界,说明残差反向扩大,这时就需要做多价差;当参加在上下边界范围之内,则清掉已有的头寸,等待下一次机会。

    # 计算新残差
    resid_new = close_ay1[-1] - self.beta * close_ay2[-1] - self.c
    
    if resid_new > self.up and curPos1 != 1:
        context.stra_log_text("[%d.%04d]残差正向扩大,做空价差" % (curDate, curTime))
        context.stra_enter_short(self.__code_1__, 1, 'OpenSA')
        context.stra_enter_long(self.__code_2__, 1, 'OpenLB')
    
    elif resid_new < self.down and curPos1 != -1:
        context.stra_log_text("[%d.%04d]残差反向扩大,做多价差" % (curDate, curTime))
        context.stra_enter_long(self.__code_1__, 1, 'OpenLA')
        context.stra_enter_short(self.__code_2__, 1, 'OpenSB')
    
    elif curPos1 != 0 and self.down  <= resid_new and resid_new <= self.up:
        context.stra_log_text("[%d.%04d]残差回归,清掉头寸" % (curDate, curTime))
        context.stra_set_position(self.__code_1__, 0, 'CutA')
        context.stra_set_position(self.__code_2__, 0, 'CutB')

回测示意图

本例中采用的wtpyv0.6.3版本中发布的增强版绩效分析工具进行分析,调用方法和老版本基本一样,但是分析的入口有所变化。

analyst = WtBtAnalyst()
analyst.add_strategy("t1_rb_i", folder="./outputs_bt/t1_rb_i/", init_capital=350000, rf=0.02, annual_trading_days=240)
# 绩效分析的入口,老版本run的仍然可用
analyst.run_new()

绩效分析截图
绩效分析截图

策略绩效概要
策略绩效概要

详细交易列表
详细交易列表

总体交易分析
总体交易分析

连续交易分析
连续交易分析

收益分布
收益分布

日度绩效分析
日度绩效分析

其他周期绩效
其他周期绩效

逐日绩效概览
逐日绩效概览

  从前面可以看出,新版本的绩效分析模块提供了更多维度的分析功能。新版本的绩效分析模块,主要参考了MultiCharts的绩效分析报告,所以从分析结果的全面性和实用性来说,应该是有保障的。
  回到策略本身,从前面的绩效报告可以看出,该策略显然是不大成功的。如何优化该策略呢?笔者考虑是否可以通过加入止盈止损逻辑来进行优化,或者通过调整阈值进行优化。不过这些已经超出本文的宗旨了,如果有朋友感兴趣,可以自行研究。

本文中的示例策略已经分享到github上wtpy项目中的/demos/cta_arbitrage_bt下,有兴趣的朋友可以自行去下载。点此跳转

  本文对利用WonderTrader编写套利策略的介绍就到此结束了。笔者今日一边写文章,一边感到忐忑不安。笔者虽然一直在优秀的量化团队里跟大家一起合作,但是由于笔者是从事技术方面的工作,所以对策略的了解毕竟还是停留在表面,因此生怕今天分享的策略出现了低级错误。好在笔者最后还是决定从网络上寻找现成的策略,这样才稍稍缓解了笔者的不安。即便是这样,本文及本文演示的策略也难免有错漏之处,也希望各位看客能够包涵指正。所幸本文的终极目标是通过笔者今日抛出的“砖头”引起大家找到更多的“美玉”,如果能够对大家有所裨益,那就再好不过了。

  最后再安利一下WonderTrader
  WonderTrader旨在给各位量化从业人员提供更好的轮子,将技术相关的东西都封装在平台中,打造更高效的底层框架,力求给策略研发带来更好的策略开发和交易体验。

WonderTradergithub地址:https://github.com/wondertrad...

WonderTrader官网地址:https://wondertrader.github.io

wtpygithub地址:https://github.com/wondertrad...


市场有风险,投资需谨慎。以上陈述仅作为对于历史事件的回顾,不代表对未来的观点,同时不作为任何投资建议。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK