Public docs are functionally complete and canonical, but contain no source code, no private account details, and no reproducible tuning.

Decimal / f64 policy and f64 inventory (Krakenbot)

Evidence: The execution L2-only feed suffered ~98.7% CRC32 mismatches because JSON numbers were parsed via f64, destroying trailing precision; combined with wrong Decimal keys, the registry never updated. Fix: RawValue wire strings + Decimal::from_str_exact + depth truncation (8188ba4 series). Same failure class can reappear on trades, ticker, order wire, sizing, and reconciliation if f64 remains on those paths.

This document is the SSOT for the engineering rule and inventory until the phased migration completes. Update it when you change a listed module.


1. DECIMAL RULE TABLE

RuleScopeException policy
No f64 for price SSOTChecksum-critical, orderbook apply, execution order/fill SSOT, registry snapshotsOnly non-critical derived metrics (e.g. normalized scores), with inline justification
No f64 for quantity / size / order amount SSOTSame as aboveSame
No f64 for notional used in caps / risk / live sizingExecution + risk gatesSame
No f64 in snapshot/delta/apply/checksum pathL2 (and any future book feeds)Lossless wire (RawValue / String) + Decimal for book state
No f64 on execution decision path where price/qty/order amount is SSOTProtection, exits, reconcile, WS order build, instrument quantize to wireTemporary legacy only; mark must-fix in inventory
Spread/mid/bpsPrefer Decimal when compared to fee floors or used as gate SSOT; f64 bps acceptable for analytics-only if documentedIf the value gates tradable / order send, treat as critical
New serde fields for exchange price/qtyMust not default to f64Use RawValue, String, or rust_decimal with correct serde

2. F64 INVENTORY TABLE

Rows are module-granular (file + primary struct/API). Many files contain additional local f64 temporaries; grep f64 in that file for exhaustiveness. Classification:

  • C = checksum-critical
  • E = execution-critical
  • A = analytics-only / non-gating derived

Priority: P0 = fix before expanding live scope; P1 = next phase; P2 = acceptable-temporary / migrate with module; P3 = analytics, low risk.

filesymbol_or_fieldcurrent typesemantic_rolechecksum_criticalexecution_criticalpriority
src/state/horizon_movers.rsHorizonWindow::push, record_trade, sample_l2_midmid_pricef64Mid/trade tick storage in ringnoyes (feeds horizon SSOT for selector)P0
src/observe/l2_feed.rsqty_f (zero / remove detection)f64 from str::parseBranch only; book state is Decimalno (state is Decimal)noP2 (replace with Decimal zero-compare)
src/exchange/messages.rsTradeData::{price,qty}DecimalPublic WS trade serdenoOK (migrated 3ead075)
src/exchange/messages.rsTickerData::{bid,ask,last,bid_qty,ask_qty}Option<Decimal>Public WS ticker serdenoOK (migrated c2657ac)
src/exchange/messages.rsBookLevel::{price,qty}f64Legacy serde surface (unused by L2 hot path after Raw path)nono if unusedP2 deprecate/remove callers
src/exchange/messages.rsExecutionReport / ExecutionFee (inbound: order_qty, limit_price, cum_qty, cum_cost, last_qty, last_price, fee qty)Option<Decimal>Private WS inbound serdenoOK (migrated 950bdd3)
src/exchange/messages.rsAddOrderParams, TriggerParams, ConditionalParams, AmendOrderParams (outbound struct fields)f64Private WS outbound JSON — kept f64 for emission; callers pass Decimal, dec_to_wire() converts after quantizationnono (safe post-quantize)P2 (optional: custom Decimal serde later)
src/exchange/auth_ws.rsfunction params, quantize_limit_price_wireDecimalOrder wire construction — DONEnoOK (migrated ed7d26b)
src/exchange/instruments.rsInstrumentConstraints, normalize_order, normalize_exit_qty, quantize_*, NormalizedOrderDecimalExact instrument arithmetic — DONEnoOK (migrated ed7d26b)
src/exchange/price_cache.rsTickerSnapshot bid/ask/qtyf64L1 cachenoyes (readiness / features)P1
src/exchange/trade_flow_window.rstrade price, qtyf64Flow aggregatesnoyes if gates routingP1
src/exchange/balance_cache.rsbalancef64BalancesnoyesP0
src/exchange/kraken_public.rsspread_bps(bid, ask)f64 inSpread helpernoyes (downstream features)P1
src/observe/l2_book_registry.rssnapshot fieldsDecimalGOOD — referenceOK
src/l3/messages.rslimit_price, order_qtyf64L3 serdenoyesP0
src/execution/live_runner.rssample_l2_mid call — mid from to_string().parse::<f64>()f64Horizon L2 samplenoyesP0
src/execution/protection_flow.rsqty, stop, notional, normalize — boundary converts f64↔Decimal at instruments/auth_ws callsf64 internalStops / protectionnoyesP1 (Phase 3: internals)
src/execution/position_monitor.rsnet_qty, prices, trail — boundary converts at auth_ws callsf64 internalLive position managementnoyesP1 (Phase 3)
src/execution/exit_lifecycle.rsfill qty/price, SL/TP — boundary converts at instruments/auth_ws callsf64 internalExit state machinenoyesP1 (Phase 3)
src/execution/ignition_exit.rssame class — boundary convertsf64 internalIgnition exitsnoyesP1 (Phase 3)
src/execution/position_reconcile.rsdb_position_qty, intended qty — boundary convertsf64 internalReconcilenoyesP1 (Phase 3)
src/execution/exposure_reconcile.rsqty_f64 parses — boundary convertsf64 internalExposurenoyesP1 (Phase 3)
src/pipeline/sizing.rssize_quote, equity_quote, spreadsf64Sizing → ordersnoyesP0
src/risk/capital_allocator.rsnotional_quote, equity_quotef64Capital slotsnoyesP0
src/config/mod.rslimits, equity, spreads (config)f64Thresholdsnoyes (compared to runtime)P1
src/trading/readiness_gate.rsspread_bps, surplusesf64Gates tradablenoyesP1
src/pipeline/strategy_pipeline.rsspread_bps, max_order_quotef64Pipeline / candidatesnoyesP1
src/route_engine/*.rs, src/trading/entry_filter.rs, conflict_lane.rsspread_bps, sizesf64Route / entrynoyes if liveP1
src/analysis/* (edge_score, fill_probability, cost_breakdown, strategy_readiness_report, …)scores, bpsf64Features / reportsnoyes when feeds gatesP1 / A
src/state/tradability.rsspread_now_bpsf64Tradability mirrornoyesP1
src/db/margin_paper_trades.rsentry_price, exit_pricef64DB row typesnopaper laneP2
src/export/parquet_cold.rsprice, qtyf64ExportnonoP3
src/observability/snapshots.rsPnL, positionsf64ObservabilitynonoP3
src/ignition/store.rson_trade(price)f64Ignitionnoyes if gatesP1

Exhaustive line-level f64: run rg ': f64|\\bf64\\b' src/ and filter by directory.


3. IMMEDIATE FIX TABLE (highest danger, smallest ambiguity)

locationwhy dangerous nowexact replacement typerisk
messages.rs TradeData / TickerData / ExecutionReportDONE — migrated to Decimal via de_decimal RawValue deserializerDecimal at serde boundaryCommits 3ead075, c2657ac, 950bdd3
auth_ws.rs + instruments.rs wire quantizeDONEDecimal end-to-end; f64 only in final outbound struct (safe post-quantize)DecimalCommits ed7d26bba0fd0e
protection_flow.rs / position_monitor.rs / exit_lifecycle.rsInternal arithmetic still f64; boundary conversions addedDecimal internally (Phase 3)Medium — mitigated by Decimal quantize at boundary
horizon_movers.rs ringMixed float mid + Decimal L2 → inconsistent horizon stateDecimal in ring or fixed-point i128Medium
live_runner.rs L2 mid → f64 for sample_l2_midReintroduces float on hot pathDecimal or rust_decimal + horizon API changeLow–medium
balance_cache.rs balances as f64Ghost exposure / reconcile errorsDecimalMedium

4. PHASED REFACTOR TABLE

phasetarget areafiles (representative)migration riskvalidation method
0Policy + inventorythis doc, cursor rulenoneReview
1Exchange serde ingress (public + private)messages.rs, kraken_public.rsHighcargo test, live WS smoke, compare Decimal string to RawValue — DONE commits 3ead075950bdd3
2Order construction + instrumentsauth_ws.rs, instruments.rs + 15 callersCriticalcargo test, live service restart, L2 checksum=0 — DONE commits ed7d26bba0fd0e
3Execution coreprotection_flow.rs, position_monitor.rs, exit_lifecycle.rs, ignition_exit.rs, position_reconcile.rs, exposure_reconcile.rsCriticalFull execution proof, reconcile invariants
4Sizing + risk + config comparisonssizing.rs, capital_allocator.rs, config/mod.rsHighUnit tests on boundaries, min notional
5Horizon + tradability + mirrorhorizon_movers.rs, live_runner.rs, tradability.rs, horizon_mirror.rsMediumMirror row parity, selector latency
6Analysis / route enginereadiness_gate.rs, strategy_pipeline.rs, route_engine/*, analysis/*MediumGolden tests on gate outcomes
7Analytics / exportexport/*, observability/snapshots.rsLowReport diff

5. NO-GO TABLE (forbidden immediately for new code)

PlaceRule
L2 OrderBook internal maps / checksum payloadNo new f64 fields; state stays Decimal + string map for CRC
l2_book_registry::L2BookSnapshotNo f64 — already Decimal
Any new Kraken book/trade/ticker serde structNo f64 for price/qty — use RawValue/String/Decimal
Private WS order JSON buildingNo new f64 for price/qty/trigger; extend Decimal path
Fill → position update SSOTNo new f64 for fill qty/price
Checksum validation branchNo f64 on compared book state

Legacy f64 in the files listed in section 2 may remain only until migrated; do not copy that pattern into new modules.


Maintenance

When you complete a phase, update section 2 (priority → OK or remove row) and note the commit range in the phase row.

Faire un don…