Indicator · Pine Script v6 · On the chart

Delta Break and Retest Stage 6 Retest and Trigger

by rosijay17-cmd

An indicator meant for a 5-minute chart that recomputes a simplified 15-minute break of session delta levels, then watches a retest of the broken level within a set number of hours and flags a trigger candle, optionally needing delta confirmation.

View on GitHub

Repaints

GitHub stars1on the repository
Pine versionv6current
Lines of code767
Settings28inputs
LicenceGPL-3.0
Last update7 days ago22 Oct 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 indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

767 lines of Pine Script v6 from delta_break_retest/Stage6_RetestTrigger.pine, published under GPL-3.0. Keep the author's credit and licence lines when you reuse it.

Stage6_RetestTrigger.pine
//@version=6

// ============================================================================
// DELTA BREAK & RETEST -- STAGE 6: 5m RETEST & TRIGGER
// ----------------------------------------------------------------------------
// Per the build spec (Section 9), Stage 6: 5m execution timeframe -- watch a
// recently-broken 15m level for a retest, and flag the specific candle that
// confirms (or invalidates) it. Still no orders -- this stage plots/logs a
// TRIGGER event, it does not place a trade. Orders/risk is Stage 7.
//
// RUN THIS ON A 5m CHART. Per Section 2's hierarchy: 15m owns setup/POI/
// break (Stage 5); 5m owns execution/retest/trigger (this stage). The
// dashboard's Chart TF row checks for "5", not "15" -- that's intentional,
// not a leftover from Stage 5.
//
// ARCHITECTURE (read before touching this file): Stage 6 does NOT try to
// read Stage 5's live POI registry -- Pine indicators can't share state
// across separate script instances that way. Instead, per the same
// cross-timeframe idiom already proven in this repo (Stage 4/5's
// f_get4hContext()/f_get1hContext()), Stage 6 recomputes a SIMPLIFIED
// version of the 15m break logic fresh, inline, pulled via
// request.security() from whatever chart this indicator is actually
// applied to.
//
// DELIBERATE SIMPLIFICATION vs. Stage 5's POI registry (scope discipline --
// validate the retest/trigger MECHANICS first, on the simplest case, before
// building back the fuller registry semantics):
//   - No contamination counter, no freshness expiry, no eviction, no cap.
//   - Only the CURRENT (most recently extracted) price per POI type is
//     tracked -- five independent slots (POC, Extreme+, Extreme-, Flip,
//     Absorption), not a rotating multi-entry registry. A new session's
//     extraction for a given type simply replaces that type's old level,
//     whether or not the old one ever broke.
//   - Only the MOST RECENT break across all five types is surfaced as the
//     active retest candidate (via ta.valuewhen, same idiom as the pivot
//     fix). If two types break on the exact same 15m bar, POC > Extreme+ >
//     Extreme- > Flip > Absorption wins the tie -- a known, documented
//     limitation, not a bug, and fine for validating the state machine.
//
// KNOWN RISK, flagged plainly per this repo's own established practice
// (same spirit as Stage 2's UDT warning): this is the first stage to
// combine per-bar `var` mutation with a request.security() pull returning
// a 5-value tuple. The individual pieces (var state inside a security-
// pulled function; ta.valuewhen + lookahead_on + per-value [1] offset for a
// multi-value tuple) are each independently proven elsewhere in this repo
// (Stage 4/5's MTF context, Stage 3's CooldownModule's var-state discipline),
// but not previously combined exactly this way. If it throws a compile
// error, send the exact message.
//
// DROPPED from this stage (deliberately, scope discipline -- this stage's
// job is retest/trigger, not re-polishing Stage 5's already-validated POI
// visualization): the price-binned box histogram. Stage 5 still has it, on
// its own 15m chart tab, if you want to reference both side by side. The
// Buyers/Sellers strength bar (native, no cross-timeframe pull needed) is
// carried forward unchanged since it's cheap and orthogonal to this.
//
// STILL NO TRADING LOGIC -- indicator only. A TRIGGERED state is a flagged
// candle, not an order. Orders/risk (position sizing, stop placement,
// actual strategy.entry calls) is Stage 7, not here.
// ============================================================================

indicator(title = "Delta Break & Retest - Stage 6 Retest & Trigger", shorttitle = "DBR Stage6", overlay = true, max_boxes_count = 50, max_labels_count = 100)

// ============================================================================
// PURE DELTA FUNCTION (unchanged from Stage 1)
// ============================================================================

f_delta(float o, float h, float l, float c, float v) =>
    float safeRange = math.max(h - l, syminfo.mintick)
    float pressure = math.max(math.min((c - o) / safeRange, 1.0), -1.0)
    v * pressure

float barDelta = f_delta(open, high, low, close, nz(volume, 0.0))

// ============================================================================
// INPUTS
// ============================================================================

string breakContextTimeframeInput = input.timeframe("15", "15m Break-Context Timeframe", group = "15m Break Context", tooltip = "The timeframe Stage 5's POI/break logic normally runs on. Recomputed fresh here (simplified, see header) and pulled into this 5m script.")
int profileRowsInput = input.int(40, "Profile Rows (Extraction Granularity)", minval = 10, maxval = 120, group = "15m Break Context")
float poiAtrMultInput = input.float(0.25, "POI Zone Half-Width (x ATR)", minval = 0.05, maxval = 2.0, step = 0.05, group = "15m Break Context")
int minEndedSessionBarsInput = input.int(10, "Minimum Bars To Finalize A Session", minval = 3, maxval = 100, group = "15m Break Context")
bool showAbsorptionInput = input.bool(true, "Track Absorption Node", group = "15m Break Context")
float minBreakAtrMultInput = input.float(0.10, "Minimum Break-Through Buffer (x ATR)", minval = 0.0, maxval = 1.0, step = 0.05, group = "15m Break Context")
bool requireBreakDeltaConfirmationInput = input.bool(true, "Require Delta Confirmation On 15m Break Bar", group = "15m Break Context")

