Strategy · Pine Script v6 · On the chart

DP2

by grinay

DP2 is a price-chart strategy that looks for a short dump between the chosen from and to prices, optionally requiring a break of a multi-day low while ignoring moves larger than a dump cap.

View on GitHub

GitHub stars3on the repository
Pine versionv6current
Lines of code354
Settings30inputs
LicenceMIT
Last update7 months ago3 Mar 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

354 lines of Pine Script v6 from strategies/dump-reversal-peak-trail-v2/strategy.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

strategy.pine
//@version=6
strategy("DP2", overlay=true, initial_capital=1000, currency=currency.USDT, commission_value = 0.1, slippage = 10, default_qty_type=strategy.percent_of_equity, default_qty_value=100, commission_type=strategy.commission.percent, margin_long=20, calc_on_every_tick=false, process_orders_on_close=true, pyramiding=1)


// === CONFIG FOR WUNDERTRADING COMMENT FORMAT ===
symbolPair   = syminfo.basecurrency + syminfo.currency       // <-- dynamically from chart, normalized (e.g., SUIUSDT.P -> SUIUSDT)

// Inputs
var string grpDump = "Dump Detection"
fromSource = input.source(open, "From", group=grpDump)
toSource = input.source(close, "To", group=grpDump)
anomalyThresholdPct = input.float(1.0, "Anomaly Threshold (%)", step=0.1, minval=0.0, group=grpDump)
daysBreakLen = input.int(7, "Days Lookback for Low Break (0=disabled)", minval=0, group=grpDump)
dumpCapPct = input.float(2.0, "Dump Cap (%) - ignore dumps larger than this", step=0.1, minval=0.0, group=grpDump)

var string grpRisk = "Risk / Exits"
armProfitPct = input.float(5.0, "Arm Exit After Profit %", step=0.1, minval=0.1, group=grpRisk)
peakRetracePct = input.float(1.5, "Exit on Peak Retrace %", step=0.1, minval=0.1, group=grpRisk)
enableSL = input.bool(false, "Enable Stop Loss", group=grpRisk)
stopLossPct = input.float(15.0, "Stop Loss % (from avg)", step=0.1, minval=0.1, group=grpRisk)
maxHoursInLoss = input.int(0, "Max Hours In Loss (0=disabled)", minval=0, group=grpRisk)

// Proximity filters
var string grpFilters = "Filters"
resistLookbackDays = input.int(3, "Resistance Lookback (days)", minval=1, group=grpFilters)
resistProximityPct = input.float(1.0, "Block entries within % of resistance", step=0.1, minval=0.0, group=grpFilters)

// Entry confirmation
var string grpEntry = "Entry Timing"
var string grpEntryClassic = "Entry: Classic"
var string grpEntryTwoCloses = "Entry: TwoCloses"
var string grpEntryBreakRetest = "Entry: BreakRetest"
var string grpEntryATRStop = "Entry: ATRStop"
var string grpEntryAuto = "Entry: Auto"
var string grpEntryRebound = "Entry: Rebound"
// Use enum for robust comparisons in active flags
enum EntryMode
    Classic
    TwoCloses
    BreakRetest
    ATRStop
    Auto
    Rebound
