Strategy · Pine Script v6 · On the chart

Adaptive Pullback Momentum 5m

by rcaldwell67· code credited to APM Strategy

This strategy trades pullbacks to the 21 EMA inside a full EMA 21, 50, 200 stack, shorts only by default.

View on GitHub

GitHub stars1on the repository
Pine versionv6current
Lines of code377
Settings36inputs
LicenceNone statedfile: MPL-2.0
Last update2 months ago8 Aug 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new strategy, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart. The trades and results appear in the Strategy Tester tab; adjust the settings with the gear icon.

Source Code

377 lines of Pine Script v6 from backend/pine_templates/APM v1.0-5m.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

APM v1.0-5m.pine
// This source code is subject to the terms of the Mozilla Public License 2.0
// © APM Strategy

//@version=6

// ─────────────────────────────────────────────────────────────────────────────
// Adaptive Pullback Momentum v1 — 5m
// Timeframe : 5-minute  ·  Instrument : <SYMBOL_PLACEHOLDER>
//
// THESIS
//   Enter short during brief intraday pullbacks to EMA21 while the macro trend
//   is confirmed by a full bearish EMA stack (21 < 50 < 200), capturing
//   momentum resumption after the pullback with tight, ATR-scaled risk.
//   v1.1 relaxes the ADX threshold and widens the pullback tolerance to
//   increase signal count, with a wider TP and later trail activation.
//
// ENTRY LOGIC  (SHORTS ONLY by default — sub-15m longs historically low WR)
//   Short : Full bearish stack  EMA21 < EMA50 < EMA200
//            EMA21 falling over 3 bars (slope filter)
//            ADX > 15  (trend strength — relaxed from 18; captures more BTCUSD setups)
//            RSI falling on entry bar  +  RSI in [30–58]
//            Close < close[5] (5-bar momentum confirms direction)
//            Prev bar high tagged EMA21 zone (±0.40%); current bar closes below with body
//            Volume ≥ 0.3× VolSMA  |  Body ≥ 0.15×ATR  |  ATR floor ≥ 0.1%
//            Session 09:00–14:00 ET  (NYSE open through midday)
//            No panic: ATR ≤ ATR-60SMA × 1.5
//            Macro filter: OFF (sweep v1.1 — removed; didn't improve 12mo net)
//   Optional: ADX slope (0=off), DI spread (0=off) — sweep: not beneficial on BTCUSD
//
// EXIT LOGIC  (v1.1 sweep-optimised on BTCUSD 5m 12-month window via Alpaca)
//   → Hard SL    : entry + ATR × 4.0  (wider; low ATR on BTCUSD means SL hits cleanly)
//   → Hard TP    : entry − ATR × 8.0  (raised from 6.0; gives trail more room)
//   → Trail      : activates at ATR × 3.5 runup (later lock-in to avoid early exit),
//                  trails ATR × 0.1 from best price (very tight — captures trail gains quickly)
//   → Max bars   : disabled (0)
//   → Sizing     : 2% of equity risked per trade (raised from 1%)
//
// SWEEP RESULTS v1.1 (12-month 2025-03-13→2026-03-11, 9,018 5m bars via Alpaca Delayed):
//   v1.0 params : 81T  WR=76.5%  PF=1.498  net=+11.62%  MaxDD=-2.32%  Calmar=5.005
//   v1.1 params : 29T  WR=79.3%  PF=2.802  net=+27.73%  MaxDD=-5.46%  Calmar=5.081
//   Key changes : ADX 18→15, PB 0.25%→0.40%, TP ×6→×8, Trail_act 2.5→3.5, Risk 1%→2%, Macro OFF
// ─────────────────────────────────────────────────────────────────────────────

strategy("Adaptive Pullback Momentum v1.1 · 5m", shorttitle="APM v1.1 5m", overlay=true, pyramiding=0, initial_capital=1000, default_qty_type=strategy.cash, default_qty_value=1000, commission_type=strategy.commission.percent, commission_value=0.06, slippage=1, margin_long=20, margin_short=20)

