Indicator · Pine Script v6 · On the chart

GemHunter v4

by Finland93

GemHunter v4 classifies market conditions into bullish, bearish, or neutral regimes using a multi-indicator voting system.

View on GitHub

GitHub stars4on the repository
Pine versionv6current
Lines of code377
Settings38inputs
LicenceMPL-2.0
Last update4 months ago29 Jun 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

377 lines of Pine Script v6 from GemHunter/gemhunter.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

gemhunter.pine
//@version=6
// ════════════════════════════════════════════════════════════════════════════════
//  GemHunter v4  —  EMA + RSI + MACD + ADX trend-regime detector
//  Anti-chop filters · non-repainting higher-timeframe confirmation · alerts
//  https://github.com/Finland93/GemHunter
//  Educational tool, not financial advice. Backtest before using.
// ════════════════════════════════════════════════════════════════════════════════
indicator("GemHunter v4 (EMA+RSI+MACD+ADX+S/R, Anti-Chop, MTF-Confirmed)", shorttitle="GemHunter v4", overlay=true, max_bars_back=5000, max_boxes_count=500, max_labels_count=500)

// ════════════════════════════════════════════════════════════════════════════════
// INPUTS
// ════════════════════════════════════════════════════════════════════════════════
grp_trend   = "Trend Engine (EMA + MACD + RSI)"
emaFastLen  = input.int(50,  "EMA Fast", minval=2, group=grp_trend, tooltip="Fast trend EMA. Lower = more responsive, more flips.")
emaSlowLen  = input.int(200, "EMA Slow", minval=5, group=grp_trend, tooltip="Slow trend EMA. Defines the macro bias.")
rsiLen      = input.int(14,  "RSI Length", minval=2, group=grp_trend)
rsiBullTh   = input.int(55,  "RSI Bull Threshold", minval=1, maxval=99, group=grp_trend)
rsiBearTh   = input.int(45,  "RSI Bear Threshold", minval=1, maxval=99, group=grp_trend)
macdFast    = input.int(12,  "MACD Fast", minval=1, group=grp_trend)
macdSlow    = input.int(26,  "MACD Slow", minval=1, group=grp_trend)
macdSignal  = input.int(9,   "MACD Signal", minval=1, group=grp_trend)
votesNeeded = input.int(2,   "Votes Needed (of 3)", minval=1, maxval=3, group=grp_trend, tooltip="How many of EMA / MACD / RSI must agree. 3 = strictest, fewest signals.")
confirmBars = input.int(3,   "Bars to Confirm Regime", minval=1, group=grp_trend, tooltip="A raw signal must persist this many bars before it counts (debounce).")

grp_filters = "Noise Filters (Anti-Chop / Hysteresis)"
emaAtrMult  = input.float(0.5, "Min EMA Separation (x ATR)", minval=0.0, step=0.05, group=grp_filters, tooltip="EMAs must be this far apart (in ATRs) to count as trending. Higher = fewer chop flips.")
slopeLb     = input.int(20,    "EMA Slow Slope Lookback", minval=1, group=grp_filters)
slopeMinRoc = input.float(0.05,"Min EMA Slow ROC % (abs)", minval=0.0, step=0.01, group=grp_filters, tooltip="Slow EMA must be sloping at least this much. Filters flat/sideways markets.")
rsiHyst     = input.int(2,     "RSI Hysteresis (+/-)", minval=0, maxval=10, group=grp_filters, tooltip="Widens the RSI bands so price has to commit before flipping.")
macdStdLen  = input.int(100,   "MACD Hist Strength Window", minval=5, group=grp_filters)
macdStdMult = input.float(0.5, "MACD Hist Strength (x StDev)", minval=0.0, step=0.1, group=grp_filters, tooltip="MACD histogram must exceed this many std-devs to count. Filters weak momentum.")
useADX      = input.bool(true, "Require ADX Trend Strength", group=grp_filters, tooltip="Blocks signals when ADX is weak or +DI/-DI disagree with the trade side. Excellent for cutting range/chop. Turn OFF to revert to pure voting.")
adxLen      = input.int(14,    "ADX / DI Length", minval=1, group=grp_filters)
adxSmooth   = input.int(14,    "ADX Smoothing", minval=1, group=grp_filters)
adxMinTh    = input.float(18.0,"ADX Min Threshold", minval=0.0, step=0.5, group=grp_filters, tooltip="Typical 18-25. Higher = only strong trends pass.")

