[Python] 백트레이더(Backtrader)에서 현금흐름지표(MFI) 를 사용하자
프로젝트/[ing]_백테스팅_툴

[Python] 백트레이더(Backtrader)에서 현금흐름지표(MFI) 를 사용하자

728x90
반응형

 

 

MFI_cal_data_example_SME.xlsx
0.01MB
data.xlsx
0.01MB

 

백트레이더는 다양한 함수를 제공하고 있습니다.
백트레이더 사이트의 인디케이터 (Indicator) 를 들어가면 확인 할 수 있습니다.

 

https://www.backtrader.com/docu/indautoref/

 

Indicators - Reference - Backtrader

Indicator Reference AccelerationDecelerationOscillator Alias: Acceleration/Deceleration Technical Indicator (AC) measures acceleration and deceleration of the current driving force. This indicator will change direction before any changes in the driving for

www.backtrader.com

 

잘 찾아보면 유용한 함수를 찾아낼 수 있을 겁니다. 하지만 기본으로 제공하지 않는 함수 중 대표적인게 현금흐름지표 (MFI, Money Flow Index) 입니다. MFI 거래량 가중 상대강도지수 (RSI, Relative Strength Index) 라 보시면 됩니다. 

 

물론 사이트에서 새로운 인디케이터 개발 예시로 들고있기도 하고, 영문 문서가 잘 되어있지만, 실제로 써봐야 의미가 있기에 한번 해볼까 합니다. 

 

 

1. 현금흐름지표 (MFI : Money Flow Index) 란?

본 글에선 MFI 를 파이썬 백트레이더 환경에서 구현하는데 초점을 맞출거기 때문에 간단히 짚고 넘어가도록 하겠습니다.

현금흐름지표(MFI) 는 일종의 거래량 가중 상대강도지수(RSI)라 말씀드렸습니다. RSI 는 지표계산에 가격만을 활용하는 반면, MFI 는 거래량까지 같이 넣어서 지표계산을 하기 때문이죠. 

14일간 현금흐름지표를 계산한다 했을 때, 계산식은 아래와 같습니다. 

 

MFI = 100 - (100 / (1+ Money Flow Ratio))

Money Flow Ratio = (14일간 Positive Money Flow) / (14일간 Negative Money Flow)

Raw Money Flow = Typical Price * 거래량

Typical Price = (고가 + 저가 + 종가) / 3

 

계산 순서는 먼저 1일치의 (고가 + 저가 + 종가) 의 평균값을 구하고 거래량을 곱합니다. 그럴 경우, 

 

이 날의 Raw Money Flow = (고가 + 저가 + 종가) * 거래량 / 3 이 되겠죠.

 

이제 이게 Positive 한지 Negative 한지 어떻게 판단할 것인가가 남았습니다. 그 판단은 오늘의 Typical Price 가 어제보다 + 면 positive, - 면 negative money flow 로 보시면 됩니다. 어제와 평균이 같아 0 일 수도 있겠죠. 그럴 경우 어디에도 포함시키지 않으면 됩니다.

 

14일치의 MFI 의 의미는 주말을 제외한 3주동안 계산된 값입니다. 따라서 데이터는 15일치의 데이터가 필요합니다. 아래의 삼성전자 일간 주식차트 데이터로 3월 3일의 MFI 를 직접 계산해봅시다. 맨위에 위치해 있는 유첨 엑셀파일 다운받으시면 됩니다.

code	date		open	high	low	close	diff	volume
005930	2022-02-10	75600	75800	74700	75400	700	17746033
005930	2022-02-11	75000	75500	74600	74900	500	12205407
005930	2022-02-14	74400	74500	73100	73700	1200	13993255
005930	2022-02-15	74100	74200	73100	73700	0	9320365
005930	2022-02-16	74600	74900	74200	74800	1100	10767386
005930	2022-02-17	75000	75600	74500	75000	200	13605382
005930	2022-02-18	74600	74800	73700	74300	700	10122226
005930	2022-02-21	73200	74300	72600	74200	100	10489717
005930	2022-02-22	73000	73400	72800	73400	800	11692469
005930	2022-02-23	73800	73800	72800	73000	400	10397964
005930	2022-02-24	72300	72300	71300	71500	1500	15759283
005930	2022-02-25	72100	72600	71900	71900	400	13168484
005930	2022-02-28	71100	72100	71000	72100	200	17781783
005930	2022-03-02	72300	72400	71500	71700	400	12481430
005930	2022-03-03	72300	73100	72200	72900	1200	13033553

 

이제 이 정보를 가지고 Typical price, raw money flow 를 계산할 수 있고 이를 positive, negative 로 분류해볼 수 있습니다. 정리하면 아래와 같습니다. sign 컬럼이 있는데 +1은 positive, -1은 negative, 0 은 어제의

typical price 와 동일한 값 입니다. 첫날은 비교대상이 없으므로 Raw money flow 에서 제외됩니다.

 

date		typical price	sign	Raw money flow
2022-02-10	75300		-	1.33628E+12
2022-02-11	75000		-1	9.15406E+11
2022-02-14	73766.66667	-1	1.03224E+12
2022-02-15	73666.66667	-1	6.866E+11
2022-02-16	74633.33333	+1	8.03606E+11
2022-02-17	75033.33333	+1	1.02086E+12
2022-02-18	74266.66667	-1	7.51744E+11
2022-02-21	73700		-1	7.73092E+11
2022-02-22	73200		-1	8.55889E+11
2022-02-23	73200		0	7.61131E+11
2022-02-24	71700		-1	1.12994E+12
2022-02-25	72133.33333	+1	9.49887E+11
2022-02-28	71733.33333	-1	1.27555E+12
2022-03-02	71866.66667	+1	8.96999E+11
2022-03-03	72733.33333	+1	9.47974E+11

 

Raw money flow positive, negative 를 날짜에 따라 분류 하여 더해보면 money flow ratio 를 계산할 수 있고, 이를 통해 MFI 를 얻을 수 있습니다. 3월 3일 삼성전자의 MFI 는 38.366 이 나오네요.

 

raw money flow
positive (날짜) = 02.16 + 02.17 + 02.25 + 03.02 + 03.03 = 4.61 * 10^12 (값)
negative (날짜) = 02.11 + 02.14 + 02.15 + 02.18 + 02.21 + 02.22 + 02.24 + 02.28 = 7.42 * 10^12 (값)

money flow ratio = positive / negative = 4.61 / 7.42 = 0.6225

MFI = 100 - (100 / (1 + 0.6225)) = 38.366

 

맞는지 확인해 봅시다. 시중의 증권사 프로그램과 비교해보겠습니다. NH투자증권 어플과 비교해 보았을 때

MFI = 38.37 로 계산식이 맞았음을 확인할 수 있습니다.

 

 

2. 백트레이더에서 구현

