Guards and risk
Configure deterministic pre-trade risk rules that evaluate every proposed order before it is dispatched — no order reaches a venue without passing all active guards.
Guards are deterministic, synchronous rules that run against every proposed order and the current book state; a single failing guard blocks the order and logs a structured reason.
How guards work
When you approve an order, Qoc runs the guard pipeline before dispatch. Each guard receives the full order object and a snapshot of the current UTA book (positions, buying power, NAV, open orders). Guards are pure functions — they cannot mutate state, place side-effect calls, or read external data.
Guards run sequentially in the order they are listed in desk.toml. The first guard to return block stops the pipeline; subsequent guards are not evaluated. The blocking reason is written to the order file and to qoc logs.
Built-in guards
| Guard key | What it checks | Configurable parameters |
|---|---|---|
| max-order-nav | Order notional must not exceed X% of NAV | max_pct (default 5.0) |
| max-position-nav | Post-fill position must not exceed X% of NAV | max_pct (default 20.0) |
| day-loss-limit | Realized + unrealized loss today must not exceed X% of NAV | max_pct (default 3.0) |
| buying-power | Order notional must not exceed available buying power | no parameters |
| symbol-allowlist | Symbol must appear in an explicit allow list | symbols: [...] |
| symbol-denylist | Symbol must not appear in a deny list | symbols: [...] |
| market-hours | Order must be submitted during configured market hours | tz, open, close |
Configuring guards in desk.toml
[[guard]]
key = "max-order-nav"
enabled = true
max_pct = 3.0
[[guard]]
key = "max-position-nav"
enabled = true
max_pct = 15.0
[[guard]]
key = "day-loss-limit"
enabled = true
max_pct = 2.5
[[guard]]
key = "buying-power"
enabled = true
[[guard]]
key = "symbol-denylist"
enabled = true
symbols = ["GME", "AMC", "BBBY"]
[[guard]]
key = "market-hours"
enabled = true
tz = "America/New_York"
open = "09:30"
close = "16:00"Blocked-order log entry
{
"order_ref": "a3f8b12c",
"timestamp": "2026-07-05T14:23:01Z",
"symbol": "NVDA",
"side": "buy",
"quantity": 200,
"notional_usd": 104200.00,
"result": "blocked",
"guard": "max-order-nav",
"reason": "Order notional 104200.00 is 6.2 pct of NAV (limit 3.0 pct). Reduce quantity or raise guard threshold."
}Guard evaluation order matters
Because the pipeline stops at the first failure, put cheap structural checks (buying-power, market-hours) before expensive percentage checks (max-order-nav, max-position-nav). This avoids unnecessary computation and produces faster, clearer block messages.
To temporarily disable a guard without removing it, set enabled = false. Disabled guards are logged as skipped in the evaluation trace.
Guards run on approved orders only
The guard pipeline runs when you approve a proposed order, not when the agent writes it. If you approve an order that was proposed hours ago, the guard evaluates the current book state, not the state at proposal time. NAV, positions, and buying power may have shifted.
Custom guards for strategy-specific rules
Built-in guards cover standard risk controls. For strategy-specific logic — concentration by sector, options delta limits, correlation caps — implement a custom guard. See the Extending section.