|
1 | 1 | # Migration |
2 | 2 |
|
| 3 | +## v2.0 → v2.1 |
| 4 | + |
| 5 | +v2.1 syncs the SDK to OpenAPI spec v3.18.0. It's **additive at the resource |
| 6 | +surface** — eight new endpoints, several new optional kwargs on existing |
| 7 | +methods, and one soft-breaking model-construction change called out below. |
| 8 | +Code that only consumes the SDK's responses needs no edits; code that |
| 9 | +constructs models directly in tests/mocks needs one small update. |
| 10 | + |
| 11 | +### `Balance.balance_dollars` — required, soft-breaking at construction |
| 12 | + |
| 13 | +Spec v3.18.0 adds `balance_dollars: FixedPointDollars` to |
| 14 | +`GetBalanceResponse` as a required field. The server now guarantees it, so |
| 15 | +callers parsing API responses (`client.portfolio.balance()`) are unaffected. |
| 16 | +**But** any code that builds `Balance(...)` directly — typically test mocks |
| 17 | +— will hit `ValidationError` until it adds the field. |
| 18 | + |
| 19 | +```python |
| 20 | +# v2.0 — broken in v2.1 |
| 21 | +Balance(balance=50000, portfolio_value=75000, updated_ts=ts) |
| 22 | + |
| 23 | +# v2.1 |
| 24 | +Balance( |
| 25 | + balance=50000, |
| 26 | + balance_dollars=Decimal("500.00"), # new required field |
| 27 | + portfolio_value=75000, |
| 28 | + updated_ts=ts, |
| 29 | +) |
| 30 | +``` |
| 31 | + |
| 32 | +The accompanying optional `balance_breakdown: list[IndexedBalance] | None` |
| 33 | +splits the total across exchange shards when present. `IndexedBalance.balance` |
| 34 | +is `DollarDecimal` (matching `balance_dollars` units), not cents — same |
| 35 | +field name as `Balance.balance` but a different type. Be deliberate when |
| 36 | +reading from `balance.balance_breakdown[i].balance`. |
| 37 | + |
| 38 | +### V2 event-market orders |
| 39 | + |
| 40 | +Six new methods on `OrdersResource` / `AsyncOrdersResource` hit the new |
| 41 | +`/portfolio/events/orders/*` paths: |
| 42 | + |
| 43 | +- `create_v2(*, request: CreateOrderV2Request)` |
| 44 | +- `cancel_v2(order_id, *, subaccount, exchange_index)` |
| 45 | +- `amend_v2(order_id, *, request: AmendOrderV2Request, subaccount)` |
| 46 | +- `decrease_v2(order_id, *, request: DecreaseOrderV2Request, subaccount)` |
| 47 | +- `batch_create_v2(*, request: BatchCreateOrdersV2Request)` |
| 48 | +- `batch_cancel_v2(*, request: BatchCancelOrdersV2Request)` |
| 49 | + |
| 50 | +Legacy `/portfolio/orders` keeps working and will be deprecated no earlier |
| 51 | +than May 6, 2026. No migration is required to stay on v1 paths. New |
| 52 | +event-market trading should target the V2 family for the cleaner shape |
| 53 | +(single `bid`/`ask` side, single `price` field, explicit idempotency). |
| 54 | + |
| 55 | +Two important differences from V1: |
| 56 | + |
| 57 | +1. **`client_order_id` is required** on `CreateOrderV2Request` and acts as |
| 58 | + the server-side idempotency key. Reusing a value returns the original |
| 59 | + order rather than placing a new one. Use a fresh UUID4 per call. |
| 60 | +2. **`side` uses `BookSideLiteral` (`"bid"` / `"ask"`)**, not V1's |
| 61 | + `SideLiteral` (`"yes"` / `"no"`). |
| 62 | + |
| 63 | +### Spec-driven asymmetry on V2 amend/decrease |
| 64 | + |
| 65 | +`amend_v2` and `decrease_v2` accept `subaccount` as a **resource-method |
| 66 | +kwarg** (query param on the wire) but read `exchange_index` from the |
| 67 | +**request body**. `cancel_v2` differs again — both are query params there, |
| 68 | +because that endpoint has no body. This mirrors the spec exactly: |
| 69 | + |
| 70 | +```python |
| 71 | +client.orders.amend_v2( |
| 72 | + "ord-1", |
| 73 | + subaccount=3, # query param |
| 74 | + request=AmendOrderV2Request( |
| 75 | + ticker="EVENT-MKT", side="bid", |
| 76 | + price=Decimal("0.55"), |
| 77 | + count=Decimal("10"), |
| 78 | + exchange_index=0, # body field |
| 79 | + ), |
| 80 | +) |
| 81 | +``` |
| 82 | + |
| 83 | +### New optional kwargs on existing endpoints |
| 84 | + |
| 85 | +All additive — existing call sites keep working: |
| 86 | + |
| 87 | +- `orders.cancel(*, exchange_index)`, `order_groups.delete(*, exchange_index)` |
| 88 | +- `communications.list_rfqs(*, user_filter)`, `communications.list_all_rfqs(*, user_filter)` |
| 89 | +- `communications.list_quotes(*, user_filter, rfq_user_filter)`, `communications.list_all_quotes(*, user_filter, rfq_user_filter)`. `user_filter="self"` and `rfq_user_filter="self"` are now standalone satisfiers for the server-side filter requirement (previously only `quote_creator_user_id` / `rfq_creator_user_id` worked). |
| 90 | +- `incentive_programs.list(*, incentive_description)` and the `list_all` variant. |
| 91 | +- `CreateQuoteRequest.post_only` — pass `post_only=True` to `create_quote()`. |
| 92 | +- `exchange_index` on order/amend/decrease/batch-cancel request models. |
| 93 | + |
| 94 | +### New endpoints (additive) |
| 95 | + |
| 96 | +- `portfolio.deposits()` / `portfolio.deposits_all()` — deposit history. |
| 97 | +- `portfolio.withdrawals()` / `portfolio.withdrawals_all()` — withdrawal history. |
| 98 | +- `account.endpoint_costs()` — token costs for endpoints whose cost differs |
| 99 | + from the default. |
| 100 | + |
| 101 | +### New public types |
| 102 | + |
| 103 | +Added to `kalshi.*` and `kalshi.models.*`: |
| 104 | + |
| 105 | +- Literals: `BookSideLiteral`, `UserFilterLiteral`, `PaymentStatusLiteral`, `PaymentTypeLiteral`. |
| 106 | +- Models: `Deposit`, `Withdrawal`, `IndexedBalance`, `AccountEndpointCosts`, `EndpointTokenCost`, and the V2 request/response family. |
| 107 | + |
| 108 | +--- |
| 109 | + |
3 | 110 | ## v1.x → v2.0 |
4 | 111 |
|
5 | 112 | v2.0 is mostly additive (new `max_pages` kwarg, `KalshiConfig.http2`/`limits`, |
|
0 commit comments