-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
/
main.py
103 lines (82 loc) · 3.85 KB
/
main.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
''' A stock OHLC chart that monitors the MACD indicator.
This example shows the streaming and updating feature of Bokeh charts.
'''
import numpy as np
from bokeh.driving import count
from bokeh.layouts import column, gridplot, row
from bokeh.models import ColumnDataSource, Select, Slider
from bokeh.plotting import curdoc, figure
np.random.seed(1)
MA12, MA26, EMA12, EMA26 = '12-tick Moving Avg', '26-tick Moving Avg', '12-tick EMA', '26-tick EMA'
source = ColumnDataSource(dict(
time=[], average=[], low=[], high=[], open=[], close=[],
ma=[], macd=[], macd9=[], macdh=[], color=[],
))
p = figure(height=500, tools="xpan,xwheel_zoom,xbox_zoom,reset", x_axis_type=None, y_axis_location="right")
p.x_range.follow = "end"
p.x_range.follow_interval = 100
p.x_range.range_padding = 0
p.line(x='time', y='average', alpha=0.2, line_width=3, color='navy', source=source)
p.line(x='time', y='ma', alpha=0.8, line_width=2, color='orange', source=source)
p.segment(x0='time', y0='low', x1='time', y1='high', line_width=2, color='black', source=source)
p.segment(x0='time', y0='open', x1='time', y1='close', line_width=8, color='color', source=source)
p2 = figure(height=250, x_range=p.x_range, tools="xpan,xwheel_zoom,xbox_zoom,reset", y_axis_location="right")
p2.line(x='time', y='macd', color='red', source=source)
p2.line(x='time', y='macd9', color='blue', source=source)
p2.segment(x0='time', y0=0, x1='time', y1='macdh', line_width=6, color='black', alpha=0.5, source=source)
mean = Slider(title="mean", value=0, start=-0.01, end=0.01, step=0.001)
stddev = Slider(title="stddev", value=0.04, start=0.01, end=0.1, step=0.01)
mavg = Select(value=MA12, options=[MA12, MA26, EMA12, EMA26])
def _create_prices(t):
last_average = 100 if t==0 else source.data['average'][-1]
returns = np.asarray(np.random.lognormal(mean.value, stddev.value, 1))
average = last_average * np.cumprod(returns)
high = average * np.exp(abs(np.random.gamma(1, 0.03, size=1)))
low = average / np.exp(abs(np.random.gamma(1, 0.03, size=1)))
delta = high - low
open = low + delta * np.random.uniform(0.05, 0.95, size=1)
close = low + delta * np.random.uniform(0.05, 0.95, size=1)
return open[0], high[0], low[0], close[0], average[0]
def _moving_avg(prices, days=10):
if len(prices) < days: return [100]
return np.convolve(prices[-days:], np.ones(days, dtype=float), mode="valid") / days
def _ema(prices, days=10):
if len(prices) < days or days < 2: return [prices[-1]]
a = 2.0 / (days+1)
kernel = np.ones(days, dtype=float)
kernel[1:] = 1 - a
kernel = a * np.cumprod(kernel)
# The 0.8647 normalizes out that we stop the EMA after a finite number of terms
return np.convolve(prices[-days:], kernel, mode="valid") / (0.8647)
@count()
def update(t):
open, high, low, close, average = _create_prices(t)
color = "green" if open < close else "red"
new_data = dict(
time=[t],
open=[open],
high=[high],
low=[low],
close=[close],
average=[average],
color=[color],
)
close = source.data['close'] + [close]
ma12 = _moving_avg(close[-12:], 12)[0]
ma26 = _moving_avg(close[-26:], 26)[0]
ema12 = _ema(close[-12:], 12)[0]
ema26 = _ema(close[-26:], 26)[0]
if mavg.value == MA12: new_data['ma'] = [ma12]
elif mavg.value == MA26: new_data['ma'] = [ma26]
elif mavg.value == EMA12: new_data['ma'] = [ema12]
elif mavg.value == EMA26: new_data['ma'] = [ema26]
macd = ema12 - ema26
new_data['macd'] = [macd]
macd_series = source.data['macd'] + [macd]
macd9 = _ema(macd_series[-26:], 9)[0]
new_data['macd9'] = [macd9]
new_data['macdh'] = [macd - macd9]
source.stream(new_data, 300)
curdoc().add_root(column(row(mean, stddev, mavg), gridplot([[p], [p2]], toolbar_location="left", width=1000)))
curdoc().add_periodic_callback(update, 50)
curdoc().title = "OHLC"