본문 바로가기
컴퓨터/Python

[Python] 파이썬, 주식 차트와 보조지표 그리기 (Plotly)

by sjblog 2021. 12. 20.
반응형

파이썬을 이용하여 주식차트와 보조지표를 그려보겠습니다.

 

<완성본>

 

1. 차트 구성

위의 <완성본>에 사용된 지표를 소개하겠습니다.

 

주식차트: 캔들차트, 거래량

보조지표: 볼린저밴드, 20일 이동평균선, MACD, 스토캐스틱 Fast_K, Slow_D, PB, MFI10, RSI

전략: 추세추종전략, 역추세 전략

 

각각의 지표들의 설명은 이번 글에서 논외로 하고, 지표들을 파이썬을 이용하여 그리는 것에 중점을 두겠습니다.

 

2. 준비물

VScode, Python

 

3. 전체 종목코드와 종목명 가져오기

from pykrx import stock
import pandas as pd
import mplfinance as mpf
import numpy as np
import plotly.graph_objects as go
import plotly.subplots as ms
import plotly.express as px

 

 
from pykrx import stock
# pykrx를 통해 국내 주식 정보를 가져올 수 있습니다.
 

GitHub - sharebook-kr/pykrx: KRX 주식 정보 스크래핑

KRX 주식 정보 스크래핑. Contribute to sharebook-kr/pykrx development by creating an account on GitHub.

github.com

 
 
 
import plotly.graph_objects as go
import plotly.subplots as ms
import plotly.express as px
# plotly를 사용하여 그래프를 그립니다.
 

Plotly Python Graphing Library

Plotly's Python graphing library makes interactive, publication-quality graphs. Examples of how to make line plots, scatter plots, area charts, bar charts, error bars, box plots, histograms, heatmaps, subplots, multiple-axes, polar charts, and bubble chart

plotly.com

 

 

 

# 전체 종목코드와 종목명 가져오기
stock_list = pd.DataFrame({'종목코드':stock.get_market_ticker_list(market="ALL")})
stock_list['종목명'] = stock_list['종목코드'].map(lambda x: stock.get_market_ticker_name(x))

# stock_name의 2021년 주가 데이터 가져오기
stock_name = "삼성전자"
stock_from = "20201220"
stock_to = "20211220"

ticker = stock_list.loc[stock_list['종목명']== stock_name, '종목코드']
df = stock.get_market_ohlcv_by_date(fromdate=stock_from, todate=stock_to, ticker=ticker)
# 칼럼명을 영문명으로 변경
df = df.rename(columns={'시가':'Open', '고가':'High', '저가':'Low', '종가':'Close', '거래량':'Volume'})

df["Close"]=df["Close"].apply(pd.to_numeric,errors="coerce")
 
 
stock_list = pd.DataFrame({'종목코드':stock.get_market_ticker_list(market="ALL")})
stock_list['종목명'] = stock_list['종목코드'].map(lambda x: stock.get_market_ticker_name(x))
# market="ALL" 모든 종목 주식
# market="KOSPI" 코스피
# market="KOSDAQ" 코스닥
# ETF 등은 get_etf_ticker_list 함수를 확인해주세요!
 
 
stock_name = "삼성전자"
stock_from = "20201220"
stock_to = "20211220"
# 조회할 종목과 날짜를 입력합니다.
 
 

4. 보조지표 구하기

볼린저 밴드 구하기

# 볼린저밴드 구하기
df['ma20'] = df['Close'].rolling(window=20).mean() # 20일 이동평균
df['stddev'] = df['Close'].rolling(window=20).std() # 20일 이동표준편차
df['upper'] = df['ma20'] + 2*df['stddev'] # 상단밴드
df['lower'] = df['ma20'] - 2*df['stddev'] # 하단밴드

 

MACD 구하기

