多因子模型系列5-套利定价模型的应用

 

套利定价模型

套利定价模型是金融领域中非常重要的资产定价理论。对于某个资产,该模型把其的收益使用线性因子模型来表示:

$$R_i = a_i + b_{i1} F_1 + b_{i2} F_2 + \ldots + b_{iK} F_K + \epsilon_i$$

该理论认为,如果我们可以按照上面的式子来表示收益的话,那么资产的预期收益就应该符合:

$$ E(R_i) = R_F + b_{i1} \lambda_1 + b_{i2} \lambda_2 + \ldots + b_{iK} \lambda_K $$

其中$R_F$表示无风险收益,$\lambda_j$表示因子$j$的风险溢价。风险溢价的出现时因为人们需要更高的收益去补偿其受到的更高风险。知道资本资产定价模型(CAPM)的读者可以很明显的发现套利定价模型模型是其的推广形式,CAPM使用了市场回报作为其唯一的因子。

我们可以构建一个投资组合来计算$\lambda_j$,这个投资组合对因子$j$的弹性是1,对于其他因子的弹性为0(因子$j$的纯因素组合),之后得到超出无风险收益的回报。

套利

实际上我们可以选择许多许多股票来构建我们的投资组合。如果我们使用不同的股票来计算$\lambda$s,我们的结果会一致吗?如果不一致的话,那么好了,就存在着套利空间。
比如,下面这种情况就存在套利空间:
某资产未来一年的预期回报是0.3,和市场的$\beta$是1.1,市场的预期收益是0.15,无风险利率是0.05。那么根据APT模型,这个资产的预期收益应该是

$$ R_F + \beta \lambda = 0.05 + 1.1 (0.15 – 0.05) = 0.16$$

这个和资产未来一年的预期回报是0.3不相符。所以如果我们购买100元的此资产,卖空110元的市场组合,买10元的无风险资产,这样我们没有任何投入而且没有承受任何市场风险,但是我们的预期收入是
$$0.3 \cdot 100 – 0.15 \cdot 110 + 10 \cdot 0.05 = 14$$

APT模型假设所以的机会被人们发现并利用,直到价格发生变化导致套利机会消失,也就是说市场上的套利者有足够的资金和耐心。这也给使用经验因子模型提供了理由:如果模型结果是不一致的,就存在套利机会,价格也会随之变化。

我们需要多少个因子?

因子的数量越多会对收益解释的越多,但是也会对数据中噪音适应的越多。如果想要发现一个好的信号,就需要参数越少越好,但是同时还要能解释收益里面大部分的变动。

下面我们就来计算一下资产的预期收益。

首先导入必要的包

In [1]:
import numpy as np
import pandas as pd
from statsmodels import regression
import matplotlib.pyplot as plt
import datetime

获取股票、市场及无风险资产(这里的无风险利率是每日中国固定利率国债收益率曲线上国债的年化到期收益率的spot rate)一年的数据。

In [2]:
start_date = '2015-01-01'
end_date = '2016-01-01'

# 我们会看标的资产未来一个月的收益去构建未来收益模型
offset_start_date = '2015-02-01'
offset_end_date = '2016-02-01'

# 获得资产收益
asset1 = get_price('601857.XSHG', start_date=offset_start_date, end_date=offset_end_date, fields='ClosingPx').pct_change()[1:]
# 获得市场收益
benchmark = get_price('000300.XSHG', start_date=start_date, end_date=end_date, fields='ClosingPx').pct_change()[1:]
date = benchmark.index
temp = pd.read_csv("2015.csv")
rf = temp['rate']
rf.index = pd.to_datetime(temp['Date'], format="%Y%m%d")
rf = rf[rf.index >= date[0]]
rf = rf[rf.index <= date[-1]]
# 转换成每日的收益率
rf = (1+rf/100)**(1/365)-1

需要定义一个常量用来计算截距。

