Backtesting Regime Signals Means Freezing What the Market Could See

If you want an API to backtest crypto strategies with historical daily snapshots, the question underneath is: on a given past day, what did the market-structure signals actually say at the time? Not what a recomputed series says now, after a data vendor revised its numbers or your indicator code changed.

Candles do not have this problem. A closed 1-minute bar is final. Composite signals do: a health score, a regime label, a breadth count, an ETF flow total, a stablecoin reserve figure. Every one of them is computed from inputs that can be revised, and most are computed by code that evolves. Recompute them from today's inputs with today's code and you have built a series the market never saw.

The daily snapshot archive exists to avoid that. Once a day the API freezes its own /api/v1/daily payload to disk and never rewrites it. This post covers what one snapshot holds, how far back the archive reaches, how it fits with the 5-minute snapshot series and the Parquet archives, and the lookahead rules you have to respect when you read it.

What One Daily Snapshot Holds

Two routes serve the archive, and both answer any valid key, Free included:

curl "https://cryptodataapi.com/api/v1/backtesting/daily-snapshots?start=2026-08-01&end=2026-08-31" \
  -H "X-API-Key: cdk_live_your_key"

curl "https://cryptodataapi.com/api/v1/backtesting/daily-snapshots/2026-08-26" \
  -H "X-API-Key: cdk_live_your_key"

The first returns {count, dates}, a list of YYYY-MM-DD UTC dates with a snapshot, bounded by optional inclusive start and end. The second returns {date, snapshot}, where snapshot is identical in shape to what /api/v1/daily served that day. A real archived day in late August 2026 carried these top-level blocks:

btc_derivatives, coins, coinmetrics, dex_trending, event_regime, exchange_flows,
fear_greed, hash_ribbon, liquidity_depth, liquidity_regime_score, macro,
market_health, meme_regime, miner_flows, onchain_health_score, policy_regime,
promoted_tokens, security_regime, signum_rgg, stablecoin_reserves, stablecoins,
technical_regime, volatility_regime, snapshot_built_at

Three of those are the reason people backtest from this archive rather than from raw feeds:

The set of blocks grows over time as collectors are added, so an older snapshot may lack a block a newer one has. Treat a missing key as "not collected yet on that date", never as zero.

How Far Back Does the Daily Snapshot Archive Go?

Snapshots are written once a day at 20:00 UTC (and once on server startup), one JSON file per UTC day. The last 30 days sit on local disk. Older days are mirrored to object storage and the /daily-snapshots/{date} route reads them from there transparently, so the date list is the whole archive, not a rolling month. The cost is latency: a mirrored day is a remote fetch of roughly a megabyte, so do not fan out 90 parallel requests for 90 days. Walk the list sequentially and cache what you read.

Asking for a day that has no snapshot yet is a 404, and when that day is today the 404 carries a Retry-After header with the seconds until the 20:00 UTC build. Honour it. One consumer was observed retrying today's date about 1,900 times in two hours.

The archive is one of three history layers, and choosing the right one is most of the work:

LayerRouteCadenceShapeTier
Daily snapshot/backtesting/daily-snapshots/{date}1 per UTC day, 20:00 UTCThe full /daily payloadAny key
Typed snapshots/backtesting/snapshots?data_type=Mostly 5-minuteOne collector's payload per rowPro Plus
Parquet archives/backtesting/archives/downloadDaily files, monthly deep tiersKlines, funding, liquidation tapes, snapshot bundlesPro Plus, or x402 per file

The typed series is where intraday resolution lives. /api/v1/backtesting/snapshots/types returns every data_type with row counts and coverage ranges; examples are market_health, fear_greed, gamma_exposure, liquidation_map, positioning, whale_activity, hl_l2_books and token_unlocks. The reader takes a required data_type and start, optional end and limit (default 100, max 1000), and streams rows so a large pull starts returning immediately. Several of those types are forward-only: positioning and whale_activity record per-account state that exists nowhere else, so nothing before their start date can ever be reconstructed.

