YAML Reference
This page documents the fields the Strategy Lab accepts in each YAML section, what each field does, and the shape of the strategy model those fields create.
Written By Axiom Admin
Last updated 1 day ago
YAML Reference
This page documents the fields the Strategy Lab accepts in each YAML section, what each field does, and the shape of the strategy model those fields create.
The important shift is this: setups, entries, take profits, and stop losses are all named units of work. Setups decide when a trading context is valid. Entries put capital into the market. Take profits and stop losses work against the current position for that direction. They are not tied to an entry ID anymore. They are tied by setup ownership, gate conditions, trigger logic, and the state tokens generated by every named unit.
For expression syntax, see Expression Reference. For built-in and generated token names, see Default Tokens. For diagnostic messages and error codes, see Diagnostics & Error Codes.
When examples use names like EMA_200, EMA_20, RSI_K, or ATR_14, assume those are custom tokens you connected from indicators on your chart. They are not built in.
YAML Structure Basics
Each YAML section is a list of items. Each item starts with a dash (-) at the root indent level, followed by key-value fields indented beneath it.
Example- setup_name: TREND_FILTER
setup_active_when: PRICE_CLOSE > EMA_200
setup_cancel_when: PRICE_CLOSE < EMA_200
- setup_name: PULLBACK_WINDOW
setup_active_when:
PRICE_CLOSE > EMA_200
&& RSI_K < 45
setup_cancel_when: PRICE_CLOSE < EMA_200Indentation matters. Tabs are normalized to spaces, but two-space indentation is still the cleanest habit.
Colons separate keys from values. setup_name: TREND_FILTER is a key-value pair.
Comments start at #. The parser strips everything from # to the end of the line. Avoid # inside expression values.
Expression fields can span multiple lines. Continuation lines must be indented deeper than the field key:
Example- entry_name: COMPLEX_ENTRY
entry_trigger_when:
PRICE_CLOSE > EMA_50
&& RISING(EMA_50, 3)
&& VOLUME > VOLUME[1] * 1.5The engine joins those continuation lines into one expression before compiling it.
Sections
Long and short strategies are configured in separate input areas. Each direction has four section types.
You can leave a section empty. If an entry does not name a setup, it belongs to GLOBAL, which is always available. Exits also default to GLOBAL, but in complex strategies it is usually better to name the setup explicitly so the YAML reads like the trade you meant to build.
Names And State Tokens
Every unit must have a unique name after token sanitization:
Names are also used for order identity where the engine needs an order ID. That means entry_name is not just a label. It is the thing the engine uses to name the entry order and generate state tokens.
Avoid names that collapse into the same token after sanitization. First TP, FIRST_TP, and first-tp are too close for comfort. If two names produce the same token base, the strategy raises a diagnostic error.
The shared lifecycle is deliberately plain. CONFIRMING means the unit's condition is true but its confirmation count is still running. WORKING only applies to entries and exits that have submitted a resting order that has not filled yet. ACTIVE means the unit has completed the job it exists to do in the current lifecycle: a setup has opened its trading context, an entry has filled, or an exit has executed. The shorthand aliases mean the same thing as the longhand tokens; they are just more compact.
Setups
Setups define when a trading window opens. They do not submit orders. They decide whether the entries and exits that belong to them are allowed to evaluate.
Setup Fields
At least one of setup_cancel_when or setup_reset_when must be present. Without one of them, the setup has no defined way to leave its active cycle.
Minimal Setup
Example- setup_name: TREND_FILTER
setup_active_when: PRICE_CLOSE > EMA_200
setup_cancel_when: PRICE_CLOSE < EMA_200Full Setup
Example- setup_name: TREND_FILTER
setup_gate_condition: TIMEFRAME_IS_INTRADAY
setup_active_when:
PRICE_CLOSE > EMA_200
&& RISING(EMA_200, 3)
setup_confirm_type: BAR_COUNT
setup_confirm_count: 3
setup_cancel_when: PRICE_CLOSE < EMA_200
setup_cancel_confirm_count: 2
setup_close_on_cancel: true
setup_cooldown_bars: 5
setup_max_entries_per_activation: 3Entries
Entries define when and how capital enters the market. Each entry belongs to a setup, either explicitly through entry_belongs_to_setup or implicitly through GLOBAL.
Entry Fields
Entry Sizing
entry_allocation_percent splits the strategy's configured position size. If the Properties tab says the strategy uses 10% of equity and the entry allocation is 50, that entry receives 5% of equity.
All entry allocations in the same direction can total any value up to 100%. A single-entry strategy can use entry_allocation_percent: 100 for the full configured size, or a smaller value for reduced exposure. A two-leg ladder might use 50 and 50 for full deployment, or 25 and 25 for half of the configured size.
If the total is below 100, the unused share of the configured position size simply stays out of the market. That is valid and often useful when you want a smaller deployment without changing the Properties tab.
Entry OCA
Entry OCA is for orders that are competing to enter the position.
Example- entry_name: BREAKOUT_LONG
entry_belongs_to_setup: TREND_FILTER
entry_gate_condition: PRICE_CLOSE > EMA_200
entry_trigger_when: PRICE_CLOSE > PRICE_HIGH[1]
entry_order_type: STOP
entry_stop_price: PRICE_HIGH[1] + MIN_TICK
entry_allocation_percent: 50
entry_oca_group: LONG_ENTRY_COMPETE
entry_oca_type: CANCEL
- entry_name: PULLBACK_LONG
entry_belongs_to_setup: TREND_FILTER
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_oca_group: LONG_ENTRY_COMPETE
entry_oca_type: CANCELUse CANCEL when one filled entry should cancel its peers. In a breakout-versus-pullback group, this is usually the plainest behavior: once one entry fills, the remaining sibling orders are cancelled.
Use REDUCE when sibling orders share one intended size pool and you want fills in one order to shrink the remaining sibling quantities instead of cancelling them outright. If one order fills 40% of the planned size, the remaining orders in that OCA group are reduced by that filled quantity. This can be useful for advanced staged entry plans, but it is easier to misread because an order can still exist after a sibling fill while its remaining quantity has changed.
Use NONE when you want the group field effectively disabled.
Minimal Entry
Example- entry_name: EMA_CROSS_OVER
entry_trigger_when: CROSS_OVER(PRICE_CLOSE, EMA_50)
entry_order_type: MARKET
entry_allocation_percent: 100Take Profits
Take profits are independent exit units against the current position for that direction. They do not point at entry IDs. If you need a TP to apply only after a specific entry or setup, use take_profit_belongs_to_setup, state tokens, and gate/trigger logic.
Take Profit Fields
Multiple Full-Exit Take Profits
Because take profits are independent units, more than one TP can use take_profit_allocation_percent: 100. That does not mean you are trying to exit 300% of the position. It means each TP is an alternative full-exit condition. The engine submits exits against the current remaining position and uses reduce-style OCA behavior for exit orders.
Example- take_profit_name: TP_RSI_REVERSAL
take_profit_belongs_to_setup: TREND_FILTER
take_profit_gate_condition: POSITION_ACTIVE
take_profit_trigger_when: CROSS_UNDER(RSI_K, 70)
take_profit_order_type: MARKET
take_profit_allocation_percent: 100
- take_profit_name: TP_FIXED_TARGET
take_profit_belongs_to_setup: TREND_FILTER
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: 100Scaling Out
Use allocations below 100 when you want actual legs.
Example- take_profit_name: TP_HALF
take_profit_belongs_to_setup: TREND_FILTER
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_belongs_to_setup: TREND_FILTER
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: 100Stop Losses
Stop losses are also independent exit units against the current position. They can be full-position alternatives, partial protective legs, break-even stops, invalidation stops, or emergency market exits.
Stop Loss Fields
Full Stop Alternatives
Multiple stop losses can each use stop_loss_allocation_percent: 100 when they represent alternative ways the same position can be invalidated.
Example- stop_loss_name: SL_STRUCTURE_BREAK
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: LOWEST(PRICE_LOW, 10)
stop_loss_lock_prices: false
stop_loss_allocation_percent: 100
- stop_loss_name: SL_MOMENTUM_FAIL
stop_loss_belongs_to_setup: TREND_FILTER
stop_loss_gate_condition: POSITION_ACTIVE
stop_loss_trigger_when: CROSS_UNDER(MOMENTUM_SCORE, 0)
stop_loss_order_type: MARKET
stop_loss_allocation_percent: 100Break-Even After First Profit
You do not need a special "TP filled" field to move a stop. Use remaining-position tokens as the eligibility gate, then let stop_loss_trigger_when: TRUE arm the working stop as soon as the gate is valid.
Example- stop_loss_name: SL_INITIAL
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 - ATR_14 * 2
stop_loss_lock_prices: true
stop_loss_allocation_percent: 100
- stop_loss_name: SL_BREAK_EVEN
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: 100Defaults And Required Fields
Removed Or Rejected Fields
These fields are not part of the current schema:
The parser reports entry_id as a removed-field diagnostic. Other stale entry-ID routing fields are not part of the current schema and may appear as unknown-field warnings. Either way, remove the old field and use the current setup ownership, state-token, and gate/trigger model. Typo fields are warnings, not silent no-ops. Read the diagnostics table before assuming the strategy is behaving as written.
Common Mistakes
For a practical debugging flow, see Troubleshooting. For the diagnostic code map, see Diagnostics & Error Codes.