// ── Inputs ────────────────────────────────────────────────────────────────────
var G_TREND = "Trend Filters"
i_ema_fast   = input.int  (21,   "EMA Fast",               group=G_TREND, minval=5)
i_ema_mid    = input.int  (50,   "EMA Mid",                group=G_TREND, minval=10)
i_ema_slow   = input.int  (200,  "EMA Slow",               group=G_TREND, minval=50)
i_adx_thresh = input.int  (15,   "ADX threshold",          group=G_TREND, minval=5, maxval=60)
i_adx_len    = input.int  (14,   "ADX / DI length",        group=G_TREND, minval=5)
i_adx_slope  = input.int  (0,    "ADX slope bars (0=off)", group=G_TREND, minval=0, maxval=20,
                 tooltip="ADX must be higher than N bars ago. 0 = disabled. Stage-1 sweep showed this filter is not beneficial on BTCUSD.")
i_di_spread  = input.float(0.0,  "DI spread (DI−−DI+)",   group=G_TREND, minval=0.0, step=0.5,
                 tooltip="For shorts: DI- must exceed DI+ by this many points. 0 = disabled. Stage-1 sweep showed DI spread hurts entries on BTCUSD — keep at 0 unless re-optimised.")
i_pb_pct     = input.float(0.40, "Pullback tolerance (%)", group=G_TREND, minval=0.05, maxval=3.0, step=0.05,
                 tooltip="How close prev bar high must come to EMA21 to qualify as a pullback.")
i_mom_bars   = input.int  (5,    "Momentum bars (0=off)",  group=G_TREND, minval=0, maxval=20,
                 tooltip="Close must be below close[N] for shorts (confirms directional pressure).")

var G_MOM = "Momentum & Volume"
i_rsi_len    = input.int  (14,   "RSI length",             group=G_MOM, minval=3)
i_rsi_lo_l   = input.int  (42,   "RSI min – Long",         group=G_MOM, minval=10, maxval=70)
i_rsi_hi_l   = input.int  (68,   "RSI max – Long",         group=G_MOM, minval=50, maxval=90)
i_rsi_lo_s   = input.int  (30,   "RSI min – Short",        group=G_MOM, minval=10, maxval=50)
i_rsi_hi_s   = input.int  (58,   "RSI max – Short",        group=G_MOM, minval=40, maxval=80)
i_vol_len    = input.int  (20,   "Volume SMA length",      group=G_MOM, minval=5)
i_vol_mult   = input.float(0.3,  "Volume multiplier",      group=G_MOM, minval=0.1, maxval=5.0, step=0.1)
i_min_body   = input.float(0.15, "Min body (x ATR)",       group=G_MOM, minval=0.0, maxval=2.0, step=0.05,
                 tooltip="Entry bar |close-open|/ATR must exceed this. Filters doji candles.")

var G_VOL = "Volatility"
i_atr_len    = input.int  (14,   "ATR length",              group=G_VOL, minval=3)
i_atr_bl_len = input.int  (60,   "ATR baseline length",     group=G_VOL, minval=10)
i_atr_floor  = input.float(0.1,  "ATR floor (% of price)",  group=G_VOL, minval=0.0, maxval=2.0, step=0.01,
                 tooltip="ATR/price must exceed this %. Stage-2 (12mo sweep) winner: 0.1% — removes near-zero ATR bars where commission drag dominates.")
i_panic_mult = input.float(1.5,  "Panic suppression mult",  group=G_VOL, minval=1.0, step=0.1,
                 tooltip="ATR > ATR_baseline x this value => skip new entries.")

var G_SESS = "Session"
i_sess_on    = input.bool (true,  "Enable session filter",   group=G_SESS)
i_sess_start = input.int  (9,    "Session start (ET hour)", group=G_SESS, minval=0, maxval=23)
i_sess_end   = input.int  (14,   "Session end (ET hour)",   group=G_SESS, minval=0, maxval=23,
                 tooltip="Entries blocked from this hour onward. Default 14 = 2:00 PM ET.")

var G_RISK = "Risk Management"
// Risk management tweaks: tighter SL, partial TP, max bars exit enabled
i_sl_mult    = input.float(3.5,  "Stop loss ATR mult",       group=G_RISK, minval=0.3, step=0.1)
i_tp_mult    = input.float(8.0,  "Take profit ATR mult",     group=G_RISK, minval=0.5, step=0.1)
i_tr_act     = input.float(3.0,  "Trail activate ATR mult",  group=G_RISK, minval=0.3, step=0.1,
                 tooltip="Trailing stop activates once price moves this many ATRs in your favour. Earlier activation to lock gains sooner.")
i_tr_dist    = input.float(0.15,  "Trail distance ATR mult",  group=G_RISK, minval=0.1, step=0.05,
                 tooltip="Trail stays this many ATRs from best price. Slightly wider to avoid premature exits.")
i_risk_pct   = input.float(2.0,  "Equity risked per trade (%)", group=G_RISK, minval=0.1, maxval=20, step=0.1)
i_max_bars   = input.int  (30,    "Max bars in trade (0=off)",   group=G_RISK, minval=0, maxval=500,
                 tooltip="30 bars max per trade. Avoids holding through adverse moves.")

var G_MACRO = "Macro Bias Filter"
i_macro_ema  = input.int  (0,   "Macro EMA period (0=off)", group=G_MACRO, minval=0, maxval=5000,
                 tooltip="Only trade shorts when close < EMA(N). Filters out shorting during sustained uptrends. 0 = disabled. v1.1 sweep result: OFF — removing macro filter increased net return on BTCUSD 5m 12mo window.")

var G_DISP = "Display"
i_longs      = input.bool  (false,       "Trade Longs",   group=G_DISP)
i_shorts     = input.bool  (true,        "Trade Shorts",  group=G_DISP)
i_show_emas  = input.bool  (true,        "Show EMAs",     group=G_DISP)
i_show_dash  = input.bool  (true,        "Show Dashboard",group=G_DISP)
i_dash_pos   = input.string("Top Right", "Dashboard position", group=G_DISP,
                 options=["Top Right","Top Left","Bottom Right","Bottom Left","Middle Right","Middle Left"])

// ── Indicators ────────────────────────────────────────────────────────────────
ema_fast   = ta.ema(close, i_ema_fast)
ema_mid    = ta.ema(close, i_ema_mid)
ema_slow   = ta.ema(close, i_ema_slow)
macro_ema  = i_macro_ema > 0 ? ta.ema(close, i_macro_ema) : na
rsi      = ta.rsi(close, i_rsi_len)
atr      = ta.atr(i_atr_len)
atr_bl   = ta.sma(atr, i_atr_bl_len)
vol_ma   = ta.sma(volume, i_vol_len)

[di_plus, di_minus, adx] = ta.dmi(i_adx_len, i_adx_len)

// ── Regime guards ─────────────────────────────────────────────────────────────
is_trending  = adx > i_adx_thresh
is_panic     = atr > atr_bl * i_panic_mult
adx_rising   = i_adx_slope > 0 ? adx > adx[i_adx_slope] : true
atr_floor_ok = atr / close * 100 >= i_atr_floor
macro_bear   = i_macro_ema > 0 ? close < macro_ema : true   // Stage-4: avoid uptrend shorts

// ── EMA structure ─────────────────────────────────────────────────────────────
ema_bull_full  = ema_fast > ema_mid  and ema_mid  > ema_slow
ema_bear_full  = ema_fast < ema_mid  and ema_mid  < ema_slow
ema_slope_up   = ema_fast > ema_fast[3]
ema_slope_down = ema_fast < ema_fast[3]

// ── RSI direction ─────────────────────────────────────────────────────────────
rsi_rising  = rsi > rsi[1]
rsi_falling = rsi < rsi[1]

// ── DI spread  (bearish / bullish directional dominance) ─────────────────────
di_bear_dom = i_di_spread > 0.0 ? (di_minus - di_plus) >= i_di_spread : true
di_bull_dom = i_di_spread > 0.0 ? (di_plus - di_minus) >= i_di_spread : true

// ── Momentum  (multi-bar directional pressure) ───────────────────────────────
mom_bear = i_mom_bars > 0 ? close < close[i_mom_bars] : true
mom_bull = i_mom_bars > 0 ? close > close[i_mom_bars] : true

// ── Session filter  (ET timezone) ─────────────────────────────────────────────
et_hour    = hour(time, "America/New_York")
in_session = not i_sess_on or (et_hour >= i_sess_start and et_hour < i_sess_end)

// ── Pullback-to-EMA trigger ───────────────────────────────────────────────────
// Long : prev bar low tagged EMA21 zone from above; current bar closes above
//        EMA21 with a bullish body (confirms resumption)
pb_tol_up = ema_fast[1] * (1.0 + i_pb_pct / 100.0)
// Short: prev bar high tagged EMA21 zone from below; current bar closes below
//        EMA21 with a bearish body (confirms resumption)
pb_tol_dn = ema_fast[1] * (1.0 - i_pb_pct / 100.0)

body_size = math.abs(close - open) / atr

long_pb  = low[1]  <= pb_tol_up and close > ema_fast and close > open and body_size >= i_min_body
short_pb = high[1] >= pb_tol_dn and close < ema_fast and close < open and body_size >= i_min_body

// ── Full entry conditions ─────────────────────────────────────────────────────
no_pos    = strategy.position_size == 0.0
stop_dist = atr * i_sl_mult
risk_qty  = stop_dist == 0.0 ? 0.0 : strategy.equity * i_risk_pct / 100.0 / stop_dist
max_qty   = close == 0.0 ? 0.0 : strategy.equity * 5.0 / close
entry_qty = math.min(risk_qty, max_qty)

long_ok = i_longs and no_pos and not is_panic and is_trending and adx_rising and atr_floor_ok and in_session and long_pb and ema_bull_full and ema_slope_up and rsi_rising and di_bull_dom and mom_bull and rsi >= i_rsi_lo_l and rsi <= i_rsi_hi_l and volume >= vol_ma * i_vol_mult

short_ok = i_shorts and no_pos and not is_panic and is_trending and adx_rising and atr_floor_ok and in_session and short_pb and ema_bear_full and ema_slope_down and rsi_falling and di_bear_dom and mom_bear and rsi >= i_rsi_lo_s and rsi <= i_rsi_hi_s and volume >= vol_ma * i_vol_mult and macro_bear

// ── State vars ────────────────────────────────────────────────────────────────
var float sl_price      = na
var float tp_price      = na
var float best_price    = na
var float entry_p       = na
var bool  trail_active  = false
var int   bars_in_trade = 0

// ── Entries ───────────────────────────────────────────────────────────────────
if long_ok
    qty          = entry_qty
    sl_price    := close - stop_dist
    tp_price    := close + atr * i_tp_mult
    best_price  := close
    trail_active := false
    strategy.entry("Long", strategy.long, qty=qty)
    long_msg = "APM v1 5m | LONG ENTRY | " + syminfo.ticker + " [" + timeframe.period + "]\nEntry: " + str.tostring(close, "#.####") + " | Stop: " + str.tostring(sl_price, "#.####") + " | Target: " + str.tostring(tp_price, "#.####") + "\nQty: " + str.tostring(qty, "#.####") + " | ATR: " + str.tostring(atr, "#.####") + " | RSI: " + str.tostring(rsi, "#.##") + " | ADX: " + str.tostring(adx, "#.##")
    alert(long_msg, alert.freq_once_per_bar_close)

if short_ok
    qty          = entry_qty
    sl_price    := close + stop_dist
    tp_price    := close - atr * i_tp_mult
    best_price  := close
    trail_active := false
    strategy.entry("Short", strategy.short, qty=qty)
    short_msg = "APM v1 5m | SHORT ENTRY | " + syminfo.ticker + " [" + timeframe.period + "]\nEntry: " + str.tostring(close, "#.####") + " | Stop: " + str.tostring(sl_price, "#.####") + " | Target: " + str.tostring(tp_price, "#.####") + "\nQty: " + str.tostring(qty, "#.####") + " | ATR: " + str.tostring(atr, "#.####") + " | RSI: " + str.tostring(rsi, "#.##") + " | ADX: " + str.tostring(adx, "#.##")
    alert(short_msg, alert.freq_once_per_bar_close)

// ── Trailing stop management (every bar while in trade) ───────────────────────
if strategy.position_size > 0.0          // long
    bars_in_trade += 1
    best_price := math.max(best_price, high)
    entry_p    := strategy.position_avg_price
    if best_price >= entry_p + atr * i_tr_act
        trail_sl = best_price - atr * i_tr_dist
        new_sl   = math.max(sl_price, trail_sl)
        if not trail_active
            trail_active := true
            trail_long_msg = "APM v1 5m | TRAIL LONG | " + syminfo.ticker + " [" + timeframe.period + "]\nBest: " + str.tostring(best_price, "#.####") + " | Entry: " + str.tostring(entry_p, "#.####") + " | New SL: " + str.tostring(new_sl, "#.####")
            alert(trail_long_msg, alert.freq_once_per_bar_close)
        sl_price := new_sl
    strategy.exit("Long Exit", "Long", stop=sl_price, limit=tp_price)
    if i_max_bars > 0 and bars_in_trade >= i_max_bars
        strategy.close("Long", comment="MaxB")

