Skip to content
Cirrus home
Sign in

Reference

Strategy builder

What a strategy can use, ready-made templates, and checks before you save or deploy.

Read the guide first

Validate a strategy document #

POST /v2/strategies/validate scope strategies

Checks a document (same body as create) with the code create, import and versions use, and answers 200 whatever it finds: errors (refused), blockers (saved, not deployable yet), warnings (needs_ack: to acknowledge at deploy), each with the document path (field), the wizard label and a plain-English message; the summary; and the hash of the compiled strategy (equal for any way of writing it).

More about this

Everything a deploy would refuse for the document is a blocker here first: the engine plan compiled for a stand-in paper account and checked by the engine (engine_refuses), symbols not in today's instrument list (not_in_instrument_list), underlyings not open, alerts not available. With accounts (and mode, default paper), the deploy's account rules come back as warnings with the deploy's own code and words. Fast enough to call as the user types (no broker calls).

Body fields

  • document any required

    The portable document: an object, or its JSON or YAML text.

  • definition object | null

    Instead of document: the definition (spec) alone.

  • name string | null

    With definition.

  • description string | null

    With definition.

  • tags string[] | null

    With definition.

  • revision integer (int64) | null

    Ignored (accepted as for update).

  • mode string | null

    Deploy mode to check against (default paper).

    One of: paper, live

  • accounts object[] | null

    Accounts to check against (as for deploy); none: account rules are left for the deploy.

Errors

HTTPCodeWhen
400malformed_json

The body is not JSON, or does not fit the request shape (unknown or missing fields, wrong types).

401unauthorized

No credential, or the credential is invalid, expired or revoked.

403forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

422invalid_document

The strategy document has problems; message lists each (where in the wizard, what is wrong) and field names the first.

429rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

All fields (request and response schema)

Loading fields…

POST
curl -X POST "https://cirrus.pocketful.in/v2/strategies/validate" \
  -H "Authorization: token $CIRRUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "document": {
      "schema_version": 1,
      "kind": "cirrus.strategy",
      "metadata": {
        "name": "NIFTY straddle",
        "tags": [
          "weekly"
        ]
      },
      "spec": {
        "underlying": {
          "name": "NIFTY",
          "exchange": "NFO"
        },
        "legs": {
          "selector": "chain@1",
          "items": [
            {
              "id": "sc",
              "instrument": "option",
              "side": "sell",
              "option_type": "call",
              "expiry": {
                "kind": "weekly"
              },
              "strike": {
                "by": "atm"
              }
            },
            {
              "id": "sp",
              "instrument": "option",
              "side": "sell",
              "option_type": "put",
              "expiry": {
                "kind": "weekly"
              },
              "strike": {
                "by": "atm"
              }
            }
          ]
        },
        "entry": {
          "window": {
            "start": "09:30",
            "end": "13:30"
          }
        },
        "risk": {
          "combined": {
            "type": "combined@1",
            "stop_loss": {
              "rupees": 3000
            },
            "target": {
              "rupees": 2000
            }
          }
        }
      }
    }
  }'
Response · 200
{
  "status": "success",
  "data": {
    "ok": true,
    "deployable": true,
    "schema_version": 1,
    "migrated_from": null,
    "errors": [],
    "blockers": [],
    "warnings": [],
    "needs_ack": [],
    "summary": "Sell 1 lot of an ATM call (weekly expiry) and sell 1 lot of an ATM put (weekly expiry) on NIFTY. Enter as soon as the window opens, between 09:30 and 13:30, Monday to Friday. Everything closes at 15:15. Size: the legs' lots times each account's multiplier, at most 10 lots per leg. Orders: hedges first when there are any, limit prices 0.5% beyond the market, chasing up to 1% slippage; if only some legs fill: close a sold option left without its hedge, keep the rest. Protection: stop at ₹3,000 loss, take ₹2,000 profit; no per-leg stops; at most 3 trades a day. No re-entries.",
    "compiled_hash": "sha256:c030cdecce518bdf50067237a17de0b5508d3e962e743a87dd41e7df3ef01ff9",
    "components": [
      "chain@1",
      "combined@1",
      "conditions@1",
      "fixed_lots@1",
      "limits@1",
      "per_leg@1",
      "protected_limit@1"
    ],
    "warm_up": null
  },
  "error": null
}

Dry-run a strategy #

POST /v2/strategies/dry-run scope strategies

What the strategy would do right now, without orders: wait (outside its window or days, or for a signal), enter (with the contracts it would pick from the live chain), skip (and why), or cannot_decide when something it needs is not available on this server yet (the strategy engine, live chain picking).

More about this

Body: a document as for validate, or strategy_id (and version). The validation result is included.

Body fields

  • document any required
  • definition object | null
  • name string | null
  • description string | null
  • tags string[] | null
  • strategy_id string | null

    Instead of a document: one of the caller's strategies.

  • version integer (int32) | null

    With strategy_id (default: the draft).

  • account string | null

    Account to size and check against.

  • at string | null

    Check the entry window and days as at this time (RFC 3339; default now). Contracts are always picked from live prices.

Errors

HTTPCodeWhen
400malformed_json

The body is not JSON, or does not fit the request shape (unknown or missing fields, wrong types).

401unauthorized

No credential, or the credential is invalid, expired or revoked.

403forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

404strategy_not_found

No automated strategy with this id for the caller.

404version_not_found

