diff --git a/docs/perps.md b/docs/perps.md index c69080f..adc22bd 100644 --- a/docs/perps.md +++ b/docs/perps.md @@ -89,6 +89,16 @@ Orders create/cancel/decrease/amend are POSTs/DELETEs and are **never retried**. record). `MarginMarket.leverage_estimates` maps notional position sizes (`"1000"`, `"10000"`, …) to `MultiplierDecimal` leverage, or `None` when margin config / price data is unavailable. +- **Mark prices** (since the v3.21.0 spec sync) — `MarginMarket` carries three + optional reference-price objects: `settlement_mark_price`, + `liquidation_mark_price`, and `reference_price`, each a nested `TickerPrice` + (`TickerPrice | None`). `TickerPrice` is a new model with `price` + (`DollarDecimal`, a `FixedPointDollars` USD string) and `ts_ms` (the source + timestamp in epoch **milliseconds**, `int`). All three are absent from the spec + `required` list, so they are `None` when the upstream price is unavailable. +- **Subaccounts** (since the v3.21.0 spec sync) — `MarginPosition` (the + `portfolio.positions()` rows) carries a **required** `subaccount` (`int`) + identifying which subaccount holds the position. ## Funding mechanics diff --git a/docs/reference.md b/docs/reference.md index d916f57..2c6fafc 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -56,6 +56,10 @@ every exception class. ::: kalshi.models.communications.AcceptQuoteRequest +::: kalshi.models.communications.ProposeBlockTradeRequest + +::: kalshi.models.communications.AcceptBlockTradeProposalRequest + ::: kalshi.models.multivariate.CreateMarketInMultivariateEventCollectionRequest ::: kalshi.models.multivariate.LookupTickersForMarketInMultivariateEventCollectionRequest diff --git a/docs/request-models.md b/docs/request-models.md index e03109f..c38b52f 100644 --- a/docs/request-models.md +++ b/docs/request-models.md @@ -74,6 +74,8 @@ OpenAPI spec. | `client.communications.create_rfq` | `CreateRFQRequest` | | `client.communications.create_quote` | `CreateQuoteRequest` | | `client.communications.accept_quote` | `AcceptQuoteRequest` | +| `client.communications.block_trade_proposals.create` | `ProposeBlockTradeRequest` | +| `client.communications.block_trade_proposals.accept` | `AcceptBlockTradeProposalRequest` | | `client.multivariate_collections.create_market` | `CreateMarketInMultivariateEventCollectionRequest` | | `client.multivariate_collections.lookup_tickers` | `LookupTickersForMarketInMultivariateEventCollectionRequest` | | `client.order_groups.create` | `CreateOrderGroupRequest` | diff --git a/docs/resources/account.md b/docs/resources/account.md index 7faaead..a1539d2 100644 --- a/docs/resources/account.md +++ b/docs/resources/account.md @@ -10,6 +10,7 @@ Auth required. |---|---| | `limits()` | `GET /account/limits` | | `endpoint_costs()` | `GET /account/endpoint_costs` | +| `volume_progress()` | `GET /account/api_usage_level/volume_progress` | | `upgrade()` | `POST /account/api_usage_level/upgrade` | ## Read tier limits @@ -49,6 +50,27 @@ Endpoints not present in `endpoint_costs` use `default_cost`. Batch endpoints typically appear here with a per-item multiplier (e.g. `POST /portfolio/orders/batched` costs ~10 tokens per order in the batch). +## Volume progress + +New in v4.1.0. `volume_progress()` (`GET /account/api_usage_level/volume_progress`) +returns the latest cron-computed trading-volume snapshots toward the +volume-based API usage tiers for the predictions (`event_contract`) lane. + +```python +progress = client.account.volume_progress() +for snapshot in progress.volume_progress: + print(snapshot.computed_ts, snapshot.trailing_30d_volume) + for goal in snapshot.goals: + # earn = volume needed to earn the level; keep = volume to retain it. + print(goal.level, goal.earn_volume_goal, goal.keep_volume_goal) +``` + +`AccountVolumeProgress.volume_progress` is a list of +`AccountApiUsageLevelVolumeProgress` snapshots. `computed_ts` is the Unix +second at which the snapshot was computed; `trailing_30d_volume` and each +goal's `earn_volume_goal` / `keep_volume_goal` are fixed-point contract counts +(`Decimal`). + ## API usage-level grants New in v4.0.0. `AccountApiLimits.grants` is a list of `ApiUsageLevelGrant` diff --git a/docs/resources/communications.md b/docs/resources/communications.md index 27a0680..0f57ee6 100644 --- a/docs/resources/communications.md +++ b/docs/resources/communications.md @@ -27,8 +27,11 @@ Auth required throughout. | `quotes.get(quote_id)` | `GET /communications/quotes/{quote_id}` | | `quotes.create(...)` | `POST /communications/quotes` | | `quotes.delete(quote_id)` | `DELETE /communications/quotes/{quote_id}` | -| `quotes.accept(quote_id, *, accepted_side)` | `POST /communications/quotes/{quote_id}/accept` | -| `quotes.confirm(quote_id)` | `POST /communications/quotes/{quote_id}/confirm` | +| `quotes.accept(quote_id, *, accepted_side)` | `PUT /communications/quotes/{quote_id}/accept` | +| `quotes.confirm(quote_id)` | `PUT /communications/quotes/{quote_id}/confirm` | +| `block_trade_proposals.list(...)` / `block_trade_proposals.list_all(...)` | `GET /communications/block-trade-proposals` | +| `block_trade_proposals.create(...)` | `POST /communications/block-trade-proposals` | +| `block_trade_proposals.accept(block_trade_proposal_id, *, subtrader_id=None, subaccount=None)` | `POST /communications/block-trade-proposals/{block_trade_proposal_id}/accept` | `get_id()` returns your `participant_id` — the value you'll pass as `quote_creator_user_id` / `rfq_creator_user_id` when filtering lists. It stays @@ -114,6 +117,26 @@ for rfq in client.communications.rfqs.list_all(user_filter="self"): server-side shorthands like `"organization"` in the future without an SDK upgrade. +## Filtering quotes by time (v4.1.0) + +`quotes.list()` / `quotes.list_all()` accept `min_ts` and `max_ts` (Unix +seconds, `int`) to bound results by the quote's last-updated timestamp. They +compose with the required user-id filter: + +```python +import time + +# Quotes you made that were updated in the last hour. +for q in client.communications.quotes.list_all( + user_filter="self", + min_ts=int(time.time()) - 3600, +): + print(q.quote_id, q.updated_ts) +``` + +`min_ts` / `max_ts` are also accepted by the deprecated `list_quotes` / +`list_all_quotes` forwarders. + ## Post-only quotes `quotes.create()` accepts `post_only=True` (added in v2.1.0) to ensure your @@ -148,6 +171,44 @@ client.communications.quotes.create( `open`, `accepted`, `confirmed`, `canceled`. Status filtering accepts these literal strings. +## Block trade proposals (v4.1.0) + +`client.communications.block_trade_proposals` backs the +`/communications/block-trade-proposals` API (OpenAPI 3.21.0) — a bilateral +negotiated trade that both the buyer and seller must accept before it settles. + +```python +# Propose a block trade. The proposer names both sides explicitly. +resp = client.communications.block_trade_proposals.create( + buyer_user_id="user_abc", + seller_user_id="user_xyz", + market_ticker="KXPRES-24-DJT", + price_centi_cents=5600, # plain int, centi-cents (NOT a _dollars price) + centicount=50_000, # plain int, centicounts (NOT a _fp count) + maker_side="yes", + expiration_ts="2026-07-01T00:00:00Z", +) +proposal_id = resp.block_trade_proposal_id + +# List open proposals on a market. +for proposal in client.communications.block_trade_proposals.list_all( + market_ticker="KXPRES-24-DJT", status="open" +): + print(proposal.id, proposal.buyer_accepted, proposal.seller_accepted) + +# Accept (the counterparty side). The body is optional — pass subtrader_id +# or subaccount only if you trade through a sub-trader / subaccount. +client.communications.block_trade_proposals.accept(proposal_id) +``` + +!!! info "Prices are centi-cents, counts are centicounts — plain integers" + Unlike the rest of the SDK, `BlockTradeProposal.price_centi_cents` and + `.centicount` (and the matching `create()` kwargs) are **plain `int`s** in + centi-cents and centicounts respectively — they are *not* + `FixedPointDollars` / `_fp` wire fields, so no `Decimal` conversion or + `_dollars` / `_fp` aliasing applies. `price_centi_cents` and `centicount` + must each be `>= 1`. + ## Migrating from v2.x | v2.x (deprecated) | v3.0.0 (canonical) | @@ -181,6 +242,10 @@ literal strings. options: heading_level: 3 +::: kalshi.resources.communications.BlockTradeProposalsResource + options: + heading_level: 3 + ::: kalshi.resources.communications.AsyncCommunicationsResource options: heading_level: 3 @@ -192,3 +257,7 @@ literal strings. ::: kalshi.resources.communications.AsyncQuotesResource options: heading_level: 3 + +::: kalshi.resources.communications.AsyncBlockTradeProposalsResource + options: + heading_level: 3 diff --git a/docs/resources/events.md b/docs/resources/events.md index ffb59ce..c9c540e 100644 --- a/docs/resources/events.md +++ b/docs/resources/events.md @@ -22,6 +22,7 @@ more markets that share a resolution. page = client.events.list( status="open", # EventStatusLiteral series_ticker="KXPRES", + tickers=["KXPRES-24", "KXSENATE-24"], # filter to specific event tickers (v4.1.0) with_nested_markets=False, with_milestones=False, min_close_ts=1_700_000_000, @@ -36,6 +37,11 @@ for event in page: `EventStatusLiteral` has values `"unopened" | "open" | "closed" | "settled"`. Unlike [`MarketStatusLiteral`](../types.md), there is no `"paused"`. +`tickers` (added in v4.1.0) restricts the result set to specific event +tickers. Pass a `list[str]` or a single comma-separated string; individual +tickers must be non-empty and must not contain commas (the list is sent as +one comma-separated value). + ## Multivariate events `list_multivariate(...)` returns only events that participate in a