Skip to content

Gallery

HTML gallery generation from pipeline results, plus the one-line plot_result entry point for rendering a single concept straight from a PipelineResult.

The gallery is assembled from a GalleryModel of PlotConcept entries built by build_gallery_model; generate_gallery renders it to a self-contained HTML page.

One-line plotting

plot_result

plot_result(
    result: PipelineResult,
    concept: str,
    level: Level | str | None = _UNSET,
    *,
    backend: str = "matplotlib",
    ax: Axes | None = None,
    volume_scale: float | None = None,
    theme: PlotTheme | None = None,
    **overrides: Any,
) -> Any

Render one plot concept straight from a :class:PipelineResult.

The one-liner the gallery is built on, without the gallery: it derives the same per-face context (spread, focus window, snapshot, volume scale) via :func:build_gallery_model -- the single source of truth for how each concept binds to its prepare function -- then prepares and renders the face. So plot_result(result, "depth_heatmap") replaces having to know the private prepare_price_levels_data name and its argument shape.

Parameters:

Name Type Description Default
result PipelineResult

Pipeline output (events / trades / depth / depth_summary).

required
concept str

Concept key, e.g. "trade_tape" (see :data:available_concepts). The name of a registered metric works too; a metric is level-less, so level does not apply to it. A metric measures result as it is, not converted with :func:display_result, and draws the table result.metric(concept) returns.

required
level Level or str

"L2" / "L3" (or a :class:Level). Leave it out when result has the concept at one level; it is required when result has the concept at both L2 and L3 (an L3 run has most concepts at both).

_UNSET
backend str

"matplotlib" (default), "plotly", or "bokeh".

'matplotlib'
ax Axes

Axes to draw on, as in :func:~ob_analytics.visualization.plot (Matplotlib only).

None
volume_scale float or None

Display volume scale; None auto-infers (as the gallery does).

None
theme PlotTheme or None

Theme for this call, on any backend; None uses :data:~ob_analytics.visualization.DEFAULT_THEME.

None
**overrides Any

Extra keyword arguments merged over the prepare call (e.g. col_bias=0.1 for the depth heatmap). For a metric, each goes to its compute when that names it and to its prepare otherwise, e.g. bucket_volume=5.0, threshold=0.8 for "vpin".

{}

Returns:

Type Description
A Matplotlib, Plotly or Bokeh figure, per *backend*.

Raises:

Type Description
UnknownNameError

If concept is not built for this result, has no variant at level, is not drawn by backend, or backend is not registered. The message names what is available: for a missing backend, the backends that draw the plot.

ConfigError

If level was left out and result has concept at both L2 and L3, or ax is given for a backend other than Matplotlib.

available_concepts

available_concepts(
    result: PipelineResult,
) -> dict[str, list[str]]

Concept keys built for result, mapped to their available level names.

A discoverability companion to :func:plot_result: shows what plot_result(result, concept, level=...) can render for this dataset (which varies by format -- e.g. hidden_executions is LOBSTER-only).

A registered metric is listed too, with an empty level list: a metric is level-less, so plot_result(result, "amihud") takes no level=.

display_result

display_result(result: PipelineResult) -> PipelineResult

Return result with prices and sizes converted to display units.

Canonical prices are integer ticks (:mod:ob_analytics.schemas); the plots show the quote currency, so this converts each price column to the ticks * tick_size float the faces render, reading tick_size from result.config. :func:build_gallery_model calls it once so every face — the prepare helpers, order_book / queue_positions called inside them, the axes — works in display units; call it yourself before the low-level prepare.* builders when you plot straight from a result.

Sizes are converted the same way, from integer lots to the base-asset lots * lot_size float.

Idempotent and legacy-safe: only integer columns are scaled, so a result whose prices or sizes are already floats (a pre-tick or pre-lot file, or a result already passed through here) is returned unchanged.

build_gallery_model(
    result: PipelineResult,
    *,
    volume_scale: float | None = None,
) -> GalleryModel

Build the default gallery model from pipeline results.

Each order-book concept comes from the concept table (:mod:ob_analytics.visualization._concepts), with a variant at each level whose data the run has: a price-level (L2) run has no per-order events, so it gets the L2 faces only. A concept with both an L2 and an L3 variant is :attr:~PlotConcept.comparable. The analytics are one panel per registered metric that applies to the run; callers may append more (built with the *_panel helpers) to :attr:GalleryModel.analytics.

Parameters:

Name Type Description Default
result PipelineResult

Pipeline output.

required
volume_scale float or None

