Skip to content
Cirrus home
Sign in

Backtests

Run a strategy version over past days and read its trades.

Read the guide first

List backtests #

GET /v2/backtests scope read

The caller's backtests, newest first (at most 50; kept 30 days), without their daily equity.

Errors

HTTP Code When
401 unauthorized

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

403 forbidden

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

429 rate_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://api.cirrus.trade/v2/backtests" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": [
    {
      "id": "01M66QA2YJGRWYHB9VJRAW6BRW",
      "strategy_id": "01M66QA2V4VGYTC47SA6EEBXZM",
      "version": 1,
      "status": "cancelled",
      "from": "2026-10-14",
      "to": "2026-10-16",
      "capital": 500000,
      "costs": {
        "charges": "india",
        "slippage_bps": 2,
        "spread_proxy_bps": 10
      },
      "progress": {
        "done": 0,
        "total": 0
      },
      "cancel_requested": true,
      "created_at": "2026-10-30T15:37:00.627Z",
      "started_at": null,
      "finished_at": "2026-10-30T15:37:00.640Z",
      "expires_at": "2026-11-29T15:37:00.627Z",
      "error": null,
      "summary": null,
      "labels": null,
      "sources": [],
      "counts": {
        "trades": 0,
        "cycles": 0
      }
    },
    {
      "id": "01M66QA2X382CT2C47RR5XN0FY",
      "strategy_id": "01M66QA2V4VGYTC47SA6EEBXZM",
      "version": 1,
      "status": "done",
      "from": "2026-10-14",
      "to": "2026-10-16",
      "capital": 500000,
      "costs": {
        "charges": "india",
        "slippage_bps": 2,
        "spread_proxy_bps": 10
      },
      "progress": {
        "done": 3,
        "total": 3
      },
      "cancel_requested": false,
      "created_at": "2026-10-30T15:37:00.588Z",
      "started_at": "2026-10-30T15:37:00.752Z",
      "finished_at": "2026-10-30T15:37:02.016Z",
      "expires_at": "2026-11-29T15:37:00.588Z",
      "error": null,
      "summary": {
        "capital": 500000,
        "final_equity": 496635.79,
        "net_pnl": -3364.21,
        "charges": 307.96,
        "cycles": 3,
        "trades": 12,
        "wins": 0,
        "losses": 3,
        "win_rate": 0,
        "best_cycle": -54.99,
        "worst_cycle": -3176.51,
        "avg_cycle": -1121.4,
        "max_drawdown": 3592.73,
        "max_drawdown_pct": 0.0072,
        "days": 3,
        "days_traded": 3,
        "metrics": {
          "total_return": -0.0066,
          "cagr": -0.4276,
          "annualized_vol": 0.0462,
          "sharpe": -12.247,
          "sortino": -9.6967,
          "calmar": -64.5952,
          "max_drawdown": 0.0066,
          "profit_factor": 0
        },
        "band": null
      },
      "labels": {
        "estimated": true,
        "text": "Estimated: option prices are modelled from NIFTY and India VIX, not real trades",
        "notes": [
          "Expiries follow the exchange's weekday rules, strikes are 50 points apart and one lot is 75 units (today's lot size).",
          "Every strike uses the same volatility: India VIX × 1; real prices have a skew.",
          "Fills follow the forward test's rules: protected limits on entry, crossing the spread on exit, 2 bps slippage."
        ]
      },
      "sources": [
        {
          "series": "underlying",
          "symbol": "CT:EX:BTNIFTY50",
          "source": "chart_api",
          "estimated": false
        },
        {
          "series": "vix",
          "symbol": "CT:EX:BTINDIAVIX",
          "source": "chart_api",
          "estimated": false
        },
        {
          "series": "options",
          "symbol": "NFO:NIFTY",
          "source": "estimator",
          "estimated": true,
          "note": "no option history yet: prices are modelled"
        },
        {
          "series": "futures",
          "symbol": "NFO:NIFTY",
          "source": "estimator",
          "estimated": true,
          "note": "no option history yet: prices are modelled"
        }
      ],
      "counts": {
        "trades": 12,
        "cycles": 3
      }
    }
  ],
  "error": null
}

Start a backtest #

POST /v2/backtests scope strategies

Queues a backtest of a saved strategy version over past days.

More about this

It runs the same engine as live deployments over 1-minute history, with simulated fills by the forward test's rules; option prices are modelled from the underlying and India VIX when no option history exists, and the result says so (labels). Poll GET /v2/backtests/{backtest_id}. Limits: at most 1098 days (3 years) per run, 3 queued or running and 50 a day per user, 10 submissions a minute.

Parameters

  • Idempotency-Key header · string

    Optional: a new UUID per request (1-128 printable characters). A repeat with the same key gets the first answer back instead of acting again.

Body fields

  • strategy_id string required
  • from string required

    First day (YYYY-MM-DD, IST).

  • to string required

    Last day, inclusive; before today. from to to covers at most 1098 days (3 years), both included.

  • version integer (int32) | null

    The version to test; the latest saved one when absent.

  • capital number (double) | null

    Virtual capital (₹); default 5,00,000.

  • costs object | null

    Costs to apply; defaults: Indian charges, 2 bps slippage.

Errors

HTTP Code When
401 unauthorized

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

403 forbidden

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

404 version_not_found

No such version of the strategy.

409 backtest_limit_reached

The most backtests that may be queued or running at once, or started today, are reached.

422 invalid_request

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

429 rate_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://api.cirrus.trade/v2/backtests" \
  -H "Authorization: token $CIRRUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "strategy_id": "01M66QA2V4VGYTC47SA6EEBXZM",
    "version": 1,
    "from": "2026-10-14",
    "to": "2026-10-16",
    "capital": 500000,
    "costs": {
      "charges": "india",
      "slippage_bps": 2
    }
  }'
Response · 202
{
  "status": "success",
  "data": {
    "id": "01M66QA2X382CT2C47RR5XN0FY",
    "strategy_id": "01M66QA2V4VGYTC47SA6EEBXZM",
    "version": 1,
    "status": "queued",
    "from": "2026-10-14",
    "to": "2026-10-16",
    "capital": 500000,
    "costs": {
      "charges": "india",
      "slippage_bps": 2,
      "spread_proxy_bps": 10
    },
    "progress": {
      "done": 0,
      "total": 0
    },
    "cancel_requested": false,
    "created_at": "2026-10-30T15:37:00.588Z",
    "started_at": null,
    "finished_at": null,
    "expires_at": "2026-11-29T15:37:00.588Z",
    "error": null,
    "summary": null,
    "labels": null,
    "sources": [],
    "counts": {
      "trades": 0,
      "cycles": 0
    }
  },
  "error": null
}

Get a backtest #

GET /v2/backtests/{backtest_id} scope read

Where the backtest is (queued, running with progress, done, failed, cancelled) and, once done, its summary, daily equity, labels and sources.

Parameters

  • backtest_id path · string required

    Backtest id.

Errors

HTTP Code When
401 unauthorized

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

403 forbidden

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

404 backtest_not_found

No backtest with this id for the caller (results are kept 30 days).

