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_factor and therefore max_exposure, regardless of the individual caps.
  • Volatility scales, never inverts. vol_mult can 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_fractionLevelLabelPosture
< 0.151BLOCK SurvivalReduce, hedge only
0.15 – 0.352BLOCK DefensiveWait, hedge, small scalps only
0.35 – 0.603CAUTIOUS PreservationDCA, R:R > 2:1 only
0.60 – 0.804GREEN SelectiveLong/short with confirmation
≥ 0.805GREEN ExpansionTrend 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.

SubscorePrimary inputs
MomentumRSI 4h, OBV, short-term price structure
On-chainMVRV, NUPL, Exchange netflow
DerivativesFunding rate, OI change, L/S ratio
Exchange flowExchange reserves, netflow direction
VolumeVolume z-score, buy/sell taker ratio
MacroDXY, 10Y yield, SPX correlation, real rates
PositioningPositioning Pressure Score (PPS) — derived from squeeze detector
ETFDaily + 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_cap when 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.

SubfamilyWhat it measures
CyclePhase + MVRV percentile / ETH-BTC phase + halving age
SupplyBTC: supply in profit + LTH supply. ETH: staking ratio + issuance regime.
DemandETF flows + netflow trend + institutional accumulation
MacroReal 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.

ComponentWhat it measures
Positioning Pressure (PPS)Squeeze setup: crowding × funding × basis × OI z-score
MomentumRSI zone + 24h change + 1h velocity
Volume / CVDCVD acceleration + volume z-score + buy-ratio
Derivatives extremityFunding percentile + OI z-score direction
L/S velocityRegression slope of L/S ratio history
Whale PressureMulti-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 regimePosture
PANICStructural leads — avoid catching a falling knife.
EUPHORIAStructural leads — fade extended tactical with slow context.
SQUEEZEStructural and tactical balanced — confluence matters.
TREND / RANGEStructural anchors, tactical tunes — the default blend.

The combiner produces:

  • risk_permission_score — weighted blend, 0–100
  • direction_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_composite
  • reason_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.

PriorityStateApproximate condition
1PANICExtreme fear + negative momentum + stressed derivatives + strong downside tape
2EUPHORIAExtreme greed + elevated on-chain valuation + stretched positioning
3SQUEEZEDirectional squeeze detector at high confidence (funding percentile + OI z-score + L/S crowding)
4TRENDTrend Strength score above a directional threshold, confirmed by MA stack + expansion
5RANGEDefault — 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_time pinned returns a hash that must match.
  • Integrity verification — any upstream data drift changes the hash. Same hash ⇒ same inputs.

Hashed fields

The hash covers:

GroupFields
Versionapi_version, scoring_version
Timets (from reference_time)
PricesBTC price, ETH price
IndicatorsRSI BTC, RSI ETH, MVRV, NUPL, funding BTC, funding ETH, L/S ratio, CVD, netflow, OI, ETF flows
PositioningFunding percentile (BTC/ETH), OI z-score (BTC/ETH), squeeze direction, PPS score
MacroRegime, coupling, DXY
VolatilityRegime + score
CyclePhase, MVRV percentile, boost flag
CompositeOverall score
StructuralOverall score
TacticalOverall score
Permissionsrisk_permission_score, direction_bias, regime context
PolicyLevel, 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).