No such version of the strategy.

429rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

All fields (request and response schema)

Loading fields…

POST
curl -X POST "https://cirrus.pocketful.in/v2/strategies/dry-run" \
  -H "Authorization: token $CIRRUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "document": {
      "schema_version": 1,
      "kind": "cirrus.strategy",
      "metadata": {
        "name": "Broken",
        "tags": [
          "weekly"
        ]
      },
      "spec": {
        "underlying": {
          "name": "NIFTY",
          "exchange": "NFO"
        },
        "legs": {
          "selector": "chain@1",
          "items": [
            {
              "id": "sc",
              "instrument": "option",
              "side": "sell",
              "option_type": "call",
              "expiry": {
                "kind": "weekly"
              },
              "strike": {
                "by": "atm"
              }
            },
            {
              "id": "sp",
              "instrument": "option",
              "side": "sell",
              "option_type": "put",
              "expiry": {
                "kind": "weekly"
              },
              "strike": {
                "by": "atm"
              }
            }
          ]
        },
        "entry": {
          "window": {
            "start": "14:00",
            "end": "10:00"
          }
        },
        "risk": {
          "combined": {
            "type": "combined@1",
            "stop_loss": {
              "rupees": 3000
            },
            "target": {
              "rupees": 2000
            }
          }
        }
      }
    }
  }'
Response · 200
{
  "status": "success",
  "data": {
    "decision": "invalid",
    "text": "Would not run: fix the problems first.",
    "legs": [],
    "net_premium": null,
    "validation": {
      "ok": false,
      "deployable": false,
      "schema_version": 1,
      "migrated_from": null,
      "errors": [
        {
          "field": "spec.entry.window",
          "label": "When · Entry · Window",
          "message": "The entry window must start before it ends.",
          "code": "invalid_combination",
          "needs_ack": false
        }
      ],
      "blockers": [],
      "warnings": [],
      "needs_ack": [],
      "summary": "Sell 1 lot of an ATM call (weekly expiry) and sell 1 lot of an ATM put (weekly expiry) on NIFTY. Enter as soon as the window opens, between 14:00 and 10:00, Monday to Friday. Everything closes at 15:15. Size: the legs' lots times each account's multiplier, at most 10 lots per leg. Orders: hedges first when there are any, limit prices 0.5% beyond the market, chasing up to 1% slippage; if only some legs fill: close a sold option left without its hedge, keep the rest. Protection: stop at ₹3,000 loss, take ₹2,000 profit; no per-leg stops; at most 3 trades a day. No re-entries.",
      "compiled_hash": null,
      "components": [
        "chain@1",
        "combined@1",
        "conditions@1",
        "fixed_lots@1",
        "limits@1",
        "per_leg@1",
        "protected_limit@1"
      ],
      "warm_up": null
    },
    "analytics_note": "No analytics: no contracts were picked."
  },
  "error": null
}

A condition's values now #

POST /v2/strategies/condition-values scope strategies

For the strategy builder: the indicator values a condition reads and whether it is true right now, e.g.

More about this

RSI(14,15m) = 62.3. The condition is compiled, its symbols resolved and its values computed by the same code a running strategy uses (the document's entry / exit condition rules, the engine's indicators and evaluator), from recent history and the last price; nothing is saved and nothing trades. true_now is null when it cannot be known yet (reason): an indicator still warming up, no price yet, or a value only a running trade has (cycle.minutes). A crossing is true when it crossed at the last candle close; a hold is not waited for. Poll at most every few seconds while the builder shows it.

Body fields

  • when string required

    The condition as the document writes it: u.rsi(14, '15m') > 60, crosses_above(u.ema(9, '5m'), u.ema(21, '5m')).

  • underlying object required

    The underlying as the document's spec.underlying names it.

  • on string | null

    close or tick; absent: close when it reads an indicator, else tick (as the document's default).

    One of: close, tick

  • place string

    Which part of the strategy the condition belongs to.

    One of: entry, exit

  • watch object

    Other watched names and their document symbols ({"bank": "NSE_INDEX:NIFTY BANK"}, @future(NIFTY, 0)); u may override the underlying's default.

Errors

HTTPCodeWhen
400malformed_json

The body is not JSON, or does not fit the request shape (unknown or missing fields, wrong types).

401unauthorized

No credential, or the credential is invalid, expired or revoked.

403forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

422invalid_request

The request is well-formed but not valid; field names the input.

429rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

503service_unavailable

A dependency is temporarily unavailable, or the feature is off on this server; retry later.

All fields (request and response schema)

Loading fields…

POST
curl -X POST "https://cirrus.pocketful.in/v2/strategies/condition-values" \
  -H "Authorization: token $CIRRUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "when": "cycle.minutes > 30",
    "underlying": {
      "name": "NIFTY",
      "exchange": "NFO"
    },
    "place": "exit"
  }'
Response · 200
{
  "status": "success",
  "data": {
    "true_now": null,
    "status": "ok",
    "reason": "cycle.minutes is known only while a trade is open",
    "on": "tick",
    "at": "2026-10-20T10:33:10.157Z",
    "values": []
  },
  "error": null
}

List strategy components #

GET /v2/strategies/components scope read

Every component type a strategy can use (sources, leg selectors, sizers, executors, risk rules, adjusters, notifiers), each with its parameters' JSON Schema, release status and phase, and whether a version using it can be deployed now.

More about this

