Skip to content

v0.7.0

Choose a tag to compare

@aiiven aiiven released this 05 Nov 14:00
· 4 commits to main since this release
7c9972d

Built on public contract 748066a, the contract of backend 9.2.5. Breaking changes are marked Breaking below; read them before upgrading.

Package version: 0.7.0. Git tag: v0.7.0. Released 2026-11-05. Built on
public contract 748066a.

  • Breaking: a trailing stop created with an activation price already
    reached now ends FAILED with
    TRIGGER_FAILURE_REASON_TRIGGER_WOULD_TRIGGER_IMMEDIATELY (244).
    ModifyTrigger and order modifies that would make a trailing stop act at
    once are refused with ERROR_CODE_TRIGGER_WOULD_TRIGGER_IMMEDIATELY (302),
    leaving the existing state unchanged. To start trailing from the market,
    clear the activation price with a present zero.
  • Breaking: remove the public holds-list method, hold models and decoders.
    Read aggregate held trading funds from each asset's reserved balance in
    the balances API. Individual hold IDs and expiry times have no replacement.
  • Breaking: listing events for an unreadable trigger now returns the same
    not-found error as reading that trigger, instead of an empty history.
    Events reads retry a catching-up UNAVAILABLE with the trigger-read policy.
  • Breaking: SpotFeeRate gains public fields, so exhaustive struct literals
    must initialize them. SpotFeeRate and SpotFeeRatesList no longer implement Eq.
  • Expose reserve fee rates, the next scheduled change and the full fee
    breakdown. Omitted optional components remain absent and omitted account
    discounts decode as an empty list.
  • Breaking: Error::PermissionDenied, Error::ApiKeyRejected,
    Error::TimestampSkew and Error::SessionRequired gain
    optional problem metadata. Include .. in variant patterns when ignoring it.
  • Preserve REST problem status, code, detail and canonical type on realtime
    authorization and early gateway authentication errors.
  • Pin execution-history continuation pages to the returned exchange order ID
    so reuse of a client order ID cannot change generations between pages.

Attached-exit self-trade prevention, sub-account balances (contract
54dc59d).
Breaking for code that builds Order, GetSubaccountResult or
GetSubaccountOpts with a struct literal naming every field.

  • GetSubaccountOpts::include_balances asks a sub-account read for the
    sub-account's balances, and GetSubaccountResult.balances returns them as a
    BalancesList, the shape of balances.list. None unless asked for; an
    empty list when the sub-account holds none. It needs the READ_BALANCES
    action on the sub-account, or the read fails with PermissionDenied; a
    root-scoped API key cannot read a sub-account's balances. A read that
    cannot reach the balances right now fails with the retryable
    AUTH_TEMPORARILY_UNAVAILABLE.
  • Order.self_trade_prevention_mode reports the order's mode
    (expire_taker, expire_maker, expire_both, UNKNOWN(<n>) for a newer
    one). The order's attached take-profit, stop-loss and trailing-stop exits
    use it; set it with CreateOrderParams.self_trade_prevention.
  • An attached exit never trades with the order it protects. When it fires
    while that order still rests, the order's unfilled rest is canceled first
    and the order ends with cancel_reason CANCEL_REASON_ATTACHED_EXIT_FIRED
    (on reads and on the private order stream).
  • A self-trade prevention block of an attached exit is a refused fire, not an
    end: a fire_refused trigger event with fire_refusal_reason
    self_trade_prevented, whose child_seq names the child order self-trade
    prevention expired. The leg stays armed. Reject reason
    ERROR_CODE_SELF_TRADE_PREVENTED decodes too; no request is answered with
    it.
  • A trigger a halted market refuses now reads market_halted as its
    failure_reason or fire_refusal_reason, never the unnamed
    UNKNOWN_TRIGGER_FAILURE_REASON(212). Unknown reasons still decode with
    their number.
  • A buy sized by a quote amount can carry attached exits; the server checks
    the legs against the pair's minimums.
  • Docs: Order.cum_notional, cum_quote() and quote_used() are exactly the
    settled quote when the pair's base_quantity_scale is at most 9; above
    that they can exceed it by less than 1e-18 per fill.

