Teaching Examples

This page gives you four complete long-side strategy configurations, from simple to complex. They are not trade recommendations. They are bench tests you can paste, run, question, and take apart until the Strategy Lab...

Written By Axiom Admin

Last updated 1 day ago

Teaching Examples

This page gives you four complete long-side strategy configurations, from simple to complex. They are not trade recommendations. They are bench tests you can paste, run, question, and take apart until the Strategy Lab starts to feel less like a form and more like a machine you understand.

Each example has multiple YAML blocks. Paste each block into the matching Strategy Lab input section: Long Setups into Long Setups, Long Entries into Long Entries, Long Take Profits into Long Take Profits, and Long Stop Losses into Long Stop Losses.

These examples are written to paste cleanly when the required custom tokens are connected. That matters because the Strategy Lab now warns when a token is unavailable during warm-up. A setup gate does not automatically protect every other field. If an entry gate, trigger, take profit, or stop references a warming token, that field needs its own NZ, SAFE_DIV, or warm-up guard.

That is not busywork. It is the difference between "this rule is intentionally quiet until the data exists" and "this rule accidentally depended on a value that was not there yet."

Use the token tables before you paste the YAML. Connect each custom token to the matching indicator plot first, then paste each block into its matching Strategy Lab section. If the diagnostics table names a token after that, check the token binding before rewriting the strategy.

Read the fallbacks literally. NZ(RSI_K, 50) means "treat missing RSI as neutral." NZ(EMA_20, PRICE_CLOSE) means "do not create an artificial EMA signal before the EMA exists." SAFE_DIV(x, y, fallback) means "if the denominator is missing or zero, use the fallback instead of poisoning the whole expression."

Read gates and triggers as two different questions.

  • A gate condition answers: "Is this unit even allowed to think yet?" Use gates to link one unit of work to another unit's state. A setup can expose L_INT_UPTREND_ACTIVE. An entry can expose L_EXT_PULLBACK_PRIME_ACTIVE. The position exposes POSITION_ACTIVE and POSITION_REMAINING_PERCENT. Those belong in gates when they are eligibility rules.

  • A trigger condition answers: "Now that this unit is eligible, what makes it do its job?" Put the actual entry signal, exit signal, breakout, cross, or failure condition there.

  • If a take profit should only exist after a certain entry has filled, put that entry's active token in take_profit_gate_condition, then put the actual profit-taking event in take_profit_trigger_when.

  • If a resting limit target or stop should be submitted as soon as the gate is true, use TRUE as the trigger. That is not a shortcut. It means, "No extra event is needed after eligibility; place the working order."

Do not optimize these examples on the first pass. First prove that the structure behaves. Then change one thing at a time. A strategy you cannot explain is not a strategy yet; it is just YAML with confidence painted on top.

For the complete field list, see YAML Reference. For tokens, see Default Tokens. For expression syntax and safe math patterns, see Expression Reference.


Beginner - Single Crossover With Fixed Exits

What this teaches: one entry, one take profit, one stop loss, no named setup, and the simplest warm-up-safe custom token pattern.

The Trading Idea

Enter long when price crosses above a 50 EMA. Once the position is open, place one fixed profit target and one fixed stop. Nothing here is trying to be clever. That is the point. This is the smallest useful Strategy Lab shape: one way in, two ways out.

The Strategy Lab Idea

There is no setup in this example, so the entry belongs to the global strategy context. The entry creates the position. The take profit and stop loss do not belong to the entry directly; they link to the overall position with POSITION_ACTIVE.

That is the first mental model to get right: entries build exposure, exits reduce or close whatever exposure exists.

Custom tokens needed:

Token

Type

Source

EMA_50

Number

50-period EMA

The EMA is wrapped with NZ(EMA_50, PRICE_CLOSE). On the first bars of a chart, the EMA may not exist yet. Falling back to current price keeps the crossover quiet until the EMA is usable.