grp_htf     = "Higher Timeframe Confirmation"
useHTF      = input.bool(true, "Enable HTF Confirmation", group=grp_htf)
htfTF       = input.timeframe("240", "HTF (e.g. 60, 240, D, W)", group=grp_htf)
htfMode     = input.string("Filter", "HTF Mode", options=["Display Only","Filter","Strict"], group=grp_htf, tooltip="Display Only = info only. Filter = block counter-trend signals. Strict = require full LTF+HTF agreement.")

grp_bg      = "Background Controls"
bgSource    = input.string("Gated", "Background Uses", options=["LTF","Gated","HTF"], group=grp_bg, tooltip="LTF = chart signal only. Gated = chart signal after HTF gating. HTF = higher-timeframe only.")
minHoldBars = input.int(5, "Min Bars to Hold Trend", minval=0, group=grp_bg, tooltip="Regime cannot flip again until this many bars pass since the last flip.")

grp_sig     = "Signals & Alerts"
showSignals = input.bool(true, "Show Flip Markers", group=grp_sig)

grp_style   = "Style"
showBG      = input.bool(true,  "Background Tint", group=grp_style)
showTable   = input.bool(true,  "Show Tables", group=grp_style)
showEMAs    = input.bool(true,  "Plot EMAs", group=grp_style)
showMACDZero= input.bool(false, "Show MACD Zero Dots", group=grp_style)

grp_sr      = "Support & Resistance"
showSR      = input.bool(true,  "Show S/R Zones", group=grp_sr)
srLeft      = input.int(15,     "Pivot Left",  minval=1, group=grp_sr)
srRight     = input.int(15,     "Pivot Right", minval=1, group=grp_sr, tooltip="Bars required AFTER a swing before it's confirmed as a pivot. Larger = stronger, fewer levels, but each level appears later. This delay is confirmation lag, not repainting.")
srMaxLevels = input.int(8,      "Max Levels Kept", minval=1, maxval=30, group=grp_sr, tooltip="Once this many zones exist, the oldest un-retested one is removed to keep the chart clean.")
srZoneAtr   = input.float(0.25, "Zone Half-Width (x ATR)", minval=0.0, step=0.05, group=grp_sr, tooltip="Half-thickness of each zone as a multiple of ATR(14).")
srMergeAtr  = input.float(0.5,  "Merge Distance (x ATR)",  minval=0.0, step=0.05, group=grp_sr, tooltip="A new pivot within this many ATRs of an existing zone reinforces it (touch count +1) instead of drawing a duplicate.")
srExtend    = input.int(10,     "Extend Right (bars)", minval=0, group=grp_sr)
srShowStrength = input.bool(true, "Show Touch Count", group=grp_sr)

// ════════════════════════════════════════════════════════════════════════════════
// CORE (LTF / chart timeframe)
// ════════════════════════════════════════════════════════════════════════════════
emaFast = ta.ema(close, emaFastLen)
emaSlow = ta.ema(close, emaSlowLen)
rsiVal  = ta.rsi(close, rsiLen)

macdLine   = ta.ema(close, macdFast) - ta.ema(close, macdSlow)
signalLine = ta.ema(macdLine, macdSignal)
macdHist   = macdLine - signalLine

atr14 = ta.atr(14)
[diPlus, diMinus, adxVal] = ta.dmi(adxLen, adxSmooth)

// Filter sub-conditions
emaSepOK  = not na(atr14) and math.abs(emaFast - emaSlow) > atr14 * emaAtrMult
rocSlow   = ta.roc(emaSlow, slopeLb)
slopeOKU  = not na(rocSlow) and rocSlow >  slopeMinRoc
slopeOKD  = not na(rocSlow) and rocSlow < -slopeMinRoc
macdStdev = ta.stdev(macdHist, macdStdLen)
macdStr   = not na(macdStdev) and math.abs(macdHist) > macdStdev * macdStdMult

// ADX strength + directional alignment (DMI). When disabled, always passes.
adxStrong = not na(adxVal) and adxVal >= adxMinTh
adxBullOK = not useADX or (adxStrong and diPlus  >= diMinus)
adxBearOK = not useADX or (adxStrong and diMinus >= diPlus)

