Methodology
This page describes the public methodology of the RiskState engine: what inputs drive each decision, how they combine into max_size_fraction, and how the output is anchored for audit. It is not a full specification — specific coefficients, rule thresholds and regime-dependent calibration are deliberately out of scope.
If you want to see the engine produce live output, use the visualizer for a side-by-side read of every input and every cap. Every field below corresponds to a field in the API response.
Current production state (v1.3.0,
score_v3)The engine is in a deliberate two-layer state during the
score_v3freeze window (frozen until 2026-10-22):
max_size_fractionis bound by the legacy 4-cap chain (rules, DeFi, macro, cycle) × quality × volatility.direction_biasis driven by the structural / tactical policy combiner (new in PR2).compositeremains in the response for backwards compatibility and still powers thetactical_statelabel, rules-engine inputs and AI-prompt context.structural_score,tactical_scoreandpolicy_permissionsare the new semantic layer — exposed today, candidates to drive sizing in the next scoring version (earliest 2026-10-22).policy_levelis an informational summary for human reading. The binding constraints are theexposure_policyfields.The sections below describe the sizing motor (caps), the direction motor (combiner) and the legacy scoring layer (composite) in that order.
Core formula
Every response is anchored on a single computation:
max_exposure = min(rules_cap, defi_cap, macro_cap, cycle_cap) × quality_factor × vol_mult
Four independent caps are computed from four independent signal families. The most restrictive one wins — this is the binding constraint surfaced in binding_constraint.source. That base cap is then multiplied by a quality factor (data integrity × signal agreement) and a volatility multiplier. The result is the max_size_fraction field.
Three properties follow from this shape:
- No cap can rescue another. If DeFi health is bad, no amount of bullish composite score can unblock exposure.
- Data quality binds last. Missing core signals collapse
quality_factorand thereforemax_exposure, regardless of the individual caps. - Volatility scales, never inverts.
vol_multcan shrink size in HIGH/EXTREME regimes but does not flip direction.
The four caps
Each cap is a number in [0, 1]. Lower means more restrictive.
rules_cap
Measures how many rules (from the rules engine) are firing and at what severity. The rules engine is a deterministic evaluator over 80+ conditions spanning price action, derivatives, on-chain, macro and cross-asset context. Each fired rule contributes to a count; counts pass through a bounded smoothing function to produce the cap.
What moves it: CRITICAL, DANGER and WARNING rules firing. Examples: RSI at extremes, funding rate percentile at extremes, macro risk-off with high coupling, liquidation cascades, Fed liquidity tightening.
defi_cap
Measures the safety of your on-chain collateral position, when a wallet is provided. Derived from the gap between the current Health Factor and the protocol's liquidation threshold, plus the distance from current LTV to max LTV.
What moves it: Health Factor trending down, LTV rising, collateral price dropping, liquidation price approaching. When no wallet is provided, defi_cap = 1.0 (neutral — not a penalty).
macro_cap
Measures how hostile the macro environment is to risk assets. A 2-dimensional cap computed from macro regime × coupling.
What moves it: Macro regime shifting from RISK-ON → NEUTRAL → RISK-OFF. Coupling increasing (BTC/SPX or ETH/NASDAQ correlation rising toward 1). Yield curve inverting. Real rates rising. Fed balance sheet contracting. DXY strengthening.
cycle_cap
Measures where BTC (or ETH) is in its market cycle. Computed from phase classification + MVRV percentile (BTC) or ETH/BTC ratio + altseason context (ETH).
What moves it: BTC cycle phase (BOTTOM / EARLY / MID / LATE / EUPHORIA / CORRECTION / POST-PEAK / LATE-CORRECTION). ETH phase (DEPRESSED / VALUE_ZONE / RECOVERY / EXTENDED / DISTRIBUTION). In structurally negative regimes, cycle_cap is further reduced by a structural downgrade mechanism (ETH only, v2).
cycle_cap vs structural_score — why both exist
Both read cycle information, but bind different outputs:
cycle_capis a hard sizing cap in the legacy 4-cap chain. One of the four min-inputs tomax_size_fraction. Today, this is the path that actually limits exposure.structural_scoreis a slow-moving 0–100 score that feeds the policy combiner. Its output bindsdirection_bias, not size. It is broader thancycle_cap— it also incorporates Supply, Demand and Macro subfamilies, not just the cycle phase.
They overlap on inputs (both consume the cycle phase) but serve different outputs (size vs direction). Retiring cycle_cap in favour of a structural-score-derived sizing path is a scheduled migration for the next scoring version (earliest 2026-10-22).
Policy engine (5 levels)
After the core formula produces max_size_fraction, that number is classified into one of 5 discrete policy levels. Levels are informational — the binding numbers are max_size_fraction, max_leverage, allowed_actions and blocked_actions. Levels exist so that humans and agents have a shared vocabulary for "how restrictive is the state."
| max_size_fraction | Level | Label | Posture |
|---|---|---|---|
| < 0.15 | 1 | BLOCK Survival | Reduce, hedge only |
| 0.15 – 0.35 | 2 | BLOCK Defensive | Wait, hedge, small scalps only |
| 0.35 – 0.60 | 3 | CAUTIOUS Preservation | DCA, R:R > 2:1 only |
| 0.60 – 0.80 | 4 | GREEN Selective | Long/short with confirmation |
| ≥ 0.80 | 5 | GREEN Expansion | Trend follow, moderate leverage |
At each level, a fixed allow-list / block-list of action tokens is applied to exposure_policy.allowed_actions and exposure_policy.blocked_actions. The full action enum is in the API reference.
Structural vs tactical (v1.3.0)
From API v1.3.0 onward, two parallel scores feed a policy combiner that binds direction_bias and exposes a risk_permission_score. These are the direction motor; the composite (below) is the legacy layer kept for backwards compatibility.
┌─────────────────────┐
slow → │ structural_score │ ─┐
(weeks-months) │ 0–100 │ │
└─────────────────────┘ │
▼
┌──────────────────┐
│ policy combiner │ → risk_permission_score
│ regime-weighted │ → direction_bias
└──────────────────┘ → direction_layer
▲
┌─────────────────────┐ │
fast → │ tactical_score │ ─┘
(24-72h) │ 0–100 │
└─────────────────────┘
Three directional fields, one taxonomy
API responses carry three fields whose names are adjacent but whose roles are distinct:
| Field | Source | Role |
|---|---|---|
tactical_state | Legacy label from composite | Human-readable tilt label (BULLISH / LEAN BULL / NEUTRAL / LEAN BEAR / BEARISH). Kept for backwards compatibility. |
tactical_score | New layer (score_v3) | 0–100 score with label STRONG_BULL / BULL / NEUTRAL / BEAR / STRONG_BEAR. The tactical input to the combiner. |
direction_bias | Policy combiner | Binding directional output (LONG_PREFERRED / NEUTRAL / SHORT_PREFERRED). The field agents should consume. |
direction_bias is the operational output. tactical_score is the signal feeding the combiner. tactical_state is a legacy label that still populates for existing integrators.
Structural score
Slow-moving fundamentals. 4 subfamilies, each 0–100, combined with fixed weights (not published). Cycle carries the largest share, followed jointly by Supply and Demand, with Macro contributing the smallest share.
| Subfamily | What it measures |
|---|---|
| Cycle | Phase + MVRV percentile / ETH-BTC phase + halving age |
| Supply | BTC: supply in profit + LTH supply. ETH: staking ratio + issuance regime. |
| Demand | ETF flows + netflow trend + institutional accumulation |
| Macro | Real rate + liquidity regime + macro regime |
Structural labels: WEAK / FRAGILE / NEUTRAL / STABILIZING / STRONG. Data-quality governance caps the label at NEUTRAL when input quality is below a minimum threshold, to avoid false confidence.
Tactical score
Fast-moving positioning. 6 components, each 0–100, combined with fixed weights (not published). Positioning Pressure carries the largest share, followed by Momentum and Volume/CVD, then Derivatives extremity, and finally L/S velocity and Whale Pressure.
| Component | What it measures |
|---|---|
| Positioning Pressure (PPS) | Squeeze setup: crowding × funding × basis × OI z-score |
| Momentum | RSI zone + 24h change + 1h velocity |
| Volume / CVD | CVD acceleration + volume z-score + buy-ratio |
| Derivatives extremity | Funding percentile + OI z-score direction |
| L/S velocity | Regression slope of L/S ratio history |
| Whale Pressure | Multi-signal whale score (proxy-based) |
Tactical labels: STRONG_BEAR / BEAR / NEUTRAL / BULL / STRONG_BULL.
Policy combiner
Combines structural and tactical using regime-dependent weights. The specific weights per regime are not published, but the posture per regime is:
| Market regime | Posture |
|---|---|
| PANIC | Structural dominates — tactical carries minimal influence on direction. |
| EUPHORIA | Structural dominates — tactical carries minimal influence on direction. |
| SQUEEZE | Structural and tactical contribute roughly equally — confluence required. |
| TREND / RANGE | Structural anchors with material tactical input — the default blend. |
The combiner produces:
risk_permission_score— weighted blend, 0–100direction_bias— tactical-driven (LONG if tactical is in the upper band, SHORT if in the lower band, NEUTRAL otherwise), with structural veto at extremes (very weak structural blocks LONG; very strong structural blocks SHORT)direction_layer— audit trail:tactical/structural_veto/structural_downgrade/legacy_compositereason_codes— machine-parseable tokens explaining the decision
Exact regime-dependent weights, veto thresholds and band cut-offs are not published.
Composite scoring (legacy layer)
The composite is the 0–100 weighted score that pre-dated the structural / tactical split. It is kept for backwards compatibility in v1.3.0 and still powers:
- The
tactical_statelabel returned in every API response (5-bucket tilt from composite value). - Some rules in the rules engine that trigger on directional composite tilt.
- AI-prompt context for the separate AI analysis features in the dashboard.
It does not drive direction_bias or policy_permissions in v1.3.0 — those come from the combiner above. A future scoring version may retire composite in favour of the structural / tactical split; no migration date is committed.
Each subscore takes raw inputs (e.g. RSI, funding, OI change, ETF flows, DXY, MVRV, etc.), normalises them to [0, 100] with a monotone transform, and contributes to the weighted sum.
BTC subscores
The composite is built from these 8 subscores. Each one is weighted — exact weights are not published, but Momentum and On-chain carry the most weight, followed by Derivatives, Exchange flow and Volume, with Macro, Positioning and ETF contributing smaller amounts.
| Subscore | Primary inputs |
|---|---|
| Momentum | RSI 4h, OBV, short-term price structure |
| On-chain | MVRV, NUPL, Exchange netflow |
| Derivatives | Funding rate, OI change, L/S ratio |
| Exchange flow | Exchange reserves, netflow direction |
| Volume | Volume z-score, buy/sell taker ratio |
| Macro | DXY, 10Y yield, SPX correlation, real rates |
| Positioning | Positioning Pressure Score (PPS) — derived from squeeze detector |
| ETF | Daily + weekly spot ETF flow, streak |
ETH composite
ETH mirrors the BTC structure with two differences:
- "On-chain" is replaced by "ETH Structural", a 4-subfamily score over Network Fundamentals, Supply/Monetary, Relative Strength (ETH/BTC, ETH DOM, altseason) and Demand/Institutional. This replaces MVRV/NUPL, which are BTC-native and not directly transferable.
- ETH Structural can trigger a structural downgrade that reduces
cycle_capwhen hard and soft signals combine to a minimum threshold.
Base weights and runtime-normalisation are equivalent between BTC and ETH — the substitution is one-for-one on the On-chain / ETH Structural slot.
Market regimes
The 5-state unified regime classifier is evaluated in priority order, so earlier states dominate later ones.
| Priority | State | Approximate condition |
|---|---|---|
| 1 | PANIC | Extreme fear + negative momentum + stressed derivatives + strong downside tape |
| 2 | EUPHORIA | Extreme greed + elevated on-chain valuation + stretched positioning |
| 3 | SQUEEZE | Directional squeeze detector at high confidence (funding percentile + OI z-score + L/S crowding) |
| 4 | TREND | Trend Strength score above a directional threshold, confirmed by MA stack + expansion |
| 5 | RANGE | Default — none of the above dominate |
Specific numeric thresholds per state (fear/greed percentiles, Trend Strength cut-offs, squeeze confidence minimums) are not published. They are inspectable live in the visualizer for any current market state.
Auditability: policy_hash
Every API response includes a policy_hash — a SHA-256 hex string over a deterministic serialization of the policy inputs. Given the same inputs (including reference_time), the hash is bit-for-bit identical across requests. This enables:
- Non-repudiation — an agent can commit a decision to an auditable log with the hash of the policy it acted on.
- Reproducibility — re-running the same request with
reference_timepinned returns a hash that must match. - Integrity verification — any upstream data drift changes the hash. Same hash ⇒ same inputs.
Hashed fields
The hash covers:
| Group | Fields |
|---|---|
| Version | api_version, scoring_version |
| Time | ts (from reference_time) |
| Prices | BTC price, ETH price |
| Indicators | RSI BTC, RSI ETH, MVRV, NUPL, funding BTC, funding ETH, L/S ratio, CVD, netflow, OI, ETF flows |
| Positioning | Funding percentile (BTC/ETH), OI z-score (BTC/ETH), squeeze direction, PPS score |
| Macro | Regime, coupling, DXY |
| Volatility | Regime + score |
| Cycle | Phase, MVRV percentile, boost flag |
| Composite | Overall score |
| Structural | Overall score |
| Tactical | Overall score |
| Permissions | risk_permission_score, direction_bias, regime context |
| Policy | Level, max_size_fraction, binding constraint source |
Fields are serialized with fixed ordering and fixed numeric precision before hashing. The exact serialization format is not published, but the contract is: same inputs → same hash, every time.
Versioning & scoring freeze
Predictable stability is a hard requirement for audit and for downstream agents: a field you consume today must mean the same thing next month.
- Current scoring version:
score_v3 - Current API version:
1.3.0 - Frozen until: 2026-10-22
RiskState applies a semi-annual structural freeze — a minimum of 6 months between scoring versions. Between freezes, only additive, backwards-compatible changes are allowed: new fields, new rules, new response keys. Scoring-breaking changes — renaming/removing fields, modifying normalisation ranges, changing subscore weights, rebalancing the policy combiner — only land at the freeze boundary and require a new scoring_version identifier.
The scoring_version field is returned on every response and is a component of policy_hash. Agents should pin their integration against a known scoring_version and re-validate behaviour when the version changes.
Live inspection
Every concept on this page maps one-to-one to a visible output in the production engine.
- Visualizer — read-only view of the full live state: all 4 caps, composite subscores, structural and tactical components, market regime, policy level. BYOK optional.
- API Reference — response schema, every field documented, action enums and risk flags.
- MCP Server — one-tool agent integration (
get_risk_policy).