For bulk multi-day history, skip paging altogether. /api/v1/backtesting/archives?data_type=snapshots lists the pre-built files and /archives/download returns pre-signed links. The same endpoint family covers klines, funding, funding_hourly, liquidations, hl_liquidations, hl_trade_flow and news_events, plus the deep tiers: klines_deep (Binance spot back to each market's listing, BTCUSDT to 2017; Hyperliquid 1d to 2023) and funding_deep. Call /archives/index once to see what exists before you page anything. Without a Pro Plus key, /archives/purchase sells one file for one USDC payment over x402; a missing file is a 404 before any quote, so you cannot pay for a file that is not there.

Regime History That Was Never Relabelled

For regime-conditioned backtests the daily snapshots are the point-in-time reference, but they only reach back to when each block started. The Hidden Markov Model regime engine has a longer, differently constructed history:

curl "https://cryptodataapi.com/api/v1/quant/regimes/history" \
  -H "X-API-Key: cdk_live_your_key"

That returns a pre-signed URL (1 hour expiry) to a Parquet file with one hourly row per bar from 2020 to yesterday, market scope. Each row carries the full probability distribution over the 6 regimes at three horizons (p_now_0..5, p_4h_0..5, p_24h_0..5, keyed by regime_map), plus regime_id, regime_label, regime_conf, candles_in_regime, model_version and a source column that separates the deep Binance-archive segment from the live Hyperliquid tail. It is rebuilt daily at 02:35 UTC and is Pro Plus.

The no-hindsight property is specific. The deep segment is labelled by the same model that serves live, run forward over the public archive it trained on; no state was relabelled afterwards. The p_4h and p_24h columns are exact transition-matrix projections of the current posterior, not realised outcomes, so they are forecasts you can score, not answers leaked from the future. The daily-label companion is /api/v1/quant/timeline, described in the no-hindsight timeline post, and the Parquet itself is covered in the 6-regime history download post.

One history you cannot get is a backfilled sequence of the long-horizon /regimes id. That id is a stateful commit through a dwell gate, not a pure function of the inputs, so replaying old signals would invent transitions that were never served. /api/v1/regimes/history serves the committed sequence from the archive, with coverage starting 2026-08-07 and coverage_start reported honestly. Nothing earlier will exist.

The No-Lookahead Discipline: Four Rules

The archive gives you point-in-time data. It cannot stop you from reading it with hindsight. These are the rules that matter in practice.

A minimal loop that respects the first rule:

import httpx

H = {"X-API-Key": "cdk_live_your_key"}
B = "https://cryptodataapi.com/api/v1/backtesting"
dates = httpx.get(f"{B}/daily-snapshots", params={"start": "2026-06-01"}, headers=H).json()["dates"]

prev = None
for d in dates:
    snap = httpx.get(f"{B}/daily-snapshots/{d}", headers=H, timeout=60).json()["snapshot"]
    if prev is not None:
        # decide at 00:00 UTC on d using only what was frozen at 20:00 UTC on the day before
        score = prev["market_health"]["total_score"]
        breadth = prev["signum_rgg"]["pct_green"]
        # ... apply your rule here, then evaluate against d's prices
    prev = snap

Archived Snapshot vs Recomputed Series

If you have the raw inputs, recomputing is tempting because it is continuous and cheap. The trade-offs are real on both sides.

Archived daily snapshotRecomputed from raw inputs
LookaheadNone, if you respect the 20:00 UTC build timeWhatever your inputs leak (revisions, repaired bars)
Code driftFrozen: each day used that day's codeToday's code over all history
DepthFrom each block's start dateAs deep as the raw inputs
ResolutionDaily; 5-min via typed snapshotsAnything
Consistency with liveExact, it was liveApproximate
GapsA missing block is missingHidden by interpolation unless you check

The practical split: backtest the signal from the archive, because that is the number your bot would have read, and backtest the fill from the Parquet klines and tapes, because those are final by construction. Mixing the two the other way round, recomputed signals against archived prices, is how a strategy ends up with a Sharpe ratio it cannot reproduce in paper trading.

When to Use This (and When Not To)

Use the daily archive to tag every historical day with the market structure a bot would have seen: health score band, regime labels, breadth, fear and greed, liquidity fragility. It is the right tool for daily or slower rebalancing rules, for regime-conditioned position sizing, and for checking whether a live signal's distribution has drifted from its own history.

Use the typed 5-minute snapshots when the decision is intraday and the signal is one of the collector payloads: dealer gamma, whale positioning, the liquidation map, order-book depth. They are Pro Plus, served from the isolated backtesting container with its own per-key rate limit (2 requests per second sustained, burst 20), and the archive files are the cheaper route once you need more than a few days.

Do not use any of it for sub-daily execution modelling. Prices and fills come from /backtesting/klines, the Hyperliquid liquidation tape and the trade-flow bars, not from a snapshot's coins block. Do not treat a block that appears mid-archive as if it had always been there. And do not expect the long-horizon regime id before 2026-08-07, for the reasons above.

The live payload the archive freezes is described in the /daily post. Reading the archive needs nothing more than a free key; the typed series and the Parquet downloads are on Pro Plus, with single files available per call over x402.

Frequently asked questions

What is in a historical daily snapshot?

Everything /api/v1/daily served that day: coin profiles, the dual market health score, derivatives and sentiment blocks, macro, on-chain states, the composite onchain_health_score, the full technical_regime per-symbol map and the signum_rgg trend radar. It is served by GET /api/v1/backtesting/daily-snapshots/{date} with any valid key.

How many days of daily snapshots can I fetch?

The date list from GET /api/v1/backtesting/daily-snapshots covers the whole archive. The last 30 days are on local disk and older days are read from an object-store mirror on demand, so fetch them sequentially rather than in parallel. Individual blocks start on the date their collector shipped; signum_rgg has been stored since 2026-03-02.

When is a day's snapshot available?

At 20:00 UTC on that UTC day, plus once on server startup. Requesting today's date before the build returns a 404 with a Retry-After header giving the seconds until it is written.

How do I avoid lookahead bias with daily snapshots?

Decide using the snapshot built before your decision time, usually the previous day's. Read signum_rgg.computed_from, which from 2026-09-09 names the last completed daily bar the colours came from. Treat neutral rsi.*_1h fields and null intraday_* fields on backfilled days as no reading.

Where do I get intraday or multi-year history for backtests?

GET /api/v1/backtesting/snapshots serves 5-minute typed snapshots (Pro Plus), /backtesting/archives/download serves pre-built Parquet files including klines_deep back to 2017 for BTCUSDT, and /quant/regimes/history returns an hourly 6-regime Parquet from 2020 labelled without hindsight.