// Per-engine votes
bullEMA  = close > emaFast and emaFast > emaSlow and emaSepOK and slopeOKU
bearEMA  = close < emaFast and emaFast < emaSlow and emaSepOK and slopeOKD
bullMACD = macdLine > 0 and macdLine > signalLine and macdStr
bearMACD = macdLine < 0 and macdLine < signalLine and macdStr
bullRSI  = rsiVal >= (rsiBullTh + rsiHyst)
bearRSI  = rsiVal <= (rsiBearTh - rsiHyst)

bullVotes = (bullEMA ? 1 : 0) + (bullMACD ? 1 : 0) + (bullRSI ? 1 : 0)
bearVotes = (bearEMA ? 1 : 0) + (bearMACD ? 1 : 0) + (bearRSI ? 1 : 0)
rawBull   = bullVotes >= votesNeeded and adxBullOK
rawBear   = bearVotes >= votesNeeded and adxBearOK

// Debounce N consecutive bars
var int bullRun = 0
var int bearRun = 0
bullRun := rawBull ? bullRun + 1 : 0
bearRun := rawBear ? bearRun + 1 : 0
ltfBull = bullRun >= confirmBars
ltfBear = bearRun >= confirmBars

// ════════════════════════════════════════════════════════════════════════════════
// HTF CONFIRMATION — one bundled, non-repainting request.security call
//   • All slope / stdev maths run inside the HTF context (correct bar spacing)
//   • A var-held "confirmed only" gate prevents intra-HTF-bar repainting
// ════════════════════════════════════════════════════════════════════════════════
htfEval() =>
    _emaF = ta.ema(close, emaFastLen)
    _emaS = ta.ema(close, emaSlowLen)
    _rsi  = ta.rsi(close, rsiLen)
    _mL   = ta.ema(close, macdFast) - ta.ema(close, macdSlow)
    _sL   = ta.ema(_mL, macdSignal)
    _hist = _mL - _sL
    _atr  = ta.atr(14)
    _sepOK = not na(_atr) and math.abs(_emaF - _emaS) > _atr * emaAtrMult
    _roc   = ta.roc(_emaS, slopeLb)
    _sU    = not na(_roc) and _roc >  slopeMinRoc
    _sD    = not na(_roc) and _roc < -slopeMinRoc
    _hStd  = ta.stdev(_hist, macdStdLen)
    _hStr  = not na(_hStd) and math.abs(_hist) > _hStd * macdStdMult
    _bE = _emaF > _emaS and _sepOK and _sU
    _rE = _emaF < _emaS and _sepOK and _sD
    _bM = _mL > 0 and _mL > _sL and _hStr
    _rM = _mL < 0 and _mL < _sL and _hStr
    _bR = _rsi >= (rsiBullTh + rsiHyst)
    _rR = _rsi <= (rsiBearTh - rsiHyst)
    _bull = ((_bE ? 1 : 0) + (_bM ? 1 : 0) + (_bR ? 1 : 0)) >= votesNeeded
    _bear = ((_rE ? 1 : 0) + (_rM ? 1 : 0) + (_rR ? 1 : 0)) >= votesNeeded
    var bool _bullHold = false
    var bool _bearHold = false
    if barstate.isconfirmed
        _bullHold := _bull
        _bearHold := _bear
    [_bullHold, _bearHold]

[htfBullRaw, htfBearRaw] = request.security(syminfo.tickerid, htfTF, htfEval(), barmerge.gaps_off, barmerge.lookahead_off)
htfBull = useHTF and htfBullRaw
htfBear = useHTF and htfBearRaw

// Gate LTF by HTF mode
bool gatedBull = ltfBull
bool gatedBear = ltfBear
if useHTF
    if htfMode == "Filter"
        gatedBull := ltfBull and not htfBear
        gatedBear := ltfBear and not htfBull
    else if htfMode == "Strict"
        gatedBull := ltfBull and htfBull
        gatedBear := ltfBear and htfBear

// ════════════════════════════════════════════════════════════════════════════════
// REGIME with flip guard + duration stats
// ════════════════════════════════════════════════════════════════════════════════
var string regime     = "Neutral"
var int    startBarIx = na
var int    lastFlip   = na
var int    upDur      = 0
var int    downDur    = 0
var float  avgUp      = na
var float  avgDown    = na
var int    upCount    = 0
var int    dnCount    = 0

