返回介绍

第一部分 新手入门

第二部分 股票量化相关

第三部分 基金、利率互换、固定收益类

第四部分 衍生品相关

Paired trading

发布于 2022-02-20 22:26:14 字数 21541 浏览 814 评论 0 收藏 0

配对交易

策略思路

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

策略实现

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

from scipy.stats.stats import pearsonr

start = datetime(2013, 1, 1)
end   = datetime(2014, 12, 1)
benchmark = 'HS300'
universe = ['000559.XSHE', '600126.XSHG']
capital_base = 1e6 

corlen = 5

def initialize(account):
    add_history('hist', corlen)
    account.cutoff = 0.9
    account.prev_prc1 = 0
    account.prev_prc2 = 0
    account.prev_prcb = 0

def handle_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 / prc2
    if (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 / prc1
    if (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 = prcb


def dmv(curr, prev):
    delta = curr / prev - 1
    return delta

def adj(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) - dhs
    return (dx, dy)

min(bt.cash)

232096.85369499651
import pandas as pd
import numpy as np
from datetime import datetime

import quartz
import quartz.backtest as qb
import quartz.performance as qp
from quartz.api import *

from scipy.stats.stats import pearsonr

start = datetime(2013, 1, 1)                # 回测起始时间
end   = datetime(2014, 12, 1)                # 回测结束时间
benchmark = 'HS300'                            # 使用沪深 300 作为参考标准
capital_base = 1e6                          # 起始资金

corlen = 5

def initialize(account):                    # 初始化虚拟账户状态
    add_history('hist', corlen)
    account.cutoff = 0.9
    account.prev_prc1 = 0
    account.prev_prc2 = 0
    account.prev_prcb = 0

def handle_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 / prc2
    if (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 / prc1
    if (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 = prcb


def dmv(curr, prev):
    delta = curr / prev - 1
    return delta

def adj(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) - dhs
    return (dx, dy)


pool_raw = pd.read_csv("po.pair.2012.csv")
pool = []
for i in range(len(pool_raw)):
    s1, s2 = pool_raw.loc[i].tolist()
    if [s2, s1] not in pool:
        pool.append([s1, s2])

outfile = []
for i, universe in enumerate(pool):
    print i
    try:
        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,k in 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([x for x in a if x>0.117]) / len(a))

'percentage of outperform HS300: 0.741379'

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文