429 rate_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://api.cirrus.trade/v2/backtests/<backtest_id>" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "id": "01M66QA2X382CT2C47RR5XN0FY",
    "strategy_id": "01M66QA2V4VGYTC47SA6EEBXZM",
    "version": 1,
    "status": "done",
    "from": "2026-10-14",
    "to": "2026-10-16",
    "capital": 500000,
    "costs": {
      "charges": "india",
      "slippage_bps": 2,
      "spread_proxy_bps": 10
    },
    "progress": {
      "done": 3,
      "total": 3
    },
    "cancel_requested": false,
    "created_at": "2026-10-30T15:37:00.588Z",
    "started_at": "2026-10-30T15:37:00.752Z",
    "finished_at": "2026-10-30T15:37:02.016Z",
    "expires_at": "2026-11-29T15:37:00.588Z",
    "error": null,
    "summary": {
      "capital": 500000,
      "final_equity": 496635.79,
      "net_pnl": -3364.21,
      "charges": 307.96,
      "cycles": 3,
      "trades": 12,
      "wins": 0,
      "losses": 3,
      "win_rate": 0,
      "best_cycle": -54.99,
      "worst_cycle": -3176.51,
      "avg_cycle": -1121.4,
      "max_drawdown": 3592.73,
      "max_drawdown_pct": 0.0072,
      "days": 3,
      "days_traded": 3,
      "metrics": {
        "total_return": -0.0066,
        "cagr": -0.4276,
        "annualized_vol": 0.0462,
        "sharpe": -12.247,
        "sortino": -9.6967,
        "calmar": -64.5952,
        "max_drawdown": 0.0066,
        "profit_factor": 0
      },
      "band": null
    },
    "labels": {
      "estimated": true,
      "text": "Estimated: option prices are modelled from NIFTY and India VIX, not real trades",
      "notes": [
        "Expiries follow the exchange's weekday rules, strikes are 50 points apart and one lot is 75 units (today's lot size).",
        "Every strike uses the same volatility: India VIX × 1; real prices have a skew.",
        "Fills follow the forward test's rules: protected limits on entry, crossing the spread on exit, 2 bps slippage."
      ]
    },
    "sources": [
      {
        "series": "underlying",
        "symbol": "CT:EX:BTNIFTY50",
        "source": "chart_api",
        "estimated": false
      },
      {
        "series": "vix",
        "symbol": "CT:EX:BTINDIAVIX",
        "source": "chart_api",
        "estimated": false
      },
      {
        "series": "options",
        "symbol": "NFO:NIFTY",
        "source": "estimator",
        "estimated": true,
        "note": "no option history yet: prices are modelled"
      },
      {
        "series": "futures",
        "symbol": "NFO:NIFTY",
        "source": "estimator",
        "estimated": true,
        "note": "no option history yet: prices are modelled"
      }
    ],
    "equity": [
      {
        "day": "2026-10-14",
        "equity": 499945.01,
        "pnl": -54.99,
        "drawdown": 283.51
      },
      {
        "day": "2026-10-15",
        "equity": 499812.3,
        "pnl": -132.71,
        "drawdown": 416.22
      },
      {
        "day": "2026-10-16",
        "equity": 496635.79,
        "pnl": -3176.51,
        "drawdown": 3592.73
      }
    ],
    "counts": {
      "trades": 12,
      "cycles": 3
    }
  },
  "error": null
}

List a backtest's trades #

GET /v2/backtests/{backtest_id}/trades scope read

The simulated fills of a finished backtest in time order, a page at a time (offset, limit up to 200).

More about this

estimated marks modelled prices.

Parameters

  • backtest_id path · string required

    Backtest id.

  • offset query · integer (int32)
  • limit query · integer (int32)

Errors

HTTP Code When
401 unauthorized

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

403 forbidden

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

404 backtest_not_found

No backtest with this id for the caller (results are kept 30 days).

422 invalid_request

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

429 rate_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://api.cirrus.trade/v2/backtests/<backtest_id>/trades" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "items": [
      {
        "n": 1,
        "day": "2026-10-14",
        "cycle": 1,
        "leg_id": "sc",
        "symbol": "BT:NFO:NIFTY:20261019:24450:CE",
        "side": "SELL",
        "qty": 75,
        "price": 36.6,
        "charges": 27.74,
        "at_ms": 1791950400000,
        "purpose": "entry",
        "estimated": true
      },
      {
        "n": 2,
        "day": "2026-10-14",
        "cycle": 1,
        "leg_id": "sp",
        "symbol": "BT:NFO:NIFTY:20261019:23850:PE",
        "side": "SELL",
        "qty": 75,
        "price": 45.5,
        "charges": 28.74,
        "at_ms": 1791950400000,
        "purpose": "entry",
        "estimated": true
      }
    ],
    "offset": 0,
    "limit": 2,
    "total": 12
  },
  "error": null
}