entryMode = input.enum(EntryMode.ATRStop, "Entry Mode", options=[EntryMode.Classic, EntryMode.TwoCloses, EntryMode.BreakRetest, EntryMode.ATRStop, EntryMode.Auto, EntryMode.Rebound], group=grpEntry)
// Per-mode flags for enabling relevant inputs only
modeClassic = entryMode == EntryMode.Classic
modeTwoCloses = entryMode == EntryMode.TwoCloses
modeBreakRetest = entryMode == EntryMode.BreakRetest
modeATRStop = entryMode == EntryMode.ATRStop
modeAuto = entryMode == EntryMode.Auto
modeRebound = entryMode == EntryMode.Rebound
// TwoCloses
twoClosesBars = input.int(2, "Closes Above Level", minval=1, maxval=5, group=grpEntryTwoCloses, active = modeTwoCloses)
// BreakRetest
brAtrLen = input.int(14, "ATR Length", minval=1, group=grpEntryBreakRetest, active = modeBreakRetest)
retestOffsetAtr = input.float(0.2, "Retest Offset ATR Mult", step=0.05, minval=-2.0, group=grpEntryBreakRetest, active = modeBreakRetest)
retestExpiryBars = input.int(8, "Retest Order Expiry Bars", minval=1, group=grpEntryBreakRetest, active = modeBreakRetest)
// ATRStop
atrStopAtrLen = input.int(14, "ATR Length", minval=1, group=grpEntryATRStop, active = modeATRStop)
atrStopMult = input.float(0.5, "ATR Stop Buffer", step=0.1, minval=0.0, group=grpEntryATRStop, active = modeATRStop)
stopExpiryBars = input.int(12, "Stop Order Expiry Bars", minval=1, group=grpEntryATRStop, active = modeATRStop)
// Auto
autoAtrLen = input.int(14, "ATR Length", minval=1, group=grpEntryAuto, active = modeAuto)
autoSensitivity = input.string("Medium", "Auto Sensitivity", options=["Low", "Medium", "High"], group=grpEntryAuto, active = modeAuto)
autoRetestExpiryBars = input.int(8, "Retest Order Expiry Bars", minval=1, group=grpEntryAuto, active = modeAuto)
autoStopExpiryBars = input.int(12, "Stop Order Expiry Bars", minval=1, group=grpEntryAuto, active = modeAuto)
// Rebound
flatLookbackBars = input.int(5, "Flat Lookback Bars", minval=1, group=grpEntryRebound, active = modeRebound)
maxFlatRangePct = input.float(0.4, "Max Flat Range %", step=0.1, minval=0.0, group=grpEntryRebound, active = modeRebound)
minReboundFromLowPct = input.float(0.8, "Min Rebound From Low %", step=0.1, minval=0.0, group=grpEntryRebound, active = modeRebound)

brAtr = ta.atr(brAtrLen)
atrStopAtr = ta.atr(atrStopAtrLen)
autoAtr = ta.atr(autoAtrLen)

// Rebound helpers
flatHH = ta.highest(high, flatLookbackBars)
flatLL = ta.lowest(low, flatLookbackBars)
flatRangePct = flatLL > 0 ? (flatHH - flatLL) / flatLL * 100.0 : 0.0
isFlat = flatRangePct <= maxFlatRangePct

// Auto-mode derived parameters
autoSensLow = autoSensitivity == "Low"
autoSensMed = autoSensitivity == "Medium"
autoSensHigh = autoSensitivity == "High"
autoTwoClosesBars = autoSensLow ? 1 : autoSensMed ? 2 : 3
autoRetestOffsetAtr = autoSensLow ? 0.10 : autoSensMed ? 0.20 : 0.35
autoAtrStopMult = autoSensLow ? 0.40 : autoSensMed ? 0.50 : 0.80

// Cooldown after trade closes
cooldownHours = input.int(4, "Cooldown Hours After Trade", minval=0, group=grpRisk)
cooldownMs = cooldownHours * 60 * 60 * 1000
var int cooldownUntilMs = na

// Dump detection (anomaly-based)
diff = fromSource != 0.0 ? (toSource / fromSource - 1.0) * 100.0 : 0.0
isAnomaly = math.abs(diff) > anomalyThresholdPct
withinDumpCap = math.abs(diff) <= dumpCapPct or dumpCapPct == 0.0
dumpSignal = isAnomaly and close < open and withinDumpCap

// N-day (configurable) low break filter using daily timeframe; 0 disables filter
daysFilterEnabled = daysBreakLen > 0
dailyLow = request.security(syminfo.tickerid, "D", low)
safeDaysLen = math.max(1, daysBreakLen)
priorDaysLowestSeries = request.security(syminfo.tickerid, "D", ta.lowest(low, safeDaysLen))
priorDaysLowest = daysFilterEnabled ? priorDaysLowestSeries[1] : na
breaksNDaysLow = daysFilterEnabled ? (low < priorDaysLowest) : true