if strategy.position_size < 0.0          // short
    bars_in_trade += 1
    best_price := math.min(best_price, low)
    entry_p    := strategy.position_avg_price
    // Partial profit-taking: exit half at ATR x 4
    partial_tp = entry_p - atr * 4.0
    if best_price <= partial_tp and strategy.position_size < 0.0
        strategy.exit("Short Partial TP", "Short", qty=strategy.position_size/2, limit=partial_tp)
    if best_price <= entry_p - atr * i_tr_act
        trail_sl = best_price + atr * i_tr_dist
        new_sl   = math.min(sl_price, trail_sl)
        if not trail_active
            trail_active := true
            trail_short_msg = "APM v1 5m | TRAIL SHORT | " + syminfo.ticker + " [" + timeframe.period + "]\nBest: " + str.tostring(best_price, "#.####") + " | Entry: " + str.tostring(entry_p, "#.####") + " | New SL: " + str.tostring(new_sl, "#.####")
            alert(trail_short_msg, alert.freq_once_per_bar_close)
        sl_price := new_sl
    strategy.exit("Short Exit", "Short", stop=sl_price, limit=tp_price)
    if i_max_bars > 0 and bars_in_trade >= i_max_bars
        strategy.close("Short", comment="MaxB")

if strategy.position_size == 0.0
    bars_in_trade := 0

// ── Exit detection alert ──────────────────────────────────────────────────────
var int _prev_closed = 0
if strategy.closedtrades > _prev_closed
    _prev_closed := strategy.closedtrades
    _ti   = strategy.closedtrades - 1
    _ep   = strategy.closedtrades.entry_price(_ti)
    _xp   = strategy.closedtrades.exit_price(_ti)
    _pnl  = strategy.closedtrades.profit(_ti)
    _comm = strategy.closedtrades.commission(_ti)
    _sz   = strategy.closedtrades.size(_ti)
    _bars = strategy.closedtrades.exit_bar_index(_ti) - strategy.closedtrades.entry_bar_index(_ti)
    _ru   = strategy.closedtrades.max_runup(_ti)
    _dir  = _sz > 0 ? "LONG" : "SHORT"
    _res  = _pnl >= 0 ? "WIN" : "LOSS"
    _mv   = _sz > 0 ? (_xp - _ep) / _ep * 100 : (_ep - _xp) / _ep * 100
    _wr   = strategy.closedtrades > 0 ? str.tostring(math.round(strategy.wintrades / strategy.closedtrades * 100, 1), "#.#") + "%" : "--"
    exit_msg = "APM v1 5m | " + _dir + " EXIT [" + _res + "] | " + syminfo.ticker + " [" + timeframe.period + "]\nEntry: " + str.tostring(_ep, "#.####") + " -> Exit: " + str.tostring(_xp, "#.####") + " | P&L: " + str.tostring(_pnl, "#.##") + " | Bars: " + str.tostring(_bars) + " | WR: " + _wr
    alert(exit_msg, alert.freq_once_per_bar_close)

// ── Panic regime alerts ───────────────────────────────────────────────────────
if is_panic and not is_panic[1]
    panic_start_msg = "APM v1 5m | PANIC START | " + syminfo.ticker + "\nATR: " + str.tostring(atr, "#.####") + " | Baseline: " + str.tostring(atr_bl, "#.####") + " | Ratio: " + str.tostring(atr / atr_bl, "#.##") + "x"
    alert(panic_start_msg, alert.freq_once_per_bar_close)

if not is_panic and is_panic[1]
    panic_clear_msg = "APM v1 5m | PANIC CLEAR | " + syminfo.ticker + "\nATR: " + str.tostring(atr, "#.####") + " | Baseline: " + str.tostring(atr_bl, "#.####") + " | Ratio: " + str.tostring(atr / atr_bl, "#.##") + "x"
    alert(panic_clear_msg, alert.freq_once_per_bar_close)

