Introduction

In today’s post we want to go through the creation of a chart using Python’s bokeh-library following an amazing tutorial by Ardit Sulce who is maintaining the website PythonHow.com and also the creator of an amazing class on udemy. Along the way we will have a look at how to efficiently retrieve stock-market data directly from within python.

Generating Plot Locally

To download stock-market data directly into our python-script we use pandas_datareader (which used to be a module on the pandas library). To be able to use it we have to install it (either globally or within our virtual environment) with pip install pandas_datareader. In particular we want to make use of the class data from pandas_datareader which comes along with the method DataReader that ultimately allows us to download stock-market data.
Executing data.DataReader? gives us an idea from the various sources from which we can download stock market data. We fetch the data by executing the following command (note that we also imported the datetime-library and store the start- and end-date for which we want to retrieve stock-market data in distinct variables because the data.DataReader - object expects a datetime-object for start- and end-date).

from pandas_datareader import data
import datetime

start = datetime.datetime(2015,11,1)
end = datetime.datetime(2016, 3, 10)
df = data.DataReader(name = "GOOG", data_source = "google", start=start, end=end)

If we execute type(data.DataReader(name = "GOOG", data_source = "google", start=start, end=end)) we see that the object that is instantiated is a pandas - dataframe. If we further print df to the screen we can inspect the header of our data:

Open High Low Close Volume
Date
2015-11-02 711.06 721.62 705.85 721.11 1871073
2015-11-03 718.86 724.65 714.72 722.16 1560770
2015-11-04 722.00 733.10 721.90 728.11 1704575
2015-11-05 729.47 739.48 729.47 731.25 1860367
2015-11-06 731.50 735.41 727.01 733.76 1509656


The candlestick chart now is typically a way to represent the price movements of a security/derivative/currency throughout the day whereby each bar represents four important prices of a certain day: open, close, high and low (note that their appearance reminds of box-plots while the two chart-types are unrelated). All the mentioned prices are already available in our dataframe.

In the following we want to re-build ‘candlesticks’ using Bokeh quadrants. For this we first need to import the required module from bokeh (simply add them to the other imported modules at the top of your script):

from bokeh.plotting import figure, show, output_file

Next, we instantiate an empty figure:

p = figure(x_axis_type = 'datetime', width=1000, height = 300, responsive = True)
p.title.text = "Candlestick Chart"

Note that we have added the responsive - parameter to the figure-object above so that the chart adjusts with the window-size.

The next question would be of how to draw the quadrants. For this we will make use of the rect method of our instantiated figure which we have called p here. Further we need to build two sets of rectangles (for days where the opening price was higher than the closing price vs. days where the closing price was higher than the opening price).

To be able to build the rectangles we need to pass four mandatory parameters:

  • x-coordinate of the central point
  • y-coordinate of the central point
  • width
  • height.

To make the entire code and the generation of the two rectangles slightly more elegant, we add the following intermediate function and intermediate results:

def inc_dec(c, o):
    if c > o:
        value = "Increase"
    elif c < o:
        value = "Decrease"
    else:
        value = "Equal"
    return value

The above function returns “Increase”, “Decrease” or “Equal” depending on which of the two input-parameters is higher. This furter allows us to add a Status - column to our data-frame as making use of exactly this function as follows:

df["Status"] = [inc_dec(c, o) for c, o in zip(df.Close, df.Open)]
df["Middle"] = (df.Open + df.Close)/2
df["Height"] = abs(df.Close - df.Open)

In addition, in the above we create two additional columns Middle and Height which we’ll then simply use in the generation of the rectangles which can then be created as follows:

# we use fill_color light-blue for days where the price increased;
# only look at days where close is greater than opening
p.rect(df.index[df.Status=="Increase"], df.Middle[df.Status=="Increase"], hours_12, df.Height[df.Status=="Increase"],
       fill_color="#CCFFFF", line_color="black")
# we use fill_color red for days where the price decreased
# only looking at days where opening is greater than close
p.rect(df.index[df.Status=="Decrease"], df.Middle[df.Status=="Decrease"], hours_12, df.Height[df.Status=="Decrease"],
       fill_color="#FF3333", line_color="black")

Looking at our dfwe can see the three additional columns:

Open High Low Close Volume Status Middle Height
Date
2015-11-02 711.06 721.62 705.85 721.11 1871073 Increase 716.085 10.05
2015-11-03 718.86 724.65 714.72 722.16 1560770 Increase 720.510 3.30
2015-11-04 722.00 733.10 721.90 728.11 1704575 Increase 725.055 6.11
2015-11-05 729.47 739.48 729.47 731.25 1860367 Increase 730.360 1.78
2015-11-06 731.50 735.41 727.01 733.76 1509656 Increase 732.630 2.26


If we would want to have a look at the plot so far locally we can execute the below:

output_file("Candlestick.html")
show(p)

There are two additional enhancements that we want to add:

  • We modify the grid-lines and
  • add the segments to finish the candlestick-charts.