// 3-day resistance proximity filter (uses prior completed daily bar)
safeResDays = math.max(1, resistLookbackDays)
dailyHighestSeries = request.security(syminfo.tickerid, "D", ta.highest(high, safeResDays))
resistancePrice = dailyHighestSeries[1]
nearResistance = not na(resistancePrice) and resistancePrice > 0 and close < resistancePrice and ((resistancePrice - close) / resistancePrice * 100.0) <= resistProximityPct

// BTC 4h trend filter (hourly change over last 4 hours)
useBtcTrendFilter = input.bool(true, "Require BTC uptrend (last 4h)", group=grpFilters)
btcTrendSymbol = input.symbol("BYBIT:BTCUSDT.P", "BTC Perpetual Trend Symbol", group=grpFilters)
btc4hChange = request.security(btcTrendSymbol, "60", ta.change(close, 4))
btcTrendUp = btc4hChange > 0

canEnter = not nearResistance and (not useBtcTrendFilter or btcTrendUp)
canPyramid = strategy.opentrades < 5

// Entry state - track dump candle for structure break
var float dumpCandleHigh = na
var bool waitingForReclaim = false
var bool waitForGreen = false
var float armedLow = na
var float dumpLegLowestLow = na

// Pending order state
var bool retestOrderActive = false
var bool stopOrderActive = false
var int retestExpiresBar = na
var int stopExpiresBar = na
var float retestPrice = na
var float stopTriggerPrice = na

// Trailing/Exit logic
inLong = strategy.position_size > 0
avg = strategy.position_avg_price
profitPct = inLong ? (close - avg) / avg * 100.0 : 0.0
var float peakProfitPct = na
var int entryOpenTimeMs = na
// Detect position close and start cooldown timer
closedThisBar = inLong[1] and not inLong
if barstate.isconfirmed and closedThisBar
    cooldownUntilMs := time + cooldownMs
    peakProfitPct := na
    entryOpenTimeMs := na
    if retestOrderActive or stopOrderActive
        strategy.cancel("Long")
        retestOrderActive := false
        stopOrderActive := false
        retestExpiresBar := na
        stopExpiresBar := na
        retestPrice := na
        stopTriggerPrice := na

// Gate entries by cooldown
noPos = strategy.position_size == 0
isCoolingDown = not na(cooldownUntilMs) and time < cooldownUntilMs

// Arm structure break watcher on dump detection
if barstate.isconfirmed and not isCoolingDown and dumpSignal and breaksNDaysLow and (noPos or (inLong and canPyramid))
    if retestOrderActive or stopOrderActive
        strategy.cancel("Long")
        retestOrderActive := false
        stopOrderActive := false
        retestExpiresBar := na
        stopExpiresBar := na
        retestPrice := na
        stopTriggerPrice := na
    if modeRebound
        waitForGreen := true
        armedLow := low
    else
        waitingForReclaim := true
        dumpCandleHigh := high
    dumpLegLowestLow := low

// Cancel waiting during cooldown
if barstate.isconfirmed and isCoolingDown and waitingForReclaim
    waitingForReclaim := false
    dumpCandleHigh := na
    dumpLegLowestLow := na
    if retestOrderActive or stopOrderActive
        strategy.cancel("Long")
        retestOrderActive := false
        stopOrderActive := false
        retestExpiresBar := na
        stopExpiresBar := na
        retestPrice := na
        stopTriggerPrice := na

// Cancel rebound arming during cooldown
if barstate.isconfirmed and isCoolingDown and waitForGreen
    waitForGreen := false
    armedLow := na