Why The YAML Looks Like This

  • entry_name gives this unit its identity. If you later inspect generated state tokens or order names, this is the name you should recognize.

  • entry_trigger_when is the actual entry moment. The market order is not submitted until the crossover becomes true.

  • take_profit_gate_condition: POSITION_ACTIVE and stop_loss_gate_condition: POSITION_ACTIVE link those exits to the open position. The exits do not even try to work while there is nothing to manage.

  • take_profit_trigger_when: TRUE and stop_loss_trigger_when: TRUE mean the working target and stop should be submitted as soon as the position link is valid.

  • NZ(EMA_50, PRICE_CLOSE) prevents a warm-up warning and avoids inventing a signal before the EMA exists.

  • POSITION_AVERAGE_PRICE makes the exits calculate from the filled position price, not from the current candle.

  • take_profit_lock_prices: true and stop_loss_lock_prices: true freeze the target and stop when the exit order is created. Without locking, dynamic prices can keep moving as the chart updates.

Long Entries

Example
- entry_name: L_BEG_EMA_CROSS entry_trigger_when: CROSS_OVER(PRICE_CLOSE, NZ(EMA_50, PRICE_CLOSE)) entry_order_type: MARKET entry_allocation_percent: 100

Long Take Profits

Example
- take_profit_name: L_BEG_TP_FIXED 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

Long Stop Losses

Example
- stop_loss_name: L_BEG_SL_FIXED stop_loss_gate_condition: POSITION_ACTIVE stop_loss_trigger_when: TRUE stop_loss_order_type: STOP stop_loss_stop_price: POSITION_AVERAGE_PRICE * 0.98 stop_loss_lock_prices: true stop_loss_allocation_percent: 100

What To Verify

  1. Direction is Long Only. This proves you are testing the same side as the YAML.

  2. Schema summary shows 1 long entry, 1 long take profit, and 1 long stop loss. This proves the YAML was parsed into the expected units.

  3. The diagnostics table stays clean when EMA_50 is connected. This proves the token binding and warm-up fallback are working.

  4. Trades appear near EMA crossovers. This proves the entry trigger is doing the thing you asked it to do.

  5. The trade list shows exits near the fixed 3% target or 2% stop. This proves the exits are watching the open position.

If It Does Not Behave

  • If the diagnostics table names EMA_50, check the custom token binding first. Do not rewrite the YAML until the token is actually connected.

  • If there are no trades, zoom out and check whether price actually crossed above the EMA during the tested range.

  • If entries happen but exits do not, make sure the take profit YAML went into Long Take Profits and the stop YAML went into Long Stop Losses.

Try One Controlled Change

Change the take profit from 1.03 to 1.015. Do not change anything else. Run it again and confirm that exits move closer. This is how you learn the lab without losing the thread.


Intermediate - Trend Setup With Scale-Out

What this teaches: named setup, setup confirmation, setup-owned entry and exits, partial take profit, and break-even stop gating.

The Trading Idea

Only buy pullbacks when the broader trend is already up. The 200 EMA defines the trend. RSI defines the pullback. The 20 EMA gives the re-entry trigger. Once the trade is open, take half off at the first target, then let the rest try for a larger target with a break-even stop underneath it.

This is a common trader thought: "I want to be paid quickly, then remove some risk and see if the rest can run."

The Strategy Lab Idea

The setup is the regime filter. It answers, "Are we even allowed to think long yet?" The entry belongs to that setup, so it should not fire while the setup is inactive. The exits also belong to the setup, but they still manage the overall open position.

The important teaching point is the scale-out. The first take profit closes 50%. After that happens, POSITION_REMAINING_PERCENT should drop to 50 or lower. That one position token becomes a gate: it turns off the wider initial stop and turns on the break-even stop.

Custom tokens needed:

Token

Type

Source

EMA_200

Number

200-period EMA

EMA_20

Number

20-period EMA

RSI_K

Number

RSI line

Notice that the setup, entry gate, and entry trigger each protect their own warming tokens. The setup's BAR_INDEX > 200 gate is useful, but the entry still needs to handle RSI_K and EMA_20 directly because diagnostics read each expression field on its own.

Why The YAML Looks Like This

  • setup_gate_condition: BAR_INDEX > 200 keeps the setup quiet until the 200 EMA has had enough chart history to mean something.

  • setup_active_when and setup_cancel_when both wrap EMA_200 anyway. The gate is for trading logic; the fallback is for clean diagnostics.

  • setup_confirm_count: 3 requires the trend condition to hold for three bars before the setup becomes active. One candle does not get to decide the whole regime.

  • entry_belongs_to_setup: L_INT_UPTREND links the entry to the setup state. The entry can still have its own gate and trigger, but it is not floating loose.

  • NZ(RSI_K, 50) treats missing RSI as neutral. Neutral RSI is not below 40, so it will not accidentally approve a pullback during warm-up.

  • The first take profit links to POSITION_ACTIVE, then uses TRUE as the trigger because a resting limit target should be placed as soon as a position exists.

  • The final take profit links to POSITION_REMAINING_PERCENT <= 50, then uses TRUE as the trigger because it is another resting limit target.

  • The two stops are not ranked by priority. They are separated by gates. Before the half take profit, the initial stop is eligible. After the half take profit, the break-even stop is eligible.

Long Setups

Example
- setup_name: L_INT_UPTREND setup_gate_condition: BAR_INDEX > 200 setup_active_when: PRICE_CLOSE > NZ(EMA_200, PRICE_CLOSE) setup_confirm_count: 3 setup_cancel_when: PRICE_CLOSE < NZ(EMA_200, PRICE_CLOSE) setup_cancel_confirm_count: 2 setup_cooldown_bars: 5

Long Entries

Example
- entry_name: L_INT_PULLBACK entry_belongs_to_setup: L_INT_UPTREND entry_gate_condition: BAR_INDEX > 200 && NZ(RSI_K, 50) < 40 entry_trigger_when: CROSS_OVER(PRICE_CLOSE, NZ(EMA_20, PRICE_CLOSE)) entry_confirm_count: 1 entry_order_type: MARKET entry_allocation_percent: 100

Long Take Profits

Example
- take_profit_name: L_INT_TP_HALF take_profit_belongs_to_setup: L_INT_UPTREND 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: 50 - take_profit_name: L_INT_TP_FINAL take_profit_belongs_to_setup: L_INT_UPTREND 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.06 take_profit_lock_prices: true take_profit_allocation_percent: 100

Long Stop Losses

Example
- stop_loss_name: L_INT_SL_INITIAL stop_loss_belongs_to_setup: L_INT_UPTREND 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 * 0.97 stop_loss_lock_prices: true stop_loss_allocation_percent: 100 - stop_loss_name: L_INT_SL_BREAK_EVEN stop_loss_belongs_to_setup: L_INT_UPTREND 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: 100

What To Verify

  1. L_INT_UPTREND_ACTIVE becomes true only after three bars above the 200 EMA. This proves setup confirmation is working.

  2. The entry does not fire before the setup is active. This proves entry_belongs_to_setup is doing real work.

  3. The diagnostics table stays clean when EMA_200, EMA_20, and RSI_K are connected. This proves each expression field is handling its own warm-up.

  4. The first TP closes about half the position. This proves exit allocation is acting against the current position.

  5. After the half TP fills, POSITION_REMAINING_PERCENT drops and the break-even stop becomes eligible.

  6. The old wider stop is gated off after the scale-out. You should not have both stop styles logically active for the same remaining position.

If It Does Not Behave

  • If the setup never activates, check EMA_200 first, then check whether price actually stays above it for three bars.

  • If the entry never fires, check RSI_K < 40 and the crossover separately. A valid uptrend with no pullback is not a broken strategy.

  • If the break-even stop never becomes eligible, the half take profit probably did not fill. Watch POSITION_REMAINING_PERCENT before blaming the stop.

  • If you see a warm-up warning, read the named token. A setup gate elsewhere does not protect an entry field that references an unguarded token.

Try One Controlled Change

Change NZ(RSI_K, 50) < 40 to NZ(RSI_K, 50) < 50. That should allow more pullbacks through the gate. If trade frequency does not change at all, the RSI gate probably was not the limiting factor.