List a backtest's cycles #

GET /v2/backtests/{backtest_id}/cycles scope read

The cycles of a finished backtest in time order, each with its P&L and journey (the engine's own steps and sentences), a page at a time (offset, limit up to 200).

Parameters

  • backtest_id path · string required

    Backtest id.

  • offset query · integer (int32)
  • limit query · integer (int32)

Errors

HTTP Code When
401 unauthorized

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

403 forbidden

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

404 backtest_not_found

No backtest with this id for the caller (results are kept 30 days).

422 invalid_request

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

429 rate_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://api.cirrus.trade/v2/backtests/<backtest_id>/cycles" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "items": [
      {
        "n": 1,
        "day": "2026-10-14",
        "cycle": 1,
        "opened_at_ms": 1791950400000,
        "closed_at_ms": 1791971100000,
        "exit_reason": "square_off",
        "pnl": -54.99,
        "charges": 103.74,
        "steps": [
          {
            "at_ms": 1791950400000,
            "code": "entry_sent",
            "phase": "entering",
            "text": "The entry window is open (time-based entry). Entering: sell 1 lot call, sell 1 lot put."
          },
          {
            "at_ms": 1791950400000,
            "code": "entered",
            "phase": "managing",
            "text": "Entered: sold 75 × BT:NFO:NIFTY:20261019:24450:CE at ₹36.60, sold 75 × BT:NFO:NIFTY:20261019:23850:PE at ₹45.50. Protected: combined stop at -₹3,000, target at +₹2,000, square-off at 15:15."
          },
          {
            "at_ms": 1791971100000,
            "code": "exit_sent",
            "phase": "exiting",
            "text": "Square-off time: P&L +₹75. Closing the trade."
          },
          {
            "at_ms": 1791971100000,
            "code": "cycle_closed",
            "phase": "cooldown",
            "text": "Closed every leg. Trade P&L +₹49 (charges excluded). Today: +₹49 · 1 of 3 trades."
          },
          {
            "at_ms": 1791971100000,
            "code": "finished",
            "phase": "finished",
            "text": "No more trades today (re-entry rules)."
          }
        ]
      }
    ],
    "offset": 0,
    "limit": 1,
    "total": 3
  },
  "error": null
}

Cancel a backtest #

POST /v2/backtests/{backtest_id}/cancel scope strategies

A queued backtest is cancelled at once; a running one stops within seconds (cancel_requested until it does).

More about this

A finished one answers 409 backtest_finished.

Parameters

  • backtest_id path · string required

    Backtest id.

  • Idempotency-Key header · string

    Optional: a new UUID per request (1-128 printable characters). A repeat with the same key gets the first answer back instead of acting again.

Errors

HTTP Code When
401 unauthorized

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

403 forbidden

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

404 backtest_not_found

No backtest with this id for the caller (results are kept 30 days).

409 backtest_finished

The backtest has already finished (done, failed or cancelled); nothing to cancel.

429 rate_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://api.cirrus.trade/v2/backtests/<backtest_id>/cancel" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "id": "01M66QA2YJGRWYHB9VJRAW6BRW",
    "strategy_id": "01M66QA2V4VGYTC47SA6EEBXZM",
    "version": 1,
    "status": "cancelled",
    "from": "2026-10-14",
    "to": "2026-10-16",
    "capital": 500000,
    "costs": {
      "charges": "india",
      "slippage_bps": 2,
      "spread_proxy_bps": 10
    },
    "progress": {
      "done": 0,
      "total": 0
    },
    "cancel_requested": true,
    "created_at": "2026-10-30T15:37:00.627Z",
    "started_at": null,
    "finished_at": "2026-10-30T15:37:00.640Z",
    "expires_at": "2026-11-29T15:37:00.627Z",
    "error": null,
    "summary": null,
    "labels": null,
    "sources": [],
    "counts": {
      "trades": 0,
      "cycles": 0
    }
  },
  "error": null
}

