The system
What we are building: long-only on CFD instruments, daily-trend filter, hourly entry, ATR-based stops, risk-per-trade sizing, alerts wired for webhook automation. Every block uses what Parts 1–4 covered.
//@version=5
strategy("Trend-Following v1", overlay=true,
default_qty_type=strategy.cash, default_qty_value=0,
initial_capital=10000, commission_type=strategy.commission.cash_per_contract,
commission_value=0, slippage=2)
// ── Inputs ────────────────────────────────────────────────────────────
fastLen = input.int(20, "Fast EMA", minval=5)
slowLen = input.int(50, "Slow EMA", minval=10)
atrLen = input.int(14, "ATR Length", minval=5)
atrMultSL = input.float(2.0, "ATR Stop Mult", minval=0.5, step=0.1)
atrMultTP = input.float(4.0, "ATR Target Mult", minval=0.5, step=0.1)
riskPct = input.float(1.0, "Risk % per Trade", minval=0.1)
// ── Higher-timeframe trend filter ─────────────────────────────────────
dailyEMA = request.security(syminfo.tickerid, "D", ta.ema(close, 200),
lookahead=barmerge.lookahead_off)
longBias = close > dailyEMA
// ── Entry signal ──────────────────────────────────────────────────────
fastMA = ta.ema(close, fastLen)
slowMA = ta.ema(close, slowLen)
entryLong = ta.crossover(fastMA, slowMA) and longBias
// ── Sizing ────────────────────────────────────────────────────────────
atrVal = ta.atr(atrLen)
stopDist = atrVal * atrMultSL
riskMoney = strategy.equity * riskPct / 100
contracts = riskMoney / (stopDist * syminfo.pointvalue)
contracts := math.max(contracts, 0)
// ── Entries and exits ─────────────────────────────────────────────────
if entryLong
strategy.entry("Long", strategy.long, qty=contracts)
strategy.exit("X", "Long", stop=close - stopDist, limit=close + stopDist * (atrMultTP / atrMultSL))
// ── Visualisation ─────────────────────────────────────────────────────
plot(fastMA, color=color.new(color.blue, 60))
plot(slowMA, color=color.new(color.orange, 60))
plot(dailyEMA, color=color.new(color.gray, 70), linewidth=2)
plotshape(entryLong, "Entry", shape.triangleup, location.belowbar, color.green, size=size.small)
// ── Webhook-ready alert payload ───────────────────────────────────────
if strategy.position_size > 0 and strategy.position_size[1] == 0
alert('{"action":"buy","ticker":"' + syminfo.ticker + '","qty":"' + str.tostring(contracts) + '","sl":"' + str.tostring(close - stopDist) + '","tp":"' + str.tostring(close + stopDist * (atrMultTP / atrMultSL)) + '"}', alert.freq_once_per_bar_close)
if strategy.position_size == 0 and strategy.position_size[1] > 0
alert('{"action":"close","ticker":"' + syminfo.ticker + '"}', alert.freq_once_per_bar_close)
How it works
Long-only. The daily 200-EMA is the regime filter — no longs in a daily downtrend. Entries fire on the fast EMA crossing the slow EMA, but only when the daily trend agrees. Stops are 2x ATR from entry, targets are 4x ATR. Position size is set so that a stop-hit costs exactly 1% of equity. The alert payload is JSON, ready to be routed to a webhook.
What you should change before going live
Add slippage to the strategy declaration if your broker requotes. Add process_orders_on_close=false if you want to execute on the next bar's open instead of the close. Run the strategy through every market regime you can find in the data (2017 crypto mania, 2020 covid crash, 2022 rates shock) before trusting it on a live account.
What you now know
You have the full toolchain. Open the Pine Editor, paste this in, and walk through it line by line. Every line is something Parts 1–4 covered. The next strategies you write will be variations on this template.