# Clearing And Ledger Gotchas ## Trade dialog dies immediately after a fill Likely cause: an explicit ledger/account write in the fill handler, or a later context-exit write, raised after event publication. Inspect, in order: 1. the first non-cancellation exception in the actor traceback; 2. `PaperBoi.fake_fill()` or the live backend fill handler; 3. explicit and context-exit `write_config()` calls; 4. FQME/`bs_mktid` qualification and TOML-compatible values; 5. the persisted ledger/account files for a partial update. Tractor cancellation logs are often the consequence. Root-cause the inner serialization, lookup, or normalization exception first. ## `MktPair` has no `len()` in RapidFuzz Cause: a mapping of string keys to `MktPair` values was passed directly to `rapidfuzz.process.extract()`. Mapping values become fuzzy choices. Fix: search a list/view of string keys and resolve matched keys back through the original mapping. Do not add sequence methods to `MktPair` and do not use `processor=str` when aliases must be preserved. ## Fuzzy qualification raises `IndexError` Cause: the caller selected the first match from an empty result after the score cutoff rejected every key. Decide explicitly whether to retain the input identity, reject the record with a descriptive exception, or obtain missing market metadata. Do not lower the cutoff blindly; the wrong market corrupts accounting. ## Cancel is ignored or sent with the wrong ID Cause: confusion between client `oid` and backend `reqid`, commonly during cancel-before-ack ordering. Verify the ack installed both mapping directions and that `Status.cancel_called` survives until `reqid` exists. Log both IDs and the actor/backend account at every transition. ## Fills appear but positions do not move Check these boundaries: - the backend emitted `BrokerdFill` but never normalized a transaction; - transaction size lost its buy/sell sign; - `tid` collided with an existing position event; - `Transaction.bs_mktid` selected a different `Account.pps` key; - `mktmaps` lacked the exact transaction identity; - position publication happened before accounting update; - the backend intentionally reports venue positions instead of local ledger-derived positions. ## Duplicate or inflated position size Do not deduplicate by price/time alone. Confirm stable transaction IDs, chronological sorting, and `Position.add_clear()` idempotence. Broker execution IDs and order IDs are not interchangeable. For partial fills, several `tid`s may correctly share one order `reqid`. Collapsing them loses clears; replaying one `tid` twice inflates nothing only if the dedupe boundary is preserved. Current paper fills use `tid=oid`, so later partials overwrite the ledger record and are deduplicated out of the position. Treat paper partial fills as broken until each clear receives a stable unique ID. ## Paper and live state disagree Paper uses normalized local transactions and can use the full FQME as its account position key. A live backend may use native IDs, raw ledger records, or venue-reported positions. Compare normalized `Transaction`s and emitted `BrokerdPosition`s, not raw TOML dictionaries across backends. ## Account appears under the wrong name Separate UI/EMS account aliases from backend account IDs and ledger file names. Backends may prefix account names for routing while persistence uses an unprefixed native account key. Trace account identity through `open_ems()`, broker dialog startup, ledger opening, and `BrokerdPosition.account` before changing naming. ## A module-global cache is unexpectedly empty Symcache, clients, contracts, and order tables are actor-local. Data loaded in `datad` is not automatically available in `brokerd` or `paperboi`. Also, `_symcache._caches` is currently read but never populated, so do not assume repeated `open_symcache()` calls hit it. Load `piker-conc-expert` and audit actor-local globals, async caches, and dialog startup warming. Never rely on an import side effect from a sibling actor. ## Teardown emits a cancellation storm Find the first application exception. Structured cancellation then closes feed streams, broker streams, and nested contexts. Avoid masking the originating exception with broad cancellation handling. For stream ownership and context cancellation semantics, use `piker-conc-expert`. ## Broker status is logged as unhandled EMS currently accepts only ack-correlated `open`, `closed`, and `canceled` broker statuses. Declared `pending` and backend-produced `fill`/`filled` variants fall through, as do statuses arriving before ack. Verify producer vocabulary and ack ordering before adding another consumer branch. ## Another client sees order or position events Current translation broadcasts order updates by FQME and positions to all attached clients; `Router.dialogs` is not used as an event privacy filter. Treat subscriber isolation as incomplete, not guaranteed. ## Debug breadcrumb Capture one row per event with: ```text actor, backend, account, fqme, bs_mktid, oid, reqid, venue_order_id, tid, message_type, status, action, signed_size, price, timestamp ``` Then compare: 1. client command order; 2. EMS status history; 3. broker event order; 4. normalized transactions; 5. position event IDs and computed size/PPU; 6. ledger/account disk state; 7. emitted position summaries. This separates protocol loss, identity skew, accounting math, and persistence failure without guessing from UI state.