R语言构建配对交易量化模型

用IT技术玩金融系列文章,将介绍如何使用IT技术,处理金融大数据。在互联网混迹多年,已经熟练掌握一些IT技术。单纯地在互联网做开发,总觉得使劲的方式不对。要想靠技术养活自己,就要把技术变现。通过“跨界”可以寻找新的机会,创造技术的壁垒。

金融是离钱最近的市场,也是变现的好渠道!今天就开始踏上“用IT技术玩金融”之旅!

关于作者:

  • 张丹(Conan), 程序员Java,R,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: bsspirit@gmail.com

转载请注明出处:
http://blog.fens.me/finance-pairs-trading/

pair-trading

前言

散户每天都在经历中国股市的上蹿下跳,赚到钱是运气,赔钱是常态。那么是否有方法可以让赚钱变成常态呢?

我们可以通过“统计套利”的方法,发现市场的无效性。配对交易,就统计套利策略的一种,通过对冲掉绝大部分的市场风险,抓住套利机会,积累小盈利汇聚大收益。

目录

  1. 什么是配对交易?
  2. 配对交易的模型
  3. 用R语言实现配对交易

1. 什么是配对交易?

配对交易(Pairs Trading)的理念最早来源于上世纪20年代华尔街传奇交易员Jesse Livermore 的姐妹股票对交易策略。配对交易的基本原理是找到两个相关性较高具备均衡关系的股票或其他金融产品,做空近期相对强势的金融产品,同时做多相对弱势金融产品,等待两者价格重返均衡值时进行平仓,赚取两者的价差变动的收益。

假设两个金融产品在未来的时期会保持良好的均衡关系,一旦两者之间的价格走势出现背离,同时这种背离在未来会被进行修复,那么就可能产生套利的机会。对于配对交易来说,就是找到这样的机会,进行统计套利。

配对交易的特点

配对交易与传统股票交易最大的不同之处在于,它的投资标的是两只股票的价差,是一种相对价值而非绝对价值。由于它在股票多头和空头方同时建仓,对冲掉了绝大部分的市场风险,所以它是一种市场的中性策略。无论大盘上涨还是下跌,配对交易策略收益都是相对平稳的,与大盘走势的相关性很低。

在市场无趋势性机会时,可以通过配对交易避免股市系统风险,获取Alpha绝对收益。趋势性的交易策略,可以参考文章 两条均线打天下

配对交易操作方法

  1. 组合筛选:在市场上寻找用于配对的金融产品或者组合,检查历史价格的走势,判断是否可以用来进行配对。主要用下面几个指标来筛选配对组合:相关系数、模型计算的均值回复速度、协整检验、基本面因素等。通过这些因素来寻找出具有稳定相关关系的组合。
  2. 风险衡量和动态组合的构建:计算配对组合各自的预期收益、预期风险、交易成本;判断两个组合之间的价差服从何种分布;判断是具有长期均衡特性还是短期均衡特性;价差发生跳跃的频率等。
  3. 确定交易规则:根据价差的特性,确定交易的频率(高频交易还是低频交易),交易的触发条件和平仓规则等。
  4. 执行交易及风险控制:除了按照交易规则执行外,还必须动态跟踪价差走势,如果发现突变,应该及时调整套利模式和交易频率。

配对交易缺点

  • 统计套利的规则都是基于历史数据计算的,但历史不能代表未来,当市场发生变化模型也会失效
  • 市场对价格进行修复的时间难以准确判断,只能根据历史大致估计。如果回归的时间过长,对套利者的资金使用成本是个考验,也有可能导致套利失败。

2. 构建配对交易的模型

根据配对交易的原理,我们就可以自己设计配对交易的模型了。首先,需要把配对交易涉及的指标都进行量化,比如如何选择不同的两个具备均衡关系金融产品,什么时候做多,什么时候做空,什么时候平仓等。

根据概念,我们生成两个虚拟的金融产品X,Y,包括时间和价格字段。让X和Y的两个产品都价格符合正态分布,生成100个日期的数据。由于是测试程序,日期字段是包括了自然日,暂时理解为连续的日期。

R语言实现的代码如下:


> set.seed(1)                         #设置随机种子
> dates<-as.Date('2010-01-01')+1:100  #100个日期
> x<-round(rnorm(100,50,40),2)        #随机生成X产品,100个正态分析的收盘价 
> y<-round(rnorm(100,50,40),2)        #随机生成Y产品,100个正态分析的收盘价 
> df<-data.frame(dates,x,y)
> df
         dates      x      y
