Hi. I’m new to bokeh - on a steep learning curve- Love bokeh and its power though… I’m pulling live data from Bitfinex and saving it to csv with chosen interval required for live streamed plotting. Data seems fine as the candles do plot perfectly with manually refreshing the browser though. As a note, my csv file is not “appending data” it rewrites the full set of data every few seconds… don’t know if the problem sits here perhaps…
My “update_data” stream function does not update my plot automatically. so i have to refresh every minute
I’m getting following error from the bokeh serve --show app and cant seem to get behind the problem:
“Must stream updates to all existing columns (missing: %s)” % ", ".join(sorted(missing)))
ValueError: "Must stream updates to all existing columns (missing: level_0)
Please can someone help that my candles can update automatically?
Code below:
###Data CSV file looks like:
###############################################################
Date,Open,High,Low,Close,Volume
2020-05-13 17:37:00,9073.25943776,9073.25943776,9073.2,9073.2,0.23892929000000002
2020-05-13 17:38:00,9073.234011,9073.234011,9073.2,9073.2,0.75118941
2020-05-13 17:39:00,9066.5,9073.19340014,9063.0,9073.19340014,3.75187717
2020-05-13 17:40:00,9072.6,9072.7,9066.4,9066.8,0.14512834
2020-05-13 17:41:00,9065.5,9065.5,9065.4,9065.4,0.07300000000000001
2020-05-13 17:42:00,9065.4,9065.40283649,9052.8,9055.0,1.2116440400000001
2020-05-13 17:43:00,9052.20215918,9054.1,9052.0,9054.1,0.5493773
2020-05-13 17:44:00,9052.4,9055.1,9052.4,9055.0,0.47964840000000003
2020-05-13 17:45:00,9055.1,9055.1,9053.2,9055.1,1.31410793
and continues with with max 121 entries at all times with latest current data at the end of the file
################################################################
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import os
from bokeh.plotting import figure, ColumnDataSource
from bokeh.models.widgets import Dropdown
from bokeh.io import curdoc
from bokeh.layouts import column
from bokeh.models import BooleanFilter, CDSView, Select, Range1d, HoverTool
from bokeh.palettes import Category20
from bokeh.models.formatters import NumeralTickFormatter
###Define BOKEH constants below
###########################
W_PLOT = 1300
H_PLOT = 600
TOOLS = 'pan,wheel_zoom,hover,reset'
VBAR_WIDTH = 0.2
RED = Category20[7][6]
GREEN = Category20[5][4]
BLUE = Category20[3][0]
BLUE_LIGHT = Category20[3][1]
ORANGE = Category20[3][2]
PURPLE = Category20[9][8]
BROWN = Category20[11][10]
Python function below to collect data from csv file to format as above file extract
def get_symbol_df(symbol=None):
df = pd.DataFrame(pd.read_csv('D_Crypto_Bitfinance/' + symbol + '.csv'))[-50:]
#df.reset_index(inplace=True)
df["Date"] = pd.to_datetime(df["Date"])
return df
Python Function to plot stock
def plot_stock_price(stock):
p = figure(plot_width=W_PLOT, plot_height=H_PLOT, tools=TOOLS,
title="Bitcoin Live - Bitfinex", toolbar_location='above')
#####
inc = stock.data['Close'] > stock.data['Open']
dec = stock.data['Open'] > stock.data['Close']
view_inc = CDSView(source=stock, filters=[BooleanFilter(inc)])
view_dec = CDSView(source=stock, filters=[BooleanFilter(dec)])
###
# map dataframe indices to date strings and use as label overrides even during zoom in function
p.xaxis.major_label_overrides = { #'%b %d
i+int(stock.data['index'][0]): date.strftime( '%d-%b %H:%M:%S' ) for i, date in enumerate(pd.to_datetime(stock.data["Date"]))
}
p.xaxis.bounds = (stock.data['index'][0], stock.data['index'][-1])
p.segment(x0='index', x1='index', y0='Low', y1='High', color=RED, source=stock, view=view_inc)
p.segment(x0='index', x1='index', y0='Low', y1='High', color=GREEN, source=stock, view=view_dec)
p.vbar(x='index', width=VBAR_WIDTH, top='Open', bottom='Close', fill_color=BLUE, line_color=BLUE,
source=stock,view=view_inc, name="price")
p.vbar(x='index', width=VBAR_WIDTH, top='Open', bottom='Close', fill_color=RED, line_color=RED,
source=stock,view=view_dec, name="price")
####LEGEND####
# p.legend.location = "top_left"
#p.legend.border_line_alpha = 0
# p.legend.background_fill_alpha = 0
# p.legend.click_policy = "mute"
####-Y - AXIS FORMATTER####
p.yaxis.formatter = NumeralTickFormatter(format='$ 0,0[.]000')
p.x_range.range_padding = 0.05
p.xaxis.ticker.desired_num_ticks = 40
p.xaxis.major_label_orientation = 3.14/4
####
#Select specific tool for the plot#
price_hover = p.select(dict(type=HoverTool))
#Choose, which glyphs are active by glyph name#
price_hover.names = ["price"]
#Creating tooltips#
price_hover.tooltips = [("Datetime", "@Date{%Y-%m-%d}"),
("Open", "@Open{$0,0.00}"),
("Close", "@Close{$0,0.00}"),
("Volume", "@Volume{($ 0.00 a)}")]
price_hover.formatters={"Date": 'datetime'}
return p
##Bokeh Magic###
stock = ColumnDataSource(data=dict(Date=[ ], Open=[ ], High=[ ], Low=[ ], Close=[ ], Volume = [ ], index=[ ]))
symbol = 'read'
df = get_symbol_df(symbol)
stock.data = stock.from_df(df)
elements = list()
# First try update- error code relating to Must stream updates to all existing columns (missing: %s)" % #", ".join(sorted(missing)))
# ValueError: "Must stream updates to all existing columns (missing: level_0)
def update_data( ):
new_data = dict(Date=[ ],Open=[ ], High = [ ], Low = [ ],Close=[ ], Volume=[ ], index = [ ])
##new_data["Date"] = pd.to_datetime(new_data["Date"])
##After 10 seconds rollover the data
stock.stream(new_data=new_data, rollover=10)
# Second try update - different error codes relating to dataframe issues
#def update_data():
# new_data = get_symbol_df()
# stock.stream(dict(Date=new_data["Date"],
# Open=new_data["Open"],
# High=new_data['High'],
# Low=new_data['Low'],
# Close=new_data['Close']),
# 1000, rollover=10)
###update_plot()###
p_stock = plot_stock_price(stock)
elements.append(p_stock)
curdoc().add_root(column(elements))
curdoc().title = 'Bitcoin Live Data Plot'
curdoc().add_root( p )
###Update the data every 10seconds (1000*10 ms)
curdoc().add_periodic_callback(callback=update_data, period_milliseconds=1000 * 10)