Skip to content

Visualization

To plot straight from a pipeline result, the one-liner plot_result (or result.plot) names a concept and wires up the prepare function and context for you; available_concepts(result) lists what a given result can render:

from ob_analytics.visualization import plot_result

fig = plot_result(result, "depth_heatmap")          # level defaults to L2
fig = result.plot("trade_tape", "L3", backend="plotly")

For full control, the unified plot() dispatcher renders a concept at a resolution level on a chosen backend from already-prepared data. Prepare the payload with the matching helper in the public prepare namespace (friendly wrappers over the internal prepare-data functions) and spread it as keyword arguments:

from ob_analytics.visualization import plot, prepare

fig = plot("trade_tape", level="L2", **prepare.trades(trades))

backend="matplotlib" (default) returns a Matplotlib figure; backend="plotly" returns an interactive Plotly figure (requires pip install ob-analytics[interactive]); backend="bokeh" returns a Bokeh figure (requires pip install ob-analytics[bokeh]), covering the core concepts — trade_tape, depth_heatmap, book_snapshot, depth_chart — for Bokeh / Panel server dashboards and streaming views. Renderers never call plt.show().

Every concept declares a resolution level — Level.L2 (Market-By-Price aggregate) or Level.L3 (Market-By-Order, per order). A concept registered at a single level resolves it automatically, so you pass only the concept name; comparable concepts (both L2 and L3 registered) take an explicit level=.

Concepts with both L2 and L3 faces: trade_tape, order_activity, cancellations, book_snapshot, depth_chart, liquidity_at_touch. L2-only: time_series, depth_heatmap, volume_percentiles, events_histogram, hidden_executions, price_view, trade_size. L3-only: order_outcome, queue_position. Level-less analytics: vpin, order_flow_imbalance, kyle_lambda, ofi_horizon, trading_halts.

Dispatcher

plot

plot(
    concept: str,
    level: Level | None = _UNSET,
    *,
    backend: str = "matplotlib",
    ax: Axes | None = None,
    **data: Any,
) -> Any

Render concept at level on backend from already-prepared data.

Prepare data with the matching prepare_<concept>_data in :mod:ob_analytics.visualization._data (or a gallery helper) and spread it as keyword arguments::

from ob_analytics.visualization import plot
from ob_analytics.visualization import _data
fig = plot("trade_tape", backend="matplotlib",
           **_data.prepare_trades_data(trades))

Parameters:

Name Type Description Default
concept str

Plot concept, e.g. "trade_tape" or "order_activity".

required
level Level

Resolution level (Level.L2/Level.L3). Omit to auto-resolve when the concept is registered at a single level; required for a comparable concept registered at both.

_UNSET
backend str

Registered backend name (default "matplotlib").

'matplotlib'
ax Axes

Axes to draw on (matplotlib only; ignored by other backends).

None
**data Any

Prepared plot data, as returned by the matching prepare_* helper. May include theme=PlotTheme(...) to override :data:DEFAULT_THEME for this call, on any backend.

{}

Returns:

Type Description
Figure or Figure or figure

Raises:

Type Description
ValueError

If backend is not registered, or concept is comparable and no level was given.

KeyError

If concept (at the resolved level) is not registered.

Theme and Saving

Pass theme=PlotTheme(...) to plot() to override DEFAULT_THEME for a single call, on any backend; there is no global theme to set.

PlotTheme dataclass

PlotTheme(
    style: str = "white",
    context: str = "notebook",
    font_scale: float = 1.05,
    palette: Palette = DEFAULT_PALETTE,
    rc: dict[str, object] = (
        lambda: {
            "axes.grid": True,
            "grid.linestyle": ":",
            "grid.alpha": 0.35,
            "axes.spines.top": False,
            "axes.spines.right": False,
            "axes.edgecolor": DEFAULT_PALETTE.rule,
            "axes.titlelocation": "left",
            "axes.titleweight": "bold",
            "lines.linewidth": 2.0,
        }
    )(),
    plotly_layout: dict[str, object] = (
        lambda: {
            "title": {
                "x": 0.0,
                "xanchor": "left",
                "xref": "paper",
                "font": {"weight": "bold"},
            }
        }
    )(),
    bokeh_figure: dict[str, object] = dict(),
)

Configurable visual theme for ob-analytics plots, in every backend.

Attributes:

Name Type Description
style str

Seaborn style name: "white", "whitegrid", "ticks", "dark", or "darkgrid". In plotly and bokeh the two dark styles draw on seaborn's gray background and "ticks" draws outside tick marks.

context str

Seaborn context name: "paper", "notebook", "talk", or "poster". Scales text in every backend.

font_scale float

Font scaling factor, applied on top of context in every backend.

palette Palette

The colours every backend draws with.

rc dict[str, object]

Matplotlib rc overrides applied on top of the seaborn theme (matplotlib only).

plotly_layout dict[str, object]

Plotly layout properties applied on top of the theme's template (plotly only), e.g. {"font": {"family": "Georgia"}}.