// While waiting to reclaim, if new lower lows form within per-bar dump cap, reset reference to that bar
if barstate.isconfirmed and waitingForReclaim and not isCoolingDown
    // per-bar dump from open->close on this bar
    currDiff = open != 0.0 ? (close / open - 1.0) * 100.0 : 0.0
    currWithinCap = math.abs(currDiff) <= dumpCapPct or dumpCapPct == 0.0
    if low < nz(dumpLegLowestLow, 1e10) and currWithinCap
        // Cancel any pending entries as reference resets to new lowest bar
        if retestOrderActive or stopOrderActive
            strategy.cancel("Long")
            retestOrderActive := false
            stopOrderActive := false
            retestExpiresBar := na
            stopExpiresBar := na
            retestPrice := na
            stopTriggerPrice := na
        dumpLegLowestLow := low
        dumpCandleHigh := high

// Entry handling by mode
if barstate.isconfirmed and waitingForReclaim and not isCoolingDown and (noPos or (inLong and canPyramid))
    classicBreak = close > dumpCandleHigh
    // TwoCloses confirmation
    if entryMode == EntryMode.TwoCloses
        closesAbove = ta.lowest(close, twoClosesBars) > dumpCandleHigh
        if closesAbove and canEnter
            strategy.entry("Long", strategy.long)
            waitingForReclaim := false
            dumpCandleHigh := na
    // Break-and-Retest limit entry with expiry
    if entryMode == EntryMode.BreakRetest
        crossedUp = ta.crossover(close, dumpCandleHigh)
        if crossedUp and not retestOrderActive and canEnter
            retestPrice := dumpCandleHigh + (retestOffsetAtr * brAtr)
            strategy.entry("Long", strategy.long, limit=retestPrice)
            retestOrderActive := true
            retestExpiresBar := bar_index + (modeAuto ? autoRetestExpiryBars : retestExpiryBars)
            waitingForReclaim := false
    // ATR-buffered stop entry with expiry
    if entryMode == EntryMode.ATRStop
        if not stopOrderActive and canEnter
            stopTriggerPrice := dumpCandleHigh + (atrStopMult * atrStopAtr)
            strategy.entry("Long", strategy.long, stop=stopTriggerPrice)
            stopOrderActive := true
            stopExpiresBar := bar_index + (modeAuto ? autoStopExpiryBars : stopExpiryBars)
            waitingForReclaim := false
    // Auto: retest-first with fallback to ATR-stop on expiry
    if entryMode == EntryMode.Auto
        crossedUp = ta.crossover(close, dumpCandleHigh)
        if crossedUp and not retestOrderActive and not stopOrderActive and canEnter
            retestPrice := dumpCandleHigh + (autoRetestOffsetAtr * autoAtr)
            strategy.entry("Long", strategy.long, limit=retestPrice)
            retestOrderActive := true
            retestExpiresBar := bar_index + autoRetestExpiryBars
            waitingForReclaim := false
        else
            closesAboveAuto = ta.lowest(close, autoTwoClosesBars) > dumpCandleHigh
            if closesAboveAuto and not retestOrderActive and not stopOrderActive and canEnter
                strategy.entry("Long", strategy.long)
                waitingForReclaim := false
                dumpCandleHigh := na
    // Classic immediate break (baseline)
    if entryMode == EntryMode.Classic and classicBreak and canEnter
        strategy.entry("Long", strategy.long)
        waitingForReclaim := false
        dumpCandleHigh := na

// Rebound entry handling
if barstate.isconfirmed and modeRebound
    // Track lowest low since arming
    if waitForGreen
        armedLow := na(armedLow) ? low : math.min(armedLow, low)
    reboundPctFromArmedLow = not na(armedLow) and armedLow > 0 ? (close - armedLow) / armedLow * 100.0 : 0.0
    reboundOk = reboundPctFromArmedLow >= minReboundFromLowPct
    if (noPos or (inLong and canPyramid)) and not isCoolingDown and waitForGreen and close > open and isFlat and reboundOk and canEnter
        strategy.entry("Long", strategy.long)
        waitForGreen := false
        armedLow := na

