Free Trading Indicator: 3 Smart Steps to Get the Script

Table of Contents

The Free Trading Indicator from Shahzeb Trades is being provided as a practical charting tool for traders who want to explore the indicator without paying an upfront price for the script.

The important part is understanding what the indicator actually does before treating any signal, level, or dashboard value as part of a trading decision.

The script is written in Pine Script version 6 and is titled PREMIUM LEVEL | SHAHZEB TRADES in the source provided for this article. It is designed around support and resistance, retests, breaks, volume-based pressure calculations, zone strength, and a market-bias dashboard.
That makes this Free Trading Indicator more than a simple one-line signal tool. It contains several components intended to help organize chart information.

This article gives you the script, explains the main components, and shows how to add and test it on TradingView.

What Is This Free Trading Indicator?

The Free Trading Indicator is a TradingView Pine Script indicator that combines several charting functions into one script.

The core system identifies potential support and resistance levels using pivot highs and pivot lows. It then tracks information about those levels, including retests, breaks, volume, and a calculated strength score.

The script also calculates an approximate buying-versus-selling pressure percentage based on where the candle closes within its range. This is an approximation built into the code, not exchange-provided order-flow data.

Alongside the levels, the indicator includes a market-bias dashboard that evaluates:

  • Overall bias
  • 1H trend
  • 15M trend
  • Trending or sideways market condition
  • Recent buying and selling pressure

The dashboard uses EMA relationships and ADX-based conditions.

So the Free Trading Indicator is best understood as a chart-analysis tool that brings several pieces of market information into one workspace.

What the Script Actually Includes

Before you install the Free Trading Indicator, it helps to understand its main features.

Support and Resistance Detection

The script uses pivot highs and pivot lows to identify support and resistance candidates.

It also avoids drawing levels that are too close together and manages a list of active support and resistance objects.

Zone Strength Calculation

The indicator calculates a strength score using three components:

  • Touches or retests
  • Average touch volume compared with average volume
  • Level age or duration

The code weights these components at 45%, 30%, and 25% respectively.

Break and Retest Detection

The Free Trading Indicator can display break labels and retest labels. It also contains alert conditions for new breaks and retests.

Volume and Pressure Information

The script estimates buy and sell volume from the candle’s position within its high-low range. It also stores pressure information from retests and calculates recent pressure changes.

Live Intrabar Pressure

When price is interacting with an active level, the indicator can update a live pressure tag using the current candle information.

Market Bias Dashboard

The dashboard uses 20 EMA and 50 EMA relationships on the 1-hour and 15-minute timeframes to classify trend direction. It then combines those readings into an overall bias.

The script also uses ADX to classify the market as Trending or Sideways according to the configured threshold.

3 Smart Steps to Get the Free Trading Indicator

1. Understand the Indicator Before Using It

The first step with any Free Trading Indicator is understanding what you are actually adding to your chart.

This script contains multiple features, so avoid treating every label as a direct trade instruction.

Start with the main components:

Support and resistance:
The indicator identifies levels using pivot-based calculations.

Strength:
Each displayed level receives a calculated score based on touches, volume, and age.

Breaks and retests:
The script can mark when levels are broken and when price retests them.

Pressure:
The script provides estimated buying and selling pressure based on candle-range information.

Market bias:
The dashboard combines 1H and 15M EMA-based trend readings with an ADX market-condition reading.

A tool becomes much easier to evaluate when each output has a defined purpose.

Your Trading Tools workflow can be used to organize the indicator alongside your other charting tools.

2. Add the Script to TradingView

If you are using the source-code version, open TradingView and access the Pine Editor from the chart workspace.

Then:

  1. Open a new Pine Editor script.
  2. Remove any existing code.
  3. Paste the complete script from the section below.
  4. Save the script.
  5. Add it to the chart.
  6. Confirm that the indicator loads without compilation errors.

The script begins with //@version=6, so it is written for Pine Script version 6. The indicator declaration also specifies overlay = true, meaning its drawings are intended to appear directly on the price chart.

If TradingView reports a compilation issue after copying, check that the complete code was pasted without missing lines or altered characters.

3. Test the Free Trading Indicator Before Relying on It

The third step is the one that matters most.

Do not judge the Free Trading Indicator from one chart or a few recent signals.

Test it across different conditions.

Look at:

  • Strong trends
  • Sideways markets
  • Pullbacks
  • Breakouts
  • Multiple timeframes
  • Different symbols
  • High-volatility periods
  • Quiet market periods

The objective is to understand how the indicator behaves.

For example, a support level may receive multiple retests and accumulate a higher strength score. A resistance level may later be broken and become invalidated. The code also allows inverse levels to be created after breaks when that option is enabled.

That behavior should be studied before you build a trading routine around it.

Full Free Trading Indicator Script

The following is the complete Pine Script provided for this article.

// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/

// PREMIUM LEVEL | SHAHZEB TRADES

//@version=6

//S&R V2.12

const bool DEBUG = false

const bool fixSRs = true

const bool fixRetests = false

indicator("PREMIUM LEVEL | SHAHZEB TRADES", overlay = true, max_labels_count = 500, max_lines_count = 500, max_boxes_count = 500, dynamic_requests = true)

const int maxSRInfoListSize = 10

const int maxBarInfoListSize = 3000

const int maxDistanceToLastBar = 500

const int minSRSize = 5

const int retestLabelCooldown = 3

const float tooCloseATR = 1.0 / 8.0

const int labelOffsetBars = 20

const int atrLen = 20

atr = ta.atr(atrLen)

avgVolume = ta.sma(volume, atrLen)

var int curTFMS = timeframe.in_seconds(timeframe.period) * 1000

var map<string, bool> alerts = map.new<string, bool>()

alerts.put("Retest", false)

alerts.put("Break", false)

srPivotLength = input.int(15, "Pivot Length", minval = 3, maxval = 50, group = "General Configuration", display = display.none)

srStrength = input.int(1, "Strength", [1, 2, 3], group = "General Configuration", display = display.none)

minStrengthToShow = input.float(25, "Minimum Strength % to Show Zone", minval = 0, maxval = 100, group = "General Configuration", tooltip = "Zones scoring below this % (touches + volume + age combined) will not be drawn. Raise this to reduce clutter and only see the strongest zones. Note: freshly-formed zones start with a low age score and need time/touches to climb — keep this low if zones aren't appearing.", display = display.none)

