feat(fix): FIX protocol bridge behind the fix feature - #220
Conversation
Maps fix-codec-decoded FIX messages onto the matching engine via a new optional `fix` feature (default off): - NewOrderSingle (D): OrdType 2 -> add_limit_order, OrdType 1 -> submit_market_order; Side/TimeInForce/Qty/Price tags converted to pricelevel types (decimal prices scaled to integer ticks by tick_size). - OrderCancelRequest (F): cancel by OrigClOrdID (tag 41). - OrderCancelReplaceRequest (G): cancel-and-replace the original. - ClOrdID (tag 11) maps deterministically to an order id via FNV-1a, so the same client id always targets the same book order across place/cancel/replace. - Protocol-only: no session state, sequence numbers, or transport; the caller supplies those (e.g. a fix-session layer). Fully cfg-gated: the module adds no dependency or behavior when `fix` is off. Clippy-clean; all tests pass (690 + feature suite). CHANGELOG under [Unreleased] and README v0.13.0 section updated. Co-Authored-By: Claude <noreply@anthropic.com>
|
Thanks for the work here. Before the code itself, a scoping decision that affects the whole PR: If you want to keep going, three options, in the order I would pick them: 1. A separate bridge crate (preferred). A new repo depending on 2. Contribute the bridge to IronFix instead. Same thing, living on the protocol side, where the FIX session layer already is. 3. Contribute the protocol-agnostic seam here. No FIX in it: an inbound-decoder / outbound-encoder trait pair over Happy to open the issues for option 3 if that is the one you want to take. |
|
Went with option 1 — the bridge is now a standalone crate: https://github.com/directwire/ironfix-orderbook-bridge Shape follows your sketch:
One note for the tracker: 9 integration tests (session isolation, cross via listener, too-late cancel, replace identity consumption, tag-level error mapping, attribution stability), clippy + rustfmt clean. Happy to take feedback here or on the repo. |
Summary
Adds a FIX protocol bridge to
OrderBook-rs, connecting a wire-level FIX codec to the matching engine. Protocol-only by design — no session state, sequence numbers, or transport lives here; the caller owns those (e.g. a futurefix-sessionlayer).New optional
fixfeature (default off, fullycfg-gated — zero dependency or behavior change for existing users):NewOrderSingle(D) —OrdType 2→add_limit_order,OrdType 1→submit_market_order. ConvertsSide(54),OrderQty(38),TimeInForce(59),Price(44) intopriceleveltypes; FIX decimal prices scale to integer ticks via the book's tick size.OrderCancelRequest(F) — cancel byOrigClOrdID(tag 41).OrderCancelReplaceRequest(G) — cancel-and-replace the original.ClOrdIDmapping — tag 11 hashed with FNV-1a to apricelevel::Id, so the same client order id always targets the same book order across place/cancel/replace.Entry point
apply_fix_messagedispatches onMsgType(35) and returns a typedFixOutcome(Placed/Cancelled/Replaced), withFixBridgeErrorcovering conversion, unsupported message types, and order-book rejections.Testing
fix::convert(side / TIF / price-to-ticks / deterministic id) andfix::engine(limit, market, cancel, cancel-and-replace incl. state assertions, missing-field, unsupported-type, missing-order paths).cargo test --features fix— 690 + 26 + 470 + 49 all green; default build and--features fixbuild both clean.cargo clippy --features fix --all-targets— zero warnings.Docs
src/fix/mod.rsmodule docs with the supported message set and mapping semantics.CHANGELOG.mdentry under[Unreleased].README.md"What's New in Version 0.13.0" section with usage snippet (version title is yours to adjust at release time).Depends only on
fix-codecv0.1 (small, std-only, MIT). Nounsafe, no API breaks, default feature set unchanged.