Indicator · Pine Script v6 · Own pane

Relative Leg Efficiency (RLE) - Panel + Chart [WavesUnchained]

by casoon

Relative Leg Efficiency (RLE) measures price efficiency across pivot-to-pivot movement legs.

View on GitHub

GitHub stars9on the repository
Pine versionv6current
Lines of code731
Settings40inputs
LicenceMIT
Last updateyesterday27 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

731 lines of Pine Script v6 from archive/indicators/relative_leg_efficiency_panel_chart/relative_leg_efficiency_panel_chart.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

relative_leg_efficiency_panel_chart.pine
//@version=6
// ============================================================================
// Relative Leg Efficiency (RLE) — Panel + Chart [WavesUnchained]
// Version: 1.0.0
// Author: WavesUnchained
// Build: 2025-12-24 01:16:05
// ============================================================================
// Pivot-to-pivot RLE scoring with panel + chart visualization
//
// Features:
//   ✓ Panel plots for live and confirmed RLE
//   ✓ Chart leg lines and labels at pivots
//   ✓ Counter-pressure via body-weighted or count mode
//   ✓ ATR-based label offsets
//
// ============================================================================
indicator("Relative Leg Efficiency (RLE) — Panel + Chart [WavesUnchained]", overlay=false, max_labels_count=500, max_lines_count=500, max_bars_back=5000)

// ═══════════════════════════════════════════════════════════════════════════
// Relative Leg Efficiency (RLE) — Panel + Chart
// ═══════════════════════════════════════════════════════════════════════════
// Version: 1.0.0
// Author: WavesUnchained
//
// MODULES:
// - inputs.pine
// - helpers.pine
// - pivots.pine
// - state.pine
// - panel.pine
// ═══════════════════════════════════════════════════════════════════════════

// NOTE: Modules are combined by the build system.

// ============================================================================
// INLINE MODULES (Combined at build time)
// ============================================================================

// ============================================================================
// MODULE: inputs
// ============================================================================

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Inputs
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
groupPivots = "Pivots / Legs"
leftBars   = input.int(4, "Pivot Left Bars", minval=1, group=groupPivots)
rightBars  = input.int(4, "Pivot Right Bars", minval=1, group=groupPivots)
minLegBars = input.int(6, "Min Leg Bars", minval=1, group=groupPivots)

groupMath = "RLE Components"
pathSrc         = input.source(hlc3, "Path Source (for path length)", group=groupMath)
directMode      = input.string("PivotPrice", "Direct Distance Mode", options=["PathSource","PivotPrice"], group=groupMath)
liveEndMode     = input.string("close", "Live End Price", options=["close","high/low"], group=groupMath)
tfMode          = input.string("Bars (current TF)", "Time Factor Mode", options=["Bars (current TF)","Time (TF-neutral)"], group=groupMath)
timeRefMinutes  = input.int(120, "Time Reference Minutes (TF-neutral)", minval=1, group=groupMath)
useTF           = input.bool(false, "Use Time Factor (TF) in score", group=groupMath)
useBodyWeighted = input.bool(true, "Counter Pressure: Body-weighted (else count)", group=groupMath)
minOppBodyAtr   = input.float(0.0, "Ignore tiny opposite bodies (ATR multiple, 0=off)", step=0.1, group=groupMath)
atrLen          = input.int(14, "ATR Length", minval=1, group=groupMath)
timeRefBars     = input.int(24, "Time Reference Bars (penalty after this)", minval=1, group=groupMath)

wPath    = input.float(0.50, "Weight: Path", minval=0, maxval=1, step=0.05, group=groupMath)
wTime    = input.float(0.20, "Weight: Time", minval=0, maxval=1, step=0.05, group=groupMath)
wCounter = input.float(0.30, "Weight: Counter", minval=0, maxval=1, step=0.05, group=groupMath)

cpEndFrac      = input.float(0.30, "CP: End segment fraction", minval=0.05, maxval=0.60, step=0.05, group=groupMath)
cpEndWeight    = input.float(0.70, "CP: Weight end segment", minval=0.0, maxval=1.0, step=0.05, group=groupMath)

relLookbackLegs = input.int(60, "Relative lookback legs", minval=20, maxval=300, group=groupMath)
relMode         = input.string("Percentile", "Relative mode", options=["Percentile","Z-Score"], group=groupMath)

divEnable     = input.bool(true, "Divergence labels", group=groupMath)
divDropPct    = input.float(12.0, "Div: min drop vs prev (%)", minval=1, maxval=80, step=1, group=groupMath)

groupReg = "Regime / Filters"
regLookback = input.int(8, "Regime legs lookback", minval=3, maxval=30, group=groupReg)
regChopPeMax = input.float(0.45, "Chop if PE <=", minval=0.10, maxval=0.90, step=0.05, group=groupReg)

regTrendMin = input.float(0.60, "Trend score min (0..1)", minval=0, maxval=1, step=0.05, group=groupReg)
regChopMax  = input.float(0.40, "Chop score max (0..1)",  minval=0, maxval=1, step=0.05, group=groupReg)

trendMinLegRLE = input.float(60, "Trend filter: last leg RLE >=", minval=0, maxval=100, group=groupReg)
trendMinRelPct = input.float(60, "Trend filter: RLE percentile >=", minval=0, maxval=100, group=groupReg)

mrMaxLegRLE    = input.float(45, "MR filter: last leg RLE <=", minval=0, maxval=100, group=groupReg)
mrMinChopShare = input.float(0.50, "MR filter: chop share >=", minval=0, maxval=1, step=0.05, group=groupReg)

groupViz = "Visuals"
showChartDrawings = input.bool(true, "Show Chart Drawings (Lines/Labels)", group=groupViz)
showPanel       = input.bool(true, "Show Panel", group=groupViz)
showLegLines   = input.bool(true, "Draw Leg Lines on Chart", group=groupViz)
showEndLabels  = input.bool(true, "Show RLE Labels at Leg End", group=groupViz)
showLiveLabel  = input.bool(false, "Show Live RLE Label (updates)", group=groupViz)
labelSize      = input.string("small", "Label Size", options=["small","normal","large"], group=groupViz)
maxDrawnLegs   = input.int(120, "Max Drawn Legs", minval=10, maxval=500, group=groupViz)
showStateTag   = input.bool(true, "Append State Tag to Labels", group=groupViz)

thrHigh = input.float(80, "Threshold High", minval=0, maxval=100, group=groupViz)
thrMid  = input.float(70, "Threshold Mid",  minval=0, maxval=100, group=groupViz)
thrLow  = input.float(55, "Threshold Low",  minval=0, maxval=100, group=groupViz)

colBull = input.color(color.new(color.lime, 0), "Bull (Up-Leg)", group=groupViz)
colBear = input.color(color.new(color.red,  0), "Bear (Down-Leg)", group=groupViz)
colMid  = input.color(color.new(color.yellow, 0), "Medium Quality", group=groupViz)
colBad  = input.color(color.new(color.gray,  0), "Low Quality", group=groupViz)

lineWidth = input.int(2, "Leg Line Width", minval=1, maxval=4, group=groupViz)
labelYOffsetAtr = input.float(0.20, "Label Y Offset (ATR)", minval=0, maxval=2, step=0.05, group=groupViz)

groupLog = "Logging"
logEnable        = input.bool(false, "Enable Pine log", group=groupLog)
logMode          = input.string("Batch (last bar)", "Log mode", options=["Batch (last bar)","Streaming"], group=groupLog)
logEveryN        = input.int(10, "Log every N bars", minval=1, group=groupLog)
logLookback      = input.int(2000, "Log lookback bars", minval=100, group=groupLog)
logMaxLines      = input.int(800, "Log max lines (batch)", minval=100, maxval=5000, group=groupLog)
logIncludeFinal  = input.bool(true, "Include final values", group=groupLog)
logIncludeLive   = input.bool(true, "Include live values", group=groupLog)
logIncludeConfig = input.bool(true, "Log config once", group=groupLog)

atr = ta.atr(atrLen)


// ============================================================================
// MODULE: helpers
// ============================================================================

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Helpers
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
clamp01(x) => math.max(0.0, math.min(1.0, x))
clamp100(x) => math.max(0.0, math.min(100.0, x))

// Optional TF weight
wTimeEffective = useTF ? wTime : 0.0

// --- FAST AGGREGATES (Cum-Sums) ---
stepPath = math.abs(pathSrc - pathSrc[1])
cumPath  = ta.cum(stepPath)

bodyAbs  = math.abs(close - open)
cumBody  = ta.cum(bodyAbs)

minOppNow = minOppBodyAtr > 0 ? (atr * minOppBodyAtr) : 0.0
bodyOK    = (minOppBodyAtr == 0.0) or (bodyAbs >= minOppNow)

bullBodyOK = (close > open and bodyOK) ? bodyAbs : 0.0
bearBodyOK = (close < open and bodyOK) ? bodyAbs : 0.0

bullCountOK = (close > open and bodyOK) ? 1.0 : 0.0
bearCountOK = (close < open and bodyOK) ? 1.0 : 0.0

cumBullBodyOK  = ta.cum(bullBodyOK)
cumBearBodyOK  = ta.cum(bearBodyOK)
cumBullCountOK = ta.cum(bullCountOK)
cumBearCountOK = ta.cum(bearCountOK)

// Sum over bar-offset range iFrom..iTo inclusive
seg_sum(series float cum, int iFrom, int iTo) =>
    cum[iFrom] - cum[iTo + 1]

// Relative stats helpers
f_percentile(float[] arr, float v) =>
    int n = array.size(arr)
    if n == 0
        na
    else
        int c = 0
        for k = 0 to n - 1
            if array.get(arr, k) <= v
                c += 1
        c / n

f_mean(float[] arr) =>
    int n = array.size(arr)
    if n == 0
        na
    else
        float s = 0.0
        for k = 0 to n - 1
            s += array.get(arr, k)
        s / n

f_std(float[] arr, float mu) =>
    int n = array.size(arr)
    if n < 2 or na(mu)
        na
    else
        float s2 = 0.0
        for k = 0 to n - 1
            float d = array.get(arr, k) - mu
            s2 += d * d
        math.sqrt(s2 / (n - 1))

f_z(float[] arr, float v) =>
    float mu = f_mean(arr)
    float sd = f_std(arr, mu)
    (not na(sd) and sd > 0) ? ((v - mu) / sd) : na

f_avg_last(float[] arr, int n) =>
    int sz = array.size(arr)
    if sz == 0
        na
    else
        int take = math.min(n, sz)
        float s = 0.0
        for k = 0 to take - 1
            s += array.get(arr, sz - 1 - k)
        s / take

