Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/perps.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions docs/request-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Expand Down
22 changes: 22 additions & 0 deletions docs/resources/account.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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`
Expand Down
73 changes: 71 additions & 2 deletions docs/resources/communications.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) |
Expand Down Expand Up @@ -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
Expand All @@ -192,3 +257,7 @@ literal strings.
::: kalshi.resources.communications.AsyncQuotesResource
options:
heading_level: 3

::: kalshi.resources.communications.AsyncBlockTradeProposalsResource
options:
heading_level: 3
6 changes: 6 additions & 0 deletions docs/resources/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down