[python] 볼린저 밴드(Bollinger Bands) - (3) MFI(현금흐름지표)

[python] 볼린저 밴드(Bollinger Bands) - (3) MFI(현금흐름지표)

MFI(Money Flow Index, 현금흐름지표)

  • 볼린저 밴드를 이용하여 추세 추종 매매를 할 때 사용할 수 있는 지표. 주가와 거래량을 동시에 고려할 수 있어 주가만 고려하거나 거래량만 고려하는 지표들보다 많은 정보를 담고 있음
  • 현금흐름(Money Flow) = 중심가격 x 거래량
  • 중심가격(Typical Price) = $\frac{저가+고가+종가}{3}$
  • MFI는 거래량 데이터에 RSI(Relative Strength Index, 상대강도지수)의 개념을 적용한 것으로 볼 수 있음. RSI가 n일 동안의 상승일의 상승폭과 하락일의 하락폭 합계를 이용하는 것처럼 MFI는 상승일 동안의 현금흐름의 합(긍정적 현금흐름)과 하락일 동안의 현금흐름의 합(부정적 현금흐름)을 이용
$$MFI=100-\frac{100}{1+\frac{긍정적현금흐름}{부정적현금흐름}}$$
  • 긍정적 현금흐름: 중심가격이 전일보다 상승한 날들의 현금흐름의 합
  • 부정적 현금흐름: 중심가격이 전일보다 상승한 날들의 현금흐름의 합
In [1]:
from pykrx import stock
import pandas as pd
from datetime import datetime # 오늘날짜 가져올때 사용
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors # 색상표

# 한글폰트 설정, 그래프 마이너스 표시 설정
import matplotlib
from matplotlib import font_manager, rc
import platform

if platform.system() == 'Windows':
# 윈도우인 경우
    font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
    rc('font', family=font_name)
else:    
# Mac 인 경우
    rc('font', family='AppleGothic')

matplotlib.rcParams['axes.unicode_minus'] = False
In [3]:
stock_list = pd.DataFrame({'종목코드':stock.get_market_ticker_list(market="ALL")})
stock_list['종목명'] = stock_list['종목코드'].map(lambda x: stock.get_market_ticker_name(x))
# 두산중공업의 2020년 이후 주가 데이터 가져오기
stock_nm = '한화솔루션'
ticker = stock_list.loc[stock_list['종목명']==stock_nm, '종목코드']
today = datetime.today().strftime("%Y%m%d")  
df = stock.get_market_ohlcv_by_date(fromdate="20200101", todate=today, ticker=ticker)
df
Out[3]:
시가 고가 저가 종가 거래량
날짜
2020-01-02 18223 18271 17932 17932 417072
2020-01-03 18029 18756 17932 18611 865038
2020-01-06 18465 18562 18029 18029 625812
2020-01-07 18223 18853 18126 18659 978306
2020-01-08 18271 18416 17544 17884 1216990
... ... ... ... ... ...
2021-06-07 44600 45700 44600 45100 1421084
2021-06-08 45300 45450 44350 44350 1581775
2021-06-09 44500 44500 43500 43500 1748572
2021-06-10 43500 43950 42800 43750 1509281
2021-06-11 43850 46100 43200 45950 3411509

358 rows × 5 columns

In [4]:
# 10일(거래일 기준으로 2주 동안) 기준의 현금흐름지표를 구하는 코드
df['TP'] = (df['고가']+df['저가']+df['종가'])/3
df['PMF'] = 0
df['NMF'] = 0
for i in range(len(df['종가'])-1):
    # 당일의 중심가격이 전일의 중심가격보다 크면 긍정적 현금흐름
    if df['TP'].values[i] < df['TP'].values[i+1]:
        df['PMF'].values[i+1] = df['TP'].values[i+1]*df['거래량'].values[i+1]
        df['NMF'].values[i+1] = 0
    # 당일의 중심가격이 전일의 중심가격보다 작거나 같으면 부정적 현금흐름
    else:
        df['NMF'].values[i+1] = df['TP'].values[i+1]*df['거래량'].values[i+1]
        df['PMF'].values[i+1] = 0
        