maxZonesToShow = input.int(6, "Max Zones to Show (0 = No Limit)", minval = 0, maxval = 50, group = "General Configuration", tooltip = "Caps how many S&R zones are drawn at once, starting with the most recently formed. Set to 0 for no limit.", display = display.none)

liveTouchToleranceMult = input.float(0.3, "Live Pressure Touch Sensitivity (x ATR)", minval = 0.05, maxval = 2.0, step = 0.05, group = "General Configuration", tooltip = "How close price needs to be to a zone for the LIVE buy/sell pressure tag to appear and update. Raise this if the live tag isn't showing up.", display = display.none)

srInvalidation = input.string("Close", "Invalidation", ["Wick", "Close"], group = "General Configuration", display = display.none)

expandZones = input.string("Only Valid", "Expand Lines & Zones", options = ["All", "Only Valid", "None"], group = "General Configuration", display = display.none)

showInvalidated = input.bool(true, "Show Invalidated", group = "General Configuration", display = display.none)

timeframe1Enabled = input.bool(true, title = "", group = "Timeframes", inline = "timeframe1", display = display.none)

timeframe1 = input.timeframe("", title = "", group = "Timeframes", inline = "timeframe1", display = display.none)

timeframe2Enabled = input.bool(false, title = "", group = "Timeframes", inline = "timeframe2", display = display.none)

timeframe2 = input.timeframe("D", title = "", group = "Timeframes", inline = "timeframe2", display = display.none)

timeframe3Enabled = input.bool(false, title = "", group = "Timeframes", inline = "timeframe3", display = display.none)

timeframe3 = input.timeframe("W", title = "", group = "Timeframes", inline = "timeframe3", display = display.none)

showBreaks = input.bool(true, "Show Breaks", group = "Breaks & Retests", inline = "ShowBR", display = display.none)

showRetests = input.bool(true, "Show Retests", group = "Breaks & Retests", inline = "ShowBR", display = display.none)

avoidFalseBreaks = input.bool(false, "Avoid False Breaks", group = "Breaks & Retests", display = display.none)

breakVolumeThreshold = input.float(0.3, "Break Volume Threshold", minval = 0.1, maxval = 2.0, step = 0.1, group = "Breaks & Retests", tooltip = "Only taken into account if Avoid False Breakouts is enabled.\nHigher values mean it's less likely to be a break.", display = display.none)

inverseBrokenLineColor = input.bool(false, "Inverse Color After Broken", group = "Breaks & Retests", display = display.none)

styleMode = input.string("Lines", "Style", ["Lines", "Zones"], group = "Style", display = display.none)

lineStyle = input.string("____", "Line Style", ["____", "----", "...."], group = "Style", display = display.none)

lineWidth = input.int(2, "Line Width", minval = 1, group = "Style", display = display.none)

zoneSize = input.float(1.0, "Zone Width", minval = 0.1, maxval = 10, step = 0.1, group = "Style", display = display.none)

zoneSizeATR = zoneSize * 0.075

