Repository navigation
Description
Separate future epic (not part of the Perps API milestone). Spans both the prediction and perps FIX gateways. Referenced from the Perps epic #387 under out-of-scope.
Overview
The SDK has zero FIX protocol support today — it is REST + WebSocket only. Kalshi exposes a FIX (Financial Information eXchange) gateway for both product lines:
- Event-contract (prediction) API — the core Kalshi exchange covered by
kalshi/client.py/kalshi/ws. - Perps / margin API — the FCM-cleared margin product covered by the new
PerpsClient(/margin/*operations inspecs/perps_openapi.yaml).
FIX is a tag=value, session-oriented binary-ish protocol used by latency- and throughput-sensitive trading firms. It is not described by any OpenAPI/AsyncAPI spec — confirmed: /tmp/perps_openapi.yaml, /tmp/perps_asyncapi.yaml, and /tmp/perps_scm_openapi.yaml contain no FIX content (no MsgType, NewOrderSingle, session, or drop-copy references). FIX support therefore cannot be contract-tested against those specs and must be driven off Kalshi's separate FIX specification documents (FIX 5.0 SP2 / FIXT.1.1 dialect with Kalshi custom tags).
This is a future placeholder epic to capture the surface area, the major sub-components, the build-vs-library decision, and the rationale for deferral. No sub-issues are spun out yet — when prioritized, this epic gets decomposed.
Scope note: there is intentionally no spec wiring, no contract-test wiring, and no
METHOD_ENDPOINT_MAP/BODY_MODEL_MAPwork in this epic. FIX rides its own protocol spec, not OpenAPI.ops_coveredandschemas_coveredare empty by design.
In scope
A future FIX subsystem would live in a new top-level package (proposed kalshi/fix/) and span the following major sub-components, shared across the prediction and perps products:
| Sub-component | Responsibility |
|---|---|
| FIX engine / session layer | Logon/Logout, heartbeat (HeartBtInt), test request, sequence number management + gap fill / resend (ResendRequest, SequenceReset), session reset, persistent message store, reconnect/recovery. This is the hard, stateful core. |
| Message codec | Encode/decode tag=value frames, SOH framing, BodyLength/CheckSum computation, repeating groups, Kalshi custom tags. Must round-trip every supported message type. |
| Auth | FIX Logon authentication. Reuse the existing RSA-PSS KalshiAuth signer where the FIX logon flow permits (signature in RawData/Password or a Kalshi custom tag — TBD from FIX spec); otherwise a FIX-specific credential flow. |
| Order entry (OE) session | NewOrderSingle (35=D), OrderCancelRequest (35=F), OrderCancelReplaceRequest (35=G), ExecutionReport (35=8), OrderCancelReject (35=9). Mirrors REST CreateMarginOrder / CancelMarginOrder / AmendMarginOrder / DecreaseMarginOrder and the prediction order endpoints. |
| Drop copy (DC) session | Read-only execution-report stream for fills/cancels/amends across all of an account's sessions. |
| Market data (MD) session | MarketDataRequest (35=V), MarketDataSnapshotFullRefresh (35=W), MarketDataIncrementalRefresh (35=X) for orderbook + trade feeds. |
| Typed model layer | Pydantic (or dataclass) message models mirroring SDK conventions: DollarDecimal for prices, FixedPointCount for quantities, enums for Side/OrdType/TimeInForce/ExecType mapped to/from the REST enum vocabulary. |
Four Kalshi session types to support, selected at logon (per the FIX spec, not the OpenAPI specs):
- NR — Order entry (New oRder / standard trading session)
- RT — Real-time / trading
- DC — Drop copy
- MD — Market data
Perps-specific differences from prediction FIX (the two products share an engine but differ in semantics):
- Decimal-dollar pricing — perps quotes prices in
FixedPointDollars(string, up to 6 decimals; spec schemaFixedPointDollars) and quantities inFixedPointCount(string, 2 decimals; spec schemaFixedPointCount), versus the prediction product's integer cents. The codec must carry full decimal precision, not cents. UseDollarsalways on for perps — no cents/contract-integer mode.- No RFQ / quotes / settlement-report message flows on the perps side (those exist only on certain prediction flows or not at all).
- Order vocabulary maps to the perps REST schema:
side∈ {bid,ask} (BookSide),time_in_force∈ {fill_or_kill,good_till_canceled,immediate_or_cancel},self_trade_prevention_type∈ {taker_at_cross,maker} (SelfTradePreventionType), pluspost_only. These must round-trip to FIXSide/TimeInForce/ custom STP tags.
Out of scope / open questions
Out of scope for this epic (deferred to decomposition):
- Any concrete code, models, resources, or tests. This epic only captures the surface.
- Spec-driven contract tests — FIX is not in OpenAPI/AsyncAPI, so
METHOD_ENDPOINT_MAP/BODY_MODEL_MAP/EXCLUSIONSare untouched. Conformance testing for FIX would instead be golden-message round-trip fixtures. - FIX session persistence/store backend selection (in-memory vs file vs pluggable).
Build-vs-library decision (must be resolved before any sub-issue is opened):
| Option | Pros | Cons |
|---|---|---|
quickfix (QuickFIX/Python) |
Battle-tested full engine: session mgmt, store, resend, recovery all handled. | Heavy C++ binding, awkward async story, opinionated config (XML data dictionaries), painful to fit the SDK's httpx/async-first style; large dependency. |
simplefix |
Pure-Python, lightweight, just codec (encode/decode of tag=value). | Codec only — no session layer; we hand-roll logon/heartbeat/seqnum/resend/recovery on top. |
| Hand-rolled | Full control, fits SDK conventions (Pydantic models, DollarDecimal/FixedPointCount, async transport patterns), no heavy deps. |
We own the entire stateful, error-prone session/recovery layer — the highest-risk part of FIX. |
Leaning recommendation (to validate at decomposition time): simplefix for the codec + a hand-rolled session layer, so the typed model/codec edges match SDK conventions while avoiding the heaviest binding, but the session-recovery cost is real and may justify quickfix.
Open questions:
- FIX dialect/version (FIX 5.0 SP2 over FIXT.1.1?) and the exact Kalshi custom tag set — pull from Kalshi's FIX spec docs, not the OpenAPI specs.
- Logon auth mechanism: can the existing RSA-PSS
KalshiAuthsignature be reused in the Logon message, or does FIX require a distinct credential flow? - Connectivity: raw TCP + TLS endpoints and ports per session type (NR/RT/DC/MD) — separate from the REST
external-api.kalshi.com/ WS hosts. - Sync vs async surface: does FIX warrant its own
FixSession/AsyncFixSessionpair mirroringPerpsClient/AsyncPerpsClient, or async-only given its streaming nature? - Shared-vs-separate engine for prediction and perps: one engine parameterized by product, or two thin facades over one core.
Why deferred
- Large and orthogonal. A correct FIX engine (session state machine, sequence/resend/recovery, persistent store) is a substantial subsystem with little code overlap with the REST/WS stack (httpx transport, retry, Pydantic-over-OpenAPI). It does not benefit from — and cannot reuse — the contract-test harness, since FIX has no OpenAPI/AsyncAPI spec.
- Niche audience. FIX connectivity is used by a small set of latency/throughput-sensitive firms; the vast majority of SDK users are served by REST + WebSocket. Prioritizing the perps REST/WS surface delivers more value first.
- No spec to drive it. The SDK's core value is spec-first contract testing. FIX would require a different correctness discipline (golden-message round-trip fixtures against Kalshi FIX spec docs), which is a separate investment to design.
- Dependency on the perps REST/WS foundation. It is cleaner to finish and validate the perps REST + WS models (enums,
DollarDecimal/FixedPointCountsemantics, order vocabulary) first, then reuse those typed models when FIX order entry / drop copy / market data are built.
Size: XL. Tracked as a future placeholder; decompose into engine / OE / DC / MD sub-issues (per product) only when prioritized.
Activity
fix(fix): address PR #422 review — Logon copy, field caching, ssl typing
fix(fix): address PR #422 second review — gap-fill buffering, Self re…
feat(fix): FIX protocol foundation (codec + session engine + auth) — …
feat(fix): repeating-group support in the message framework (closes #423
fix(fix): address PR #431 review — top-level export + test gaps
fix(fix): address PR #431 2nd review — robust decode + immutable regi…
feat(fix): order-entry message flow (closes #424) (#431)
feat(fix): drop-copy message flow + listener sessions (closes #425) (#…
feat(fix): market-data session + order-book reconstruction (closes #426…
feat(fix): order-group lifecycle messages (closes #427) (#435)
feat(fix): RFQ / quoting message family (closes #428) (#436)
feat(fix): market-settlement / post-trade reports (closes #429) (#438)
TexasCoding commentedon Jun 6, 2026
✅ FIX subsystem feature-complete on
mainThis epic is delivered. The SDK went from zero FIX support to a hand-rolled, async-first FIX engine (FIXT.1.1 transport / FIX50SP2 application) shared by the prediction and perps products —
kalshi.fixcore + a thinkalshi.perps.fixmargin facade — built in 9 reviewed PRs:a632aa60ff1009fc4af90416afca279fb698fdce1a3ef00118c818e3What shipped
DollarDecimal/FixedPointCount(no float drift), inbound-robust (optional fields, raw codes), with a central inbound dispatch registry.FixOrderBook(MD reconstruction),SettlementReassembler(paginated UMS), market-maker/creator RFQ lifecycle, order-group lifecycle.Every phase was hardened by a pre-PR multi-agent adversarial review and the CI
claude-reviewloop. Comprehensive FIX test suite (250+ tests onmain; 530+ including the drift parametrization in #440).Spec note
The bundled FIX dictionary is v1.03 and predates market data, so the MD models (V/W/X/Y/e/f) were derived from
docs.kalshi.com/fix/market-data; that's the only documented dialect deviation and is now guarded by the #437 drift test's exclusions.Follow-ups in review (then this epic closes)
FixSession.on_decode_error+decode_app_message_strict).The subsystem is on
mainbut unreleased — a version bump / CHANGELOG entry is separate release-prep. Closing this epic once #439 and #440 merge.feat(fix): surface inbound decode failures (closes #432) (#439)
test(fix): dictionary<->model drift test for the FIX message set (closes
TexasCoding commentedon Jun 6, 2026
Epic complete ✅
All planned phases plus both spawned follow-ups are merged to
main:c928cc3) and [FIX] Dictionary↔model drift test for the FIX message set (analogous to REST TestRequestBodyDrift) #437 (FIX dictionary↔model drift test, PR test(fix): dictionary↔model drift test for the FIX message set (closes #437) #440,c0716de) — merged.mainis green: ruff + mypy --strict clean, 538 FIX tests passing. The hand-rolled, async-first FIX subsystem (kalshi.fixcore +kalshi.perps.fixmargin facade) is feature-complete.Reminder: this is on
mainbut unreleased — a version bump + CHANGELOG/ROADMAP entry is separate release-prep.docs: FIX documentation + accuracy pass + release prep for 3.3.0 (#441)