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

    internal: first production recordings in the broker corpus

    No change for users (not in the API). The Pocketful and Zerodha replies production first met (empty books, real holdings rows, margins, socket subscribe / heartbeat / idle) join the conformance corpus, with the instruments their mappers resolve. Noren's "no data" and Dhan's DH-1111 empty answers are classed as accepted. The rule that a filled order must carry an average price is dropped (the trade book prices it).

  2. Additive

    broker onboarding gate (off by default)

    Behind OMS_BROKER_GATE (off by default; when off nothing changes). When on, an account whose broker is not verified yet (its conformance pack is below stage production and not grandfathered; paper, Zerodha and Pocketful are verified) cannot open new exposure: POST /v1/orders answers that account's slices rejected with reason validation, and PATCH /v1/orders/{order_id}, bracket place / modify, GTT create / modify (/v1/orders/bracket, /v1/gtt) and POST /v1/positions/convert answer 422 invalid_request (field: account). The message says the broker is not verified and its conformance stage. Nothing is sent to the broker. Reads, cancels, exits and square-offs are never refused. No error code, enum or wire value is added.

  3. Additive

    internal: broker conformance corpus and unhandled list

    No change for users (not in the API). Broker conformance packs frozen statuses, redacted recordings and expectations) become contract files: recordings are append-only, the rest changes only with a Contract-Change: trailer. cirrus-oms writes broker interactions it has not handled to Mongo broker_unhandled (one document per type: status, count, accounts, redacted sample), read by the watchdog and the conformance command. The account health hash gains a broker field. No error code, enum or wire value is added.

  4. Additive

    internal: trigger exit follow-up records

    No change for users (not in the API). After a trigger fires, cirrus-oms keeps a follow-up record in Redis (oms:trigger_exit:follow_up:<trigger id>:<hit>, due times in the sorted set oms:trigger_exit:follow_ups, expiring after 2 days): a Cirrus exit is watched until it fills, a broker mirror until the broker's own exit is seen. Only cirrus-oms reads it (triggers-workers-py does not). Its samples are in (exit, mirror) are frozen in wire_enums.json. Pods on the previous version do not read these records; a follow-up written during a rollout is taken by a new pod once the rollout completes.

  5. Additive

    webhook deliveries recorded as goldens

    No change to any payload. The exact delivery of every webhook event (order_update, trade, account_alert, positions, ping: body and header formats) is now recorded in docs/api/examples/webhooks/ and every push is checked against it.

  6. Additive

    stored-record fixtures and frozen enum values

    No change for users. Samples of every record kind the service stores (orders, protections, triggers, positions, events, webhooks, API keys, signal URLs) and the wire values of stored enums are recorded, so a change that leaves old records unreadable, or that old pods cannot read during a deploy, fails before it is pushed.

  7. Additive

    internal: new order-leg role `square_off`

    No change for users (not in the API). Order legs in portfolio snapshots can now carry square_off (b9b7aa6). Pods on the previous version skip a row that carries it until they are replaced: deploy the engine and the api together, and expect order views to settle once the rollout completes.

  8. Additive

    list_gtts example instrument

    No change to the API. The list_gtts from_broker example now shows a test instrument (CT:TEST:GTT_RELIANCE) instead of a real one that only existed in one developer's Redis, so the example is the same everywhere.

  9. Additive

    market protection refuses stray prices; clearer refusals

    Changes in behaviour, no change to response shapes, codes or fields:

    • A protected MARKET order is refused (reason: stale_quote) when the best bid (SELL) or ask (BUY) is further from the last price than 2 protection bands or 2 ticks, instead of trading at that price (#21). Options and futures are also held within their circuit limits.
    • The refusal message says to place a LIMIT order: "Not placed: no live price for X right now (the market may be closed), so it cannot be sent at market. Place a LIMIT order instead." (#24). market_protection:false is no longer suggested.
    • POST /v1/positions/exit refuses (503 service_unavailable) a Pocketful position whose size is ambiguous (the same contract under two products in the broker's two lists) instead of possibly exiting twice (#22).
    • A broker cover / bracket stop measured from the expected fill is moved to where the rules put it once the entry fills; the protection list shows what the broker holds (#23).
  10. Additive

    ended protections, protection statuses, refusal reasons

    • GET /v1/triggers takes an optional status filter (400 on an unknown one) and lists ended protections for 7 days, with ended_reason and paired_trigger_id on each protection.
    • POST /v1/triggers may answer 504 when the broker's answer did not arrive in time.
    • New error codes gtt_refused and order_not_open (error codes are open: handle unknown ones by their HTTP status).
    • A protected MARKET order refused because the best bid / ask is too far from the last price answers reason / error code stale_quote (as every unpriced market order does; the message says which). The book_far_from_last value that briefly replaced it was never announced and is gone.
  11. Additive

    exits priced from the order book depth

    No change to request or response shapes. An exit (POST /v1/positions/exit, trigger exits, signal square-offs) with market protection is priced from the order book level that covers its whole quantity, a small band past it (#36). A stale last trade no longer refuses an exit while the book is live (09:15-15:40 IST); with neither, or with the book level more than five protection bands from the last price, it is refused with stale_quote and the message says which.