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

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

## 7.3.0 — 2026-07-23

Syncs the upstream core OpenAPI **3.25.0 → 3.26.0** and closes the remaining
Klear settlement additive-optional drift (Closes #484). Additive only — no
breaking public-API changes.

### Added

- **`historical.positions()` / `positions_all()`** (sync + async) —
`GET /historical/positions`. Settled market positions archived to the
historical database (positions whose markets were archived before
`market_positions_last_updated_ts` on `GET /historical/cutoff`). Returns
`PositionsResponse` (same shape as `portfolio.positions()`). Query params
are a subset of the live portfolio surface: `limit`, `cursor`, `ticker`,
`event_ticker` (no `count_filter` / `subaccount`). Auth required.
`positions_all()` auto-paginates `market_positions` (mirrors
`portfolio.positions_all()`).
- **`HistoricalCutoff.market_positions_last_updated_ts`**
(`AwareDatetime | None`) — archival boundary for historical positions.
Unsettled positions remain on `GET /portfolio/positions`.
- **`GetSettlementEstimateResponse.omitted_subtrader_count`** and
**`AssetClassSettlementEstimate.omitted_subtrader_count`** (Klear SCM;
`int | None`) — number of subtraders omitted from `subtrader_breakdowns`
(their amounts remain included in `user_breakdown`).

### Spec notes

- Core OpenAPI `info.version` **3.25.0 → 3.26.0** (paths 91→92, 103 operations /
102 mapped). Still unimplemented:
`POST /portfolio/intra_exchange_instance_transfer` (use
`PerpsClient.transfers.transfer_instance()` on the margin product).
- Perps OpenAPI / AsyncAPI / SCM specs unchanged in content for this release
beyond the Klear field already present on the SCM settlement schemas.

## 7.2.0 — 2026-07-22

Reconciles upstream OpenAPI/perps OpenAPI drift under version string **3.25.0**
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.25.0, 102 operations; 101 mapped in the core SDK — `POST /portfolio/intra_exchange_instance_transfer` is currently not available upstream)
- OpenAPI spec: https://docs.kalshi.com/openapi.yaml (v3.26.0, 103 operations; 102 mapped in the core SDK — `POST /portfolio/intra_exchange_instance_transfer` is currently not available upstream)
- 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.25.0) and WebSocket API (12 typed `subscribe_*` channels + 2 escape-hatch).
- **Full coverage** of the Kalshi REST API (102 operations across 19 resources, OpenAPI v3.26.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).
- **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
4 changes: 4 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Shipped

