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
61 changes: 61 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,67 @@

All notable changes to kalshi-sdk will be documented in this file.

## 11.0.0 — 2026-08-09

Reconciles upstream OpenAPI **3.27.0** content drift, AsyncAPI trade payload
fields, perps OpenAPI, and a Klear (SCM) obligation reshape after nightly
contract failures (Closes #499). **Breaking** for Klear singular-obligation /
flat settlement-estimate callers and for constructors of a few response models
that gained required fields.

### Removed (breaking)

- **`klear.margin.active_obligation()`** (sync + async) and model
`GetActiveMarginObligationResponse`. Upstream deleted
`GET /margin/active_obligation`. Use
**`klear.margin.active_obligations()`** (`GET /margin/active_obligations`).
- **`klear.margin.settlement_estimate()`** (sync + async) and model
`GetSettlementEstimateResponse`. Upstream deleted
`GET /margin/settlement_estimate`. Use
**`klear.margin.settlement_estimate_by_asset_class()`**.

### Added

- **Klear** paged obligation detail endpoints (limit max 1000; also `*_all`
paginators):
- `settlement_details(obligation_id)` /
`settlement_details_all(obligation_id)`
- `maintenance_margin_details(obligation_id)` /
`maintenance_margin_details_all(obligation_id)`
- `funding_payments(obligation_id)` /
`funding_payments_all(obligation_id)`
Models: `FundingPaymentDetail`,
`GetObligationSettlementDetailsResponse`,
`GetObligationMaintenanceMarginDetailsResponse`,
`GetObligationFundingPaymentsResponse`.
- **`ExchangeIndexStatus.description`** (`str`, required) — human-readable
shard label on `exchange.status()`.
- **Perps** `MarginMarket.exchange_index` (`int`, required) — order-group
shard membership.
- **WS** `TradePayload.is_block_trade` (`bool`, required) — off-book block
trade flag on the public trade channel.

### Changed

- **`SettlementDetail.position_quantity_fp`** (`FixedPointCount`, required) —
signed fixed-point position at settlement.
- **`ObligationEntry`**: required `funding_payments`; optional
`settlement_details_truncated` / `maintenance_margin_details_truncated` /
`funding_payments_truncated` when inline arrays hit the 1000-row cap (page
via the new detail endpoints).

### Spec notes

- Core OpenAPI `info.version` still **3.27.0** (paths 93; 104 operations;
103 mapped). Still unimplemented on the core client:
`POST /portfolio/intra_exchange_instance_transfer` (use
`PerpsClient.transfers.transfer_instance()`).
- AsyncAPI: channels still 14; `tradePayload.msg.is_block_trade` required.
- Perps OpenAPI: `MarginMarket.exchange_index` required.
- Perps SCM OpenAPI: paths 14→15 (removed singular active obligation + flat
settlement estimate; added three paged obligation-detail routes). Still
unimplemented: `GET /margin/large_trader_positions` (surveillance).

## 10.0.0 — 2026-08-06

Reconciles upstream OpenAPI **3.27.0** content drift plus AsyncAPI / perps
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ tests/
## API Reference

- OpenAPI spec: https://docs.kalshi.com/openapi.yaml (v3.27.0, 104 operations; 103 mapped in the core SDK — `POST /portfolio/intra_exchange_instance_transfer` is implemented on `PerpsClient.transfers.transfer_instance` and left unimplemented on the core client)
- AsyncAPI spec: https://docs.kalshi.com/asyncapi.yaml (13 WebSocket channels)
- AsyncAPI spec: https://docs.kalshi.com/asyncapi.yaml (14 WebSocket channels; 11 typed `subscribe_*` + escape-hatch)
- Base URL: https://api.elections.kalshi.com/trade-api/v2
- Demo URL: https://demo-api.kalshi.co/trade-api/v2
- Auth: RSA-PSS / SHA256 / MGF1(SHA256) / salt_length=DIGEST_LENGTH / base64
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ A professional, spec-first Python SDK for the [Kalshi](https://kalshi.com) predi
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Type checked: mypy strict](https://img.shields.io/badge/mypy-strict-blue.svg)](https://mypy.readthedocs.io/)

- **Full coverage** of the Kalshi REST API (102 operations across 19 resources, OpenAPI v3.27.0) and WebSocket API (12 typed `subscribe_*` channels + 2 escape-hatch).
- **Perps (margin) API**: standalone `PerpsClient` / `AsyncPerpsClient` + `PerpsWebSocket` for the perpetual-futures exchange (34 REST operations, 6 WS channels), plus a `KlearClient` for the Self-Clearing-Member "Klear" settlement API (11 operations). See [Perps (margin) trading](#perps-margin-trading).
- **Full coverage** of the Kalshi REST API (103 mapped of 104 operations across 19 resources, OpenAPI v3.27.0) and WebSocket API (11 typed `subscribe_*` channels + 2 escape-hatch).
- **Perps (margin) API**: standalone `PerpsClient` / `AsyncPerpsClient` + `PerpsWebSocket` for the perpetual-futures exchange (35 REST operations, 6 WS channels), plus a `KlearClient` for the Self-Clearing-Member "Klear" settlement API (16 operations). See [Perps (margin) trading](#perps-margin-trading).
- **FIX protocol**: an async-first FIX engine (FIXT.1.1 / FIX50SP2) for both products — order-entry, drop-copy, market-data, post-trade (prediction), and RFQ (prediction) sessions (plus order-group management over the order-entry session) with typed message models, sequence recovery, and order-book / settlement reassembly. `from kalshi import FixClient` / `MarginFixClient`. See [FIX protocol](#fix-protocol-low-latency-trading).
- **V2 event-market orders**: `create_v2` / `amend_v2` / `decrease_v2` / `cancel_v2` plus batched variants on `/portfolio/events/orders/*` — the only order-write surface.
- **Funding & cost introspection**: `portfolio.deposits()`, `portfolio.withdrawals()`, `account.endpoint_costs()`.
Expand Down Expand Up @@ -179,7 +179,7 @@ Available channels (11 typed + 2 escape-hatch). Eleven have dedicated
`subscribe_communications`, `subscribe_cfbenchmarks_value`. The
AsyncAPI-declared `control_frames` and `root` channels are reachable
through the generic `subscribe(channel, ...)` escape hatch. See
[docs/websockets.md](docs/websockets.md#the-12-channels) for the full
[docs/websockets.md](docs/websockets.md#the-11-channels) for the full
channel table.

## Perps (margin) trading
Expand Down
7 changes: 7 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## Shipped

- **v11.0.0 (2026-08-09)** — Spec-drift reconcile (#499). **Breaking:** removed
Klear `active_obligation()` / `settlement_estimate()` after upstream deleted
the singular endpoints. Additive: Klear paged obligation detail endpoints
(`settlement_details` / `maintenance_margin_details` / `funding_payments`),
`ExchangeIndexStatus.description`, perps `MarginMarket.exchange_index`, WS
`TradePayload.is_block_trade`. Required fields on `SettlementDetail` /
`ObligationEntry` for position quantity + funding payments.
- **v10.0.0 (2026-08-06)** — Spec-drift reconcile (#496 / #497). **Breaking:**
removed `multivariate_collections.lookup_tickers()` and WS
`subscribe_multivariate()` after upstream deleted the REST lookup endpoint
Expand Down
16 changes: 8 additions & 8 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@
A professional, spec-first Python SDK for the [Kalshi](https://kalshi.com) prediction
markets API.

- **Full REST coverage** — 103 operations across 19 resources (OpenAPI v3.27.0),
every kwarg drift-tested against the spec.
- **Full REST coverage** — 103 mapped of 104 operations across 19 resources
(OpenAPI v3.27.0), every kwarg drift-tested against the spec.
- **V2 event-market orders** — new `create_v2` / `amend_v2` / `decrease_v2` /
`cancel_v2` family on `/portfolio/events/orders/*`. Legacy `/portfolio/orders`
keeps working; deprecation no earlier than May 6, 2026.
- **Funding + cost introspection** — `portfolio.deposits()`,
`portfolio.withdrawals()`, `portfolio.intra_exchange_transfers()`,
`account.endpoint_costs()`.
- **Full WebSocket coverage** — 11 channels with sequence-gap detection, automatic
reconnection (with resubscribe-window frame stashing for high-volume channels),
backpressure strategies, and an in-memory orderbook builder. Async-only —
access via `AsyncKalshiClient.ws`.
- **Full WebSocket coverage** — 11 typed channels with sequence-gap detection,
automatic reconnection (with resubscribe-window frame stashing for high-volume
channels), backpressure strategies, and an in-memory orderbook builder.
Async-only — access via `AsyncKalshiClient.ws`.
- **Perps (margin) API** — standalone `PerpsClient` / `AsyncPerpsClient` +
`PerpsWebSocket` for the perpetual-futures exchange (34 REST operations, 6 WS
`PerpsWebSocket` for the perpetual-futures exchange (35 REST operations, 6 WS
channels), and a `KlearClient` for the Self-Clearing-Member settlement API
(11 operations, Bearer token auth). See [Perps](perps.md).
(16 operations, Bearer token auth). See [Perps](perps.md).
- **FIX protocol** — a hand-rolled, async-first FIX engine (FIXT.1.1 / FIX50SP2)
for both products: order-entry, drop-copy, market-data, post-trade (prediction),
and RFQ (prediction) sessions — plus order-group management over the order-entry
Expand Down
49 changes: 49 additions & 0 deletions docs/migration.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,54 @@
# Migration

## v10.0 → v11.0.0

Reconciles upstream OpenAPI **3.27.0** content, AsyncAPI trade payload fields,
perps `MarginMarket.exchange_index`, and a Klear (SCM) obligation reshape
(Closes #499). **Breaking** for callers of the removed Klear singular endpoints
and for code that constructs a few response models without their new required
fields.

### Removed

- **`klear.margin.active_obligation()`** (sync + async) and
`GetActiveMarginObligationResponse`. Upstream deleted
`GET /margin/active_obligation`.
- **`klear.margin.settlement_estimate()`** (sync + async) and
`GetSettlementEstimateResponse`. Upstream deleted
`GET /margin/settlement_estimate`.

```python
# Before (gone):
# resp = klear.margin.active_obligation()
# if resp.obligation is not None: ...
# est = klear.margin.settlement_estimate()

# After:
resp = klear.margin.active_obligations()
for ob in resp.obligations:
...
est = klear.margin.settlement_estimate_by_asset_class()
```

### Added (non-breaking for most callers)

- **Klear** paged obligation detail rows when inline arrays on
`ObligationEntry` are truncated (`*_truncated` flags):
`settlement_details` / `maintenance_margin_details` /
`funding_payments` (+ `*_all` paginators). Limit max 1000.
- **`ExchangeIndexStatus.description`** (required string).
- **Perps** `MarginMarket.exchange_index` (required int).
- **WS** `TradePayload.is_block_trade` (required bool).

### Response model field changes

- **`SettlementDetail.position_quantity_fp`** — required fixed-point count.
- **`ObligationEntry.funding_payments`** — required list (may be empty);
optional `*_truncated` flags.

See the [changelog](https://github.com/TexasCoding/kalshi-python-sdk/blob/main/CHANGELOG.md)
for the full list.

## v9.0 → v10.0.0

Reconciles upstream OpenAPI **3.27.0** content (paths still 92; +2 GET
Expand Down
36 changes: 24 additions & 12 deletions docs/perps.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ Orders create/cancel/decrease/amend are POSTs/DELETEs and are **never retried**.
(`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.
- **Exchange shard** (SDK v11.0.0) — `MarginMarket.exchange_index` (`int`) is
required; order groups may only reference markets that share their
`exchange_index`.
- **Trading schedule** (since the v3.25.0 spec sync / SDK v7.1.0) — `MarginMarket`
carries a **required** `schedule` key typed `MarginMarketSchedule | None`.
`None` means the market trades 24/7. When present, `MarginMarketSchedule` has
Expand Down Expand Up @@ -193,20 +196,29 @@ with KlearClient(admin_user_id="...", access_token="...", demo=True) as klear:
Credentials can also come from the environment via `KlearClient.from_env()` (reads
`KALSHI_KLEAR_ADMIN_USER_ID` / `KALSHI_KLEAR_ACCESS_TOKEN`).

New in spec v3.24.0: `klear.margin.active_obligations()` returns all
currently-active settlement obligations (the plural sibling of the single-obligation
`active_obligation()`), and `klear.margin.settlement_estimate_by_asset_class()`
returns next-settlement estimates keyed by asset class.
`klear.margin.active_obligations()` returns all currently-active settlement
obligations. `klear.margin.settlement_estimate_by_asset_class()` returns
next-settlement estimates keyed by asset class. (The singular
`active_obligation()` / `settlement_estimate()` endpoints were removed upstream
in the v11.0.0 reconcile.)

Settlement-estimate responses also expose optional
`omitted_subtrader_count` (`int | None`, SDK v7.3.0) on
`GetSettlementEstimateResponse` and each `AssetClassSettlementEstimate` — the
number of subtraders left out of `subtrader_breakdowns` (their amounts remain
in `user_breakdown`). SDK v9.0.0 adds optional `group_breakdowns` /
`omitted_group_count` for netted subtrader groups, and optional
`margin_group_id` on `MaintenanceMarginDetail`.
When an `ObligationEntry` inline detail array is capped at 1000 rows, the
matching `*_truncated` flag is set; page the full set via:

Subtrader groups (SDK v9.0.0) — margined as one netted portfolio:
```python
for row in klear.margin.settlement_details_all(ob.id):
...
for row in klear.margin.maintenance_margin_details_all(ob.id):
...
for row in klear.margin.funding_payments_all(ob.id):
...
```

Each `AssetClassSettlementEstimate` exposes optional
`omitted_subtrader_count` / `group_breakdowns` / `omitted_group_count`.
`MaintenanceMarginDetail` may include `margin_group_id`.

Subtrader groups — margined as one netted portfolio:

```python
groups = klear.margin.list_subtrader_groups()
Expand Down
4 changes: 4 additions & 0 deletions docs/resources/exchange.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ Top-level exchange status, schedule, announcements, and your account's
```python
status = client.exchange.status()
print(status.exchange_active, status.trading_active)
for shard in status.exchange_index_statuses:
print(shard.exchange_index, shard.description, shard.trading_active)
```

Use this as a liveness check before placing orders.
`ExchangeIndexStatus.description` is a required human-readable shard label
(SDK v11.0.0).

## Exchange schedule

Expand Down
11 changes: 6 additions & 5 deletions docs/websockets.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# WebSocket

The SDK ships an async-only WebSocket client, `KalshiWebSocket`, that covers
all 12 Kalshi channels. It handles RSA-PSS auth on the upgrade handshake,
the Kalshi market-data WebSocket surface (11 typed channels plus the generic
escape hatch). It handles RSA-PSS auth on the upgrade handshake,
per-subscription sequence-gap detection, automatic reconnection with
re-subscription, and a configurable backpressure strategy on each per-channel
queue.
Expand Down Expand Up @@ -267,7 +268,7 @@ when the queue fills depends on `OverflowStrategy`:

| Strategy | Behavior | Default for |
|---|---|---|
| `DROP_OLDEST` | Ring-buffer: evict oldest, keep newest. | `ticker`, `trade`, `fill`, `user_orders`, `market_positions`, `market_lifecycle`, `multivariate`, `multivariate_lifecycle`, `communications` |
| `DROP_OLDEST` | Ring-buffer: evict oldest, keep newest. | `ticker`, `trade`, `fill`, `user_orders`, `market_positions`, `market_lifecycle`, `multivariate_lifecycle`, `communications` |
| `ERROR` | Raise `KalshiBackpressureError` from the producer side. | `orderbook_delta`, `order_group_updates` |

The choice tracks state semantics: latest-wins channels (`ticker`) survive a
Expand Down Expand Up @@ -415,8 +416,8 @@ REST. There's no token in the URL, no signed message after open. The signature
is re-computed on every reconnect attempt.

Public channels (ticker, trade, orderbook_delta, market_lifecycle,
multivariate, multivariate_lifecycle) work without auth — pass
`auth=None` if you don't need private channels.
multivariate_lifecycle) work without auth — pass `auth=None` if you don't
need private channels.

## Performance

Expand Down Expand Up @@ -460,7 +461,7 @@ silently corrupting book state.

| Strategy | Use for | Why |
|---|---|---|
| `DROP_OLDEST` | Read-only / coalesced feeds: `ticker`, `trade`, `market_lifecycle`, `multivariate*`, `user_orders` | Newest sample is the one that matters; an evicted old frame is recoverable from the next one. |
| `DROP_OLDEST` | Read-only / coalesced feeds: `ticker`, `trade`, `market_lifecycle`, `multivariate_lifecycle`, `user_orders` | Newest sample is the one that matters; an evicted old frame is recoverable from the next one. |
| `ERROR` | Stateful, sequenced feeds: `orderbook_delta`, `order_group_updates` | A dropped delta corrupts derived state (the reconstructed book / order-group tracking). Surface the backpressure to the consumer rather than continuing on corrupted state. |

`ERROR` is fatal — the recv loop broadcasts sentinels and exits when it fires
Expand Down
2 changes: 1 addition & 1 deletion kalshi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -381,4 +381,4 @@
"Withdrawal",
]

__version__ = "10.0.0"
__version__ = "11.0.0"
24 changes: 16 additions & 8 deletions kalshi/_contract_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -816,19 +816,31 @@ class ContractEntry:
sdk_model="kalshi.perps.klear.models.margin.MaintenanceMarginDetail",
spec_schema="MaintenanceMarginDetail",
),
ContractEntry(
sdk_model="kalshi.perps.klear.models.margin.FundingPaymentDetail",
spec_schema="FundingPaymentDetail",
),
ContractEntry(
sdk_model="kalshi.perps.klear.models.margin.ObligationEntry",
spec_schema="ObligationEntry",
notes="Folds spec allOf[ObligationInfo, inline] into one model",
),
ContractEntry(
sdk_model="kalshi.perps.klear.models.margin.GetActiveMarginObligationResponse",
spec_schema="GetActiveMarginObligationResponse",
),
ContractEntry(
sdk_model="kalshi.perps.klear.models.margin.GetActiveMarginObligationsResponse",
spec_schema="GetActiveMarginObligationsResponse",
),
ContractEntry(
sdk_model="kalshi.perps.klear.models.margin.GetObligationSettlementDetailsResponse",
spec_schema="GetObligationSettlementDetailsResponse",
),
ContractEntry(
sdk_model="kalshi.perps.klear.models.margin.GetObligationMaintenanceMarginDetailsResponse",
spec_schema="GetObligationMaintenanceMarginDetailsResponse",
),
ContractEntry(
sdk_model="kalshi.perps.klear.models.margin.GetObligationFundingPaymentsResponse",
spec_schema="GetObligationFundingPaymentsResponse",
),
ContractEntry(
sdk_model="kalshi.perps.klear.models.margin.MarketSettlementEstimate",
spec_schema="MarketSettlementEstimate",
Expand All @@ -837,10 +849,6 @@ class ContractEntry:
sdk_model="kalshi.perps.klear.models.margin.SettlementEstimate",
spec_schema="SettlementEstimate",
),
ContractEntry(
sdk_model="kalshi.perps.klear.models.margin.GetSettlementEstimateResponse",
spec_schema="GetSettlementEstimateResponse",
),
ContractEntry(
sdk_model="kalshi.perps.klear.models.margin.AssetClassSettlementEstimate",
spec_schema="AssetClassSettlementEstimate",
Expand Down
1 change: 1 addition & 0 deletions kalshi/models/exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ExchangeIndexStatus(BaseModel):
"""Operational status of a single exchange index (shard)."""

exchange_index: int
description: str
exchange_active: bool
trading_active: bool
intra_exchange_transfers_active: bool
Expand Down
Loading
Loading