import plotly.graph_objects as go
def plot_lob(bid_prices, bid_vols, ask_prices, ask_vols, title="", annotations=None, height=350):
fig = go.Figure()
# Bids (blue, bars going up)
fig.add_trace(go.Bar(
x=[f"${p/100:.2f}" for p in bid_prices],
y=bid_vols,
marker_color="rgba(55, 128, 191, 0.8)",
marker_line=dict(color="rgba(55, 128, 191, 1)", width=1.5),
text=[f"q<sub>{-len(bid_prices)+i}</sub> = {v}" for i, v in enumerate(bid_vols)],
textposition="outside",
textfont=dict(size=11),
name="Bid",
hovertemplate="Price: %{x}<br>Volume: %{y}<extra>Bid</extra>",
))
# Asks (red, bars going down — plotted as negative)
fig.add_trace(go.Bar(
x=[f"${p/100:.2f}" for p in ask_prices],
y=[-v for v in ask_vols],
marker_color="rgba(219, 64, 82, 0.8)",
marker_line=dict(color="rgba(219, 64, 82, 1)", width=1.5),
text=[f"q<sub>{i+1}</sub> = {v}" for i, v in enumerate(ask_vols)],
textposition="outside",
textfont=dict(size=11),
name="Ask",
hovertemplate="Price: %{x}<br>Volume: %{customdata}<extra>Ask</extra>",
customdata=ask_vols,
))
max_vol = max(max(bid_vols), max(ask_vols))
fig.update_layout(
title=dict(text=title, font=dict(size=14)),
xaxis=dict(title="Price", categoryorder="array",
categoryarray=[f"${p/100:.2f}" for p in sorted(bid_prices + ask_prices)]),
yaxis=dict(title="Volume", range=[-max_vol*1.4, max_vol*1.4], zeroline=True,
zerolinewidth=2, zerolinecolor="#2c3e50"),
template="plotly_white",
height=height,
margin=dict(t=50, b=50, l=60, r=30),
showlegend=True,
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
font=dict(family="Inter, -apple-system, sans-serif"),
bargap=0.15,
)
if annotations:
fig.update_layout(annotations=annotations)
return fig
# Initial book state
fig = plot_lob(
bid_prices=[2997, 2998, 2999, 3000],
bid_vols=[6, 0, 9, 5],
ask_prices=[3001, 3002, 3003, 3004],
ask_vols=[7, 0, 4, 3],
title="Limit Order Book — queues indexed from best",
annotations=[
dict(x=3.5, y=0, text="<b>spread n=1</b>", showarrow=True,
ax=0, ay=-40, font=dict(size=11, color="#2c3e50")),
],
)
fig.show()