Skip to content
Cirrus home
Sign in

Changelog

Every change you can see, newest first. Additive changes never break a client that ignores unknown fields; breaking ones are announced before they ship.

  1. Additive

    Partner tokens carry an audience; MCP channel attribution

    • Stored partner access tokens (Redis) gain an optional audience (api, the default for every token issued before, or mcp). Readers first: only clients from dynamic registration (above) get mcp tokens, and registration is turned on only once every pod runs this build; an mcp token is accepted only through the Cirrus MCP (its X-Cirrus-Mcp-Key service credential, with OMS_MCP_SERVICE_SECRET set and the tenant's agent_access on), else 401.
    • A request carrying an X-Cirrus-Mcp-Key that is not the MCP's is refused with 401 unauthorized. Requests without the header are unchanged.
    • With a valid service credential, the MCP's forwarded agent IP (X-Cirrus-Mcp-Client-Ip) is used for API-key IP allowlists and throttles, and activity records via: mcp with agent and tool (fields announced in "Activity names the channel and connection"). These headers are ignored on every other request. Deployments without OMS_MCP_SERVICE_SECRET see no change.
  2. Additive

    Stored samples for partner OAuth records

    Stored-record fixtures for partner access tokens, connections, apps and refresh tokens, with reader tests. No REST response, webhook or stream message changes.

  3. Additive

    Stored records gain optional created_by

    Deployments and v2 trigger records store who created them (app, key:{id} or partner:{client_id}). Stored-record change only: no REST response, webhook or stream message shows it yet. Old records read as absent; new fixtures added.

  4. Additive

    Strategy, deployment, backtest, risk-limit and basket actions are recorded in activity

    Strategy, deployment, backtest, risk-limit and basket actions are now recorded in activity (kinds added 2026-10-30). No request or response shape changes; the kinds are announced in the entry below and ship in the same release as the code that reads them (owner decision). During the rolling deploy (one replica, a window of seconds) a pod still on the previous build reads a record of a new kind as unreadable: it is logged and counted (infra::unreadable, the watchdog alerts), never dropped, and reads normally once the new build serves.

  5. Breaking

    Activity kinds for strategies, deployments, backtests, baskets and connection limits

    • GET /v1/activity and its filters (kind, category) now accept and may return 16 new kind values: strategy_created, strategy_updated, strategy_archived, strategy_version_saved, deployment_started, deployment_paused, deployment_resumed, deployment_stopped, deployment_went_live, deployment_exit_now, strategies_stop_all, backtest_submitted, risk_limits_changed (category strategies), basket_saved, basket_deleted (category orders) and connection_limits_changed (category access).
    • New category value strategies.
    • The values are readable and recorded from the same release (the entry above; owner decision, no separate readers-first release). During the rolling deploy (one replica, a window of seconds) a pod on the previous build reports a new-kind record as unreadable (logged and counted, the watchdog alerts); nothing is lost. Notified: 2026-10-31, owner: no external consumer affected — partners with read access (Kuberhunt) only send webhooks and do not read /v1/activity or call /v1/triggers; Pocketful/5Paisa users have no public API; the app moves to /v2 in app-cirrus#322.
  6. Additive

    Activity names the channel and connection

    • Activity records (GET /v1/activity, /v1/activity/{id}, the /v1/stream activity messages) carry four optional fields on source, omitted when unknown: client_id (the partner connection, partner sources), via (app, api or mcp), agent (name, version, verified) and tool (for via: mcp). Records made before this change have none of them.
  7. Additive

    Baskets for API keys and partner tokens; shared views for API keys

    • GET /v1/baskets, GET /v1/baskets/{id} need read; POST /v1/baskets, PUT and DELETE /v1/baskets/{id} need orders. API keys and partner tokens holding the scope may now call them (they were app-session only). This widens what an existing orders grant allows: a partner app the user already consented to with orders may now create, replace and delete that user's saved baskets without a new consent (saving a basket places nothing). Partners and users are told with this entry.
    • GET /v1/shared, /v1/shared/consolidated, /v1/shared/consolidated/{view}, /v1/shared/{owner}/{view}, POST /v1/shared/consolidated/refresh, POST /v1/shared/{owner}/refresh, GET /v2/shared and GET

    /v2/shared/strategies/{id} accept the account holder's app session or their own API key with read. Partner tokens get 403 forbidden ("Only available with your own API key or the app"). GET /v2/shared/{link_token} is unchanged.

  8. Breaking

    margin estimates for brokers without margin calculation

    • POST /v1/margins/orders (calculate_order_margins): results the broker cannot quote (error_code: not_supported_by_broker: Pocketful, 5paisa, Motilal Oswal, Tradejini, XTS, ODIN) or whose broker call failed now carry total_required = an estimate from the exchanges' published risk files where the instrument is in them (was null). message and error_code are unchanged on those rows. Rows the estimate cannot price stay null.
    • New on every result: source ("broker" | "estimate", null without a figure; a closed set), stale (bool) and flags (string list, open: clients ignore values they do not know, e.g. positions_unknown).
    • New accounts: one total per account (account, total_required, source, stale, flags, message) for every account that has a result: the sum of the account's results (source: "broker" when all are broker figures). When its orders hedge each other (futures / options of one underlying, or an exchange inter-commodity credit pair), the sum less the estimated hedge benefit (standalone estimates minus the hedged estimate), never below the hedged estimate (source: "estimate"); not when every result is the broker's and the estimate is stale. null with a message ("No margin figure for order #N.", 1-based) when any of the account's results has no figure (never a partial sum).
    • Top-level total_required keeps its meaning (sum of results with a figure) and now includes estimated results. Notified: not required for Kuberhunt (webhooks only, no webhook/stream/signal change); app-cirrus updated in the same release; API-key users told via the public API changelog when this deploys.
  9. Additive

    cirrus-oms reads the margin tables

    • New stored-record kind read by cirrus-oms: CIRRUS:MARGIN:INSTRUMENTS, CIRRUS:MARGIN:COMMODITIES and CIRRUS:MARGIN:META, written by Nothing users see changes yet. Real samples in cannot read (including a commodity stored under another commodity's field) is reported as margin_instrument / margin_commodity / margin_meta and left out. A refused META segment reads as stale; kept with today's trade_date is current.
  10. Change

    Pocketful refuses an order-book reply with no orders list; 10-30 evening ledger

    Behaviour (no field added, removed or retyped; no new enum value)

    • Pocketful order book: a 200 reply whose data holds other fields but no orders list (once, its funds view, on a pending read: Cirrus prod 2026-10-30, recording order_book-120cc967) now fails the read (unexpected response), read again on the next poll, instead of reading as an empty pending list that made every working order look gone. No existing expectation changed.
    • Paper error classes: cancel 400 "Order is CANCELLED and cannot be cancelled" is order_not_modifiable (book read back, as the COMPLETE refusal; the catalogue's w.cancel_twice); place 503 "Request timed out" is outcome_unknown (paper-go's 2 s handler deadline; the order may exist, the book decides); order-book 503 "Request timed out" is transient (a read, as the gateway timeouts on reads).
    • Conformance packs: new production recordings (Zerodha BFO / NSE CNC / gttp-meta order book rows order_book-d5ac9118, BFO positions positions-b3b1abc6 and trades trades-357184d0, the instruments_meta frame ws_ignore-179a896b; paper transport timeouts gtt_list-6d559932, place_order-70672464, modify_order-9a5b7481; paper refusals place_order-96540235, order_book-53e95c79, cancel_order-dfca95df). Additive.
  11. Change

    NSE holidays in every tenant profile

    Behaviour (no field added, removed or retyped; no new enum value)

    • The tenant profiles listed no NSE holidays, so strategies, the backtest calendar and volatility lookbacks treated NSE holidays as trading days. They now list the 2026 NSE holidays. Backtests whose lookback crosses a holiday give slightly different numbers: the recorded /v2/backtests examples were re-recorded (values only).
  12. Additive

    atm_iv, iv_rank and iv_pct conditions are evaluated

    • GET /v2/strategies/capabilities (get_auto_strategy_capabilities): with indicator conditions on, the functions atm_iv, iv_rank and iv_pct are available: true (were false); off, unchanged. Strategy documents using them now validate without the "not available yet" blocker; atm_iv's expiry is 'weekly' / 'monthly' with an optional n (0 = current, 1 = next, up to 3), other values are refused with the message. Units: atm_iv vol points, iv_rank / iv_pct 0–100. The recorded examples (indicators off) are unchanged.
    • Backtest results: option legs on real 1-minute option candles where GreptimeDB has them; new labels.text "Partly estimated: ..." and notes for runs with some modelled days, and a source row (greptime_options). No field changes.
  13. Additive

    delta_hedge@1 released (preview); high-IV condor hedges its delta

    • GET /v2/strategies/components (list_auto_strategy_components): delta_hedge@1 is status: preview, phase: a, deployable: true (was planned, c, false); its delta_band trigger variant is x-phase: a (was c). GET /v2/strategies/capabilities: the delta_band trigger is phase: a, deployable: true. The document schema (strategy.schema.v1.json) carries the same x-phase.
    • Behaviour: POST /v2/strategies/validate no longer gives the not_available_yet blocker for delta_hedge@1 (a net.delta condition still gets its own blocker; use trigger: {on: delta_band}). A version with delta_hedge@1 on a delta_band trigger deploys.
    • GET /v2/templates (list_auto_strategy_templates): the high_iv_iron_condor template gains a third adjustment, delta_hedge@1 on delta_band 0.5, target 0.1, with the future, 300 s apart, at most 3 a trade; its document description says so.
  14. Additive

    Backtest sweeps with overfitting warnings

    • New operations POST /v2/backtests/sweeps (create_auto_backtest_sweep), GET /v2/backtests/sweeps (list_auto_backtest_sweeps), GET /v2/backtests/sweeps/{sweep_id} (get_auto_backtest_sweep) and DELETE /v2/backtests/sweeps/{sweep_id} (cancel_auto_backtest_sweep): one backtest per version of up to 3 settings (JSON pointers into the strategy document, at most 200 versions), then the recommended version (best plateau), warnings, notes, deflated Sharpe, PBO and walk-forward. Errors reuse invalid_request (field axes), version_not_found, backtest_limit_reached, backtest_not_found and backtest_finished. A sweep counts as 1 + versions/20 of the 50 backtests a day and as one of the 3 running at once; one sweep at a time per user (409 backtest_limit_reached, "One sweep at a time").
    • Backtest workers share fairly: at most 3 of a user's backtests run at once, a sweep's versions included, and the user with the fewest running goes next, so a sweep never holds other users' backtests back.
    • GET /v2/backtests no longer lists a sweep's versions (each is still readable by id, with its trades and cycles); nothing listed before is left out.
  15. Change

    Pocketful delivery positions report no realised P&L

    Behaviour (no field added, removed or retyped; no new enum value)

    • Pocketful positions: a CNC or MTF row now reports realised_pnl absent, as a carried row does. Pocketful's realized_mtm books nothing on delivery rows (a sale out of holdings reads 0: recordings positions-5f192576, positions-089eea71), so a closed delivery row (a same-day CNC round trip, an MTF buy sold the same day) disagreed with its overall P&L: position.realised_on_overall_basis kept firing on the Pocketful tenant after the carried-row fix started (prod 2026-10-30). pnl is unchanged. Expectations re-recorded, only realised_pnl: 0.0 removed on the delivery row: positions-089eea71, 1168e67f, 346c366a, 5f192576, 89d6b288, 957afbcd, d21babfb, f937538b.
    • Conformance packs: new production recordings (AngelOne order book row and modify request, Kotak Neo BSE holdings, Pocketful MTF order / trade rows and seven order-feed frames, a paper modify refusal). Error templates: paper modify "Order is CANCELLED and cannot be modified" is order_not_modifiable (as the COMPLETE refusal); AngelOne order book HTTP 404 "not found" is outcome_unknown (owner decision 2026-10-29, now with its template).
  16. Additive

    Margin-check journey entries for automated entries

    • A live automated entry that goes out with no margin pre-check writes one journey entry of the new kind margin_unchecked ("Margin was not checked before this entry: ..."): when its broker gives no margin quote, or when the broker's margin quote fails ("<Broker>'s margin check didn't answer"). The orders still go out as before and the broker checks funds; nothing is refused.
    • New ops anomaly kinds (no user notice): auto_fill_swept (a fill the entry's own report missed was reported by the late-fill sweep) and gtt_cancel_not_found (a v1 GTT cancel answered not found and the GTT is unlisted; the record stays pending and re-checks back off to 15 min).
    • A broker-held protection GTT (a /v1/gtt record or an entry's protection) is read back from the broker every 2 min. Shown triggered, its record ends fired_at_broker; shown cancelled / deleted / expired / disabled / rejected without firing, its record ends with the existing status: cancelled (copied from the broker, nothing re-placed; its journal says "<Broker> shows this GTT <status>" and, once the notice is queued, "Notified you: stop-loss removed at <Broker>" with "Cancelled at <Broker> (GTT status: <status>), not by Cirrus. You were notified; this position is no longer protected."), the user gets the existing protection_failed activity / notice ("Your stop-loss for <symbol> was removed at <Broker> (<status>); this position is no longer protected."), and, for a GTT protecting an entry's position only, ops the new anomaly kind broker_protection_removed (once per record, paged; a user's own /v1/gtt deleted in the broker app is not paged). Absent from the broker's list or unreadable: kept.
    • TriggerView (/v1/triggers, list and detail) has a new optional field ended_message: why an ended trigger ended, in a plain sentence, when the server has one; omitted otherwise (no existing field or value changed). Set on the broker-removed GTT path above ("Cancelled at <Broker> (GTT status: <status>), not by Cirrus. You were notified; this position is no longer protected."). Webhooks unchanged.
    • Tenant profiles may set strategies.live_brokers; live deploys on other brokers answer the existing 403 live_not_enabled. Unset on every tenant.
  17. Additive

    Conformance acks live in the repository

    • Conformance packs gain acks.toml ([[ack]] key, note, by, date): ledger keys acknowledged on every tenant. At release start the sweep turns a new broker_unhandled entry whose key is listed acknowledged, and the writer records a first sighting of such a key acknowledged, so it never alerts. A handled entry stays handled; an entry a tenant acknowledged keeps its own note.
    • Stored record broker_unhandled: ack gains an optional source ("repo" when it came from acks.toml). Readers take the document as it is (admin list, watchdog, CLI), so old and new pods read both.
    • conformance ack <key> --note also appends the key to its pack's acks.toml and says to commit it; an entry already acknowledged on that tenant is added to the file only.
    • Back-filled 44 keys (one shape or message each) from the acknowledged entries on Cirrus prod and the Pocketful tenant, notes kept.
  18. Change

    Pocketful carried positions report no realised P&L

    Behaviour (no field added, removed or retyped; no new enum value)

    • Pocketful positions: a row with a carried quantity (cf_buy_quantity or cf_sell_quantity above 0) now reports realised_pnl absent, as Zerodha does. Pocketful's realized_mtm books only today's own trades, so a closed carried row read 0 (or a part) while its overall P&L was booked (Pocketful tenant prod 2026-10-28, 871 position.realised_on_overall_basis). Rows with no carried side keep realized_mtm. pnl is unchanged. Expectations re-recorded: positions-8f2cb23b, cf26a31c, dbfc1648, ea1fd1ca (only realised_pnl: 0.0 removed).
    • Pocketful place refusals "you're not allowed to place order in exchange 'BFO' / 'NFO'" (an account's segment not enabled) are now rms_block (were outcome_unknown), as Zerodha's and 5paisa's segment refusals; expectations place_order-65d79de3, place_order-96ac3a6f.
  19. Change

    Pocketful: triggered stop reads OPEN

    Behaviour (no field added, removed or retyped; no new enum value)

    • Pocketful order book: a stop whose trigger was hit (order_status SL_TRIGGER_CONFIRMED, relisted as LIMIT and working at its limit) now reads OPEN (PARTIALLY_FILLED once something filled) instead of TRIGGER_PENDING, as paper's triggered SL resting at its limit does. A broker stop that gapped past its limit then shows as went-off, so Cirrus cancels it and exits at its level. Users see the order's state as OPEN in the order book and order_update webhooks. Five expectations re-recorded (order_book-8fd7038c, -ea09119a, -f2740bcb, -8f11205e, -c103a838): only the state changed.
  20. Additive

    Pocketful order book and book lookup rows since 16 Oct

    • Conformance: 94 Pocketful row types seen in production on both tenants since 2026-10-16 (52 order_book and 2 more from the Cirrus tenant, 40 book_lookup) are handled types, each with a promoted recording. They are new combinations of the categorical fields (status, product, order type, side, exchange, execution type, leg indicator) on row shapes already known; no new field. Every mapped row reads as before: state from its list and status, filled quantity and average price as sent.