of_core 0.2.0

Core domain models and analytics primitives for the Orderflow engine
Documentation
  • Coverage
  • 100%
    103 out of 103 items documented1 out of 26 items with examples
  • Size
  • Source code size: 37.8 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 5.91 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 34s Average build duration of successful builds.
  • all releases: 23s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • gregorian-09/orderflow
    3 1 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • gregorian-09

of_core

of_core defines the canonical data model and analytics primitives used across the Orderflow stack. It is provider-agnostic and intentionally lightweight so every binding (C, Python, Java) can rely on the same normalized semantics.

What This Crate Contains

  • Market identity: [SymbolId]
  • Event model: [TradePrint], [BookUpdate], [BookLevel], [BookSnapshot], [Side], [BookAction]
  • Quality flags: [DataQualityFlags]
  • Runtime outputs: [AnalyticsSnapshot], [DerivedAnalyticsSnapshot], [SessionCandleSnapshot], [IntervalCandleSnapshot], [SignalSnapshot], [SignalState]
  • Deterministic analytics engine: [AnalyticsAccumulator]

New In 0.2.0

Relative to the 0.1.x line, of_core now includes:

  • [BookSnapshot] as a first-class materialized depth model
  • [DerivedAnalyticsSnapshot] for additive totals such as vwap and trade_count
  • [SessionCandleSnapshot] for session-wide OHLC state
  • [IntervalCandleSnapshot] for rolling-window OHLC state

These are additive data-model extensions. They do not change the older AnalyticsSnapshot contract.

Public API Inventory

Public types:

  • [SymbolId]
  • [Side]
  • [BookAction]
  • [BookUpdate]
  • [BookLevel]
  • [BookSnapshot]
  • [TradePrint]
  • [AnalyticsSnapshot]
  • [DerivedAnalyticsSnapshot]
  • [SessionCandleSnapshot]
  • [IntervalCandleSnapshot]
  • [SignalState]
  • [SignalSnapshot]
  • [DataQualityFlags]
  • [AnalyticsAccumulator]

Public DataQualityFlags methods:

  • NONE
  • STALE_FEED
  • SEQUENCE_GAP
  • CLOCK_SKEW
  • DEPTH_TRUNCATED
  • OUT_OF_ORDER
  • ADAPTER_DEGRADED
  • [DataQualityFlags::bits]
  • [DataQualityFlags::from_bits_truncate]
  • [DataQualityFlags::intersects]

Public AnalyticsAccumulator methods:

  • [AnalyticsAccumulator::on_trade]
  • [AnalyticsAccumulator::reset_session_delta]
  • [AnalyticsAccumulator::reset_session]
  • [AnalyticsAccumulator::snapshot]
  • [AnalyticsAccumulator::derived_snapshot]
  • [AnalyticsAccumulator::session_candle_snapshot]
  • [AnalyticsAccumulator::interval_candle_snapshot]

Design Principles

  • Deterministic arithmetic: prices and sizes are integer values, avoiding float drift in replay/backtests.
  • Stable schema: types are designed for cross-language transport and long-lived storage.
  • Minimal dependencies: this crate stays small so it can be embedded broadly.

Type Semantics Reference

Market identity and direction

  • [SymbolId] is the canonical identity key used everywhere in the project. venue should be the normalized venue name and symbol should remain stable for the life of a stream.
  • [Side] uses Bid and Ask for both book updates and trade aggressor direction.
  • [BookAction] uses Upsert for insert-or-replace semantics and Delete for level removal.

Book event types

  • [BookUpdate] represents a single level mutation. level is the depth index from top of book. price and size are integer-normalized values. sequence, ts_exchange_ns, and ts_recv_ns preserve replay ordering and latency analysis.
  • [BookLevel] is the materialized view of one price level after runtime consolidation.
  • [BookSnapshot] is the full reconstructed book for one symbol at one point in time. bids and asks are level-ordered arrays. last_sequence is the sequence of the last applied update. ts_exchange_ns and ts_recv_ns are copied from that last applied update.

