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

    Webhook schemas declare the legacy names the bodies carry

    • Webhooks order_update, trade and positions (and the partner app bodies with the same data): the schemas now declare the deprecated legacy names every body already carries with the same value as the documented field: cirrus_tag (= order_tag) and cirrus_token (= instrument_token) in OrderData, WebhookTradeData and SplitFrom, cirrus_token in each PositionRow. They are marked deprecated: true and still listed in x-deprecated-aliases. Before, the schemas left them out while declaring additionalProperties:

    false, so a receiver validating strictly against the document rejected every such delivery. Nothing on the wire changes. OrderData and PositionRow are shared with the REST order book and positions responses, which send the same names, so those declare them too.

  2. Additive

    Modify and cancel take an order's tag as GET does

    • PATCH / DELETE /v1/orders/{order_id} and PATCH / DELETE

    /v1/orders/bracket/{order_id}: order_id may now be the order's order_tag or client_tag, as GET /v1/orders/{order_id} already takes it; the order is modified / cancelled at the broker under its broker order id. Before, a tag was sent to the broker as the order id: the answer was 200 status: rejected ("Order not found") and nothing changed. A tag the account's order book does not know, an order the broker has not acknowledged yet, or a client_tag naming several orders in the account is refused with the routes' existing 422 invalid_request (field order_id), nothing sent; an unreadable order book is the existing 503 service_unavailable. A broker order id is handled as before.

  3. Additive

    Exit notices name their v2 trigger

    • Notifications (GET /v1/notifications, the /v1/stream notifications topic, the notification webhook): exit_placed, exit_filled and exit_refused notices about a v2 trigger's exit now carry its trigger_id (it was null), so a fill can be matched to its trigger. Exits of v1 triggers keep trigger_id null (the field names v2 triggers). No shape change. Exits recorded by a pod running the previous build during a deploy keep null.
  4. Additive

    A bracket waiting for its entry is refused while entries are off

    • POST /v2/triggers, PATCH /v2/triggers/{id} and POST

    /v2/triggers/validate: a bracket with spec.entry is refused with 422 invalid_request, field spec.entry (validate lists it in errors) unless automatic entries are on for the user (GET /v2/triggers/capabilities entries_enabled). Before, with entries off (production today) such a bracket was accepted, its entry was never placed (dead-lettered as trigger_action_unsupported) and only its stop-loss / target ran. The message says to place the entry by hand and give its price as entry_px. With entries on, the allowlist check is unchanged. Existing triggers are not touched: one such bracket exists in production (created 2026-10-16, still waiting; if its entry condition holds, its entry is dead-lettered as before). The entries_enabled description says so.

  5. Additive

    Notifications for users who never opened their settings

    • Users without saved notification settings now get the default notifications, as GET /v1/notifications/preferences already showed them: notices in the app inbox (GET /v1/notifications) and on the /v1/stream notifications topic for every event on by default (trigger fired, exit placed / filled / refused, order rejected, order not confirmed, protection failed, strategy events, session expired...). Telegram only once they link it; the notification webhook stays off until they turn it on. Before, such users got no notices at all (in production 31 of the 33 users active in the last 7 days). No shape changes; nothing is stored for them until they save settings.
  6. Additive

    Tradejini order-book row shape with a faked client code

    • The Tradejini conformance pack handles one more order-book row shape (80c811b6, an RMS rejection whose message names a client code, now faked in the recording). No REST, webhook or stream field changes: rows of this shape were already accepted; they no longer raise an unhandled-type entry.
  7. Additive

    An account's static IP is the address the broker sees

    • static_ip on GET /v1/accounts and GET /v1/accounts/{account} keeps its name and type. On an IPv6 static-IP seat it is now the seat's IPv6 whitelist_ip (the address the broker sees and the user registers) instead of the IPv4 address calls dial through; IPv4 seats are unchanged. The static-IP refusal advice names the same address.
  8. Additive

    Clearer notification wording

    • The opt-in notification webhook event, the notifications stream topic and GET /v1/notifications keep every field, type and enum value; only the English in title, what_happened, what_we_did and what_you_should_do is reworded (asked for by the product owner). Examples: a trigger title no longer repeats the strategy in brackets (Stop-loss hit on RELIANCE-EQ; the strategy stays in strategy); what_happened says where the market was (RELIANCE-EQ traded at

    ₹2,474.50.); exit and order fills are titled Exit filled / Order filled with the fill in what_happened; a closed strategy trade's title carries its P&L (…: trade closed, ₹2,555 profit); live_updates (stopped) now has what_you_should_do: null (the advice moved into what_we_did). Clients must not parse these strings (they never were a contract); match on event, severity and the ids.

    • The same wording is shared with the strategy run journey (GET /v2/deployments/{deployment_id}/journey what_we_did, and the journey in the deployment detail), e.g. a closed trade now reads Closed every leg, so nothing is left open: ₹3,637 loss. (was Closed

    every leg; trade P&L -₹3637.).

    • Internal: the oms.strategy.* step record gains an optional realized (rupees, closed trades only; absent on older records, ignored by older readers).
    • Telegram messages (not an API) are redesigned: one headline emoji, "What to do" only when the user must act, the time in IST, a button that opens the app page, and a short quip on good or neutral news only.
  9. Additive

    Kotak Neo refusals seen live

    The kotakneo conformance pack classes the refusals Kotak gave live, which differ from its docs' examples: 1022 "order is rejected" on modify / cancel (order_not_modifiable), 1018 "please provide valid internal order number" on cancel (order_not_found) and HTTP 401 100022 "invalid session token" on any call (session_expired). Four live recordings back them. No response shape or status code changes.

  10. Additive

    Strategy capabilities mark engine-evaluated indicators available

    • GET /v2/strategies/capabilities indicators[].available is now true for every catalogue function the engine's indicator registry evaluates (the same functions /v2/triggers/capabilities lists in indicators.functions and as candle patterns: rsi, adx, bullish_pattern, ...) while indicator conditions are on; it was false for all 89, so the app showed them as "coming soon". Functions the engine does not evaluate yet (phase B vortex_*, fisher, tsi, rvol, hv, yz_vol, regime, session levels, opening range, ltp(sym), atm_iv, iv_rank, iv_pct, expected_move, dte, leg, minutes, bb_mid) stay false, and all stay false with indicator conditions switched off. variables[].available is derived from the engine too (unchanged: cycle.minutes).
    • Validation and templates were already checked by the engine: no change.
    • Same fields, same types; only the values are now accurate.
  11. Additive

    Automated strategies, indicators and notifications for everyone (BETA)

    • Automated strategies (/v2/strategies*, /v2/deployments*, signals on algo mode /v2/signal-modes, /v2/backtests*, sharing), indicator conditions (v2 triggers and strategy documents) and user notifications (/v1/notifications*, the notifications stream topic) are on for every user the deployment serves; routes that answered 403 / 422 / 503 notifications_disabled for users outside an allowlist now answer as documented. The server can still switch each off (same answers as before).
    • Live deployments are open on every broker: live_not_enabled now means only a credential without the strategies_live scope (or a broker this build does not trade); GET /v2/strategies/capabilities live_brokers lists every broker (same field, more values).
    • POST /v2/backtests answers 503 "Backtests aren't available on this server yet" when no backtest worker runs, instead of queueing a job that never starts.
    • Nothing is removed or renamed; no new field or enum value.
  12. Additive

    Guided adjustments: triggers, picked legs, `cycle.minutes`

    • GET /v2/strategies/capabilities: adjustment_triggers (each trigger.on an adjuster may fire on, with label, fields (key, label, help, required, default), phase, deployable; written into the engine plan as is: loss ₹, profit ₹, loss / profit % of premium, minutes after entry (elapsed), a leg's price × its entry (premium_ratio), days to expiry from a time (expiry; expiry day at a time: dte_at_most 0 and at), a condition) and adjustment_targets (tested, sold). variables lists cycle.minutes (available: true); labels.leg_targets is new.
    • New trigger leg_delta (leg, delta) and the existing delta_band (net delta beyond ± a band) are phase C: saved, not deployable (the engine reads no greeks yet).
    • close_leg@1, roll_strike@1: leg is optional when the new target (tested: the sold option whose price rose most against its entry; sold: every open sold option) picks the legs as the adjustment fires; reduce@1 takes target to close only those legs. Exactly one of leg / target; with target a roll names no move_hedge and no relative strike. Left out of normalized documents while absent.
    • A roll_strike@1 with a relative strike counts from the rolled leg itself: to must be that leg (anything else is now an error at validation instead of a skipped adjustment at run time).
    • Conditions of exits and adjustments may read cycle.minutes (whole minutes since the trade entered).
    • GET /v2/strategies/components: the adjusters' params_schema shows the above (close_leg@1 is now bare, as widen@1: no field is required by shape).
  13. Additive

    Several trades at once per deployment (`max_open_trades`)

    • Strategy documents: spec.risk.limits.max_open_trades (limits@1, integer 1 to 10, default 1). Above 1, each entry (time cycle, conditions, alert) opens its own trade with its own stops, exits and adjustments; the day's limits, the kill switch and the breakers count every open trade. Left out of normalized documents and exports while 1, so existing documents read and export exactly as before. New blocker open_trades_above_day (above "Most trades a day"); the limits summary says "up to N trades open at once". GET /v2/strategies/components and the strategy JSON Schema list the field.
    • GET /v2/deployments/{id}/live: each unit's unit.trades (new optional array, AutoLiveTrade: trade, cycle, day, phase, pnl, floor, entered_at, exit_reason, legs) lists the open trades of a strategy holding several; legs and pnl are then every open trade's together. Absent for one trade at a time (unchanged).
    • The journey and cycles list stay one entry per cycle; each trade is a cycle of the day.
  14. Additive

    A signal URL never binds to a strategy without legs

    PUT /v2/signal-modes/{endpoint_id} also refuses (422 invalid_request, same shape as the other binding refusals) a deployment whose strategy has no legs at all (no leg selector, or no leg items and not from_signal@1): its alerts could never start a trade. Request and reply shapes unchanged.

  15. Additive

    Re-entries that can never happen are a blocker

    POST /v2/strategies/validate (and create, update, versions, dry-run's validation) report a new blocker reentry_capped at spec.reentry.max when re-entries are on but "Most trades a day" (spec.risk.limits.max_cycles_per_day) is 1, so the strategy would never re-enter. Response shape unchanged. The high_iv_iron_condor template now allows 2 trades a day (GET /v2/strategies/templates: its document's max_cycles_per_day and summary), so its one re-entry after a target can happen.

  16. Additive

    Validate reports every deploy refusal first

    POST /v2/strategies/validate now finds, while the user types, everything a deploy would refuse for the document; response shape unchanged.

    • New blockers (saved, not deployable), each at the document field to change, with the words the deploy refuses with: engine_refuses (the engine plan, compiled for a stand-in paper account and checked by the engine), not_in_instrument_list (watched symbols, fixed contracts, the underlying's price), underlying_not_open, not_available_yet at a webhook@1 source when alerts on signal URLs are off, square_off_in_window (spec.exit.square_off inside the entry window), lots_above_cap (spec.legs.items[i].lots above the sizing's max_lots), hedged_twice, needs_option_leg (spec.entry.expiry_day / spec.exit.expiry_day_exit without an option leg). The last four (and reentry_never) also show in create, update and versions; dry-run's validation shows them all.
    • reentry_never (re-entries on, no reason chosen) is now a blocker, not a warning: the engine always refused such a plan at deploy.
    • New warnings: daily_loss_below_stop (spec.risk.limits.max_loss_per_day below one trade's combined stop), signal_url_incompatible (no signal URL could be bound).
    • New optional request fields mode (default paper) and accounts (as for deploy): the deploy's account rules come back as warnings with the deploy's own code, field and message (invalid_request, account_not_found, live_not_enabled, deployment_limit_reached).
    • Rate limit: validate has its own budget of 240 a minute (was 60, shared with dry-run); dry-run keeps 60.
  17. Additive

    Notifications without a Telegram bot

    New server switch OMS_USER_NOTIFICATIONS (and OMS_USER_NOTIFICATIONS_USERS) serves notifications without a Telegram bot; OMS_USER_TELEGRAM behaves as before.

    • GET /v1/notifications/channels: telegram gains available (boolean) and unavailable_reason (string or null). Without a bot, available is false, unavailable_reason says "Telegram isn't set up on this server yet", bot_username is empty and prompts never contains connect_telegram or telegram_broken. New example telegram_unavailable.
    • POST /v1/notifications/telegram/link and POST

    /v1/notifications/telegram/test without a bot: 503 notifications_disabled (existing code) with the message "Telegram isn't set up on this server yet". New examples telegram_unavailable.

    • Users outside OMS_USER_NOTIFICATIONS_USERS get the existing 503 notifications_disabled on every /v1/notifications route, and no notifications stream topic.
  18. Additive

    Signals on algo mode trade what the alert names (flag off)

    Behind OMS_SIGNALS_ALGO_MODE (and the automated-strategies allowlist); with it off nothing users see changes. Inbound TradingView, Chartink and Kuberhunt payloads, URLs and replies are unchanged. None of this has shipped.

    • Strategy documents: from_signal@1 legs are now preview (were planned), with new parameters instrument (signal | stock | future | option), side (signal | buy | sell), option_type, strike_offset, lots, max_shares, max_per_alert, max_per_day, skip_held (all defaulted; underlyings and max_lots as before). from_signal@1 sizing is preview. webhook@1 sources take trigger_only (default false). New validation problems (invalid_combination): legs from the signal need a webhook@1 entry that is not trigger only and no adjustments; lots above max_lots / max_shares; sizing from the signal without legs from the signal. GET /v2/strategies/capabilities lists two new enums with labels: signal_instruments, signal_sides. Strategy JSON Schema re-exported.
    • PUT /v2/signal-modes/{endpoint_id}: switching to algo is refused (422 invalid_request, field deployment_id, in plain words) unless the strategy trades what the URL's alerts send: its legs come from the signal, or every alert source is trigger_only (the alert only starts the strategy's own legs); a Chartink URL also needs an alert entry source.
    • In algo mode a Chartink alert's stocks (each on the Chartink strategy's side, with its shares or ₹ per stock and stop / target) and a TradingView entry's legs (as direct mode maps them) become the strategy's legs, within its limits; an exit closes only what that URL's alerts opened, and only the instruments it names. Journey and notifications say per stock what was bought or skipped and why.
  19. Additive

    More indicators and candle patterns in conditions (flag off)

    Behind OMS_TRIGGERS_V2_INDICATORS (v2 triggers) and the automated strategies flags; with them off nothing users see changes. None of this has shipped.

    • New condition functions (each also in GET /v2/triggers/capabilities → indicators.functions, with its settings, ranges and defaults): adx, plus_di, minus_di, aroon_up, aroon_down, aroon_osc, cci, willr, roc, mom, mfi, obv, cmf, keltner_upper / _middle / _lower, donchian_upper / _middle / _lower, hull, wma, dema, tema, kama, psar, ichimoku_tenkan / _kijun / _span_a / _span_b, chop, stochrsi_k, stochrsi_d, stddev.
    • Candle patterns: bullish_pattern('engulfing', '15m'), bearish_pattern(...), pattern('doji', '15m'), read at the candle close; new indicators.patterns in capabilities (TriggerV2IndicatorPattern: name, label, directions both | bullish | bearish | neither, candles, functions). indicators.functions does not list the three pattern functions.
    • validate refuses mfi, obv and cmf on an index like VWAP ("indices have no volume, so MFI isn't available; try EMA instead").
    • Strategy catalogue (GET /v2/strategies/capabilities → indicators, labels): the new names above that it did not have, kind streaming, phase a. Existing entries are unchanged.
    • Strategy templates: adx_trend_futures (ADX trend futures) and engulfing_call (Bullish engulfing call).