Market Regimes

How Forven classifies markets into four regimes — TREND_UP, TREND_DOWN, RANGE_BOUND, HIGH_VOL — and gates each strategy to the conditions it was built for.

A market regime is Forven's label for the condition a market is in right now: trending up, trending down, ranging, or violently volatile. Forven detects the regime from price structure, then uses it as a gate — a strategy only takes trades in the regimes it declares it can handle. This keeps a mean-reversion strategy from fighting a strong trend, and a breakout strategy from churning in a flat range.

This page is for users who want to understand how regimes are detected and how they shape backtests and promotion. Developers writing strategies should also read writing custom strategies to learn how to declare compatible_regimes.

The four regimes

Forven classifies every market into one of four regimes:

RegimeMeaningRough trigger
TREND_UPSustained upward trendADX above 25 with bullish EMA alignment
TREND_DOWNSustained downward trendADX above 25 with bearish EMA alignment
RANGE_BOUNDNo clear direction; mean-revertingADX below 25, mixed EMA alignment
HIGH_VOLElevated volatility regardless of directionATR ratio above 2.0

The triggers above are the classifier's broad logic, not exhaustive rules. Treat the thresholds as the engine's current defaults, not promises — they are tunable and may change.

How detection works

The regime detector reads four classic indicators off recent price history and combines them into a single label plus a confidence score:

  • ADX — trend strength. High ADX means a directional market; low ADX means a range.
  • EMA alignment — the relative order of fast and slow exponential moving averages, which gives the trend's direction.
  • ATR ratio — current volatility against its baseline. A high ratio flags a HIGH_VOL regime.
  • RSI — momentum, used to corroborate direction and exhaustion.

On a cache miss the detector fetches roughly 300 bars, computes the indicators, and classifies the regime. The result is cached for 5 minutes so repeated lookups on a hot path stay cheap. Each detection returns the regime, a confidence value, and the underlying adx, ema_alignment, atr_ratio, and rsi readings for telemetry.

A low-confidence reading — for example ADX near 18 with mixed EMAs — is reported as RANGE_BOUND with a confidence around 0.3. That is by design: when the market gives no clear signal, the safe default is "no trend".

Caveat: detection is synchronous and a cache miss fetches data, so a cold lookup can briefly stall its caller. Internally, latency-sensitive paths read a cached-only peek that returns nothing on a miss instead of fetching.

Regime gating

Detection on its own is just a label. Gating is where it does work.

Every strategy declares a compatible_regimes list — for example [TREND_UP, RANGE_BOUND]. Two things consume that list:

In backtests

When a strategy runs through the backtest engine with regime gating enabled, Forven pre-computes the regime for every bar and then:

  • blocks entries on bars whose regime is not in the strategy's compatible_regimes, and
  • forces an exit if the regime changes to an incompatible one mid-trade.

This means a backtest measures the strategy only where it claims an edge, not across conditions it was never meant to trade. The per-bar regimes also drive the colour shading you see behind the candles on a backtest chart.

Beta rough edge: regime pre-computation needs a warmup. Short windows under roughly 210 bars cannot establish a stable regime and default the whole window to RANGE_BOUND. Backtest long enough to give the classifier real history.

At promotion

Promotion gates — and the live signal scanner — call regime detection before advancing or acting on a strategy. The gate fetches the strategy's compatible_regimes and compares them to the detected regime. The behaviour depends on your gating mode:

  • Strict mode blocks a strategy that is incompatible with the current regime, and also rejects detections below your minimum confidence.
  • Permissive mode warns instead of blocking.

Some examples of the policy in practice, from the engine's defaults:

  • A mean-reversion strategy is blocked when ADX is above 25 (the market is trending, so reversion is the wrong tool).
  • A trend-following strategy requires ADX of at least 20 before it is allowed to act.

The promotion gates themselves — fitness scoring, the metrics that feed them, and the full walk-forward gauntlet — are documented separately. Regime compatibility is one input among several.

Per-regime metrics

Because regimes are computed per bar, the backtester can slice your results by regime. Alongside the usual metrics, a backtest produces a by_regime breakdown — performance split across trend_up, trend_down, range_bound, and high_vol. Reading this breakdown tells you whether a strategy's edge is real and broad, or whether it is leaning on a single lucky regime. Prefer out-of-sample numbers over in-sample ones; in-sample metrics are optimistic by construction.

Forven is a research tool. Backtest and regime metrics describe the past on historical data; they are not predictive of future results, and nothing here is financial advice.

Configuration

Regime gating is governed by a small set of settings (found under Settings; precedence is environment variable, then key-value store, then config.json, then defaults):

KeyMeaningDefault
regime_min_confidenceMinimum confidence [0.0–1.0] for a detection to pass the gate. Raise it for stricter gating.0.3
strict_regime_gatingWhen true, block incompatible strategies and low-confidence detections. Permissive when false.
allow_unknown_regime_strategiesWhen true, allow strategies that declare no regime compatibility. Blocked in strict mode when false.

Raising regime_min_confidence above the floor has a sharp consequence worth knowing: a valid but low-confidence label (such as a quiet RANGE_BOUND reported at 0.3) will be rejected in strict mode. Tune it to your risk appetite rather than maxing it out.

Where you see it

The detected regime surfaces in the UI on the /strategies view and in the backtester's status pane. On a backtest chart, regimes appear as colour-coded shadings behind the price — one colour per TREND_UP, TREND_DOWN, RANGE_BOUND, and HIGH_VOL — so you can see, at a glance, which conditions a strategy was trading in when it won or lost.

Steps: gate a strategy to its regimes

  1. In your strategy, declare the regimes it is built for via compatible_regimes — see writing custom strategies. Built-in strategies already declare theirs.
  2. Decide your enforcement: set strict_regime_gating to block incompatible strategies, or leave it permissive to warn only.
  3. Set regime_min_confidence (default 0.3). Raise it if you want the gate to reject ambiguous, low-confidence regimes.
  4. Run a backtest with regime gating enabled. The engine pre-computes the per-bar regime, blocks out-of-regime entries, and forces exits on mid-trade regime flips.
  5. Read the by_regime breakdown in the result to confirm the edge holds where you expected it — and only there.

What you'll see: the backtest result includes a by_regime metrics slice and, on the chart, colour-coded regime shadings behind the candles. A gated strategy will show no entries during regimes it is incompatible with.

  • The gauntlet — the full robustness battery a strategy must survive.
  • Metrics — every number a backtest computes, including the by_regime slice.
  • Writing custom strategies — how to declare compatible_regimes on your own strategy.