1   2010-01-02  24.94  25.19
2   2010-01-03  57.35  51.68
3   2010-01-04  16.57  13.56
4   2010-01-05 113.81  56.32
5   2010-01-06  63.18  23.82
6   2010-01-07  17.18 120.69
7   2010-01-08  69.50  78.67
8   2010-01-09  79.53  86.41
9   2010-01-10  73.03  65.37
10  2010-01-11  37.78 117.29
11  2010-01-12 110.47  24.57
12  2010-01-13  65.59  31.53
13  2010-01-14  25.15 107.29
14  2010-01-15 -38.59  23.97
15  2010-01-16  95.00  41.70
16  2010-01-17  48.20  34.29
17  2010-01-18  49.35  37.20
18  2010-01-19  87.75  38.84
19  2010-01-20  82.85  69.77
20  2010-01-21  73.76  42.91
21  2010-01-22  86.76  29.76
22  2010-01-23  81.29 103.72
23  2010-01-24  52.98  41.42
24  2010-01-25 -29.57  42.82
25  2010-01-26  74.79  45.99
26  2010-01-27  47.75  78.51
27  2010-01-28  43.77  47.06
28  2010-01-29  -8.83  48.49
29  2010-01-30  30.87  22.73
30  2010-01-31  66.72  37.03
31  2010-02-01 104.35  52.41
32  2010-02-02  45.89  26.44
33  2010-02-03  65.51  71.26
34  2010-02-04  47.85 -10.74
35  2010-02-05  -5.08  62.26
36  2010-02-06  33.40 -11.46
37  2010-02-07  34.23  37.96
38  2010-02-08  47.63  28.87
39  2010-02-09  94.00  23.92
40  2010-02-10  80.53  47.72
41  2010-02-11  43.42 -26.57
42  2010-02-12  39.87  97.06
43  2010-02-13  77.88 -16.60
44  2010-02-14  72.27  31.46
45  2010-02-15  22.45   5.36
46  2010-02-16  21.70  19.97
47  2010-02-17  64.58 133.49
48  2010-02-18  80.74  50.70
49  2010-02-19  45.51  -1.45
50  2010-02-20  85.24 -15.62
51  2010-02-21  65.92  68.01
52  2010-02-22  25.52  49.26
53  2010-02-23  63.64  37.28
54  2010-02-24   4.83  12.83
55  2010-02-25 107.32  -9.50
56  2010-02-26 129.22   6.99
57  2010-02-27  35.31  90.00
58  2010-02-28   8.23  25.15
59  2010-03-01  72.79  -5.38
60  2010-03-02  44.60 124.77
61  2010-03-03 146.06  67.00
62  2010-03-04  48.43  40.45
63  2010-03-05  77.59  92.34
64  2010-03-06  51.12  85.46
65  2010-03-07  20.27  25.23
66  2010-03-08  57.55 138.24
67  2010-03-09 -22.20  39.80
68  2010-03-10 108.62  -6.98
69  2010-03-11  56.13  44.22
70  2010-03-12 136.90  58.30
71  2010-03-13  69.02 142.32
72  2010-03-14  21.60  54.23
73  2010-03-15  74.43  68.28
74  2010-03-16  12.64  46.91
75  2010-03-17  -0.15  36.64
76  2010-03-18  61.66  48.61
77  2010-03-19  32.27  81.51
78  2010-03-20  50.04 133.01
79  2010-03-21  52.97  91.10
80  2010-03-22  26.42  98.32
81  2010-03-23  27.25   0.75
82  2010-03-24  44.59  89.36
83  2010-03-25  97.12  58.80
84  2010-03-26 -10.94  -8.69
85  2010-03-27  73.76  70.84
86  2010-03-28  63.32  43.65
87  2010-03-29  92.52 108.58
88  2010-03-30  37.83  19.36
89  2010-03-31  64.80  32.79
90  2010-04-01  60.68  12.96
91  2010-04-02  28.30  42.92
92  2010-04-03  98.31  66.08
93  2010-04-04  96.42  20.73
94  2010-04-05  78.01  83.21
95  2010-04-06 113.47   1.68
96  2010-04-07  72.34   8.08
97  2010-04-08  -1.06 107.65
98  2010-04-09  27.07   9.37
99  2010-04-10   1.02  66.48
100 2010-04-11  31.06  34.76

把数据进行可视化,可以更直观地理解数据本身。


# 加载R语言类库
> library(ggplot2)
> library(scales)
> library(reshape2)

# 数据转型 
> df2<-melt(df,c('dates'))