df['MFR'] = df['PMF'].rolling(window=10).sum()/df['NMF'].rolling(window=10).sum()
df['MFI10'] = 100 - 100/(1+df['MFR'])
In [5]:
df
Out[5]:
시가 고가 저가 종가 거래량 TP PMF NMF MFR MFI10
날짜
2020-01-02 18223 18271 17932 17932 417072 18045.000000 0 0 NaN NaN
2020-01-03 18029 18756 17932 18611 865038 18433.000000 15945245454 0 NaN NaN
2020-01-06 18465 18562 18029 18029 625812 18206.666667 0 11393950480 NaN NaN
2020-01-07 18223 18853 18126 18659 978306 18546.000000 18143663076 0 NaN NaN
2020-01-08 18271 18416 17544 17884 1216990 17948.000000 0 21842536520 NaN NaN
... ... ... ... ... ... ... ... ... ... ...
2021-06-07 44600 45700 44600 45100 1421084 45133.333333 64138257866 0 0.663966 39.902618
2021-06-08 45300 45450 44350 44350 1581775 44716.666667 0 70731705416 0.437701 30.444531
2021-06-09 44500 44500 43500 43500 1748572 43833.333333 0 76645739333 0.271179 21.332900
2021-06-10 43500 43950 42800 43750 1509281 43500.000000 0 65653723500 0.261305 20.717015
2021-06-11 43850 46100 43200 45950 3411509 45083.333333 153802197416 0 0.646701 39.272530

358 rows × 10 columns

In [15]:
# 볼린저밴드를 그리기
df = df.assign(이동평균=df['종가'].rolling(window=20).mean(),
               표준편차=df['종가'].rolling(window=20).std())
df = df.assign(상단밴드=df['이동평균'] + df['표준편차']*2,
               하단밴드=df['이동평균'] - df['표준편차']*2)
df = df.assign(PB=(df['종가']-df['하단밴드']) / (df['상단밴드']-df['하단밴드']),
               밴드폭=(df['상단밴드']-df['하단밴드'])/df['이동평균'])
df = df[19:]
In [20]:
tab_cols = mcolors.TABLEAU_COLORS
plt.figure(figsize=(10, 5))
plt.plot(df.index, df['종가'], color=tab_cols['tab:gray'], linewidth=1, label='종가')
plt.plot(df.index, df['상단밴드'], color=tab_cols['tab:red'], linestyle='dashed', linewidth=1, label='상단밴드')
plt.plot(df.index, df['이동평균'], color=tab_cols['tab:green'], linestyle='dashed', linewidth=1, label='20일 이동평균')
plt.plot(df.index, df['하단밴드'], color=tab_cols['tab:blue'], linestyle='dashed', linewidth=1, label='하단밴드')
plt.title(f'{stock_nm}({int(ticker.values)})의 볼린저 밴드(20일, 2 표준편차)')

for i in range(df.shape[0]):
    if df['PB'].values[i] > 0.8 and df['MFI10'].values[i] > 80:
        plt.plot(df.index.values[i], df['종가'].values[i], 'r^')
    elif df['PB'].values[i] < 0.2 and df['MFI10'].values[i] < 20:
        plt.plot(df.index.values[i], df['종가'].values[i], 'bv')
plt.legend(loc='best');
  • %b 값이 0.8보다 크고, MFI 값이 80보다 크면 매수, %b값이 0.2보다 작고, MFI 값이 20보다 작으면 매도하는 전략

  • 빨간색 화살표가 매수지점을 표시한 것이고, 파란색 화살표가 매도지점을 표시한 것

  • 2020년에는 추세추종 매매가 효과가 있었지만, 2021년에는 추세추종 매매를 했으면 큰 손해를 보았을 것임