• QQ空间
  • 回复
  • 收藏

【宽客策略源码】配对交易

geek168 数据策略 2019-8-11 16:22 62407人围观

什么是配对交易?
配对交易(Pairs Trading)是指八十年代中期华尔街著名投行Morgan Stanley的数量交易员Nunzio Tartaglia成立的一个数量分析团队提出的一种市场中性投资策略,,其成员主要是物理学家、数学家、以及计算机学家。
Ganapathy Vidyamurthy在《Pairs Trading: Quantitative Methods and Analysis》一书中定义配对交易为两种类型:一类是基于统计套利的配对交易,一类是基于风险套利的配对交易。
配对交易的原理
该策略监控两个历史相关证券的表现。 当两种证券之间的相关性暂时减弱时,即一只股票向上移动而另一只股票向下移动,对货币交易将做空缩短表现优异的股票并做多表现不佳的股票,并认为两者之间的“差价”最终趋同。 一对股票的价格的分歧可能是由于临时供需变化,一个股票的大单交易,一家公司出现重要新闻等等。

由于配对交易利用配对间的短期错误定价,通过持有相对低估,卖空相对高估,因此其本质上是一个反转投资策略,其核心是学术文献中的股票价格均值回复。尽管配对交易策略非常简单,但却被广泛应用,其之所以能被广泛应用的主要原因是:首先,配对交易的收益与市场相独立,即市场中性,也就是说它与市场的上涨或者下跌无关;其次,其收益的波动性相对较小;第三,其收益相对稳定。
配对交易特点
配对交易利用了两个资产的短暂价格偏离的对称性,进行对冲以获取两个资产的Alpha收益,其核心假设是配对资产的价差具有均值回复性。而这种均值回复是以交易者的非理性行为相关的。因为当市场中存在大量这类想法的交易者时,股票价格会产生上升的惯性,但这种上升惯性仅仅由于这一个非理性因素所驱动,并没有具体实际的基本面因素支撑,因而股价很快会跌落。相反,股价下跌的股票也会产生下跌的惯性,当市场的理性因素占据主导时,价格又回复到原先的水平。而如果交易者能够采用配对交易策略,就可以获得这两项资产价格偏离的收益了。在实际操作中,其执行过程可以简单地描述为:投资者首先选择相互匹配的两个资产,当配对资产价格差异增加的时候,做多价格偏低的资产,同时做空价格偏高的资产,而当价格差异减小的时候,则结束头寸,完成交易;同时,为了控制风险,当价差进一步扩大时,需要在适当的止损点结束头寸。
配对交易是一种市场中性的交易策略。当观察到配对的资产价格差异增大到一定程度时,对价格上升的资产建立空头,对价格下跌的资产建立多头。在一价定律的作用下,配对资产的价格差异将很快减小。这时,再买入价格下跌的资产对冲之前建立的空头头寸,同时卖出价格上涨的资产,获取两项资产的收益。就整个过程来看,配对交易在单一资产上都有系统风险和个别风险,但由于头寸始终是相反的,因此系统风险完全对冲,配对交易整体上只承担了配对资产的个别风险。而在一价定律下,价格对于价值的回复使得配对的资产的个别风险直接转换为个别收益。这种收益是于市场无关的。
配对交易策略需要良好的头寸规模,市场时机和决策技巧。 虽然该策略没有太大的下行风险,但交易机会很少,而且,为了获利,交易者必须是“最先下手的人”,这就需要强大的编程和硬件能力。
配对交易实证
我们来看A股市场上一对被比较常见的配对股票:美的集团(000333),格力电器(000651)
时间为2014-01-01至2018-01-01,中间有一段时间格力电器停牌,数据缺失。

配对01.png

经计算,两者相关性系数为98.73%,可以说相关性相当高,这也就是为什么两者经常被用来做配对交易。

二者在大趋势上都是上涨的,时间段拉长反而会造成两者相关性提高,我们再来看最近一年两者的相关性。

配对02.png
长周期来看,两只股票虽然都为汽车板块,但相关系数只有76.52%配对03.png


短期来看,两者的相关性更低,相关性系数只有56.76%

接下来,我们来看一下配对交易的一般策略思想是怎样的。

以下回测参数均设置为:

时间:2015-01-01至2018-08-01

调仓频率:1天

基准指数:沪深三百指数(SHSE.000300)

标的:美的集团(000333)格力电器(000651)

滑点:0.0001

手续费:0.0001