// Reset state when position opens
openedThisBar = not inLong[1] and inLong
if barstate.isconfirmed and openedThisBar
    waitingForReclaim := false
    dumpCandleHigh := na
    waitForGreen := false
    armedLow := na
    dumpLegLowestLow := na
    entryOpenTimeMs := time
    peakProfitPct := na
    if retestOrderActive or stopOrderActive
        retestOrderActive := false
        stopOrderActive := false
        retestExpiresBar := na
        stopExpiresBar := na
        retestPrice := na
        stopTriggerPrice := na

// Reset pending add state when position size increases (pyramiding add filled)
addFilledThisBar = inLong and strategy.position_size > strategy.position_size[1]
if barstate.isconfirmed and addFilledThisBar
    waitingForReclaim := false
    dumpCandleHigh := na
    waitForGreen := false
    armedLow := na
    dumpLegLowestLow := na
    peakProfitPct := na
    if retestOrderActive or stopOrderActive
        retestOrderActive := false
        stopOrderActive := false
        retestExpiresBar := na
        stopExpiresBar := na
        retestPrice := na
        stopTriggerPrice := na

// (legacy trailing/ATR TP removed)

// Profit-taking: overall position peak profit retrace using weighted average entry
if barstate.isconfirmed and inLong
    // profitPct already computed vs avg above
    peakProfitPct := na(peakProfitPct) ? profitPct : math.max(peakProfitPct, profitPct)
    armed = peakProfitPct >= armProfitPct
    retraceFromPeakPct = peakProfitPct - profitPct
    if armed and retraceFromPeakPct >= peakRetracePct
        strategy.close("Long")

    // Configurable stop loss
    if enableSL
        stopPrice = avg * (1.0 - stopLossPct / 100.0)
        strategy.exit("SL-Long", from_entry="Long", stop=stopPrice)

    // Time-in-loss limiter
    if maxHoursInLoss > 0 and avg > 0 and close < avg and not na(entryOpenTimeMs)
        lossDurationMs = time - entryOpenTimeMs
        limitMs = maxHoursInLoss * 60 * 60 * 1000
        if lossDurationMs >= limitMs
            strategy.close("Long")

// Cancel pending entries on expiry
if barstate.isconfirmed
    if retestOrderActive and not na(retestExpiresBar) and bar_index >= retestExpiresBar
        strategy.cancel("Long")
        retestOrderActive := false
        retestExpiresBar := na
        retestPrice := na
        // Auto-mode fallback to ATR stop after retest timeout
        if entryMode == EntryMode.Auto and not stopOrderActive and canEnter and (noPos or (inLong and canPyramid))
            stopTriggerPrice := dumpCandleHigh + (autoAtrStopMult * autoAtr)
            strategy.entry("Long", strategy.long, stop=stopTriggerPrice)
            stopOrderActive := true
            stopExpiresBar := bar_index + autoStopExpiryBars
    if stopOrderActive and not na(stopExpiresBar) and bar_index >= stopExpiresBar
        strategy.cancel("Long")
        stopOrderActive := false
        stopExpiresBar := na
        stopTriggerPrice := na

Overview

DP2 can enter through classic, two-close, break-and-retest, ATR-stop, auto, or rebound timing. A trade arms a peak-trail exit after a set profit and closes on a retrace; stop loss is off by default. Resistance proximity, a BTC four-hour uptrend filter, and a post-trade cooldown can block entries.

Details

Published by

grinay on GitHub

Type

Strategy, Pine Script v6

Draws

On the price chart

Category

Mean reversion, Volatility & bands, Support & resistance

Markets

Crypto

Timeframes

M1-M5, M15-H1

Risk Notes

  • The strategy enters trades without a stop loss

Topics

algorithmic-tradingbreakout-strategybybitcrypto-tradingcryptocurrencymomentum-strategypine-scripttrading-bottradingviewwebhook

Settings

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

