본문 바로가기
개발일지

파이썬 투자 보조 지표 분석 / 전략 설계 - MFI

by kirion 2022. 11. 21.
728x90
반응형

주식 투자지표 MFI 파이썬 구현, 벡테스팅

 

투자 전략의 설계를 위해 앞으로 투자지표를 하나씩 소개하고자 한다. 

투자지표의 개요, 전략, 파이썬 구현, 벡테스팅까지 해서 전략을 검증하고 이해하는 것에 초점을 둔다.

 

첫 번째 글로 MFI에 대해 알아보자

 

MFI지표는 RSI지표에 거래량을 합산하여 계산되는 지표이다. 즉, RSI는 주가만 반영이 되었는데 여기에 거래량까지 추가되었기 때문에 한 단계 발전된 지표라 할 수 있다.

 

계산 공식은 다음과 같다.

평균 가격 = (고가 + 저가 + 종가) /3

MF(Money Flow) = 거래량 * 평균 가격

PMF(Positive Money Flow) = 현재 가격이 전일 가격보다 상승하였을 때의 합계

NMF(Negative Money Flow) = 현재 가격이 전일 가격보다 하락하였을 때의 합계

 

MR = PMF / NMF

MFI = MR / (1 + MR)

 

RSI와 동일하게 14일을 기준으로 계산한다. 0~100 사이의 값이 산출될 것이다.

 

투자전략은 RSI와 비슷하다.

MFI의 기본 전략은

  • 80 이상으로 올라갔다가 80 이하로 내려오면 매도
  • 20 이하로 내려갔다가 20 이상으로 올라가면 매수 

또는 다른 전략으로 

  • 50 이하 돌파시 매도
  • 50 이상 돌파시 매수

이렇게 간단한 전략이다.

나는 이중 기본 전략을 활용해 벡테스팅을 해볼 것이다.

 

그럼 MFI 지표가 어떤 모습으로 나타나는지 파이썬 mfi를 계산 후 시각화해보자.

 

import ta
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import FinanceDataReader as fdr
df_krx = fdr.StockListing('KRX')

kospi_data = df_krx[(df_krx["Market"]=="KOSPI") & (~df_krx["Sector"].isna())]

target = kospi_data[kospi_data["Name"]=="KT&G"]
target

price_data = fdr.DataReader("033780","2020","2022-11-17")

KT&G를 대상으로 RSI와 MFI를 계산해서 시각화해보겠다.

 

mfi = ta.volume.MFIIndicator(price_data["High"],price_data["Low"],price_data["Close"],price_data["Volume"]).money_flow_index()

rsi = ta.momentum.RSIIndicator(price_data["Close"]).rsi()

price_data["rsi"] = rsi
price_data["mfi"] = mfi

투자지표를 계산하려고 찾아보다 발견했는데 파이썬에서 사용 가능한 ta 패키지가 투자지표들을 계산해 준다고 한다.

하나씩 구현하는 것이 가장 좋지만, 단순 비교를 위해 사용하거나 계산이 너무 복잡한 지표 같은 경우는 활용하면 편리할 것 같다. 

ta패키지의 MFIIndicator와 RSIIndicator함수를 이용해 지표를 계산해 주었다.

 

price_data_tmp = price_data[price_data.index>"2022"].copy()

여기서는 너무 많은 데이터를 넣으면 보기 어려움으로 2022년도 1년 치만 확인한다.

 

price_data_tmp["lower1"] = 20
price_data_tmp["lower2"] = 30
price_data_tmp["upper1"] = 80
price_data_tmp["upper2"] = 70

RSI는 30,70선을 이용했고, MFI는 20,80선을 이용했기 때문에 비교를 위해 해당 선을 그려준다.

fig, ax1 = plt.subplots(figsize = (14,7)) 
ax1.set_ylabel('Price') 
ax1.plot(price_data_tmp["Close"],color = "black",alpha = 0.6)
# ax1.set_ylabel('Volume') 
# ax1.plot(price_data_tmp["Volume"],color = "black",alpha = 0.6)

ax2 = ax1.twinx() 
ax2.set_ylabel('Index') 

ax2.plot(price_data_tmp["rsi"],color = "orange",alpha = 0.8)
ax2.plot(price_data_tmp["mfi"],color = "blue",alpha = 0.8)

ax2.plot(price_data_tmp["lower1"],color = "tab:blue",alpha = 0.5)
ax2.plot(price_data_tmp["lower2"],color = "tab:blue",alpha = 0.3)
ax2.plot(price_data_tmp["upper1"],color = "tab:red",alpha = 0.5)
ax2.plot(price_data_tmp["upper2"],color = "tab:red",alpha = 0.3)