# MACD 구하기
df['ma12'] = df['Close'].rolling(window=12).mean() # 12일 이동평균
df['ma26'] = df['Close'].rolling(window=26).mean() # 26일 이동평균
df['MACD'] = df['ma12'] - df['ma26']    # MACD
df['MACD_Signal'] = df['MACD'].rolling(window=9).mean() # MACD Signal(MACD 9일 이동평균)
df['MACD_Oscil'] = df['MACD'] - df['MACD_Signal']   #MACD 오실레이터

 

스토캐스틱 구하기

# 스토캐스틱 구하기
df['ndays_high'] = df['High'].rolling(window=14, min_periods=1).max()    # 14일 중 최고가
df['ndays_low'] = df['Low'].rolling(window=14, min_periods=1).min()      # 14일 중 최저가
df['fast_k'] = (df['Close'] - df['ndays_low']) / (df['ndays_high'] - df['ndays_low']) * 100  # Fast %K 구하기
df['slow_d'] = df['fast_k'].rolling(window=3).mean()    # Slow %D 구하기

 

MFI 구하기

# MFI 구하기
df['PB'] = (df['Close'] - df['lower']) / (df['upper'] - df['lower'])
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
    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'])

 

역추세 전략을 위한 IIP 계산

#역추세전략을 위한 IIP계산
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

 

RSI 구하기

#RSI 구하기
U = np.where(df['Close'].diff(1) > 0, df['Close'].diff(1), 0)
D = np.where(df['Close'].diff(1) < 0, df['Close'].diff(1) *(-1), 0)
AU = pd.DataFrame(U, index=df.index).rolling(window=14).mean()
AD = pd.DataFrame(D, index=df.index).rolling(window=14).mean()
RSI = AU / (AD+AU) *100
df['RSI'] = RSI

 

정리

df = df[25:]
 

df = df[25:]

# 위의 보조지표들이 초기 시작일부터 최대 25일까지 표시되지 않기 때문입니다.

 

 

5. Plotly를 사용하여 차트 그리기

Plotly 차트 데이터

candle = go.Candlestick(x=df.index,open=df['Open'],high=df['High'],low=df['Low'],close=df['Close'], increasing_line_color = 'red',decreasing_line_color = 'blue', showlegend=False)
upper = go.Scatter(x=df.index, y=df['upper'], line=dict(color='red', width=2), name='upper', showlegend=False)
ma20 = go.Scatter(x=df.index, y=df['ma20'], line=dict(color='black', width=2), name='ma20', showlegend=False)
lower = go.Scatter(x=df.index, y=df['lower'], line=dict(color='blue', width=2), name='lower', showlegend=False)

Volume = go.Bar(x=df.index, y=df['Volume'], marker_color='red', name='Volume', showlegend=False)

MACD = go.Scatter(x=df.index, y=df['MACD'], line=dict(color='blue', width=2), name='MACD', legendgroup='group2', legendgrouptitle_text='MACD')
MACD_Signal = go.Scatter(x=df.index, y=df['MACD_Signal'], line=dict(dash='dashdot', color='green', width=2), name='MACD_Signal')
MACD_Oscil = go.Bar(x=df.index, y=df['MACD_Oscil'], marker_color='purple', name='MACD_Oscil')

fast_k = go.Scatter(x=df.index, y=df['fast_k'], line=dict(color='skyblue', width=2), name='fast_k', legendgroup='group3', legendgrouptitle_text='%K %D')
slow_d = go.Scatter(x=df.index, y=df['slow_d'], line=dict(dash='dashdot', color='black', width=2), name='slow_d')

PB = go.Scatter(x=df.index, y=df['PB']*100, line=dict(color='blue', width=2), name='PB', legendgroup='group4', legendgrouptitle_text='PB, MFI')
MFI10 = go.Scatter(x=df.index, y=df['MFI10'], line=dict(dash='dashdot', color='green', width=2), name='MFI10')

RSI = go.Scatter(x=df.index, y=df['RSI'], line=dict(color='red', width=2), name='RSI', legendgroup='group5', legendgrouptitle_text='RSI')

 

Plotly 차트 스타일