List sweeps #

GET /v2/backtests/sweeps scope read

The caller's sweeps, newest first (at most 50; kept 30 days), without their versions.

Errors

HTTP Code When
401 unauthorized

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

403 forbidden

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

429 rate_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://api.cirrus.trade/v2/backtests/sweeps" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": [
    {
      "id": "01M6729QKEQ99A4TXHNMZAA7NG",
      "strategy_id": "01M6729PTSDV47N0JD25NBZQVX",
      "version": 1,
      "status": "done",
      "from": "2026-10-14",
      "to": "2026-10-16",
      "capital": 500000,
      "costs": {
        "charges": "india",
        "slippage_bps": 2,
        "spread_proxy_bps": 10
      },
      "axes": [
        {
          "path": "/spec/legs/items/0/strike/delta",
          "values": [
            0.15,
            0.2
          ]
        },
        {
          "path": "/spec/risk/combined/stop_loss/rupees",
          "values": [
            2000,
            3000
          ]
        }
      ],
      "progress": {
        "total": 4,
        "queued": 0,
        "running": 0,
        "done": 3,
        "failed": 1,
        "cancelled": 0
      },
      "cost": 1,
      "cancel_requested": false,
      "created_at": "2026-10-30T18:49:03.347Z",
      "finished_at": "2026-10-30T18:49:04.277Z",
      "expires_at": "2026-11-29T18:49:03.347Z",
      "error": null,
      "recommended": 0,
      "warnings": [
        "Best result may be luck: after testing 3 versions, the chance the best one is really better than average is low (deflated Sharpe 0.17).",
        "Fewer than 30 trades: too few to judge."
      ],
      "notes": [
        "Walk-forward test skipped: it needs at least 100 trading days of history (20 per window), and this sweep has 3.",
        "Overfitting check (PBO) skipped: it needs at least 16 versions, and this sweep has 3.",
        "1 of 4 versions did not finish (failed or cancelled) and are left out of the checks."
      ],
      "deflated_sharpe": 0.1691982360279356,
      "pbo": null,
      "walk_forward": null
    }
  ],
  "error": null
}

Start a sweep #

POST /v2/backtests/sweeps scope strategies

Backtests every version of up to 3 settings of a saved strategy version over the same past days (one backtest per version, same history and fill rules as POST /v2/backtests).

More about this

Each setting is a JSON pointer to a number in the strategy document and the values to try; at most 200 versions, each checked as a strategy document. Poll GET /v2/backtests/sweeps/{sweep_id}: once done it names the recommended version (the best plateau of neighbouring settings, not the best single result) and the overfitting warnings to show before deploy. One sweep at a time per user; its versions run at most 3 at a time, sharing workers fairly with other users. A sweep counts as 1 backtest plus 1 per 20 versions against the 50 a day, and as one of the 3 running at once.

Parameters

  • Idempotency-Key header · string

    Optional: a new UUID per request (1-128 printable characters). A repeat with the same key gets the first answer back instead of acting again.

Body fields

  • strategy_id string required
  • axes object[] required

    1 to 3 settings; at most 200 versions in all.

  • from string required

    First day (YYYY-MM-DD, IST).

  • to string required

    Last day, inclusive; before today. from to to covers at most 1098 days (3 years), both included.

  • version integer (int32) | null

    The saved version the sweep starts from; the latest when absent.

  • capital number (double) | null

    Virtual capital (₹); default 5,00,000.

  • costs object | null

    Costs to apply; defaults: Indian charges, 2 bps slippage.

Errors

HTTP Code When
401 unauthorized

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

403 forbidden

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

404 version_not_found

No such version of the strategy.

409 backtest_limit_reached

The most backtests that may be queued or running at once, or started today, are reached.

422 invalid_request

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

429 rate_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://api.cirrus.trade/v2/backtests/sweeps" \
  -H "Authorization: token $CIRRUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "strategy_id": "01M66YJSSC5QFRPRW0AF5Y9HXM",
    "version": 1,
    "from": "2026-10-14",
    "to": "2026-10-16",
    "axes": [
      {
        "path": "/spec/legs/items/0/strike/delta",
        "values": [
          0.15,
          0.2
        ]
      },
      {
        "path": "/spec/risk/combined/stop_loss/rupees",
        "values": [
          2000,
          3000
        ]
      }
    ]
  }'
Response · 202
{
  "status": "success",
  "data": {
    "id": "01M66YJTX11JWSA4K90RGYJPMA",
    "strategy_id": "01M66YJSSC5QFRPRW0AF5Y9HXM",
    "version": 1,
    "status": "running",
    "from": "2026-10-14",
    "to": "2026-10-16",
    "capital": 500000,
    "costs": {
      "charges": "india",
      "slippage_bps": 2,
      "spread_proxy_bps": 10
    },
    "axes": [
      {
        "path": "/spec/legs/items/0/strike/delta",
        "values": [
          0.15,
          0.2
        ]
      },
      {
        "path": "/spec/risk/combined/stop_loss/rupees",
        "values": [
          2000,
          3000
        ]
      }
    ],
    "progress": {
      "total": 4,
      "queued": 4,
      "running": 0,
      "done": 0,
      "failed": 0,
      "cancelled": 0
    },
    "cost": 1,
    "cancel_requested": false,
    "created_at": "2026-10-30T17:44:07.342Z",
    "finished_at": null,
    "expires_at": "2026-11-29T17:44:07.342Z",
    "error": null,
    "recommended": null,
    "warnings": [],
    "notes": [],
    "deflated_sharpe": null,
    "pbo": null,
    "walk_forward": null
  },
  "error": null
}

Get a sweep #

GET /v2/backtests/sweeps/{sweep_id} scope read

Where the sweep is (running with progress, then done, failed or cancelled), every version with its values, status and summary, and once done the recommended version, its scores, the warnings to show before deploy and notes on checks that did not run or versions left out.

Parameters

  • sweep_id path · string required

    Sweep id.

Errors

HTTP Code When
401 unauthorized

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

403 forbidden

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

404 backtest_not_found

No backtest with this id for the caller (results are kept 30 days).

