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.


You need to install pandas to run the program

import justpy as jp
import pandas as pd
import datetime

epoch = datetime.datetime(1970, 1, 1)

def convert_date(date_string):
    date = datetime.datetime.strptime(date_string, '%Y-%m-%d')
    return (date - epoch).total_seconds()*1000

def stock_test(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'{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_date))
    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


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

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 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_date does this using the Python datetime library. We use map to apply convert_date to all values in the 'Date' column in order to generate the list of x values for the series.

Stock Chart with Volume

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_date(date_string):
    date = datetime.datetime.strptime(date_string, '%Y-%m-%d')
    return (date - epoch).total_seconds()*1000

async def stock_test(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'{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_date))
    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


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.