Skip to content

Strategy Cookbook

The Strategy Examples page shows complete end-to-end trading systems. This page is different: it is a catalog of small, reusable patterns — each one just two or three rules — that you can drop into any strategy as a building block.

Think of these as the vocabulary of Stratifyre. Once you recognize the shapes, you will find yourself combining them without thinking about it, the same way a programmer combines loops and conditionals.

Every pattern on this page is built from the same two primitives: Conditions and Variables. There are no hidden features. If you understand Rules and Expressions, you already have everything you need.

Each pattern includes:

  • The shape — what it does in one sentence
  • When to use it — the kinds of strategies it fits
  • The rules — the minimal configuration you need
  • Variations — common tweaks

Patterns are grouped by what they let you express: state, counting, sequencing, coordination, and risk.


The shape: Set a flag when something happens once; leave it set forever (or until you explicitly clear it).

When to use it: Marking that a condition has ever occurred in this session. “Did the market gap up at the open?” “Has RSI been below 20 today?” “Did we already see the setup?”

Rules:

  1. Set the latch — Conditions: whatever you want to detect. Action: Set Variable gappedUp = 1. Frequency: Once (per period, if you want daily reset).
  2. Use the latch — In any other rule, add a condition vars.gappedUp == 1.

Variations:

  • Auto-clearing daily latch — Add a portfolio rule at the top with condition time.min_since_rth_open == 0 that sets vars.gappedUp = 0. It runs once at the open each day and resets the flag.
  • Arm/disarm latch — Pair two rules: one sets the flag to 1 when an event arms the strategy, a second sets it to 0 when an event disarms it. This is the simplest state machine.

The shape: A boolean that flips between two states based on opposing events.

When to use it: “Trading is enabled during the trend, disabled during chop.” “We’re long-biased until a reversal signal flips us short-biased.”

Rules:

  1. Turn it on — Conditions: activation signal. Action: Set Variable tradingEnabled = 1. Frequency: No limit.
  2. Turn it off — Conditions: deactivation signal. Action: Set Variable tradingEnabled = 0. Frequency: No limit.
  3. Gate with it — In your entry rule, add vars.tradingEnabled == 1 as a condition.

The shape: A running maximum (or minimum) of some value over time, that only moves in one direction.

When to use it: Trailing stops that ratchet up but never down. Recording peak equity for drawdown calculations. Tracking the highest RSI reading during an overbought phase.

Rules:

  1. Update the mark — Condition: high > vars.peakHigh. Action: Set Variable peakHigh = high. Frequency: No limit.
  2. Use the mark — In any rule, reference vars.peakHigh (for example, as a trailing-stop level: price < vars.peakHigh - 2 * ATR(14)).

Variations:

  • Running minimum — Flip the comparison: low < vars.troughLow → set troughLow = low.
  • Since-event high-water mark — Reset the mark when a triggering event occurs (for example, set peakHigh = price when a position is opened), so the mark tracks the peak since entry.

The shape: Record a specific price, value, or bar index at the moment something happens, and reference it later.

When to use it: Entry price, entry bar index, the high of the signal bar, the value of an indicator when a condition first fired.

Rules:

  1. Anchor it — Condition: the triggering event (for example, an entry condition). Action: Set Variable entryPrice = price (and optionally more anchors like entryBar = bar_index, signalHigh = high).
  2. Reference it — Use vars.entryPrice, vars.entryBar, or vars.signalHigh anywhere else.

This is the foundation of almost every exit rule. See also the ATR trailing-stop example.


The shape: Count how many bars in a row a condition has been true. Reset to zero on the first bar it isn’t.

When to use it: “Enter after 3 consecutive rising-volume bars.” “Exit after 5 bars without a new high.” “Count the streak.”

Rules:

  1. Increment on match — Condition: the event (for example, volume > volume[1]). Action: Set Variable streak = vars.streak + 1. Frequency: No limit.
  2. Reset on miss — Condition: the inverse (volume <= volume[1]). Action: Set Variable streak = 0. Frequency: No limit.
  3. Consume the count — In your entry/exit rule, gate on vars.streak >= 3.

The shape: Count how many times something has happened in the current session (or day, or other period), without requiring the events to be consecutive.

When to use it: “Enter only after the third pullback of the day.” “Limit to 5 trades per session.” “Alert after the 10th volume spike.”

Rules:

  1. Daily reset — Portfolio rule with condition time.min_since_rth_open == 0. Action: Set Variable pullbackCount = 0. Frequency: Once per day.
  2. Increment on event — Condition: the event. Action: Set Variable pullbackCount = vars.pullbackCount + 1. Frequency: No limit.
  3. Gate on the count — In a consumer rule, check vars.pullbackCount == 3 (or >= 3, or <= 5 to cap).