429 rate_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://api.cirrus.trade/v2/backtests/sweeps/<sweep_id>" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "id": "01M6729QKEQ99A4TXHNMZAA7NG",
    "strategy_id": "01M6729PTSDV47N0JD25NBZQVX",
    "version": 1,
    "status": "done",
    "from": "2026-10-14",
    "to": "2026-10-16",
    "capital": 500000,
    "costs": {
      "charges": "india",
      "slippage_bps": 2,
      "spread_proxy_bps": 10
    },
    "axes": [
      {
        "path": "/spec/legs/items/0/strike/delta",
        "values": [
          0.15,
          0.2
        ]
      },
      {
        "path": "/spec/risk/combined/stop_loss/rupees",
        "values": [
          2000,
          3000
        ]
      }
    ],
    "progress": {
      "total": 4,
      "queued": 0,
      "running": 0,
      "done": 3,
      "failed": 1,
      "cancelled": 0
    },
    "cost": 1,
    "cancel_requested": false,
    "created_at": "2026-10-30T18:49:03.347Z",
    "finished_at": "2026-10-30T18:49:04.277Z",
    "expires_at": "2026-11-29T18:49:03.347Z",
    "error": null,
    "recommended": 0,
    "warnings": [
      "Best result may be luck: after testing 3 versions, the chance the best one is really better than average is low (deflated Sharpe 0.17).",
      "Fewer than 30 trades: too few to judge."
    ],
    "notes": [
      "Walk-forward test skipped: it needs at least 100 trading days of history (20 per window), and this sweep has 3.",
      "Overfitting check (PBO) skipped: it needs at least 16 versions, and this sweep has 3.",
      "1 of 4 versions did not finish (failed or cancelled) and are left out of the checks."
    ],
    "deflated_sharpe": 0.1691982360279356,
    "pbo": null,
    "walk_forward": null,
    "versions": [
      {
        "n": 0,
        "values": [
          0.15,
          2000
        ],
        "backtest_id": "01M6729QKFE6A2XEC6Q2PBZZ26",
        "status": "done",
        "error": null,
        "summary": {
          "capital": 500000,
          "final_equity": 498079.58,
          "net_pnl": -1920.42,
          "charges": 304.17,
          "cycles": 3,
          "trades": 12,
          "wins": 2,
          "losses": 1,
          "win_rate": 0.67,
          "best_cycle": 182.21,
          "worst_cycle": -2193.13,
          "avg_cycle": -640.14,
          "max_drawdown": 2394.76,
          "max_drawdown_pct": 0.0048,
          "days": 3,
          "days_traded": 3,
          "metrics": {
            "total_return": -0.0042,
            "cagr": -0.298,
            "annualized_vol": 0.0349,
            "sharpe": -9.2477,
            "sortino": -8.025,
            "calmar": -67.9822,
            "max_drawdown": 0.0044,
            "profit_factor": 0
          },
          "band": null
        },
        "sharpe": -0.475648339981058,
        "plateau": -0.5498070234280426,
        "recommended": true
      },
      {
        "n": 1,
        "values": [
          0.15,
          3000
        ],
        "backtest_id": "01M6729QKG9GG46QSCPWZ7S1Q8",
        "status": "done",
        "error": null,
        "summary": {
          "capital": 500000,
          "final_equity": 497097.07,
          "net_pnl": -2902.93,
          "charges": 304.18,
          "cycles": 3,
          "trades": 12,
          "wins": 2,
          "losses": 1,
          "win_rate": 0.67,
          "best_cycle": 182.21,
          "worst_cycle": -3175.64,
          "avg_cycle": -967.64,
          "max_drawdown": 3377.27,
          "max_drawdown_pct": 0.0067,
          "days": 3,
          "days_traded": 3,
          "metrics": {
            "total_return": -0.0062,
            "cagr": -0.4053,
            "annualized_vol": 0.0496,
            "sharpe": -9.8352,
            "sortino": -8.3778,
            "calmar": -63.8507,
            "max_drawdown": 0.0063,
            "profit_factor": 0
          },
          "band": null
        },
        "sharpe": -0.5058669394394733,
        "plateau": -0.5498070234280426,
        "recommended": false
      },
      {
        "n": 2,
        "values": [
          0.2,
          2000
        ],
        "backtest_id": "01M6729QKHZPHC8KN8E4K30RKP",
        "status": "done",
        "error": null,
        "summary": {
          "capital": 500000,
          "final_equity": 497933.3,
          "net_pnl": -2066.7,
          "charges": 307.95,
          "cycles": 3,
          "trades": 12,
          "wins": 0,
          "losses": 3,
          "win_rate": 0,
          "best_cycle": -54.99,
          "worst_cycle": -1879,
          "avg_cycle": -688.9,
          "max_drawdown": 2397.95,
          "max_drawdown_pct": 0.0048,
          "days": 3,
          "days_traded": 3,
          "metrics": {
            "total_return": -0.004,
            "cagr": -0.2873,
            "annualized_vol": 0.0267,
            "sharpe": -12.9856,
            "sortino": -10.0511,
            "calmar": -71.3976,
            "max_drawdown": 0.004,
            "profit_factor": 0
          },
          "band": null
        },
        "sharpe": -0.6679057908635965,
        "plateau": -0.5498070234280426,
        "recommended": false
      },
      {
        "n": 3,
        "values": [
          0.2,
          3000
        ],
        "backtest_id": "01M6729QKJ72C8CF01BZ1ND5C9",
        "status": "failed",
        "error": "History for NIFTY 50 is missing on 2026-10-15",
        "summary": null,
        "sharpe": null,
        "plateau": null,
        "recommended": false
      }
    ]
  },
  "error": null
}

