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

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

## 13.0.0 — 2026-08-28

Reconciles upstream OpenAPI **3.28.0 → 3.29.0**, plus matching perps, Klear,
and AsyncAPI updates, after nightly contract failures (Closes #507, Closes #508).
**Breaking** for constructors of `Fill`, `MarketPosition`, `Settlement`,
and the WS `FillPayload` / `UserOrdersPayload` that omit the new required
`exchange_index`.

### Changed (breaking)

- **`Fill.exchange_index`**, **`MarketPosition.exchange_index`**, and
**`Settlement.exchange_index`** (`int`, required) — which exchange shard
produced the row. Live list callers are unaffected; tests/mocks that
construct these models must pass the shard (typically `0`).

### Added

- **`live_data.weather(city, *, from_ts, to, last_sec, detailed)`** —
`GET /live_data/weather/{city}`. `from_ts` is the spec `from` query
(unix milliseconds); named to avoid the Python keyword.
- **`portfolio.target_balance_allocation()`** /
**`portfolio.set_target_balance_allocation(...)`** —
get/replace per-shard sweepable-balance targets.
- **`orders.cancel_all_v2(*, subaccount)`** —
`DELETE /portfolio/events/orders` (up to 10,000 resting orders; 204).
- Optional **`GetApiKeysResponse.api_key_region_expiration_ts`**.
- Optional **`IncentiveProgram.max_reward_per_account`**.
- **Perps** exit triggers on a position (sync + async):
- `portfolio.cross_exit_triggers` / `set_cross_exit_trigger` /
`cancel_cross_exit_triggers`
- `portfolio.update_cross_exit_trigger` / `cancel_cross_exit_trigger`
- `portfolio.isolated_exit_triggers` / `set_isolated_exit_trigger` /
`cancel_isolated_exit_triggers`
- **Perps** `orders.cancel_all(*, subaccount)` —
`DELETE /margin/orders` (up to 10,000 resting margin orders; 204).
- **Perps FCM** optional `asset_class` on `risk_controls` /
`update_risk_controls` / `delete_risk_controls` (mutually exclusive
with `market_ticker`).
- **Klear** `margin.settlement_prices(asset_class, settlement_time)` and
`margin.estimate_maintenance_margin(...)`.
- **Klear** `MarginReport.snapshot_ts` (optional) and report types
`maintenance_margin` / `maintenance_margin_aggregate`.
- **WS** required `exchange_index` on `FillPayload` and `UserOrdersPayload`.

### Spec notes

- Core OpenAPI `info.version` **3.29.0** (paths 95; 108 operations; 107
mapped). Still unimplemented on the core client:
`POST /portfolio/intra_exchange_instance_transfer` (use
`PerpsClient.transfers.transfer_instance()`).
- AsyncAPI still 14 channels; fill/user-order payloads gained required
`exchange_index`.
- Perps OpenAPI: 38 → 47 operations (exit triggers + cancel-all).
- Perps SCM OpenAPI: 17 → 19 operations. Still unimplemented:
`GET /margin/large_trader_positions` (surveillance).

## 12.0.0 — 2026-08-16

Reconciles upstream OpenAPI **3.27.0 → 3.28.0**, plus additive perps FCM
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,13 @@ tests/

## Testing

- pytest + pytest-asyncio + respx (httpx mock); ~1920 tests across unit + contract drift suites.
- pytest + pytest-asyncio + respx (httpx mock); ~4330 tests across unit + contract drift suites.
- Use `respx.mock` for HTTP mocking. Generate test RSA keys via conftest.py fixtures.
- New function → write a test. Bug fix → write a regression test. New error path → write a test that triggers it.

## API Reference

- OpenAPI spec: https://docs.kalshi.com/openapi.yaml (v3.28.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)
- OpenAPI spec: https://docs.kalshi.com/openapi.yaml (v3.29.0, 108 operations; 107 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 (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
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ 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 (103 mapped of 104 operations across 19 resources, OpenAPI v3.28.0) and WebSocket API (11 typed `subscribe_*` channels + 2 escape-hatch).
- **Perps (margin) API**: standalone `PerpsClient` / `AsyncPerpsClient` + `PerpsWebSocket` for the perpetual-futures exchange (38 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).
- **Full coverage** of the Kalshi REST API (107 mapped of 108 operations across 19 resources, OpenAPI v3.29.0) and WebSocket API (11 typed `subscribe_*` channels + 2 escape-hatch).
- **Perps (margin) API**: standalone `PerpsClient` / `AsyncPerpsClient` + `PerpsWebSocket` for the perpetual-futures exchange (47 REST operations, 6 WS channels), plus a `KlearClient` for the Self-Clearing-Member "Klear" settlement API (18 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.
- **V2 event-market orders**: `create_v2` / `amend_v2` / `decrease_v2` / `cancel_v2` / `cancel_all_v2` plus batched variants on `/portfolio/events/orders/*` — the only order-write surface.
- **Funding & cost introspection**: `portfolio.deposits()`, `portfolio.withdrawals()`, `account.endpoint_costs()`.
- **Sync and async** clients sharing one transport — no thread-pool wrapping.
- **Typed end-to-end**: Pydantic v2 models, `mypy --strict` clean, ships `py.typed`. `Literal` types on fixed-enum kwargs.
Expand Down
6 changes: 6 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Shipped

- **v13.0.0 (2026-08-28)** — Spec-drift reconcile (#507 / #508). OpenAPI
3.28.0 → 3.29.0. **Breaking:** `Fill` / `MarketPosition` / `Settlement`
require `exchange_index`. Additive: weather index, target-balance
allocation, cancel-all (event + margin), perps exit triggers, FCM
`asset_class` IM caps, Klear settlement prices + maintenance-margin
estimate.
- **v12.0.0 (2026-08-16)** — Spec-drift reconcile (#503). OpenAPI
3.27.0 → 3.28.0. **Breaking:** `TotalRestingOrderValue` requires
`resting_order_value_breakdown`. Additive: `exchange_index` query on
Expand Down
10 changes: 5 additions & 5 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
A professional, spec-first Python SDK for the [Kalshi](https://kalshi.com) prediction
markets API.

- **Full REST coverage** — 103 mapped of 104 operations across 19 resources
(OpenAPI v3.28.0), every kwarg drift-tested against the spec.
- **Full REST coverage** — 107 mapped of 108 operations across 19 resources
(OpenAPI v3.29.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`
`cancel_v2` / `cancel_all_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()`,
Expand All @@ -16,9 +16,9 @@ markets API.
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 (38 REST operations, 6 WS)
`PerpsWebSocket` for the perpetual-futures exchange (47 REST operations, 6 WS
channels), and a `KlearClient` for the Self-Clearing-Member settlement API
(16 operations, Bearer token auth). See [Perps](perps.md).
(18 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
38 changes: 38 additions & 0 deletions docs/migration.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,43 @@
# Migration

## v12.0 → v13.0.0

Reconciles upstream OpenAPI **3.28.0 → 3.29.0**, plus matching perps, Klear,
and AsyncAPI updates (Closes #507, Closes #508). **Breaking** only for code
that constructs `Fill`, `MarketPosition`, or `Settlement` without
`exchange_index`.

### Response model field changes

- **`Fill.exchange_index`**, **`MarketPosition.exchange_index`**,
**`Settlement.exchange_index`** — required `int`. Live
`orders.fills()` / `portfolio.positions()` / `portfolio.settlements()`
callers are unaffected.
- **WS** `FillPayload.exchange_index` and `UserOrdersPayload.exchange_index`
— required `int` (same constructor impact on fixtures).

```python
# Before (constructors / test fixtures):
# Fill(..., outcome_side="yes", book_side="bid")

# After:
Fill(..., outcome_side="yes", book_side="bid", exchange_index=0)
```

### Added (non-breaking)

- `live_data.weather(city, *, from_ts, to, last_sec, detailed)`
- `portfolio.target_balance_allocation()` /
`set_target_balance_allocation(...)`
- `orders.cancel_all_v2(*, subaccount)`
- Perps exit triggers on `perps.portfolio.*`
- Perps `orders.cancel_all(*, subaccount)`
- Perps FCM `asset_class=` on IM-cap methods
- Klear `settlement_prices()` / `estimate_maintenance_margin()`

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

## v11.0 → v12.0.0

Reconciles upstream OpenAPI **3.27.0 → 3.28.0**, plus additive perps FCM
Expand Down
29 changes: 27 additions & 2 deletions docs/perps.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ async with AsyncPerpsClient.from_env(demo=True) as perps:
|---|---|
| `exchange` | `status()`, `enabled()` (per-member access gate), `risk_parameters()` |
| `markets` | `list()`, `get()`, `orderbook()`, `candlesticks()` |
| `orders` | `create()`, `get()`, `list()` / `list_all()`, `cancel()`, `decrease()`, `amend()` |
| `orders` | `create()`, `get()`, `list()` / `list_all()`, `cancel()`, `cancel_all()`, `decrease()`, `amend()` |
| `order_groups` | `list()`, `get()`, `create()`, `delete()`, `reset()`, `trigger()`, `update_limit()` |
| `portfolio` | `positions()`, `fills()` / `fills_all()`, `trades()` / `trades_all()` |
| `portfolio` | `positions()`, `fills()` / `fills_all()`, `trades()` / `trades_all()`, cross/isolated exit triggers |
| `margin` | `balance()`, `risk()`, `notional_risk_limit()`, `fee_tiers()`, `api_limits()` |
| `funding` | `rate_estimate()`, `historical_rates()`, `history()` |
| `transfers` | `transfer_instance()`, `create_subaccount()`, `transfer_subaccount()` |
Expand Down Expand Up @@ -92,6 +92,26 @@ perps.fcm.update_risk_controls(
market_ticker="BTC-PERP",
)
perps.fcm.delete_risk_controls(subtrader_id="user_desk1", market_ticker="BTC-PERP")
# asset_class is mutually exclusive with market_ticker
perps.fcm.risk_controls(subtrader_id="user_desk1", asset_class="Crypto")
```

Exit triggers (stop-loss / take-profit / trailing) sit on a position slot:

```python
from decimal import Decimal
from kalshi.perps.models.portfolio import SetCrossExitTriggerRequest

listed = perps.portfolio.cross_exit_triggers("BTC-PERP")
trig = perps.portfolio.set_cross_exit_trigger(
"BTC-PERP",
request=SetCrossExitTriggerRequest(
kind="bracket",
stop_loss_price=Decimal("50000.0000"),
take_profit_price=Decimal("70000.0000"),
),
)
perps.portfolio.cancel_cross_exit_trigger("BTC-PERP", trig.id)
```

## Value types & timestamps
Expand Down Expand Up @@ -218,6 +238,11 @@ next-settlement estimates keyed by asset class. (The singular
`active_obligation()` / `settlement_estimate()` endpoints were removed upstream
in the v11.0.0 reconcile.)

`klear.margin.settlement_prices(asset_class="Crypto", settlement_time=...)`
returns a ticker → centicents map at a settlement cycle.
`klear.margin.estimate_maintenance_margin(asset_class="Crypto", positions=[...])`
margins a hypothetical portfolio.

When an `ObligationEntry` inline detail array is capped at 1000 rows, the
matching `*_truncated` flag is set; page the full set via:

Expand Down
15 changes: 15 additions & 0 deletions docs/resources/live-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Public — no auth required.
| `get_event(event_ticker, *, range=None)` | `GET /live_data/events/{event_ticker}` |
| `batch(milestone_ids, *, include_player_stats=None)` | `GET /live_data/batch` |
| `game_stats(milestone_id)` | `GET /live_data/milestone/{milestone_id}/game_stats` |
| `weather(city, *, from_ts, to, last_sec, detailed)` | `GET /live_data/weather/{city}` |
| `get_typed(milestone_type, milestone_id)` | `GET /live_data/{type}/milestone/{milestone_id}` (legacy) |

## Get one milestone's live data
Expand Down Expand Up @@ -67,6 +68,20 @@ else:
Other milestone types return `pbp=None`. Each period's `events` is a list of
loose dicts (no fixed play schema upstream).

## Weather index

```python
idx = client.live_data.weather("miami", last_sec=3600, detailed=True)
print(idx.city, idx.units, idx.config_version)
for point in idx.timeseries:
print(point.t, point.status, point.v)
```

`from_ts` is the spec `from` query (unix milliseconds, inclusive). Named
`from_ts` to avoid the Python keyword; the wire key is still `from`.
`last_sec` is mutually exclusive with `from_ts`/`to` per spec. `detailed=True`
attaches per-station audit readings on every point.

## Legacy `get_typed`

```python
Expand Down
1 change: 1 addition & 0 deletions docs/resources/orders.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Reads stay on `/portfolio/orders/*`.
| `create_v2(*, request)` | `POST /portfolio/events/orders` | **never** — see [Retries & idempotency](../retries.md) |
| `batch_create_v2(*, request)` | `POST /portfolio/events/orders/batched` | never |
| `cancel_v2(order_id, *, subaccount, exchange_index, market_ticker)` | `DELETE /portfolio/events/orders/{order_id}` | never |
| `cancel_all_v2(*, subaccount)` | `DELETE /portfolio/events/orders` | never |
| `batch_cancel_v2(*, request)` | `DELETE /portfolio/events/orders/batched` | never |
| `amend_v2(order_id, *, request, subaccount)` | `POST /portfolio/events/orders/{order_id}/amend` | never |
| `decrease_v2(order_id, *, request, subaccount)` | `POST /portfolio/events/orders/{order_id}/decrease` | never |
Expand Down
21 changes: 21 additions & 0 deletions docs/resources/portfolio.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Auth required throughout.
| `withdrawals(*, limit, cursor)` / `withdrawals_all(*, limit, max_pages)` | `GET /portfolio/withdrawals` |
| `intra_exchange_transfers(...)` / `intra_exchange_transfers_all(...)` | `GET /portfolio/intra_exchange_instance_transfers` |
| `get_intra_exchange_transfer(transfer_id)` | `GET /portfolio/intra_exchange_instance_transfers/{transfer_id}` |
| `target_balance_allocation()` | `GET /portfolio/target_balance_allocation` |
| `set_target_balance_allocation(*, allocations)` | `POST /portfolio/target_balance_allocation` |

`balance()`, `positions()` / `positions_all()`, `settlements()` /
`settlements_all()`, and `fills()` / `fills_all()` all take an optional
Expand Down Expand Up @@ -220,6 +222,25 @@ print(t.status, t.created_ts)
`IntraExchangeInstanceTransfer.amount` is a fixed-point dollar
`DollarDecimal` (not the integer centicents used on the POST create body).

## Target balance allocation

Per-shard sweepable-balance targets. POST is never retried.

```python
from kalshi import TargetBalanceAllocationInput

current = client.portfolio.target_balance_allocation()
client.portfolio.set_target_balance_allocation(
allocations=[TargetBalanceAllocationInput(exchange_index=0, percent=100)]
)
```

Percents are integers 0–100. At most 101 allocation rows.

`Fill`, `MarketPosition`, and `Settlement` each carry a required
`exchange_index` as of OpenAPI 3.29.0 (SDK v13.0.0). Live list callers
are unaffected; constructors/fixtures must pass it.

## Position fields

`MarketPosition` and `EventPosition` use the standard `_dollars` / `_fp`
Expand Down
16 changes: 15 additions & 1 deletion kalshi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@
GetSubaccountBalancesResponse,
GetSubaccountNettingResponse,
GetTagsForSeriesCategoriesResponse,
GetTargetBalanceAllocationResponse,
GetWeatherIndexResponse,
HistoricalCutoff,
IncentiveProgram,
IncentiveProgramStatusLiteral,
Expand Down Expand Up @@ -152,6 +154,7 @@
SelfTradePreventionTypeLiteral,
Series,
SeriesFeeChange,
SetTargetBalanceAllocationRequest,
Settlement,
SettlementSource,
SettlementStatusLiteral,
Expand All @@ -161,6 +164,8 @@
SubaccountBalance,
SubaccountNettingConfig,
SubaccountTransfer,
TargetBalanceAllocation,
TargetBalanceAllocationInput,
TickerPair,
TimeInForceLiteral,
TotalRestingOrderValue,
Expand All @@ -169,6 +174,8 @@
UpdateSubaccountNettingRequest,
UserDataTimestamp,
UserFilterLiteral,
WeatherIndexPoint,
WeatherIndexStationReading,
WeeklySchedule,
Withdrawal,
)
Expand Down Expand Up @@ -285,6 +292,8 @@
"GetSubaccountBalancesResponse",
"GetSubaccountNettingResponse",
"GetTagsForSeriesCategoriesResponse",
"GetTargetBalanceAllocationResponse",
"GetWeatherIndexResponse",
"HistoricalCutoff",
"IncentiveProgram",
"IncentiveProgramStatusLiteral",
Expand Down Expand Up @@ -359,6 +368,7 @@
"SelfTradePreventionTypeLiteral",
"Series",
"SeriesFeeChange",
"SetTargetBalanceAllocationRequest",
"Settlement",
"SettlementSource",
"SettlementStatusLiteral",
Expand All @@ -369,6 +379,8 @@
"SubaccountBalance",
"SubaccountNettingConfig",
"SubaccountTransfer",
"TargetBalanceAllocation",
"TargetBalanceAllocationInput",
"TickerPair",
"TimeInForceLiteral",
"TotalRestingOrderValue",
Expand All @@ -377,8 +389,10 @@
"UpdateSubaccountNettingRequest",
"UserDataTimestamp",
"UserFilterLiteral",
"WeatherIndexPoint",
"WeatherIndexStationReading",
"WeeklySchedule",
"Withdrawal",
]

__version__ = "12.0.0"
__version__ = "13.0.0"
Loading
Loading