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

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

## 15.0.0 — 2026-09-20

Reconciles upstream OpenAPI **3.29.0 → 3.30.0** plus matching perps, Klear,
and AsyncAPI updates after nightly contract failures (Closes #515,
Closes #517). **Breaking** for constructors of `Series`,
`GetTargetBalanceAllocationResponse`, `MarginMarket`, and WS
`QuoteCreatedPayload` / `QuoteAcceptedPayload` that omit newly required
fields.

### Changed (breaking)

- **`Series.categories`** (`list[str]`, required) — discovery categories
the series belongs to. The `category` filter on `series.list()` matches
any entry. Live list callers are unaffected; tests/mocks that construct
`Series` must pass `categories`.
- **`GetTargetBalanceAllocationResponse.resting_margin_reservation`**
(`"max"` / `"sum"`, required). The GET now echoes the reservation policy
previously write-only on `set_target_balance_allocation`.
- **Perps** `MarginMarket.underlying_multiplier` (`str`, required) —
underlying units per contract-size unit.
- **WS** `QuoteCreatedPayload.rfq_creator_id` and
`QuoteAcceptedPayload.rfq_creator_id` (`str`, required). Live stream
callers are unaffected; tests/mocks that construct these payloads must
pass the creator id.

### Added

- **FCM subtrader admin** on `client.fcm`:
`list_subtraders()` / `create_subtrader(subtrader_suffix=...)`,
`blocked_categories(subtrader_id=)` /
`update_blocked_categories(subtrader_id=, category=, blocked=)`,
`event_contract_daily_cap(subtrader_id=)` /
`update_event_contract_daily_cap(subtrader_id=, limit=)` /
`delete_event_contract_daily_cap(subtrader_id=)`.
- Optional **`CreateRFQRequest.target_cost_excludes_fees`** (and the same
field on `RFQ` / `Quote` responses) — sizes quotes against the target
cost as principal only, with taker fees charged on top.
- Optional **`historical.fills` / `fills_all` / `orders` / `orders_all`**
`min_ts=` query (mirrors `historical.trades`).
- Optional **`MarginMarket.product_metadata`**.
- **Klear** `estimate_maintenance_margin_metadata(asset_class=, date=)`,
`funding_estimate_by_asset_class()`, `funding_schedule(asset_class=)`.
- Optional **`id`** on orderbook snapshot envelopes (core + perps) when
the snapshot is a `get_snapshot` reply.
- Perps WS `UpdateSubscriptionAction.get_snapshot` — request a fresh
orderbook snapshot without changing the subscription.

### Changed (non-breaking)

- Perps WS `ListSubscriptionsResponse.id` is optional (spec dropped it
from required). `OkMsg` accepts optional `index_ids` /
`underlying_tickers` on subscribed-indices / subscribed-underlyings
acks.

### Spec notes

- Core OpenAPI `info.version` **3.30.0** (paths 99; 116 operations;
115 mapped). Still unimplemented on the core client:
`POST /portfolio/intra_exchange_instance_transfer`.
- AsyncAPI still 15 channels. 12 typed `subscribe_*` helpers.
- Perps OpenAPI: 48 operations.
- Perps SCM OpenAPI: 24 → 27 operations. Still unimplemented:
`GET /margin/large_trader_positions` (surveillance).

## 14.0.0 — 2026-09-06

Reconciles upstream OpenAPI **3.29.0** content drift plus matching perps,
Expand Down
21 changes: 11 additions & 10 deletions 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.29.0, 109 operations; 108 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.30.0, 116 operations; 115 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 (15 WebSocket channels; 12 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 All @@ -144,24 +144,25 @@ Reference issues from PRs via `Closes #N` so the issue closes on merge.
<!-- gitnexus:start -->
# GitNexus — Code Intelligence

This project is indexed by GitNexus as **kalshi-python-sdk** (13553 symbols, 29700 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
This project is indexed by GitNexus as **kalshi-python-sdk** (12128 symbols, 23376 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.

> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
> Index stale? Run `node .gitnexus/run.cjs analyze` from the project root — it auto-selects an available runner. No `.gitnexus/run.cjs` yet? `npx gitnexus analyze` (npm 11 crash → `npm i -g gitnexus`; #1939).

## Always Do

- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows.
- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
- **MUST run `detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows. For regression review, compare against the default branch: `detect_changes({scope: "compare", base_ref: "main"})`.
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`.
- When exploring unfamiliar code, use `query({search_query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `context({name: "symbolName"})`.
- For security review, `explain({target: "fileOrSymbol"})` lists taint findings (source→sink flows; needs `analyze --pdg`).

## Never Do

- NEVER edit a function, class, or method without first running `gitnexus_impact` on it.
- NEVER edit a function, class, or method without first running `impact` on it.
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope.
- NEVER rename symbols with find-and-replace — use `rename` which understands the call graph.
- NEVER commit changes without running `detect_changes()` to check affected scope.

## Resources

Expand Down
4 changes: 2 additions & 2 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 (108 mapped of 109 operations across 19 resources, OpenAPI v3.29.0) and WebSocket API (12 typed `subscribe_*` channels + escape-hatch).
- **Perps (margin) API**: standalone `PerpsClient` / `AsyncPerpsClient` + `PerpsWebSocket` for the perpetual-futures exchange (48 REST operations, 6 WS channels), plus a `KlearClient` for the Self-Clearing-Member "Klear" settlement API (23 operations). See [Perps (margin) trading](#perps-margin-trading).
- **Full coverage** of the Kalshi REST API (115 mapped of 116 operations across 19 resources, OpenAPI v3.30.0) and WebSocket API (12 typed `subscribe_*` channels + escape-hatch).
- **Perps (margin) API**: standalone `PerpsClient` / `AsyncPerpsClient` + `PerpsWebSocket` for the perpetual-futures exchange (48 REST operations, 6 WS channels), plus a `KlearClient` for the Self-Clearing-Member "Klear" settlement API (26 of 27 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` / `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()`.
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

- **v15.0.0 (2026-09-20)** — Spec-drift reconcile (#515 / #517). OpenAPI
3.29.0 → 3.30.0. **Breaking:** `Series.categories`,
`GetTargetBalanceAllocationResponse.resting_margin_reservation`,
`MarginMarket.underlying_multiplier`, WS quote created/accepted
`rfq_creator_id` required. Additive: FCM subtrader admin, RFQ
`target_cost_excludes_fees`, historical fills/orders `min_ts`, Klear
maintenance-margin metadata + funding estimate/schedule.
- **v14.0.0 (2026-09-06)** — Spec-drift reconcile (#510 / #511). OpenAPI
3.29.0 content + perps/Klear/AsyncAPI. **Breaking:** perps WS
`MarginFillPayload` / `MarginUserOrderPayload` require `order_source`.
Expand Down
6 changes: 3 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
A professional, spec-first Python SDK for the [Kalshi](https://kalshi.com) prediction
markets API.

- **Full REST coverage** — 108 mapped of 109 operations across 19 resources
(OpenAPI v3.29.0), every kwarg drift-tested against the spec.
- **Full REST coverage** — 115 mapped of 116 operations across 19 resources
(OpenAPI v3.30.0), every kwarg drift-tested against the spec.
- **V2 event-market orders** — new `create_v2` / `amend_v2` / `decrease_v2` /
`cancel_v2` / `cancel_all_v2` family on `/portfolio/events/orders/*`. Legacy `/portfolio/orders`
keeps working; deprecation no earlier than May 6, 2026.
Expand All @@ -18,7 +18,7 @@ markets API.
- **Perps (margin) API** — standalone `PerpsClient` / `AsyncPerpsClient` +
`PerpsWebSocket` for the perpetual-futures exchange (48 REST operations, 6 WS
channels), and a `KlearClient` for the Self-Clearing-Member settlement API
(23 operations, Bearer token auth). See [Perps](perps.md).
(26 of 27 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
46 changes: 46 additions & 0 deletions docs/migration.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,51 @@
# Migration

## v14.0 → v15.0.0

Reconciles upstream OpenAPI **3.29.0 → 3.30.0** plus matching perps, Klear,
and AsyncAPI updates (Closes #515, Closes #517). **Breaking** only for code
that constructs `Series`, `GetTargetBalanceAllocationResponse`,
`MarginMarket`, or WS quote created/accepted payloads without the new
required fields.

### Response model field changes

- **`Series.categories`** — required `list[str]`.
- **`GetTargetBalanceAllocationResponse.resting_margin_reservation`** —
required `"max"` / `"sum"`.
- **Perps** `MarginMarket.underlying_multiplier` — required `str`.
- **WS** `QuoteCreatedPayload.rfq_creator_id` and
`QuoteAcceptedPayload.rfq_creator_id` — required `str`.

```python
# Before (constructors / test fixtures):
# Series(..., category="Politics")
# GetTargetBalanceAllocationResponse(allocations=[...])
# MarginMarket(..., contract_size="1.000000")
# QuoteCreatedPayload(..., quote_creator_id="u2")

# After:
Series(..., category="Politics", categories=["Politics"])
GetTargetBalanceAllocationResponse(allocations=[...], resting_margin_reservation="sum")
MarginMarket(..., contract_size="1.000000", underlying_multiplier="1")
QuoteCreatedPayload(..., quote_creator_id="u2", rfq_creator_id="u1")
```

Live list / stream callers are unaffected.

### Added (non-breaking)

- `fcm.list_subtraders()` / `create_subtrader()` / blocked-categories /
event-contract daily cap
- `communications.rfqs.create(..., target_cost_excludes_fees=)`
- `historical.fills(..., min_ts=)` / `orders(..., min_ts=)`
- Klear `estimate_maintenance_margin_metadata` /
`funding_estimate_by_asset_class` / `funding_schedule`
- Perps WS `update_subscription(..., action="get_snapshot")`

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

## v13.0 → v14.0.0

Reconciles upstream OpenAPI **3.29.0** content drift plus matching perps,
Expand Down
8 changes: 8 additions & 0 deletions docs/perps.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,14 @@ returns a ticker → centicents map at a settlement cycle.
margins a hypothetical portfolio. Optional `date=` (YYYY-MM-DD) and
`clearing_type=` (`"FCM"` / `"SelfClearing"`) select the matrix day and
clearing arrangement.
`klear.margin.estimate_maintenance_margin_metadata(asset_class="Crypto", date=...)`
returns the matrices, liquidation configs, and subgroups that feed that
estimate.

`klear.margin.funding_estimate_by_asset_class()` returns the next-funding
estimate keyed by asset class.
`klear.margin.funding_schedule(asset_class="Crypto")` returns a cron
expression evaluated in US Eastern Time.

`klear.margin.member_funding_payments(funding_time=...)` returns the
member's funding payments for one funding execution (distinct from the
Expand Down
1 change: 1 addition & 0 deletions docs/resources/communications.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ rfq = client.communications.rfqs.create(
market_ticker="KXPRES-24-DJT",
contracts=500,
rest_remainder=True,
target_cost_excludes_fees=True, # optional; principal-only target cost
)
print(rfq.rfq.rfq_id)

Expand Down
26 changes: 26 additions & 0 deletions docs/resources/fcm.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ calls come back 401/403. Auth required throughout.
| `orders(*, subtrader_id=None, client_order_ids=None, ...)` | `GET /fcm/orders` |
| `orders_all(*, subtrader_id=None, client_order_ids=None, ...)` | walks `orders` |
| `positions(*, subtrader_id, ...)` | `GET /fcm/positions` |
| `list_subtraders()` | `GET /fcm/subtraders` |
| `create_subtrader(*, subtrader_suffix)` | `POST /fcm/subtraders` |
| `blocked_categories(*, subtrader_id)` | `GET /fcm/subtraders/blocked_categories` |
| `update_blocked_categories(*, subtrader_id, category, blocked)` | `PUT /fcm/subtraders/blocked_categories` |
| `event_contract_daily_cap(*, subtrader_id=None)` | `GET /fcm/subtraders/event_contract_daily_cap` |
| `update_event_contract_daily_cap(*, subtrader_id, limit)` | `PUT /fcm/subtraders/event_contract_daily_cap` |
| `delete_event_contract_daily_cap(*, subtrader_id)` | `DELETE /fcm/subtraders/event_contract_daily_cap` |

## List orders

Expand Down Expand Up @@ -66,6 +73,25 @@ for mp in client.fcm.positions_all(subtrader_id="st_alpha", settlement_status="u
`settlement_status` is the FCM-specific kwarg that does **not** exist on
`portfolio.positions()`.

## Subtrader admin

List and create subtraders, block event categories, and set a daily
event-contract notional cap. POST/PUT/DELETE are never retried.

```python
owned = client.fcm.list_subtraders()
created = client.fcm.create_subtrader(subtrader_suffix="desk1")
client.fcm.update_blocked_categories(
subtrader_id=created.subtrader_id, category="Politics", blocked=True
)
client.fcm.update_event_contract_daily_cap(
subtrader_id=created.subtrader_id, limit="10000.00"
)
```

`create_subtrader` composes the full id server-side as
`{account_id}_{suffix}` (suffix is 1–16 ASCII alphanumeric characters).

## Reference

::: kalshi.resources.fcm.FcmResource
Expand Down
4 changes: 2 additions & 2 deletions docs/resources/historical.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ trades = client.historical.trades(
Both require auth — these are your own trade history.

```python
for fill in client.historical.fills_all(ticker="KXPRES-24-DJT"):
for fill in client.historical.fills_all(ticker="KXPRES-24-DJT", min_ts=1_600_000_000):
print(fill.fill_id, fill.price, fill.count)

for order in client.historical.orders_all(status="executed"):
for order in client.historical.orders_all(ticker="KXPRES-24-DJT", min_ts=1_600_000_000):
print(order.order_id, order.client_order_id)
```

Expand Down
1 change: 1 addition & 0 deletions docs/resources/portfolio.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ Per-shard sweepable-balance targets. POST is never retried.
from kalshi import TargetBalanceAllocationInput

current = client.portfolio.target_balance_allocation()
print(current.resting_margin_reservation) # "max" or "sum"
client.portfolio.set_target_balance_allocation(
allocations=[TargetBalanceAllocationInput(exchange_index=0, percent=100)]
)
Expand Down
2 changes: 1 addition & 1 deletion docs/resources/series.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ all_series = client.series.list(
include_volume=True,
)
for s in all_series:
print(s.series_ticker, s.title)
print(s.ticker, s.title, s.category, s.categories)
```

## Get one series
Expand Down
20 changes: 19 additions & 1 deletion kalshi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
Candlestick,
CreateApiKeyRequest,
CreateApiKeyResponse,
CreateFCMSubtraderRequest,
CreateFCMSubtraderResponse,
CreateMarketInMultivariateEventCollectionRequest,
CreateMarketResponse,
CreateOrderGroupRequest,
Expand Down Expand Up @@ -82,6 +84,7 @@
ExchangeIndexStatus,
ExchangeInstanceLiteral,
ExchangeStatus,
FCMSubtrader,
Fill,
ForecastPercentilesPoint,
GenerateApiKeyRequest,
Expand All @@ -90,6 +93,8 @@
GetBlockTradeProposalsResponse,
GetCommunicationsIDResponse,
GetEventLiveDataResponse,
GetFCMEventContractDailyCapResponse,
GetFCMSubtraderBlockedCategoriesResponse,
GetFiltersBySportsResponse,
GetGameStatsResponse,
GetIncentiveProgramsResponse,
Expand Down Expand Up @@ -117,6 +122,7 @@
IndexedBalance,
IntraExchangeInstanceTransfer,
IntraExchangeInstanceTransferStatusLiteral,
ListFCMSubtradersResponse,
LiveData,
MaintenanceWindow,
Market,
Expand Down Expand Up @@ -172,6 +178,9 @@
TimeInForceLiteral,
TotalRestingOrderValue,
Trade,
UpdateFCMEventContractDailyCapRequest,
UpdateFCMSubtraderBlockedCategoriesRequest,
UpdateFCMSubtraderBlockedCategoriesResponse,
UpdateOrderGroupLimitRequest,
UpdateSubaccountNettingRequest,
UserDataTimestamp,
Expand Down Expand Up @@ -240,6 +249,8 @@
"Candlestick",
"CreateApiKeyRequest",
"CreateApiKeyResponse",
"CreateFCMSubtraderRequest",
"CreateFCMSubtraderResponse",
"CreateMarketInMultivariateEventCollectionRequest",
"CreateMarketResponse",
"CreateOrderGroupRequest",
Expand Down Expand Up @@ -267,6 +278,7 @@
"ExchangeIndexStatus",
"ExchangeInstanceLiteral",
"ExchangeStatus",
"FCMSubtrader",
"Fill",
"FixClient",
"FixConfig",
Expand All @@ -279,6 +291,8 @@
"GetBlockTradeProposalsResponse",
"GetCommunicationsIDResponse",
"GetEventLiveDataResponse",
"GetFCMEventContractDailyCapResponse",
"GetFCMSubtraderBlockedCategoriesResponse",
"GetFiltersBySportsResponse",
"GetGameStatsResponse",
"GetIncentiveProgramsResponse",
Expand Down Expand Up @@ -328,6 +342,7 @@
"KlearAuth",
"KlearClient",
"KlearConfig",
"ListFCMSubtradersResponse",
"LiveData",
"MaintenanceWindow",
"MarginFixClient",
Expand Down Expand Up @@ -391,6 +406,9 @@
"TimeInForceLiteral",
"TotalRestingOrderValue",
"Trade",
"UpdateFCMEventContractDailyCapRequest",
"UpdateFCMSubtraderBlockedCategoriesRequest",
"UpdateFCMSubtraderBlockedCategoriesResponse",
"UpdateOrderGroupLimitRequest",
"UpdateSubaccountNettingRequest",
"UserDataTimestamp",
Expand All @@ -403,4 +421,4 @@
"Withdrawal",
]

__version__ = "14.0.0"
__version__ = "15.0.0"
Loading
Loading