Indicator · Pine Script v6 · On the chart

Bottom Level Finder

by chris-c-thomas

Bottom Level Finder is a support identification indicator designed to locate correction and re-entry zones directly on the price chart.

View on GitHub

GitHub stars14on the repository
Pine versionv6current
Lines of code766
Settings16inputs
LicenceMIT
Last update6 months ago8 Apr 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

766 lines of Pine Script v6 from scripts/bottom-level-finder/bottom-level-finder.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

bottom-level-finder.pine
// ============================================================================
// Bottom Level Finder — Unified
// Timeframe: Daily chart (recommended for all modes)
// Version: 1.0.0
// Author: Chris Thomas
// ============================================================================
// Multi-horizon bottom/support identification with a single mode selector.
// Switch the mode to match your TradingView zoom preset (1D/5D/1M/3M/6M/1Y/5Y).
//
// Each mode adapts:
//   - Drawdown level granularity (e.g., -0.5% steps for 1D, -10% steps for 5Y)
//   - Moving average suite (short-term for 1D, weekly/monthly for 5Y)
//   - MA cross pair (10/20 for short, 50/200 for long)
//   - Bottom signal thresholds (RSI, VIX, MA support level)
//   - Fibonacci retracements (ATH → swing low, shared across modes)
//
// request.security() budget: 3 calls
//   1. Weekly: 50W/100W/200W SMA + RSI + BB %B (1 tuple)
//   2. Monthly: MACD histogram current + prior (1 tuple)
//   3. VIX: close + 20-period SMA (1 call)
//
// Chart setup: Set top bar to Daily (D). Use bottom bar to zoom.
// ============================================================================

//@version=6
indicator(
     title      = "Bottom Level Finder",
     shorttitle = "BLF",
     overlay    = true,
     max_lines_count   = 50,
     max_labels_count  = 50,
     max_bars_back     = 5000
 )

// ============================================================================
// CONSTANTS
// ============================================================================
var string GP_MODE     = "Mode"
var string GP_REF      = "Reference Levels"
var string GP_FIB      = "Fibonacci Retracements"
var string GP_DD       = "Drawdown Levels"
var string GP_MA       = "Moving Averages"
var string GP_VIX      = "VIX"
var string GP_DASH     = "Dashboard"

var color C_BULL       = #00E676
var color C_BEAR       = #FF1744
var color C_NEUTRAL    = #B0BEC5
var color C_YELLOW     = #FFD600
var color C_ORANGE     = #FF9100
var color C_PURPLE     = #7C4DFF
var color C_GRAY       = #616161

var color C_FIB_236    = #4FC3F7
var color C_FIB_382    = #29B6F6
var color C_FIB_500    = #FFD600
var color C_FIB_618    = #FF9100
var color C_FIB_786    = #FF5722

var color C_DD1        = #C8E6C9
var color C_DD2        = #A5D6A7
var color C_DD3        = #FFD54F
var color C_DD4        = #FFB74D
var color C_DD5        = #E57373

var color C_MA1        = #81C784
var color C_MA2        = #26A69A
var color C_MA3        = #42A5F5
var color C_MA4        = #EF5350

var color C_DASH_BG    = #1A1A2E
var color C_DASH_HDR   = #2A2A4E
var color C_DASH_TEXT  = #E0E0E0
var color C_DASH_DIM   = #78909C

// ============================================================================
// INPUTS
// ============================================================================
i_mode = input.string("6M", "Horizon Mode", group=GP_MODE,
     options=["1D", "5D", "1M", "3M", "6M", "1Y", "5Y"],
     tooltip="Match to your TradingView zoom preset.\nChart timeframe should be Daily (D) for all modes.")

i_athOverride      = input.float(0.0,  "ATH Price Override (0 = auto)",  group=GP_REF,
     tooltip="Leave at 0 to auto-detect from loaded data.")
i_swingLowOverride = input.float(0.0,  "Swing Low Reference (0 = auto)", group=GP_REF,
     tooltip="Fibonacci anchor. 0 = auto.\nSPY: Oct-2022 ≈ 348 | COVID ≈ 218")
i_showATHLine      = input.bool(true,  "Show ATH Line",                  group=GP_REF)

i_showFib      = input.bool(true,     "Show Fibonacci Levels",    group=GP_FIB)
i_fibLineStyle = input.string("Dashed", "Line Style",             group=GP_FIB, options=["Solid", "Dashed", "Dotted"])
i_fibLineWidth = input.int(1,         "Line Width",               group=GP_FIB, minval=1, maxval=4)

i_showDD       = input.bool(true,     "Show Drawdown Levels",     group=GP_DD)
i_ddLineStyle  = input.string("Dotted", "Line Style",             group=GP_DD, options=["Solid", "Dashed", "Dotted"])
i_ddLineWidth  = input.int(1,         "Line Width",               group=GP_DD, minval=1, maxval=4)

i_showMA       = input.bool(true,     "Show Moving Averages",     group=GP_MA)
i_showVIX      = input.bool(true,     "Show VIX Data",            group=GP_VIX)
i_vixSymbol    = input.symbol("CBOE:VIX", "VIX Symbol",           group=GP_VIX)