# 画图
> g<-ggplot(data=df2,aes(x=dates,y=value,colour=variable))
> g<-g+geom_line()
> g<-g+scale_x_date(date_breaks = "1 week",date_labels='%m-%d')
> g<-g+labs(x='date',y='Price')
> g

01

上图中,X轴为时间,Y轴是价格,红色线为X的产品的价格,蓝色线为Y产品的价格。我们可以直观的看出,X,Y两个产品无任何关系。

根据配对交易的假设条件,如果两个金融产品的价差是收敛的。我们用X的产品价格减去Y产品的价格,当差值为正的时候,我们认为X的价格过高,则做空X,同时Y的价格过低,则做多Y;当差值为负的时候,我们认为X的价格过低,则做多X,同时Y的价格过高,则做空Y;当差值为0时,则价格被市场所修复,则全部平仓。

为了让差异更明显,我们定义的计算公式如下。


价差Z = X价格-Y价格
Z >  10时,做空X,做多Y ;Z<0时,平仓
Z < -10时,做多X,做空Y ;Z>0时,平仓

计算差价,然后计算交易统计。


# 计算差价
> df$diff<-df$x-df$y

# 找到差价大于10时的点
> idx<-which(df$diff>10)
> idx<-idx[-which(diff(idx)==1)-1]

# 打印差价的索引值
> idx
 [1]  4 11 15 23 25 30 34 36 38 43 48 53 55 59 61 68 76 81 83 86 88 92 95 98

接下来,我们进行模拟交易,取第一个索引值的点,在2010-01-04时做空X,做多Y。当差价小于0在2010-01-06时,进行平仓。


# 打印前20个数据
> head(df,20)
        dates      x      y    diff
1  2010-01-02  24.94  25.19   -0.25
2  2010-01-03  57.35  51.68    5.67
3  2010-01-04  16.57  13.56    3.01
4  2010-01-05 113.81  56.32   57.49
5  2010-01-06  63.18  23.82   39.36
6  2010-01-07  17.18 120.69 -103.51
7  2010-01-08  69.50  78.67   -9.17
8  2010-01-09  79.53  86.41   -6.88
9  2010-01-10  73.03  65.37    7.66
10 2010-01-11  37.78 117.29  -79.51
11 2010-01-12 110.47  24.57   85.90
12 2010-01-13  65.59  31.53   34.06
13 2010-01-14  25.15 107.29  -82.14
14 2010-01-15 -38.59  23.97  -62.56
15 2010-01-16  95.00  41.70   53.30
16 2010-01-17  48.20  34.29   13.91
17 2010-01-18  49.35  37.20   12.15
18 2010-01-19  87.75  38.84   48.91
19 2010-01-20  82.85  69.77   13.08
20 2010-01-21  73.76  42.91   30.85

# 当差价大于10时,做空X,当差价小于0时,平仓。
# 第4行做空,第6行平仓
> xprofit<- df$x[4]-df$x[6];xprofit
[1] 96.63

# 当差价大于10时,做多Y;当差价小于0时,平仓。
# 第4行做空,第6行平仓
> yprofit<- df$y[6]-df$y[4];yprofit
[1] 64.37

从交易结果来看,我们第一笔配对交易就是赚钱的。

这是为什么呢?

根据配对交易的假设条件,如果两个金融产品的价差是收敛的,通过协整性检验的方法,我们可验证数据的收敛性。那么如果数据是收敛的,他还会具备均值回归的特性,请参考文章 均值回归,逆市中的投资机会

画出X,Y的价差图,我们可以明显的看出,价差一直围绕着0上下波动,这是明显收敛的,同时符合均值回归的特性。


> plot(df$diff,type='l')

02

这就是市场的规则,通过配对交易的方法,我们找到市场无效性,从而可以赚去套利的收益。

3. 用R语言实现配对交易

看到上面的赚钱方法,也许大家会很兴奋!但是大部分市场的数据,都不会像我们的假设条件一样,轻而易举就能实现赚钱的目标。我们可以用计算机程序进行全市场的扫描发现交易机会,当然你也可以通过肉眼的方式来观察。

市场上有一些天生就具备均衡关系的金融产品,可以作为我们套利的入手对象。

  • 股票类,同行业、市值和基本面相似的个股,比如,中国银行(601988)和农业银行(601288)。
  • 基金类,以相同指数作为标的的不同基金,比如,证券B(150172),券商B(150201)。
  • 期货类,同一期货品种的不同合约,比如,铜(cu1605, cu1606)。
  • 混合类,跨市场为标的的金融产品,比如,沪深300指数,IF的期货合约

