Paired trading

来源:https://uqer.io/community/share/54895a8df9f06c31c3950ca0

配对交易

策略思路

寻找走势相关且股价相近的一对股票,根据其价格变动买卖

策略实现

历史前五日的Pearson相关系数若大于给定的阈值则触发买卖操作

from scipy.stats.statsimport pearsonrstart = datetime(2013,1,1)end   = datetime(2014,12,1)benchmark ='HS300'universe = ['000559.XSHE','600126.XSHG']capital_base =1e6 corlen =5definitialize(account):    add_history('hist', corlen)    account.cutoff =0.9    account.prev_prc1 =0    account.prev_prc2 =0    account.prev_prcb =0defhandle_data(account, data):    stk1 = universe[0]    stk2 = universe[1]    prc1 = data[stk1]['closePrice']    prc2 = data[stk2]['closePrice']    prcb = data['HS300']['return']    px1 = account.hist[stk1]['closePrice'].values    px2 = account.hist[stk2]['closePrice'].values    pxb = account.hist['HS300']['return'].values    corval, pval = pearsonr(px1, px2)    mov1, mov2 = adj(prc1, prc2, prcb, account.prev_prc1, account.prev_prc2, account.prev_prcb)    amount =1e4 / prc2if (mov1 >0)and (abs(corval) > account.cutoff):        order(stk2, amount)elif (mov1 <0)and (abs(corval) > account.cutoff):if (account.position.stkpos.get(stk2,0) > amount):            order(stk2, -amount)else:            order_to(stk2,0)    amount =1e4 / prc1if (mov2 >0)and (abs(corval) > account.cutoff):        order(stk1, amount)elif (mov2 <0)and (abs(corval) > account.cutoff):if (account.position.stkpos.get(stk1,0) > amount):            order(stk1, -amount)else:            order_to(stk1,0)    account.prev_prc1 = prc1    account.prev_prc2 = prc2    account.prev_prcb = prcbdefdmv(curr, prev):    delta = curr / prev -1return deltadefadj(x, y, base, prev_x, prev_y, prev_base):    dhs = dmv(base, prev_base)    dx = dmv(x, prev_x) - dhs    dy = dmv(y, prev_y) - dhsreturn (dx, dy)

