Skip to content

perps: [EPIC] FIX API support (prediction + perps) — future #402

Closed
8 of 8 issues completed
Closed

Description

@TexasCoding

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 in specs/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_MAP work in this epic. FIX rides its own protocol spec, not OpenAPI. ops_covered and schemas_covered are 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 schema FixedPointDollars) and quantities in FixedPointCount (string, 2 decimals; spec schema FixedPointCount), versus the prediction product's integer cents. The codec must carry full decimal precision, not cents.
  • UseDollars always 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), plus post_only. These must round-trip to FIX Side / 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 / EXCLUSIONS are 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 KalshiAuth signature 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 / AsyncFixSession pair mirroring PerpsClient / 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/FixedPointCount semantics, 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

  1. added
    enhancementNew feature or request
    perpsPerps / margin (perpetual futures) API
    on Jun 4, 2026
  2. added 6 commits that reference this issue on Jun 5, 2026
  3. TexasCoding commented on Jun 6, 2026

    @TexasCoding
    OwnerAuthor

    ✅ FIX subsystem feature-complete on main

    This 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.fix core + a thin kalshi.perps.fix margin facade — built in 9 reviewed PRs:

    Phase Issue PR Squash
    Foundation (codec, session engine, RSA-PSS logon, connectivity config, typed-message framework) #422 — a632aa6
    Repeating-group support (nested) #423 #430 0ff1009
    Order Entry (D/F/G/8/9/q/r/j) #424 #431 fc4af90
    Drop Copy (U1/U2/U3) + listener sessions #425 #433 416afca
    Market Data (V/W/X/Y/e/f) + order-book reconstruction #426 #434 279fb69
    Order Groups (UOG/UOH) #427 #435 8fdce1a
    RFQ / Quoting (13 messages, prediction) #428 #436 3ef0011
    Market Settlement / Post-Trade (UMS) + fragment reassembly #429 #438 8c818e3

    What shipped

    • Codec — hand-rolled tag=value/SOH framing, BodyLength + mod-256 CheckSum, length-prefixed DATA fields, repeating groups (nested), resync/bounded-buffer hardening.
    • Session engine — async logon/heartbeat/test-request, sequence tracking with buffered gap-fill/resend, reset-on-logon policy, AWS full-jitter reconnect; per-session connectivity (NR/RT/DC/PT/RFQ/MD) for prod + demo, prediction + margin.
    • Typed messages — Pydantic v2 models for every documented Kalshi FIX message across the 7 application flows, money on DollarDecimal/FixedPointCount (no float drift), inbound-robust (optional fields, raw codes), with a central inbound dispatch registry.
    • Higher-level helpers — 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-review loop. Comprehensive FIX test suite (250+ tests on main; 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)

    The subsystem is on main but unreleased — a version bump / CHANGELOG entry is separate release-prep. Closing this epic once #439 and #440 merge.

  4. TexasCoding commented on Jun 6, 2026

    @TexasCoding
    OwnerAuthor

    Epic complete ✅

    All planned phases plus both spawned follow-ups are merged to main:

    main is green: ruff + mypy --strict clean, 538 FIX tests passing. The hand-rolled, async-first FIX subsystem (kalshi.fix core + kalshi.perps.fix margin facade) is feature-complete.

    Reminder: this is on main but unreleased — a version bump + CHANGELOG/ROADMAP entry is separate release-prep.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestperpsPerps / margin (perpetual futures) API

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions