Scenario Gates

Forward-looking tail-risk checks: what if X happens?

ScenarioGuard runs forward-looking risk checks. Instead of watching current drawdown (which is reactive), scenarios ask “what if the market drops 10% right now?” and compute the hypothetical P&L against the current portfolio.

Available scenarios

python
from horizon.risk import Scenario

Scenario.MarketDrop(pct=-0.10)
Scenario.VolSpike(vix_to=40)
Scenario.CryptoCrash(pct=-0.30)
Scenario.PredictionMarketBinary(asset="ELECTION_YES", outcome="NO")
Scenario.CorrelationToOne()

Each scenario is a dataclass that knows how to apply its shock to a portfolio position and compute P&L impact.

ScenarioGuard

python
from horizon.risk import Scenario, ScenarioGuard

scenarios = ScenarioGuard(
    enabled_scenarios=[
        Scenario.MarketDrop(pct=-0.10),
        Scenario.VolSpike(vix_to=40),
        Scenario.CryptoCrash(pct=-0.30),
        Scenario.CorrelationToOne(),
    ],
    max_scenario_drawdown_pct=0.15,
    check_frequency="daily",
    block_orders_if_worsens=True,
    alert_on_breach=True,
)
enabled_scenarioslist[Scenario]
Scenarios to evaluate.
max_scenario_drawdown_pctfloat
Threshold for "this scenario would hurt us too much". When any scenario implies a drawdown beyond this, fire.
check_frequencystr
How often to run the full scenario suite. Too frequent is wasteful; too infrequent and you miss a buildup.
block_orders_if_worsensbool
When True, pre-trade checks reject orders that would make any scenario worse.
alert_on_breachbool
Fire alerts when any scenario crosses the threshold.

Scenario types

How it works

Snapshot current portfolio

Read all open positions and their current P&L.

Apply each scenario

For each enabled scenario, compute the hypothetical P&L impact on the current portfolio.

Compute implied drawdown

For each scenario, implied drawdown = scenario_pnl_impact / current_equity.

Check threshold

If any scenario's drawdown > max_scenario_drawdown_pct, the scenario is "breached."

Act on breach

- Alert the operator (if alert_on_breach=True) - Block orders that would worsen the breach (if block_orders_if_worsens=True)

Usage

python
from horizon.risk import RiskConfig, Scenario, ScenarioGuard

risk = RiskConfig(
    scenarios=ScenarioGuard(
        enabled_scenarios=[
            Scenario.MarketDrop(-0.10),
            Scenario.VolSpike(vix_to=40),
            Scenario.CorrelationToOne(),
        ],
        max_scenario_drawdown_pct=0.20,
        check_frequency="daily",
    ),
)

Why it matters

Traditional risk (drawdown guards, stops) is reactive: it only fires after losses have already occurred. Scenario gates are proactive: they catch portfolios that look fine today but have hidden tail risk.

A portfolio can:

  • Be at a new equity peak (drawdown = 0%)
  • Have no positions near stop levels
  • Have perfect margin utilization

…and still implode if one specific scenario plays out. The scenario guard is the layer that catches this before it happens.

Status

Next