백트레이더는 다양한 함수를 제공하고 있습니다.
백트레이더 사이트의 인디케이터 (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 지표 활용 하는 방법에 대해 알아보았습니다.
이 방법을 응용하면 다른 새로운 지표 또한 백트레이더 환경에서 구현할 수 있을 거 같네요.
다음에는 좀 더 정교한 로직을 백트레이더에서 적용하는 방법에 대해 포스팅 해보도록 하겠습니다.
'프로젝트 > [ing]_백테스팅_툴' 카테고리의 다른 글
OpenDartReader 로 종목을 분류해보자 (4) - 근데 pykrx를 곁들인... (2) | 2022.01.12 |
---|---|
OpenDartReader 로 종목을 분류해보자 (3) (0) | 2021.12.16 |
OpenDartReader 로 종목을 분류해보자 (2) (0) | 2021.12.03 |
OpenDartReader 로 종목을 분류해보자 (1) (2) | 2021.12.02 |
[python] 인터넷 연결 안되있으면 컴퓨터 다시 시작하는 프로그램 (0) | 2021.10.21 |