Exact order totals, refused trigger fires, API-key refusal reasons
(contract e96897c).
Breaking for code that builds these types with struct
literals or matches Error exhaustively.

  • Order::avg_price() is the exact average execution price from the new
    Order.avg_price_e18 (18 decimals; avg_px stays rounded down to 1e-9).
    Order.cum_notional / Order::cum_quote(base_quantity_scale) is the exact
    quote amount of the fills before fees, and Order.fee_total
    (OrderFeeTotal: amount_e18 / amount(), is_rebate, qty) their fee
    net of rebates. Order::quote_used(base_scale) and
    Order::quote_unspent(base_scale, quote_scale) tell how much of a
    quote-sized buy's amount the fills used; None until the fee total covers
    every fill. All exact Decimals, zero with no fills, and None rather
    than a rounded value when a result needs more digits than a Decimal
    holds.
  • Trigger events: fire_refused (also a list_events filter) for a fire
    refused while the trigger stays live, with the new
    TriggerEvent.fire_refusal_reason (rate_limited, account_repairing,
    market_halted, min_notional, ...). It is not terminal. The failure reason
    account_repairing is named. Unknown event types and reasons still read as
    UNKNOWN(<n>) / UNKNOWN_TRIGGER_FAILURE_REASON(<n>).
  • Error::auth_code() returns the typed auth.v1.AuthErrorCode of a refusal
    on ConnectRPC (AuthErrorDetail) and REST (code with AUTH_ added,
    UNAUTHENTICATED as AUTH_AUTHENTICATION_REQUIRED);
    errors::auth_codes::from_rest_code maps a REST code, and auth_codes names
    the seven new values (SIGNATURE_INVALID, MISSING_HEADERS, KEY_EXPIRED,
    KEY_DISABLED, IP_NOT_ALLOWED, API_KEY_NOT_ACCEPTED, plus the existing
    TIMESTAMP_SKEW).
  • Changed: a ConnectRPC AUTH_TIMESTAMP_SKEW detail is the retryable
    Error::TimestampSkew, and the other API-key reasons are
    Error::ApiKeyRejected with the REST form of the code (SIGNATURE_INVALID),
    the same errors the API gateway's answers map to. They were Error::Api.
  • New Error::SessionRequired (is_session_required()) for
    AUTH_SESSION_KIND_NOT_ALLOWED: a session-only method called with an
    account API key, read from the Connect permission_denied detail and the
    REST 403 code alike. It was Error::Api on Connect and
    Error::ApiKeyRejected on REST.
  • Error::Api gains violations (Error::violations()): the request fields
    an order refusal names, with ConnectRPC field paths. A missing or wrong
    attached trailing-stop maximum on create, preview or batch create is now
    refused before sending with the server's code, rule ID and field path
    (ERROR_CODE_BAD_PRICE / sell_unset or buy_at_least_activation,
    ERROR_CODE_TRIGGER_BUY_REQUIRES_LIMIT / buy_required) as Error::Api
    instead of Error::Validation; a modify names new_attached_risk... and
    a batch replace item items[N].new_attached_risk....
  • Error::is_order_already_terminal() and
    order_codes::ORDER_ALREADY_TERMINAL / ORDER_UNKNOWN: a cancel of an order
    that has already ended, a repeat of the cancel that ended it included, is
    now always refused with ERROR_CODE_ORDER_ALREADY_TERMINAL. Treat it as
    "already done".
  • client.stop_all_trading(scope) cancels every live trigger, then every open
    order. Cancel-all and the dead-man switch cancel orders only, never
    triggers; cancel_all, cancel_all_after and cancel document it.
  • Fixed: with WireFormat::Json, a response naming an enum value newer than
    this SDK (a new changelog surface, for example) failed the whole call with
    a retryable Error::Server. The value now reads as unset (and is dropped
    from a repeated field), as protojson's DiscardUnknown does; binary
    responses keep the value's number.
  • Changelog: surface labels market_data, quickswap and rewards;
    ChangelogSection.surface names the product area a section covers;
    list_changes refuses more than 10 surfaces before sending.
  • Docs: an API key's permission_denied message names the missing policy
    action and reaches the caller as is; a quote-sized buy that spent its amount
    ends filled; a ladder whose levels were all canceled before any filled
    ends cancelled.

New: public product changelog (client.changelog). Read the changelog of
the environment you call without an API key. It is available on
devnet now and on testnet with its next release.

  • list_posts, get_post and list_changes wrap
    changelog.v1.ChangelogService; posts and changes stream every page.
  • breaking_changes_since(t) and action_required_since(t) collect the
    matching changes published since t.
  • Typed models ChangelogPostSummary, ChangelogPost, ChangelogSection,
    ChangelogChange and ChangelogChangeEntry. Enums are lowercase labels and
    unknown values read as unknown(<n>); change ids are base58.
  • The generated module tree now compiles changelog.v1.

Batch replace refusals, trigger read-your-writes, owner role lock.

  • orders.wait_batch_replace(batch_request_id, subaccount_id, timeout) polls
    get_batch_replace_status with a short backoff until no item is
    admitted, polling through the not_found and unavailable answers of the
    first seconds after admission. It stops at the 60-second status window (or
    an earlier timeout) with Error::Transport: reconcile through the orders
    then.
  • BatchReplaceStatusItem::error_code() turns an item's code
    (POST_ONLY_CROSS) into the orders error code (ERROR_CODE_POST_ONLY_CROSS),
    keeping a code this SDK does not know unchanged;
    errors::order_codes::from_status_code does the same for any string.
  • Fixed: get_batch_replace_status failed with Error::ResponseContract for
    an item admitted and then refused when applied (phase rejected, still
    counted in accepted_count). It now decodes; an item rejected at admission
    must still read rejected.
  • A status item with no successor, every rejected item included, has an
    empty replacement_order_id (was "0"). The admission's planned
    replacement_order_id is not proof of an order.
  • triggers.get and triggers.list (and get_by_id, list_with) retry
    unavailable / HTTP 503 with the same request: 25 ms, doubling, at most
    500 ms, giving up after about 2 s. The server answers it right after a
    trigger change until the change is readable.
  • errors::auth_codes::SUBACCOUNT_OWNER_ROLE_LOCKED and
    Error::is_subaccount_owner_role_locked(): a member call targeting the
    sub-account's owner. A REST 412 problem with that code now maps to the same
    Error::Api as the ConnectRPC failed_precondition answer.
  • Docs: batch replace items amend in place or replace, as the trading
    WebSocket schema now states; market-halt cancel reasons on orders
    (CANCEL_REASON_MARKET_HALTED) and triggers (market_halted).