4-2. [트레이딩 전략 구현] 볼린저 밴드 매매 기법 : 추세 추정 매매 기법

2021. 6. 13. 18:07Financial Analysis

지금까지 볼린저 밴드, %b, 밴드폭이라는 지표를 살펴보았다. 

 

이 외에도 실제로 투자를 할 때  참고해야할 여러가지 지표가 존재한다. 

 

이번 장에서는 다른 지표에 대해서도 살펴보고 지표를 참고하여 매매 기법을 구현해보도록 하겠다.

 

추세 추종(Trading Following)


Trend following or trend trading is a trading strategy according to which one should buy an asset when its price trend goes up, and sell when its trend goes down, expecting price movements 
to continue
출처 : https://en.wikipedia.org/wiki/Trend_following

위키리스크에 따르면 추세 추종(Trading following)이란 자산 가격이 오르면 매수하고, 내려가면 매도하는

 

규칙을 적용한 트레이딩 전략이다. 가격이 한동안 추세를 지속한다는 믿음 위에 세워진 전략이다. 

 

쉽게 말해, 오르는 놈만 오르고 내리는 놈만 내린다는 원리에 기반한 매매 전략이라고 할 수 있겠다. 

 

추세 추종 기법에서는 MFI(현금흐름) 등의 지표를 활용하는데 Investopedia에 따르면 MFI의 정의는 다음과 같다.

 

What Is the Money Flow Index (MFI)?

The Money Flow Index (MFI) is a technical oscillator that uses price and volume data for identifying overbought or oversold signals in an asset. It can also be used to spot divergences which warn of a trend change in price. The oscillator moves between 0 and 100.

MFI formula

 

​출처 : https://www.investopedia.com/terms/m/mfi.asp

 

직역하자면, MFI는 가격과 거래량 지표를 활용하여 해당 종목이 과매수되었는지, 과매도되었는지

 

판단하는 기술적 지표이다. 중심 가격(Typical Price)은 일정 기간 동안의 고가, 저가, 종가의 평균이며

 

중심 가격에 거래량을 곱한 것이 '현금 흐름'이다. 그리고 일정 기간 동안 이 현금 흐름이 양수일 경우(다시 말해 상승일)의 

 

총합을 긍정적 현금 흐름으로, 음수일 경우(하락일일 경우) 부정적 현금 흐름으로 계산한다. 

 

이를 나눈 후에 MFI 공식에 대입하여 산출해낼 수 있다.

 

이를 코드로 구현해보자. 

from Investar.bollingerBand import bollingerBand
import matplotlib.pyplot as plt

class trading_following():

    def __init__(self, company, start_date):
        ###볼린저 밴드 모듈에서 반환하는 데이터 프레임을 가져온다. 
       
    def calc_mfi(self):
        ###MFI를 계산한다. 

    def show_MFI_chart(self, df):
        #MFI의 차트를 출력한다. 

전체적인 구조는 이러하다. 하나하나 구현해보자.

    def __init__(self, company, start_date):
        bB = bollingerBand()
        self.df = bB.get_bollinger_band(company, start_date)

해당 클래스는 회사명과 시작일을 인자로 받는다. 볼린저 밴드 모듈에 이 인자를 전달하여 반환받은 데이터프레임을 

 

클래스 멤버인 df에 저장한다.

    def calc_mfi(self):
        df = self.df
        df['TP'] = (df['high'] + df['low'] + df['close']) / 3
        df['PMF'] = 0
        df['NMF'] = 0
        for i in range(len(df.close)-1):
            if df.TP.values[i] < df.TP.values[i+1]:
                df.PMF.values[i+1] = df.TP.values[i+1] * df.volume.values[i+1]
                df.NMF.values[i+1] = 0
            if df.TP.values[i] > df.TP.values[i+1]:
                df.NMF.values[i+1] = df.TP.values[i+1] * df.volume.values[i+1]
                df.PMF.values[i+1] = 0
        df['MFR'] = df.PMF.rolling(window=14).sum()/df.NMF.rolling(window=14).sum()
        df['MFI'] = 100 - 100/(1 + df['MFR'])
        df = df[13:]
        return df

