Skip to content

Commit 0a3fb23

Browse files
authored
release: v1.1.0 (#76)
* release: v1.1.0 Post-1.0 enhancements and polish. 17 issues closed across four parallel waves of work. Bump: - pyproject.toml version 1.0.0 → 1.1.0 - kalshi.__version__ 1.0.0 → 1.1.0 Docs: - CHANGELOG.md gets a comprehensive `## 1.1.0 — 2026-05-16` entry covering Added / Changed / Fixed / Test infrastructure / Infrastructure sections, the mypy-only breaking changes (Page TypeVar bound + Literal enum kwargs), and coverage stats. - ROADMAP.md updated — v1.1 feature-complete; only the two deferred / blocked issues remain (#45 prod creds, #53 nested $ref). - README.md surfaces v1.1 features: model-first `request=` overload, DataFrame integration, `kalshi.testing` mock transport, docs site link. Channel count corrected from 12 → 11 to match `CLAUDE.md` and the AsyncAPI spec. - AGENTS.md / CLAUDE.md gitnexus counts refreshed to the v1.1 indexed state (5819 symbols, 12421 relationships, 236 execution flows). Per `docs/RELEASING.md`, the next step is `git tag v1.1.0 && git push origin v1.1.0` — the release workflow will extract the CHANGELOG section, build sdist + wheel, publish to PyPI via OIDC trusted publisher (gated by the `pypi` environment reviewer), and create a GitHub Release with the extracted body and artifacts attached. * release: address PR #76 review — fix doc inconsistencies - CHANGELOG.md "Coverage at 1.1.0": - Source files 75 → 76 (matches `uv run mypy kalshi/` output) - Endpoints 90 → 89 (verified: 89 verb,path pairs across 77 paths) - Test count reworded as "1407 unit tests passing (1455 collected, 48 skipped)" so the framing matches the actual pytest output and matches the Verified section of the PR body - ROADMAP.md v1.0 shipped entry: "12 WS message types" → "11 WS channels" so the WS count is internally consistent across all docs (CLAUDE.md, README, AsyncAPI spec all say 11 channels) - README.md: revert 90 → 89 endpoints. v1.1 did not add a REST endpoint; the original 89 count from v1.0 is still correct - CLAUDE.md Testing section: stale "917 tests" → current "1455 collected (1407 passing, 48 skipped without live credentials)" - CLAUDE.md tree comment "(827 total)" dropped — structural overview doesn't need a count that drifts with every PR Verified: ruff/mypy untouched; the changes are doc-only.
1 parent 1afa5a2 commit 0a3fb23

7 files changed

Lines changed: 237 additions & 14 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!-- gitnexus:start -->
22
# GitNexus — Code Intelligence
33

4-
This project is indexed by GitNexus as **kalshi-python-sdk** (5324 symbols, 11635 relationships, 161 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
4+
This project is indexed by GitNexus as **kalshi-python-sdk** (5819 symbols, 12421 relationships, 236 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
55

66
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
77

CHANGELOG.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,164 @@
22

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

5+
## 1.1.0 — 2026-05-16
6+
7+
Post-1.0 enhancements and polish. 17 issues closed across four parallel waves of
8+
work. No breaking changes to runtime behavior; two mypy-level type-system
9+
tightenings are called out below.
10+
11+
### Added
12+
13+
- **Model-first request API.** Every POST/PUT/DELETE-with-body resource method
14+
now accepts a pre-built request model as an alternative to individual kwargs.
15+
Backward-compatible — the existing kwarg form continues to work unchanged
16+
(#56).
17+
18+
```python
19+
client.orders.amend(request=AmendOrderRequest(order_id=..., yes_price=...))
20+
client.orders.amend(order_id=..., yes_price=...) # also works
21+
```
22+
23+
Each method has typed `@overload` stubs (32 methods × 2 = 64 stubs) so mypy
24+
catches misuse at type-check time, not runtime.
25+
26+
- **DataFrame integration.** `Page[T]` gained `.to_dataframe()` and
27+
`.to_polars()` methods with optional dependency extras (#12):
28+
29+
```bash
30+
pip install 'kalshi-sdk[pandas]' # or [polars] or [all]
31+
```
32+
33+
```python
34+
page = client.markets.list(limit=100)
35+
df = page.to_dataframe()
36+
```
37+
38+
Lazy imports; `Decimal` and `datetime` preserved as native types via
39+
`model_dump(mode="python")`.
40+
41+
- **Record / replay mock transport** (`kalshi.testing`) for offline integration
42+
testing (#13). `RecordingTransport` proxies real calls and saves
43+
request/response pairs as JSON; `ReplayTransport` serves the fixtures with
44+
no network. Sync and async transports both supported; signature/timestamp
45+
headers are excluded from fingerprinting so signatures can drift between
46+
record and replay.
47+
48+
```python
49+
with KalshiClient.from_env(transport=RecordingTransport("fixtures")) as c:
50+
c.exchange.status() # records once
51+
52+
with KalshiClient(transport=ReplayTransport("fixtures")) as c:
53+
c.exchange.status() # offline replay
54+
```
55+
56+
- **Typed `Literal` aliases for fixed-enum kwargs** (#50). 13 new aliases
57+
exported from `kalshi` and `kalshi.models`: `SideLiteral`, `ActionLiteral`,
58+
`TimeInForceLiteral`, `SelfTradePreventionTypeLiteral`, `OrderStatusLiteral`,
59+
`EventStatusLiteral`, `MarketStatusLiteral`, `MveFilterLiteral`,
60+
`MveHistoricalFilterLiteral`, `MultivariateCollectionStatusLiteral`,
61+
`IncentiveProgramStatusLiteral`, `IncentiveProgramTypeLiteral`,
62+
`SettlementStatusLiteral`. mypy now catches typos in resource kwargs at
63+
authoring time.
64+
65+
- **MkDocs documentation site** (#14, #57). Material theme + mkdocstrings;
66+
Getting Started, Authentication, WebSockets, Resources, Errors, Migration
67+
guides plus auto-generated API reference. GitHub Pages deploy workflow at
68+
`.github/workflows/docs.yml`.
69+
70+
- **Constructor-variant integration tests** (#54). Exercises every supported
71+
`KalshiClient` construction path against the demo API (`from_env`,
72+
`key_id + private_key_path`, in-memory PEM string, pre-built `KalshiAuth`,
73+
`demo=True`). Includes an async sibling test to catch signing-path drift.
74+
75+
- **Per-method auth guards on `markets.orderbook`** (#49). Spec walk confirmed
76+
this was the only public-resource GET endpoint missing `_require_auth()`;
77+
unauthenticated callers now get a clear `AuthRequiredError` instead of a
78+
confusing 401 from Kalshi.
79+
80+
### Changed
81+
82+
- **Sync/async dedup refactor** (#46). Extracted shared body-builder,
83+
query-param-builder, and response-parser helpers across 13 resource modules.
84+
Dispatcher logic and request-model construction now exist once per
85+
method-pair instead of twice. Sync/async signatures, `@overload` stubs, and
86+
the `async for item in client.markets.list_all():` ergonomic are all
87+
preserved.
88+
89+
- **Async `OrdersResource.batch_cancel` routed through shared
90+
`_delete_with_body` helper** (#47). Previously called the transport directly,
91+
bypassing the sync path's helper; any future retry / error-mapping change to
92+
the helper now applies to both transports symmetrically.
93+
94+
- **Sync `test_list_all` iteration idiom standardized** (#48). Sync tests now
95+
use the same manual-counter loop as their async siblings; cosmetic only.
96+
97+
### Fixed
98+
99+
- **Async `multivariate.lookup_tickers` 204 spec-drift guard** (#72). Sync had
100+
a clear `RuntimeError("spec drift: ...")` on an unexpected 204; async would
101+
surface a confusing `TypeError` from `model_validate(None)`. Extracted a
102+
shared `_parse_lookup_tickers_response` helper so both paths share the
103+
guard.
104+
105+
### Test infrastructure
106+
107+
- **Typed `ExclusionKind`** discriminator on contract drift exclusions (#51).
108+
Replaces the free-text `reason` substring matching in
109+
`test_exclusion_map_is_current` with explicit
110+
`Literal["body_param", "spec_deprecated", "paginator_handled",
111+
"wire_normalization", "kwarg_rename"]` classification. All 47 existing
112+
exclusions reclassified.
113+
114+
- **Nested-model body-drift detection** (#52). The drift test now recurses
115+
into nested `BaseModel` fields (e.g. `TickerPair` inside
116+
`CreateMarketInMultivariateEventCollectionRequest.selected_markets`).
117+
`TickerPair.extra="allow"` intentionally preserved because `LookupPoint`
118+
responses echo provider keys; the existing pin test documents the carve-out.
119+
120+
- **`kwarg_rename` `ExclusionKind`** split out of `wire_normalization` (#68).
121+
Python-naming-hygiene renames (`milestone_type`, `target_type`,
122+
`incentive_type` — spec field `type` shadows the builtin) are now
123+
classified separately from wire-format normalization.
124+
125+
### Infrastructure
126+
127+
- **Weekly OpenAPI + AsyncAPI spec sync CI** (#16). Cron + manual dispatch
128+
workflow snapshots both specs, regenerates models, runs ruff / mypy /
129+
pytest, opens a PR with version + endpoint diff if any changes detected.
130+
Third-party actions pinned to commit SHAs because the workflow holds
131+
`contents: write` + `pull-requests: write`. Concurrency-guarded against
132+
same-branch races.
133+
134+
- **Nightly integration CI** against the demo API (#55). Secret-gated, skips
135+
cleanly on forks, dedupes failure issues by stable title.
136+
137+
### mypy-only breaking changes
138+
139+
These tighten types without changing runtime behavior. Existing valid calls are
140+
unaffected; the type system will now reject calls that would have failed at
141+
runtime anyway (or that were always wrong but mypy couldn't see it).
142+
143+
- `Page[T]` TypeVar tightened from `TypeVar("T")` to
144+
`TypeVar("T", bound=BaseModel)`. Matches the bound already used inside
145+
`kalshi/resources/_base.py` and the actual usage pattern across the SDK
146+
(every concrete `Page[X]` parameterizes with a `BaseModel` subclass).
147+
- Resource-method kwargs for the 13 enum fields above are now `Literal[...]`
148+
instead of `str | None`. Callers passing arbitrary strings (typos, in-flight
149+
variable values) now fail at mypy-check time.
150+
151+
### Coverage at 1.1.0
152+
153+
- 89/89 REST endpoints implemented (sync + async); auth-guard audit complete
154+
across public resources.
155+
- 11 WebSocket channels with sequence-gap detection, configurable backpressure,
156+
and automatic reconnection.
157+
- **1407 unit tests passing** (1455 collected, 48 skipped — up from 899 at
158+
1.0.0), mypy `--strict` clean (76 source files), ruff clean.
159+
- Drift tests now detect query/path/body/WS-payload schema drift _and_ nested
160+
body-model drift.
161+
- Optional extras: `pandas`, `polars`, `all`, `docs`.
162+
5163
## 1.0.0 — 2026-05-10
6164

7165
First stable release. No behavioral changes from 0.15.0 — this release marks

CLAUDE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ tests/
7979
_contract_support.py # Contract test helpers
8080
integration/ # Live API integration tests
8181
ws/ # WebSocket tests
82-
test_*.py # Per-resource + per-feature tests (827 total)
82+
test_*.py # Per-resource + per-feature tests
8383
```
8484

8585
## Key conventions
@@ -116,7 +116,7 @@ tests/
116116

117117
## Testing
118118

119-
- pytest + pytest-asyncio + respx (httpx mock); 917 tests.
119+
- pytest + pytest-asyncio + respx (httpx mock); 1455 tests collected (1407 passing, 48 skipped without live credentials).
120120
- Use `respx.mock` for HTTP mocking. Generate test RSA keys via conftest.py fixtures.
121121
- New function → write a test. Bug fix → write a regression test. New error path → write a test that triggers it.
122122

@@ -144,7 +144,7 @@ Reference issues from PRs via `Closes #N` so the issue closes on merge.
144144
<!-- gitnexus:start -->
145145
# GitNexus — Code Intelligence
146146

147-
This project is indexed by GitNexus as **kalshi-python-sdk** (5324 symbols, 11635 relationships, 161 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
147+
This project is indexed by GitNexus as **kalshi-python-sdk** (5819 symbols, 12421 relationships, 236 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
148148

149149
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
150150

README.md

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ A professional, spec-first Python SDK for the [Kalshi](https://kalshi.com) predi
77
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
88
[![Type checked: mypy strict](https://img.shields.io/badge/mypy-strict-blue.svg)](https://mypy.readthedocs.io/)
99

10-
- **Full coverage** of the Kalshi REST API (89 endpoints across 19 resources) and WebSocket API (12 message types).
10+
- **Full coverage** of the Kalshi REST API (89 endpoints across 19 resources) and WebSocket API (11 channels).
1111
- **Sync and async** clients sharing one transport — no thread-pool wrapping.
12-
- **Typed end-to-end**: Pydantic v2 models, `mypy --strict` clean, ships `py.typed`.
12+
- **Typed end-to-end**: Pydantic v2 models, `mypy --strict` clean, ships `py.typed`. `Literal` types on fixed-enum kwargs.
1313
- **Spec-aligned with drift guards**: hard-fail contract tests catch query, body, and WebSocket payload drift on every commit.
1414
- **Safe defaults**: only idempotent verbs (`GET`/`HEAD`/`OPTIONS`) retry; `POST`/`DELETE` never retry to avoid duplicate orders or cancels.
15+
- **DataFrame-ready**: optional `pandas` / `polars` extras for analysis workflows.
16+
- **Offline-testable**: record/replay mock transport (`kalshi.testing`) for SDK consumers building integration tests.
17+
18+
📖 Full documentation: <https://texascoding.github.io/kalshi-python-sdk/>
1519

1620
## Install
1721

@@ -120,6 +124,19 @@ with KalshiClient.from_env() as client:
120124
Prices are decimal dollars (e.g. `"0.65"`) per the Kalshi spec. Internally
121125
the SDK uses `Decimal` via the `DollarDecimal` type — never `float`.
122126

127+
Every POST/PUT/DELETE-with-body method also accepts a pre-built request model
128+
as an alternative to individual kwargs (useful for programmatic order
129+
construction):
130+
131+
```python
132+
from kalshi import CreateOrderRequest
133+
134+
client.orders.create(request=CreateOrderRequest(
135+
ticker="EXAMPLE-25-T", side="yes", action="buy",
136+
count=10, yes_price="0.65",
137+
))
138+
```
139+
123140
## WebSocket streaming
124141

125142
```python
@@ -140,7 +157,7 @@ async def main() -> None:
140157
asyncio.run(main())
141158
```
142159

143-
Available channels: `ticker`, `trade`, `orderbook_delta`, `fill`,
160+
Available channels (11): `ticker`, `trade`, `orderbook_delta`, `fill`,
144161
`market_positions`, `user_orders`, `order_group`, `market_lifecycle`,
145162
`multivariate`, `multivariate_lifecycle`, `communications`.
146163

@@ -213,10 +230,46 @@ for market in client.markets.list_all(status="open"):
213230
...
214231
```
215232

233+
`Page[T]` also converts to a DataFrame when the optional extras are installed:
234+
235+
```bash
236+
pip install 'kalshi-sdk[pandas]' # or [polars] or [all]
237+
```
238+
239+
```python
240+
df = client.markets.list(status="open", limit=100).to_dataframe()
241+
# Decimal and datetime preserved as native types in object columns.
242+
```
243+
244+
## Testing against the SDK (no live API)
245+
246+
For SDK consumers who want offline integration tests, `kalshi.testing` ships
247+
record-and-replay transports:
248+
249+
```python
250+
from kalshi import KalshiClient
251+
from kalshi.testing import RecordingTransport, ReplayTransport
252+
253+
# Record once against the real demo API:
254+
with KalshiClient.from_env(transport=RecordingTransport("fixtures")) as c:
255+
c.exchange.status()
256+
257+
# Replay in tests — no network:
258+
with KalshiClient(transport=ReplayTransport("fixtures")) as c:
259+
c.exchange.status() # served from fixtures/GET_*.json
260+
```
261+
262+
Fixtures are JSON; the fingerprint ignores `KALSHI-ACCESS-SIGNATURE` and
263+
`KALSHI-ACCESS-TIMESTAMP` so signature drift between record and replay does not
264+
break matching. **Always `.gitignore` the fixture directory when recording
265+
against an authenticated account** — fixtures contain the full response body
266+
(balances, positions, PII).
267+
216268
## Resources
217269

218270
| | |
219271
|---|---|
272+
| **Documentation site** | https://texascoding.github.io/kalshi-python-sdk/ |
220273
| **Kalshi REST OpenAPI spec** | https://docs.kalshi.com/openapi.yaml |
221274
| **Kalshi WebSocket AsyncAPI spec** | https://docs.kalshi.com/asyncapi.yaml |
222275
| **Production base URL** | `https://api.elections.kalshi.com/trade-api/v2` |

ROADMAP.md

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
# Roadmap
22

3-
Active milestone: **[v1.1 — Post-1.0 enhancements and polish](https://github.com/TexasCoding/kalshi-python-sdk/milestone/1)**.
3+
**v1.1 is feature-complete** — see `CHANGELOG.md`. The remaining two issues in
4+
the [v1.1 milestone](https://github.com/TexasCoding/kalshi-python-sdk/milestone/1)
5+
are deferred and don't block a release:
46

5-
All planned and proposed work lives in [GitHub Issues](https://github.com/TexasCoding/kalshi-python-sdk/issues).
7+
- **#45** — verify the `json={}` Content-Type workaround under production
8+
credentials. Blocked on prod-key access.
9+
- **#53** — resolve nested `$ref` pointers in the body-schema drift check.
10+
Premature — the spec currently has no nested refs.
11+
12+
Future work lives in [GitHub Issues](https://github.com/TexasCoding/kalshi-python-sdk/issues).
613
File a new issue (or comment on an existing one) rather than editing this file.
714

815
## Shipped
916

10-
See `CHANGELOG.md` for release history. v1.0 (2026-05-10) marks the public API surface
11-
as stable — REST coverage is complete (89/89 endpoints), WebSocket dispatch covers all
12-
12 message types, and contract drift tests block CI on any spec divergence.
17+
See `CHANGELOG.md` for full release history.
18+
19+
- **v1.1.0 (2026-05-16)** — model-first request API, DataFrame integration,
20+
record/replay mock transport, MkDocs documentation site, `Literal` enum
21+
kwargs, sync/async dedup refactor, weekly spec sync + nightly integration
22+
CI workflows.
23+
- **v1.0.0 (2026-05-10)** — public API stable. 89/89 REST endpoints, 11 WS
24+
channels, contract drift tests, PyPI trusted-publisher release pipeline.

kalshi/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,4 +224,4 @@
224224
"UserDataTimestamp",
225225
]
226226

227-
__version__ = "1.0.0"
227+
__version__ = "1.1.0"

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "kalshi-sdk"
3-
version = "1.0.0"
3+
version = "1.1.0"
44
description = "A professional Python SDK for the Kalshi prediction markets API"
55
readme = "README.md"
66
license = { text = "MIT" }

0 commit comments

Comments
 (0)