Repository navigation
fix(ws/models): type count/size as FixedPointCount; timestamps as datetime - #230
Conversation
…etime - TickerPayload, TradePayload, FillPayload, UserOrdersPayload, MarketPositionsPayload count/size/volume fields now FixedPointCount instead of str (matches REST models; eliminates silent str+int TypeErrors in consumer code). - OrderbookSnapshotPayload.yes/.no now list[tuple[DollarDecimal, FixedPointCount]] (matches OrderbookDeltaPayload). - RfqCreatedPayload.contracts + QuoteAcceptedPayload.contracts_accepted now FixedPointCount (matches RfqDeletedPayload.contracts). - UserOrdersPayload + CommunicationsMessage timestamps now datetime instead of str. - Simplified OrderbookManager.apply_snapshot: dropped redundant Decimal() wrappers now that payloads return Decimal. Closes #198 Refs #224
Code ReviewOverall: Clean, focused mechanical retype sweep. The changes are well-scoped, internally consistent, and the simplification in What this PR does well
Issues / concerns1. Removed TODO in The original
This PR removes that TODO and commits to Suggestion: Either add a comment confirming the live capture was done, or add a 2.
3. Both are Nits
SummaryApprove with the suggestion to address (1) before merge or immediately after — the removed uncertainty acknowledgment about live frame capture is the only non-trivial risk in this PR. Everything else is mechanical and correct. |
… WS drift check _sdk_type_kind now recognizes datetime; _ws_field_type_violations Rule 2 accepts 'str' or 'datetime' for spec string + format=date-time fields. Pydantic auto-coerces RFC3339 strings into tz-aware datetime, matching the REST model convention that has been used for these fields all along. Fixes a gap exposed by W1-D's WS timestamp retyping (#198).
Code Review — PR #230: WS payload type sweep (FixedPointCount + datetime)Verdict: Approve with two pre-merge notes. What's good
Issues1. Removed TODO without confirming live capture (pre-merge) The deleted block in
The PR description doesn't mention a live capture was done. The risk: if the demo server sends Suggested fix: If live capture confirmed ISO string output, add a comment: 2.
Suggested addition to rfq_deleted = RfqDeletedPayload.model_validate({
"id": "rfq-1", "creator_id": "u1", "market_ticker": "T",
"deleted_ts": "2026-04-19T18:43:37Z",
})
assert isinstance(rfq_deleted.deleted_ts, datetime)Nits
|
…258, #259) (#284) * fix(ws)!: retype OrderGroup.contracts_limit and Ticker dollar fields to Decimal (#258) BREAKING CHANGE: Three WS payload fields previously typed as str now parse to Decimal per the #225/#230 SDK invariant that every _fp field is FixedPointCount and every _dollars field is DollarDecimal. - OrderGroupPayload.contracts_limit: str | None → FixedPointCount | None - TickerPayload.dollar_volume: str → DollarDecimal - TickerPayload.dollar_open_interest: str → DollarDecimal Callers reading these as Python strings (e.g. concatenation, string compares, JSON dumps without a Decimal serializer) will need to adjust. The motivation is precisely the dual hazard the previous typing carried: manual Decimal(x) wrapping was required for arithmetic, and an empty server payload would trip Decimal('') → InvalidOperation at the call site rather than at parse. With these annotations, arithmetic works directly (msg.dollar_volume + msg.dollar_open_interest) and bool inputs are rejected per the #225 invariant. Wire keys are unchanged: contracts_limit_fp / contracts_limit for the order-group alias choices; dollar_volume / dollar_open_interest for ticker (these have no _fp/_dollars suffix on the wire). Closes #258 * fix!: route strike + fee-multiplier fields through _coerce_decimal (#259) BREAKING CHANGE: Five money-adjacent fields previously typed as bare ``Decimal`` or ``float`` now route through the ``_coerce_decimal`` invariant established in #225. Bare ``Decimal`` accepts a JSON number ``1.23`` by routing through ``float`` first \u2014 the precision-loss path the SDK paid an audit to eliminate. ``float`` is strictly worse: the value commits to binary float on parse (``0.65 -> 0.6500000000000000222``) and that drift propagates into payout math. - Market.floor_strike / cap_strike: Decimal | None \u2192 DollarDecimal | None - Event.fee_multiplier_override: Decimal | None \u2192 MultiplierDecimal | None - MarketLifecyclePayload.floor_strike: Decimal | None \u2192 DollarDecimal | None - Series.fee_multiplier / SeriesFeeChange.fee_multiplier: float \u2192 MultiplierDecimal Adds a new ``MultiplierDecimal`` alias in ``kalshi.types`` (internal \u2014 not re-exported via ``kalshi.__init__``). It applies the same ``_coerce_decimal`` + string-serializer treatment as ``DollarDecimal`` / ``FixedPointCount``, kept distinct to document that these are rate multipliers, not dollar quantities. User-visible consequences: callers reading ``series.fee_multiplier`` as a Python ``float`` will now see a ``Decimal`` (use it directly for arithmetic; cast with ``float()`` only at display boundaries). Bool inputs are rejected per the #225 invariant. The stale doc-comment in ``tests/integration/test_assertions.py`` that named ``Series.fee_multiplier`` as the canonical float-bearing example is updated to reflect the post-#259 typing. Closes #259
Wave 1-D of the audit umbrella. One issue, mechanical retype sweep across WS payload models.
Changes
FixedPointCountinstead ofstr(matches REST models; eliminates silent str+int TypeErrors in consumer code).TickerPayload/TradePayload/FillPayload/UserOrdersPayload/MarketPositionsPayloadcount/size/volume fields.OrderbookSnapshotPayload.yes/.no→list[tuple[DollarDecimal, FixedPointCount]](matchesOrderbookDeltaPayload).RfqCreatedPayload.contracts+QuoteAcceptedPayload.contracts_accepted→FixedPointCount(matchesRfqDeletedPayload.contracts).UserOrdersPayload+ Communications RFC3339 timestamps →datetime.OrderbookManager.apply_snapshot— dropped redundantDecimal()wrappers now that payloads return Decimal.Tests
16 new tests including a parametrized REST↔WS field type symmetry test; 85 targeted tests pass; ruff + mypy --strict clean.
Coordination note
This branch must merge before W1-C (
audit/w1c-ws-correctness) because W1-C will rebase onto it (overlap onorderbook.pyagreed via IRC during execution).Closes #198
Refs #224