calc_mfi 함수에서는 중심가격을 계산하고, 상승일이라고 판단될 경우 긍정적 현금흐름을 나타내는 PMF 칼럼에

 

중심가격과 거래량을 곱하고, 하락일이라고 판단될 경우 부정적 현금흐름을 나타내는 NMF 칼럼에 저장한다. 

 

MFR 칼럼에는 rolling 함수를 이용하여 14일 간의 PMF의 합을 14일 간의 NMF의 합으로 나눠준다. 

 

최종적으로 MFI를 계산하여 반환한다. rolling 함수에서 [:12]까지의 값이 NaN이므로 MFI 칼럼은 [13:]부터 존재한다. 

    def show_MFI_chart(self, df):
        plt.plot(self.df.index, self.df['MFI'], 'g--', label='MFI')
        plt.grid(linestyle='dotted')
        plt.legend(loc='best')
        plt.xticks(rotation=45)

MFI 차트를 출력하는 함수도 그려준다.

    tf = trading_following(company, '2019-10-10')
    df = tf.calc_mfi()
    bB.show_pb_chart(df, 100)
    tf.show_MFI_chart(df)
    plt.show()

%B 차트와 같이 출력해준다. 기존 %B 차트에 배율 개념을 적용하기 위해 수정하였다.

%b와 MFI 지표 차트

자, 모든 지표를 계산했으니 이제 언제 매수를 하고 매도를 할지 결정하는 추세 추종 기법을 구현해보자. 

 

    def __init__(self, company, start_date):
        bB = bollingerBand()
        self.df = bB.get_bollinger_band(company, start_date)
        self.buy_tim = []
        self.sell_tim = []

매수 타이밍과 매도 타이밍을 저장하는 멤버 변수를 추가하였다. 

    def trading_follow(self, df):
        for i in range(len(df.close)):
            if df.PB.values[i] > 0.8 and df.MFI.values[i] > 80:
                self.buy_tim.append(df.index.values[i])
            if df.PB.values[i] < 0.2 and df.MFI.values[i] < 20:
                self.sell_tim.append(df.date.values[i])

데이터프레임의 모든 날짜를 순회하면서 %b가 0.8을 넘으면서 MFI가 80 이상일 경우 매수 타이밍으로,

 

%b가 0.2 미만, MFI가 20 미만일 경우 매도 타이밍으로 지정한다.

    def show_trading_follow(self, df):
        plt.plot(self.buy_tim, df.loc[self.buy_tim]['close'], 'r^')
        plt.plot(self.sell_tim, df.loc[self.sell_tim]['close'], 'b^')
        plt.grid(linestyle='dotted')
        plt.legend(loc='best')
        plt.xticks(rotation=45)

그래프에 추세 추종 기법에 따라 결정된 매도, 매수 타이밍을 종가와 함께 표시해주는 함수를 구현한다. 

    tf = trading_following(company, '2019-10-10')
    df = tf.calc_mfi()
    tf.trading_follow(df)

    plt.subplot(2, 1, 1)
    bB.show_bollinger_chart(df,company)
    tf.show_trading_follow(df)
    plt.subplot(2, 1, 2)
    bB.show_pb_chart(df, 100)
    tf.show_MFI_chart(df)
    plt.tight_layout()
    plt.show()

볼린저 밴드 그래프와 함께 추세 추종 매수 매도 시점을 표시하고, 하단에는 MFI 차트를 출력해준다. 

구현된 기법대로 가상 매매를 진행해보자.

    def __init__(self, company, start_date):
        bB = bollingerBand()
        self.df = bB.get_bollinger_band(company, start_date)
        self.buy_tim = []
        self.sell_tim = []
        self.account = 1000000
        self.stock = 0
        self.profit = 0

멤버 변수에 account, stock, profit을 추가하였다. 초기 자본금은 1,000,000원으로 설정하였다.

    def trading_follow(self, df):
        for i in range(len(df.close)):
            if df.PB.values[i] > 0.8 and df.MFI.values[i] > 80:
                self.buy_tim.append(df.index.values[i])
                self.account -= df.close.values[i] * 1
                self.stock += 1
                print(f"[{df.index.values[i]}] 체결완료 - 매수가 : {df.close.values[i]} (원)"
                      f" | 체결 수량 : 1 | 총 {self.stock} 주")
            if df.PB.values[i] < 0.2 and df.MFI.values[i] < 20:
                self.sell_tim.append(df.date.values[i])
                if self.stock == 0:
                    continue
                print(f"[{df.index.values[i]}] 체결완료 - 매도가 : {df.close.values[i]} (원)"
                      f" | 체결 수량 : {self.stock} 주")
                self.account += df.close.values[i]*self.stock
                self.stock = 0
            if i == len(df.close)-1:
                print(f"[{df.index.values[i]}] 체결완료 - 매도가 : {df.close.values[i]} (원)"
                      f" | 체결 수량 : {self.stock} 주")
                self.account += df.close.values[i] * self.stock
                self.stock = 0
                self.profit = self.account - 1000000

 

매수는 1주씩, 매도는 전량 매도하도록 구현하였다.

[2020-04-09] 체결완료 - 매수가 : 34200 (원) | 체결 수량 : 1 | 총 1 주
[2020-04-10] 체결완료 - 매수가 : 35100 (원) | 체결 수량 : 1 | 총 2 주
[2020-04-14] 체결완료 - 매수가 : 34250 (원) | 체결 수량 : 1 | 총 3 주
[2020-04-21] 체결완료 - 매수가 : 35950 (원) | 체결 수량 : 1 | 총 4 주
[2020-04-22] 체결완료 - 매수가 : 35600 (원) | 체결 수량 : 1 | 총 5 주
[2020-07-22] 체결완료 - 매수가 : 44650 (원) | 체결 수량 : 1 | 총 6 주
[2020-07-23] 체결완료 - 매수가 : 43850 (원) | 체결 수량 : 1 | 총 7 주
[2020-07-24] 체결완료 - 매수가 : 42450 (원) | 체결 수량 : 1 | 총 8 주
[2020-07-27] 체결완료 - 매수가 : 43600 (원) | 체결 수량 : 1 | 총 9 주
[2020-09-04] 체결완료 - 매수가 : 59100 (원) | 체결 수량 : 1 | 총 10 주
[2020-09-07] 체결완료 - 매수가 : 63800 (원) | 체결 수량 : 1 | 총 11 주
[2020-10-30] 체결완료 - 매도가 : 51800 (원) | 체결 수량 : 11 주
[2020-12-03] 체결완료 - 매수가 : 67900 (원) | 체결 수량 : 1 | 총 1 주
[2020-12-04] 체결완료 - 매수가 : 67200 (원) | 체결 수량 : 1 | 총 2 주
[2020-12-15] 체결완료 - 매수가 : 70600 (원) | 체결 수량 : 1 | 총 3 주
[2020-12-16] 체결완료 - 매수가 : 74100 (원) | 체결 수량 : 1 | 총 4 주
[2020-12-17] 체결완료 - 매수가 : 73200 (원) | 체결 수량 : 1 | 총 5 주
[2021-06-11] 체결완료 - 매도가 : 71400 (원) | 체결 수량 : 5 주
총순익 :  101250 (원)

LS 주식을 추세 추종 매매법으로 했다면 2020-4-9 ~ 2021-6-11 동안 10만원 이상의 수익을 거둘 수 있었다.