统计套利策略运行出错,大家帮忙看看!

策略分享
标签: #<Tag:0x00007fcf6e473890>

(horrrrrse) #1
克隆策略
In [18]:
import numpy as np
import pandas as pd
import statsmodels.api as sm

stock_list1 = ['000001.SZA','000001.SZA','000001.SZA','000001.SZA','600015.SHA']
stock_list2 = ['601169.SHA','601009.SHA','002142.SZA','601288.SHA','601169.SHA']
# stock_list1 = ['000001.SZA']
# stock_list2 = ['601169.SHA']
stock_data = D.history_data(stock_list1+stock_list2,start_date='2015-12-01', end_date='2016-12-01',fields=['close'])

for i in range(len(stock_list1)):
    instrument1 = stock_data[stock_data['instrument'] == stock_list1[i]]
    instrument2 = stock_data[stock_data['instrument'] == stock_list2[i]]
    result = pd.merge(instrument1, instrument2, on='date')
    result.dropna(inplace=True)
    close1 = result.close_x
    close2 = result.close_y
    close2_1 = sm.add_constant(close2)
    print((sm.OLS(close1,close2_1)).fit().params)
    
k_list = [18.9165,12.3354,16.0155,254.5426,1.0471]
b_list = [528.8719,553.2580,589.4451,-114.3154,12.0623]
mean_list = []
std_list = []
upper_list = []
lower_list = []

for i in range(len(stock_list1)):
    instrument1 = stock_data[stock_data['instrument'] == stock_list1[i]]
    instrument2 = stock_data[stock_data['instrument'] == stock_list2[i]]
    result = pd.merge(instrument1, instrument2, on='date')
    result.dropna(inplace=True)
    close1 = result.close_x
    close2 = result.close_y
    s = close1-close2*k_list[i]
    mean_list.append(s.mean())
    std_list.append(s.std())
    upper_list.append(mean_list[i]+std_list[i])
    lower_list.append(mean_list[i]-std_list[i])
    # zscore = (s-mean_list[i])/std_list[i]
    T.plot(pd.DataFrame({'zscore':s, 'Mean':mean_list[i], 'upper':mean_list[i]+std_list[i], 'lower':mean_list[i]-std_list[i]}) ,chart_type='line', title='zscore')

test_zscore = []
test_stock_data = D.history_data(stock_list1+stock_list2,start_date='2015-12-01', end_date='2016-12-01',fields=['close'])
for i in range(len(stock_list1)):
    merge_stock = pd.merge(test_stock_data[test_stock_data['instrument'] == stock_list1[i]],test_stock_data[test_stock_data['instrument'] == stock_list2[i]],on='date')
    merge_stock.dropna(inplace=True)
    close1 = merge_stock.close_x
    close2 = merge_stock.close_y
    merge_stock['zscore'] = close1-close2*k_list[i]
    zscore_value = pd.DataFrame({'date':merge_stock['date'],'zscore':merge_stock['zscore']})
#     print((zscore_value[zscore_value['date']=='2017-01-12']['zscore'].tolist()[0]))
    
    
    test_zscore.append(zscore_value)
    
    

    
# A-B大于upper时,买入B;A-B小于lower时,买入A
def initialize(context):
    context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
    
def handle_data(context,data):
    date = data.current_dt.strftime('%Y-%m-%d')
    print(date)
    cash_for_buy = context.portfolio.cash
    zscore = []
    buy_list = []
    sell_list = []
    for i in range(len(stock_list1)):
        symbol_1 = context.symbol(stock_list1[i]) # 转换成回测引擎所需要的symbol格式
        symbol_2 = context.symbol(stock_list2[i])
        cur_position_1 = context.portfolio.positions[symbol_1].amount
        cur_position_2 = context.portfolio.positions[symbol_2].amount
        zscore_list = test_zscore[i]
        zscore = zscore_list[zscore_list['date']==date]['zscore'].tolist()[0]
        print(zscore)
        if zscore > upper_list[i] and cur_position_2 == 0 and data.can_trade(symbol_1) and data.can_trade(symbol_2):  
            buy_list.append(symbol_2)
            sell_list.append(symbol_1)

        
        # 如果zesore小于下轨(<-1),则价差会向上回归均值,因此需要买入股票y,卖出股票x
        elif zscore < lower_list[i] and cur_position_1 == 0 and data.can_trade(symbol_1) and data.can_trade(symbol_2):
            buy_list.append(symbol_1)
            sell_list.append(symbol_2)
    print(buy_list)
    print(sell_list)
    cash_for_buy = context.portfolio.cash
    # 卖出
    for instrument in sell_list:
        context.order_target_percent(instrument, 0)
    # 买入
    for instrument in buy_list:
        context.order_target_percent(instrument,1/len(buy_list))
        # context.order_value(instrument, cash_for_buy/len(buy_list))
        
m = M.trade.v3(
    instruments=stock_list1+stock_list2,
    start_date='2015-12-01',
    end_date='2016-12-01',
    initialize=initialize,
    handle_data=handle_data,
    order_price_field_buy='open',
    order_price_field_sell='open',
    capital_base=100000,
    benchmark='000300.SHA',
    m_cached =False,
)
        
const      528.871912
close_y     18.916511
dtype: float64
const      553.258097
close_y     12.335410
dtype: float64
const      589.445123
close_y     16.015529
dtype: float64
const     -114.315453
close_y    254.542679
dtype: float64
const      12.063572
close_y     1.047104
dtype: float64
[2017-12-18 14:50:00.240745] INFO: bigquant: backtest.v7 开始运行..
[2017-12-18 14:50:00.371509] INFO: algo: set price type:PriceType.post_right
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-18-86ee1e264096> in <module>()
    107     capital_base=100000,
    108     benchmark='000300.SHA',
--> 109     m_cached =False,
    110 )
    111 

ValueError: Duplicate entries in Panel.items: [0, 3, 3, 3].

配对交易策略,只有一对股票时不会报错,加入多对股票就会报如下错误:
ValueError: Duplicate entries in Panel.items: [0, 3, 3, 3].
请问是什么原因?该如何解决?@iQuant


(iQuant) #2

已解决,这个策略看起来还不错呢。
出错的原因是stock_list1+stock_list2 这样拼接起来的证券代码列表有重合的。

克隆策略
In [27]:
import numpy as np
import pandas as pd
import statsmodels.api as sm

stock_list1 = ['000001.SZA','000001.SZA','000001.SZA','000001.SZA','600015.SHA']
stock_list2 = ['601169.SHA','601009.SHA','002142.SZA','601288.SHA','601169.SHA']
 
stock_data = D.history_data(stock_list1+stock_list2,start_date='2015-12-01', end_date='2016-12-01',fields=['close'])

for i in range(len(stock_list1)):
    instrument1 = stock_data[stock_data['instrument'] == stock_list1[i]]
    instrument2 = stock_data[stock_data['instrument'] == stock_list2[i]]
    result = pd.merge(instrument1, instrument2, on='date')
    result.dropna(inplace=True)
    close1 = result.close_x
    close2 = result.close_y
    close2_1 = sm.add_constant(close2)
    print((sm.OLS(close1,close2_1)).fit().params)
    
k_list = [18.9165,12.3354,16.0155,254.5426,1.0471]
b_list = [528.8719,553.2580,589.4451,-114.3154,12.0623]
mean_list = []
std_list = []
upper_list = []
lower_list = []

for i in range(len(stock_list1)):
    instrument1 = stock_data[stock_data['instrument'] == stock_list1[i]]
    instrument2 = stock_data[stock_data['instrument'] == stock_list2[i]]
    result = pd.merge(instrument1, instrument2, on='date')
    result.dropna(inplace=True)
    close1 = result.close_x
    close2 = result.close_y
    s = close1-close2*k_list[i]
    mean_list.append(s.mean())
    std_list.append(s.std())
    upper_list.append(mean_list[i]+std_list[i])
    lower_list.append(mean_list[i]-std_list[i])
    # zscore = (s-mean_list[i])/std_list[i]
