You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
AsyncAPI v0.14+ added 33 fields across 11 WebSocket payload models. The most consequential additions are:
*_ms timestamp fields — the spec promotes Unix-ms integer timestamps as the primary form going forward (TickerPayload.ts_ms, OrderbookDeltaPayload.ts_ms, TradePayload.ts_ms, FillPayload.ts_ms, OrderGroupPayload.ts_ms, UserOrdersPayload.{created,last_updated,expiration}_ts_ms). The existing ts: datetime fields remain for compat. Do not auto-convert — surface both. Users on latency-sensitive paths want the integer form directly.
Direction-encoding fields — outcome_side / book_side / taker_outcome_side / taker_book_side on FillPayload, TradePayload, UserOrdersPayload. Mirror the REST-side backfill in PR1: backfill v3.18.0 fields on Market / Order / Fill #159.
MVE and RFQ contracts/cost fields on the communications payloads — mve_collection_ticker, mve_selected_legs, contracts_fp, target_cost_dollars, yes_contracts_offered_fp, no_contracts_offered_fp, rfq_target_cost_dollars.
Depends on #158. Independent of #159, #160, #161 — can land in parallel.
WS payload models already have model_config={"extra": "allow"} — do not change.
Type with the existing *Literal aliases (SideLiteral, BookSideLiteral, SelfTradePreventionTypeLiteral) imported from kalshi.models.orders. Add the import where needed.
For _dollars / _fp fields, use validation_alias so both wire names resolve — see existing kalshi/ws/models/fill.py for the pattern.
ts_ms fields are plain int | None, not datetime — the spec gives ms since epoch as an integer. Don't auto-convert.
Don't reorder existing fields; append.
Acceptance criteria
All 11 parameterized tests/test_contracts.py::TestWsSpecDrift::test_ws_additive_drift[*Payload] cases pass without warnings.
uv run pytest tests/ws/ -v passes; new (de)serialization tests cover at least one new field per payload.
uv run pytest tests/ -q --ignore=tests/integration passes.
uv run mypy kalshi/ strict-clean.
uv run ruff check . clean.
Notes
additional_metadata may be either an object or a JSON-stringified blob in the spec — verify and type accordingly. If string, use str | None; if object, use dict[str, Any] | None.
The _ts_ms fields on UserOrdersPayload correspond to existing datetime fields (created_time, last_updated_time, expiration_time). Spec keeps both. Don't merge or alias them — the wire names are different and consumers may want either.
Effort
~90 lines model code + ~30 lines tests.
Part of the response-side spec drift hardening stack.
Context
AsyncAPI v0.14+ added 33 fields across 11 WebSocket payload models. The most consequential additions are:
*_mstimestamp fields — the spec promotes Unix-ms integer timestamps as the primary form going forward (TickerPayload.ts_ms,OrderbookDeltaPayload.ts_ms,TradePayload.ts_ms,FillPayload.ts_ms,OrderGroupPayload.ts_ms,UserOrdersPayload.{created,last_updated,expiration}_ts_ms). The existingts: datetimefields remain for compat. Do not auto-convert — surface both. Users on latency-sensitive paths want the integer form directly.outcome_side/book_side/taker_outcome_side/taker_book_sideonFillPayload,TradePayload,UserOrdersPayload. Mirror the REST-side backfill in PR1: backfill v3.18.0 fields on Market / Order / Fill #159.mve_collection_ticker,mve_selected_legs,contracts_fp,target_cost_dollars,yes_contracts_offered_fp,no_contracts_offered_fp,rfq_target_cost_dollars.Depends on #158. Independent of #159, #160, #161 — can land in parallel.
Scope
Files
kalshi/ws/models/ticker.pykalshi/ws/models/fill.pykalshi/ws/models/orderbook_delta.pykalshi/ws/models/trade.pykalshi/ws/models/user_orders.pykalshi/ws/models/market_lifecycle.pykalshi/ws/models/order_group.pykalshi/ws/models/communications.pytests/ws/test_models.py— extend per-payload testsField-level changes (33 total)
ticker.TickerPayload(+2):price_dollarsDollarDecimal | None = Nonevalidation_alias=AliasChoices("price_dollars", "price")if a sibling existsts_msint | None = Nonets: datetimefill.FillPayload(+3):book_sideBookSideLiteral | None = Noneoutcome_sideSideLiteral | None = Nonets_msint | None = Noneorderbook_delta.OrderbookDeltaPayload(+1):ts_msint | None = Nonetrade.TradePayload(+3):taker_book_sideBookSideLiteral | None = Nonetaker_outcome_sideSideLiteral | None = Nonets_msint | None = Noneuser_orders.UserOrdersPayload(+6):book_sideBookSideLiteral | None = Noneoutcome_sideSideLiteral | None = Noneself_trade_prevention_typeSelfTradePreventionTypeLiteral | None = Nonecreated_ts_msint | None = Nonelast_updated_ts_msint | None = Noneexpiration_ts_msint | None = Nonemarket_lifecycle.MarketLifecyclePayload(+4):additional_metadatadict[str, Any] | None = NonestringJSON blobfloor_strikestr | None = Noneprice_level_structurestr | None = NoneMarket.price_level_structurefrom #159yes_sub_titlestr | None = Noneorder_group.OrderGroupPayload(+1):ts_msint | None = Nonecommunications.RfqCreatedPayload(+2):mve_collection_tickerstr | None = Nonemve_selected_legslist[str] | None = Nonecommunications.RfqDeletedPayload(+3):contracts_fpFixedPointCount | None = Noneevent_tickerstr | None = Nonetarget_cost_dollarsDollarDecimal | None = Nonecommunications.QuoteCreatedPayload(+4):event_tickerstr | None = Noneyes_contracts_offered_fpFixedPointCount | None = Noneno_contracts_offered_fpFixedPointCount | None = Nonerfq_target_cost_dollarsDollarDecimal | None = Nonecommunications.QuoteAcceptedPayload(+4):Same four fields as
QuoteCreatedPayload.Conventions
model_config={"extra": "allow"}— do not change.*Literalaliases (SideLiteral,BookSideLiteral,SelfTradePreventionTypeLiteral) imported fromkalshi.models.orders. Add the import where needed._dollars/_fpfields, usevalidation_aliasso both wire names resolve — see existingkalshi/ws/models/fill.pyfor the pattern.ts_msfields are plainint | None, not datetime — the spec gives ms since epoch as an integer. Don't auto-convert.Acceptance criteria
tests/test_contracts.py::TestWsSpecDrift::test_ws_additive_drift[*Payload]cases pass without warnings.uv run pytest tests/ws/ -vpasses; new (de)serialization tests cover at least one new field per payload.uv run pytest tests/ -q --ignore=tests/integrationpasses.uv run mypy kalshi/strict-clean.uv run ruff check .clean.Notes
additional_metadatamay be either an object or a JSON-stringified blob in the spec — verify and type accordingly. Ifstring, usestr | None; ifobject, usedict[str, Any] | None._ts_msfields onUserOrdersPayloadcorrespond to existing datetime fields (created_time,last_updated_time,expiration_time). Spec keeps both. Don't merge or alias them — the wire names are different and consumers may want either.Effort
~90 lines model code + ~30 lines tests.
Part of the response-side spec drift hardening stack.
Tracking: #157.