Equity Rank Backtest API

The backtest API from Equity Rank — 2 operation(s) for backtest.

GitHub OpenAPI

Operations 2

GET /api/backtest/summary Get Backtest Summary #
GET /api/backtest/track-record Get Track Record #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/equity-rank-com:equity-rank-com-backtest-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

equity-rank-com-backtest-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Equity Rank Com Backtest API
  version: 0.1.0
  description: 'Operations tagged backtest across 2 of this provider''s published API definitions: openapi.json, equity-rank-com-openapi.yml. Each path carries the servers of the definition it was published in.'
tags:
- name: backtest
paths:
  /api/backtest/summary:
    get:
      tags:
      - backtest
      summary: Get Backtest Summary
      description: 'Return the latest BacktestRunner result.


        Public — no auth. The runner self-caches for 6 hours; the response

        is built from whatever the runner returns (including the empty-shell

        `sufficient_data=False` result when there''s < 30 valuation_history rows).'
      operationId: get_backtest_summary_api_backtest_summary_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/app__routers__backtest__BacktestSummaryResponse'
  /api/backtest/track-record:
    get:
      tags:
      - backtest
      summary: Get Track Record
      description: '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.'
      operationId: get_track_record_api_backtest_track_record_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrackRecordResponse'
components:
  schemas:
    BenchmarkBlock:
      properties:
        total_return:
          anyOf:
          - type: number
          - type: 'null'
          title: Total Return
        max_drawdown:
          anyOf:
          - type: number
          - type: 'null'
          title: Max Drawdown
      type: object
      title: BenchmarkBlock
      description: '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.'
    TrackRecordBacktestBlock:
      properties:
        sufficient_data:
          type: boolean
          title: Sufficient Data
        real_data_since:
          anyOf:
          - type: string
          - type: 'null'
          title: Real Data Since
        period_start:
          anyOf:
          - type: string
          - type: 'null'
          title: Period Start
        period_end:
          anyOf:
          - type: string
          - type: 'null'
          title: Period End
        n_rebalances:
          type: integer
          title: N Rebalances
          default: 0
        max_drawdown_suppressed:
          type: boolean
          title: Max Drawdown Suppressed
          default: false
        coverage:
          $ref: '#/components/schemas/CoverageBlock'
        strategy:
          $ref: '#/components/schemas/BacktestMetricsBlock'
        spy:
          $ref: '#/components/schemas/BenchmarkBlock'
        qqq:
          $ref: '#/components/schemas/BenchmarkBlock'
        validation:
          $ref: '#/components/schemas/ValidationGateBlock'
        caveats:
          $ref: '#/components/schemas/BacktestComplianceBlock'
      type: object
      required:
      - sufficient_data
      title: TrackRecordBacktestBlock
      description: 'The existing /backtest simulation, passed through with its

        compliance caveats verbatim (byte-identical to what /summary serves).'
    BacktestMetricsBlock:
      properties:
        cagr:
          anyOf:
          - type: number
          - type: 'null'
          title: Cagr
        sharpe:
          anyOf:
          - type: number
          - type: 'null'
          title: Sharpe
        max_drawdown:
          anyOf:
          - type: number
          - type: 'null'
          title: Max Drawdown
        total_return:
          anyOf:
          - type: number
          - type: 'null'
          title: Total Return
        alpha_vs_spy:
          anyOf:
          - type: number
          - type: 'null'
          title: Alpha Vs Spy
        alpha_vs_qqq:
          anyOf:
          - type: number
          - type: 'null'
          title: Alpha Vs Qqq
      type: object
      title: BacktestMetricsBlock
      description: '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".'
    CoverageBlock:
      properties:
        min_snapshot_universe:
          type: integer
          title: Min Snapshot Universe
          default: 0
        min_priced_fraction:
          type: number
          title: Min Priced Fraction
          default: 0.0
        min_basket_names:
          type: integer
          title: Min Basket Names
          default: 0
        steps_attempted:
          type: integer
          title: Steps Attempted
          default: 0
        steps_measured:
          type: integer
          title: Steps Measured
          default: 0
        steps_abstained:
          type: integer
          title: Steps Abstained
          default: 0
        measured_steps_contiguous:
          type: boolean
          title: Measured Steps Contiguous
          default: true
        abstained:
          items:
            $ref: '#/components/schemas/AbstainedStep'
          type: array
          title: Abstained
      type: object
      title: CoverageBlock
      description: '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.'
    ValidationGateBlock:
      properties:
        dsr:
          anyOf:
          - type: number
          - type: 'null'
          title: Dsr
        pbo:
          anyOf:
          - type: number
          - type: 'null'
          title: Pbo
        pbo_applicable:
          anyOf:
          - type: boolean
          - type: 'null'
          title: Pbo Applicable
        pbo_n_candidates:
          anyOf:
          - type: integer
          - type: 'null'
          title: Pbo N Candidates
        passes_gates:
          anyOf:
          - type: boolean
          - type: 'null'
          title: Passes Gates
        validation_status:
          type: string
          title: Validation Status
          default: not_evaluated
      type: object
      title: ValidationGateBlock
      description: '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".'
    app__routers__backtest__BacktestSummaryResponse:
      properties:
        schema_version:
          type: string
          title: Schema Version
          default: '2.0'
        sufficient_data:
          type: boolean
          title: Sufficient Data
        real_data_since:
          anyOf:
          - type: string
          - type: 'null'
          title: Real Data Since
        period_start:
          anyOf:
          - type: string
          - type: 'null'
          title: Period Start
        period_end:
          anyOf:
          - type: string
          - type: 'null'
          title: Period End
        n_rebalances:
          type: integer
          title: N Rebalances
          default: 0
        annualized_metrics_suppressed:
          type: boolean
          title: Annualized Metrics Suppressed
          default: false
        max_drawdown_suppressed:
          type: boolean
          title: Max Drawdown Suppressed
          default: false
        benchmark_degraded:
          type: boolean
          title: Benchmark Degraded
          default: false
        price_basis:
          anyOf:
          - type: string
          - type: 'null'
          title: Price Basis
        coverage:
          $ref: '#/components/schemas/CoverageBlock'
        equity_curve:
          $ref: '#/components/schemas/EquityCurveBlock'
        strategy:
          $ref: '#/components/schemas/BacktestMetricsBlock'
        spy:
          $ref: '#/components/schemas/BenchmarkBlock'
        qqq:
          $ref: '#/components/schemas/BenchmarkBlock'
        universe:
          $ref: '#/components/schemas/BenchmarkBlock'
        validation:
          $ref: '#/components/schemas/ValidationGateBlock'
        compliance:
          $ref: '#/components/schemas/BacktestComplianceBlock'
      type: object
      required:
      - sufficient_data
      title: BacktestSummaryResponse
      description: Response model for GET /api/backtest/summary.
    TrackRecordResponse:
      properties:
        live_tracking_started:
          type: string
          title: Live Tracking Started
        hit_rates:
          items:
            $ref: '#/components/schemas/TrackRecordHitRate'
          type: array
          title: Hit Rates
        backtest:
          $ref: '#/components/schemas/TrackRecordBacktestBlock'
        limitations:
          items:
            type: string
          type: array
          title: Limitations
        total_realized_outcomes:
          type: integer
          title: Total Realized Outcomes
          default: 0
        total_ticker_count:
          type: integer
          title: Total Ticker Count
          default: 0
        degraded:
          type: boolean
          title: Degraded
          default: false
      type: object
      required:
      - live_tracking_started
      - backtest
      title: TrackRecordResponse
      description: '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.'
    BacktestComplianceBlock:
      properties:
        framing:
          type: string
          title: Framing
          default: Historical model performance under stated assumptions. Not a forecast, recommendation, or guarantee of future returns.
        look_ahead_controls:
          type: string
          title: Look Ahead Controls
          default: At each rebalance date the strategy uses ONLY data already in valuation_history as of that date. No forward-looking model values used to make past selections.
        live_history_disclaimer:
          type: string
          title: Live History Disclaimer
          default: Model live since March 2026. Track record covers a limited period. Historical output does not predict future returns.
        survivorship_bias_note:
          type: string
          title: Survivorship Bias Note
          default: 'IMPORTANT — Survivorship bias: this backtest selects from stocks currently in the Equity Rank universe, which excludes companies that were delisted, acquired, or went bankrupt during the historical period. This inflates displayed results by an amount that varies with the window and cannot currently be stated precisely, but which can be large relative to the figures shown — prior internal point-in-time work on an earlier window estimated the effect in the tens of percentage points. A point-in-time universe reconstruction is planned to bound it for the current window. Past model performance is not indicative of future results; provided for informational purposes only.'
      type: object
      title: BacktestComplianceBlock
      description: 'Compliance copy surfaced to the frontend (BACK-05).


        All language locks to "historical model performance" framing and

        explicitly negates "forecast" / "recommendation" / "guarantee".'
    TrackRecordHitRate:
      properties:
        signal:
          type: string
          title: Signal
        horizon_days:
          type: integer
          title: Horizon Days
        n:
          type: integer
          title: N
        ticker_count:
          type: integer
          title: Ticker Count
        window_start:
          type: string
          title: Window Start
        window_end:
          type: string
          title: Window End
        hit_rate:
          anyOf:
          - type: number
          - type: 'null'
          title: Hit Rate
        base_rate:
          anyOf:
          - type: number
          - type: 'null'
          title: Base Rate
        excess_hit_rate:
          anyOf:
          - type: number
          - type: 'null'
          title: Excess Hit Rate
        excess_n:
          type: integer
          title: Excess N
          default: 0
        excess_ci_low:
          anyOf:
          - type: number
          - type: 'null'
          title: Excess Ci Low
        excess_ci_high:
          anyOf:
          - type: number
          - type: 'null'
          title: Excess Ci High
        sufficient:
          type: boolean
          title: Sufficient
      type: object
      required:
      - signal
      - horizon_days
      - n
      - ticker_count
      - window_start
      - window_end
      - sufficient
      title: TrackRecordHitRate
      description: 'One realized-outcome bucket, always carrying its full sample context.


        `hit_rate` is None (never a synthetic or borrowed number) whenever

        `sufficient` is False — i.e. the bucket has fewer than the honest

        minimum (`_OUTCOME_BUCKET_MIN_N`) evaluated outcomes.'
    EquityCurveBlock:
      properties:
        dates:
          items:
            type: string
          type: array
          title: Dates
        strategy:
          items:
            type: number
          type: array
          title: Strategy
        spy:
          items:
            type: number
          type: array
          title: Spy
        qqq:
          items:
            type: number
          type: array
          title: Qqq
        universe:
          items:
            type: number
          type: array
          title: Universe
      type: object
      title: EquityCurveBlock
      description: 'Per-series equity curve segment — normalized to 100.0 at index 0.


        P0-3 (2026-08-03): `universe` is the equal-weight mean of every scored

        symbol over the same rebalance dates — the like-for-like comparator.

        `qqq` is retired (always empty, kept for response-shape compatibility).

        An empty `spy` with `benchmark_degraded=True` on the response means the

        external feed failed — never rendered as a flat 0% line.'
    AbstainedStep:
      properties:
        entry_date:
          type: string
          title: Entry Date
        exit_date:
          type: string
          title: Exit Date
        reason:
          anyOf:
          - type: string
          - type: 'null'
          title: Reason
        entry_universe:
          type: integer
          title: Entry Universe
          default: 0
        exit_universe:
          type: integer
          title: Exit Universe
          default: 0
        basket_size:
          type: integer
          title: Basket Size
          default: 0
        priced:
          type: integer
          title: Priced
          default: 0
      type: object
      required:
      - entry_date
      - exit_date
      title: AbstainedStep
      description: One rebalance step left out of every curve and figure (MDD-01a/b).
  securitySchemes:
    HTTPBearer:
      type: http
      scheme: bearer
x-refined-from:
- openapi.json
- equity-rank-com-openapi.yml