- **v7.3.0 (2026-07-23)** — OpenAPI sync 3.25.0 → 3.26.0 (#484). Additive:
`historical.positions()` / `positions_all()` (`GET /historical/positions`),
`HistoricalCutoff.market_positions_last_updated_ts`, and Klear
`omitted_subtrader_count` on settlement-estimate responses.
- **v7.2.0 (2026-07-22)** — Spec-drift reconcile under OpenAPI 3.25.0
(#481 / #482 / #483). Soft-deprecate perps `orders.list_fcm` /
`list_all_fcm` (upstream removed `GET /margin/fcm/orders`).
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.25.0),
- **Full REST coverage** — 102 operations across 19 resources (OpenAPI v3.26.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
26 changes: 26 additions & 0 deletions docs/migration.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# Migration

## v7.2 → v7.3.0

Syncs the SDK to core OpenAPI **3.26.0** and closes residual Klear settlement
additive-optional drift (Closes #484). **No breaking changes.**

### Added

- **`historical.positions()` / `positions_all()`** (sync + async) —
`GET /historical/positions`. Auth required. Returns `PositionsResponse`
(same shape as `portfolio.positions()`). Filters: `limit`, `cursor`,
`ticker`, `event_ticker` only — no `count_filter` / `subaccount`.
- **`HistoricalCutoff.market_positions_last_updated_ts`**
(`AwareDatetime | None`) — archival boundary for historical positions.
- **`omitted_subtrader_count`** (`int | None`) on
`GetSettlementEstimateResponse` and `AssetClassSettlementEstimate` (Klear).

```python
co = client.historical.cutoff()
if co.market_positions_last_updated_ts is not None:
for mp in client.historical.positions_all():
print(mp.ticker, mp.position)
```

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

## v7.1 → v7.2.0

Reconciles in-place OpenAPI/perps OpenAPI edits under version string **3.25.0**
Expand Down
6 changes: 6 additions & 0 deletions docs/perps.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ currently-active settlement obligations (the plural sibling of the single-obliga
`active_obligation()`), and `klear.margin.settlement_estimate_by_asset_class()`
returns next-settlement estimates keyed by asset class.

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`).

Money fields on the Klear margin schemas are integer **centicents** (`1 USD =
10,000 centicents`); only the withdrawal `amount` is a fixed-point dollar string.
`klear.margin.withdraw_settlement_balance(amount="500.00")` validates the amount as
Expand Down
41 changes: 37 additions & 4 deletions docs/resources/historical.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,23 @@ analytics; live trading needs the real-time surfaces.
| `trades(...)` / `trades_all(...)` | `GET /historical/trades` | no |
| `fills(...)` / `fills_all(...)` | `GET /historical/fills` | **yes** |
| `orders(...)` / `orders_all(...)` | `GET /historical/orders` | **yes** |
| `positions(...)` / `positions_all(...)` | `GET /historical/positions` | **yes** |

## Cutoff

```python
co = client.historical.cutoff()
print(co.cutoff_ts)
print(co.market_settled_ts) # settled markets archival boundary
print(co.trades_created_ts)
print(co.orders_updated_ts)
print(co.market_positions_last_updated_ts) # v3.26.0; may be None on older payloads
```

The "everything older than this timestamp is finalized" boundary. Use it to
decide whether to query the historical archive or the live `markets.*` /
`portfolio.*` endpoints.
Per-surface archival boundaries for the historical store. Data older than each
timestamp for that surface has been moved out of the live `markets.*` /
`portfolio.*` endpoints and must be read from the matching `historical.*`
method. Use `market_positions_last_updated_ts` to decide between
`historical.positions` and live `portfolio.positions` for settled positions.

## Historical markets

Expand Down Expand Up @@ -68,6 +74,33 @@ for order in client.historical.orders_all(status="executed"):
print(order.order_id, order.client_order_id)
```

## Historical positions

Auth required. Settled market positions archived to the historical database
(OpenAPI v3.26.0 / SDK v7.3.0). Positions whose markets were archived before
`market_positions_last_updated_ts` on `cutoff()` are available here; unsettled
positions remain on `portfolio.positions()`. Query params are a subset of the
live portfolio surface — no `count_filter` or `subaccount`.

```python
resp = client.historical.positions(
ticker="KXPRES-24-DJT",
event_ticker="KXPRES-24",
limit=100,
)
for mp in resp.market_positions:
print(mp.ticker, mp.position, mp.realized_pnl)

# Auto-paginate market_positions (event_positions are page-local aggregates;
# walk positions() page-by-page if you need the event view):
for mp in client.historical.positions_all(event_ticker="KXPRES-24"):
print(mp.ticker, mp.position)
```

`positions()` returns `PositionsResponse` — the same shape as
[`portfolio.positions`](portfolio.md#positions) (`market_positions`,
`event_positions`, `cursor` / `has_next`).

## Historical candlesticks

```python
Expand Down
3 changes: 2 additions & 1 deletion docs/resources/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ A few public-looking endpoints also require auth at the SDK layer:
book behind auth.
- `series.forecast_percentile_history()` — auth required.
- `exchange.user_data_timestamp()` — auth required.
- All of `historical.fills` / `historical.orders` (user data).
- All of `historical.fills` / `historical.orders` / `historical.positions`
(user data).

Use `client.is_authenticated` to branch on it explicitly:

Expand Down
2 changes: 1 addition & 1 deletion kalshi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,4 +379,4 @@
"Withdrawal",
]

__version__ = "7.2.0"
__version__ = "7.3.0"
2 changes: 2 additions & 0 deletions kalshi/models/historical.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class HistoricalCutoff(BaseModel):
market_settled_ts: AwareDatetime
trades_created_ts: AwareDatetime
orders_updated_ts: AwareDatetime
# Spec v3.26.0 — archival boundary for GET /historical/positions.
market_positions_last_updated_ts: AwareDatetime | None = None

model_config = {"extra": "allow"}

Expand Down
8 changes: 8 additions & 0 deletions kalshi/perps/klear/models/margin.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,16 @@ class GetSettlementEstimateResponse(BaseModel):

``prev_settlement_prices`` (spec v3.22.0) maps market ticker → most
recent settlement (mark) price in centicents; optional.

``omitted_subtrader_count`` is the number of subtraders omitted from
``subtrader_breakdowns`` (their amounts remain in ``user_breakdown``).
"""

user_breakdown: SettlementEstimate
subtrader_breakdowns: dict[str, SettlementEstimate] | None = None
prev_settlement_prices: dict[str, int] | None = None
settlement_balance_centicents: int
omitted_subtrader_count: int | None = None

model_config = {"extra": "allow"}

Expand Down Expand Up @@ -278,12 +282,16 @@ class AssetClassSettlementEstimate(BaseModel):
+ previous settlement prices) and adds ``next_runtime``, the next
settlement-cycle time. Only ``next_runtime`` is spec-required; the breakdowns
are optional.

``omitted_subtrader_count`` is the number of subtraders omitted from
``subtrader_breakdowns`` (their amounts remain in ``user_breakdown``).
"""

next_runtime: AwareDatetime
user_breakdown: SettlementEstimate | None = None
subtrader_breakdowns: dict[str, SettlementEstimate] | None = None
prev_settlement_prices: dict[str, int] | None = None
omitted_subtrader_count: int | None = None

model_config = {"extra": "allow"}

Expand Down
Loading
Loading