float retestWindowHoursInput = input.float(12.0, "Retest Window (Hours)", minval = 0.5, maxval = 168.0, step = 0.5, group = "Retest / Trigger", tooltip = "How long a broken level stays eligible for a retest before it's marked EXPIRED. Measured from the 15m break bar's own timestamp, not from bar count -- comparable across the 15m break context and this 5m chart.")
bool requireTriggerDeltaConfirmationInput = input.bool(true, "Require Delta Confirmation On 5m Trigger Bar", group = "Retest / Trigger")
color retestZoneColorInput = input.color(color.yellow, "Active Retest Zone Color", group = "Retest / Trigger")
color triggerUpColorInput = input.color(color.lime, "Trigger Up Color", group = "Retest / Trigger")
color triggerDownColorInput = input.color(color.red, "Trigger Down Color", group = "Retest / Trigger")

string biasTimeframeInput = input.timeframe("240", "4H Bias Timeframe", group = "MTF Context (Tag Only)")
int pivotLeftInput = input.int(3, "Swing Pivot Left Bars", minval = 1, maxval = 20, group = "MTF Context (Tag Only)")
int pivotRightInput = input.int(3, "Swing Pivot Right Bars", minval = 1, maxval = 20, group = "MTF Context (Tag Only)")
int biasEmaLengthInput = input.int(50, "4H EMA Length", minval = 2, group = "MTF Context (Tag Only)")
int biasSlopeLookbackInput = input.int(5, "4H EMA Slope Lookback (Bars)", minval = 1, maxval = 50, group = "MTF Context (Tag Only)")
string trendTimeframeInput = input.timeframe("60", "1H Trend Timeframe", group = "MTF Context (Tag Only)")
int adxLengthInput = input.int(14, "1H ADX / DI Length", minval = 1, group = "MTF Context (Tag Only)")
float adxMinimumInput = input.float(20.0, "1H Minimum ADX", minval = 0, step = 0.5, group = "MTF Context (Tag Only)")
int fastEmaInput = input.int(21, "1H Fast EMA", minval = 1, group = "MTF Context (Tag Only)")
int slowEmaInput = input.int(50, "1H Slow EMA", minval = 2, group = "MTF Context (Tag Only)")
int erLengthInput = input.int(10, "1H Efficiency Ratio Length", minval = 2, group = "MTF Context (Tag Only)")
float erMinimumInput = input.float(0.30, "1H Minimum Efficiency Ratio", minval = 0, maxval = 1, step = 0.05, group = "MTF Context (Tag Only)")

int deltaStrengthWindowInput = input.int(20, "Buyer/Seller Strength Window (Bars)", minval = 5, maxval = 200, group = "Signal Summary")
color buyColorInput = input.color(color.teal, "Buyer Strength Color", group = "Signal Summary")
color sellColorInput = input.color(color.orange, "Seller Strength Color", group = "Signal Summary")
bool showDiagnosticsInput = input.bool(false, "Show Detailed Diagnostics", group = "Signal Summary", tooltip = "Off by default -- adds Chart TF, active retest zone bounds/type, and time-since-break to the dashboard.")

// ============================================================================
// SESSION PROFILE EXTRACTION -- same algorithm as Stage 2/5's
// f_extractSessionPois(), unchanged. Called from inside the 15m break-
// context function below.
// ============================================================================

f_extractSessionPois(int barsBack, int rows) =>
    float scanHigh = -1.0e20
    float scanLow = 1.0e20

    for i = 1 to barsBack
        scanHigh := math.max(scanHigh, high[i])
        scanLow := math.min(scanLow, low[i])

    float rowStep = math.max((scanHigh - scanLow) / rows, syminfo.mintick)

    array<float> rowDelta = array.new_float(rows, 0.0)
    array<float> rowRange = array.new_float(rows, 0.0)
    array<float> rowVolume = array.new_float(rows, 0.0)

    for i = 1 to barsBack
        float d = f_delta(open[i], high[i], low[i], close[i], nz(volume[i], 0.0))
        int row = int(math.floor((close[i] - scanLow) / rowStep))
        row := math.max(0, math.min(rows - 1, row))
        array.set(rowDelta, row, array.get(rowDelta, row) + d)
        array.set(rowRange, row, array.get(rowRange, row) + (high[i] - low[i]))
        array.set(rowVolume, row, array.get(rowVolume, row) + nz(volume[i], 0.0))

    float maxAbs = -1.0
    int pocRow = -1

    for row = 0 to rows - 1
        float d = array.get(rowDelta, row)
        if math.abs(d) > maxAbs
            maxAbs := math.abs(d)
            pocRow := row

    float pocPrice = pocRow >= 0 ? scanLow + (pocRow + 0.5) * rowStep : na

    float maxPos = 0.0
    int maxPosRow = -1
    float maxNeg = 0.0
    int maxNegRow = -1

    for row = 0 to rows - 1
        float d = array.get(rowDelta, row)
        if d > maxPos
            maxPos := d
            maxPosRow := row
        if d < maxNeg
            maxNeg := d
            maxNegRow := row

    float extremeHighPrice = maxPosRow >= 0 ? scanLow + (maxPosRow + 0.5) * rowStep : na
    float extremeLowPrice = maxNegRow >= 0 ? scanLow + (maxNegRow + 0.5) * rowStep : na

    float cum = 0.0
    float prevCum = 0.0
    float flipPrice = na

    for row = 0 to rows - 1
        prevCum := cum
        cum := cum + array.get(rowDelta, row)
        if row > 0 and ((prevCum <= 0 and cum > 0) or (prevCum >= 0 and cum < 0))
            flipPrice := scanLow + row * rowStep

    float totalVol = 0.0
    for row = 0 to rows - 1
        totalVol += array.get(rowVolume, row)
    float avgVol = totalVol / rows

    float bestRatio = -1.0
    int absorptionRow = -1

    for row = 0 to rows - 1
        if row != pocRow and array.get(rowVolume, row) > avgVol * 0.5
            float rng = array.get(rowRange, row)
            float ratio = rng > 0 ? math.abs(array.get(rowDelta, row)) / rng : 0.0
            if ratio > bestRatio
                bestRatio := ratio
                absorptionRow := row

    float absorptionPrice = absorptionRow >= 0 ? scanLow + (absorptionRow + 0.5) * rowStep : na

    [pocPrice, extremeHighPrice, extremeLowPrice, flipPrice, absorptionPrice]

