Skip to content

Reconstruction models

Family: SDK-crate (atelier-connect::framework) · Min version: atelier-connect 0.0.10

A ReconstructionModel says how a SourcedOrderBook turns a venue's frames into a live book and how it detects a lost update. The model is chosen per channel by the venue adapter; the apply path is otherwise shared. Every model has one invariant: an in-order update advances the book to Synced, and any continuity break drops it to Gapped and raises ResyncNeeded rather than silently dropping.

Models

Model How it syncs When to use
FullRefresh Every frame is a complete top-N book and replaces the prior one The venue pushes whole snapshots each tick
SeqDelta A snapshot seed plus incremental deltas, validated by a SeqPredicate The venue streams id-sequenced deltas off a seed
ChecksumDelta Incremental deltas whose running book is verified against the venue's CRC32 checksum The venue publishes a per-frame book checksum
L3 A per-order (order-id keyed) book aggregated to price levels The venue streams individual order add/remove events

SeqDelta also carries a SnapshotSource (where its seed comes from) and a needs_rest flag; L3 carries its SnapshotSource. When a model needs a REST seed, a RestSnapshot seeder fetches the snapshot over the shared rate-limited HTTP client, and the reconstruction runtime buffers deltas until the seed arrives, discards any at or below the snapshot id, then replays the rest in order.

let model = ReconstructionModel::SeqDelta {
    predicate: SeqPredicate::RangeInclusive,
    source: SnapshotSource::RestSnapshot,
    needs_rest: true,
};
flowchart TD
    F["normalized delta"] --> M{"ReconstructionModel"}
    M -->|FullRefresh| FR["replace whole book"]
    M -->|SeqDelta| SD["check SeqPredicate<br/>vs last id"]
    M -->|ChecksumDelta| CD["apply, then verify<br/>CRC32 checksum"]
    M -->|L3| L3["upsert order id,<br/>aggregate to levels"]
    SD -->|continuous| OK["Synced"]
    CD -->|match| OK
    FR --> OK
    L3 --> OK
    SD -->|gap| G["Gapped + ResyncNeeded"]
    CD -->|mismatch| G

Diagram: a normalized delta is routed by the chosen ReconstructionModel; FullRefresh replaces the book, SeqDelta checks a sequence predicate, ChecksumDelta verifies a CRC32 after applying, and L3 aggregates per-order events — a continuous update reaches Synced while a gap or mismatch raises ResyncNeeded.

SeqPredicate

For SeqDelta, the SeqPredicate defines what "in order" means against the last applied id. The choice depends on how the venue numbers its updates.

Predicate Continuity rule Fits
RangeInclusive The delta's id range must abut the last applied id: first_id <= last + 1 and final_id > last. A forward jump is a dropped-frame gap Venues with per-delta id ranges
ExactPrev The delta's prev-id pointer must equal the last applied id: sequence == last Venues carrying an explicit previous-id pointer
Monotonic The id is a connection-wide counter bumped by every message, so only strict advance is required: update_id > last Venues without a per-book contiguous id

Monotonic is the weakest rule because its id is not per-book contiguous — per-book drop detection is unprovable there, so only strict advance can be required.

Checksum

For ChecksumDelta, after applying a delta the running book is hashed with the venue's recipe and compared to the venue-reported checksum. A mismatch means a delta was lost or misapplied, so the book gaps and re-seeds. Each recipe is validated against real captured frames.

Recipe (ChecksumFmt) Hash input
OkxTop25 CRC32 of the top-25 levels interleaved bid-then-ask per rank, as a signed i32
KrakenTop10 CRC32 (unsigned) of the top-10 asks then bids, each price and qty with the decimal point and leading zeros removed
BitgetBidFirstTop25 Shares the OKX recipe

Book depth follows the recipe

A checksum is computed over a fixed depth, so a SourcedOrderBook on the Kraken recipe holds its book at the subscribed depth (10) rather than the full book, keeping the hash input exact.

Epoch normalization

Venues report event times in different units — seconds, milliseconds, microseconds, or nanoseconds. Reconstruction normalizes every event timestamp to milliseconds by magnitude, so a downstream consumer reads one unit regardless of venue. Modern epochs are well-separated by digit count, so classifying by magnitude is unambiguous for any plausible market timestamp; 0 stays 0.

Input magnitude Unit Result
>= 10^17 nanoseconds v / 1_000_000
>= 10^14 microseconds v / 1_000
>= 10^11 milliseconds v (as-is)
otherwise seconds v * 1_000

Recovery

When a book gaps, ResyncNeeded carries a RecoveryAction telling the runtime how to re-seed: Resubscribe (unsubscribe and resubscribe), RestSnapshot (re-fetch the REST seed and replay), or ReqOnSocket (re-issue an in-band seed request). A self-seeded book that can only recover by resubscribing signals the caller to reconnect the socket; a REST-seeded book re-fetches its snapshot and reconciles in place.

See also