Repository navigation
Description
Epic: part of #387 · Depends on: #389, #390, #391, #392, #393, #394, #395, #396, #398, #400
Summary
Cross-cutting closeout for the PERPS (margin) API work: user-facing documentation (README section + docs/ page + mkdocs nav), runnable examples/, a CHANGELOG entry + version bump, the perps repo label, the spec-version banner update, and demo-environment integration tests gated exactly like the existing live tests but pointed at external-api.demo.kalshi.co. This issue ships last — it documents and exercises the PerpsClient / AsyncPerpsClient surface that the REST and WS feature issues build. No new SDK models or resource methods are introduced here; this is documentation, examples, tests, and release plumbing only.
This is the release-closeout issue: it depends on all perps REST resources, the WebSocket channels, and the SCM/Klear endpoints landing first. Docs, examples, and the CHANGELOG must cover the full shipped surface — REST, WS, and SCM/Klear — with no silent omissions.
Endpoints / scope
N/A — no new endpoints. This issue documents and integration-tests the already-implemented perps surface. For reference, the surface under documentation/test is the perps REST resource families and the perps WS channels:
| Area | Surface being documented / tested |
|---|---|
| REST exchange | GetMarginExchangeStatus, GetMarginRiskParameters |
| REST orders | GetMarginOrders, CreateMarginOrder, GetMarginOrder, CancelMarginOrder, DecreaseMarginOrder, AmendMarginOrder, GetMarginFCMOrders |
| REST markets | GetMarginMarkets, GetMarginMarket, GetMarginMarketOrderbook, GetMarginMarketCandlesticks |
| REST portfolio/risk | GetMarginFills, GetMarginPositions, GetMarginTrades, GetMarginBalance, GetMarginRisk, GetMarginEnabled, GetMarginNotionalRiskLimit, GetMarginFeeTiers |
| REST funding | GetMarginFundingHistory, GetMarginHistoricalFundingRates, GetMarginFundingRateEstimate |
| REST subaccounts/transfer | IntraExchangeInstanceTransfer, CreateMarginSubaccount, ApplyMarginSubaccountTransfer |
| REST order groups | GetMarginOrderGroups, CreateMarginOrderGroup, GetMarginOrderGroup, DeleteMarginOrderGroup, ResetMarginOrderGroup, TriggerMarginOrderGroup, UpdateMarginOrderGroupLimit |
| WS channels | orderbook_delta (snapshot + delta), ticker (carries funding_rate / next_funding_time_ms), trade, fill, user_orders, order_group_updates |
Each implemented family must have at least one runnable example and at least one demo integration test (see Tests). Whichever families have NOT shipped at merge time are explicitly scoped out and noted as "pending" in the docs.
Models
N/A — no new Pydantic models. See the Deliverables checklist below in place of Models/Resource methods.
Deliverables checklist
1. README perps section (README.md)
- Add a top-of-file feature bullet noting perps/margin support (mirror the existing terse bullet style, e.g. the
V2 event-market ordersbullet). - Add a
## Perps (margin) tradingsection after the WebSocket quickstart with:- a one-paragraph explainer (standalone
PerpsClient/AsyncPerpsClient, own base URLhttps://external-api.kalshi.com/trade-api/v2prod /https://external-api.demo.kalshi.co/trade-api/v2demo, reuses the same RSA-PSS auth asKalshiClient). - a sync quickstart and an async quickstart, mirroring the existing
## Quickstart — sync/## Quickstart — asyncblocks but constructingPerpsClient(key_id=..., private_key_path=...)/PerpsClient.from_env(). - a short note that prices are
DollarDecimal(FixedPointDollars, up to 6 decimals), counts areFixedPointCount(2 decimals, wire field suffix_fp), REST timestamps are RFC3339, and WS timestamps are Unix-epoch milliseconds (*_msfields).
- a one-paragraph explainer (standalone
2. Docs page (docs/perps.md, NEW)
- New page covering: client construction, auth (links to existing
authentication.md), environment variables (from_env()+ demo routing), the resource families, funding mechanics (funding rate,next_funding_time_ms), risk/balance introspection, and the perps WS channels (link towebsockets.mdpatterns; note the WS hostexternal-api-margin-ws.kalshi.comprod /external-api-margin-ws.demo.kalshi.codemo, path/trade-api/ws/v2/margin). - Register in
mkdocs.ymlnav: add a top-level- Perps: perps.mdentry (place it afterWebSocket: websockets.md). If per-resource pages are desired, add them under aPerpssection mirroring theResources:block — but a singleperps.mdis sufficient for this issue. - If the perps resources expose mkdocstrings-rendered API reference, add the module path to
reference.md(mirror how existing resources are referenced).
3. Examples (examples/, NEW directory — none exists today)
examples/perps_create_order.py— constructPerpsClient.from_env(), place a resting (non-marketable) margin order via the perps orders create method, print the returned order, then cancel it. Mirror the safety pattern oftests/integration/conftest.py::non_marketable_price(far-from-market limit so it rests, not fills).examples/perps_stream_ticker.py— async example: connect to the perps margin WS, subscribe to thetickerchannel for a market, and printfunding_rate+next_funding_time_msas messages arrive (demonstrates funding mechanics).examples/perps_balance_risk.py— callGetMarginBalance(balance()) andGetMarginRisk(risk()); print cash balance, position value, total balance, maintenance-margin requirement, account leverage, and per-position liquidation prices.- Each example must be runnable as
python examples/perps_*.py, read credentials viafrom_env(), default to demo (KALSHI_DEMO=trueor the perps demo base URL), and carry a top-of-file docstring stating the required env vars. Keep examples minimal — no argparse scaffolding unless a ticker arg is genuinely needed.
4. CHANGELOG + version bump
CHANGELOG.md: add a new top entry following the existing format (heading## <new-version> — <date>, prose summary,### Addedbullets forPerpsClient/AsyncPerpsClientand the perps resource families + WS channels,### Internalbullets for the vendoredspecs/perps_openapi.yaml(+specs/perps_asyncapi.yaml) sha256 snapshots and the README/docs/index.mdbanner bump). Cross-linkdocs/perps.md.- Bump
versioninpyproject.toml:3and__version__inkalshi/__init__.py:356to the same value. This is an additive release (new standalone client, no breaking changes toKalshiClient) → minor bump from3.1.0(e.g.3.2.0). Do not use thebreakinglabel.
5. Spec-version banner update
README.mdanddocs/index.mdcurrently advertise "99 operations across 19 resources (OpenAPI v3.20.0)" and "13 channels"/"11 typed channels". Add a perps line to both banners stating the perps surface (operation count from/tmp/perps_openapi.yaml, WS channel count from/tmp/perps_asyncapi.yaml) and the perps spec version. Do not overwrite the existing core-spec banner — add a sibling line for perps.
6. perps repo label
- Create the
perpsGitHub label onTexasCoding/kalshi-python-sdk(gh label create perps --description "Perps (margin) API" --color <hex>). All perps issues should carry it. (This issue itself is labeleddocumentation,testingper the planned labels; the foundation/REST/WS issues carryperps.)
Resource methods
N/A — no new resource methods. See Deliverables checklist above.
Contract-test wiring
N/A for this issue — METHOD_ENDPOINT_MAP / BODY_MODEL_MAP / EXCLUSIONS entries are owned by the REST and WS feature issues, not here.
This issue only consumes the contract-test harness parameterization established by the foundation issue (loading the vendored specs/perps_openapi.yaml). Acceptance for this issue includes confirming the full suite — including the perps drift tests added upstream — is green after the docs/version changes. If the version bump or banner edits touch anything the drift tests read, fix forward; do not add EXCLUSIONS to paper over a real drift.
Tests
New file(s) under tests/integration/ (one per family is fine; e.g. tests/integration/test_perps_orders.py, test_perps_markets.py, test_perps_balance_risk.py, test_perps_funding.py, test_perps_ws.py). These are live demo tests, not respx unit tests — mirror the existing tests/integration/ style exactly:
- Reuse
tests/integration/conftest.pyfixture conventions. Add perps client fixtures (perps_sync_client,perps_async_client) that buildPerpsClient.from_env()/AsyncPerpsClient.from_env(), auto-skip whenKALSHI_KEY_IDis unset (_credentials_available()), and hard-fail unless pointed at demo — extend_assert_demo_url(or add a perps analog) to assertexternal-api.demo.kalshi.cois the resolved base URL andexternal-api-margin-ws.demo.kalshi.cofor WS. Reuse the conftest RSA /from_envenv-bridging plumbing; do not duplicate it. - Gate exactly like existing live tests:
@pytest.mark.integration, auto-skip without credentials. Use@pytest.mark.integration_real_api_only(the existing marker, skipped unlessKALSHI_ENABLE_REAL_API_ONLY=1) for any perps endpoint the demo server cannot service (e.g. SCM/Klear, or margin endpoints requiring a margin-enabled demo account). - Add
skip_if_*guards mirroringskip_if_low_balancefor: account not margin-enabled (GetMarginEnabledreturns false → skip order/balance tests), and demo having no margin markets (GetMarginMarketsempty → skip). - Register tested methods in
tests/integration/coverage_harness.py. Since perps resources live inkalshi/perps/resources/*, add the perps resource module paths toRESOURCE_MODULES(or introduce a parallel registry) sotest_coverage.pyintrospects them; otherwise perps methods are invisible to the coverage check. Callregister("PerpsOrdersResource", [...])etc. from each test file.
Per-family coverage (each at least one happy-path live call; mark order-mutating tests with cleanup):
- orders: create a resting margin order (non-marketable price), assert it returns, then cancel (cleanup in an autouse session sweep mirroring
cleanup_orders); list orders; get a single order. - markets: list margin markets, get one, fetch its orderbook and candlesticks.
- balance/risk:
balance()returns cash/position/total/maintenance-margin fields;risk()returns leverage + liquidation prices;enabled()boolean. - funding:
funding_history,historical_funding_rates,funding_rate_estimatereturn without error and parsefunding_rateasDecimal. - ws (async): connect to demo margin WS, subscribe
ticker, receive at least one message, assertfunding_rateparses andnext_funding_time_msis an int epoch-ms; mirror thews_sessionfixture pattern. - Add a session-end autouse cleanup sweep for any resting perps orders tagged with the run id (mirror
cleanup_orders+TEST_RUN_ID), so live order tests never leak.
Examples themselves are not part of CI unit tests, but add a lightweight import/syntax check (e.g. a tests/test_examples_importable.py that imports each examples/perps_*.py module without executing main()) so a broken example reds CI.
Acceptance criteria
- README has a perps feature bullet +
## Perps (margin) tradingsection with sync and async quickstarts usingPerpsClient/AsyncPerpsClient. -
docs/perps.mdcreated and added tomkdocs.ymlnav;mkdocs build --strictpasses (no broken links / orphan pages). -
examples/directory created withperps_create_order.py,perps_stream_ticker.py(funding),perps_balance_risk.py— each runnable, demo-defaulted, credential-via-from_env(). -
CHANGELOG.mdhas a new top entry;versioninpyproject.tomland__version__inkalshi/__init__.pybumped to the same new minor version. - README +
docs/index.mdbanners updated with a perps line (operation/channel counts from the perps specs + perps spec version); existing core-spec banner left intact. -
perpsGitHub label created on the repo. - Demo integration tests added under
tests/integration/for every shipped perps family, gated by@pytest.mark.integration, auto-skipping withoutKALSHI_KEY_ID, hard-failing unless resolved toexternal-api.demo.kalshi.co(and the margin WS demo host). Order-mutating tests have a cleanup sweep. - Perps resource modules registered in
coverage_harness.RESOURCE_MODULES(or a parallel registry) andregister(...)called from each perps integration test file;test_coverage.pyis green for perps resources. - Examples are import-checked in CI (
tests/test_examples_importable.py). -
uv run ruff check .clean (incl. newexamples/and tests). -
uv run mypy kalshi/strict clean (no perps-source changes here, but version-file edits must not regress); examples annotated enough to not break if mypy scope includes them, usingbuiltins.list[T]if any example touches a resource.list()return inside a shadowed scope. -
uv run pytest tests/ -vgreen including the perps contract/drift tests added by upstream issues (the docs/version edits must not red them). - No markdown trackers (TODOS/BACKLOG) added to the repo (per CLAUDE.md); all follow-ups go to GitHub Issues.
Dependencies
- perps: foundation — PerpsClient/AsyncPerpsClient, PerpsConfig, spec vendoring & contract-test parameterization (provides the clients, vendored
specs/perps_openapi.yaml/specs/perps_asyncapi.yaml, and the contract-test harness parameterization this issue's tests rely on). - Implicitly depends on the perps REST resource issues and the perps WebSocket issue landing first — this closeout documents and integration-tests their surface. Whichever families have not merged at the time this lands are scoped out of docs/examples/tests and noted as "pending".
Activity