Rules & Risk
Before you get lost in individual fields, understand the shape of the machine. Strategy Lab is not a pile of isolated conditions. It is a small state system:
Written By Axiom Admin
Last updated 1 day ago
Rules & Risk
Before you get lost in individual fields, understand the shape of the machine. Strategy Lab is not a pile of isolated conditions. It is a small state system:
Setups decide when a trading context is valid.
Entries decide when capital enters the market.
Take profits and stop losses decide how the current position is reduced or closed.
Risk controls can stop new entries even when the YAML still looks valid.
If something feels haunted, it is usually not haunted. It is usually one of those layers doing exactly what you asked, while another layer is doing something you forgot you asked.
The Hierarchy
The engine is managing one pot of money per side: the position. Entries can split how that pot gets deployed. Exits are independent units of work against whatever position remains.
That means a take profit does not need to point at a specific entry ID. A stop loss does not need to know which entry filled. If you want an exit to behave as if it is linked to a particular entry, you express that linkage with setup ownership, gate conditions, and generated state tokens.
Setup Ownership
Every setup has a name. Entries and exits can belong to that setup.
Example- setup_name: TREND_FILTER
setup_active_when: PRICE_CLOSE > EMA_200
setup_cancel_when: PRICE_CLOSE < EMA_200An entry attached to TREND_FILTER only evaluates after that setup is active:
Example- entry_name: PULLBACK_LONG
entry_belongs_to_setup: TREND_FILTER
entry_gate_condition: RSI_K < 45
entry_trigger_when: CROSS_OVER(PRICE_CLOSE, EMA_20)
entry_order_type: MARKET
entry_allocation_percent: 100An exit attached to TREND_FILTER uses that same setup context:
Example- stop_loss_name: TREND_BREAK_STOP
stop_loss_belongs_to_setup: TREND_FILTER
stop_loss_gate_condition: POSITION_ACTIVE
stop_loss_trigger_when: TRUE
stop_loss_order_type: STOP
stop_loss_stop_price: EMA_200
stop_loss_allocation_percent: 100If you omit *_belongs_to_setup, the unit belongs to GLOBAL. GLOBAL is useful for simple strategies, but in a multi-setup strategy it can make the YAML harder to reason about. Name the setup when you care about context.
State Tokens
Every named unit creates state tokens.
Setups produce:
Entries, take profits, and stop losses produce:
Read those states literally. CONFIRMING means the rule is holding while the confirmation counter runs. WORKING means a resting order is on the books but has not filled. ACTIVE means the unit has completed its job for this lifecycle.
This is the clean way to create soft links. If a stop should only activate after a specific entry has done its job, use the entry's _ACTIVE token in the stop's gate. The shorthand form works too, so BREAKOUT_LONG_ACTIVE and BREAKOUT_LONGACT carry the same state.
Example- stop_loss_name: BREAKOUT_STOP
stop_loss_belongs_to_setup: TREND_FILTER
stop_loss_gate_condition:
POSITION_ACTIVE
&& BREAKOUT_LONG_ACTIVE
stop_loss_trigger_when: TRUE
stop_loss_order_type: STOP
stop_loss_stop_price: POSITION_AVERAGE_PRICE - ATR_14 * 2
stop_loss_lock_prices: true
stop_loss_allocation_percent: 100If a break-even stop should only activate after a partial TP has likely reduced the position, use remaining-position tokens.
Example- stop_loss_name: BREAK_EVEN_STOP
stop_loss_belongs_to_setup: TREND_FILTER
stop_loss_gate_condition:
POSITION_ACTIVE
&& POSITION_REMAINING_PERCENT <= 50
stop_loss_trigger_when: TRUE
stop_loss_order_type: STOP
stop_loss_stop_price: POSITION_AVERAGE_PRICE
stop_loss_lock_prices: true
stop_loss_allocation_percent: 100The point is not to create a hidden web of special links. The point is to make the logic visible in the YAML.
Entries And Allocation
Entry allocation splits the position budget for the direction. If the strategy Properties tab says the default order size is 10% of equity, then:
Entry allocations in a direction can total any value up to 100%. They do not have to spend the full configured pool. Under-allocating is valid when you want smaller exposure; going over 100% is blocked because it would ask the strategy to deploy more than the Properties-tab position budget.
For laddered entries, use multiple entries whose allocations stay within that cap. If the ladder is meant to use the full configured size, the entries can total 100:
Example- entry_name: LADDER_1
entry_gate_condition: PRICE_CLOSE > EMA_200
entry_trigger_when: PRICE_CLOSE < EMA_20
entry_order_type: LIMIT
entry_limit_price: EMA_20
entry_allocation_percent: 50
- entry_name: LADDER_2
entry_gate_condition: PRICE_CLOSE > EMA_200
entry_trigger_when: PRICE_CLOSE < EMA_50
entry_order_type: LIMIT
entry_limit_price: EMA_50
entry_allocation_percent: 50Entry OCA
One-cancels-all logic belongs most naturally on entries. Sometimes you want a breakout entry and a pullback entry to compete. Sometimes you want staged orders where one fill cancels the alternatives. Sometimes you may want reduce behavior for a more advanced order set.
Entry OCA has two fields:
Exampleentry_oca_group: LONG_ENTRY_COMPETE
entry_oca_type: CANCELUse entry_oca_group to name the group. Use entry_oca_type to choose behavior: CANCEL, REDUCE, or NONE.
If you set entry_oca_type without entry_oca_group, the engine warns you because there is no group for the type to act on.
Exits Are Independent Units
Take profits and stop losses are not summed into one allocation budget. Each exit is its own unit of work against the current remaining position.
For resting limit or stop exits, put POSITION_ACTIVE in the gate. The gate says the exit is allowed to work only while a position exists. If no extra event is needed after that, use *_trigger_when: TRUE; the limit or stop price decides where the order rests.
This matters.
You can have several full-exit take profits:
Example- take_profit_name: TP_FIXED_TARGET
take_profit_gate_condition: POSITION_ACTIVE
take_profit_trigger_when: TRUE
take_profit_order_type: LIMIT
take_profit_limit_price: POSITION_AVERAGE_PRICE * 1.03
take_profit_lock_prices: true
take_profit_allocation_percent: 100
- take_profit_name: TP_MOMENTUM_FADE
take_profit_gate_condition: POSITION_ACTIVE
take_profit_trigger_when: CROSS_UNDER(RSI_K, 70)
take_profit_order_type: MARKET
take_profit_allocation_percent: 100That is not a 200% exit request. It is two alternative ways to close the same position. The engine works against the remaining position and uses reduce-style OCA behavior for exit orders so sibling exits do not try to over-close the same pot of money.
You can also build real legs:
Example- take_profit_name: TP_HALF
take_profit_gate_condition: POSITION_ACTIVE
take_profit_trigger_when: TRUE
take_profit_order_type: LIMIT
take_profit_limit_price: POSITION_AVERAGE_PRICE * 1.02
take_profit_lock_prices: true
take_profit_allocation_percent: 50
- take_profit_name: TP_FINAL
take_profit_gate_condition:
POSITION_ACTIVE
&& POSITION_REMAINING_PERCENT <= 50
take_profit_trigger_when: TRUE
take_profit_order_type: LIMIT
take_profit_limit_price: POSITION_AVERAGE_PRICE * 1.05
take_profit_lock_prices: true
take_profit_allocation_percent: 100The first TP attempts to close half. The second TP waits until the remaining position is at or below half, then attempts to close the rest.
Stop Logic After Scaling Out
A common pattern is:
Take partial profit.
Move the stop to break even.
Disable the original wider stop.
In Strategy Lab, you do this with gates.
Example- stop_loss_name: INITIAL_STOP
stop_loss_gate_condition:
POSITION_ACTIVE
&& POSITION_REMAINING_PERCENT > 50
stop_loss_trigger_when: TRUE
stop_loss_order_type: STOP
stop_loss_stop_price: POSITION_AVERAGE_PRICE - ATR_14 * 2
stop_loss_lock_prices: true
stop_loss_allocation_percent: 100
- stop_loss_name: BREAK_EVEN_STOP
stop_loss_gate_condition:
POSITION_ACTIVE
&& POSITION_REMAINING_PERCENT <= 50
stop_loss_trigger_when: TRUE
stop_loss_order_type: STOP
stop_loss_stop_price: POSITION_AVERAGE_PRICE
stop_loss_lock_prices: true
stop_loss_allocation_percent: 100The stop-loss priority is not a hidden priority field. The priority comes from the conditions. Once the remaining position is smaller, the initial stop's gate closes and the break-even stop's gate opens.
Risk Controls
Risk controls are strategy-level brakes. They can prevent new entries even when setups and entries are otherwise valid.
There are two different kinds of brakes:
Strategy Lab logic brakes come from your YAML and engine state. Examples include setup gates, entry gates, setup cooldowns, max entries per activation, and conditions based on position or strategy tokens.
TradingView risk stops come from the Inputs tab and are enforced by TradingView's
strategy.risksystem. Examples include max strategy drawdown, max consecutive loss days, max intraday loss, and max intraday filled orders.
That distinction matters. A YAML gate can open again when its condition changes. A setup cooldown can expire. A TradingView risk stop is outside the YAML engine. When it trips, TradingView can cancel pending orders, close the open position, and block new order activity according to that stop's reset behavior.
Strategy settings such as pyramiding, order size, slippage, commission, and fill model also shape risk, but they are test assumptions rather than rule conditions. They change what the strategy tester reports even when your YAML is unchanged.
The important debugging habit is this: when trades stop appearing, do not only stare at the entry trigger. Check the full chain:
Is the direction enabled?
Is the setup active?
Is the entry gate true?
Is the entry trigger true?
Has the entry already fired under one-shot rules?
Has the setup hit its max entries per activation?
Has a risk brake halted new entries?
Does the order have a valid quantity and price?
Position Cycles
A position cycle starts when the strategy moves from flat into a position, or changes direction. It ends when the position closes or reverses.
At cycle boundaries, runtime state is reset so old exit state does not leak into the next trade. This matters for latches, working exit orders, remaining-position logic, and state tokens.
If you are testing a complex strategy, watch these position tokens:
ExamplePOSITION_ACTIVE POSITION_DIRECTION POSITION_AVERAGE_PRICE POSITION_REMAINING_QUANTITY POSITION_REMAINING_PERCENT POSITION_EXITED_PERCENT
They tell you what the engine thinks it is managing right now.
The Mature Read
The mature reading is not "my entry condition is true, so why didn't I get a trade?"
It is:
"My entry can only fire if its owning setup is active, its gate and trigger pass, it has not already exhausted one-shot or pyramiding limits, entry allocation is valid, risk controls allow another order, and the order can be constructed at a valid price and quantity. My exits can only fire if a position exists, their gates and triggers pass, their price expressions are valid, their allocation creates a nonzero quantity, and their working orders have not been cancelled or made irrelevant by the position changing."
That sentence is a little uncomfortable. It is also the work. The tool gives you room to express all of that without writing Pine, but it still expects you to know what you asked for.