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.
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).
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.
Composite scoring
The composite is a 0–100 weighted score over 8 normalised subscores. It answers: "given everything, does the tape lean bullish, bearish, or neutral?" It is consumed by the tactical layer (see below) and by rules that trigger on directional tilt.
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.
Structural vs tactical (v1.3.0)
From API v1.3.0 onward, the composite is complemented by two parallel scores routed to a policy combiner. The composite still exists for backwards compatibility and continues to drive tactical_state labelling, but direction_bias now comes from the combiner.
┌─────────────────────┐
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 │
└─────────────────────┘
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 leads — avoid catching a falling knife. |
| EUPHORIA | Structural leads — fade extended tactical with slow context. |
| SQUEEZE | Structural and tactical balanced — confluence matters. |
| TREND / RANGE | Structural anchors, tactical tunes — the default blend. |
The combiner produces:
risk_permission_score— weighted blend, 0–100direction_bias— tactical-driven (LONG if tactical ≥ 60, SHORT if ≤ 40, 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 for intermediate states, and the calibration of the structural-veto thresholds, are not published.
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).