Skip to content

DollarDecimal accepts arbitrary precision and negative values on request price fields #343

Closed

Description

@TexasCoding

Problem

DollarDecimal is shared between request-side price fields (CreateOrderRequest.yes_price / no_price, CreateOrderV2Request.price, AmendOrderRequest.yes_price / no_price, AmendOrderV2Request.price, V2 batch entries) and response-side fields (PnL, fees, settlements) where negative values and sub-tick fractions are legitimate. The validator coerces NaN/Inf away (#270) but applies no precision bound or sign check on the request path.

A caller passing Decimal("0.12345") (5 decimals, finer than the $0.0001 / 4-decimal tick) or Decimal("-0.65") constructs cleanly, the SDK signs the request, and the server rejects with 400. This is not a money-loss bug (server is source of truth) but it's exactly the class of failure the StrictInt / Literal / _reject_bool_int retrofits were rolled out to prevent — fail at construction, not after a round trip.

Evidence

  • kalshi/types.py — DollarDecimal is a bare Annotated[Decimal, BeforeValidator(_coerce_decimal), PlainSerializer(_decimal_to_str)] with no tick or sign constraint.
  • kalshi/models/orders.py:177-184 (V1 price fields, request side):
yes_price: DollarDecimal | None = Field(
    default=None,
    serialization_alias="yes_price_dollars",
)
no_price: DollarDecimal | None = Field(
    default=None,
    serialization_alias="no_price_dollars",
)
  • kalshi/models/orders.py:475 — V2 CreateOrderV2Request.price: DollarDecimal — same bare type, no bound.

Suggested fix

Introduce an annotated request-only alias (e.g. OrderPrice = Annotated[DollarDecimal, AfterValidator(_ensure_request_price)]) where _ensure_request_price checks value >= 0 (and, if the desk confirms ticks are uniform $0.0001, asserts value.quantize(Decimal("0.0001"), rounding=ROUND_HALF_EVEN) == value). Apply to CreateOrderRequest.yes_price / no_price, AmendOrderRequest.yes_price / no_price, CreateOrderV2Request.price, AmendOrderV2Request.price, and the V2 batch entries. Leave bare DollarDecimal on PnL/fee/settlement response fields where negatives are legal. If tick alignment is too strict (some markets accept finer precision), keep just the non-negative check.

Source

Round-3 independent audit (reviewer: models_contracts).

Metadata

Metadata

Assignees

No one assigned

    Labels

    polishCode-quality and DX improvements; non-functional

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions