Search code examples
pythonplotlyhistogramsubplot

plotly,python, plot histogram over other subplot?


The data is time series, and I imagine several subplots with some overlaying the others in attempt to consolidate my favorite technical indicators. In regards to the histogram, I would like to achieve something like this:

histogram overlay

How can I achieve this properly?

have tried:

  • googling: "plotly overlay histogram","plotly overlay barchart","plotly overlay multiple sublots"
  • adding specs with secondary_y=True when creating the subplots.
  • adding barmode='stack' in different places, which doesn't seem to work by itself.

fig = make_subplots(
    rows=3,
    cols=1,
    shared_xaxes=False,
    vertical_spacing=0,
    #subplot_titles=('candles','volume','atr')
    row_width=[.3,.5,1],
    specs=[
        [{'secondary_y':False}],
        [{'secondary_y':False}],
        [{'secondary_y':True}]
    ]

)

candle = go.Candlestick(
        x=df.date,
        open=df.open,
        high=df.high,
        low=df.low,
        close=df.close,
        name='Candles',
        
        increasing_line_color='#0ebc6e', 
        decreasing_line_color='#e8482c',
    
        #increasing_line_color='green',
        #decreasing_line_color='gray',
        line={'width': 1},
        hoverlabel={
            'font':{
                'color':'white',
                'family':'Open Sans',
                'size':15
            }
        },
)


vol_hist = go.Histogram(
    x=df.volume,
    y=df.close,
    orientation='h',
    name='vol hist',
    nbinsx=len(df.volume),
    nbinsy=len(df.close),
    hovertemplate=[],
    marker={
        'color':'steelblue'
    },
)

bb_hi = go.Scatter(
    x=df.date,
    y=df.bb_high,
    name='bollinger_high',
    line={'color':'orange','width':1},
    hovertemplate=[],
)

bb_low = go.Scatter(
    x=df.date,
    y=df.bb_low,
    name='bollinger_low',
    line={'color':'orange','width':1},
    hovertemplate=[],
)

vol = go.Bar(
    x=df.date,
    y=df.volume,
    name='Volume',
    marker_color='steelblue',
)

atr = go.Scatter(
    x=df.date,
    y=df.atr,
    name='ATR',
    line={'color':'steelblue','width':1}
)

fig.add_trace(candle,row=1,col=1)
fig.add_trace(vol_hist,row=1,col=1)
#fig.add_trace(bb_hi,row=1,col=1)
#fig.add_trace(bb_low,row=1,col=1)
fig.add_trace(atr,row=3,col=1)
fig.add_trace(vol,row=2,col=1)
#fig.add_trace(anime)


Solution

  • I first overlaid the histogram of the candlestick, volume, and closing price using two axes of the x-axis, since it cannot be done in layers. This could be achieved by referring to this example. Secondly, I added a bar chart of volume, then Bollinger Bands, and finally EMA. This is the result of outputting fig.data one step at a time, checking the contents, and making modifications. The remaining task is to display the x-axis of volume and EMA. This is as far as I can go.

    import plotly.graph_objects as go
    from plotly.subplots import make_subplots
    import pandas as pd
    import numpy as np
    import yfinance as yf
    
    df = yf.download("AAPL", start="2021-01-01", end="2021-12-01")
    df.reset_index(inplace=True)
    df.columns = ['date', 'open', 'high', 'low', 'close', 'adj close', 'volume']
    df['ma'] = df['close'].rolling(25).mean()
    df['sigma'] = df['close'].rolling(25).std()
    df['bb_high'] = df['ma'] + df['sigma'] * 2
    df['bb_low'] = df['ma'] - df['sigma'] * 2
    
    fig = make_subplots(rows=3, cols=1, 
                        vertical_spacing=0.025, 
                        row_heights=[0.6,0.20,0.20],
                        shared_xaxes=False,
                        specs=[[{"secondary_y": True}],[{"secondary_y": False}],[{"secondary_y": False}]])
    
    fig.add_trace(
        go.Histogram(
            x=df.volume,
            y=df.close,
            orientation='h',
            name='vol hist',
            nbinsx=len(df.volume),
            nbinsy=len(df.close),
            hovertemplate=[],
            opacity=0.4,
            marker={
                'color':'steelblue'
            },
    ), secondary_y=True)
    
    fig.add_trace(
        go.Candlestick(
            x=df.date,
            open=df.open,
            high=df.high,
            low=df.low,
            close=df.close,
            name='Candles',
            increasing_line_color='#0ebc6e', 
            decreasing_line_color='#e8482c',
            line={'width': 1},
            hoverlabel={
                'font':{
                    'color':'white',
                    'family':'Open Sans',
                    'size':15
                }
            },
    ), secondary_y=True)
    
    fig.add_trace(
        go.Scatter(
            x=df.date, 
            y=df.bb_high,
            name='bollinger_high',
            line={'color':'orange','width':1},
            hovertemplate=[],
    ), secondary_y=False)
    
    fig.add_trace(
        go.Scatter(
            x=df.date,
            y=df.bb_low,
            name='bollinger_low',
            line={'color':'orange','width':1},
            hovertemplate=[],
    ), secondary_y=False)
    
    fig.add_trace(
        go.Bar(
            x=df.date,
            y=df.volume,
            name='Volume',
            marker_color='steelblue',
    ), secondary_y=False, row=2, col=1)
    
    fig.add_trace(
        go.Scatter(
            x=df.date,
            y=df.close.ewm(26).mean(),
            name='EMA',
            line={'color':'steelblue','width':2}
    ), secondary_y=False, row=3, col=1)
    
    
    fig.update_layout(xaxis2={'anchor': 'y', 'overlaying': 'x', 'side': 'top', 'autorange':'reversed'})
    fig.data[0].update(xaxis='x2')
    fig.update_layout(yaxis={'tickvals': np.arange(120,170,10)})
    fig.data[4].update(xaxis='x')
    fig.data[5].update(xaxis='x')
    fig.update_xaxes(rangeslider_visible=False, showticklabels=True, row=1, col=1)
    fig.update_layout(autosize=False, width=1000, height=800)
    fig.show()
    

    enter image description here