Showing posts with label bokeh. Show all posts
Showing posts with label bokeh. Show all posts

Sunday, April 9, 2017

Embed Javascript in Bokeh




In the same spirit of pragmatism, my approach to embed JavaScript in Bokeh is to embed whole JavaScript files into Bokeh, rather than embedding JavaScript fragments. The reasons are:


  1.  It is difficult to write grammar-correct JavaScript fragments in Bokeh – Pycharm doesn’t help, and you can’t blame it. 
  2.  It is difficult to debug JavaScript.

With JQuery, and some creative maneuvering, you can do many things with Bokeh graphs. 

Here is how to embed JavaScript files into Bokeh:


from jinja2 import Template

from bokeh.embed import file_html

from bokeh.resources import INLINE

from bokeh.util.string import decode_utf8
import os, io

script_in_header='\n    <link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.css" type="text/css" />'+\

                 '\n     <script type="text/javascript" src="https://cdn.pydata.org/bokeh/release/bokeh-0.12.4.min.js"></script>'+\

                 '\n    <link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.css" type="text/css" />'+\

                 '\n    <script type="text/javascript" src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.4.min.js"></script>'+\

                 '\n    <script type="text/javascript" src=jquery-3.2.0.min.js"></script>'+\

                 '\n    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">'

template = Template("""
    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        """
+
        script_in_header  +
"""
    </head>
    <body>
        {{ plot_script }}
        {{ plot_div }} """
+
        script_end +
"""
    </body>
    </html>
    """
)

def save_graph(graph, output):

    output=os.path.realpath(output)

    destDir = os.path.dirname(output)

    



    html = file_html(graph, INLINE, template=template)

    with io.open(output, "w", encoding="utf-8") as f:

        f.write(decode_utf8(html))


One caveat, the graph elements are generated by Bokeh JavaScript at runtime, so do not count that some elements will be definitely there in $(document).ready.

Tuesday, March 21, 2017

Candlestick chart using Bokeh without date gaps



Candlestick chart is an important for stock technical analysis, and Bokeh is the best Python tool I’ve found so far to chart. Boken has a candlestick chart example http://bokeh.pydata.org/en/latest/docs/gallery/candlestick.html, there are some problems with this example though:


  1.  When there are gaps between dates (e.g. on weekends, most stock exchanges to not work), there will be gaps on the neighbouring candlesticks.    
  2. (This is a minor issue) The up and bottom tails should be of the same colour as the candle body, instead, they are of black.

For the first problem, google shows there are people who are looking at the same issue and without answers. I decided not to tackle the problem head on(because I am pragmatic), but work around it like the following (the head-on tackle would be some kind of DateGapTickFormatter):

  1.  Do not use date as the axis, rather use a sequence.
  2. Use tooltip when hover over a candlestick, the tooltip will show the detailed info of the candlestick.

Here is the relevant code:

#df contains stock information
#append a sequence to df

seqs=np.arange(df.shape[0])
df[
"seq"]=pd.Series(seqs)

df[
"date"] = pd.to_datetime(df["date"])
df[
'date']=df['date'].apply(lambda x: x.strftime('%m/%d'))
df[
'changepercent']=df['changepercent'].apply(lambda x: str(x)+"%")

df[
'mid']=df.apply(lambda x:(x['open']+x['close'])/2,axis=1)
df[
'height']=df.apply(lambda x:abs(x['close']-x['open'] if x['close']!=x['open'] else 0.001),axis=1)

inc = df.close > df.open
dec = df.open > df.close
w=
0.3

#use ColumnDataSource to pass in data for tooltips
sourceInc=ColumnDataSource(ColumnDataSource.from_df(df.loc[inc]))
sourceDec=ColumnDataSource(ColumnDataSource.from_df(df.loc[dec]))

#the values for the tooltip come from ColumnDataSource
hover = HoverTool(
   
tooltips=[
        (
"date", "@date"),
        (
"open", "@open"),
        (
"close", "@close"),
        (
"percent", "@changepercent"),
    ]
)

TOOLS = [CrosshairTool(), hover]
p = figure(
plot_width=700, plot_height=400, tools=TOOLS,title = df["code"][0]+" "+df["name"][0])
p.xaxis.major_label_orientation = pi/
4
p.grid.grid_line_alpha=0.3

#this is the up tail
p.segment(df.seq[inc], df.high[inc], df.seq[inc], df.low[inc], color="red")
#this is the bottom tail
p.segment(df.seq[dec], df.high[dec], df.seq[dec], df.low[dec],
color="green")
#this is the candle body for the red dates
p.rect(x='seq', y='mid', width=w, height='height', fill_color="red", line_color="red", source=sourceInc)
#this is the candle body for the green dates
p.rect(
x='seq', y='mid', width=w, height='height', fill_color="green", line_color="green", source=sourceDec)

show(p)

This is how the chart looks like:

If using date as axis, you will get this:

Notice how annoying the gaps are!