A long-only crypto spot strategy variant that makes a multi-timeframe moving average trend the primary entry trigger, with 300-period averages on one-minute and five-minute charts and a rising-slope requirement.
1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new strategy, select all of the template text and paste. Press Save and give it a name.
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
1,317 lines of Pine Script v6 from platform/Mystrategy/MA_RR STRATEGY.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.
MA_RR STRATEGY.pine
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// SR+Trend v9 — MTF pivot S/R + MA trend (MTF) + SuperTrend + LinReg + VWMA + Volume + HH/HL structure
//
// WHAT CHANGED vs v8:
// ADDED: Session Time Filter (Logic 1) — restrict entries to UTC hours; auto-close at session end.
// ADDED: Entry Bar Body Filter (Logic 2) — reject bearish entry bars exceeding ATR threshold.
// ADDED: Vol Exhaust Re-Entry Block (Logic 4) — prevent re-entry near exit zone for N bars.
// ADDED: Adaptive TP (Logic 5) — higher R in strong HH-confirmed trend, lower in neutral market.
// ADDED: ATR Spike Filter (Logic 6) — block entries during abnormal volatility spikes.
//
// WHAT CHANGED vs v5:
// REMOVED: All MACD, RSI, ADX, and IMBA (Fair Value Gap / Fib-channel) code.
// ADDED: HTF Regime EMA filter (clean ADX replacement — price above rising 4h EMA).
// ADDED: Volume Exhaustion exit (new N-bar high on drying volume = distribution top).
// ADDED: HL Structure Break exit (confirmed close below last pivot low = early trend invalidation).
// ADDED: Break-Even Stop (move SL to entry after +0.75R — kills "gave-back-all-gains" losses).
// ADDED: Consecutive-Loss Pause / Choppy-Market Circuit Breaker (blocks re-entry during ranging).
//
// --- 3Commas Signal bot (TradingView Strategy) ---
// 1) 3Commas → Signal bot → Alert type "TradingView Strategy" → copy webhook + JSON "secret" + "bot_uuid".
// 2) Webhook URL (Signal bots only): https://api.3commas.io/signal_bots/webhooks
// 3) TradingView alert: prefer "Order fills only" (not "… and alert()") when Signal delivery = 3Commas.
// 4) Properties → "Recalculate after order is filled": ON vs OFF uses a different backtest timeline.
// Keep the same setting you used while tuning.
// 5) Alert "Message": paste JSON from 3commas_alert_message_template.json (replace secret + bot_uuid only).
// 6) Chart exchange + symbol must match your Signal bot pair (e.g. BINANCE:APTUSDT).
//@version=6
strategy("MA + R:R Strategy", shorttitle = "MA_RR", overlay = true,
max_labels_count = 500, max_lines_count = 300,
initial_capital = 1000, default_qty_type = strategy.percent_of_equity, default_qty_value = 100,
commission_type = strategy.commission.percent, commission_value = 0.1,
slippage = 0, pyramiding = 0,
calc_on_every_tick = false, calc_on_order_fills = false, process_orders_on_close = true)
// ── Backtest window: the exact range the local optimizer evaluated ──────
// start 2025-07-20 00:00 UTC -> end 2026-07-23 14:42 UTC (record ts)
// input.time() demands a const int; timestamp(...) is a simple int, so these are
// plain constants. Edit the numbers here to change the window (y, m, d, h, m).
bt_start = timestamp("UTC", 2025, 7, 20, 0, 0)
bt_end = timestamp("UTC", 2026, 7, 23, 14, 42)
inWindow = time >= bt_start and time <= bt_end
// ── Groups ────────────────────────────────────────────────────────────────────
string G_GEN = "1 General"
string G_RUNLIM = "1b Profit-run limit (winning cluster cap)"
string G_TR_MA = "3 Trend — Moving averages (MTF)"
string G_TR_ST = "4 Trend — SuperTrend"
string G_TR_LR = "5 Trend — Linear regression"
string G_TR_VW = "6a Trend — VWMA (optional)"
string G_TR_LOCAL = "6d Trend — Local EMA stack (micro)"
string G_TR_VOL = "6e Volume filter"
string G_TR_HH = "6f Higher-high structure"
string G_ENT_LONG = "7 Entries — Long"
string G_ENT_SHRT = "8 Entries — Short"
string G_EXIT = "9 Exits — signal-based (soft exits)"
string G_RR = "9b R:R — swing-low stop + ratio TP"
string G_SPIKE = "11 Peak / spike filters (anti-chase)"
string G_BTC = "12 BTC market filter (BTC-specific)"
string G_VIS = "10 Visualization"
string G_RSI = "14 RSI (embedded)"
string G_RF = "18 Indicator — Range Filter"
string G_QT = "19 Indicator — Q-Trend"
string G_AT = "20 Indicator — AlphaTrend"
string G_HAC = "21 Indicator — HACOLT (Heiken-Ashi)"
string G_UT = "22 Indicator — UT Bot Alerts"
// ── General ───────────────────────────────────────────────────────────────────
useBarConfirm = input.bool(true, "Signals on confirmed bar only", group = G_GEN,
tooltip = "OFF = entry conditions can go true every tick → same-bar stop/re-entry loops + many 3Commas webhooks. Keep ON for live.")
ordersOnConfirmedBar = input.bool(true, "New entries: bar close only (3Commas)", group = G_GEN,
tooltip = "When ON, only strategy.entry waits for barstate.isconfirmed. Exits are not delayed.")
cooldownBarsAfterExit = input.int(0, "Min bars after exit before new entry", minval = 0, maxval = 50, group = G_GEN)
// Choppy-market circuit breaker — pauses entries after back-to-back losses.
// Solves the NEAR May 20-21 pattern: 4 consecutive losses in 36h during a sideways range
// where all MA filters remained bullish but every support touch immediately reversed.
useChoppyFilter = input.bool(false, "Pause entries after N consecutive losses", group = G_GEN,
tooltip = "After maxConsecLoss losing trades in a row, block all new entries for choppyPauseBars bars. Detects distribution / choppy phases that look bullish on the macro but fail at the micro level.")
maxConsecLoss = input.int(2, "Max consecutive losses before pause", minval = 1, maxval = 10, group = G_GEN,
tooltip = "2 is the recommended default. Raise to 3 on high win-rate coins where two losses in a row still represent normal variance.")
choppyPauseBars = input.int(20, "Pause duration (bars) after max losses hit", minval = 1, maxval = 500, group = G_GEN,
tooltip = "On a 5m chart: 20 bars = 100 minutes. Set to 48 for a 4-hour pause. The market usually resolves its range or breaks out within this window.")
// ── Profit-run limit — stop after a profitable winning cluster ────────────────
// Counts consecutive WINNING trades and their combined % profit. Once both the
// trade-count and profit thresholds are hit, new entries pause for a cooldown so
// the strategy doesn't take the final "top" trade of an extended run. A losing
// trade (or the cooldown elapsing) resets the streak.
useRunLimit = input.bool(true, "Stop entries after a profitable winning run", group = G_RUNLIM,
tooltip = "After N consecutive winning trades that together made at least the profit % below, pause new entries for the cooldown. Targets the last 'top' trade of a winning cluster. A loss resets the streak.")
runLimTrades = input.int(5, "Winning trades in the run", minval = 1, maxval = 50, group = G_RUNLIM,
tooltip = "How many back-to-back winners before the cap can trigger. 3 = after 3 straight wins.")
runLimProfitPct = input.float(9.0, "Min combined profit % of the run", step = 0.5, minval = 0, group = G_RUNLIM,
tooltip = "The winners' summed Net PnL % must reach this before entries pause. Both this AND the trade-count must be met.")
runLimPauseBars = input.int(30, "Pause new entries for N bars after cap hit", minval = 1, maxval = 5000, group = G_RUNLIM,
tooltip = "Cooldown length once the cap fires. After it elapses the streak resets and entries resume.")
signal_delivery = input.string("3Commas Signal Bot", "Signal delivery",
options = ["3Commas Signal Bot", "Custom webhook bot", "Off"], group = G_GEN,
tooltip = "3Commas: add a TradingView alert manually with 3Commas JSON + webhook (see header). Custom: FastAPI bot JSON via Pine alert().")
string ALERT_SECRET = input.string("REPLACE_ME", "[Custom bot] secret → WEBHOOK_SECRET", group = G_GEN, display = display.none)
float alert_buy_usdt = input.float(800.0, "[Custom bot] BUY quote USDT", minval = 1, step = 1, group = G_GEN, display = display.none)
float alert_sell_base = input.float(0.001, "[Custom bot] SELL base qty", minval = 0.00000001, step = 0.001, group = G_GEN, display = display.none)
string alert_symbol = input.string("APTUSDT", "[Custom bot] symbol", group = G_GEN, display = display.none)
use_custom_webhook = signal_delivery == "Custom webhook bot"
// ── MA MTF trend ──────────────────────────────────────────────────────────────
useMaTrend = input.bool(true, "Require MA uptrend / downtrend", group = G_TR_MA)
maPrimary = input.bool(true, "MA trend is PRIMARY (entry trigger)", group = G_TR_MA,
tooltip = "ON = MA alignment (close above all enabled rising MAs) TRIGGERS entries (primary signal).\nOFF = MA trend only acts as a confirming FILTER while another primary indicator triggers the entry.")
maBlockWhenNa = input.bool(false, "MA: block entries while MTF MA is na", group = G_TR_MA)
plotMaLines = input.bool(false, "Plot MTF MA lines (TF1–TF4)", group = G_TR_MA)
requireMaSlope = input.bool(true, "Require enabled MAs to be rising (slope)", group = G_TR_MA)
// Each MA has its own type (SMA/EMA/WMA/RMA/VWMA/HMA), source (O/H/L/C) and slope lookback.
ma1_en = input.bool(true, "TF1", group = G_TR_MA, inline = "m1")
ma1_tf = input.timeframe("1", "", group = G_TR_MA, inline = "m1")
ma1_len = input.int(300, "", group = G_TR_MA, inline = "m1")
ma1_type = input.string("SMA", "", options = ["SMA", "EMA", "WMA", "RMA", "VWMA", "HMA"], group = G_TR_MA, inline = "m1")
ma1_src = input.string("Close", "", options = ["Open", "High", "Low", "Close"], group = G_TR_MA, inline = "m1")
ma1_slopeLb = input.int(15, "TF1 slope lookback (bars)", minval = 1, maxval = 200, group = G_TR_MA)
ma2_en = input.bool(true, "TF2", group = G_TR_MA, inline = "m2")
ma2_tf = input.timeframe("5", "", group = G_TR_MA, inline = "m2")
ma2_len = input.int(300, "", group = G_TR_MA, inline = "m2")
ma2_type = input.string("SMA", "", options = ["SMA", "EMA", "WMA", "RMA", "VWMA", "HMA"], group = G_TR_MA, inline = "m2")
ma2_src = input.string("Close", "", options = ["Open", "High", "Low", "Close"], group = G_TR_MA, inline = "m2")
ma2_slopeLb = input.int(20, "TF2 slope lookback (bars)", minval = 1, maxval = 200, group = G_TR_MA)
ma3_en = input.bool(true, "TF3", group = G_TR_MA, inline = "m3")
ma3_tf = input.timeframe("15", "", group = G_TR_MA, inline = "m3")
ma3_len = input.int(14, "", group = G_TR_MA, inline = "m3")
ma3_type = input.string("SMA", "", options = ["SMA", "EMA", "WMA", "RMA", "VWMA", "HMA"], group = G_TR_MA, inline = "m3")
ma3_src = input.string("Close", "", options = ["Open", "High", "Low", "Close"], group = G_TR_MA, inline = "m3")
ma3_slopeLb = input.int(13, "TF3 slope lookback (bars)", minval = 1, maxval = 200, group = G_TR_MA)
ma4_en = input.bool(true, "TF4", group = G_TR_MA, inline = "m4")
ma4_tf = input.timeframe("15", "", group = G_TR_MA, inline = "m4")
ma4_len = input.int(21, "", group = G_TR_MA, inline = "m4")
ma4_type = input.string("SMA", "", options = ["SMA", "EMA", "WMA", "RMA", "VWMA", "HMA"], group = G_TR_MA, inline = "m4")
ma4_src = input.string("Close", "", options = ["Open", "High", "Low", "Close"], group = G_TR_MA, inline = "m4")
ma4_slopeLb = input.int(6, "TF4 slope lookback (bars)", minval = 1, maxval = 200, group = G_TR_MA)
// ── SuperTrend ────────────────────────────────────────────────────────────────
useSuperTrend = input.bool(false, "Require SuperTrend alignment", group = G_TR_ST)
stTf = input.timeframe("60", "SuperTrend timeframe", group = G_TR_ST)
stAtrLen = input.int(10, "ATR period", minval = 1, group = G_TR_ST)
stMult = input.float(1.1, "Multiplier", step = 0.1, minval = 0.1, group = G_TR_ST)
stSrc = input.source(hl2, "Source (same TF as chart only)", group = G_TR_ST)
// ── LinReg ────────────────────────────────────────────────────────────────────
useLinReg = input.bool(false, "Use LinReg candle filter", group = G_TR_LR)
lrTf = input.timeframe("5", "LinReg timeframe", group = G_TR_LR)
lrLen = input.int(12, "LinReg length", minval = 2, group = G_TR_LR)
sigLen = input.int(11, "Signal line length (chart TF)", minval = 1, group = G_TR_LR)
sigUseSma = input.bool(false, "Signal = SMA (off = EMA)", group = G_TR_LR)
plotLinReg = input.bool(false, "Plot LinReg close line", group = G_TR_LR)
// ── VWMA 1 ────────────────────────────────────────────────────────────────────
useVwmaTrend = input.bool(false, "Require close above VWMA (HTF)", group = G_TR_VW)
vwmaTf = input.timeframe("240", "VWMA timeframe", group = G_TR_VW)
vwmaLen = input.int(200, "VWMA length", minval = 1, group = G_TR_VW)
plotVwmaLine = input.bool(false, "Plot VWMA line", group = G_TR_VW)
// ── Local EMA stack (micro) ───────────────────────────────────────────────────
useLocalTrend = input.bool(false, "Require local EMA trend alignment", group = G_TR_LOCAL,
tooltip = "Fast EMA > slow EMA on the chosen TF confirms local directional bias. Use 5m to block entries when the 5m structure is down inside an HTF uptrend.")
localTrendTf = input.timeframe("5", "Local EMA timeframe", group = G_TR_LOCAL)
localEmaFast = input.int(9, "Fast EMA period", minval = 2, maxval = 200, group = G_TR_LOCAL)
localEmaSlow = input.int(21, "Slow EMA period", minval = 2, maxval = 500, group = G_TR_LOCAL)
requireAboveFast = input.bool(true, "Also require close above fast EMA (long)", group = G_TR_LOCAL,
tooltip = "For longs: close must also be above the fast EMA. Adds momentum confirmation.")
plotLocalEma = input.bool(false, "Plot local EMA lines", group = G_TR_LOCAL)
// ── Volume filter ─────────────────────────────────────────────────────────────
useVolumeFilter = input.bool(true, "Require above-average volume on entry", group = G_TR_VOL)
volTf = input.timeframe("15", "Volume timeframe (blank = chart)", group = G_TR_VOL,
tooltip = "Timeframe used to evaluate volume vs its SMA for the entry filter. Blank = chart timeframe.")
volMaLen = input.int(50, "Volume SMA length", minval = 5, group = G_TR_VOL)
volMultMin = input.float(1.5, "Min volume (× SMA)", step = 0.1, minval = 0.1, group = G_TR_VOL,
tooltip = "1.0 = at-least-average volume; 1.5 = 50% above average.")
plotVolSma = input.bool(false, "Plot volume SMA line", group = G_TR_VOL)
// ── Higher-high structure filter ──────────────────────────────────────────────
useHhStructure = input.bool(true, "Require higher-high structure (HTF longs)", group = G_TR_HH,
tooltip = "Last confirmed pivot high on the chosen TF must be above the prior pivot high. Validates real HTF uptrend structure.")
hhTf = input.timeframe("5", "Higher-high timeframe", group = G_TR_HH)
hhPivotLen = input.int(10, "Pivot length (HH TF)", minval = 3, maxval = 30, group = G_TR_HH)
plotHhStruct = input.bool(false, "Plot higher-high structure (last/prior pivot highs)", group = G_TR_HH,
tooltip = "Plots the most recent and prior confirmed pivot highs on the HH timeframe so you can see the structure the filter evaluates. Works even when the filter itself is off.")
// ── Long entries ──────────────────────────────────────────────────────────────
long_en = input.bool(true, "Enable longs", group = G_ENT_LONG)
lrLongRule = input.string("Green candle", "LinReg long rule", options = ["Green candle", "Close > signal", "Both"], group = G_ENT_LONG)
// Logic 2 — Entry Bar Body Filter
// Rejects bearish entry candles whose body exceeds a fraction of ATR.
// A bearish close on the entry bar = momentum already shifted against you; skip it.
useBodyFilter = input.bool(true, "Body filter: reject bearish entry bar (Logic 2)", group = G_ENT_LONG,
tooltip = "Skips entries where the trigger bar is bearish AND its body (open−close) exceeds entryBodyAtrMult × ATR. A big bearish close on the setup bar is a momentum warning — skip it and wait for the next bar.")
entryBodyAtrMult = input.float(0.4, "Body filter: max bearish body size (× ATR)", step = 0.05, minval = 0.05, group = G_ENT_LONG,
tooltip = "0.3 = reject only if the bearish body is larger than 30% of ATR. Increase to be more lenient (0.5 allows a medium bearish bar). Decrease to be stricter.")
// ── Short entries ─────────────────────────────────────────────────────────────
short_en = input.bool(false, "Enable shorts at resistance", group = G_ENT_SHRT)
lrShortRule = input.string("Red candle", "LinReg short rule", options = ["Red candle", "Close < signal", "Both"], group = G_ENT_SHRT)
// ── Exits (signal-based) — ATR primitive ──────────────────────────────────────
atrLenExit = input.int(14, "ATR length (risk)", minval = 1, group = G_EXIT)
// ── R:R — swing-low stop + ratio TP ───────────────────────────────────────────
// Dedicated risk:reward mode for longs: SL placed below the previous swing low,
// TP at an adjustable reward:risk multiple. When ON, it overrides the SL/TP for
// long entries and uses the single-target bracket (partial-TP engine is bypassed).
useRR = input.bool(true, "Use R:R mode (swing-low SL + ratio TP) — longs", group = G_RR,
tooltip = "ON = long SL is set just below the previous swing low and TP is taken at the reward:risk ratio below. Overrides the group-9 SL/TP and the partial-TP engine for longs. Shorts are unaffected.")
rrSwingLb = input.int(20, "Swing low lookback (bars)", minval = 2, maxval = 200, group = G_RR,
tooltip = "Number of bars used to find the previous swing low. The stop is placed below this low.")
rrBufAtr = input.float(1.0, "SL buffer below swing low (× ATR)", step = 0.05, minval = 0.0, group = G_RR,
tooltip = "Extra distance below the swing low, in ATR units, so the stop sits clear of the exact wick. 0 = exactly at the swing low.")
rrRatio = input.float(2.0, "Reward : Risk ratio (TP)", step = 0.25, minval = 0.25, group = G_RR,
tooltip = "Final take-profit distance as a multiple of risk. 2.0 = 1:2. This is the runner's target when partial TPs are on, or the single target when they are off. Adjustable.")
minSlDistAtr = input.float(0.25, "Floor: min SL distance from entry (× ATR)", step = 0.05, minval = 0.05, group = G_RR,
tooltip = "The stop is never placed closer than this many ATRs below entry, even if the swing low is very near price. Sets the minimum risk used for the reward:risk target.")
plotRR = input.bool(false, "Plot R:R levels (live SL + TP)", group = G_RR,
tooltip = "Plots the active long's stop (incl. trailing) and the reward:risk target. Also shows the PROJECTED SL/TP while flat so you can see where they would sit on the next entry.")
// R:R — multiple partial take-profits (percentage-based, toggleable)
rrUsePartialTp = input.bool(false, "R:R: take partial TPs (%) — TP1 / TP2 + runner", group = G_RR,
tooltip = "ON = scale out at two percentage levels (TP1, TP2) and let the remainder run to the reward:risk target. OFF = single exit at the reward:risk target.")
rrTp1Pct = input.float(2.0, "TP1 — price move %", step = 0.1, minval = 0.1, group = G_RR, inline = "rtp1")
rrTp1Size = input.float(40.0, "close % of pos", step = 5, minval = 1, maxval = 100, group = G_RR, inline = "rtp1")
rrTp2Pct = input.float(4.0, "TP2 — price move %", step = 0.1, minval = 0.1, group = G_RR, inline = "rtp2")
rrTp2Size = input.float(30.0, "close % of pos", step = 5, minval = 1, maxval = 100, group = G_RR, inline = "rtp2")
// R:R — trailing stop loss (percentage-based, toggleable)
rrUseTrailSl = input.bool(false, "R:R: trailing stop (%)", group = G_RR,
tooltip = "ON = a percentage trailing stop ratchets up under price (never down). Combines with the swing-low stop and break-even — the highest of them is used.")
rrTrailPct = input.float(3.0, "Trailing stop distance (%)", step = 0.1, minval = 0.1, group = G_RR,
tooltip = "Stop trails this % below the close. 3 = stop sits 3% under price and only moves up.")
rrTrailActPct = input.float(0.0, "Trailing: activate after +% in profit (0 = once green)", step = 0.1, minval = 0.0, group = G_RR,
tooltip = "Trailing stop only arms after price is this % above entry. 0 = arm as soon as the trade is in profit.")
// ── Peak / spike filters (anti-chase) — block longs that chase extended moves ──
// 1) Over-extension vs MA: reject entries too far above a reference EMA.
useExtFilter = input.bool(false, "Block entries too far above MA (over-extension)", group = G_SPIKE,
tooltip = "Reject a long when close is more than the distance below ABOVE the reference EMA. Targets entries that chase a stretched micro-top; pullback/early-trend entries near the MA still pass.")
extRefLen = input.int(10, "Reference EMA length", minval = 1, group = G_SPIKE)
extMode = input.string("ATR", "Extension distance mode", options = ["ATR", "Percent"], group = G_SPIKE)
extMaxAtr = input.float(2.0, "Max distance above EMA (× ATR)", step = 0.1, minval = 0.1, group = G_SPIKE)
extMaxPct = input.float(5.0, "Max distance above EMA (%)", step = 0.1, minval = 0.1, group = G_SPIKE)
// 2) Parabolic run guard: reject entries after an over-fast recent run.
usePeakRun = input.bool(true, "Block entries after parabolic run", group = G_SPIKE,
tooltip = "Reject a long when price has already risen more than the limit over the last N bars (blow-off / pyramiding into a top).")
runLb = input.int(12, "Run lookback (bars)", minval = 2, maxval = 500, group = G_SPIKE)
runMode = input.string("Percent", "Run measured in", options = ["Percent", "ATR"], group = G_SPIKE)
runMaxPct = input.float(25.0, "Max rise over lookback (%)", step = 0.5, minval = 0.1, group = G_SPIKE)
runMaxAtr = input.float(6.0, "Max rise over lookback (× ATR)", step = 0.5, minval = 0.1, group = G_SPIKE)
// 3) ATR spike filter: reject entries on climax volatility bars.
useAtrSpike = input.bool(false, "Block entries on ATR volatility spikes", group = G_SPIKE,
tooltip = "Reject a long when current ATR is far above its recent average (climax / panic bars).")
atrSpikeAvgLen = input.int(20, "ATR spike: average lookback (bars)", minval = 2, group = G_SPIKE)
atrSpikeMaxMult = input.float(1.8, "ATR spike: max ATR (× average)", step = 0.1, minval = 1.0, group = G_SPIKE)
// 4) Upper-wick rejection: skip entries on bars with a big upper shadow.
useWickFilter = input.bool(true, "Block entries with large upper wick (rejection)", group = G_SPIKE,
tooltip = "Reject a long when the trigger bar's upper wick (high − max(open,close)) exceeds the limit — a rejection at the highs.")
wickMaxAtr = input.float(2.0, "Max upper wick (× ATR)", step = 0.05, minval = 0.05, group = G_SPIKE)
plotExtBand = input.bool(false, "Plot over-extension ceiling (ref EMA + limit)", group = G_SPIKE,
tooltip = "Plots the reference EMA and the over-extension ceiling. Longs are blocked while close is above the ceiling.")
// ── BTC market filter (BTC-specific) — only allow entries when BTC is trending up ──
// All three checks below are computed on the BTC symbol, independent of the chart coin.
btcEnable = input.bool(false, "Require BTC uptrend to allow entries", group = G_BTC,
tooltip = "Master switch. When ON, long entries are only allowed when the enabled BTC checks below confirm BTC is in an uptrend — computed on BTC data even though the strategy runs on this coin.")
btcSym = input.symbol("BINANCE:BTCUSDT", "BTC symbol", group = G_BTC,
tooltip = "The symbol used for the BTC trend checks. Use a feed you have access to (BINANCE:BTCUSDT is universal).")
// BTC moving averages (like Section 3, but on BTC)
useBtcMa = input.bool(true, "BTC: require MA uptrend", group = G_BTC)
requireBtcMaSlope = input.bool(true, "BTC: require MAs rising (slope)", group = G_BTC)
btcMa1_en = input.bool(true, "BTC MA1", group = G_BTC, inline = "b1")
btcMa1_tf = input.timeframe("60", "", group = G_BTC, inline = "b1")
btcMa1_len = input.int(200, "", group = G_BTC, inline = "b1")
btcMa1_type = input.string("SMA", "", options = ["SMA", "EMA", "WMA", "RMA", "VWMA", "HMA"], group = G_BTC, inline = "b1")
btcMa1_src = input.string("Close", "", options = ["Open", "High", "Low", "Close"], group = G_BTC, inline = "b1")
btcMa1_slopeLb = input.int(5, "BTC MA1 slope lookback (bars)", minval = 1, maxval = 200, group = G_BTC)
btcMa2_en = input.bool(false, "BTC MA2", group = G_BTC, inline = "b2")
btcMa2_tf = input.timeframe("240", "", group = G_BTC, inline = "b2")
btcMa2_len = input.int(100, "", group = G_BTC, inline = "b2")
btcMa2_type = input.string("SMA", "", options = ["SMA", "EMA", "WMA", "RMA", "VWMA", "HMA"], group = G_BTC, inline = "b2")
btcMa2_src = input.string("Close", "", options = ["Open", "High", "Low", "Close"], group = G_BTC, inline = "b2")
btcMa2_slopeLb = input.int(5, "BTC MA2 slope lookback (bars)", minval = 1, maxval = 200, group = G_BTC)
btcMa3_en = input.bool(false, "BTC MA3", group = G_BTC, inline = "b3")
btcMa3_tf = input.timeframe("15", "", group = G_BTC, inline = "b3")
btcMa3_len = input.int(50, "", group = G_BTC, inline = "b3")
btcMa3_type = input.string("SMA", "", options = ["SMA", "EMA", "WMA", "RMA", "VWMA", "HMA"], group = G_BTC, inline = "b3")
btcMa3_src = input.string("Close", "", options = ["Open", "High", "Low", "Close"], group = G_BTC, inline = "b3")
btcMa3_slopeLb = input.int(5, "BTC MA3 slope lookback (bars)", minval = 1, maxval = 200, group = G_BTC)
// BTC SuperTrend (like Section 4, but on BTC)
useBtcSt = input.bool(false, "BTC: require SuperTrend bullish", group = G_BTC)
btcStTf = input.timeframe("60", "BTC SuperTrend timeframe", group = G_BTC)
btcStAtrLen = input.int(15, "BTC SuperTrend ATR period", minval = 1, group = G_BTC)
btcStMult = input.float(2.5, "BTC SuperTrend multiplier", step = 0.1, minval = 0.1, group = G_BTC)
// BTC Higher-high structure (like Section 6f, but on BTC)
useBtcHh = input.bool(false, "BTC: require higher-high structure", group = G_BTC)
btcHhTf = input.timeframe("60", "BTC HH timeframe", group = G_BTC)
btcHhPivLen = input.int(9, "BTC HH pivot length", minval = 3, maxval = 30, group = G_BTC)
// BTC AlphaTrend (like Section 20, but on BTC)
useBtcAt = input.bool(false, "BTC: require AlphaTrend bullish", group = G_BTC)
btcAtTf = input.timeframe("60", "BTC AlphaTrend timeframe", group = G_BTC)
btcAtCoeff = input.float(1.0, "BTC AlphaTrend multiplier", step = 0.1, group = G_BTC)
btcAtAp = input.int(14, "BTC AlphaTrend common period", minval = 1, group = G_BTC)
btcAtSrc = input.string("Close", "BTC AlphaTrend source", options = ["Open", "High", "Low", "Close"], group = G_BTC)
btcAtNovol = input.bool(false, "BTC AlphaTrend: change calc (no volume data)?", group = G_BTC)
// One-trade-per-indicator-signal gate.
useOneTradePerSignal = input.bool(false, "One trade per RF / Q-Trend / AlphaTrend / UT Bot buy signal", group = G_ENT_LONG,
tooltip = "When ON, after a long is taken no new long is allowed until a FRESH buy trigger fires on Range Filter, Q-Trend, AlphaTrend, or UT Bot (whichever of those are enabled). Each indicator buy signal allows exactly one trade.")
minRForSoftExit = input.float(0.0, "Soft exit gate: min unrealized R (0 = off)", step = 0.25, minval = 0, group = G_EXIT,
tooltip = "Signal-based (soft) exits are suppressed until unrealized profit reaches this R multiple.")
// Exit MA (MTF cross)
useExitLongMa = input.bool(false, "Exit long: MTF close crosses below MA", group = G_EXIT)
useExitShortMa = input.bool(false, "Exit short: MTF close crosses above MA", group = G_EXIT)
exitMaTf = input.timeframe("240", "Exit MA timeframe", group = G_EXIT)
exitMaLen = input.int(100, "Exit MA length", minval = 1, group = G_EXIT)
exitMaType = input.string("SMA", "Exit MA type", options = ["SMA", "EMA", "WMA", "RMA", "VWMA", "HMA"], group = G_EXIT)
exitMaSrc = input.string("Close", "Exit MA source", options = ["Open", "High", "Low", "Close"], group = G_EXIT)
plotExitMa = input.bool(false, "Plot exit MA (aligned to chart)", group = G_EXIT)
// Micro exits
useExitBelowSt = input.bool(false, "Exit long: close below SuperTrend line", group = G_EXIT)
useExitBelowMa1 = input.bool(false, "Exit long: close below TF1 MA", group = G_EXIT)
useExitBelowLinReg = input.bool(false, "Exit long: close below LinReg close", group = G_EXIT)
// HL Structure Break Exit ─────────────────────────────────────────────────────
// A confirmed close below the most recent pivot low on hlBreakTf is the first
// lower-low — structural invalidation of the local uptrend. Fires meaningfully
// earlier than waiting for the 4h MA cross or SuperTrend flip to catch up.
useHlBreakExit = input.bool(true, "Exit long: confirmed break of last pivot low (HL break)", group = G_EXIT,
tooltip = "Closes the long when price closes below the most recent pivot low on hlBreakTf. First lower-low = uptrend structure gone. Fires faster than trailing stop or MA cross.")
hlBreakTf = input.timeframe("1", "HL break pivot TF", group = G_EXIT,
tooltip = "15m pivot low recommended when running on 5m charts. The 15m pivot low represents a meaningful local higher-low; breaking it is an early invalidation signal.")
hlBreakPivLen = input.int(10, "HL break pivot length", minval = 3, maxval = 30, group = G_EXIT)
hlBreakMinR = input.float(0.25, "HL break: min unrealized R to activate (0 = always)", step = 0.25, minval = 0, group = G_EXIT,
tooltip = "Set > 0 to avoid exiting on the normal post-entry consolidation dip. 0 means always active. Recommended: 0 (let structure break always exit).")
// ── RSI (embedded) ────────────────────────────────────────────────────────────
// Wilder RSI ported from the provided RSI source (RMA-based up/down smoothing).
// Computed in the background and available to strategy conditions. Optional filter:
// require RSI within [rsiLongMin, rsiLongMax] on entry — keeps you out of exhausted
// (overbought) or falling-knife (deeply oversold) conditions when enabled.
useRsiFilter = input.bool(false, "RSI filter: gate long entries by RSI band", group = G_RSI,
tooltip = "When ON, a long entry requires RSI between the min and max below. Default band 45–70 favours momentum without buying into overbought blow-offs.")
rsiLengthInput = input.int(14, "RSI Length", minval = 1, group = G_RSI)
rsiTf = input.timeframe("", "RSI timeframe (blank = chart)", group = G_RSI,
tooltip = "Timeframe the RSI is computed on. Blank = chart timeframe.")
rsiSourceInput = input.source(close, "RSI Source", group = G_RSI)
rsiLongMin = input.float(45.0, "Long entry: min RSI", minval = 0, maxval = 100, step = 1, group = G_RSI)
rsiLongMax = input.float(70.0, "Long entry: max RSI", minval = 0, maxval = 100, step = 1, group = G_RSI)
useRsiExit = input.bool(false, "Exit long when RSI crosses below level", group = G_RSI,
tooltip = "Optional soft exit: closes the long on a confirmed RSI crossunder of rsiExitLevel. Momentum-rollover exit, independent of price structure.")
rsiExitLevel = input.float(50.0, "RSI exit level (crossunder)", minval = 0, maxval = 100, step = 1, group = G_RSI)
plotRsiState = input.bool(false, "Plot RSI state marker (background availability check)", group = G_RSI)
// ── Visualization ─────────────────────────────────────────────────────────────
showMarkers = input.bool(true, "Plot Buy/Sell markers", group = G_VIS)
showStBand = input.bool(false, "Plot SuperTrend band", group = G_VIS)
plotAtr = input.bool(false, "Plot ATR (risk ATR) value", group = G_VIS)
showExitPnL = input.bool(true, "Label P/L % when a trade closes", group = G_VIS)
showCumPnL = input.bool(true, "Show cumulative net P/L % (table, last bar)", group = G_VIS)
// ══════════════════════════════════════════════════════════════════════════════
// NEW INDICATORS (4) — each: Off / Primary (trigger) / Filter (confirm), plot toggle.
// All compute on the chart timeframe and default OFF. Ported inline (no imports).
// ══════════════════════════════════════════════════════════════════════════════
// ── Indicator 18 — Range Filter (DonovanWall / guikroth) ──────────────────────
rf_en = input.bool(false, "Enable Range Filter", group = G_RF)
rf_primary = input.bool(false, "Range Filter is PRIMARY (entry trigger)", group = G_RF,
tooltip = "ON = Range Filter Buy/Sell breakout TRIGGERS entries. OFF = used only as a confirming FILTER (trend direction must agree).")
rf_src = input.source(close, "Source", group = G_RF)
rf_per = input.int(100, "Sampling Period", minval = 1, group = G_RF)
rf_mult = input.float(3.0, "Range Multiplier", minval = 0.1, step = 0.1, group = G_RF)
rf_tf = input.timeframe("", "Signal timeframe (blank = chart)", group = G_RF, tooltip = "Compute Range Filter signals on this timeframe. Blank = chart TF. Set higher for HTF buy/sell signals.")
rf_useExit = input.bool(false, "Exit long on Range Filter SELL", group = G_RF, tooltip = "Close the open long when Range Filter prints a SELL on the exit timeframe below.")
rf_exitTf = input.timeframe("", "Exit timeframe (blank = signal TF)", group = G_RF)
plotRangeFilter = input.bool(false, "Plot Range Filter (line + bands)", group = G_RF)
// ── Indicator 19 — Q-Trend (tarasenko_) ───────────────────────────────────────
qt_en = input.bool(false, "Enable Q-Trend", group = G_QT)
qt_primary = input.bool(false, "Q-Trend is PRIMARY (entry trigger)", group = G_QT,
tooltip = "ON = Q-Trend Buy/Sell TRIGGERS entries. OFF = used only as a confirming FILTER (last signal must agree).")
qt_src = input.source(close, "Source", group = G_QT)
qt_p = input.int(200, "Trend period", minval = 1, group = G_QT)
qt_atrP = input.int(14, "ATR Period", minval = 1, group = G_QT)
qt_mult = input.float(1.0, "ATR Multiplier", minval = 0.1, step = 0.1, group = G_QT)
plotQTrend = input.bool(false, "Plot Q-Trend trend line", group = G_QT)
// ── Indicator 20 — AlphaTrend (KivancOzbilgic) ────────────────────────────────
at_en = input.bool(true, "Enable AlphaTrend", group = G_AT)
at_primary = input.bool(false, "AlphaTrend is PRIMARY (entry trigger)", group = G_AT,
tooltip = "ON = AlphaTrend Buy/Sell crossover TRIGGERS entries. OFF = used only as a confirming FILTER (AlphaTrend must be rising).")
at_coeff = input.float(1.0, "Multiplier", step = 0.1, group = G_AT)
at_ap = input.int(50, "Common Period", minval = 1, group = G_AT)
at_src = input.source(close, "Source", group = G_AT)
at_novol = input.bool(false, "Change calc (no volume data)?", group = G_AT)
at_tf = input.timeframe("", "Signal timeframe (blank = chart)", group = G_AT, tooltip = "Compute AlphaTrend signals on this timeframe. Blank = chart TF. Set higher for HTF buy/sell signals.")
at_useExit = input.bool(false, "Exit long on AlphaTrend SELL", group = G_AT, tooltip = "Close the open long when AlphaTrend prints a SELL on the exit timeframe below.")
at_exitTf = input.timeframe("", "Exit timeframe (blank = signal TF)", group = G_AT)
plotAlphaTrend = input.bool(false, "Plot AlphaTrend lines", group = G_AT)
// ── Indicator 21 — HACOLT (Vervoort Heiken-Ashi, LazyBear) ────────────────────
hac_en = input.bool(true, "Enable HACOLT", group = G_HAC)
hac_primary = input.bool(false, "HACOLT is PRIMARY (entry trigger)", group = G_HAC,
tooltip = "ON = HACOLT flip to long/short TRIGGERS entries. OFF = used only as a confirming FILTER (HACOLT state must agree).")
hac_length = input.int(55, "TEMA Period", minval = 1, group = G_HAC)
hac_emaLen = input.int(100, "EMA Period", minval = 1, group = G_HAC)
hac_csf = input.float(1.1, "Candle size factor", step = 0.1, group = G_HAC)
hac_tf = input.timeframe("", "Signal timeframe (blank = chart)", group = G_HAC, tooltip = "Compute HACOLT signals on this timeframe. Blank = chart TF. Set higher for HTF buy/sell signals.")
hac_useExit = input.bool(false, "Exit long on HACOLT SELL", group = G_HAC, tooltip = "Close the open long when HACOLT flips to short on the exit timeframe below.")
hac_exitTf = input.timeframe("", "Exit timeframe (blank = signal TF)", group = G_HAC)
plotHacolt = input.bool(false, "Plot HACOLT state (background tint)", group = G_HAC)
// ── Indicator 22 — UT Bot Alerts (QuantNomad / Yo_adriiiiaan) ─────────────────
ut_en = input.bool(false, "Enable UT Bot", group = G_UT)
ut_primary = input.bool(false, "UT Bot is PRIMARY (entry trigger)", group = G_UT,
tooltip = "ON = a UT Bot buy/sell flip TRIGGERS entries. OFF = used only as a confirming FILTER (UT Bot state must agree).")
ut_key = input.float(1.0, "Key value (sensitivity)", step = 0.1, group = G_UT,
tooltip = "Multiplier on ATR for the trailing stop. Higher = less sensitive (wider stop), fewer flips.")
ut_atrP = input.int(10, "ATR Period", minval = 1, group = G_UT)
ut_heikin = input.bool(false, "Signals from Heikin Ashi candles", group = G_UT)
ut_tf = input.timeframe("", "Signal timeframe (blank = chart)", group = G_UT, tooltip = "Compute UT Bot signals on this timeframe. Blank = chart TF. Set higher for HTF buy/sell signals.")
ut_useExit = input.bool(false, "Exit long on UT Bot SELL", group = G_UT, tooltip = "Close the open long when UT Bot prints a SELL on the exit timeframe below.")
ut_exitTf = input.timeframe("", "Exit timeframe (blank = signal TF)", group = G_UT)
plotUtBot = input.bool(false, "Plot UT Bot trailing stop", group = G_UT)
// ── Helper functions ──────────────────────────────────────────────────────────
f_ma(string t, series float src, int length) =>
float maS = ta.sma(src, length)
float maE = ta.ema(src, length)
float maW = ta.wma(src, length)
float maR = ta.rma(src, length)
float maV = ta.vwma(src, length)
// HMA = WMA( 2·WMA(src, len/2) − WMA(src, len), floor(sqrt(len)) ). Ported from the
// provided Hull Moving Average source; embedded inline (no external dependency).
float maH = ta.wma(2 * ta.wma(src, int(math.max(1, length / 2))) - ta.wma(src, length), int(math.max(1, math.floor(math.sqrt(length)))))
t == "SMA" ? maS : t == "EMA" ? maE : t == "WMA" ? maW : t == "VWMA" ? maV : t == "HMA" ? maH : maR
// Picks an OHLC source by name — evaluated inside request.security so it resolves
// to the target timeframe's open/high/low/close.
f_pick_src(string s) =>
s == "Open" ? open : s == "High" ? high : s == "Low" ? low : close
// Normalize a timeframe input: blank ("") = chart timeframe (request.security needs a valid TF).
f_tf(string t) => t == "" ? timeframe.period : t
// MTF MA — explicit VWMA expansion so `volume` is visible to Pine v6's request.security analyser.
// `typeSel` chooses the MA type and `srcSel` the OHLC source — both per MA.
f_ma_mtf(string tf, float lenF, bool en, string srcSel, string typeSel) =>
int _L = int(lenF)
float mSec = request.security(syminfo.tickerid, tf,
typeSel == "EMA" ? ta.ema(f_pick_src(srcSel), _L) :
typeSel == "WMA" ? ta.wma(f_pick_src(srcSel), _L) :
typeSel == "RMA" ? ta.rma(f_pick_src(srcSel), _L) :
typeSel == "VWMA" ? ta.sma(f_pick_src(srcSel) * volume, _L) / ta.sma(volume, _L) :
typeSel == "HMA" ? ta.wma(2 * ta.wma(f_pick_src(srcSel), int(math.max(1, _L / 2))) - ta.wma(f_pick_src(srcSel), _L), int(math.max(1, math.floor(math.sqrt(_L))))) :
ta.sma(f_pick_src(srcSel), _L),
barmerge.gaps_off, barmerge.lookahead_off)
en and lenF > 0 ? mSec : na
// MTF VWMA — explicit formula keeps `volume` visible to Pine v6 dependency analyser.
f_vwma_mtf(string tf, int lenV, bool en) =>
[cv_ma, v_ma] = request.security(syminfo.tickerid, tf,
[ta.sma(close * volume, lenV), ta.sma(volume, lenV)],
barmerge.gaps_off, barmerge.lookahead_off)
float result = (not na(v_ma) and v_ma > 0) ? cv_ma / v_ma : na
en and lenV > 0 ? result : na
f_exit_ma_long_cross() =>
float m = f_ma(exitMaType, f_pick_src(exitMaSrc), exitMaLen)
bool cu = ta.crossunder(close, m)
barstate.isconfirmed and cu
f_exit_ma_short_cross() =>
float m = f_ma(exitMaType, f_pick_src(exitMaSrc), exitMaLen)
bool co = ta.crossover(close, m)
barstate.isconfirmed and co
f_last_pivot_low(string tf, int len_) =>
request.security(syminfo.tickerid, tf,
ta.valuewhen(not na(ta.pivotlow(len_, len_)), ta.pivotlow(len_, len_), 0),
barmerge.gaps_off, barmerge.lookahead_off)
f_last_pivot_high(string tf, int len_) =>
request.security(syminfo.tickerid, tf,
ta.valuewhen(not na(ta.pivothigh(len_, len_)), ta.pivothigh(len_, len_), 0),
barmerge.gaps_off, barmerge.lookahead_off)
f_hhph_last(int len_) =>
ta.valuewhen(not na(ta.pivothigh(len_, len_)), ta.pivothigh(len_, len_), 0)
f_hhph_prior(int len_) =>
ta.valuewhen(not na(ta.pivothigh(len_, len_)), ta.pivothigh(len_, len_), 1)
// SuperTrend bands evaluated on the TF where this runs (for request.security).
f_super_trend_htf() =>
atr_ = ta.atr(stAtrLen)
src = hl2
up = src - stMult * atr_
up1 = nz(up[1], up)
up := close[1] > up1 ? math.max(up, up1) : up
dn = src + stMult * atr_
dn1 = nz(dn[1], dn)
dn := close[1] < dn1 ? math.min(dn, dn1) : dn
tr = 1
tr := nz(tr[1], 1)
tr := tr == -1 and close > dn1 ? 1 : tr == 1 and close < up1 ? -1 : tr
[up, dn, tr]
// ── BTC market-filter helpers (evaluated inside request.security on the BTC symbol) ──
// MA uptrend test on the requested symbol/TF: close above the MA and (optionally) rising.
f_btc_ma_ok(string srcSel, int len, string t, int slopeLb, bool reqSlope) =>
float m = f_ma(t, f_pick_src(srcSel), len)
not na(m) and close > m and (not reqSlope or na(m[slopeLb]) or m > m[slopeLb])
// SuperTrend bullish test (own ATR length + multiplier).
f_btc_st_bull(int atrLen, float mult) =>
float a = ta.atr(atrLen)
float up = hl2 - mult * a
float up1 = nz(up[1], up)
up := close[1] > up1 ? math.max(up, up1) : up
float dn = hl2 + mult * a
float dn1 = nz(dn[1], dn)
dn := close[1] < dn1 ? math.min(dn, dn1) : dn
var int tr = 1
tr := nz(tr[1], 1)
tr := tr == -1 and close > dn1 ? 1 : tr == 1 and close < up1 ? -1 : tr
tr == 1
// Higher-high structure test: last confirmed pivot high above the prior one.
f_btc_hh_ok(int len) =>
float lastPH = ta.valuewhen(not na(ta.pivothigh(len, len)), ta.pivothigh(len, len), 0)
float priorPH = ta.valuewhen(not na(ta.pivothigh(len, len)), ta.pivothigh(len, len), 1)
not na(lastPH) and not na(priorPH) and lastPH > priorPH
// AlphaTrend bullish test (line rising vs its 2-bar-lagged value) — like Section 20.
f_btc_at_bull(float coeff, int ap, string srcSel, bool novol) =>
float a = ta.sma(ta.tr, ap)
float up = low - a * coeff
float dn = high + a * coeff
float mom = novol ? ta.rsi(f_pick_src(srcSel), ap) : ta.mfi(hlc3, ap)
var float at = 0.0
at := (mom >= 50) ? (up < nz(at[1]) ? nz(at[1]) : up) : (dn > nz(at[1]) ? nz(at[1]) : dn)
at > at[2]
f_linreg_ohlc(int lenLR) =>
[ta.linreg(open, lenLR, 0), ta.linreg(high, lenLR, 0), ta.linreg(low, lenLR, 0), ta.linreg(close, lenLR, 0)]
// ── Core series ───────────────────────────────────────────────────────────────
atr = ta.atr(atrLenExit)
tickSize = syminfo.mintick
// ── MA trend ──────────────────────────────────────────────────────────────────
float ma1 = f_ma_mtf(ma1_tf, ma1_len, ma1_en, ma1_src, ma1_type)
float ma2 = f_ma_mtf(ma2_tf, ma2_len, ma2_en, ma2_src, ma2_type)
float ma3 = f_ma_mtf(ma3_tf, ma3_len, ma3_en, ma3_src, ma3_type)
float ma4 = f_ma_mtf(ma4_tf, ma4_len, ma4_en, ma4_src, ma4_type)
float vwTrend = f_vwma_mtf(vwmaTf, vwmaLen, useVwmaTrend)
maPassLong(bool en, float m) =>
not en ? true :
maBlockWhenNa and na(m) ? false :
not maBlockWhenNa and na(m) ? true :
close > m
maPassShort(bool en, float m) =>
not en ? true :
maBlockWhenNa and na(m) ? false :
not maBlockWhenNa and na(m) ? true :
close < m
bool maUp = maPassLong(ma1_en, ma1) and maPassLong(ma2_en, ma2) and maPassLong(ma3_en, ma3) and maPassLong(ma4_en, ma4)
bool maDn = maPassShort(ma1_en, ma1) and maPassShort(ma2_en, ma2) and maPassShort(ma3_en, ma3) and maPassShort(ma4_en, ma4)
bool vwOkLong = maPassLong(useVwmaTrend, vwTrend)
bool vwOkShort = maPassShort(useVwmaTrend, vwTrend)
float exitMaAligned = (useExitLongMa or useExitShortMa or plotExitMa) ? request.security(syminfo.tickerid, exitMaTf, f_ma(exitMaType, f_pick_src(exitMaSrc), exitMaLen), barmerge.gaps_off, barmerge.lookahead_off) : na
// Always compute cross signals unconditionally so their internal historical series never skip a bar.
bool _exitMaLongRaw = request.security(syminfo.tickerid, exitMaTf, f_exit_ma_long_cross(), barmerge.gaps_off, barmerge.lookahead_off)
bool _exitMaShortRaw = request.security(syminfo.tickerid, exitMaTf, f_exit_ma_short_cross(), barmerge.gaps_off, barmerge.lookahead_off)
bool exitLongMaTrigger = useExitLongMa and _exitMaLongRaw
bool exitShortMaTrigger = useExitShortMa and _exitMaShortRaw
// ── SuperTrend ────────────────────────────────────────────────────────────────
bool stIsChartTf = timeframe.in_seconds(timeframe.period) == timeframe.in_seconds(stTf)
float stAtr_c = ta.atr(stAtrLen)
float stUp_c = stSrc - stMult * stAtr_c
float stUp1_c = nz(stUp_c[1], stUp_c)
stUp_c := close[1] > stUp1_c ? math.max(stUp_c, stUp1_c) : stUp_c
float stDn_c = stSrc + stMult * stAtr_c
float stDn1_c = nz(stDn_c[1], stDn_c)
stDn_c := close[1] < stDn1_c ? math.min(stDn_c, stDn1_c) : stDn_c
var int stTrend_c = 1
stTrend_c := nz(stTrend_c[1], 1)
stTrend_c := stTrend_c == -1 and close > stDn1_c ? 1 : stTrend_c == 1 and close < stUp1_c ? -1 : stTrend_c
[stUp_h, stDn_h, stTrend_h] = request.security(syminfo.tickerid, stTf, f_super_trend_htf(), barmerge.gaps_off, barmerge.lookahead_off)
float stUp = stIsChartTf ? stUp_c : stUp_h
float stDn = stIsChartTf ? stDn_c : stDn_h
int stTrendVal = stIsChartTf ? stTrend_c : int(stTrend_h)
bool stBull = stTrendVal == 1
bool stBear = stTrendVal == -1
// ── LinReg ────────────────────────────────────────────────────────────────────
[lrO, lrH, lrL, lrC] = request.security(syminfo.tickerid, lrTf, f_linreg_ohlc(lrLen), barmerge.gaps_off, barmerge.lookahead_off)
float bopen = useLinReg ? lrO : open
float bhigh = useLinReg ? lrH : high
float blow = useLinReg ? lrL : low
float bclose = useLinReg ? lrC : close
float sigSma = ta.sma(bclose, sigLen)
float sigEma = ta.ema(bclose, sigLen)
float sig = sigUseSma ? sigSma : sigEma
bool lrGreen = bopen < bclose
bool lrRed = bopen > bclose
bool lrLongOk =
not useLinReg ? true :
lrLongRule == "Green candle" ? lrGreen :
lrLongRule == "Close > signal" ? bclose > sig :
lrGreen and bclose > sig
bool lrShortOk =
not useLinReg ? true :
lrShortRule == "Red candle" ? lrRed :
lrShortRule == "Close < signal" ? bclose < sig :
lrRed and bclose < sig
// ── MA slope ──────────────────────────────────────────────────────────────────
bool ma1SlopeOk = not requireMaSlope or not ma1_en or na(ma1) or na(ma1[ma1_slopeLb]) or ma1 > ma1[ma1_slopeLb]
bool ma2SlopeOk = not requireMaSlope or not ma2_en or na(ma2) or na(ma2[ma2_slopeLb]) or ma2 > ma2[ma2_slopeLb]
bool ma3SlopeOk = not requireMaSlope or not ma3_en or na(ma3) or na(ma3[ma3_slopeLb]) or ma3 > ma3[ma3_slopeLb]
bool ma1SlopeDnOk = not requireMaSlope or not ma1_en or na(ma1) or na(ma1[ma1_slopeLb]) or ma1 < ma1[ma1_slopeLb]
bool ma2SlopeDnOk = not requireMaSlope or not ma2_en or na(ma2) or na(ma2[ma2_slopeLb]) or ma2 < ma2[ma2_slopeLb]
bool ma3SlopeDnOk = not requireMaSlope or not ma3_en or na(ma3) or na(ma3[ma3_slopeLb]) or ma3 < ma3[ma3_slopeLb]
bool ma4SlopeOk = not requireMaSlope or not ma4_en or na(ma4) or na(ma4[ma4_slopeLb]) or ma4 > ma4[ma4_slopeLb]
bool ma4SlopeDnOk = not requireMaSlope or not ma4_en or na(ma4) or na(ma4[ma4_slopeLb]) or ma4 < ma4[ma4_slopeLb]
bool maSlopeOkLong = ma1SlopeOk and ma2SlopeOk and ma3SlopeOk and ma4SlopeOk
bool maSlopeOkShort = ma1SlopeDnOk and ma2SlopeDnOk and ma3SlopeDnOk and ma4SlopeDnOk
// ── Local EMA stack ───────────────────────────────────────────────────────────
float localFastEma = useLocalTrend ? request.security(syminfo.tickerid, localTrendTf, ta.ema(close, localEmaFast), barmerge.gaps_off, barmerge.lookahead_off) : na
float localSlowEma = useLocalTrend ? request.security(syminfo.tickerid, localTrendTf, ta.ema(close, localEmaSlow), barmerge.gaps_off, barmerge.lookahead_off) : na
bool localTrendOkLong = not useLocalTrend or (not na(localFastEma) and not na(localSlowEma) and localFastEma > localSlowEma and (not requireAboveFast or close > localFastEma))
bool localTrendOkShort = not useLocalTrend or (not na(localFastEma) and not na(localSlowEma) and localFastEma < localSlowEma and (not requireAboveFast or close < localFastEma))
// ── Volume filter (timeframe-selectable) ──────────────────────────────────────
// Chart-TF volume SMA — used by the Volume Exhaustion exit and the plot.
float volMaSeries = ta.sma(volume, volMaLen)
// Entry-filter volume + SMA evaluated on the chosen volTf (blank = chart TF).
[volEntryCur, volEntrySma] = request.security(syminfo.tickerid, volTf == "" ? timeframe.period : volTf,
[volume, ta.sma(volume, volMaLen)], barmerge.gaps_off, barmerge.lookahead_off)
bool volOkLong = not useVolumeFilter or (not na(volEntrySma) and volEntrySma > 0 and volEntryCur >= volMultMin * volEntrySma)
bool volOkShort = volOkLong
// ── Higher-high structure ─────────────────────────────────────────────────────
float hhLastPH = (useHhStructure or plotHhStruct) ? request.security(syminfo.tickerid, hhTf, f_hhph_last(hhPivotLen), barmerge.gaps_off, barmerge.lookahead_off) : na
float hhPriorPH = (useHhStructure or plotHhStruct) ? request.security(syminfo.tickerid, hhTf, f_hhph_prior(hhPivotLen), barmerge.gaps_off, barmerge.lookahead_off) : na
bool hhOkLong = not useHhStructure or (not na(hhLastPH) and not na(hhPriorPH) and hhLastPH > hhPriorPH)
// ══════════════════════════════════════════════════════════════════════════════
// EMBEDDED INDICATORS — RSI (MTF) + 4 NEW INDICATORS (Range Filter, Q-Trend,
// AlphaTrend, HACOLT). All ported inline from the provided sources; no imports.
// ══════════════════════════════════════════════════════════════════════════════
// ── RSI (Wilder) — timeframe-selectable ───────────────────────────────────────
float rsiVal = request.security(syminfo.tickerid, rsiTf == "" ? timeframe.period : rsiTf,
ta.rsi(rsiSourceInput, rsiLengthInput), barmerge.gaps_off, barmerge.lookahead_off)
bool rsiOkLong = not useRsiFilter or (not na(rsiVal) and rsiVal >= rsiLongMin and rsiVal <= rsiLongMax)
// Compute the crossunder unconditionally (every bar) so its internal historical state
// is always maintained, then gate it — avoids CW10002 inconsistent-calculation warning.
bool _rsiXunder = ta.crossunder(rsiVal, rsiExitLevel)
bool rsiExitNow = useRsiExit and barstate.isconfirmed and _rsiXunder
// ── NEW Indicator 18 — Range Filter (MTF) ─────────────────────────────────────
f_smoothrng(float x, int t, float m) =>
float avrng = ta.ema(math.abs(x - x[1]), t)
ta.ema(avrng, t * 2 - 1) * m
f_rngfilt(float x, float r) =>
var float rf = na
rf := na(rf[1]) ? x : x > rf[1] ? (x - r < rf[1] ? rf[1] : x - r) : (x + r > rf[1] ? rf[1] : x + r)
rf
// Returns [buy, sell, bullish, bearish, filter, hband, lband, dir] in the target TF.
f_range_filter() =>
float sm = f_smoothrng(rf_src, rf_per, rf_mult)
float filt = f_rngfilt(rf_src, sm)
var float up = 0.0
up := filt > filt[1] ? nz(up[1]) + 1 : filt < filt[1] ? 0.0 : nz(up[1])
var float dn = 0.0
dn := filt < filt[1] ? nz(dn[1]) + 1 : filt > filt[1] ? 0.0 : nz(dn[1])
bool lraw = (rf_src > filt and rf_src > rf_src[1] and up > 0) or (rf_src > filt and rf_src < rf_src[1] and up > 0)
bool sraw = (rf_src < filt and rf_src < rf_src[1] and dn > 0) or (rf_src < filt and rf_src > rf_src[1] and dn > 0)
var int ci = 0
ci := lraw ? 1 : sraw ? -1 : nz(ci[1])
[lraw and ci[1] == -1, sraw and ci[1] == 1, up > 0, dn > 0, filt, filt + sm, filt - sm, up > 0 ? 1 : dn > 0 ? -1 : 0]
[rfEvtLong, rfEvtShort, rfStateLong, rfStateShort, rfFilt, rfHband, rfLband, rfDir] = request.security(syminfo.tickerid, f_tf(rf_tf), f_range_filter(), barmerge.gaps_off, barmerge.lookahead_off)
// Exit-side evaluation (own timeframe) — only the SELL event is used.
[rfXbuy, rfExitSell, rfXbl, rfXbr, rfXf, rfXh, rfXl, rfXd] = request.security(syminfo.tickerid, f_tf(rf_exitTf == "" ? rf_tf : rf_exitTf), f_range_filter(), barmerge.gaps_off, barmerge.lookahead_off)
// ── NEW Indicator 19 — Q-Trend ────────────────────────────────────────────────
float qtH = ta.highest(qt_src, qt_p)
float qtL = ta.lowest(qt_src, qt_p)
float qtAtr = ta.atr(qt_atrP)[1]
float qtEps = qt_mult * qtAtr
float qtMbase = (qtH + qtL) / 2
var float qtM = na
qtM := bar_index > qt_p ? nz(qtM[1], qtMbase) : qtMbase
bool qtChangeUp = ta.crossover(qt_src, qtM + qtEps) or qt_src > qtM + qtEps
bool qtChangeDn = ta.crossunder(qt_src, qtM - qtEps) or qt_src < qtM - qtEps
qtM := (qtChangeUp or qtChangeDn) and qtM != qtM[1] ? qtM : qtChangeUp ? qtM + qtEps : qtChangeDn ? qtM - qtEps : nz(qtM[1], qtM)
var string qtLs = na
qtLs := qtChangeUp ? "B" : qtChangeDn ? "S" : qtLs[1]
bool qtEvtLong = qtChangeUp and qtLs[1] != "B"
bool qtStateLong = qtLs == "B"
bool qtEvtShort = qtChangeDn and qtLs[1] != "S"
bool qtStateShort = qtLs == "S"
// ── NEW Indicator 20 — AlphaTrend (MTF) ───────────────────────────────────────
// Returns [buy, sell, bullish, bearish, line, lag] in the target TF.
f_alphatrend() =>
float aAtr = ta.sma(ta.tr, at_ap)
float aUp = low - aAtr * at_coeff
float aDn = high + aAtr * at_coeff
float aMom = at_novol ? ta.rsi(at_src, at_ap) : ta.mfi(hlc3, at_ap)
var float at = 0.0
at := (aMom >= 50) ? (aUp < nz(at[1]) ? nz(at[1]) : aUp) : (aDn > nz(at[1]) ? nz(at[1]) : aDn)
bool buyRaw = ta.crossover(at, at[2])
bool sellRaw = ta.crossunder(at, at[2])
int k1 = ta.barssince(buyRaw)
int k2 = ta.barssince(sellRaw)
int o1 = ta.barssince(buyRaw[1])
int o2 = ta.barssince(sellRaw[1])
[buyRaw and o1 > k2, sellRaw and o2 > k1, at > at[2], at < at[2], at, at[2]]
[atEvtLong, atEvtShort, atStateLong, atStateShort, AlphaTrend, AlphaTrendLag] = request.security(syminfo.tickerid, f_tf(at_tf), f_alphatrend(), barmerge.gaps_off, barmerge.lookahead_off)
[atXbuy, atExitSell, atXbl, atXbr, atXline, atXlag] = request.security(syminfo.tickerid, f_tf(at_exitTf == "" ? at_tf : at_exitTf), f_alphatrend(), barmerge.gaps_off, barmerge.lookahead_off)
// ── NEW Indicator 21 — HACOLT (Vervoort Heiken-Ashi Candlestick Oscillator) ───
f_tema(float src, int len) =>
float e1 = ta.ema(src, len)
float e2 = ta.ema(e1, len)
float e3 = ta.ema(e2, len)
3 * (e1 - e2) + e3
// Returns [buy, sell, bullish, bearish, hacoltValue] in the target TF.
f_hacolt() =>
var float haOpen = na
haOpen := na(haOpen[1]) ? ohlc4 : (haOpen[1] + ohlc4) / 2
float haClose = (haOpen + math.max(high, haOpen) + math.min(low, haOpen) + ohlc4) / 4
float thaClose = f_tema(haClose, hac_length)
float thl2 = f_tema(hl2, hac_length)
float closeSm = 2 * thaClose - f_tema(thaClose, hac_length)
float hl2Sm = 2 * thl2 - f_tema(thl2, hac_length)
bool shortCandle = math.abs(close - open) < ((high - low) * hac_csf)
bool keepn1 = ((haClose >= haOpen) and (haClose[1] >= haOpen[1])) or (close >= haClose) or (high > high[1]) or (low > low[1]) or (hl2Sm >= closeSm)
bool keepAll1 = keepn1 or (keepn1[1] and (close >= open) or (close >= close[1]))
bool keep13 = shortCandle and (high >= low[1])
bool utr = keepAll1 or (keepAll1[1] and keep13)
bool keepn2 = (haClose < haOpen) and (haClose[1] < haOpen[1]) or (hl2Sm < closeSm)
bool keep23 = shortCandle and (low <= high[1])
bool keepAll2 = keepn2 or (keepn2[1] and (close < open) or (close < close[1]))
bool dtr = keepAll2 or (keepAll2[1] and keep23)
bool upw = (not dtr) and dtr[1] and utr
bool dnw = (not utr) and utr[1] and dtr
var float upwOffF = 0.0
upwOffF := upw != dnw ? (upw ? 1.0 : 0.0) : nz(upwOffF[1], 0.0)
bool buySig = upw or ((not dnw) and upwOffF > 0)
bool ltSell = close < ta.ema(close, hac_emaLen)
var float neutralF = 0.0
neutralF := buySig ? 1.0 : (ltSell ? 0.0 : nz(neutralF[1], 0.0))
int hv = buySig ? 1 : neutralF > 0 ? 0 : -1
[hv == 1 and hv[1] != 1, hv == -1 and hv[1] != -1, hv > 0, hv < 0, hv]
[hacEvtLong, hacEvtShort, hacStateLong, hacStateShort, hacolt] = request.security(syminfo.tickerid, f_tf(hac_tf), f_hacolt(), barmerge.gaps_off, barmerge.lookahead_off)
[hacXbuy, hacExitSell, hacXbl, hacXbr, hacXv] = request.security(syminfo.tickerid, f_tf(hac_exitTf == "" ? hac_tf : hac_exitTf), f_hacolt(), barmerge.gaps_off, barmerge.lookahead_off)
// ── NEW Indicator 22 — UT Bot Alerts (MTF) ────────────────────────────────────
// ATR-based trailing stop. A close-cross above the stop = long flip (buy); a cross
// below = short flip (sell). Heikin-Ashi source computed inline (so it works MTF
// without a nested request.security). Returns [buy, sell, bullish, bearish, stop, pos].
f_ut_bot() =>
// Inline Heikin-Ashi close (no nested security) when ut_heikin is on.
var float haO = na
haO := na(haO[1]) ? (open + close) / 2 : (haO[1] + ((open + high + low + close) / 4)) / 2
float haC = (open + high + low + close) / 4
float src = ut_heikin ? haC : close
float nLoss = ut_key * ta.atr(ut_atrP)
var float stop = 0.0
stop := src > nz(stop[1], 0) and src[1] > nz(stop[1], 0) ? math.max(nz(stop[1]), src - nLoss) :
src < nz(stop[1], 0) and src[1] < nz(stop[1], 0) ? math.min(nz(stop[1]), src + nLoss) :
src > nz(stop[1], 0) ? src - nLoss : src + nLoss
var int posv = 0
posv := src[1] < nz(stop[1], 0) and src > nz(stop[1], 0) ? 1 :
src[1] > nz(stop[1], 0) and src < nz(stop[1], 0) ? -1 : nz(posv[1], 0)
float em = ta.ema(src, 1)
[src > stop and ta.crossover(em, stop), src < stop and ta.crossover(stop, em), src > stop, src < stop, stop, posv]
[utEvtLong, utEvtShort, utStateLong, utStateShort, utStop, utPos] = request.security(syminfo.tickerid, f_tf(ut_tf), f_ut_bot(), barmerge.gaps_off, barmerge.lookahead_off)
[utXbuy, utExitSell, utXbl, utXbr, utXstop, utXpos] = request.security(syminfo.tickerid, f_tf(ut_exitTf == "" ? ut_tf : ut_exitTf), f_ut_bot(), barmerge.gaps_off, barmerge.lookahead_off)
// ── Consecutive-loss / choppy-market circuit breaker ─────────────────────────
// Tracks losing streaks and pauses entries after maxConsecLoss straight losses.
// Directly solves the NEAR May 20-21 cluster: 4 back-to-back losses in a tight
// range where all macro MA/VWMA filters remained bullish but every touch failed.
var int consecLosses = 0
var int choppyUntilBar = 0
if barstate.isconfirmed and strategy.closedtrades > nz(strategy.closedtrades[1])
float lastPnl = strategy.closedtrades.profit(strategy.closedtrades - 1)
if lastPnl < 0
consecLosses += 1
if consecLosses >= maxConsecLoss
// Arm the pause: block entries for choppyPauseBars from this bar.
choppyUntilBar := bar_index + choppyPauseBars
consecLosses := 0
else
consecLosses := 0 // winning trade resets the streak
bool choppyOk = not useChoppyFilter or bar_index > choppyUntilBar
// ── Profit-run limit — winning-cluster cap ────────────────────────────────────
// Track consecutive winners and their combined % profit; pause after the cap.
var int runWinStreak = 0
var float runStreakPnlPct = 0.0
var int runPauseUntil = 0
if barstate.isconfirmed and strategy.closedtrades > nz(strategy.closedtrades[1])
int _li = strategy.closedtrades - 1
float _pnl = strategy.closedtrades.profit(_li)
float _ev = strategy.closedtrades.entry_price(_li) * math.abs(strategy.closedtrades.size(_li))
float _pnlP = _ev != 0 ? _pnl / _ev * 100.0 : 0.0
if _pnl > 0
runWinStreak += 1
runStreakPnlPct += _pnlP
if useRunLimit and runWinStreak >= runLimTrades and runStreakPnlPct >= runLimProfitPct
runPauseUntil := bar_index + runLimPauseBars
runWinStreak := 0
runStreakPnlPct := 0.0
else
runWinStreak := 0 // a loss breaks the winning cluster
runStreakPnlPct := 0.0
bool runLimitOk = not useRunLimit or bar_index > runPauseUntil
// ── Logic 2 — Entry Bar Body Filter ──────────────────────────────────────────
// A bearish entry bar (close < open) whose body exceeds entryBodyAtrMult × ATR is
// rejected. This filters out high-momentum bearish closes right at support which
// have already shifted intra-bar momentum against the long setup.
bool bodyFilterOkLong = not useBodyFilter or close >= open or (open - close) <= entryBodyAtrMult * atr
// ── Peak / spike filters (anti-chase) ─────────────────────────────────────────
// 1) Over-extension: close must not be too far above the reference EMA.
float extRefEma = ta.ema(close, extRefLen)
float extLimit = extMode == "ATR" ? extMaxAtr * atr : extRefEma * extMaxPct / 100.0
bool extOkLong = not useExtFilter or na(extRefEma) or close <= extRefEma + extLimit
// 2) Parabolic run guard: rise over the lookback must not exceed the cap.
float runRise = close - close[runLb]
float runCapPct = close[runLb] * runMaxPct / 100.0
bool runOkLong = not usePeakRun or na(close[runLb]) or (runMode == "Percent" ? runRise <= runCapPct : runRise <= runMaxAtr * atr)
// 3) ATR spike: current ATR must not be far above its average.
float atrAvgSpk = ta.sma(atr, atrSpikeAvgLen)
bool atrSpkOk = not useAtrSpike or na(atrAvgSpk) or atrAvgSpk <= 0 or atr <= atrSpikeMaxMult * atrAvgSpk
// 4) Upper-wick rejection: trigger bar's upper shadow must be small.
float upperWick = high - math.max(open, close)
bool wickOkLong = not useWickFilter or upperWick <= wickMaxAtr * atr
bool spikeFiltLong = extOkLong and runOkLong and atrSpkOk and wickOkLong
// ── BTC market filter — read BTC trend (independent of the chart coin) ─────────
// Each check is computed on the BTC symbol/TF via request.security (confirmed bars,
// no look-ahead). Disabled checks pass through.
bool _btcMa1 = request.security(btcSym, f_tf(btcMa1_tf), f_btc_ma_ok(btcMa1_src, btcMa1_len, btcMa1_type, btcMa1_slopeLb, requireBtcMaSlope), barmerge.gaps_off, barmerge.lookahead_off)
bool _btcMa2 = request.security(btcSym, f_tf(btcMa2_tf), f_btc_ma_ok(btcMa2_src, btcMa2_len, btcMa2_type, btcMa2_slopeLb, requireBtcMaSlope), barmerge.gaps_off, barmerge.lookahead_off)
bool _btcMa3 = request.security(btcSym, f_tf(btcMa3_tf), f_btc_ma_ok(btcMa3_src, btcMa3_len, btcMa3_type, btcMa3_slopeLb, requireBtcMaSlope), barmerge.gaps_off, barmerge.lookahead_off)
bool _btcStBull = request.security(btcSym, f_tf(btcStTf), f_btc_st_bull(btcStAtrLen, btcStMult), barmerge.gaps_off, barmerge.lookahead_off)
bool _btcHh = request.security(btcSym, f_tf(btcHhTf), f_btc_hh_ok(btcHhPivLen), barmerge.gaps_off, barmerge.lookahead_off)
bool _btcAtBull = request.security(btcSym, f_tf(btcAtTf), f_btc_at_bull(btcAtCoeff, btcAtAp, btcAtSrc, btcAtNovol), barmerge.gaps_off, barmerge.lookahead_off)
// Per-MA gate (enabled MAs must each pass).
bool btcMaOk = not useBtcMa or ((not btcMa1_en or _btcMa1) and (not btcMa2_en or _btcMa2) and (not btcMa3_en or _btcMa3))
bool btcStOk = not useBtcSt or _btcStBull
bool btcHhOk = not useBtcHh or _btcHh
bool btcAtOk = not useBtcAt or _btcAtBull
bool btcTrendOk = not btcEnable or (btcMaOk and btcStOk and btcHhOk and btcAtOk)
// ── Always-on filters (gated by their own use* toggles) ───────────────────────
// These remaining filters always act as AND-confirmations when enabled (they are
// not part of the Primary/Filter trigger framework). SuperTrend, LinReg, VWMA,
// Local EMA, Volume, Higher-high, Body filter, RSI, Peak/spike filters.
bool filtLong =
(not useSuperTrend or stBull) and
lrLongOk and
vwOkLong and
localTrendOkLong and volOkLong and hhOkLong and
bodyFilterOkLong and
rsiOkLong and
spikeFiltLong and
btcTrendOk
bool filtShort =
(not useSuperTrend or stBear) and
lrShortOk and
vwOkShort and
localTrendOkShort and volOkShort
// ── MA section condition (used by the Primary/Filter framework) ────────────────
bool maCondLong = maUp and maSlopeOkLong
bool maCondShort = maDn and maSlopeOkShort
// ══════════════════════════════════════════════════════════════════════════════
// PRIMARY / FILTER ENTRY FRAMEWORK
// • Sections set to PRIMARY are entry TRIGGERS — if ANY enabled primary fires
// (an event), an entry is initiated.
// • Sections set to NOT-PRIMARY are confirmation FILTERS — ALL enabled filters
// (state must be bullish/bearish) must pass for the entry to go through.
// • Participating sections: S/R, MA trend, Range Filter, Q-Trend, AlphaTrend, HACOLT.
// • An entry requires: ≥1 primary active AND that primary fired AND all framework
// filters pass AND all always-on filters (filtLong/filtShort) pass.
// ══════════════════════════════════════════════════════════════════════════════
// f_primary applies one section to the trigger/filter accumulators.
// en = section enabled, prim = section is primary,
// evt = trigger event (used when primary), state = bullish/bearish state (used when filter)
// LONG accumulators
bool priLongActive = false
bool priLongFired = false
bool fltLongPass = true
// MA trend
if useMaTrend
if maPrimary
priLongActive := true
priLongFired := priLongFired or maCondLong
else
fltLongPass := fltLongPass and maCondLong
// Range Filter
if rf_en
if rf_primary
priLongActive := true
priLongFired := priLongFired or rfEvtLong
else
fltLongPass := fltLongPass and rfStateLong
// Q-Trend
if qt_en
if qt_primary
priLongActive := true
priLongFired := priLongFired or qtEvtLong
else
fltLongPass := fltLongPass and qtStateLong
// AlphaTrend
if at_en
if at_primary
priLongActive := true
priLongFired := priLongFired or atEvtLong
else
fltLongPass := fltLongPass and atStateLong
// HACOLT
if hac_en
if hac_primary
priLongActive := true
priLongFired := priLongFired or hacEvtLong
else
fltLongPass := fltLongPass and hacStateLong
// UT Bot
if ut_en
if ut_primary
priLongActive := true
priLongFired := priLongFired or utEvtLong
else
fltLongPass := fltLongPass and utStateLong
// SHORT accumulators
bool priShortActive = false
bool priShortFired = false
bool fltShortPass = true
if useMaTrend
if maPrimary
priShortActive := true
priShortFired := priShortFired or maCondShort
else
fltShortPass := fltShortPass and maCondShort
if rf_en
if rf_primary
priShortActive := true
priShortFired := priShortFired or rfEvtShort
else
fltShortPass := fltShortPass and rfStateShort
if qt_en
if qt_primary
priShortActive := true
priShortFired := priShortFired or qtEvtShort
else
fltShortPass := fltShortPass and qtStateShort
if at_en
if at_primary
priShortActive := true
priShortFired := priShortFired or atEvtShort
else
fltShortPass := fltShortPass and atStateShort
if hac_en
if hac_primary
priShortActive := true
priShortFired := priShortFired or hacEvtShort
else
fltShortPass := fltShortPass and hacStateShort
if ut_en
if ut_primary
priShortActive := true
priShortFired := priShortFired or utEvtShort
else
fltShortPass := fltShortPass and utStateShort
// Final framework gate: at least one primary must be active AND have fired.
bool frameworkLongOk = priLongActive and priLongFired and fltLongPass
bool frameworkShortOk = priShortActive and priShortFired and fltShortPass
confirmed = not useBarConfirm or barstate.isconfirmed
// ── Stop / target calculations (R:R) ──────────────────────────────────────────
// R:R mode swing low (its own lookback) — stop is placed below this.
float rrSwingLow = ta.lowest(low, rrSwingLb)
// ── State variables ───────────────────────────────────────────────────────────
var float savedLongStop = na
var float savedLongTp = na
// Live effective protective stop (for plotting) — set by the R:R bracket manager.
var float liveLongStop = na
var int lastExitToFlatBar = na
if strategy.position_size == 0 and strategy.position_size[1] != 0
lastExitToFlatBar := bar_index
int barsSinceExitFlat = na(lastExitToFlatBar) ? 999999 : bar_index - lastExitToFlatBar
bool cooldownExitOk = cooldownBarsAfterExit <= 0 or na(lastExitToFlatBar) or barsSinceExitFlat > cooldownBarsAfterExit
bool entryBarOk = not ordersOnConfirmedBar or barstate.isconfirmed
// Entry = framework trigger/filter result AND the always-on filters AND master enable.
bool rawLongSetup = confirmed and strategy.position_size == 0 and long_en and frameworkLongOk and filtLong
// ── One-trade-per-indicator-signal latch ──────────────────────────────────────
// A fresh buy trigger on any ENABLED RF / Q-Trend / AlphaTrend arms the gate.
// An executed long consumes it. While unconsumed, at most one long may be taken;
// no further long until another indicator fires.
bool anyIndBuyTrig = (rf_en and rfEvtLong) or (qt_en and qtEvtLong) or (at_en and atEvtLong) or (ut_en and utEvtLong)
var bool indSigArmed = false
if anyIndBuyTrig
indSigArmed := true
bool oneTradeGateOk = not useOneTradePerSignal or indSigArmed
bool longSignal = rawLongSetup and cooldownExitOk and choppyOk and entryBarOk and oneTradeGateOk and runLimitOk
if longSignal
// R:R: SL just below the previous swing low (ATR buffer), floored to a minimum
// distance; TP at the reward:risk ratio above entry.
float rrSl = math.min(rrSwingLow - rrBufAtr * atr, close - minSlDistAtr * atr)
// One-trade-per-signal: consume the armed indicator buy signal on this entry.
indSigArmed := false
savedLongStop := rrSl
savedLongTp := close + (close - rrSl) * rrRatio
liveLongStop := rrSl
if inWindow
strategy.entry("Long", strategy.long)
// ── Unrealized R (gates soft exits) ──────────────────────────────────────────
float longUnrealR = strategy.position_size > 0 and not na(savedLongStop) and strategy.position_avg_price > savedLongStop
? (close - strategy.position_avg_price) / (strategy.position_avg_price - savedLongStop)
: 0.0
bool softExitRGateOk = minRForSoftExit <= 0 or longUnrealR >= minRForSoftExit
// ── Micro exits (SuperTrend, TF1 MA, LinReg) ─────────────────────────────────
float stLineLong = stBull ? stUp : stDn
bool exitBelowStNow = useExitBelowSt and not na(stLineLong) and close < stLineLong
bool exitBelowMa1Now = useExitBelowMa1 and ma1_en and not na(ma1) and close < ma1
bool exitBelowLrNow = useExitBelowLinReg and useLinReg and close < bclose
// ── HL Structure Break Exit ───────────────────────────────────────────────────
// A confirmed bar close below the most recent pivot low on hlBreakTf means the
// local uptrend has printed its first lower-low: structural invalidation.
// Fires significantly earlier than waiting for the 4h MA cross or full trailing
// stop to trigger — avoids giving back 2-3% of gained profit on breakdown bars.
float hlBreakPivLow = useHlBreakExit ? f_last_pivot_low(hlBreakTf, hlBreakPivLen) : na
bool hlBreakRGateOk = hlBreakMinR <= 0 or longUnrealR >= hlBreakMinR
bool exitHlBreakNow = useHlBreakExit
and strategy.position_size > 0
and barstate.isconfirmed
and hlBreakRGateOk
and not na(hlBreakPivLow)
and close < hlBreakPivLow // confirmed close below last pivot low = first lower-low
// ── Composite long exit ───────────────────────────────────────────────────────
// Soft exits (RSI rollover) are folded in here alongside the MA/ST/LinReg exits.
bool exitLongSignalNow = strategy.position_size > 0 and confirmed and softExitRGateOk
and (exitLongMaTrigger or exitBelowStNow or exitBelowMa1Now or exitBelowLrNow
or rsiExitNow)
// Indicator SELL exits — close the long when an enabled indicator prints a SELL on
// its exit timeframe (Range Filter / AlphaTrend / HACOLT / UT Bot).
bool exitRfNow = rf_useExit and rfExitSell
bool exitAtNow = at_useExit and atExitSell
bool exitHacNow = hac_useExit and hacExitSell
bool exitUtNow = ut_useExit and utExitSell
bool indExitNow = strategy.position_size > 0 and confirmed and (exitRfNow or exitAtNow or exitHacNow or exitUtNow)
bool maCloseLongNow = exitLongSignalNow or exitHlBreakNow or indExitNow
if maCloseLongNow
string exitWhy =
exitHlBreakNow ? "HL Break" :
exitLongMaTrigger ? "MTF MA exit" :
exitBelowStNow ? "Below ST" :
exitBelowMa1Now ? "Below TF1 MA" :
rsiExitNow ? "RSI rollover" :
exitRfNow ? "RF Sell" :
exitAtNow ? "AlphaTrend Sell" :
exitHacNow ? "HACOLT Sell" :
exitUtNow ? "UT Bot Sell" :
"Below LinReg"
strategy.close("Long", comment = exitWhy)
// ── R:R trade state (captured at fill; consumed by the R:R exit engine) ────────
// At fill we capture the original filled quantity (ptpEntryQty) and avg fill price
// (ptpEntryPx). The R:R engine sizes partial TPs against ptpEntryQty and arms the
// % trailing stop / tier flags from these.
var float ptpEntryQty = na
var float ptpEntryPx = na
var bool ptpTrailArmed = false
var float ptpTrailAnchor = na
var bool ptpTp1Done = false // set once TP1 price is reached → stop re-issuing TP1
var bool ptpTp2Done = false // set once TP2 price is reached → stop re-issuing TP2
// Capture original size/price the bar the long position first appears.
bool justEnteredLong = strategy.position_size > 0 and nz(strategy.position_size[1]) <= 0
if justEnteredLong
ptpEntryQty := strategy.position_size
ptpEntryPx := strategy.position_avg_price
ptpTrailArmed := false
ptpTrailAnchor := na
ptpTp1Done := false
ptpTp2Done := false
// ══════════════════════════════════════════════════════════════════════════════
// R:R EXIT ENGINE — swing-low stop + ratio TP, optional % partial TPs + % trailing
// • Base stop = savedLongStop (swing low − buffer), set at entry.
// • Optional % trailing stop ratchets the stop up (never down).
// • Optional partial TPs: scale out at TP1 % and TP2 %; the runner exits at the
// reward:risk target (savedLongTp). When partials are off, a single ratio TP is used.
// • The shared protective stop (max of swing-low SL, break-even, % trail) brackets
// every leg, so the position is always protected.
// ══════════════════════════════════════════════════════════════════════════════
if useRR and strategy.position_size > 0 and not na(savedLongStop) and not na(ptpEntryPx) and not maCloseLongNow
float rrEntryPx = ptpEntryPx
// Percentage partial-TP tier prices + sizes (fractions of the ORIGINAL fill).
float rrTp1P = rrEntryPx * (1 + rrTp1Pct / 100)
float rrTp2P = rrEntryPx * (1 + rrTp2Pct / 100)
float rrTp1Q = na(ptpEntryQty) ? na : ptpEntryQty * rrTp1Size / 100
float rrTp2Q = na(ptpEntryQty) ? na : ptpEntryQty * rrTp2Size / 100
// Mark a tier done once price trades through it (don't re-issue → no over-sell).
if rrUsePartialTp and not ptpTp1Done and high >= rrTp1P
ptpTp1Done := true
if rrUsePartialTp and not ptpTp2Done and high >= rrTp2P
ptpTp2Done := true
// Percentage trailing stop — arms when in profit (optionally after +rrTrailActPct%).
if rrUseTrailSl and not ptpTrailArmed and (rrTrailActPct <= 0 ? close > rrEntryPx : high >= rrEntryPx * (1 + rrTrailActPct / 100))
ptpTrailArmed := true
float rrTrailLvl = na
if rrUseTrailSl and ptpTrailArmed
float cand = close * (1 - rrTrailPct / 100)
ptpTrailAnchor := na(ptpTrailAnchor) ? cand : math.max(ptpTrailAnchor, cand)
rrTrailLvl := ptpTrailAnchor
// Shared protective stop = max(swing-low SL, % trail).
float rrStopUse = savedLongStop
if rrUseTrailSl and not na(rrTrailLvl)
rrStopUse := math.max(rrStopUse, rrTrailLvl)
liveLongStop := rrStopUse
// Place the exit legs. Runner always targets the reward:risk TP (savedLongTp).
if rrUsePartialTp
if not ptpTp1Done
strategy.exit("RR1", from_entry = "Long", qty = rrTp1Q, limit = rrTp1P, stop = rrStopUse, comment_profit = "TP1", comment_loss = "SL")
if not ptpTp2Done
strategy.exit("RR2", from_entry = "Long", qty = rrTp2Q, limit = rrTp2P, stop = rrStopUse, comment_profit = "TP2", comment_loss = "SL")
strategy.exit("RR3", from_entry = "Long", limit = savedLongTp, stop = rrStopUse, comment_profit = "TP", comment_loss = "SL")
else
strategy.exit("RR X", from_entry = "Long", limit = savedLongTp, stop = rrStopUse, comment_profit = "TP", comment_loss = "SL")
// Reset all long state when flat (but not on the bar that just opened a position).
flat = strategy.position_size == 0
if flat and not longSignal
savedLongStop := na
savedLongTp := na
liveLongStop := na
ptpEntryQty := na
ptpEntryPx := na
ptpTrailArmed := false
ptpTrailAnchor := na
ptpTp1Done := false
ptpTp2Done := false
// ── Plotting ──────────────────────────────────────────────────────────────────
plot(showStBand ? (stTrendVal == 1 ? stUp : na) : na, "ST up band", color = color.new(color.green, 40), linewidth = 2)
plot(showStBand ? (stTrendVal == -1 ? stDn : na) : na, "ST dn band", color = color.new(color.red, 40), linewidth = 2)
plot(plotMaLines and ma1_en ? ma1 : na, "MA TF1", color = color.new(color.orange, 0), linewidth = 2)
plot(plotMaLines and ma2_en ? ma2 : na, "MA TF2", color = color.new(color.white, 15), linewidth = 2)
plot(plotMaLines and ma3_en ? ma3 : na, "MA TF3", color = color.new(color.fuchsia, 0), linewidth = 2)
plot(plotMaLines and ma4_en ? ma4 : na, "MA TF4", color = color.new(color.aqua, 0), linewidth = 2)
plot(plotVwmaLine and useVwmaTrend ? vwTrend : na, "VWMA (trend)", color = color.new(color.yellow, 0), linewidth = 2)
plot(plotLocalEma and useLocalTrend ? localFastEma : na, "Local EMA Fast", color = color.new(color.blue, 20), linewidth = 1)
plot(plotLocalEma and useLocalTrend ? localSlowEma : na, "Local EMA Slow", color = color.new(color.navy, 30), linewidth = 1)
plot(plotExitMa and useExitLongMa ? exitMaAligned : na, "Exit MA (MTF)", color = color.new(color.purple, 0), linewidth = 2)
// ── Over-extension ceiling (anti-chase) ───────────────────────────────────────
plot(plotExtBand and useExtFilter ? extRefEma : na, "Ext ref EMA", color = color.new(color.gray, 0), linewidth = 1)
plot(plotExtBand and useExtFilter ? extRefEma + extLimit : na, "Ext ceiling", color = color.new(color.red, 30), linewidth = 1)
// ── Higher-high structure plot (last + prior pivot highs on HH TF) ────────────
plot(plotHhStruct ? hhLastPH : na, "HH last pivot high", color = color.new(color.lime, 0), linewidth = 2, style = plot.style_linebr)
plot(plotHhStruct ? hhPriorPH : na, "HH prior pivot high", color = color.new(color.orange, 0), linewidth = 1, style = plot.style_linebr)
// ── R:R levels ────────────────────────────────────────────────────────────────
// Projected R:R levels (shown while flat so you can preview the next trade's SL/TP).
float rrProjSl = math.min(rrSwingLow - rrBufAtr * atr, close - minSlDistAtr * atr)
float rrProjTp = close + (close - rrProjSl) * rrRatio
plot(plotRR and useRR ? (strategy.position_size > 0 ? liveLongStop : rrProjSl) : na, "R:R Stop", color = color.new(color.red, 0), linewidth = 2, style = plot.style_linebr)
plot(plotRR and useRR ? (strategy.position_size > 0 ? savedLongTp : rrProjTp) : na, "R:R Target", color = color.new(color.green, 0), linewidth = 2, style = plot.style_linebr)
// ── Extra requested plots — LinReg close, ATR, Volume SMA (all default OFF) ────
plot(plotLinReg ? bclose : na, "LinReg close", color = color.new(color.aqua, 0), linewidth = 2)
plot(plotAtr ? atr : na, "ATR (risk)", color = color.new(color.gray, 0), linewidth = 1)
plot(plotVolSma ? volMaSeries : na, "Volume SMA", color = color.new(color.gray, 0), linewidth = 1)
// ── NEW indicator plots (all default OFF) ─────────────────────────────────────
// Range Filter line + target bands.
plot(plotRangeFilter ? rfFilt : na, "Range Filter", color = rfDir == 1 ? color.new(color.white, 0) : rfDir == -1 ? color.new(color.blue, 0) : color.new(color.rgb(144, 191, 249), 0), linewidth = 2)
plot(plotRangeFilter ? rfHband : na, "RF High Target", color = color.new(color.white, 70))
plot(plotRangeFilter ? rfLband : na, "RF Low Target", color = color.new(color.blue, 70))
// Q-Trend trend line (colored by last signal).
plot(plotQTrend ? qtM : na, "Q-Trend", color = qtLs == "B" ? color.new(color.green, 0) : color.new(color.red, 0), linewidth = 3)
// AlphaTrend (line + 2-bar lag line, like the original).
plot(plotAlphaTrend ? AlphaTrend : na, "AlphaTrend", color = color.new(color.rgb(0, 34, 252), 0), linewidth = 3)
plot(plotAlphaTrend ? AlphaTrendLag : na, "AlphaTrend lag", color = color.new(color.rgb(252, 4, 0), 0), linewidth = 3)
// HACOLT state as a background tint (oscillator can't share the price scale cleanly).
bgcolor(plotHacolt ? (hacolt > 0 ? color.new(color.green, 85) : hacolt < 0 ? color.new(color.red, 85) : color.new(color.blue, 90)) : na, title = "HACOLT state")
// UT Bot ATR trailing stop (green when long, red when short).
plot(plotUtBot ? utStop : na, "UT Bot trailing stop", color = utPos == 1 ? color.new(color.green, 0) : utPos == -1 ? color.new(color.red, 0) : color.new(color.blue, 0), linewidth = 2, style = plot.style_linebr)
// RSI availability check marker (plots a small dot when RSI is in the long band).
plotshape(plotRsiState and not na(rsiVal) and rsiVal >= rsiLongMin and rsiVal <= rsiLongMax ? high : na,
title = "RSI in long band", style = shape.circle, location = location.abovebar, color = color.new(color.purple, 40), size = size.tiny)
plotshape(showMarkers and longSignal ? low : na, title = "Buy long", style = shape.labelup, location = location.absolute, color = color.new(color.green, 0), textcolor = color.white, text = "Buy", size = size.small)
// Small ✕ at top of bar while the choppy-market pause is active and flat.
plotshape(useChoppyFilter and bar_index <= choppyUntilBar and strategy.position_size == 0 ? high : na,
title = "Choppy pause", style = shape.xcross, location = location.absolute,
color = color.new(color.red, 40), size = size.tiny)
// Small ◆ at top of bar while the profit-run pause is active and flat.
plotshape(useRunLimit and bar_index <= runPauseUntil and strategy.position_size == 0 ? high : na,
title = "Profit-run pause", style = shape.diamond, location = location.absolute,
color = color.new(color.orange, 30), size = size.tiny)
// Closed-trade SELL marker + P/L label (fires on each closed trade, incl. partial TPs).
int ctClosed = strategy.closedtrades
if (showExitPnL or showMarkers) and ctClosed > nz(ctClosed[1])
int ti = ctClosed - 1
float esz = strategy.closedtrades.size(ti)
float ep = strategy.closedtrades.entry_price(ti)
float xp = strategy.closedtrades.exit_price(ti)
float pctx = na
if ep != 0 and esz != 0
pctx := esz > 0 ? (xp - ep) / ep * 100.0 : (ep - xp) / ep * 100.0
string side = esz > 0 ? "Sell" : "Cover"
string pStr = na(pctx) ? "—" : (pctx >= 0 ? "+" : "") + str.tostring(pctx, "#.##") + "%"
string tTxt = side + " " + pStr
color tCol = na(pctx) ? color.gray : pctx >= 0 ? color.new(color.green, 0) : color.new(color.red, 0)
float yAt = esz > 0 ? high : low
label.new(bar_index, yAt, tTxt, xloc = xloc.bar_index, style = esz > 0 ? label.style_label_down : label.style_label_up,
color = tCol, textcolor = color.white, size = size.small)
// Cumulative P/L table (top-right, last bar)
var table tblCum = na
if showCumPnL
if na(tblCum)
tblCum := table.new(position.top_right, 2, 2, border_width = 1, frame_color = color.new(color.gray, 50), frame_width = 1)
if barstate.islastconfirmedhistory
float netPct = strategy.initial_capital != 0 ? 100.0 * strategy.netprofit / strategy.initial_capital : na
table.cell(tblCum, 0, 0, "Net P/L %", text_color = color.white, bgcolor = color.new(color.black, 75))
table.cell(tblCum, 1, 0, str.tostring(netPct, "#.##") + "%", text_color = color.white, bgcolor = color.new(color.black, 75))
table.cell(tblCum, 0, 1, "Closed trades", text_color = color.white, bgcolor = color.new(color.black, 75))
table.cell(tblCum, 1, 1, str.tostring(strategy.closedtrades), text_color = color.white, bgcolor = color.new(color.black, 75))
// ── Alerts — Custom FastAPI bot ───────────────────────────────────────────────
if use_custom_webhook and (ALERT_SECRET == "REPLACE_ME" or str.length(ALERT_SECRET) < 32)
runtime.error("Set [Custom bot] secret to your full bot webhook secret")
f_bot_json_buy() =>
"{\"secret\":\"" + ALERT_SECRET + "\",\"action\":\"buy\",\"symbol\":\"" + syminfo.ticker +
"\",\"quote_order_qty\":" + str.tostring(alert_buy_usdt) +
",\"dedupe_key\":\"L-" + str.tostring(bar_index) + "-" + str.tostring(time) + "\"}"
f_bot_json_sell_exit() =>
"{\"secret\":\"" + ALERT_SECRET + "\",\"action\":\"sell\",\"symbol\":\"" + syminfo.ticker +
"\",\"dedupe_key\":\"X-" + str.tostring(bar_index) + "-" + str.tostring(time) + "\"}"
if use_custom_webhook and longSignal
alert(f_bot_json_buy(), alert.freq_once_per_bar_close)
// Exit fires the moment the position closes intra-bar (TP/SL fill) — not at bar close.
// freq_once_per_bar = first tick the condition is true within the bar (needs calc_on_every_tick).
bool longJustClosed = strategy.position_size == 0 and strategy.position_size[1] > 0
if use_custom_webhook and longJustClosed
alert(f_bot_json_sell_exit(), alert.freq_once_per_bar)
// Close any position left open when the backtest window ends.
if not inWindow and strategy.position_size > 0
strategy.close_all(comment = "window end")
Overview
Other trend, volume and structure filters are available. The stop sits at a swing low and the take profit uses a reward-to-risk ratio. A cap stops new entries after a cluster of winning trades, and orders are processed on bar close. Backtests are limited to a fixed date window.
Multi-timeframe 300-period moving average trend trigger
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Signals on confirmed bar only
bool
true
useBarConfirm
New entries: bar close only (3Commas)
bool
true
ordersOnConfirmedBar
Min bars after exit before new entry
int
0
cooldownBarsAfterExit
Pause entries after N consecutive losses
bool
false
useChoppyFilter
Max consecutive losses before pause
int
2
maxConsecLoss
Pause duration (bars) after max losses hit
int
20
choppyPauseBars
Stop entries after a profitable winning run
bool
true
useRunLimit
Winning trades in the run
int
5
runLimTrades
Min combined profit % of the run
float
9.0
runLimProfitPct
Pause new entries for N bars after cap hit
int
30
runLimPauseBars
Signal delivery
string
3Commas Signal Bot
signal_delivery
[Custom bot] secret → WEBHOOK_SECRET
string
REPLACE_ME
ALERT_SECRET
Show all 40 settings
Setting
Type
Default
Variable
[Custom bot] BUY quote USDT
float
800.0
alert_buy_usdt
[Custom bot] SELL base qty
float
0.001
alert_sell_base
[Custom bot] symbol
string
APTUSDT
alert_symbol
Require MA uptrend / downtrend
bool
true
useMaTrend
MA trend is PRIMARY (entry trigger)
bool
true
maPrimary
MA: block entries while MTF MA is na
bool
false
maBlockWhenNa
Plot MTF MA lines (TF1–TF4)
bool
false
plotMaLines
Require enabled MAs to be rising (slope)
bool
true
requireMaSlope
TF1
bool
true
ma1_en
ma1_tf
timeframe
1
ma1_tf
ma1_len
int
300
ma1_len
ma1_type
string
SMA
ma1_type
ma1_src
string
Close
ma1_src
TF1 slope lookback (bars)
int
15
ma1_slopeLb
TF2
bool
true
ma2_en
ma2_tf
timeframe
5
ma2_tf
ma2_len
int
300
ma2_len
ma2_type
string
SMA
ma2_type
ma2_src
string
Close
ma2_src
TF2 slope lookback (bars)
int
20
ma2_slopeLb
TF3
bool
true
ma3_en
ma3_tf
timeframe
15
ma3_tf
ma3_len
int
14
ma3_len
ma3_type
string
SMA
ma3_type
ma3_src
string
Close
ma3_src
TF3 slope lookback (bars)
int
13
ma3_slopeLb
TF4
bool
true
ma4_en
ma4_tf
timeframe
15
ma4_tf
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.
Doge SMA is a long-only moving-average crossover strategy drawn on the price chart. It calculates a fast and a slow simple moving average of close and enters a long position when the fast average crosses above the slow average, but only inside a user-defined s
This strategy calculates and plots a fast Exponential Moving Average (EMA) and a slow EMA directly on the main price chart. It enters long trades when the fast EMA crosses above the slow EMA and exits when the fast EMA crosses below the slow EMA. Inputs allow
2 EMA/SMA + RSI Strategy [Alorse] combines three configurable moving averages with a short-period Relative Strength Index filter. It plots fast, slow, and trend moving averages on the price chart. Long entries trigger when the fast moving average is above the
This strategy plots on the price chart and opens long or short positions when a weighted score from MACD, Stochastic RSI, RSI, Supertrend and a moving-average crossover exceeds a user-set trigger. Each component has its own weight and candle delay. Entries can
This strategy calculates dynamic support and resistance levels using pivot highs and lows alongside a custom smoothed trend filter and a 3-period EMA. It identifies entry opportunities when price breaks out relative to the trend filter and short-term EMA cross
This educational strategy demonstrates how to configure and handle user input parameters in Pine Script. It calculates two Simple Moving Averages (SMAs) for fast and slow periods, plotting both directly on the price chart. The strategy initiates a long positio