canFlip = na(lastFlip) ? true : (bar_index - lastFlip) >= minHoldBars

wantBull = gatedBull and regime != "Uptrend"   and canFlip
wantBear = gatedBear and regime != "Downtrend" and canFlip

if wantBull
    if regime == "Downtrend" and not na(startBarIx)
        downDur := bar_index - startBarIx
        dnCount += 1
        avgDown := na(avgDown) ? downDur : (avgDown * (dnCount - 1) + downDur) / dnCount
    regime     := "Uptrend"
    startBarIx := bar_index
    lastFlip   := bar_index
    upDur      := 0
if wantBear
    if regime == "Uptrend" and not na(startBarIx)
        upDur   := bar_index - startBarIx
        upCount += 1
        avgUp   := na(avgUp) ? upDur : (avgUp * (upCount - 1) + upDur) / upCount
    regime     := "Downtrend"
    startBarIx := bar_index
    lastFlip   := bar_index
    downDur    := 0

if regime == "Uptrend" and not na(startBarIx)
    upDur := bar_index - startBarIx
if regime == "Downtrend" and not na(startBarIx)
    downDur := bar_index - startBarIx

flipUp   = regime == "Uptrend"   and regime[1] != "Uptrend"
flipDown = regime == "Downtrend" and regime[1] != "Downtrend"

// ════════════════════════════════════════════════════════════════════════════════
// TABLES
// ════════════════════════════════════════════════════════════════════════════════
var table tblTopRight = table.new(position.top_right,    2, 3, bgcolor=color.new(color.white, 88), frame_color=color.new(color.black, 75), frame_width=1)
var table tblBotLeft  = table.new(position.bottom_left,  2, 2, bgcolor=color.new(color.white, 88), frame_color=color.new(color.black, 75), frame_width=1)
var table tblBotRight = table.new(position.bottom_right, 2, 2, bgcolor=color.new(color.white, 88), frame_color=color.new(color.black, 75), frame_width=1)

ltfStatusTxt = gatedBull ? "Bullish" : gatedBear ? "Bearish" : "Neutral"
ltfStatusCol = gatedBull ? color.green : gatedBear ? color.red : color.gray
htfStatusTxt = useHTF ? (htfBull ? "Bullish" : htfBear ? "Bearish" : "Neutral") : "—"
htfStatusCol = useHTF ? (htfBull ? color.green : htfBear ? color.red : color.gray) : color.gray
adxTxt = na(adxVal) ? "—" : str.tostring(math.round(adxVal))
adxCol = na(adxVal) ? color.gray : (adxVal >= adxMinTh ? color.green : color.orange)
barsNow = regime == "Uptrend" ? upDur : regime == "Downtrend" ? downDur : 0

if showTable and barstate.islast
    table.cell(tblTopRight, 0, 0, "HTF:", text_color=color.white, bgcolor=color.new(color.blue, 0))
    table.cell(tblTopRight, 1, 0, htfStatusTxt, text_color=htfStatusCol, bgcolor=color.white)
    table.cell(tblTopRight, 0, 1, "LTF:", text_color=color.white, bgcolor=color.new(color.blue, 0))
    table.cell(tblTopRight, 1, 1, ltfStatusTxt, text_color=ltfStatusCol, bgcolor=color.white)
    table.cell(tblTopRight, 0, 2, "ADX:", text_color=color.white, bgcolor=color.new(color.blue, 0))
    table.cell(tblTopRight, 1, 2, adxTxt, text_color=adxCol, bgcolor=color.white)

    table.cell(tblBotLeft, 0, 0, "AVG UP:",   text_color=color.white, bgcolor=color.new(color.blue, 0))
    table.cell(tblBotLeft, 1, 0, na(avgUp)   ? "—" : str.tostring(math.round(avgUp)),   text_color=color.green, bgcolor=color.white)
    table.cell(tblBotLeft, 0, 1, "AVG DOWN:", text_color=color.white, bgcolor=color.new(color.blue, 0))
    table.cell(tblBotLeft, 1, 1, na(avgDown) ? "—" : str.tostring(math.round(avgDown)), text_color=color.red, bgcolor=color.white)

    table.cell(tblBotRight, 0, 0, "Trend:", text_color=color.white, bgcolor=color.new(color.blue, 0))
    table.cell(tblBotRight, 1, 0, regime,   text_color=color.black, bgcolor=color.white)
    table.cell(tblBotRight, 0, 1, "Bars:",  text_color=color.white, bgcolor=color.new(color.blue, 0))
    table.cell(tblBotRight, 1, 1, str.tostring(barsNow), text_color=color.black, bgcolor=color.white)