接下来,以相同品种不同合约的期货为例,我们把配对交易用在cu1605和cu1606的两个合约上,试试效果如何。由于期货是支持的T+0日内的交易的,而对于套利的操作,通常都不会持仓过夜,所以我们在尽量的短周期上进行操作,而且日内平仓。下面我将以1分钟做为交易周期。

3.1 数据准备

R语言本身提供了丰富的金融函数工具包,时间序列包zoo和xts,指标计算包TTR,可视包ggplot2等,我们会一起使用这些工具包来完成建模、计算和可视化的工作。关于zoo包和xts包的详细使用可以参考文章,R语言时间序列基础库zoo可扩展的时间序列xts

本文用到的数据,是铜的1分钟线的数据,从2016年日2月1日到2016年日2月29日,日盘的交易数据,以CSV格式保存到本地文件cu1605.csv,cu1606.csv。商品期货的日盘交易时间分为3段:09:00:00-10:14:59,10:30:00-11:29:59,13:30:00-14:59:59。当前测试,不考虑夜盘的数据。

数据格式如下:


2016-02-01 09:00:00,35870,35900,35860,35880
2016-02-01 09:01:00,35890,35890,35860,35870
2016-02-01 09:02:00,35870,35870,35860,35870
2016-02-01 09:03:00,35870,35900,35870,35900
2016-02-01 09:04:00,35900,35900,35870,35870
2016-02-01 09:05:00,35870,35880,35860,35870
2016-02-01 09:06:00,35880,35880,35860,35870

一共5列:

  • 第1列,交易时间,date,2016-02-01 09:00:00
  • 第2列,开盘价,Open,35870
  • 第3列,最高价,High,35900
  • 第4列,最低价,Low,35860
  • 第5列,收盘价,Close,35880

通过R语言加载铜的1分钟线数据,因为我们进行日内交易,所以在加载时我就进行了转换,按日期进行分组,生成R语言的list对象,同时把每日的data.frame类型对象转成XTS时间序列类型对象,方便后续的数据处理。


#加载工具包
> library(xts)
> library(TTR)

# 读取CSV数据文件
> read<-function(file){ 
+     df<-read.table(file=file,header=FALSE,sep = ",", na.strings = "NULL")  # 读文件
+     names(df)<-c("date","Open","High","Low","Close")                       # 设置列名
+     dl<-split(df,format(as.POSIXct(df$date),'%Y-%m-%d'))                   # 按日期分组
+     
+     lapply(dl,function(item){                                              # 换成xts类型数据
+         xts(item[-1],order.by = as.POSIXct(item$date))
+     })
+ }

# 加载数据
> cu1605<-read(file='cu1605.csv')
> cu1606<-read(file='cu1606.csv')

# 查看数据类型
> class(cu1605)
[1] "list"

# 查看数据的日期索引值
> names(cu1605)
 [1] "2016-02-01" "2016-02-02" "2016-02-03" "2016-02-04" "2016-02-05"
 [6] "2016-02-15" "2016-02-16" "2016-02-17" "2016-02-18" "2016-02-19"
[11] "2016-02-22" "2016-02-23" "2016-02-24" "2016-02-25" "2016-02-26"
[16] "2016-02-29"

# 查看每日的数据量
> nrow(cu1605[[1]])
[1] 223

# 查看cu1605合约的数据
> head(cu1605[['2016-02-01']])
                     Open  High   Low Close
2016-02-01 09:00:00 35870 35900 35860 35880
2016-02-01 09:01:00 35890 35890 35860 35870
2016-02-01 09:02:00 35870 35870 35860 35870
2016-02-01 09:03:00 35870 35900 35870 35900
2016-02-01 09:04:00 35900 35900 35870 35870
2016-02-01 09:05:00 35870 35880 35860 35870

把数据准备好了,我们就可以来建立模型了。

3.2 配对交易模型

以2016年02月01日为例进行交易,以1分钟线的close价格来计算cu1605和cu1606的两个合约的价差。下面我们对数据进行操作,合并2个合约在2016年02月01日的数据,并对空值进行处理,最后计算出两个合约的价差。


# 合并数据
> xdf<-merge(cu1605[['2016-02-01']]$Close,cu1606[['2016-02-01']]$Close)
> names(xdf)<-c('x1','x2')

# 用前值替换空值
> xdf<-na.locf(xdf)

# 计算价差
> xdf$diff<-xdf$x1-xdf$x2

# 打印前20行数据
> head(xdf,20)
                     x1     x2     diff
