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

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

## 22.5.0 — 2026-10-29

Reconciles nightly spec drift (#561) against core OpenAPI 3.35.0 and the
latest perps specs. Additive only. No breaking changes.

### Added

- **Exit triggers** (sync and async): `client.portfolio.exit_triggers(ticker)`
(`GET /portfolio/positions/{ticker}/exit_trigger`),
`set_exit_trigger` (`PUT`), `cancel_exit_trigger` (`DELETE`), and
`client.orders.create_with_exit_trigger`
(`POST /portfolio/events/orders/with_exit_trigger`). New models
`EventContractExitTrigger`, `SetEventContractExitTriggerRequest`,
`AttachedEventContractExitTrigger`, `EventContractExitSlippage`,
`CreateOrderWithExitTriggerRequest`. PUT/DELETE/POST are never retried.
(Closes #561).
- **Credit history**: `client.portfolio.credit_history()` and
`credit_history_all()` (`GET /portfolio/credit_history`), returning
`CreditHistory` with `amount` as `DollarDecimal`. (Closes #561).
- **Perps market schedules**: `perps.markets.schedules()`
(`GET /margin/markets/schedules`), returning
`MarginMarketScheduleDetails` / `MarginHolidayWindow`. (Closes #561).
- **Perps WebSocket `market_lifecycle_v2`** channel:
`PerpsWebSocket.subscribe_market_lifecycle`, yielding
`MarginMarketLifecycleV2Message`.
- Optional **`subaccount_type`** (`"cross"`, `"isolated"`,
`"auto_rebalance"` or `None`) on `MarginPosition`,
`MarginSubaccountBalance`, `MarginRiskPosition` and
`MarginFundingHistoryEntry`. The spec marks it required; the SDK keeps it
optional (contract exclusion `server_omits_despite_required`) so this
stays a minor release. (Closes #561).

### Internal

- Test config: set `asyncio_default_fixture_loop_scope = "function"` and
fixed fixture decorator order for pytest-asyncio 1.4.1 / pytest 9.2
(#560, closes #559).

## 22.4.0 — 2026-10-26

Reconciles nightly spec drift (#556, #557). Additive on the RFQ-batch
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.34.0, 128 operations; 127 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.35.0, 133 operations; 132 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 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 (127 mapped of 128 operations across 19 resources, OpenAPI v3.34.0) and WebSocket API (12 typed `subscribe_*` channels + escape-hatch).
- **Perps (margin) API**: standalone `PerpsClient` / `AsyncPerpsClient` + `PerpsWebSocket` for the perpetual-futures exchange (50 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).
- **Full coverage** of the Kalshi REST API (132 mapped of 133 operations across 19 resources, OpenAPI v3.35.0) and WebSocket API (12 typed `subscribe_*` channels + escape-hatch).
- **Perps (margin) API**: standalone `PerpsClient` / `AsyncPerpsClient` + `PerpsWebSocket` for the perpetual-futures exchange (54 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 Expand Up @@ -220,7 +220,7 @@ Resource families on the perps client: `exchange`, `markets`, `orders`,
Real-time streaming via `PerpsWebSocket` — `subscribe_ticker` (carries
`funding_rate` + `next_funding_time_ms`), `subscribe_orderbook_delta`,
`subscribe_trade`, `subscribe_fill`, `subscribe_user_orders`,
`subscribe_order_group`.
`subscribe_order_group`, `subscribe_market_lifecycle`.

Prices are `DollarDecimal` (FixedPointDollars, up to 6 decimals); counts are
`FixedPointCount` (2 decimals, `_fp` wire suffix). REST timestamps are RFC3339;
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** — 127 mapped of 128 operations across 19 resources
(OpenAPI v3.34.0), every kwarg drift-tested against the spec.
- **Full REST coverage** — 132 mapped of 133 operations across 19 resources
(OpenAPI v3.35.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 @@ -16,7 +16,7 @@ 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 (50 REST operations, 6 WS
`PerpsWebSocket` for the perpetual-futures exchange (54 REST operations, 6 WS
channels), and a `KlearClient` for the Self-Clearing-Member settlement API
(26 of 27 operations, Bearer token auth). See [Perps](perps.md).
- **FIX protocol** — a hand-rolled, async-first FIX engine (FIXT.1.1 / FIX50SP2)
Expand Down
34 changes: 26 additions & 8 deletions docs/perps.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async with AsyncPerpsClient.from_env(demo=True) as perps:
| Attribute | Endpoints |
|---|---|
| `exchange` | `status()`, `enabled()` (per-member access gate), `risk_parameters()` |
| `markets` | `list()`, `get()`, `orderbook()`, `bulk_orderbooks(*, tickers)`, `candlesticks()` |
| `markets` | `list()`, `get()`, `orderbook()`, `bulk_orderbooks(*, tickers)`, `candlesticks()`, `schedules(*, tickers)` |
| `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()`, cross/isolated exit triggers |
Expand All @@ -77,8 +77,19 @@ is not a margin market returns 404.
books = perps.markets.bulk_orderbooks(tickers=["BTC-PERP", "ETH-PERP"])
for ob in books:
print(ob.ticker, len(ob.bids), len(ob.asks))

schedules = perps.markets.schedules(tickers=["BTC-PERP", "ETH-PERP"])
for ticker, details in schedules.items():
print(ticker, len(details.kalshi_trading_holidays))
```

`markets.schedules(*, tickers)` returns Kalshi trading-holiday closures
(`GET /margin/markets/schedules`) as a ticker → `MarginMarketScheduleDetails`
map. `tickers` is required (1–100, each 1–200 characters) and serializes as a
comma-separated query string. Regular daily and weekend closures are not
included. Each holiday window's spec field `from` is `start` on
`MarginHolidayWindow` (`until` is the exclusive end).

The margin order side is `bid` / `ask` (not the prediction API's `yes` / `no`).
Orders create/cancel/decrease/amend are POSTs/DELETEs and are **never retried**.
`orders.amend(..., expiration_time=)` is int64 Unix seconds: omit it to keep
Expand Down Expand Up @@ -212,7 +223,11 @@ perps.portfolio.cancel_cross_exit_trigger("BTC-PERP", trig.id)
null while open.
- **Subaccounts** (since the v3.21.0 spec sync) — `MarginPosition` (the
`portfolio.positions()` rows) carries a **required** `subaccount` (`int`)
identifying which subaccount holds the position.
identifying which subaccount holds the position. `subaccount_type`
(`"cross"` / `"isolated"` / `"auto_rebalance"`, or `None` when the key is
absent) is optional on `MarginPosition`, `MarginSubaccountBalance`,
`MarginRiskPosition`, and `MarginFundingHistoryEntry`. The spec marks it
required; the SDK keeps it optional so older payloads still parse.
- **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
Expand Down Expand Up @@ -287,16 +302,19 @@ async with ws.connect() as session:
print(msg.msg.funding_rate.rate, msg.msg.funding_rate.next_funding_time_ms)
```

Seven data channels — `orderbook_delta` (snapshot + delta, sequenced), `ticker`,
`trade`, `fill`, `user_orders`, `order_group_updates`, `corporate_actions`. The
connection, sequence-gap detection, reconnect, and backpressure machinery are
reused from the event-contract WS stack (see [WebSocket](websockets.md)); the
perps orderbook is `bid` / `ask`. The prediction-only channels
(`market_positions`, `multivariate*`, `communications`, `market_lifecycle_v2`,
Eight data channels — `orderbook_delta` (snapshot + delta, sequenced), `ticker`,
`trade`, `fill`, `user_orders`, `order_group_updates`, `corporate_actions`,
`market_lifecycle_v2`. The connection, sequence-gap detection, reconnect, and
backpressure machinery are reused from the event-contract WS stack (see
[WebSocket](websockets.md)); the perps orderbook is `bid` / `ask`. The
prediction-only channels (`market_positions`, `multivariate*`, `communications`,
`cfbenchmarks_value`) have no perps counterpart. `corporate_actions` streams
applied split / final-settlement / ticker-change notices (cash actions are
REST-only); subscribe with `session.subscribe(channel="corporate_actions")`.
Upcoming and recent notices are listed by `margin.corporate_actions()`.
`subscribe_market_lifecycle()` yields `MarginMarketLifecycleV2Message` for
activation and deactivation of every margin market (`event_type` is
`activated` or `deactivated`). Ticker filters are not supported on that channel.

## Self-Clearing-Member "Klear" API

Expand Down
8 changes: 8 additions & 0 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ every exception class.

::: kalshi.models.orders.CreateOrderV2Request

::: kalshi.models.orders.CreateOrderWithExitTriggerRequest

::: kalshi.models.portfolio.AttachedEventContractExitTrigger

::: kalshi.models.portfolio.SetEventContractExitTriggerRequest

::: kalshi.models.portfolio.EventContractExitSlippage

::: kalshi.models.orders.AmendOrderV2Request

::: kalshi.models.orders.DecreaseOrderV2Request
Expand Down
2 changes: 2 additions & 0 deletions docs/request-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ exposed by each resource method stays in lockstep with the OpenAPI spec.
| Resource | Request model |
|---|---|
| `client.orders.create_v2` | `CreateOrderV2Request` |
| `client.orders.create_with_exit_trigger` | `CreateOrderWithExitTriggerRequest` (adds `exit_trigger`: `AttachedEventContractExitTrigger`) |
| `client.portfolio.set_exit_trigger` | `SetEventContractExitTriggerRequest` (at least one of `stop_loss_price` / `take_profit_price`; nested `EventContractExitSlippage`) |
| `client.orders.amend_v2` | `AmendOrderV2Request` |
| `client.orders.decrease_v2` | `DecreaseOrderV2Request` (XOR `reduce_by` / `reduce_to`) |
| `client.orders.batch_create_v2` | `BatchCreateOrdersV2Request` (wraps `list[CreateOrderV2Request]`) |
Expand Down
8 changes: 8 additions & 0 deletions docs/resources/orders.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Reads stay on `/portfolio/orders/*`.
| Method | Endpoint | Retry |
|---|---|---|
| `create_v2(*, request)` | `POST /portfolio/events/orders` | **never** — see [Retries & idempotency](../retries.md) |
| `create_with_exit_trigger(*, request)` | `POST /portfolio/events/orders/with_exit_trigger` | never |
| `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 |
Expand All @@ -30,6 +31,13 @@ V2 uses `BookSideLiteral` (`"bid"` / `"ask"`), a single `price` field
idempotency key. The surface is **model-only** — every write method takes a
pre-built request model rather than kwargs.

`create_with_exit_trigger` places that same V2 order with an attached
take-profit / stop-loss bracket (`CreateOrderWithExitTriggerRequest`, which
subclasses `CreateOrderV2Request` and adds a required `exit_trigger`). The
route is hidden in the published spec (`x-excluded`) until event-contract
TP/SL launches. The POST is never retried. An empty `exit_trigger` object is
spec-valid; prices on the bracket are YES prices.

## Place an order

```python
Expand Down
42 changes: 42 additions & 0 deletions docs/resources/portfolio.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ Auth required throughout.
| `fills(...)` / `fills_all(..., exchange_index=None)` | `GET /portfolio/fills` |
| `total_resting_order_value()` | `GET /portfolio/summary/total_resting_order_value` (FCM only) |
| `deposits(*, limit, cursor)` / `deposits_all(*, limit, max_pages)` | `GET /portfolio/deposits` |
| `credit_history(*, limit, cursor, min_ts, max_ts)` / `credit_history_all(...)` | `GET /portfolio/credit_history` |
| `exit_triggers(ticker, *, kind, subaccount)` | `GET /portfolio/positions/{ticker}/exit_trigger` |
| `set_exit_trigger(ticker, *, request, subaccount)` | `PUT /portfolio/positions/{ticker}/exit_trigger` (never retried) |
| `cancel_exit_trigger(ticker, *, kind, subaccount)` | `DELETE /portfolio/positions/{ticker}/exit_trigger` (never retried) |
| `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}` |
Expand Down Expand Up @@ -176,6 +180,44 @@ constructors (tests/mocks) must pass it; an empty list is valid.
Non-FCM accounts get a `403` (mapped to `KalshiAuthError`). Demo mirrors
production behavior here.

## Credit history

`credit_history` returns `Page[CreditHistory]`, newest first. `min_ts` is
inclusive and `max_ts` is exclusive (credit creation time). `limit` is 1–1000.
`credit_type` is the spec's `type` field. `amount` is a `DollarDecimal`
(`amount_dollars` on the wire).

```python
page = client.portfolio.credit_history(limit=50)
for credit in page:
print(credit.credit_id, credit.credit_type, credit.amount, credit.status)

for credit in client.portfolio.credit_history_all():
...
```

## Exit triggers

Take-profit / stop-loss brackets on an event-contract position. These routes
are hidden in the published OpenAPI (`x-excluded`) until the feature launches.
`PUT` and `DELETE` are never retried. Prices are YES prices (`DollarDecimal`).
`set_exit_trigger` replaces both legs — omit a leg to clear it, and pass at
least one of `stop_loss_price` or `take_profit_price`.

```python
from decimal import Decimal
from kalshi import SetEventContractExitTriggerRequest

trigger = client.portfolio.set_exit_trigger(
"HIGHNY-24JAN01-T60",
request=SetEventContractExitTriggerRequest(stop_loss_price=Decimal("0.5600")),
)
print(trigger.id, trigger.status, trigger.stop_loss_price)

current = client.portfolio.exit_triggers("HIGHNY-24JAN01-T60", kind="bracket")
client.portfolio.cancel_exit_trigger("HIGHNY-24JAN01-T60", kind="bracket")
```

## Deposits and withdrawals

New in v2.1.0. Standard `Page[T]` pagination — see [Pagination](../pagination.md).
Expand Down
28 changes: 27 additions & 1 deletion kalshi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
ApiUsageLevelGrant,
ApplySubaccountTransferRequest,
AssociatedEvent,
AttachedEventContractExitTrigger,
Balance,
BatchCancelOrdersV2Request,
BatchCancelOrdersV2RequestOrder,
Expand All @@ -68,6 +69,7 @@
CreateOrderGroupResponse,
CreateOrderV2Request,
CreateOrderV2Response,
CreateOrderWithExitTriggerRequest,
CreateQuoteRequest,
CreateQuoteResponse,
CreateRFQBatchChild,
Expand All @@ -79,13 +81,22 @@
CreateRFQResponse,
CreateSubaccountRequest,
CreateSubaccountResponse,
CreditHistory,
CreditStatusLiteral,
DailySchedule,
DecreaseOrderV2Request,
DecreaseOrderV2Response,
Deposit,
EndpointTokenCost,
Event,
EventCandlesticks,
EventContractExitKindLiteral,
EventContractExitSideLiteral,
EventContractExitSlippage,
EventContractExitSlippageUnitLiteral,
EventContractExitStatusLiteral,
EventContractExitTrigger,
EventContractExitTriggeredLegLiteral,
EventFeeChange,
EventLiveData,
EventMetadata,
Expand All @@ -103,6 +114,7 @@
GetApiKeysResponse,
GetBlockTradeProposalsResponse,
GetCommunicationsIDResponse,
GetEventContractExitTriggersResponse,
GetEventLiveDataResponse,
GetFCMEventContractDailyCapResponse,
GetFcmFillsResponse,
Expand Down Expand Up @@ -186,6 +198,7 @@
SelfTradePreventionTypeLiteral,
Series,
SeriesFeeChange,
SetEventContractExitTriggerRequest,
SetTargetBalanceAllocationRequest,
Settlement,
SettlementSource,
Expand Down Expand Up @@ -265,6 +278,7 @@
"AsyncRFQBatchQuotesResource",
"AsyncRFQBatchesResource",
"AsyncRFQsResource",
"AttachedEventContractExitTrigger",
"AuthRequiredError",
"Balance",
"BatchCancelOrdersV2Request",
Expand Down Expand Up @@ -292,6 +306,7 @@
"CreateOrderGroupResponse",
"CreateOrderV2Request",
"CreateOrderV2Response",
"CreateOrderWithExitTriggerRequest",
"CreateQuoteRequest",
"CreateQuoteResponse",
"CreateRFQBatchChild",
Expand All @@ -303,13 +318,22 @@
"CreateRFQResponse",
"CreateSubaccountRequest",
"CreateSubaccountResponse",
"CreditHistory",
"CreditStatusLiteral",
"DailySchedule",
"DecreaseOrderV2Request",
"DecreaseOrderV2Response",
"Deposit",
"EndpointTokenCost",
"Event",
"EventCandlesticks",
"EventContractExitKindLiteral",
"EventContractExitSideLiteral",
"EventContractExitSlippage",
"EventContractExitSlippageUnitLiteral",
"EventContractExitStatusLiteral",
"EventContractExitTrigger",
"EventContractExitTriggeredLegLiteral",
"EventFeeChange",
"EventLiveData",
"EventMetadata",
Expand All @@ -331,6 +355,7 @@
"GetApiKeysResponse",
"GetBlockTradeProposalsResponse",
"GetCommunicationsIDResponse",
"GetEventContractExitTriggersResponse",
"GetEventLiveDataResponse",
"GetFCMEventContractDailyCapResponse",
"GetFCMSubtraderBlockedCategoriesResponse",
Expand Down Expand Up @@ -445,6 +470,7 @@
"SelfTradePreventionTypeLiteral",
"Series",
"SeriesFeeChange",
"SetEventContractExitTriggerRequest",
"SetTargetBalanceAllocationRequest",
"Settlement",
"SettlementSource",
Expand Down Expand Up @@ -477,4 +503,4 @@
"Withdrawal",
]

__version__ = "22.4.0"
__version__ = "22.5.0"
Loading
Loading