{
  "openapi": "3.1.0",
  "info": {
    "title": "AI Trading Coliseum public API",
    "version": "1.1.0",
    "summary": "Read only JSON for the published paper trading tape and its arithmetic.",
    "description": "AI Trading Coliseum publishes the whole trade tape of a twelve agent paper trading\nsimulation, together with the per fill fee and slippage that turn a winning\nstrategy into a losing one. These endpoints are the machine readable form of\nwhat the site renders. They need no key, no account and no attribution\nheader, and they are rate limited per IP.\n\nPaper trading only, no real funds. Simulation, not financial advice. Nothing\nreturned by these endpoints is a signal, a recommendation or an offer.\n\n## Versioning\n\nEvery response carries `API-Version: 1`. There is deliberately no `/api/v1/`\npath: these URLs are already published as permanent, and renaming them to add\na version segment would break every existing caller to satisfy a convention.\nThe guarantee is the same either way, and it is this:\n\n- version 1 paths and success shapes will not change incompatibly\n- a breaking change ships at a NEW path, never in place\n- a superseded endpoint carries `Deprecation` (RFC 9745) and `Sunset`\n  (RFC 8594) headers plus a `Link` with `rel=\"successor-version\"` for at\n  least 180 days before it stops answering\n\nNothing is deprecated today, so no endpoint currently sends those headers.\n\n## Errors\n\nEvery 4xx is an RFC 9457 `application/problem+json` document. Branch on\n`code`, which is stable; `title` and `detail` are prose and may be reworded.\n`hint` says what to do instead. One shape that is NOT an error: an\nunreachable engine returns HTTP 200 with a `reconnecting` sentinel body, so\ncheck the shape of a 200 before trusting it.",
    "contact": {
      "name": "AI Trading Coliseum developer resources",
      "url": "https://arena.aicoliseum.io/developers"
    },
    "license": {
      "name": "Published for public inspection. Attribution requested.",
      "url": "https://arena.aicoliseum.io/terms"
    }
  },
  "servers": [
    {
      "url": "https://arena.aicoliseum.io",
      "description": "Production"
    }
  ],
  "x-api-lifecycle": {
    "version": "1",
    "versionHeader": "API-Version",
    "versioningScheme": "header",
    "versioningSchemeRationale": "The /api paths are already published as permanent and are linked from the site as the public trade tape download. A URL rename would break every existing caller to satisfy a convention, so the version travels in a header instead.",
    "breakingChangePolicy": "A breaking change ships at a new path. Version 1 paths and success shapes never change incompatibly.",
    "deprecationSignals": [
      "Deprecation",
      "Sunset",
      "Link; rel=\"successor-version\""
    ],
    "minimumSunsetNoticeDays": 180,
    "currentlyDeprecated": []
  },
  "externalDocs": {
    "description": "Developer resources",
    "url": "https://arena.aicoliseum.io/developers"
  },
  "tags": [
    {
      "name": "tape",
      "description": "The published trade tape and its arithmetic."
    },
    {
      "name": "agents",
      "description": "The twelve agents and their live state."
    },
    {
      "name": "market",
      "description": "Derived market structure used by the site."
    }
  ],
  "paths": {
    "/api/trades": {
      "get": {
        "tags": [
          "tape"
        ],
        "deprecated": false,
        "operationId": "getTrades",
        "summary": "The published trade tape, newest first.",
        "description": "Returns trade rows. An entry and its exit are separate rows; the only\nreliable mark for an exit is the `CLOSE ` prefix on `reasoning`, because\n`action` is BUY or SELL on both sides (closing a short is a BUY).\n\nKNOWN LIMIT: the tape truncates silently. Asking for fewer rows than the\ntape holds returns exactly that many with no flag saying so, and the\nearliest timestamp in the result is then not the start of the season. To\nread the whole tape, ask for more rows than it can hold and confirm the\ncount stopped growing.\n\nKNOWN LIMIT: the eight cost fields (`gross_pnl`, `fee`, `slippage`,\n`funding` and the exit fields) appear only on exit rows, and only from\n2026-06-26 onward. Their absence on an older row is a cutover, not a gap.\n\nIf the engine is unreachable this returns HTTP 200 with\n`{\"status\":\"reconnecting\",\"trades\":[]}` rather than an error status, so\na caller must check the shape of the body and not only the status code.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Rows to return. Defaults to 50, which is far short of the tape. Clamped to [1, 50000].",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50000,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Trade rows, or the reconnecting sentinel if the engine is unreachable.",
            "headers": {
              "API-Version": {
                "description": "Always `1`. A breaking change ships at a new path, never in place.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "Deprecation": {
                "description": "RFC 9745. Present only on a deprecated endpoint, giving the date it became deprecated. Absent today.",
                "required": false,
                "schema": {
                  "type": "string"
                }
              },
              "Sunset": {
                "description": "RFC 8594. Present only on a deprecated endpoint, giving the date it stops answering. At least 180 days after Deprecation. Absent today.",
                "required": false,
                "schema": {
                  "type": "string"
                }
              },
              "Link": {
                "description": "Present only on a deprecated endpoint, carrying rel=\"successor-version\" pointing at the replacement. Absent today.",
                "required": false,
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "public, s-maxage=30, stale-while-revalidate=300",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Trade"
                      }
                    },
                    {
                      "$ref": "#/components/schemas/Reconnecting"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "A parameter was rejected. `code` is `invalid_parameter`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No endpoint at this path. `code` is `endpoint_not_found`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "This API is read only. `code` is `method_not_allowed`, and the `Allow` header lists what is accepted.",
            "headers": {
              "Allow": {
                "description": "Methods this endpoint accepts.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `code` is `rate_limited`. `Retry-After` gives the seconds to wait, and the `RateLimit` field repeats it as the effective window.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "Live remainder, e.g. `\"default\";r=0;t=25`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The quota, e.g. `\"default\";q=100;w=60`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/agents": {
      "get": {
        "tags": [
          "agents"
        ],
        "deprecated": false,
        "operationId": "getAgents",
        "summary": "The twelve agents and their live state.",
        "description": "The agents carry the names of well known AI models. They are strategy\npersonas, not those models: all twelve run one mean reversion strategy and\ndiffer only in four parameters. `sharpe_measurable` and\n`max_drawdown_measurable` say whether the corresponding figure was\ncomputed at all, and a false there means the number is absent rather than\nzero.\n\nReturns `{\"status\":\"reconnecting\",\"agents\":[]}` at HTTP 200 when the\nengine is unreachable.",
        "responses": {
          "200": {
            "description": "Agent rows, or the reconnecting sentinel.",
            "headers": {
              "API-Version": {
                "description": "Always `1`. A breaking change ships at a new path, never in place.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "Deprecation": {
                "description": "RFC 9745. Present only on a deprecated endpoint, giving the date it became deprecated. Absent today.",
                "required": false,
                "schema": {
                  "type": "string"
                }
              },
              "Sunset": {
                "description": "RFC 8594. Present only on a deprecated endpoint, giving the date it stops answering. At least 180 days after Deprecation. Absent today.",
                "required": false,
                "schema": {
                  "type": "string"
                }
              },
              "Link": {
                "description": "Present only on a deprecated endpoint, carrying rel=\"successor-version\" pointing at the replacement. Absent today.",
                "required": false,
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Agent"
                      }
                    },
                    {
                      "$ref": "#/components/schemas/Reconnecting"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "A parameter was rejected. `code` is `invalid_parameter`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No endpoint at this path. `code` is `endpoint_not_found`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "This API is read only. `code` is `method_not_allowed`, and the `Allow` header lists what is accepted.",
            "headers": {
              "Allow": {
                "description": "Methods this endpoint accepts.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `code` is `rate_limited`. `Retry-After` gives the seconds to wait, and the `RateLimit` field repeats it as the effective window.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "Live remainder, e.g. `\"default\";r=0;t=25`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The quota, e.g. `\"default\";q=100;w=60`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/research/cost-decomposition": {
      "get": {
        "tags": [
          "tape"
        ],
        "deprecated": false,
        "operationId": "getCostDecomposition",
        "summary": "The whole season decomposed into gross result, fee, slippage and funding.",
        "description": "The same arithmetic the home page and /research render, over the same\ntape, so a reader of the page and a reader of this endpoint cannot be\nlooking at different numbers.\n\nRead `integrity` before the figures. `totalsFullyVerified` is false when\nrows could not be reconciled to within `toleranceUsd`, and\n`unverifiableRows` counts rows that carry no cost fields at all.",
        "responses": {
          "200": {
            "description": "The decomposition, with an integrity report beside it.",
            "headers": {
              "API-Version": {
                "description": "Always `1`. A breaking change ships at a new path, never in place.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "Deprecation": {
                "description": "RFC 9745. Present only on a deprecated endpoint, giving the date it became deprecated. Absent today.",
                "required": false,
                "schema": {
                  "type": "string"
                }
              },
              "Sunset": {
                "description": "RFC 8594. Present only on a deprecated endpoint, giving the date it stops answering. At least 180 days after Deprecation. Absent today.",
                "required": false,
                "schema": {
                  "type": "string"
                }
              },
              "Link": {
                "description": "Present only on a deprecated endpoint, carrying rel=\"successor-version\" pointing at the replacement. Absent today.",
                "required": false,
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CostDecompositionEnvelope"
                }
              }
            }
          },
          "400": {
            "description": "A parameter was rejected. `code` is `invalid_parameter`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No endpoint at this path. `code` is `endpoint_not_found`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "This API is read only. `code` is `method_not_allowed`, and the `Allow` header lists what is accepted.",
            "headers": {
              "Allow": {
                "description": "Methods this endpoint accepts.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `code` is `rate_limited`. `Retry-After` gives the seconds to wait, and the `RateLimit` field repeats it as the effective window.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "Live remainder, e.g. `\"default\";r=0;t=25`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The quota, e.g. `\"default\";q=100;w=60`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/levels": {
      "get": {
        "tags": [
          "market"
        ],
        "deprecated": false,
        "operationId": "getLevels",
        "summary": "Derived price levels for one symbol and timeframe.",
        "description": "Prior extremes, session opens and the current ATR, each carrying the\nmethod that produced it. `method` is part of the payload on purpose: a\nlevel with no stated lookback is an assertion rather than a measurement.",
        "parameters": [
          {
            "name": "symbol",
            "in": "query",
            "required": false,
            "description": "Symbol id. Defaults to BTC.",
            "schema": {
              "type": "string",
              "default": "BTC"
            }
          },
          {
            "name": "tf",
            "in": "query",
            "required": false,
            "description": "Timeframe. Defaults to 1h.",
            "schema": {
              "type": "string",
              "default": "1h"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The level set for the requested symbol and timeframe.",
            "headers": {
              "API-Version": {
                "description": "Always `1`. A breaking change ships at a new path, never in place.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              },
              "Deprecation": {
                "description": "RFC 9745. Present only on a deprecated endpoint, giving the date it became deprecated. Absent today.",
                "required": false,
                "schema": {
                  "type": "string"
                }
              },
              "Sunset": {
                "description": "RFC 8594. Present only on a deprecated endpoint, giving the date it stops answering. At least 180 days after Deprecation. Absent today.",
                "required": false,
                "schema": {
                  "type": "string"
                }
              },
              "Link": {
                "description": "Present only on a deprecated endpoint, carrying rel=\"successor-version\" pointing at the replacement. Absent today.",
                "required": false,
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Levels"
                }
              }
            }
          },
          "400": {
            "description": "Unsupported symbol or timeframe. An RFC 9457 problem document with `code` = `invalid_parameter`. It additionally carries `ok: false` and `error`, kept because two components in this app already branch on them; new callers should use `code`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Problem"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean",
                          "const": false
                        },
                        "error": {
                          "type": "string",
                          "description": "Legacy message. Prefer `code`."
                        },
                        "parameter": {
                          "type": "string",
                          "enum": [
                            "symbol",
                            "tf"
                          ]
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "No endpoint at this path. `code` is `endpoint_not_found`.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "This API is read only. `code` is `method_not_allowed`, and the `Allow` header lists what is accepted.",
            "headers": {
              "Allow": {
                "description": "Methods this endpoint accepts.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. `code` is `rate_limited`. `Retry-After` gives the seconds to wait, and the `RateLimit` field repeats it as the effective window.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "Live remainder, e.g. `\"default\";r=0;t=25`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The quota, e.g. `\"default\";q=100;w=60`.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "An RFC 9457 problem document, served as application/problem+json on every 4xx from this API. Branch on `code`, which is a stable machine readable string, never on `title` or `detail`, which are prose and may be reworded. `hint` says what to do instead, because a bare status code tells a caller that it failed and not how to succeed.",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "Dereferenceable: it resolves to the documentation for this error."
          },
          "title": {
            "type": "string",
            "description": "Short and identical for every occurrence of a given code."
          },
          "status": {
            "type": "integer",
            "description": "Repeats the HTTP status."
          },
          "detail": {
            "type": "string",
            "description": "What went wrong on this request. May name the offending value."
          },
          "code": {
            "type": "string",
            "description": "Stable machine readable code. Values may be added, never renamed or repurposed.",
            "enum": [
              "endpoint_not_found",
              "method_not_allowed",
              "invalid_parameter",
              "upstream_unavailable",
              "rate_limited"
            ]
          },
          "hint": {
            "type": "string",
            "description": "What to do instead."
          },
          "instance": {
            "type": "string",
            "description": "The path that produced it."
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "detail",
          "code",
          "hint"
        ]
      },
      "Reconnecting": {
        "type": "object",
        "description": "Returned at HTTP 200 when the engine is unreachable. Check for this shape before treating a 200 as data.",
        "properties": {
          "status": {
            "type": "string",
            "const": "reconnecting"
          },
          "trades": {
            "type": "array",
            "items": {}
          },
          "agents": {
            "type": "array",
            "items": {}
          }
        },
        "required": [
          "status"
        ]
      },
      "Trade": {
        "type": "object",
        "description": "One side of a trade. Entries and exits are separate rows.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Monotonic with timestamp, so usable as a cursor."
          },
          "agent_id": {
            "type": "string"
          },
          "agent_name": {
            "type": "string"
          },
          "symbol": {
            "type": "string"
          },
          "action": {
            "type": "string",
            "enum": [
              "BUY",
              "SELL"
            ],
            "description": "Direction of this fill, NOT entry versus exit. Closing a short is a BUY."
          },
          "amount": {
            "type": "number",
            "description": "USD notional, not units."
          },
          "price": {
            "type": "number"
          },
          "pnl": {
            "type": [
              "number",
              "null"
            ],
            "description": "Net of fee, slippage and funding. Present on exit rows."
          },
          "reasoning": {
            "type": "string",
            "description": "Free text. An exit row is prefixed with 'CLOSE '."
          },
          "timestamp": {
            "type": "string",
            "description": "UTC, 'YYYY-MM-DD HH:MM:SS'."
          },
          "gross_pnl": {
            "type": [
              "number",
              "null"
            ],
            "description": "Before costs. Exit rows from 2026-06-26."
          },
          "fee": {
            "type": [
              "number",
              "null"
            ]
          },
          "slippage": {
            "type": [
              "number",
              "null"
            ]
          },
          "funding": {
            "type": [
              "number",
              "null"
            ]
          },
          "order_type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "taker",
              "maker",
              null
            ]
          },
          "entry_ts": {
            "type": [
              "string",
              "null"
            ],
            "description": "Entry time of the position this row closes. Captured at a second clock, so it can sit one second off the matching entry row's timestamp."
          },
          "exit_ts": {
            "type": [
              "string",
              "null"
            ]
          },
          "exit_reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "tp",
              "sl",
              "timeout",
              null
            ]
          }
        },
        "required": [
          "id",
          "agent_id",
          "symbol",
          "action",
          "amount",
          "price",
          "timestamp"
        ]
      },
      "Agent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "agent_type": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "portfolio_value": {
            "type": "number"
          },
          "profit_loss": {
            "type": "number"
          },
          "profit_loss_pct": {
            "type": "number"
          },
          "total_trades": {
            "type": "integer"
          },
          "winning_trades": {
            "type": "integer"
          },
          "win_rate": {
            "type": "number",
            "description": "Fraction in [0,1], not a percentage."
          },
          "sharpe_ratio": {
            "type": [
              "number",
              "null"
            ]
          },
          "sharpe_measurable": {
            "type": "boolean",
            "description": "False means Sharpe was never computed. Do not render the value as 0."
          },
          "max_drawdown": {
            "type": [
              "number",
              "null"
            ]
          },
          "max_drawdown_measurable": {
            "type": "boolean"
          },
          "strategy": {
            "type": "string",
            "description": "A persona label, not a distinct strategy class."
          },
          "current_generation": {
            "type": "integer"
          },
          "current_season": {
            "type": "integer"
          }
        },
        "required": [
          "id",
          "name",
          "status"
        ]
      },
      "CostDecompositionEnvelope": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "asOf": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "integrity": {
            "type": "object",
            "properties": {
              "totalsFullyVerified": {
                "type": "boolean"
              },
              "unreconciledRows": {
                "type": "integer"
              },
              "unverifiableRows": {
                "type": "integer"
              },
              "closedRowsExamined": {
                "type": "integer"
              },
              "toleranceUsd": {
                "type": "number"
              },
              "tapeLimit": {
                "type": "integer"
              }
            }
          },
          "data": {
            "type": "object",
            "properties": {
              "tapeRows": {
                "type": "integer"
              },
              "closes": {
                "type": "integer"
              },
              "gross": {
                "type": "number",
                "description": "Before costs. Can be negative."
              },
              "fee": {
                "type": "number"
              },
              "slippage": {
                "type": "number"
              },
              "funding": {
                "type": "number"
              },
              "costs": {
                "type": "number"
              },
              "net": {
                "type": "number",
                "description": "gross minus costs. Can be negative."
              },
              "notional": {
                "type": "number"
              },
              "winRateBeforeCosts": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Percent."
              },
              "winRateAfterCosts": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Percent."
              },
              "flipped": {
                "type": "integer",
                "description": "Trades that won before costs and lost after them."
              },
              "medianHoldMinutes": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "roundTripBps": {
                "type": "number"
              },
              "reconciles": {
                "type": "boolean"
              }
            }
          }
        },
        "required": [
          "ok"
        ]
      },
      "Levels": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "symbol": {
            "type": "string"
          },
          "timeframe": {
            "type": "string"
          },
          "lastClose": {
            "type": [
              "number",
              "null"
            ]
          },
          "atr": {
            "type": [
              "number",
              "null"
            ]
          },
          "levels": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "price": {
                  "type": "number"
                },
                "kind": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "method": {
                  "type": "string",
                  "description": "The lookback that produced the level, stated on the level itself."
                },
                "distanceAtr": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "state": {
                  "type": "string"
                }
              }
            }
          }
        },
        "required": [
          "ok",
          "symbol",
          "timeframe"
        ]
      }
    }
  }
}