min(bt.cash)232096.85369499651
import pandasas pdimport numpyas npfrom datetimeimport datetimeimport quartzimport quartz.backtestas qbimport quartz.performanceas qpfrom quartz.apiimport *from scipy.stats.statsimport pearsonrstart = datetime(2013,1,1)# 回测起始时间end   = datetime(2014,12,1)# 回测结束时间benchmark ='HS300'# 使用沪深 300 作为参考标准capital_base =1e6# 起始资金corlen =5definitialize(account):# 初始化虚拟账户状态    add_history('hist', corlen)    account.cutoff =0.9    account.prev_prc1 =0    account.prev_prc2 =0    account.prev_prcb =0defhandle_data(account, data):# 每个交易日的买入卖出指令    stk1 = universe[0]    stk2 = universe[1]    prc1 = data[stk1]['closePrice']    prc2 = data[stk2]['closePrice']    prcb = data['HS300']['return']    px1 = account.hist[stk1]['closePrice'].values    px2 = account.hist[stk2]['closePrice'].values    pxb = account.hist['HS300']['return'].values    corval, pval = pearsonr(px1, px2)    mov1, mov2 = adj(prc1, prc2, prcb, account.prev_prc1, account.prev_prc2, account.prev_prcb)#amount = int( 0.08 * capital_base / prc2)    amount =1e4 / prc2if (mov1 >0)and (abs(corval) > account.cutoff):        order(stk2, amount)elif (mov1 <0)and (abs(corval) > account.cutoff):if (account.position.stkpos.get(stk2,0) > amount):            order(stk2, -amount)else:            order_to(stk2,0)#amount = int(0.08 * capital_base / prc1)    amount =1e4 / prc1if (mov2 >0)and (abs(corval) > account.cutoff):        order(stk1, amount)elif (mov2 <0)and (abs(corval) > account.cutoff):if (account.position.stkpos.get(stk1,0) > amount):            order(stk1, -amount)else:            order_to(stk1,0)    account.prev_prc1 = prc1    account.prev_prc2 = prc2    account.prev_prcb = prcbdefdmv(curr, prev):    delta = curr / prev -1return deltadefadj(x, y, base, prev_x, prev_y, prev_base):    dhs = dmv(base, prev_base)    dx = dmv(x, prev_x) - dhs    dy = dmv(y, prev_y) - dhsreturn (dx, dy)pool_raw = pd.read_csv("po.pair.2012.csv")pool = []for iin range(len(pool_raw)):    s1, s2 = pool_raw.loc[i].tolist()if [s2, s1]notin pool:        pool.append([s1, s2])outfile = []for i, universein enumerate(pool):print itry:        bt = qb.backtest(start, end, benchmark, universe, capital_base, initialize = initialize, handle_data = handle_data)        perf = qp.perf_parse(bt)        outfile.append(universe + [perf["annualized_return"], perf["sharpe"]])except:pass    keys = ['stock1','stock2','annualized_return','sharpe']outdict = {}outfile =  zip(*sorted(outfile, key=lambda x:x[2], reverse=True))for i,kin enumerate(keys):    outdict[k] = outfile[i]outdict = pd.DataFrame(outdict).loc[:, keys]outdict['000066.XSHE','000707.XSHE']['000066.XSHE','600117.XSHG']['000066.XSHE','600126.XSHG']['000066.XSHE','600819.XSHG']['000089.XSHE','600035.XSHG']['000089.XSHE','600037.XSHG']['000089.XSHE','600595.XSHG']['000159.XSHE','000967.XSHE']['000159.XSHE','600595.XSHG']['000417.XSHE','000541.XSHE']['000417.XSHE','000685.XSHE']['000417.XSHE','600875.XSHG']['000425.XSHE','000528.XSHE']['000507.XSHE','600391.XSHG']['000541.XSHE','000987.XSHE']['000541.XSHE','600330.XSHG']['000541.XSHE','600883.XSHG']['000554.XSHE','000707.XSHE']['000559.XSHE','600026.XSHG']['000559.XSHE','600126.XSHG']['000559.XSHE','600477.XSHG']['000559.XSHE','600581.XSHG']['000559.XSHE','601666.XSHG']['000635.XSHE','000707.XSHE']['000635.XSHE','600068.XSHG']['000635.XSHE','600117.XSHG']['000635.XSHE','600188.XSHG']['000635.XSHE','600295.XSHG']['000635.XSHE','600550.XSHG']['000635.XSHE','600819.XSHG']['000635.XSHE','601168.XSHG']['000635.XSHE','601233.XSHG']['000650.XSHE','600261.XSHG']['000683.XSHE','000936.XSHE']['000683.XSHE','600595.XSHG']['000685.XSHE','000988.XSHE']['000685.XSHE','601101.XSHG']['000698.XSHE','000949.XSHE']['000707.XSHE','000911.XSHE']['000707.XSHE','000969.XSHE']['000707.XSHE','000987.XSHE']['000707.XSHE','600117.XSHG']['000707.XSHE','600295.XSHG']['000707.XSHE','600550.XSHG']['000707.XSHE','600831.XSHG']['000707.XSHE','601168.XSHG']['000707.XSHE','601233.XSHG']['000708.XSHE','600327.XSHG']['000709.XSHE','601107.XSHG']['000709.XSHE','601618.XSHG']['000717.XSHE','600282.XSHG']['000717.XSHE','600307.XSHG']['000717.XSHE','600808.XSHG']['000761.XSHE','600320.XSHG']['000761.XSHE','600548.XSHG']['000822.XSHE','600117.XSHG']['000830.XSHE','600068.XSHG']['000830.XSHE','600320.XSHG']['000830.XSHE','600550.XSHG']['000877.XSHE','601519.XSHG']['000898.XSHE','600022.XSHG']['000898.XSHE','600808.XSHG']['000911.XSHE','600550.XSHG']['000916.XSHE','600033.XSHG']['000916.XSHE','600035.XSHG']['000916.XSHE','600126.XSHG']['000930.XSHE','600026.XSHG']['000932.XSHE','600569.XSHG']['000933.XSHE','600348.XSHG']['000933.XSHE','600595.XSHG']['000936.XSHE','600477.XSHG']['000937.XSHE','600348.XSHG']['000937.XSHE','600508.XSHG']['000937.XSHE','600997.XSHG']['000937.XSHE','601001.XSHG']['000939.XSHE','600819.XSHG']['000967.XSHE','600879.XSHG']['000969.XSHE','600831.XSHG']['000973.XSHE','600460.XSHG']['000987.XSHE','600636.XSHG']['000987.XSHE','600827.XSHG']['000987.XSHE','601001.XSHG']['600008.XSHG','600035.XSHG']['600012.XSHG','600428.XSHG']['600020.XSHG','600033.XSHG']['600020.XSHG','600035.XSHG']['600026.XSHG','600068.XSHG']['600026.XSHG','600089.XSHG']['600026.XSHG','600126.XSHG']['600026.XSHG','600307.XSHG']['600026.XSHG','600331.XSHG']['600026.XSHG','600375.XSHG']['600026.XSHG','600581.XSHG']['600026.XSHG','600963.XSHG']['600026.XSHG','601666.XSHG']['600026.XSHG','601898.XSHG']['600033.XSHG','600035.XSHG']['600035.XSHG','600126.XSHG']['600035.XSHG','600269.XSHG']['600035.XSHG','600307.XSHG']['600035.XSHG','600586.XSHG']['600037.XSHG','600327.XSHG']['600068.XSHG','600126.XSHG']['600068.XSHG','600269.XSHG']['600068.XSHG','600320.XSHG']['600068.XSHG','600550.XSHG']['600068.XSHG','601001.XSHG']['600068.XSHG','601666.XSHG']['600089.XSHG','600581.XSHG']['600100.XSHG','600117.XSHG']['600117.XSHG','600295.XSHG']['600117.XSHG','600339.XSHG']['600117.XSHG','601168.XSHG']['600117.XSHG','601233.XSHG']['600126.XSHG','600282.XSHG']['600126.XSHG','600327.XSHG']['600126.XSHG','600569.XSHG']['600126.XSHG','600581.XSHG']['600126.XSHG','600808.XSHG']['600126.XSHG','600963.XSHG']['600160.XSHG','600449.XSHG']['600160.XSHG','601216.XSHG']['600160.XSHG','601311.XSHG']['600188.XSHG','600295.XSHG']['600188.XSHG','601001.XSHG']['600231.XSHG','600282.XSHG']['600269.XSHG','601618.XSHG']['600282.XSHG','600307.XSHG']['600282.XSHG','600569.XSHG']['600282.XSHG','600808.XSHG']['600282.XSHG','600963.XSHG']['600307.XSHG','600581.XSHG']['600307.XSHG','600808.XSHG']['600307.XSHG','600963.XSHG']['600320.XSHG','600548.XSHG']['600320.XSHG','601600.XSHG']['600330.XSHG','600883.XSHG']['600330.XSHG','601268.XSHG']['600331.XSHG','600581.XSHG']['600348.XSHG','600508.XSHG']['600348.XSHG','600997.XSHG']['600348.XSHG','601001.XSHG']['600368.XSHG','600527.XSHG']['600375.XSHG','600581.XSHG']['600391.XSHG','601100.XSHG']['600449.XSHG','601311.XSHG']['600449.XSHG','601519.XSHG']['600460.XSHG','601908.XSHG']['600477.XSHG','600581.XSHG']['600508.XSHG','600546.XSHG']['600508.XSHG','600997.XSHG']['600522.XSHG','600973.XSHG']['600550.XSHG','600831.XSHG']['600569.XSHG','600808.XSHG']['600569.XSHG','600963.XSHG']['600581.XSHG','600963.XSHG']['600581.XSHG','601001.XSHG']['600581.XSHG','601168.XSHG']['600581.XSHG','601666.XSHG']['600586.XSHG','601268.XSHG']['600595.XSHG','601001.XSHG']['600595.XSHG','601168.XSHG']['600595.XSHG','601666.XSHG']['600688.XSHG','600871.XSHG']['600785.XSHG','600827.XSHG']['600808.XSHG','600963.XSHG']['600827.XSHG','601001.XSHG']['600875.XSHG','601001.XSHG']['600883.XSHG','601268.XSHG']['601001.XSHG','601101.XSHG']['601001.XSHG','601168.XSHG']['601001.XSHG','601666.XSHG']['601101.XSHG','601666.XSHG']['601168.XSHG','601666.XSHG']
stock1stock2annualized_returnsharpe
0000761.XSHE600548.XSHG0.4894732.411514
1000708.XSHE600327.XSHG0.4473372.021270
2600126.XSHG600327.XSHG0.4383801.946916
3000554.XSHE000707.XSHE0.4311231.331038
4000939.XSHE600819.XSHG0.4094711.919758
5600026.XSHG600963.XSHG0.4087911.681338
6600037.XSHG600327.XSHG0.3956241.691877
7600808.XSHG600963.XSHG0.3919881.724114
8000559.XSHE600126.XSHG0.3890431.413595
9000761.XSHE600320.XSHG0.3843251.807262
10600126.XSHG600963.XSHG0.3780641.662569
11600126.XSHG600808.XSHG0.3758251.513791
12000936.XSHE600477.XSHG0.3751351.707097
13000930.XSHE600026.XSHG0.3729241.524350
14600320.XSHG600548.XSHG0.3724992.083496
15000507.XSHE600391.XSHG0.3656371.813873
16000559.XSHE601666.XSHG0.3502350.925901
17600012.XSHG600428.XSHG0.3278341.722317
18000916.XSHE600033.XSHG0.3277951.406093
19600035.XSHG600126.XSHG0.3261671.442674
20600827.XSHG601001.XSHG0.3227050.957791
21000717.XSHE600808.XSHG0.3207371.293439
22000559.XSHE600477.XSHG0.3066701.218095
23000685.XSHE000988.XSHE0.3025931.692933
24000683.XSHE000936.XSHE0.3018041.550496
25000559.XSHE600026.XSHG0.2955101.279449
26600269.XSHG601618.XSHG0.2942151.486413
27600026.XSHG600126.XSHG0.2938841.441490
28600068.XSHG600126.XSHG0.2894571.261351
29000159.XSHE600595.XSHG0.2889820.946365
30600020.XSHG600033.XSHG0.2882431.489764
31600126.XSHG600569.XSHG0.2876071.371374
32000635.XSHE600819.XSHG0.2851351.364688
33600068.XSHG600320.XSHG0.2735131.262845
34600785.XSHG600827.XSHG0.2726580.842093
35000089.XSHE600595.XSHG0.2699031.256524
36000898.XSHE600808.XSHG0.2697171.074201
37000717.XSHE600282.XSHG0.2674781.270872
38600282.XSHG600808.XSHG0.2664021.181157
39000916.XSHE600035.XSHG0.2643251.079520
40000089.XSHE600037.XSHG0.2642011.467101
41600026.XSHG600068.XSHG0.2639591.107977
42600026.XSHG600331.XSHG0.2610250.977858
43600020.XSHG600035.XSHG0.2601761.119975
44600569.XSHG600963.XSHG0.2600061.154372
45600307.XSHG600963.XSHG0.2584881.322409
46000898.XSHE600022.XSHG0.2582461.100292
47600282.XSHG600963.XSHG0.2574961.175741
48600307.XSHG600808.XSHG0.2560711.062023
49600126.XSHG600282.XSHG0.2556571.318676
50600033.XSHG600035.XSHG0.2556341.055682
51000709.XSHE601618.XSHG0.2531291.062565
52600026.XSHG600307.XSHG0.2531190.985825
53600026.XSHG600375.XSHG0.2507931.063874
54000066.XSHE600126.XSHG0.2474931.469341
55000830.XSHE600320.XSHG0.2470011.370327
56600320.XSHG601600.XSHG0.2465340.966634
57000717.XSHE600307.XSHG0.2458051.202750
58000417.XSHE000685.XSHE0.2450311.189700
59600330.XSHG600883.XSHG0.2434371.086147
............
174 rows × 4 columns
a = list(outfile[2])'percentage of outperform HS300: %f' % (1.*len([xfor xin aif x>0.117]) / len(a))'percentage of outperform HS300: 0.741379'

results matching ""

    No results matching ""