Per-Asset-Class Risk

Different rules for equities, options, perps, and prediction markets

Different asset classes have different risk profiles. A 10% stop makes sense for an equity position but is way too wide for an options position and misleading for a prediction market where the native concept is “days to resolution” not “unrealized P&L.” per_asset_class lets you configure each class with its own AssetRisk bundle.

The dataclass

python
@dataclass
class AssetRisk:
    # Stops for this asset class
    stop_loss: Any = None
    time_stop: Any = None

    # Generic sizing caps
    max_gross_notional_usd: float | None = None
    max_position_pct_of_adv: float | None = None
    max_position_notional_usd: float | None = None

    # Equity-specific
    max_sector_exposure: float | None = None
    exclude_earnings_within_days: int | None = None

    # Option-specific
    max_portfolio_delta: float | None = None
    max_portfolio_gamma: float | None = None
    max_portfolio_vega: float | None = None
    max_theta_burn_per_day_usd: float | None = None

    # Perp-specific
    max_leverage: float | None = None
    liquidation_buffer_pct: float | None = None
    max_funding_exposure_per_day_usd: float | None = None

    # Prediction-market-specific
    max_per_event_usd: float | None = None
    min_days_to_resolution: int | None = None

Usage

python
from horizon.asset_classes import Equity, Option, Perp, Prediction
from horizon.risk import (
    AssetRisk,
    GreekStop,
    RiskConfig,
    StopLoss,
)

risk = RiskConfig(
    per_asset_class={
        Equity: AssetRisk(
            stop_loss=StopLoss(per_position_pct=0.10, trailing_pct=0.05),
            max_sector_exposure=0.40,
            exclude_earnings_within_days=3,
        ),
        Option: AssetRisk(
            stop_loss=GreekStop(
                close_if_delta_over=0.70,
                close_if_gamma_over=0.08,
                close_if_dte_under=2,
            ),
            max_portfolio_delta=1000,
            max_portfolio_gamma=50,
            max_portfolio_vega=500,
            max_theta_burn_per_day_usd=200,
        ),
        Perp: AssetRisk(
            stop_loss=StopLoss(per_position_pct=0.08),
            max_leverage=3.0,
            liquidation_buffer_pct=0.30,
            max_funding_exposure_per_day_usd=50,
        ),
        Prediction: AssetRisk(
            stop_loss=StopLoss(per_position_usd=500),
            max_per_event_usd=2_000,
            min_days_to_resolution=3,
        ),
    },
)

Equity

Option

Perp

Prediction

Combining with global risk

Per-asset-class rules live alongside the global risk layers:

python
risk = RiskConfig(
    # Global. applies across all asset classes
    per_order=OrderRisk(max_order_notional_usd=25_000),
    drawdown=[DrawdownGuard(daily_pct=0.05, action=HaltNew)],
    kill_switch=KillSwitch(auto_trigger_on=[PortfolioDrawdownEvent(0.25)]),

    # Per asset class. applies only to that class
    per_asset_class={
        Equity: AssetRisk(stop_loss=StopLoss(per_position_pct=0.10)),
        Option: AssetRisk(stop_loss=GreekStop(close_if_delta_over=0.70)),
    },
)

Both run. Per-order checks apply to every order regardless of class; drawdown guards apply to total equity regardless of composition; kill switch fires on total drawdown. The per-class stops fire only on positions in the matching class.

When the strategy is multi-asset

If your strategy trades multiple asset classes and you want different rules for each, per_asset_class is the cleanest way to express it. Without it, you’d need separate risk configs per strategy, which wouldn’t work because risk is evaluated at the portfolio level.

Next