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_utilizationfloatAt 80% buying power used, fire `MarginWarning` events. Alerts the operator; new orders may still go through.
reduce_at_utilizationfloatAt 90%, fire `MarginReduce` events. The run loop force-closes positions until utilization drops.
emergency_at_utilizationfloatAt 98%, fire `MarginEmergency`. Aggressive flatten of everything on the affected venue.
reduce_methodMarginReduceMethodWhich positions to close first when reducing. Options: `LargestLoser`, `LowestConviction`, `HighestGreek`, `Proportional`.
per_venueboolTrack 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)