2016-02-01 09:00:00  35880  35900  -20
2016-02-01 09:01:00  35870  35920  -50
2016-02-01 09:02:00  35870  35910  -40
2016-02-01 09:03:00  35900  35940  -40
2016-02-01 09:04:00  35870  35910  -40
2016-02-01 09:05:00  35870  35920  -50
2016-02-01 09:06:00  35870  35910  -40
2016-02-01 09:07:00  35860  35910  -50
2016-02-01 09:08:00  35840  35880  -40
2016-02-01 09:09:00  35790  35840  -50
2016-02-01 09:10:00  35800  35840  -40
2016-02-01 09:11:00  35790  35830  -40
2016-02-01 09:12:00  35820  35860  -40
2016-02-01 09:13:00  35810  35850  -40
2016-02-01 09:14:00  35790  35830  -40
2016-02-01 09:15:00  35780  35830  -50
2016-02-01 09:16:00  35770  35810  -40
2016-02-01 09:17:00  35760  35820  -60
2016-02-01 09:18:00  35750  35800  -50
2016-02-01 09:19:00  35760  35810  -50

数据解释:

  • x1列,为第一腿对应cu1605合约
  • x2列,为第二腿对应cu1606合约。
  • diff列,为cu1605-cu1606

从价差的结果看,每1分钟cu1605合约都小于cu1606合约,从-110到-20价差不等,并且以-63为均值上下反复震荡。


# 计算价差范围
> range(xdf$diff)
[1] -110  -20

# 计算价差均值
> mean(xdf$diff)
[1] -63.90135

# 画出价差分布柱状图
> hist(xdf$diff,10)

画出价差分布柱状图
03

我们假设以-63为均值回归点,当差值为大于-45的时候,认为X的价格过高做空X,同时Y的价格过低做多Y;当差值小于-75的时候,我们认为X的价格过低做多X,同时Y的价格过高做空Y;当差值为-63时,价格被市场所修复,则全部平仓。以cu1605和cu1606的两个合约按照1:1持仓进行配比,1手多单对1手空单。

定义模型指标,计算价值列为diff,均值回归列为mid,最大阈值列为top,最小阈值列为bottom。


target.pair<-function(xdf){
  xdf$diff<-xdf$x1-xdf$x2   #差值
  xdf$mid<- -63             #均值回归点
  xdf$top<- -45             #最大阈值
  xdf$bottom<- -75          #最小阈值
  return(xdf)
}

完成指标的定义后,我们创建配对交易模型,并对合同数据进行回测,产生交易信号后,模拟交易输出清单,并可视化交易结果。

回测过程代码省略,产生的交易信号如下所示。


                  date    x1    x2 diff mid top bottom op
21 2016-02-01 09:00:00 35880 35900  -20 -63 -45    -75 ks
1  2016-02-01 09:25:00 35740 35810  -70 -63 -45    -75 pb
22 2016-02-01 09:40:00 35690 35730  -40 -63 -45    -75 ks
2  2016-02-01 09:47:00 35700 35770  -70 -63 -45    -75 pb
13 2016-02-01 10:00:00 35690 35770  -80 -63 -45    -75 kb
5  2016-02-01 10:01:00 35710 35760  -50 -63 -45    -75 ps
23 2016-02-01 10:02:00 35710 35750  -40 -63 -45    -75 ks
3  2016-02-01 10:07:00 35680 35750  -70 -63 -45    -75 pb
14 2016-02-01 10:37:00 35720 35800  -80 -63 -45    -75 kb
6  2016-02-01 10:42:00 35740 35790  -50 -63 -45    -75 ps
15 2016-02-01 11:20:00 35700 35780  -80 -63 -45    -75 kb
7  2016-02-01 11:21:00 35710 35750  -40 -63 -45    -75 ps
24 2016-02-01 11:21:00 35710 35750  -40 -63 -45    -75 ks
4  2016-02-01 11:23:00 35690 35760  -70 -63 -45    -75 pb
16 2016-02-01 11:29:00 35690 35770  -80 -63 -45    -75 kb
8  2016-02-01 13:36:00 35660 35720  -60 -63 -45    -75 ps
17 2016-02-01 13:45:00 35660 35740  -80 -63 -45    -75 kb
9  2016-02-01 13:46:00 35670 35730  -60 -63 -45    -75 ps
18 2016-02-01 13:52:00 35650 35730  -80 -63 -45    -75 kb
10 2016-02-01 13:53:00 35650 35710  -60 -63 -45    -75 ps
19 2016-02-01 13:56:00 35640 35720  -80 -63 -45    -75 kb
11 2016-02-01 14:49:00 35600 35660  -60 -63 -45    -75 ps
20 2016-02-01 14:52:00 35610 35700  -90 -63 -45    -75 kb
12 2016-02-01 14:58:00 35610 35690  -80 -63 -45    -75 ps