supportColor = input.color(#08998180, "Support Color", group = "Style", inline = "RScolors", display = display.none)

resistanceColor = input.color(#f2364580, "Resistance Color", group = "Style", inline = "RScolors", display = display.none)

breakColor = input.color(color.blue, "Break Color", group = "Style", inline = "RScolors2", display = display.none)

textColor = input.color(#ffffff80, "Text Color", group = "Style", inline = "RScolors2", display = display.none)

enableRetestAlerts = input.bool(true, "Enable Retest Alerts", tooltip = "Needs Show Retests option enabled.", group = "Alerts", display = display.none)

enableBreakAlerts = input.bool(true, "Enable Break Alerts", tooltip = "Needs Show Breaks option enabled.", group = "Alerts", display = display.none)

// ============ MARKET BIAS DASHBOARD INPUTS ============

showBiasBox = input.bool(true, "Show Market Bias Box", group = "Market Bias Dashboard")

biasBoxPosition = input.string("Top Left", "Box Position", ["Top Left", "Middle Left", "Bottom Left"], group = "Market Bias Dashboard")

adxLength = input.int(14, "ADX Length (Trend Strength)", group = "Market Bias Dashboard")

adxThreshold = input.float(20, "ADX Trending Threshold", group = "Market Bias Dashboard")

pressureLookback = input.int(20, "Pressure Lookback Bars", group = "Market Bias Dashboard")

insideBounds = (bar_index > last_bar_index - maxDistanceToLastBar)

type srInfo

    int startTime

    float price

    string srType

    int strength

    string timeframeStr

    bool ephemeral = false

    int breakTime

    array<int> retestTimes

    float formationVolume = na

    float volumeSum = 0.0

    float buyVolumeSum = 0.0

    float sellVolumeSum = 0.0

    array<float> retestBuyVols

    array<float> retestSellVols

type srObj

    srInfo info

    bool startFixed

    bool breakFixed

    bool rendered

    string combinedTimeframeStr

    line srLine

    box srBox

    label srLabel

    label breakLabel

    array<label> retestLabels

    string baseLabelText = na

type barInfo

    int t

    int tc

    float c

    float h

    float l

    float v

    float buyVol

    float sellVol

var allSRList = array.new<srObj>()

var bool needsRebuild = true

//#region Find Val RTN Time

findValRtnTime (barInfo[] biList, valToFind, toSearch, searchMode, minTime, maxTime, int defVal = na) =>

    int rtnTime = defVal

    float minDiff = na

    if biList.size() > 0

        for i = biList.size() - 1 to 0

            curBI = biList.get(i)

            if curBI.t >= minTime and curBI.t < maxTime

                toLook = (toSearch == "Low" ? curBI.l : toSearch == "High" ? curBI.h : curBI.c)

                if searchMode == "Nearest"

                    curDiff = math.abs(valToFind - toLook)

                    if na(minDiff)

                        rtnTime := curBI.t

                        minDiff := curDiff

                    else

                        if curDiff <= minDiff

                            minDiff := curDiff

                            rtnTime := curBI.t

                if searchMode == "Higher"

                    if toLook >= valToFind

                        rtnTime := curBI.t

                        break

                if searchMode == "Lower"

                    if toLook <= valToFind

                        rtnTime := curBI.t

                        break

    rtnTime

//#endregion

formatTimeframeString (string formatTimeframe, bool short = false) =>

    timeframeF = (formatTimeframe == "" ? timeframe.period : formatTimeframe)

    if str.contains(timeframeF, "D") or str.contains(timeframeF, "W") or str.contains(timeframeF, "S") or str.contains(timeframeF, "M")

        timeframe.from_seconds(timeframe.in_seconds(timeframeF))

    else

        seconds = timeframe.in_seconds(timeframeF)

        if seconds >= 3600

            hourCount = int(seconds / 3600)

            if short

                str.tostring(hourCount) + "h"

            else

                str.tostring(hourCount) + " Hour" + (hourCount > 1 ? "s" : "")

        else

            if short

                timeframeF + "m"

            else

                timeframeF + " Min"

// ============ STRENGTH % CALCULATION ============

 // Combines: touches/retests (45%), avg touch volume vs average volume (30%), level age/duration held (25%)

calcSRStrength (srInfo info) =>

    touches = info.strength

    touchFactor = math.min(touches / 5.0, 1.0) * 100

    totalVol = nz(info.formationVolume) + info.volumeSum

    avgTouchVol = touches > 0 ? totalVol / touches : nz(info.formationVolume)

    volFactor = math.min(avgTouchVol / avgVolume, 2.0) / 2.0 * 100

    endPoint = nz(info.breakTime, time)

    ageBars = (endPoint - info.startTime) / curTFMS

    ageFactor = math.min(ageBars / 50.0, 1.0) * 100

    score = touchFactor * 0.45 + volFactor * 0.30 + ageFactor * 0.25

    math.round(math.min(score, 100))

// Returns buyer percentage (0-100). Seller % = 100 - buyer %.

// Approximates buy/sell pressure from where price closed within each bar's range (close near high = buying pressure, close near low = selling pressure).

calcSRDelta (srInfo info) =>

    totalDelta = info.buyVolumeSum + info.sellVolumeSum

    buyPct = totalDelta > 0 ? (info.buyVolumeSum / totalDelta) * 100 : 50.0

    math.round(buyPct)

// Buyer % using only the last N touches (recent trend, not full history)

calcSRRecentDelta (srInfo info, int n) =>

    cnt = math.min(n, info.retestBuyVols.size())

    recentBuy = 0.0

    recentSell = 0.0

    if cnt > 0

        for i = 0 to cnt - 1

            recentBuy += info.retestBuyVols.get(i)

            recentSell += info.retestSellVols.get(i)

    totalRecent = recentBuy + recentSell

    totalRecent > 0 ? math.round((recentBuy / totalRecent) * 100) : na

// Compares recent pressure vs overall pressure to detect a shift (e.g. buyers fading, sellers taking over)

calcPressureShift (srInfo info) =>

    overallBuy = calcSRDelta(info)

    recentBuy = calcSRRecentDelta(info, 3)

    string shiftTag = ""

    if not na(recentBuy) and info.retestBuyVols.size() >= 2

        diff = recentBuy - overallBuy

        if diff >= 15

            shiftTag := " | 🔴→🟢 Buyers Building"

        else if diff <= -15

            shiftTag := " | 🟢→🔴 Sellers Building"

        else

            shiftTag := " | ⚖ Stable"

    shiftTag

// Formats raw volume numbers into readable K/M form (e.g. 12500 -> 12.5K)

formatVol (float v) =>

    v >= 1000000 ? str.tostring(math.round(v / 100000) / 10) + "M" : v >= 1000 ? str.tostring(math.round(v / 100) / 10) + "K" : str.tostring(math.round(v))

renderSRObj (srObj sr) =>

    if na(sr.info.breakTime) or showInvalidated

        sr.rendered := true

        endTime = nz(sr.info.breakTime, time + curTFMS * labelOffsetBars)

        extendType = extend.none

        if na(sr.info.breakTime)

            extendType := extend.right

        if expandZones == "Only Valid" and na(sr.info.breakTime)

            extendType := extend.both

        else if expandZones == "All"

            extendType := extend.both

            endTime := time + curTFMS * labelOffsetBars

        labelTitle = formatTimeframeString(sr.info.timeframeStr)

        if not na(sr.combinedTimeframeStr)

            labelTitle := sr.combinedTimeframeStr

        labelTitle += " | " + str.tostring(sr.info.price, format.mintick) + ((sr.info.ephemeral and DEBUG) ? " [E]" : "")

        srStrengthScore = calcSRStrength(sr.info)

        srStrengthTag = srStrengthScore >= 85 ? "EXTREME" : srStrengthScore >= 70 ? "STRONG" : srStrengthScore < 40 ? "WEAK" : "MEDIUM"

        buyerPct = calcSRDelta(sr.info)

        sellerPct = 100 - buyerPct

        recentBuyPct = calcSRRecentDelta(sr.info, 3)

        shiftTag = calcPressureShift(sr.info)

        labelTitle += " | " + str.tostring(srStrengthScore) + "% " + srStrengthTag

        labelTitle += " | 🟢" + str.tostring(buyerPct) + "% 🔴" + str.tostring(sellerPct) + "%"

        labelTitle += " | Vol 🟢" + formatVol(sr.info.buyVolumeSum) + " 🔴" + formatVol(sr.info.sellVolumeSum)

        if not na(recentBuyPct)

            labelTitle += " | Last3 🟢" + str.tostring(recentBuyPct) + "% 🔴" + str.tostring(100 - recentBuyPct) + "%" + shiftTag

        if styleMode == "Lines"

            // Line

            sr.srLine := line.new(sr.info.startTime, sr.info.price, endTime, sr.info.price, xloc = xloc.bar_time, color = sr.info.srType == "Resistance" ? resistanceColor : supportColor, width = lineWidth, style = lineStyle == "----" ? line.style_dashed : lineStyle == "...." ? line.style_dotted : line.style_solid, extend = extendType)

            // Label

            sr.srLabel := label.new(extendType == extend.none ? ((sr.info.startTime + endTime) / 2) : endTime, sr.info.price, xloc = xloc.bar_time, text = labelTitle, textcolor = textColor, style = label.style_none)

            sr.baseLabelText := labelTitle

        else

            // Zone

            sr.srBox := box.new(sr.info.startTime, sr.info.price + atr * zoneSizeATR, endTime, sr.info.price - atr * zoneSizeATR, xloc = xloc.bar_time, bgcolor = sr.info.srType == "Resistance" ? resistanceColor : supportColor, border_color = na, text = labelTitle, text_color = textColor, extend = extendType, text_size = size.normal, text_halign = (extendType != extend.none) ? text.align_right : text.align_center)

            sr.baseLabelText := labelTitle

        // Break Label

        if showBreaks

            if not na(sr.info.breakTime)

                sr.breakLabel := label.new(sr.info.breakTime, sr.info.price, "B", yloc = sr.info.srType == "Resistance" ? yloc.belowbar : yloc.abovebar, style = sr.info.srType == "Resistance" ? label.style_label_up : label.style_label_down, color = breakColor, textcolor = color.new(textColor, 0), xloc = xloc.bar_time, size = size.small)

                if (time - curTFMS <= sr.info.breakTime) and (time + curTFMS >= sr.info.breakTime)

                    alerts.put("Break", true)

        // Retest Labels

        if showRetests

            if sr.info.retestTimes.size() > 0

                for i = sr.info.retestTimes.size() - 1 to 0

                    curRetestTime = sr.info.retestTimes.get(i)

                    cooldownOK = true

                    if sr.retestLabels.size() > 0

                        lastLabel = sr.retestLabels.get(0)

                        if math.abs(lastLabel.get_x() - curRetestTime) < curTFMS * retestLabelCooldown

                            cooldownOK := false

                    if cooldownOK and (curRetestTime >= sr.info.startTime) and (na(sr.info.breakTime) or curRetestTime < sr.info.breakTime)

                        if time - curTFMS <= curRetestTime and time >= curRetestTime

                            alerts.put("Retest", true)

                        sr.retestLabels.unshift(label.new(curRetestTime, sr.info.price, "R" + (DEBUG ? (" " + str.tostring(sr.info.price)) : ""), yloc = sr.info.srType == "Resistance" ? yloc.abovebar : yloc.belowbar, style = sr.info.srType == "Resistance" ? label.style_label_down : label.style_label_up, color = sr.info.srType == "Resistance" ? resistanceColor : supportColor, textcolor = color.new(textColor, 0), xloc = xloc.bar_time, size = size.small))

safeDeleteSRObj (srObj sr) =>

    if sr.rendered

        line.delete(sr.srLine)

        box.delete(sr.srBox)

        label.delete(sr.srLabel)

        label.delete(sr.breakLabel)

        if sr.retestLabels.size() > 0

            for i = 0 to sr.retestLabels.size() - 1

                curRetestLabel = sr.retestLabels.get(i)

                label.delete(curRetestLabel)

        sr.rendered := false

var allSRInfoList = array.new<srInfo>()

var barInfoList = array.new<barInfo>()

pivotHigh = ta.pivothigh(srPivotLength, srPivotLength)

pivotLow = ta.pivotlow(srPivotLength, srPivotLength)

curBarRange = high - low

curBuyVol = curBarRange != 0 ? volume * (close - low) / curBarRange : volume * 0.5

curSellVol = curBarRange != 0 ? volume * (high - close) / curBarRange : volume * 0.5

barInfoList.unshift(barInfo.new(time, time_close, close, high, low, volume, curBuyVol, curSellVol))

if barInfoList.size() > maxBarInfoListSize

    barInfoList.pop()

if insideBounds and barstate.isconfirmed

    // Find Supports

    if not na(pivotLow)

        validSR = true

        if allSRInfoList.size() > 0

            for i = 0 to allSRInfoList.size() - 1

                curRSInfo = allSRInfoList.get(i)

                if (math.abs(curRSInfo.price - pivotLow) < atr * tooCloseATR) and na(curRSInfo.breakTime)

                    validSR := false

                    break

        if validSR

            newSRInfo = srInfo.new(barInfoList.get(srPivotLength).t, pivotLow, "Support", 1, timeframe.period)

            newSRInfo.retestTimes := array.new<int>()

            newSRInfo.retestBuyVols := array.new<float>()

            newSRInfo.retestSellVols := array.new<float>()

            newSRInfo.formationVolume := barInfoList.get(srPivotLength).v

            newSRInfo.buyVolumeSum := barInfoList.get(srPivotLength).buyVol

            newSRInfo.sellVolumeSum := barInfoList.get(srPivotLength).sellVol

            allSRInfoList.unshift(newSRInfo)

            while allSRInfoList.size() > maxSRInfoListSize

                allSRInfoList.pop()

            needsRebuild := true

    // Find Resistances

    if not na(pivotHigh)

        validSR = true

        if allSRInfoList.size() > 0

            for i = 0 to allSRInfoList.size() - 1

                curRSInfo = allSRInfoList.get(i)

                if (math.abs(curRSInfo.price - pivotLow) < atr * tooCloseATR) and na(curRSInfo.breakTime)

                    validSR := false

                    break

        if validSR

            newSRInfo = srInfo.new(barInfoList.get(srPivotLength).t, pivotHigh, "Resistance", 1, timeframe.period)

            newSRInfo.retestTimes := array.new<int>()

            newSRInfo.retestBuyVols := array.new<float>()

            newSRInfo.retestSellVols := array.new<float>()

            newSRInfo.formationVolume := barInfoList.get(srPivotLength).v

            newSRInfo.buyVolumeSum := barInfoList.get(srPivotLength).buyVol

            newSRInfo.sellVolumeSum := barInfoList.get(srPivotLength).sellVol

            allSRInfoList.unshift(newSRInfo)

            if allSRInfoList.size() > maxSRInfoListSize

                allSRInfoList.pop()

            needsRebuild := true

// Handle SR Infos

if insideBounds and (srInvalidation == "Wick" or barstate.isconfirmed)

    if allSRInfoList.size() > 0

        for i = 0 to allSRInfoList.size() - 1

            srInfo curSRInfo = allSRInfoList.get(i)

            // Breaks

            invHigh = (srInvalidation == "Close" ? close : high)

            invLow = (srInvalidation == "Close" ? close : low)

            closeTime = time

            if na(curSRInfo.breakTime)

                if curSRInfo.srType == "Resistance" and invHigh > curSRInfo.price

                    if (not avoidFalseBreaks) or (volume > avgVolume * breakVolumeThreshold)

                        curSRInfo.breakTime := closeTime

                        needsRebuild := true

                        if inverseBrokenLineColor and (not curSRInfo.ephemeral) and curSRInfo.strength >= srStrength

                            ephSR = srInfo.new(closeTime, curSRInfo.price, "Support", curSRInfo.strength, curSRInfo.timeframeStr, true)

                            ephSR.retestTimes := array.new<int>()

                            ephSR.retestBuyVols := array.new<float>()

                            ephSR.retestSellVols := array.new<float>()

                            allSRInfoList.unshift(ephSR)

                else if curSRInfo.srType == "Support" and invLow < curSRInfo.price

                    if (not avoidFalseBreaks) or (volume > avgVolume * breakVolumeThreshold)

                        curSRInfo.breakTime := closeTime

                        needsRebuild := true

                        if inverseBrokenLineColor and (not curSRInfo.ephemeral) and curSRInfo.strength >= srStrength

                            ephSR = srInfo.new(closeTime, curSRInfo.price, "Resistance", curSRInfo.strength, curSRInfo.timeframeStr, true)

                            ephSR.retestTimes := array.new<int>()

                            ephSR.retestBuyVols := array.new<float>()

                            ephSR.retestSellVols := array.new<float>()

                            allSRInfoList.unshift(ephSR)

// Strength & Retests

            curBarRangeR = high - low

            curBuyVolR = curBarRangeR != 0 ? volume * (close - low) / curBarRangeR : volume * 0.5

            curSellVolR = curBarRangeR != 0 ? volume * (high - close) / curBarRangeR : volume * 0.5

            if na(curSRInfo.breakTime) and time > curSRInfo.startTime and barstate.isconfirmed

                if curSRInfo.srType == "Resistance" and high >= curSRInfo.price and close <= curSRInfo.price

                    int lastRetestTime = 0

                    if curSRInfo.retestTimes.size() > 0

                        lastRetestTime := curSRInfo.retestTimes.get(0)

                    if lastRetestTime != time

                        if not curSRInfo.ephemeral

                            curSRInfo.strength += 1

                            curSRInfo.volumeSum += volume

                            curSRInfo.buyVolumeSum += curBuyVolR

                            curSRInfo.sellVolumeSum += curSellVolR

                            curSRInfo.retestBuyVols.unshift(curBuyVolR)

                            curSRInfo.retestSellVols.unshift(curSellVolR)

                            needsRebuild := true

                        curSRInfo.retestTimes.unshift(time)

                else if curSRInfo.srType == "Support" and low <= curSRInfo.price and close >= curSRInfo.price

                    int lastRetestTime = 0

                    if curSRInfo.retestTimes.size() > 0

                        lastRetestTime := curSRInfo.retestTimes.get(0)

                    if lastRetestTime != time

                        if not curSRInfo.ephemeral

                            curSRInfo.strength += 1

                            curSRInfo.volumeSum += volume

                            curSRInfo.buyVolumeSum += curBuyVolR

                            curSRInfo.sellVolumeSum += curSellVolR

                            curSRInfo.retestBuyVols.unshift(curBuyVolR)

                            curSRInfo.retestSellVols.unshift(curSellVolR)

                            needsRebuild := true

                        curSRInfo.retestTimes.unshift(time)

fixSRToTimeframe (srObj sr) =>

    srMS = math.max(timeframe.in_seconds(sr.info.timeframeStr), timeframe.in_seconds()) * 1000

    if (not sr.startFixed)

        if not sr.info.ephemeral

            if sr.info.srType == "Resistance"

                sr.info.startTime := findValRtnTime(barInfoList, sr.info.price, "High", "Nearest", sr.info.startTime - srMS, sr.info.startTime + srMS, sr.info.startTime)

            else

                sr.info.startTime := findValRtnTime(barInfoList, sr.info.price, "Low", "Nearest", sr.info.startTime - srMS, sr.info.startTime + srMS, sr.info.startTime)

            sr.startFixed := true

        else

            if allSRList.size() > 0

                for i = 0 to allSRList.size() - 1

                    curSR = allSRList.get(i)

                    if (not curSR.info.ephemeral) and (not na(curSR.info.breakTime)) and curSR.info.price == sr.info.price and ((sr.info.srType == "Resistance" and curSR.info.srType == "Support") or (sr.info.srType == "Support" and curSR.info.srType == "Resistance"))

                        if curSR.breakFixed

                            sr.info.startTime := curSR.info.breakTime

                            sr.startFixed := true

                        break

    if not na(sr.info.breakTime)

        if (not sr.breakFixed)

            if sr.info.srType == "Resistance"

                sr.info.breakTime := findValRtnTime(barInfoList, sr.info.price, srInvalidation == "Wick" ? "High" : "Close", "Higher", sr.info.breakTime - srMS, sr.info.breakTime + srMS, sr.info.breakTime)

            else

                sr.info.breakTime := findValRtnTime(barInfoList, sr.info.price, srInvalidation == "Wick" ? "Low" : "Close", "Lower", sr.info.breakTime - srMS, sr.info.breakTime + srMS, sr.info.breakTime)

            sr.breakFixed := true

    if sr.info.retestTimes.size() > 0 and fixRetests

        for i = 0 to sr.info.retestTimes.size() - 1

            curRetestTime = sr.info.retestTimes.get(i)

            retestStartTime = curRetestTime - srMS

            retestStartTime := math.max(retestStartTime, sr.info.startTime + 1)

            retestEndTime = curRetestTime + srMS

            if not na(sr.info.breakTime)

                retestEndTime := math.min(retestEndTime, sr.info.breakTime - 1)

            if sr.info.srType == "Resistance"

                sr.info.retestTimes.set(i, findValRtnTime(barInfoList, sr.info.price, "High", "Higher", retestStartTime, retestEndTime, sr.info.retestTimes.get(i)))

            else

                sr.info.retestTimes.set(i, findValRtnTime(barInfoList, sr.info.price, "Low", "Lower", retestStartTime, retestEndTime, sr.info.retestTimes.get(i)))

getSR (srObj[] list, srPrice, eph, srType, timeframeStr) =>

    srObj rtnSR = na

    if list.size() > 0

        for i = 0 to list.size() - 1

            curSR = list.get(i)

            if curSR.info.price == srPrice and curSR.info.ephemeral == eph and curSR.info.srType == srType and curSR.info.timeframeStr == timeframeStr

                rtnSR := curSR

                break

    rtnSR

// Handle SR

handleTF (tfStr, tfEnabled) =>

    if tfEnabled

        // FIX (delayed-zone bug): when the requested timeframe is the chart's own timeframe,

        // read allSRInfoList directly instead of round-tripping it through request.security().

        // request.security() on the same timeframe re-enters its own execution context and can

        // lag the render list a bar behind the detection list — that lag is exactly why zones

        // were only appearing after price had already touched and moved away from the level.

        // This changes nothing about what is detected or how it's drawn — only removes the

        // unnecessary, lag-inducing round-trip for the same-timeframe case. Other/higher

        // timeframes still go through request.security() exactly as before.

        tfSRInfoList = (tfStr == "" or tfStr == timeframe.period) ? allSRInfoList : request.security(syminfo.tickerid, tfStr, allSRInfoList)

        if not na(tfSRInfoList) and tfSRInfoList.size() > 0

            for i = 0 to tfSRInfoList.size() - 1

                srInfo curSRInfo = tfSRInfoList.get(i)

                if fixSRs

                    currentSameSR = getSR(allSRList, curSRInfo.price, curSRInfo.ephemeral, curSRInfo.srType, curSRInfo.timeframeStr)

                    if not na(currentSameSR)

                        if currentSameSR.startFixed

                            curSRInfo.startTime := currentSameSR.info.startTime

                        if currentSameSR.breakFixed

                            curSRInfo.breakTime := currentSameSR.info.breakTime

                        curSRInfo.retestTimes := currentSameSR.info.retestTimes

                        curSRInfo.retestBuyVols := currentSameSR.info.retestBuyVols

                        curSRInfo.retestSellVols := currentSameSR.info.retestSellVols

                        // All other info should be replaced except fixed start, break and all retests.

                        currentSameSR.info := curSRInfo

                        if not currentSameSR.breakFixed

                            fixSRToTimeframe(currentSameSR)

                    else

                        srObj newSRObj = srObj.new(curSRInfo)

                        // We handle retests in current timeframe so no need to get them from upper.

                        newSRObj.info.retestTimes := array.new<int>()

                        newSRObj.info.retestBuyVols := array.new<float>()

                        newSRObj.info.retestSellVols := array.new<float>()

                        newSRObj.retestLabels := array.new<label>()

                        fixSRToTimeframe(newSRObj)

                        allSRList.unshift(newSRObj)

                else

                    srObj newSRObj = srObj.new(curSRInfo)

                    newSRObj.retestLabels := array.new<label>()

                    allSRList.unshift(newSRObj)

    true

if (bar_index > last_bar_index - maxDistanceToLastBar * 8) and barstate.isconfirmed and (needsRebuild or barstate.islast)

    if not fixSRs

        if allSRList.size() > 0

            for i = 0 to allSRList.size() - 1

                srObj curSRObj = allSRList.get(i)

                safeDeleteSRObj(curSRObj)

        allSRList.clear()

    handleTF(timeframe1, timeframe1Enabled)

    handleTF(timeframe2, timeframe2Enabled)

    handleTF(timeframe3, timeframe3Enabled)

    if allSRList.size() > 0

        // Capture which zones were already showing BEFORE we clear drawings (for sticky priority)

        var array<bool> wasShown = array.new<bool>()

        array.clear(wasShown)

        for i = 0 to allSRList.size() - 1

            array.push(wasShown, allSRList.get(i).rendered)

        // Pass 1: clear ALL existing drawings first so nothing stale lingers

        for i = 0 to allSRList.size() - 1

            safeDeleteSRObj(allSRList.get(i))

        // Build a render order: zones that were ALREADY showing (and are still active/unbroken) keep

        // their slot first (sticky, prevents flicker from being bumped by a newer stronger zone).

        // Remaining slots are filled by strength, strongest first.

        var array<int> renderOrder = array.new<int>()

        array.clear(renderOrder)

        var array<bool> usedIdx = array.new<bool>()

        array.clear(usedIdx)

        for i = 0 to allSRList.size() - 1

            array.push(usedIdx, false)

        for i = 0 to allSRList.size() - 1

            if array.get(wasShown, i) and na(allSRList.get(i).info.breakTime)

                array.push(renderOrder, i)

                array.set(usedIdx, i, true)

        for k = 0 to allSRList.size() - 1

            bestIdx = -1

            bestScore = -1.0

            for i = 0 to allSRList.size() - 1

                if not array.get(usedIdx, i)

                    sc = calcSRStrength(allSRList.get(i).info)

                    if sc > bestScore

                        bestScore := sc

                        bestIdx := i

            if bestIdx != -1

                array.push(renderOrder, bestIdx)

                array.set(usedIdx, bestIdx, true)

        // Pass 2: render in the order above (same tooClose logic as before), stop once cap is hit

        renderedCount = 0

        for k = 0 to renderOrder.size() - 1

            idx = array.get(renderOrder, k)

            srObj curSRObj = allSRList.get(idx)

            tooClose = false

            for j = 0 to allSRList.size() - 1

                closeSR = allSRList.get(j)

                if closeSR.rendered and math.abs(closeSR.info.price - curSRObj.info.price) <= tooCloseATR * atr and closeSR.info.srType == curSRObj.info.srType and closeSR.info.ephemeral == curSRObj.info.ephemeral

                    tooClose := true

                    if not str.contains((na(closeSR.combinedTimeframeStr) ? formatTimeframeString(closeSR.info.timeframeStr) : closeSR.combinedTimeframeStr), formatTimeframeString(curSRObj.info.timeframeStr))

                        if na(closeSR.combinedTimeframeStr)

                            closeSR.combinedTimeframeStr := formatTimeframeString(closeSR.info.timeframeStr) + " & " + formatTimeframeString(curSRObj.info.timeframeStr)

                        else

                            closeSR.combinedTimeframeStr += " & " + formatTimeframeString(curSRObj.info.timeframeStr)

                    break

            if (curSRObj.info.strength >= srStrength) and (na(curSRObj.info.breakTime) or (curSRObj.info.breakTime - curSRObj.info.startTime) >= minSRSize * curTFMS) and (not tooClose) and (calcSRStrength(curSRObj.info) >= minStrengthToShow)

                renderSRObj(curSRObj)

                renderedCount += 1

                if maxZonesToShow > 0 and renderedCount >= maxZonesToShow

                    break

    needsRebuild := false

// Current Timeframe Retests

if allSRList.size() > 0 and barstate.isconfirmed

    for i = 0 to allSRList.size() - 1

        srObj curSR = allSRList.get(i)

        if na(curSR.info.breakTime) and time > curSR.info.startTime

            if curSR.info.srType == "Resistance" and high >= curSR.info.price and close <= curSR.info.price

                int lastRetestTime = 0

                if curSR.info.retestTimes.size() > 0

                    lastRetestTime := curSR.info.retestTimes.get(0)

                if lastRetestTime != time

                    curSR.info.retestTimes.unshift(time)

            else if curSR.info.srType == "Support" and low <= curSR.info.price and close >= curSR.info.price

                int lastRetestTime = 0

                if curSR.info.retestTimes.size() > 0

                    lastRetestTime := curSR.info.retestTimes.get(0)

                if lastRetestTime != time

                    curSR.info.retestTimes.unshift(time)

// ============ LIVE INTRABAR PRESSURE (updates every tick, not just on candle close) ============

// Only touches zones price is currently sitting at, so this stays cheap even on unconfirmed/realtime ticks.

if allSRList.size() > 0

    liveBarRange = high - low

    liveBuyVol = liveBarRange != 0 ? volume * (close - low) / liveBarRange : volume * 0.5

    liveSellVol = liveBarRange != 0 ? volume * (high - close) / liveBarRange : volume * 0.5

    liveTotalVol = liveBuyVol + liveSellVol

    liveBuyPct = liveTotalVol > 0 ? math.round(liveBuyVol / liveTotalVol * 100) : 50

    liveSellPct = 100 - liveBuyPct

    liveTouchTolerance = atr * liveTouchToleranceMult

    for i = 0 to allSRList.size() - 1

        liveSR = allSRList.get(i)

        if liveSR.rendered and na(liveSR.info.breakTime) and not na(liveSR.baseLabelText)

            isTouching = math.abs(close - liveSR.info.price) <= liveTouchTolerance or (low - liveTouchTolerance <= liveSR.info.price and high + liveTouchTolerance >= liveSR.info.price)

            if isTouching

                liveTag = " | LIVE 🟢" + str.tostring(liveBuyPct) + "% 🔴" + str.tostring(liveSellPct) + "%"

                if styleMode == "Lines" and not na(liveSR.srLabel)

                    label.set_text(liveSR.srLabel, liveSR.baseLabelText + liveTag)

                else if styleMode == "Zones" and not na(liveSR.srBox)

                    box.set_text(liveSR.srBox, liveSR.baseLabelText + liveTag)

// ============ MARKET BIAS DASHBOARD LOGIC ============

getTrend(simple string tf) =>

    [ema20, ema50, curClose] = request.security(syminfo.tickerid, tf, [ta.ema(close, 20), ta.ema(close, 50), close], lookahead = barmerge.lookahead_off)

    string trend = "Neutral"

    if curClose > ema20 and ema20 > ema50

        trend := "Bullish"

    else if curClose < ema20 and ema20 < ema50

        trend := "Bearish"

    trend

getOverallBias(t1h, t15m) =>

    string bias = "Neutral"

    if t1h == "Bullish" and t15m == "Bullish"

        bias := "Strong Bullish"

    else if t1h == "Bearish" and t15m == "Bearish"

        bias := "Strong Bearish"

    else if t1h == "Bullish" or t15m == "Bullish"

        bias := "Mild Bullish"

    else if t1h == "Bearish" or t15m == "Bearish"

        bias := "Mild Bearish"

    bias

getTrendColor(t) =>

    t == "Bullish" ? color.new(color.green, 0) : t == "Bearish" ? color.new(color.red, 0) : color.new(color.gray, 0)

getBiasColor(b) =>

    b == "Strong Bullish" ? color.new(color.green, 0) : b == "Mild Bullish" ? color.new(color.lime, 20) : b == "Strong Bearish" ? color.new(color.red, 0) : b == "Mild Bearish" ? color.new(color.orange, 20) : color.new(color.gray, 0)

// Trend on 1H and 15M

trend1H = getTrend("60")

trend15M = getTrend("15")

overallBias = getOverallBias(trend1H, trend15M)

// Market condition (Trending vs Sideways) via ADX on current chart timeframe

[diPlus, diMinus, adxVal] = ta.dmi(adxLength, adxLength)

marketCondition = adxVal >= adxThreshold ? "Trending" : "Sideways"

// Buy/Sell pressure using approximate volume from recent bars

var float pressureBuySum = 0.0

var float pressureSellSum = 0.0

if barInfoList.size() >= pressureLookback

    pressureBuySum := 0.0

    pressureSellSum := 0.0

    for i = 0 to pressureLookback - 1

        bi = barInfoList.get(i)

        pressureBuySum += bi.buyVol

        pressureSellSum += bi.sellVol

totalPressureVol = pressureBuySum + pressureSellSum

buyPressurePct = totalPressureVol > 0 ? math.round((pressureBuySum / totalPressureVol) * 100) : 50

sellPressurePct = 100 - buyPressurePct

var table biasTable = table.new(biasBoxPosition == "Top Left" ? position.top_left : biasBoxPosition == "Middle Left" ? position.middle_left : position.bottom_left, 2, 6, border_width = 1, border_color = color.new(color.gray, 50), frame_color = color.new(color.gray, 30), frame_width = 1)

if showBiasBox and barstate.islast

    table.cell(biasTable, 0, 0, "MARKET BIAS", text_color = color.white, bgcolor = color.new(color.blue, 60), text_size = size.small, text_halign = text.align_center)

    table.merge_cells(biasTable, 0, 0, 1, 0)

    table.cell(biasTable, 0, 1, "Overall Bias", text_color = color.white, bgcolor = color.new(color.black, 70), text_size = size.small)

    table.cell(biasTable, 1, 1, overallBias, text_color = getBiasColor(overallBias), bgcolor = color.new(color.black, 70), text_size = size.small, text_halign = text.align_right)

    table.cell(biasTable, 0, 2, "1H Trend", text_color = color.white, bgcolor = color.new(color.black, 70), text_size = size.small)

    table.cell(biasTable, 1, 2, trend1H, text_color = getTrendColor(trend1H), bgcolor = color.new(color.black, 70), text_size = size.small, text_halign = text.align_right)

    table.cell(biasTable, 0, 3, "15M Trend", text_color = color.white, bgcolor = color.new(color.black, 70), text_size = size.small)

    table.cell(biasTable, 1, 3, trend15M, text_color = getTrendColor(trend15M), bgcolor = color.new(color.black, 70), text_size = size.small, text_halign = text.align_right)

    table.cell(biasTable, 0, 4, "Market Condition", text_color = color.white, bgcolor = color.new(color.black, 70), text_size = size.small)

    table.cell(biasTable, 1, 4, marketCondition, text_color = marketCondition == "Trending" ? color.new(color.yellow, 0) : color.new(color.gray, 0), bgcolor = color.new(color.black, 70), text_size = size.small, text_halign = text.align_right)

    table.cell(biasTable, 0, 5, "Pressure", text_color = color.white, bgcolor = color.new(color.black, 70), text_size = size.small)

    table.cell(biasTable, 1, 5, str.tostring(buyPressurePct) + "%🟢 / " + str.tostring(sellPressurePct) + "%🔴", text_color = buyPressurePct > sellPressurePct ? color.new(color.green, 0) : color.new(color.red, 0), bgcolor = color.new(color.black, 70), text_size = size.small, text_halign = text.align_right)

//plotchar(alerts.get("Break") ? high : na, "", "✅", size = size.normal)

//plotchar(alerts.get("Retest") ? high : na, "", "❤️", size = size.normal, location = location.belowbar)

alertcondition(alerts.get("Retest"), "New Retest", "")

alertcondition(alerts.get("Break"), "New Break", "")

if enableRetestAlerts and alerts.get("Retest")

    alert("New Retests Occured.")

if enableBreakAlerts and alerts.get("Break")

    alert("New Breaks Occured.")

How to Use the Indicator on Your Chart

Once the Free Trading Indicator is added successfully, avoid turning on every possible setting immediately.

Start with the default configuration.

The source code includes configuration areas for:

  • General Configuration
  • Timeframes
  • Breaks & Retests
  • Style
  • Alerts
  • Market Bias Dashboard

The default settings include a pivot length of 15, a minimum strength threshold of 25%, a maximum of six displayed zones, break and retest display enabled, and the market-bias dashboard enabled.

Change one setting at a time.

That makes it easier to understand what each adjustment actually changes on the chart.

What the Indicator Shows

The Free Trading Indicator can display support and resistance information directly on the price chart.

A displayed level can include:

  • Timeframe
  • Price
  • Strength score
  • Strength label
  • Estimated buyer percentage
  • Estimated seller percentage
  • Buy and sell volume estimates
  • Recent pressure information

The script formats this information into the level label when the object is rendered.

The market-bias dashboard adds another layer of information.

It can show:

Overall Bias
Strong Bullish, Strong Bearish, Mild Bullish, Mild Bearish, or Neutral.

1H Trend
Bullish, Bearish, or Neutral.

15M Trend
Bullish, Bearish, or Neutral.

Market Condition
Trending or Sideways.

Pressure
Estimated recent buy versus sell pressure.

This information is generated by the script’s own calculations and should be interpreted as part of a broader analysis process.

How to Test the Free Trading Indicator Properly

Testing the Free Trading Indicator should be structured rather than random.

Test 1: Trend Conditions

Look at charts where price has a clear directional movement.

Check whether support and resistance levels help organize the structure.

Test 2: Sideways Conditions

Examine ranges and less directional environments.

This is useful because an indicator may behave differently when price repeatedly interacts with nearby levels.

Test 3: Break and Retest Behavior

Review levels that are broken and later tested again.

Check whether the break and retest labels correspond to the conditions you expect from the code.

Test 4: Multi-Timeframe Behavior

The script can process multiple timeframes, with the second and third timeframe inputs disabled by default.

Test them one at a time rather than changing several settings together.

Test 5: Pressure Readings

Remember that the script explicitly describes its pressure calculation as an approximation based on where the candle closes within its range.

That means the pressure percentage should not automatically be interpreted as institutional order-flow or exchange-level bid/ask volume.

The purpose of testing is to understand the tool, not to force the chart to confirm an existing opinion.

Keep Risk Management Separate From the Indicator

A Free Trading Indicator should support analysis, not determine how much capital you expose.

Your risk management process should define position size, stop placement, maximum risk, and when you stop trading.

The indicator can highlight a level.

It cannot know your account size.

It cannot know how much loss you can financially tolerate.

And its output does not remove the uncertainty of the market.

That separation is important.

Use the Free Trading Indicator to organize information, then use your own trading plan to decide whether a trade is appropriate.

FAQs

Is the Free Trading Indicator actually free?

Yes. The script described in this article is being provided without an upfront charge. Free access refers to the cost of accessing the script; it does not imply a particular trading result.

How do I install the Free Trading Indicator in TradingView?

Open TradingView’s Pine Editor, create a new script, remove the existing code, paste the complete Pine Script shown in this article, save it, and add it to the chart. The script uses Pine Script version 6.

What does the Free Trading Indicator show?

It can show pivot-based support and resistance levels, strength information, break and retest labels, estimated pressure and volume information, and a market-bias dashboard. The exact display depends on the script settings and market data available on the chart.

Can I use the Free Trading Indicator on every timeframe?

The script contains timeframe inputs and can process multiple timeframes. However, you should test it on the timeframe and market you actually trade rather than assuming that identical behavior will occur everywhere.

Does the Free Trading Indicator give automatic buy and sell signals?

Not in the simple sense of a guaranteed entry system. The code is primarily focused on levels, breaks, retests, estimated pressure, and market-bias information. Those outputs can be used as part of analysis, but they do not determine a particular future market outcome.

Should I trust the pressure percentage as real order flow?

No. The script itself describes the buy/sell pressure calculation as an approximation based on candle position within its range. It should not automatically be treated as exchange-level order-flow or true bid/ask delta data.

Risk Disclaimer

Trading financial markets involves substantial risk. A Free Trading Indicator is a technical analysis tool and cannot remove market risk or guarantee a particular result. This article is for educational purposes only and is not personal financial advice. Test the script before relying on it, understand your platform and broker conditions, use appropriate risk management, and trade only with capital you can afford to lose.

Conclusion

The Free Trading Indicator is designed to bring several charting components into one TradingView script.

You can use it to study:

  • Support and resistance
  • Level strength
  • Breaks
  • Retests
  • Estimated pressure
  • Multi-timeframe trend
  • Market condition
  • Alerts

The three-step process is straightforward:

Understand the script.

Add it correctly.

Test it before relying on it.

The biggest mistake would be to install the Free Trading Indicator and immediately treat every level or dashboard reading as a trade instruction.

Use the script as a tool inside a broader process.

Keep your risk plan separate.

Test its behavior on your own charts.

And judge the indicator by how useful it is to your analysis rather than by assumptions about what it should do.

Leave a Comment