Python: Plot candlesticks with automatic Y zoom

陌路散爱 提交于 2019-12-04 14:43:58

I struggled with getting the plotly candlestick chart to work the way I wanted to work so I developed a code example to share.

This does not depend on any additional javascript or other libraries except Dash, Plotly, and a date library called pytz. Example code provides automatic updates to the yaxis range when and x range slider is updated. Y axis is set to show only price range of bars that are in the chart with a two tick padding.

This worked well for me and I can potentially add any number of trace overlays that I want on top of the chart. Plus, I can run it locally without having to use plotly services. See gitub repo here:

https://github.com/gersteing/DashCandlestickCharting/blob/master/README.md

For whom ever wants to try it out. Runs locally using Flask. Enjoy!

The best solution I have found is to use Bokeh. There is a related question here - Bokeh, zoom only on single axis, adjust another axis accordingly. One answer links a gist which gives an example of setting up candlesticks with automatic Y zoom.

Unfortunately this is far from an 'out of the box' solution as it involves coding a custom JavaScript callback. However, the solution is still fairly simple. I couldn't get the code on the gist to run, mainly due to issues with the Pandas DataReader. Here is an updated version of the code which uses the example data Bokeh provides (as per the Bokeh Candlesicks example), adds more similarities with TradingView and irons out a few other issues I found:

import pandas as pd

from bokeh.io import output_file, show
from bokeh.plotting import figure
from bokeh.models import CustomJS, ColumnDataSource
from bokeh.sampledata.stocks import MSFT


def candlestick_plot(df):
    fig = figure(sizing_mode='stretch_both',
                 tools="xpan,xwheel_zoom,undo,redo,reset,crosshair,save",
                 active_drag='xpan',
                 active_scroll='xwheel_zoom',
                 x_axis_type='datetime')

    inc = df.close > df.open
    dec = ~inc

    fig.segment(df.date[inc], df.high[inc], df.date[inc], df.low[inc], color="green")
    fig.segment(df.date[dec], df.high[dec], df.date[dec], df.low[dec], color="red")
    width_ms = 12*60*60*1000 # half day in ms
    fig.vbar(df.date[inc], width_ms, df.open[inc], df.close[inc], color="green")
    fig.vbar(df.date[dec], width_ms, df.open[dec], df.close[dec], color="red")

    source = ColumnDataSource({'date': df.date, 'high': df.high, 'low': df.low})
    callback = CustomJS(args={'y_range': fig.y_range, 'source': source}, code='''
        clearTimeout(window._autoscale_timeout);

        var date = source.data.date,
            low = source.data.low,
            high = source.data.high,
            start = cb_obj.start,
            end = cb_obj.end,
            min = Infinity,
            max = -Infinity;

        for (var i=0; i < date.length; ++i) {
            if (start <= date[i] && date[i] <= end) {
                max = Math.max(high[i], max);
                min = Math.min(low[i], min);
            }
        }
        var pad = (max - min) * .05;

        window._autoscale_timeout = setTimeout(function() {
            y_range.start = min - pad;
            y_range.end = max + pad;
        });
    ''')

    fig.x_range.callback = callback
    show(fig)

df = pd.DataFrame(MSFT)
df["date"] = pd.to_datetime(df["date"])
output_file("candlestick.html")
candlestick_plot(df)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!