From b007f2d217ac5d4e1d09a10a9f1acdf842cd4f80 Mon Sep 17 00:00:00 2001 From: Jeff West Date: Fri, 22 May 2026 06:59:48 -0500 Subject: [PATCH 1/2] docs(resources): sync/async docstring parity + CreateOrderV2 type fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sweep async `*_all` iterator-factory methods across orders, markets, portfolio, and communications so every one carries the same one-line marker docstring ("Returns an async iterator — use ``async for``."). Previously this marker was present on a subset (`list_all`, `list_trades_all`, `deposits_all`, `withdrawals_all`) and missing on others (`fills_all`, `settlements_all`, `list_all_rfqs`, `list_all_quotes`), letting users primed by `await client.orders.list` trip over `TypeError: object AsyncIterator can't be used in 'await'` without any in-source hint. Pull the `event_positions` caveat and the FCM/403/demo-parity note from `PortfolioResource` over to their `AsyncPortfolioResource` counterparts so Sphinx / IDE hover renders the same guidance on both surfaces. Replace the phantom `FixedPointDollars` reference in the `CreateOrderV2Request` docstring with `DollarDecimal` (the actual type the field is annotated with). Closes #334, #337, #338, #339 --- kalshi/models/orders.py | 2 +- kalshi/resources/communications.py | 2 ++ kalshi/resources/markets.py | 2 +- kalshi/resources/orders.py | 3 ++- kalshi/resources/portfolio.py | 17 +++++++++++++++-- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/kalshi/models/orders.py b/kalshi/models/orders.py index 00e44dd6..dcef055e 100644 --- a/kalshi/models/orders.py +++ b/kalshi/models/orders.py @@ -464,7 +464,7 @@ class CreateOrderV2Request(BaseModel): overload at the resource-method boundary (see model_only V2 surface). - ``client_order_id`` is **required** in V2, unlike V1 where it is optional. The server uses it for idempotency in V2. - - Price is a single ``price: FixedPointDollars`` field rather than the + - Price is a single ``price: DollarDecimal`` field rather than the paired ``yes_price`` / ``no_price`` from V1. """ diff --git a/kalshi/resources/communications.py b/kalshi/resources/communications.py index f137f77e..41f47667 100644 --- a/kalshi/resources/communications.py +++ b/kalshi/resources/communications.py @@ -566,6 +566,7 @@ def list_all_rfqs( max_pages: int | None = None, extra_headers: dict[str, str] | None = None, ) -> AsyncIterator[RFQ]: + """Returns an async iterator — use ``async for``.""" # Plain `def` so _require_auth + _validate_max_pages run at call time. self._require_auth() _validate_max_pages(max_pages) @@ -703,6 +704,7 @@ def list_all_quotes( max_pages: int | None = None, extra_headers: dict[str, str] | None = None, ) -> AsyncIterator[Quote]: + """Returns an async iterator — use ``async for``.""" self._require_auth() _require_quote_filter( quote_creator_user_id, diff --git a/kalshi/resources/markets.py b/kalshi/resources/markets.py index 16f97cb3..6608bc8e 100644 --- a/kalshi/resources/markets.py +++ b/kalshi/resources/markets.py @@ -488,7 +488,7 @@ def list_all( max_pages: int | None = None, extra_headers: dict[str, str] | None = None, ) -> AsyncIterator[Market]: - """Non-async method that returns an async iterator for direct use with `async for`.""" + """Returns an async iterator — use ``async for``.""" _validate_max_pages(max_pages) params = _list_markets_params( status=status, diff --git a/kalshi/resources/orders.py b/kalshi/resources/orders.py index 5615af4d..bb24b2cc 100644 --- a/kalshi/resources/orders.py +++ b/kalshi/resources/orders.py @@ -1051,7 +1051,7 @@ def list_all( max_pages: int | None = None, extra_headers: dict[str, str] | None = None, ) -> AsyncIterator[Order]: - """Non-async method that returns an async iterator for direct use with `async for`.""" + """Returns an async iterator — use ``async for``.""" self._require_auth() _validate_max_pages(max_pages) params = _list_orders_params( @@ -1162,6 +1162,7 @@ def fills_all( max_pages: int | None = None, extra_headers: dict[str, str] | None = None, ) -> AsyncIterator[Fill]: + """Returns an async iterator — use ``async for``.""" self._require_auth() _validate_max_pages(max_pages) params = _fills_params( diff --git a/kalshi/resources/portfolio.py b/kalshi/resources/portfolio.py index 574fbb66..389885ab 100644 --- a/kalshi/resources/portfolio.py +++ b/kalshi/resources/portfolio.py @@ -335,7 +335,15 @@ def positions_all( max_pages: int | None = None, extra_headers: dict[str, str] | None = None, ) -> AsyncIterator[MarketPosition]: - """Async counterpart of :meth:`PortfolioResource.positions_all`. Use ``async for``.""" + """Async counterpart of :meth:`PortfolioResource.positions_all`. Use ``async for``. + + The endpoint response also carries ``event_positions`` (aggregate + roll-ups over the same underlying markets); those are *not* + surfaced here because page boundaries cut the aggregate + arbitrarily and concatenating across pages would not recompute a + meaningful event-level total. Callers that need the event view + should iterate :meth:`positions` page-by-page. + """ self._require_auth() _validate_max_pages(max_pages) params = _positions_params( @@ -397,6 +405,7 @@ def settlements_all( max_pages: int | None = None, extra_headers: dict[str, str] | None = None, ) -> AsyncIterator[Settlement]: + """Returns an async iterator — use ``async for``.""" self._require_auth() _validate_max_pages(max_pages) params = _settlements_params( @@ -420,7 +429,11 @@ def settlements_all( async def total_resting_order_value( self, *, extra_headers: dict[str, str] | None = None ) -> TotalRestingOrderValue: - """Total value of resting orders in cents. FCM-members only.""" + """Total value of resting orders in cents. FCM-members only. + + Non-FCM accounts receive 403; demo mirrors prod on this route + per Path B audit (2026-04-18). + """ self._require_auth() data = await self._get( "/portfolio/summary/total_resting_order_value", extra_headers=extra_headers From 83a227fba0289ef0dfaed472840bbc2773352b51 Mon Sep 17 00:00:00 2001 From: Jeff West Date: Fri, 22 May 2026 07:07:41 -0500 Subject: [PATCH 2/2] polish(docs): restore Mirrors :meth:settlements_all cross-ref on async positions_all --- kalshi/resources/portfolio.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kalshi/resources/portfolio.py b/kalshi/resources/portfolio.py index 389885ab..6f4194a1 100644 --- a/kalshi/resources/portfolio.py +++ b/kalshi/resources/portfolio.py @@ -337,12 +337,12 @@ def positions_all( ) -> AsyncIterator[MarketPosition]: """Async counterpart of :meth:`PortfolioResource.positions_all`. Use ``async for``. - The endpoint response also carries ``event_positions`` (aggregate - roll-ups over the same underlying markets); those are *not* - surfaced here because page boundaries cut the aggregate - arbitrarily and concatenating across pages would not recompute a - meaningful event-level total. Callers that need the event view - should iterate :meth:`positions` page-by-page. + Mirrors :meth:`settlements_all`. The endpoint response also carries + ``event_positions`` (aggregate roll-ups over the same underlying + markets); those are *not* surfaced here because page boundaries cut + the aggregate arbitrarily and concatenating across pages would not + recompute a meaningful event-level total. Callers that need the + event view should iterate :meth:`positions` page-by-page. """ self._require_auth() _validate_max_pages(max_pages)