i_showDash     = input.bool(true,     "Show Dashboard",           group=GP_DASH)
i_dashPos      = input.string("Bottom Left", "Position",             group=GP_DASH, options=["Top Right", "Top Left", "Bottom Right", "Bottom Left"])
i_dashSize     = input.string("Small", "Text Size",                group=GP_DASH, options=["Tiny", "Small", "Normal"])

// ============================================================================
// HELPERS
// ============================================================================
f2(float val) =>
    str.tostring(val, "#.##")

fPct(float val) =>
    (val >= 0 ? "+" : "") + str.tostring(val, "#.#") + "%"

distPct(float level, float current) =>
    level > 0 ? (current - level) / level * 100 : na

// ============================================================================
// ATH & SWING LOW
// ============================================================================
var float detectedATH = na
var float detectedLow = na

if na(detectedATH) or high > detectedATH
    detectedATH := high
if na(detectedLow) or low < detectedLow
    detectedLow := low

float ath      = i_athOverride > 0      ? i_athOverride      : detectedATH
float swingLow = i_swingLowOverride > 0 ? i_swingLowOverride : detectedLow
float athSpan  = ath - swingLow
float currentDD = ath > 0 ? (close - ath) / ath * 100 : na

// ============================================================================
// FIBONACCI (shared across all modes)
// ============================================================================
float fib236 = ath - athSpan * 0.236
float fib382 = ath - athSpan * 0.382
float fib500 = ath - athSpan * 0.500
float fib618 = ath - athSpan * 0.618
float fib786 = ath - athSpan * 0.786

// ============================================================================
// MODE-DEPENDENT DRAWDOWN LEVELS
// ============================================================================
dd1m = switch i_mode
    "1D" => 0.995
    "5D" => 0.99
    "1M" => 0.98
    "3M" => 0.97
    "6M" => 0.95
    => 0.90
dd2m = switch i_mode
    "1D" => 0.99
    "5D" => 0.98
    "1M" => 0.95
    "3M" => 0.95
    "6M" => 0.90
    => 0.85
dd3m = switch i_mode
    "1D" => 0.98
    "5D" => 0.97
    "1M" => 0.92
    "3M" => 0.90
    "6M" => 0.85
    => 0.80
dd4m = switch i_mode
    "1D" => 0.97
    "5D" => 0.95
    "1M" => 0.90
    "3M" => 0.85
    "6M" => 0.80
    => 0.75
dd5m = switch i_mode
    "1D" => 0.95
    "5D" => 0.92
    "1M" => 0.85
    "3M" => 0.80
    "6M" => 0.75
    => 0.70

dd1lbl = switch i_mode
    "1D" => "-0.5%"
    "5D" => "-1%"
    "1M" => "-2%"
    "3M" => "-3%"
    "6M" => "-5%"
    => "-10%"
dd2lbl = switch i_mode
    "1D" => "-1%"
    "5D" => "-2%"
    "1M" => "-5%"
    "3M" => "-5%"
    "6M" => "-10%"
    => "-15%"
dd3lbl = switch i_mode
    "1D" => "-2%"
    "5D" => "-3%"
    "1M" => "-8%"
    "3M" => "-10%"
    "6M" => "-15%"
    => "-20%"
dd4lbl = switch i_mode
    "1D" => "-3%"
    "5D" => "-5%"
    "1M" => "-10%"
    "3M" => "-15%"
    "6M" => "-20%"
    => "-25%"
dd5lbl = switch i_mode
    "1D" => "-5%"
    "5D" => "-8%"
    "1M" => "-15%"
    "3M" => "-20%"
    "6M" => "-25%"
    => "-30%"

float dd1 = ath * dd1m
float dd2 = ath * dd2m
float dd3 = ath * dd3m
float dd4 = ath * dd4m
float dd5 = ath * dd5m

// ============================================================================
// ALL DAILY MOVING AVERAGES
// ============================================================================
float sma5d   = ta.sma(close, 5)
float sma10d  = ta.sma(close, 10)
float sma20d  = ta.sma(close, 20)
float sma50d  = ta.sma(close, 50)
float sma100d = ta.sma(close, 100)
float sma200d = ta.sma(close, 200)

// ============================================================================
// request.security() — WEEKLY (1 call)
// ============================================================================
float _sma50w  = ta.sma(close, 50)
float _sma100w = ta.sma(close, 100)
float _sma200w = ta.sma(close, 200)
float _rsiW    = ta.rsi(close, 14)
[_wBbM, _wBbU, _wBbL] = ta.bb(close, 20, 2.0)
float _wBbPctB = (_wBbU - _wBbL) != 0 ? (close - _wBbL) / (_wBbU - _wBbL) : na

[sma50w, sma100w, sma200w, rsiW, bbPctBW] = request.security(
    syminfo.tickerid, "W",
    [_sma50w, _sma100w, _sma200w, _rsiW, _wBbPctB],
    lookahead = barmerge.lookahead_off
)

// ============================================================================
// request.security() — MONTHLY (1 call)
// ============================================================================
[_mLine, _mSig, _mHist] = ta.macd(close, 12, 26, 9)

[macdHistM, macdHistPrevM] = request.security(
    syminfo.tickerid, "M",
    [_mHist, _mHist[1]],
    lookahead = barmerge.lookahead_off
)

// ============================================================================
// request.security() — VIX (1 call)
// ============================================================================
float _vixC = close
float _vixS = ta.sma(close, 20)

[vixVal, vixSMA] = request.security(
    i_vixSymbol, timeframe.period,
    [_vixC, _vixS],
    lookahead = barmerge.lookahead_off
)

// ============================================================================
// MODE-DEPENDENT MA SLOTS
// ============================================================================
ma1 = switch i_mode
    "1D" => sma5d
    "5D" => sma5d
    "1M" => sma5d
    "3M" => sma10d
    "6M" => sma20d
    "1Y" => sma50d
    => sma200d
ma2 = switch i_mode
    "1D" => sma10d
    "5D" => sma10d
    "1M" => sma10d
    "3M" => sma20d
    "6M" => sma50d
    "1Y" => sma100d
    => sma50w
ma3 = switch i_mode
    "1D" => sma20d
    "5D" => sma20d
    "1M" => sma20d
    "3M" => sma50d
    "6M" => sma100d
    "1Y" => sma200d
    => sma100w
ma4 = switch i_mode
    "1D" => sma50d
    "5D" => sma50d
    "1M" => sma50d
    "3M" => sma100d
    "6M" => sma200d
    "1Y" => sma50w
    => sma200w

ma1name = switch i_mode
    "1D" => "5D"
    "5D" => "5D"
    "1M" => "5D"
    "3M" => "10D"
    "6M" => "20D"
    "1Y" => "50D"
    => "200D"
ma2name = switch i_mode
    "1D" => "10D"
    "5D" => "10D"
    "1M" => "10D"
    "3M" => "20D"
    "6M" => "50D"
    "1Y" => "100D"
    => "50W"
ma3name = switch i_mode
    "1D" => "20D"
    "5D" => "20D"
    "1M" => "20D"
    "3M" => "50D"
    "6M" => "100D"
    "1Y" => "200D"
    => "100W"
ma4name = switch i_mode
    "1D" => "50D"
    "5D" => "50D"
    "1M" => "50D"
    "3M" => "100D"
    "6M" => "200D"
    "1Y" => "50W"
    => "200W"

// ============================================================================
// MA CROSS DETECTION
// ============================================================================
var int bsc1020 = 0
bsc1020 := ta.cross(sma10d, sma20d) ? 0 : bsc1020 + 1
var int bsc2050 = 0
bsc2050 := ta.cross(sma20d, sma50d) ? 0 : bsc2050 + 1
var int bsc50200 = 0
bsc50200 := ta.cross(sma50d, sma200d) ? 0 : bsc50200 + 1

crossBull = switch i_mode
    "1D" => sma10d > sma20d
    "5D" => sma10d > sma20d
    "1M" => sma10d > sma20d
    "3M" => sma20d > sma50d
    => sma50d > sma200d
crossName = switch i_mode
    "1D" => "10/20"
    "5D" => "10/20"
    "1M" => "10/20"
    "3M" => "20/50"
    => "50/200"
crossBars = switch i_mode
    "1D" => bsc1020
    "5D" => bsc1020
    "1M" => bsc1020
    "3M" => bsc2050
    => bsc50200

// ============================================================================
// DAILY INDICATORS
// ============================================================================
float rsiD    = ta.rsi(close, 14)
[_bbM, _bbU, _bbL] = ta.bb(close, 20, 2.0)
float bbPctBD = (_bbU - _bbL) != 0 ? (close - _bbL) / (_bbU - _bbL) : na
[_dLine, _dSig, _dHist] = ta.macd(close, 12, 26, 9)
float volSMA20 = ta.sma(volume, 20)
float volRatio = volSMA20 > 0 ? volume / volSMA20 : na

// ============================================================================
// 6-FACTOR BOTTOM SIGNAL SCORECARD
// ============================================================================
// S1: RSI oversold
s1fired = switch i_mode
    "1D" => rsiD < 20
    "5D" => rsiD < 25
    "1M" => rsiD < 25
    "3M" => rsiD < 30
    "6M" => rsiD < 30
    => (not na(rsiW) and rsiW < 30)
s1name = switch i_mode
    "1D" => "D RSI (< 20)"
    "5D" => "D RSI (< 25)"
    "1M" => "D RSI (< 25)"
    "3M" => "D RSI (< 30)"
    "6M" => "D RSI (< 30)"
    => "W RSI (< 30)"
s1valStr = switch i_mode
    "1Y" => (na(rsiW) ? "N/A" : f2(rsiW))
    "5Y" => (na(rsiW) ? "N/A" : f2(rsiW))
    => f2(rsiD)

// S2: Bollinger %B < 0
s2fired = switch i_mode
    "1Y" => (not na(bbPctBW) and bbPctBW < 0.0)
    "5Y" => (not na(bbPctBW) and bbPctBW < 0.0)
    => (not na(bbPctBD) and bbPctBD < 0.0)
s2name = switch i_mode
    "1Y" => "W BB %B (< 0)"
    "5Y" => "W BB %B (< 0)"
    => "D BB %B (< 0)"
s2valStr = switch i_mode
    "1Y" => (na(bbPctBW) ? "N/A" : f2(bbPctBW))
    "5Y" => (na(bbPctBW) ? "N/A" : f2(bbPctBW))
    => (na(bbPctBD) ? "N/A" : f2(bbPctBD))

// S3: Momentum turn (MACD daily for short, W RSI for mid, M MACD for long)
s3fired = switch i_mode
    "1D" => (_dHist < 0 and _dHist > nz(_dHist[1]))
    "5D" => (_dHist < 0 and _dHist > nz(_dHist[1]))
    "1M" => (_dHist < 0 and _dHist > nz(_dHist[1]))
    "3M" => (not na(rsiW) and rsiW < 40)
    "6M" => (not na(rsiW) and rsiW < 40)
    => (not na(macdHistM) and not na(macdHistPrevM) and macdHistM < 0 and macdHistM > macdHistPrevM)
s3name = switch i_mode
    "1D" => "D MACD Turn"
    "5D" => "D MACD Turn"
    "1M" => "D MACD Turn"
    "3M" => "W RSI (< 40)"
    "6M" => "W RSI (< 40)"
    => "M MACD Turn"
s3valStr = switch i_mode
    "1D" => ((_dHist > nz(_dHist[1]) ? "UP " : "DN ") + f2(_dHist))
    "5D" => ((_dHist > nz(_dHist[1]) ? "UP " : "DN ") + f2(_dHist))
    "1M" => ((_dHist > nz(_dHist[1]) ? "UP " : "DN ") + f2(_dHist))
    "3M" => (na(rsiW) ? "N/A" : f2(rsiW))
    "6M" => (na(rsiW) ? "N/A" : f2(rsiW))
    => (na(macdHistM) ? "N/A" : ((macdHistM > nz(macdHistPrevM) ? "UP " : "DN ") + f2(macdHistM)))

// S4: Price below key MA
s4fired = switch i_mode
    "1D" => (not na(sma10d) and close <= sma10d)
    "5D" => (not na(sma20d) and close <= sma20d)
    "1M" => (not na(sma50d) and close <= sma50d)
    "3M" => (not na(sma100d) and close <= sma100d)
    "6M" => (not na(sma200d) and close <= sma200d)
    "1Y" => (not na(sma200d) and close <= sma200d)
    => (not na(sma200w) and close <= sma200w)
s4name = switch i_mode
    "1D" => "vs 10D SMA"
    "5D" => "vs 20D SMA"
    "1M" => "vs 50D SMA"
    "3M" => "vs 100D SMA"
    "6M" => "vs 200D SMA"
    "1Y" => "vs 200D SMA"
    => "vs 200W SMA"
s4ma = switch i_mode
    "1D" => sma10d
    "5D" => sma20d
    "1M" => sma50d
    "3M" => sma100d
    "6M" => sma200d
    "1Y" => sma200d
    => sma200w
s4valStr = na(s4ma) ? "N/A" : fPct(distPct(s4ma, close))

// S5: VIX
vixThresh = switch i_mode
    "1D" => 20.0
    "5D" => 20.0
    "1M" => 25.0
    "3M" => 25.0
    => 30.0
s5fired = not na(vixVal) and vixVal > vixThresh
s5name  = "VIX (> " + str.tostring(vixThresh, "#") + ")"
s5valStr = na(vixVal) ? "N/A" : f2(vixVal)

// S6: Volume
bool s6fired = not na(volRatio) and volRatio > 2.0

sigCount = ((s1fired ? 1 : 0) + (s2fired ? 1 : 0) + (s3fired ? 1 : 0)
         +  (s4fired ? 1 : 0) + (s5fired ? 1 : 0) + (s6fired ? 1 : 0))

vixTier = na(vixVal) ? "N/A" : (vixVal > 35 ? "EXTREME" : (vixVal > 30 ? "VERY HIGH" : (vixVal > 25 ? "HIGH" : (vixVal > 20 ? "ELEVATED" : (vixVal > 15 ? "NORMAL" : "LOW")))))

// ============================================================================
// MA PLOTS
// ============================================================================
plot(i_showMA ? ma1 : na, "MA 1", color=C_MA1, linewidth=1)
plot(i_showMA ? ma2 : na, "MA 2", color=C_MA2, linewidth=2)
plot(i_showMA ? ma3 : na, "MA 3", color=C_MA3, linewidth=2)
plot(i_showMA ? ma4 : na, "MA 4", color=C_MA4, linewidth=3)

// ============================================================================
// LEVEL LINES & LABELS
// ============================================================================
fibStyle = switch i_fibLineStyle
    "Solid" => line.style_solid
    "Dashed" => line.style_dashed
    => line.style_dotted
ddStyle = switch i_ddLineStyle
    "Solid" => line.style_solid
    "Dashed" => line.style_dashed
    => line.style_dotted
labelX = bar_index - 5

var line  l_ath  = na, var label lb_ath  = na
var line  l_f236 = na, var label lb_f236 = na
var line  l_f382 = na, var label lb_f382 = na
var line  l_f500 = na, var label lb_f500 = na
var line  l_f618 = na, var label lb_f618 = na
var line  l_f786 = na, var label lb_f786 = na
var line  l_dd1  = na, var label lb_dd1  = na
var line  l_dd2  = na, var label lb_dd2  = na
var line  l_dd3  = na, var label lb_dd3  = na
var line  l_dd4  = na, var label lb_dd4  = na
var line  l_dd5  = na, var label lb_dd5  = na

