大家好, 我是Lucy@FinTech社区。FinTech 社区是一个拥有50,000+会员的金融科技行业资源共享社区,旨在为金融科技行业赋能,致力于金融科技行业资源共享社群。我们目前有机器学习群,技术群,量化群,校招群,数据开发群等等。欢迎加入社区, 攒人脉,提认知, 求职招聘。官网:网页链接,微信公众号:FinTech社区。
今天的文章将首先简介卡尔曼滤波基本原理;接着用Python和R分别实现卡尔曼滤波计算两种ETF配对交易的对冲比率;最后介绍卡尔曼滤波在量化交易中的最近的研究应用成果。希望对大家有所帮助!
传统的配对交易策略,其配对比率一般固定在某一个恒定的值,但是随着时间的推移,最优的对冲比率必然会发生变化。如果继续使用某一段时间估计出的最优对冲比率继续进行交易,会出现样本内过拟合的情况。为了克服这个缺点,算法工程师们引入了Kalman Filter的思想,来改善配对交易策略的表现。
本文首先简介卡尔曼滤波基本原理(若对卡尔曼滤波不太熟悉,请阅读文末参考"How a Kalman filter works, in pictures");
接着用Python和R分别实现卡尔曼滤波计算两种ETF配对交易的对冲比率;
最后介绍了卡尔曼滤波在量化交易中的最近的研究应用成果。
卡尔曼滤波的基本思想:
卡尔曼滤波器是一个最优化的自回归数据处理算法。
简而言之,就是以上一时刻状态变量的最优估值为准,预测下一时刻状态变量,同时对下一时刻的状态进行观测,得到观察变量,再用观测量对预测测量修正,从而得到下一时刻的最有效状态估计。
下图来自wiki,可辅助理解整个过程:
卡尔曼滤波器有5条核心的公式。卡尔曼滤波器对于解决大部分问题,是最优效率最高甚至最有用的。
核心方程如下图所示:
卡尔曼滤波算法基本流程:
根据当前状态和状态转换模型,预测隐藏变量的下一个状态;更新状态协方差预测值;给定隐藏变量和观察模型的预测,预测观察变量的下一个值;更新测得的协方差预测值;计算观测变量的观测值与预测值之间的误差;计算卡尔曼增益;更新隐藏变量的估算值;更新状态协方差预测。
卡尔曼滤波器应用于国债ETF配对交易(Python):
这里使用两种固定收益ETF,一种是iShares 20年以上国债ETF(TLT)和iShares 3到7年国债ETF(IEI)。
这里考虑两个ETF的线性组合,所以两个变量的数学上的关系是线性方程,我们通过使用卡尔曼滤波器,动态估计这对ETF之间的斜率和截距(即hedge ration)。
这里我们使用pykalman库提供的卡尔曼滤波算法。以及numpy、pandas、matplotlib等库。
首先通过绘制TLT和IEI的散点图,探索两种ETF之间的关系。
下一步是使用pykalman的卡尔曼滤波函数,动态计算TFT和IEI之间的截距和斜率。具体实现算法:
最后,使用上面calc_slope_intercept_kalman计算的值绘制斜率和截距的的关系图。
计算结果如下图所示:
现在,我们已经使用卡尔曼滤波建立两个ETF之间的动态关系,接下来就可以根据这些信息制定国债ETF的交易策略,由于不是本文重点,这里不做展开(全部源码见附录)。
卡尔曼滤波器应用于黄金ETF配对交易(R语言):
这里我们使用的两个价格系列是:GLD和GDX。也是考虑两个ETF的线性关系,这里用R语言实现。
首先,也是看一下两种ETF的价格关系,这里使用时序图。
接下来用卡尔曼滤波器计算对冲比率的代码:
上面算法计算结果如下图所示:
现在我们已经得到这对ETF之间的对冲比率随时间的变化,至此我们就可以利用这个,来构建我们的黄金ETF对冲的交易策略。由于不是本文重点,这里不做展开。
Carlos Eduardo de Moura等人(参考链接中论文)提出了一种基于线性状态空间模型的资产对的交易策略。
该模型用于对一对资产形成的价差进行建模。一旦估计出足够的价差状态空间模型,会使用卡尔曼滤波器来计算条件概率,当这些条件概率的值很大时就会激活策略:进行相应地价差买卖。
作者通过对美国和巴西市场的真实数据测试,尽管可能数据量有限,但测试结果表明,由单一利差组成的基本的投资组合的表现优于某些主要市场基准。
本期的内容就到这里,希望大家有所收获!
参考:
How a Kalman filter works, in pictures
https://www.bzarg.com/p/how-a-kalman-filter-works-in-pictures/Dynamic Hedge Ratio Between ETF Pairs Using the Kalman Filter
https://www.quantstart.com/articles/Dynamic-Hedge-Ratio-Between-ETF-Pairs-Using-the-Kalman-Filter/Carlos Eduardo de Moura等 A pairs trading strategy based on linear state space models and the Kalman filter
https://www.researchgate.net/publication/301644668_A_pairs_trading_strategy_based_on_linear_state_space_models_and_the_Kalman_filter策略不给力?来一发卡尔曼滤波
网页链接
参考:Python全部源码
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pandas_datareader.data as web
from pykalman import KalmanFilter
def draw_date_coloured_scatterplot(etfs, prices):
"""
展示两种ETF价格关系的散点图
"""
# 分别用红和黄色代表两种ETF
plen = len(prices)
colour_map = plt.cm.get_cmap('YlOrRd')
colours = np.linspace(0.1, 1, plen)
# 创建散点图对象
scatterplot = plt.scatter(
prices[etfs[0]], prices[etfs[1]],
s=30, c=colours, cmap=colour_map,
edgecolor='k', alpha=0.8
)
colourbar = plt.colorbar(scatterplot)
colourbar.ax.set_yticklabels(
[str(p.date()) for p in prices[::plen//9].index]
)
plt.xlabel(prices.columns[0])
plt.ylabel(prices.columns[1])
plt.show()
def calc_slope_intercept_kalman(etfs, prices):
"""
利用pyKalman软件包中的Kalman过滤器,计算ETF对的价格回归的斜率和回归的截距。
"""
delta = 1e-5 # 控制过渡协方差矩阵的噪音
trans_cov = delta / (1 - delta) * np.eye(2) # 过渡协方差矩阵
# 创建观测矩阵:一个一维矩阵存储TFT的值
obs_mat = np.vstack(
[prices[etfs[0]], np.ones(prices[etfs[0]].shape)]
).T[:, np.newaxis]
# 创建卡尔曼滤波器实例
kf = KalmanFilter(
n_dim_obs=1,
n_dim_state=2,#状态,这里是2,我们要求的是线性回归的斜率和截距
initial_state_mean=np.zeros(2),#斜率和截距的状态均值初始化为0
initial_state_covariance=np.ones((2, 2)),
transition_matrices=np.eye(2),
observation_matrices=obs_mat,#观测矩阵
observation_covariance=1.0,
transition_covariance=trans_cov
)
# 调用过滤器。计算截距和斜率的状态。
state_means, state_covs = kf.filter(prices[etfs[1]].values)
return state_means, state_covs
def draw_slope_intercept_changes(prices, state_means):
"""
从卡尔曼滤波器计算结果,绘制斜率和截距的变化
"""
pd.DataFrame(
dict(
slope=state_means[:, 0],
intercept=state_means[:, 1]
), index=prices.index
).plot(subplots=True)
plt.show()
if __name__ == "__main__":
etfs = ['TLT', 'IEI']
start_date = "2010-8-01"
end_date = "2016-08-01"
# 从雅虎金融获取数据
prices = web.DataReader(
etfs, 'yahoo', start_date, end_date
)['Adj Close']
draw_date_coloured_scatterplot(etfs, prices)
state_means, state_covs = calc_slope_intercept_kalman(etfs, prices)
draw_slope_intercept_changes(prices, state_means)