Promotion gates

The gates between every pipeline stage — quick_screen overfitting guardrails, fitness scoring, the lean paper gate, the strict paper→live gate, and operator overrides.

A gate is the check a strategy must pass to move from one pipeline stage to the next. Gates are where Forven kills strategies. They are the disciplined, mechanical answer to the question "is this edge real enough to risk the next thing" — where "the next thing" escalates from compute, to simulation, to actual capital.

This page is the operator's reference to every gate in the pipeline: what each one checks, the default thresholds, and how to override one when you genuinely disagree. The single source of truth in the code is policy.py (evaluate_promotion); everything here mirrors its defaults as of the 2026-06-20 policy version.

Forven is a research tool. Gate thresholds describe how a strategy behaved on historical and simulated data — they do not predict future results, and nothing here is financial advice. A strategy passing every gate is evidence of process discipline, not a promise of profit.

The four stages and three gates

The real pipeline stages are quick_screen → gauntlet → paper → live_graduated, with archived, rejected, and research_only as terminal states. The public site simplifies these to screen → gauntlet → candidate → live; in the app, the public "candidate" is the paper stage. Use the real names — they are what the UI and API report.

Three gates sit between those stages:

GateTransitionCharacterWhat it protects
Quick-screen gatequick_screen → gauntletCheap triage, hard guardrailsCompute — don't waste robustness tests on overfit junk
Paper gate (lean)gauntlet → paperAchievable, keeps the funnel movingPipeline volume — filter clear losers, not adverse regimes
Paper→live gate (strict)paper → live_graduatedStrict edge proofReal capital — unforgiving, so the bar is high

The design is deliberate. Paper costs nothing, so the paper gate asks only "is this not obviously broken." Live risks real money, so the paper→live gate asks "prove a forward edge." That achievable-paper vs. strict-live split is the spine of Forven's gate policy.

The gauntlet page documents the robustness battery and the lean paper gate in detail. This page covers the guardrails on either side of it and the strict capital gate beyond it.

The quick-screen gate (overfitting guardrails)

This is the first gate and the cheapest. Before a strategy is allowed to burn compute in the gauntlet, the quick-screen gate — internally the S00552 guardrails — runs a fast triage on its backtest metrics. It is designed to fail fast.

A strategy is rejected at this gate if any of the following is true (defaults shown):

  • In-sample Sharpe <= 0.1 (quick_screen.min_is_sharpe, hardcoded)
  • Out-of-sample Sharpe < -0.1 — a clearly negative OOS edge is disqualifying
  • IS/OOS Sharpe gap too wide — the ratio guard rejects an IS/OOS Sharpe ratio above 3.0 (quick_screen.max_is_oos_ratio); the policy targets an IS−OOS gap no larger than ~1.5 points
  • Profit factor < 1.05 at either IS or OOS (quick_screen.min_profit_factor)
  • Effective trade count < 30 (quick_screen.min_trades) — fewer trades aren't statistically meaningful
  • Robustness score < 10/100 — a hardcoded pre-gauntlet floor (distinct from quick_screen.min_robustness_score, which defaults to 50 for the broader screen)
  • IS max drawdown > 30% (quick_screen.max_is_maxdd_pct)

Implausible-metrics rejection

A second, defense-in-depth guard rejects metrics that are too good: any Sharpe >= 5.0 or profit factor >= 8.0 on either IS or OOS (quick_screen.max_plausible_sharpe, quick_screen.max_plausible_profit_factor). On honest crypto data these are not achievable — they are the signature of a look-ahead leak, such as an accidental .shift(-1) that reads tomorrow's price. The same guard runs again at gauntlet entry. If a strategy looks too good to be true, Forven assumes it cheated.

The OOS aliasing trap

A strategy with no distinct out-of-sample data has zero validation, not full validation. The quick-screen gate refuses to alias in-sample as out-of-sample: when distinct OOS evidence is missing it blocks admission to the gauntlet with the explicit reason no distinct OOS evidence. Trust OOS metrics, never IS.

Fitness scoring

Alongside the hard guardrails, every backtest produces a fitness score on a 0–100 scale. Fitness is a composite used to rank strategies (for example, to pick the best child of a hypothesis) and as an automatic promotion signal out of quick_screen.

The five factors and their weights:

FactorWeightNotes
Sharpe30%Score maxes out at a Sharpe of 3.0
Win rate20%
Profit factor20%Score maxes out at a PF of ~3.3 (fitness_min_profit_factor + 2.0)
Max drawdown15%Penalty reaches 0 at the configured drawdown limit (quick_screen.max_drawdown_pct, default 30%)
Trade count15%Bonus scales toward a 20-trade baseline (quick_screen.fitness_min_trades)

Fitness is shape-aware. The scorer validates the backtest first and subtracts penalties for weak fundamentals — a wide IS/OOS gap, too few trades, high drawdown. A strategy can clear an individual floor (say, Sharpe 1.1) and still score near zero if its shape is poor (a 0.6-point IS/OOS gap is a heavy penalty). The conventional thresholds are fitness >= 60 for paper-eligible and >= 70 for deploy-eligible, with < 40 marking a strategy for retirement.

The lean paper gate (gauntlet → paper)

After a strategy survives the gauntlet, the lean paper gate decides whether it earns a paper trading seat. It is deliberately achievable so the funnel keeps moving even in a bad regime. Defaults:

  • Robustness score >= 50/100 (gauntlet.min_robustness_score)
  • Minimum total return >= 0% (gauntlet.min_total_return_pct)
  • Maximum drawdown <= 30% (gauntlet.max_drawdown_pct)
  • Minimum OOS profit factor >= 1.05 (gauntlet.min_oos_profit_factor)
  • Minimum OOS Sharpe >= 0 — advisory only

These are floors, not loose ends. A set of hardcoded minimums (_PAPER_GATE_FLOORS) means config can make this gate stricter but never looser: you cannot drop robustness below 50, Monte-Carlo drawdown above 40%, the WFA fold pass rate below 40%, the param-jitter pass rate below 60%, or trade count below 30. Relaxing the config does not open a back door to the capital stages.

The strict paper→live gate

This is the gate that guards real money, and it is the hardest in the system. A strategy in paper must accumulate genuine forward evidence before it can reach live_graduated. Defaults:

CheckDefaultConfig key
Days in paper>= 14paper_trading.min_paper_days
Closed paper trades>= 50paper_trading.min_closed_trades
Paper total return> 0%paper_trading.min_total_return_pct
Paper max drawdown< 15%paper_trading.max_drawdown_pct
Paper Sharpe (per-trade t-stat)>= 1.0paper_trading.min_paper_sharpe
Paper profit factor>= 1.2paper_trading.min_profit_factor_paper
Live profit-factor floor>= 1.5paper_trading.min_profit_factor_live

The paper Sharpe here is a per-trade t-statistic (mean / stdev * sqrt(n)), a real forward-edge proof rather than an annualized headline number. On top of these, when paper_trading.live_strict_robustness_enabled is true (the default) the gate re-enforces the full robustness battery from the gauntlet — it does not take the earlier pass on trust. It also checks funding-data completeness and the optional source-reconciliation divergence (below). If the paper profit factor lands between the floor and 2.0 (paper_trading.pf_position_reduction_threshold), the strategy is admitted but with position size reduced to 50%.

A strategy that clears this gate enters live_graduated on a capped allocation schedule — 25% of target size in weeks 1–2, 50% in weeks 3–4, 100% from week 5 (live_graduated.allocation_schedule). See going live safely for the operator checklist that should precede this.

Real capital is unforgiving. The strict gate measures past behaviour; it cannot guarantee future behaviour. Treat a pass as permission to risk a small, capped allocation, not as a verdict that the strategy will be profitable.

Other gates and guards

Several quieter gates run alongside the three main ones.

Phantom-container gate

A strategy row with no backtest results has nothing to validate. The verification gate (verify_backtest_exists_for_stage_transition) blocks any transition to a forward stage — paper, live, deployed — unless an operator forces it. This stops trade execution on an unvalidated strategy.

Demotion thrash protection

When a strategy fails the gauntlet it is demoted back to quick_screen, and its demotion_count increments. The first and second demotions remit it to quick_screen for another attempt; on the third, it is redirected to research_only and removed from the tradable pipeline. Without this cap, a strategy that fails on repeat would loop forever, burning backtest compute.

Canonical protection

A graduated hypothesis winner is flagged canonical=1 and cannot be archived or rejected by automated actors. Only the decay kill-switch (actor=decay_tracker) or an explicit operator force=true can clear the flag. This protects the frozen thesis edge from accidental loss.

Decay kill-switch (live retirement)

Once a strategy is live, the decay tracker watches for degradation. If its Sharpe declines by >= 30% over a rolling 72-hour window with at least 5 trades (live_graduated.decay_kill_switch_pct), it is auto-retired to archived. decay_tracker is a privileged system actor — it is allowed to override canonical protection, because a degraded canonical strategy draining capital is worse than losing the thesis. Safety beats availability.

Source-reconciliation gate

An optional gate blocks promotion if the backtest price source (for example Binance) diverges by more than 2% from the live trade venue (HyperLiquid). It is pre-computed out of band and disabled by default; when data is missing it fails open (allows promotion) unless an operator explicitly sets it to block.

Slot competition (optional)

Off by default. When enabled, only one strategy may hold a given (symbol, timeframe) slot in paper or live. A challenger must materially beat the incumbent (a Sharpe ceiling near 3.0) to trigger a dethrone approval; otherwise the gate returns gate_contention and the challenger waits for the slot to free. Default mode places no cap — every gauntlet-passing strategy is promoted.

Gate-rejection reasons

