pythonbokehholoviewscandlestick-charthvplot

How to add data into hover in Hvplot (bokeh) candlestick without missing-date gaps


I have an simple example, run in jupyter notebook:

packages:

python 3.12
bokeh 3.3.2
hvplot 0.9.0
holoviews 1.18.1

import pandas as pd
import hvplot.pandas

data = pd.DataFrame({
        "Open": [100.00, 101.25, 102.75],
        "High": [104.10, 105.50, 110.00],
        "Low": [94.00, 97.10, 99.20],
        "Close": [101.15, 99.70, 109.50],
        "Volume": [10012, 5000, 18000],
    }, index=[pd.Timestamp("2022-08-01"), pd.Timestamp("2022-08-03"), pd.Timestamp("2022-08-04")])

df = pd.DataFrame(data)
# remove datetime gaps
df = df.reset_index(names="Date")
df['Idx'] = pd.RangeIndex(0, df.shape[0], 1)

# fix hover ------
from bokeh.models import HoverTool

hover = HoverTool(
    tooltips=[
        ('Date', '@Date{%Y-%m-%d}'),
        ('Open', '@Open{0.00}'),
        ('High', '@High{0.00}'),
        ('Low', '@Low{0.00}'),
        ('Close', '@Close{0.00}'),
        ('Volume', '@Volume{0}'),
    ],
    formatters={'@Date': 'datetime'},
    mode='vline'
)
# fix hover ------

ohlc_cols = ["Open", "High", "Low", "Close"]
ohlc = df.hvplot.ohlc(x='Idx', y=ohlc_cols, hover_cols=["Date", *ohlc_cols, "Volume"], tools=[hover])

# fix x tick labels ------
import holoviews as hv
from bokeh.io import show

fig = hv.render(ohlc)
fig.xaxis.major_label_overrides = {
    i: dt.strftime("%b %d") for i, dt in enumerate(df['Date'])
}
# fix x tick labels ------

show(fig)

But the output is: enter image description here


Solution

  • The solution, or more a workaround, is to manupilate the ColumnDataSource of the first renderer in the bokeh figure.

    You can do so b adding the line below:

    fig.renderers[0].data_source.data['Volume'] = df['Volume'].values
    

    Explanation

    To understand why the line above is the solution, I want to explain, what df.hvplot.ohlc() does.

    This function creates a bokeh figure with 2 renderers, the first rederer is for all whiskers, which gets the HoverTool, and the seconde renderer draws the boxes. The DataFrame is converted to a ColumnsDataSource in the inside. The ColumnsDataSources don't have any information about the "Volume", because only used columns used by the whiskers and boxes are stored inside the ColumnDataSource. The HoverTool can't find information for the Volumne, so it prints ???. But we can extend the source by this information.

    Minimal Example

    I adapted your example, because I think it is simpler, but the downside is, that there is one empty date.

    import pandas as pd
    import hvplot.pandas
    import holoviews as hv
    from bokeh.io import show
    from bokeh.models import HoverTool
    
    df= pd.DataFrame({
            "Open": [100.00, 101.25, 102.75],
            "High": [104.10, 105.50, 110.00],
            "Low": [94.00, 97.10, 99.20],
            "Close": [101.15, 99.70, 109.50],
            "Volume": [10012, 5000, 18000],
        },
        index=[pd.Timestamp("2022-08-01"), pd.Timestamp("2022-08-03"), pd.Timestamp("2022-08-04")]
    )
    df.index.name = "Date"
    
    fig = hv.render(ohlc)
    # add missing values to the source for the HoverTool
    fig.renderers[0].data_source.data['Volume'] = df['Volume'].values
    show(fig)
    

    Output

    ohlc plot with extra data for HoverTool