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 1:

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