The wizard, import and validation read this one list.

Errors

HTTPCodeWhen
401unauthorized

No credential, or the credential is invalid, expired or revoked.

403forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

429rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

All fields (request and response schema)

Loading fields…

GET
curl "https://cirrus.pocketful.in/v2/strategies/components" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200 · lists shortened
{
  "status": "success",
  "data": [
    {
      "id": "conditions@1",
      "name": "conditions",
      "major": 1,
      "slots": [
        "entry_source",
        "exit_source"
      ],
      "label": "Conditions",
      "explain": "Indicators, prices and time, checked by the server.",
      "group": "Built in",
      "status": "preview",
      "phase": "a",
      "deployable": true,
      "deprecated": false,
      "sunset_after": null,
      "needs": [],
      "bare": true,
      "params_schema": {
        "type": "object",
        "properties": {
          "type": {
            "const": "conditions@1"
          },
          "when": {
            "anyOf": [
              {
                "type": "string",
                "minLength": 1,
                "maxLength": 2048,
                "x-expression": true
              },
              {
                "type": "null"
              }
            ],
            "title": "Condition",
            "description": "Leave empty to enter as soon as the window opens (time-only).",
            "default": null
          },
          "on": {
            "enum": [
              "close",
              "tick"
            ],
            "x-labels": {
              "close": "At each candle close",
              "tick": "On every price change"
            },
            "title": "Checked on",
            "description": "close: at each bar close (default); tick: on every price change.",
            "default": "close"
          },
          "hold_s": {
            "type": "integer",
            "minimum": 0,
            "maximum": 3600,
            "title": "Hold for",
            "description": "Seconds the condition must stay true before it counts.",
            "default": 0
          }
        },
        "additionalProperties": false,
        "required": [
          "type"
        ],
        "title": "Conditions",
        "description": "Indicators, prices and time, checked by the server.",
        "x-status": "preview",
        "x-phase": "a"
      }
    },
    {
      "id": "manual@1",
      "name": "manual",
      "major": 1,
      "slots": [
        "entry_source",
        "exit_source"
      ],
      "label": "Manual",
      "explain": "You decide when, with a button in the app.",
      "group": "You",
      "status": "preview",
      "phase": "a",
      "deployable": true,
      "deprecated": false,
      "sunset_after": null,
      "needs": [],
      "bare": true,
      "params_schema": {
        "type": "object",
        "properties": {
          "type": {
            "const": "manual@1"
          }
        },
        "additionalProperties": false,
        "required": [
          "type"
        ],
        "title": "Manual",
        "description": "You decide when, with a button in the app.",
        "x-status": "preview",
        "x-phase": "a"
      }
    }
  ],
  "error": null
}

Strategy capabilities #

GET /v2/strategies/capabilities scope read