f_chop_share_last(float[] peArr, int n, float peMax) =>
    int sz = array.size(peArr)
    if sz == 0
        na
    else
        int take = math.min(n, sz)
        int c = 0
        for k = 0 to take - 1
            float pe = array.get(peArr, sz - 1 - k)
            if pe <= peMax
                c += 1
        c / take

f_label_size() =>
    switch labelSize
        "large"  => size.large
        "normal" => size.normal
        => size.small

// Quality color based on score (for both legs)
scoreColor(score, dir) =>
    base = dir == 1 ? colBull : colBear
    score >= thrHigh ? base : score >= thrMid ? colMid : score >= thrLow ? color.new(base, 35) : colBad

// Compute RLE for a finished leg between startIdx and endIdx (both are bar_index values in the past)
f_rle_for_leg(int startIdx, float startPrice, int endIdx, float endPrice, int dir) =>
    // Offsets from current bar (0 = now, bigger = past)
    int oStart = bar_index - startIdx
    int oEnd   = bar_index - endIdx

    // Safety: ensure ordering
    bool ok = oStart > oEnd and oStart < 5000 and oEnd >= 0 and (oStart + 1) < 5000

    float direct = na
    if directMode == "PathSource"
        direct := math.abs(pathSrc[oEnd] - pathSrc[oStart])
    else
        direct := math.abs(endPrice - startPrice)
    int bars = endIdx - startIdx

    float pathSum = na
    float totalBody = na
    float oppBody = na
    int   oppCount = na

    if ok
        int iFrom = oEnd + 1
        int iTo = oStart

        // Path length: sum stepPath[k] for k=oEnd..oStart-1
        pathSum := (cumPath[oEnd] - cumPath[oStart])

        // Body sum: sum bodyAbs[i] for i=oEnd+1..oStart
        totalBody := seg_sum(cumBody, iFrom, iTo)

        float oppFullBody = na
        float oppFullCnt = na

        if useBodyWeighted
            oppFullBody := dir == 1 ? seg_sum(cumBearBodyOK, iFrom, iTo) : seg_sum(cumBullBodyOK, iFrom, iTo)
        else
            oppFullCnt := dir == 1 ? seg_sum(cumBearCountOK, iFrom, iTo) : seg_sum(cumBullCountOK, iFrom, iTo)
        float pe = pathSum > 0 ? clamp01(direct / pathSum) : 0.0

        // Time factor: penalty for slow legs (0..1)
        float tf = 1.0
        if useTF
            tf := 1.0 / (1.0 + (bars / math.max(1.0, timeRefBars)))
            if tfMode == "Time (TF-neutral)"
                int startTime = time[oStart]
                int endTime   = time[oEnd]
                float legMinutes = (endTime - startTime) / 60000.0
                tf := 1.0 / (1.0 + (legMinutes / math.max(1.0, timeRefMinutes)))

        int segLen = iTo - iFrom + 1
        int nEnd = math.max(1, int(math.round(segLen * cpEndFrac)))
        int endFrom = iFrom
        int endTo = math.min(iTo, iFrom + nEnd - 1)

        float endBody = seg_sum(cumBody, endFrom, endTo)
        float oppEndBody = na
        float oppEndCnt = na

        if useBodyWeighted
            oppEndBody := dir == 1 ? seg_sum(cumBearBodyOK, endFrom, endTo) : seg_sum(cumBullBodyOK, endFrom, endTo)
        else
            oppEndCnt := dir == 1 ? seg_sum(cumBearCountOK, endFrom, endTo) : seg_sum(cumBullCountOK, endFrom, endTo)
        float cpFull = useBodyWeighted ? (totalBody > 0 ? clamp01(1.0 - (oppFullBody / totalBody)) : 1.0) : (bars > 0 ? clamp01(1.0 - (int(oppFullCnt) / bars)) : 1.0)

        float cpEnd = useBodyWeighted ? (endBody > 0 ? clamp01(1.0 - (oppEndBody / endBody)) : cpFull) : (nEnd > 0 ? clamp01(1.0 - (int(oppEndCnt) / nEnd)) : cpFull)

        float cp = clamp01((1.0 - cpEndWeight) * cpFull + cpEndWeight * cpEnd)

        float wSum = math.max(0.0001, (wPath + wTimeEffective + wCounter))
        float score = 100.0 * (wPath * pe + wTimeEffective * tf + wCounter * cp) / wSum

        oppBody := oppFullBody
        oppCount := int(oppFullCnt)

        [clamp100(score), pe, tf, cp, bars, direct, pathSum, totalBody, oppBody, oppCount]
    else
        [na, na, na, na, na, na, na, na, na, na]

// Live estimate from last pivot to current bar
f_rle_live(int startIdx, float startPrice, int dir) =>
    int endIdx = bar_index
    float endPrice = liveEndMode == "close" ? close : (dir == 1 ? high : low)
    f_rle_for_leg(startIdx, startPrice, endIdx, endPrice, dir)


// ============================================================================
// MODULE: pivots
// ============================================================================

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Pivot detection (confirmed pivots)
// pivots are confirmed only after rightBars, pivot bar is (bar_index - rightBars)
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
ph = ta.pivothigh(high, leftBars, rightBars)
pl = ta.pivotlow(low, leftBars, rightBars)

bool newPH = not na(ph)
bool newPL = not na(pl)

int pivotIdx = bar_index - rightBars
float pivotPrice = na
int pivotType = 0  // +1 = high, -1 = low

if newPH
    pivotPrice := ph
    pivotType := +1
else if newPL
    pivotPrice := pl
    pivotType := -1


// ============================================================================
// MODULE: state
// ============================================================================

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// State: last confirmed pivot (start of active leg)
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var int   lastPivotIdx   = na
var float lastPivotPrice = na
var int   lastPivotType  = 0

var float lastFinalRLE = na
var float lastFinalPE  = na
var float lastFinalTF  = na
var float lastFinalCP  = na
var int   lastFinalBars = na
var int   lastFinalDir  = 0
var float lastFinalDirect = na
var float lastFinalPathSum = na
var float lastFinalTotalBody = na
var float lastFinalOppBody = na
var int   lastFinalOppCount = na
var float lastRelRLE = na
var float lastRelPE = na
var float lastRelCP = na

var float prevUpPE = na
var float prevUpPrice = na
var float prevDnPE = na
var float prevDnPrice = na

var float[] histRLE = array.new_float()
var float[] histPE = array.new_float()
var float[] histCP = array.new_float()

var float regAvgPE = na
var float regAvgCP = na
var float regAvgRLE = na
var float regChopShr = na
var float regScore = na
var bool regimeTrend = false
var bool regimeChop = false
var bool trendOk = false
var bool mrOk = false
var float relRLEpct = na

var label liveLbl = na
var line[]  legLines = array.new_line()
var label[] endLabels = array.new_label()

f_push_line(line ln) =>
    array.push(legLines, ln)
    if array.size(legLines) > maxDrawnLegs
        line.delete(array.shift(legLines))

f_push_label(label lb) =>
    array.push(endLabels, lb)
    if array.size(endLabels) > maxDrawnLegs
        label.delete(array.shift(endLabels))

f_state_tag(float pe, float tf, float cp) =>
    if pe > 0.75 and tf > 0.55 and cp > 0.55
        "Drive"
    else if pe > 0.65 and tf < 0.45 and cp > 0.55
        "Grind"
    else if pe < 0.45 and cp < 0.45
        "Chop"
    else if pe > 0.60 and cp < 0.45
        "Exhaust"
    else
        "Neutral"