// ============================================================================
// 15m BREAK CONTEXT -- simplified (see header): five independent "current
// level" slots, one break check each per bar, only the MOST RECENT break
// across all five surfaced via ta.valuewhen. Returns
// [zoneLow, zoneHigh, direction, typeCode, eventTimeMs], each offset [1]
// individually before return (multi-value tuple -- see the
// pine-proven-patterns skill's CE10123 note; the outer request.security
// call must NOT also apply [1]).
// direction: 1.0 = broke up, -1.0 = broke down, 0.0 = no event this bar.
// typeCode: 1=POC, 2=Extreme+, 3=Extreme-, 4=Flip, 5=Absorption.
// ============================================================================

f_get15mBreakContextRaw() =>
    float atr15Local = ta.atr(14)
    bool newSessionLocal = ta.change(time("D")) != 0
    var int sessionStartBarLocal = bar_index
    var int sessionIndexLocal = 0
    int barsIntoSessionLocal = bar_index - sessionStartBarLocal + 1

    var float pocLevel = na
    var bool pocBroken = false
    var float exHighLevel = na
    var bool exHighBroken = false
    var float exLowLevel = na
    var bool exLowBroken = false
    var float flipLevel = na
    var bool flipBroken = false
    var float absLevel = na
    var bool absBroken = false

    // DIAGNOSTIC (temporary, second probe): sessionIndexLocal proved var
    // state advances fine, but Active Zone/Hours Since Break stayed dead --
    // so the break somewhere between "extraction runs" and "an event
    // reaches the outer script" is failing, not var-state-in-security
    // generally. levelSetCount answers "does extraction ever actually
    // produce a level" and breakEventCount answers "does the break
    // condition itself ever actually fire" -- narrows which of those two
    // links in the chain is broken.
    var int levelSetCount = 0
    var int breakEventCount = 0

    // Declared here (not var, so still reset to the "no event" default
    // every bar) so they're visible both to the mutation logic below AND
    // to the ta.valuewhen calls that read them afterward.
    // FIX (round 4): rounds 1-3 all failed, including round 3's raw
    // (non-valuewhen) signal -- confirmed on-device running [r3], Break
    // Events climbed to a real, growing count (proving events genuinely
    // fire) while Active Zone stayed dead regardless. Re-examined every
    // WORKING signal in this whole investigation side by side: ph/pl (the
    // proven pivot pattern) default to na; close > open's valuewhen source
    // effectively rode on a dense boolean, but the na-vs-0.0 asymmetry
    // stood out as the one structural difference nothing else has tested
    // in isolation. eventDirection/eventTypeCode were the only signals in
    // this whole file using a real number (0.0) as their "nothing
    // happened" baseline instead of na -- every proven-working signal uses
    // na. Switched both to na, matching the pivot pattern exactly.
    float eventDirection = na
    float eventTypeCode = na
    float eventZoneLow = na
    float eventZoneHigh = na

    // HISTORY: round 2 temporarily removed the "and not pocBroken" guard
    // (etc.) from these conditions as a hypothesis test -- it wasn't the
    // actual bug (round 6 found the real one, an unsafe na comparison in
    // the outer script's event-adoption logic, nothing to do with these
    // guards at all). Confirmed on [r6] on-device: with the guard removed,
    // now-working retest detection fired on nearly every bar, since a
    // level chopping back and forth re-fired a "break" on every fresh
    // crossing. Restored the *Broken guards below -- a level only counts
    // as broken once per session, matching Stage 5's original design and
    // producing meaningful, sparse events instead of chop noise.
    if newSessionLocal
        if bar_index > 0 and barsIntoSessionLocal > 1
            int endedBarsLocal = bar_index - sessionStartBarLocal
            if endedBarsLocal >= minEndedSessionBarsInput and not na(atr15Local)
                [pocPx, exHighPx, exLowPx, flipPx, absPx] = f_extractSessionPois(endedBarsLocal, profileRowsInput)
                if not na(pocPx)
                    pocLevel := pocPx
                    pocBroken := false
                    levelSetCount += 1
                if not na(exHighPx)
                    exHighLevel := exHighPx
                    exHighBroken := false
                    levelSetCount += 1
                if not na(exLowPx)
                    exLowLevel := exLowPx
                    exLowBroken := false
                    levelSetCount += 1
                if not na(flipPx)
                    flipLevel := flipPx
                    flipBroken := false
                    levelSetCount += 1
                if showAbsorptionInput and not na(absPx)
                    absLevel := absPx
                    absBroken := false
                    levelSetCount += 1
        sessionIndexLocal += 1
        sessionStartBarLocal := bar_index

    float localBarDelta = f_delta(open, high, low, close, nz(volume, 0.0))
    float buffer = not na(atr15Local) ? atr15Local * minBreakAtrMultInput : 0.0
    float halfWidth = not na(atr15Local) ? atr15Local * poiAtrMultInput : na
    bool deltaOkUp = not requireBreakDeltaConfirmationInput or localBarDelta > 0
    bool deltaOkDown = not requireBreakDeltaConfirmationInput or localBarDelta < 0

    if not na(pocLevel) and not pocBroken and not na(halfWidth)
        float zLow = pocLevel - halfWidth
        float zHigh = pocLevel + halfWidth
        bool brokeUp = close > zHigh + buffer and close[1] <= zHigh + buffer
        bool brokeDown = close < zLow - buffer and close[1] >= zLow - buffer
        if brokeUp and deltaOkUp
            pocBroken := true
            eventDirection := 1.0
            eventTypeCode := 1.0
            eventZoneLow := zLow
            eventZoneHigh := zHigh
        else if brokeDown and deltaOkDown
            pocBroken := true
            eventDirection := -1.0
            eventTypeCode := 1.0
            eventZoneLow := zLow
            eventZoneHigh := zHigh

    if not na(exHighLevel) and not exHighBroken and not na(halfWidth)
        float zLow = exHighLevel - halfWidth
        float zHigh = exHighLevel + halfWidth
        bool brokeUp = close > zHigh + buffer and close[1] <= zHigh + buffer
        bool brokeDown = close < zLow - buffer and close[1] >= zLow - buffer
        if brokeUp and deltaOkUp
            exHighBroken := true
            eventDirection := 1.0
            eventTypeCode := 2.0
            eventZoneLow := zLow
            eventZoneHigh := zHigh
        else if brokeDown and deltaOkDown
            exHighBroken := true
            eventDirection := -1.0
            eventTypeCode := 2.0
            eventZoneLow := zLow
            eventZoneHigh := zHigh

    if not na(exLowLevel) and not exLowBroken and not na(halfWidth)
        float zLow = exLowLevel - halfWidth
        float zHigh = exLowLevel + halfWidth
        bool brokeUp = close > zHigh + buffer and close[1] <= zHigh + buffer
        bool brokeDown = close < zLow - buffer and close[1] >= zLow - buffer
        if brokeUp and deltaOkUp
            exLowBroken := true
            eventDirection := 1.0
            eventTypeCode := 3.0
            eventZoneLow := zLow
            eventZoneHigh := zHigh
        else if brokeDown and deltaOkDown
            exLowBroken := true
            eventDirection := -1.0
            eventTypeCode := 3.0
            eventZoneLow := zLow
            eventZoneHigh := zHigh

    if not na(flipLevel) and not flipBroken and not na(halfWidth)
        float zLow = flipLevel - halfWidth
        float zHigh = flipLevel + halfWidth
        bool brokeUp = close > zHigh + buffer and close[1] <= zHigh + buffer
        bool brokeDown = close < zLow - buffer and close[1] >= zLow - buffer
        if brokeUp and deltaOkUp
            flipBroken := true
            eventDirection := 1.0
            eventTypeCode := 4.0
            eventZoneLow := zLow
            eventZoneHigh := zHigh
        else if brokeDown and deltaOkDown
            flipBroken := true
            eventDirection := -1.0
            eventTypeCode := 4.0
            eventZoneLow := zLow
            eventZoneHigh := zHigh

    if showAbsorptionInput and not na(absLevel) and not absBroken and not na(halfWidth)
        float zLow = absLevel - halfWidth
        float zHigh = absLevel + halfWidth
        bool brokeUp = close > zHigh + buffer and close[1] <= zHigh + buffer
        bool brokeDown = close < zLow - buffer and close[1] >= zLow - buffer
        if brokeUp and deltaOkUp
            absBroken := true
            eventDirection := 1.0
            eventTypeCode := 5.0
            eventZoneLow := zLow
            eventZoneHigh := zHigh
        else if brokeDown and deltaOkDown
            absBroken := true
            eventDirection := -1.0
            eventTypeCode := 5.0
            eventZoneLow := zLow
            eventZoneHigh := zHigh

    if not na(eventDirection)
        breakEventCount += 1

    // FIX (round 3): rounds 1 (barstate.isconfirmed) and 2 (remove the
    // Broken-flag dependency) BOTH failed -- confirmed on-device, Break
    // Events climbed to 616 (proving the round-2 change took effect and
    // events fire constantly) while Active Zone/Hours Since Break stayed
    // dead regardless. Two rounds of hypotheses about WHY ta.valuewhen
    // won't surface this signal have now both been falsified. Rather than
    // keep guessing at ta.valuewhen's behavior in this exact shape,
    // sidestep it: don't aggregate/forward-fill inside this
    // security-pulled function at all. Return the RAW, mostly-zero
    // per-bar signal instead (0.0 / na on non-event bars, a real value
    // only on the specific 15m bar an event happens), and let the outer
    // 5m script do the "remember the last real event" job with its own
    // var state -- which has worked reliably all session (Stage 3's
    // CooldownModule, this file's own retest state machine). gaps_off
    // will naturally repeat one 15m bar's raw value across its 3
    // overlapping 5m bars; the outer script's existing
    // "breakEventTime != activeEventTime" check already de-duplicates
    // that correctly without any change needed there.
    //
    // sanityLatestTime (ta.valuewhen-based) is kept as an ONGOING
    // reference point, not because it's needed for anything now, but so
    // the dashboard keeps proving ta.valuewhen itself still works in
    // general even while this function stops relying on it for the real
    // signal.
    float sanityLatestTime = ta.valuewhen(close > open, float(time), 0)

    [eventZoneLow, eventZoneHigh, eventDirection, eventTypeCode, float(time), float(sessionIndexLocal), float(levelSetCount), float(breakEventCount), sanityLatestTime]

// FIX (round 5): rounds 1-4 all failed despite each targeting a real,
// falsifiable hypothesis. Found something structurally different by
// reading an actual proven MTF zone script already in this repo
// (Supply_and_Demand_Zones_XL.pine's f_structure_series() /
// f_structure_series_confirmed() pair) rather than continuing to guess:
// that script NEVER applies [1] to a local variable still sitting in the
// same scope as the mutation logic that produced it. It computes
// everything in a RAW function with no offset at all, then a SEPARATE
// wrapper function CALLS that raw function, destructures its returned
// tuple into brand-new local bindings, and only THEN applies [1] to
// those fresh bindings, in a completely different function scope. Every
// one of my four previous rounds applied [1] directly to variables still
// in the same scope as 5 nested if-blocks' worth of mutation. Replicating
// that exact two-layer split here.
f_get15mBreakContext() =>
    [zoneLow, zoneHigh, direction, typeCode, eventTime, sessCount, lvlCount, evtCount, sanityTime] = f_get15mBreakContextRaw()
    [zoneLow[1], zoneHigh[1], direction[1], typeCode[1], eventTime[1], sessCount[1], lvlCount[1], evtCount[1], sanityTime[1]]

[breakZoneLow, breakZoneHigh, breakDirectionRaw, breakTypeCodeRaw, breakEventTimeRaw, breakDiagSessionCountRaw, breakDiagLevelSetCountRaw, breakDiagEventCountRaw, breakDiagSanityTimeRaw] = request.security(syminfo.tickerid, breakContextTimeframeInput, f_get15mBreakContext(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on)
int breakDiagSessionCount = na(breakDiagSessionCountRaw) ? na : int(breakDiagSessionCountRaw)
int breakDiagLevelSetCount = na(breakDiagLevelSetCountRaw) ? na : int(breakDiagLevelSetCountRaw)
int breakDiagEventCount = na(breakDiagEventCountRaw) ? na : int(breakDiagEventCountRaw)
int breakDiagSanityTime = na(breakDiagSanityTimeRaw) ? na : int(breakDiagSanityTimeRaw)
int breakDiagSanityHoursAgo = na(breakDiagSanityTime) ? na : int((time - breakDiagSanityTime) / (60 * 60 * 1000))

// breakDirectionRaw/breakTypeCodeRaw are now the RAW per-bar signal (0.0 on
// most pulls, a real value only on the specific bar an event happened) --
// not a continuously forward-filled "last known event" the way the
// ta.valuewhen version was. haveBrokenAny below means "did THIS pull
// carry a real event," not "has one ever happened by now."
bool haveBrokenAny = not na(breakDirectionRaw)
int breakDirection = haveBrokenAny ? int(breakDirectionRaw) : 0
int breakTypeCode = haveBrokenAny ? int(breakTypeCodeRaw) : 0
int breakEventTime = haveBrokenAny ? int(breakEventTimeRaw) : na

f_typeCodeName(int code) =>
    code == 1 ? "Delta POC" : code == 2 ? "Delta Extreme +" : code == 3 ? "Delta Extreme -" : code == 4 ? "Delta Flip" : code == 5 ? "Absorption Node" : "?"

// ============================================================================
// 4H / 1H MTF CONTEXT -- carried forward unchanged from Stage 4/5, confirmed
// working on-device. Used for the Bias/Regime/Score dashboard rows and to
// tag a trigger as bias-aligned or not.
// ============================================================================

f_get4hContext() =>
    float ph = ta.pivothigh(high, pivotLeftInput, pivotRightInput)
    float pl = ta.pivotlow(low, pivotLeftInput, pivotRightInput)

    float latestHigh = ta.valuewhen(not na(ph), ph, 0)
    float previousHigh = ta.valuewhen(not na(ph), ph, 1)
    float latestLow = ta.valuewhen(not na(pl), pl, 0)
    float previousLow = ta.valuewhen(not na(pl), pl, 1)

    float ema = ta.ema(close, biasEmaLengthInput)
    bool aboveEma = close > ema
    bool slopeUp = ema > ema[biasSlopeLookbackInput]
    bool slopeDown = ema < ema[biasSlopeLookbackInput]

    [latestHigh[1], previousHigh[1], latestLow[1], previousLow[1], aboveEma[1], slopeUp[1], slopeDown[1]]

[lastPivotHigh, prevPivotHigh, lastPivotLow, prevPivotLow, closeAboveEma4h, emaSlopeUp4h, emaSlopeDown4h] = request.security(syminfo.tickerid, biasTimeframeInput, f_get4hContext(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on)

bool haveEnoughPivots = not na(lastPivotHigh) and not na(prevPivotHigh) and not na(lastPivotLow) and not na(prevPivotLow)
bool higherHigh = haveEnoughPivots and lastPivotHigh > prevPivotHigh
bool lowerHigh = haveEnoughPivots and lastPivotHigh < prevPivotHigh
bool higherLow = haveEnoughPivots and lastPivotLow > prevPivotLow
bool lowerLow = haveEnoughPivots and lastPivotLow < prevPivotLow
bool structureUp = higherHigh and higherLow
bool structureDown = lowerHigh and lowerLow
bool slowFilterUp = closeAboveEma4h and emaSlopeUp4h
bool slowFilterDown = (not closeAboveEma4h) and emaSlopeDown4h
string bias = structureUp and slowFilterUp ? "LONG_ONLY" : structureDown and slowFilterDown ? "SHORT_ONLY" : "NO_TRADE"

f_efficiencyRatio(int length) =>
    float netMove = math.abs(close - close[length])
    float sumMoves = 0.0
    for i = 0 to length - 1
        sumMoves += math.abs(close[i] - close[i + 1])
    sumMoves > 0 ? netMove / sumMoves : 0.0

f_get1hContext() =>
    [plusDi, minusDi, adx] = ta.dmi(adxLengthInput, adxLengthInput)
    float emaFast = ta.ema(close, fastEmaInput)
    float emaSlow = ta.ema(close, slowEmaInput)
    float er = f_efficiencyRatio(erLengthInput)
    [adx[1], emaFast[1], emaSlow[1], er[1]]

[adx1h, emaFast1h, emaSlow1h, er1h] = request.security(syminfo.tickerid, trendTimeframeInput, f_get1hContext(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on)

bool adxOk = adx1h >= adxMinimumInput
bool erOk = er1h >= erMinimumInput
bool emaStackBullish1h = emaFast1h > emaSlow1h
bool emaStackBearish1h = emaFast1h < emaSlow1h

// ============================================================================
// RETEST / TRIGGER STATE MACHINE (5m, native chart bars)
// ----------------------------------------------------------------------------
// A new 15m break event (breakEventTime changes) always adopts as the new
// active candidate, overriding whatever the previous one was doing -- only
// one candidate tracked at a time (see header, same simplification as the
// break context above). Retest window is time-based (hours since the break
// bar's own timestamp), not a bar-count countdown, per the cooldown
// module's proven no-countdown-variable discipline (Stage 3) -- comparable
// across the 15m break context and this 5m chart precisely because it's
// wall-clock time, not bar index.
// ============================================================================

var float activeZoneLow = na
var float activeZoneHigh = na
var int activeDirection = 0
var int activeTypeCode = 0
var int activeEventTime = na
var string retestState = "NONE"
var array<string> retestLog = array.new_string()

// FIX (round 6): rounds 1-5 all targeted the security-pull layer and all
// failed -- confirmed on-device through [r5], with 616+ real break events
// proven firing inside the pulled function the whole time. The actual bug
// was never there: it was HERE. Comparing a real value against a variable
// that starts as na using != is a classic Pine footgun -- na comparisons
// with ==/!= don't reliably behave like normal comparisons; na(x)/not na(x)
// is the only safe check. activeEventTime starts as na (line above), so on
// the very first break event ever, "breakEventTime != activeEventTime"
// most likely evaluated to na (falsy) instead of true -- meaning
// newBreakEvent never fired on that first event, activeEventTime stayed na
// forever, and therefore EVERY subsequent event hit the exact same
// still-na comparison, permanently. This explains why five rounds of
// fixing the security layer changed nothing: that layer was fine.
bool newBreakEvent = breakDirection != 0 and (na(activeEventTime) or breakEventTime != activeEventTime)

if newBreakEvent
    activeZoneLow := breakZoneLow
    activeZoneHigh := breakZoneHigh
    activeDirection := breakDirection
    activeTypeCode := breakTypeCode
    activeEventTime := breakEventTime
    retestState := "WATCHING"
    array.unshift(retestLog, "bar" + str.tostring(bar_index) + " NEW " + f_typeCodeName(breakTypeCode) + " " + (breakDirection == 1 ? "UP" : "DOWN"))
    if array.size(retestLog) > 8
        array.pop(retestLog)

int hoursSinceEvent = na(activeEventTime) ? na : int((time - activeEventTime) / (60 * 60 * 1000))
bool windowExpired = not na(activeEventTime) and hoursSinceEvent > retestWindowHoursInput

if retestState == "WATCHING" or retestState == "RETESTED"
    if windowExpired
        retestState := "EXPIRED"
        array.unshift(retestLog, "bar" + str.tostring(bar_index) + " EXPIRED " + f_typeCodeName(activeTypeCode))
        if array.size(retestLog) > 8
            array.pop(retestLog)
    else
        bool touchingZone = high >= activeZoneLow and low <= activeZoneHigh
        if touchingZone and retestState == "WATCHING"
            retestState := "RETESTED"

        if retestState == "RETESTED"
            bool triggerOkUp = not requireTriggerDeltaConfirmationInput or barDelta > 0
            bool triggerOkDown = not requireTriggerDeltaConfirmationInput or barDelta < 0
            bool triggerUp = activeDirection == 1 and close > activeZoneHigh and triggerOkUp
            bool triggerDown = activeDirection == -1 and close < activeZoneLow and triggerOkDown
            bool invalidateUp = activeDirection == 1 and close < activeZoneLow
            bool invalidateDown = activeDirection == -1 and close > activeZoneHigh

            if triggerUp or triggerDown
                retestState := "TRIGGERED"
                bool aligned = (triggerUp and bias == "LONG_ONLY") or (triggerDown and bias == "SHORT_ONLY")
                label.new(bar_index, triggerUp ? activeZoneHigh : activeZoneLow, (triggerUp ? "▲▲ " : "▼▼ ") + f_typeCodeName(activeTypeCode) + " RETEST", style = triggerUp ? label.style_label_up : label.style_label_down, color = color.new(triggerUp ? triggerUpColorInput : triggerDownColorInput, aligned ? 0 : 55), textcolor = color.white, size = size.small)
                array.unshift(retestLog, "bar" + str.tostring(bar_index) + " TRIGGERED " + f_typeCodeName(activeTypeCode) + " " + (triggerUp ? "UP" : "DOWN") + (aligned ? " (aligned)" : ""))
                if array.size(retestLog) > 8
                    array.pop(retestLog)
            else if invalidateUp or invalidateDown
                retestState := "INVALIDATED"
                array.unshift(retestLog, "bar" + str.tostring(bar_index) + " INVALIDATED " + f_typeCodeName(activeTypeCode))
                if array.size(retestLog) > 8
                    array.pop(retestLog)

// ============================================================================
// ACTIVE ZONE VISUAL -- a single box tracking the current candidate only
// (not a registry), matching the "one candidate at a time" simplification.
// ============================================================================

var box activeZoneBox = na

if barstate.islast
    if not na(activeZoneBox)
        box.delete(activeZoneBox)
    if retestState == "WATCHING" or retestState == "RETESTED"
        color zoneDrawColor = retestState == "RETESTED" ? (activeDirection == 1 ? triggerUpColorInput : triggerDownColorInput) : retestZoneColorInput
        activeZoneBox := box.new(left = bar_index - 40, top = activeZoneHigh, right = bar_index + 5, bottom = activeZoneLow, xloc = xloc.bar_index, bgcolor = color.new(zoneDrawColor, 85), border_color = zoneDrawColor)

// ============================================================================
// BUYER / SELLER STRENGTH (native 5m delta, unchanged concept from Stage 5)
// ============================================================================

// (computed inline inside the dashboard block below, same as Stage 5)

// ============================================================================
// DASHBOARD -- same compact philosophy as Stage 5: core rows always visible,
// detail behind a toggle. One new row added: Retest.
// ============================================================================

int coreRows = 8
int diagnosticRows = 8
int tableRows = showDiagnosticsInput ? coreRows + diagnosticRows : coreRows

var table statusTable = table.new(position.bottom_right, 2, tableRows, bgcolor = color.new(color.rgb(15, 20, 27), 10), border_width = 1)

if barstate.islast
    string regime = (not adxOk or not erOk) ? "CHOPPY" : bias == "LONG_ONLY" ? "TRENDING UP" : bias == "SHORT_ONLY" ? "TRENDING DOWN" : "TRANSITIONAL"
    color regimeColor = regime == "CHOPPY" ? color.gray : regime == "TRANSITIONAL" ? color.orange : regime == "TRENDING UP" ? color.lime : color.red

    int bullPoints = 0
    int bearPoints = 0
    if structureUp
        bullPoints += 1
    if structureDown
        bearPoints += 1
    if slowFilterUp
        bullPoints += 1
    if slowFilterDown
        bearPoints += 1
    if adxOk
        bullPoints += 1
        bearPoints += 1
    if emaStackBullish1h
        bullPoints += 1
    if emaStackBearish1h
        bearPoints += 1
    if erOk
        bullPoints += 1
        bearPoints += 1

    float rollingBuyDelta = 0.0
    float rollingSellDelta = 0.0
    for i = 0 to deltaStrengthWindowInput - 1
        float d = f_delta(open[i], high[i], low[i], close[i], nz(volume[i], 0.0))
        if d > 0
            rollingBuyDelta += d
        else
            rollingSellDelta += -d

    if rollingBuyDelta > rollingSellDelta
        bullPoints += 1
    else if rollingSellDelta > rollingBuyDelta
        bearPoints += 1

    float totalDelta = rollingBuyDelta + rollingSellDelta
    float buyRatio = totalDelta > 0 ? rollingBuyDelta / totalDelta : 0.5
    float sellRatio = 1.0 - buyRatio
    int buyBlocks = int(math.round(buyRatio * 10))
    int sellBlocks = int(math.round(sellRatio * 10))
    string buyBar = str.repeat("█", buyBlocks) + str.repeat("░", 10 - buyBlocks)
    string sellBar = str.repeat("█", sellBlocks) + str.repeat("░", 10 - sellBlocks)

    string retestDisplay = retestState == "NONE" ? "NONE" : retestState + " " + f_typeCodeName(activeTypeCode)
    color retestColor = retestState == "TRIGGERED" ? (activeDirection == 1 ? color.lime : color.red) : retestState == "RETESTED" ? color.orange : retestState == "WATCHING" ? color.yellow : retestState == "INVALIDATED" ? color.gray : retestState == "EXPIRED" ? color.gray : color.gray

    // Version tag on the ALWAYS-VISIBLE header row (not diagnostics-only)
    // specifically so a screenshot settles "which round is this running"
    // at a glance, without needing the diagnostics toggle on -- this
    // exact question has cost several rounds already.
    table.cell(statusTable, 0, 0, "DELTA BREAK & RETEST [r7]", text_color = color.white, bgcolor = color.rgb(22, 52, 86), text_size = size.tiny)
    table.cell(statusTable, 1, 0, "", text_color = color.white, bgcolor = color.rgb(22, 52, 86), text_size = size.tiny)

    table.cell(statusTable, 0, 1, "Bias", text_color = color.white, text_size = size.small)
    table.cell(statusTable, 1, 1, bias, text_color = bias == "LONG_ONLY" ? color.lime : bias == "SHORT_ONLY" ? color.red : color.gray, text_size = size.small)

    table.cell(statusTable, 0, 2, "Regime", text_color = color.white, text_size = size.small)
    table.cell(statusTable, 1, 2, regime, text_color = regimeColor, text_size = size.small)

    table.cell(statusTable, 0, 3, "Bull Score", text_color = color.white, text_size = size.small)
    table.cell(statusTable, 1, 3, str.tostring(bullPoints) + " / 6", text_color = bullPoints >= 5 ? color.lime : bullPoints >= 3 ? color.white : color.gray, text_size = size.small)

    table.cell(statusTable, 0, 4, "Bear Score", text_color = color.white, text_size = size.small)
    table.cell(statusTable, 1, 4, str.tostring(bearPoints) + " / 6", text_color = bearPoints >= 5 ? color.red : bearPoints >= 3 ? color.white : color.gray, text_size = size.small)

    table.cell(statusTable, 0, 5, "Buyers", text_color = buyColorInput, text_size = size.small)
    table.cell(statusTable, 1, 5, buyBar + " " + str.tostring(int(math.round(buyRatio * 100))) + "%", text_color = buyColorInput, text_size = size.small)

    table.cell(statusTable, 0, 6, "Sellers", text_color = sellColorInput, text_size = size.small)
    table.cell(statusTable, 1, 6, sellBar + " " + str.tostring(int(math.round(sellRatio * 100))) + "%", text_color = sellColorInput, text_size = size.small)

    table.cell(statusTable, 0, 7, "Retest", text_color = color.white, text_size = size.small)
    table.cell(statusTable, 1, 7, retestDisplay, text_color = retestColor, text_size = size.small)

    if showDiagnosticsInput
        table.cell(statusTable, 0, 8, "Chart TF", text_color = color.white, text_size = size.tiny)
        table.cell(statusTable, 1, 8, timeframe.period, text_color = timeframe.period == "5" ? color.lime : color.orange, text_size = size.tiny)

        table.cell(statusTable, 0, 9, "Active Zone", text_color = color.white, text_size = size.tiny)
        table.cell(statusTable, 1, 9, na(activeZoneLow) ? "--" : str.tostring(activeZoneLow, format.mintick) + " - " + str.tostring(activeZoneHigh, format.mintick), text_color = color.white, text_size = size.tiny)

        table.cell(statusTable, 0, 10, "Hours Since Break", text_color = color.white, text_size = size.tiny)
        table.cell(statusTable, 1, 10, na(hoursSinceEvent) ? "--" : str.tostring(hoursSinceEvent), text_color = color.white, text_size = size.tiny)

        // DIAGNOSTIC (temporary): proves whether var mutation inside the
        // security-pulled 15m function is advancing at all. If this stays
        // "0 / na" forever despite plenty of loaded 15m history, that's the
        // same dead-value signature as the original Stage 4 pivot bug, just
        // for hand-rolled session-count state instead of pivot state -- and
        // confirms the whole f_get15mBreakContext() approach needs the same
        // kind of fix (rely on a stateful built-in, not hand-rolled var).
        table.cell(statusTable, 0, 11, "15m Session # (diag)", text_color = color.white, text_size = size.tiny)
        table.cell(statusTable, 1, 11, na(breakDiagSessionCount) ? "-- (dead)" : str.tostring(breakDiagSessionCount), text_color = na(breakDiagSessionCount) or breakDiagSessionCount == 0 ? color.orange : color.lime, text_size = size.tiny)

        // DIAGNOSTIC (temporary): second probe, narrows the chain further.
        // Levels Set = 0 means extraction never produces a real price for
        // any of the 5 POI types (the bug is in f_extractSessionPois or how
        // it's called). Levels Set > 0 but Break Events = 0 means levels
        // exist but the close-vs-buffered-zone-edge condition never fires
        // (the bug is in the break check itself). Both > 0 but Active Zone
        // upstream still shows -- means the bug is in the ta.valuewhen /
        // tuple-pull step specifically.
        table.cell(statusTable, 0, 12, "Levels Set / Break Events (diag)", text_color = color.white, text_size = size.tiny)
        table.cell(statusTable, 1, 12, (na(breakDiagLevelSetCount) ? "--" : str.tostring(breakDiagLevelSetCount)) + " / " + (na(breakDiagEventCount) ? "--" : str.tostring(breakDiagEventCount)), text_color = (nz(breakDiagLevelSetCount) > 0 and nz(breakDiagEventCount) > 0) ? color.lime : color.orange, text_size = size.tiny)

        // DIAGNOSTIC (temporary, third probe): a trivial ta.valuewhen with
        // no relation to break state at all (close > open, true on roughly
        // half of all bars) pulled through the exact same [1] +
        // lookahead_on chain. If this shows a real, small "hours ago"
        // number, ta.valuewhen itself works fine here and the bug is
        // specific to how eventDirection/eventZoneLow are built. If this
        // ALSO shows dead, ta.valuewhen doesn't work reliably in this exact
        // shape regardless of the condition -- contradicts the Stage 4
        // pivot precedent and needs a bigger rethink.
        table.cell(statusTable, 0, 13, "Sanity ValueWhen Hrs Ago (diag)", text_color = color.white, text_size = size.tiny)
        table.cell(statusTable, 1, 13, na(breakDiagSanityHoursAgo) ? "-- (dead)" : str.tostring(breakDiagSanityHoursAgo), text_color = na(breakDiagSanityHoursAgo) ? color.orange : color.lime, text_size = size.tiny)

        table.cell(statusTable, 0, 14, "RETEST LOG", text_color = color.white, bgcolor = color.rgb(22, 52, 86), text_size = size.tiny)
        table.cell(statusTable, 1, 14, "", text_color = color.white, bgcolor = color.rgb(22, 52, 86), text_size = size.tiny)

        if array.size(retestLog) > 0
            table.cell(statusTable, 0, 15, array.get(retestLog, 0), text_color = color.white, text_halign = text.align_left, text_size = size.tiny)
            table.cell(statusTable, 1, 15, "", text_color = color.white, text_size = size.tiny)
        else
            table.cell(statusTable, 0, 15, "", text_color = color.white, text_size = size.tiny)
            table.cell(statusTable, 1, 15, "", text_color = color.white, text_size = size.tiny)

// ============================================================================
// DATA WINDOW
// ============================================================================

plot(barDelta, title = "Per-Bar Delta", display = display.data_window)
plot(activeDirection, title = "Active Retest Direction", display = display.data_window)
plot(retestState == "TRIGGERED" ? 1 : 0, title = "Triggered This State", display = display.data_window)

Overview

Delta Break and Retest Stage 6 Retest and Trigger also shows buyer and seller strength over a window and a dashboard with 4-hour and 1-hour context. A trigger is only a flagged candle, not an order. It pulls higher timeframe data.

Details

Published by

rosijay17-cmd on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Volume, Support & resistance, Breakout

Markets

Any symbol

Timeframes

M1-M5

Risk Notes

  • Reads higher-timeframe data with lookahead on, so the history can show signals that were not there in real time

Settings

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

SettingTypeDefaultVariable
15m Break-Context Timeframetimeframe15breakContextTimeframeInput
Profile Rows (Extraction Granularity)int40profileRowsInput
POI Zone Half-Width (x ATR)float0.25poiAtrMultInput
Minimum Bars To Finalize A Sessionint10minEndedSessionBarsInput
Track Absorption NodebooltrueshowAbsorptionInput
Minimum Break-Through Buffer (x ATR)float0.10minBreakAtrMultInput
Require Delta Confirmation On 15m Break BarbooltruerequireBreakDeltaConfirmationInput
Retest Window (Hours)float12.0retestWindowHoursInput
Require Delta Confirmation On 5m Trigger BarbooltruerequireTriggerDeltaConfirmationInput
Active Retest Zone Colorcolorcolor.yellowretestZoneColorInput
Trigger Up Colorcolorcolor.limetriggerUpColorInput
Trigger Down Colorcolorcolor.redtriggerDownColorInput
Show all 28 settings
SettingTypeDefaultVariable
4H Bias Timeframetimeframe240biasTimeframeInput
Swing Pivot Left Barsint3pivotLeftInput
Swing Pivot Right Barsint3pivotRightInput
4H EMA Lengthint50biasEmaLengthInput
4H EMA Slope Lookback (Bars)int5biasSlopeLookbackInput
1H Trend Timeframetimeframe60trendTimeframeInput
1H ADX / DI Lengthint14adxLengthInput
1H Minimum ADXfloat20.0adxMinimumInput
1H Fast EMAint21fastEmaInput
1H Slow EMAint50slowEmaInput
1H Efficiency Ratio Lengthint10erLengthInput
1H Minimum Efficiency Ratiofloat0.30erMinimumInput
Buyer/Seller Strength Window (Bars)int20deltaStrengthWindowInput
Buyer Strength Colorcolorcolor.tealbuyColorInput
Seller Strength Colorcolorcolor.orangesellColorInput
Show Detailed DiagnosticsboolfalseshowDiagnosticsInput
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

13 stars

This script identifies and visualizes bullish and bearish order blocks on the price chart. It detects low-volatility consolidation ranges followed by breakout moves accompanied by high trading volume. Users can select between Simple, Relative, or Weighted volu

Indicator v6 SMC / ICT / price actionSupport & resistance
Updated 6 months ago · MIT GitHub repository of Advanced Order Blocks with Volume

This indicator identifies and plots price compression zones directly on the price chart using multi-module scoring. It measures ATR compression, ADX chop levels, boundary touches, volume dry-up, and historical pivot levels to identify consolidation areas and s

Indicator v6 BreakoutSupport & resistance
Updated 2 days ago · MIT GitHub repository of CoilForge Zones [WavesUnchained]

Nexural ORB

by JasonTeixeira
9 stars

This indicator plots opening range breakout levels across multiple timeframes, including five, fifteen, thirty, and sixty minutes, directly on the price chart. It also displays the session open and overnight high and low levels. The script calculates an openin

Indicator v6 BreakoutSessions & time
Updated 13 days ago · Custom licence GitHub repository of Nexural ORB

This indicator identifies dynamic support and resistance levels using pivot highs and lows. It plots resistance and support lines directly on the price chart once pivot points are confirmed. Additionally, it calculates a volume oscillator using 9-period and 26

Indicator v5 Support & resistanceSMC / ICT / price action
Updated 1 year ago · MPL-2.0 GitHub repository of Support & Resistance Levels with Break of Structure
2 stars

This overlay indicator approximates a Bookmap-style liquidity view from price and volume. It draws heatmap boxes where volume exceeds a rolling average, volume dots above a relative-volume filter, and a smoothed cumulative-delta proxy. Optional signals use abs

Indicator v6 VolumeSupport & resistance
Updated 6 months ago · None stated GitHub repository of Bookmap Heatmap & Order Flow

Equity Trading Strategy

by AmjadSaidam
2 stars

This indicator draws a daily-anchored volume-weighted average price on the price chart and marks the first long or short reclaim of that level. A long needs a bar below the average whose high reaches it, a later close above it, and confirmation after at least

Indicator v6 VolumeSupport & resistance Repaints
Updated 5 weeks ago · MIT GitHub repository of Equity Trading Strategy

More indicators

Trade Your Charts With a Regulated Broker

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

Compare brokers