이론적인 내용을 습득했으니 이제 백트레이더에서 구현을 해보겠습니다. 백트레이더 홈페이지에서 기본적인 코드 (정석적인 (?, Canonical) 방식으로 가져왔습니다. 코드가 깔끔한 대신 이해가 조금 필요합니다. 

 

class MFI_Canonical(bt.Indicator):
    lines = ('mfi',)
    params = dict(period=14)

    def __init__(self):
        tprice = (self.data.close + self.data.low + self.data.high) / 3.0
        mfraw = tprice * self.data.volume

        flowpos = bt.ind.SumN(mfraw * (tprice > tprice(-1)), period=self.p.period)
        flowneg = bt.ind.SumN(mfraw * (tprice < tprice(-1)), period=self.p.period)

        mfiratio = bt.ind.DivByZero(flowpos, flowneg, zero=100.0)
        self.l.mfi = 100.0 - 100.0 / (1.0 + mfiratio)

 

전체 코드는 이런데, 이해가 안되는 부분은 두군데 일거 같습니다.

첫번째는 flowpos, flowneg 를 계산하는 부분입니다.

 

# period 만큼의 기간동안 이번 계산의 tprice 를 이전의 tprice(-1) 를 비교해서 
# 크면 flowpos 에 합쳐지고 작으면 flowneg 에 합쳐집니다.

	flowpos = bt.ind.SumN(mfraw * (tprice > tprice(-1)), period=self.p.period)
        flowneg = bt.ind.SumN(mfraw * (tprice < tprice(-1)), period=self.p.period)

 

두번째는 DivByZero 입니다. 0으로 나눌때의 처리가 들어간 나눗셈으로 보시면 됩니다.

 

# 분모가 0이 아닐 경우, mfiratio = flowpos / flowneg
# 0으로 나누는 상황이 생길 경우, mfiratio = 100
		mfiratio = bt.ind.DivByZero(flowpos, flowneg, zero=100.0)

 

이제 이걸 실제로도 써먹어야겠죠. 

 

아래와 같은 테스트 코드를 준비했습니다. 포스팅 맨 위에 data.xlsx 를 다운받아서 코드와 같은 폴더에서 실행시키시면 됩니다. MFI 지수가 20이면 10주를 매수하고 80이 넘어가면 파는 전략이 세팅되어 있습니다. 

코드에 주석도 함께 달아두었습니다. 이해에 도움이 되셨으면 좋겠네요.

 

import backtrader as bt
from backtrader import cerebro
import pandas as pd

class MyStrategy(bt.Strategy): # 매매로직 구현 부분
    def __init__(self):
        self.mfi = MFI_Canonical(self.data) # MFI 기법 적용할 변수 할당
    def next(self):
        if not self.position: # 매수 포지션이 없을 때,
            if self.mfi < 20: # MFI 20 미만이라면 과매도 판단해서 buy
                self.order = self.buy()
        else:
            if self.mfi > 80: # MFI 80 초과하면 과매수 판단해서 sell
                self.order = self.sell()

class MFI_Canonical(bt.Indicator): # MFI 지표계산 부분
    lines = ('mfi',)
    params = dict(period=14)

    def __init__(self):
        tprice = (self.data.close + self.data.low + self.data.high) / 3.0
        mfraw = tprice * self.data.volume

        flowpos = bt.ind.SumN(mfraw * (tprice > tprice(-1)), period=self.p.period)
        flowneg = bt.ind.SumN(mfraw * (tprice < tprice(-1)), period=self.p.period)

        mfiratio = bt.ind.DivByZero(flowpos, flowneg, zero=100.0)
        self.l.mfi = 100.0 - 100.0 / (1.0 + mfiratio)

cerebro = bt.Cerebro() # 백테스트 기본세팅
cerebro.addstrategy(MyStrategy) # 매매로직 적용

# 엑셀 데이터를 백트레이더에서 인식가능한 데이터로 변환
df = pd.read_excel('C:/myPackage/data.xlsx') # 각자의 data.xlsx 가 있는 경로로 지정
df['datetime'] = pd.to_datetime(df['datetime'])
data = bt.feeds.PandasData(dataname = df) 

cerebro.adddata(data)
cerebro.broker.setcash(10000000) # 초기 투자 자금 설정 : 천만원
cerebro.broker.setcommission(commission=0.0014) # 1회 매매당 수수료 설정. 매수 / 매도 합치면 0.28% 정도 이므로, 그 절반인 0.14%.
cerebro.addsizer(bt.sizers.SizerFix, stake=10) # 매매수량 설정. 1회당 10주씩.

print(f'Initial Portfolio Value : {cerebro.broker.getvalue():,.0f} KRW')
cerebro.run() # 백테스팅 시작
print(f'final Portfolio Value : {cerebro.broker.getvalue():,.0f} KRW')
cerebro.plot(style='candlestick') # 백테스팅 결과를 캔들스틱 그래프 형태로 출력

 

이걸 실행시키면 아래와 같은 콘솔창과 그래프 화면을 확인할 수 있습니다. 

 

Initial Portfolio Value : 10,000,000 KRW
final Portfolio Value : 10,001,002 KRW

 

 

 

이상으로 백트레이더에서 MFI 지표 활용 하는 방법에 대해 알아보았습니다. 

이 방법을 응용하면 다른 새로운 지표 또한 백트레이더 환경에서 구현할 수 있을 거 같네요. 

다음에는 좀 더 정교한 로직을 백트레이더에서 적용하는 방법에 대해 포스팅 해보도록 하겠습니다.

 

 

 

728x90
반응형