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¶
- Feeds and Books — the Book FSM these models drive.
- Ingestion framework — where a venue adapter selects its model.