Skip to content

L2 (price-level) depth

Price-level (L2 / market-by-price) components: the depth loader, trade reader, CSV writer, and DepthCsvSource. For the guide, see Process L2 feeds.

A price-level feed carries [price, quantity] levels and diffs with no order IDs, so the loader yields the depth frame directly and the pipeline skips the per-order stages (see Level).

L2DepthLoader

L2DepthLoader(
    config: PipelineConfig | None = None,
    *,
    settings: DepthCsvSettings | None = None,
    venue: str | None = None,
    symbol: str | None = None,
)

Load a price-level depth stream into the canonical depth frame.

Satisfies the :class:~ob_analytics.protocols.DepthSource protocol.

Parameters:

Name Type Description Default
config PipelineConfig

Pipeline configuration. tick_size quantises the price to integer ticks and lot_size the size to integer lots.

None
settings DepthCsvSettings

How the file encodes its timestamps and prices.

None
venue str

Optional instrument identity. When either is supplied, the loaded depth frame gains per-row venue / symbol columns. A generic price-level CSV carries no venue of its own, so venue is left NA unless supplied. Both None (the default) leaves the frame untagged.

None
symbol str

Optional instrument identity. When either is supplied, the loaded depth frame gains per-row venue / symbol columns. A generic price-level CSV carries no venue of its own, so venue is left NA unless supplied. Both None (the default) leaves the frame untagged.

None

load

load(source: str | Path) -> pd.DataFrame

Read source and return a canonical depth DataFrame.

Parameters:

Name Type Description Default
source str or Path

The L2 CSV file, or a directory containing depth.csv.

required

Returns:

Type Description
DataFrame

Columns timestamp, price, volume (absolute level size), direction (categorical bid/ask), sorted by timestamp — a :func:~ob_analytics.schemas.validate_depth_df frame. exchange_timestamp follows timestamp when the file has it, and origin (which part of a capture wrote the row) is kept when the file has it.

L2TradeReader

L2TradeReader(
    config: PipelineConfig | None = None,
    *,
    settings: DepthCsvSettings | None = None,
)

Read raw trade prints from a companion trades.csv.

Projects each print into the canonical trades schema. Price-level feeds have no order IDs, so maker / taker (and their event-id / og columns) are left unset. direction (the taker's aggressor side) is taken from a native side column when present; otherwise it is left unlabelled and the pipeline classifies it (Lee–Ready against the reconstructed touch — see :func:~ob_analytics.trade_sign.resolve_direction).

Trades are optional for an L2 run: a missing / empty trades.csv yields an empty trades frame (depth analytics still run).

Satisfies the :class:~ob_analytics.protocols.TradeSource protocol.

Parameters:

Name Type Description Default
config PipelineConfig

Pipeline configuration: the instrument's tick_size and lot_size.

None
settings DepthCsvSettings

How the file encodes its timestamps and prices, as for :class:L2DepthLoader.

None

DepthCsvWriter

DepthCsvWriter(
    config: PipelineConfig | None = None,
    *,
    settings: DepthCsvSettings | None = None,
)

Write a depth frame (and trades) back to the L2 CSV schema.

Round-trips :class:L2DepthLoader / :class:L2TradeReader: writes depth.csv (and a companion trades.csv when trades are supplied) into the dest directory.

Satisfies the :class:~ob_analytics.protocols.DataWriter protocol.

Parameters:

Name Type Description Default
config PipelineConfig

Pipeline configuration: the tick_size and lot_size the frames are in.

None
settings DepthCsvSettings

How to encode the timestamps and prices, as the loader reads them.

None

write

write(
    data: dict[str, DataFrame],
    dest: str | Path,
    **kwargs: Any,
) -> Path

Write data['depth'] (and optional data['trades']) to dest.

Parameters:

Name Type Description Default
data dict of str to DataFrame

Must contain a "depth" key (the canonical depth frame). An optional "trades" key triggers a companion trades.csv.

required
dest str or Path

Output directory.

required

Returns:

Type Description
Path

The written depth.csv path.

DepthCsvSource dataclass

DepthCsvSource(
    name: str = "depth_csv",
    level: Level = Level.L2,
    feed_type: FeedType = FeedType.PRICE_LEVELS,
    trade_attribution: TradeAttribution = TradeAttribution.NONE,
    settings: SourceSettings = DepthCsvSettings(),
    grid: InstrumentGrid = InstrumentGrid(),
)

The canonical L2 (price-level) CSV source — offline depth replay.

Declares :attr:~ob_analytics.protocols.Level.L2, so :class:~ob_analytics.pipeline.Pipeline takes the price-level path: depth in, per-order stages skipped. The shared entry point the aggregated venue connectors (Binance, Kalshi, Polymarket) narrow to domain modelling on top of, and the offline replay target for a live L2 capture (e.g. the depth.csv a :class:~ob_analytics.live.ccxt_source.CcxtSource writes).

Conforms structurally to :class:~ob_analytics.protocols.OfflineSource — no inheritance required.

DepthCsvSettings

DepthCsvSettings(**data: Any)

Bases: SourceSettings

How a price-level CSV encodes its timestamps and prices.

The settings of :class:DepthCsvSource, read by :class:L2DepthLoader, :class:L2TradeReader and :class:DepthCsvWriter. The defaults are what a capture writes: millisecond epochs and prices in the quote currency.

Attributes:

Name Type Description
timestamp_unit {'ms', 'us', 'ns'}

The unit of an integer epoch timestamp. A timestamp written as a string is parsed as it is, whatever this says.

price_divisor int

What a price in the file is divided by to reach the quote currency. 1 (the default) means it is in the quote currency already.

A capture's meta.json is read by read_record.