Advanced - Competing Entries With Shared Exits

What this teaches: multiple setups, entry OCA, state-token gates, alternative full exits, no entry-ID targeting, and warm-up-safe shared logic.

The Trading Idea

Trade only when the larger trend is bullish and volatility is expanding. Once that context exists, allow two different entry tactics to compete: a Bollinger-band bounce and a breakout. They are different paths into the same long exposure, not separate strategy accounts.

After entry, manage the open position with shared exits. One exit is an ATR-based target. Another exit is an RSI fade while already profitable. The stop can either be ATR-based or immediate if the macro setup fails.

The Strategy Lab Idea

This is where the "one pot of money" model starts to matter. The two entries can compete through an OCA group, but the exits do not need to know which entry won. They are independent units watching the overall position.

The volatility setup is also important. No entry belongs to L_ADV_VOL_EXPANSION. It is being used as a gate condition through its generated state token, L_ADV_VOL_EXPANSION_ACTIVE. That is a clean pattern: one setup can describe context without directly owning an entry.

Custom tokens needed:

Token

Type

Source

EMA_200

Number

200-period EMA

EMA_50

Number

50-period EMA

BB_LOWER

Number

Bollinger lower band

RSI_K

Number

RSI line

ATR_14

Number

ATR

This example has more moving parts, so every field that touches a warming token owns its fallback. The macro setup protects EMAs, volatility rules protect ATR ratios, and entry triggers protect Bollinger and historical-high references.

Why The YAML Looks Like This

  • L_ADV_MACRO_TREND owns the actual trade idea. Entries and exits belong to it because losing the macro trend should matter to the whole position.

  • L_ADV_VOL_EXPANSION is a context switch. It does not own trades; it creates a state token that entries can consult.

  • Entry gates link each entry to setup state. Entry triggers hold the actual entry events.

  • Exit gates link each exit to position or setup state. Exit triggers hold the actual exit event, or TRUE when the unit should place a resting order immediately after the gate is true.

  • entry_oca_group: L_ADV_PRIMARY and entry_oca_type: CANCEL tell the two primary entries to compete. If one entry gets submitted, the other should not also become a duplicate expression of the same idea.

  • The two entries allocate 50% each, so combined entry exposure stays within the 100% cap.

  • The take profits both allocate 100%, but they are not promising to exit 200% of the position. They are alternative full exits against whatever position remains when one of them triggers.

  • NZ(ATR_14, MIN_TICK) keeps dynamic target and stop prices usable during diagnostics without inventing a large ATR value.

  • The breakout trigger uses BAR_INDEX > 20 and NZ(HIGHEST(...), PRICE_HIGH) because historical lookbacks need enough chart history before they can answer honestly.

Long Setups

Example
- setup_name: L_ADV_MACRO_TREND setup_gate_condition: BAR_INDEX > 200 setup_active_when: PRICE_CLOSE > NZ(EMA_200, PRICE_CLOSE) && NZ(EMA_50, PRICE_CLOSE) > NZ(EMA_200, PRICE_CLOSE) setup_confirm_count: 5 setup_cancel_when: PRICE_CLOSE < NZ(EMA_200, PRICE_CLOSE) && NZ(EMA_50, PRICE_CLOSE) < NZ(EMA_200, PRICE_CLOSE) setup_cancel_confirm_count: 3 setup_close_on_cancel: true setup_cooldown_bars: 10 - setup_name: L_ADV_VOL_EXPANSION setup_gate_condition: BAR_INDEX > 20 setup_active_when: SAFE_DIV(NZ(ATR_14, 0), NZ(ATR_14[20], 1), 0) > 1.20 setup_confirm_count: 2 setup_cancel_when: SAFE_DIV(NZ(ATR_14, 0), NZ(ATR_14[20], 1), 1) < 0.80

Long Entries

Example
- entry_name: L_ADV_BB_BOUNCE entry_belongs_to_setup: L_ADV_MACRO_TREND entry_gate_condition: L_ADV_VOL_EXPANSION_ACTIVE && NZ(RSI_K, 50) < 35 entry_trigger_when: CROSS_OVER(PRICE_CLOSE, NZ(BB_LOWER, PRICE_CLOSE)) entry_order_type: MARKET entry_allocation_percent: 50 entry_oca_group: L_ADV_PRIMARY entry_oca_type: CANCEL - entry_name: L_ADV_BREAKOUT entry_belongs_to_setup: L_ADV_MACRO_TREND entry_gate_condition: L_ADV_VOL_EXPANSION_ACTIVE && NZ(RSI_K, 50) > 50 entry_trigger_when: BAR_INDEX > 20 && PRICE_HIGH > NZ(HIGHEST(PRICE_HIGH[1], 20), PRICE_HIGH) entry_order_type: MARKET entry_allocation_percent: 50 entry_oca_group: L_ADV_PRIMARY entry_oca_type: CANCEL

Long Take Profits

Example
- take_profit_name: L_ADV_TP_ATR_TARGET take_profit_belongs_to_setup: L_ADV_MACRO_TREND take_profit_gate_condition: POSITION_ACTIVE take_profit_trigger_when: TRUE take_profit_order_type: LIMIT take_profit_limit_price: POSITION_AVERAGE_PRICE + NZ(ATR_14, MIN_TICK) * 2 take_profit_lock_prices: true take_profit_allocation_percent: 100 - take_profit_name: L_ADV_TP_RSI_FADE take_profit_belongs_to_setup: L_ADV_MACRO_TREND take_profit_gate_condition: POSITION_ACTIVE && POSITION_PROFIT_PERCENT > 2 take_profit_trigger_when: CROSS_UNDER(NZ(RSI_K, 50), 65) take_profit_order_type: MARKET take_profit_allocation_percent: 100

Long Stop Losses

Example
- stop_loss_name: L_ADV_SL_ATR stop_loss_belongs_to_setup: L_ADV_MACRO_TREND stop_loss_gate_condition: POSITION_ACTIVE stop_loss_trigger_when: TRUE stop_loss_order_type: STOP stop_loss_stop_price: POSITION_AVERAGE_PRICE - NZ(ATR_14, MIN_TICK) * 1.5 stop_loss_lock_prices: true stop_loss_allocation_percent: 100 - stop_loss_name: L_ADV_SL_SETUP_FAIL stop_loss_belongs_to_setup: L_ADV_MACRO_TREND stop_loss_gate_condition: POSITION_ACTIVE stop_loss_trigger_when: !L_ADV_MACRO_TREND_ACTIVE stop_loss_order_type: MARKET stop_loss_allocation_percent: 100

What To Verify

  1. L_ADV_MACRO_TREND_ACTIVE and L_ADV_VOL_EXPANSION_ACTIVE can be true or false independently. This proves the context setup is not secretly owning the trade.

  2. The two entries share L_ADV_PRIMARY, so they compete. This proves entry OCA is controlling entry alternatives.

  3. Entry order names are based on entry_name. This proves the strategy is using named units, not removed entry-ID targeting.

  4. No exit uses entry-ID targeting. This proves exits are managing the position, not one specific entry label.

  5. Both take profits are 100% exits, but they are alternatives against the same remaining position.

  6. The setup-failure stop exits the position if the macro setup is lost.

  7. The diagnostics table stays clean while the example still uses custom tokens and historical lookbacks.

If It Does Not Behave

  • If neither entry fires, check whether both setups are active at the same time. A trend without volatility expansion should stay quiet here.

  • If only one entry style ever appears, that may be normal. OCA competition means one tactic can dominate on a given market and timeframe.

  • If you see an allocation diagnostic, check only the entries. Exit allocations are allowed to be alternative full exits, but entry allocations cannot exceed 100% combined.

  • If the ATR target or stop looks too close, check whether ATR_14 is connected. The fallback is MIN_TICK, which is intentionally tiny.

Try One Controlled Change

Change entry_oca_type: CANCEL to entry_oca_type: REDUCE on both entries and compare behavior. Do not keep that change blindly. The point is to see how OCA changes the order relationship, not to assume one mode is better.


