python/kourgeorge/project-origin/visualization/dash_online.py

dash_online.py
__author__ = 'gkour'
from bokeh.plotting import figure, output_file, show, save
from bokeh.models import ColumnDataSource, Slider, Select
import numpy as np


def _create_prices(t):
    last_average = 100
    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)


MA12, MA26, EMA12, EMA26 = '12-tick Moving Avg', '26-tick Moving Avg', '12-tick EMA', '26-tick EMA'
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])


class Dashborad:
    def __init__(self, file_path="lines.html"):
        self._source = ColumnDataSource(dict(
            time=[], average=[], low=[], high=[], open=[], close=[],
            ma=[], macd=[], macd9=[], macdh=[], color=[]))

        output_file(file_path)

        p = figure(plot_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=self._source)
        p.line(x='time', y='ma', alpha=0.8, line_width=2, color='orange', source=self._source)
        p.segment(x0='time', y0='low', x1='time', y1='high', line_width=2, color='black', source=self._source)
        p.segment(x0='time', y0='open', x1='time', y1='close', line_width=8, color='color', source=self._source)

        show(p)

    def update(self, t):
        # df = pd.DataFrame.from_csv(path=self._input_csv, header=0, index_col=0)
        #
        # x = [1, 2, 3, 4, 5]
        # y = np.random.randint(1, 10, 5)
        #
        # self._p.line(x, y, legend="Temp.", line_width=2)
        # save(self._p)

        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 = self._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 = self._source.data['macd'] + [macd]
        macd9 = _ema(macd_series[-26:], 9)[0]
        new_data['macd9'] = [macd9]
        new_data['macdh'] = [macd - macd9]

        self._source.stream(new_data, 300)

dash = Dashborad()
for t in range(1000):
    dash.update(t)