본문 바로가기
Trading/Strategies

ALT - Mean reversion Strategy

by bents 2020. 11. 27.
# Mean reversion 전략이란?

 

price of an asset is prone to random fluctuation around an underlying stable trend. Therefore, values deviating far from the trend or observed mean will tend to reverse direction and revert to the mean. If the value is unusually high, we expect it to go back down and go up if it is unusually low.

 

## 가정

- 시계열 데이터가 과거의 평균값으로 회귀하려는 경향이 있습니다.

- 시계열 데이터가 정규 분포를 따른다고 가정합니다. 

# Mean reversion전략의 핵심은?

 

1) 회귀하는 평균추세선을 찾고,

2) 그로부터 현재가격까지의 거리를 표준화하는 것.

 

ex.

1) 평균추세선

- 가격의 누적평균선 ( 가격이 오르지 않는 주식이 얼마나 있을까? )

- 가격의 120일 이동평균선 

- 자기상관모델

2) 거리 

- z score : (price - mean)/std

 

# Mean revserion사용전, 적합성 검사하기

1) 정규성 테스트 : 허스프 지수 , ADF 테스트

2) Half-life 계산하기** 

- 평균회귀까지 걸리는 시간단위가 작을수록 '평균회귀전략'이 유용하지 않을까?

ex. 장기추세 (모멘텀 전략!) : get_half_life(data['Adj Close']['RXI'].dropna()) # 330일!

ex. 평균회귀 (회귀 전략!) : get_half_life(data['Adj Close']['IXM'].dropna()) # 43일!

def get_half_life(df):
    price = pd.Series(df)
    lagged_price = price.shift(1).fillna(method="bfill")
    delta = price - lagged_price
    beta = np.polyfit(lagged_price, delta, 1)[0]
    half_life = (-1*np.log(2)/beta)

    return half_life

# Mean reverion 전략의 활용

single stock 은 시장변동성과 개별주식의 이슈 등 개별 팩터에 의한 리스크에 많이 노출되어있다. 변동성/리스크 큼.

 

## 시장중립 롱숏 포트폴리오 만들기

--> 리밸런싱 주기 계획하기

--> 주기마다 평균상회 수익률군은 매도포지션, 평균하회 수익률군은 매수포지션

# SP500 Sector etf 사용하기
def get_yield_of_portfolio(TS, wstart, wend, mstart, mend):
  # For each security, take the return for the first week
  wreturns = (TS[wend] - TS[wstart])/TS[wstart]
  # Rank securities by return, with 0 being the lowest return
  ranks = order.argsort()

  # For each security, take the return for the month following the first week
  # Normalization for the time period doesn't matter since we're only using the returns to rank them
  mreturns = (TS[mend] - TS[mstart])/TS[mstart]

  # Go long (by one share each) in the bottom 20% of securities and short in the top 20%
  longs = np.array([int(x < 2) for x in ranks])
  shorts = np.array([int(x > 7) for x in ranks])
  
  # # Resolve all positions and calculate how much we would have earned
  # print('Yield:', sum((TS[mend] - TS[mstart])*(longs - shorts)))

  return sum((TS[mend] - TS[mstart])*(longs - shorts))


# performance 측정하기
result = []
for _ in range(100):
  starting_point = np.random.randint(0,220)
  yield_dict = {}
  for i in range(starting_point,len(TS)-26, 21):
    forward_period = (i+4), (i+22)
    ans = get_yield_of_portfolio(TS, i, i+4, forward_period[0], forward_period[1])
    yield_dict[i]=ans
  result.append(sum(yield_dict.values()))

print('sharp ratio : ', (np.mean(result)-0.01)/np.std(result))

plt.plot([i for i in range(len(result))],result )
100 Random Cases of accumulative performance Distribution of performance 

 

## Pair trading 하기

--> 공적분관계가 높은 주식 그룹/짝을 찾는다.

from statsmodels.tsa.stattools import coint
_, pvalue, _ = coint(X,Y)

--> 스프레드, 평균 스프레드, z-score 계산한다.

# spread(difference), accumulative mean of spread, moving average
val = pd.DataFrame(index = X.index, columns=['diff','mu'])
val['diff'] = X - Y
val['mu']= [val['diff'][:i].mean() for i in range(len(val['diff']))]
# val['mu'] = val['diff'].ewm(50).mean()
# val['mu'] = val['diff'].rolling(50).mean()

# Compute the z-score of the difference on each day
val['zscores'] = [(val['diff'][i] - val['mu'][i]) / np.std(val['diff'][:i]) for i in range(len(val['diff']))]
val = val.dropna()[50:]

--> 평균범위 이탈/회귀에 따른포지션 진입/청산하기.

for i in range(len(val['diff'])):
    # Sell short if the z-score is > 1
    if zscores[i] > 1:
        money += val['diff'][i]
        count -= 1
    # Buy long if the z-score is < 1
    elif zscores[i] < -1:
        money -= val['diff'][i]
        count += 1
    # Clear positions if the z-score between -.5 and .5
    elif abs(zscores[i]) < 0.5:
        money += count*val['diff'][i]
        count = 0
30~500 이동평균선 사용시, 샤프지수30~500 이동평균선 사용시, 절대 수익금

누적평균 이외에 단순/지수 이동평균을 적용해봄.
주기(windows)를 25부터 500까지 적용한 share ratio / 절대수익금 계산했다.
*지수이동평균을 사용하거나 윈도우크기가 작은 단순이동평균의 경우, inf 뜨거나 nan많음. 최신자료에 편향됨.
--> 결론) 장기적으로 statioinarity가 보장하도록 모델을 만든다면 모를까. 이렇게 대충 만든 모델을 그대로 사용하다면, 절대 샤프지수도 별반 차이나지 않고, 절대수익금은 높은 100미만의 단순이동평균을 사용해보자.