ax2.legend(["rsi","mfi"],loc = "upper right")
plt.title("KT&G")
plt.show()

주가와 지표 비교
거래량과 지표 비교

RSI와 MFI 흐름을 보면 전체적인 트렌드는 유사함을 볼 수 있다. 두 지표 모두 주가를 기반으로 하기 때문이다. 하지만 거래량이 많고 적은 날에 따라 MFI 지표가 출렁이는 것을 볼 수 있다.

그런데 80선 이상으로 오르는 매도신호가 과도하게 많이 나오는 것을 볼 수 있다. 

 

몇 개 종목의 그래프만 봐서는 MFI 지표가 나은지 모르기 때문에 손절선에 따른 벡테스팅으로 과연 MFI 지표가 RSI보다 손익률이 나을지 검증해보자. 

MFI 지표도 0~100 사이 값을 가지기 때문에 기존 벡테스팅 코드를 활용할 수 있었다.

벡테스팅 코드는 아래 글을 참고 바란다.

2022.11.07 - [개발일지] - 3. 투자지표를 활용한 매매 시점 모니터링 - 벡테스팅

2022.11.08 - [개발일지] - 5. 투자지표를 활용한 매매 시점 모니터링 - Kospi 종목 벡테스팅

2022.11.09 - [개발일지] - 6. 투자지표를 활용한 매매 시점 모니터링 - 손절선

 

시드 머니 1,000만원 1회 매매 기준 금액 500만원으로 2012년부터 벡테스팅한 결과이다.

손절선 RSI MFI
손절선 X
  • 손익 확률 + : 57.73%, - : 42.27%
  • 최대 손익률 + : 30.14%, - 66.8%
  • 손익 + 연평균 수익률
    중위수 : 5.17%, 평균 : 5.96%
  • 손익 - 연평균 수익률
    중위수 : -4.45%, 평균 : -7.29%
  • 기대 수익금
    중위수 : 289만원, 평균 : 254만원
  • 손익 확률 + : 59.07%, - : 40.93%
  • 최대 손익률 + : 36.09%, - 58.94%
  • 손익 + 연평균 수익률
    중위수 : 4.91%, 평균 : 5.58%
  • 손익 - 연평균 수익률
    중위수 : -4.02%, 평균 : -6.42%
  • 기대 수익금
    중위수 : 278만원, 평균 : 237만원
-3.5%
  • 손익 확률 + : 69.43%, - : 30.57%
  • 최대 손익률 + : 17.21%, - 24.15%
  • 손익 + 연평균 수익률
    중위수 : 3.09%, 평균 : 3.71%
  • 손익 - 연평균 수익률
    중위수 : -1.95%, 평균 : -3.36%
  • 기대 수익금
    중위수 : 177만원, 평균 : 174만원
  • 손익 확률 + : 56.88%, - : 43.0%
  • 최대 손익률 + : 33.29%, - 24.72%
  • 손익 + 연평균 수익률
    중위수 : 2.62%, 평균 : 3.36%
  • 손익 - 연평균 수익률
    중위수 : -2.18%, 평균 : -3.08%
  • 기대 수익금
    중위수 : 97만원, 평균 : 123만원
-10%
  • 손익 확률 + : 59.56%, - : 40.44%
  • 최대 손익률 + : 30.14%, - 43.2%
  • 손익 + 연평균 수익률
    중위수 : 3.56%, 평균 : 4.28%
  • 손익 - 연평균 수익률
    중위수 : -2.48%, 평균 : -4.38%
  • 기대 수익금
    중위수 : 197만원, 평균 : 160만원
  • 손익 확률 + : 54.93%, - : 45.07%
  • 최대 손익률 + : 32.84%, - 23.36%
  • 손익 + 연평균 수익률
    중위수 : 3.26%, 평균 : 4.0%
  • 손익 - 연평균 수익률
    중위수 : -3.6%, 평균 : -4.46%
  • 기대 수익금
    중위수 : 72만원, 평균 : 114만원

손절선에 따라 요약 비교한 표이다.

 

역시 MFI 지표는 80선 이상으로 과도하게 많은 매도 시그널이 나오기 때문에 매매 타이밍이 매우 짧게와 수익이 좋지 않은 것을 볼 수 있다. 

손절선을 가져갔을 때 손익 확률이 더 안 좋다는 것은 매수 시그널이 엉뚱한 위치에서 잡히는 것 같다.

왼쪽 RSI, 오른쪽 MFI