f_push_hist(float[] arr, float v) =>
    array.push(arr, v)
    if array.size(arr) > relLookbackLegs
        array.shift(arr)

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// When we get a new pivot: close the leg from lastPivot -> this pivot
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
if pivotType != 0
    if na(lastPivotIdx)
        lastPivotIdx   := pivotIdx
        lastPivotPrice := pivotPrice
        lastPivotType  := pivotType
    else
        if pivotType != lastPivotType
            int startIdx = lastPivotIdx
            float startPrice = lastPivotPrice
            int endIdx = pivotIdx
            float endPrice = pivotPrice

            int bars = endIdx - startIdx
            if bars >= minLegBars
                int dir = (endPrice >= startPrice) ? 1 : -1
                [score, pe, tf, cp, lbars, direct, pathSum, totalBody, oppBody, oppCount] = f_rle_for_leg(startIdx, startPrice, endIdx, endPrice, dir)

                if not na(score)
                    lastFinalRLE := score
                    lastFinalPE  := pe
                    lastFinalTF  := tf
                    lastFinalCP  := cp
                    lastFinalBars := lbars
                    lastFinalDir := dir
                    lastFinalDirect := direct
                    lastFinalPathSum := pathSum
                    lastFinalTotalBody := totalBody
                    lastFinalOppBody := oppBody
                    lastFinalOppCount := oppCount

                    f_push_hist(histRLE, score)
                    f_push_hist(histPE, pe)
                    f_push_hist(histCP, cp)

                    if array.size(histRLE) >= 20
                        if relMode == "Percentile"
                            lastRelRLE := 100.0 * f_percentile(histRLE, score)
                            lastRelPE  := 100.0 * f_percentile(histPE, pe)
                            lastRelCP  := 100.0 * f_percentile(histCP, cp)
                        else
                            lastRelRLE := f_z(histRLE, score)
                            lastRelPE  := f_z(histPE, pe)
                            lastRelCP  := f_z(histCP, cp)

                    regAvgPE   := array.size(histPE)  > 0 ? f_avg_last(histPE,  regLookback) : na
                    regAvgCP   := array.size(histCP)  > 0 ? f_avg_last(histCP,  regLookback) : na
                    regAvgRLE  := array.size(histRLE) > 0 ? f_avg_last(histRLE, regLookback) : na
                    regChopShr := array.size(histPE)  > 0 ? f_chop_share_last(histPE, regLookback, regChopPeMax) : na

                    regScore := na
                    if not na(regAvgPE) and not na(regAvgCP) and not na(regChopShr)
                        float peN = clamp01(regAvgPE)
                        float cpN = clamp01(regAvgCP)
                        float chopPenalty = clamp01(regChopShr)
                        regScore := clamp01(0.45 * peN + 0.45 * cpN + 0.10 * (1.0 - chopPenalty))

                    regimeTrend := not na(regScore) and regScore >= regTrendMin
                    regimeChop  := not na(regScore) and regScore <= regChopMax

                    relRLEpct := na
                    if relMode == "Percentile"
                        relRLEpct := lastRelRLE

                    trendOk := regimeTrend and not na(lastFinalRLE) and lastFinalRLE >= trendMinLegRLE and (na(relRLEpct) or relRLEpct >= trendMinRelPct)
                    mrOk := regimeChop and not na(lastFinalRLE) and lastFinalRLE <= mrMaxLegRLE and (not na(regChopShr) and regChopShr >= mrMinChopShare)

                    if divEnable
                        float drop = divDropPct / 100.0

                        if dir == 1 and pivotType == +1
                            if not na(prevUpPE) and endPrice > prevUpPrice and pe < prevUpPE * (1.0 - drop)
                                if showChartDrawings
                                    label.new(endIdx, endPrice + atr * labelYOffsetAtr, "RLE DIV ↓",
                                        xloc=xloc.bar_index,
                                        style=label.style_label_down,
                                        textcolor=color.white,
                                        color=color.new(color.orange, 0),
                                        force_overlay=true)
                            prevUpPE := pe
                            prevUpPrice := endPrice

                        if dir == -1 and pivotType == -1
                            if not na(prevDnPE) and endPrice < prevDnPrice and pe < prevDnPE * (1.0 - drop)
                                if showChartDrawings
                                    label.new(endIdx, endPrice - atr * labelYOffsetAtr, "RLE DIV ↑",
                                        xloc=xloc.bar_index,
                                        style=label.style_label_up,
                                        textcolor=color.white,
                                        color=color.new(color.aqua, 0),
                                        force_overlay=true)
                            prevDnPE := pe
                            prevDnPrice := endPrice

                    if showChartDrawings and showLegLines
                        line ln = line.new(startIdx, startPrice, endIdx, endPrice,
                            xloc=xloc.bar_index,
                            color=scoreColor(score, dir),
                            width=lineWidth,
                            force_overlay=true)
                        f_push_line(ln)

                    if showChartDrawings and showEndLabels
                        string styleEnd = dir == 1 ? label.style_label_down : label.style_label_up
                        float yOff = atr * labelYOffsetAtr
                        float y = dir == 1 ? (endPrice + yOff) : (endPrice - yOff)
                        labelSz = f_label_size()

                        string labelTxt = "RLE " + str.tostring(score, "#.0")
                        if showStateTag
                            string tag = f_state_tag(pe, tf, cp)
                            labelTxt := labelTxt + " | " + tag
                        string tip = str.format( "Relative Leg Efficiency (RLE)\nScore: {0} / 100\n\nPath Efficiency (PE): {1}%\nTime Factor (TF): {2}%\nCounter Pressure (CP): {3}%\n\nBars in leg: {4}", str.tostring(score, "#.0"), str.tostring(pe * 100, "#.0"), str.tostring(tf * 100, "#.0"), str.tostring(cp * 100, "#.0"), str.tostring(lbars) )

                        label lb = label.new(endIdx, y, labelTxt,
                            xloc=xloc.bar_index,
                            style=styleEnd,
                            color=scoreColor(score, dir),
                            textcolor=color.white,
                            size=labelSz,
                            force_overlay=true,
                            tooltip=tip)
                        f_push_label(lb)

            lastPivotIdx   := pivotIdx
            lastPivotPrice := pivotPrice
            lastPivotType  := pivotType
        else
            if pivotType == +1 and pivotPrice > lastPivotPrice
                lastPivotIdx   := pivotIdx
                lastPivotPrice := pivotPrice
            if pivotType == -1 and pivotPrice < lastPivotPrice
                lastPivotIdx   := pivotIdx
                lastPivotPrice := pivotPrice

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Live RLE (optional) from last pivot to current bar
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
int activeDir =
     lastPivotType == -1 ? 1 : lastPivotType == +1 ? -1 : 0

float rleLive = na
float peLive = na
float tfLive = na
float cpLive = na
int   barsLive = na
float directLive = na
float pathSumLive = na
float totalBodyLive = na
float oppBodyLive = na
int   oppCountLive = na

if not na(lastPivotIdx) and activeDir != 0
    [rleLive, peLive, tfLive, cpLive, barsLive, directLive, pathSumLive, totalBodyLive, oppBodyLive, oppCountLive] = f_rle_live(lastPivotIdx, lastPivotPrice, activeDir)

if showChartDrawings and showLiveLabel
    if not na(liveLbl)
        label.delete(liveLbl)
    if not na(rleLive)
        string styleLive = activeDir == 1 ? label.style_label_down : label.style_label_up
        float yOff = atr * labelYOffsetAtr
        float y = activeDir == 1 ? (high + yOff) : (low - yOff)
        labelSz = f_label_size()

        string labelTxt = "Live " + str.tostring(rleLive, "#.0")
        string tip = str.format( "Live estimate (can change until the leg is confirmed)\n\nRLE: {0}\nPE:  {1}%\nTF:  {2}%\nCP:  {3}%\nBars: {4}", str.tostring(rleLive, "#.0"), str.tostring(peLive * 100, "#.0"), str.tostring(tfLive * 100, "#.0"), str.tostring(cpLive * 100, "#.0"), str.tostring(barsLive) )

        liveLbl := label.new(bar_index, y, labelTxt,
            xloc=xloc.bar_index,
            style=styleLive,
            color=scoreColor(rleLive, activeDir),
            textcolor=color.white,
            size=labelSz,
            force_overlay=true,
            tooltip=tip)
else
    if not na(liveLbl)
        label.delete(liveLbl)
        liveLbl := na

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Logging (last bar): sample past bars for optimization
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var int logLastBar = na
if logEnable and logMode == "Batch (last bar)" and barstate.islast
    if na(logLastBar) or logLastBar != bar_index
        logLastBar := bar_index

        if logIncludeConfig
            log.info(str.format( "RLE_CFG,directMode={0},tfMode={1},liveEndMode={2},wPath={3},wTime={4},wCounter={5},timeRefBars={6},timeRefMinutes={7},minOppBodyAtr={8},useBodyWeighted={9},minLegBars={10}", directMode, tfMode, liveEndMode, str.tostring(wPath, "#.00"), str.tostring(wTime, "#.00"), str.tostring(wCounter, "#.00"), str.tostring(timeRefBars), str.tostring(timeRefMinutes), str.tostring(minOppBodyAtr, "#.00"), str.tostring(useBodyWeighted), str.tostring(minLegBars) ))

        int logCount = 0
        for i = 0 to logLookback
            if i <= bar_index and (i % logEveryN == 0)
                if logCount >= logMaxLines
                    break
                logCount += 1

                string fScore = logIncludeFinal ? str.tostring(lastFinalRLE[i], "#.0") : ""
                string fPE    = logIncludeFinal ? str.tostring(lastFinalPE[i],  "#.000") : ""
                string fTF    = logIncludeFinal ? str.tostring(lastFinalTF[i],  "#.000") : ""
                string fCP    = logIncludeFinal ? str.tostring(lastFinalCP[i],  "#.000") : ""
                string fBars  = logIncludeFinal ? str.tostring(lastFinalBars[i]) : ""
                string fDir   = logIncludeFinal ? str.tostring(lastFinalDir[i]) : ""
                string fDirect = logIncludeFinal ? str.tostring(lastFinalDirect[i], "#.000") : ""
                string fPath   = logIncludeFinal ? str.tostring(lastFinalPathSum[i], "#.000") : ""
                string fBody   = logIncludeFinal ? str.tostring(lastFinalTotalBody[i], "#.000") : ""
                string fOppB   = logIncludeFinal ? str.tostring(lastFinalOppBody[i], "#.000") : ""
                string fOppC   = logIncludeFinal ? str.tostring(lastFinalOppCount[i]) : ""
                string fTag   = logIncludeFinal and not na(lastFinalPE[i]) ? f_state_tag(lastFinalPE[i], lastFinalTF[i], lastFinalCP[i]) : ""
                string fRelRLE = logIncludeFinal ? str.tostring(lastRelRLE[i], "#.000") : ""
                string fRelPE  = logIncludeFinal ? str.tostring(lastRelPE[i],  "#.000") : ""
                string fRelCP  = logIncludeFinal ? str.tostring(lastRelCP[i],  "#.000") : ""
                string fRegSc  = logIncludeFinal ? str.tostring(regScore[i], "#.000") : ""
                string fChop   = logIncludeFinal ? str.tostring(regChopShr[i], "#.000") : ""
                string fTrend  = logIncludeFinal ? str.tostring(trendOk[i]) : ""
                string fMR     = logIncludeFinal ? str.tostring(mrOk[i]) : ""

                bool liveOk = logIncludeLive and (i == 0)
                string lScore = liveOk ? str.tostring(rleLive, "#.0") : ""
                string lPE    = liveOk ? str.tostring(peLive,  "#.000") : ""
                string lTF    = liveOk ? str.tostring(tfLive,  "#.000") : ""
                string lCP    = liveOk ? str.tostring(cpLive,  "#.000") : ""
                string lBars  = liveOk ? str.tostring(barsLive) : ""
                string lDir   = liveOk ? str.tostring(activeDir) : ""
                string lDirect = liveOk ? str.tostring(directLive, "#.000") : ""
                string lPath   = liveOk ? str.tostring(pathSumLive, "#.000") : ""
                string lBody   = liveOk ? str.tostring(totalBodyLive, "#.000") : ""
                string lOppB   = liveOk ? str.tostring(oppBodyLive, "#.000") : ""
                string lOppC   = liveOk ? str.tostring(oppCountLive) : ""
                string lTag   = liveOk and not na(peLive) ? f_state_tag(peLive, tfLive, cpLive) : ""

                log.info(str.format( "RLE,bar={0},time={1},final_score={2},final_pe={3},final_tf={4},final_cp={5},final_bars={6},final_dir={7},final_direct={8},final_path={9},final_body={10},final_opp_body={11},final_opp_count={12},final_tag={13},final_rel_rle={14},final_rel_pe={15},final_rel_cp={16},reg_score={17},chop_share={18},trend_ok={19},mr_ok={20},live_score={21},live_pe={22},live_tf={23},live_cp={24},live_bars={25},live_dir={26},live_direct={27},live_path={28},live_body={29},live_opp_body={30},live_opp_count={31},live_tag={32}", str.tostring(bar_index - i), str.tostring(time[i]), fScore, fPE, fTF, fCP, fBars, fDir, fDirect, fPath, fBody, fOppB, fOppC, fTag, fRelRLE, fRelPE, fRelCP, fRegSc, fChop, fTrend, fMR, lScore, lPE, lTF, lCP, lBars, lDir, lDirect, lPath, lBody, lOppB, lOppC, lTag ))

if logEnable and logMode == "Streaming"
    if barstate.isconfirmed and (bar_index % logEveryN == 0)
        string fScore = logIncludeFinal ? str.tostring(lastFinalRLE, "#.0") : ""
        string fPE    = logIncludeFinal ? str.tostring(lastFinalPE,  "#.000") : ""
        string fTF    = logIncludeFinal ? str.tostring(lastFinalTF,  "#.000") : ""
        string fCP    = logIncludeFinal ? str.tostring(lastFinalCP,  "#.000") : ""
        string fBars  = logIncludeFinal ? str.tostring(lastFinalBars) : ""
        string fDir   = logIncludeFinal ? str.tostring(lastFinalDir) : ""
        string fDirect = logIncludeFinal ? str.tostring(lastFinalDirect, "#.000") : ""
        string fPath   = logIncludeFinal ? str.tostring(lastFinalPathSum, "#.000") : ""
        string fBody   = logIncludeFinal ? str.tostring(lastFinalTotalBody, "#.000") : ""
        string fOppB   = logIncludeFinal ? str.tostring(lastFinalOppBody, "#.000") : ""
        string fOppC   = logIncludeFinal ? str.tostring(lastFinalOppCount) : ""
        string fTag   = logIncludeFinal and not na(lastFinalPE) ? f_state_tag(lastFinalPE, lastFinalTF, lastFinalCP) : ""
        string fRelRLE = logIncludeFinal ? str.tostring(lastRelRLE, "#.000") : ""
        string fRelPE  = logIncludeFinal ? str.tostring(lastRelPE,  "#.000") : ""
        string fRelCP  = logIncludeFinal ? str.tostring(lastRelCP,  "#.000") : ""
        string fRegSc  = logIncludeFinal ? str.tostring(regScore, "#.000") : ""
        string fChop   = logIncludeFinal ? str.tostring(regChopShr, "#.000") : ""
        string fTrend  = logIncludeFinal ? str.tostring(trendOk) : ""
        string fMR     = logIncludeFinal ? str.tostring(mrOk) : ""

        string lScore = logIncludeLive ? str.tostring(rleLive, "#.0") : ""
        string lPE    = logIncludeLive ? str.tostring(peLive,  "#.000") : ""
        string lTF    = logIncludeLive ? str.tostring(tfLive,  "#.000") : ""
        string lCP    = logIncludeLive ? str.tostring(cpLive,  "#.000") : ""
        string lBars  = logIncludeLive ? str.tostring(barsLive) : ""
        string lDir   = logIncludeLive ? str.tostring(activeDir) : ""
        string lDirect = logIncludeLive ? str.tostring(directLive, "#.000") : ""
        string lPath   = logIncludeLive ? str.tostring(pathSumLive, "#.000") : ""
        string lBody   = logIncludeLive ? str.tostring(totalBodyLive, "#.000") : ""
        string lOppB   = logIncludeLive ? str.tostring(oppBodyLive, "#.000") : ""
        string lOppC   = logIncludeLive ? str.tostring(oppCountLive) : ""
        string lTag   = logIncludeLive and not na(peLive) ? f_state_tag(peLive, tfLive, cpLive) : ""

        log.info(str.format( "RLE,bar={0},time={1},final_score={2},final_pe={3},final_tf={4},final_cp={5},final_bars={6},final_dir={7},final_direct={8},final_path={9},final_body={10},final_opp_body={11},final_opp_count={12},final_tag={13},final_rel_rle={14},final_rel_pe={15},final_rel_cp={16},reg_score={17},chop_share={18},trend_ok={19},mr_ok={20},live_score={21},live_pe={22},live_tf={23},live_cp={24},live_bars={25},live_dir={26},live_direct={27},live_path={28},live_body={29},live_opp_body={30},live_opp_count={31},live_tag={32}", str.tostring(bar_index), str.tostring(time), fScore, fPE, fTF, fCP, fBars, fDir, fDirect, fPath, fBody, fOppB, fOppC, fTag, fRelRLE, fRelPE, fRelCP, fRegSc, fChop, fTrend, fMR, lScore, lPE, lTF, lCP, lBars, lDir, lDirect, lPath, lBody, lOppB, lOppC, lTag ))


// ============================================================================
// MODULE: panel
// ============================================================================

//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Panel plots
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
panelDisplay = showPanel ? display.pane : display.none

// Invisible anchor to force pane creation when overlay=true
plot(showPanel ? 0 : na, "Pane Anchor", color=color.new(color.gray, 100), display=panelDisplay)

plot(showPanel ? thrHigh : na, "High", color=color.new(color.white, 70), display=panelDisplay)
plot(showPanel ? thrMid  : na, "Mid",  color=color.new(color.white, 75), display=panelDisplay)
plot(showPanel ? thrLow  : na, "Low",  color=color.new(color.white, 80), display=panelDisplay)

plot(lastFinalRLE, "RLE Final (confirmed)", style=plot.style_stepline, linewidth=2, display=panelDisplay)
plot(rleLive, "RLE Live (estimate)", linewidth=1, display=panelDisplay)

plot(showPanel ? (lastFinalPE * 100.0) : na, "PE % (final)", display=panelDisplay)
plot(showPanel ? (lastFinalCP * 100.0) : na, "CP % (final)", display=panelDisplay)

plot(showPanel ? lastRelRLE : na, "REL RLE", display=panelDisplay)
plot(showPanel ? lastRelPE : na, "REL PE", display=panelDisplay)
plot(showPanel ? lastRelCP : na, "REL CP", display=panelDisplay)

plotchar(showPanel ? trendOk : na, "trendOk", "▲", location=location.top, size=size.tiny, color=color.new(color.lime, 0), display=panelDisplay)
plotchar(showPanel ? mrOk : na,    "mrOk",    "▼", location=location.top, size=size.tiny, color=color.new(color.aqua, 0), display=panelDisplay)

plot(showPanel ? (regScore * 100.0) : na, "RegimeScore %", display=panelDisplay)
plot(showPanel ? (regChopShr * 100.0) : na, "ChopShare %", display=panelDisplay)

alertcondition(trendOk, "RLE Trend OK", "RLE regime says TREND ok (trendOk=true)")
alertcondition(mrOk, "RLE MR OK", "RLE regime says MEAN-REVERSION ok (mrOk=true)")

Overview

Plotted in a separate pane with chart pivot overlays, it calculates path distance, execution duration, and counter-pressure to produce efficiency scores. Traders can adjust pivot detection sensitivity, weight components for path, time, and counter-pressure, and set percentile lookbacks. It also features market regime detection to classify trends or chop, alongside divergence labels on efficiency drops.

Details

Published by

casoon on GitHub

Type

Indicator, Pine Script v6

Draws

Separate pane below the chart

Category

Trend, Oscillators & momentum, Support & resistance, Divergence

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

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

Topics

pine-scriptpinescripttechnical-analysistrading-indicatorstrading-strategiestradingview

Settings

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

SettingTypeDefaultVariable
Pivot Left Barsint4leftBars
Pivot Right Barsint4rightBars
Min Leg Barsint6minLegBars
Path Source (for path length)sourcehlc3pathSrc
Direct Distance ModestringPivotPricedirectMode
Live End PricestringcloseliveEndMode
Time Factor ModestringBars (current TF)tfMode
Time Reference Minutes (TF-neutral)int120timeRefMinutes
Use Time Factor (TF) in scoreboolfalseuseTF
Counter Pressure: Body-weighted (else count)booltrueuseBodyWeighted
Ignore tiny opposite bodies (ATR multiple, 0=off)float0.0minOppBodyAtr
ATR Lengthint14atrLen
Show all 40 settings
SettingTypeDefaultVariable
Time Reference Bars (penalty after this)int24timeRefBars
Weight: Pathfloat0.50wPath
Weight: Timefloat0.20wTime
Weight: Counterfloat0.30wCounter
CP: End segment fractionfloat0.30cpEndFrac
CP: Weight end segmentfloat0.70cpEndWeight
Relative lookback legsint60relLookbackLegs
Relative modestringPercentilerelMode
Divergence labelsbooltruedivEnable
Div: min drop vs prev (%)float12.0divDropPct
Regime legs lookbackint8regLookback
Chop if PE <=float0.45regChopPeMax
Trend score min (0..1)float0.60regTrendMin
Chop score max (0..1)float0.40regChopMax
Trend filter: last leg RLE >=float60trendMinLegRLE
Trend filter: RLE percentile >=float60trendMinRelPct
MR filter: last leg RLE <=float45mrMaxLegRLE
MR filter: chop share >=float0.50mrMinChopShare
Show Chart Drawings (Lines/Labels)booltrueshowChartDrawings
Show PanelbooltrueshowPanel
Draw Leg Lines on ChartbooltrueshowLegLines
Show RLE Labels at Leg EndbooltrueshowEndLabels
Show Live RLE Label (updates)boolfalseshowLiveLabel
Label SizestringsmalllabelSize
Max Drawn Legsint120maxDrawnLegs
Append State Tag to LabelsbooltrueshowStateTag
Threshold Highfloat80thrHigh
Threshold Midfloat70thrMid
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

2 stars

This indicator plots a momentum-based ZigZag on the price chart from a QQE calculation driven by a multi-timeframe RSI. It marks bullish and bearish ZigZag segments, optional divergence and convergence between price and that RSI, and an optional Supertrend lin

Indicator v5 Oscillators & momentumDivergence
Updated 3 years ago · None stated GitHub repository of Modified Momentum QQE-ZigZag
114 stars

This indicator combines a scaled Forecast Oscillator with a Point of Force state detector. The Forecast Oscillator calculates the percentage deviation of price from its linear regression curve and normalizes the output between 0 and 1, plotting a line in a sep

Indicator v4 Oscillators & momentumSupport & resistance Repaints

WaveTrend 3D

by nersent
29 stars

WaveTrend 3D is a redesigned oscillator that implements the classic WaveTrend algorithm with kernel-based regression for trend reversal confirmation and multi-timeframe support. It calculates a smoothed oscillator line and displays a symmetrical three-dimensio

Indicator v5 Oscillators & momentumDivergence
Updated 11 months ago · Custom licence GitHub repository of WaveTrend 3D

Benso RSI

by benso87
24 stars

This indicator calculates a Relative Strength Index using Wilder or Cutler calculation methods, accompanied by an optional smoothing moving average and a signal line. Plotted in a separate pane, it features defined bull and bear control and critical threshold

Indicator v5 Oscillators & momentumDivergence
Updated 4 years ago · MPL-2.0 GitHub repository of Benso RSI

QFA Pro Elite Refined

by JasonTeixeira
9 stars

This indicator plots a smoothed fear index in a separate pane. It measures how far the current low sits below the highest close over a lookback, scales that reading by an ATR percentile, and smooths it with a zero-lag EMA. A volatility regime raises or lowers

Indicator v6 Oscillators & momentumVolatility & bands
Updated 12 days ago · Custom licence GitHub repository of QFA Pro Elite Refined

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