Extreme - Regime Engine With Dynamic Risk

What this teaches: multiple cooperating setups, safe historical math, add-on entries, partial exits, break-even transition, time-based escape, and expression diagnostics coverage.

The Trading Idea

Build a long-only regime engine. The strategy wants a bullish trend, acceptable volatility, and acceptable volume before it allows primary entries. It can enter through a pullback or a momentum breakout. If the position starts working, it can add on. Then it scales out, protects the remainder, and has several ways to abandon the trade if the story changes.

This is not "better" because it is bigger. It is bigger because it is trying to model more decisions a trader might normally hold in their head.

The Strategy Lab Idea

This example treats setups, entries, take profits, and stop losses as separate units of work against one overall position. Some units create context. Some create exposure. Some reduce exposure. Some close it entirely.

The important thing to watch is not just whether trades happen. Watch why they become eligible. A complex strategy that trades but cannot explain itself is not advanced; it is just noisy.

Custom tokens needed:

Token

Type

Source

EMA_200

Number

200-period EMA

EMA_50

Number

50-period EMA

EMA_20

Number

20-period EMA

RSI_K

Number

RSI line

ATR_14

Number

ATR

VOLUME_SMA

Number

Volume SMA

This is the stress test. It uses several custom tokens, historical lookbacks, generated state tokens, and alternative exits. The YAML is deliberately defensive so the diagnostic table teaches you about real problems instead of yelling about normal warm-up behavior.

Why The YAML Looks Like This

  • L_EXT_REGIME_BULL is the main authority. Entries and exits belong to this setup because the bullish regime is the reason the strategy is allowed to be long.

  • L_EXT_VOL_OK and L_EXT_VOLUME_OK are permission layers. They do not own trades; they expose state tokens that entries can require.

  • The two prime entries share L_EXT_PRIMARY, so the pullback and momentum tactics compete instead of stacking blindly.

  • The add-on entry does not share that OCA group. It has a different job: add only after the position is already profitable and volatility is still acceptable.

  • Entry allocations total 100% across the three entry units: 40 + 40 + 20. That respects the entry allocation cap while still allowing different ways to deploy the same capital pool.

  • The first take profit exits 50%. Its gate links it to a profitable active position; its trigger is TRUE because the limit order should be placed immediately once that link is valid.

  • The runner take profit and break-even stop both depend on POSITION_REMAINING_PERCENT <= 50, so they only become logical after the scale-out.

  • The time escape watches generated entry state tokens with BARS_SINCE(...). It is asking, "Has one of these entry units been active long enough, and has price lost the 20 EMA while the trade is still profitable?"

  • The strategy uses NZ, SAFE_DIV, and same-field BAR_INDEX guards because this example intentionally touches historical values, token warm-up, and ratios. The diagnostics table should be reserved for real mistakes, not predictable early-chart emptiness.

Long Setups

Example
- setup_name: L_EXT_REGIME_BULL setup_gate_condition: BAR_INDEX > 200 && TIMEFRAME_IS_INTRADAY setup_active_when: PRICE_CLOSE > NZ(EMA_200, PRICE_CLOSE) && NZ(EMA_50, PRICE_CLOSE) > NZ(EMA_200, PRICE_CLOSE) && NZ(EMA_20, PRICE_CLOSE) > NZ(EMA_50, PRICE_CLOSE) setup_confirm_count: 5 setup_cancel_when: NZ(EMA_20, PRICE_CLOSE) < NZ(EMA_50, PRICE_CLOSE) && NZ(EMA_50, PRICE_CLOSE) < NZ(EMA_200, PRICE_CLOSE) setup_cancel_confirm_count: 3 setup_close_on_cancel: true setup_cooldown_bars: 15 setup_max_entries_per_activation: 5 setup_reset_when: STRATEGY_MAX_DRAWDOWN_PERCENT > 15 - setup_name: L_EXT_VOL_OK setup_gate_condition: BAR_INDEX > 20 setup_active_when: SAFE_DIV(NZ(ATR_14, 0), NZ(ATR_14[10], 1), 0) > 1.10 && SAFE_DIV(NZ(ATR_14, 0), NZ(ATR_14[10], 1), 0) < 3.00 setup_confirm_count: 2 setup_cancel_when: SAFE_DIV(NZ(ATR_14, 0), NZ(ATR_14[10], 1), 1) < 0.80 || SAFE_DIV(NZ(ATR_14, 0), NZ(ATR_14[10], 1), 1) > 3.00 - setup_name: L_EXT_VOLUME_OK setup_gate_condition: BAR_INDEX > 20 setup_active_when: SAFE_DIV(VOLUME, NZ(VOLUME_SMA, VOLUME), 0) > 1.20 setup_confirm_count: 1 setup_cancel_when: SAFE_DIV(VOLUME, NZ(VOLUME_SMA, VOLUME), 1) < 0.50 setup_cooldown_bars: 3