Cancel a sweep #

DELETE /v2/backtests/sweeps/{sweep_id} scope strategies

Queued versions are cancelled at once and running ones stop within seconds; the sweep is cancelled when the last one has (cancel_requested until then).

More about this

The sweep and the versions that finished stay readable. A finished sweep answers 409 backtest_finished.

Parameters

  • sweep_id path · string required

    Sweep id.

  • Idempotency-Key header · string

    Optional: a new UUID per request (1-128 printable characters). A repeat with the same key gets the first answer back instead of acting again.

Errors

HTTP Code When
401 unauthorized

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

403 forbidden

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

404 backtest_not_found

No backtest with this id for the caller (results are kept 30 days).

409 backtest_finished

The backtest has already finished (done, failed or cancelled); nothing to cancel.

429 rate_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…

DELETE
curl -X DELETE "https://api.cirrus.trade/v2/backtests/sweeps/<sweep_id>" \
  -H "Authorization: token $CIRRUS_API_KEY"
Response · 200
{
  "status": "success",
  "data": {
    "id": "01M66YJW74ZVAWVXYG8XH6E9KA",
    "strategy_id": "01M66YJSSC5QFRPRW0AF5Y9HXM",
    "version": 1,
    "status": "cancelled",
    "from": "2026-10-14",
    "to": "2026-10-16",
    "capital": 500000,
    "costs": {
      "charges": "india",
      "slippage_bps": 2,
      "spread_proxy_bps": 10
    },
    "axes": [
      {
        "path": "/spec/legs/items/0/strike/delta",
        "values": [
          0.15,
          0.2
        ]
      },
      {
        "path": "/spec/risk/combined/stop_loss/rupees",
        "values": [
          2000,
          3000
        ]
      }
    ],
    "progress": {
      "total": 4,
      "queued": 0,
      "running": 0,
      "done": 0,
      "failed": 0,
      "cancelled": 4
    },
    "cost": 1,
    "cancel_requested": true,
    "created_at": "2026-10-30T17:44:08.683Z",
    "finished_at": "2026-10-30T17:44:08.693Z",
    "expires_at": "2026-11-29T17:44:08.683Z",
    "error": null,
    "recommended": null,
    "warnings": [],
    "notes": [],
    "deflated_sharpe": null,
    "pbo": null,
    "walk_forward": null,
    "versions": [
      {
        "n": 0,
        "values": [
          0.15,
          2000
        ],
        "backtest_id": "01M66YJW76BYE5CF5QP9WSQYKM",
        "status": "cancelled",
        "error": null,
        "summary": null,
        "sharpe": null,
        "plateau": null,
        "recommended": false
      },
      {
        "n": 1,
        "values": [
          0.15,
          3000
        ],
        "backtest_id": "01M66YJW78S2SFS8FPXBTX3GWA",
        "status": "cancelled",
        "error": null,
        "summary": null,
        "sharpe": null,
        "plateau": null,
        "recommended": false
      },
      {
        "n": 2,
        "values": [
          0.2,
          2000
        ],
        "backtest_id": "01M66YJW79PPVQMH0ERFK033DJ",
        "status": "cancelled",
        "error": null,
        "summary": null,
        "sharpe": null,
        "plateau": null,
        "recommended": false
      },
      {
        "n": 3,
        "values": [
          0.2,
          3000
        ],
        "backtest_id": "01M66YJW7BQT1DP0N3MPMV006A",
        "status": "cancelled",
        "error": null,
        "summary": null,
        "sharpe": null,
        "plateau": null,
        "recommended": false
      }
    ]
  },
  "error": null
}