Repository navigation
Description
Summary
WebSocket payload models silently regress to plain str for count/size/volume/timestamp fields that REST models type as FixedPointCount / DollarDecimal / datetime. The same logical quantity (e.g. count, volume, created_ts) round-trips as Decimal/datetime on REST and as raw string on WS.
Consumers driving limits, hedging, P&L, or cross-stream correlation off WS must Decimal(...) / datetime.fromisoformat(...) every field by hand. Any oversight is a silent type error:
str + int→ TypeErrorstr * 2→ doubled string (not doubled number)- mixing REST
Decimalpositions with WSstrfills → comparison silently false Decimal('5.00') != '5.00'quietly
Even within kalshi/ws/models/, types are inconsistent across closely-related payloads.
Affected fields
Count/size/volume (should be FixedPointCount per CHANGELOG v2.0 retyping)
kalshi/ws/models/ticker.py:34-50—volume,open_interest,yes_bid_size,yes_ask_size,last_trade_sizekalshi/ws/models/trade.py:21-26—countkalshi/ws/models/fill.py:25-32—count,post_positionkalshi/ws/models/user_orders.py:33-43—fill_count,remaining_count,initial_countkalshi/ws/models/market_positions.py:19-37—position,volumekalshi/ws/models/orderbook_delta.py:18-26—OrderbookSnapshotPayload.yes/.noislist[tuple[str, str]]
Note the internal inconsistency: OrderbookDeltaPayload (same module) correctly uses DollarDecimal/FixedPointCount. So the same logical pair (price, qty) is typed differently on snapshot vs delta.
RFQ/Quote contracts typed inconsistently
kalshi/ws/models/communications.py:31-34—RfqCreatedPayload.contracts: str | Nonekalshi/ws/models/communications.py:64-68—RfqDeletedPayload.contracts: FixedPointCount | None← already correctkalshi/ws/models/communications.py:117-120—QuoteAcceptedPayload.contracts_accepted: str | None
Timestamps (should be datetime / pydantic.AwareDatetime per REST convention)
kalshi/ws/models/user_orders.py:57-59—created_time,last_update_time,expiration_time(allstr)kalshi/ws/models/communications.py:30, 52, 79, 114, 158, 163—created_ts,deleted_ts,executed_ts, etc. (with an inlineTODO(v0.15.1)comment acknowledging the drift)
REST equivalent: kalshi/models/orders.py:88-89 created_time: datetime | None.
Recommended fix
Replace str with FixedPointCount / DollarDecimal on every _fp / _dollars-aliased field, and with datetime (or pydantic.AwareDatetime to force tz-awareness) on every ISO-8601 timestamp field. Pydantic handles both coercions automatically via the existing BeforeValidator and built-in datetime parser.
For OrderbookSnapshotPayload.yes/.no, change list[tuple[str, str]] to list[tuple[DollarDecimal, FixedPointCount]] (Pydantic coerces per element). Drop the manual Decimal(...) coercion in kalshi/ws/orderbook.py:79-80.
Update fixtures in tests/ws/test_models.py to match — the wire format does not change.
Add a parametrized contract test that asserts type symmetry across REST and WS for shared logical fields (count, volume, created_time).
Severity & category
high / consistency, ws
Activity
test(contracts): allow datetime for spec string (date-time) fields in…