Skip to main content
The spread is the only number Pegana publishes that you really need to care about.
  • spread = 0 → asset trades at intrinsic
  • spread > 0 → market below intrinsic (under-priced, common stress signal)
  • spread < 0 → market above intrinsic (over-priced, less common)
A 100 bps spread means market is 1.00% below intrinsic.

EWMA smoothing

Raw spread ticks are noisy. We smooth with an exponentially-weighted moving average:
with α = 0.3 default. That is:
  • New tick contributes 30% to the published spread
  • Prior smoothed value carries 70%
A higher α (say 0.6) reacts faster but is noisier. A lower α (0.1) is laggy and misses real depegs. 0.3 is calibrated against 24h p99 noise per asset class — tight enough to catch a real depeg inside ~15s, loose enough that a single bad Jupiter quote doesn’t trip the FSM.

Per-asset thresholds

A single fixed threshold across all 63 assets tracked live (50 monitoring-only) would produce false positives on noisy LSTs (INF: 24h p99 ~1.12%) and false negatives on tight stables (USDC: 24h p99 ~10 bps). We calibrate per-asset. These are representative bands; live per-asset values vary (e.g. jitoSOL 0.45/1.10/2.50%, xSOL 15/30/60%). Every asset’s exact threshold is in assets.toml. To inspect:

The 5-state FSM

On routine stale / low-confidence / decoder-drift input the engine skips the recompute and holds the last published state — it does not enter UNKNOWN. The engine publishes UNKNOWN (honest-dark) at cold-start and when an anchor is detectably broken — a >10% premium on a NAV-priced asset, via the NAV-sanity gate (ADR-0025) — but never as a routine stale-data verdict. Each transition has a dwell timer: the smoothed spread must hold the crossed threshold for the specified time before the transition fires.

Asymmetric hysteresis

Entry and exit thresholds are deliberately different, like a thermostat: Why exit dwell > entry dwell? Entering a stressed state is something subscribers want fast — 30s is fine. Exiting back to PEGGED is something they want to trust — a single tick at 29 bps after a 200 bps spike is not the same as a sustained recovery. The longer exit dwell (120s vs the 30s entry dwell) is a small cost — an extra minute and a half of “alert” state — and a big honesty win. See hysteresis-fsm for the deeper read.

What the FSM guarantees

  • A transition fires once per real state change. A signal that oscillates inside a threshold band stays in the stressed state until it genuinely recovers.
  • Every transition is timestamped server-side with the spread that triggered it.
  • BLACK_SWAN is reachable from both the spread path (a discount beyond 2× critical by default, overridable per asset via black_swan / black_swan_bps) and the CR path (hyUSD, CR < 100%). It re-labels only the most extreme moves — a USDC 4%+ break, a UST-style CRITICAL → BLACK_SWAN cascade — and auto-exits via the standard decay-down hysteresis like every other band (ADR-0025); it is not a no-reset terminal state, and there is no public ack endpoint.

What the FSM cannot fix

  • A stale input. If Sanctum stops publishing, the smoothed spread will lag — the FSM has no way to detect that. We mitigate via a staleness gate (30s) at the source layer: on a quote older than 30s the engine skips the recompute and holds the last published state rather than emitting a fresh verdict on degraded input. High-risk illiquid / single-source assets are flagged monitoring_only (beta): their alerts are live but labeled provisional, and a detectably frozen anchor publishes UNKNOWN via the NAV-sanity gate rather than a false reading.
  • A wrong threshold. We calibrated against 24h p99 over the soak period. If an asset’s noise profile changes (new venue, new MEV regime), the threshold may need re-calibration. CALIBRATION-v1.md documents the procedure.

Next

Honesty about limits

What we don’t model. Where the signal can lie. When to mistrust an alert.