> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sandmark.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Run a backtest

> Run an options strategy over history and get back the equity curve, every trade, the positions
they belong to and the performance statistics.

Describe the strategy in the request body: the legs and how their strikes and expiries are chosen, how
positions are sized, when they are closed, and the cost model. See
[Strategy specification](/backtesting/strategy-spec) for every field.

**Denomination is coin.** Inverse options are quoted and settled in the base coin, so equity, premiums
and P&L are in BTC (or ETH). `equity_usd` is a derived reading alongside.

**Lookback.** `start` must fall inside your backtest lookback: 1 year on the monthly add-on, 3 years on
quarterly, full history (from September 2021) on annual. An earlier `start` returns `403`. The
`Sandmark-Backtest-Window-Start` response header gives your earliest allowed date.

**Warnings are part of the result.** Degraded fills, carried marks and bars the option chain could not
serve are counted in `warnings` and `diagnostics`. Read them before trusting a number.

> [!TIP]
>
> Start from a [recipe](https://docs.sandmark.com/backtesting/recipes.md) rather than a blank spec, and see
> [Strategy specification](https://docs.sandmark.com/backtesting/strategy-spec.md) for what every field does.


## OpenAPI

````yaml api-reference/backtest-openapi.json POST /v1/backtest
openapi: 3.1.0
info:
  title: Sandmark Backtesting API
  version: 1.0.0
  description: Options strategy backtests on Sandmark's crypto derivatives history.
  contact:
    name: Sandmark
    url: https://sandmark.com
servers:
  - url: https://api.sandmark.com/backtesting
    description: Production
security:
  - apiKey: []
tags:
  - name: Backtesting
    description: Strategy backtests.
paths:
  /v1/backtest:
    post:
      tags:
        - Backtesting
      summary: Run a backtest
      description: >-
        Run an options strategy over history and get back the equity curve,
        every trade, the positions

        they belong to and the performance statistics.


        Describe the strategy in the request body: the legs and how their
        strikes and expiries are chosen, how

        positions are sized, when they are closed, and the cost model. See

        [Strategy specification](/backtesting/strategy-spec) for every field.


        **Denomination is coin.** Inverse options are quoted and settled in the
        base coin, so equity, premiums

        and P&L are in BTC (or ETH). `equity_usd` is a derived reading
        alongside.


        **Lookback.** `start` must fall inside your backtest lookback: 1 year on
        the monthly add-on, 3 years on

        quarterly, full history (from September 2021) on annual. An earlier
        `start` returns `403`. The

        `Sandmark-Backtest-Window-Start` response header gives your earliest
        allowed date.


        **Warnings are part of the result.** Degraded fills, carried marks and
        bars the option chain could not

        serve are counted in `warnings` and `diagnostics`. Read them before
        trusting a number.
      operationId: runBacktest
      parameters:
        - name: format
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/FormatEnum'
            default: json
          description: >-
            `json` for the full result, `html` for a self-contained report,
            `csv` for a zip of trades, positions and equity, `xlsx` for an Excel
            workbook.
        - name: theme
          in: query
          required: false
          schema:
            $ref: '#/components/schemas/ThemeEnum'
            default: light
          description: Colour theme of the `html` report. Ignored for other formats.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BacktestRequest'
            example:
              name: BTC 20-delta covered call
              asset: DER.BTC_USD
              start: '2026-05-01'
              end: '2026-10-31'
              capital:
                initial_equity: 1
                sizing:
                  mode: contracts
                  value: 1
              structure:
                expiry:
                  by: monthly
                  min_dte: 20
                legs:
                  - id: call
                    type: call
                    direction: short
                    strike:
                      by: delta
                      value: 0.2
              lifecycle:
                rules:
                  - name: take_profit_75
                    when:
                      predicate:
                        subject: structure
                        metric: premium
                        op: below
                        value: -0.75
                        unit: percent
                        reference: entry
                    do:
                      action: close_all
                  - name: delta_stop
                    when:
                      predicate:
                        subject: leg:call
                        metric: contract_delta
                        op: above
                        value: 0.45
                    do:
                      action: close_all
                  - name: roll_before_expiry
                    when:
                      predicate:
                        subject: structure
                        metric: dte
                        op: below
                        value: 2
                    do:
                      action: close_all
      responses:
        '200':
          description: The backtest result, in the requested `format`.
          headers:
            Sandmark-Backtest-Window-Start:
              description: >-
                Earliest `start` your add-on allows, `YYYY-MM-DD`, or
                `2021-09-01` on full history.
              schema:
                type: string
                format: date
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BacktestResponse'
              example:
                name: BTC 20-delta covered call
                asset: DER.BTC_USD
                bars: 183
                runtime_seconds: 0.149
                strategy:
                  headline: Short call
                  legs:
                    - id: call
                      direction: Short
                      kind: call
                      strike: 20 delta
                      expiry: front monthly (last Friday) at least 20 days out
                      ratio: 1
                      sentence: >-
                        Short 20 delta call, front monthly (last Friday) at
                        least 20 days out
                  lifecycle:
                    - enter whenever flat
                    - >-
                      close the position when the structure's premium falls -75%
                      below entry
                    - >-
                      close the position when the call leg's delta rises above
                      0.45
                    - close the position when days to expiry falls below 2
                  sizing: 1 contracts, fixed
                  hedge: null
                  costs: filled at mark, 0.03% taker fee, capped at 12.5% of premium
                stats:
                  start: '2026-05-01T08:00:00+00:00'
                  end: '2026-10-30T08:00:00+00:00'
                  duration_days: 182
                  initial_equity: 0.9997
                  final_equity: 0.998437
                  total_return: -0.001263
                  cagr: -0.002531
                  volatility: 0.09877
                  sharpe: 0.024579
                  sortino: 0.026966
                  calmar: -0.028629
                  drawdown:
                    max_drawdown: -0.088411
                    avg_drawdown: -0.038878
                    longest_underwater_days: 75
                    max_drawdown_start: '2026-08-16T08:00:00+00:00'
                    max_drawdown_trough: '2026-09-23T08:00:00+00:00'
                  trades:
                    count: 14
                    winners: 11
                    losers: 3
                    win_rate: 0.785714
                    avg_pnl: -0.000112
                    avg_win: 0.009131
                    avg_loss: -0.034
                    best: 0.01155
                    worst: -0.0764
                    profit_factor: 0.984681
                    avg_holding_days: 13
                    total_fees: 0.008063
                  structures:
                    count: 14
                    win_rate: 0.785714
                    avg_pnl: -0.000112
                    best: 0.01155
                    worst: -0.0764
                    profit_factor: 0.984681
                    avg_legs: 1
                    avg_holding_days: 13
                  yearly_returns:
                    '2026': -0.001263
                equity:
                  - timestamp: '2026-05-01T08:00:00+00:00'
                    equity: 0.9997
                    equity_usd: 77088.725817
                    cash: 1.0111
                    positions_value: -0.0114
                    spot: 77111.859375
                    open_positions: 1
                  - timestamp: '2026-05-02T08:00:00+00:00'
                    equity: 0.9974
                    equity_usd: 78011.633222
                    cash: 1.0111
                    positions_value: -0.0137
                    spot: 78214.992188
                    open_positions: 1
                positions:
                  - position: BTC 20-delta covered call-00001
                    entry_time: '2026-05-01T08:00:00'
                    exit_time: '2026-05-18T08:00:00'
                    legs: 1
                    expiry: '2026-05-29T08:00:00'
                    net_premium: 0.0114
                    fees: 0.000588
                    pnl: 0.008513
                    reason: take_profit_75
                trades:
                  - instrument: BTC-29MAY26-84000-C
                    type: C
                    strike: 84000
                    expiry: '2026-05-29T08:00:00'
                    entry_time: '2026-05-01T08:00:00'
                    exit_time: '2026-05-18T08:00:00'
                    quantity: -1
                    entry_price: 0.0114
                    exit_price: 0.0023
                    fees: 0.000588
                    pnl: 0.008513
                    entry_dte: 28
                    exit_dte: 11
                    holding_days: 17
                    reason: take_profit_75
                    position: BTC 20-delta covered call-00001
                    tags:
                      - call
                    entry_delta: 0.21217
                    exit_delta: 0.08431
                    entry_iv: 0.3584
                    exit_iv: 0.3539
                warnings:
                  - >-
                    only 14 positions: ratio estimates are dominated by noise
                    below ~30
                diagnostics:
                  fills_total: 28
                  fills_degraded: 0
                  fills_degraded_pct: 0
            text/html:
              schema:
                type: string
            application/zip:
              schema:
                type: string
                format: binary
            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
              schema:
                type: string
                format: binary
        '400':
          description: The body is not valid JSON, or `start` is not a `YYYY-MM-DD` date.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                statusCode: 400
                message: start must be a date such as 2025-11-01
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                statusCode: 401
                message: >-
                  Access denied due to invalid subscription key. Make sure to
                  provide a valid key for an active subscription.
        '403':
          description: '`start` is before your backtest lookback.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                statusCode: 403
                message: >-
                  start is before your backtest lookback, which begins
                  2025-11-07
        '422':
          description: >-
            The strategy specification failed validation. `detail` names each
            field.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
        '429':
          description: >-
            Rate limit exceeded (10 backtests per minute). Retry after
            `Retry-After` seconds.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                statusCode: 429
                message: Rate limit is exceeded. Try again in 31 seconds.
components:
  schemas:
    FormatEnum:
      type: string
      enum:
        - json
        - html
        - csv
        - xlsx
      title: FormatEnum
    ThemeEnum:
      type: string
      enum:
        - light
        - dark
      title: ThemeEnum
    BacktestRequest:
      properties:
        schema_version:
          type: integer
          maximum: 1
          minimum: 1
          title: Schema Version
          description: Defaults to 1.
        name:
          type: string
          maxLength: 120
          minLength: 1
          title: Name
          default: backtest
        asset:
          $ref: '#/components/schemas/AssetEnum'
          default: DER.BTC_USD
          description: >-
            Underlying to trade. Inverse BTC and ETH carry history from
            September 2021.
        start:
          type: string
          format: date
          title: Start
          description: >-
            First day of the backtest, `YYYY-MM-DD`. Must fall inside your
            backtest lookback; data begins 2021-09-01.
        end:
          type: string
          format: date
          title: End
          description: Last day of the backtest, `YYYY-MM-DD`, inclusive.
        clock:
          $ref: '#/components/schemas/ClockSpec'
          description: When the strategy looks at the market.
        capital:
          $ref: '#/components/schemas/CapitalSpec'
          description: Starting equity, in coin, and how each new position is sized.
        structure:
          $ref: '#/components/schemas/StructureSpecModel'
          description: >-
            The legs to trade, how their expiry is chosen, and an optional
            hedge.
        lifecycle:
          $ref: '#/components/schemas/LifecycleSpec'
          description: >-
            When positions are opened and closed. With no rules, positions are
            held to expiry.
        costs:
          $ref: '#/components/schemas/CostsSpec'
          description: Fill model and fees.
      type: object
      required:
        - start
        - end
        - structure
      title: BacktestRequest
    BacktestResponse:
      properties:
        name:
          type: string
          title: Name
        asset:
          type: string
          title: Asset
        bars:
          type: integer
          title: Bars
        runtime_seconds:
          type: number
          title: Runtime Seconds
        strategy:
          anyOf:
            - $ref: '#/components/schemas/StrategyDescriptionModel'
            - type: 'null'
        stats:
          $ref: '#/components/schemas/StatsModel'
        equity:
          items:
            $ref: '#/components/schemas/EquityPointModel'
          type: array
          title: Equity
        positions:
          items:
            $ref: '#/components/schemas/PositionModel'
          type: array
          title: Positions
        trades:
          items:
            $ref: '#/components/schemas/TradeModel'
          type: array
          title: Trades
        warnings:
          items:
            type: string
          type: array
          title: Warnings
          description: >-
            Not decoration. Silent data holes are the main way an options
            backtest lies, so degraded fills, carried marks and bars the chain
            could not serve are counted here.
        diagnostics:
          additionalProperties:
            type: number
          type: object
          title: Diagnostics
      type: object
      required:
        - name
        - asset
        - bars
        - runtime_seconds
        - stats
        - equity
        - positions
        - trades
      title: BacktestResponse
    Error:
      type: object
      required:
        - statusCode
        - message
      properties:
        statusCode:
          type: integer
        message:
          type: string
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    AssetEnum:
      type: string
      enum:
        - DER.BTC_USD
        - DER.ETH_USD
        - DER.SOL_USDC
        - DER.XRP_USDC
      title: AssetEnum
      description: >-
        Markets a backtest can run on.


        The two USD lines are inverse and settle in the base coin; the USDC
        lines

        are linear and settle in USDC. Nothing else in the engine needs to know

        which is which -- `denomination.for_asset` reads it off the id and the

        contract spec, the intrinsic and the equity ledger follow from there.


        The list is bounded by option history rather than by mechanics. BTC and
        ETH

        carry marks and greeks from 2021-09; SOL from 2024-02 and XRP from
        2024-03,

        which is enough to say something. The remaining USDC lines start in
        2025-08

        and would be a year of noise, so they stay out until they are worth
        reading.
    ClockSpec:
      properties:
        decision_hour_utc:
          anyOf:
            - type: integer
              maximum: 23
              minimum: 0
            - type: 'null'
          title: Decision Hour Utc
          description: >-
            Hour the strategy looks, or null to look every hour. Defaults to
            expiry time, 08:00 UTC. Sampling one hour in twenty-four is what
            makes a multi-year run cheap: a year of BTC is 312k rows at one hour
            and 7.5M at all of them, 0.9s against 14s just to load. Null is
            therefore for short windows -- a run has 120 seconds in total.
        max_dte:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: 'null'
          title: Max Dte
          description: >-
            Prunes the load; contracts beyond this are never traded. Defaults to
            400.
        trim_to_first_trade:
          type: boolean
          title: Trim To First Trade
          description: >-
            Start the reported curve at the first bar holding a position. A
            window opened before the data supports trading otherwise reports
            months of flat equity, inflating the duration and deflating the
            CAGR.
          default: true
      type: object
      title: ClockSpec
    CapitalSpec:
      properties:
        initial_equity:
          type: number
          exclusiveMinimum: 0
          title: Initial Equity
          description: Denominated in coin. Defaults to 100.0.
        sizing:
          $ref: '#/components/schemas/SizingSpec'
      type: object
      title: CapitalSpec
    StructureSpecModel:
      properties:
        legs:
          items:
            $ref: '#/components/schemas/LegSpecModel'
          type: array
          maxItems: 8
          minItems: 1
          title: Legs
        expiry:
          $ref: '#/components/schemas/ExpirySpec'
        hedge:
          $ref: '#/components/schemas/HedgeSpec'
        shared_strike:
          anyOf:
            - type: string
            - type: 'null'
          title: Shared Strike
          description: >-
            Id of the leg whose resolved strike every other leg adopts. This is
            what separates a straddle from a strangle: two legs asking for the
            same delta independently land on opposite sides of spot. Anchoring
            also makes the other leg's delta a consequence rather than a choice,
            so a straddle anchored on a 20-delta call carries an 80-delta put
            and is directional.
      type: object
      required:
        - legs
      title: StructureSpecModel
    LifecycleSpec:
      properties:
        entry_weekdays:
          anyOf:
            - items:
                anyOf:
                  - $ref: '#/components/schemas/WeekdayEnum'
                  - type: integer
              type: array
            - type: 'null'
          title: Entry Weekdays
          description: >-
            By name, or Monday-0 number. Crypto trades continuously, so a
            calendar strategy must name its days rather than inherit a session.
        exit_weekdays:
          anyOf:
            - items:
                anyOf:
                  - $ref: '#/components/schemas/WeekdayEnum'
                  - type: integer
              type: array
            - type: 'null'
          title: Exit Weekdays
          description: >-
            By name, or Monday-0 number. Closes whatever is open on these days,
            at the run's decision hour.
        rules:
          items:
            $ref: '#/components/schemas/RuleSpec'
          type: array
          maxItems: 16
          title: Rules
          description: Evaluated in order; the first to fire owns a leg.
      type: object
      title: LifecycleSpec
    CostsSpec:
      properties:
        fill:
          $ref: '#/components/schemas/FillEnum'
          default: mark
        slippage_bps:
          type: number
          maximum: 1000
          minimum: 0
          title: Slippage Bps
          description: Defaults to 0.0.
        fallback_spread_pct:
          type: number
          maximum: 1
          minimum: 0
          title: Fallback Spread Pct
          description: Defaults to 0.05.
        taker_fee_rate:
          type: number
          maximum: 0.01
          minimum: 0
          title: Taker Fee Rate
          description: Defaults to 0.0003.
        fee_premium_cap:
          type: number
          maximum: 1
          minimum: 0
          title: Fee Premium Cap
          description: Defaults to 0.125.
      type: object
      title: CostsSpec
    StrategyDescriptionModel:
      properties:
        headline:
          type: string
          title: Headline
        legs:
          items:
            $ref: '#/components/schemas/LegDescriptionModel'
          type: array
          title: Legs
        lifecycle:
          items:
            type: string
          type: array
          title: Lifecycle
        sizing:
          type: string
          title: Sizing
          default: ''
        hedge:
          anyOf:
            - type: string
            - type: 'null'
          title: Hedge
        costs:
          type: string
          title: Costs
          default: ''
      type: object
      required:
        - headline
      title: StrategyDescriptionModel
      description: |-
        What the strategy does, derived from its configuration rather than its
        name, so it cannot describe something other than what ran.
    StatsModel:
      properties:
        start:
          type: string
          title: Start
        end:
          type: string
          title: End
        duration_days:
          type: number
          title: Duration Days
        initial_equity:
          type: number
          title: Initial Equity
        final_equity:
          type: number
          title: Final Equity
        total_return:
          type: number
          title: Total Return
        cagr:
          type: number
          title: Cagr
        volatility:
          type: number
          title: Volatility
        sharpe:
          type: number
          title: Sharpe
        sortino:
          type: number
          title: Sortino
        calmar:
          type: number
          title: Calmar
        drawdown:
          $ref: '#/components/schemas/DrawdownModel'
        trades:
          $ref: '#/components/schemas/TradeStatsModel'
        structures:
          anyOf:
            - $ref: '#/components/schemas/StructureStatsModel'
            - type: 'null'
        yearly_returns:
          additionalProperties:
            type: number
          type: object
          title: Yearly Returns
      type: object
      required:
        - start
        - end
        - duration_days
        - initial_equity
        - final_equity
        - total_return
        - cagr
        - volatility
        - sharpe
        - sortino
        - calmar
        - drawdown
        - trades
        - structures
        - yearly_returns
      title: StatsModel
    EquityPointModel:
      properties:
        timestamp:
          type: string
          title: Timestamp
        equity:
          type: number
          title: Equity
        equity_usd:
          anyOf:
            - type: number
            - type: 'null'
          title: Equity Usd
        cash:
          type: number
          title: Cash
        positions_value:
          type: number
          title: Positions Value
        spot:
          anyOf:
            - type: number
            - type: 'null'
          title: Spot
        open_positions:
          type: integer
          title: Open Positions
      type: object
      required:
        - timestamp
        - equity
        - equity_usd
        - cash
        - positions_value
        - spot
        - open_positions
      title: EquityPointModel
    PositionModel:
      properties:
        position:
          anyOf:
            - type: string
            - type: 'null'
          title: Position
        entry_time:
          type: string
          title: Entry Time
        exit_time:
          type: string
          title: Exit Time
        legs:
          type: integer
          title: Legs
        expiry:
          type: string
          title: Expiry
        net_premium:
          type: number
          title: Net Premium
        fees:
          type: number
          title: Fees
        pnl:
          type: number
          title: Pnl
        reason:
          type: string
          title: Reason
      type: object
      required:
        - position
        - entry_time
        - exit_time
        - legs
        - expiry
        - net_premium
        - fees
        - pnl
        - reason
      title: PositionModel
    TradeModel:
      properties:
        instrument:
          type: string
          title: Instrument
        type:
          type: string
          title: Type
        strike:
          anyOf:
            - type: integer
            - type: 'null'
          title: Strike
        expiry:
          type: string
          title: Expiry
        entry_time:
          type: string
          title: Entry Time
        exit_time:
          type: string
          title: Exit Time
        quantity:
          type: number
          title: Quantity
        entry_price:
          type: number
          title: Entry Price
        exit_price:
          type: number
          title: Exit Price
        fees:
          type: number
          title: Fees
        pnl:
          type: number
          title: Pnl
        entry_dte:
          anyOf:
            - type: number
            - type: 'null'
          title: Entry Dte
        exit_dte:
          anyOf:
            - type: number
            - type: 'null'
          title: Exit Dte
        holding_days:
          type: number
          title: Holding Days
        reason:
          type: string
          title: Reason
        position:
          anyOf:
            - type: string
            - type: 'null'
          title: Position
        tags:
          items:
            type: string
          type: array
          title: Tags
        entry_delta:
          anyOf:
            - type: number
            - type: 'null'
          title: Entry Delta
        exit_delta:
          anyOf:
            - type: number
            - type: 'null'
          title: Exit Delta
        entry_iv:
          anyOf:
            - type: number
            - type: 'null'
          title: Entry Iv
        exit_iv:
          anyOf:
            - type: number
            - type: 'null'
          title: Exit Iv
      type: object
      required:
        - instrument
        - type
        - strike
        - expiry
        - entry_time
        - exit_time
        - quantity
        - entry_price
        - exit_price
        - fees
        - pnl
        - entry_dte
        - exit_dte
        - holding_days
        - reason
        - position
        - tags
        - entry_delta
        - exit_delta
        - entry_iv
        - exit_iv
      title: TradeModel
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    SizingSpec:
      properties:
        mode:
          $ref: '#/components/schemas/SizingModeEnum'
          default: percent_of_equity
        value:
          type: number
          exclusiveMinimum: 0
          title: Value
          description: Defaults to 0.5.
        lot_step:
          type: number
          exclusiveMinimum: 0
          title: Lot Step
          description: Defaults to 0.1.
        min_contracts:
          type: number
          exclusiveMinimum: 0
          title: Min Contracts
          description: Defaults to 0.1.
      type: object
      title: SizingSpec
    LegSpecModel:
      properties:
        id:
          type: string
          maxLength: 40
          minLength: 1
          title: Id
          description: Your name for the leg, used by `shared_strike` and `leg:<id>` rules.
        type:
          $ref: '#/components/schemas/KindEnum'
        direction:
          $ref: '#/components/schemas/DirectionEnum'
        strike:
          anyOf:
            - $ref: '#/components/schemas/StrikeSpec'
            - type: 'null'
          description: How the strike is chosen. Omit on a futures leg, which has none.
        ratio:
          type: number
          exclusiveMinimum: 0
          title: Ratio
          description: Relative size. Sign lives in direction. Defaults to 1.0.
        expiry:
          anyOf:
            - $ref: '#/components/schemas/ExpirySpec'
            - type: 'null'
          description: Per-leg override, which is what makes calendars expressible.
      type: object
      required:
        - id
        - type
        - direction
      title: LegSpecModel
    ExpirySpec:
      properties:
        by:
          $ref: '#/components/schemas/ExpiryByEnum'
          default: monthly
        nth:
          type: integer
          maximum: 12
          minimum: 1
          title: Nth
          description: Defaults to 1.
        min_dte:
          type: number
          minimum: 0
          title: Min Dte
          description: Defaults to 0.0.
        target_dte:
          anyOf:
            - type: number
              minimum: 0
            - type: 'null'
          title: Target Dte
          description: >-
            Aim for a tenor instead of clearing a floor. `min_dte` takes the
            first expiry above a threshold, which is right on an unbroken
            ladder; where the board thins out, a floor can land in a gap and
            silently take the far side of it. A target picks whichever listed
            expiry is nearest, so the realised tenor is the best the board can
            offer rather than an artifact of where the floor fell. Set this and
            `min_dte` becomes a floor the target is chosen above.
        weekdays:
          anyOf:
            - items:
                anyOf:
                  - $ref: '#/components/schemas/WeekdayEnum'
                  - type: integer
              type: array
            - type: 'null'
          title: Weekdays
          description: >-
            Settle only on these weekdays, by name or Monday-0 number. Deribit
            lists a daily contract alongside its weeklies and monthlies, so "the
            Friday" is not something a tenor can say on its own: at a 7-day
            target a Thursday daily is exactly as close as the Friday weekly.
            This narrows the ladder before anything else looks at it, so `nth`,
            `min_dte` and `target_dte` all count off what is left. Distinct from
            `by`, which picks a contract off the ladder rather than saying which
            contracts are on it: `by: listed` with `["friday"]` is every Friday
            expiry, while `by: monthly` is the last Friday of each month. Omit
            for the whole ladder.
      type: object
      title: ExpirySpec
    HedgeSpec:
      properties:
        enabled:
          type: boolean
          title: Enabled
          default: false
        greek:
          $ref: '#/components/schemas/HedgeGreekEnum'
          description: >-
            Which exposure to hedge. Delta uses dated futures. Vega and gamma
            cannot: a future's payoff is linear in spot, so its vega and gamma
            are zero and no quantity of it changes either -- those are hedged
            with a listed at-the-money option, and the delta that option carries
            is then hedged in futures underneath, so the run measures the
            exposure asked about rather than the direction the hedge added.
          default: delta
        option_delta:
          type: number
          exclusiveMaximum: 1
          exclusiveMinimum: 0
          title: Option Delta
          description: >-
            Delta of the option used to hedge vega or gamma; 0.5 is at the
            money. Defaults to 0.5.
        option_min_dte:
          type: number
          minimum: 0
          title: Option Min Dte
          description: >-
            Floor on the hedge option's tenor, and the point it is rolled at.
            Worth setting per greek: vega lives in the back of the curve and
            gamma in the front. Defaults to 30.0.
        option_band:
          type: number
          minimum: 0
          title: Option Band
          description: >-
            Residual tolerated before rebalancing a vega or gamma hedge, as a
            fraction of the book's own greek. Relative because vega and gamma
            are not in comparable units; `band` stays absolute, for delta.
            Defaults to 0.1.
        numeraire:
          $ref: '#/components/schemas/NumeraireEnum'
          default: coin
        band:
          type: number
          minimum: 0
          title: Band
          description: Drift tolerated before rebalancing. Defaults to 0.05.
        holding_hedge_ratio:
          type: number
          maximum: 1
          minimum: 0
          title: Holding Hedge Ratio
          description: >-
            Fraction of the coin balance to hedge on top of the option book.
            Only meaningful with numeraire=usd: in coin the holding has no
            delta, because one coin is always one coin. Defaults to 0.0.
        min_dte:
          type: number
          minimum: 0
          title: Min Dte
          description: Defaults to 1.0.
        match_expiry:
          type: boolean
          title: Match Expiry
          default: true
        allow_perpetual:
          type: boolean
          title: Allow Perpetual
          description: >-
            Off by default: funding rates are absent from the warehouse, so a
            perpetual hedge carries a cost the backtest cannot see.
          default: false
      type: object
      title: HedgeSpec
    WeekdayEnum:
      type: string
      enum:
        - monday
        - tuesday
        - wednesday
        - thursday
        - friday
        - saturday
        - sunday
      title: WeekdayEnum
      description: >-
        A day, by name, for the fields that gate on the calendar.


        The numbers stay Monday 0, which is Python's own `date.weekday()` and
        what

        these fields have always accepted. Names are here because a spec is read
        far

        more often than it is written -- `["friday"]` says what `[4]` only
        encodes,

        and the panel's Copy-spec payload is something a desk reads to work out
        what

        was tested.
    RuleSpec:
      properties:
        name:
          type: string
          maxLength: 40
          minLength: 1
          title: Name
          default: rule
        when:
          $ref: '#/components/schemas/ConditionSpec'
        do:
          $ref: '#/components/schemas/ActionSpec'
      type: object
      required:
        - when
      title: RuleSpec
    FillEnum:
      type: string
      enum:
        - mark
        - bid_ask
      title: FillEnum
    LegDescriptionModel:
      properties:
        id:
          type: string
          title: Id
        direction:
          type: string
          title: Direction
        kind:
          type: string
          title: Kind
        strike:
          type: string
          title: Strike
        expiry:
          type: string
          title: Expiry
        ratio:
          type: number
          title: Ratio
        sentence:
          type: string
          title: Sentence
      type: object
      required:
        - id
        - direction
        - kind
        - strike
        - expiry
        - ratio
        - sentence
      title: LegDescriptionModel
    DrawdownModel:
      properties:
        max_drawdown:
          type: number
          title: Max Drawdown
        avg_drawdown:
          type: number
          title: Avg Drawdown
        longest_underwater_days:
          type: number
          title: Longest Underwater Days
        max_drawdown_start:
          anyOf:
            - type: string
            - type: 'null'
          title: Max Drawdown Start
        max_drawdown_trough:
          anyOf:
            - type: string
            - type: 'null'
          title: Max Drawdown Trough
      type: object
      required:
        - max_drawdown
        - avg_drawdown
        - longest_underwater_days
        - max_drawdown_start
        - max_drawdown_trough
      title: DrawdownModel
    TradeStatsModel:
      properties:
        count:
          type: integer
          title: Count
        winners:
          type: integer
          title: Winners
        losers:
          type: integer
          title: Losers
        win_rate:
          type: number
          title: Win Rate
        avg_pnl:
          type: number
          title: Avg Pnl
        avg_win:
          type: number
          title: Avg Win
        avg_loss:
          type: number
          title: Avg Loss
        best:
          type: number
          title: Best
        worst:
          type: number
          title: Worst
        profit_factor:
          type: number
          title: Profit Factor
        avg_holding_days:
          type: number
          title: Avg Holding Days
        total_fees:
          type: number
          title: Total Fees
      type: object
      required:
        - count
        - winners
        - losers
        - win_rate
        - avg_pnl
        - avg_win
        - avg_loss
        - best
        - worst
        - profit_factor
        - avg_holding_days
        - total_fees
      title: TradeStatsModel
    StructureStatsModel:
      properties:
        count:
          type: integer
          title: Count
        win_rate:
          type: number
          title: Win Rate
        avg_pnl:
          type: number
          title: Avg Pnl
        best:
          type: number
          title: Best
        worst:
          type: number
          title: Worst
        profit_factor:
          type: number
          title: Profit Factor
        avg_legs:
          type: number
          title: Avg Legs
        avg_holding_days:
          type: number
          title: Avg Holding Days
      type: object
      required:
        - count
        - win_rate
        - avg_pnl
        - best
        - worst
        - profit_factor
        - avg_legs
        - avg_holding_days
      title: StructureStatsModel
    SizingModeEnum:
      type: string
      enum:
        - contracts
        - coin_notional
        - percent_of_equity
        - percent_of_initial_equity
        - premium_budget
      title: SizingModeEnum
    KindEnum:
      type: string
      enum:
        - call
        - put
        - future
      title: KindEnum
    DirectionEnum:
      type: string
      enum:
        - long
        - short
      title: DirectionEnum
    StrikeSpec:
      properties:
        by:
          $ref: '#/components/schemas/StrikeByEnum'
          default: delta
        value:
          type: number
          title: Value
          description: Delta, multiple of the forward, or price.
      type: object
      required:
        - value
      title: StrikeSpec
    ExpiryByEnum:
      type: string
      enum:
        - monthly
        - third_friday
        - listed
      title: ExpiryByEnum
    HedgeGreekEnum:
      type: string
      enum:
        - delta
        - vega
        - gamma
      title: HedgeGreekEnum
    NumeraireEnum:
      type: string
      enum:
        - coin
        - usd
      title: NumeraireEnum
    ConditionSpec:
      properties:
        any:
          anyOf:
            - items:
                $ref: '#/components/schemas/ConditionSpec'
              type: array
            - type: 'null'
          title: Any
        all:
          anyOf:
            - items:
                $ref: '#/components/schemas/ConditionSpec'
              type: array
            - type: 'null'
          title: All
        predicate:
          anyOf:
            - $ref: '#/components/schemas/PredicateSpec'
            - type: 'null'
      type: object
      title: ConditionSpec
      description: A predicate, or a boolean combination of them.
    ActionSpec:
      properties:
        action:
          type: string
          enum:
            - close
            - close_all
          title: Action
          default: close_all
          description: >-
            `close_all` closes the whole structure; `close` closes only
            `target`.
        target:
          type: string
          title: Target
          description: '`structure` or `leg:<id>`.'
          default: structure
      type: object
      title: ActionSpec
    StrikeByEnum:
      type: string
      enum:
        - delta
        - moneyness
        - absolute
      title: StrikeByEnum
    PredicateSpec:
      properties:
        subject:
          type: string
          title: Subject
          description: '`leg:<id>`, `structure`, `underlying` or `portfolio`.'
          default: structure
        metric:
          type: string
          enum:
            - premium
            - pnl
            - pnl_pct
            - delta
            - contract_delta
            - gamma
            - vega
            - theta
            - iv
            - underlying_price
            - dte
            - days_held
            - equity
          title: Metric
          description: >-
            What to measure on `subject`. Position metrics: `premium`, `pnl`,
            `pnl_pct`, `delta`, `contract_delta`, `gamma`, `vega`, `theta`,
            `iv`, `dte`, `days_held`; market and account: `underlying_price`,
            `equity`.
        op:
          type: string
          enum:
            - above
            - below
            - crosses_above
            - crosses_below
          title: Op
          default: above
          description: >-
            `above` / `below` test the level; `crosses_above` / `crosses_below`
            fire only on the bar it is crossed.
        value:
          type: number
          title: Value
        unit:
          type: string
          enum:
            - absolute
            - percent
            - coin
            - ticks
            - days
            - vol_points
          title: Unit
          default: absolute
          description: >-
            How `value` is read. `absolute` is the metric itself; `percent` is a
            fractional change from `reference` (`-0.75` is a 75% fall); `coin`,
            `ticks`, `days` and `vol_points` are changes in those units.
        reference:
          type: string
          enum:
            - absolute
            - entry
            - prev_bar
            - high_water
            - low_water
          title: Reference
          default: absolute
          description: >-
            What a change is measured from: `absolute` (no reference), `entry`
            (when the position opened), `prev_bar`, `high_water` or `low_water`
            (best or worst since entry).
      type: object
      required:
        - metric
        - value
      title: PredicateSpec
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: Your Sandmark Backtesting API key.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.