SettingTypeDefaultVariable
FromsourceopenfromSource
TosourceclosetoSource
Anomaly Threshold (%)float1.0anomalyThresholdPct
Days Lookback for Low Break (0=disabled)int7daysBreakLen
Dump Cap (%) - ignore dumps larger than thisfloat2.0dumpCapPct
Arm Exit After Profit %float5.0armProfitPct
Exit on Peak Retrace %float1.5peakRetracePct
Enable Stop LossboolfalseenableSL
Stop Loss % (from avg)float15.0stopLossPct
Max Hours In Loss (0=disabled)int0maxHoursInLoss
Resistance Lookback (days)int3resistLookbackDays
Block entries within % of resistancefloat1.0resistProximityPct
Show all 30 settings
SettingTypeDefaultVariable
Entry ModeenumEntryMode.ATRStopentryMode
Closes Above Levelint2twoClosesBars
ATR Lengthint14brAtrLen
Retest Offset ATR Multfloat0.2retestOffsetAtr
Retest Order Expiry Barsint8retestExpiryBars
ATR Lengthint14atrStopAtrLen
ATR Stop Bufferfloat0.5atrStopMult
Stop Order Expiry Barsint12stopExpiryBars
ATR Lengthint14autoAtrLen
Auto SensitivitystringMediumautoSensitivity
Retest Order Expiry Barsint8autoRetestExpiryBars
Stop Order Expiry Barsint12autoStopExpiryBars
Flat Lookback Barsint5flatLookbackBars
Max Flat Range %float0.4maxFlatRangePct
Min Rebound From Low %float0.8minReboundFromLowPct
Cooldown Hours After Tradeint4cooldownHours
Require BTC uptrend (last 4h)booltrueuseBtcTrendFilter
BTC Perpetual Trend SymbolsymbolBYBIT:BTCUSDT.PbtcTrendSymbol
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

Buy The Dip

by hasnocool
106 stars

Calculates a historical dip level based on the lowest low over a specified lookback period. It enters a long strategy position when the current bar close falls below the previous dip level. Positions are managed with take profit and stop loss exits placed at f

Strategy v4 Mean reversionVolatility & bands
Updated 2 years ago · GPL-3.0 GitHub repository of Buy The Dip
3 stars

This strategy combines two sets of Bollinger Bands with a Laguerre RSI oscillator plotted on the price chart. It generates buy and sell signals when price touches the upper or lower bands or zones. The strategy allows enabling or disabling entries and includes

Strategy v5 Mean reversionVolatility & bands
Updated 5 months ago · None stated GitHub repository of BB + LaRSI Strategy

3C QFL Mean Reversal

by wynnforthework
2 stars

This strategy implements a Quickfinger Luc mean-reversion trading model tailored for automated execution. It calculates base support levels and volume moving averages across timeframes to identify sharp price dips below established base levels. The script incl

Strategy v5 Mean reversionSupport & resistance Repaints
Updated 1 year ago · None stated GitHub repository of 3C QFL Mean Reversal

Bollinger Bands

by Studentcodee
1 stars

This strategy plots Bollinger Bands directly on the price chart. It computes the middle band as the simple moving average of the close price over the input length and the upper and lower bands by multiplying the standard deviation by the multiplier and adding

Strategy v6 Volatility & bandsMoving averages
Updated 1 year ago · None stated GitHub repository of Bollinger Bands

ES Professional Fade

by knectardev
1 stars

The ES Professional Fade strategy trades overnight price gaps during regular trading hours on intraday charts. It establishes an opening range and enters counter-trend positions when price fails near overnight extremes. Positions target the previous day's clos

Strategy v5 Mean reversionSessions & time
Updated 9 months ago · None stated GitHub repository of ES Professional Fade
1 stars

The strategy detects range expansion by comparing the previous bar's range to its ATR multiple. It places pending stop entries above highs of up bars or below lows of down bars. On trigger it enters positions with take profit and stop loss in ticks using OCA g

Strategy v6 Volatility & bandsSupport & resistance
Updated 5 days ago · Apache-2.0 GitHub repository of PF probe 97 - tp/sl gap reversal oca

More strategys

Trade Your Charts With a Regulated Broker

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

Compare brokers