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:
market_healthis the dual score:long_term_score,short_term_score,total_score, each with a sentiment label and thecomponentsbehind it.technical_regimeis universe breadth (above_200ma,in_squeeze,near_high,overbought_1dand so on) plus the full per-symbolby_symbolmap with ma, bollinger, range_state and rsi per coin, frozen point-in-time. The live/dailytrims that map unless you passtechnical_detail=true; the archive always has it.signum_rggis the RED/GREY/GREEN trend radar with its ownby_symbolmap (colour, signed score, ADX/DI,days_in_color,flipped_at, price,hl_symboljoin key), stored since 2026-03-02. If that block is all you need,/api/v1/backtesting/signum-rgg?date=returns just it.
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:
| Layer | Route | Cadence | Shape | Tier |
|---|---|---|---|---|
| Daily snapshot | /backtesting/daily-snapshots/{date} | 1 per UTC day, 20:00 UTC | The full /daily payload | Any key |
| Typed snapshots | /backtesting/snapshots?data_type= | Mostly 5-minute | One collector's payload per row | Pro Plus |
| Parquet archives | /backtesting/archives/download | Daily files, monthly deep tiers | Klines, funding, liquidation tapes, snapshot bundles | Pro 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 snapshot for day D is built at 20:00 UTC on D. A strategy that trades at the 00:00 UTC open on D cannot know D's snapshot. Use the snapshot from D-1, or, if you trade after 20:00 UTC, D's own. Pin the decision time explicitly in your code.
- Read
signum_rgg.computed_from. On days archived from 2026-09-09 it names the last completed daily bar the colours were read from, which is the previous UTC close. Days before that were archived with the forming bar included, so their colours carry a few hours of same-day information. Either skip them for a close-driven strategy or accept the bias and say so. - Backfilled fields are neutral or null by design. In
technical_regime.by_symbolthersi.*_1hfields are neutral on backfilled days, and insignum_rggtheintraday_*fields are null on backfilled days. Neutral is not a reading; branch on it. - Do not rebuild a block from live inputs and splice it in. If a day is missing a block, it was not collected. The ETF-flow values archived before 2026-09-25 are a cautionary case: they are point-in-time copies of what was then served, which on most weekdays was a partial sum, and they are deliberately not rewritten. Use
/market-intelligence/etf/{asset}/flows?days=Nfor the settled series and keep the snapshot as the record of what was visible.
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 snapshot | Recomputed from raw inputs | |
|---|---|---|
| Lookahead | None, if you respect the 20:00 UTC build time | Whatever your inputs leak (revisions, repaired bars) |
| Code drift | Frozen: each day used that day's code | Today's code over all history |
| Depth | From each block's start date | As deep as the raw inputs |
| Resolution | Daily; 5-min via typed snapshots | Anything |
| Consistency with live | Exact, it was live | Approximate |
| Gaps | A missing block is missing | Hidden 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.