p.grid.grid_line_alpha=0.3

p.segment(df.index, df.High, df.index, df.Low, color="Black")

The entire code then looks as follows:

from pandas_datareader import data
from bokeh.plotting import figure, show, output_file
import datetime
from bokeh.embed import components
from bokeh.resources import CDN

start = datetime.datetime(2015,11,1)
end = datetime.datetime(2016, 3, 10)
df = data.DataReader(name = "GOOG", data_source = "google", start=start, end=end)

# date_increase = df.index[df.Close > df.Open]
# date_decrease = df.index[df.Close < df.Open]

def inc_dec(c, o):
    if c > o:
        value = "Increase"
    elif c < o:
        value = "Decrease"
    else:
        value = "Equal"
    return value

df["Status"] = [inc_dec(c, o) for c, o in zip(df.Close, df.Open)]
df["Middle"] = (df.Open + df.Close)/2
df["Height"] = abs(df.Close - df.Open)

p = figure(x_axis_type = 'datetime', width=1000, height = 300, responsive = True)
p.title.text = "Candlestick Chart"
p.grid.grid_line_alpha=0.3

# we create a variable that stores the value for 12 hours in milliseconds
hours_12 = 12*60*60*1000

p.segment(df.index, df.High, df.index, df.Low, color="Black")
# we use fill_color light-blue for days where the price increased;
# only look at days where close is greater than opening
p.rect(df.index[df.Status=="Increase"], df.Middle[df.Status=="Increase"], hours_12, df.Height[df.Status=="Increase"],
       fill_color="#CCFFFF", line_color="black")
# we use fill_color red for days where the price decreased
# only looking at days where opening is greater than close
p.rect(df.index[df.Status=="Decrease"], df.Middle[df.Status=="Decrease"], hours_12, df.Height[df.Status=="Decrease"],
       fill_color="#FF3333", line_color="black")



# if we want to output the file locally:
# output_file("Candlestick.html")
# show(p)

Embed Chart in Webpage

The last thing we want to do is to embed the chart into our webpage (as you can see below) and now only have a look at it locally.

If we would inspect (in Chrome click Cmd+Option+C on a Mac) the generated “Candlestick.html” output from above, we could see the entire source-code that generates the graph. More specifically, we would see the html-code that is rendering the chart-area as well as the javascript-code in the <body> of our page that holds the data that is being rendered in the chart. In addition, in the <head> - tag we see a link to a particular bokeh-css-file as well as a corresponding bokeh javascript-file which constructs all the elements of the chart (as shown below):

<link rel="stylesheet" href="https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.css" type="text/css" />

<script type="text/javascript" src="https://cdn.pydata.org/bokeh/release/bokeh-0.12.5.min.js"></script>

This means that we need to grab all the above mentioned elements out of the generated chart.

To grab those elements we can use a function called components which is part of the embed - interface of bokeh. To be able to use this, we have to import the respective module in the header of our script:

from bokeh.embed import components

Then we can pass the figure-object p into components and store the result (the source-code which is a tuple including the javascript- and html-code):

script1, div1 = components(p)

script1 contains the entire javascript-code, div1 contains the html-structure and tags (which are both nothing other than string-objects).

To retrieve the links to the css-link and js-script in the header, we can generate those two links using a final module called CDN(CDN stands for ‘Content Delivery Network’). We have to import CDN

from bokeh.resources import CDN

and can then create two variables:

cdn_js = CDN.js_files
cdn_css = CDN.css_files

Inspecting cdn_js reveals that we have a list of three links:

  • basic bokeh javascript,
  • bokeh widgets (which we will not make use of), and
  • bokeh css.

All those elements then allow us to embed our plot in a dedicated webpage as follows:

  • assuming that we have built our web-app with python’s flask-library, we would have to add a dedicated route where we would execute the entire code that generates the plot from above:
@app.route('/plot/')
def plot():
    from pandas_datareader import data
    from bokeh.plotting import figure, show, output_file
    import datetime
    from bokeh.embed import components

    # rest of code placed here!
    [...]

    script1, div1 = components(p)
    cdn_js = CDN.js_files[0]
    cdn_css = CDN.css_files[0]
    return render_template("plot.html",
            script1 = script1, div1=div1,
            cdn_css = cdn_css, cdn_js=cdn_js)

Being executed in the back-end of our flask-app, the generated variables from our python-script can then be handed over to the following template called, e.g., plot.html (note that we assume that you have a webpage with a layout-template and the plot.html just inherits from the layout.html as specified below):

{%extends "layout.html"%}
{%block content%}
<link rel="stylesheet" href={{cdn_css | safe}} type="text/css" />
<script type="text/javascript" src={{cdn_js | safe}}></script>
<div class="plot">
    <h1>A Plot </h1>
    <p>...produced with Python's Bokeh - library.</p>
</div>
{{script1 | safe}}
{{div1| safe }}
{%endblock%}

The final plot is embedded below:

Bokeh Plot