if barstate.islast
    if i_showATHLine and not na(ath)
        line.delete(l_ath), label.delete(lb_ath)
        l_ath  := line.new(bar_index - 200, ath, bar_index + 5, ath, extend=extend.both, color=C_PURPLE, style=line.style_solid, width=2)
        lb_ath := label.new(labelX, ath, "ATH  " + f2(ath), style=label.style_none, color=color.new(C_PURPLE, 50), textcolor=C_PURPLE, size=size.normal)

    if i_showFib and athSpan > 0
        line.delete(l_f236), label.delete(lb_f236), line.delete(l_f382), label.delete(lb_f382)
        line.delete(l_f500), label.delete(lb_f500), line.delete(l_f618), label.delete(lb_f618)
        line.delete(l_f786), label.delete(lb_f786)
        l_f236 := line.new(bar_index - 200, fib236, bar_index + 5, fib236, extend=extend.both, color=C_FIB_236, style=fibStyle, width=i_fibLineWidth)
        lb_f236 := label.new(labelX, fib236, "Fib 23.6%  " + f2(fib236), style=label.style_none, color=color.new(C_FIB_236, 50), textcolor=C_FIB_236, size=size.normal)
        l_f382 := line.new(bar_index - 200, fib382, bar_index + 5, fib382, extend=extend.both, color=C_FIB_382, style=fibStyle, width=i_fibLineWidth)
        lb_f382 := label.new(labelX, fib382, "Fib 38.2%  " + f2(fib382), style=label.style_none, color=color.new(C_FIB_382, 50), textcolor=C_FIB_382, size=size.normal)
        l_f500 := line.new(bar_index - 200, fib500, bar_index + 5, fib500, extend=extend.both, color=C_FIB_500, style=fibStyle, width=i_fibLineWidth)
        lb_f500 := label.new(labelX, fib500, "Fib 50.0%  " + f2(fib500), style=label.style_none, color=color.new(C_FIB_500, 50), textcolor=C_FIB_500, size=size.normal)
        l_f618 := line.new(bar_index - 200, fib618, bar_index + 5, fib618, extend=extend.both, color=C_FIB_618, style=fibStyle, width=i_fibLineWidth)
        lb_f618 := label.new(labelX, fib618, "Fib 61.8%  " + f2(fib618), style=label.style_none, color=color.new(C_FIB_618, 50), textcolor=C_FIB_618, size=size.normal)
        l_f786 := line.new(bar_index - 200, fib786, bar_index + 5, fib786, extend=extend.both, color=C_FIB_786, style=fibStyle, width=i_fibLineWidth)
        lb_f786 := label.new(labelX, fib786, "Fib 78.6%  " + f2(fib786), style=label.style_none, color=color.new(C_FIB_786, 50), textcolor=C_FIB_786, size=size.normal)

    if i_showDD and not na(ath) and ath > 0
        line.delete(l_dd1), label.delete(lb_dd1), line.delete(l_dd2), label.delete(lb_dd2)
        line.delete(l_dd3), label.delete(lb_dd3), line.delete(l_dd4), label.delete(lb_dd4)
        line.delete(l_dd5), label.delete(lb_dd5)
        l_dd1 := line.new(bar_index - 200, dd1, bar_index + 5, dd1, extend=extend.both, color=C_DD1, style=ddStyle, width=i_ddLineWidth)
        lb_dd1 := label.new(labelX, dd1, dd1lbl + "  " + f2(dd1), style=label.style_none, color=color.new(C_DD1, 50), textcolor=C_DD1, size=size.normal)
        l_dd2 := line.new(bar_index - 200, dd2, bar_index + 5, dd2, extend=extend.both, color=C_DD2, style=ddStyle, width=i_ddLineWidth)
        lb_dd2 := label.new(labelX, dd2, dd2lbl + "  " + f2(dd2), style=label.style_none, color=color.new(C_DD2, 50), textcolor=C_DD2, size=size.normal)
        l_dd3 := line.new(bar_index - 200, dd3, bar_index + 5, dd3, extend=extend.both, color=C_DD3, style=ddStyle, width=i_ddLineWidth)
        lb_dd3 := label.new(labelX, dd3, dd3lbl + "  " + f2(dd3), style=label.style_none, color=color.new(C_DD3, 50), textcolor=C_DD3, size=size.normal)
        l_dd4 := line.new(bar_index - 200, dd4, bar_index + 5, dd4, extend=extend.both, color=C_DD4, style=ddStyle, width=i_ddLineWidth)
        lb_dd4 := label.new(labelX, dd4, dd4lbl + "  " + f2(dd4), style=label.style_none, color=color.new(C_DD4, 50), textcolor=C_DD4, size=size.normal)
        l_dd5 := line.new(bar_index - 200, dd5, bar_index + 5, dd5, extend=extend.both, color=C_DD5, style=ddStyle, width=i_ddLineWidth)
        lb_dd5 := label.new(labelX, dd5, dd5lbl + "  " + f2(dd5), style=label.style_none, color=color.new(C_DD5, 50), textcolor=C_DD5, size=size.normal)

// ============================================================================
// DASHBOARD
// ============================================================================
if i_showDash and barstate.islast
    string tblPos = switch i_dashPos
        "Top Right" => position.top_right
        "Top Left" => position.top_left
        "Bottom Right" => position.bottom_right
        => position.bottom_left
    string tblSize = switch i_dashSize
        "Tiny" => size.tiny
        "Normal" => size.normal
        => size.small

    var table dash = table.new(position.top_right, 5, 50, bgcolor=C_DASH_BG, border_width=1, border_color=C_GRAY)
    table.set_position(dash, tblPos)
    // --- Padding spacers (top, left, right) ---
    table.cell(dash, 0, 0, "  ", text_size=tblSize, bgcolor=C_DASH_BG, text_color=C_DASH_BG)
    table.cell(dash, 4, 0, "       ", text_size=tblSize, bgcolor=C_DASH_BG, text_color=C_DASH_BG)
    table.cell(dash, 1, 0, "", text_size=tblSize, bgcolor=C_DASH_BG)
    table.cell(dash, 2, 0, "", text_size=tblSize, bgcolor=C_DASH_BG)
    table.cell(dash, 3, 0, "", text_size=tblSize, bgcolor=C_DASH_BG)
    int r = 1

    // --- Header ---
    table.cell(dash, 1, r, "BLF — " + i_mode, text_color=C_PURPLE, text_size=tblSize, text_halign=text.align_left, bgcolor=C_DASH_HDR)
    table.cell(dash, 2, r, syminfo.ticker, text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_right, bgcolor=C_DASH_HDR)
    table.cell(dash, 3, r, timeframe.period, text_color=C_DASH_DIM, text_size=tblSize, text_halign=text.align_right, bgcolor=C_DASH_HDR)
    r += 1

    table.cell(dash, 1, r, "Close", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, f2(close), text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, "", text_size=tblSize, text_halign=text.align_right)
    r += 1

    color ddClr = (not na(currentDD) and currentDD <= -20) ? C_BEAR : ((not na(currentDD) and currentDD <= -10) ? C_ORANGE : C_YELLOW)
    table.cell(dash, 1, r, "Drawdown", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, na(currentDD) ? "N/A" : fPct(currentDD), text_color=ddClr, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, "from ATH", text_color=C_DASH_DIM, text_size=tblSize, text_halign=text.align_right)
    r += 1

    color crossClr = crossBull ? C_BULL : C_BEAR
    table.cell(dash, 1, r, crossName + " SMA", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, crossBull ? "BULLISH" : "BEARISH", text_color=crossClr, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, str.tostring(crossBars) + " bars", text_color=C_DASH_DIM, text_size=tblSize, text_halign=text.align_right)
    r += 1

    if i_showVIX
        color vixClr = na(vixVal) ? C_GRAY : (vixVal > 30 ? C_BEAR : (vixVal > 20 ? C_ORANGE : C_BULL))
        table.cell(dash, 1, r, "VIX", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
        table.cell(dash, 2, r, na(vixVal) ? "N/A" : f2(vixVal), text_color=vixClr, text_size=tblSize, text_halign=text.align_right)
        table.cell(dash, 3, r, vixTier, text_color=vixClr, text_size=tblSize, text_halign=text.align_right)
        r += 1

    // --- Column Headers ---
    table.cell(dash, 1, r, "", text_size=tblSize, bgcolor=C_DASH_HDR)
    table.cell(dash, 2, r, "Price", text_color=C_DASH_DIM, text_size=tblSize, text_halign=text.align_right, bgcolor=C_DASH_HDR)
    table.cell(dash, 3, r, "Dist", text_color=C_DASH_DIM, text_size=tblSize, text_halign=text.align_right, bgcolor=C_DASH_HDR)
    r += 1

    // --- Drawdown ---
    table.cell(dash, 1, r, "DRAWDOWN", text_color=C_DD3, text_size=tblSize, text_halign=text.align_left, bgcolor=C_DASH_HDR)
    table.cell(dash, 2, r, "", text_size=tblSize, bgcolor=C_DASH_HDR)
    table.cell(dash, 3, r, "", text_size=tblSize, bgcolor=C_DASH_HDR)
    r += 1
    color cd1 = close >= dd1 ? C_BULL : C_DD1
    table.cell(dash, 1, r, dd1lbl, text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, f2(dd1), text_color=cd1, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, fPct(distPct(dd1, close)), text_color=cd1, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color cd2 = close >= dd2 ? C_BULL : C_DD2
    table.cell(dash, 1, r, dd2lbl, text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, f2(dd2), text_color=cd2, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, fPct(distPct(dd2, close)), text_color=cd2, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color cd3 = close >= dd3 ? C_BULL : C_DD3
    table.cell(dash, 1, r, dd3lbl, text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, f2(dd3), text_color=cd3, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, fPct(distPct(dd3, close)), text_color=cd3, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color cd4 = close >= dd4 ? C_BULL : C_DD4
    table.cell(dash, 1, r, dd4lbl, text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, f2(dd4), text_color=cd4, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, fPct(distPct(dd4, close)), text_color=cd4, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color cd5 = close >= dd5 ? C_BULL : C_DD5
    table.cell(dash, 1, r, dd5lbl, text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, f2(dd5), text_color=cd5, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, fPct(distPct(dd5, close)), text_color=cd5, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // --- Fibonacci ---
    table.cell(dash, 1, r, "FIBONACCI", text_color=C_FIB_382, text_size=tblSize, text_halign=text.align_left, bgcolor=C_DASH_HDR)
    table.cell(dash, 2, r, "→ " + f2(swingLow), text_color=C_DASH_DIM, text_size=tblSize, text_halign=text.align_right, bgcolor=C_DASH_HDR)
    table.cell(dash, 3, r, "", text_size=tblSize, bgcolor=C_DASH_HDR)
    r += 1
    color cf236 = close >= fib236 ? C_BULL : C_FIB_236
    table.cell(dash, 1, r, "23.6%", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, f2(fib236), text_color=cf236, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, fPct(distPct(fib236, close)), text_color=cf236, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color cf382 = close >= fib382 ? C_BULL : C_FIB_382
    table.cell(dash, 1, r, "38.2%", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, f2(fib382), text_color=cf382, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, fPct(distPct(fib382, close)), text_color=cf382, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color cf500 = close >= fib500 ? C_BULL : C_FIB_500
    table.cell(dash, 1, r, "50.0%", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, f2(fib500), text_color=cf500, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, fPct(distPct(fib500, close)), text_color=cf500, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color cf618 = close >= fib618 ? C_BULL : C_FIB_618
    table.cell(dash, 1, r, "61.8%", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, f2(fib618), text_color=cf618, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, fPct(distPct(fib618, close)), text_color=cf618, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color cf786 = close >= fib786 ? C_BULL : C_FIB_786
    table.cell(dash, 1, r, "78.6%", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, f2(fib786), text_color=cf786, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, fPct(distPct(fib786, close)), text_color=cf786, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // --- Moving Averages ---
    table.cell(dash, 1, r, "MOVING AVGS", text_color=C_MA2, text_size=tblSize, text_halign=text.align_left, bgcolor=C_DASH_HDR)
    table.cell(dash, 2, r, "", text_size=tblSize, bgcolor=C_DASH_HDR)
    table.cell(dash, 3, r, "", text_size=tblSize, bgcolor=C_DASH_HDR)
    r += 1
    color cm1 = close >= nz(ma1) ? C_BULL : C_MA1
    table.cell(dash, 1, r, ma1name + " SMA", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, na(ma1) ? "N/A" : f2(ma1), text_color=cm1, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, na(ma1) ? "" : fPct(distPct(ma1, close)), text_color=cm1, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color cm2 = close >= nz(ma2) ? C_BULL : C_MA2
    table.cell(dash, 1, r, ma2name + " SMA", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, na(ma2) ? "N/A" : f2(ma2), text_color=cm2, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, na(ma2) ? "" : fPct(distPct(ma2, close)), text_color=cm2, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color cm3 = close >= nz(ma3) ? C_BULL : C_MA3
    table.cell(dash, 1, r, ma3name + " SMA", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, na(ma3) ? "N/A" : f2(ma3), text_color=cm3, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, na(ma3) ? "" : fPct(distPct(ma3, close)), text_color=cm3, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color cm4 = close >= nz(ma4) ? C_BULL : C_MA4
    table.cell(dash, 1, r, ma4name + " SMA", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, na(ma4) ? "N/A" : f2(ma4), text_color=cm4, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, na(ma4) ? "" : fPct(distPct(ma4, close)), text_color=cm4, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // --- Bottom Signals ---
    color sigHdrClr = sigCount >= 4 ? C_BEAR : (sigCount >= 2 ? C_ORANGE : C_BULL)
    table.cell(dash, 1, r, "BOTTOM SIGNALS", text_color=sigHdrClr, text_size=tblSize, text_halign=text.align_left, bgcolor=C_DASH_HDR)
    table.cell(dash, 2, r, str.tostring(sigCount) + "/6", text_color=sigHdrClr, text_size=tblSize, text_halign=text.align_right, bgcolor=C_DASH_HDR)
    table.cell(dash, 3, r, "", text_size=tblSize, bgcolor=C_DASH_HDR)
    r += 1

    color s1c = s1fired ? C_BEAR : C_BULL
    table.cell(dash, 1, r, s1name, text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, s1valStr, text_color=s1c, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, s1fired ? "FIRED" : "—", text_color=s1fired ? C_BEAR : C_DASH_DIM, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color s2c = s2fired ? C_BEAR : C_BULL
    table.cell(dash, 1, r, s2name, text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, s2valStr, text_color=s2c, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, s2fired ? "FIRED" : "—", text_color=s2fired ? C_BEAR : C_DASH_DIM, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color s3c = s3fired ? C_BULL : C_NEUTRAL
    table.cell(dash, 1, r, s3name, text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, s3valStr, text_color=s3c, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, s3fired ? "FIRED" : "—", text_color=s3fired ? C_BULL : C_DASH_DIM, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color s4c = s4fired ? C_BEAR : C_BULL
    table.cell(dash, 1, r, s4name, text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, s4valStr, text_color=s4c, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, s4fired ? "FIRED" : "—", text_color=s4fired ? C_BEAR : C_DASH_DIM, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color s5c = s5fired ? C_BEAR : C_BULL
    table.cell(dash, 1, r, s5name, text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, s5valStr, text_color=s5c, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, s5fired ? "FIRED" : "—", text_color=s5fired ? C_BEAR : C_DASH_DIM, text_size=tblSize, text_halign=text.align_right)
    r += 1
    color s6c = s6fired ? C_BEAR : C_NEUTRAL
    table.cell(dash, 1, r, "Vol (> 2x)", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left)
    table.cell(dash, 2, r, na(volRatio) ? "N/A" : (f2(volRatio) + "x"), text_color=s6c, text_size=tblSize, text_halign=text.align_right)
    table.cell(dash, 3, r, s6fired ? "FIRED" : "—", text_color=s6fired ? C_BEAR : C_DASH_DIM, text_size=tblSize, text_halign=text.align_right)
    r += 1

    // --- Summary ---
    string assessment = switch
        sigCount >= 5 => "EXTREME"
        sigCount >= 4 => "ELEVATED"
        sigCount >= 3 => "MODERATE"
        sigCount >= 1 => "LOW"
        => "NONE"
    color assessClr = switch
        sigCount >= 5 => C_BEAR
        sigCount >= 4 => C_ORANGE
        sigCount >= 3 => C_YELLOW
        sigCount >= 1 => C_NEUTRAL
        => C_BULL

    table.cell(dash, 1, r, "Bottom Proximity", text_color=C_DASH_TEXT, text_size=tblSize, text_halign=text.align_left, bgcolor=C_DASH_HDR)
    table.cell(dash, 2, r, assessment, text_color=assessClr, text_size=tblSize, text_halign=text.align_right, bgcolor=C_DASH_HDR)
    table.cell(dash, 3, r, "", text_size=tblSize, bgcolor=C_DASH_HDR)
    r += 1
    // --- Bottom padding spacer ---
    table.cell(dash, 1, r, "", text_size=tblSize, bgcolor=C_DASH_BG)
    table.cell(dash, 2, r, "", text_size=tblSize, bgcolor=C_DASH_BG)
    table.cell(dash, 3, r, "", text_size=tblSize, bgcolor=C_DASH_BG)

// ============================================================================
// END
// ============================================================================

Overview

Bottom Level Finder calculates horizon-adaptive Fibonacci retracements, drawdown percentage levels, moving averages, and integrated VIX volatility metrics. Main settings allow traders to select horizon modes ranging from one day to five years, override ATH and swing low reference anchors, and toggle visuals. The indicator plots reference lines directly on the chart alongside an information dashboard. It serves as an analytical visual aid and does not generate automated trade signals.

Details

Published by

chris-c-thomas on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Support & resistance, Moving averages, Volatility & bands, Screeners & dashboards

Markets

Stocks, Indices

Timeframes

H4 and above, M15-H1

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

financeoptionspinescriptpinescript-indicatorspinescript-strategiestradingtradingvewtradingview-chart-analysischarting

Settings

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

SettingTypeDefaultVariable
Horizon Modestring6Mi_mode
ATH Price Override (0 = auto)float0.0i_athOverride
Swing Low Reference (0 = auto)float0.0i_swingLowOverride
Show ATH Linebooltruei_showATHLine
Show Fibonacci Levelsbooltruei_showFib
Line StylestringDashedi_fibLineStyle
Line Widthint1i_fibLineWidth
Show Drawdown Levelsbooltruei_showDD
Line StylestringDottedi_ddLineStyle
Line Widthint1i_ddLineWidth
Show Moving Averagesbooltruei_showMA
Show VIX Databooltruei_showVIX
Show all 16 settings
SettingTypeDefaultVariable
VIX SymbolsymbolCBOE:VIXi_vixSymbol
Show Dashboardbooltruei_showDash
PositionstringBottom Lefti_dashPos
Text SizestringSmalli_dashSize
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

20 stars

This indicator calculates a base moving average (EMA, SMA, WMA, or HMA) and projects multiple symmetrical envelope bands around it directly on the price chart. Users can adjust the length, source, moving average type, and initial envelope multiplier. Additiona

Indicator v5 Moving averagesVolatility & bands
Updated 1 year ago · None stated GitHub repository of Multiple Moving Average Envelope
9 stars

Calculates volume-weighted average price anchored to automatically detected swing pivots, period starts, or a custom manual timestamp. It plots volume-weighted standard deviation bands with configurable multiples and measures price distance from VWAP in standa

Indicator v6 Moving averagesVolatility & bands Repaints
Updated yesterday · MIT GitHub repository of Anchored VWAP [WavesUnchained]
7 stars

This indicator plots standard Bollinger Bands alongside scaled Average True Range (ATR) bands directly on the price chart. It is designed to assist traders in identifying price consolidation zones, potential breakout levels, dynamic stops, and take-profit targ

Indicator v5 Moving averagesVolatility & bands
Updated 1 year ago · MPL-2.0 GitHub repository of Bollinger and ATR Bands

ATR Based Stoploss

by rubystar0512
5 stars

ATR Based Stoploss is an overlay indicator that calculates dynamic stop loss and take profit bands around a central simple moving average. It uses standard Average True Range or an ATR Surge Average derived from volatility spikes to offset profit and loss boun

Indicator v5 Volatility & bandsMoving averages
Updated 2 years ago · None stated GitHub repository of ATR Based Stoploss

This overlay indicator plots a Supertrend from an ATR length and factor, four exponential moving averages, and a selectable VWAP or TWAP taken from a chosen timeframe on the price chart. It also shows MACD and stochastic with adjustable lengths, smoothing, ver

Indicator v5 Oscillators & momentumMoving averages
Updated 1 year ago · MPL-2.0 GitHub repository of Momentum Setup: Vijay Thakare Option Buying Scalping Setup

Snag - VWAP

by dearvn
257 stars

This indicator plots a custom Anchored Volume-Weighted Average Price (VWAP) directly on the price chart along with optional standard deviation bands. Users can anchor the VWAP to standard time periods like sessions, weeks, or months, or to corporate events suc

Indicator v5 Moving averagesVolatility & bands Repaints
Updated 6 weeks ago · None stated GitHub repository of Snag - VWAP

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