# Changelog
All notable changes to this package are documented in this file.
## 0.8.21 - 2026-07-10
### Added
- Added GET /api/v1/orders/conditional/{conditionalOrderId} to the vendored OpenAPI spec. The generated Rust REST SDK now exposes get_conditional_order for fetching a single TP/SL conditional order by its UUID.
### Changed
- Realized PnL semantics updated for the unrealized-PnL rollout - spot withdrawals are now booked as fair-value disposals (realizing (last trade price - average cost) x quantity), deposits and withdrawals are marked at the last trade price for the pair in event order (previously the covering 1m candle close), transfers that predate any trade on the pair are PnL-neutral, and short-entry fees now reduce short profits instead of inflating them. Affects values returned by getPortfolioStats (pnl) and the getPortfolioChart pnl metric.
### Fixed
- A taker order's realized fee fields (monacoTakerFee, totalTakerFees, takerTotalPayment) now report the tier-resolved fee the fill was actually charged, matching the order's balance movement. Previously these reported the flat trading-pair rate, which diverged from the tiered rate debited at settlement.
- POST /api/v1/margin/risk-buckets/simulate-order-risk with marginMode=CROSS now works before the user's cross risk bucket has been initialized. The preview uses the same deterministic cross risk bucket id and first-use auto-collateral estimate as order placement, instead of returning "No cross risk bucket found" for the first cross order preview.
No request or response fields changed.
- POST /api/v1/orders and POST /api/v1/orders/batch-create now reject ambiguous parent margin-account orders that omit marginMode and do not provide an isolated risk-bucket selector, instead of silently routing them through the isolated risk-bucket path. Clients should send marginMode=CROSS or marginMode=ISOLATED for parent margin-account margin orders.
No request or response fields changed.
## 0.8.20 - 2026-07-09
### Changed
- POST /api/v1/orders and POST /api/v1/orders/batch-create now treat marginMode=CROSS like isolated auto risk-bucket order placement.
When the authenticated user's margin account has no active cross risk bucket, the API resolves the deterministic cross risk bucket, estimates required order collateral, and forwards that bucket/collateral to matching-engine admission. When an active cross risk bucket exists but does not yet include the order tradingPairId, the API adds that trading pair to the cross risk-bucket scope before forwarding the order.
No request or response fields changed.
- Cross-tenant access to a sub-account's spending limits now returns Not Found (HTTP 404 / gRPC NOT_FOUND) instead of Forbidden (403 / PERMISSION_DENIED) when the sub-account belongs to another master. This matches the create/update/delete limit RPCs and makes an unauthorized read indistinguishable from a nonexistent sub-account. No request or response fields changed.
### Fixed
- simulateFees now quotes the caller's tier-resolved fee (monacoTakerFee, totalTakerFees, takerTotalPayment) — the rate the ledger actually debits — instead of the flat trading-pair rate, which under-quoted the fee at any tier whose rate exceeds the flat column.
- Replacing an isolated margin order with a lower notional now returns the freed collateral to the parent account, so getMarginAccountSummary / getParentMarginAccountSummary / getAvailableCollateral report the higher freeCollateral immediately. Previously the bucket's allocated collateral was left unchanged on replace, so freeCollateral stayed stale at its pre-replace value until the order was cancelled or filled.
## 0.8.19 - 2026-07-07
### Added
- Expose cross margin as a risk-bucket mode on margin risk-bucket transfer and simulation APIs, including selected trading pairs and resolved risk-bucket metadata in SDK responses.
- REST create order and batch create order requests now accept optional marginMode ("ISOLATED" or "CROSS") for authenticated margin order placement. Passing "CROSS" requires marginAccountId, routes the order to the active cross risk bucket for the selected tradingPairId, and cannot be combined with riskBucketId, riskBucketCollateral, or strategyKey.
Position list/get responses now include optional marginMode so clients can distinguish cross and isolated risk-bucket positions. Margin account list/summary responses now include selectedTradingPairIds on cross risk-bucket rows so clients can restore the cross selected-pair scope from account reads.
## 0.8.18 - 2026-07-07
### Added
- Added get_my_fee_tier (GET /api/v1/fees/tier): returns the authenticated caller's current fee tier, rolling 14-day volumes (weighted plus the spot/perp breakdown), the volume needed to reach the next tier, and the requested trading pair's six-row fee schedule.
## 0.8.16 - 2026-07-06
### Fixed
- REST portfolio endpoints (GET /api/v1/accounts/me/portfolio and .../portfolio/chart?metric=pnl): pnl now returns correct average-cost realized PnL (spot + perps incl. funding, net of fees, booked only on position reduction/close) instead of net quote cash-flow. stats win_loss_ratio and max_drawdown are likewise derived from realized closes; perps_equity (previously hard-coded 0) and total_equity now include margin-account equity. Field names and shapes are unchanged.
## 0.8.15 - 2026-07-04
### Added
- Added `list_pending_withdrawals` (GET /api/v1/withdrawals; authenticated). Lists the caller's pending withdrawals (status "pending", awaiting on-chain confirmation), scoped to the authenticated user + application, newest first, paged.
Query params: page (optional, 1-based, default 1), page_size (optional, 1–100, default 20).
Response: withdrawals[] { withdrawal_index (i64), asset_id (uuid), asset_symbol, amount (raw smallest-unit string), destination (EVM address), status ("pending"), created_at (RFC 3339) }, plus total, page, page_size, total_pages. Calldata is not included — fetch it from GET /api/v1/withdrawals/{withdrawal_index} once confirmed.
### Changed
- BREAKING: every REST JSON field, request body key, and query parameter is now camelCase instead of snake_case (e.g. trading_pair_id -> tradingPairId, page_size -> pageSize, order_ids -> orderIds). Applies to all endpoints. Request bodies with unknown (old snake_case) keys are rejected with 400 via strict deserialization; there is no dual-casing grace period. Error envelopes follow suit (status_code -> statusCode, details keys validation_errors -> validationErrors, retry_after -> retryAfter), and validation error messages reference the camelCase field names. Enum VALUES (e.g. "BUY", "TRANSFER_IN") and WebSocket event payloads are unchanged. Generated Rust client idents stay snake_case with one exception: fields with digit segments lose the underscore before the digits (remaining_requests_24h -> remaining_requests24h, high_24h -> high24h, snapshot_7d -> snapshot7d).
### Fixed
- Fix margin orders being rejected with "insufficient balance" when the account's collateral lives in an existing isolated risk bucket.
- Affects REST `POST /api/v1/orders` (create order) and the generated Rust REST SDK; no request or response fields change.
- Behavior: a margin order that does not name a bucket (`risk_bucket_id`) and is not creating one (`risk_bucket_collateral`) now reserves against the existing isolated bucket for the order's `(margin_account_id, trading_pair_id)` when one exists, instead of the cross/parent account. Previously such orders reserved against the parent account — which is empty once collateral has been allocated to a bucket — and were rejected with `Insufficient balance: Available: 0, Required: ...` even though the account was fully funded. Cross-margin orders and orders that explicitly name or create a bucket are unaffected.
- Margin order creation now auto-resolves server-managed risk buckets from trading_pair_id and strategy_key before calling the matching engine, so SDK clients can omit risk_bucket_id without creating bucketless margin orders.
- Margin order creation through `POST /api/v1/orders` now auto-initializes the
default/strategy isolated risk bucket from parent margin collateral when no
`risk_bucket_id` or `risk_bucket_collateral` is provided and no active bucket
exists.
The server computes the collateral as initial margin plus taker fees using the
order reserve price, then forwards it through the existing
`risk_bucket_collateral` matching-engine path.
- Public GET /api/v1/market/pairs/{tradingPairId}/funding now reports
fundingIntervalSeconds from the market funding_interval_hours configuration
when available, instead of deriving the public cadence from the latest
settlement window duration.
Historical funding records still expose actual window start and close times.
## 0.8.14 - 2026-06-30
### Added
- Add `batch_close_all_positions` to the Rust REST SDK — `POST /api/v1/positions/batch-close-all` (auth: monacoSignature). Closes every open perpetual position for the caller with MARKET reduce-only orders, optionally filtered by `trading_pair_id`, with an optional `slippage_tolerance_bps`. Returns aggregated `total_requested`/`total_closed`/`total_failed` counts plus a per-position `results` array (each with `position_id`, optional `close_order_id`/`status`/`submitted_quantity`, and an optional `error` code/message). Best-effort: per-position failures are reported rather than aborting the batch.
- Regenerated the REST SDK spec with explicit parent margin account and risk bucket collateral routes.
- POST /api/v1/margin/parent-margin-account/collateral/transfer-in (user auth): asset and amount move spot collateral into the caller's parent margin account only; no trading_pair_id is accepted and no risk bucket is opened.
- POST /api/v1/margin/parent-margin-account/collateral/transfer-out (user auth): asset and amount move unallocated parent margin account collateral back to the spot wallet.
- POST /api/v1/margin/risk-buckets/collateral/transfer-in (user auth): asset, amount, trading_pair_id, and optional strategy_key allocate collateral into the selected risk bucket.
- POST /api/v1/margin/risk-buckets/simulate-order-risk (user auth): trading_pair_id, optional strategy_key, side, position_side, order_type, optional price, quantity, leverage, and optional reduce_only simulate order risk against the selected risk bucket.
Transfer responses include movement_id, margin_account_id, asset, amount, status, new_equity, new_total_collateral_value, new_withdrawable_collateral, and optional strategy_key.
- Added Rust REST SDK coverage for authenticated idless parent margin account endpoints:
- `GET /api/v1/margin/parent-margin-account` with optional `trading_pair_id`, returning `MarginAccountSummary`.
- `GET /api/v1/margin/parent-margin-account/movements` with optional `movement_type`, `page`, and `page_size`, returning paginated margin account movements.
- `POST /api/v1/margin/parent-margin-account/simulate-order-risk`, accepting order-risk simulation fields without `margin_account_id`, returning `SimulateOrderRiskResponse`.
These endpoints resolve the parent account from the authenticated wallet/application scope; legacy id-based endpoints remain available.
- Balance endpoints (GET /api/v1/accounts/me/balances and GET /api/v1/accounts/me/balances/{asset_id}) now return `total_balance_raw`: the raw, smallest-unit total balance (`available_balance_raw + locked_balance_raw`). Complements the existing normalized `total_balance` for raw-field consistency with the existing `available_balance_raw`/`locked_balance_raw` fields. Additive, optional string.
### Changed
- Withdrawals migrated to the merkle-proof executeWithdrawal path. GET /api/v1/withdrawals/{withdrawal_index} now returns executeWithdrawal(...) calldata and responds 409 while the withdrawal's root is not yet confirmed on-chain (404 until the row is persisted); POST /api/v1/withdrawals returns an empty calldata field (only the withdrawal_index + vault_address). Poll the GET endpoint until it returns 200 with calldata, then submit it on-chain.
- Market category filters and responses now recognize `fx` as a supported asset-class category alongside `crypto`, `equities`, and `commodities`.
This lets the new EUR/USDC and GBP/USDC perp markets be exposed and filtered without being treated as crypto.
- `POST /api/v1/withdrawals` now supports `source: "margin"` to directly debit parent margin account collateral into the external wallet flow while enforcing parent withdrawable collateral.
- Withdrawals: `GET /api/v1/withdrawals/{withdrawal_index}` now returns 409 Conflict ("not confirmed on-chain yet") instead of 404 Not Found while a freshly-initiated withdrawal's proof is pending — the row is now created at initiate time. Clients polling for the `executeWithdrawal` calldata should treat 409 as retry-and-wait; a 404 now means the index was never allocated. Request/response shapes are unchanged.
## 0.8.11 - 2026-06-23
### Added
- MarginAccounts: GetAvailableCollateralResponse gained an optional margin_available_collateral field — the free collateral in the caller's parent margin account for the requested asset (equity minus initial margin required), distinct from the spot-wallet wallet_available. Absent when the user has no margin account.
- Trading pair responses now include an asset-class `category` field (`crypto`, `equities`, or `commodities`).
- `GET /api/v1/market/pairs`, `GET /api/v1/market/pairs/{trading_pair_id}`, and `GET /api/v1/market/screener` responses now expose `category`.
- `GET /api/v1/market/pairs` and `GET /api/v1/market/screener` accept an optional `category` query parameter to filter by asset class.
- Market endpoint for life-to-date cumulative statistics.
New `GET /api/v1/market/stats` (no auth) returning exchange-wide cumulative totals:
- `total_quote_volume_ltd` (string) — cumulative quote-token volume across all pairs
- `total_trade_count_ltd` (int64) — cumulative trade count across all pairs
Per-pair life-to-date fields also added to the existing responses:
- `GET /api/v1/market/pairs/{trading_pair_id}/metadata`: `total_base_volume_ltd`, `total_quote_volume_ltd` (strings), `total_trade_count_ltd` (int64)
- `GET /api/v1/market/screener` items: `total_quote_volume_ltd` (string), `total_trade_count_ltd` (int64)
Volume fields are decimal strings in display units; pairs with no trades report "0"/0, never null.
### Changed
- Public OpenAPI server metadata now lists Staging as the documented REST base
URL. Endpoint paths, auth requirements, request fields, and response fields
are unchanged; Develop and Local remain internal contributor targets only.
### Fixed
- Direct parent margin account transfer-in and transfer-out without `trading_pair_id` now stay scoped to the parent account, even when an active cross-margin bucket exists. Parent transfer-out also honors live collateral during projection lag instead of rejecting it as isolated bucket collateral.
## 0.8.10 - 2026-06-18
### Added
- Resync the Rust REST SDK (`monaco-sdk`) from the canonical OpenAPI spec. The vendored spec the client generates from had drifted and was missing most of the API; it now exposes every REST operation, adding ~38 previously-absent methods:
- Margin accounts: `list_margin_accounts`, `get_margin_account_summary`, `ensure_parent_margin_account`, `get_margin_account_movements`, `get_available_collateral`, `transfer_collateral_to_margin_account`, `transfer_collateral_from_margin_account`, `transfer_collateral_to_auto_margin_account`, `simulate_order_risk`, `simulate_auto_margin_order_risk`.
- Positions: `list_positions`, `get_position`, `close_position`, `get_position_risk`, `add_position_margin`, `reduce_position_margin`, `list_position_history`, `attach_position_tp_sl`.
- Perp markets: `get_perp_market_config`, `get_perp_market_summary`, `get_mark_price`, `get_index_price`, `get_funding_state`, `list_funding_history`.
- Conditional orders: `create_conditional_order`, `cancel_conditional_order`, `list_conditional_orders`.
- Also: market `get_screener` / `get_open_interest`; trades `get_trade_by_id` / `get_user_trades` / `list_user_trades`; accounts `list_funding_payments` / `get_portfolio_stats` / `get_portfolio_chart`; the applications and delegated-agents surfaces.
Breaking rename: the auth refresh method is now `refresh_session` (was `refresh_token`). The endpoint is unchanged (`POST /api/v1/auth/refresh`); it was repurposed from JWT-token refresh to session-key expiry extension during the v0.8.8 auth cutover, and the SDK method name now matches.
The vendored spec is no longer hand-maintained: `make docs-gen` regenerates it from the canonical spec via the `sync-rust-sdk-spec` transform, and a CI drift gate fails if the committed copy is stale.
### Changed
- `POST /api/v1/orders` and `POST /api/v1/orders/batch-create`: `strategy_key` (optional string) is now documented in the OpenAPI spec for the request body. The REST handler already accepted this field; this change just brings the spec / generated Rust REST SDK type into line with shipping behaviour.
`POST /api/v1/margin/accounts/{margin_account_id}/simulate-order-risk` and `POST /api/v1/margin/auto/simulate-order-risk`: response now documents `margin_account_id` (string, always populated) and optional `strategy_key` (populated for auto-resolved buckets). The REST handler already returned these fields; the OpenAPI spec / generated Rust REST SDK response type are now in line.
- `UserTrade.executed_at` returned by `GET /api/v1/accounts/trades` (session-signed auth required) now serializes as RFC3339 with an explicit `+00:00` UTC offset (e.g. `2025-01-15T10:30:00+00:00`) instead of the previous bare `Z` suffix (e.g. `2025-01-15T10:30:00Z`).
- Endpoint: `GET /api/v1/accounts/trades` — returns the authenticated account's trades.
- Field: `executed_at` (string, optional) — trade execution timestamp.
- This aligns the REST timestamp format with the gRPC/proto trade serialization, which already emits the numeric `+00:00` offset.
- Both forms denote the same UTC instant, so any RFC3339/ISO-8601 date-time parser is unaffected; only code doing a literal string comparison against a trailing `Z` needs updating.
- Two REST-surface corrections from the spec-vs-reality audit:
- `GET /api/v1/accounts/funding-payments` (`list_funding_payments`) is now served by the gateway with auth, pagination, and optional `trading_pair_id` / `position_id` / `margin_account_id` filters. Response is `ListFundingPaymentsResponse` (`records`, `total`, `page`, `page_size`, `total_pages`). It was previously advertised in the spec/SDK but returned 404.
- `GET /api/v1/trades/user` (`list_user_trades`) is removed from the REST surface; it is now gRPC-only. It duplicated `GET /api/v1/accounts/trades` (`get_user_trades`).
- Update the Rust REST SDK OpenAPI spec for cross-margin parent account initialization.
- Affects REST `POST /api/v1/margin/accounts` with session authentication.
- Request fields added: optional `margin_mode` string (`ISOLATED` or `CROSS`) and repeated `selected_trading_pair_ids` UUID strings. `selected_trading_pair_ids` is required when `margin_mode` is `CROSS`.
- Response fields added: optional `margin_bucket_id`, optional `margin_mode`, and repeated `selected_trading_pair_ids`.
- Semantic change: callers can idempotently ensure a parent account and its single parent-scoped cross-margin bucket in one request, replacing the selected trading-pair membership on repeated calls.
- Margin account summary endpoints now separate parent account summaries from risk bucket summaries.
Affected authenticated REST endpoints:
- GET /api/v1/margin/accounts/{margin_account_id}: margin_account_id is required; optional trading_pair_id now selects a bucket summary. When trading_pair_id is omitted, the response is always the parent account summary.
- GET /api/v1/margin/accounts: optional page, page_size, state, and trading_pair_id filters are unchanged. Without trading_pair_id, the response now contains parent rows plus separate risk bucket rows instead of using bucket rows as account replacements.
Response fields now include optional margin_bucket_id and margin_mode. Parent rows leave margin_bucket_id, margin_mode, trading_pair_id, and strategy_key unset; bucket rows set margin_bucket_id and margin_mode, and keep trading_pair_id and strategy_key when available.
- `GET /api/v1/fees/simulate` (session signature auth) is now a thin wrapper over the shared fee-simulation handler; the success response JSON is unchanged.
- Request query params are unchanged: `trading_pair_id`, `side`, `price`, `quantity` (required) plus optional `order_type` and `slippage_tolerance_bps`.
- Malformed `trading_pair_id`, `price`, or `quantity` now return `400` with the messages `Invalid trading pair ID format` / `Invalid price format` / `Invalid quantity format` (previously a generic query-deserialization error), matching the gRPC surface.
- Unknown query parameters are now ignored rather than rejected with `400`.
- Response fields, types, and omitted-when-unset optionals are unchanged.
- Health check now returns the shared proto `PublicHealthCheckResponse` from the `handlers` crate.
- Affects REST `GET /health` (no authentication).
- Response fields (unchanged): `status` (string, "healthy"), `service` (string, "api-gateway"), `version` (string), `timestamp` (integer Unix epoch seconds).
- No change to the JSON wire shape; the endpoint is now a thin pass-through to the shared handler rather than building its own payload.
- Trade response types in the generated Rust REST SDK now match the actual REST wire shape for `GET /api/v1/trades/{trading_pair_id}` and `GET /api/v1/trades/by-id/{trade_id}` (public, no auth).
- `PublicTrade` is now `{ data: TradeData, event_type, trading_pair_id, trading_mode }`; `TradeData` carries `{ executed_at?, maker_side, price, quantity, quantity_raw, trade_id }`.
- `GetTradeByIdResponse` serializes bare (the envelope fields directly), no longer wrapped under `trade`.
- Gotcha: the REST HTTP responses are unchanged — the previous generated types were flat and did not match the server's nested output; this corrects the SDK.
- The market endpoints (`/api/v1/market/*`: trading pairs, candles, metadata, screener, open interest, funding state/history, perp config/summary, mark/index price) are now served by the shared handler that also backs the gRPC API.
Response wire shape and decimal precision are unchanged — the REST gateway remains the source of truth, so existing clients of the Rust REST SDK see no change to request parameters or response bodies. This entry records the consolidation that brought the gRPC surface into parity with REST.
- Serve the positions REST endpoints from shared handler logic. The JSON request and response wire shapes are unchanged — this is an internal consolidation with no contract change for SDK consumers.
- Affects `GET /api/v1/positions`, `GET /api/v1/positions/history`, `GET /api/v1/positions/{position_id}`, `GET /api/v1/positions/{position_id}/risk`, `POST /api/v1/positions/{position_id}/close`, `POST /api/v1/positions/{position_id}/margin/add`, `POST /api/v1/positions/{position_id}/margin/reduce`, and `POST /api/v1/positions/{position_id}/tp-sl` (all session signature auth).
- Validation, decimal/precision parsing, trigger-price direction checks, margin-bucket-state checks, pagination, and all matching-engine interaction now live in the shared service; the REST handlers are thin pass-throughs. Status codes, error messages, field set, ordering, and `null`/omitted-field behavior are preserved.
- Order response types are corrected to match what the REST API has always returned (the spec previously under-described them).
- `GetOrderResponse` (returned by `GET /api/v1/orders` and `GET /api/v1/orders/{order_id}`) gains: `quantity_raw`, `filled_quantity_raw`, `remaining_quantity`, `remaining_quantity_raw`, `client_order_id`, `cancelled_at`, `filled_at`, `expired_at`, `submitted_at`, `acknowledged_at`, `trigger_price`, `quote_volume`, `application_id`, `parent_order_id`, `batch_id` (all optional strings).
- The matching result embedded in create/replace/batch responses renames its `order_status` field to `status`, matching the JSON the API actually emits.
- `GET /api/v1/orders` documents the existing optional `order_by` query parameter (`ASC`/`DESC`, default `DESC`).
- No change to the HTTP wire: requests and JSON responses are byte-for-byte identical; only the generated SDK types/spec are brought in line with the live contract.
- REST margin-account APIs now enforce one parent margin account per
authenticated user/application scope.
Affected endpoints:
- POST /api/v1/margin/accounts (ensure_parent_margin_account, user-authenticated)
remains idempotent. Request fields are unchanged: optional label, optional
collateral_asset, optional margin_mode, and repeated selected_trading_pair_ids.
Response fields are unchanged: margin_account_id, account_state,
collateral_asset, created_at, optional label, optional margin_bucket_id,
optional margin_mode, and selected_trading_pair_ids.
- GET /api/v1/margin/accounts (list_margin_accounts, user-authenticated) now
lists parent and bucket summaries only for the authenticated application
scope.
Clients should treat margin_account_id as the wallet's single parent account
for the current app and use risk buckets for isolated/cross portfolio state.
- Expose bucket-scoped direct margin collateral transfer request fields and strategy_key responses in the vendored OpenAPI spec for the Rust REST SDK.
### Removed
- Remove the generated `create_conditional_order` client method (`POST /api/v1/orders/conditional`) from the Rust REST SDK. The endpoint was advertised in the OpenAPI spec but had no REST handler and returned 404; standalone conditional TP/SL create is deprecated. Use `create_order` with attached TP/SL, or `attach_position_tp_sl`.
- Removed the public EnsureParentMarginAccount / POST /api/v1/margin/accounts provisioning endpoint and the corresponding SDK methods/types.
Parent margin account creation is now internal to margin funding flows. Clients should use TransferCollateralToAutoMarginAccount / POST /api/v1/margin/collateral/transfer-in instead.
Replacement request fields: asset (string), amount (decimal string), tradingPairId/trading_pair_id (margin trading pair UUID), and optional strategyKey/strategy_key.
Replacement response fields include margin_account_id, movement_id, asset, amount, status, new_equity, new_total_collateral_value, and new_withdrawable_collateral.
## 0.8.8 - 2026-06-04
### Added
- Add `margin_locked` and `margin_locked_raw` fields to the `AccountBalance` and `GetBalanceByAssetResponse` messages, exposing collateral posted into margin (perps) accounts on a per-asset basis.
- Affects REST `GET /api/v1/accounts/balances` and `GET /api/v1/accounts/balances/{asset_id}`, and gRPC `AccountsService.GetBalances` / `GetBalanceByAsset`.
- `margin_locked` (string): collateral locked into margin (perps) accounts, in token units.
- `margin_locked_raw` (string, optional): the same value in the smallest token unit.
- Gotcha: `total_balance` already equals `available_balance + locked_balance + margin_locked`; this change breaks out the previously-unlabeled collateral portion and corrects the `total_balance` description to match.
## 0.8.7 - 2026-05-28
### Added
- remove redis fields from response (eg latest_orders)
### Fixed
- Margin account `withdrawable_collateral` and the transfer-in/out responses' `new_withdrawable_collateral` now return the amount that can actually be moved back to spot — `MIN(free_collateral, wallet.margin_locked)` — instead of `available_margin` alone.
Previously the field could overstate withdrawability when an account had realized PnL gains or lower live free collateral: equity-based `available_margin` includes PnL but the BE only releases up to what was originally transferred in and what live risk allows. Using the field as the Max amount for `POST /accounts/{id}/collateral/transfer-out` could fail with "Insufficient wallet margin-locked collateral".
Also fixes a regression where `transfer-out` rejected all amounts with "Insufficient margin-locked collateral for this margin account asset" for users whose pre-existing `wallet.margin_locked` had no corresponding `margin_account_transfer_in` ledger entries. The ledger-based check has been removed; the wallet UPDATE's `margin_locked >= amount` guard remains the safety gate (matching the gRPC path's existing behavior).
Affects: `GET /accounts`, `GET /accounts/{id}`, `POST /accounts/{id}/collateral/transfer-in`, `POST /accounts/{id}/collateral/transfer-out`.
- Position margin reduction now resolves the collateral asset from the margin bucket/default collateral asset instead of historical transfer ledger rows, so accounts funded before margin transfer ledger entries existed can reduce isolated margin when wallet margin-locked collateral is available.
## 0.8.5 - 2026-05-25
### Added
- Add GET /api/v1/applications/stats endpoint exposing volume and fee aggregates for the authenticated application. Backend-auth only (Authorization: Bearer sk_...).
Query params:
- since (optional): ISO 8601 datetime to filter from. Omit for all-time.
Response: { volume, maker_fee, taker_fee, application_taker_fee, trade_count } — stats scoped to trades where the application's users were the taker.
## 0.8.4 - 2026-05-20
### Fixed
- Updated SDK to match 0.8.4 API changes
## 0.8.1 - 2026-05-12
### Fixed
- Retrigger release pipeline after 0.8.0 publish failure
## 0.8.0 - 2026-05-12
### Changed
- sdk update
## 0.7.8 - 2026-05-06
### Changed
- Synchronized repository metadata with the already-published 0.7.8 SDK release.