---
title: "Get Track Record"
method: GET
path: "/api/backtest/track-record"
tags: ["backtest"]
---

# Get Track Record

`GET /api/backtest/track-record`

## Request

- Base URL: `https://api.equity-rank.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.equity-rank.com/api/backtest/track-record`
- Auth: none declared

Published, point-in-time track record (AWL-08).

Public — no auth. Built from two real sources and nothing else:
  - hit_rates: methodology._fetch_realized_outcomes over
    signal_outcomes, every bucket carrying n / ticker_count / window /
    horizon; a bucket below the honest minimum is emitted with
    sufficient=False and hit_rate=None, never a borrowed number.
  - backtest: this file's own /summary simulation result, with its
    compliance caveats passed through verbatim.

Degrades to `degraded=True` + empty hit_rates on any Supabase failure —
never falls through to a synthetic value.

## Response `200`

Successful Response

- TrackRecordResponse: Response model for GET /api/backtest/track-record. `total_realized_outcomes` / `total_ticker_count` are the aggregate live counts (across every evaluated signal_outcomes row, not just the published buckets) — the exact numbers the UI-SPEC's mandatory insufficient-data copy substitutes into "{n} realized outcomes across {ticker_count} tickers". Kept as explicit numeric fields (not just prose inside `limitations`) so the frontend never has to parse text to render the mandated string.
  - `live_tracking_started` string, required
  - `hit_rates` TrackRecordHitRate[]
    - `signal` string, required
    - `horizon_days` integer, required
    - `n` integer, required
    - `ticker_count` integer, required
    - `window_start` string, required
    - `window_end` string, required
    - `hit_rate` number, nullable
    - `base_rate` number, nullable
    - `excess_hit_rate` number, nullable
    - `excess_n` integer
    - `excess_ci_low` number, nullable
    - `excess_ci_high` number, nullable
    - `sufficient` boolean, required
  - `backtest` TrackRecordBacktestBlock, required: The existing /backtest simulation, passed through with its compliance caveats verbatim (byte-identical to what /summary serves).
    - `sufficient_data` boolean, required
    - `real_data_since` string, nullable
    - `period_start` string, nullable
    - `period_end` string, nullable
    - `n_rebalances` integer
    - `max_drawdown_suppressed` boolean
    - `coverage` CoverageBlock: MDD-01 (2026-10-09): per-step coverage floor and what it removed. A step is measured only when both of its monthly snapshots hold at least `min_snapshot_universe` distinct scored symbols AND at least max(`min_basket_names`, `min_priced_fraction` x basket) of the basket is priced at both ends. Any other step abstains: it is absent from the curves and from every figure (never published as a flat 0.00% month) and is listed in `abstained`. `measured_steps_contiguous` is False when an abstained step sits BETWEEN measured ones, i.e. the curve joins non-adjacent periods.
      - `min_snapshot_universe` integer
      - `min_priced_fraction` number
      - `min_basket_names` integer
      - `steps_attempted` integer
      - `steps_measured` integer
      - `steps_abstained` integer
      - `measured_steps_contiguous` boolean
      - `abstained` AbstainedStep[]
        - `entry_date` string, required
        - `exit_date` string, required
        - `reason` string, nullable
        - `entry_universe` integer
        - `exit_universe` integer
        - `basket_size` integer
        - `priced` integer
    - `strategy` BacktestMetricsBlock: Headline metrics — None when sufficient_data=False. P0-3: cagr / sharpe / alpha_vs_spy are additionally None until n_rebalances >= 24 (annualizing a ~4-month window is statistically meaningless — df=3). MDD-01 (2026-10-09): max_drawdown is gated the same way (a sample maximum is most biased on a short panel); the response's `max_drawdown_suppressed` says so. `alpha_vs_spy` is a CAGR DIFFERENCE, not risk-adjusted alpha; surfaces must label it "CAGR gap vs SPY".
      - `cagr` number, nullable
      - `sharpe` number, nullable
      - `max_drawdown` number, nullable
      - `total_return` number, nullable
      - `alpha_vs_spy` number, nullable
      - `alpha_vs_qqq` number, nullable
    - `spy` BenchmarkBlock: Per-benchmark figures over the SAME measured steps as the strategy. MDD-01e (2026-10-09): `max_drawdown` is published exactly when the strategy's is (n_rebalances >= 24 measured steps) so the strategy drawdown is never shown without its comparator; None otherwise, and None for SPY when the feed degraded.
      - `total_return` number, nullable
      - `max_drawdown` number, nullable
    - `qqq` BenchmarkBlock: Per-benchmark figures over the SAME measured steps as the strategy. MDD-01e (2026-10-09): `max_drawdown` is published exactly when the strategy's is (n_rebalances >= 24 measured steps) so the strategy drawdown is never shown without its comparator; None otherwise, and None for SPY when the feed degraded.
      - `total_return` number, nullable
      - `max_drawdown` number, nullable
    - `validation` ValidationGateBlock: D3 / VALGATE hard gates (item 37, .planning/PRICE-PREDICTION-TIMING-PLAN-2026-09-03.md). Deflated Sharpe Ratio + Probability of Backtest Overfitting (Bailey & López de Prado 2014 — computed once in app/services/factor_ic_significance.py and never re-derived here) applied to the persisted strategy result. `dsr`/`pbo`/`passes_gates` are all None and `validation_status` stays 'not_evaluated' until the annualized Sharpe itself is populated (fewer than MIN_REBALANCES_FOR_ANNUALIZED periods — nothing to deflate yet). `passes_gates` is true only when dsr >= 0.95 AND, where PBO is applicable, pbo < 0.35; a False or None value here means this result must NOT be read as validated, regardless of how the headline metrics look. `pbo_applicable` (2026-10-08): PBO measures selection among >= 2 candidate configurations. This strategy is one fixed configuration, so `pbo` is null and `pbo_applicable` is false: no overfitting-of-selection test was run, and the gate rests on the DSR alone. It is never published as 0.0. `pbo_n_candidates` is the number of configurations compared. `dsr` IS A PROBABILITY IN [0, 1] AS OF 2026-10-07, NOT A RATIO. It estimates P(true Sharpe > the Sharpe the best of n_trials null configurations would be expected to show by luck), so 0.95 is a one-sided 95% confidence level. The field previously carried a ratio-valued statistic whose documented 1.5 threshold was unreachable at any trial count of 4 or more (DSR-01); it was never non-null on this surface, because the live result has had fewer than MIN_REBALANCES_FOR_ANNUALIZED rebalances throughout, so no published value changed. Both fields stay None rather than 0.0 so "not measurable" can never read as "measured".
      - `dsr` number, nullable
      - `pbo` number, nullable
      - `pbo_applicable` boolean, nullable
      - `pbo_n_candidates` integer, nullable
      - `passes_gates` boolean, nullable
      - `validation_status` string
    - `caveats` BacktestComplianceBlock: Compliance copy surfaced to the frontend (BACK-05). All language locks to "historical model performance" framing and explicitly negates "forecast" / "recommendation" / "guarantee".
      - `framing` string
      - `look_ahead_controls` string
      - `live_history_disclaimer` string
      - `survivorship_bias_note` string
  - `limitations` string[]
  - `total_realized_outcomes` integer
  - `total_ticker_count` integer
  - `degraded` boolean

---

[API](https://skmtc.dev/equity-rank/apis/equity-rank-api.md) · [All operations](https://skmtc.dev/equity-rank/apis/equity-rank-api/llms.txt) · [OpenAPI document](https://skmtc.dev/equity-rank/apis/equity-rank-api/revisions/c2f2463fee33?raw)
