Skip to content

batch_create_v2 / batch_cancel_v2 missed the v2.4 #223 bytes-fast-path optimization #329

Description

@TexasCoding

Problem

v2.4 #223 added _post_json / _delete_with_body_json helpers for batch endpoints with the rationale: "skips the dict-walk pass httpx's json= does. Use this when a Pydantic model already produced the wire bytes via model_dump_json(...).encode()." V1 batch_create / batch_cancel were rewired through _build_batch_create_body / _build_batch_cancel_body returning bytes and POSTed via _post_json / _delete_with_body_json.

The corresponding V2 methods (batch_create_v2, batch_cancel_v2) still call request.model_dump(mode='json') to produce a dict and ship it through _post(json=...) / _delete_with_body(json=...), so every V2 batch pays the Pydantic-walk-to-dict + httpx-walk-to-bytes overhead twice — exactly the cost #223 measured and removed for V1. With up to 100 orders × ~6-10 Decimal fields per V2 entry, that's hundreds of redundant dict-walks per batch.

The V2 surface is the spec-recommended path for new code (V1 deprecation "no earlier than May 6, 2026" per README); leaving the perf regression on the recommended path is a regression of #223's own contract.

Evidence

  • kalshi/resources/orders.py:534-539 — V1 batch_create already on the fast path:
body = _build_batch_create_body(request, orders)
data = self._post_json(
    "/portfolio/orders/batched", content=body, extra_headers=extra_headers
)
  • kalshi/resources/orders.py:866-886 — V2 batch_create / batch_cancel still on the slow path:
def batch_create_v2(
    self, *, request: BatchCreateOrdersV2Request, extra_headers: dict[str, str] | None = None
) -> BatchCreateOrdersV2Response:
    self._require_auth()
    body = request.model_dump(exclude_none=True, by_alias=True, mode="json")
    data = self._post(
        "/portfolio/events/orders/batched", json=body, extra_headers=extra_headers
    )
    ...

def batch_cancel_v2(...):
    ...
    body = request.model_dump(exclude_none=True, by_alias=True, mode="json")
    data = self._delete_with_body(
        "/portfolio/events/orders/batched", json=body, extra_headers=extra_headers
    )

Suggested fix

Add _build_batch_create_v2_body / _build_batch_cancel_v2_body helpers next to the existing V1 builders that call request.model_dump_json(exclude_none=True, by_alias=True).encode(). Route batch_create_v2 through _post_json(content=body, ...) and batch_cancel_v2 through _delete_with_body_json(content=body, ...). Mirror the changes on AsyncOrdersResource. Extend scripts/bench_request_hot_path.py (or add a new V2-specific bench) to lock in parity with V1.

Source

Round-3 independent audit (reviewer: resources_api).

Metadata

Metadata

Assignees

No one assigned

    Labels

    performancePerformance / hot-path concernpolishCode-quality and DX improvements; non-functional

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions