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

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

## 6.0.0 — 2026-07-04

Syncs the upstream OpenAPI/AsyncAPI specs **3.22.0 → 3.23.0** (#463). The
headline change is **breaking**: Kalshi removed the multivariate lookup-history
endpoint from the spec, so the SDK removes the corresponding method and model.
Every other change is additive and backward-compatible.

### Removed (breaking)

- **`lookup_history()`** on `client.multivariate_collections` (sync + async) and
the **`LookupPoint`** model (no longer exported from `kalshi` /
`kalshi.models`). Upstream removed the backing `GET
/multivariate_event_collections/{collection_ticker}/lookup` operation
(`GetMultivariateEventCollectionLookupHistory`) and the `LookupPoint` schema in
3.23.0 — the endpoint now 404s. The `lookup_tickers()` sibling (the `PUT` on the
same path) is unaffected.

### Added

- **`ApiKey.subaccount`**, **`CreateApiKeyRequest.subaccount`**,
**`GenerateApiKeyRequest.subaccount`** (`int | None`) — when set, restricts the
API key to a single subaccount. `api_keys.create()` / `generate()` (sync +
async) accept a `subaccount` kwarg that threads into the body. The request
models bound it to the spec's `0-63` range (`ge=0, le=63`) client-side; the
response model stays permissive.
- **`ApplySubaccountTransferRequest.exchange_index`** (`int | None`) — exchange
shard to apply the transfer on (spec `ExchangeIndex`; defaults to 0).
- **`SubaccountTransfer`** additive fields: `exchange_index` and `transfer_type`
(`"cash"`/`"position"`, both required), plus the position-transfer-only
`market_ticker` / `side` (`"yes"`/`"no"`) / `count` / `price_cents`.
- **`MarginPosition.is_portfolio`** and **`MarginRiskPosition.is_portfolio`**
(`bool`, required) — true when the position is hedged within a portfolio.
- **`MarginOrder.order_reason`** (`"liquidation"`/`"take_profit_stop_loss"`,
optional) — reason for a system-generated order.
- **`MarketLifecyclePayload.price_ranges`** (WS `market_lifecycle_v2`) — valid
price bands emitted alongside `price_level_structure`.
- **`subaccounts.transfer_position(...)`** (sync + async) — new
`POST /portfolio/subaccounts/positions/transfer` endpoint (spec 3.23.0): moves
an open **position** (contracts) between subaccounts, distinct from the
cash-only `transfer()`. Returns `ApplySubaccountPositionTransferResponse`
(`position_transfer_id`). New models `ApplySubaccountPositionTransferRequest` /
`ApplySubaccountPositionTransferResponse` are exported from `kalshi` /
`kalshi.models`.
- **`subaccounts.create(exchange_index=...)`** — `POST /portfolio/subaccounts`
gained an optional `CreateSubaccountRequest` body (spec 3.23.0); `create()`
now accepts an optional `exchange_index` to target a specific exchange shard.
New `CreateSubaccountRequest` model exported from `kalshi` / `kalshi.models`.

### Fixed

- **Defensive optional-ization of fields 3.23.0 removed/relaxed.** The drift
suite is spec→SDK only, so a field the spec drops but the SDK still *requires*
is a latent `ValidationError` if the server stops emitting it. Three such
fields are now `... | None = None`:
- `Market.fractional_trading_enabled` (removed from spec),
- `MarketPosition.resting_orders_count` (removed from spec),
- `MarginPosition.margin_used` (relaxed from required to optional).

## 5.0.1 — 2026-06-27

Spec-drift catch-up (#460). Kalshi added fields to the `ExchangeStatus` schema
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ tests/

## API Reference

- OpenAPI spec: https://docs.kalshi.com/openapi.yaml (v3.22.0, 101 operations)
- OpenAPI spec: https://docs.kalshi.com/openapi.yaml (v3.23.0, 101 operations)
- AsyncAPI spec: https://docs.kalshi.com/asyncapi.yaml (13 WebSocket channels)
- Base URL: https://api.elections.kalshi.com/trade-api/v2
- Demo URL: https://demo-api.kalshi.co/trade-api/v2
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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 (101 operations across 19 resources, OpenAPI v3.22.0) and WebSocket API (12 typed `subscribe_*` channels + 2 escape-hatch).
- **Full coverage** of the Kalshi REST API (101 operations across 19 resources, OpenAPI v3.23.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 (9 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.
Expand Down
21 changes: 21 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@

## Shipped

- **v6.0.0 (2026-07-04)** — OpenAPI sync v3.22.0 → v3.23.0 (#463). **Breaking:**
removed the multivariate lookup-history endpoint (`lookup_history()` +
`LookupPoint`), deleted upstream. Additive: subaccount-scoped API keys
(`api_keys.create/generate(subaccount=)`), subaccount **position** transfers
(`subaccounts.transfer_position()` + models), `subaccounts.create(exchange_index=)`,
and new response fields on `SubaccountTransfer`, `MarginOrder`,
`MarginPosition` / `MarginRiskPosition` (`is_portfolio`), and the WS
`market_lifecycle_v2` payload (`price_ranges`). Defensive optional-ization of
three fields the spec removed/relaxed (#464, #465 folded in).
- **v5.0.1 (2026-06-27)** — spec-drift catch-up (#460): additive `ExchangeStatus`
fields (`intra_exchange_transfers_active`, `exchange_index_statuses` +
`ExchangeIndexStatus`).
- **v5.0.0 (2026-06-26)** — OpenAPI sync v3.21.0 → v3.22.0 (#454, #458).
**Breaking:** removed the V1 order-write endpoints/models (use the `*_v2`
family); added RFQ-scoped quote actions.
- **v4.2.0 (2026-06-19)** — in-place spec-drift reconciliation (#451): additive
fields the live OpenAPI/AsyncAPI specs gained after 4.1.0.
- **v4.1.0 (2026-06-14)** — OpenAPI sync v3.20.0 → v3.21.0: additive new query
params, response fields, and four new endpoints.
- **v4.0.0 (2026-06-09)** — spec-drift reconciliation against OpenAPI 3.20.0
(#443); one breaking change on the Self-Clearing-Member (Klear) surface.
- **v3.3.0 (2026-06-06)** — complete **FIX protocol** subsystem (FIXT.1.1 /
FIX50SP2) for both products: `FixClient` (prediction) + `MarginFixClient`
(margin) over a hand-rolled async session engine, with five session types
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
A professional, spec-first Python SDK for the [Kalshi](https://kalshi.com) prediction
markets API.

- **Full REST coverage** — 101 operations across 19 resources (OpenAPI v3.22.0),
- **Full REST coverage** — 101 operations across 19 resources (OpenAPI v3.23.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`
Expand Down
42 changes: 33 additions & 9 deletions docs/migration.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# Migration

## v5 → v6.0.0

**Breaking: multivariate lookup-history is gone.** Kalshi removed the
`GET /multivariate_event_collections/{ticker}/lookup` endpoint
(`GetMultivariateEventCollectionLookupHistory`) and the `LookupPoint` schema from
the OpenAPI spec in 3.23.0, so the SDK removed the matching method and model.

### Removed

| Removed (v5) | Replacement |
| --------------------------------------------------- | ----------- |
| `multivariate_collections.lookup_history(...)` (sync + async) | none — the endpoint was deleted upstream |
| `LookupPoint` model (exported from `kalshi` / `kalshi.models`) | none |

Everything else in 6.0.0 is additive and needs no code changes:

- New optional/required response fields on `ApiKey`, `SubaccountTransfer`,
`MarginPosition`, `MarginRiskPosition`, `MarginOrder`, and the WS
`market_lifecycle_v2` payload.
- New `subaccounts.transfer_position(...)` method (`POST
/portfolio/subaccounts/positions/transfer`) for moving open positions between
subaccounts.
- `subaccounts.create()` gained an optional `exchange_index` argument.

See the [changelog](../CHANGELOG.md) for the full list.

## v4 → v5.0.0

**Breaking: the V1 order-write API is gone.** Kalshi removed the V1 order
Expand Down Expand Up @@ -402,16 +428,14 @@ for position in client.portfolio.positions_all():

### Multivariate endpoints emit `DeprecationWarning`

Per #269, `multivariate.lookup_tickers`, `multivariate.lookup_history`,
and `multivariate.create_market` (sync + async) carry
`@typing_extensions.deprecated` decorators citing the spec's "should
not be used for new integrations" guidance. Use RFQs instead. The
endpoints still work; calls just emit a `DeprecationWarning` on first
use.
Per #269, `multivariate.lookup_tickers` and `multivariate.create_market`
(sync + async) carry `@typing_extensions.deprecated` decorators citing
the spec's "should not be used for new integrations" guidance. Use RFQs
instead. The endpoints still work; calls just emit a `DeprecationWarning`
on first use.

`multivariate.lookup_history` also now validates `lookback_seconds`
locally against the spec enum `{10, 60, 300, 3600}` and raises
`ValueError` for any other value before the round trip.
(`multivariate.lookup_history`, also deprecated here in #269, was removed
entirely in 6.0.0 — see the [v5 → v6.0.0](#v5--v600) section above.)

### `orders.list(event_ticker=...)` accepts lists

Expand Down
6 changes: 6 additions & 0 deletions docs/perps.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ Orders create/cancel/decrease/amend are POSTs/DELETEs and are **never retried**.
- **Subaccounts** (since the v3.21.0 spec sync) — `MarginPosition` (the
`portfolio.positions()` rows) carries a **required** `subaccount` (`int`)
identifying which subaccount holds the position.
- **Portfolio hedging** (since the v3.23.0 spec sync) — `MarginPosition` and
`MarginRiskPosition` carry a **required** `is_portfolio` (`bool`), `True` when
the position is hedged within a portfolio so its margin is computed at the
portfolio level. `MarginOrder` gained an optional `order_reason`
(`"liquidation"` / `"take_profit_stop_loss"`) identifying system-generated
orders.

## 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 @@ -68,8 +68,12 @@ every exception class.

::: kalshi.models.order_groups.UpdateOrderGroupLimitRequest

::: kalshi.models.subaccounts.CreateSubaccountRequest

::: kalshi.models.subaccounts.ApplySubaccountTransferRequest

::: kalshi.models.subaccounts.ApplySubaccountPositionTransferRequest

::: kalshi.models.subaccounts.UpdateSubaccountNettingRequest

## Response models
Expand Down
10 changes: 8 additions & 2 deletions docs/resources/api-keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ Auth required throughout (you need an existing key to manage keys).
| Method | Endpoint |
|---|---|
| `list()` | `GET /api_keys` |
| `create(*, name, public_key, scopes=None)` | `POST /api_keys` |
| `generate(*, name, scopes=None)` | `POST /api_keys/generate` |
| `create(*, name, public_key, scopes=None, subaccount=None)` | `POST /api_keys` |
| `generate(*, name, scopes=None, subaccount=None)` | `POST /api_keys/generate` |
| `delete(api_key)` | `DELETE /api_keys/{api_key}` |

!!! note "Subaccount-scoped keys (spec v3.23.0)"
Pass `subaccount=<0-63>` to `create()` / `generate()` to restrict a key to a
single subaccount. Omit it (the default) for an account-wide key. The value is
bounded to `0-63` client-side; `ApiKey.subaccount` echoes it back on `list()`
(`None` for account-wide keys).

## List

```python
Expand Down
23 changes: 8 additions & 15 deletions docs/resources/multivariate.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ Public listing, auth-required minting. Attribute name on the client:
`multivariate_collections`.

!!! warning "Deprecated methods"
`create_market()`, `lookup_tickers()`, and `lookup_history()` are
deprecated — "This endpoint predates RFQs and should not be used for new
integrations." Calling them emits a `DeprecationWarning`. Use the
`create_market()` and `lookup_tickers()` are deprecated — "This endpoint
predates RFQs and should not be used for new integrations." Calling them
emits a `DeprecationWarning`. Use the
[Communications (RFQ/Quote)](communications.md) surface instead. `list()` /
`list_all()` / `get()` remain supported.

!!! danger "Removed in 6.0.0"
`lookup_history()` and the `LookupPoint` model were removed — Kalshi deleted
the backing `GET /multivariate_event_collections/{ticker}/lookup` endpoint
from the spec in 3.23.0.

## Quick reference

| Method | Endpoint | Auth |
Expand All @@ -23,7 +28,6 @@ Public listing, auth-required minting. Attribute name on the client:
| `get(collection_ticker)` | `GET /multivariate_event_collections/{ticker}` | no |
| `create_market(collection_ticker, *, selected_markets, with_market_payload=False)` | `POST /multivariate_event_collections/{ticker}` | yes |
| `lookup_tickers(collection_ticker, *, selected_markets)` | `PUT /multivariate_event_collections/{ticker}/lookup` | yes |
| `lookup_history(collection_ticker, *, lookback_seconds)` | `GET /multivariate_event_collections/{ticker}/lookup` (with `lookback_seconds` query param) | no |

## List collections

Expand Down Expand Up @@ -77,17 +81,6 @@ if resp.market is not None:
print(resp.market.yes_bid, resp.market.yes_ask)
```

## Recent lookup history

```python
history = client.multivariate_collections.lookup_history(
"KXWEATHER-SPORTS-COMBO",
lookback_seconds=3600,
)
for point in history:
print(point.last_queried_ts, point.market_ticker, point.event_ticker)
```

## Reference

::: kalshi.resources.multivariate.MultivariateCollectionsResource
Expand Down
42 changes: 33 additions & 9 deletions docs/resources/subaccounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ primary; `1`–`63` are numbered extras. Auth required throughout.

| Method | Endpoint |
|---|---|
| `create()` | `POST /portfolio/subaccounts` |
| `create(*, exchange_index=None)` | `POST /portfolio/subaccounts` |
| `transfer(*, client_transfer_id, from_subaccount, to_subaccount, amount_cents)` | `POST /portfolio/subaccounts/transfer` |
| `transfer_position(*, client_transfer_id, from_subaccount, to_subaccount, market_ticker, side, count, price_cents)` | `POST /portfolio/subaccounts/positions/transfer` |
| `list_balances()` | `GET /portfolio/subaccounts/balances` |
| `list_transfers(*, cursor=None, limit=None)` | `GET /portfolio/subaccounts/transfers` |
| `list_all_transfers(*, limit=None, max_pages=None)` | walks `list_transfers` |
Expand All @@ -20,31 +21,54 @@ primary; `1`–`63` are numbered extras. Auth required throughout.
```python
resp = client.subaccounts.create()
print(resp.subaccount_number)

# Or target a specific exchange shard (spec v3.23.0; defaults to 0):
resp = client.subaccounts.create(exchange_index=0)
```

The body is intentionally empty — the SDK sends `json={}` to force a JSON
content-type so demo doesn't reject the call.
With no `exchange_index` the SDK sends `json={}` (an empty
`CreateSubaccountRequest`) to force a JSON content-type so demo doesn't reject
the call.

## Transfer between subaccounts
## Transfer cash between subaccounts

`amount_cents` is **integer cents**. `client_transfer_id` is a UUID — the
idempotency key for the transfer:
idempotency key for the transfer. `transfer()` returns `None` (the endpoint's
response body is empty); list transfers to reconcile:

```python
import uuid

resp = client.subaccounts.transfer(
client.subaccounts.transfer(
client_transfer_id=uuid.uuid4(), # or str
from_subaccount=0, # primary
to_subaccount=1,
amount_cents=500, # $5.00
)
print(resp.transfer_id)
```

`client_transfer_id` accepts a `UUID` or a `str`. On a network failure, retry
with the same id; the server dedupes. List transfers to reconcile (see
below).
with the same id; the server dedupes.

## Transfer a position between subaccounts

Spec v3.23.0 added `transfer_position()` for moving open contracts (not cash)
between subaccounts. Unlike `transfer()`, it returns a
`position_transfer_id`. `price_cents` (0–100) sets the cost basis on the
destination:

```python
resp = client.subaccounts.transfer_position(
client_transfer_id=uuid.uuid4(), # or str
from_subaccount=0,
to_subaccount=1,
market_ticker="KXBTC-25DEC31-B100000",
side="yes", # "yes" | "no"
count=10, # contracts (> 0)
price_cents=55, # per-contract cents, 0–100
)
print(resp.position_transfer_id)
```

## List balances

Expand Down
10 changes: 7 additions & 3 deletions kalshi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
Announcement,
ApiKey,
ApiUsageLevelGrant,
ApplySubaccountPositionTransferRequest,
ApplySubaccountPositionTransferResponse,
ApplySubaccountTransferRequest,
AssociatedEvent,
Balance,
Expand Down Expand Up @@ -65,6 +67,7 @@
CreateQuoteResponse,
CreateRFQRequest,
CreateRFQResponse,
CreateSubaccountRequest,
CreateSubaccountResponse,
DailySchedule,
DecreaseOrderV2Request,
Expand Down Expand Up @@ -109,7 +112,6 @@
IncentiveProgramTypeLiteral,
IndexedBalance,
LiveData,
LookupPoint,
LookupTickersForMarketInMultivariateEventCollectionRequest,
LookupTickersResponse,
MaintenanceWindow,
Expand Down Expand Up @@ -201,6 +203,8 @@
"Announcement",
"ApiKey",
"ApiUsageLevelGrant",
"ApplySubaccountPositionTransferRequest",
"ApplySubaccountPositionTransferResponse",
"ApplySubaccountTransferRequest",
"AssociatedEvent",
"AsyncBlockTradeProposalsResource",
Expand Down Expand Up @@ -236,6 +240,7 @@
"CreateQuoteResponse",
"CreateRFQRequest",
"CreateRFQResponse",
"CreateSubaccountRequest",
"CreateSubaccountResponse",
"DailySchedule",
"DecreaseOrderV2Request",
Expand Down Expand Up @@ -306,7 +311,6 @@
"KlearClient",
"KlearConfig",
"LiveData",
"LookupPoint",
"LookupTickersForMarketInMultivariateEventCollectionRequest",
"LookupTickersResponse",
"MaintenanceWindow",
Expand Down Expand Up @@ -375,4 +379,4 @@
"Withdrawal",
]

__version__ = "5.0.1"
__version__ = "6.0.0"
Loading