数据解释:

  • date列,为交易时间
  • x1列,为第一腿对应cu1605合约
  • x2列,为第二腿对应cu1606合约。
  • diff列,为cu1605-cu1606
  • mid列,为均值回归点
  • top列,为最大阈值
  • bottom列,为最小阈值
  • op列,为交易信号

交易信号一共有4种。

  • ks, 开仓, 做空(卖),对应反向操作为pb。
  • kb, 开仓, 做多(买),对应反向操作为ps。
  • ps, 平仓, 做空(卖),对应反向操作为kb。
  • pb,平仓, 做多(买),对应反向操作为ks。

一共出现了24个交易信号,由于我们进行的是配对交易,所以当出现ks(开仓做空)信号时,实际上会进行2笔操作,开仓做空第一腿,开仓做多第二腿。

接下来,进行模拟交易,计算出交易清单。


$x1
                       code op price pos    fee  value  margin balance     cash
2016-02-01 09:00:00  cu1605 ks 35880   1 8.9700 179400 26910.0      NA 173081.0
2016-02-01 09:25:00  cu1605 pb 35740   0 8.9350      0     0.0     700 173748.1
2016-02-01 09:40:00  cu1605 ks 35690   1 8.9225 178450 26767.5      NA 173437.7
2016-02-01 09:47:00  cu1605 pb 35700   0 8.9250      0     0.0     -50 173339.9
2016-02-01 10:00:00  cu1605 kb 35690   1 8.9225 178450 26767.5      NA 173552.0
2016-02-01 10:01:00  cu1605 ps 35710   0 8.9275      0     0.0     100 173574.2
2016-02-01 10:02:00  cu1605 ks 35710   1 8.9275 178550 26782.5      NA 173651.3
2016-02-01 10:07:00  cu1605 pb 35680   0 8.9200      0     0.0     150 173753.4
2016-02-01 10:37:00  cu1605 kb 35720   1 8.9300 178600 26790.0      NA 173758.1
2016-02-01 10:42:00  cu1605 ps 35740   0 8.9350      0     0.0     100 173780.2
2016-02-01 11:20:00  cu1605 kb 35700   1 8.9250 178500 26775.0      NA 173887.3
2016-02-01 11:21:00  cu1605 ps 35710   0 8.9275      0     0.0      50 173859.4
2016-02-01 11:21:001 cu1605 ks 35710   1 8.9275 178550 26782.5      NA 174044.1
2016-02-01 11:23:00  cu1605 pb 35690   0 8.9225      0     0.0     100 174096.2
2016-02-01 11:29:00  cu1605 kb 35690   1 8.9225 178450 26767.5      NA 174173.3
2016-02-01 13:36:00  cu1605 ps 35660   0 8.9150      0     0.0    -150 173945.5
2016-02-01 13:45:00  cu1605 kb 35660   1 8.9150 178300 26745.0      NA 174260.1
2016-02-01 13:46:00  cu1605 ps 35670   0 8.9175      0     0.0      50 174232.3
2016-02-01 13:52:00  cu1605 kb 35650   1 8.9125 178250 26737.5      NA 174331.9
2016-02-01 13:53:00  cu1605 ps 35650   0 8.9125      0     0.0       0 174254.1
2016-02-01 13:56:00  cu1605 kb 35640   1 8.9100 178200 26730.0      NA 174403.8
2016-02-01 14:49:00  cu1605 ps 35600   0 8.9000      0     0.0    -200 174125.9
2016-02-01 14:52:00  cu1605 kb 35610   1 8.9025 178050 26707.5      NA 174490.6
2016-02-01 14:58:00  cu1605 ps 35610   0 8.9025      0     0.0       0 174405.3

$x2
                       code op price pos    fee  value  margin balance     cash
