为 3 个 ETF(EWA、EWC、IGE)在 Python 中实施协整投资组合
Implementing a cointegration portfolio in Python for 3 ETFs (EWA, EWC, IGE)
我正在尝试使用 P.E 博士在“算法交易”中描述的策略来实施均值回归投资组合。陈。但是,由于他使用的示例是在 MATLAB 中编写的,因此我无法将它们正确翻译成 Python。我完全无法尝试使用 3 个 ETF 创建一个协整投资组合。我认为我的问题始于尝试确定对冲,然后建立所需的投资组合。
任何帮助或提示都会非常有用。
因此,我首先下载调整后的价格并创建 W、X 和 Y 数据系列。我选择的时间段是2007/07/22到2012/3/28
import numpy as np
import pandas as pd
import pandas_datareader.data as web
import matplotlib.pyplot as plt
%matplotlib inline
import statsmodels.api as sm
import datetime
start = datetime.datetime(2007, 7, 22)
end = datetime.datetime(2012, 3, 28)
EWA = web.DataReader('EWA', 'yahoo', start, end)
EWC = web.DataReader('EWC', 'yahoo', start, end)
IGE = web.DataReader('IGE', 'yahoo', start, end)
w = IGE['Adj Close']
x = EWA['Adj Close']
y = EWC['Adj Close']
df = pd.DataFrame([w,x,y]).transpose()
df.columns = ['W','X','Y']
df.plot(figsize=(20,12))
from statsmodels.tsa.vector_ar.vecm import coint_johansen
y3 = df
j_results = coint_johansen(y3,0,1)
print(j_results.lr1)
print(j_results.cvt)
print(j_results.eig)
print(j_results.evec)
print(j_results.evec[:,0])
那么我应该通过将特征向量 [0.30.., 1.36.., -1.35..] 乘以每种工具的股价来构建一个投资组合,以获得 y_port 值。之后我 运行 进行相关性测试以确定该投资组合的每日价格变化与最后一天的价格变化之间的相关性,从而能够确定该系列的半衰期。
我只是通过将特征向量乘以收盘价来做到这一点,我不知道这是不是我出错的地方。
hedge_ratios = j_results.evec[:,0]
y_port = (hedge_ratios * df).sum(axis=1)
y_port.plot(figsize=(20,12))
y_port_lag = y_port.shift(1)
y_port_lag[0]= 0
delta_y = y_port-y_port_lag
X = y_port_lag
Y = delta_y
X = sm.add_constant(X)
model = OLS(Y,X)
regression_results = model.fit()
regression_results.summary()
然后我计算半衰期,大约是19天。
halflife = -np.log(2)/regression_results.params[0]
halflife
我根据账面说明定义要持有的单位数量(投资组合价值的 -Z 值,根据半衰期回溯 window 19 天)。
num_units = -(y_port-y_port.rolling(19).mean())/y_port.rolling(19).std()
num_units.plot(figsize=(20,12))
所以接下来我要采取的步骤是:
检查数据帧是否仍然正确。
添加之前计算的“要持有的单位数”,它是 y_port 值的负 Z 分数。
可能有更简单的乘法或执行此操作的方法,但我通过将工具价格乘以特征向量给出的对冲比率来计算我应该为每种工具持有的 $ 数量,按要持有的投资组合单位数量。
最后我通过乘以每日变化*我持有的单位数量来计算每个工具的盈亏。
结果很糟糕。就是从头输到尾。
¿我哪里搞砸了? ¿如何正确乘以特征向量中的值,确定要持有的头寸数量,并正确创建投资组合?
如有任何帮助,我们将不胜感激。
- 我不知道为什么 num_units 系列是“水平”的,我不得不在将它附加到 DataFrame 之前转置它。
num_units = num_units.transpose()
df['Portfolio Units'] = num_units
df
df['W $ Units'] = df['W']*hedge_ratios[0]*df['Portfolio Units']
df['X $ Units'] = df['X']*hedge_ratios[1]*df['Portfolio Units']
df['Y $ Units'] = df['Y']*hedge_ratios[2]*df['Portfolio Units']
positions = df[['W $ Units','X $ Units','Y $ Units']]
positions
pnl = pd.DataFrame()
pnl['W Pnl'] = (df['W']/df['W'].shift(1)-1)*df['W $ Units']
pnl['X Pnl'] = (df['X']/df['X'].shift(1)-1)*df['X $ Units']
pnl['Y Pnl'] = (df['Y']/df['Y'].shift(1)-1)*df['Y $ Units']
pnl['Total PNL'] = pnl.sum(axis=1)
pnl['Total PNL'].cumsum().plot(figsize=(20,12))
我知道,如果我只是恢复我的位置(不在 y_port 中使用 -1),结果会改变,我会得到一个积极的 return。但是,我想知道我做错了什么。将 -Z 用于均值回归策略是有道理的,我想知道我在哪里犯了错误,这样我就可以跟上本书的其余部分,
我认为您需要将 df['W $ Units']、df['X $ Units'] 和 df['Y $ Units'] 也移动 1。例如,使用 df['Y $ Units'].shift(1) 而不是 df['Y $ Units']。
您收到的结果并不糟糕,而是不切实际。在不移动 df['... $ Units'] 的情况下,您正在展望未来并使用尚不可用的数据。
发现part4有些问题,修改如下:
positions = df[['W $ Units','X $ Units','Y $ Units']]
df5=df.iloc[:,0:3]
pnl=np.sum((positions.shift().values)*(df5.pct_change().values), axis=1)
ret=pnl/np.sum(np.abs(positions.shift()), axis=1)
plt.figure(figsize=(8,5))
plt.plot(np.cumprod(1+ret)-1)
print('APR=%f Sharpe=%f' % (np.prod(1+ret)**(252/len(ret))-1, np.sqrt(252)*np.mean(ret)/np.std(ret)))
因此我们有 APR=0.130122 Sharpe=1.518595 daily rets plot
我正在尝试使用 P.E 博士在“算法交易”中描述的策略来实施均值回归投资组合。陈。但是,由于他使用的示例是在 MATLAB 中编写的,因此我无法将它们正确翻译成 Python。我完全无法尝试使用 3 个 ETF 创建一个协整投资组合。我认为我的问题始于尝试确定对冲,然后建立所需的投资组合。
任何帮助或提示都会非常有用。
因此,我首先下载调整后的价格并创建 W、X 和 Y 数据系列。我选择的时间段是2007/07/22到2012/3/28
import numpy as np
import pandas as pd
import pandas_datareader.data as web
import matplotlib.pyplot as plt
%matplotlib inline
import statsmodels.api as sm
import datetime
start = datetime.datetime(2007, 7, 22)
end = datetime.datetime(2012, 3, 28)
EWA = web.DataReader('EWA', 'yahoo', start, end)
EWC = web.DataReader('EWC', 'yahoo', start, end)
IGE = web.DataReader('IGE', 'yahoo', start, end)
w = IGE['Adj Close']
x = EWA['Adj Close']
y = EWC['Adj Close']
df = pd.DataFrame([w,x,y]).transpose()
df.columns = ['W','X','Y']
df.plot(figsize=(20,12))
from statsmodels.tsa.vector_ar.vecm import coint_johansen
y3 = df
j_results = coint_johansen(y3,0,1)
print(j_results.lr1)
print(j_results.cvt)
print(j_results.eig)
print(j_results.evec)
print(j_results.evec[:,0])
那么我应该通过将特征向量 [0.30.., 1.36.., -1.35..] 乘以每种工具的股价来构建一个投资组合,以获得 y_port 值。之后我 运行 进行相关性测试以确定该投资组合的每日价格变化与最后一天的价格变化之间的相关性,从而能够确定该系列的半衰期。
我只是通过将特征向量乘以收盘价来做到这一点,我不知道这是不是我出错的地方。
hedge_ratios = j_results.evec[:,0]
y_port = (hedge_ratios * df).sum(axis=1)
y_port.plot(figsize=(20,12))
y_port_lag = y_port.shift(1)
y_port_lag[0]= 0
delta_y = y_port-y_port_lag
X = y_port_lag
Y = delta_y
X = sm.add_constant(X)
model = OLS(Y,X)
regression_results = model.fit()
regression_results.summary()
然后我计算半衰期,大约是19天。
halflife = -np.log(2)/regression_results.params[0]
halflife
我根据账面说明定义要持有的单位数量(投资组合价值的 -Z 值,根据半衰期回溯 window 19 天)。
num_units = -(y_port-y_port.rolling(19).mean())/y_port.rolling(19).std()
num_units.plot(figsize=(20,12))
所以接下来我要采取的步骤是:
检查数据帧是否仍然正确。
添加之前计算的“要持有的单位数”,它是 y_port 值的负 Z 分数。
可能有更简单的乘法或执行此操作的方法,但我通过将工具价格乘以特征向量给出的对冲比率来计算我应该为每种工具持有的 $ 数量,按要持有的投资组合单位数量。
最后我通过乘以每日变化*我持有的单位数量来计算每个工具的盈亏。
结果很糟糕。就是从头输到尾。 ¿我哪里搞砸了? ¿如何正确乘以特征向量中的值,确定要持有的头寸数量,并正确创建投资组合?
如有任何帮助,我们将不胜感激。
- 我不知道为什么 num_units 系列是“水平”的,我不得不在将它附加到 DataFrame 之前转置它。
num_units = num_units.transpose()
df['Portfolio Units'] = num_units
df
df['W $ Units'] = df['W']*hedge_ratios[0]*df['Portfolio Units']
df['X $ Units'] = df['X']*hedge_ratios[1]*df['Portfolio Units']
df['Y $ Units'] = df['Y']*hedge_ratios[2]*df['Portfolio Units']
positions = df[['W $ Units','X $ Units','Y $ Units']]
positions
pnl = pd.DataFrame()
pnl['W Pnl'] = (df['W']/df['W'].shift(1)-1)*df['W $ Units']
pnl['X Pnl'] = (df['X']/df['X'].shift(1)-1)*df['X $ Units']
pnl['Y Pnl'] = (df['Y']/df['Y'].shift(1)-1)*df['Y $ Units']
pnl['Total PNL'] = pnl.sum(axis=1)
pnl['Total PNL'].cumsum().plot(figsize=(20,12))
我知道,如果我只是恢复我的位置(不在 y_port 中使用 -1),结果会改变,我会得到一个积极的 return。但是,我想知道我做错了什么。将 -Z 用于均值回归策略是有道理的,我想知道我在哪里犯了错误,这样我就可以跟上本书的其余部分,
我认为您需要将 df['W $ Units']、df['X $ Units'] 和 df['Y $ Units'] 也移动 1。例如,使用 df['Y $ Units'].shift(1) 而不是 df['Y $ Units']。
您收到的结果并不糟糕,而是不切实际。在不移动 df['... $ Units'] 的情况下,您正在展望未来并使用尚不可用的数据。
发现part4有些问题,修改如下:
positions = df[['W $ Units','X $ Units','Y $ Units']]
df5=df.iloc[:,0:3]
pnl=np.sum((positions.shift().values)*(df5.pct_change().values), axis=1)
ret=pnl/np.sum(np.abs(positions.shift()), axis=1)
plt.figure(figsize=(8,5))
plt.plot(np.cumprod(1+ret)-1)
print('APR=%f Sharpe=%f' % (np.prod(1+ret)**(252/len(ret))-1, np.sqrt(252)*np.mean(ret)/np.std(ret)))
因此我们有 APR=0.130122 Sharpe=1.518595 daily rets plot