Variations:

  • Weekly/monthly windows — Use Once per period on the reset rule with a different interval.
  • Rolling windows — Harder, but possible: store timestamps of events in separate variables (event1Time, event2Time, …) and count how many fall within the lookback.

The shape: Fire when at least N out of M independent signals are currently true.

When to use it: Multi-factor entries where you want flexibility. “Enter when at least 2 of these 4 signals agree.” Avoids the brittleness of requiring all signals simultaneously.

Rules:

This is a single-condition pattern that lives entirely inside an expression — no helper rules needed. Add one condition to your entry rule:

(RSI(14) < 30) + (MACD().histogram > 0) + (close > SMA(50)) + (volume > AVG(volume, 20)) >= 2

Each parenthesized comparison evaluates to 1 or 0; summing counts them.

Variations:

  • Weighted voting — Multiply each signal by its weight: 3 * (trendOK) + 2 * (momentumOK) + 1 * (volumeOK) >= 4.
  • Veto signal — Combine with an AND: a veto can negate the whole vote. ((a) + (b) + (c) >= 2) && !vetoCondition.

See Counting signals for more detail.


The shape: Event A must happen before event B, and the combined trigger fires on B.

When to use it: “RSI must go oversold, then we wait for a bullish engulfing candle.” “Price must break resistance, then pull back and hold.” Any “first, then” description.

Rules:

  1. Arm on step 1 — Conditions: the first event. Action: Set Variable armed = 1. Frequency: No limit.
  2. Fire on step 2 — Conditions: vars.armed == 1 AND the second event. Action: place order, then Set Variable armed = 0 to reset.

Variations:

  • With expiration — When arming, also set vars.armedBar = bar_index. In the fire rule, add condition bar_index - vars.armedBar < 20 so the setup expires after 20 bars.
  • Three-step sequence — Chain the pattern: step 1 arms, step 2 advances to a second flag, step 3 fires.

The shape: Move through a sequence of named states, where each transition is triggered by a different event.

When to use it: Strategies with clear phases — “watching → armed → entered → in-profit → trailing → exited” — where different rules apply in each phase.

Rules:

Use a single vars.state variable with integer codes (0 = watching, 1 = armed, 2 = entered, …). Each transition is one rule:

  1. Watching → Armed — Condition: vars.state == 0 && <arm event>. Action: vars.state = 1.
  2. Armed → Entered — Condition: vars.state == 1 && <entry event>. Action: place order, vars.state = 2.
  3. Entered → Exited — Condition: vars.state == 2 && <exit event>. Action: flatten, vars.state = 0.

Other rules can gate on the current state: “only show alerts when vars.state == 1.”


The shape: A flag or signal that auto-clears after a fixed number of bars.

When to use it: “The breakout signal is only valid for 5 bars after it fires.” “Cancel the setup if it hasn’t triggered within 30 minutes.”

Rules:

  1. Set with timestamp — Condition: the event. Action: Set Variable signalBar = bar_index. (Combine with other variables you want to anchor.)
  2. Check freshness — In the consumer rule, use bar_index - vars.signalBar < 5 as a condition. The signal is automatically “stale” once 5 bars pass.

No explicit reset rule needed — you just stop trusting the variable once it’s old.


The shape: After an exit, block re-entry for a fixed period.

When to use it: Preventing churning on sideways markets. “No new entries for 10 bars after a stop-out.” “Wait at least 30 minutes between trades.”

Rules:

  1. Stamp on exit — Condition: your exit event (or add to the exit rule’s actions). Action: Set Variable lastExitBar = bar_index.
  2. Gate the entry — In your entry rule, add condition bar_index - vars.lastExitBar > 10 (or vars.lastExitBar == 0 OR that expression, to allow the very first entry).

Variations:

  • Time-based cooldown — Use time.min_since_rth_open - vars.lastExitMin > 30 with a lastExitMin stamp.
  • Per-reason cooldowns — Different cooldowns after stop-outs vs. profit-takes. Store separate variables.

The shape: Limit the total number of open positions across all instruments.

When to use it: Risk management when trading a basket. “Never hold more than 5 positions at once.” “Limit sector exposure.”

Rules:

Uses global variables (shared across instruments):

  1. Increment on entry — Action on your entry rule: Set Variable (scope: Global) openPositions = globals.openPositions + 1.
  2. Decrement on exit — Action on your exit rule: openPositions = globals.openPositions - 1.
  3. Gate the entry — Add condition globals.openPositions < 5 to the entry rule.

Leader-follower (cross-instrument signals)

Section titled “Leader-follower (cross-instrument signals)”

The shape: One instrument’s signal gates entry in other instruments.

When to use it: “Only trade my basket when SPY is above its 200-day SMA.” “Enter sector ETFs when the sector leader breaks out.”

