多个套利对配对交易

配对交易
统计套利
协整
标签: #<Tag:0x00007f25ae528d90> #<Tag:0x00007f25ae528bb0> #<Tag:0x00007f25ae5288b8>

(小马哥) #1
克隆策略

本策略为多个套利对的统计套利策略示例,仅供参考!

In [1]:
# 10个套利对
pairs = {'0':['601328.SHA', "600000.SHA"],
         '1':['600015.SHA', '601169.SHA'],
         '2':['600015.SHA', '002142.SZA'],
         '3':['600016.SHA', '601169.SHA'],
         '4':['600036.SHA', '601169.SHA'],
         '5':['601398.SHA', '601166.SHA'],
         '6':['601328.SHA', '601398.SHA'],
         '7':['601169.SHA', '002142.SZA'],
         '8':['601328.SHA', '600000.SHA'],
         '9':['600016.SHA', '600015.SHA'],
        }
# 整理股票列表
all_instruments = []
for i in pairs.keys():
    all_instruments = all_instruments+pairs[i]
    
all_instruments = list(set(all_instruments))

# 获取价格数据
start_date = '2015-01-01' 
end_date = '2017-07-18' 
prices_temp = D.history_data(all_instruments,start_date,end_date,
              fields=['close'] )
prices_df=pd.pivot_table(prices_temp, values='close', index=['date'], columns=['instrument'])
In [2]:
import statsmodels.api as sm
# 计算z得分
def zscore(series):
    return (series - series.mean()) / np.std(series)

# 将各个套利对的z得分汇总到一块
zscore_dict = {}
for key in pairs.keys():
    stock_0 = pairs[key][0]
    stock_1 = pairs[key][1]
    x = prices_df[stock_0] # 这里传入每个套利对的单条腿
    y = prices_df[stock_1]
    X = sm.add_constant(x)
    result = (sm.OLS(y,X)).fit()
    stock_1 = result.params.index.values[1]
    coef = result.params.ix[stock_1]
    # T.plot(pd.DataFrame({'Stationary Series':0.8753*x-y, 'Mean':[np.mean(0.8753*x-y)]}), chart_type='line')
    df = pd.DataFrame({'Stationary Series':y-coef*x, 'Mean':np.mean(y-coef*x)})
    zscore_calcu = zscore(y-coef*x)
    zscore_dict[key] = zscore_calcu
zscore_df = pd.DataFrame(zscore_dict)    

策略完整交易系统设计

交易信号: 当zscore大于1时,全仓买入交通银行,全仓卖出中信银行→做空价差 当zscore小于-1时,全仓卖出中信银行,全仓买入交通银行→做多价差

策略回测部分

In [3]:
# 初始化账户和传入需要的变量
def initialize(context):
    context.set_commission(PerDollar(0.0015)) # 手续费设置
    context.zscore = zscore_df  
    context.pairs  = pairs # 传入协整股票对
    
# 策略主题函数   
def handle_data(context, data):
    
    date = data.current_dt.strftime('%Y-%m-%d') # 运行到当根k线的日期
    weight = 1/len(context.pairs)
    
    for i in zscore_df.columns:  # 对各个套利对循环
        
        stock_1 = context.pairs[i][0] # 股票y
        stock_2 = context.pairs[i][1] # 股票x
         
        zscore = context.zscore[i].ix[date]  # 当日的zscore
        symbol_1 = context.symbol(stock_1) # 转换成回测引擎所需要的symbol格式
        symbol_2 = context.symbol(stock_2)
    
        # 持仓
        cur_position_1 = context.portfolio.positions[symbol_1].amount
        cur_position_2 = context.portfolio.positions[symbol_2].amount
   
        # 交易逻辑
        # 如果zesore大于上轨(>1),则价差会向下回归均值,因此需要买入股票x,卖出股票y
        if zscore > 1 and cur_position_2 == 0 and data.can_trade(symbol_1) and data.can_trade(symbol_2):  
            context.order_target_percent(symbol_1, 0)
            context.order_target_percent(symbol_2, 1*weight)
           
        
        # 如果zesore小于下轨(<-1),则价差会向上回归均值,因此需要买入股票y,卖出股票x
        elif zscore < -1 and cur_position_1 == 0 and data.can_trade(symbol_1) and data.can_trade(symbol_2):  
            context.order_target_percent(symbol_1, 1*weight)
            context.order_target_percent(symbol_2, 0)     
In [4]:
# 回测启动接口
m=M.trade.v2( 
    instruments=all_instruments, 
    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=1000000,
    benchmark='000300.INDX',
)
[2017-08-10 12:38:40.327780] INFO: bigquant: backtest.v7 start ..
/var/app/enabled/pandas/tseries/index.py:817: PerformanceWarning: Non-vectorized DateOffset being applied to Series or DatetimeIndex
  "or DatetimeIndex", PerformanceWarning)
[2017-08-10 12:38:47.299484] INFO: Performance: Simulated 619 trading days out of 619.
[2017-08-10 12:38:47.300788] INFO: Performance: first open: 2015-01-05 14:30:00+00:00
[2017-08-10 12:38:47.301837] INFO: Performance: last close: 2017-07-18 19:00:00+00:00
/var/app/enabled/pandas/core/generic.py:1138: PerformanceWarning: 
your performance may suffer as PyTables will pickle object types that it cannot
map directly to c-types [inferred_type->mixed,key->block4_values] [items->['LOG', 'POS_FAC', 'TRA_FAC', 'orders', 'period_label', 'positions', 'transactions']]

  return pytables.to_hdf(path_or_buf, key, self, **kwargs)
/var/app/enabled/pandas/core/indexing.py:141: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
  self._setitem_with_indexer(indexer, value)
  • 收益率9.49%
  • 年化收益率3.76%
  • 基准收益率3.78%
  • 阿尔法0.01
  • 贝塔0.66
  • 夏普比率-0.03
  • 收益波动率26.25%
  • 信息比率0.11
  • 最大回撤38.64%
[2017-08-10 12:38:49.672443] INFO: bigquant: backtest.v7 end [9.344665s].

(behrends) #2

stock_1 = context.pairs[i][0] # 股票y
stock_2 = context.pairs[i][1] # 股票x
这里的01要调换