GitHubBlog

Search Documentation

Search for a page in the docs

Guards & Risk

Guards are pre-execution safety checks that run inside each UTA before orders reach the broker. They enforce risk limits automatically, catching mistakes before they become trades.

How Guards Work

When you push a commit, the guard pipeline:

  1. Fetches current positions and account info from the broker
  2. Builds a GuardContext with the operation, positions, and account data
  3. Runs each guard sequentially — first rejection wins
  4. If all guards pass, the operation is dispatched to the broker
  5. If any guard rejects, the operation fails with a clear reason
tradingPush → Guard Pipeline → Broker
                  │
                  ├── max-position-size ✓
                  ├── cooldown ✓
                  └── symbol-whitelist ✗ "DOGE not in whitelist"
                                         → Operation rejected

Built-in Guards

max-position-size

Prevents any single position from exceeding a percentage of your total equity. Default threshold: 25%.

{
  "type": "max-position-size",
  "options": { "maxPercentOfEquity": 20 }
}

The guard estimates the projected position value after the order and compares it to net liquidation. If the projected size exceeds the threshold, the order is rejected:

[guard:max-position-size] Position for NVDA would be 32.5% of equity (limit: 20%)

cooldown

Enforces a minimum time between trades on the same symbol, preventing impulsive rapid-fire trading.

{
  "type": "cooldown",
  "options": { "minutes": 5 }
}

symbol-whitelist

Restricts trading to a predefined list of symbols.

{
  "type": "symbol-whitelist",
  "options": { "symbols": ["AAPL", "MSFT", "GOOGL", "NVDA", "TSLA"] }
}

Any order for a symbol not in the list is rejected immediately.

Configuring Guards

Guards are configured per-account in data/config/accounts.json:

{
  "id": "alpaca-paper",
  "type": "alpaca",
  "enabled": true,
  "guards": [
    { "type": "max-position-size", "options": { "maxPercentOfEquity": 15 } },
    { "type": "cooldown", "options": { "minutes": 10 } },
    { "type": "symbol-whitelist", "options": { "symbols": ["AAPL", "MSFT"] } }
  ],
  "brokerConfig": { ... }
}

Guards are resolved from the registry at account init time. Unknown guard types are skipped with a warning.

Guard Categories

Each broker type declares a guard category:

BrokerCategory
CCXTcrypto
Alpacasecurities
IBKRsecurities

This categorization is available for guard implementations that need to behave differently for crypto vs traditional securities.

Rejection Flow

When a guard rejects an operation during push:

  1. The operation result is recorded as rejected with the guard name and reason
  2. The commit is still recorded in the git history (with rejection details)
  3. Alice sees the rejection reason and can adjust her approach

This means rejected trades are part of the audit trail — you can always trace why something was blocked.

Custom Guards

You can create custom guards by implementing the OperationGuard interface and registering them. See Custom Guards for a step-by-step guide.