策略思想:
  • 获取两支股票交易情况,如果停牌,则跳过。
  • 计算前四日,两者收盘价差值,并计算差值的方差及平均值。
  • 依据以上数据,构造差值的布林通道。
  • 当价差上破布林上轨,做空价差(半仓)。
  • 当价差下破布林下轨,做多价差(半仓)。


  1. # coding=utf-8
  2. from __future__ import print_function, absolute_import, unicode_literals
  3. from gm.api import *
  4. import  numpy as np



  5. def init(context):
  6.     #获得N日股票交易数据
  7.     context.N=5
  8.     #选择一对股票
  9.     context.stock=['SZSE.000651','SZSE.000333']
  10.     # 每个交易日的09:40 定时执行algo任务
  11.     schedule(schedule_func=algo, date_rule='1d', time_rule='09:40:00')



  12. def algo(context):
  13.     # 获取上一个交易日的日期
  14.     last_day = get_previous_trading_date(exchange='SHSE', date=context.now)
  15.     # 获取当天有交易的股票,似乎无法同时获得两只股票的数据,所以只能麻烦一点
  16.     not_suspended = get_history_instruments(symbols=context.stock[0], start_date=last_day, end_date=last_day)
  17.     a = len([item['symbol'] for item in not_suspended if not item['is_suspended']])
  18.     not_suspended = get_history_instruments(symbols=context.stock[1], start_date=last_day,end_date=last_day)
  19.     b = len([item['symbol'] for item in not_suspended if not item['is_suspended']])
  20.     #如果有一支停牌,就跳过
  21.     if a+b<2:
  22.         return
  23.     #获得交易数据
  24.     prices1 = history_n(symbol=context.stock[0], frequency='1d', count=context.N, end_time=last_day, fields='close',
  25.                        skip_suspended=True,
  26.                        fill_missing=None, adjust=ADJUST_PREV, adjust_end_time='', df=True)
  27.     prices2=history_n(symbol=context.stock[1], frequency='1d', count=context.N, end_time=last_day, fields='close',
  28.                        skip_suspended=True,
  29.                        fill_missing=None, adjust=ADJUST_PREV, adjust_end_time='', df=True)
  30.     p1=list(prices1['close'])
  31.     p2=list(prices2['close'])
  32.     spread = np.array(p1[:-1]) - np.array(p2[:-1])
  33.     # 计算布林带的上下轨
  34.     up = np.mean(spread) + 2 * np.std(spread)
  35.     down = np.mean(spread) - 2 * np.std(spread)
  36.     # 计算最新价差
  37.     spread_now = p1[-1] - p2[-1]
  38.     # 无交易时若价差上(下)穿布林带上(下)轨则做空(多)价差
  39.     position_s1_long = context.account().position(symbol=context.stock[0], side=PositionSide_Long)
  40.     position_s2_long = context.account().position(symbol=context.stock[1], side=PositionSide_Long)
  41.     if not position_s1_long and not position_s2_long:
  42.         if spread_now > up:
  43.             order_target_percent(symbol=context.stock[1], percent=0.5, order_type=OrderType_Market,
  44.                                  position_side=PositionSide_Long)
  45.         if spread_now < down:
  46.             order_target_percent(symbol=context.stock[0], percent=0.5, order_type=OrderType_Market,
  47.                                  position_side=PositionSide_Long)
  48.             # 价差回归时平仓
  49.     elif position_s2_long:
  50.         if spread_now <= up:
  51.             order_close_all()
  52.     elif position_s1_long:
  53.         if spread_now >= down:
  54.             order_close_all()


  55. if __name__ == '__main__':
  56.     '''
  57.     strategy_id策略ID,由系统生成
  58.     filename文件名,请与本文件名保持一致
  59.     mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST
  60.     token绑定计算机的ID,可在系统设置-密钥管理中生成
  61.     backtest_start_time回测开始时间
  62.     backtest_end_time回测结束时间
  63.     backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
  64.     backtest_initial_cash回测初始资金
  65.     backtest_commission_ratio回测佣金比例
  66.     backtest_slippage_ratio回测滑点比例
  67.     '''
  68.     run(strategy_id='73bb5bf2-a536-11e8-bd52-9cd21ef04ea9',
  69.         filename='配对交易.py',
  70.         mode=MODE_BACKTEST,
  71.         token='c395247a76e8a5caeee699d668d6f550213bc418',
  72.         backtest_start_time='2014-01-01 08:00:00',
  73.         backtest_end_time='2018-08-01 16:00:00',
  74.         backtest_adjust=ADJUST_PREV,
  75.         backtest_initial_cash=10000000,
  76.         backtest_commission_ratio=0.0001,
  77.         backtest_slippage_ratio=0.0001)
复制代码







路过

雷人

握手

鲜花

鸡蛋
原作者: quant-TT
关注微信