When a transition is blocked, transition_stage records a motion code so you can tell why. Common reason codes:

  • overfitting_guardrails — failed the S00552 quick-screen checks
  • gate_failure — failed a robustness or capital-gate threshold (no auto-retry)
  • gate_contention — slot occupied (transient; retries until the slot frees)
  • archive_rejected_ghost_protection — phantom container, no backtest results
  • canonical_protected — automated archival blocked on a canonical strategy
  • operator_approval_required — gate passed, but the change is queued for sign-off

These codes drive retry logic too: contention retries, failures archive.

Operator overrides and approvals

Capital gates are hard. Automated actors cannot force-bypass paper → live_graduated — if agent code attempts force=true on a capital stage, the force is silently downgraded to false. Only a human operator (or the privileged decay_tracker) can override a capital gate, and only through explicit, informed-consent UI.

Two mechanisms are independent and easy to confuse:

  • Override (override=true) — bypasses the gate evaluation. You see the gate rejection (for example, "paper Sharpe 0.95 vs. 1.0 floor"), tick the override box, and submit a reason. The override is logged as a warning activity for audit.
  • Approval — an approval_id queued for manual sign-off in the approvals panel. Dethrone and promotion approvals queue here. Unless auto_approve_promotions is enabled, an approval still queues even after you override the gate.

So a fully manual live promotion can require you to clear both: override the gate, then approve the queued promotion.

Steps: override a gate rejection

  1. Open the strategy in the lab or lifecycle view and click Promote to the target stage.
  2. Read the gate-rejection reason the UI shows — confirm the failure is marginal and you understand it.
  3. Tick the Override checkbox (informed consent) and enter a short reason, e.g. Sharpe near threshold, edge confirmed out-of-band.
  4. Submit. Under the hood this calls POST /api/strategies/{strategy_id}/promote with override=true.
  5. If auto_approve_promotions is off, open the approvals panel, find the pending promotion, review the memo (gate reason + your justification), and click Approve.
  6. The strategy transitions to the target stage; the override and approval are both recorded in the activity log.

What you'll see: the promotion dialog renders the exact gate-rejection reason and an override control; the approvals panel shows the queued item with the gate memo and your reason. Before promoting, call GET /api/lifecycle/strategies/{strategy_id}/readiness (or /paper-live-readiness) to see the full checklist of required artifacts.

# Inspect promotion readiness before overriding
curl.exe http://127.0.0.1:8003/api/lifecycle/strategies/$StrategyId/readiness

# Promote with an explicit operator override
curl.exe -X POST http://127.0.0.1:8003/api/strategies/$StrategyId/promote `
  -H "content-type: application/json" `
  -d '{"to_status":"live_graduated","override":true,"reason":"edge confirmed out-of-band"}'

Tuning gate thresholds

Gate thresholds live in config under the quick_screen.*, gauntlet.*, paper_trading.*, and robustness_thresholds.* key groups. Drawdown and return values are stored as fractions (0.30 = 30%) and shown as percentages in the UI.

{
  "quick_screen": {
    "min_profit_factor": 1.05,
    "min_trades": 30,
    "min_robustness_score": 10,
    "max_plausible_sharpe": 5.0,
    "max_plausible_profit_factor": 8.0
  },
  "gauntlet": {
    "min_robustness_score": 50,
    "max_drawdown_pct": 0.30,
    "min_oos_profit_factor": 1.05
  },
  "paper_trading": {
    "min_paper_days": 14,
    "min_closed_trades": 50,
    "max_drawdown_pct": 0.15,
    "min_paper_sharpe": 1.0,
    "min_profit_factor_paper": 1.2,
    "min_profit_factor_live": 1.5,
    "live_strict_robustness_enabled": true
  }
}

Two safety notes when tuning:

  • Floors only tighten. _PAPER_GATE_FLOORS clamps the paper gate so config can make it stricter but never looser. Loosening below a hard minimum has no effect.
  • testing_mode never opens the capital gates. Setting testing_mode: true bypasses the quick_screen and gauntlet gates for rapid iteration, but it always enforces the paper and live capital gates. There is no real-money shortcut.

Config changes self-heal in the KV store on next load (_normalize_pipeline_config); for example, a config that drops walk_forward from the required tests is repaired with a warning. See the configuration reference for precedence and the complete key list.

Caveats

  • Capital gates cannot be force-bypassed by automation — by design. If you script a promotion, expect force=true on a capital stage to be ignored.
  • A kill-switch reset re-baselines the high-water mark to current equity, which shifts where future drawdown gates measure from. It is a deliberate, destructive action; understand it before resetting under live trading.
  • Beta builds hard-lock execution to paper, so the strict paper→live gate is documented but not exercisable with real capital during beta.
  • The pipeline — the full lifecycle these gates sit between
  • The gauntlet — the robustness battery feeding the lean paper gate
  • Metrics — what each gate reads, and why OOS is the ground truth
  • Going live safely — the operator checklist before the strict gate