Trade event types

  • [TradePrint] represents one normalized trade. price and size are integer-normalized. aggressor_side is the trade direction used by the analytics engine. sequence may be zero when a source does not provide a venue sequence.

Analytics and signal output types

  • [AnalyticsSnapshot] is the base session analytics payload. It includes directional volume, delta, cumulative delta, POC, value area, and the active quality flags.
  • [DerivedAnalyticsSnapshot] adds additive session totals that were intentionally kept out of the original analytics payload so older consumers would not break.
  • [SessionCandleSnapshot] is a session-wide candle view derived from ingested trades.
  • [IntervalCandleSnapshot] is a rolling-window candle view computed on demand from recent trades for a caller-supplied window_ns.
  • [SignalState] is the stable directional state machine used across the runtime and bindings.
  • [SignalSnapshot] packages state, confidence, reason text, and quality flags for downstream consumers.

AnalyticsAccumulator Contract

[AnalyticsAccumulator] is session-oriented.

  • [AnalyticsAccumulator::on_trade] mutates all session analytics state from one trade.
  • [AnalyticsAccumulator::reset_session_delta] clears directional volume and delta state but keeps longer-lived session context that is not explicitly reset.
  • [AnalyticsAccumulator::reset_session] clears the full session state, including candle and derived totals.
  • [AnalyticsAccumulator::snapshot] returns the base analytics payload.
  • [AnalyticsAccumulator::derived_snapshot] returns additive totals such as vwap and average_trade_size.
  • [AnalyticsAccumulator::session_candle_snapshot] returns the session candle built from all trades seen since the last session reset.
  • [AnalyticsAccumulator::interval_candle_snapshot] computes a rolling candle over the recent trade window without mutating session state.

Important behavior:

  • No book data is required for AnalyticsAccumulator; it is trade-driven.
  • All price and size arithmetic uses integer math at ingest time and converts only where a derived floating result is needed, such as vwap.
  • point_of_control is volume-based, not quote-based.
  • Value area fields are derived from traded-volume distribution, not full order-book depth.

Quick Start

use of_core::{AnalyticsAccumulator, Side, SymbolId, TradePrint};

let symbol = SymbolId {
    venue: "CME".to_string(),
    symbol: "ESM6".to_string(),
};

let mut acc = AnalyticsAccumulator::default();
acc.on_trade(&TradePrint {
    symbol,
    price: 505_000,
    size: 10,
    aggressor_side: Side::Ask,
    sequence: 1,
    ts_exchange_ns: 1,
    ts_recv_ns: 2,
});

let snap = acc.snapshot();
assert_eq!(snap.buy_volume, 10);
assert_eq!(snap.delta, 10);

Quality Flags

[DataQualityFlags] is a bitset used to express data-health issues such as stale feed, sequence gaps, and out-of-order events. Signals and runtime gating can use these flags to block unsafe decisions.

use of_core::DataQualityFlags;

let q = DataQualityFlags::STALE_FEED | DataQualityFlags::SEQUENCE_GAP;
assert!(q.intersects(DataQualityFlags::STALE_FEED));
assert_eq!(q.bits() & DataQualityFlags::SEQUENCE_GAP.bits(), DataQualityFlags::SEQUENCE_GAP.bits());

Analytics Model Notes

  • delta tracks current session directional imbalance.
  • cumulative_delta retains directional accumulation over time.
  • point_of_control is computed as highest-volume price level.
  • value_area_low / value_area_high approximate the high-volume range around POC.
  • [DerivedAnalyticsSnapshot] adds session totals such as total_volume, trade_count, vwap, average_trade_size, and imbalance_bps without changing the original analytics payload.
  • [SessionCandleSnapshot] adds a candle-style session view with open, high, low, close, trade_count, and first/last exchange timestamps.
  • [IntervalCandleSnapshot] adds a parameterized rolling-window candle view with window_ns, open, high, low, close, trade_count, total_volume, vwap, and first/last exchange timestamps.

