ファイナンス機械学習:ラベリング 練習問題 一次モデルと二次モデル ボリンジャーバンド
E-mini S&P500先物ティックデータのドルバーに対し、ボリンジャーバンドからなる平均回帰戦略を作成する。
ラベリングで作成したスニペットを、Labels.pyとしてインポートして使う。
import pandas as pd
import numpy as np
from datetime import datetime
import Labels as labels
import Bars as bars
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix
from sklearn.metrics import roc_curve,auc
import matplotlib.pyplot as plt
%matplotlib inline
ドルバーの閾値は200,000とする。
一般的に、価格が移動平均より下からボリンジャーバンドの上$${+1\sigma}$$を突き抜けたら、順張りの買い、移動平均よりも上から下$${-1\sigma}$$に抜けたら、順張りの売りとされる。
def getGCrossBB(df):
crit1 = df.TP > df.BB_u
crit2 = df.TP.shift(1)<df.BB_u.shift(1)
return df.TP[(crit1) & (crit2)]
def getDCrossBB(df):
crit2 = df.TP < df.BB_d
crit1 = df.TP.shift(1) > df.BB_d.shift(1)
return df.TP[(crit1) & (crit2)]
DCross, GCross = getDCrossBB(ddf_BB), getGCrossBB(ddf_BB)
fig = plt.figure(figsize=(14, 8))
plt.plot(ddf_BB['TP'].loc['2018':],ls='-', label='price')
plt.plot(ddf_BB['BB_u'].loc['2018':],ls='-', label='+1sig')
plt.plot(ddf_BB['BB_d'].loc['2018':],ls='-', label='-1sig')
plt.plot(GCross.loc['2018':],ls='',marker='^',label='Gold')
plt.plot(DCross.loc['2018':],ls='',marker='v',label="Dead")
plt.legend()
plt.show
順張りで、ゴールデンクロスを買い、デッドクロスを売りとしてサイドに入れる。
利食いと損切りのバリアのptSlを$${[0,2]}$$とし、trgtには日次ボラティリティをいれCUSUMフィルタを適用する。また保有期間は1日とし、トリプルバリアを適用する。
buy = pd.Series(1, index=GCross.index)
sell = pd.Series(-1, index=DCross.index)
side = pd.concat([sell,buy]).sort_index()
minRet = .005
ptsl=[0,2]
span0=20
price=ddf['Close']
daily_vol=labels.getDailyVol(price,span0)
h=daily_vol.mean()
ddf_rtn = price.pct_change().dropna()
events=bars.getTEvents(ddf_rtn.loc[daily_vol.index[0]:], h) #CUSUM
t1 = labels.addVerticalBarrier(price, events, numDays=1)
BBPevents=labels.getEventsML(price,events,ptsl,daily_vol,minRet,t1=t1,side=side)
BBPevents=BBPevents.dropna()
ここで得たイベントにメタラベリングを行う。
BBlabels=labels.getBinsTUML(BBPevents,price,t1)
BBlabels.bin.value_counts()
ランダムフォレストモデルを訓練して、トレードを試行するかしないかを決定させる。特徴量は、日次ボラティリティ、日次リターンの系列相関、単純移動平均のクロス、ボリンジャーバンドの上下、TPとする。
単純移動平均のクロスからサイドを導出する。
short=5
long=20
ddfSMA=(pd.DataFrame()
.assign(price=price)
.assign(short=price.rolling(short).mean())
.assign(long=price.rolling(long).mean())).dropna()
DCrossSMA, GCrossSMA = labels.getDCross(ddfSMA), labels.getGCross(ddfSMA)
buySMA = pd.Series(1, index=GCrossSMA.index)
sellSMA = pd.Series(-1, index=DCrossSMA.index)
sideSMA = pd.concat([sellSMA,buySMA]).sort_index()
以下のコードで実装される日次リターンの系列相関をとる。
def getDailyCorr(close, span0):
df0 = close.index.searchsorted(close.index - pd.Timedelta(days=1))
df0 = df0[df0 > 0]
df0 = pd.Series(close.index[df0 - 1], index=close.index[close.shape[0] - df0.shape[0]:])
v=[]
for i in range(len(df0)):
v.append(close.loc[df0.index[i]] / close.loc[df0[i]] - 1)
#df0= close.loc[df0.index] / close.loc[df0.values] - 1 # daily returns
rtn=pd.Series(v,index=df0.index)
df0_corr = rtn.ewm(span=span0).corr(rtn.shift(1))
return df0_corr.dropna()
daily_corr=getDailyCorr(price,span0)
これらを特徴量$${\bf{X}}$$とし、yをメタラベルとする。
Xy = pd.DataFrame({'vol': daily_vol,
'side': sideSMA,
'corr': daily_corr,
'BBU':ddf_BB['BB_u'],
'BBD':ddf_BB['BB_d'],
'TP':ddf_BB['TP'],
'bin':BBlabels['bin'] }).dropna()
Xy = Xy[~Xy.index.duplicated(keep='first')]
これを使って、ランダムフォレストモデルを訓練し、テストデータでフィットする。max_depthとn_estimatorsはグリッドサーチで求めた値を使う。
X= Xy.drop('bin',axis=1).values
y = Xy['bin'].values.astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, shuffle=False)
rf = RandomForestClassifier(max_depth=4, n_estimators=800,class_weight='balanced',
criterion='entropy',random_state=0)
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
メタラベルをフィルタリングしない場合の混合行列と性能指標は以下の通りとなる。
ここでメタラベルをフィルタリングし、双方に1を出した事例が、真の陽性とする。この混合行列と性能指標は以下になり、精度が0.58から0.96に上がっている。
FinalPrediction = (y_pred & y_test)
print('Meta Label Metrics: ')
print(classification_report(y_true=y_test, y_pred=FinalPrediction))
print('Confusion Matrix')
confmat=confusion_matrix(y_test, FinalPrediction)
fig, ax = plt.subplots(figsize=(2.5, 2.5))
ax.matshow(confmat, cmap=plt.cm.Blues, alpha=0.3)
for i in range(confmat.shape[0]):
for j in range(confmat.shape[1]):
ax.text(x=j, y=i, s=confmat[i, j], va='center', ha='center')
plt.xlabel('Predicted label')
plt.ylabel('True label')
plt.tight_layout()
plt.show()
accuracy = (y_test == FinalPrediction).sum() / y_test.shape[0]
print('Accuracy: ', round(accuracy, 4))