// ════════════════════════════════════════════════════════════════════════════════
// BACKGROUND
// ════════════════════════════════════════════════════════════════════════════════
bgBull = color.new(color.green, 85)
bgBear = color.new(color.red,   85)
bgNeu  = color.new(color.gray,  90)

bgBullSig = bgSource == "LTF" ? ltfBull : bgSource == "HTF" ? htfBull : gatedBull
bgBearSig = bgSource == "LTF" ? ltfBear : bgSource == "HTF" ? htfBear : gatedBear
bgColor   = bgBullSig ? bgBull : bgBearSig ? bgBear : bgNeu
bgcolor(showBG ? bgColor : na, title="Regime Tint")

// ════════════════════════════════════════════════════════════════════════════════
// PLOTS
// ════════════════════════════════════════════════════════════════════════════════
plot(showEMAs ? emaFast : na, "EMA Fast", color=color.new(color.teal,   0), linewidth=2)
plot(showEMAs ? emaSlow : na, "EMA Slow", color=color.new(color.orange, 0), linewidth=2)

plotshape(showSignals and flipUp,   title="Flip Up",   style=shape.triangleup,   location=location.belowbar, color=color.new(color.green, 0), size=size.tiny, text="Bull")
plotshape(showSignals and flipDown, title="Flip Down", style=shape.triangledown, location=location.abovebar, color=color.new(color.red, 0),   size=size.tiny, text="Bear")

plotshape(showMACDZero and macdLine > 0, title="MACD>0", style=shape.circle, location=location.belowbar, color=color.new(color.green, 0), size=size.tiny)
plotshape(showMACDZero and macdLine < 0, title="MACD<0", style=shape.circle, location=location.abovebar, color=color.new(color.red,   0), size=size.tiny)

// ════════════════════════════════════════════════════════════════════════════════
// SUPPORT & RESISTANCE — confirmed-pivot zones with touch-count strength
//   • Levels come from confirmed swing pivots, so they DO NOT repaint. A zone simply
//     appears srRight bars after its pivot (confirmation lag, not repainting).
//   • A new pivot within "Merge Distance" of an existing zone reinforces it
//     (touch count +1) instead of cluttering the chart with a duplicate.
//   • Each zone is colored by its CURRENT role: green support while price trades
//     above it, red resistance while price trades below — a broken level flips role.
//   • The oldest un-retested zones are pruned so only the most relevant ones remain.
// ════════════════════════════════════════════════════════════════════════════════
phSR = ta.pivothigh(high, srLeft, srRight)
plSR = ta.pivotlow(low,  srLeft, srRight)

var array<float> srPx   = array.new<float>()
var array<float> srHalf = array.new<float>()
var array<int>   srHits = array.new<int>()
var array<int>   srBar  = array.new<int>()
var array<box>   srBx   = array.new<box>()
var array<label> srLb   = array.new<label>()

srBrokeUp = false
srBrokeDn = false