In [3]:
constant = pd.TimeSeries(1, index = asset1.index)
df = pd.DataFrame({'A1': asset1, 'HS300': benchmark, 'RF': rf, 'Constant': constant})
df = df.dropna()
df
Out[3]:
A1 Constant HS300 RF
2015-02-03 0.041889 1 0.024891 0.000064
2015-02-04 -0.023952 1 -0.010379 0.000064
2015-02-05 -0.022787 1 -0.010236 0.000062
2015-02-06 -0.023318 1 -0.016194 0.000061
2015-02-09 0.012856 1 0.010114 0.000060
2015-02-10 0.018132 1 0.018238 0.000061
2015-02-11 -0.006233 1 0.007978 0.000063
2015-02-12 0.001792 1 0.002548 0.000067
2015-02-13 0.000000 1 0.007829 0.000068
2015-02-16 -0.009839 1 0.008547 0.000070
2015-02-17 0.000903 1 0.006526 0.000076
2015-02-25 0.013538 1 -0.012376 0.000074
2015-02-26 0.009795 1 0.025172 0.000073
2015-02-27 0.007055 1 0.001836 0.000074
2015-03-02 -0.008757 1 0.007955 0.000074
2015-03-03 -0.034452 1 -0.025926 0.000074
2015-03-04 -0.000915 1 0.006534 0.000074
2015-03-05 -0.019231 1 -0.009765 0.000073
2015-03-06 0.005602 1 -0.005098 0.000073
2015-03-09 0.011142 1 0.017027 0.000072
2015-03-10 -0.011019 1 -0.004845 0.000071
2015-03-11 -0.001857 1 0.001148 0.000071
2015-03-12 0.026047 1 0.019347 0.000074
2015-03-13 0.002720 1 0.006906 0.000074
2015-03-16 0.012658 1 0.024329 0.000073
2015-03-17 0.025000 1 0.013884 0.000072
2015-03-18 0.014808 1 0.023671 0.000072
2015-03-19 -0.000858 1 -0.001642 0.000071
2015-03-20 0.003436 1 0.013760 0.000070
2015-03-23 0.010274 1 0.020420 0.000068
2015-11-20 -0.005531 1 -0.000155 0.000043
2015-11-23 -0.007786 1 -0.005577 0.000043
2015-11-24 -0.001121 1 0.000147 0.000043
2015-11-25 0.007856 1 0.007384 0.000043
2015-11-26 -0.001114 1 -0.005865 0.000043
2015-11-27 -0.056856 1 -0.053848 0.000043
2015-11-30 -0.007092 1 0.002648 0.000043
2015-12-01 0.001190 1 0.007089 0.000043
2015-12-02 0.027348 1 0.036267 0.000043
2015-12-03 0.020833 1 0.007347 0.000044
2015-12-04 -0.027211 1 -0.019125 0.000045
2015-12-07 -0.003497 1 0.002723 0.000045
2015-12-08 -0.019883 1 -0.017515 0.000045
2015-12-09 -0.003580 1 0.003566 0.000045
2015-12-10 -0.002395 1 -0.003535 0.000045
2015-12-11 -0.003601 1 -0.004147 0.000045
2015-12-14 0.015663 1 0.028620 0.000045
2015-12-15 -0.007117 1 -0.004563 0.000045
2015-12-16 0.010753 1 -0.002422 0.000045
2015-12-17 0.002364 1 0.019115 0.000045
2015-12-18 0.000000 1 0.003202 0.000045
2015-12-21 0.005896 1 0.026023 0.000046
2015-12-22 -0.001172 1 0.002785 0.000047
2015-12-23 0.002347 1 -0.002670 0.000048
2015-12-24 -0.007026 1 -0.009564 0.000048
2015-12-25 0.000000 1 0.002298 0.000048
2015-12-28 -0.012972 1 -0.028808 0.000048
2015-12-29 0.005974 1 0.009186 0.000048
2015-12-30 -0.001188 1 0.000877 0.000048
2015-12-31 -0.007134 1 -0.009075 0.000048

223 rows × 4 columns

OLS的回归结果。

In [4]:
OLS = regression.linear_model.OLS(df['A1'], df[['HS300', 'RF', 'Constant']])
fitted_model = OLS.fit()
print('p-value', fitted_model.f_pvalue)
print(fitted_model.params)
A1_params = fitted_model.params
p-value 8.17578293389e-22
HS300       0.689253
RF         -8.686192
Constant   -0.001100
dtype: float64

仅仅通过这个结果并不能说明什么问题,我们需要看一看预测系数的分布以及其是否稳定。我们来看一下一个滑动的100天的回归结果。

In [5]:
model = pd.stats.ols.MovingOLS(y = df['A1'], x=df[['HS300', 'RF']], 
                             window_type='rolling', 
                             window=100)
rolling = model.beta

plt.figure(figsize=(25, 18)) 
plt.plot_date(rolling.index,rolling,'-')
plt.hlines(A1_params['HS300'], df.index[100], df.index[-1], linestyles='dashed', colors='blue')
plt.hlines(A1_params['RF'], df.index[100], df.index[-1], linestyles='dashed', colors='green')
plt.hlines(A1_params['Constant'], df.index[100], df.index[-1], linestyles='dashed', colors='red')

plt.title('Asset1 Computed Betas');
plt.legend(['Market Beta', 'Risk Free Beta', 'Intercept', 'Market Beta Static', 'Risk Free Beta Static', 'Intercept Static']);

