파이썬 캔들 차트 구현
주가 분석 연구를 하게 되면 차트를 통해 시각화해야하는 경우가 많다.
그러나 캔들 차트는 구현이 까다로워서 대중적으로 사용되지는 않았었는데, FinaceDataReader 라이브러리에
차트 기능이 추가되어 간편하게 차트 시각화가 가능해졌다.
직접 사용해 본 결과, 코드 한줄로도 간단한 캔들 차트가 완성되어 무척 신기했다. 그러나 각 캔들의 정확한 날짜를
알기 위해서는 줌 인 기능을 통해 엄청 크게 확대한 후 눈으로 짐작해야해서 매우 불편했다.
따라서 가격 캔들과 거래량 봉에 마우스를 갖다대면 날짜와 가격을 알 수 있도록 코드를 커스터마이징 했다.
코드는 다음과 같으며 깃허브에도 공유할 예정이다.
사용하기 위해서는 FDR의 chart.plot 함수를 재정의 하거나 아래 코드의 함수를 자신만의 이름으로 바꿔서 사용하면 된다.
def get_width(df):
mindate = min(df.index)
maxdate = max(df.index)
width = 0.8 #vbar의 가로폭 0.8 로 설정
return width * (maxdate-mindate).total_seconds()*1000 / len(df.index)
def chart_plot(df,start=None,end=None,volume=False,ma_lines=[5,20,60]):
try:
from bokeh.plotting import figure, gridplot
from bokeh.models import NumeralTickFormatter, DatetimeTickFormatter, Span
from bokeh.io import output_notebook, show, export_png
from bokeh.palettes import d3
except ModuleNotFoundError as e:
raise ModuleNotFoundError(bokeh_install_msg)
for n in ma_lines: # 이동평균 계산
df[f'MA_{n}'] = df.Close.rolling(window=n).mean()
inc = df.Close > df.Open
dec = df.Open > df.Close
##############
df['date_'] = df.index
# plot price OHLC candles
x = np.arange(len(df))
width = 940
height = 680 # 그래프 전체 높이 설정
if volume == True:
height = int(height - height * 0.3) # 거래량 비중 0.3
TOOLTIPS = [
("index", "$index"),
("y", "$y{0.0})"),
("date", "$x{%F}"),
]
pp = figure(plot_width=width,
plot_height=height,
x_range=Range1d(start=df.index[0], end=df.index[-1]),
y_range=(df.Low.min(), df.High.max()),
title='',
y_axis_label='',
tools=[PanTool(), WheelZoomTool(), BoxZoomTool(),SaveTool(), ResetTool(), HelpTool(),
HoverTool(formatters={'$x':'datetime'})],
tooltips=TOOLTIPS)
pp.segment(df.index[inc], df.High[inc], df.index[inc], df.Low[inc], color='red')
pp.segment(df.index[dec], df.High[dec], df.index[dec], df.Low[dec], color='blue')
pp.vbar(df.index[inc], get_width(df), df.Open[inc], df.Close[inc], fill_color='red', line_color='red')
pp.vbar(df.index[dec], get_width(df), df.Open[dec], df.Close[dec], fill_color='blue', line_color='blue')
pp.yaxis[0].formatter = NumeralTickFormatter(format='0,0')
if volume == True:
pp.xaxis.visible = False
else:
x_labels = {i: dt.strftime('%Y-%m-%d') for i,dt in enumerate(df.index)}
x_labels.update({len(df): ''})
pp.xaxis.major_label_overrides = x_labels
pp.xaxis.formatter=DatetimeTickFormatter(hours=["%H:%M"], days=["%Y-%m-%d"])
pp.xaxis.major_label_orientation = np.pi / 5
source = ColumnDataSource(data = {i:df[i] for i in df.columns})
for ix,n in enumerate(ma_lines):
pal = d3['Category10'][10]
pp.line(df.index, df[f'MA_{n}'], line_color=pal[ix % len(pal)],legend_label=f'MA_{n}')
# plot volume
if volume == True:
inc = df.Volume.diff() >= 0
dec = df.Volume.diff() < 0
height = int(height * 0.3)
pv = figure(plot_width= width, plot_height=height, x_range = pp.x_range,
tools= [HoverTool(formatters={'$x':'datetime'})],tooltips=TOOLTIPS)
pv.vbar(df.index[inc], get_width(df), df.Volume[inc], fill_color='red', line_color="black")
pv.vbar(df.index[dec], get_width(df), df.Volume[dec], fill_color='blue', line_color="black")
pv.yaxis[0].formatter = NumeralTickFormatter(format='0,0')
x_labels = {i: dt.strftime('%Y-%m-%d') for i,dt in enumerate(df.index)}
x_labels.update({len(df): ''})
pv.xaxis.major_label_overrides = x_labels
pv.xaxis.formatter=DatetimeTickFormatter(hours=["%H:%M"], days=["%Y-%m-%d"])
pv.xaxis.major_label_orientation = np.pi / 5
pv.y_range.range_padding = 0
# 가격(pp)과 거래량(pv) 함께 그리기
p = gridplot([[pp], [pv]])
else:
p = gridplot([[pp]])
output_notebook()
show(p)
수정한 코드를 통해서 함수를 실행하면 캔들에 마우스를 갖다 댈 경우 해당 캔들의 날짜가 보이도록 하였고,
차트에 거래량을 함께 볼지 말지, 이동평균으로 쓰고싶은 값을 한번에 입력받아 표시하도록 수정하였다.