Python Bokeh CustomJS: how to make the chart auto-scale / auto-resize when zoom-in or zoom-out and panning?

Viewed 37

I'm plotting a candlestick chart for stock price analysis and I want to make the chart auto-scale / auto-resize when I zoom-in or zoom-out of the chart.

You can use any stock price data, below is the python code (which is working properly, main issue is in the CustomJS code):

new_bokeh_figure = partial(
    figure,
    x_axis_type='linear',
    height = 500,
    sizing_mode = "stretch_width",
    # title = f'{stock}',
    tools="pan,wheel_zoom,box_zoom,reset,save",
    active_drag='pan',
    active_scroll='wheel_zoom'
)

pad = (index[-1] - index[0]) / 20

fig_ohlc = new_bokeh_figure(
    x_range=Range1d(index[0], index[-1],
                    min_interval=10,
                    bounds=(index[0] - pad,
                            index[-1] + pad)) if index.size > 1 else None
                            )
ohlc_extreme_values = dfpl[['high', 'low']].copy(deep=False)
source.add(ohlc_extreme_values.min(1), 'ohlc_low')
source.add(ohlc_extreme_values.max(1), 'ohlc_high')

custom_js_args = dict(ohlc_range=fig_ohlc.y_range,
                      source=source)

if plot_volume:
    custom_js_args.update(volume_range=fig_volume.y_range)

Here is the CustomJS code I'm using:

fig_ohlc.x_range.js_on_change('end', CustomJS(args=custom_js_args,
                                code="""
    if (!window._bt_scale_range) {
        window._bt_scale_range = function (range, min, max, pad) {
            "use strict";
            if (min !== Infinity && max !== -Infinity) {
                pad = pad ? (max - min) * .05 : 0;
                range.start = min - pad;
                range.end = max + pad;
            } else console.error('backtesting: scale range error:', min, max, range);
        };
    }

    clearTimeout(window._bt_autoscale_timeout);

    window._bt_autoscale_timeout = setTimeout(function () {
        /**
         * @variable cb_obj `fig_ohlc.x_range`.
         * @variable source `ColumnDataSource`
         * @variable ohlc_range `fig_ohlc.y_range`.
         * @variable volume_range `fig_volume.y_range`.
         */
        "use strict";

        let i = Math.max(Math.floor(cb_obj.start), 0),
            j = Math.min(Math.ceil(cb_obj.end), source.data['ohlc_high'].length);

        let max = Math.max.apply(null, source.data['ohlc_high'].slice(i, j)),
            min = Math.min.apply(null, source.data['ohlc_low'].slice(i, j));
        _bt_scale_range(ohlc_range, min, max, true);

        if (volume_range) {
            max = Math.max.apply(null, source.data['Volume'].slice(i, j));
            _bt_scale_range(volume_range, 0, max * 1.03, false);
        }

    }, 50); 
"""))

I got it working half-way correct, however as you can see if you run the code, it's far from perfect. I've uploaded a video to show what's not working: video_url It does not auto-scale correctly if I zoom-in or zoom-out then pan the chart. How to make it auto-scale properly?

0 Answers
Related