Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
old way. PROTOCOL.md records the same note beside the deprecation.

### Fixed
- Market-by-Order books were keyed `(publisher, instrument_id)`, dropping the `channel_id` that the
edge-feed-spec makes part of an instrument's unique key — `instrument_id` is scoped to its channel
and need not be unique across channels. Two channels on one group both carrying instrument id 7
had their books merged, applying each channel's deltas to the other's state; every per-publisher
sequence check still passed, so it surfaced only as a silently corrupt book. The book key, every
map keyed off it, and the `SnapshotOrder` routing filter now all carry the raw `channel_id` from
the datagram header. Latent — the live publishers put everything on channel 0. Reference data
remains channel-flat, so two such channels still resolve one definition and publish one symbol,
which the venue-wide depth floor then leaves flip-flopping between the two books. (#110)
- **A mirror publisher's `publisher_offset` was applied only by the market-by-price processor**, so
top-of-book, midpoint and market-by-order stamped the raw wire `channel_id` into consumer-facing
identity. `edge-kalshi-perps-tob` is a top-of-book row with an offset of 100, and un-darking it
Expand Down
19 changes: 15 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -656,10 +656,21 @@ Modules are grouped by role under `src/`:
a shared port block one publisher's restart would otherwise clear every publisher's definitions and
blank the venue. Reads take the **non-inserting** `PerPublisher::def`; only the refdata handlers use
the inserting `get`, so a forged-publisher market-data flood cannot evict a real publisher's definitions.
`MboProcessor` reconstructs an **independent book per `(publisher, instrument)`** (keyed on the
datagram source IP address): two publishers mirror one feed but their instance-scoped per-instrument delta
sequences collide, so the books can't be merged. `SnapshotOrder` carries only a `snapshot_id` (no
instrument id) and routes **only to the originating publisher's** building book. `emit_depth` stamps
`MboProcessor` reconstructs an **independent book per `BookKey` = `(publisher, channel_id,
instrument_id)`** — the same three axes `MbpProcessor`'s `PriceBookKey` carries, and every map
keyed off `books` (`last_top`, `emitted_symbol`, `revealed`, `synced_reported`,
`reveal_rebaselined_ns`) moves with it or an eviction strands, or silently un-reveals, a sibling.
*Publisher* (the source IP address): two publishers mirror one feed but their instance-scoped
per-instrument delta sequences collide, so the books can't be merged. *Channel* (the **raw** wire
id from the datagram header, never canonicalized and never a message body): the edge-feed-spec
scopes `instrument_id` to its channel, so two channels reusing an id would otherwise share one book
and cross-apply deltas — silent corruption no sequence check catches. ⚠️ `RefDataState` is **not**
channel-scoped, so such a pair still resolves one definition and publishes one symbol, and the
venue-wide depth floor then drops whichever channel trails each tick: the consumer sees that symbol
flip-flopping between two books, not merely a shared name. Moot while publishers stay on channel 0.
`SnapshotOrder` carries only a `snapshot_id` (no instrument id) and routes **only to the originating
channel instance's** building book — publisher alone stopped guaranteeing a single builder the
moment a second channel existed. `emit_depth` stamps
`source_ts_ns = book.last_event_ts()` (a per-*event* time) while coalescing per *datagram*, so two
datagrams in one tick can emit two depths with the same `source_ts`; this is **benign** under the
content-inclusive depth floor (same tick + same leader + new content → both admitted, distinct
Expand Down
Loading
Loading