forked from FinanceData/FinanceDataReader
-
Notifications
You must be signed in to change notification settings - Fork 0
/
chart.py
162 lines (134 loc) · 5.81 KB
/
chart.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#-*- coding: utf-8 -*-
# chart_utils.py
# (c) 2018,2021 FinaceData.KR
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
__fact_def_params = { # factory default params
'width': 800,
'height': 480,
'volume_height': 0.3, # 30% size of figure height
'recent_high': False,
'volume': True,
'title': '',
'ylabel': '',
'moving_average_type': 'SMA', # 'SMA', 'WMA', 'EMA'
'moving_average_lines': (5, 20, 60),
'color_up': 'red',
'color_down': 'blue',
'color_volume_up': 'red',
'color_volume_down': 'blue',
}
__plot_params = dict(__fact_def_params)
# tableau 10 colors for moving_average_lines
tab_colors =['tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple',
'tab:brown', 'tab:pink', 'tab:gray', 'tab:olive', 'tab:cyan']
bokeh_install_msg = '''
FinanceDataReade.chart.plot() dependen on bokeh
bokeh not installed please install as follows
FinanceDataReade.chart.plot()는 bokeh에 의존성이 있습니다.
명령창에서 다음과 같이 bokeh를 설치하세요
pip insatll bokeh
'''
def config(**kwargs):
global __plot_params
for key,value in kwargs.items():
if key.lower()=='reset' and value:
__plot_params = dict(__fact_def_params)
elif key=='config':
for k,v in value.items():
__plot_params[k] = v
else:
__plot_params[key] = value
def plot(df, start=None, end=None, **kwargs):
'''
plot candle chart with 'df'(DataFrame) from 'start' to 'end'
* df: DataFrame to plot
* start(default: None)
* end(default: None)
* recent_high: display recent high price befre n-days (if recent_high == -1 plot plot recent high yesterday)
'''
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)
params = dict(__plot_params)
for key,value in kwargs.items():
if key == 'config':
for key,value in kwargs.items():
params[key] = value
else:
params[key] = value
df = df.loc[start:end].copy()
ma_type = params['moving_average_type']
weights = np.arange(240) + 1
for n in params['moving_average_lines']: # moving average lines
if ma_type.upper() == 'SMA':
df[f'MA_{n}'] = df.Close.rolling(window=n).mean()
elif ma_type.upper() == 'WMA':
df[f'MA_{n}'] = df.Close.rolling(n).apply(lambda prices: np.dot(prices, weights[:n])/weights[:n].sum())
elif ma_type.upper() == 'EMA':
df[f'MA_{n}'] = df.Close.ewm(span=n).mean()
elif ma_type.upper() == 'None':
pass
else:
raise ValueError(f"moving_average_type '{ma_type}' is invalid")
inc = df.Close > df.Open
dec = df.Open > df.Close
output_notebook()
# plot price OHLC candles
x = np.arange(len(df))
height = params['height']
if params['volume']:
height = int(params['height'] - params['height'] * params['volume_height'])
pp = figure(plot_width=params['width'],
plot_height=height,
x_range=(-1, min(120, len(df))),
y_range=(df.Low.min(), df.High.max()),
title=params['title'],
y_axis_label=params['ylabel'])
pp.segment(x[inc], df.High[inc], x[inc], df.Low[inc], color=params['color_up'])
pp.segment(x[dec], df.High[dec], x[dec], df.Low[dec], color=params['color_down'])
pp.vbar(x[inc], 0.8, df.Open[inc], df.Close[inc], fill_color=params['color_up'], line_color=params['color_up'])
pp.vbar(x[dec], 0.8, df.Open[dec], df.Close[dec], fill_color=params['color_down'], line_color=params['color_down'])
pp.yaxis[0].formatter = NumeralTickFormatter(format='0,0')
if params['volume']:
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
for ix,n in enumerate(params['moving_average_lines']):
pal = d3['Category10'][10]
pp.line(x, df[f'MA_{n}'], line_color=pal[ix % len(pal)])
if params['recent_high']:
to = df.index.max() + timedelta(days=params['recent_high'])
hline = Span(location=df.Close[:to].max(), dimension='width', line_dash='dashed', line_color='gray', line_width=2)
pp.renderers.extend([hline])
# plot volume
if params['volume']:
inc = df.Volume.diff() >= 0
dec = df.Volume.diff() < 0
height = int(params['height'] * params['volume_height'])
pv = figure(plot_width=params['width'], plot_height=height, x_range = pp.x_range)
pv.vbar(x[inc], 0.8, df.Volume[inc], fill_color=params['color_volume_up'], line_color="black")
pv.vbar(x[dec], 0.8, df.Volume[dec], fill_color=params['color_volume_down'], 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]])
show(p)
if 'save' in kwargs:
export_png(p, filename=kwargs['save'])