손절선 10%를 적용한 RSI(30,70) 전략과 MFI(20,80) 전략을 비교해 보면 전혀 다른 양상을 띄고 있다.

최종 연평균 수익률도 RSI 7% MFI 2%로 차이가 많이 난다.

MFI가 RSI의 업그레이드 전략이라고 하는데.. 벡테스팅만으로는 잘 모르겠다. 아마 RSI에는 최근 날짜일수록 지표에 가중하는 계산이 들어갔고, MFI에는 없어서 그런 것 같다. 

 

그래서 만들었다.

최근 날짜에 가중을 둘 수 있도록 지수이동평균을 반영한 MFI 수정 지표를.

def mfi_f(ohlc: pd.DataFrame,y_label1,y_label2,y_label3,y_label4, period: int = 14): 
    mean_price = (ohlc[y_label1] + ohlc[y_label2] + ohlc[y_label3])/3
    MF = mean_price * ohlc[y_label4]
    delta = mean_price.diff() 
    delta[delta<=0] = False
    delta[delta>0] = True
    mean_price = pd.concat([mean_price,delta],axis = 1)
    mean_price.columns = ["mean","delta"]
    mean_price["MF"] = mean_price["mean"]  * price_data["Volume"]
    ups, downs = mean_price["MF"].copy(), mean_price["MF"].copy() 
    ups[mean_price["delta"] == False] = 0 
    downs[mean_price["delta"] == True] = 0 
#     PMF = ups.rolling(period).sum()
#     NMF = downs.rolling(period).sum()
    PMF = ups.ewm(com = period-1, min_periods = period).mean() 
    NMF = downs.abs().ewm(com = period-1, min_periods = period).mean()
    MR = PMF/NMF
    return pd.Series(100 - (100/(1 + MR)), name = "MFI")

주석 처리된 부분으로 PMF, NMF를 계산하면 MFI 값과 동일하게 나오고 위와 동일하고 계산하면 MFI 수정 지표가 나온다.

RSI, MFI, MFI 수정지표와 가격, 거래량 비교

초록색 선이 MFI 수정 지표인데 MFI보다 조금 완화된 추세를 보인다.

SK하이닉스를 20, 80선으로 적용해서 벡테스팅해보면

이런 결과가 나온다. 거래량이 확 줄었다. 그래도 매수 포인트는 제대로 잡아 연평균 수익률 4.66 %를 기록했다. 거래 횟수가 적음에도 기존 2프로의 두배가 넘는 수치이다.

 

가장 성과가 안 좋았던 손절선 -10%로 Kospi 전체 종목을 벡테스팅해보겠다.

RSI MFI MFI 수정지표
  • 손익 확률 + : 59.56%, - : 40.44%
  • 최대 손익률 + : 30.14%, - 43.2%
  • 손익 + 연평균 수익률
    중위수 : 3.56%, 평균 : 4.28%
  • 손익 - 연평균 수익률
    중위수 : -2.48%, 평균 : -4.38%
  • 기대 수익금
    중위수 : 197만원, 평균 : 160만원
  • 손익 확률 + : 54.93%, - : 45.07%
  • 최대 손익률 + : 32.84%, - 23.36%
  • 손익 + 연평균 수익률
    중위수 : 3.26%, 평균 : 4.0%
  • 손익 - 연평균 손해률
    중위수 : -3.6%, 평균 : -4.46%
  • 기대 수익금
    중위수 : 72만원, 평균 : 114만원
  • 손익 확률 + : 55.18%, - : 37.15%
  • 최대 손익률 + : 57.22%, - 17.28%
  • 손익 + 연평균 수익률
    중위수 : 2.49%, 평균 : 3.09%
  • 손익 - 연평균 손해률
    중위수 : -1.63%, 평균 : -2.12%
  • 기대 수익금
    중위수 : 127만원, 평균 : 162만원

거래 횟수가 매우 줄어들어 전체적인 연평균 손익률도 줄어들었지만, 손해률이 굉장히 안정적으로 바뀐것을 볼 수 있다.

최대 손익률도 차이크지만, 평균과 중위수가 1~2% 대로 절반이 넘게 줄어들었기 때문에 기대수익금이 꽤 상승했다.

RSI 보다 더 나은 전략이라고는 말하기 어렵겠지만, 안정적으로 가져갈 수 있는 전략으로 보인다.(거래 횟수가 적지만, 손해가 날 종목에 또는 무분별한 투자를 하지 않았다고도 생각이 된다)

 

그나저나.. 벡테스팅 한 번 실행하면 약 1시간이 걸린다. 최적화 방안을 고민해봐야겠다.

728x90
반응형

댓글