Rules & Risk

Before you read any detail on this page, understand the shape of the system. The strategy builder has a hierarchy, and if you skip the hierarchy and jump into individual fields, you will spend hours debugging problems...

Written By Axiom Admin

Last updated About 1 month ago

Rules & Risk

Before you read any detail on this page, understand the shape of the system. The strategy builder has a hierarchy, and if you skip the hierarchy and jump into individual fields, you will spend hours debugging problems that are actually ownership problems β€” an entry attached to the wrong setup, an exit targeting the wrong entry group, a risk control silently halting everything while you stare at your YAML wondering why nothing fires.

Learn the hierarchy first. The details will make sense once you know where each piece lives. For the complete field-level reference of every YAML section, see YAML Reference. For the tokens and expressions used in condition fields, see Default Tokens and Expression Reference.


The YAML hierarchy

Your trade logic is organized into four layers per direction (long and short). Each layer depends on the one above it.

Layer

What it defines

What it depends on

Setups

Conditions that must be met before any entries are allowed to evaluate

Direction mode (Long Only, Short Only, Swing Mode)

Entries

When and how to submit entry orders

A confirmed setup (explicit or GLOBAL)

Take Profits

When and how to exit for profit

An open trade from a specific entry or entry group

Stop Losses

When and how to exit for protection

An open trade from a specific entry or entry group

The ownership chain runs top-down:

  • Every entry belongs to a setup (named by belongs_to_setup, or the implicit GLOBAL setup if not specified).

  • Every take profit and stop loss belongs to a setup (named by belongs_to_setup, or GLOBAL if not specified) and optionally targets a specific entry group (via from_entry_id). If from_entry_id is omitted, exits in a named setup target all entries in that setup. Exits in GLOBAL with no from_entry_id target every entry group in the direction β€” see YAML Reference for the full scoping rules.

If you get this ownership wrong, the engine will still run β€” but conditions will gate against the wrong state, exits will target the wrong position, and your results will not mean what you think they mean. The schema summary table (see Quick Start) is your first check: do the parsed counts match what you intended?

The GLOBAL setup

Every enabled direction has an implicit setup called GLOBAL. It starts in CONFIRMED state and never cancels. You cannot configure it β€” it is always present, always confirmed.

If an entry does not specify belongs_to_setup, it is attached to GLOBAL. This means it will evaluate every bar without any setup gating. For simple strategies with a single entry condition, this is fine. For complex multi-setup strategies, leaving entries on GLOBAL when you meant to attach them to a named setup is one of the most common configuration mistakes.

Check: if you defined named setups but your entries do not specify belongs_to_setup, those entries are ignoring your setups entirely.


Setup state machine

Named setups (not GLOBAL) follow a three-state lifecycle that controls when their attached entries are allowed to evaluate.

State

What it means

Entries can evaluate?

INACTIVE

The setup's conditions are not met, or the setup is in cooldown after a cancel or reset

No

CONFIRMING

The activation condition fired and the confirmation counter is counting toward the threshold

No

CONFIRMED

Confirmation complete. The setup is live.

Yes

Note: the engine publishes a <SETUP_NAME>_ACTIVE token, but this is not a distinct state. It is true whenever the setup is not INACTIVE β€” meaning either CONFIRMING or CONFIRMED. If no confirmation is configured (confirm_count = 0), the setup transitions directly from INACTIVE to CONFIRMED, skipping CONFIRMING entirely.

What moves the state

  • Gate condition (gate_condition): A boolean expression that must be true before activation can begin. Think of it as a prerequisite β€” a regime filter, a time window, a volatility threshold. Defaults to true (always passing).

  • Activation expression (active_when): The primary trigger. When the gate passes and this expression is true, the setup transitions from INACTIVE to CONFIRMING (or directly to CONFIRMED if no confirmation is configured).

  • Confirmation (confirm_type + confirm_count): Requires the activation condition to remain true for a specified number of bars (BAR_COUNT) or script ticks (NUM_TICKS) before the setup reaches CONFIRMED. This is a persistence filter β€” it weeds out conditions that flash true for one bar and disappear.

  • Cancel expression (cancel_when): Forces the setup back to INACTIVE. If cancel confirmation is configured, the cancel itself must persist before taking effect. When close_on_cancel is true, cancellation also closes all open positions from entries belonging to this setup and cancels their pending orders.

  • Reset expression (reset_when): A hard reset. Clears all intent states, latches, counters, and forces the setup to INACTIVE. More aggressive than cancel.

  • Cooldown (cooldown_bars): After a cancel or reset, the setup stays INACTIVE for this many bars before re-activation is allowed.

  • Entry cap (max_entries_per_activation): Limits how many entry fills the setup allows during a single activation window. Once the cap is reached, no more entries will fire until the setup resets and re-activates.

The transition flow

INACTIVE ──(gate passes + activation fires)──▢ CONFIRMING (or CONFIRMED if confirm_count = 0)CONFIRMING ──(count reaches threshold)──▢ CONFIRMEDCONFIRMED ──(cancel fires)──▢ INACTIVE (with optional cooldown)Any state ──(reset fires)──▢ INACTIVE (hard reset, with optional cooldown)

What trips people up

The most common misread is expecting entries to fire as soon as the activation expression is true. They do not. If confirmation is configured, the setup must pass through CONFIRMING first. If a gate condition is defined, it must also be true. If the setup was recently cancelled, cooldown bars must elapse. Each of these delays is invisible unless you enable expression diagnostics and check the setup's state tokens (<SETUP_NAME>_CONFIRMED, <SETUP_NAME>_ACTIVE, <SETUP_NAME>_CONFIRMING, etc.).

Scenario: You define a setup called TREND_FILTER with a gate on a 200-bar EMA slope, an activation on price crossing above the EMA, and a 3-bar confirmation. You expect entries to start firing once price crosses above the EMA and holds for three bars. But nothing fires. You check the diagnostics: the setup is stuck in INACTIVE. The activation expression is true, but the gate β€” the EMA slope β€” is negative. The EMA has not caught up to the price move yet. The activation condition is true, but the gate is blocking it from transitioning to CONFIRMING. You stare at the YAML for an hour before you realize the gate must pass before the activation is even considered.

This is the shape of most state machine debugging: the user assumes the conditions are sequential (gate then activation then confirmation), but they are actually simultaneous requirements at each step. The gate must still be true during confirmation, not just when activation started. If the gate drops false while the setup is in CONFIRMING, the confirmation may reset.

If you are seeing zero trades from a named setup, trace the state: is the gate passing? Is the activation firing? Is confirmation counting? Is cooldown blocking re-entry? The diagnostics will tell you.


Entry evaluation

Once a setup reaches CONFIRMED, its attached entries become eligible. But eligible does not mean they fire immediately. Each entry has its own evaluation sequence:

  1. Direction check β€” is the entry's direction enabled in the current direction mode?

  2. Setup check β€” is the owning setup CONFIRMED?

  3. Gate condition β€” the entry's own gate expression must be true. This is separate from the setup's gate.

  4. Trigger condition (trigger_when) β€” the primary entry signal. Both the gate and the trigger must pass on the same bar.

  5. Confirmation β€” if the entry has its own confirmation requirement, the trigger must persist before the order is submitted.

  6. Entry cap check β€” has the owning setup already reached its max_entries_per_activation limit?

  7. Order construction β€” the engine computes the order type (MARKET, LIMIT, STOP, or STOPLIMIT), the allocation size, and limit/stop prices from your expressions.

  8. Fire eligibility:

  • One-shot entries (default): fire once per setup activation. After the entry submits once for that activation β€” even if a working order later expires or is cancelled β€” it will not re-submit until the setup resets.

  • Pyramiding entries (oneShot = false): can fire multiple times, up to pyramidingMaxAdds + 1 concurrent open trades for this entry ID.

  1. Submission β€” the order goes to TradingView's broker emulator.

  2. Pending order management β€” for non-MARKET orders: the engine tracks the order, applies expiration (entry_expire_after_bars), and cancels on cancel_when, expiry, invalid prices, or zero quantity.

What to watch for

  • One-shot is the default. If you do not set oneShot = false, your entry fires once per setup activation and then stops. If you expected it to fire repeatedly, you need to explicitly enable pyramiding behavior.

  • Gate and trigger are independent. The gate is a prerequisite (like a regime filter). The trigger is the actual signal. Both must be true on the same bar. A common mistake is putting the signal logic in the gate and the filter logic in the trigger β€” the engine does not care which is which, but your mental model will suffer.

  • Limit and stop orders can expire. If entry_expire_after_bars is set, non-MARKET orders are automatically cancelled after the configured number of bars if not filled. If your strategy uses limit entries in a slow market, a short expiration will cancel orders before they have a chance to fill. If the expiration is too long, orders will fill at prices you no longer want.

  • OCA groups β€” entries that share an OCA (One-Cancels-All) group name are mutually exclusive. When one fills, the others in the same group are cancelled. Use this to define alternative entry conditions where you want whichever triggers first, not both.


Exit evaluation

Exits β€” take profits and stop losses β€” are evaluated per entry group. An entry group is the set of open trades that share the same entry order ID.