# 스타일
fig = ms.make_subplots(rows=5, cols=2, specs=[[{'rowspan':4},{}],[None,{}],[None,{}],[None,{}],[{},{}]], shared_xaxes=True, horizontal_spacing=0.03, vertical_spacing=0.01)

fig.add_trace(candle,row=1,col=1)
fig.add_trace(upper,row=1,col=1)
fig.add_trace(ma20,row=1,col=1)
fig.add_trace(lower,row=1,col=1)

fig.add_trace(Volume,row=5,col=1)

fig.add_trace(candle,row=1,col=2)
fig.add_trace(upper,row=1,col=2)
fig.add_trace(ma20,row=1,col=2)
fig.add_trace(lower,row=1,col=2)

fig.add_trace(MACD,row=2,col=2)
fig.add_trace(MACD_Signal,row=2,col=2)
fig.add_trace(MACD_Oscil,row=2,col=2)

fig.add_trace(fast_k,row=3,col=2)
fig.add_trace(slow_d,row=3,col=2)

fig.add_trace(PB,row=4,col=2)
fig.add_trace(MFI10,row=4,col=2)

fig.add_trace(RSI,row=5,col=2)

# 추세추종
for i in range(len(df['Close'])):
    if df['PB'][i] > 0.8 and df['MFI10'][i] > 80:
        trend_fol = go.Scatter(x=[df.index[i]], y=[df['Close'][i]], marker_color='orange', marker_size=20, marker_symbol='triangle-up', opacity=0.7, showlegend=False)
        fig.add_trace(trend_fol,row=1,col=1)
    elif df['PB'][i] < 0.2 and df['MFI10'][i] < 20:
        trend_fol = go.Scatter(x=[df.index[i]], y=[df['Close'][i]], marker_color='darkblue', marker_size=20, marker_symbol='triangle-down', opacity=0.7, showlegend=False)
        fig.add_trace(trend_fol,row=1,col=1)

# 역추세추종
for i in range(len(df['Close'])):
    if df['PB'][i] < 0.05 and df['IIP21'][i] > 0:
        trend_refol = go.Scatter(x=[df.index[i]], y=[df['Close'][i]], marker_color='purple', marker_size=20, marker_symbol='triangle-up', opacity=0.7, showlegend=False)  #보라
        fig.add_trace(trend_refol,row=1,col=1)
    elif df['PB'][i] > 0.95 and df['IIP21'][i] < 0:
        trend_refol = go.Scatter(x=[df.index[i]], y=[df['Close'][i]], marker_color='skyblue', marker_size=20, marker_symbol='triangle-down', opacity=0.7, showlegend=False)  #하늘
        fig.add_trace(trend_refol,row=1,col=1)

 

fig.add_trace(trend_refol,row=1,col=1)

# 추세추총전략을 통해 캔들차트에 표시합니다.

 

 

fig.add_trace(trend_refol,row=1,col=1)

# 역추세 전략을 통해 캔들차트에 표시합니다.

 

 

Plotly 레이아웃

fig.update_layout(autosize=True, xaxis1_rangeslider_visible=False, xaxis2_rangeslider_visible=False, margin=dict(l=50,r=50,t=50,b=50), template='seaborn', title=f'{stock_name}({int(ticker.values)})의 날짜: {stock_to} [추세추종전략:오↑파↓] [역추세전략:보↑하↓]')
fig.update_xaxes(tickformat='%y년%m월%d일', zeroline=True, zerolinewidth=1, zerolinecolor='black', showgrid=True, gridwidth=2, gridcolor='lightgray', showline=True,linewidth=2, linecolor='black', mirror=True)
fig.update_yaxes(tickformat=',d', zeroline=True, zerolinewidth=1, zerolinecolor='black', showgrid=True, gridwidth=2, gridcolor='lightgray',showline=True,linewidth=2, linecolor='black', mirror=True)
fig.update_traces(xhoverformat='%y년%m월%d일')

config = dict({'scrollZoom': True})
fig.show(config=config)

 

config = dict({'scrollZoom': True})

# Plotly 스크롤 줌을 가능하게 합니다.

 

반응형