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.
How to read this page
Section titled “How to read this page”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.
State patterns
Section titled “State patterns”Latch (one-shot flag)
Section titled “Latch (one-shot flag)”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:
- Set the latch — Conditions: whatever you want to detect. Action:
Set VariablegappedUp = 1. Frequency: Once (per period, if you want daily reset). - 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 == 0that setsvars.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.
Toggle (on/off switch)
Section titled “Toggle (on/off switch)”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:
- Turn it on — Conditions: activation signal. Action:
Set VariabletradingEnabled = 1. Frequency: No limit. - Turn it off — Conditions: deactivation signal. Action:
Set VariabletradingEnabled = 0. Frequency: No limit. - Gate with it — In your entry rule, add
vars.tradingEnabled == 1as a condition.
High-water mark
Section titled “High-water mark”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:
- Update the mark — Condition:
high > vars.peakHigh. Action:Set VariablepeakHigh = high. Frequency: No limit. - 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→ settroughLow = low. - Since-event high-water mark — Reset the mark when a triggering event occurs (for example, set
peakHigh = pricewhen a position is opened), so the mark tracks the peak since entry.
Anchored level
Section titled “Anchored level”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:
- Anchor it — Condition: the triggering event (for example, an entry condition). Action:
Set VariableentryPrice = price(and optionally more anchors likeentryBar = bar_index,signalHigh = high). - Reference it — Use
vars.entryPrice,vars.entryBar, orvars.signalHighanywhere else.
This is the foundation of almost every exit rule. See also the ATR trailing-stop example.
Counting patterns
Section titled “Counting patterns”Consecutive-event counter
Section titled “Consecutive-event counter”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:
- Increment on match — Condition: the event (for example,
volume > volume[1]). Action:Set Variablestreak = vars.streak + 1. Frequency: No limit. - Reset on miss — Condition: the inverse (
volume <= volume[1]). Action:Set Variablestreak = 0. Frequency: No limit. - Consume the count — In your entry/exit rule, gate on
vars.streak >= 3.
Event-count within a window
Section titled “Event-count within a window”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:
- Daily reset — Portfolio rule with condition
time.min_since_rth_open == 0. Action:Set VariablepullbackCount = 0. Frequency: Once per day. - Increment on event — Condition: the event. Action:
Set VariablepullbackCount = vars.pullbackCount + 1. Frequency: No limit. - Gate on the count — In a consumer rule, check
vars.pullbackCount == 3(or>= 3, or<= 5to cap).
Variations:
- Weekly/monthly windows — Use
Once per periodon 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.
N-of-M confirmation
Section titled “N-of-M confirmation”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)) >= 2Each 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.
Sequencing patterns
Section titled “Sequencing patterns”Two-step setup
Section titled “Two-step setup”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:
- Arm on step 1 — Conditions: the first event. Action:
Set Variablearmed = 1. Frequency: No limit. - Fire on step 2 — Conditions:
vars.armed == 1AND the second event. Action: place order, thenSet Variablearmed = 0to reset.
Variations:
- With expiration — When arming, also set
vars.armedBar = bar_index. In the fire rule, add conditionbar_index - vars.armedBar < 20so 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.
State machine
Section titled “State machine”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:
- Watching → Armed — Condition:
vars.state == 0 && <arm event>. Action:vars.state = 1. - Armed → Entered — Condition:
vars.state == 1 && <entry event>. Action: place order,vars.state = 2. - 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.”
Expiration / timeout
Section titled “Expiration / timeout”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:
- Set with timestamp — Condition: the event. Action:
Set VariablesignalBar = bar_index. (Combine with other variables you want to anchor.) - Check freshness — In the consumer rule, use
bar_index - vars.signalBar < 5as 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.
Coordination patterns
Section titled “Coordination patterns”Re-entry cooldown
Section titled “Re-entry cooldown”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:
- Stamp on exit — Condition: your exit event (or add to the exit rule’s actions). Action:
Set VariablelastExitBar = bar_index. - Gate the entry — In your entry rule, add condition
bar_index - vars.lastExitBar > 10(orvars.lastExitBar == 0OR that expression, to allow the very first entry).
Variations:
- Time-based cooldown — Use
time.min_since_rth_open - vars.lastExitMin > 30with alastExitMinstamp. - Per-reason cooldowns — Different cooldowns after stop-outs vs. profit-takes. Store separate variables.
Portfolio position cap
Section titled “Portfolio position cap”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):
- Increment on entry — Action on your entry rule:
Set Variable(scope: Global)openPositions = globals.openPositions + 1. - Decrement on exit — Action on your exit rule:
openPositions = globals.openPositions - 1. - Gate the entry — Add condition
globals.openPositions < 5to 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:
- 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. - Reverse — Same pattern with the opposite condition, setting
spyBullish = 0. - Followers gate on it — Every follower entry rule adds condition
globals.spyBullish == 1.
See Multi-Instrument Strategies for more patterns.
Rotation (pick the strongest)
Section titled “Rotation (pick the strongest)”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:
- Each instrument computes its own score — A per-instrument rule with no conditions, action:
Set VariablemyScore = (close - close[20]) / close[20]. - A portfolio rule tracks the top score seen this bar — Condition:
vars.myScore > globals.topScoreThisBar. Action:Set Variable(Global)topScoreThisBar = vars.myScore,topSymbol = <identifier>. - Reset each bar — A portfolio rule at the top of the strategy that clears
topScoreThisBar = -999at the start of each evaluation. - 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.
Risk patterns
Section titled “Risk patterns”Daily loss limit
Section titled “Daily loss limit”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:
- Daily reset — Portfolio rule, condition
time.min_since_rth_open == 0. Action:Set Variable(Global)dailyStartEquity = account.equity. Frequency: Once per day. - Check and lock — Portfolio rule, condition
(account.equity - globals.dailyStartEquity) / globals.dailyStartEquity < -0.03. Action:Set Variable(Global)tradingLocked = 1. Frequency: Once per day. - Gate all entries — Every entry rule adds condition
globals.tradingLocked == 0. - Daily unlock — Add to the reset rule: also set
tradingLocked = 0.
Breakeven stop move
Section titled “Breakeven stop move”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:
- Record entry — In the entry rule, also set
vars.entryPrice = priceandvars.stopPrice = price - 2 * ATR(14). - Promote to breakeven — Condition:
price > vars.entryPrice + 1.5 * ATR(14)ANDvars.stopPrice < vars.entryPrice. Action:Set VariablestopPrice = vars.entryPrice. Frequency: Once (per trade — reset when position closes). - Exit on stop — Condition:
price < vars.stopPriceANDposition.open_qty > 0. Action: flatten.
Scale-in / scale-out
Section titled “Scale-in / scale-out”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:
- Initial entry — Standard entry rule. Also sets
vars.trancheCount = 1andvars.entryPrice = price. - Tranche 2 — Condition:
vars.trancheCount == 1 && price > vars.entryPrice + ATR(14). Action: place order (buy 1/3 more),vars.trancheCount = 2. - Tranche 3 — Condition:
vars.trancheCount == 2 && price > vars.entryPrice + 2 * ATR(14). Action: place order,vars.trancheCount = 3. - Exit all — Standard exit rule resets
vars.trancheCount = 0.
Works symmetrically for scale-outs: flip the increment direction and use partial-close actions.
Putting patterns together
Section titled “Putting patterns together”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.
Next steps
Section titled “Next steps”- Browse Strategy Examples for complete systems that use these patterns end-to-end.
- Read Expressions for the full expression-language reference, including the
Set Variableaction mechanics. - See Debugging Strategies to learn how to inspect variable state when a pattern isn’t behaving as expected.