For full orchestration and adapter integration, see of_runtime.

Book Snapshot Model

[BookSnapshot] materializes the latest known order book for one symbol:

  • bids: bid-side levels ordered by level
  • asks: ask-side levels ordered by level
  • last_sequence: sequence number of the most recent applied book event
  • ts_exchange_ns / ts_recv_ns: timestamps from the most recent applied book event

This snapshot model is used by the runtime and exposed through the FFI and bindings.

Choosing the Right Snapshot Type

  • Use [AnalyticsSnapshot] when you want the original compact analytics contract.
  • Use [DerivedAnalyticsSnapshot] when you need totals such as trade_count or vwap.
  • Use [SessionCandleSnapshot] when you want one candle for the entire active session.
  • Use [IntervalCandleSnapshot] when you want a rolling lookback window.
  • Use [BookSnapshot] when you need reconstructed depth instead of raw incremental updates.

Real-World Use Cases

1. Offline research and replay analytics

Use [AnalyticsAccumulator] directly when you already have normalized trade data and want deterministic session analytics without the full runtime.

Typical use cases:

  • replaying one session from a CSV/JSONL converter
  • validating a strategy hypothesis before wiring live adapters
  • generating session summaries for dashboards or reports

2. Shared schema between components

Use of_core as the common contract when writing:

  • custom adapters that emit normalized [BookUpdate] and [TradePrint]
  • custom signal modules that consume [AnalyticsSnapshot]
  • persistence or replay tools that must stay aligned with runtime semantics

3. Strategy prototyping before runtime integration

For early-stage ideas, it is often faster to work only with [TradePrint], [AnalyticsAccumulator], and the output snapshot types before integrating with of_runtime.

Detailed Example: Build Session Analytics From Trades

use of_core::{AnalyticsAccumulator, Side, SymbolId, TradePrint};

fn main() {
    let symbol = SymbolId {
        venue: "CME".to_string(),
        symbol: "ESM6".to_string(),
    };

    let trades = vec![
        TradePrint {
            symbol: symbol.clone(),
            price: 505_000,
            size: 8,
            aggressor_side: Side::Ask,
            sequence: 1,
            ts_exchange_ns: 1_000,
            ts_recv_ns: 1_100,
        },
        TradePrint {
            symbol: symbol.clone(),
            price: 505_025,
            size: 4,
            aggressor_side: Side::Ask,
            sequence: 2,
            ts_exchange_ns: 2_000,
            ts_recv_ns: 2_100,
        },
        TradePrint {
            symbol,
            price: 505_000,
            size: 6,
            aggressor_side: Side::Bid,
            sequence: 3,
            ts_exchange_ns: 3_000,
            ts_recv_ns: 3_100,
        },
    ];

    let mut acc = AnalyticsAccumulator::default();
    for trade in &trades {
        acc.on_trade(trade);
    }

    let analytics = acc.snapshot();
    let derived = acc.derived_snapshot();
    let session_candle = acc.session_candle_snapshot();
    let interval_candle = acc.interval_candle_snapshot(5_000);

    println!(
        "delta={} poc={} total_volume={} vwap={:.2}",
        analytics.delta,
        analytics.point_of_control,
        derived.total_volume,
        derived.vwap
    );
    println!(
        "session ohlc=({}, {}, {}, {}) trades={}",
        session_candle.open,
        session_candle.high,
        session_candle.low,
        session_candle.close,
        session_candle.trade_count
    );
    println!(
        "interval close={} interval_vwap={:.2}",
        interval_candle.close,
        interval_candle.vwap
    );
}

Strategy-Prototyping Pattern

A common progression is:

  1. use [TradePrint] and [AnalyticsAccumulator] to compute deterministic features
  2. test threshold logic over [AnalyticsSnapshot] and [DerivedAnalyticsSnapshot]
  3. once stable, move the logic into an of_signals::SignalModule
  4. finally run it inside of_runtime for live or replay orchestration