Rules:

  1. Leader signal — Portfolio rule (applicability: None) referencing the leader explicitly: condition bars['EQ:NYSE:SPY~D'].close > AVG(bars['EQ:NYSE:SPY~D'].close, 200). Action: Set Variable (Global) spyBullish = 1. Frequency: No limit.
  2. Reverse — Same pattern with the opposite condition, setting spyBullish = 0.
  3. Followers gate on it — Every follower entry rule adds condition globals.spyBullish == 1.

See Multi-Instrument Strategies for more patterns.


The shape: From a basket, only trade the N strongest (or weakest) instruments.

When to use it: Relative-strength strategies. “Buy the top 3 sector ETFs by 1-month return.” Momentum rotation.

Rules:

This is one of the harder patterns because ranking across instruments requires global coordination. A common approximation:

  1. Each instrument computes its own score — A per-instrument rule with no conditions, action: Set Variable myScore = (close - close[20]) / close[20].
  2. A portfolio rule tracks the top score seen this bar — Condition: vars.myScore > globals.topScoreThisBar. Action: Set Variable (Global) topScoreThisBar = vars.myScore, topSymbol = <identifier>.
  3. Reset each bar — A portfolio rule at the top of the strategy that clears topScoreThisBar = -999 at the start of each evaluation.
  4. Only the top instrument enters — Entry rule gates on vars.myScore == globals.topScoreThisBar.

This is a simplification of “pick top 1.” For top-N, consider external ranking via a scanner.


The shape: Stop trading for the day once losses hit a threshold.

When to use it: Discipline enforcement. “If I’m down 3% today, I’m done until tomorrow.”

Rules:

  1. Daily reset — Portfolio rule, condition time.min_since_rth_open == 0. Action: Set Variable (Global) dailyStartEquity = account.equity. Frequency: Once per day.
  2. Check and lock — Portfolio rule, condition (account.equity - globals.dailyStartEquity) / globals.dailyStartEquity < -0.03. Action: Set Variable (Global) tradingLocked = 1. Frequency: Once per day.
  3. Gate all entries — Every entry rule adds condition globals.tradingLocked == 0.
  4. Daily unlock — Add to the reset rule: also set tradingLocked = 0.

The shape: Once a trade is in profit by X, move the stop to breakeven so it can’t turn into a loss.

When to use it: Locking in “free trades.” Classic discretionary-trading idiom in rule form.

Rules:

  1. Record entry — In the entry rule, also set vars.entryPrice = price and vars.stopPrice = price - 2 * ATR(14).
  2. Promote to breakeven — Condition: price > vars.entryPrice + 1.5 * ATR(14) AND vars.stopPrice < vars.entryPrice. Action: Set Variable stopPrice = vars.entryPrice. Frequency: Once (per trade — reset when position closes).
  3. Exit on stop — Condition: price < vars.stopPrice AND position.open_qty > 0. Action: flatten.

The shape: Add to a winning position in tranches, or exit in tranches.

When to use it: “Enter 1/3, add another 1/3 if it goes 1 ATR in my favor, final 1/3 at 2 ATR.” “Take 1/3 off at each of three profit targets.”

Rules:

Track the tranche count in a variable:

  1. Initial entry — Standard entry rule. Also sets vars.trancheCount = 1 and vars.entryPrice = price.
  2. Tranche 2 — Condition: vars.trancheCount == 1 && price > vars.entryPrice + ATR(14). Action: place order (buy 1/3 more), vars.trancheCount = 2.
  3. Tranche 3 — Condition: vars.trancheCount == 2 && price > vars.entryPrice + 2 * ATR(14). Action: place order, vars.trancheCount = 3.
  4. Exit all — Standard exit rule resets vars.trancheCount = 0.

Works symmetrically for scale-outs: flip the increment direction and use partial-close actions.


Most real strategies are three or four of these patterns layered together. A typical swing strategy might use:

  • Latch to detect that the higher-timeframe trend is bullish
  • Two-step setup to wait for a pullback and then a reversal
  • Anchored level to record the entry price
  • High-water mark for a trailing stop
  • Re-entry cooldown to avoid churning
  • Daily loss limit for discipline

That is not a 20-rule monstrosity — it’s maybe 10 small, named rules, each doing one thing. When something misfires, you can look at each variable independently (vars.trendBullish, vars.pullbackArmed, vars.entryPrice, vars.peakHigh, vars.lastExitBar, globals.tradingLocked) and see exactly which step failed.

This is the power of the variable-first model: complex behavior from simple, inspectable parts.

  • Browse Strategy Examples for complete systems that use these patterns end-to-end.
  • Read Expressions for the full expression-language reference, including the Set Variable action mechanics.
  • See Debugging Strategies to learn how to inspect variable state when a pattern isn’t behaving as expected.