Subplot 기능을 활용하여 캔들차트와 보조지표(MACD) 출력하기

Subplot 기능을 활용하여 캔들차트와 보조지표(MACD) 출력하기

2019, Jan 05    
03_MACD

Subplot 기능을 활용하여 캔들차트와 보조지표(MACD) 출력하기

by JunPyo Park

Part of the Alpha Square Lecture Series:


오늘은 matplotlib의 subplot 기능을 활용하여 캔들차트와 MACD 같은 보조지표를 같이 그리는 방법에 대해 알아보도록 하겠습니다. 주피터 노트북 상에서 주가 데이터를 읽어와 캔들차트와 이동평균선을 그리는 방법, MACD에 대한 설명은 위의 링크를 참조해 주세요.

1. 주가 데이터를 읽어와 MACD 계산하기

주가 데이터를 읽어와 내용물을 확인합니다.

In [2]:
import pandas as pd
import pandas_datareader.data as web
from datetime import datetime

f = web.DataReader('AAPL', 'robinhood')
data = f.loc['AAPL']['2018-01':'2018-09']
In [3]:
data.head()
Out[3]:
close_price high_price interpolated low_price open_price session volume
begins_at
2018-01-02 169.712100 169.751500 False 166.756500 167.643100 reg 25555934
2018-01-03 169.682500 171.968200 False 169.416500 169.978100 reg 29517899
2018-01-04 170.470700 170.904200 False 169.534700 169.987900 reg 22434597
2018-01-05 172.411600 172.776100 False 170.490400 170.874600 reg 23660018
2018-01-08 171.771200 173.012500 False 171.357400 171.771200 reg 20567766
In [4]:
data.tail()
Out[4]:
close_price high_price interpolated low_price open_price session volume
begins_at
2018-09-24 220.022300 220.490700 False 215.876800 216.066100 reg 27693358
2018-09-25 221.417400 222.045300 False 218.936100 218.985900 reg 24554379
2018-09-26 219.653600 222.972000 False 218.995900 220.231600 reg 23984706
2018-09-27 224.167800 225.652700 False 222.762700 223.041800 reg 30181227
2018-09-28 224.955100 225.054800 False 223.241100 224.008400 reg 22929364

인덱스에 들어가 있는 날짜들의 타입을 문자열로 바꾸어 줍니다.

In [5]:
index = data.index.astype('str')
print("Period from ", index[0], " To ", index[-1])
Period from  2018-01-02  To  2018-09-28

5일 이동평균선20일 이동평균선을 계산하여 저장합니다.

In [6]:
ma5 = data['close_price'].rolling(window=5).mean()
In [7]:
ma20 = data['close_price'].rolling(window=20).mean()

MACD를 계산하도록 하겠습니다. MACD에 대한 설명은 기술적 분석 시리즈 #5 : MACD를 참고해 주세요. 기본적으로 MACD는 다음과 같이 계산됩니다.

MACD는 단순이동평균이 아닌 지수이동평균(Exponential Moving Average : EMA)을 사용합니다. 지수이동평균은 가중치를 부여한 평균의 일종으로써 최근의 데이터에 좀 더 가중치를 두는 방식입니다. 식을 전개했을 때 가중치를 살펴보면 k라는 평활 상수(smoothing constant)가 지수함수의 형태로 나타나기에 지수이동평균이라고 불립니다. 지수이동평균은 다음과 같은 코드를 통해 함수화할 수 있습니다.

In [8]:
def EMA(close, timeperiod):  # Exponential Moving Average : 지수이동평균
    k = 2/(1+timeperiod) # k : smoothing constant
    close = close.dropna()
    ema = pd.Series(index=close.index)
    ema[timeperiod-1] = close.iloc[0:timeperiod].sum() / timeperiod
    for i in range(timeperiod,len(close)):
        ema[i] = close[i]*k + ema[i-1] * (1-k)
    return ema

위의 지수이동평균 함수를 활용하여 다음과 같이 MACD 함수를 만들 수 있습니다. 12, 26, 9의 기간이 MACD를 산출하는데 보통 사용되기에 초깃값으로 지정해 두었습니다. 물론 차트 분석가의 재량에 따라 다른 값을 사용할 수 있습니다. 이런 경우에는 파라미터에 다른 값을 넘겨 주면 됩니다.

In [9]:
def MACD(close, fastperiod=12, slowperiod=26, signalperiod=9):
    macd = EMA(close, fastperiod) - EMA(close, slowperiod)
    macd_signal = EMA(macd, signalperiod)
    macd_osc = macd - macd_signal
    df = pd.concat([macd, macd_signal, macd_osc],axis=1)
    df.columns = ['MACD','Signal','Oscillator']
    return df

