Veto and reason codes
Every code Guard, the risk engine and the browser tools use to say why an entry was refused or resized.
A refusal always carries a code (stable, for programs) and a reason (a sentence, for people). Codes are snake_case and never change meaning; new ones may be added.
Codes marked run a tiny example through the real risk engine in your browser.
Risk engine: in code today
Section titled “Risk engine: in code today”Veto in crates/zunder-risk/src/engine.rs. The codes are the variant names in snake_case, as the WebAssembly build serialises them (VetoCode in crates/zunder-risk-wasm/src/api.rs).
| Code | Rule | Reason (the engine’s own) | Example |
|---|---|---|---|
halted_for_day | (h) | trading is halted for the day | down 6% since 00:00 UTC |
stopped | (i) | trading is stopped until a manual review | 25% below the peak |
stop_on_wrong_side | (g) | the stop is not on the losing side of the entry price | buy at 100, stop at 101 |
open_risk_exhausted | (e) | the open-risk budget is used up | stops already risk 6% |
leverage_exhausted | (c) | the leverage cap is reached | positions already worth 5× equity |
below_minimum | (g) | the sized quantity is below the venue minimum | the allowed size is worth less than the minimum order |
unprotected_position | (e) | an open position has no protective stop, or its price is at or through it | an ETH position without a stop blocks a BTC entry |
invalid_request | (g) | the sizing request contains a negative or non-positive amount | a price of 0 |
overflow | (g) | the numbers are too large or too small to size safely | absurd inputs |
Policy: in the website’s judge (being built)
Section titled “Policy: in the website’s judge (being built)”crates/zunder-risk-wasm/src/judge.rs. Planned to be the same in Guard’s policy.
| Code | Rule | Example reason |
|---|---|---|
coin_not_allowed | (a) | HYPE is not on the market allowlist |
no_protective_stop | (b) | no stop order protects the ETH position |
unbounded_risk | (b) | neither a stop nor a liquidation price bounds the loss |
liquidation_too_close | (d) | liquidation is 7% from the price; the minimum is 10% |
position_cap_reached | (f) | a position may be worth at most 200% of account value |
equity_not_positive | (g) | the account has no positive equity |
Resizes carry the rule that bound the size instead of a code: max_leverage, max_open_risk, max_position_size or max_loss_per_trade, with a reason such as “a stop-out may lose at most 2% of equity”.
Watch: account warnings (being built)
Section titled “Watch: account warnings (being built)”crates/zunder-risk-wasm/src/account.rs. Not refusals: warnings about the account as a whole.
| Code | Meaning |
|---|---|
halted_for_day | the daily loss stop is reached |
stopped | the drawdown halt is reached |
position_without_stop | a position has no protective stop |
open_risk_over_cap | open risk is over your cap |
leverage_over_cap | leverage is over your cap |
Guard only: planned
Section titled “Guard only: planned”| Code | Meaning |
|---|---|
killed | the kill switch is pulled; nothing opens until a person releases it |
client_not_allowed | the request was signed by a key that is not a client of this Guard |
nonce_reused | the nonce was already used, or is too old |
action_not_allowed | an action Guard never forwards: withdrawals, transfers, key or builder approvals |
stop_loosening_ignored | not a refusal: the stop stayed where it was (Stops only tighten) |
not_ready | Guard is reconciling, or waiting out its start-up wait |
margin_not_set | isolated margin could not be confirmed before the entry |
no_safe_leverage | no leverage of 1x or more keeps liquidation beyond the stop |
The last two follow SessionError::MarginNotSet and SessionError::NoSafeLeverage in Zunder’s session, which exist today.
How a refusal reaches your bot
Section titled “How a refusal reaches your bot”This page as plain Markdown, for people and LLMs: /docs/reference/veto-codes.md