Volume display scale. None (default) infers a power-of-10 scale via :func:infer_volume_scale, from the events' volumes, or on a price-level run from the depth's (the trades' when there is no depth).

None

Returns:

Type Description
GalleryModel

GalleryModel dataclass

GalleryModel(
    concepts: list[PlotConcept],
    analytics: list[PlotSpec] = list(),
)

The gallery's inventory: leveled concepts + level-less analytics.

PlotConcept dataclass

PlotConcept(
    key: str,
    title: str,
    variants: dict[Level, PlotSpec],
    note: str = "",
)

A level-neutral plot identity with up to one variant per :class:Level.

The key never carries a level; the level lives in the registry coordinate (concept, level, backend) and in the keys of variants.

comparable property

comparable: bool

True when both an L2 and an L3 face are registered.

Derived, never curated: the existence of both variants is the L2<->L3 pairing, so it can never drift out of sync with what is built.

at

at(level: Level) -> PlotSpec | None

Return the variant registered at level, or None.

PlotSpec dataclass

PlotSpec(
    name: str,
    title: str,
    plot_name: str,
    prepare: Callable[..., dict],
    prep_kwargs: dict[str, Any] = dict(),
)

How to prepare and label a single plot face.

A spec carries no level of its own -- the level is a coordinate on the owning :class:PlotConcept (for leveled concepts) or None (for the level-less analytics in :attr:GalleryModel.analytics).

Parameters:

Name Type Description Default
name str

Identifier / file stem for analytic panels (e.g. "vpin"). For a concept variant the file stem is derived from the concept + level, so name is only metadata there.

required
title str

Human-readable title (used directly for analytic cards; concept cards take their title from the concept).

required
plot_name str

Dispatcher concept key for :func:ob_analytics.visualization.plot.

required
prepare Callable

prepare_<name>_data helper returning the renderer payload.

required
prep_kwargs dict

Keyword arguments passed to prepare.

dict()
generate_gallery(
    result: PipelineResult | None,
    output_dir: str | Path,
    *,
    model: GalleryModel | None = None,
    view: str = "both",
    volume_scale: float | None = None,
    backends: list[str] | None = None,
    title: str = "ob-analytics Plot Gallery",
) -> Path

Render a gallery view and write a standalone HTML page.

Parameters:

Name Type Description Default
result PipelineResult or None

Pipeline output. May be None when model is supplied (the result is only consulted by :func:build_gallery_model).

required
output_dir str or Path

Root directory for gallery output.

required
model GalleryModel

Inventory to render. Defaults to :func:build_gallery_model.

None
view str

One of "l2", "l3", "both" (default), "comparison".

'both'
volume_scale float or None

Volume display scale, forwarded to :func:build_gallery_model when model is built here. Ignored when model is supplied.

None
backends list of str

Backends to render. Defaults to ["plotly", "matplotlib"] when plotly is installed (plotly is the primary column), else ["matplotlib"]. Pass "bokeh" explicitly to add a Bokeh column -- it is not auto-detected like plotly since it only covers the core concepts (trade_tape, depth_heatmap, book_snapshot, depth_chart); other concepts render as "Not available" in that column. In comparison view the backend axis collapses to a single backend (plotly if available) so the two columns carry L2 vs L3.

None
title str

Gallery page title.

'ob-analytics Plot Gallery'

Returns:

Type Description
Path

Path to the generated HTML gallery file.

Extra panels

The built-in metrics (VPIN, Kyle's λ, order flow imbalance, the OFI horizon graph and the L1 quote) already have cards, with their default settings. Build a panel yourself to add bars, transaction costs, LOBSTER trading halts, or a flow-toxicity face from a frame you computed with other settings, and append it to model.analytics before rendering:

from ob_analytics import Pipeline, compute_vpin, sample_csv_path
from ob_analytics.visualization.gallery import (
    build_gallery_model, generate_gallery, vpin_panel,
)

result = Pipeline().run(sample_csv_path())
vpin_df = compute_vpin(result.trades)

model = build_gallery_model(result)
model.analytics.append(vpin_panel(vpin_df))
generate_gallery(result, "out/gallery", model=model)

bars_panel

bars_panel(
    bars_df: DataFrame, *, label: str = ""
) -> PlotSpec

Build a bars analytic panel for :attr:GalleryModel.analytics.

bars_df is a table from :func:ob_analytics.bars.bars. label names the rule and threshold it was cut with and is shown in the chart title; the default reads them off the frame. Prices are drawn as they arrive, so cut the bars from a display-unit trades frame (see :func:display_result) when the rest of the gallery is in the quote currency.

vpin_panel

vpin_panel(
    vpin_df: DataFrame, *, threshold: float = 0.7
) -> PlotSpec

Build a VPIN analytic panel for :attr:GalleryModel.analytics.

ofi_panel

ofi_panel(
    ofi_df: DataFrame, trades: DataFrame | None = None
) -> PlotSpec

Build an order-flow-imbalance analytic panel.

ofi_horizon_panel

ofi_horizon_panel(
    trades: DataFrame,
    *,
    horizons: tuple[str, ...] = (
        "5s",
        "15s",
        "60s",
        "300s",
    ),
    grid: str = "5s",
    start_time: Timestamp | None = None,
    end_time: Timestamp | None = None,
) -> PlotSpec

Build an OFI-vs-horizon analytic panel (diverging band per horizon).

kyle_panel

kyle_panel(kyle_result: Any) -> PlotSpec

Build a Kyle's-Lambda analytic panel.

transaction_costs_panel

transaction_costs_panel(
    costs: DataFrame, *, window: str = "1min"
) -> PlotSpec

Build a transaction-cost panel: effective and realized spread, and impact.

costs is a :func:~ob_analytics.cost.transaction_costs frame; window is the span the per-trade costs are volume-weighted onto for the lines.

trading_halts_panel

trading_halts_panel(
    trades: DataFrame, halts: DataFrame
) -> PlotSpec

Build a trading-halts analytic panel.

LOBSTER halts are not part of the slim :class:PipelineResult; read them from :attr:~ob_analytics.lobster.LobsterLoader.trading_halts and pass them here.