ファイナンス機械学習:メタラベリング
売りか買いかのサイドの取り方は、SWMAやBB、MACDなどの他のモデル(以下第一のモデルとする)で与えられているとする。
しかしこれらのモデルは、いくら買うか(売るか)のサイズに関しては何も教えてはくれないので、機械学習アルゴリズムに求めるのは、適切なベットサイズである。
第一のモデルが出す結果に、利益が出た場合は1、損失の場合は0とラベルをつけ、この第一モデルがいかに成功するかを予測し、サイズだけを学習する第二の機械学習モデルを作るのが目的である。
前記事でのgetEventsを第一モデルによるメタラベリングも扱えるように修正する。第一モデルによって決定されたサイドを受け取り(注:デフォルトはNone)、メタラベリングが有効であるとする。
水平バリアの位置を決定する係数のptSlは、今はサイドが既知のため、非負の二つの実数リストで、ゼロであった場合は、そのバリアが無効とされる。
この拡張はスニペット3.6で与えられている。
def getEventsML(close, tEvents, ptSl, trgt, minRet, t1=False,side=None):
trgt = trgt.loc[tEvents]
trgt = trgt[trgt > minRet]
if t1 is False:
t1 = pd.Series(pd.NaT, index=tEvents)
if side is None:
side_, ptSl_ = pd.Series(np.array([1.] * len(trgt.index)), index=trgt.index), [pt_sl[0], pt_sl[0]]
else:
side_, ptSl_ = side.loc[trgt.index], pt_sl[:2]
events = pd.concat({'t1': t1, 'trgt': trgt, 'side': side_}, axis=1).dropna(subset=['trgt'])
df0 = applyTPBarrier(close, events, ptSl_)
events['t1'] = df0.dropna(how='all').min(axis=1)
if side is None:
events = events.drop('side', axis=1)
return events
同様に、getBinsもメタラベリングを扱えるように拡張し、出力は$${0,1}$$であり、ベットするかしないかの判断となる。
def getBinsTUML(events,close,t1):
'''
Generating labels with possibility of knowing the side (metalabeling)
Parameters:
close (pd.Series): close prices of bars
events (pd.DataFrame): dataframe returned by 'get_events' with columns:
- index: event starttime
- t1: event endtime
- trgt: event target
- side (optional): position side
t1 (pd.Series): series with the timestamps of the vertical barriers (pass False
to disable vertical barriers)
Returns:
out (pd.DataFrame): dataframe with columns:
- ret: return realized at the time of the first touched barrier
- bin: if metalabeling ('side' in events), then {0, 1} (take the bet or pass)
if no metalabeling, then {-1, 1} (buy or sell)
'''
#1) prices aligned with events
events_=events.dropna(subset=['t1'])
px=events_.index.union(events_['t1'].values).drop_duplicates()
px=close.reindex(px,method='bfill')
#2) create out object
out=pd.DataFrame(index=events_.index)
out['ret']=px.loc[events_['t1'].values].values/px.loc[events_.index]-1
if 'side' in events_:
out['ret'] *= events_['side']
out['bin']=np.sign(out['ret'])
if 'side' in events_:
out.loc[out['ret'] <= 0, 'bin'] = 0
else:
if t1 is not None:
vertical_first_touch_idx = events_[events_['t1'].isin(t1.values)].index
out.loc[vertical_first_touch_idx, 'bin'] = 0
return out
ここでも、マルチスレッド化はしていない。このコードをそのまま大型データに対して使うのは避けるべきである。