-
Notifications
You must be signed in to change notification settings - Fork 2
Labeling
jaeaehkim edited this page Jun 8, 2022
·
5 revisions
- Data Structures 파트에선 Financial Raw Data가 어떤 종류 및 형태로 존재하는 지부터 IID한 형태의 구조화 Bar로 만들고, Bar를 Event-Based Sampling 하는 방법까지 연구를 진행하였다.
- 이젠 Data가 존재하는 상태에서 이 데이터를 어떻게 Labeling 하여 ML Model에게 전달해줄 것인가 라는 문제가 남았다. 지도학습의 특성상 Input은 (문제 X - 답지 y)로 구성해주어야 한다.
- 전반적으로 Trading, Finance Paper를 보면 대부분 Time Bar로 시작해서 일정 시간 구간의 Return을 가지고 0보다 크면 1, 아니면 -1 혹은 0이 아닌 기준으로 한다든지 혹은 3-4가지 class로 labeling 방식을 사용한다. 이번 장에선 ML Model이 사람의 생각을 특화해서 학습한다는 인사이트를 적용하여 Path-dependent한 Labeling 기법을 소개할 것이다.
- 대부분의 페이퍼는 "고정-시간 Horizon" 방식으로 Labeling을 시도한다. 직관적이나 단순하다. 이를 수식으로 general하게 표현하면 위와 같다.
- X는 어떤 feature vector라고 보면 된다. (ex. close price 쉽게) y는 시간 구간 내에서의 수익률(r_(t~t+h))의 임계값(tau)에 따른 label
-
문제점
- 일단 Fixed-Time을 쓰기 위해선 기초 Bar가 Time Bar여야 한다. 이는 Data Structures 에서 봤듯 좋은 통계적 성질 (IID)을 갖지 못하는 것에서 부터 문제가 된다.
- 추가로, Fixed-Time으로 Bar를 구성하게 되면 모든 시간에 동일한 정보가 있다고 가정하는 오류를 범하는데 이로 인해 변동성이 적은 많은 구간에선 label이 임계값을 넘지 못하게 될 것이고(대부분 label=0) ML Model이 Accuracy만 높이는 방향으로 학습될 가능성이 높다. 즉, 실질적 유효성이 떨어진다.
-
기본 해결책
- 임계값 tau를 상수로 고정하지 않고 return's rolling std를 활용하여 변동하는 임계값 sigma_t를 활용하는 것이다. 그럼 시기별로 적절하게 임계를 넘는 label=1이 생성될 것이다.
- 처음부터 Volume/Dollar Bar를 활용하여 constant한 임계값 tau를 사용하는 것이다. 이미 Bar 자체에 거래량/거래대금에 비례해 정보가중이 되어 있기 때문에 constant한 tau여도 좀 더 낫게 labeling이 될 수 있다.
- AFML(Advances in Financial Machine Learning)에서 Marcos Lopez 교수가 소개한 Labeling 기법이다. 이를 Triple-Barrier Method(트리플 배리어 기법)라 부르자.
- 해당 기법은 2개의 Horizontal Barrier(수평선)과 1개의 Vertical Barrier(수직선)을 정의하는 것이다.
- 투자 시점 t에서 직사각형의 Barrier Box를 그릴 수 있고, price의 움직임이 어떤 barrier를 닿는 그 순간의 값으로 labeling 하는 방식이다.
-
즉, 동적으로 타임컷, 익절, 손절의 라인을 잡아주는 그 general한 행위를 labeling 함으로써 ML Model이 이를 동적으로 계산한 inference 값을 내게 해준다.
- 이때, bar 첫 시작점에서 수평선2개와 수직선 1개 중에서 처음 만난 부분으로 끊어서 labeling을 진행한다.
- 이를 이해하기 쉽게 그림으로 표현하면 다음과 같다.
def getTEvents(gRaw, upper, lower=None):
if lower is None:
lower = - upper
assert (upper >=0 and lower <= 0)
tEvents, sPos, sNeg = [], 0, 0
diff = gRaw.diff()
for i, change in enumerate(diff.values[1:]):
sPos, sNeg = max(0, sPos + change), min(0, sNeg + change)
if sNeg < lower:
sNeg = 0
tEvents.append(i)
if sPos > upper:
sPos = 0
tEvents.append(i)
return gRaw.index[tEvents]
-
- Data Structures 에서 VB/DB or TIB/VIB/DIB or TRB/VRB/DRB의 형태의 Bar로 만들고 CUSUM Filter를 이용해서 해당 시점의 T index를 추출
def getBins(events, price):
"""
refer to the explanation before snippet 3.5
:param events: returned value of getEvents, columns=['trgt','tl']
:param price: volume_df.price
:return: columns=['ret', 'bin']
"""
events_ = events.dropna(subset=['tl'])
px = events_.index.union(events_['tl'].values).drop_duplicates()
px = price.reindex(px, method='bfill')
out = pd.DataFrame(index=events_.index)
out['ret'] = px.loc[events_['tl'].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
return out
- getBins를 통해 최종적인 Triple Barrier Method의 output이 전달됨. 중간 과정에 add Vertical Barrier로 수직선 정보, add Horizontal Barrier로 수평선 정보를 반영하여 이 둘을 반영한 데이터를 getEvents에서 가공하여 getBins로 전달함.
- getBins의 output으로 추가적인 feature를 가공하고 이를 ML Model에 학습시킴.
def getEvents(price, tEvents, ptSl, trgt, minRet, numThreads, tl=False, side=None):
"""
refer to the explanation before and after snippet 3.3, with enhancements explained right before snippet 3.6
:param price:
:param tEvents:
:param ptSl:
:param trgt: dailyVol(horizontal bar unit width), index = price.index which contains tEvents
:param minRet: at last dailyVol must be larger than this scalar
:param numThreads:
:param tl: index = tEvents
:param side: side
:return: index=subset of trgt.index(tEvents), columns=['tl','trgt', 'side'(if side is not None)] where 'tl' is not vertical bar timestamp but first-time for barrier touch
"""
trgt = trgt.loc[trgt.index.isin(tEvents)]
trgt = trgt[trgt > minRet]
if tl is False:
tl = pd.Series(pd.NaT, index=tEvents)
if side is None:
side_, ptSl_ = pd.Series(1., index=trgt.index), [ptSl[0],ptSl[0]]
else:
side_, ptSl_ = side.loc[trgt.index], ptSl[:]
events = pd.concat({'tl': tl, 'trgt': trgt, 'side': side_}, axis=1).dropna(subset=['trgt'])
df0 = mpPandasObj(func=applyPtSlonTl, pdObj=('molecule', events.index), numThreads=numThreads, price=price, events=events, ptSl=ptSl_)
events['tl'] = df0.dropna(how='all').min(axis=1)
if side is None:
events = events.drop('side', axis=1)
return events
- 최종 getBins에서 나온 것과 bin column의 임계점을 활용한 labeling 차이만 존재한다. getEvents는 Vertical/Horizontal Barrier를 반영한 최종 정보를 산출한다.
def addVerticalBarrier(tEvents, price, numDays=None):
"""
refer to snippet 3.4, in book, it resorts to pd.Timedelta(days=Numdays), but here we assume general-bars, which are not necessarily time-bars, so I set h=10
:param tEvents: getTEvents(returns(volume_df.price), threshold) = pd.DateTimeIndex(sampled with cumsum)
:param price: whole data before sampling, volume_df.price with index=np.datetime64
:param horizon: vertical barrier size, initialization=10
:return: Series with values of tl timestamp and index of tEvents timestamp
"""
tl = price.index.searchsorted(tEvents) + 10 if numDays is None else price.index.searchsorted(tEvents+pd.Timedelta(days=numDays))
tl = tl[tl < len(price)]
return pd.Series(price.index[tl], index=tEvents[:len(tl)])
- getTEvents의 T Index만 있는 값에서 아무리 손익절(ptsl:profit taking / stop loss)이 안 걸리더라도 최대로 Time cut으로 끊어주어 시작점(index)와 마지막점(vertical)이 존재하는 output이 나온다.
def applyPtSlonTl(price, events, ptSl, molecule):
"""
refer to the explanation before snippet 3.2
:param price: volume_df.price
:param events: index=tEvents, columns=['tl','target'(dailyvol), 'side']
:param ptSl: horizontal bar width ratio
:param molecule: a list with the subset of event indices that will be processed by a single thread
:return: index=tEvents, columns=['pt', 'sl'], first time touching each barrier
"""
events_ = events.loc[molecule]
out = events_[['tl']].copy(deep=True)
if ptSl[0] > 0:
pt = ptSl[0]*events_['trgt']
else:
pt = pd.Series(index=events.index) # NaNs
if ptSl[1] > 0:
sl = -ptSl[1]*events_['trgt']
else:
sl = pd.Series(index=events.index)
for loc, tl in events_['tl'].fillna(price.index[-1]).iteritems():
df0 = price[loc:tl]
df0 = (df0 / price[loc] - 1) * events_.at[loc,'side']
out.loc[loc, 'sl'] = df0[df0 < sl[loc]].index.min()
out.loc[loc, 'pt'] = df0[df0 > pt[loc]].index.min()
return out
- vertical은 일단 생각하지 않고 익절(pt),손절(sl)라인을 최근 변동성을 활용하여 동적으로 잡아주는 역할을 하고 첫 번째 도달했던 시점을 기록한다.
- Triple-Barrier를 활용해서 Labeling을 하고 이를 ML Model에 적용하는 경우에는 ML Model의 Inference로 side(Long/Short), size(probability)를 출력할 수 있다. 이를 그대로 활용해도 된다. Meta-Labeling의 목적은 하나의 모델로 처리될 수 있는 프로세스를 Decomposition 해서 설계하여 Model의 Metric 중에서 F1-Score를 극대화 시킴에 있다. 이것은 side,size를 결정하는 모델 안에서의 decomposition을 저서에서 얘기하고 있지만 투자하는 프로세스의 모든 부분에서 Meta-Labeling화 시킬 수 있고 decomposition된 모든 부분이 ML Model일 필요도 없다고 생각한다. (Quantametal Way)
- Meta-Labeling : Detailed decomposition of model
- label을 1차적으로 min return 값을 이용해 1,0으로 붙여서 베팅 여부를 결정
- pt,sl 수치
- Question
- model을 decomposing해서 운영하는게 무조건 좋을까?
- 모델 관리 비용이 많이 들어감.
- 하나의 model 마다 어떻게 labeling 할 것인지, feature들을 어떻게 관리할 것인지 etc
- single-labeling에 이미 다 포함된 것을 잘게 나누어서 세부적으로 하는 것 뿐이기 때문에 main model을 triple-barrier method를 활용해서 train 한 이후에 나머지 부분은 rule-based로 처리하는게 나을 것이라는게 개인적인 생각.
- 다양한 1-config = 1-strategy 를 찾아내어 ensemble 시키는 방식
- rule에서 필요한 hyper parameter 값들을 config에 포함시키는 방식으로 설계하여 최대한 model search를 할 때 넓은 영역을 탐구할 수 있도록 진행
- pt,sl 비대칭
- betting model : single prob betting, average prob betting etc
- ensemble 시킬 때 거래비용/회전율을 극대화시킬 수 있는 방식으로 설계해야 함.
- 단일 전략 시뮬레이터 / 메타 전략 시뮬레이터 -> Point in Time 방식
- rule에서 필요한 hyper parameter 값들을 config에 포함시키는 방식으로 설계하여 최대한 model search를 할 때 넓은 영역을 탐구할 수 있도록 진행
- Model의 Metric과 Backtest의 Metric을 구별하는 방식으로 어느정도 Meta-Labeling 기법이 스며들어 갈 것이라고 생각. Meta-Labeling은 비용과 효용간의 Trade-off 관계에 있으므로 적당한 선에서 Decision을 내려줘야 함.
- Meta-Labeling을 확장시킨 개념이다. 결국엔 Meta-Labeling은 정량화된 것 안에서 더 세부적으로 쪼개들어가는 것을 얘기한다면, Quantamental Way는 비정량화된 모든 경험을 정량화 하는 과정을 얘기하는 것이다. 정량화는 헤지펀드가 일하는 프로세스에서 발생하는 모든 의사결정에 적용될 수 있으며 회사 내부에서 얼마나 많은 부분이 정량화 되었는지가 매우 중요한 시대가 올 것이다.
- Lagbeling을 첫 barrier가 touch 하는 부분으로만 할 필요가 있을까?
- barrier touch하는 것도 다양하게 한다면 다양한 모델이 만들어질 것으로 보임.