Stock Charts¶
In addition to Highcharts, Highsoft offers a great stock charting product called Highstock. To use Highstock, set the attribute stock
of an HighCharts instance to True
or use the HighStock class.
The program below serves a chart of stock price data.
Hint
You need to install pandas to run the program successfully
Stock Charts price data¶
Stock Charts price data live demo
import justpy as jp
import pandas as pd
import datetime
epoch = datetime.datetime(1970, 1, 1)
def convert_date1(date_string):
date = datetime.datetime.strptime(date_string, "%Y-%m-%d")
return (date - epoch).total_seconds() * 1000
def stock_test1(request):
wp = jp.WebPage()
ticker = request.query_params.get("ticker", "msft")
if ticker not in ["aapl", "ibm", "intc", "msft"]:
ticker = "msft"
data = pd.read_csv(f"https://elimintz.github.io/stocks/{ticker.upper()}.csv")
chart = jp.HighStock(a=wp, classes="m-1 p-2 border w-10/12")
o = chart.options
o.title.text = "Historical Stock Price"
o.legend = {"enabled": True, "align": "right", "layout": "proximate"}
o.rangeSelector.selected = 4 # Set default range to 1 year
x = list(data["Date"].map(convert_date1))
y = data["Adj Close"].to_list()
s = jp.Dict({"name": ticker.upper(), "data": jp.make_pairs_list(x, y)})
o.series = [s]
s.tooltip.valueDecimals = 2 # Price displayed by tooltip will have 2 decimal values
return wp
jp.justpy(stock_test1)
I used yahoo finance to download data in CSV format. The first few lines of the file look like this:
Date,Open,High,Low,Close,Adj Close,Volume
2005-01-03,4.627143,4.650714,4.471428,4.520714,3.945287,172998000
2005-01-04,4.556428,4.676429,4.497857,4.567143,3.985806,274202600
2005-01-05,4.604286,4.660714,4.575000,4.607143,4.020715,170108400
2005-01-06,4.619286,4.636428,4.523571,4.610714,4.023832,176388800
2005-01-07,4.642857,4.973571,4.625000,4.946429,4.316814,556862600
2005-01-10,4.987857,5.050000,4.848571,4.925714,4.298736,431327400
2005-01-11,4.875000,4.939286,4.581429,4.611429,4.024456,652906800
2005-01-12,4.675000,4.707143,4.521429,4.675714,4.080557,479925600
2005-01-13,5.265000,5.315714,4.980714,4.985714,4.351099,791179200
2005-01-14,5.017857,5.122857,4.942143,5.014286,4.376033,442685600
2005-01-18,4.989286,5.050000,4.839286,5.046429,4.404085,251615000
2005-01-19,5.035000,5.104286,4.982143,4.991428,4.356086,187973800
2005-01-20,4.975000,5.090714,4.962143,5.032857,4.392241,228730600
The program uses pandas to read a CSV file corresponding to the ticker parameter (only the tickers MSFT, AAPL, IBM and INTC have data behind them, the rest default to MSFT). Try http://127.0.0.1:8000/?ticker=intc for example.
The program needs to convert the dates to support the Highcharts (standard JavaScript) format which is number of milliseconds since the Epoch (1/1/1970). The short function convert_date1 does this using the Python datetime library. We use map
to apply convert_date1
to all values in the 'Date' column in order to generate the list of x values for the series.
Stock Chart with Volume¶
Stock Chart with Volume live demo
The CSV file contains additional data, not just the end of day price. We will now create a more sophisticated chart that uses this data.
import justpy as jp
import pandas as pd
import datetime
epoch = datetime.datetime(1970, 1, 1)
grouping_units = [["week", [1]], ["month", [1, 2, 3, 4, 6]]]
chart_dict = {
"rangeSelector": {"selected": 1},
"yAxis": [
{
"labels": {"align": "right", "x": -3},
"title": {"text": "OHLC"},
"height": "60%",
"lineWidth": 2,
"resize": {"enabled": True},
},
{
"labels": {"align": "right", "x": -3},
"title": {"text": "Volume"},
"top": "65%",
"height": "35%",
"offset": 0,
"lineWidth": 2,
},
],
"tooltip": {"split": True},
"series": [
{
"type": "candlestick",
"tooltip": {"valueDecimals": 2},
"dataGrouping": {"units": grouping_units},
},
{
"type": "column",
"name": "Volume",
"yAxis": 1,
"dataGrouping": {"units": grouping_units},
},
],
}
def convert_date2(date_string):
date = datetime.datetime.strptime(date_string, "%Y-%m-%d")
return (date - epoch).total_seconds() * 1000
async def stock_test2(request):
wp = jp.WebPage(highcharts_theme="grid")
ticker = request.query_params.get("ticker", "MSFT").upper()
if ticker not in ["AAPL", "IBM", "INTC", "MSFT"]:
ticker = "MSFT"
data = await jp.JustPy.loop.run_in_executor(
None, pd.read_csv, f"https://elimintz.github.io/stocks/{ticker}.csv"
)
chart = jp.HighStock(
a=wp,
classes="m-1 p-2 border w-10/12",
options=chart_dict,
style="height: 600px",
)
o = chart.options
o.title.text = f"{ticker} Historical Prices"
x = list(data["Date"].map(convert_date2))
o.series[0].data = list(
zip(x, data["Open"], data["High"], data["Low"], data["Close"])
)
o.series[0].name = ticker
o.series[1].data = list(zip(x, data["Volume"]))
return wp
jp.justpy(stock_test2)
The chart is defined in this case using a standard Python dictionary. When assigned to the chart options
attribute, it is automatically converted to a Dict in order to enable dot notation.
In this example, reading the remote CSV file is done in a non-blocking manner (if you are unfamiliar with asyncio
, please skip this paragraph). The loop that JustPy runs in can be found in JustPy.loop
. It is used to run pd.read_csv
in the default thread or process pool. In order to allow awaiting a coroutine, stock_test
is also defined as a coroutine using the async
keyword.
The chart has two series with two different Y axis. The first series is a candlestick series and shows the OHLC (open high low close) data succinctly, and the second series is a simple column series that shows the volume. The data list for each series is created by zipping together the appropriate columns of the pandas frame.
We also use the Highcharts theme 'grid' to give the chart a different look.