bokeh_figure dict[str, object]

Keyword arguments passed to :func:bokeh.plotting.figure on top of the theme's defaults (bokeh only), e.g. {"width": 1200}.

dark property

dark: bool

Whether the style draws on a dark (gray) background.

text_scale property

text_scale: float

Text size relative to the default theme (context x font_scale).

The plotly and bokeh backends multiply their base font sizes by this, so the default theme keeps their base sizes exactly.

DEFAULT_THEME module-attribute

DEFAULT_THEME: PlotTheme = PlotTheme()

Palette dataclass

Palette(
    bid: str = "#0072B2",
    ask: str = "#D55E00",
    buy: str = "#009E73",
    sell: str = "#D55E00",
    flashed: str = "#E69F00",
    resting: str = "#009E73",
    filled: str = "#009E73",
    partial: str = "#CC79A7",
    cancelled: str = "#E69F00",
    hidden_trade: str = "#F0E442",
    check_trade: str = "#999999",
    price_line: str = "#222222",
    reference_line: str = "#888888",
    rule: str = "#444444",
    label: str = "#555555",
    spread_fill: str = "#9aa0a6",
    neutral: str = "#7f8c8d",
    series: str = "#5dade2",
    emphasis: str = "#e74c3c",
    threshold: str = "#f39c12",
    secondary_axis: str = "#f1c40f",
    imbalance: str = "#6d28d9",
    effective_spread: str = "#0072B2",
    realized_spread: str = "#CC79A7",
)

Named plot colours, shared by every rendering backend.

Every field is a hex colour string. Override single fields with :func:dataclasses.replace or by passing them to the constructor::

Palette(bid="#1f77b4", ask="#d62728")

Attributes:

Name Type Description
bid, ask str

Side of a resting order (Okabe–Ito blue / vermillion).

buy, sell str

Aggressor side of an execution: buyer-initiated (lifts the ask) and seller-initiated (hits the bid).

flashed, resting str

Order-activity fate: placed and pulled / rested or filled.

filled, partial, cancelled str

Order-outcome fate: fully executed / partly executed with the remainder removed / removed without any execution.

hidden_trade str

A confirmed hidden-order trade.

check_trade str

A trade flagged as hidden whose maker order was visible (a diff feed's stale depth summary), which needs checking rather than trusting.

price_line str

The main price line (trade price, microprice) and dark markers.

reference_line str

Secondary reference marks: the mid line, dotted guides, "created" markers, and "no data" messages.

rule str

Zero lines, the book's mid rule, and axis edges.

label str

Annotation text.

spread_fill str

The band between best bid and best ask.

neutral str

Points with no side (for example hidden executions of unknown direction).

series str

A single unclassified data series (time series, VPIN buckets, Kyle's-lambda scatter).

emphasis str

A summary drawn over a series: rolling average, fitted line, halt span.

threshold str

A threshold line.

secondary_axis str

A series on a secondary y-axis, and that axis's labels.

imbalance str

The order book imbalance line.

effective_spread, realized_spread str

The two transaction-cost series.

DEFAULT_PALETTE module-attribute

DEFAULT_PALETTE: Palette = Palette()

save_figure

save_figure(
    fig: Figure,
    path: str | Path,
    *,
    dpi: int = 150,
    **kwargs: object,
) -> None

Save a matplotlib fig to path with sensible defaults.

Parameters:

Name Type Description Default
fig Figure

The matplotlib figure to save.

required
path str or Path

Destination file path (e.g. "output/plot.png").

required
dpi int

Resolution in dots per inch (default 150).

150
**kwargs object

Additional keyword arguments forwarded to :meth:~matplotlib.figure.Figure.savefig. Pass bbox_inches="tight" explicitly to crop to artist extents — it is not the default because it forces a second full draw (roughly doubling save time on dense figures), and every renderer already applies tight_layout.

{}

infer_volume_scale module-attribute

infer_volume_scale = infer_volume_scale

Renderer registry

Backends self-register their renderers into RENDERERS, keyed by the coordinate (concept, level, backend) (where level is a Level or None for level-less analytics). Register a whole new backend module with register_plot_backend, or a single renderer directly with RENDERERS.register((concept, level, backend), fn).

register_plot_backend

register_plot_backend(name: str, module_path: str) -> None

Register a visualization backend module.

The module at module_path must call RENDERERS.register((concept, level, name), fn) for each plot it supports (typically at import time). It is imported lazily on the first :func:plot call that targets name.

Parameters:

Name Type Description Default
name str

Backend name used in plot(..., backend=name).

required
module_path str

Dotted import path, e.g. "my_package._bokeh_backend".

required

Examples:

>>> from ob_analytics.visualization import register_plot_backend
>>> register_plot_backend("bokeh", "my_pkg._bokeh")

RENDERERS module-attribute

RENDERERS: Registry[
    tuple[str, Level | None, str], RendererFn
] = Registry("renderer")