Long Entries

Example
- entry_name: L_EXT_PULLBACK_PRIME entry_belongs_to_setup: L_EXT_REGIME_BULL entry_gate_condition: L_EXT_VOL_OK_ACTIVE && L_EXT_VOLUME_OK_ACTIVE && NZ(RSI_K, 50) < 40 && STRATEGY_OPEN_TRADES < 3 entry_trigger_when: CROSS_OVER(PRICE_CLOSE, NZ(EMA_20, PRICE_CLOSE)) && PRICE_CLOSE > NZ(EMA_50, PRICE_CLOSE) entry_confirm_count: 1 entry_order_type: MARKET entry_allocation_percent: 40 entry_oca_group: L_EXT_PRIMARY entry_oca_type: CANCEL - entry_name: L_EXT_MOMENTUM_PRIME entry_belongs_to_setup: L_EXT_REGIME_BULL entry_gate_condition: L_EXT_VOL_OK_ACTIVE && L_EXT_VOLUME_OK_ACTIVE && NZ(RSI_K, 50) > 55 && NZ(RSI_K, 50) < 75 && STRATEGY_OPEN_TRADES < 3 entry_trigger_when: BAR_INDEX > 15 && PRICE_HIGH > NZ(HIGHEST(PRICE_HIGH[1], 15), PRICE_HIGH) && SAFE_DIV(VOLUME, NZ(VOLUME_SMA, VOLUME), 0) > 1.50 entry_order_type: MARKET entry_allocation_percent: 40 entry_oca_group: L_EXT_PRIMARY entry_oca_type: CANCEL - entry_name: L_EXT_ADD_ON_STRENGTH entry_belongs_to_setup: L_EXT_REGIME_BULL entry_gate_condition: POSITION_ACTIVE && POSITION_PROFIT_PERCENT > 1.5 && L_EXT_VOL_OK_ACTIVE entry_trigger_when: CROSS_OVER(PRICE_CLOSE, NZ(EMA_20, PRICE_CLOSE)) && NZ(RSI_K, 50) > 40 && NZ(RSI_K, 50) < 65 entry_order_type: MARKET entry_allocation_percent: 20 entry_one_shot: false entry_pyramiding_max_adds: 2

Long Take Profits

Example
- take_profit_name: L_EXT_TP_SCALE_HALF take_profit_belongs_to_setup: L_EXT_REGIME_BULL take_profit_gate_condition: POSITION_ACTIVE && POSITION_PROFIT_PERCENT > 1 take_profit_trigger_when: TRUE take_profit_order_type: LIMIT take_profit_limit_price: POSITION_AVERAGE_PRICE + NZ(ATR_14, MIN_TICK) * 2 take_profit_lock_prices: true take_profit_allocation_percent: 50 - take_profit_name: L_EXT_TP_RUNNER_RSI take_profit_belongs_to_setup: L_EXT_REGIME_BULL take_profit_gate_condition: POSITION_ACTIVE && POSITION_REMAINING_PERCENT <= 50 && POSITION_PROFIT_PERCENT > 3 take_profit_trigger_when: CROSS_UNDER(NZ(RSI_K, 50), 65) take_profit_order_type: MARKET take_profit_allocation_percent: 100 - take_profit_name: L_EXT_TP_TIME_ESCAPE take_profit_belongs_to_setup: L_EXT_REGIME_BULL take_profit_gate_condition: POSITION_ACTIVE && POSITION_PROFIT_PERCENT > 0 && BAR_INDEX > 1 && ( BARS_SINCE(L_EXT_PULLBACK_PRIME_ACTIVE && !L_EXT_PULLBACK_PRIME_ACTIVE[1]) > 60 || BARS_SINCE(L_EXT_MOMENTUM_PRIME_ACTIVE && !L_EXT_MOMENTUM_PRIME_ACTIVE[1]) > 60 || BARS_SINCE(L_EXT_ADD_ON_STRENGTH_ACTIVE && !L_EXT_ADD_ON_STRENGTH_ACTIVE[1]) > 60 ) take_profit_trigger_when: PRICE_CLOSE < NZ(EMA_20, PRICE_CLOSE) take_profit_order_type: MARKET take_profit_allocation_percent: 100