if showSR and not na(atr14)
    float half  = atr14 * srZoneAtr
    float mDist = atr14 * srMergeAtr

    // 1) register this bar's confirmed pivot(s): reinforce a nearby zone, else add one
    candPx = array.new<float>()
    if not na(phSR)
        array.push(candPx, phSR)
    if not na(plSR)
        array.push(candPx, plSR)
    if array.size(candPx) > 0
        for c = 0 to array.size(candPx) - 1
            px = array.get(candPx, c)
            nearIdx = -1
            float nearDst = na
            if array.size(srPx) > 0
                for i = 0 to array.size(srPx) - 1
                    d = math.abs(array.get(srPx, i) - px)
                    if d <= mDist and (na(nearDst) or d < nearDst)
                        nearDst := d
                        nearIdx := i
            if nearIdx >= 0
                array.set(srHits, nearIdx, array.get(srHits, nearIdx) + 1)
                array.set(srBar,  nearIdx, bar_index)
            else
                box   nb = box.new(bar_index - srRight, px + half, bar_index, px - half, border_color=color.new(color.gray, 45), bgcolor=color.new(color.gray, 82))
                label nl = label.new(bar_index, px, "", style=label.style_label_left, textcolor=color.white, color=color.new(color.gray, 100), size=size.small)
                array.push(srPx,   px)
                array.push(srHalf, half)
                array.push(srHits, 1)
                array.push(srBar,  bar_index)
                array.push(srBx,   nb)
                array.push(srLb,   nl)

    // 2) prune the oldest un-retested zone(s) beyond the cap
    while array.size(srPx) > srMaxLevels
        oldIdx = 0
        oldBar = array.get(srBar, 0)
        for i = 1 to array.size(srPx) - 1
            if array.get(srBar, i) < oldBar
                oldBar := array.get(srBar, i)
                oldIdx := i
        box.delete(array.get(srBx, oldIdx))
        label.delete(array.get(srLb, oldIdx))
        array.remove(srPx,   oldIdx)
        array.remove(srHalf, oldIdx)
        array.remove(srHits, oldIdx)
        array.remove(srBar,  oldIdx)
        array.remove(srBx,   oldIdx)
        array.remove(srLb,   oldIdx)

    // 3) restyle + extend every active zone; flag a break when price closes through one
    if array.size(srPx) > 0
        for i = 0 to array.size(srPx) - 1
            px = array.get(srPx, i)
            hf = array.get(srHalf, i)
            isSup = close >= px
            col = isSup ? color.green : color.red
            bx = array.get(srBx, i)
            box.set_top(bx, px + hf)
            box.set_bottom(bx, px - hf)
            box.set_right(bx, bar_index + srExtend)
            box.set_bgcolor(bx, color.new(col, 82))
            box.set_border_color(bx, color.new(col, 45))
            lb = array.get(srLb, i)
            if srShowStrength
                label.set_xy(lb, bar_index + srExtend, px)
                label.set_text(lb, str.tostring(array.get(srHits, i)))
                label.set_textcolor(lb, color.new(col, 0))
                label.set_color(lb, color.new(col, 85))
                label.set_style(lb, label.style_label_left)
            else
                label.set_text(lb, "")
                label.set_color(lb, color.new(color.gray, 100))
            if not na(close[1])
                if close > px and close[1] <= px
                    srBrokeUp := true
                if close < px and close[1] >= px
                    srBrokeDn := true

// ════════════════════════════════════════════════════════════════════════════════
// ALERTS  (create these in TradingView via Alerts → Condition: GemHunter v4)
// ════════════════════════════════════════════════════════════════════════════════
alertcondition(flipUp,   title="GemHunter ▲ Flip to Uptrend",   message="GemHunter: trend flipped to UPTREND — {{ticker}} {{interval}}")
alertcondition(flipDown, title="GemHunter ▼ Flip to Downtrend", message="GemHunter: trend flipped to DOWNTREND — {{ticker}} {{interval}}")
alertcondition(gatedBull and not gatedBull[1], title="GemHunter • LTF Bullish (gated)", message="GemHunter: gated BULLISH signal — {{ticker}} {{interval}}")
alertcondition(gatedBear and not gatedBear[1], title="GemHunter • LTF Bearish (gated)", message="GemHunter: gated BEARISH signal — {{ticker}} {{interval}}")
alertcondition(useHTF and htfBull and not htfBull[1], title="GemHunter • HTF turned Bullish", message="GemHunter: higher timeframe BULLISH — {{ticker}} {{interval}}")
alertcondition(useHTF and htfBear and not htfBear[1], title="GemHunter • HTF turned Bearish", message="GemHunter: higher timeframe BEARISH — {{ticker}} {{interval}}")
alertcondition(srBrokeUp, title="GemHunter • Broke a Level (up)",   message="GemHunter: price closed UP through an S/R level — {{ticker}} {{interval}}")
alertcondition(srBrokeDn, title="GemHunter • Broke a Level (down)", message="GemHunter: price closed DOWN through an S/R level — {{ticker}} {{interval}}")

Overview

GemHunter v4 combines exponential moving average alignment, MACD momentum, and RSI strength, optionally gated by ADX trend strength and higher-timeframe confirmation. Filters such as ATR-based EMA separation, slow EMA slope, and RSI hysteresis suppress sideways chop. The indicator overlays regime background tints, reversal markers, EMA lines, dynamic support and resistance zones from swing pivots, and informational dashboard tables directly onto the price chart.

