Margin Watchdog

Per-venue buying-power monitoring with progressive defensive actions

MarginGuard watches per-venue buying-power utilization and fires progressively stronger events as it approaches forced liquidation.

Signature

python
from horizon.risk import MarginGuard
from horizon.risk.margin import MarginReduceMethod

margin = MarginGuard(
    warn_at_utilization=0.80,
    reduce_at_utilization=0.90,
    emergency_at_utilization=0.98,
    reduce_method=MarginReduceMethod.LargestLoser,
    per_venue=True,
)
warn_at_utilizationfloat
At 80% buying power used, fire `MarginWarning` events. Alerts the operator; new orders may still go through.
reduce_at_utilizationfloat
At 90%, fire `MarginReduce` events. The run loop force-closes positions until utilization drops.
emergency_at_utilizationfloat
At 98%, fire `MarginEmergency`. Aggressive flatten of everything on the affected venue.
reduce_methodMarginReduceMethod
Which positions to close first when reducing. Options: `LargestLoser`, `LowestConviction`, `HighestGreek`, `Proportional`.
per_venuebool
Track utilization per venue separately. When False, use aggregate.

How it fires

python
def _check_margin(self, state: RiskEngineState) -> list[LifecycleEvent]:
    events = []
    cfg = self.config.margin
    if cfg is None:
        return events
    for venue, bp in state.buying_power_by_venue.items():
        if bp <= 0:
            continue
        used = state.used_buying_power_by_venue.get(venue, 0.0)
        util = used / bp
        if util >= cfg.emergency_at_utilization:
            events.append(LifecycleEvent(
                kind=LifecycleEventKind.MarginEmergency,
                payload={"venue": venue, "utilization": util},
            ))
        elif util >= cfg.reduce_at_utilization:
            events.append(LifecycleEvent(
                kind=LifecycleEventKind.MarginReduce,
                payload={"venue": venue, "utilization": util},
            ))
        elif util >= cfg.warn_at_utilization:
            events.append(LifecycleEvent(
                kind=LifecycleEventKind.MarginWarning,
                payload={"venue": venue, "utilization": util},
            ))
    return events

Runs on every tick. Reads state.buying_power_by_venue and state.used_buying_power_by_venue (populated by the run loop from each venue’s balance()).

Why three levels

  • Warning (80%): heads-up for the operator. Log, alert, but don’t intervene yet.
  • Reduce (90%): actively de-risk. The strategy is close to forced liquidation territory; reduce before it gets worse.
  • Emergency (98%): flatten. Below this, the venue’s own margin call is imminent; better to exit ourselves at market prices than be force-liquidated.

Reduce methods

Tests

python
# tests/test_risk_engine.py::TestMarginWatchdog

def test_warn_level(self) -> None:
    cfg = RiskConfig(margin=MarginGuard(warn_at_utilization=0.8, reduce_at_utilization=0.9))
    engine = RiskEngine(cfg)
    state = _state()
    state.buying_power_by_venue = {"paper": 100_000}
    state.used_buying_power_by_venue = {"paper": 85_000}
    events = engine.watchdog(state)
    assert any(e.kind == LifecycleEventKind.MarginWarning for e in events)

def test_reduce_level(self) -> None:
    cfg = RiskConfig(margin=MarginGuard(warn_at_utilization=0.8, reduce_at_utilization=0.9))
    engine = RiskEngine(cfg)
    state = _state()
    state.buying_power_by_venue = {"paper": 100_000}
    state.used_buying_power_by_venue = {"paper": 92_000}
    events = engine.watchdog(state)
    assert any(e.kind == LifecycleEventKind.MarginReduce for e in events)

Next