Skip to content

Risk Management

gordon-risk is the portfolio-level safety layer. It watches trading.* aggregates and intervenes when portfolio-level thresholds are breached. It never touches individual orders — it commands, it does not execute. Per-order invariants (SL, notional cap, idempotency) live in gordon-executor.

Five circuit breakers

The scheduler runs at 30 Hz. Each tick evaluates all five breakers independently:

BreakerSignalThreshold
DrawdownBreakerPeak-to-trough portfolio equity drawdownConfigurable percentage from equity high
ConnectivityBreakergordon-data freshness check (latest kline age)Stale if no kline within threshold window
VpinBreakerVPIN (Volume-synchronized Probability of Informed Trading) computed live from 1m klinesToxic-flow threshold
MacroBreakerFRED macro regime indicatorsMacro deterioration threshold
CorrelationBreakerCross-asset correlation densityHigh-correlation (risk-on/risk-off) regime

When a breaker fires, commit_risk_action writes an atomic five-row transaction: risk_events, bot_commands, risk_state, risk_state_history, and bus.outbox (for the NATS risk.events.{breaker} payload). Either all five rows commit or none do.

Halt-latch state machine

The halt is a hard latch, not a soft signal. Once set, the system stays halted until an operator explicitly calls POST /risk/resume. A resolved breaker condition does not automatically clear the halt — this is intentional. The operator must verify conditions before resuming.

The latch state is stored in trading.risk_state.halted (singleton row). The halt-latch contract is documented in full at docs/contracts/halt-latch.md.

Emergency flatten

POST /risk/emergency-flatten (operator-only, proxied through gordon-manager BFF):

  1. Writes a flatten_all command to trading.bot_commands and bus.outbox (same transaction).
  2. gordon-executor's executor-risk consumer (risk.commands durable) receives the command.
  3. gordon-executor closes all open positions via Binance REST.
  4. All bots receive a pause command via risk.commands.
  5. The halt latch is set.

Drill target: less than 30 seconds end-to-end from operator POST to positions closed.

The POST /risk/emergency-flatten endpoint also writes to trading.risk_audit_log and trading.risk_events. gordon-risk is the sole writer of these audit tables.

Pause commands

POST /bots/:id/pause suspends a specific bot without affecting others:

  • gordon-risk writes a column-level UPDATE on bot_configs.desired_state (column-level grant from migration 0011 — no other columns can be written by the risk role).
  • gordon-bot's reconciler loop detects the state change and stops emitting intents.
  • The pause is reversible via POST /risk/resume (fleet-wide) or manager bot lifecycle controls (per-bot).

Escalation state machine

The escalation manager tracks whether a flatten has been attempted and whether it succeeded. States: Idle, FlattenRequested, FlattenInFlight, FlattenConfirmed, FlattenFailed. A failed flatten after the full drain timeout triggers a second escalation attempt and an operator alert.

Metrics

All circuit-breaker evaluations, fires, and command writes are tracked via Prometheus counters. Key metrics:

MetricLabels
gordon_risk_breaker_evaluations_total
gordon_risk_breaker_fires_totalbreaker
gordon_risk_bot_commands_issued_totalcommand
gordon_risk_command_write_errors_totalbreaker, outcome_type, error_class
gordon_risk_operator_flatten_requests_totalscope, outcome
gordon_risk_operator_resume_requests_totaloutcome

Alert GordonRiskCommandWriteFailed fires on any command_write_errors_total rate above zero over a 5-minute window — a breaker fired but the command never landed.

Invariants

  • Portfolio-level only. Per-order invariants live in gordon-executor.
  • Halt is a hard latch. Only explicit POST /risk/resume clears it.
  • commit_risk_action is an atomic five-row transaction. No partial writes.
  • gordon-risk is the sole writer of trading.risk_events, trading.risk_audit_log, and trading.risk_state.
  • gordon-risk holds a column-level UPDATE grant on bot_configs.desired_state only — widening this grant requires an explicit migration and a test update.
  • SL is mandatory on all positions — enforced in gordon-executor, not here.
  • Emergency flatten drill target: less than 30 seconds.
  • Execution — how gordon-executor enforces per-order invariants and reacts to risk commands.
  • BFF Boundary — how operator commands reach gordon-risk through gordon-manager.
  • Event Flowrisk.commands and risk.events.> subject topology.

Gordon — keep compounding without blowing up