GET /api/metrics/regime-transitions Regime Transitions
Returns an event log of trend-regime transitions across the asset population — for each (symbol, transitionDate) pair where a regime change occurred, returns the from and to regime labels plus a snapshot of metric values at the transition. Designed for “which assets entered or left this regime, when, and what did the rest of the metric set look like at the moment they did?”.
Request
# Assets that entered Leading Trend on the 200w-CAPR window in 2024
curl "https://api.bytetree.io/api/metrics/regime-transitions?window=200w-CAPR®ime=Leading%20Trend&from=2024-01-01&to=2024-12-31"
# Any 30d-CAPR transition in the last 90 days for active stocks
curl "https://api.bytetree.io/api/metrics/regime-transitions?window=30d-CAPR&direction=any&from=2026-02-01&type=stock&status=active"
# Custom metric snapshot at transition
curl "https://api.bytetree.io/api/metrics/regime-transitions?window=200w-CAPR®ime=Leading%20Trend&from=2025-01-01&fields=closeUsd,sharpe52w,returnUsd1M"
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
window | string | Yes | Score window. One of 200w-CAPR, 200w-Local, 30d-CAPR, 30d-Local |
regime | string | Yes when direction !== 'any' | Target regime label (e.g. Leading Trend, Emerging Trend, Late Stage, Crash). May be omitted when direction=any |
direction | string | No | One of into, outOf, any (case-insensitive). Default into. any returns every transition regardless of regime |
from | string | No | Inclusive ISO YYYY-MM-DD lower bound on transitionDate. Omitting it applies a 90-day look-back rather than scanning all history — see Default date window |
to | string | No | Inclusive ISO YYYY-MM-DD upper bound on transitionDate |
type | string | No | Asset-type filter — stock, crypto, etf, index, commodity. fx is rejected |
status | string | No | Asset status — active, pending, inactive |
sector | string | No | Sector filter (case-insensitive) |
q | string | No | Free-text symbol/name search |
universe | string | No | Universe slug (e.g. macro, public-50, global-trends-200). Use with dimension and group. Out-of-scope slugs return 403 |
dimension | string | No | Dimension within the universe: region, sector, industry, type, or country |
group | string | No | Group label within the dimension, exact match (paired with universe + dimension) |
fields | string | No | Comma-separated metric keys included in metricsAtTransition. Default: closeUsd, byteTrendCapr200w, byteTrendCapr30d, returnUsd1M, marketCapUsd, sharpe52w |
format | string | No | Response format: json (default), csv, or toon |
Response
| Field | Type | Description |
|---|---|---|
generatedAt | string | ISO timestamp captured when the server ran the aggregation |
regime | string or null | Echo of the resolved target regime. null when direction=any |
window | string | Echo of the resolved window |
direction | string | Echo of the resolved direction |
appliedRange | object | The date window actually queried — see Default date window |
appliedRange.from | string | ISO YYYY-MM-DD lower bound applied, whether you supplied it or it was defaulted |
appliedRange.to | string or null | ISO YYYY-MM-DD upper bound applied, or null when you supplied none. Here null is meaningful rather than missing: it means open-ended to the latest available data, not “defaulted to today” |
appliedRange.defaulted | boolean | true when from was defaulted, false when you supplied it |
count | number | Number of transition rows in data |
data | object[] | One row per transition event |
data[].symbol | string | Asset ticker symbol |
data[].transitionDate | string | ISO YYYY-MM-DD. For raw windows, this IS the confirmation date; see below |
data[].fromRegime | string | Regime label immediately before the transition |
data[].toRegime | string | Regime label after the transition |
data[].metricsAtTransition | object | Snapshot of fields evaluated at transitionDate |
data[].trendAgeAtTransition | number | Confirmed window only. Days the prior regime had been in force at the transition |
data[].confirmedTransitionDate | string | Confirmed window only. Same value as transitionDate — kept for explicitness |
data[].pendingStartDate | string | Confirmed window only. Date the pending transition first appeared. Confirmation lag is the trading-day delta to confirmedTransitionDate |
Two output shapes
window=200w-CAPR(confirmed window) — every row carriestrendAgeAtTransition,confirmedTransitionDate, andpendingStartDate. The 4-trading-day delta betweenpendingStartDateandconfirmedTransitionDateis the confirmation lag built into the long-window CAPR score.window=200w-Local,30d-CAPR,30d-Local(raw windows — no confirmation lag) — those three fields are omitted entirely from each row.transitionDateIS the confirmation date.
{
"generatedAt": "2026-05-07T19:42:00.613Z",
"regime": "Leading Trend",
"window": "200w-CAPR",
"direction": "into",
"appliedRange": { "from": "2024-01-01", "to": "2024-12-31", "defaulted": false },
"count": 160,
"data": [
{
"symbol": "NVDA",
"transitionDate": "2024-03-12",
"fromRegime": "Emerging Trend",
"toRegime": "Leading Trend",
"trendAgeAtTransition": 5,
"confirmedTransitionDate": "2024-03-12",
"pendingStartDate": "2024-03-07",
"metricsAtTransition": {
"closeUsd": 880.21,
"byteTrendCapr200w": 5,
"byteTrendCapr30d": 5,
"returnUsd1M": 0.084,
"marketCapUsd": 2200000000000,
"sharpe52w": 1.42
}
}
]
}
Default date window
A call without from does not scan all history. When from is omitted the server applies a 90-day look-back, anchored on to if you supplied one and on the current date otherwise. An explicit from is always honoured as given — widening the range is opt-in.
Every response reports the window it actually used:
// No dates supplied — 90-day look-back, open-ended at the recent end
"appliedRange": { "from": "2026-04-24", "to": null, "defaulted": true }
// from supplied — honoured as-is
"appliedRange": { "from": "2020-01-01", "to": null, "defaulted": false }
// to supplied without from — look-back anchored on `to`
"appliedRange": { "from": "2023-10-03", "to": "2024-01-01", "defaulted": true }
Two things to read carefully:
defaulted—truemeans you are looking at a 90-day slice, not the whole history. Check it before interpreting a small result set as a genuine absence of transitions.to: null— not an omission. The upper bound reports what you bounded;nullmeans the query ran open-ended to the latest available data. The 90-day look-back is anchored internally on the current date, but that anchor is not reported as atoyou didn’t ask for.
This default matches the equivalent ByteTree MCP tool, so both surfaces bound an unqualified query the same way.
Regime transitions accumulate over decades across the whole population, and an unbounded scan was slow enough to exceed upstream timeouts — returning an unhelpful error instead of data. The default keeps a date-less exploratory call fast and useful.
Bounds
- 5001-row short-circuit. The query is capped at
$limit: 5001; if 5001 rows return the server replies400with a hint to narrow the date range, the asset filter, ordirection. The cap protects both sides —direction=anyover the entire population without filters will reliably trip it. type=fxis rejected with400.- Widen deliberately. An explicit multi-year
fromcombined withdirection=anyand no asset filter is the shape most likely to hit either the row cap or a server-side time limit. Narrow withtype,universe+dimension+group, or a tighter range.
Regime labels
Regime labels are the values of confirmedRegime* / pendingRegime* produced by the scoring engine. See the Latest Metrics field reference for the full label set per window. The MCP bytetree://glossary resource holds authoritative wording sourced from engine doc-strings.
Notes
- Requires an API key.
- Backs the public ByteTree MCP’s
get_regime_transitionstool. - The
metricsAtTransitionblock is a point-in-time snapshot — values are read from the symbol’s metric record ontransitionDate, not recomputed.