Details

Published by

Finland93 on GitHub

Draws

On the price chart

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

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

Topics

pinescriptpinescript-indicatorstradingviewgemhunter

Settings

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

SettingTypeDefaultVariable
EMA Fastint50emaFastLen
EMA Slowint200emaSlowLen
RSI Lengthint14rsiLen
RSI Bull Thresholdint55rsiBullTh
RSI Bear Thresholdint45rsiBearTh
MACD Fastint12macdFast
MACD Slowint26macdSlow
MACD Signalint9macdSignal
Votes Needed (of 3)int2votesNeeded
Bars to Confirm Regimeint3confirmBars
Min EMA Separation (x ATR)float0.5emaAtrMult
EMA Slow Slope Lookbackint20slopeLb
Show all 38 settings
SettingTypeDefaultVariable
Min EMA Slow ROC % (abs)float0.05slopeMinRoc
RSI Hysteresis (+/-)int2rsiHyst
MACD Hist Strength Windowint100macdStdLen
MACD Hist Strength (x StDev)float0.5macdStdMult
Require ADX Trend StrengthbooltrueuseADX
ADX / DI Lengthint14adxLen
ADX Smoothingint14adxSmooth
ADX Min Thresholdfloat18.0adxMinTh
Enable HTF ConfirmationbooltrueuseHTF
HTF (e.g. 60, 240, D, W)timeframe240htfTF
HTF ModestringFilterhtfMode
Background UsesstringGatedbgSource
Min Bars to Hold Trendint5minHoldBars
Show Flip MarkersbooltrueshowSignals
Background TintbooltrueshowBG
Show TablesbooltrueshowTable
Plot EMAsbooltrueshowEMAs
Show MACD Zero DotsboolfalseshowMACDZero
Show S/R ZonesbooltrueshowSR
Pivot Leftint15srLeft
Pivot Rightint15srRight
Max Levels Keptint8srMaxLevels
Zone Half-Width (x ATR)float0.25srZoneAtr
Merge Distance (x ATR)float0.5srMergeAtr
Extend Right (bars)int10srExtend
Show Touch CountbooltruesrShowStrength
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

BTE Acc AO SMIO

by f-cksociety
11 stars

This script provides an indicator-based backtesting engine framework that simulates trade entries, exits, and filtering directly on the price chart. It evaluates selected entry rules and multi-layered filters, including moving average trends, volume increases,

Indicator v5 TrendOscillators & momentum
Updated 4 years ago · MPL-2.0 GitHub repository of BTE Acc AO SMIO
5 stars

This indicator combines pivot-based support and resistance levels with multi-timeframe MACD crossovers and a higher-timeframe exponential moving average trend filter. It identifies support and resistance touches using pivot highs and lows within a user-defined

Indicator v4 Oscillators & momentumSupport & resistance Repaints
Updated 3 years ago · Apache-2.0 GitHub repository of MACD + Support / Resistance

This overlay indicator plots Central Pivot Range levels and bands across yearly, quarterly, monthly, weekly, daily, and six-hour periods. Each timeframe can be shown or hidden, and pivot and band colors are set separately. It also marks MACD momentum and EMA t

Indicator v5 Support & resistanceOscillators & momentum
Updated 3 years ago · None stated GitHub repository of Pivot Boss Multi Timeframe CPR with MACD and EMA

Xiaotang System V5.0

by TUSKIJAY
4 stars

Xiaotang System V5.0 is a price-chart overlay indicator that identifies momentum breakouts across moving averages and session VWAP. It computes Heikin-Ashi price data, 50-period and 200-period SMAs, and intraday VWAP while monitoring 5-minute higher-timeframe

Indicator v5 TrendOscillators & momentum Repaints
Updated 3 days ago · None stated GitHub repository of Xiaotang System V5.0

Icarus

by niloysaha
3 stars

Icarus is an overlay indicator that projects momentum and level indicators directly onto the price chart. It calculates higher-timeframe equivalents for moving averages, computes Supertrend, and constructs price-equivalent RSI upper and lower bands based on La

Indicator v4 TrendOscillators & momentum
Updated 4 years ago · None stated GitHub repository of Icarus

More Trend scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

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

Compare brokers