2016-02-01 09:00:00  cu1606 kb 35900   1 8.9750 179500 26925.0      NA 146147.1
2016-02-01 09:25:00  cu1606 ps 35810   0 8.9525      0     0.0    -450 200214.2
2016-02-01 09:40:00  cu1606 kb 35730   1 8.9325 178650 26797.5      NA 146631.3
2016-02-01 09:47:00  cu1606 ps 35770   0 8.9425      0     0.0     200 200328.4
2016-02-01 10:00:00  cu1606 ks 35770   1 8.9425 178850 26827.5      NA 146715.6
2016-02-01 10:01:00  cu1606 pb 35760   0 8.9400      0     0.0      50 200442.7
2016-02-01 10:02:00  cu1606 kb 35750   1 8.9375 178750 26812.5      NA 146829.8
2016-02-01 10:07:00  cu1606 ps 35750   0 8.9375      0     0.0       0 200557.0
2016-02-01 10:37:00  cu1606 ks 35800   1 8.9500 179000 26850.0      NA 146899.1
2016-02-01 10:42:00  cu1606 pb 35790   0 8.9475      0     0.0      50 200671.2
2016-02-01 11:20:00  cu1606 ks 35780   1 8.9450 178900 26835.0      NA 147043.4
2016-02-01 11:21:00  cu1606 pb 35750   0 8.9375      0     0.0     150 200835.5
2016-02-01 11:21:001 cu1606 kb 35750   1 8.9375 178750 26812.5      NA 147222.6
2016-02-01 11:23:00  cu1606 ps 35760   0 8.9400      0     0.0      50 200949.8
2016-02-01 11:29:00  cu1606 ks 35770   1 8.9425 178850 26827.5      NA 147336.9
2016-02-01 13:36:00  cu1606 pb 35720   0 8.9300      0     0.0     250 201014.1
2016-02-01 13:45:00  cu1606 ks 35740   1 8.9350 178700 26805.0      NA 147446.2
2016-02-01 13:46:00  cu1606 pb 35730   0 8.9325      0     0.0      50 201078.4
2016-02-01 13:52:00  cu1606 ks 35730   1 8.9325 178650 26797.5      NA 147525.5
2016-02-01 13:53:00  cu1606 pb 35710   0 8.9275      0     0.0     100 201142.7
2016-02-01 13:56:00  cu1606 ks 35720   1 8.9300 178600 26790.0      NA 147604.8
2016-02-01 14:49:00  cu1606 pb 35660   0 8.9150      0     0.0     300 201207.0
2016-02-01 14:52:00  cu1606 ks 35700   1 8.9250 178500 26775.0      NA 147706.7
2016-02-01 14:58:00  cu1606 pb 35690   0 8.9225      0     0.0      50 201221.4

数据解释:

  • $x1部分,为第一腿的交易清单。
  • $x2部分,为第二腿的交易清单。
  • code,合约代码
  • op,交易信号
  • price,成交价格
  • pos,成交数量
  • fee,手续费
  • value,对应价值
  • margin,保证金
  • balance,平仓盈亏
  • cash,账号资金

我通过交易清单,统计交易结果。


> page  
$day     # 交易日期
[1] "2016-02-01"

$capital   # 初始资金
[1] 2e+05

$cash      # 账户余额
[1] 201221.4

$num       # 交易信号数
[1] 24

$record    # 配对交易平仓盈亏
                      x1   x2 balance
2016-02-01 09:25:00  700 -450     250
2016-02-01 09:47:00  -50  200     150
2016-02-01 10:01:00  100   50     150
2016-02-01 10:07:00  150    0     150
2016-02-01 10:42:00  100   50     150
2016-02-01 11:21:00   50  150     200
2016-02-01 11:23:00  100   50     150
2016-02-01 13:36:00 -150  250     100
2016-02-01 13:46:00   50   50     100
2016-02-01 13:53:00    0  100     100
2016-02-01 14:49:00 -200  300     100
2016-02-01 14:58:00    0   50      50

$balance   # 汇总平仓盈亏,第一腿盈亏,第二腿盈亏
[1] 1650  850  800

$fee       # 汇总手费费,第一腿手续费,第二腿手续费
[1] 429 214 215

$profit    # 账户净收益,收益率(占保证金)
[1] 1221.000    0.023

$wins      # 胜率,胜数,败数
[1]  1 12  0

最后,通过可视化输出交易信号。

04

图例解释

  • 棕色线,为价差diff
  • 紫色线,为最大阈值top
  • 红色线,为最小阈值bottom
  • 蓝色线,为均值线mid,平行于top和bottom
  • 浅蓝线,为ks开仓做空的交易
  • 绿色线,为kb开仓做多的交易

从图中看就更直观了,我们进行了12次交易,每次4笔,胜率100%。

最后,我们对2月份整个的数据进行回测。回测结果如下。


         date profit    ret balance fee winRate win fail maxProfit maxLoss avgProfit avgLoss