由于无风险收益的$\beta$波动范围很大,导致我们无法观测到市场$\beta$是否稳定,下面我们在图中去掉无风险收益的$\beta$来看一下。

In [6]:
rolling = rolling.drop('RF', 1)
plt.figure(figsize=(25, 18)) 
plt.plot_date(rolling.index,rolling,'-')
plt.hlines(A1_params['HS300'], df.index[100], df.index[-1], linestyles='dashed', colors='blue')
plt.hlines(A1_params['Constant'], df.index[100], df.index[-1], linestyles='dashed', colors='green')

plt.title('Asset1 Computed Betas');
plt.legend(['Market Beta', 'Intercept', 'Market Beta Static', 'Intercept Static']);

可以发现市场$\beta$还是有一定的波动的。

预测未来走势

下面我们使用APT来预测一下股票未来的走势。

In [7]:
start_date = '2015-01-01'
end_date = '2015-11-01'

# 我们会看标的资产未来一个月的收益去构建未来收益模型
offset_start_date = '2015-02-01'
offset_end_date = '2015-12-01'

# 获得资产收益
asset1 = get_price('000030.XSHG', start_date=offset_start_date, end_date=offset_end_date, fields='ClosingPx').pct_change()[1:]
# 获得市场收益
benchmark = get_price('000300.XSHG', start_date=start_date, end_date=end_date, fields='ClosingPx').pct_change()[1:]
date = benchmark.index
temp = pd.read_csv("2015.csv")
rf = temp['rate']
rf.index = pd.to_datetime(temp['Date'], format="%Y%m%d")
rf = rf[rf.index >= date[0]]
rf = rf[rf.index <= date[-1]]
# 转换成每日的收益率
rf = (1+rf/100)**(1/365)-1
constant = pd.TimeSeries(1, index=date)
df = pd.DataFrame({'A1': asset1, 'HS300': benchmark, 'RF': rf, 'Constant': constant})
df = df.dropna()
In [8]:
OLS = regression.linear_model.OLS(df['A1'], df[['HS300', 'RF', 'Constant']])
fitted_model = OLS.fit()
print('p-value', fitted_model.f_pvalue)
print(fitted_model.params)

b_HS300 = fitted_model.params['HS300']
b_RF = fitted_model.params['RF']
a = fitted_model.params['Constant']
p-value 7.97319026658e-150
HS300       1.058529
RF          3.633765
Constant   -0.000448
dtype: float64

获取过去一个月因子数据用来预测未来一个月的数据。

In [9]:
start_date = '2015-11-01'
end_date = '2015-12-01'

# 获得市场收益
last_benchmark = get_price('000300.XSHG', start_date=start_date, end_date=end_date, fields='ClosingPx').pct_change()[1:]
date = last_benchmark.index
temp = pd.read_csv("2015.csv")
last_rf = temp['rate']
last_rf.index = pd.to_datetime(temp['Date'], format="%Y%m%d")
last_rf = last_rf[last_rf.index >= date[0]]
last_rf = last_rf[last_rf.index <= date[-1]]
# 转换成每日的收益率
last_rf = (1+last_rf/100)**(1/365)-1

预测未来收益。

In [10]:
predictions = b_HS300 * last_benchmark + b_RF * last_rf + a

预测结果和实际对比见下图:

In [11]:
offset_start_date = '2015-12-01'
offset_end_date = '2016-01-01'
asset1_act = get_price('000030.XSHG', start_date=offset_start_date, end_date=offset_end_date, fields='ClosingPx').pct_change()[1:]
plt.figure(figsize=(25, 18)) 
plt.plot(asset1_act.values, 'ro-')
plt.plot(predictions, 'bo--')
plt.ylabel('Returns')
plt.legend(['Actual', 'Predicted']);

 

 

多因子模型系列目录

1.多因子模型系列1-数据预处理之离群值处理
2. 多因子模型系列2-多因子模型的步骤梳理
3. 多因子模型系列3-Fama Franch三因子及其拓展五因子模型
4. 多因子模型系列4-验证Fama French三因子分解的有效性
当前阅读> 5. 多因子模型系列5-套利定价模型的应用
6. 多因子模型系列6-沪深300指数的风格因子暴露度分析
7. 多因子模型系列7-多因子模型水平测试题试答
8. 多因子模型系列8-基于组合权重优化的风格中性多因子选股策略框架
9. 多因子模型系列9-阿里巴巴与101个alpha
10. 多因子模型系列10-因子分析
11. 多因子模型系列11-绩效分析之Brinson模型