62 lines
2.1 KiB
Python
62 lines
2.1 KiB
Python
# Bollinger Band Mean Reversion Strategy
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
#import matplotlib.pyplot as plt
|
|
import statsmodels.formula.api as sm
|
|
import statsmodels.tsa.stattools as ts
|
|
#import statsmodels.tsa.vector_ar.vecm as vm
|
|
|
|
df=pd.read_csv('inputData_GLD_USO.csv')
|
|
df['Date']=pd.to_datetime(df['Date'], format='%Y%m%d').dt.date # remove HH:MM:SS
|
|
df.set_index('Date', inplace=True)
|
|
|
|
lookback=20
|
|
hedgeRatio=np.full(df.shape[0], np.nan)
|
|
for t in np.arange(lookback, len(hedgeRatio)):
|
|
regress_results=sm.ols(formula="USO ~ GLD", data=df[(t-lookback):t]).fit() # Note this can deal with NaN in top row
|
|
hedgeRatio[t-1]=regress_results.params[1]
|
|
|
|
yport=np.sum(ts.add_constant(-hedgeRatio)[:, [1,0]]*df, axis=1)
|
|
yport.plot()
|
|
|
|
# Bollinger band strategy
|
|
entryZscore=1
|
|
exitZscore=0
|
|
|
|
MA=yport.rolling(lookback).mean()
|
|
MSTD=yport.rolling(lookback).std()
|
|
zScore=(yport-MA)/MSTD
|
|
|
|
longsEntry=zScore < -entryZscore
|
|
longsExit =zScore > -entryZscore
|
|
|
|
shortsEntry=zScore > entryZscore
|
|
shortsExit =zScore < exitZscore
|
|
|
|
numUnitsLong=np.zeros(longsEntry.shape)
|
|
numUnitsLong[:]=np.nan
|
|
|
|
numUnitsShort=np.zeros(shortsEntry.shape)
|
|
numUnitsShort[:]=np.nan
|
|
|
|
numUnitsLong[0]=0
|
|
numUnitsLong[longsEntry]=1
|
|
numUnitsLong[longsExit]=0
|
|
numUnitsLong=pd.DataFrame(numUnitsLong)
|
|
numUnitsLong.fillna(method='ffill', inplace=True)
|
|
|
|
numUnitsShort[0]=0
|
|
numUnitsShort[shortsEntry]=-1
|
|
numUnitsShort[shortsExit]=0
|
|
numUnitsShort=pd.DataFrame(numUnitsShort)
|
|
numUnitsShort.fillna(method='ffill', inplace=True)
|
|
|
|
numUnits=numUnitsLong+numUnitsShort
|
|
positions=pd.DataFrame(np.tile(numUnits.values, [1, 2]) * ts.add_constant(-hedgeRatio)[:, [1,0]] *df.values) # [hedgeRatio -ones(size(hedgeRatio))] is the shares allocation, [hedgeRatio -ones(size(hedgeRatio))].*y2 is the dollar capital allocation, while positions is the dollar capital in each ETF.
|
|
pnl=np.sum((positions.shift().values)*(df.pct_change().values), axis=1) # daily P&L of the strategy
|
|
ret=pnl/np.sum(np.abs(positions.shift()), axis=1)
|
|
pd.DataFrame((np.cumprod(1+ret)-1)).plot()
|
|
|
|
print('APR=%f Sharpe=%f' % (np.prod(1+ret)**(252/len(ret))-1, np.sqrt(252)*np.mean(ret)/np.std(ret)))
|