#     T.plot(pd.DataFrame({'zscore':s, 'Mean':mean_list[i], 'upper':mean_list[i]+std_list[i], 'lower':mean_list[i]-std_list[i]}) ,chart_type='line', title='zscore')

test_zscore = []
test_stock_data = D.history_data(stock_list1+stock_list2,start_date='2015-12-01', end_date='2016-12-01',fields=['close'])
for i in range(len(stock_list1)):
    merge_stock = pd.merge(test_stock_data[test_stock_data['instrument'] == stock_list1[i]],test_stock_data[test_stock_data['instrument'] == stock_list2[i]],on='date')
    merge_stock.dropna(inplace=True)
    close1 = merge_stock.close_x
    close2 = merge_stock.close_y
    merge_stock['zscore'] = close1-close2*k_list[i]
    zscore_value = pd.DataFrame({'date':merge_stock['date'],'zscore':merge_stock['zscore']})
    test_zscore.append(zscore_value)
const      528.871912
close_y     18.916511
dtype: float64
const      553.258097
close_y     12.335410
dtype: float64
const      589.445123
close_y     16.015529
dtype: float64
const     -114.315453
close_y    254.542679
dtype: float64
const      12.063572
close_y     1.047104
dtype: float64
In [28]:
# A-B大于upper时,买入B;A-B小于lower时,买入A
def initialize(context):
    context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
    
def handle_data(context,data):
    date = data.current_dt.strftime('%Y-%m-%d')
    zscore = []
    buy_list = []
    sell_list = []
    for i in range(len(stock_list1)):
        symbol_1 = context.symbol(stock_list1[i]) # 转换成回测引擎所需要的symbol格式
        symbol_2 = context.symbol(stock_list2[i])
        cur_position_1 = context.portfolio.positions[symbol_1].amount
        cur_position_2 = context.portfolio.positions[symbol_2].amount
        zscore_list = test_zscore[i] # 获取该股票对的z分数据
        zscore = zscore_list[zscore_list['date']==date]['zscore'].tolist()[0]
     
        if zscore > upper_list[i] and cur_position_2 == 0 and data.can_trade(symbol_1) and data.can_trade(symbol_2):  
            buy_list.append(symbol_2)
            sell_list.append(symbol_1)

        
        # 如果zesore小于下轨(<-1),则价差会向上回归均值,因此需要买入股票y,卖出股票x
        elif zscore < lower_list[i] and cur_position_1 == 0 and data.can_trade(symbol_1) and data.can_trade(symbol_2):
            buy_list.append(symbol_1)
            sell_list.append(symbol_2)
            
    cash_for_buy = context.portfolio.cash
    # 卖出
    for instrument in sell_list:
        context.order_target_percent(instrument, 0)
    # 买入
    for instrument in buy_list:
        context.order_target_percent(instrument,1/len(stock_list1))
       
        
m = M.trade.v3(
    instruments=list(set(list(stock_list1+stock_list2))), #主要修改此处
    start_date='2015-12-01',
    end_date='2016-12-01',
    initialize=initialize,
    handle_data=handle_data,
    order_price_field_buy='open',
    order_price_field_sell='open',
    capital_base=1000000,
    benchmark='000300.SHA',
    volume_limit=0,
)
[2017-12-21 19:50:56.398543] INFO: bigquant: backtest.v7 开始运行..
[2017-12-21 19:50:56.557275] INFO: algo: set price type:PriceType.post_right
[2017-12-21 19:51:02.387283] INFO: Performance: Simulated 246 trading days out of 246.
[2017-12-21 19:51:02.389174] INFO: Performance: first open: 2015-12-01 01:30:00+00:00
[2017-12-21 19:51:02.390317] INFO: Performance: last close: 2016-12-01 07:00:00+00:00
  • 收益率24.43%
  • 年化收益率25.09%
  • 基准收益率-0.04%
  • 阿尔法0.24
  • 贝塔0.76
  • 夏普比率1.04
  • 收益波动率21.14%
  • 信息比率1.92
  • 最大回撤18.67%
[2017-12-21 19:51:03.698991] INFO: bigquant: backtest.v7 运行完成[7.300467s].


(horrrrrse) #3

非常感谢!!