{
  "components": {
    "headers": {
      "RateLimit-Limit": {
        "description": "The class's capacity per hour",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Policy": {
        "description": "`{limit};w=3600;class={study_create|study_poll}`",
        "schema": {
          "type": "string"
        }
      },
      "RateLimit-Remaining": {
        "description": "Whole requests left in the bucket",
        "schema": {
          "type": "integer"
        }
      },
      "RateLimit-Reset": {
        "description": "Seconds until the bucket is full again",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before the next attempt",
        "schema": {
          "type": "integer"
        }
      },
      "X-Chainwalk-Quota-Materialised-Cells-Limit": {
        "description": "The meter's capacity for this caller",
        "schema": {
          "type": "integer"
        }
      },
      "X-Chainwalk-Quota-Materialised-Cells-Remaining": {
        "description": "Budget left on the meter, after this request's reservation",
        "schema": {
          "type": "integer"
        }
      },
      "X-Chainwalk-Quota-Meter": {
        "description": "On a `quota_exhausted` 429: the meter that refused",
        "schema": {
          "type": "string"
        }
      },
      "X-Chainwalk-Quota-Report-Cells-Limit": {
        "schema": {
          "type": "integer"
        }
      },
      "X-Chainwalk-Quota-Report-Cells-Remaining": {
        "schema": {
          "type": "integer"
        }
      },
      "X-Chainwalk-Quota-Reset": {
        "description": "When the 24 h meters reset",
        "schema": {
          "format": "date-time",
          "type": "string"
        }
      },
      "X-Chainwalk-Quota-Stored-Bytes-Limit": {
        "schema": {
          "type": "integer"
        }
      },
      "X-Chainwalk-Quota-Stored-Bytes-Remaining": {
        "schema": {
          "type": "integer"
        }
      },
      "X-Request-Id": {
        "description": "The request id, also inside every error body",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Forbidden": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "`403 insufficient_scope` (the key lacks the scope) or `403 csrf_failed` (a cookie request without `X-CW-CSRF`)"
      },
      "NotFound": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "`404 not_found`. A study or run that exists but is not yours is a 404 too, never a 403"
      },
      "QuotaExhausted": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "`429 quota_exhausted` with `Retry-After` and `X-Chainwalk-Quota-Meter`: a work meter refused. The body carries `meter`, `required`, `remaining`, `limit`, `resets_at`"
      },
      "RateLimited": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "`429 rate_limited` with `Retry-After`: the request class is spent for this hour. The body carries `class`, `limit`, `window_secs`, `retry_after`"
      },
      "Unauthenticated": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "`401 unauthenticated`: no session, no key, or a key that did not verify"
      },
      "UpstreamBusy": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        },
        "description": "`503 upstream_busy` with `Retry-After`: no worker heartbeat is fresh, or the simulator has no permit"
      }
    },
    "schemas": {
      "Artifact": {
        "properties": {
          "artifact_id": {
            "format": "uuid",
            "type": "string"
          },
          "bytes": {
            "type": "integer"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "expires_at": {
            "description": "Per-path tables expire 30 days after creation; the others never",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "format": {
            "enum": [
              "parquet",
              "json"
            ],
            "type": "string"
          },
          "name": {
            "description": "The download file name, `{table}.{parquet|json}`",
            "type": "string"
          },
          "reclaimed_at": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "rows": {
            "type": "integer"
          },
          "schema_tag": {
            "const": "chainwalk.study_artifact.v1",
            "type": "string"
          },
          "sha256": {
            "description": "Lowercase hex of the file; also the download's `ETag`",
            "type": "string"
          },
          "table": {
            "enum": [
              "path_summary",
              "step_stats",
              "pnl_by_step",
              "legs_by_step",
              "fills",
              "manifest"
            ],
            "type": "string"
          },
          "url": {
            "description": "A signed path, valid for `url_ttl_secs`; absent once reclaimed. Fetch it with no credentials",
            "type": [
              "string",
              "null"
            ]
          },
          "url_expires_at": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "artifact_id",
          "name",
          "table",
          "format",
          "schema_tag",
          "rows",
          "bytes",
          "sha256",
          "created_at"
        ],
        "type": "object"
      },
      "ArtifactList": {
        "properties": {
          "artifact_bytes": {
            "type": "integer"
          },
          "artifacts": {
            "description": "Live files, each behind a fresh URL",
            "items": {
              "$ref": "#/components/schemas/Artifact"
            },
            "type": "array"
          },
          "artifacts_expire_at": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "reclaimed": {
            "description": "What existed and when it went",
            "items": {
              "$ref": "#/components/schemas/Artifact"
            },
            "type": "array"
          },
          "state": {
            "type": "string"
          },
          "study_id": {
            "format": "uuid",
            "type": "string"
          },
          "url_ttl_secs": {
            "type": "integer"
          }
        },
        "required": [
          "study_id",
          "state",
          "artifacts",
          "reclaimed",
          "artifact_bytes",
          "url_ttl_secs"
        ],
        "type": "object"
      },
      "Catalog": {
        "description": "The machine-readable copy of docs/06-catalog.md: the same table the validator enforces",
        "properties": {
          "chainwalk_version": {
            "type": "string"
          },
          "expiry_rule_kinds": {
            "items": {
              "properties": {
                "forbids": {
                  "items": {
                    "type": "string"
                  },
                  "type": "array"
                },
                "kind": {
                  "enum": [
                    "daily",
                    "weekly",
                    "monthly",
                    "yearly"
                  ],
                  "type": "string"
                },
                "month_default": {
                  "description": "For `yearly` only",
                  "type": "integer"
                },
                "optional": {
                  "items": {
                    "type": "string"
                  },
                  "type": "array"
                },
                "requires": {
                  "items": {
                    "type": "string"
                  },
                  "type": "array"
                }
              },
              "required": [
                "kind",
                "requires",
                "forbids",
                "optional"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "fee_schedules": {
            "description": "Three presets and `custom`, one of them `default`",
            "items": {
              "properties": {
                "custom": {
                  "description": "Present and true on the `custom` entry, which carries no amounts",
                  "type": "boolean"
                },
                "default": {
                  "type": "boolean"
                },
                "id": {
                  "enum": [
                    "zero",
                    "retail_flat",
                    "retail_order",
                    "custom"
                  ],
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "per_contract_cents": {
                  "type": "integer"
                },
                "per_order_cents": {
                  "type": "integer"
                },
                "settlement_cents": {
                  "type": "integer"
                }
              },
              "required": [
                "id",
                "label",
                "default"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "greeks": {
            "items": {
              "properties": {
                "decimals": {
                  "type": "integer"
                },
                "label": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "order": {
                  "enum": [
                    "first",
                    "second",
                    "third",
                    "ratio"
                  ],
                  "type": "string"
                },
                "unit": {
                  "type": "string"
                }
              },
              "required": [
                "name",
                "order",
                "label",
                "unit",
                "decimals"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "limits": {
            "properties": {
              "default_multiplier": {
                "const": 100,
                "type": "integer"
              },
              "max_chain_size": {
                "type": "integer"
              },
              "max_contract_budget": {
                "description": "The cap on `steps x strikes x expirations`",
                "type": "integer"
              },
              "max_legs": {
                "const": 8,
                "type": "integer"
              },
              "max_schedule_rules": {
                "type": "integer"
              },
              "max_steps": {
                "type": "integer"
              },
              "max_target_count": {
                "type": "integer"
              },
              "max_target_count_sum": {
                "type": "integer"
              }
            },
            "required": [
              "max_steps",
              "max_chain_size",
              "max_schedule_rules",
              "max_target_count",
              "max_target_count_sum",
              "max_contract_budget",
              "max_legs",
              "default_multiplier"
            ],
            "type": "object"
          },
          "models": {
            "items": {
              "$ref": "#/components/schemas/CatalogModel"
            },
            "type": "array"
          },
          "pricing": {
            "description": "The one basis, deployment-wide, stated so the form can echo it; never offered as a choice",
            "properties": {
              "convention": {
                "const": "liquidation",
                "type": "string"
              },
              "mid_is_display_only": {
                "const": true,
                "type": "boolean"
              },
              "quote_states": {
                "items": {
                  "$ref": "#/components/schemas/QuoteState"
                },
                "type": "array"
              },
              "spread_model": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SpreadModel"
                  }
                ],
                "properties": {
                  "bounds": {
                    "properties": {
                      "spread_proportional_max": {
                        "type": "number"
                      },
                      "spread_tick_max": {
                        "type": "number"
                      },
                      "widening_max": {
                        "type": "number"
                      }
                    },
                    "type": "object"
                  }
                }
              },
              "tick": {
                "type": "number"
              }
            },
            "required": [
              "convention",
              "mid_is_display_only",
              "tick",
              "quote_states",
              "spread_model"
            ],
            "type": "object"
          },
          "strategies": {
            "items": {
              "$ref": "#/components/schemas/CatalogStrategy"
            },
            "type": "array"
          },
          "time_frames": {
            "description": "The seven frames a scenario may name, each with the `dt` and step interval a form prefills from it",
            "items": {
              "properties": {
                "id": {
                  "enum": [
                    "Minute",
                    "Hour",
                    "Day",
                    "Week",
                    "Month",
                    "Quarter",
                    "Year"
                  ],
                  "type": "string"
                },
                "prefill_dt": {
                  "type": "number"
                },
                "prefill_step_interval_seconds": {
                  "type": "integer"
                }
              },
              "required": [
                "id",
                "prefill_dt",
                "prefill_step_interval_seconds"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "weekdays": {
            "description": "The eligible weekdays; `Sat` and `Sun` never appear",
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "chainwalk_version",
          "models",
          "time_frames",
          "expiry_rule_kinds",
          "weekdays",
          "strategies",
          "greeks",
          "pricing",
          "fee_schedules",
          "limits"
        ],
        "type": "object"
      },
      "CatalogModel": {
        "properties": {
          "id": {
            "description": "The walk type, in the simulator's PascalCase: `GeometricBrownian`, `Heston`, ...",
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "offered": {
            "description": "False for a `deferred` model, which the validator refuses by that name",
            "type": "boolean"
          },
          "params": {
            "items": {
              "properties": {
                "default": {
                  "type": "number"
                },
                "exclusive_min": {
                  "description": "Present and true when the bound is strictly positive",
                  "type": "boolean"
                },
                "is_base_volatility": {
                  "description": "The one parameter that is the scenario's base volatility; there is no top-level `volatility` field",
                  "type": "boolean"
                },
                "max": {
                  "type": "number"
                },
                "min": {
                  "type": "number"
                },
                "name": {
                  "type": "string"
                },
                "optional": {
                  "type": "boolean"
                },
                "type": {
                  "const": "f64",
                  "type": "string"
                }
              },
              "required": [
                "name",
                "type",
                "optional",
                "is_base_volatility"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "tier": {
            "enum": [
              "featured",
              "standard",
              "advanced",
              "deferred"
            ],
            "type": "string"
          },
          "upload": {
            "description": "True for `Historical`, which takes a series in `model.prices` where the others take a parameter table",
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "label",
          "tier",
          "offered",
          "upload",
          "params"
        ],
        "type": "object"
      },
      "CatalogStrategy": {
        "description": "A preset fills the legs and lets go: nothing downstream learns the name (ADR 0003)",
        "properties": {
          "featured": {
            "type": "boolean"
          },
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "leg_count": {
            "type": "integer"
          },
          "legs": {
            "items": {
              "properties": {
                "expiry_slot": {
                  "description": "Ordinal into the tape's expirations, ascending; 0 is the nearest",
                  "minimum": 0,
                  "type": "integer"
                },
                "quantity": {
                  "minimum": 1,
                  "type": "integer"
                },
                "side": {
                  "enum": [
                    "long",
                    "short"
                  ],
                  "type": "string"
                },
                "strike_steps": {
                  "description": "Ladder steps from the strike nearest the spot; zero is at the money, positive walks up",
                  "type": "integer"
                },
                "style": {
                  "enum": [
                    "call",
                    "put"
                  ],
                  "type": "string"
                }
              },
              "required": [
                "style",
                "side",
                "quantity",
                "strike_steps",
                "expiry_slot"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "needs_spot": {
            "description": "Hidden until a spot leg exists in the leg model",
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "label",
          "featured",
          "needs_spot",
          "leg_count",
          "legs"
        ],
        "type": "object"
      },
      "ContractFee": {
        "description": "A fee on a fill, one key naming its mode: fixed cents per contract, or basis points of the premium the fill traded (`|price| x quantity x multiplier`)",
        "oneOf": [
          {
            "additionalProperties": false,
            "properties": {
              "fixed_cents_per_contract": {
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "fixed_cents_per_contract"
            ]
          },
          {
            "additionalProperties": false,
            "properties": {
              "pct_of_premium_bps": {
                "description": "10 is 0.10%",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "pct_of_premium_bps"
            ]
          }
        ],
        "type": "object"
      },
      "EntryBlock": {
        "description": "What opening the position cost, in currency units except where named in cents",
        "properties": {
          "commission_cents": {
            "description": "The entry commission; a multi-leg entry is one order",
            "type": "integer"
          },
          "cost": {
            "description": "Spread plus commission: the honest headline number",
            "type": "number"
          },
          "cost_basis": {
            "description": "The signed cash of opening: negative for a net credit, so `pnl(t) = value(t) - cost_basis - fees` needs no special case",
            "type": "number"
          },
          "spread_cost": {
            "description": "The full quote width paid across every leg: exactly the mark-to-market loss at the open step before commission",
            "type": "number"
          },
          "step": {
            "description": "The step the position opened at",
            "type": "integer"
          }
        },
        "required": [
          "step",
          "cost_basis",
          "spread_cost",
          "commission_cents",
          "cost"
        ],
        "type": "object"
      },
      "EntryCost": {
        "properties": {
          "cost_basis": {
            "description": "The signed cash of opening; negative for a net credit",
            "type": "number"
          },
          "entry_commission_cents": {
            "type": "integer"
          },
          "entry_cost": {
            "description": "Spread plus commission",
            "type": "number"
          },
          "entry_spread_cost": {
            "type": "number"
          },
          "legs": {
            "items": {
              "properties": {
                "index": {
                  "type": "integer"
                },
                "open_price": {
                  "description": "The touch actually paid",
                  "type": "number"
                },
                "quote_state": {
                  "$ref": "#/components/schemas/QuoteState"
                },
                "spread": {
                  "type": "number"
                }
              },
              "required": [
                "index",
                "open_price",
                "spread",
                "quote_state"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "open_at_step": {
            "type": "integer"
          },
          "pnl": {
            "description": "At the open step: minus the spread and the commission, which is the cost of getting in",
            "type": "number"
          },
          "pricing": {
            "$ref": "#/components/schemas/PricingBlock"
          },
          "tape_id": {
            "type": "string"
          },
          "value": {
            "description": "The liquidation value at the open step",
            "type": "number"
          }
        },
        "required": [
          "tape_id",
          "open_at_step",
          "cost_basis",
          "value",
          "pnl",
          "entry_spread_cost",
          "entry_commission_cents",
          "entry_cost",
          "legs",
          "pricing"
        ],
        "type": "object"
      },
      "EntryCostRequest": {
        "additionalProperties": false,
        "properties": {
          "fee_schedule": {
            "$ref": "#/components/schemas/FeeScheduleSpec"
          },
          "legs": {
            "items": {
              "$ref": "#/components/schemas/LegSpec"
            },
            "maxItems": 8,
            "minItems": 1,
            "type": "array"
          },
          "open_at_step": {
            "default": 0,
            "minimum": 0,
            "type": "integer"
          },
          "spread_model": {
            "$ref": "#/components/schemas/SpreadModelSpec"
          }
        },
        "required": [
          "legs"
        ],
        "type": "object"
      },
      "Error": {
        "additionalProperties": true,
        "properties": {
          "code": {
            "description": "Stable, snake_case; the contract",
            "type": "string"
          },
          "field": {
            "description": "The refused field on a 400/422",
            "type": "string"
          },
          "message": {
            "description": "For people; may change",
            "type": "string"
          },
          "request_id": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ],
        "type": "object"
      },
      "ExpirationSummary": {
        "properties": {
          "expires_at": {
            "description": "Exactly as the simulator published it: the string a leg names",
            "format": "date-time",
            "type": "string"
          },
          "expires_at_step": {
            "description": "The step contracts on it settle at, or null when no step of this tape reaches it",
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "expires_at",
          "expires_at_step"
        ],
        "type": "object"
      },
      "FeeEvent": {
        "description": "Known in advance because the tape is fixed: chainwalk knows the underlying at each leg's expiry step and therefore which legs finish in the money, and the marks at every step and therefore what holding the position costs there",
        "properties": {
          "cents": {
            "minimum": 0,
            "type": "integer"
          },
          "kind": {
            "enum": [
              "entry",
              "settlement",
              "close",
              "hold"
            ],
            "type": "string"
          },
          "leg": {
            "description": "Null for the entry, the close and the hold, which belong to the position and not to a leg",
            "type": [
              "integer",
              "null"
            ]
          },
          "step": {
            "type": "integer"
          }
        },
        "required": [
          "step",
          "kind",
          "leg",
          "cents"
        ],
        "type": "object"
      },
      "FeeSchedule": {
        "description": "A fee schedule by value, as every stored or served copy spells it (ADR 0005 as amended 2026-09-02): five components, integer cents and integer basis points, a percentage resolved to cents per event rounded half away from zero. A multi-leg position is one order",
        "properties": {
          "close": {
            "$ref": "#/components/schemas/ContractFee"
          },
          "hold": {
            "$ref": "#/components/schemas/HoldFee"
          },
          "open": {
            "$ref": "#/components/schemas/ContractFee"
          },
          "order_cents": {
            "description": "Once per order, on its first fill",
            "minimum": 0,
            "type": "integer"
          },
          "settle": {
            "$ref": "#/components/schemas/SettleFee"
          }
        },
        "required": [
          "open",
          "close",
          "order_cents",
          "settle",
          "hold"
        ],
        "type": "object"
      },
      "FeeScheduleSpec": {
        "additionalProperties": false,
        "description": "A fee schedule as a request writes it: a preset `id`, or the five components (with `id: custom` or no id), never both. Absent or empty means the catalog default. A preset beside a component is `400 invalid_fee_schedule`; a custom schedule missing a component is the same code, because zero is a fee somebody may mean and is written rather than assumed. The older three amounts (`per_contract_cents`, `per_order_cents`, `settlement_cents`) are still accepted and map onto the five",
        "properties": {
          "close": {
            "$ref": "#/components/schemas/ContractFee"
          },
          "hold": {
            "$ref": "#/components/schemas/HoldFee"
          },
          "id": {
            "enum": [
              "zero",
              "retail_flat",
              "retail_order",
              "custom"
            ],
            "type": "string"
          },
          "open": {
            "$ref": "#/components/schemas/ContractFee"
          },
          "order_cents": {
            "minimum": 0,
            "type": "integer"
          },
          "per_contract_cents": {
            "deprecated": true,
            "minimum": 0,
            "type": "integer"
          },
          "per_order_cents": {
            "deprecated": true,
            "minimum": 0,
            "type": "integer"
          },
          "settle": {
            "$ref": "#/components/schemas/SettleFee"
          },
          "settlement_cents": {
            "deprecated": true,
            "minimum": 0,
            "type": "integer"
          }
        },
        "type": "object"
      },
      "FeedbackNote": {
        "properties": {
          "message": {
            "description": "The note, trimmed; the limit is `CW__FEEDBACK__MAX_CHARS`, 500 by default",
            "maxLength": 500,
            "minLength": 1,
            "type": "string"
          },
          "page": {
            "description": "The route the note was written on",
            "type": "string"
          },
          "run_slug": {
            "description": "The saved run on screen, when there is one",
            "type": "string"
          },
          "step": {
            "description": "The step the visitor was looking at",
            "minimum": 0,
            "type": "integer"
          },
          "tape_id": {
            "description": "The tape on screen, when there is one",
            "type": "string"
          }
        },
        "required": [
          "message"
        ],
        "type": "object"
      },
      "FeedbackStatus": {
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "max_chars": {
            "description": "The note limit, 0 when disabled",
            "type": "integer"
          }
        },
        "required": [
          "enabled",
          "max_chars"
        ],
        "type": "object"
      },
      "FirstStep": {
        "description": "What a position can be opened on at the first step: the contracts whose state is `quoted` there, under the same predicate the projection opens with. A provisioning fact, not a quote, so it carries no prices",
        "properties": {
          "chains": {
            "description": "One entry per expiration with at least one quoted contract; empty means nothing can be opened",
            "items": {
              "properties": {
                "calls": {
                  "description": "Strikes whose call can be filled",
                  "items": {
                    "type": "number"
                  },
                  "type": "array"
                },
                "expires_at": {
                  "format": "date-time",
                  "type": "string"
                },
                "puts": {
                  "description": "Strikes whose put can be filled",
                  "items": {
                    "type": "number"
                  },
                  "type": "array"
                }
              },
              "required": [
                "expires_at",
                "calls",
                "puts"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "step": {
            "const": 0,
            "type": "integer"
          }
        },
        "required": [
          "step",
          "chains"
        ],
        "type": "object"
      },
      "FunnelEvent": {
        "properties": {
          "kind": {
            "description": "The three marks the SPA reports; the api records the rest (tape materialised, run saved, share opened, study run, note sent) where they happen",
            "enum": [
              "landing",
              "workspace_open",
              "first_pick"
            ],
            "type": "string"
          },
          "run_slug": {
            "description": "The saved run on screen, when there is one",
            "type": "string"
          },
          "tape_id": {
            "description": "The tape on screen, when there is one",
            "type": "string"
          }
        },
        "required": [
          "kind"
        ],
        "type": "object"
      },
      "Histogram": {
        "properties": {
          "counts": {
            "description": "40 counts",
            "items": {
              "type": "integer"
            },
            "type": "array"
          },
          "edges": {
            "description": "41 edges, ascending",
            "items": {
              "type": "number"
            },
            "type": "array"
          }
        },
        "required": [
          "edges",
          "counts"
        ],
        "type": "object"
      },
      "HoldFee": {
        "description": "A fee on every step boundary crossed while a leg is alive, one key naming its mode: fixed cents per step, or basis points of the position's absolute liquidation value at that step",
        "oneOf": [
          {
            "additionalProperties": false,
            "properties": {
              "fixed_cents_per_step": {
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "fixed_cents_per_step"
            ]
          },
          {
            "additionalProperties": false,
            "properties": {
              "pct_of_position_value_bps": {
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "pct_of_position_value_bps"
            ]
          }
        ],
        "type": "object"
      },
      "KeyRecord": {
        "additionalProperties": true,
        "properties": {
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "key_id": {
            "type": "string"
          },
          "last_used_at": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "revoked_at": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "scopes": {
            "items": {
              "type": "string"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "LegSelector": {
        "additionalProperties": false,
        "description": "One leg, with the strike and the expiration chosen by selector; admission resolves both against step 0 and echoes `resolved_legs`. Exactly one strike selector and one expiration selector.",
        "properties": {
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "expiry_slot": {
            "description": "Ordinal into the expirations active at step 0, ascending",
            "minimum": 0,
            "type": "integer"
          },
          "moneyness": {
            "description": "K/S",
            "type": "number"
          },
          "multiplier": {
            "default": 100,
            "type": "integer"
          },
          "quantity": {
            "minimum": 1,
            "type": "integer"
          },
          "side": {
            "enum": [
              "long",
              "short"
            ],
            "type": "string"
          },
          "strike": {
            "description": "Absolute strike, snapped to the ladder",
            "type": "number"
          },
          "strike_offset": {
            "description": "Signed fraction of spot: -0.10 is 10% below",
            "type": "number"
          },
          "style": {
            "enum": [
              "call",
              "put"
            ],
            "type": "string"
          },
          "target_delta": {
            "description": "Signed: positive for a call, negative for a put. The rung of the step-0 ladder whose Black-Scholes delta is nearest, refused when none is within 0.10",
            "exclusiveMaximum": 1,
            "exclusiveMinimum": -1,
            "type": "number"
          }
        },
        "required": [
          "style",
          "side",
          "quantity"
        ],
        "type": "object"
      },
      "LegSpec": {
        "additionalProperties": false,
        "description": "One leg exactly as a client writes it, absolute: the SPA fills it from a materialised chain and already knows the ladder. `side` is the only carrier of position sign; a negative quantity is a refusal. At most 8 legs per position",
        "properties": {
          "expires_at": {
            "description": "One of the tape's `expirations`, spelled exactly as the tape publishes it",
            "format": "date-time",
            "type": "string"
          },
          "multiplier": {
            "default": 100,
            "minimum": 1,
            "type": "integer"
          },
          "quantity": {
            "maximum": 1000,
            "minimum": 1,
            "type": "integer"
          },
          "side": {
            "description": "Long fills at the ask and marks at the bid; short the reverse",
            "enum": [
              "long",
              "short"
            ],
            "type": "string"
          },
          "strike": {
            "description": "Must be on the tape's `strikes` ladder",
            "type": "number"
          },
          "style": {
            "description": "`underlying` is in the vocabulary and refused with `422 unsupported_model` until the spot leg lands",
            "enum": [
              "call",
              "put",
              "underlying"
            ],
            "type": "string"
          }
        },
        "required": [
          "style",
          "side",
          "quantity",
          "strike",
          "expires_at"
        ],
        "type": "object"
      },
      "Me": {
        "additionalProperties": true,
        "properties": {
          "avatar_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "display_name": {
            "type": "string"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "provider": {
            "enum": [
              "apple",
              "google",
              "github"
            ],
            "type": "string"
          },
          "user_id": {
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "user_id",
          "provider",
          "display_name",
          "created_at"
        ],
        "type": "object"
      },
      "MeterStanding": {
        "properties": {
          "limit": {
            "type": "integer"
          },
          "remaining": {
            "type": "integer"
          },
          "resets_at": {
            "description": "Null for `stored_bytes`, which only a delete frees",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "used": {
            "type": "integer"
          }
        },
        "required": [
          "limit",
          "used",
          "remaining"
        ],
        "type": "object"
      },
      "MintKeyRequest": {
        "additionalProperties": false,
        "properties": {
          "expires_days": {
            "description": "Default 365",
            "maximum": 730,
            "minimum": 1,
            "type": [
              "integer",
              "null"
            ]
          },
          "name": {
            "maxLength": 64,
            "minLength": 1,
            "type": "string"
          },
          "scopes": {
            "description": "Never `keys:*` and never account deletion: a key cannot mint keys",
            "items": {
              "enum": [
                "studies:write",
                "studies:read",
                "runs:read",
                "catalog:read"
              ],
              "type": "string"
            },
            "maxItems": 4,
            "minItems": 1,
            "type": "array"
          }
        },
        "required": [
          "name",
          "scopes"
        ],
        "type": "object"
      },
      "MintedKey": {
        "additionalProperties": true,
        "properties": {
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "key_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "plaintext": {
            "description": "`cw_live_<id>_<secret>_<crc>`; shown once, never stored",
            "type": "string"
          },
          "scopes": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "shown_once": {
            "const": true,
            "type": "boolean"
          }
        },
        "required": [
          "key_id",
          "name",
          "scopes",
          "plaintext",
          "shown_once"
        ],
        "type": "object"
      },
      "Payoff": {
        "properties": {
          "at": {
            "enum": [
              "expiry",
              "step"
            ],
            "type": "string"
          },
          "breakevens": {
            "description": "Where the curve crosses zero, interpolated",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "entry": {
            "$ref": "#/components/schemas/EntryBlock"
          },
          "max_loss": {
            "description": "Null when unbounded; a loss is negative",
            "type": [
              "number",
              "null"
            ]
          },
          "max_profit": {
            "description": "Null when unbounded",
            "type": [
              "number",
              "null"
            ]
          },
          "net_of": {
            "description": "What the curve already subtracts. There is no gross mode; the components are reported so a user can see where the money went",
            "properties": {
              "commission": {
                "const": true,
                "type": "boolean"
              },
              "settlement": {
                "description": "True for `at: expiry`, false for `at: step`",
                "type": "boolean"
              },
              "spread": {
                "const": true,
                "type": "boolean"
              }
            },
            "required": [
              "spread",
              "commission",
              "settlement"
            ],
            "type": "object"
          },
          "prices": {
            "description": "The grid, ascending",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "pricing": {
            "$ref": "#/components/schemas/PricingBlock"
          },
          "step": {
            "type": "integer"
          },
          "tape_id": {
            "type": "string"
          },
          "underlying": {
            "description": "The actual spot at the step",
            "type": "number"
          },
          "values": {
            "description": "The P&L at each grid price, same length as `prices`",
            "items": {
              "type": "number"
            },
            "type": "array"
          }
        },
        "required": [
          "tape_id",
          "step",
          "underlying",
          "at",
          "pricing",
          "entry",
          "net_of",
          "prices",
          "values",
          "breakevens",
          "max_profit",
          "max_loss"
        ],
        "type": "object"
      },
      "PayoffRequest": {
        "additionalProperties": false,
        "properties": {
          "at": {
            "default": "expiry",
            "enum": [
              "expiry",
              "step"
            ],
            "type": "string"
          },
          "fee_schedule": {
            "$ref": "#/components/schemas/FeeScheduleSpec"
          },
          "grid": {
            "additionalProperties": false,
            "description": "The evaluation grid. Absent means the strike ladder plus 12% each side, 201 points",
            "properties": {
              "max": {
                "exclusiveMinimum": 0,
                "type": "number"
              },
              "min": {
                "exclusiveMinimum": 0,
                "type": "number"
              },
              "points": {
                "maximum": 2001,
                "minimum": 1,
                "type": "integer"
              }
            },
            "required": [
              "min",
              "max",
              "points"
            ],
            "type": "object"
          },
          "legs": {
            "items": {
              "$ref": "#/components/schemas/LegSpec"
            },
            "maxItems": 8,
            "minItems": 1,
            "type": "array"
          },
          "open_at_step": {
            "default": 0,
            "description": "Fixes the entry prices the curve is measured from",
            "minimum": 0,
            "type": "integer"
          },
          "spread_model": {
            "$ref": "#/components/schemas/SpreadModelSpec"
          },
          "step": {
            "description": "The step whose curve is wanted",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "legs",
          "step"
        ],
        "type": "object"
      },
      "PricingBlock": {
        "description": "Which numbers the reader is looking at. Emitted by every response that carries a price; `fee_schedule` is present only where a position is priced, because a chain is not a position",
        "properties": {
          "convention": {
            "const": "liquidation",
            "description": "A long leg marks at the bid, a short leg at the ask. There is no other value; it is stated so a reader in a year knows nothing changed",
            "type": "string"
          },
          "fee_schedule": {
            "$ref": "#/components/schemas/FeeSchedule"
          },
          "quote_source": {
            "const": "ocs_per_contract_spread",
            "type": "string"
          },
          "spread_model": {
            "$ref": "#/components/schemas/SpreadModel"
          },
          "tick": {
            "type": "number"
          }
        },
        "required": [
          "convention",
          "quote_source",
          "tick",
          "spread_model"
        ],
        "type": "object"
      },
      "Projection": {
        "description": "The `format: json` projection: the same numbers the packed block carries, step-major for reading rather than leg-major for mapping",
        "properties": {
          "close_at_step": {
            "description": "The step the position closed at, or null when held to the horizon",
            "type": [
              "integer",
              "null"
            ]
          },
          "entry": {
            "$ref": "#/components/schemas/EntryBlock"
          },
          "fees": {
            "properties": {
              "entry_cents": {
                "type": "integer"
              },
              "events": {
                "items": {
                  "$ref": "#/components/schemas/FeeEvent"
                },
                "type": "array"
              }
            },
            "required": [
              "entry_cents",
              "events"
            ],
            "type": "object"
          },
          "from_step": {
            "type": "integer"
          },
          "greek_convention": {
            "const": "per_one_long_contract",
            "type": "string"
          },
          "greeks": {
            "enum": [
              "none",
              "first",
              "all"
            ],
            "type": "string"
          },
          "kind": {
            "const": "chainwalk.leg_projection",
            "type": "string"
          },
          "leg_count": {
            "type": "integer"
          },
          "legs": {
            "items": {
              "$ref": "#/components/schemas/ProjectionLeg"
            },
            "type": "array"
          },
          "pricing": {
            "$ref": "#/components/schemas/PricingBlock"
          },
          "row_order": {
            "const": "leg_major",
            "description": "The packed block's order; this JSON nests rows under steps",
            "type": "string"
          },
          "step_count": {
            "type": "integer"
          },
          "steps": {
            "description": "`from_step` to `to_step` inclusive",
            "items": {
              "$ref": "#/components/schemas/ProjectionStep"
            },
            "type": "array"
          },
          "symbol": {
            "type": "string"
          },
          "tape_id": {
            "type": "string"
          },
          "to_step": {
            "type": "integer"
          }
        },
        "required": [
          "kind",
          "tape_id",
          "symbol",
          "from_step",
          "to_step",
          "step_count",
          "leg_count",
          "row_order",
          "greeks",
          "greek_convention",
          "pricing",
          "entry",
          "fees",
          "legs",
          "steps"
        ],
        "type": "object"
      },
      "ProjectionLeg": {
        "properties": {
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "index": {
            "description": "The row's `leg`; the order the request wrote",
            "type": "integer"
          },
          "multiplier": {
            "type": "integer"
          },
          "open_price": {
            "description": "The touch actually paid at `open_at_step`: the ask if long, the bid if short",
            "type": "number"
          },
          "quantity": {
            "type": "integer"
          },
          "side": {
            "enum": [
              "long",
              "short"
            ],
            "type": "string"
          },
          "sign": {
            "description": "Applied exactly once, by the client, at the sum",
            "enum": [
              1,
              -1
            ],
            "type": "integer"
          },
          "spread": {
            "description": "The quote width paid on this leg's fill",
            "type": "number"
          },
          "strike": {
            "type": "number"
          },
          "style": {
            "enum": [
              "call",
              "put"
            ],
            "type": "string"
          }
        },
        "required": [
          "index",
          "style",
          "side",
          "sign",
          "quantity",
          "multiplier",
          "strike",
          "expires_at",
          "open_price",
          "spread"
        ],
        "type": "object"
      },
      "ProjectionRequest": {
        "additionalProperties": false,
        "properties": {
          "close_at_step": {
            "description": "The step the position is closed at, if before the end of the slice: every alive leg fills to close at its mark there (a long leg at the bid, a short one at the ask), the closing commission lands as one order (`fees.events` kind `close`), and the curve is flat from then on. Absent means held to the horizon. Before `open_at_step` or past `to_step` is `400 invalid_request` on `close_at_step`",
            "minimum": 0,
            "type": "integer"
          },
          "fee_schedule": {
            "$ref": "#/components/schemas/FeeScheduleSpec"
          },
          "format": {
            "default": "packed",
            "enum": [
              "packed",
              "json"
            ],
            "type": "string"
          },
          "from_step": {
            "description": "Inclusive; the start of the tape when absent",
            "minimum": 0,
            "type": "integer"
          },
          "greeks": {
            "default": "first",
            "description": "`none` is 10 packed columns, `first` 16 (delta, gamma, theta, vega, rho, rho_d), `all` 22",
            "enum": [
              "none",
              "first",
              "all"
            ],
            "type": "string"
          },
          "legs": {
            "items": {
              "$ref": "#/components/schemas/LegSpec"
            },
            "maxItems": 8,
            "minItems": 1,
            "type": "array"
          },
          "open_at_step": {
            "description": "The step the position was opened at, `from_step` when absent. Fixes `open_price` per leg and the entry fee, and therefore the whole P&L curve",
            "minimum": 0,
            "type": "integer"
          },
          "spread_model": {
            "$ref": "#/components/schemas/SpreadModelSpec"
          },
          "to_step": {
            "description": "Inclusive; the end of the tape when absent",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "legs"
        ],
        "type": "object"
      },
      "ProjectionRow": {
        "description": "One leg at one step. Prices are per one contract; greeks are per one long contract and null where not meaningful. `theta` to `rho_d` appear with `greeks: first` and above, `alpha` to `color` with `greeks: all`",
        "properties": {
          "alpha": {
            "type": [
              "number",
              "null"
            ]
          },
          "ask": {
            "description": "What buying fills at",
            "type": [
              "number",
              "null"
            ]
          },
          "bid": {
            "description": "What selling fills at",
            "type": [
              "number",
              "null"
            ]
          },
          "charm": {
            "type": [
              "number",
              "null"
            ]
          },
          "color": {
            "type": [
              "number",
              "null"
            ]
          },
          "days_to_expiration": {
            "description": "Reaches 0 at expiry and stays there",
            "type": [
              "number",
              "null"
            ]
          },
          "delta": {
            "type": [
              "number",
              "null"
            ]
          },
          "gamma": {
            "type": [
              "number",
              "null"
            ]
          },
          "implied_volatility": {
            "type": [
              "number",
              "null"
            ]
          },
          "leg": {
            "type": "integer"
          },
          "mark": {
            "description": "The bid for a long leg, the ask for a short leg, intrinsic once expired or not quoted. The only price the P&L uses",
            "type": "number"
          },
          "mid": {
            "description": "Display only. Never an input to a P&L, a fill or an aggregate",
            "type": [
              "number",
              "null"
            ]
          },
          "quote_state": {
            "$ref": "#/components/schemas/QuoteState"
          },
          "rho": {
            "type": [
              "number",
              "null"
            ]
          },
          "rho_d": {
            "type": [
              "number",
              "null"
            ]
          },
          "theta": {
            "type": [
              "number",
              "null"
            ]
          },
          "vanna": {
            "type": [
              "number",
              "null"
            ]
          },
          "vega": {
            "type": [
              "number",
              "null"
            ]
          },
          "veta": {
            "type": [
              "number",
              "null"
            ]
          },
          "vomma": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "leg",
          "bid",
          "ask",
          "mid",
          "mark",
          "quote_state",
          "implied_volatility",
          "days_to_expiration",
          "delta",
          "gamma"
        ],
        "type": "object"
      },
      "ProjectionStep": {
        "properties": {
          "atm_iv": {
            "description": "The at-the-money implied volatility at the step: the mean of the call and put quoted at the strike nearest the spot on the nearest alive expiration; null where nothing was quoted at the money",
            "type": [
              "number",
              "null"
            ]
          },
          "close_commission_cents": {
            "description": "What closing every open leg at this step's marks would cost in commission: zero before the entry and once every leg has settled",
            "type": "integer"
          },
          "fees_paid_cents": {
            "description": "Every fee event at or before this step, the holding cost included",
            "type": "integer"
          },
          "legs": {
            "description": "One row per leg, in leg order",
            "items": {
              "$ref": "#/components/schemas/ProjectionRow"
            },
            "type": "array"
          },
          "pnl": {
            "description": "`value - cost_basis - fees_paid_cents / 100`",
            "type": "number"
          },
          "quote_states": {
            "additionalProperties": {
              "type": "integer"
            },
            "description": "A census keyed by `QuoteState`, so a client can say \"3 of 4 legs quoted\"",
            "type": "object"
          },
          "step": {
            "type": "integer"
          },
          "underlying": {
            "description": "Spot at the step",
            "type": "number"
          },
          "value": {
            "description": "The position's liquidation value: `sum(mark x sign x quantity x multiplier)`",
            "type": "number"
          }
        },
        "required": [
          "step",
          "underlying",
          "atm_iv",
          "value",
          "pnl",
          "fees_paid_cents",
          "close_commission_cents",
          "quote_states",
          "legs"
        ],
        "type": "object"
      },
      "Quota": {
        "additionalProperties": true,
        "properties": {
          "identity": {
            "enum": [
              "anonymous",
              "authenticated",
              "api_key"
            ],
            "type": "string"
          },
          "key_id": {
            "description": "With a bearer key",
            "type": "string"
          },
          "materialisations": {
            "description": "The interactive quota: distinct tapes per rolling 24 h. Absent with a bearer key",
            "properties": {
              "limit": {
                "type": "integer"
              },
              "remaining": {
                "type": "integer"
              },
              "resets_at": {
                "format": "date-time",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          },
          "studies": {
            "description": "Null while anonymous",
            "oneOf": [
              {
                "$ref": "#/components/schemas/StudyMeters"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "type": "object"
      },
      "QuoteState": {
        "description": "Whether a contract could be marked at a step. The wire encoding in the packed block is the position in this list, 0 to 6. `stale` and `underlying` are reserved: no chainwalk tape produces them today. `closed` is a leg the position closed at its close step: carried at its fill price from then on, no spread, zero greeks",
        "enum": [
          "quoted",
          "not_quoted",
          "expired",
          "off_ladder",
          "stale",
          "underlying",
          "closed"
        ],
        "type": "string"
      },
      "ResolvedLeg": {
        "additionalProperties": true,
        "description": "A leg after admission: absolute `strike` and `expires_at`, as the tape spells them",
        "properties": {
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "multiplier": {
            "type": "integer"
          },
          "quantity": {
            "type": "integer"
          },
          "side": {
            "enum": [
              "long",
              "short"
            ],
            "type": "string"
          },
          "strike": {
            "type": "number"
          },
          "style": {
            "enum": [
              "call",
              "put"
            ],
            "type": "string"
          }
        },
        "required": [
          "style",
          "side",
          "quantity",
          "strike",
          "expires_at",
          "multiplier"
        ],
        "type": "object"
      },
      "Run": {
        "description": "A saved position. The pricing basis is stored with it, which is why a shared link shows the same numbers to whoever opens it",
        "properties": {
          "closed_at_step": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "entry": {
            "allOf": [
              {
                "$ref": "#/components/schemas/EntryBlock"
              }
            ],
            "description": "Present on the `201` of a save only; a read re-derives it by projecting"
          },
          "legs": {
            "description": "As saved, with `multiplier` present",
            "items": {
              "$ref": "#/components/schemas/LegSpec"
            },
            "type": "array"
          },
          "opened_at_step": {
            "type": "integer"
          },
          "owner": {
            "properties": {
              "display_name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "display_name"
            ],
            "type": "object"
          },
          "pricing": {
            "description": "The stored basis: `convention`, the spread model by value and the fee schedule by value",
            "properties": {
              "convention": {
                "const": "liquidation",
                "type": "string"
              },
              "fee_schedule": {
                "$ref": "#/components/schemas/FeeSchedule"
              },
              "spread_model": {
                "$ref": "#/components/schemas/SpreadModel"
              }
            },
            "required": [
              "convention",
              "spread_model",
              "fee_schedule"
            ],
            "type": "object"
          },
          "slug": {
            "description": "Unguessable; what appears in URLs",
            "type": "string"
          },
          "tape": {
            "description": "What a client can do next. `ready` carries `steps`; `evicted` means re-POST the scenario and the same `tape_id` comes back",
            "properties": {
              "state": {
                "enum": [
                  "ready",
                  "evicted"
                ],
                "type": "string"
              },
              "steps": {
                "type": "integer"
              },
              "tape_id": {
                "type": "string"
              }
            },
            "required": [
              "tape_id",
              "state"
            ],
            "type": "object"
          },
          "title": {
            "type": "string"
          },
          "url": {
            "description": "`{public_base_url}/r/{slug}`",
            "format": "uri",
            "type": "string"
          },
          "visibility": {
            "enum": [
              "private",
              "unlisted"
            ],
            "type": "string"
          }
        },
        "required": [
          "slug",
          "url",
          "title",
          "legs",
          "opened_at_step",
          "pricing",
          "visibility",
          "created_at",
          "owner",
          "tape"
        ],
        "type": "object"
      },
      "RunCreate": {
        "additionalProperties": false,
        "properties": {
          "closed_at_step": {
            "description": "The step the position was closed at, when it was closed before the horizon; never before `opened_at_step`, never past the tape",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "fee_schedule": {
            "$ref": "#/components/schemas/FeeScheduleSpec"
          },
          "legs": {
            "items": {
              "$ref": "#/components/schemas/LegSpec"
            },
            "maxItems": 8,
            "minItems": 1,
            "type": "array"
          },
          "opened_at_step": {
            "default": 0,
            "minimum": 0,
            "type": "integer"
          },
          "spread_model": {
            "$ref": "#/components/schemas/SpreadModelSpec"
          },
          "tape_id": {
            "type": "string"
          },
          "title": {
            "default": "",
            "description": "A label, not a classification: nothing reads it back as a strategy identity",
            "maxLength": 120,
            "type": "string"
          },
          "visibility": {
            "default": "private",
            "enum": [
              "private",
              "unlisted"
            ],
            "type": "string"
          }
        },
        "required": [
          "tape_id",
          "legs"
        ],
        "type": "object"
      },
      "RunList": {
        "properties": {
          "next_cursor": {
            "description": "Opaque; pass back as `cursor`. Null on the last page",
            "type": [
              "string",
              "null"
            ]
          },
          "runs": {
            "items": {
              "$ref": "#/components/schemas/Run"
            },
            "type": "array"
          }
        },
        "required": [
          "runs",
          "next_cursor"
        ],
        "type": "object"
      },
      "RunPatch": {
        "additionalProperties": false,
        "description": "`title` and `visibility` only; everything else about a run is immutable",
        "properties": {
          "title": {
            "maxLength": 120,
            "type": "string"
          },
          "visibility": {
            "enum": [
              "private",
              "unlisted"
            ],
            "type": "string"
          }
        },
        "type": "object"
      },
      "ScenarioRequest": {
        "additionalProperties": false,
        "description": "A scenario as a client writes it. Every field is optional and an absent one takes the reference scenario's value (SPX, 500 daily steps from 2026-01-05T14:30:00Z, GeometricBrownian at 18% volatility, three schedules, seed 42). Unknown fields are refused, including a top-level `volatility`",
        "properties": {
          "chain_size": {
            "description": "Per-side half width: `n` yields `2n + 1` strikes",
            "minimum": 1,
            "type": "integer"
          },
          "dividend_yield": {
            "maximum": 1,
            "minimum": 0,
            "type": "number"
          },
          "expiration_time": {
            "description": "`HH:MM` or `HH:MM:SS`, local to `timezone`",
            "pattern": "^\\d\\d:\\d\\d(:\\d\\d)?$",
            "type": "string"
          },
          "initial_price": {
            "exclusiveMinimum": 0,
            "type": "number"
          },
          "model": {
            "$ref": "#/components/schemas/WalkModelSpec"
          },
          "risk_free_rate": {
            "maximum": 1,
            "minimum": -1,
            "type": "number"
          },
          "schedules": {
            "items": {
              "$ref": "#/components/schemas/ScheduleRule"
            },
            "minItems": 1,
            "type": "array"
          },
          "seed": {
            "description": "Default 42. A tape without a fixed seed is not addressable",
            "minimum": 0,
            "type": "integer"
          },
          "skew_slope": {
            "type": "number"
          },
          "smile_curve": {
            "type": "number"
          },
          "spread_model": {
            "$ref": "#/components/schemas/SpreadModelSpec"
          },
          "start_at": {
            "description": "RFC 3339. The default is fixed, so a tape is reproducible even when it is omitted",
            "format": "date-time",
            "type": "string"
          },
          "step_interval_seconds": {
            "minimum": 1,
            "type": "integer"
          },
          "steps": {
            "description": "Capped by the catalog's `limits.max_steps` and by the contract budget",
            "minimum": 1,
            "type": "integer"
          },
          "strike_interval": {
            "exclusiveMinimum": 0,
            "type": "number"
          },
          "symbol": {
            "maxLength": 32,
            "minLength": 1,
            "pattern": "^[A-Za-z0-9._-]+$",
            "type": "string"
          },
          "time_frame": {
            "enum": [
              "Minute",
              "Hour",
              "Day",
              "Week",
              "Month",
              "Quarter",
              "Year"
            ],
            "type": "string"
          },
          "timezone": {
            "description": "IANA name",
            "type": "string"
          }
        },
        "type": "object"
      },
      "ScheduleRule": {
        "additionalProperties": false,
        "description": "One expiration rule. Which of `weekdays`, `weekday` and `month` a kind requires, forbids or admits is the catalog's `expiry_rule_kinds` matrix",
        "properties": {
          "kind": {
            "enum": [
              "daily",
              "weekly",
              "monthly",
              "yearly"
            ],
            "type": "string"
          },
          "month": {
            "description": "Yearly only, default 12",
            "maximum": 12,
            "minimum": 1,
            "type": "integer"
          },
          "rule_id": {
            "description": "Unique within the scenario",
            "type": "string"
          },
          "target_count": {
            "description": "How many expirations this rule provisions",
            "minimum": 1,
            "type": "integer"
          },
          "weekday": {
            "description": "Monthly and yearly",
            "enum": [
              "Mon",
              "Tue",
              "Wed",
              "Thu",
              "Fri"
            ],
            "type": "string"
          },
          "weekdays": {
            "description": "Weekly only",
            "items": {
              "enum": [
                "Mon",
                "Tue",
                "Wed",
                "Thu",
                "Fri"
              ],
              "type": "string"
            },
            "type": "array"
          }
        },
        "required": [
          "rule_id",
          "kind",
          "target_count"
        ],
        "type": "object"
      },
      "SettleFee": {
        "description": "A fee on a leg settling in the money, one key naming its mode: flat per settlement event, or per contract in the money. An out-of-the-money leg expires free either way",
        "oneOf": [
          {
            "additionalProperties": false,
            "properties": {
              "flat_cents": {
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "flat_cents"
            ]
          },
          {
            "additionalProperties": false,
            "properties": {
              "cents_per_contract": {
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "cents_per_contract"
            ]
          }
        ],
        "type": "object"
      },
      "Snapshot": {
        "properties": {
          "chains": {
            "description": "One per expiration rendered, in ladder order",
            "items": {
              "$ref": "#/components/schemas/SnapshotChain"
            },
            "type": "array"
          },
          "pricing": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PricingBlock"
              }
            ],
            "description": "Without `fee_schedule`: a chain is not a position"
          },
          "step": {
            "type": "integer"
          },
          "tape_id": {
            "type": "string"
          },
          "underlying": {
            "properties": {
              "price": {
                "type": "number"
              },
              "symbol": {
                "type": "string"
              }
            },
            "required": [
              "symbol",
              "price"
            ],
            "type": "object"
          }
        },
        "required": [
          "tape_id",
          "step",
          "underlying",
          "pricing",
          "chains"
        ],
        "type": "object"
      },
      "SnapshotChain": {
        "properties": {
          "contracts": {
            "description": "One per strike rendered, ascending",
            "items": {
              "$ref": "#/components/schemas/SnapshotContract"
            },
            "type": "array"
          },
          "days_to_expiration": {
            "description": "Null when no contract of the chain publishes one",
            "type": [
              "number",
              "null"
            ]
          },
          "expires_at": {
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "expires_at",
          "days_to_expiration",
          "contracts"
        ],
        "type": "object"
      },
      "SnapshotContract": {
        "description": "Implied volatility and gamma are published per strike upstream and land on both sides, so they sit at the contract level",
        "properties": {
          "call": {
            "$ref": "#/components/schemas/SnapshotSide"
          },
          "gamma": {
            "description": "Null with `greeks=none`, and whenever not published",
            "type": [
              "number",
              "null"
            ]
          },
          "implied_volatility": {
            "type": [
              "number",
              "null"
            ]
          },
          "put": {
            "$ref": "#/components/schemas/SnapshotSide"
          },
          "strike": {
            "type": "number"
          }
        },
        "required": [
          "strike",
          "implied_volatility",
          "gamma",
          "call",
          "put"
        ],
        "type": "object"
      },
      "SnapshotSide": {
        "description": "One side of one contract, exactly as the tape stores it. Every greek is per one long contract, and a null greek means not published, never zero. The further ten appear only with `greeks=all`",
        "properties": {
          "alpha": {
            "type": [
              "number",
              "null"
            ]
          },
          "ask": {
            "type": "number"
          },
          "bid": {
            "type": "number"
          },
          "charm": {
            "type": [
              "number",
              "null"
            ]
          },
          "color": {
            "type": [
              "number",
              "null"
            ]
          },
          "delta": {
            "description": "Null with `greeks=none`",
            "type": [
              "number",
              "null"
            ]
          },
          "mid": {
            "description": "Display only; never an input to a P&L",
            "type": [
              "number",
              "null"
            ]
          },
          "quote_state": {
            "$ref": "#/components/schemas/QuoteState"
          },
          "rho": {
            "type": [
              "number",
              "null"
            ]
          },
          "rho_d": {
            "type": [
              "number",
              "null"
            ]
          },
          "theta": {
            "type": [
              "number",
              "null"
            ]
          },
          "vanna": {
            "type": [
              "number",
              "null"
            ]
          },
          "vega": {
            "type": [
              "number",
              "null"
            ]
          },
          "veta": {
            "type": [
              "number",
              "null"
            ]
          },
          "vomma": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "required": [
          "bid",
          "ask",
          "mid",
          "quote_state",
          "delta"
        ],
        "type": "object"
      },
      "SpreadModel": {
        "description": "The spread model a tape was quoted under, by value: the simulator's own per-contract model, whose coefficients are create parameters and therefore inside `tape_id`. Echoed so a reader can argue with the assumption; never settable per request",
        "properties": {
          "kind": {
            "const": "ocs_per_contract",
            "type": "string"
          },
          "spread": {
            "type": "number"
          },
          "spread_moneyness_widening": {
            "type": "number"
          },
          "spread_proportional": {
            "type": "number"
          },
          "spread_tenor_widening": {
            "type": "number"
          },
          "spread_tick": {
            "type": "number"
          }
        },
        "required": [
          "kind",
          "spread",
          "spread_proportional",
          "spread_moneyness_widening",
          "spread_tenor_widening",
          "spread_tick"
        ],
        "type": "object"
      },
      "SpreadModelSpec": {
        "additionalProperties": false,
        "description": "A spread model as a request names it: the kind and nothing else, and there is exactly one kind. `chainwalk_v1` and `ocs_native` are `422 spread_model_retired`; anything else is `400 unknown_spread_model`",
        "properties": {
          "kind": {
            "const": "ocs_per_contract",
            "type": "string"
          }
        },
        "required": [
          "kind"
        ],
        "type": "object"
      },
      "StepTable": {
        "additionalProperties": true,
        "description": "Tier 2: one row per step over every path used, columnar, independent of the path count",
        "properties": {
          "max": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "mean": {
            "description": "Mean P&L",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "min": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "n_paths_used": {
            "description": "Paths alive at this step",
            "items": {
              "type": "integer"
            },
            "type": "array"
          },
          "p05": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "p25": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "p50": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "p75": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "p95": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "share_in_loss": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "share_in_profit": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "standard_error_of_mean": {
            "description": "std_dev / sqrt(n)",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "std_dev": {
            "description": "Population standard deviation",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "step": {
            "description": "The step index",
            "items": {
              "type": "integer"
            },
            "type": "array"
          },
          "underlying_mean": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "underlying_p05": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "underlying_p50": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "underlying_p95": {
            "description": "",
            "items": {
              "type": "number"
            },
            "type": "array"
          }
        },
        "type": "object"
      },
      "Study": {
        "additionalProperties": true,
        "description": "The job resource. `report` and the tier 2 table land together with a terminal state; `Retry-After` is set while it is not",
        "properties": {
          "artifact_bytes": {
            "description": "Live tier 3 bytes",
            "type": "integer"
          },
          "artifacts_expire_at": {
            "description": "When the next per-path file is reclaimed",
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "base_seed": {
            "type": "integer"
          },
          "completed_at": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "errors": {
            "items": {
              "$ref": "#/components/schemas/StudyError"
            },
            "type": "array"
          },
          "paths": {
            "type": "integer"
          },
          "progress": {
            "additionalProperties": true,
            "description": "`{done, failed, total}` while running",
            "type": "object"
          },
          "report": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/StudyReport"
              },
              {
                "type": "null"
              }
            ]
          },
          "resolved_legs": {
            "items": {
              "$ref": "#/components/schemas/ResolvedLeg"
            },
            "type": "array"
          },
          "spec": {
            "$ref": "#/components/schemas/StudySpec"
          },
          "started_at": {
            "format": "date-time",
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "enum": [
              "queued",
              "running",
              "cancelling",
              "succeeded",
              "partial",
              "failed",
              "cancelled"
            ],
            "type": "string"
          },
          "study_hash": {
            "type": "string"
          },
          "study_id": {
            "format": "uuid",
            "type": "string"
          },
          "usage": {
            "$ref": "#/components/schemas/StudyUsage"
          }
        },
        "required": [
          "study_id",
          "state",
          "paths",
          "base_seed",
          "study_hash",
          "spec",
          "resolved_legs",
          "progress",
          "usage",
          "errors",
          "created_at"
        ],
        "type": "object"
      },
      "StudyAccepted": {
        "properties": {
          "cost": {
            "$ref": "#/components/schemas/StudyCost"
          },
          "resolved_legs": {
            "items": {
              "$ref": "#/components/schemas/ResolvedLeg"
            },
            "type": "array"
          },
          "state": {
            "const": "queued",
            "type": "string"
          },
          "study_hash": {
            "description": "sha256 of the canonical spec; two identical submissions share it",
            "type": "string"
          },
          "study_id": {
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "study_id",
          "state",
          "study_hash",
          "resolved_legs",
          "cost"
        ],
        "type": "object"
      },
      "StudyCost": {
        "properties": {
          "cells_per_path": {
            "description": "steps x (2 x chain_size + 1) x expirations alive",
            "type": "integer"
          },
          "estimated_bytes": {
            "type": "integer"
          },
          "materialised_cells": {
            "description": "cells_per_path x paths, the worst case reserved",
            "type": "integer"
          },
          "report_cells": {
            "description": "steps x legs x paths",
            "type": "integer"
          }
        },
        "required": [
          "cells_per_path",
          "materialised_cells",
          "report_cells",
          "estimated_bytes"
        ],
        "type": "object"
      },
      "StudyDryRun": {
        "properties": {
          "cost": {
            "$ref": "#/components/schemas/StudyCost"
          },
          "dry_run": {
            "const": true,
            "type": "boolean"
          },
          "fits": {
            "description": "Whether the meters would admit it now",
            "type": "boolean"
          },
          "meters": {
            "$ref": "#/components/schemas/StudyMeters"
          },
          "refusal": {
            "description": "The 429 body creation would answer, when `fits` is false",
            "oneOf": [
              {
                "$ref": "#/components/schemas/Error"
              },
              {
                "type": "null"
              }
            ]
          },
          "resolved_legs": {
            "items": {
              "$ref": "#/components/schemas/ResolvedLeg"
            },
            "type": "array"
          },
          "study_hash": {
            "type": "string"
          }
        },
        "required": [
          "dry_run",
          "study_hash",
          "resolved_legs",
          "cost",
          "fits",
          "meters"
        ],
        "type": "object"
      },
      "StudyError": {
        "additionalProperties": true,
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "path_index": {
            "type": "integer"
          },
          "seed": {
            "type": "integer"
          },
          "tape_id": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "type": "object"
      },
      "StudyList": {
        "properties": {
          "next_cursor": {
            "description": "Opaque; pass back as `cursor`",
            "type": [
              "string",
              "null"
            ]
          },
          "studies": {
            "items": {
              "$ref": "#/components/schemas/Study"
            },
            "type": "array"
          }
        },
        "required": [
          "studies"
        ],
        "type": "object"
      },
      "StudyMeters": {
        "description": "The three work meters of docs/10-study-api.md §7, per account: two over a rolling 24 h, one live",
        "properties": {
          "materialised_cells": {
            "$ref": "#/components/schemas/MeterStanding"
          },
          "report_cells": {
            "$ref": "#/components/schemas/MeterStanding"
          },
          "stored_bytes": {
            "$ref": "#/components/schemas/MeterStanding"
          }
        },
        "required": [
          "materialised_cells",
          "report_cells",
          "stored_bytes"
        ],
        "type": "object"
      },
      "StudyReport": {
        "description": "Tier 1, inline once terminal",
        "properties": {
          "assumptions": {
            "additionalProperties": true,
            "description": "Fees, spread model, entry, exit, resolved legs, the seed formula, the fill convention",
            "type": "object"
          },
          "n_paths": {
            "type": "integer"
          },
          "n_paths_used": {
            "type": "integer"
          },
          "report_schema": {
            "const": "chainwalk.study_report.v1",
            "type": "string"
          },
          "terminal_pnl": {
            "$ref": "#/components/schemas/TerminalDistribution"
          },
          "terminal_pnl_per_path": {
            "items": {
              "type": "number"
            },
            "type": "array"
          }
        },
        "required": [
          "report_schema",
          "n_paths",
          "n_paths_used",
          "terminal_pnl",
          "terminal_pnl_per_path",
          "assumptions"
        ],
        "type": "object"
      },
      "StudySpec": {
        "additionalProperties": false,
        "description": "The submission. `scenario` is exactly what `POST /api/v1/scenarios` takes, minus `seed`: a caller seed is a 400, because every path seed derives from `base_seed` (`sha256(tag || base_seed || path_index)[0..8]`, stated in the report).",
        "properties": {
          "base_seed": {
            "maximum": 18446744073709551615,
            "minimum": 0,
            "type": "integer"
          },
          "entry": {
            "additionalProperties": false,
            "properties": {
              "step": {
                "default": 0,
                "minimum": 0,
                "type": "integer"
              }
            },
            "type": "object"
          },
          "exit": {
            "additionalProperties": false,
            "properties": {
              "close_at_step": {
                "minimum": 1,
                "type": "integer"
              },
              "policy": {
                "default": "hold_to_horizon",
                "enum": [
                  "hold_to_horizon"
                ],
                "type": "string"
              }
            },
            "type": "object"
          },
          "fees": {
            "additionalProperties": true,
            "description": "Required, never defaulted: `{id: <a catalog fee schedule>}` such as `{id: \"retail_flat\"}`, or an explicit schedule. Part of the study identity",
            "type": "object"
          },
          "greeks": {
            "default": "none",
            "enum": [
              "none",
              "first",
              "all"
            ],
            "type": "string"
          },
          "include": {
            "description": "Tier 3 tables to write. Default `[path_summary, step_stats, pnl_by_step]`; `legs_by_step` and `fills` are large and opt-in; `attribution_by_step` is reserved and refused with `422 invalid_study`",
            "items": {
              "enum": [
                "path_summary",
                "step_stats",
                "pnl_by_step",
                "legs_by_step",
                "fills",
                "attribution_by_step"
              ],
              "type": "string"
            },
            "type": "array"
          },
          "legs": {
            "items": {
              "$ref": "#/components/schemas/LegSelector"
            },
            "maxItems": 8,
            "minItems": 1,
            "type": "array"
          },
          "max_failed_fraction": {
            "default": 0.05,
            "description": "Past this share of failed paths the study is `failed`, below it `partial`",
            "maximum": 1,
            "minimum": 0,
            "type": "number"
          },
          "on_path_error": {
            "default": "continue",
            "enum": [
              "continue",
              "abort"
            ],
            "type": "string"
          },
          "paths": {
            "maximum": 2000,
            "minimum": 1,
            "type": "integer"
          },
          "scenario": {
            "additionalProperties": true,
            "description": "A `ScenarioRequest` without `seed`",
            "type": "object"
          },
          "spread_model": {
            "additionalProperties": true,
            "description": "Required, and must be the deployment's: `{kind: \"ocs_per_contract\"}`; anything else is `422 spread_model_immutable`",
            "type": "object"
          }
        },
        "required": [
          "paths",
          "base_seed",
          "scenario",
          "legs",
          "fees",
          "spread_model"
        ],
        "type": "object"
      },
      "StudyUsage": {
        "additionalProperties": true,
        "description": "The reservation until the worker settles actuals (`reserved: true`), then what the study cost",
        "properties": {
          "artifact_bytes": {
            "type": "integer"
          },
          "cache_hit_cells": {
            "type": "integer"
          },
          "cache_hit_tapes": {
            "type": "integer"
          },
          "cells_per_path": {
            "type": "integer"
          },
          "materialised_cells": {
            "type": "integer"
          },
          "materialised_tapes": {
            "type": "integer"
          },
          "paths_failed": {
            "type": "integer"
          },
          "paths_succeeded": {
            "type": "integer"
          },
          "report_cells": {
            "type": "integer"
          },
          "report_cells_per_path": {
            "type": "integer"
          },
          "reserved": {
            "type": "boolean"
          },
          "stored_bytes": {
            "description": "tape_bytes + artifact_bytes",
            "type": "integer"
          },
          "tape_bytes": {
            "type": "integer"
          }
        },
        "type": "object"
      },
      "TapeSummary": {
        "description": "A tape is market data and nothing else: no spread model and no fee schedule here, because both are properties of a position or an account",
        "properties": {
          "bytes": {
            "description": "The export the tape was built from",
            "type": "integer"
          },
          "created_at": {
            "format": "date-time",
            "type": "string"
          },
          "dividend_yield": {
            "type": "number"
          },
          "effective_start": {
            "format": "date-time",
            "type": "string"
          },
          "expirations": {
            "description": "In the order a leg's expiration indexes into",
            "items": {
              "$ref": "#/components/schemas/ExpirationSummary"
            },
            "type": "array"
          },
          "first_step": {
            "$ref": "#/components/schemas/FirstStep"
          },
          "risk_free_rate": {
            "description": "The market the payoff evaluates under, echoed so no client has to guess it",
            "type": "number"
          },
          "seed": {
            "type": "integer"
          },
          "state": {
            "const": "ready",
            "description": "The store holds whole tapes only, so there is no `materialising` a client can observe",
            "type": "string"
          },
          "step_interval_seconds": {
            "description": "Step `k` is at `effective_start + k x step_interval_seconds`",
            "type": "integer"
          },
          "steps": {
            "type": "integer"
          },
          "strikes": {
            "description": "The ladder, ascending: the union over every step. A leg naming anything else is `422 strike_off_ladder`; being on it does not mean a contract is fillable at any given step, which is what `first_step` is for",
            "items": {
              "type": "number"
            },
            "type": "array"
          },
          "symbol": {
            "type": "string"
          },
          "tape_id": {
            "description": "`blake3(\"cw-tape-v1\" || 0x00 || ocs_version || 0x00 || canonical_params)` (ADR 0002)",
            "pattern": "^[0-9a-f]{64}$",
            "type": "string"
          }
        },
        "required": [
          "tape_id",
          "state",
          "symbol",
          "steps",
          "seed",
          "effective_start",
          "step_interval_seconds",
          "created_at",
          "bytes",
          "expirations",
          "strikes",
          "first_step",
          "risk_free_rate",
          "dividend_yield"
        ],
        "type": "object"
      },
      "TerminalDistribution": {
        "additionalProperties": true,
        "description": "The pooled terminal P&L across the paths used",
        "properties": {
          "excess_kurtosis": {
            "type": "number"
          },
          "expected_loss_given_loss": {
            "type": [
              "number",
              "null"
            ]
          },
          "expected_profit_given_profit": {
            "type": [
              "number",
              "null"
            ]
          },
          "expected_shortfall_95": {
            "type": "number"
          },
          "expected_shortfall_99": {
            "type": "number"
          },
          "histogram": {
            "$ref": "#/components/schemas/Histogram"
          },
          "iqr": {
            "type": "number"
          },
          "max": {
            "type": "number"
          },
          "mean": {
            "type": "number"
          },
          "min": {
            "type": "number"
          },
          "n": {
            "type": "integer"
          },
          "n_tail_95": {
            "type": "integer"
          },
          "n_tail_99": {
            "type": "integer"
          },
          "percentiles": {
            "description": "`[[\"p05\", value], ...]`, numpy method 7",
            "items": {
              "prefixItems": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ],
              "type": "array"
            },
            "type": "array"
          },
          "prob_breakeven_band": {
            "type": "number"
          },
          "prob_loss": {
            "type": "number"
          },
          "prob_profit": {
            "type": "number"
          },
          "skewness": {
            "type": "number"
          },
          "standard_error_of_mean": {
            "type": "number"
          },
          "std_dev": {
            "description": "Sample (n-1)",
            "type": "number"
          },
          "var_95": {
            "description": "A positive number is a loss",
            "type": "number"
          },
          "var_99": {
            "type": "number"
          }
        },
        "type": "object"
      },
      "WalkModelSpec": {
        "additionalProperties": false,
        "description": "The stochastic model, internally tagged as chainwalk writes it rather than externally tagged as the simulator does. `Historical` takes `prices` instead of `params`",
        "properties": {
          "id": {
            "description": "A catalog model id",
            "type": "string"
          },
          "params": {
            "additionalProperties": {
              "type": "number"
            },
            "description": "The model's parameters by name; the catalog states each one's band. Empty for `Historical`",
            "type": "object"
          },
          "prices": {
            "description": "`Historical` only: the series to replay, one price per period of `time_frame`, at least `steps + 1` of them, finite and strictly positive, the first three not all equal, realising an annualised volatility of at most 1.0; refused with `422 invalid_scenario` on `model.prices` otherwise. The first price is the scenario's initial price and the realised volatility prices the first chain",
            "items": {
              "exclusiveMinimum": 0,
              "type": "number"
            },
            "maxItems": 100000,
            "minItems": 2,
            "type": "array"
          }
        },
        "required": [
          "id"
        ],
        "type": "object"
      }
    },
    "securitySchemes": {
      "bearer": {
        "bearerFormat": "cw_live_<key_id>_<secret>_<crc>",
        "description": "An API key from `POST /api/v1/keys`. Scoped; never accepted as a query parameter.",
        "scheme": "bearer",
        "type": "http"
      },
      "session": {
        "description": "The browser's session. Unsafe methods also need `X-CW-CSRF` matching the `cw_csrf` cookie.",
        "in": "cookie",
        "name": "cw_session",
        "type": "apiKey"
      }
    }
  },
  "info": {
    "contact": {
      "name": "CapitalDelta"
    },
    "description": "Two audiences, one surface. A browser arrives with a session cookie on the same origin and sends `X-CW-CSRF` on every POST, PATCH and DELETE. A machine arrives with `Authorization: Bearer cw_live_...` and sends no CSRF header. Every refusal is a JSON body `{code, message, request_id}`; the `code` is the contract, the `message` is for people.",
    "summary": "Options positions replayed over simulated option chains, one tape at a time or as a study of many.",
    "title": "chainwalk",
    "version": "0.7.22"
  },
  "openapi": "3.1.0",
  "paths": {
    "/api/v1/artifacts/{artifact_id}": {
      "get": {
        "description": "Self-authenticating: `exp` and `sig` come from the listing and nothing else is checked. Supports `Range` (206, `Accept-Ranges: bytes`) and `If-None-Match` against the `ETag` (the sha256). `Content-Disposition: attachment`.",
        "operationId": "downloadArtifact",
        "parameters": [
          {
            "in": "path",
            "name": "artifact_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "description": "Unix seconds the signature expires at",
            "in": "query",
            "name": "exp",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Hex, keyed over `{artifact_id}|{exp}`",
            "in": "query",
            "name": "sig",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Range",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "If-None-Match",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "description": "`manifest.json`",
                  "type": "object"
                }
              },
              "application/vnd.apache.parquet": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The file",
            "headers": {
              "Accept-Ranges": {
                "schema": {
                  "type": "string"
                }
              },
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              },
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "X-Artifact-Study": {
                "schema": {
                  "format": "uuid",
                  "type": "string"
                }
              },
              "X-Artifact-Table": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "206": {
            "description": "The requested range, with `Content-Range`"
          },
          "304": {
            "description": "Unchanged"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`url_signature_invalid` or `url_expired`: list the artifacts again for a fresh URL"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`artifact_expired`: the file was reclaimed; the body carries `reclaimed_at`, the study's `spec` and `rerun: true`"
          },
          "416": {
            "description": "Unsatisfiable range, with `Content-Range: bytes */{length}`"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`artifacts_disabled`: this deployment stores no files"
          }
        },
        "security": [
          {}
        ],
        "summary": "Download one file through its signed URL",
        "tags": [
          "artifacts"
        ]
      }
    },
    "/api/v1/catalog": {
      "get": {
        "description": "Compiled into the binary, never fetched from the simulator, so it cannot be empty and cannot be slow. No credential is read: a session or a bearer key may be sent and neither changes the answer. `Cache-Control: public, max-age=300, stale-while-revalidate=86400` with a strong `ETag` from the build; `304` on a matching `If-None-Match`.",
        "operationId": "catalog",
        "parameters": [
          {
            "description": "A previous `ETag`; a match is `304` with no body",
            "in": "header",
            "name": "If-None-Match",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Catalog"
                }
              }
            },
            "description": "The catalog",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "const": "public, max-age=300, stale-while-revalidate=86400",
                  "type": "string"
                }
              },
              "ETag": {
                "description": "Strong validator",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "304": {
            "description": "Unchanged since the `ETag` sent",
            "headers": {
              "ETag": {
                "description": "Strong validator",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {}
        ],
        "summary": "Everything the builder's dropdowns need, in one request",
        "tags": [
          "catalog"
        ]
      }
    },
    "/api/v1/events": {
      "post": {
        "description": "One row in `funnel_event`: the kind, the moment, the tape or run named. No person, no cookie, no address is stored; the anonymous cookie only feeds the rate class. Summed once a day into the maker's Telegram chat (docs/ROADMAP.md §1.3). Session or anonymous cookie plus CSRF; rate class `events`, 120 per hour per caller by default.",
        "operationId": "recordEvent",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FunnelEvent"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Recorded, or dropped on a deployment without a database; the caller cannot tell and need not"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`400 invalid_request` on `kind`: not one of the three the SPA may report"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "session": []
          },
          {}
        ],
        "summary": "Record one funnel mark",
        "tags": [
          "account"
        ]
      }
    },
    "/api/v1/feedback": {
      "get": {
        "description": "The SPA asks once and hides the note box when `enabled` is false. No session needed.",
        "operationId": "feedbackStatus",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeedbackStatus"
                }
              }
            },
            "description": "The standing"
          }
        },
        "security": [
          {}
        ],
        "summary": "Whether this deployment can deliver a note",
        "tags": [
          "account"
        ]
      },
      "post": {
        "description": "Delivered to a Telegram chat by the backend; the bot token never leaves the server. What travels with the note is the tape, step, run and page named in the body and, for a signed-in session, the display name and provider. Never the email. Session or anonymous cookie plus CSRF; rate class `feedback`, 10 per hour per caller by default.",
        "operationId": "sendFeedback",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedbackNote"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Delivered"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`400 invalid_request` on `message`: empty, or past the limit"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`feedback_undelivered`: Telegram could not be reached or refused the message; nothing was lost on the caller's side"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`feedback_unavailable`: this deployment has no Telegram target configured"
          }
        },
        "security": [
          {
            "session": []
          },
          {}
        ],
        "summary": "Send a short note to the person who runs Chainwalk",
        "tags": [
          "account"
        ]
      }
    },
    "/api/v1/keys": {
      "get": {
        "operationId": "listKeys",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "keys": {
                      "items": {
                        "$ref": "#/components/schemas/KeyRecord"
                      },
                      "type": "array"
                    }
                  },
                  "required": [
                    "keys"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "The keys"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        },
        "security": [
          {
            "session": []
          }
        ],
        "summary": "The account's keys, never any secret material",
        "tags": [
          "account"
        ]
      },
      "post": {
        "description": "Session and CSRF only. The secret is hashed at rest under a pepper derived from the session key, so rotating that key invalidates every API key.",
        "operationId": "mintKey",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MintKeyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MintedKey"
                }
              }
            },
            "description": "Minted"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`invalid_request` naming `field`: an unknown scope, a blank or overlong name, an `expires_days` outside 1 to 730"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "session": []
          }
        ],
        "summary": "Mint an API key; the plaintext is shown once",
        "tags": [
          "account"
        ]
      }
    },
    "/api/v1/keys/{key_id}": {
      "delete": {
        "operationId": "revokeKey",
        "parameters": [
          {
            "in": "path",
            "name": "key_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked; a bearer request with it is a 401 from now on"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "session": []
          }
        ],
        "summary": "Revoke a key immediately",
        "tags": [
          "account"
        ]
      }
    },
    "/api/v1/me": {
      "delete": {
        "description": "Session and CSRF only; no key scope grants this.",
        "operationId": "deleteMe",
        "responses": {
          "204": {
            "description": "Gone"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        },
        "security": [
          {
            "session": []
          }
        ],
        "summary": "Delete the account and everything it owns",
        "tags": [
          "account"
        ]
      },
      "get": {
        "operationId": "me",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            },
            "description": "The account"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        },
        "security": [
          {
            "session": []
          }
        ],
        "summary": "Who the session belongs to",
        "tags": [
          "account"
        ]
      }
    },
    "/api/v1/quota": {
      "get": {
        "description": "With a session: the interactive materialisation quota and, when signed in, the study meters. With a bearer key: the study meters. The `X-Chainwalk-Quota-*` headers restate the meters.",
        "operationId": "quota",
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quota"
                }
              }
            },
            "description": "The standing"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          }
        },
        "security": [
          {
            "bearer": []
          },
          {
            "session": []
          },
          {}
        ],
        "summary": "Where the caller stands on every budget",
        "tags": [
          "account"
        ]
      }
    },
    "/api/v1/runs": {
      "get": {
        "operationId": "listRuns",
        "parameters": [
          {
            "description": "Clamped into 1 to 100",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 25,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "`next_cursor` from the previous page, opaque",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RunList"
                }
              }
            },
            "description": "A page"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`invalid_request` naming `cursor`: not a value a previous page handed out"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`identity_disabled`: this deployment has no identity surface (no database or no session cookie key), so it saves nothing"
          }
        },
        "security": [
          {
            "session": []
          }
        ],
        "summary": "The caller's runs, newest first",
        "tags": [
          "runs"
        ]
      },
      "post": {
        "description": "Session and CSRF. The position is validated against the tape exactly as a projection would validate it, so a run that could not have been opened is refused, not stored. The fee schedule is stored by value; legs, `opened_at_step`, `spread_model` and `fee_schedule` are immutable afterwards. Stores legs and never a strategy name (ADR 0003).",
        "operationId": "createRun",
        "parameters": [
          {
            "description": "The value of the `cw_csrf` cookie, echoed. Required on every POST, PATCH and DELETE of this surface, anonymous ones included; absent or mismatched is `403 csrf_failed`. A request carrying `Authorization: Bearer` is exempt",
            "in": "header",
            "name": "X-CW-CSRF",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunCreate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Run"
                }
              }
            },
            "description": "Saved, with the exact `entry` block of the save"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "As the projection's 400, plus `invalid_request` naming `title` (over 120 characters), `visibility` (not `private` or `unlisted`) or `opened_at_step`"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`not_found`: no such tape on this deployment"
          },
          "410": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`tape_evicted` with `\"rematerialise\": true`: the id is known but its bytes are gone. Re-POST the identical scenario and the identical `tape_id` comes back"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`too_many_legs` (above 8); `strike_off_ladder` naming `legs[i].strike` or `legs[i].expires_at` for a value this tape does not carry; `unsupported_model` for a `style: underlying` leg; `leg_not_quoted_at_open` for a leg that cannot be filled at `open_at_step`; `spread_model_retired` for `chainwalk_v1` or `ocs_native`"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`identity_disabled`: this deployment has no identity surface (no database or no session cookie key), so it saves nothing"
          }
        },
        "security": [
          {
            "session": []
          }
        ],
        "summary": "Save a position: its tape, its legs, the step it opened at and the basis it was priced on",
        "tags": [
          "runs"
        ]
      }
    },
    "/api/v1/runs/{slug}": {
      "delete": {
        "operationId": "deleteRun",
        "parameters": [
          {
            "description": "The run's slug, which is the capability: the internal id never crosses the wire",
            "in": "path",
            "name": "slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The value of the `cw_csrf` cookie, echoed. Required on every POST, PATCH and DELETE of this surface, anonymous ones included; absent or mismatched is `403 csrf_failed`. A request carrying `Authorization: Bearer` is exempt",
            "in": "header",
            "name": "X-CW-CSRF",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Gone"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`identity_disabled`: this deployment has no identity surface (no database or no session cookie key), so it saves nothing"
          }
        },
        "security": [
          {
            "session": []
          }
        ],
        "summary": "Delete a run, owner only, hard",
        "tags": [
          "runs"
        ]
      },
      "get": {
        "description": "No credential for an `unlisted` run: the slug is the grant. A `private` run answers its owner and is a `404` to everyone else, never a `403`, so the archive of a hidden slug is never confirmed. The `tape` block says what a client can do next: `ready` opens at once, `evicted` means re-POST the scenario and the same `tape_id` comes back. The `entry` block is absent on a read; a client re-derives it by projecting, which lands on the same numbers because the tape, the legs and the schedule are pinned.",
        "operationId": "getRun",
        "parameters": [
          {
            "description": "The run's slug, which is the capability: the internal id never crosses the wire",
            "in": "path",
            "name": "slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Run"
                }
              }
            },
            "description": "The run"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`identity_disabled`: this deployment has no identity surface (no database or no session cookie key), so it saves nothing"
          }
        },
        "security": [
          {
            "session": []
          },
          {}
        ],
        "summary": "One run by its slug",
        "tags": [
          "runs"
        ]
      },
      "patch": {
        "description": "Owner only. Legs and the pricing basis are immutable: changing the position means a new run, because a run is the record of a position that was opened.",
        "operationId": "patchRun",
        "parameters": [
          {
            "description": "The run's slug, which is the capability: the internal id never crosses the wire",
            "in": "path",
            "name": "slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The value of the `cw_csrf` cookie, echoed. Required on every POST, PATCH and DELETE of this surface, anonymous ones included; absent or mismatched is `403 csrf_failed`. A request carrying `Authorization: Bearer` is exempt",
            "in": "header",
            "name": "X-CW-CSRF",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RunPatch"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Run"
                }
              }
            },
            "description": "The run as it now reads"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`invalid_json`; `invalid_request` naming `title` (over 120 characters), `visibility` (not `private` or `unlisted`) or an unknown field"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`identity_disabled`: this deployment has no identity surface (no database or no session cookie key), so it saves nothing"
          }
        },
        "security": [
          {
            "session": []
          }
        ],
        "summary": "Rename a run or change its visibility; nothing else",
        "tags": [
          "runs"
        ]
      }
    },
    "/api/v1/runs/{slug}/bundle": {
      "get": {
        "description": "Same visibility rule as the run itself. One stored archive (no deflate; the Parquet members are already compressed) whose every member sits under `<run_id>/`: `manifest.json`, `fills.parquet`, `equity_curve.parquet`, `positions.parquet`, `greeks_attribution.parquet`, `chain.parquet`. The projection is obtained exactly as the viewer obtains it, so the equity curve is the chart to the cent (ADR 0007). `run_id` is IronCondor's derivation over the seed, the config including the capital, the strategy, the `tape_id` and the code version; two exports of one run are the same bytes in every member, only the manifest's `created_utc` moves. `Cache-Control: no-store`.",
        "operationId": "runBundle",
        "parameters": [
          {
            "description": "The run's slug, which is the capability: the internal id never crosses the wire",
            "in": "path",
            "name": "slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The `C` in `equity(t) = C + pnl(t)`: a reporting constant, never a price input. Part of the bundle's `run_id`, so two capitals are two archives",
            "in": "query",
            "name": "initial_capital_cents",
            "required": false,
            "schema": {
              "default": 10000000,
              "minimum": 1,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/zip": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The archive",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "const": "no-store",
                  "type": "string"
                }
              },
              "Content-Disposition": {
                "description": "`attachment; filename=\"<run_id>.bundle.zip\"`",
                "schema": {
                  "type": "string"
                }
              },
              "x-chainwalk-run-id": {
                "description": "The archive's `run_id`, so a client can name it without opening it",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`invalid_request` naming `initial_capital`: zero, or a value that does not fit signed cents"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`tape_evicted` with `\"rematerialise\": true`: the run is intact, the tape it lives on is not resident; re-POST the scenario and the same `tape_id` comes back"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`bundle_unrepresentable` (`legs`): a value IronCondor's contract refuses, such as a symbol outside its ticker grammar; the message names the leg. Never a shape: every chainwalk position is a `StrategySpec::Legs`"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`identity_disabled`: this deployment has no identity surface (no database or no session cookie key), so it saves nothing"
          }
        },
        "security": [
          {
            "session": []
          },
          {}
        ],
        "summary": "The run as an `ironcondor.bundle.v1` zip, the archive ChainView's replay opens",
        "tags": [
          "runs"
        ]
      }
    },
    "/api/v1/runs/{slug}/card.png": {
      "get": {
        "description": "Title, legs, the P&L where the run ends and the P&L curve, drawn by the api from the same projection the viewer shows. What a link to /r/{slug} unfurls into. A private run is a 404 to anyone but its owner; a run whose tape is not resident draws without the curve. Cached a day.",
        "operationId": "runCard",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "image/png": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The card"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "session": []
          },
          {}
        ],
        "summary": "The run's OpenGraph card, a 1200x630 PNG",
        "tags": [
          "account"
        ]
      }
    },
    "/api/v1/runs/{slug}/og": {
      "get": {
        "description": "Always 204. The `X-Chainwalk-OG` header carries the `<meta>` tags for the run, HTML-escaped and ASCII; the web's nginx makes this subrequest for /r/{slug} and splices the header into the SPA shell, so a crawler sees the title and the card. A run nobody may see yields an empty header, never an error: an unfurl must not say more than the page.",
        "operationId": "runOpenGraph",
        "parameters": [
          {
            "in": "path",
            "name": "slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The tags, in `X-Chainwalk-OG`",
            "headers": {
              "X-Chainwalk-OG": {
                "description": "The `<meta>` tags, or empty",
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "session": []
          },
          {}
        ],
        "summary": "The run's OpenGraph meta tags, in one response header",
        "tags": [
          "account"
        ]
      }
    },
    "/api/v1/scenarios": {
      "post": {
        "description": "The only route that can call the simulator, and therefore the only one with a daily quota: 3 distinct tapes per rolling 24 hours anonymously (keyed on the `cw_anon` cookie, minted on the first call), 30 with a session. `tape_id` is a content address computed before any call, so a scenario that already exists is joined with a `200` and consumes nothing; a request that arrives while the same scenario is materialising waits on it and joins too. Every field of the body is optional and an absent one takes the reference scenario's value, so an empty body materialises the reference at seed 42. There is no top-level `volatility` (the model's is the one base volatility) and `spread_model` is checked, never chosen: the deployment has one, inside `tape_id`.",
        "operationId": "createScenario",
        "parameters": [
          {
            "description": "The value of the `cw_csrf` cookie, echoed. Required on every POST, PATCH and DELETE of this surface, anonymous ones included; absent or mismatched is `403 csrf_failed`. A request carrying `Authorization: Bearer` is exempt",
            "in": "header",
            "name": "X-CW-CSRF",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScenarioRequest"
              }
            }
          },
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TapeSummary"
                }
              }
            },
            "description": "Joined a tape that already existed; the body is the same summary a `201` carries"
          },
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TapeSummary"
                }
              }
            },
            "description": "Materialised by this request. With an identity surface the quota headers state the rolling day after this charge, and a first-time anonymous caller receives the `cw_anon` cookie",
            "headers": {
              "Set-Cookie": {
                "description": "`cw_anon`, for a caller that had neither a session nor one already",
                "schema": {
                  "type": "string"
                }
              },
              "X-Chainwalk-Quota-Limit": {
                "description": "Distinct tapes per rolling 24 hours for this identity",
                "schema": {
                  "type": "integer"
                }
              },
              "X-Chainwalk-Quota-Remaining": {
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`invalid_json` (not JSON) or `invalid_request` naming `field`: an unknown field such as a top-level `volatility`, or a value of the wrong type"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`invalid_scenario` naming `field`, before any simulator call: a model outside the catalog, a parameter outside its band, a schedule the calendar refuses, a contract budget exceeded, or a scalar that is not finite; `spread_model_retired` for `chainwalk_v1` or `ocs_native`; `upstream_rejected` when the simulator refused a scenario chainwalk admitted"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`quota_exhausted` with `Retry-After` and `X-Chainwalk-Quota-Limit`/`-Remaining`: the rolling day's distinct tapes are spent. The message says whether signing in raises the limit"
          },
          "502": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`upstream_error`: the simulator answered something chainwalk cannot use"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamBusy"
          },
          "504": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`upstream_timeout`: the simulator did not answer within the export deadline"
          }
        },
        "security": [
          {
            "session": []
          },
          {}
        ],
        "summary": "Materialise a scenario into a tape, or join the tape that already is that scenario",
        "tags": [
          "tapes"
        ]
      }
    },
    "/api/v1/studies": {
      "get": {
        "operationId": "listStudies",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 25,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "description": "`next_cursor` from the previous page",
            "in": "query",
            "name": "cursor",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StudyList"
                }
              }
            },
            "description": "A page",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "bearer": []
          },
          {
            "session": []
          }
        ],
        "summary": "The caller's studies, newest first",
        "tags": [
          "studies"
        ]
      },
      "post": {
        "description": "Admission is the wall: the spec is validated, the legs resolved against step 0, the cost computed and checked against the cell, disk and meter budgets, all before anything touches the simulator. A 202 has reserved the worst case on the meters; the worker settles actuals. Rate class `study_create` (30/h session, 300/h key); a dry run polls instead.",
        "operationId": "createStudy",
        "parameters": [
          {
            "description": "Resolve, cost and check; reserve nothing, queue nothing",
            "in": "query",
            "name": "dry_run",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          },
          {
            "description": "`Prefer: wait=N` for tools that cannot set headers: hold the request up to N seconds (capped at 30) for a terminal state",
            "in": "query",
            "name": "wait",
            "required": false,
            "schema": {
              "maximum": 30,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "`wait=N`: answer 200 with the terminal study when it lands inside N seconds (capped at 30), else the usual response. `Preference-Applied: wait=N` says it did",
            "in": "header",
            "name": "Prefer",
            "required": false,
            "schema": {
              "pattern": "^wait=\\d+$",
              "type": "string"
            }
          },
          {
            "description": "Replays the original response for the same body (`Idempotency-Replayed: true`); a different body is `409 idempotency_conflict`",
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "maxLength": 200,
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StudySpec"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/StudyDryRun"
                    },
                    {
                      "$ref": "#/components/schemas/Study"
                    }
                  ]
                }
              }
            },
            "description": "A dry run (`StudyDryRun`), or the terminal study when `Prefer: wait` landed it (`Study`, with `Preference-Applied`)"
          },
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StudyAccepted"
                }
              }
            },
            "description": "Queued. `Location` is the job resource, `Retry-After` the first poll interval; the `X-Chainwalk-Quota-*` headers are the meters after this reservation",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              },
              "X-Chainwalk-Quota-Materialised-Cells-Limit": {
                "$ref": "#/components/headers/X-Chainwalk-Quota-Materialised-Cells-Limit"
              },
              "X-Chainwalk-Quota-Materialised-Cells-Remaining": {
                "$ref": "#/components/headers/X-Chainwalk-Quota-Materialised-Cells-Remaining"
              },
              "X-Chainwalk-Quota-Report-Cells-Limit": {
                "$ref": "#/components/headers/X-Chainwalk-Quota-Report-Cells-Limit"
              },
              "X-Chainwalk-Quota-Report-Cells-Remaining": {
                "$ref": "#/components/headers/X-Chainwalk-Quota-Report-Cells-Remaining"
              },
              "X-Chainwalk-Quota-Reset": {
                "$ref": "#/components/headers/X-Chainwalk-Quota-Reset"
              },
              "X-Chainwalk-Quota-Stored-Bytes-Limit": {
                "$ref": "#/components/headers/X-Chainwalk-Quota-Stored-Bytes-Limit"
              },
              "X-Chainwalk-Quota-Stored-Bytes-Remaining": {
                "$ref": "#/components/headers/X-Chainwalk-Quota-Stored-Bytes-Remaining"
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`invalid_json` (not JSON at all) or `invalid_request` naming `field`: an unknown field, a wrong type, an unparseable `scenario.start_at`, a `fees` schedule the backend cannot read"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`idempotency_conflict`: the key was used with a different body"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Well formed and refused before any simulator call. `invalid_study` naming `field` for the spec's own rules: a caller `scenario.seed`, a leg with two strike selectors or none, a `target_delta` of the wrong sign or one no rung comes within 0.10 of, a strike off the ladder, an unknown or unbuilt `include` table (`attribution_by_step`), too many paths or legs. Then the budgets, each naming its dimension: `spread_model_immutable`, `cells_per_path_exceeded`, `study_cells_exceeded`, `study_bytes_exceeded`. Never carries `Retry-After`: waiting does not help"
          },
          "429": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`quota_exhausted` (a meter, with `X-Chainwalk-Quota-Meter`; also the concurrent-study cap) or `rate_limited` (the class), both with `Retry-After`"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamBusy"
          }
        },
        "security": [
          {
            "bearer": []
          },
          {
            "session": []
          }
        ],
        "summary": "Submit a study (or preflight it with `dry_run=true`)",
        "tags": [
          "studies"
        ]
      }
    },
    "/api/v1/studies/{study_id}": {
      "delete": {
        "description": "Never means cancel: a running study keeps running until its worker notices the row is gone. Scope `studies:write`.",
        "operationId": "deleteStudy",
        "parameters": [
          {
            "in": "path",
            "name": "study_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Gone"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "security": [
          {
            "bearer": []
          },
          {
            "session": []
          }
        ],
        "summary": "Delete a study, its tape links and its artefacts",
        "tags": [
          "studies"
        ]
      },
      "get": {
        "description": "Poll it: `Retry-After` is 1 s while queued and 5 s while running, and absent once terminal. Rate class `study_poll`, 600/h.",
        "operationId": "getStudy",
        "parameters": [
          {
            "in": "path",
            "name": "study_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "description": "`Prefer: wait=N` for tools that cannot set headers: hold the request up to N seconds (capped at 30) for a terminal state",
            "in": "query",
            "name": "wait",
            "required": false,
            "schema": {
              "maximum": 30,
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "`wait=N`: answer 200 with the terminal study when it lands inside N seconds (capped at 30), else the usual response. `Preference-Applied: wait=N` says it did",
            "in": "header",
            "name": "Prefer",
            "required": false,
            "schema": {
              "pattern": "^wait=\\d+$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Study"
                }
              }
            },
            "description": "The study",
            "headers": {
              "Preference-Applied": {
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "bearer": []
          },
          {
            "session": []
          }
        ],
        "summary": "The job resource with the tier 1 report inline once terminal",
        "tags": [
          "studies"
        ]
      }
    },
    "/api/v1/studies/{study_id}/artifacts": {
      "get": {
        "description": "The URLs are capabilities valid for `url_ttl_secs` (900): fetch them with no cookie and no bearer. Reclaimed files are listed under `reclaimed` without a URL. `Cache-Control: private, no-store`.",
        "operationId": "listArtifacts",
        "parameters": [
          {
            "in": "path",
            "name": "study_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArtifactList"
                }
              }
            },
            "description": "The listing",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`identity_unavailable`: this deployment cannot sign URLs"
          }
        },
        "security": [
          {
            "bearer": []
          },
          {
            "session": []
          }
        ],
        "summary": "Tier 3: the study's files, each live one behind a fresh signed URL",
        "tags": [
          "artifacts"
        ]
      }
    },
    "/api/v1/studies/{study_id}/bundle": {
      "get": {
        "description": "The path is re-projected from its tape (a cache hit when still resident, a rematerialisation otherwise) and written as the bundle IronCondor's reader and ChainView open (ADR 0007). Byte-identical to the same position replayed as a run.",
        "operationId": "studyBundle",
        "parameters": [
          {
            "in": "path",
            "name": "study_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "description": "The path index, default 0",
            "in": "query",
            "name": "path",
            "required": false,
            "schema": {
              "minimum": 0,
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/zip": {
                "schema": {
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The zip",
            "headers": {
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`study_not_terminal`, or the path failed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamBusy"
          }
        },
        "security": [
          {
            "bearer": []
          },
          {
            "session": []
          }
        ],
        "summary": "One path of a terminal study as an `ironcondor.bundle.v1` zip",
        "tags": [
          "studies"
        ]
      }
    },
    "/api/v1/studies/{study_id}/cancel": {
      "post": {
        "description": "A queued study is `cancelled` at once; a running one is `cancelling` until its worker lands it, refunding the paths not yet materialised. Scope `studies:write`.",
        "operationId": "cancelStudy",
        "parameters": [
          {
            "in": "path",
            "name": "study_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "state": {
                      "enum": [
                        "cancelled",
                        "cancelling"
                      ],
                      "type": "string"
                    }
                  },
                  "required": [
                    "state"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Moving"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`study_terminal`: already finished"
          }
        },
        "security": [
          {
            "bearer": []
          },
          {
            "session": []
          }
        ],
        "summary": "Ask a study to stop",
        "tags": [
          "studies"
        ]
      }
    },
    "/api/v1/studies/{study_id}/events": {
      "get": {
        "description": "A convenience over polling, never a second source of truth: every event is a snapshot of the same row `GET /api/v1/studies/{id}` serves. Events: `state` (`{state, progress}` on a change), `progress` (counters), `done` (the full study, then the stream closes). `Last-Event-ID` resumes exactly. A `: ping` comment every 15 s.",
        "operationId": "studyEvents",
        "parameters": [
          {
            "in": "path",
            "name": "study_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "Last-Event-ID",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "The stream"
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "bearer": []
          },
          {
            "session": []
          }
        ],
        "summary": "Progress as Server-Sent Events",
        "tags": [
          "studies"
        ]
      }
    },
    "/api/v1/studies/{study_id}/steps": {
      "get": {
        "operationId": "studySteps",
        "parameters": [
          {
            "in": "path",
            "name": "study_id",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StepTable"
                }
              }
            },
            "description": "The table",
            "headers": {
              "RateLimit-Limit": {
                "$ref": "#/components/headers/RateLimit-Limit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimit-Policy"
              },
              "RateLimit-Remaining": {
                "$ref": "#/components/headers/RateLimit-Remaining"
              },
              "RateLimit-Reset": {
                "$ref": "#/components/headers/RateLimit-Reset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthenticated"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`study_not_terminal`: the table lands with the report"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        },
        "security": [
          {
            "bearer": []
          },
          {
            "session": []
          }
        ],
        "summary": "Tier 2: the per-step table, columnar",
        "tags": [
          "studies"
        ]
      }
    },
    "/api/v1/tapes/{tape_id}": {
      "get": {
        "description": "The same body the create answered with. Immutable once `state` is `ready`; `Cache-Control: public, max-age=60` keeps the window short so an eviction is noticed.",
        "operationId": "getTape",
        "parameters": [
          {
            "description": "The content address `POST /api/v1/scenarios` answered with",
            "in": "path",
            "name": "tape_id",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{64}$",
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TapeSummary"
                }
              }
            },
            "description": "The tape",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "const": "public, max-age=60",
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`tape_evicted` with `\"rematerialise\": true`: the id is known but its bytes are gone. Re-POST the identical scenario and the identical `tape_id` comes back"
          }
        },
        "security": [
          {}
        ],
        "summary": "What a tape is",
        "tags": [
          "tapes"
        ]
      }
    },
    "/api/v1/tapes/{tape_id}/entry-cost": {
      "post": {
        "description": "The same position priced at one step, so the browser can put the entry cost on screen, labelled, before it draws anything the position did. Spread plus commission, with the touch actually paid per leg.",
        "operationId": "entryCost",
        "parameters": [
          {
            "description": "The content address `POST /api/v1/scenarios` answered with",
            "in": "path",
            "name": "tape_id",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{64}$",
              "type": "string"
            }
          },
          {
            "description": "The value of the `cw_csrf` cookie, echoed. Required on every POST, PATCH and DELETE of this surface, anonymous ones included; absent or mismatched is `403 csrf_failed`. A request carrying `Authorization: Bearer` is exempt",
            "in": "header",
            "name": "X-CW-CSRF",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EntryCostRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EntryCost"
                }
              }
            },
            "description": "The entry"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`invalid_json` (not JSON); `invalid_request` naming `field`: a missing or unknown field, an empty `legs`, a `quantity` outside 1 to 1000, a zero `multiplier`, or a step outside the tape (`to_step`, `open_at_step`); `unknown_fee_schedule` or `invalid_fee_schedule` (`fee_schedule`); `unknown_spread_model` (`spread_model.kind`)"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`tape_evicted` with `\"rematerialise\": true`: the id is known but its bytes are gone. Re-POST the identical scenario and the identical `tape_id` comes back"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`too_many_legs` (above 8); `strike_off_ladder` naming `legs[i].strike` or `legs[i].expires_at` for a value this tape does not carry; `unsupported_model` for a `style: underlying` leg; `leg_not_quoted_at_open` for a leg that cannot be filled at `open_at_step`; `spread_model_retired` for `chainwalk_v1` or `ocs_native`"
          }
        },
        "security": [
          {
            "session": []
          },
          {}
        ],
        "summary": "What getting in costs: the number the curve starts below zero by",
        "tags": [
          "tapes"
        ]
      }
    },
    "/api/v1/tapes/{tape_id}/payoff": {
      "post": {
        "description": "Two curves behind one route, chosen by `at`. `expiry`: the terminal payoff at each hypothetical spot, every live leg settled at intrinsic with no spread, net of the entry, the commission and the settlement fee of every leg finishing in the money. `step`: the liquidation value at the requested step, each leg re-priced at the hypothetical spot under its published implied volatility and the tape's spread model, then taken at the bid if long and the ask if short. Both are net of spread and fees; there is no gross mode. The market is the scenario's own (`risk_free_rate`, `dividend_yield`, the tape's spread model), never a request parameter.",
        "operationId": "payoff",
        "parameters": [
          {
            "description": "The content address `POST /api/v1/scenarios` answered with",
            "in": "path",
            "name": "tape_id",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{64}$",
              "type": "string"
            }
          },
          {
            "description": "The value of the `cw_csrf` cookie, echoed. Required on every POST, PATCH and DELETE of this surface, anonymous ones included; absent or mismatched is `403 csrf_failed`. A request carrying `Authorization: Bearer` is exempt",
            "in": "header",
            "name": "X-CW-CSRF",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PayoffRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payoff"
                }
              }
            },
            "description": "The curve"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "As the projection's 400, plus `invalid_request` naming `grid` for an empty, inverted or non-positive range or more than 2001 points, and `step` for a missing step"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`tape_evicted` with `\"rematerialise\": true`: the id is known but its bytes are gone. Re-POST the identical scenario and the identical `tape_id` comes back"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`too_many_legs` (above 8); `strike_off_ladder` naming `legs[i].strike` or `legs[i].expires_at` for a value this tape does not carry; `unsupported_model` for a `style: underlying` leg; `leg_not_quoted_at_open` for a leg that cannot be filled at `open_at_step`; `spread_model_retired` for `chainwalk_v1` or `ocs_native`"
          }
        },
        "security": [
          {
            "session": []
          },
          {}
        ],
        "summary": "Where the position wins and loses, as a curve over the spot, at a step",
        "tags": [
          "tapes"
        ]
      }
    },
    "/api/v1/tapes/{tape_id}/projection": {
      "post": {
        "description": "Safe and idempotent; a POST because the leg set is structured and the response is binary. Every number comes from the five lines of docs/04-api.md: a long leg fills at the ask and marks at the bid, a short leg the reverse, and the position is under water at the step it opens by the spread plus the commission. Greeks are per one long contract; the client applies sign and size once, at the sum. The default body is the packed block (`application/vnd.chainwalk.packed.v1`, container `OCSP`, schema block version 2; the layout is in docs/04-api.md); `format: json` answers the same numbers as JSON, about six times larger, for debugging. The `ETag` is a hash of the bytes and `Cache-Control` is `private, no-cache`: a validated response is valid forever, and a projection is somebody's position.",
        "operationId": "project",
        "parameters": [
          {
            "description": "The content address `POST /api/v1/scenarios` answered with",
            "in": "path",
            "name": "tape_id",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{64}$",
              "type": "string"
            }
          },
          {
            "description": "The value of the `cw_csrf` cookie, echoed. Required on every POST, PATCH and DELETE of this surface, anonymous ones included; absent or mismatched is `403 csrf_failed`. A request carrying `Authorization: Bearer` is exempt",
            "in": "header",
            "name": "X-CW-CSRF",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "A previous `ETag`; a match is `304` with no body",
            "in": "header",
            "name": "If-None-Match",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Projection"
                }
              },
              "application/vnd.chainwalk.packed.v1": {
                "schema": {
                  "description": "The packed leg projection: a 64-byte `OCSP` header, a UTF-8 JSON schema block, then 8-byte-aligned little-endian column blocks with LSB-first validity bitmaps",
                  "format": "binary",
                  "type": "string"
                }
              }
            },
            "description": "The projection, in the encoding `format` asked for",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "const": "private, no-cache",
                  "type": "string"
                }
              },
              "ETag": {
                "description": "Strong validator",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "304": {
            "description": "Unchanged since the `ETag` sent",
            "headers": {
              "ETag": {
                "description": "Strong validator",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`invalid_json` (not JSON); `invalid_request` naming `field`: a missing or unknown field, an empty `legs`, a `quantity` outside 1 to 1000, a zero `multiplier`, or a step outside the tape (`to_step`, `open_at_step`); `unknown_fee_schedule` or `invalid_fee_schedule` (`fee_schedule`); `unknown_spread_model` (`spread_model.kind`)"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`tape_evicted` with `\"rematerialise\": true`: the id is known but its bytes are gone. Re-POST the identical scenario and the identical `tape_id` comes back"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`too_many_legs` (above 8); `strike_off_ladder` naming `legs[i].strike` or `legs[i].expires_at` for a value this tape does not carry; `unsupported_model` for a `style: underlying` leg; `leg_not_quoted_at_open` for a leg that cannot be filled at `open_at_step`; `spread_model_retired` for `chainwalk_v1` or `ocs_native`"
          }
        },
        "security": [
          {
            "session": []
          },
          {}
        ],
        "summary": "The legs across every step, priced",
        "tags": [
          "tapes"
        ]
      }
    },
    "/api/v1/tapes/{tape_id}/steps/{step}": {
      "get": {
        "description": "Read from the stored tape, never from a second simulator call. A contract that is not quoted is rendered, never omitted. `mid` is the one number here a user should read; nothing in a snapshot feeds a P&L. `Cache-Control: public, max-age=31536000, immutable`: a tape step never changes and no accepted parameter can change the body for a given URL.",
        "operationId": "getSnapshot",
        "parameters": [
          {
            "description": "The content address `POST /api/v1/scenarios` answered with",
            "in": "path",
            "name": "tape_id",
            "required": true,
            "schema": {
              "pattern": "^[0-9a-f]{64}$",
              "type": "string"
            }
          },
          {
            "description": "0 to `steps - 1`",
            "in": "path",
            "name": "step",
            "required": true,
            "schema": {
              "minimum": 0,
              "type": "integer"
            }
          },
          {
            "description": "Comma-separated: RFC 3339 instants exactly as the tape spells them, or indices into the tape's `expirations`. Absent means every expiration still alive at the step; a settled one is never in a chain",
            "in": "query",
            "name": "expiries",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "`all`, `atm:N` for the N ladder strikes nearest the spot, or a comma-separated list every member of which must be on the ladder",
            "in": "query",
            "name": "strikes",
            "required": false,
            "schema": {
              "default": "atm:21",
              "type": "string"
            }
          },
          {
            "description": "`none` leaves `gamma` and each side's `delta` null; `first` fills them; `all` adds theta, vega, rho, rho_d, alpha, vanna, vomma, veta, charm and color on each side",
            "in": "query",
            "name": "greeks",
            "required": false,
            "schema": {
              "default": "first",
              "enum": [
                "none",
                "first",
                "all"
              ],
              "type": "string"
            }
          },
          {
            "description": "Refused with `422 spread_model_immutable`: the model is inside `tape_id`, so a different model is a different tape",
            "in": "query",
            "name": "spread_model",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Snapshot"
                }
              }
            },
            "description": "The chain at the step",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "const": "public, max-age=31536000, immutable",
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`invalid_request` naming `field`: `step` past the end of the tape, an `expiries` item naming no expiration, a `strikes` item that is not a strike or an `atm:0` window, or an unknown `greeks` level"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`tape_evicted` with `\"rematerialise\": true`: the id is known but its bytes are gone. Re-POST the identical scenario and the identical `tape_id` comes back"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "`strike_off_ladder` (`strikes`): an explicit strike the ladder does not carry; `spread_model_immutable` (`spread_model`)"
          }
        },
        "security": [
          {}
        ],
        "summary": "One snapshot of the whole chain at one step, for the leg picker",
        "tags": [
          "tapes"
        ]
      }
    }
  },
  "servers": [
    {
      "description": "This deployment",
      "url": "/"
    }
  ],
  "tags": [
    {
      "description": "What the builder's dropdowns need",
      "name": "catalog"
    },
    {
      "description": "Materialised scenarios and what can be asked of one",
      "name": "tapes"
    },
    {
      "description": "Saved positions over a tape, shareable",
      "name": "runs"
    },
    {
      "description": "Who you are, your keys and your budgets",
      "name": "account"
    },
    {
      "description": "Monte Carlo studies: many paths, one report",
      "name": "studies"
    },
    {
      "description": "Tier 3 files behind short-lived signed URLs",
      "name": "artifacts"
    }
  ]
}