Long Stop Losses

Example
- stop_loss_name: L_EXT_SL_INITIAL stop_loss_belongs_to_setup: L_EXT_REGIME_BULL 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 - NZ(ATR_14, MIN_TICK) * 2.5 stop_loss_lock_prices: true stop_loss_allocation_percent: 100 - stop_loss_name: L_EXT_SL_BREAK_EVEN stop_loss_belongs_to_setup: L_EXT_REGIME_BULL 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: 100 - stop_loss_name: L_EXT_SL_REGIME_FAIL stop_loss_belongs_to_setup: L_EXT_REGIME_BULL stop_loss_gate_condition: POSITION_ACTIVE stop_loss_trigger_when: !L_EXT_REGIME_BULL_ACTIVE || !L_EXT_VOL_OK_ACTIVE stop_loss_order_type: MARKET stop_loss_allocation_percent: 100

What To Verify

  1. No warm-up warnings appear when the required custom tokens are connected because warming values are guarded in the same fields that use them.

  2. L_EXT_REGIME_BULL_ACTIVE, L_EXT_VOL_OK_ACTIVE, and L_EXT_VOLUME_OK_ACTIVE move independently. This proves each setup is carrying a separate job.

  3. Entry allocations stay within the 100% cap: 40 + 40 + 20.

  4. The two prime entries compete through L_EXT_PRIMARY. This proves the primary tactics are alternatives, not duplicate exposure.

  5. The add-on entry only becomes eligible when the position is already profitable.

  6. The first TP reduces the position; the runner TP and break-even stop depend on POSITION_REMAINING_PERCENT.

  7. The time escape only exits profitable positions after one of the entries has been active for a while and price loses the 20 EMA.

  8. Multiple 100% exits are present, but they are alternative full exits against the current remaining position.

If It Does Not Behave

  • If the strategy barely trades, check the setup state tokens first. Three cooperating setups can easily become too strict together.

  • If the add-on never appears, check POSITION_PROFIT_PERCENT > 1.5 and L_EXT_VOL_OK_ACTIVE. The add-on is intentionally not a first entry.

  • If the break-even stop appears too early, inspect whether the 50% scale-out filled and whether POSITION_REMAINING_PERCENT actually dropped.

  • If the time escape never fires, remember that it requires profit, an active-position history condition, and price below the 20 EMA.

  • If the diagnostics table is noisy, do not wave it off. This example is written defensively. A warning usually means a token binding, field edit, or copied block is wrong.

Try One Controlled Change

Disable L_EXT_VOLUME_OK_ACTIVE from the two prime entry gates and run the same chart again. You should see whether volume was acting as a meaningful filter or just making the strategy quieter. Put it back before testing anything else.


Building From Here

Start simple, then add one unit at a time. After each addition:

  1. Check the diagnostics table.

  2. Check the schema summary.

  3. Watch generated state tokens.

  4. Confirm the trade list matches the story you think the YAML is telling.

  5. Run slippage sensitivity before believing the equity curve.

Complexity is not the goal. Clarity is. Complexity is only useful when you can still explain what each unit is supposed to do and how you would know if it failed.