위에서 추출한 종가 데이터가 소숫점(.)이 들어간 포맷팅 때문에 문자열 타입을 가지고 있습니다. 이를 소수점 자료형(float)으로 바꾸어 줍니다.

In [10]:
close_price = data['close_price'].astype(float)

다음과 같이 만들어둔 함수를 통해 MACD와 시그널 오실레이터를 얻을 수 있습니다.

In [11]:
macd = MACD(close_price)
In [12]:
macd.tail()
Out[12]:
MACD Signal Oscillator
begins_at
2018-09-24 2.159969 3.456346 -1.296377
2018-09-25 2.152658 3.195608 -1.042951
2018-09-26 1.981696 2.952826 -0.971130
2018-09-27 2.185275 2.799316 -0.614040
2018-09-28 2.382676 2.715988 -0.333312

2. subplot 기능 활용하기

여러 개의 차트를 레이아웃에 맞추어 그려야 할 때 matplotlib에서 제공하는 subplot 기능을 활용할 수 있습니다. 여기서는 subplot2grid를 활용하도록 하겠습니다. 예제를 하나 살펴보도록 하겠습니다.

In [77]:
import matplotlib.pyplot as plt

plt.close('all')
fig = plt.figure(figsize=(9,9))

ax1 = plt.subplot2grid((3, 3), (0, 0))
ax2 = plt.subplot2grid((3, 3), (0, 1), colspan=2)
ax3 = plt.subplot2grid((3, 3), (1, 0), colspan=2, rowspan=2)
ax4 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)

plt.tight_layout()

제각기 다른 사이즈의 차트 4개가 생성되었는데 다음 그림과 같이 각 차트 왼쪽 위 꼭짓점을 좌표에 대응 시켜보면 코드 이해가 한결 쉬워집니다.

코드 마지막 부분에 있는 plt.tight_layout() 명령어는 차트끼리 충돌하는 것을 막아주는 명령어입니다.

3. 캔들차트와 보조지표 plot 하기

이제 위 내용들을 바탕으로 최종 차트를 그려보도록 하겠습니다. 전체 코드는 다음과 같습니다. 파이썬에서 캔들차트와 이동평균선 그리기를 먼저 완료한 뒤 진행 하시면 쉽게 따라하실 수 있습니다.

In [19]:
from mpl_finance import candlestick2_ohlc
import matplotlib.pyplot as plt
%matplotlib inline
import matplotlib.ticker as ticker
import numpy as np

# 차트 레이아웃을 설정합니다.
fig = plt.figure(figsize=(12,10))
ax_main = plt.subplot2grid((5, 1), (0, 0), rowspan=3)
ax_sub = plt.subplot2grid((5, 1), (3, 0))
ax_sub2 = plt.subplot2grid((5, 1), (4, 0))

# 메인차트를 그립니다.
ax_main.set_title('Apple Inc. Q1~Q3 2018 Stock Price',fontsize=20)
ax_main.plot(index, ma5, label='MA5')
ax_main.plot(index, ma20, label='MA20')
candlestick2_ohlc(ax_main,data['open_price'],data['high_price'],
                  data['low_price'],data['close_price'],width=0.6)

ax_main.legend(loc=5)

# 아래는 날짜 인덱싱을 위한 함수 입니다.
def mydate(x,pos):
    try:
        return index[int(x-0.5)]
    except IndexError:
        return ''

# ax_sub 에 MACD 지표를 출력합니다.
ax_sub.set_title('MACD',fontsize=15)
macd['MACD'].iloc[0] = 0
ax_sub.plot(index,macd['MACD'], label='MACD')
ax_sub.plot(index,macd['Signal'], label='MACD Signal')
ax_sub.legend(loc=2)

# ax_sub2 에 MACD 오실레이터를 bar 차트로 출력합니다.
ax_sub2.set_title('MACD Oscillator',fontsize=15)
oscillator = macd['Oscillator']
oscillator.iloc[0] = 1e-16
ax_sub2.bar(list(index),list(oscillator.where(oscillator > 0)), 0.7)
ax_sub2.bar(list(index),list(oscillator.where(oscillator < 0)), 0.7)

# x 축을 조정합니다.
ax_main.xaxis.set_major_locator(ticker.MaxNLocator(7))
ax_main.xaxis.set_major_formatter(ticker.FuncFormatter(mydate))
ax_sub.xaxis.set_major_locator(ticker.MaxNLocator(7))
ax_sub2.xaxis.set_major_locator(ticker.MaxNLocator(7))
fig.autofmt_xdate()

# 차트끼리 충돌을 방지합니다.
plt.tight_layout()
plt.show()