What the wizard may offer: slots and their components, enums and strike methods (with their phase), limits, timeframes, the indicator and variable catalogue (and whether each is available on this server), live brokers, underlyings (and underlying_details: per underlying its derivatives segment, lot size, strike step, expiry kinds listed, next expiries and current spot, from today's instrument list; mcx_underlyings, only while MCX strategies are on: every MCX commodity with a live future, priced off its nearest future), plain-English labels for every enum value, and the caller's usage (active deployments left).

More about this

With indicator conditions on, indicator_functions: the engine's indicator registry as /v2/triggers/capabilities lists it (labels, parameters with defaults and ranges, outputs, presets, timeframes, limits). switches: this server's switches, each true / false only (gallery, portfolio_risk, mcx, stock_fno, equity, stock_universe: a stock universe is configured). mcx_sessions: the MCX hours strategies are held to: late (bullion, energy, base metals: open, entry window close, square-off cap and default, the close in each US season, the commodities in it), early (every other commodity: the agri close and its square-off cap and default) and commodities: per commodity in mcx_underlyings its session and hours (empty while MCX is off).

Errors

HTTPCodeWhen
401unauthorized

No credential, or the credential is invalid, expired or revoked.

403forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

429rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

GET
curl "https://cirrus.pocketful.in/v2/strategies/capabilities" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200 · lists shortened
{
  "status": "success",
  "data": {
    "schema_version": 1,
    "schema_versions": [
      1
    ],
    "phase": "a",
    "slots": [
      {
        "slot": "entry_source",
        "label": "Entry",
        "tag_key": "type",
        "components": [
          "conditions@1",
          "manual@1"
        ]
      },
      {
        "slot": "exit_source",
        "label": "Exit",
        "tag_key": "type",
        "components": [
          "conditions@1",
          "manual@1"
        ]
      }
    ],
    "enums": {
      "instruments": [
        "option",
        "future"
      ],
      "sides": [
        "buy",
        "sell"
      ],
      "option_types": [
        "call",
        "put"
      ],
      "expiry_kinds": [
        "weekly",
        "monthly"
      ],
      "premium_modes": [
        "nearest",
        "at_least"
      ],
      "strike_methods": [
        {
          "by": "atm",
          "label": "At the money",
          "phase": "a",
          "deployable": true
        },
        {
          "by": "pct_otm",
          "label": "Percent out of the money",
          "phase": "a",
          "deployable": true
        }
      ],
      "exchanges": [
        "NFO",
        "BFO"
      ],
      "deployable_exchanges": [
        "NFO",
        "BFO"
      ],
      "hedge_with": [
        "future",
        "roll_short"
      ],
      "references": [
        "future",
        "spot"
      ],
      "days": [
        "mon",
        "tue"
      ],
      "expiry_day": [
        "normal",
        "skip"
      ],
      "evaluate_on": [
        "close",
        "tick"
      ],
      "reentry_on": [
        "stop_loss",
        "target"
      ],
      "cost_models": [
        "auto",
        "options_india"
      ],
      "orders": [
        "auto",
        "hedge_first"
      ],
      "on_incomplete": [
        "safest",
        "keep_and_manage"
      ],
      "on_leg_stop": [
        "nothing",
        "move_others_to_cost"
      ],
      "combined_scopes": [
        "account",
        "deployment"
      ],
      "bases": [
        "rupees",
        "pct_premium"
      ],
      "on_breaker": [
        "nothing",
        "close_open_trade"
      ],
      "notify_events": [
        "entered",
        "exited"
      ]
    },
    "limits": {
      "max_document_bytes": 65536,
      "max_depth": 20,
      "max_entry_legs": 6,
      "max_legs": 8,
      "max_expr_bytes": 2048,
      "max_indicator_calls": 20,
      "max_timeframes": 3,
      "max_adjustments": 10,
      "max_watch_symbols": 5,
      "max_notifiers": 4,
      "max_name_chars": 60,
      "max_description_chars": 500,
      "max_tags": 10,
      "max_accounts_per_deployment": 4,
      "max_active_deployments": 3,
      "max_cycles_per_day": 20,
      "max_orders_per_day": 200,
      "min_reentry_delay_s": 60,
      "latest_mis_square_off": "15:20",
      "session_open": "09:15",
      "session_close": "15:30",
      "min_multiplier": 0.1,
      "max_multiplier": 10
    },
    "segments": [
      {
        "segment": "NSE",
        "exchanges": [
          "NSE",
          "NFO"
        ],
        "open": "09:15",
        "close": "15:30",
        "phase": "a",
        "available": true,
        "latest_square_off": "15:20",
        "default_square_off": "15:15"
      },
      {
        "segment": "BSE",
        "exchanges": [
          "BSE",
          "BFO"
        ],
        "open": "09:15",
        "close": "15:30",
        "phase": "a",
        "available": true,
        "latest_square_off": "15:20",
        "default_square_off": "15:15"
      }
    ],
    "timeframes": [
      "1m",
      "3m"
    ],
    "indicators": [
      {
        "name": "sma",
        "args": "p, iv[, sym]",
        "kind": "streaming",
        "phase": "a",
        "label": "Simple moving average",
        "available": false
      },
      {
        "name": "ema",
        "args": "p, iv[, sym]",
        "kind": "streaming",
        "phase": "a",
        "label": "Exponential moving average",
        "available": false
      }
    ],
    "variables": [
      {
        "name": "cycle.pnl",
        "phase": "a",
        "available": false
      },
      {
        "name": "cycle.minutes",
        "phase": "a",
        "available": true
      }
    ],
    "adjustment_triggers": [
      {
        "on": "loss",
        "label": "Loss reaches (rupees)",
        "fields": [
          {
            "key": "rupees",
            "label": "Rupees",
            "help": "",
            "required": true,
            "default": null
          }
        ],
        "phase": "a",
        "deployable": true
      },
      {
        "on": "loss_pct",
        "label": "Loss reaches (percent of premium)",
        "fields": [
          {
            "key": "pct",
            "label": "Percent",
            "help": "",
            "required": true,
            "default": null
          }
        ],
        "phase": "a",
        "deployable": true
      }
    ],
    "adjustment_targets": [
      {
        "target": "tested",
        "label": "The leg being tested",
        "explain": "the sold option whose price rose most against its entry, from fresh prices",
        "adjusters": [
          "close_leg@1",
          "reduce@1"
        ]
      },
      {
        "target": "sold",
        "label": "All sold legs",
        "explain": "every open sold option (hedges stay)",
        "adjusters": [
          "close_leg@1",
          "reduce@1"
        ]
      }
    ],
    "labels": {
      "instruments": [
        {
          "value": "option",
          "label": "Option"
        },
        {
          "value": "future",
          "label": "Future"
        }
      ],
      "sides": [
        {
          "value": "buy",
          "label": "Buy"
        },
        {
          "value": "sell",
          "label": "Sell"
        }
      ],
      "signal_instruments": [
        {
          "value": "signal",
          "label": "As the alert says"
        },
        {
          "value": "stock",
          "label": "The stock"
        }
      ],
      "signal_sides": [
        {
          "value": "signal",
          "label": "As the alert says"
        },
        {
          "value": "buy",
          "label": "Buy"
        }
      ],
      "option_types": [
        {
          "value": "call",
          "label": "Call"
        },
        {
          "value": "put",
          "label": "Put"
        }
      ],
      "stock_cover": [
        {
          "value": "strategy",
          "label": "Shares this strategy bought"
        },
        {
          "value": "any",
          "label": "Any shares held"
        }
      ],
      "expiry_kinds": [
        {
          "value": "weekly",
          "label": "Weekly"
        },
        {
          "value": "monthly",
          "label": "Monthly"
        }
      ],
      "premium_modes": [
        {
          "value": "nearest",
          "label": "Nearest to"
        },
        {
          "value": "at_least",
          "label": "At least"
        }
      ],
      "exchanges": [
        {
          "value": "NFO",
          "label": "NSE futures & options"
        },
        {
          "value": "BFO",
          "label": "BSE futures & options"
        }
      ],
      "references": [
        {
          "value": "future",
          "label": "Futures price"
        },
        {
          "value": "spot",
          "label": "Spot (index or stock) price"
        }
      ],
      "days": [
        {
          "value": "mon",
          "label": "Monday"
        },
        {
          "value": "tue",
          "label": "Tuesday"
        }
      ],
      "expiry_day": [
        {
          "value": "normal",
          "label": "Trade as usual"
        },
        {
          "value": "skip",
          "label": "Skip expiry day"
        }
      ],
      "evaluate_on": [
        {
          "value": "close",
          "label": "At each candle close"
        },
        {
          "value": "tick",
          "label": "On every price change"
        }
      ],
      "reentry_on": [
        {
          "value": "stop_loss",
          "label": "After a stop-loss"
        },
        {
          "value": "target",
          "label": "After a target"
        }
      ],
      "cost_models": [
        {
          "value": "auto",
          "label": "Automatic"
        },
        {
          "value": "options_india",
          "label": "Indian options charges"
        }
      ],
      "orders": [
        {
          "value": "auto",
          "label": "Automatic"
        },
        {
          "value": "hedge_first",
          "label": "Hedges first"
        }
      ],
      "on_incomplete": [
        {
          "value": "safest",
          "label": "Do the safest thing"
        },
        {
          "value": "keep_and_manage",
          "label": "Keep and manage what filled"
        }
      ],
      "on_leg_stop": [
        {
          "value": "nothing",
          "label": "Do nothing"
        },
        {
          "value": "move_others_to_cost",
          "label": "Move the other legs' stops to cost"
        }
      ],
      "combined_scopes": [
        {
          "value": "account",
          "label": "Each account"
        },
        {
          "value": "deployment",
          "label": "The whole deployment"
        }
      ],
      "bases": [
        {
          "value": "rupees",
          "label": "Rupees"
        },
        {
          "value": "pct_premium",
          "label": "% of premium"
        }
      ],
      "on_breaker": [
        {
          "value": "nothing",
          "label": "Do nothing"
        },
        {
          "value": "close_open_trade",
          "label": "Close the open trade"
        }
      ],
      "leg_targets": [
        {
          "value": "tested",
          "label": "The leg being tested"
        },
        {
          "value": "sold",
          "label": "All sold legs"
        }
      ],
      "notify_events": [
        {
          "value": "entered",
          "label": "Entered a trade"
        },
        {
          "value": "exited",
          "label": "Exited a trade"
        }
      ],
      "hedge_with": [
        {
          "value": "future",
          "label": "With futures"
        },
        {
          "value": "roll_short",
          "label": "By rolling the sold leg"
        }
      ],
      "timeframes": [
        {
          "value": "1m",
          "label": "1 minute"
        },
        {
          "value": "3m",
          "label": "3 minutes"
        }
      ],
      "deployable_exchanges": [
        {
          "value": "NFO",
          "label": "NSE futures & options"
        },
        {
          "value": "BFO",
          "label": "BSE futures & options"
        }
      ],
      "strike_methods": [
        {
          "value": "atm",
          "label": "At the money"
        },
        {
          "value": "pct_otm",
          "label": "Percent out of the money"
        }
      ],
      "slots": [
        {
          "value": "entry_source",
          "label": "Entry"
        },
        {
          "value": "exit_source",
          "label": "Exit"
        }
      ],
      "components": [
        {
          "value": "conditions@1",
          "label": "Conditions"
        },
        {
          "value": "manual@1",
          "label": "Manual"
        }
      ],
      "segments": [
        {
          "value": "NSE",
          "label": "NSE: stocks, index futures and options"
        },
        {
          "value": "BSE",
          "label": "BSE: stocks, index futures and options"
        }
      ],
      "indicators": [
        {
          "value": "sma",
          "label": "Simple moving average"
        },
        {
          "value": "ema",
          "label": "Exponential moving average"
        }
      ],
      "template_categories": [
        {
          "value": "income",
          "label": "Earn from a quiet market"
        },
        {
          "value": "hedged_income",
          "label": "Earn with protection"
        }
      ],
      "risk": [
        {
          "value": "limited",
          "label": "Limited loss"
        },
        {
          "value": "unlimited",
          "label": "Unlimited loss"
        }
      ]
    },
    "live_brokers": [
      "zerodha"
    ],
    "underlyings": [
      "NIFTY",
      "BANKNIFTY"
    ],
    "underlying_details": [
      {
        "name": "NIFTY",
        "exchange": "NFO",
        "lot_size": 75,
        "strike_step": 50,
        "expiry_kinds": [
          "weekly",
          "monthly"
        ],
        "next_expiry": "2030-12-19",
        "next_monthly_expiry": "2030-12-26",
        "spot": 25000,
        "spot_stale": false,
        "note": null
      },
      {
        "name": "BANKNIFTY",
        "exchange": null,
        "lot_size": null,
        "strike_step": null,
        "expiry_kinds": [],
        "next_expiry": null,
        "next_monthly_expiry": null,
        "spot": null,
        "spot_stale": false,
        "note": "No live options or futures in today's instrument list."
      }
    ],
    "mcx_sessions": {
      "late": {
        "open": "09:00",
        "window_close": "23:30",
        "latest_square_off": "23:00",
        "default_square_off": "22:45",
        "close_us_dst": "23:30",
        "close_us_standard": "23:55",
        "commodities": [
          "GOLD",
          "GOLDM"
        ]
      },
      "early": {
        "open": "09:00",
        "window_close": "17:00",
        "latest_square_off": "16:30",
        "default_square_off": "16:15",
        "close": "17:00"
      },
      "commodities": []
    },
    "switches": {
      "gallery": true,
      "portfolio_risk": true,
      "mcx": false,
      "stock_fno": false,
      "equity": false,
      "stock_universe": false
    },
    "engine_connected": false,
    "usage": {
      "active_deployments": 0,
      "active_deployments_left": 3
    }
  },
  "error": null
}

Strategy document JSON Schema #

GET /v2/strategies/schema/{schema_file} no auth

The JSON Schema (draft 2020-12) of the strategy document format, as {schema_version}.json (e.g.

More about this

1.json), so editors can validate and autocomplete documents. The raw schema, not the usual envelope; no credential needed.

Parameters

  • schema_file path · string required

    {schema_version}.json, e.g. 1.json.

Errors

HTTPCodeWhen
404schema_not_found

No such strategy document format on this server.

GET
curl "https://cirrus.pocketful.in/v2/strategies/schema/<schema_file>"

List strategy templates #

GET /v2/strategies/templates scope read

Ready strategies to start from (the wizard's goal step): each with a plain-English name, the goal it serves, a one-line description, a category, a risk badge (limited or unlimited loss), the components and indicators it needs, its summary, whether this server can deploy it now (with the blockers when not), and the document to create from.

More about this

Templates are data in the component registry: a new one appears here without an app release.

Errors

HTTPCodeWhen
401unauthorized

No credential, or the credential is invalid, expired or revoked.

403forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

429rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

All fields (request and response schema)

Loading fields…

GET
curl "https://cirrus.pocketful.in/v2/strategies/templates" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200 · lists shortened
{
  "status": "success",
  "data": [
    {
      "id": "short_strangle",
      "name": "Short strangle",
      "goal": "Earn from a quiet market",
      "description": "Sell an out-of-the-money call and put each morning; keep the premium while the market stays in a range.",
      "category": "income",
      "category_label": "Earn from a quiet market",
      "risk": "unlimited",
      "risk_label": "Unlimited loss",
      "components": [
        "chain@1",
        "combined@1"
      ],
      "indicators": [],
      "needs": [],
      "summary": "Sell 1 lot of a 0.2 delta call (weekly expiry) and sell 1 lot of a 0.2 delta put (weekly expiry) on NIFTY. Enter as soon as the window opens, between 09:30 and 13:30, Monday to Friday. Everything closes at 15:15. Size: the legs' lots times each account's multiplier, at most 10 lots per leg. Orders: hedges first when there are any, limit prices 0.5% beyond the market, chasing up to 1% slippage; if only some legs fill: close a sold option left without its hedge, keep the rest. Protection: stop at ₹3,000 loss, take ₹2,000 profit; no per-leg stops; at most 3 trades a day. No re-entries.",
      "deployable": true,
      "blockers": [],
      "warnings": [],
      "document": {
        "$schema": "/v2/strategies/schema/1.json",
        "schema_version": 1,
        "kind": "cirrus.strategy",
        "metadata": {
          "name": "NIFTY short strangle",
          "description": "Sell a 0.2 delta call and put at 09:30 with a combined stop.",
          "tags": [
            "nifty",
            "weekly"
          ]
        },
        "spec": {
          "underlying": {
            "name": "NIFTY",
            "exchange": "NFO",
            "reference": "future"
          },
          "watch": {},
          "positional": false,
          "legs": {
            "selector": "chain@1",
            "items": [
              {
                "id": "sc",
                "instrument": "option",
                "side": "sell",
                "option_type": "call",
                "expiry": {
                  "kind": "weekly",
                  "n": 0
                },
                "strike": {
                  "by": "delta",
                  "delta": 0.2
                },
                "lots": 1,
                "hedge_for": null,
                "risk": null,
                "liquidity": {
                  "max_spread_pct": 3,
                  "min_oi": 0,
                  "min_premium": 1
                }
              },
              {
                "id": "sp",
                "instrument": "option",
                "side": "sell",
                "option_type": "put",
                "expiry": {
                  "kind": "weekly",
                  "n": 0
                },
                "strike": {
                  "by": "delta",
                  "delta": 0.2
                },
                "lots": 1,
                "hedge_for": null,
                "risk": null,
                "liquidity": {
                  "max_spread_pct": 3,
                  "min_oi": 0,
                  "min_premium": 1
                }
              }
            ]
          },
          "entry": {
            "source": {
              "type": "conditions@1",
              "when": null,
              "on": "close",
              "hold_s": 0
            },
            "window": {
              "start": "09:30",
              "end": "13:30"
            },
            "skip_first_min": 0,
            "skip_last_min": 0,
            "days": [
              "mon",
              "tue"
            ],
            "expiry_day": "normal",
            "active_from": null,
            "active_until": null
          },
          "exit": {
            "sources": [],
            "square_off": "15:15",
            "expiry_day_exit": null,
            "max_hold_min": null
          },
          "sizing": {
            "type": "fixed_lots@1",
            "max_lots": 10
          },
          "execution": {
            "type": "protected_limit@1",
            "order": "auto",
            "hedge_wait_ms": 5000,
            "stagger_ms": 0,
            "buffer_pct": 0.5,
            "chase": {
              "every_ms": 1000,
              "max_slippage_pct": 1,
              "max_attempts": 10
            },
            "on_incomplete": "safest",
            "retry_next_signal": null
          },
          "risk": {
            "per_leg": {
              "type": "per_leg@1",
              "stop_loss": null,
              "target": null,
              "trail": null,
              "on_leg_stop": "nothing",
              "at_broker": false
            },
            "combined": {
              "type": "combined@1",
              "scope": "account",
              "basis": "rupees",
              "stop_loss": {
                "rupees": 3000
              },
              "target": {
                "rupees": 2000
              },
              "breakeven": null,
              "trail": null,
              "lock_ladder": [],
              "deployment": null
            },
            "limits": {
              "type": "limits@1",
              "max_loss_per_day": null,
              "max_profit_per_day": null,
              "max_loss_per_week": null,
              "max_cycles_per_day": 3,
              "max_orders_per_day": 50,
              "exit_on_limit": true,
              "on_breaker": "nothing"
            }
          },
          "reentry": {
            "on": [],
            "max": 0,
            "delay_s": 300,
            "require_fresh_signal": true,
            "same_direction_only": true,
            "until": null
          },
          "adjustments": [],
          "notify": [],
          "costs": {
            "model": "auto",
            "slippage_pct": null
          }
        }
      }
    },
    {
      "id": "rsi_strangle",
      "name": "RSI-gated strangle",
      "goal": "Earn from a quiet market",
      "description": "Sell a 0.2 delta strangle only when the 5-minute RSI says the market is calm (between 45 and 60).",
      "category": "income",
      "category_label": "Earn from a quiet market",
      "risk": "unlimited",
      "risk_label": "Unlimited loss",
      "components": [
        "app@1",
        "chain@1"
      ],
      "indicators": [
        "rsi"
      ],
      "needs": [],
      "summary": "Sell 1 lot of a 0.2 delta call (weekly expiry) and sell 1 lot of a 0.2 delta put (weekly expiry) on NIFTY. Enter when rsi(14, '5m') > 45 && rsi(14, '5m') < 60 (checked at each bar close), between 09:30 and 13:30, Monday to Friday, not on expiry day. Everything closes at 15:15. Size: the legs' lots times each account's multiplier, at most 4 lots per leg. Orders: all legs together, limit prices 0.5% beyond the market, chasing up to 1% slippage; if only some legs fill: keep and manage whatever filled. Protection: stop at ₹3,000 loss, take ₹2,000 profit, move the stop to cost at ₹1,000 profit; each leg: stop at 50% against it; when a leg stops, move the others' stops to cost; stops are also placed at the broker; at most 3 trades and ₹6,000 loss a day. Try again up to 2 times after a stop, 10 minutes later, until 13:30, only on a fresh signal. Updates to app: entered, exited, stops, adjusted, needs attention and daily summary.",
      "deployable": false,
      "blockers": [
        {
          "field": "spec.entry.source.when",
          "label": "When · Entry",
          "message": "`rsi(14, '5m') > 45 && rsi(14, '5m') < 60` uses rsi, which isn't available on this server yet (indicator conditions are coming). This strategy can be saved but not deployed until then.",
          "code": "not_available_yet",
          "needs_ack": false
        }
      ],
      "warnings": [],
      "document": {
        "$schema": "/v2/strategies/schema/1.json",
        "schema_version": 1,
        "kind": "cirrus.strategy",
        "metadata": {
          "name": "NIFTY RSI strangle",
          "description": "Sell a 0.2 delta strangle when NIFTY's 5-minute RSI is between 45 and 60.",
          "tags": [
            "weekly",
            "nifty"
          ]
        },
        "spec": {
          "underlying": {
            "name": "NIFTY",
            "exchange": "NFO",
            "reference": "future"
          },
          "watch": {
            "u": "NSE_INDEX:NIFTY 50"
          },
          "positional": false,
          "legs": {
            "selector": "chain@1",
            "items": [
              {
                "id": "sc",
                "instrument": "option",
                "side": "sell",
                "option_type": "call",
                "expiry": {
                  "kind": "weekly",
                  "n": 0
                },
                "strike": {
                  "by": "delta",
                  "delta": 0.2
                },
                "lots": 1,
                "hedge_for": null,
                "risk": null,
                "liquidity": {
                  "max_spread_pct": 3,
                  "min_oi": 0,
                  "min_premium": 1
                }
              },
              {
                "id": "sp",
                "instrument": "option",
                "side": "sell",
                "option_type": "put",
                "expiry": {
                  "kind": "weekly",
                  "n": 0
                },
                "strike": {
                  "by": "delta",
                  "delta": 0.2
                },
                "lots": 1,
                "hedge_for": null,
                "risk": null,
                "liquidity": {
                  "max_spread_pct": 3,
                  "min_oi": 0,
                  "min_premium": 1
                }
              }
            ]
          },
          "entry": {
            "source": {
              "type": "conditions@1",
              "when": "rsi(14, '5m') > 45 && rsi(14, '5m') < 60",
              "on": "close",
              "hold_s": 0
            },
            "window": {
              "start": "09:30",
              "end": "13:30"
            },
            "skip_first_min": 0,
            "skip_last_min": 0,
            "days": [
              "mon",
              "tue"
            ],
            "expiry_day": "skip",
            "active_from": null,
            "active_until": null
          },
          "exit": {
            "sources": [],
            "square_off": "15:15",
            "expiry_day_exit": null,
            "max_hold_min": null
          },
          "sizing": {
            "type": "fixed_lots@1",
            "max_lots": 4
          },
          "execution": {
            "type": "protected_limit@1",
            "order": "simultaneous",
            "hedge_wait_ms": 5000,
            "stagger_ms": 0,
            "buffer_pct": 0.5,
            "chase": {
              "every_ms": 1000,
              "max_slippage_pct": 1,
              "max_attempts": 10
            },
            "on_incomplete": "keep_and_manage",
            "retry_next_signal": 0
          },
          "risk": {
            "per_leg": {
              "type": "per_leg@1",
              "stop_loss": {
                "pct": 50
              },
              "target": null,
              "trail": null,
              "on_leg_stop": "move_others_to_cost",
              "at_broker": true
            },
            "combined": {
              "type": "combined@1",
              "scope": "account",
              "basis": "rupees",
              "stop_loss": {
                "rupees": 3000
              },
              "target": {
                "rupees": 2000
              },
              "breakeven": {
                "at_rupees": 1000
              },
              "trail": null,
              "lock_ladder": [],
              "deployment": null
            },
            "limits": {
              "type": "limits@1",
              "max_loss_per_day": 6000,
              "max_profit_per_day": null,
              "max_loss_per_week": null,
              "max_cycles_per_day": 3,
              "max_orders_per_day": 50,
              "exit_on_limit": true,
              "on_breaker": "nothing"
            }
          },
          "reentry": {
            "on": [
              "stop_loss"
            ],
            "max": 2,
            "delay_s": 600,
            "require_fresh_signal": true,
            "same_direction_only": true,
            "until": "13:30"
          },
          "adjustments": [],
          "notify": [
            {
              "type": "app@1",
              "events": [
                "entered",
                "exited"
              ]
            }
          ],
          "costs": {
            "model": "auto",
            "slippage_pct": null
          }
        }
      }
    }
  ],
  "error": null
}

Today's stock safety checks #

GET /v2/strategies/stock-status scope read

For each stock asked (1 to 50 NSE symbols), the checks the engine runs before a new stock entry today: fno_ban (NSE's F&O ban list; options and futures), ex_date (today's corporate actions; shares and F&O) and circuit (the price within circuit_gap_pct of a circuit limit; shares).

More about this

Each check is clear, blocked or unknown (a list not written for today, stale or unreadable, a stock not in today's list, or no fresh price: new entries wait, as in the engine), with the engine's waiting code and a reason. new_entries is the worst of the three. Open trades keep every exit whatever these say.

Parameters

  • symbols query · string required

    NSE symbols, comma separated (1 to 50), e.g. RELIANCE,TCS.

  • circuit_gap_pct query · number (double)

    The circuit gap to check (0.5 to 10 percent of the limit, default 2): a strategy's stock.circuit_gap_pct.

Errors

HTTPCodeWhen
401unauthorized

No credential, or the credential is invalid, expired or revoked.

403forbidden

The credential may not use this route (missing scope, app-session-only route, IP not allowed).

422invalid_request

The request is well-formed but not valid; field names the input.

429rate_limited

Too many requests for this credential (or too many failed attempts); retry after the Retry-After header (seconds; error.retry_after_ms has the exact wait).

All fields (request and response schema)

Loading fields…

GET
curl "https://cirrus.pocketful.in/v2/strategies/stock-status?symbols=<symbols>" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "trading_day": "2026-10-26",
    "circuit_gap_pct": 2,
    "stocks": [
      {
        "name": "RELIANCE",
        "new_entries": "blocked",
        "fno_ban": {
          "state": "clear",
          "code": null,
          "reason": null
        },
        "ex_date": {
          "state": "blocked",
          "code": "ex_date",
          "reason": "RELIANCE goes ex-date today"
        },
        "corporate_actions": [
          "Dividend - Rs 10 Per Share"
        ],
        "circuit": {
          "state": "clear",
          "code": null,
          "reason": null,
          "ltp": 1400,
          "lower_circuit": 1260,
          "upper_circuit": 1540
        }
      },
      {
        "name": "TCS",
        "new_entries": "blocked",
        "fno_ban": {
          "state": "clear",
          "code": null,
          "reason": null
        },
        "ex_date": {
          "state": "clear",
          "code": null,
          "reason": null
        },
        "corporate_actions": [],
        "circuit": {
          "state": "blocked",
          "code": "circuit",
          "reason": "TCS: it trades at 3500.00, within 2% of its upper circuit 3520.00",
          "ltp": 3500,
          "lower_circuit": 3150,
          "upper_circuit": 3520
        }
      },
      {
        "name": "IDEA",
        "new_entries": "blocked",
        "fno_ban": {
          "state": "blocked",
          "code": "fno_ban",
          "reason": "IDEA is in NSE's F&O ban period today"
        },
        "ex_date": {
          "state": "clear",
          "code": null,
          "reason": null
        },
        "corporate_actions": [],
        "circuit": {
          "state": "unknown",
          "code": "circuit_unknown",
          "reason": "IDEA: its circuit limits or price are not known",
          "ltp": null,
          "lower_circuit": null,
          "upper_circuit": null
        }
      }
    ]
  },
  "error": null
}