Skip to main content

Pine Strategy Cookbook — 7 Ready-to-Copy Strategies

Seven complete Pine v5 strategies you can paste into TradingView, replace the token, and deploy. Each one carries the full Pine source plus the exact JSON payload TVH receives after TradingView resolves the placeholders.

TL;DR
  • Every strategy includes Pine source and the resolved JSON for entry and exit.
  • Replace YOUR_TOKEN with the value from your Account & API Keys page and apiKey with the name of the credential you saved.
  • Use {{strategy.order.alert_message}} as the TradingView alert Message field for all seven.
  • The Pine code targets Pine v5 and follows the official Pine v5 syntax conventions. Run each snippet in TradingView's Pine Editor before deploying it live to confirm compilation on your chart.

Reading the cookbook

Every recipe has the same shape:

  • Use case — one-sentence purpose.
  • Best for — exchange, timeframe, and the kind of market that suits it.
  • Pine + JSON tabs — the source and the wire-format result.
  • Configuration tips — what to tune before going live.
  • Common variations — adjacent patterns built on the same skeleton.

Strategy 1 — EMA Crossover (Spot, Beginner)

Use case: simplest possible Pine bot. Long when the fast EMA crosses up over the slow EMA, exit on the reverse cross.

Best for: Binance / Bybit Spot · 4H or 1D timeframe · trending majors (BTCUSDT, ETHUSDT, SOLUSDT).

//@version=5
strategy("EMA Crossover — TVH Spot", overlay=true,
default_qty_type=strategy.percent_of_equity, default_qty_value=5,
pyramiding=0, process_orders_on_close=true)

fastLen = input.int(9, "Fast EMA")
slowLen = input.int(21, "Slow EMA")

fast = ta.ema(close, fastLen)
slow = ta.ema(close, slowLen)
plot(fast, "Fast", color.new(color.aqua, 0))
plot(slow, "Slow", color.new(color.orange, 0))

cross_up = ta.crossover(fast, slow)
cross_down = ta.crossunder(fast, slow)

buy_command = '{"exchange":"binance","pair":"{{ticker}}","apiKey":"binance-main","isBuy":true,"unitsType":"percent","unitsPercent":5,"isMarket":true,"alertTimestamp":"{{ticker}}-{{timenow}}","token":"YOUR_TOKEN"}'
close_command = '{"exchange":"binance","pair":"{{ticker}}","apiKey":"binance-main","isClose":true,"token":"YOUR_TOKEN"}'

if cross_up
strategy.entry("Long", strategy.long, alert_message=buy_command)
if cross_down and strategy.position_size > 0
strategy.close("Long", alert_message=close_command)

Configuration tips

  • Spot account, no leverage. unitsPercent: 5 means 5 % of available quote balance.
  • process_orders_on_close=true prevents intra-bar repaints.
  • Skip stop-loss — the reverse cross is the exit. Add stopLoss if you want disaster protection.
  • Pair the alert with "Once Per Bar Close" or use the strategy's "Order fills only" mode.

Common variations

  • Filter with the 200 EMA: add in_uptrend = close > ta.ema(close, 200) and require it for entries.
  • Pyramid to scale in: set pyramiding=3 and remove the cross_down close so the same signal stacks.

Strategy 2 — RSI Pullback with Multi-TP (Spot or Futures)

Use case: buy oversold pullbacks during an uptrend, scale out at three TPs.

Best for: Bybit / Binance Spot or Futures · 1H–4H · ranging-to-trending altcoins.

//@version=5
strategy("RSI Pullback Multi-TP — TVH", overlay=true,
default_qty_type=strategy.percent_of_equity, default_qty_value=3,
pyramiding=0, process_orders_on_close=true)

rsiLen = input.int(14, "RSI Length")
overSold = input.int(30, "Oversold Level")
exitRsi = input.int(60, "Exit RSI")
trendLen = input.int(200, "Trend EMA")

r = ta.rsi(close, rsiLen)
trendOk = close > ta.ema(close, trendLen)

long_signal = ta.crossover(r, overSold) and trendOk
exit_signal = ta.crossunder(r, exitRsi)

// 30 / 30 / 40 ladder, 0.8 % / 1.6 % / 3 % distances.
buy_command = '{"exchange":"bybit","pair":"{{ticker}}","apiKey":"bybit-main","isBuy":true,"unitsType":"percent","unitsPercent":3,"isMarket":true,"stopLossType":"percent","stopLossPercent":1.5,"targetType":"percent","targetAmountInPercent":true,"targets":[{"takeProfitPercent":0.8,"amount":30},{"takeProfitPercent":1.6,"amount":30},{"takeProfitPercent":3,"amount":40}],"alertTimestamp":"{{ticker}}-{{timenow}}","token":"YOUR_TOKEN"}'
close_command = '{"exchange":"bybit","pair":"{{ticker}}","apiKey":"bybit-main","isClose":true,"token":"YOUR_TOKEN"}'

if long_signal
strategy.entry("Long", strategy.long, alert_message=buy_command)
if exit_signal and strategy.position_size > 0
strategy.close("Long", alert_message=close_command)

plotshape(long_signal, "Buy", shape.triangleup, location.belowbar, color.new(color.green, 0))

Configuration tips

  • targetAmountInPercent: true makes each amount a percent share of the position — three reduce-only TP orders sized 30 / 30 / 40 %. Without this flag, the values would be interpreted as base-asset units.
  • The 200 EMA filter prevents counter-trend longs during downtrends.
  • stopLossPercent: 1.5 sits 1.5 % below entry. TVH's auto-flip applies — same value works on shorts.

Common variations

  • Trailing stop after TP1: add "useTrailingStopLoss": true, "trailingStopLossPercent": 1 to the JSON for runner exits.
  • Short side: mirror the logic with overBought=70, exit on crossover(r, 40), and orderType:"sell".

Strategy 3 — Donchian Breakout (Futures, Trend-Following)

Use case: classic Turtle-style breakout. Long on a 20-bar high break, short on a 20-bar low break, both on leverage with a trailing stop.

Best for: Binance Futures / Bybit Linear Perps · 1H–4H · momentum altcoins.

//@version=5
strategy("Donchian Breakout — TVH Futures", overlay=true,
default_qty_type=strategy.percent_of_equity, default_qty_value=10,
pyramiding=0, process_orders_on_close=true)

length = input.int(20, "Donchian Length")
lev = input.int(5, "Leverage")

upper = ta.highest(high, length)[1]
lower = ta.lowest(low, length)[1]
plot(upper, "Upper", color.new(color.green, 0))
plot(lower, "Lower", color.new(color.red, 0))

break_up = ta.crossover(close, upper)
break_down = ta.crossunder(close, lower)

buy_command = '{"exchange":"binance-futures","pair":"{{ticker}}","apiKey":"bin-fut-main","leverage":5,"marginType":"cross","isBuy":true,"unitsType":"percent","unitsPercent":10,"isMarket":true,"stopLossType":"percent","stopLossPercent":2,"useTrailingStopLoss":true,"trailingStopLossPercent":1,"alertTimestamp":"{{ticker}}-{{timenow}}","token":"YOUR_TOKEN"}'
sell_command = '{"exchange":"binance-futures","pair":"{{ticker}}","apiKey":"bin-fut-main","leverage":5,"marginType":"cross","isSell":true,"unitsType":"percent","unitsPercent":10,"isMarket":true,"stopLossType":"percent","stopLossPercent":2,"useTrailingStopLoss":true,"trailingStopLossPercent":1,"alertTimestamp":"{{ticker}}-{{timenow}}","token":"YOUR_TOKEN"}'
close_command = '{"exchange":"binance-futures","pair":"{{ticker}}","apiKey":"bin-fut-main","isClose":true,"token":"YOUR_TOKEN"}'

if break_up
strategy.entry("Long", strategy.long, alert_message=buy_command)
if break_down
strategy.entry("Short", strategy.short, alert_message=sell_command)

// Internal Pine SL/TP for the backtest. The real SL is exchange-side via the JSON.
strategy.exit("Long-X", from_entry="Long", loss=400, profit=800, alert_message=close_command)
strategy.exit("Short-X", from_entry="Short", loss=400, profit=800, alert_message=close_command)

Configuration tips

  • marginType: "cross" is lowercase. Hyperliquid is the only exchange where casing matters — every other exchange tolerates either, so stick with lowercase as documented in Futures Options.
  • useTrailingStopLoss: true arms the trail; trailingStopLossPercent: 1 ratchets the stop at a 1 % distance. Add trailingStopLossActivationPercent to delay activation until a profit threshold is hit (see Stop Loss).
  • Use demo identifiers (binance-futures-testnet, bybit-testnet) for paper testing — see Demo Trading.

Common variations

  • Wider breakout (Turtle 55-bar): length=55 for longer-horizon trends.
  • One-sided long only: remove the break_down block entirely.

Strategy 4 — ATR Trailing Stop (Futures)

Use case: an EMA momentum bot that trails the stop by 2× ATR. SL is computed in Pine and shipped to TVH as a concrete number — no server-side math expression needed.

Best for: Bybit / OKX Perps · 30 min – 2 h · volatile altcoins where percentage SLs misfire.

//@version=5
strategy("ATR Trailing Stop — TVH", overlay=true,
default_qty_type=strategy.percent_of_equity, default_qty_value=5,
pyramiding=0, process_orders_on_close=true)

atrLen = input.int(14, "ATR Length")
atrMult = input.float(2, "ATR Multiplier")
emaLen = input.int(50, "EMA")

ema = ta.ema(close, emaLen)
atr = ta.atr(atrLen) * atrMult
upTrend = close > ema
dnTrend = close < ema

go_long = ta.crossover(close, ema) and upTrend
go_short = ta.crossunder(close, ema) and dnTrend

sl_long = close - atr
sl_short = close + atr

buy_command = str.format('{{"exchange":"bybit","pair":"{0}","apiKey":"bybit-main","leverage":3,"marginType":"cross","isBuy":true,"unitsType":"percent","unitsPercent":5,"isMarket":true,"stopLossType":"absolute","stopLoss":{1},"alertTimestamp":"{0}-{2}","token":"YOUR_TOKEN"}}', syminfo.ticker, sl_long, time)
sell_command = str.format('{{"exchange":"bybit","pair":"{0}","apiKey":"bybit-main","leverage":3,"marginType":"cross","isSell":true,"unitsType":"percent","unitsPercent":5,"isMarket":true,"stopLossType":"absolute","stopLoss":{1},"alertTimestamp":"{0}-{2}","token":"YOUR_TOKEN"}}', syminfo.ticker, sl_short, time)
close_command = '{"exchange":"bybit","pair":"{{ticker}}","apiKey":"bybit-main","isClose":true,"token":"YOUR_TOKEN"}'

if go_long
strategy.entry("Long", strategy.long, alert_message=buy_command)
if go_short
strategy.entry("Short", strategy.short, alert_message=sell_command)

// Pine-side trailing for the backtest. The exchange holds the static SL from the JSON.
strategy.exit("Long-Trail", from_entry="Long", trail_points=atr/syminfo.mintick, trail_offset=0, alert_message=close_command)
strategy.exit("Short-Trail", from_entry="Short", trail_points=atr/syminfo.mintick, trail_offset=0, alert_message=close_command)

Configuration tips

  • The SL is a literal price — stopLossType: "absolute" skips TVH's server-side expression evaluation.
  • Pine's str.format doubles braces ({{{) because {0}, {1} are format-placeholders.
  • trail_points=atr/syminfo.mintick converts ATR (price units) into Pine's tick-based trail input.

Common variations

  • Server-side trailing via stopLossExpression: swap to "stopLossExpression":"{{strategy.order.price}}-2*atr" and pre-substitute atr on the Pine side.
  • Tighter trail after TP1: add a targets ladder and lift the trail percent after the first TP fills.

Strategy 5 — DCA Grid Entry (Spot or Futures)

Use case: dollar-cost-average into longs with four limit legs spaced 1.5 % apart. Single TP at 3 % above the average entry.

Best for: Binance Spot for cash deployment · 1H–4H · mean-reversion plays on high-quality coins.

//@version=5
strategy("DCA Grid Entry — TVH", overlay=true,
default_qty_type=strategy.percent_of_equity, default_qty_value=20,
pyramiding=4, process_orders_on_close=true)

rsiLen = input.int(14, "RSI Length")
rsiBuy = input.int(35, "RSI Trigger")

r = ta.rsi(close, rsiLen)
long_signal = ta.crossover(r, rsiBuy)

// 4 legs: base + 3 DCA, each 1.5 % below the previous. 25 % of cash per leg.
buy_command = '{"exchange":"binance","pair":"{{ticker}}","apiKey":"binance-main","isBuy":true,"unitsType":"percent","unitsPercent":25,"isLimit":true,"postOnly":false,"limitPriceType":"fixedPrice","price":"{{close}}","useDca":true,"dcaPercent":1.5,"dcaOrderCount":3,"targetType":"percent","targetAmountInPercent":true,"targets":[{"takeProfitPercent":3,"amount":100}],"stopLossType":"percent","stopLossPercent":7,"alertTimestamp":"{{ticker}}-{{timenow}}","token":"YOUR_TOKEN"}'
close_command = '{"exchange":"binance","pair":"{{ticker}}","apiKey":"binance-main","isClose":true,"token":"YOUR_TOKEN"}'

if long_signal and strategy.position_size == 0
strategy.entry("DCA", strategy.long, alert_message=buy_command)

Configuration tips

  • useDca: true is the master switch — without it, dcaOrderCount and dcaPercent are ignored. The base order plus dcaOrderCount: 3 = 4 legs total. Each is 25 % of the budget so the average fills exhaust 100 %.
  • dcaPercent: 1.5 spaces legs 1.5 % below the previous fill.
  • stopLossPercent: 7 sits 7 % below the average entry price — gives the grid room before bailing.
  • Full mechanics: DCA & Scaling.

Common variations

  • Bigger ladder: dcaOrderCount: 5 plus dcaPercent: 2 for wider deeper averages.
  • Scaled distances via scaledOrderStyle: add "scaledOrderStyle":"bigSmall" (or even / smallBig) to weight the legs differently — see DCA & Scaling.

Strategy 6 — Multi-Timeframe Confirmation (Futures)

Use case: entry only when the 1H trend aligns with a 5-minute signal. SL above the 1H swing high (for shorts) or below the 1H swing low (for longs).

Best for: Binance Futures / Bybit Perps · 5m execution + 1h filter · liquid majors.

//@version=5
strategy("MTF Confirmation — TVH", overlay=true,
default_qty_type=strategy.percent_of_equity, default_qty_value=5,
pyramiding=0, process_orders_on_close=true)

// 1H trend filter
htf_ema = request.security(syminfo.tickerid, "60", ta.ema(close, 50), lookahead=barmerge.lookahead_off)
htf_swing_high = request.security(syminfo.tickerid, "60", ta.highest(high, 10), lookahead=barmerge.lookahead_off)
htf_swing_low = request.security(syminfo.tickerid, "60", ta.lowest(low, 10), lookahead=barmerge.lookahead_off)
htf_up = close > htf_ema
htf_down = close < htf_ema

// 5m entry signal
ltf_fast = ta.ema(close, 9)
ltf_slow = ta.ema(close, 21)
ltf_long = ta.crossover(ltf_fast, ltf_slow)
ltf_short = ta.crossunder(ltf_fast, ltf_slow)

long_ok = ltf_long and htf_up and barstate.isconfirmed
short_ok = ltf_short and htf_down and barstate.isconfirmed

buy_command = str.format('{{"exchange":"binance-futures","pair":"{0}","apiKey":"bin-fut-main","leverage":3,"marginType":"cross","isBuy":true,"unitsType":"percent","unitsPercent":5,"isMarket":true,"stopLossType":"absolute","stopLoss":{1},"targetType":"percent","targetAmountInPercent":true,"targets":[{{"takeProfitPercent":2,"amount":100}}],"alertTimestamp":"{0}-{2}","token":"YOUR_TOKEN"}}', syminfo.ticker, htf_swing_low, time)
sell_command = str.format('{{"exchange":"binance-futures","pair":"{0}","apiKey":"bin-fut-main","leverage":3,"marginType":"cross","isSell":true,"unitsType":"percent","unitsPercent":5,"isMarket":true,"stopLossType":"absolute","stopLoss":{1},"targetType":"percent","targetAmountInPercent":true,"targets":[{{"takeProfitPercent":2,"amount":100}}],"alertTimestamp":"{0}-{2}","token":"YOUR_TOKEN"}}', syminfo.ticker, htf_swing_high, time)
close_command = '{"exchange":"binance-futures","pair":"{{ticker}}","apiKey":"bin-fut-main","isClose":true,"token":"YOUR_TOKEN"}'

if long_ok
strategy.entry("Long", strategy.long, alert_message=buy_command)
if short_ok
strategy.entry("Short", strategy.short, alert_message=sell_command)

strategy.exit("Long-X", from_entry="Long", alert_message=close_command)
strategy.exit("Short-X", from_entry="Short", alert_message=close_command)

Configuration tips

  • lookahead=barmerge.lookahead_off is mandatory on request.security() — anything else repaints.
  • barstate.isconfirmed adds defence-in-depth on top of "Once Per Bar Close".
  • The Pine htf_swing_low is the recent 1H low; the JSON ships it as an absolute SL price. No server-side math needed.

Common variations

  • 3 timeframes: add a 4H filter (request.security(..., "240", ...)) on top.
  • ATR-padded swing SL: add 0.5 × ATR below the swing low (or above the swing high) before serialising.

Strategy 7 — Strategy Tester Setup (Backtesting Walkthrough)

Use case: a non-live strategy used purely for TradingView's Strategy Tester. Once you are happy with the backtest, the upgrade to live is a one-line change.

Best for: any backtest where you want clean equity curves before risking capital.

//@version=5
strategy("Backtest — EMA + RSI", overlay=true,
initial_capital=10000,
commission_type=strategy.commission.percent, commission_value=0.04, // 4 bps
default_qty_type=strategy.percent_of_equity, default_qty_value=10,
pyramiding=0, process_orders_on_close=true,
slippage=2) // 2 ticks slippage on every fill

fastLen = input.int(9, "Fast EMA")
slowLen = input.int(21, "Slow EMA")
rsiLen = input.int(14, "RSI")
rsiTrend = input.int(50, "RSI Trend")

fast = ta.ema(close, fastLen)
slow = ta.ema(close, slowLen)
r = ta.rsi(close, rsiLen)

long_ok = ta.crossover(fast, slow) and r > rsiTrend
short_ok = ta.crossunder(fast, slow) and r < rsiTrend

if long_ok
strategy.entry("Long", strategy.long)
if short_ok
strategy.entry("Short", strategy.short)

strategy.exit("Long-X", from_entry="Long", loss=200, profit=400)
strategy.exit("Short-X", from_entry="Short", loss=200, profit=400)

plot(fast, "Fast", color.new(color.aqua, 0))
plot(slow, "Slow", color.new(color.orange, 0))

Configuration tips

  • commission_value=0.04 represents 4 bps (0.04 %) per fill — realistic for Binance and Bybit.
  • slippage=2 adds 2 ticks of slippage to every fill. Use higher values for thinner pairs.
  • Strategy Tester does not fire alerts. No live trade is placed until you create the alert with {{strategy.order.alert_message}}.
  • The Strategy Tester tab reports profit factor, max drawdown, Sharpe ratio, and the full trade list — read them before sizing live.

Common variations

  • Walk-forward Optimisation: TradingView's Premium tier exposes optimisation — sweep fastLen / slowLen and harvest top configs.
  • Parameter persistence: save the input set with the strategy under a versioned name (e.g. Backtest v3 - 2026-05).

Next

  • Strategy Alerts — the {{strategy.order.alert_message}} mechanism powering all seven recipes.
  • Boilerplate Strategy — the input-driven GitHub template, an alternative to writing strategies from scratch.
  • Trade Command Anatomy — full field-level reference for every JSON property used above.
  • Gotchas — read this before going live with any of the strategies.