For each entry group with open trades, the engine collects all applicable exits (both TPs and SLs that match by from_entry_id or by setup ownership) and evaluates them together:

  1. Gate condition β€” each exit has its own gate expression.

  2. Trigger condition β€” the exit's primary signal.

  3. Cancel condition β€” if the exit's cancel expression fires, the exit is deactivated for the current cycle.

  4. Confirmation β€” same as entries, if configured.

  5. Sizing β€” the engine calculates the exit quantity:

  • The baseline is the anchor quantity: the peak open quantity observed for this entry group while it was open.

  • Each exit's allocationPct is applied against the anchor, not the current open quantity.

  • If total TP allocation exceeds 100%, all TP allocations are scaled down proportionally.

  • Actual order quantities are clamped to the currently available open position.

  1. Order submission:

  • MARKET exits use immediately = true, meaning they execute on the same bar the trigger fires β€” not on the next bar's open. This is different from MARKET entries, which fill on the next bar's open.

  • LIMIT/STOP/STOPLIMIT exits are submitted as working orders with OCA.reduce groups that share a position budget across the exit ladder.

  1. Price latching β€” when lock_prices = true, the limit or stop price is captured the first time the trigger fires and then frozen. The order works at that price until it fills, cancels, or the position closes. Without latching, the price expression recalculates every bar.

Why anchored sizing matters

If you define two take profits at 50% each, you probably expect each one to close half your position. Without an anchor, the first TP closes 50% of the current position, and the second TP closes 50% of what remains β€” which is only 25% of the original. Anchored sizing solves this by computing both quantities against the peak, so each TP closes 50% of the original regardless of fill order.

This is a detail that does not matter until it matters a lot. If your exit allocations do not add up the way you expect, see For the Geeks for the full mental model.


Risk controls

The strategy has four circuit breakers that halt all new entry orders when triggered:

Control

Default

What it does

Max strategy drawdown

20% of equity

Stops all entries when the strategy's drawdown from peak equity exceeds the threshold

Max consecutive loss days

5

Stops all entries after N consecutive losing days

Max intraday loss

10% of equity

Stops all entries when intraday loss exceeds the threshold

Max intraday filled orders

10

Stops all entries after N fills in one session

What you need to know about these

They are circuit breakers, not risk management. They trigger after the damage has already occurred. They stop further bleeding β€” they do not prevent the wound.

They are silent. When a circuit breaker trips, entries simply stop firing. There is no popup, no special alert, no red banner. The only evidence is that trades stop appearing in the trade list and the error table may still say "Strategy Active." If you are looking at a backtest that seems to stop trading partway through, check whether a risk limit was reached before you assume your entry conditions stopped working.

They are strategy-level, not setup-level. A single bad day can halt trading across all setups, all directions. If you have a multi-setup strategy and one setup has a drawdown event, the circuit breaker does not distinguish β€” it halts everything.

Primary risk management should live in your position sizing, your entry logic, and your exit discipline. These controls are the last line of defense, not the first.


How the pieces interact

The full evaluation flow on each bar, simplified:

  1. The engine refreshes position state and token maps.

  2. For each enabled direction:

  • Each setup evaluates its state machine (gate β†’ activation β†’ confirmation β†’ cancel/reset).

  • Each entry belonging to a confirmed setup evaluates its gate β†’ trigger β†’ confirmation β†’ order logic.

  • Each exit group evaluates its exits against the current open trades.

  1. Strategy-level risk circuit breakers are already configured on the script and can block new entries while this loop runs.

  2. Position state is refreshed again.

The key interaction points:

  • Setup confirmation gates entries. No confirmed setup, no entry evaluation.

  • Entry fills create exit targets. Until an entry fills, its take profits and stop losses have nothing to work against.

  • Position cycles reset state. When the position changes sign (flat β†’ long, long β†’ short, etc.), all exit-leg tracking, anchored quantities, and intent states are cleared. A new cycle starts fresh.

  • Swing Mode adds direction reversals. A long entry can close an existing short position. This is the most complex interaction in the engine. When a reversal happens, several things occur at once: the short position closes, a new long position opens, all exit-leg tracking for the short side resets, and the position cycle counter increments. Any pending short-side exits are cleared. Any short-side setups that had confirmed states lose their exit targets. If you also have pyramiding enabled, the new long entry adds to the long side while the short close triggers a cycle boundary that resets all the tracking your long-side exits depend on. The YAML reads as two clean directions. The runtime produces a cascade of state changes that is not obvious from the YAML alone. Test Swing Mode in isolation before combining it with complex multi-setup logic β€” and when you do combine them, use the diagnostics to watch the position cycle counter and the exit-leg state changes around reversals.

The naive reading vs. the mature reading

The naive reading of a strategy's rules is: "if my conditions are true, the engine enters; if my exit conditions are true, it exits." That reading is correct enough to get started but not correct enough to debug problems.

The mature reading is: "my entry can only fire if its owning setup is confirmed, its gate and trigger both pass on the same bar, it has not already fired (if one-shot), the entry cap has not been reached, the risk controls have not halted trading, and the order construction produces a valid quantity at a valid price. My exit can only fire if the entry group has open trades, the exit's gate and trigger pass, the sizing math produces a nonzero quantity, and the order is not cancelled by a cancel condition or a position cycle boundary."

Most debugging happens in the gap between those two readings. When your strategy does something you did not expect, the answer is almost always somewhere in that list of conditions.