Qoc

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 keyWhat it checksConfigurable parameters
max-order-navOrder notional must not exceed X% of NAVmax_pct (default 5.0)
max-position-navPost-fill position must not exceed X% of NAVmax_pct (default 20.0)
day-loss-limitRealized + unrealized loss today must not exceed X% of NAVmax_pct (default 3.0)
buying-powerOrder notional must not exceed available buying powerno parameters
symbol-allowlistSymbol must appear in an explicit allow listsymbols: [...]
symbol-denylistSymbol must not appear in a deny listsymbols: [...]
market-hoursOrder must be submitted during configured market hourstz, open, close

Configuring guards in desk.toml

All guards are under the [[guard]] array
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

Written to orders/blocked/ and emitted by qoc logs
json
{
  "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.