Skip to content

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

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.

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