ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • (파이썬 증권데이터 분석) 볼린저 밴드 지표
    computer_IT 2022. 7. 23. 22:13

     

    2022.07.22 - [computer_IT] - (파이썬 증권데이터 분석) 일별시세 DB구축 및 시세 조회 API 개발

     

    (파이썬 증권데이터 분석) 일별시세 DB구축 및 시세 조회 API 개발

    2022.07.17 - [computer_IT] - (파이썬 증권데이터 분석) 상장법인 정보 불러오기, 주가 스크레이핑 (파이썬 증권데이터 분석) 상장법인 정보 불러오기, 주가 스크레이핑 2022.07.16 - [computer_IT] - [파이썬 증

    lifenlight.tistory.com

    소스코드는 아래 링크

    https://github.com/Investar/StockAnalysisInPython

     

    GitHub - INVESTAR/StockAnalysisInPython

    Contribute to INVESTAR/StockAnalysisInPython development by creating an account on GitHub.

    github.com

    볼린저 밴드는 주가의 변동이 표준 정규분포를 따른다는 가정에서 주가의 위아래 밴드를 표시함으로써 주가의 상대적인 높낮이를 알려준다. 기술적 분석을 선호하는 투자자들이 주로 사용한다. 볼린저 밴드는 주가의 20일 이동 평균선을 기준으로 상대적인 고점을 나타내는 상단 밴드와 상대적인 저점을 나타내는 하단 밴드로 구성된다. 주가가 상단 밴드 근처에 있을수록 상대적인 고점에, 하단 근처에 있을수록 상대적인 저점에 있다고 판단한다. 

     

    표준 볼린저 밴드 공식

    상단 볼린저 밴드 = 중간 볼린저 밴드 + (2 * 표준편차)

    중간 볼린저 밴드 = 종가의 20일 이동평균

    하단 볼린저 밴드 = 중간 볼린저 밴드 - (2 * 표준편차)

     

    통계학에 따르면 평균값에서 2 x 표준편차 이내에 표본값의 95.4%가 존재하므로, 주가가 볼린저 밴드 내부에 존재할 확률도 95.4%이다.

    볼린저 밴드 지표 : %b

    주가가 볼린저 밴드 어디에 위치하는지를 나타내는 지표가 %b이다. 상단 밴드에 닿으면 1.0이 되고, 중간은 0.5, 하단에 닿으면 0.0이 된다. 산출 공식은 (종가 - 하단 볼린저 밴드) / (상단 볼린저 밴드 - 중간 볼린저 밴드) 이다. 

    볼랜저 밴드 지표 : 밴드폭

    밴드폭은 상단 볼린저 밴드와 하단 볼린저 밴드 사이의 폭을 의미한다. (상단 볼린저 밴드 - 하단 볼린저 밴드) / (중간 볼린저 밴드)로 구하고 변동성이 커지면 밴드폭이 커진다. 강한 상승 추세에서는 하단 볼린저 밴드가 아래로 향하면서 밴드폭이 넓어지고, 이후 밴드폭이 줄어들면서 볼린저 밴드가 추세 반대쪽으로 방향을 바꾸면 추세가 끝났다고 본다.

    소스코드에는 05 폴더에 Investar 폴더가 있으므로 06폴더에 있는 실습 파일을 읽지 못하는 오류가 발생한다. 그래서 sys.path.insert로 Investar 상위 폴더까지 지정해준다.

    import sys
    sys.path.insert(0, '/home/05_Stock_Price_API/')  # Investar폴더의 상위폴더까지 지정
    import matplotlib.pyplot as plt
    from Investar import Analyzer
    
    mk = Analyzer.MarketDB()
    df = mk.get_daily_price('NAVER', '2019-01-02')
    df['MA20'] = df['close'].rolling(window=20).mean()  # ① 20개 종가의 평균
    df['stddev'] = df['close'].rolling(window=20).std() # ② 표준편차를 구하고 stddev 컬럼 추가
    df['upper'] = df['MA20'] + (df['stddev'] * 2)   # ③ 상단 볼린저 밴드
    df['lower'] = df['MA20'] - (df['stddev'] * 2)   # ④ 하단 볼린저 밴드
    df['bandwidth'] = (df['upper'] - df['lower']) / df['MA20'] * 100 # 볼린저밴드 지표 : 밴드폭
    df = df[19:]
    
    plt.figure(figsize=(9, 8))
    plt.subplot(2, 1, 1)
    plt.plot(df.index, df['close'], color='#0000ff', label='Close')
    plt.plot(df.index, df['upper'], 'r--', label ='Upper band')
    plt.plot(df.index, df['MA20'], 'k--', label='Moving average 20')
    plt.plot(df.index, df['lower'], 'c--', label ='Lower band')
    plt.fill_between(df.index, df['upper'], df['lower'], color='0.9')
    plt.title('NAVER Bollinger Band(20 day, 2 std)')
    plt.legend(loc='best')
    plt.subplot(2, 1, 2)
    plt.plot(df.index, df['bandwidth'], color='m', label='BandWidth') # ②
    plt.grid(True)
    plt.legend(loc='best')
    plt.savefig('bollinger.png')

    결과 그래프

    네이버 주식 볼린저 밴드와 밴드폭

    볼린저 밴드를 이용한 추세 추종 매매

    추세 추종은 상승 추세에 매수하고 하락 추세에 매도하는 기법이다. 일반적으로 주가를 나타낼 때는 종가를 사용하지만, 중심 가격(Typical Price)을 사용하면 트레이딩이 집중적으로 발생하는 주가 지점을 더 잘 나타낼 수 있다. 중심 가격은 일정 기간의 고가, 저가, 종가를 합한 뒤 3으로 나눈 값이다. 중심 가격에 거래량을 곱하면 현금 흐름(Money Flow)으로, 가격과 거래량을 동시에 분석하므로 상대적으로 신뢰도가 더 높다고 볼 수 있다. 현금흐름지표(Money Flow Index: MFI)는 다음과 같이 구한다.

    MFI = 100 - (100 / (1 + 긍정적 현금흐름/부정적 현금흐름)) 

    긍정적 현금흐름 : 중심 가격이 전일보다 상승한 날들의 현금흐름의 합

    부정적 현금흐름 : 중심 가격이 전일보다 하락한 날들의 현금흐름의 합

    MFI는 0에서 100 사이를 움직이는 한계 지표로, 80을 상회하면 아주 강력한 매수 신호를 나타내고 20을 하회하면 아주 강력한 매도 신호를 나타낸다.

    import sys
    sys.path.insert(0, '/home/05_Stock_Price_API/')  # Investar폴더의 상위폴더까지 지정
    import matplotlib.pyplot as plt
    from Investar import Analyzer
    
    mk = Analyzer.MarketDB()
    df = mk.get_daily_price('NAVER', '2020-01-02')
      
    df['MA20'] = df['close'].rolling(window=20).mean() 
    df['stddev'] = df['close'].rolling(window=20).std() 
    df['upper'] = df['MA20'] + (df['stddev'] * 2)
    df['lower'] = df['MA20'] - (df['stddev'] * 2)
    df['PB'] = (df['close'] - df['lower']) / (df['upper'] - df['lower'])  # 볼린저 밴드지표: %b
    df['TP'] = (df['high'] + df['low'] + df['close']) / 3  # 중심가격
    df['PMF'] = 0  # 긍정적 현금흐름
    df['NMF'] = 0  # 부정적 현금흐름
    for i in range(len(df.close)-1):  # range 함수는 마지막 값을 포함하지 않음
        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
        else:
            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=10).sum() /
        df.NMF.rolling(window=10).sum())
    df['MFI10'] = 100 - 100 / (1 + df['MFR'])  # 10일 동안의 현금흐름지표
    df = df[19:]
    
    plt.figure(figsize=(9, 8))
    plt.subplot(2, 1, 1)
    plt.title('NAVER Bollinger Band(20 day, 2 std) - Trend Following')
    plt.plot(df.index, df['close'], color='#0000ff', label='Close')
    plt.plot(df.index, df['upper'], 'r--', label ='Upper band')
    plt.plot(df.index, df['MA20'], 'k--', label='Moving average 20')
    plt.plot(df.index, df['lower'], 'c--', label ='Lower band')
    plt.fill_between(df.index, df['upper'], df['lower'], color='0.9')
    for i in range(len(df.close)):
        if df.PB.values[i] > 0.8 and df.MFI10.values[i] > 80:       # ① %b가 0.8, MFI가 80보다 크면 매수시점
            plt.plot(df.index.values[i], df.close.values[i], 'r^')  # ② 빨간색 삼각형
        elif df.PB.values[i] < 0.2 and df.MFI10.values[i] < 20:     # ③ %b가 0.2, MFI가 20보다 작으면 매도 시점
            plt.plot(df.index.values[i], df.close.values[i], 'bv')  # ④ 파란색 역삼각현
    plt.legend(loc='best')
    
    plt.subplot(2, 1, 2)
    plt.plot(df.index, df['PB'] * 100, 'b', label='%B x 100')       # ⑤ 
    plt.plot(df.index, df['MFI10'], 'g--', label='MFI(10 day)')     # ⑥
    plt.yticks([-20, 0, 20, 40, 60, 80, 100, 120])                  # ⑦ y축 눈금표시
    for i in range(len(df.close)):
        if df.PB.values[i] > 0.8 and df.MFI10.values[i] > 80:
            plt.plot(df.index.values[i], 0, 'r^')
        elif df.PB.values[i] < 0.2 and df.MFI10.values[i] < 20:
            plt.plot(df.index.values[i], 0, 'bv')
    plt.grid(True)
    plt.legend(loc='best')
    plt.savefig('trendfollowing.png')

    결과 그래프

     

    볼린저 밴드 추세 추종 매매

    책에서는 2019년 1월부터 11월까지의 예시가 나와있으며, 2019년 7월말에 매수 신호가 나와 매수했다면 11월에는 약 32% 수익이 발생했다고 나와있다. 위의 경우는 2020년 1월부터 2022년 7월 현재까지의 그래프이며, 상승추세에서는 수익이 발생할 수 있으나(2020년 5월 ~ 2021년 5월), 하락 추세에서는 손실이 발생하는 것 같다.(2021년 6월 ~ 2022년 7월)

    볼린저 밴드를 이용한 반전 매매

    이 기법은 주가가 반전하는 지점을 찾아내 매매하는 기법으로 주가가 하단 밴드를 여러 차례 터치하는 과정에서 강세 지표가 발생하면 매수하고, 주가가 상단 밴드를 여러 차례 터치하는 과정에서 약세 지표가 발생하면 매도한다. 존 볼린저는 책에서 일중 강도율과 매집 분산율을 예로 들지만 책에서는 일중 강도율(Intraday Intensity %: II%)만 사용한다. 

    • 매수 : 주가가 하단 밴드 부근에서 W형 패턴을 나타내고, 강세 지표가 확증할 때 매수(%b < 0.05, II% > 0)
    • 매수 : 주가가 상단 밴드 부근에서 일련의 주가 태그가 일어나며, 약세 지표가 확증할 때 매수(%b > 0.95, II% < 0)

    일중 강도 = (2 * 종가 - 고가 - 저가) / (고가 - 저가) * 거래량

    일중 강도율 = 일중강도의 21일합 / 거래랴의 21일합 * 100

     

    앞서 추세 추종 매매와 비교하여 어떤 결과가 나오는지 그려보자.

    import sys
    sys.path.insert(0, '/home/05_Stock_Price_API/')  # Investar폴더의 상위폴더까지 지정
    import matplotlib.pyplot as plt
    from Investar import Analyzer
    
    mk = Analyzer.MarketDB()
    df = mk.get_daily_price('NAVER', '2020-01-02')
      
    df['MA20'] = df['close'].rolling(window=20).mean() 
    df['stddev'] = df['close'].rolling(window=20).std() 
    df['upper'] = df['MA20'] + (df['stddev'] * 2)
    df['lower'] = df['MA20'] - (df['stddev'] * 2)
    df['PB'] = (df['close'] - df['lower']) / (df['upper'] - df['lower'])
    
    df['II'] = (2*df['close']-df['high']-df['low'])/(df['high']-df['low'])*df['volume']  # 일중 강도
    df['IIP21'] = df['II'].rolling(window=21).sum()/df['volume'].rolling(window=21).sum()*100  # 일중 강도율
    df = df.dropna()
    
    plt.figure(figsize=(9, 9))
    plt.subplot(3, 1, 1)
    plt.title('Naver Bollinger Band(20 day, 2 std) - Reversals')
    plt.plot(df.index, df['close'], 'm', label='Close')
    plt.plot(df.index, df['upper'], 'r--', label ='Upper band')
    plt.plot(df.index, df['MA20'], 'k--', label='Moving average 20')
    plt.plot(df.index, df['lower'], 'c--', label ='Lower band')
    plt.fill_between(df.index, df['upper'], df['lower'], color='0.9')
    for i in range(0, len(df.close)):
        if df.PB.values[i] < 0.05 and df.IIP21.values[i] > 0:       # ①
            plt.plot(df.index.values[i], df.close.values[i], 'r^')  # ②
        elif df.PB.values[i] > 0.95 and df.IIP21.values[i] < 0:     # ③
            plt.plot(df.index.values[i], df.close.values[i], 'bv')  # ④
    plt.legend(loc='best')
    
    plt.subplot(3, 1, 2)
    plt.plot(df.index, df['PB'], 'b', label='%b')
    plt.grid(True)
    plt.legend(loc='best')
    
    plt.subplot(3, 1, 3)
    plt.bar(df.index, df['IIP21'], color='g', label='II% 21day')
    for i in range(0, len(df.close)):
        if df.PB.values[i] < 0.05 and df.IIP21.values[i] > 0:
            plt.plot(df.index.values[i], 0, 'r^') # ⑤
        elif df.PB.values[i] > 0.95 and df.IIP21.values[i] < 0:
            plt.plot(df.index.values[i], 0, 'bv') # ⑥
    plt.grid(True)
    plt.legend(loc='best')
    plt.savefig('BollingerReversals.png')

    결과 그래프

     

    네이버 볼린저 밴드 반전 매매

    반전 매매도 상승 추세인 경우 수익이 발생하고, 추세 추종과 비교해서 하락장에서 매수 신호가 나오는 것을 볼 수 있다. 향후에 상승장이 발생하는 경우 수익이 발생할 수 있다.

    다음에는 삼중창 매매 시스템에 대해 알아보자.

    댓글

Designed by Tistory.