1  2016-02-01   1221  0.023    1650 429    1.00  12    0       250      50       138     NaN
2  2016-02-02   1077  0.020    1650 573    1.00  15    0       150       0       110     NaN
3  2016-02-03     64  0.001     100  36    1.00   1    0       100     100       100     NaN
4  2016-02-04    113  0.002     150  37    1.00   1    0       150     150       150     NaN
5  2016-02-05    926  0.017    1400 474    1.00  13    0       150     100       108     NaN 
6  2016-02-15   1191  0.022    1550 359    1.00  10    0       250     100       155     NaN 
7  2016-02-16     78  0.001     150  72    1.00   1    0       150       0       150     NaN  
8  2016-02-17    179  0.003     250  71    1.00   2    0       200      50       125     NaN 
9  2016-02-18     14  0.000      50  36    1.00   1    0        50      50        50     NaN  
10 2016-02-19    -36 -0.001       0  36     NaN   0    0         0       0       NaN     NaN   
11 2016-02-22     64  0.001     100  36    1.00   1    0       100     100       100     NaN 
12 2016-02-23    632  0.012     850 218    1.00   6    0       200     100       142     NaN 
13 2016-02-24    470  0.009     650 180    1.00   4    0       200       0       162     NaN 
14 2016-02-25    114  0.002     150  36    1.00   1    0       150     150       150     NaN 
15 2016-02-26    178  0.003     250  72    1.00   2    0       150     100       125     NaN 
16 2016-02-29    511  0.009     800 289    0.88   7    1       150     -50       121     -50 

数据解释:

  • date,交易日期
  • profit,净收益
  • ret,每日收益率
  • balance,平仓盈亏
  • fee,手续费
  • winRate,胜率
  • win,胜数
  • fail,败数
  • maxProfit,单笔最大盈利
  • maxLoss,单笔最大亏损
  • avgProfit,平均盈利
  • avgLoss,平均亏损

从结果来看,多么开心啊,几乎每天都是赚钱的!!

cu1605和cu1606两个合同是完美地具备均衡关系的两个金融产品,大家常常所说的跨期套利就是基于这个思路实现的。本文介绍的配对交易模型,是统计套利的一个基本模型,原理很简单,当大家都掌握后拼的就是交易速度了。

利用市场的无效性来获取利润,是每个套利策略都在寻找的目标。通过统计方法,我们可以发现市场的无效性,再以对冲的操作方式,规避绝大部分的市场风险,等待市场的自我修复后来赚钱利润。说起来很简单,但市场的无效性,可能会在极短时间内就被修复。

“天下武功为快不破”,通过量化的手段,让计算机来发现机会,进行交易,实现收益。一切就和谐了!!

转载请注明出处:
http://blog.fens.me/finance-pairs-trading/

打赏作者

This entry was posted in R语言实践, 金融

0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

22 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments

[…] R语言构建配对交易量化模型 […]

taipeialpha

dear 张丹: can you provide the cvs files(cu1605.csv,cu1606.csv) for testing? Thanks a lot !

Conan Zhang

留个邮箱吧,我稍后发你。

taipeialpha

thanks a lot,Conan, maxclchen@yahoo.com

Conan Zhang

已发

taipeialpha

Dear Conan, got your file, thank you very mcuh and have a nice weekend in China !

Conan Zhang

🙂

Francis Tsai

please also send me the files, thank you!
fttsai@gm.asia.edu.tw

Conan Zhang

已发邮件。

jiangsheng

你好,可以转发一下给我吗!1414545606@qq.com,谢谢

Cen Hu

Please send me a sample of test data
tsen.hu@gmail.com
Thanks,

Conan Zhang

已发

伊戴天

yidaitian@foxmail.com。希望能发我一份数据,不胜感激

ChenghuaSun

Hi,
Can u sent me the data set?
Email: ftd_sch_@hotmail.com
Thx a lot.

唐國淯

please also send me the files, thank you!
13240681844@163.COM

Conan Zhang

已发

jiangsheng

可以发给我一份吗1414545606@qq.com

Kang Ji

could i have the file of back-test please?thanks
kaj219@lehigh.edu

汪揚

please also send me the files, thank you!
penguinwang96825@gmail.com

Todd

您好,我正在學習用R建構pairs trading,請問您教學裡的回測過程到統計交易结果的代碼要怎麼寫?謝謝。

Dan Zhang

抱歉,这部分程序,我还没有完全整理好。暂时还不能开放。

Hao

现在能购买代码了吗?

22
0
Would love your thoughts, please comment.x
()
x