// ── Plots ─────────────────────────────────────────────────────────────────────
plot(i_show_emas ? ema_fast : na, "EMA Fast", color=color.new(#5b9ef4, 0), linewidth=1)
plot(i_show_emas ? ema_mid  : na, "EMA Mid",  color=color.new(#f6ad55, 0), linewidth=1)
plot(i_show_emas ? ema_slow : na, "EMA Slow", color=color.new(#f6e05e, 0), linewidth=2)

plot(strategy.position_size != 0 ? sl_price : na, "Stop",   color=color.new(color.red,  0), linewidth=1, style=plot.style_linebr)
plot(strategy.position_size != 0 ? tp_price : na, "Target", color=color.new(color.lime, 0), linewidth=1, style=plot.style_linebr)

plotshape(long_ok,  title="Long entry",  style=shape.triangleup,   location=location.belowbar, color=color.new(color.lime, 0), size=size.small)
plotshape(short_ok, title="Short entry", style=shape.triangledown, location=location.abovebar, color=color.new(color.red,  0), size=size.small)

// Background: panic (red) or outside session (grey)
_bg_active = is_panic ? color.new(color.red, 90) : (not in_session ? color.new(color.gray, 93) : na)
bgcolor(_bg_active, title="Regime / session bg")

// ── Dashboard colours ─────────────────────────────────────────────────────────
var color D_HDR = color.new(#1a1a2e,  5)
var color D_BG  = color.new(#0d0d1a, 15)
var color D_BRT = color.new(#e2e8f0,  0)
var color D_DIM = color.new(#718096,  0)
var color D_GRN = color.new(#48bb78,  0)
var color D_RED = color.new(#fc8181,  0)
var color D_YEL = color.new(#f6e05e,  0)
var color D_ORG = color.new(#f6ad55,  0)
var color D_BLU = color.new(#63b3ed,  0)

// ── Dashboard row helper ──────────────────────────────────────────────────────
_row(tbl, r, lbl, val, vc) =>
    table.cell(tbl, 0, r, lbl, text_color=D_DIM, bgcolor=D_BG, text_size=size.small, text_halign=text.align_left)
    table.cell(tbl, 1, r, val, text_color=vc,    bgcolor=D_BG, text_size=size.small, text_halign=text.align_right)

// ── Dashboard ─────────────────────────────────────────────────────────────────
if i_show_dash and barstate.islast
    tbl_pos = position.top_right
    if i_dash_pos == "Top Left"
        tbl_pos := position.top_left
    else if i_dash_pos == "Bottom Right"
        tbl_pos := position.bottom_right
    else if i_dash_pos == "Bottom Left"
        tbl_pos := position.bottom_left
    else if i_dash_pos == "Middle Right"
        tbl_pos := position.middle_right
    else if i_dash_pos == "Middle Left"
        tbl_pos := position.middle_left

    tbl = table.new(tbl_pos, columns=2, rows=29,
         border_width=1, border_color=color.new(color.gray, 60),
         frame_width=1,  frame_color=color.new(color.gray, 40))

    // header
    table.cell(tbl, 0, 0, "APM v1  5m", text_color=D_BRT, bgcolor=D_HDR, text_size=size.normal, text_halign=text.align_left)
    table.cell(tbl, 1, 0, "5m | " + syminfo.ticker, text_color=D_YEL, bgcolor=D_HDR, text_size=size.small, text_halign=text.align_right)

    // position
    pos_size = strategy.position_size
    _row(tbl, 1, "Position",       pos_size > 0 ? "LONG" : pos_size < 0 ? "SHORT" : "FLAT", pos_size > 0 ? D_GRN : pos_size < 0 ? D_RED : D_DIM)
    _row(tbl, 2, "Entry",          pos_size == 0 ? "--" : str.tostring(strategy.position_avg_price, "#.####"), D_BRT)
    _row(tbl, 3, "Stop",           na(sl_price) or pos_size == 0 ? "--" : str.tostring(sl_price, "#.####"), D_RED)
    _row(tbl, 4, "Target",         na(tp_price) or pos_size == 0 ? "--" : str.tostring(tp_price, "#.####"), D_GRN)
    _row(tbl, 5, "Trail",          pos_size == 0 ? "--" : trail_active ? "ACTIVE" : "waiting", trail_active ? D_GRN : D_DIM)
    _row(tbl, 6, "Bars in trade",  pos_size == 0 ? "--" : str.tostring(bars_in_trade) + " / " + str.tostring(i_max_bars > 0 ? i_max_bars : 999), D_BRT)

    upnl     = pos_size == 0 ? 0.0 : (close - strategy.position_avg_price) * math.abs(pos_size) * (pos_size > 0 ? 1 : -1)
    upnl_str = pos_size == 0 ? "--" : (upnl >= 0 ? "+" : "") + str.tostring(upnl, "#.##")
    _row(tbl, 7, "Unrealised P&L", upnl_str, upnl >= 0 ? D_GRN : D_RED)

    // indicators header
    table.cell(tbl, 0, 8, "Indicators", text_color=D_DIM, bgcolor=D_BG, text_size=size.tiny, text_halign=text.align_left)
    table.cell(tbl, 1, 8, "",           text_color=D_DIM, bgcolor=D_BG)

    _row(tbl,  9, "ADX",     str.tostring(adx, "#.#")      + (is_trending ? " ok" : " --") + (adx_rising ? " rising" : ""), is_trending ? D_GRN : D_RED)
    _row(tbl, 10, "DI+ / DI-", str.tostring(di_plus, "#.#") + " / " + str.tostring(di_minus, "#.#"), di_minus > di_plus ? D_RED : D_GRN)
    _row(tbl, 11, "RSI",     str.tostring(rsi, "#.#"),      rsi > 70 ? D_RED : rsi < 30 ? D_GRN : D_BRT)
    _row(tbl, 12, "ATR",     str.tostring(atr, "#.####"),   D_BLU)
    _row(tbl, 13, "Vol/MA",  str.tostring(volume / vol_ma, "#.##") + "x", volume >= vol_ma * i_vol_mult ? D_GRN : D_DIM)

    // filters header
    table.cell(tbl, 0, 14, "Filters", text_color=D_DIM, bgcolor=D_BG, text_size=size.tiny, text_halign=text.align_left)
    table.cell(tbl, 1, 14, "",        text_color=D_DIM, bgcolor=D_BG)

    _row(tbl, 15, "Trend",      is_trending   ? "PASS"     : "FAIL",          is_trending   ? D_GRN : D_RED)
    _row(tbl, 16, "ADX slope",  adx_rising    ? "RISING"   : "FLAT/FALL",     adx_rising    ? D_GRN : D_DIM)
    _row(tbl, 17, "DI spread",  di_bear_dom   ? "BEAR DOM" : di_bull_dom ? "BULL DOM" : "WEAK", di_bear_dom ? D_RED : di_bull_dom ? D_GRN : D_DIM)
    _row(tbl, 18, "Momentum",   mom_bear      ? "DOWN" : mom_bull ? "UP" : "--",  mom_bear ? D_RED : mom_bull ? D_GRN : D_DIM)
    _row(tbl, 19, "Panic",      is_panic      ? "ACTIVE"   : "OK",             is_panic      ? D_YEL : D_GRN)
    _row(tbl, 20, "ATR floor",  atr_floor_ok  ? "PASS"     : "FAIL",           atr_floor_ok  ? D_GRN : D_RED)
    _row(tbl, 21, "Session",    in_session    ? "ACTIVE"   : "CLOSED",         in_session    ? D_GRN : D_DIM)
    _row(tbl, 22, "Macro bias",   ema_bull_full ? "Bullish"  : ema_bear_full ? "Bearish" : "Neutral", ema_bull_full ? D_GRN : ema_bear_full ? D_RED : D_ORG)
    _row(tbl, 23, "Macro EMA",    macro_bear ? "PASS" : "BLOCKED",  macro_bear ? D_GRN : D_RED)

    // performance header
    table.cell(tbl, 0, 24, "Performance", text_color=D_DIM, bgcolor=D_BG, text_size=size.tiny, text_halign=text.align_left)
    table.cell(tbl, 1, 24, "",            text_color=D_DIM, bgcolor=D_BG)

    total_trades = strategy.wintrades + strategy.losstrades
    wr_str  = total_trades == 0 ? "--" : str.tostring(math.round(strategy.wintrades / total_trades * 100, 1)) + "%"
    pf_raw  = strategy.grossloss == 0 ? na : strategy.grossprofit / math.abs(strategy.grossloss)
    var float _peak_equity = strategy.equity
    _peak_equity := math.max(_peak_equity, strategy.equity)
    drawdown_pct = _peak_equity == 0 ? 0.0 : ((_peak_equity - strategy.equity) / _peak_equity) * 100.0

    _row(tbl, 25, "Net P&L",      (strategy.netprofit >= 0 ? "+" : "") + str.tostring(strategy.netprofit, "#.##"), strategy.netprofit >= 0 ? D_GRN : D_RED)
    _row(tbl, 26, "Win rate",     wr_str + " (" + str.tostring(strategy.wintrades) + "/" + str.tostring(total_trades) + ")", D_BRT)
    _row(tbl, 27, "Prof. factor", na(pf_raw) ? "--" : str.tostring(pf_raw, "#.##"), na(pf_raw) ? D_DIM : pf_raw >= 1.0 ? D_GRN : D_RED)
    _row(tbl, 28, "Max DD",       str.tostring(math.round(drawdown_pct, 2)) + "%", D_RED)

Overview

Filters include ADX strength, an RSI range, volume relative to its average, a minimum candle body, an ATR floor, a panic-volatility check and an optional New York session window. Exits use an ATR-based stop loss and take profit, a trailing stop and an optional maximum bar count. Size is set by percent of equity risked. It draws on the price chart.

Details

Published by

rcaldwell67 on GitHub

Code credit

APM Strategy, as written in the script's header

Draws

On the price chart

Markets

Crypto

Timeframes

M1-M5

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
EMA Fastint21i_ema_fast
EMA Midint50i_ema_mid
EMA Slowint200i_ema_slow
ADX thresholdint15i_adx_thresh
ADX / DI lengthint14i_adx_len
ADX slope bars (0=off)int0i_adx_slope
DI spread (DI−−DI+)float0.0i_di_spread
Pullback tolerance (%)float0.40i_pb_pct
Momentum bars (0=off)int5i_mom_bars
RSI lengthint14i_rsi_len
RSI min – Longint42i_rsi_lo_l
RSI max – Longint68i_rsi_hi_l
Show all 36 settings
SettingTypeDefaultVariable
RSI min – Shortint30i_rsi_lo_s
RSI max – Shortint58i_rsi_hi_s
Volume SMA lengthint20i_vol_len
Volume multiplierfloat0.3i_vol_mult
Min body (x ATR)float0.15i_min_body
ATR lengthint14i_atr_len
ATR baseline lengthint60i_atr_bl_len
ATR floor (% of price)float0.1i_atr_floor
Panic suppression multfloat1.5i_panic_mult
Enable session filterbooltruei_sess_on
Session start (ET hour)int9i_sess_start
Session end (ET hour)int14i_sess_end
Stop loss ATR multfloat3.5i_sl_mult
Take profit ATR multfloat8.0i_tp_mult
Trail activate ATR multfloat3.0i_tr_act
Trail distance ATR multfloat0.15i_tr_dist
Equity risked per trade (%)float2.0i_risk_pct
Max bars in trade (0=off)int30i_max_bars
Macro EMA period (0=off)int0i_macro_ema
Trade Longsboolfalsei_longs
Trade Shortsbooltruei_shorts
Show EMAsbooltruei_show_emas
Show Dashboardbooltruei_show_dash
Dashboard positionstringTop Righti_dash_pos
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

106 stars

This trading strategy combines three Exponential Moving Averages (8, 14, and 50 periods by default) with Stochastic RSI and Average True Range (ATR). It enters a long position when EMA 1 is above EMA 2, EMA 2 is above EMA 3, and the Stochastic RSI %K line cros

Strategy v4 TrendOscillators & momentum
Updated 2 years ago · GPL-3.0 GitHub repository of 3 x EMA + Stochastic RSI + ATR

Momentum Rider DAILY FINAL

by greenpeas007
58 stars

This strategy combines exponential moving averages (EMA 9, 21, 50), RSI, MACD, and Supertrend indicators to enter long positions. It triggers trades using six dynamic setups based on momentum breakouts, EMA crossovers, and pullbacks. Position sizing is compute

Strategy v6 TrendOscillators & momentum Repaints
Updated 4 months ago · Custom licence GitHub repository of Momentum Rider DAILY FINAL
57 stars

This strategy identifies trend reversals through market structure changes (CHoCH) filtered by MACD momentum. It initiates long positions on bullish structure breaks when MACD is positive and above its signal line, and short positions on bearish breaks when MAC

Strategy v6 SMC / ICT / price actionOscillators & momentum Repaints
Updated 1 year ago · Apache-2.0 GitHub repository of Market Structure MACD 4H + Trailing

CombinedBinHAndClucV6

by viktors1982
36 stars

CombinedBinHAndClucV6 is a trading strategy based on the popular BinHV45 and ClucMay72018 algorithms. It calculates dual Bollinger Bands, Exponential Moving Averages, Relative Strength Index, Average True Range, and volume metrics across primary and one-hour h

Strategy v4 Volatility & bandsOscillators & momentum Repaints
Updated 2 years ago · None stated GitHub repository of CombinedBinHAndClucV6
27 stars

LuxAlgo Hybrid Gold Scalper is a price-chart strategy aimed at gold. It stacks a fast, slow, trend and 200-period bias EMA to mark trend direction, then filters strength with RSI, ADX and an intraday VWAP. Swing highs and lows are marked from pivot length. Set

Strategy v5 TrendOscillators & momentum
Updated 5 months ago · None stated GitHub repository of LuxAlgo Hybrid Gold Scalper

More Trend scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers