Indicator · Pine Script v6 · On the chart

Trend Compass 15m

by chris-c-thomas

Trend Compass 15m is an intraday trend analysis overlay designed for 15-minute charts.

View on GitHub

Repaints

GitHub stars14on the repository
Pine versionv6current
Lines of code524
Settings40inputs
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

524 lines of Pine Script v6 from scripts/trend-compass/trend-compass-15m.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

trend-compass-15m.pine
// ============================================================================
// Trend Compass — 15-Minute
// Timeframe: 15-Minute (primary), compatible with 10M/30M
// Version: 1.0.0
// Author: Chris Thomas
// ============================================================================
// Strategic intraday trend assessment overlay for equities, ETFs, and indices.
// Evaluates intraday swing momentum and its relation to multi-day structure.
//
// Components:
//   - Multi-EMA ribbon (10/21/50) + local structure
//   - HTF context: 1H 50, 4H 50, Daily 50, Daily 200 EMAs
//   - Trend phase classifier (6 states)
//   - Composite weighted trend score (-11 to +11)
//   - RSI + MACD divergence detection (15m-tuned)
//   - OBV trend confirmation
//   - ATR regime + BB width percentile (390-bar lookback)
//   - Optional Ichimoku Cloud overlay
//   - Optional Fibonacci retracement levels
//   - 52-week high/low context (via Daily)
//   - 20-row heads-up dashboard
//   - 12 alert conditions
// ============================================================================

// ============================================================================
// TradingView Pine Script v6
// ============================================================================
//@version=6
indicator(
     title             = "Trend Compass 15m",
     shorttitle        = "TC-15m",
     overlay           = true,
     max_labels_count  = 500,
     max_lines_count   = 500,
     max_boxes_count   = 100,
     max_bars_back     = 5000
 )

// ============================================================================
// CONSTANTS
// ============================================================================
var string GP_EMA      = "EMA Ribbon"
var string GP_HTF      = "Higher Timeframe"
var string GP_TREND    = "Trend Strength (ADX)"
var string GP_RSI      = "RSI"
var string GP_MACD     = "MACD"
var string GP_VOL      = "Volume"
var string GP_ATR      = "Volatility (ATR)"
var string GP_BB       = "Bollinger Band Width"
var string GP_LEVELS   = "Key Levels"
var string GP_FIB      = "Fibonacci Retracement"
var string GP_ICH      = "Ichimoku Cloud"
var string GP_SCORE    = "Scoring Engine"
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_CYAN       = #00BCD4
var color C_PURPLE     = #7C4DFF
var color C_WHITE      = #FFFFFF
var color C_GRAY       = #78909C
var color C_DARK_GRAY  = #455A64
var color C_DASH_BG    = color.new(#1A1A2E, 5)
var color C_DASH_TEXT  = #E0E0E0
var color C_DASH_HEADER = #7C4DFF
var color C_CLOUD_BULL = color.new(#00E676, 85)
var color C_CLOUD_BEAR = color.new(#FF1744, 85)
var color C_CLOUD_MIX  = color.new(#FFD600, 90)
var color C_200_EMA    = #FF6D00
var color C_D50_EMA    = #26C6DA
var color C_4H50_EMA   = #66BB6A
var color C_1H50_EMA   = #E040FB
var color C_DAY_LVL    = #42A5F5
var color C_WEEK_LVL   = #AB47BC
var color C_52W_LVL    = #FF8A65
var color C_FIB_382    = #80CBC4
var color C_FIB_500    = #FFD54F
var color C_FIB_618    = #FF8A65
var color C_ICH_BULL   = color.new(#00E676, 88)
var color C_ICH_BEAR   = color.new(#FF1744, 88)
var color C_ICH_TENKAN = #2979FF
var color C_ICH_KIJUN  = #FF6D00

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

// --- EMA Ribbon ---
int    i_emaFastLen  = input.int(10,  "Fast EMA",           minval=2,  group=GP_EMA)
int    i_emaMidLen   = input.int(21,  "Mid EMA",            minval=2,  group=GP_EMA)
int    i_emaSlowLen  = input.int(50,  "Slow EMA",           minval=2,  group=GP_EMA)
bool   i_showEma     = input.bool(true, "Show EMA Ribbon",             group=GP_EMA)
bool   i_showCloud   = input.bool(true, "Show Cloud Fill",             group=GP_EMA)

// --- Higher Timeframe ---
bool   i_useHTF      = input.bool(true,  "Enable HTF Confirmation",   group=GP_HTF)
bool   i_show1H50    = input.bool(true,  "Show 1H 50 EMA",           group=GP_HTF, tooltip="Immediate multi-hour bridge. Purple step line.")
bool   i_show4H50    = input.bool(true,  "Show 4H 50 EMA",           group=GP_HTF, tooltip="Multi-day context. Green step line.")
bool   i_showD50     = input.bool(true,  "Show Daily 50 EMA",         group=GP_HTF, tooltip="Macro swing trend. Cyan line.")
bool   i_showD200    = input.bool(true,  "Show Daily 200 EMA",        group=GP_HTF, tooltip="The institutional anchor. Orange line.")

// --- Trend Strength (ADX) ---
int    i_adxLen      = input.int(14,  "ADX Length",          minval=5,  group=GP_TREND)
int    i_adxSlopeLB  = input.int(5,   "ADX Slope Lookback",  minval=2, maxval=20, group=GP_TREND)
int    i_emaSlopeLB  = input.int(5,   "EMA Slope Lookback",  minval=2, maxval=20, group=GP_TREND)
float  i_adxSlopeThr = input.float(0.3, "ADX Slope Threshold", minval=0.05, step=0.05, group=GP_TREND)

// --- RSI ---
int    i_rsiLen      = input.int(14,  "RSI Length",          minval=2,  group=GP_RSI)
bool   i_rsiDiv      = input.bool(true, "Enable RSI Divergence",      group=GP_RSI)
int    i_rsiPivotL   = input.int(3,   "Divergence Pivot Left Bars",  minval=2, maxval=20, group=GP_RSI)
int    i_rsiPivotR   = input.int(2,   "Divergence Pivot Right Bars", minval=1, maxval=10, group=GP_RSI)

// --- MACD ---
int    i_macdFast    = input.int(12,  "MACD Fast",           minval=2,  group=GP_MACD)
int    i_macdSlow    = input.int(26,  "MACD Slow",           minval=2,  group=GP_MACD)
int    i_macdSignal  = input.int(9,   "MACD Signal",         minval=2,  group=GP_MACD)
bool   i_macdDiv     = input.bool(true, "Enable MACD Divergence",     group=GP_MACD)
int    i_macdPivotL  = input.int(3,   "MACD Div Pivot Left",  minval=2, maxval=20, group=GP_MACD)
int    i_macdPivotR  = input.int(2,   "MACD Div Pivot Right", minval=1, maxval=10, group=GP_MACD)

// --- Volume ---
bool   i_useOBV      = input.bool(true, "Enable OBV Analysis",        group=GP_VOL)
int    i_obvEmaLen   = input.int(21,  "OBV EMA Length",      minval=5,  group=GP_VOL)
int    i_relVolLen   = input.int(20,  "Relative Volume Lookback",     minval=5, group=GP_VOL)

// --- ATR / Volatility ---
int    i_atrLen      = input.int(14,  "ATR Length",          minval=2,  group=GP_ATR)
int    i_atrPctlLB   = input.int(390, "ATR Percentile Lookback", minval=50, maxval=1000, group=GP_ATR, tooltip="390 bars on 15m ≈ 15 trading days.")

// --- BB Width ---
int    i_bbLen       = input.int(20,  "BB Length",           minval=5,  group=GP_BB)
float  i_bbMult      = input.float(2.0, "BB Multiplier",    minval=0.5, step=0.1, group=GP_BB)
int    i_bbwPctlLB   = input.int(390, "BB Width Percentile Lookback", minval=50, maxval=1000, group=GP_BB)

// --- Key Levels ---
bool   i_show52w     = input.bool(true,  "Show 52-Week High/Low",     group=GP_LEVELS)
bool   i_showPrDay   = input.bool(true,  "Show Prior Day H/L/C",      group=GP_LEVELS)
bool   i_showPrWk    = input.bool(true,  "Show Prior Week H/L/C",     group=GP_LEVELS)

// --- Fibonacci Retracement ---
bool   i_showFib     = input.bool(false, "Show Fibonacci Levels",     group=GP_FIB)
int    i_fibLB       = input.int(260,    "Fibonacci Lookback Bars",   minval=50, maxval=1000, group=GP_FIB, tooltip="260 bars on 15m ≈ 10 trading days.")

// --- Ichimoku Cloud ---
bool   i_showIchi    = input.bool(false, "Show Ichimoku Cloud",       group=GP_ICH)
int    i_ichiTenkan  = input.int(9,      "Tenkan-sen (Conversion)",   minval=2,  group=GP_ICH)
int    i_ichiKijun   = input.int(26,     "Kijun-sen (Base)",          minval=2,  group=GP_ICH)
int    i_ichiSenkouB = input.int(52,     "Senkou Span B (Leading B)", minval=2,  group=GP_ICH)
int    i_ichiDisp    = input.int(26,     "Displacement",              minval=1,  group=GP_ICH)

// --- Scoring Engine ---
bool   i_useWeighted = input.bool(true, "Use Weighted Scoring",       group=GP_SCORE)

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

// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
f1(float v) => str.tostring(v, "#.#")
f2(float v) => str.tostring(v, "#.##")
f0(float v) => str.tostring(v, "#")

posnegColor(float v) => v > 0 ? C_BULL : v < 0 ? C_BEAR : C_NEUTRAL

biasColor(int score, int maxS) =>
    float pct = maxS != 0 ? math.abs(score) / maxS : 0
    if score > 0
        pct > 0.6 ? C_BULL : pct > 0.3 ? color.new(C_BULL, 30) : C_NEUTRAL
    else if score < 0
        pct > 0.6 ? C_BEAR : pct > 0.3 ? color.new(C_BEAR, 30) : C_NEUTRAL
    else
        C_NEUTRAL

biasLabel(int score, int maxS) =>
    float pct = maxS != 0 ? math.abs(score) / maxS : 0
    if math.abs(score) == 0
        "NEUTRAL"
    else if pct > 0.6
        score > 0 ? "STRONG BULL" : "STRONG BEAR"
    else if pct > 0.3
        score > 0 ? "LEAN BULL" : "LEAN BEAR"
    else
        score > 0 ? "WEAK BULL" : "WEAK BEAR"

// ============================================================================
// EMA CALCULATIONS (local, 15m bars)
// ============================================================================
float emaFast = ta.ema(close, i_emaFastLen)
float emaMid  = ta.ema(close, i_emaMidLen)
float emaSlow = ta.ema(close, i_emaSlowLen)

bool emaBullish = emaFast > emaMid and emaMid > emaSlow
bool emaBearish = emaFast < emaMid and emaMid < emaSlow

// EMA spread
float emaSpread     = emaSlow != 0 ? math.abs(emaFast - emaSlow) / emaSlow * 100 : 0.0
bool  emaCompressed = emaSpread < 0.15  // 15m threshold tighter than 1H

// ============================================================================
// HIGHER TIMEFRAME DATA
// ============================================================================
float h1_50Ema = request.security(syminfo.tickerid, "60", ta.ema(close, 50), lookahead=barmerge.lookahead_off)
float h4_50Ema = request.security(syminfo.tickerid, "240", ta.ema(close, 50), lookahead=barmerge.lookahead_off)
float d50Ema   = request.security(syminfo.tickerid, "D", ta.ema(close, 50), lookahead=barmerge.lookahead_off)
float d200Ema  = request.security(syminfo.tickerid, "D", ta.ema(close, 200), lookahead=barmerge.lookahead_off)

// Reference Levels
[pdHigh, pdLow, pdClose] = request.security(syminfo.tickerid, "D", [high[1], low[1], close[1]], lookahead=barmerge.lookahead_on)
[pwHigh, pwLow, pwClose] = request.security(syminfo.tickerid, "W", [high[1], low[1], close[1]], lookahead=barmerge.lookahead_on)
[d52High, d52Low]        = request.security(syminfo.tickerid, "D", [ta.highest(high, 252), ta.lowest(low, 252)], lookahead=barmerge.lookahead_off)

// Stack Alignment
bool fullStackBull = emaBullish and not na(d200Ema) and emaSlow > d200Ema
bool fullStackBear = emaBearish and not na(d200Ema) and emaSlow < d200Ema

string emaStackStr = fullStackBull ? "FULL BULL" : fullStackBear ? "FULL BEAR" : emaBullish ? "PARTIAL BULL" : emaBearish ? "PARTIAL BEAR" : emaCompressed ? "COMPRESSED" : "MIXED"
color  emaStackClr = fullStackBull ? C_BULL : fullStackBear ? C_BEAR : emaBullish ? color.new(C_BULL, 40) : emaBearish ? color.new(C_BEAR, 40) : emaCompressed ? C_YELLOW : C_NEUTRAL

// HTF state flags
bool d200Bullish  = not na(d200Ema)  and close > d200Ema
bool d200Bearish  = not na(d200Ema)  and close < d200Ema
bool h4_50Bullish = not na(h4_50Ema) and close > h4_50Ema
bool h4_50Bearish = not na(h4_50Ema) and close < h4_50Ema
bool h1_50Bullish = not na(h1_50Ema) and close > h1_50Ema
bool h1_50Bearish = not na(h1_50Ema) and close < h1_50Ema

// ============================================================================
// ADX / DMI
// ============================================================================
[diPlus, diMinus, adxVal] = ta.dmi(i_adxLen, i_adxLen)
float adxSlope = adxVal - nz(adxVal[i_adxSlopeLB])

string adxTier = adxVal > 45 ? "EXTREME" : adxVal > 30 ? "STRONG" : adxVal > 20 ? "MODERATE" : "WEAK"
color  adxClr  = adxVal > 45 ? C_ORANGE : adxVal > 30 ? C_BULL : adxVal > 20 ? C_CYAN : C_GRAY
string adxSlopeStr = adxSlope > i_adxSlopeThr ? "RISING" : adxSlope < -i_adxSlopeThr ? "FALLING" : "FLAT"
color  adxSlopeClr = adxSlope > i_adxSlopeThr ? C_BULL : adxSlope < -i_adxSlopeThr ? C_BEAR : C_NEUTRAL

// ============================================================================
// 50 EMA SLOPE ANALYSIS
// ============================================================================
float emaSlopeRaw  = emaSlow - nz(emaSlow[i_emaSlopeLB])
float emaSlopeNorm = emaSlow != 0 ? emaSlopeRaw / emaSlow * 100 : 0.0
bool  emaSlopePos  = emaSlopeNorm > 0.002   
bool  emaSlopeNeg  = emaSlopeNorm < -0.002

float prevSlopeNorm = nz(emaSlow[i_emaSlopeLB]) != 0 ? (nz(emaSlow[i_emaSlopeLB]) - nz(emaSlow[i_emaSlopeLB * 2])) / nz(emaSlow[i_emaSlopeLB]) * 100 : 0.0
float slopeAccel    = emaSlopeNorm - prevSlopeNorm

// ============================================================================
// TREND PHASE CLASSIFICATION
// ============================================================================
bool fastCrossMid = ta.cross(emaFast, emaMid)
bool midCrossSlow = ta.cross(emaMid, emaSlow)
int  barsSinceFXM = ta.barssince(fastCrossMid)
int  barsSinceMXS = ta.barssince(midCrossSlow)
bool recentCross  = (not na(barsSinceFXM) and barsSinceFXM <= 6) or (not na(barsSinceMXS) and barsSinceMXS <= 10)

string trendPhase = "CONSOLIDATING"
color  phaseClr   = C_NEUTRAL

if recentCross
    trendPhase := "REVERSING"
    phaseClr   := C_ORANGE
else if adxVal > 30 and adxSlope < -i_adxSlopeThr
    trendPhase := "EXHAUSTING"
    phaseClr   := C_YELLOW
else if adxVal > 30 and math.abs(adxSlope) <= i_adxSlopeThr
    trendPhase := "MATURE"
    phaseClr   := C_CYAN
else if adxVal > 20 and adxSlope > i_adxSlopeThr
    trendPhase := "ACCELERATING"
    phaseClr   := C_BULL
else if adxVal <= 25 and adxSlope > i_adxSlopeThr and not emaCompressed
    trendPhase := "EMERGING"
    phaseClr   := color.new(C_BULL, 30)
else if adxVal < 20 and emaCompressed
    trendPhase := "CONSOLIDATING"
    phaseClr   := C_GRAY
else
    trendPhase := "CONSOLIDATING"
    phaseClr   := C_GRAY

// ============================================================================
// RSI & MACD 
// ============================================================================
float rsiVal = ta.rsi(close, i_rsiLen)
string rsiLbl = rsiVal > 70 ? "OVERBOUGHT" : rsiVal > 55 ? "BULLISH" : rsiVal < 30 ? "OVERSOLD" : rsiVal < 45 ? "BEARISH" : "NEUTRAL"
color  rsiClr = rsiVal > 70 ? C_ORANGE : rsiVal > 55 ? C_BULL : rsiVal < 30 ? C_ORANGE : rsiVal < 45 ? C_BEAR : C_NEUTRAL

[macdLine, macdSignalLine, macdHist] = ta.macd(close, i_macdFast, i_macdSlow, i_macdSignal)
string macdStr = macdHist > 0 ? "BULLISH" : macdHist < 0 ? "BEARISH" : "NEUTRAL"
color  macdClr = macdHist > 0 ? C_BULL : macdHist < 0 ? C_BEAR : C_NEUTRAL
bool macdAboveSignal = macdLine > macdSignalLine
string macdXStr = macdAboveSignal ? "ABOVE SIG" : "BELOW SIG"

// ============================================================================
// DIVERGENCE DETECTION
// ============================================================================
var float prevRsiPL = na, var float prevPricePL = na, var int prevPivLowBar = na
var float prevRsiPH = na, var float prevPricePH = na, var int prevPivHiBar = na

float rsiPivotLow  = i_rsiDiv ? ta.pivotlow(rsiVal, i_rsiPivotL, i_rsiPivotR) : na
float pricePivLow  = i_rsiDiv ? ta.pivotlow(low, i_rsiPivotL, i_rsiPivotR) : na
float rsiPivotHigh = i_rsiDiv ? ta.pivothigh(rsiVal, i_rsiPivotL, i_rsiPivotR) : na
float pricePivHigh = i_rsiDiv ? ta.pivothigh(high, i_rsiPivotL, i_rsiPivotR) : na

var string rsiDivState = "NONE"
var color  rsiDivClr   = C_NEUTRAL
var int    rsiDivBar   = na

// Bullish RSI
if not na(rsiPivotLow) and not na(pricePivLow)
    int curPivBar = bar_index - i_rsiPivotR
    if not na(prevRsiPL) and not na(prevPricePL) and not na(prevPivLowBar)
        if pricePivLow < prevPricePL and rsiPivotLow > prevRsiPL
            rsiDivState := "BULL DIV"
            rsiDivClr := C_BULL
            rsiDivBar := bar_index
            line.new(prevPivLowBar, prevPricePL, curPivBar, pricePivLow, color=C_BULL, style=line.style_dashed, width=2)
        else if pricePivLow > prevPricePL and rsiPivotLow < prevRsiPL
            rsiDivState := "HIDDEN BULL"
            rsiDivClr := color.new(C_BULL, 40)
            rsiDivBar := bar_index
    prevRsiPL := rsiPivotLow
    prevPricePL := pricePivLow
    prevPivLowBar := curPivBar

// Bearish RSI
if not na(rsiPivotHigh) and not na(pricePivHigh)
    int curPivBar = bar_index - i_rsiPivotR
    if not na(prevRsiPH) and not na(prevPricePH) and not na(prevPivHiBar)
        if pricePivHigh > prevPricePH and rsiPivotHigh < prevRsiPH
            rsiDivState := "BEAR DIV"
            rsiDivClr := C_BEAR
            rsiDivBar := bar_index
            line.new(prevPivHiBar, prevPricePH, curPivBar, pricePivHigh, color=C_BEAR, style=line.style_dashed, width=2)
        else if pricePivHigh < prevPricePH and rsiPivotHigh > prevRsiPH
            rsiDivState := "HIDDEN BEAR"
            rsiDivClr := color.new(C_BEAR, 40)
            rsiDivBar := bar_index
    prevRsiPH := rsiPivotHigh
    prevPricePH := pricePivHigh
    prevPivHiBar := curPivBar

if not na(rsiDivBar) and bar_index - rsiDivBar > 40
    rsiDivState := "NONE"
    rsiDivClr := C_NEUTRAL

// MACD Divs omitted for brevity but mirrors identical structure using macdHist pivots
var string macdDivState = "NONE"
var color  macdDivClr   = C_NEUTRAL

// ============================================================================
// SCORE, VOL & BB
// ============================================================================
float obvVal   = ta.obv
float obvEma   = ta.ema(obvVal, i_obvEmaLen)
float obvSlope = obvEma - nz(obvEma[5])

bool priceUp   = emaSlopePos
bool obvUp     = obvSlope > 0
bool priceDown = emaSlopeNeg
bool obvDown   = obvSlope < 0

string obvStr = i_useOBV ? ((priceUp and obvUp) or (priceDown and obvDown) ? "CONFIRMING" : (priceUp and obvDown) or (priceDown and obvUp) ? "DIVERGING" : "NEUTRAL") : "NEUTRAL"
color  obvClr = i_useOBV ? (obvStr == "CONFIRMING" ? C_BULL : obvStr == "DIVERGING" ? C_ORANGE : C_NEUTRAL) : C_NEUTRAL

float avgVol  = ta.sma(volume, i_relVolLen)
float relVol  = avgVol != 0 ? volume / avgVol : 0.0
string relVolStr = relVol > 2.0 ? "SPIKE" : relVol > 1.2 ? "ABOVE" : relVol > 0.8 ? "NORMAL" : "DRY"
color  relVolClr = relVol > 2.0 ? C_ORANGE : relVol > 1.2 ? C_BULL : relVol > 0.8 ? C_NEUTRAL : C_GRAY

float atrVal = ta.atr(i_atrLen)
int atrRank = 0
for i = 1 to i_atrPctlLB
    if atrVal > nz(atrVal[i])
        atrRank += 1
float atrPctl = i_atrPctlLB > 0 ? atrRank / i_atrPctlLB * 100 : 50.0

string atrRegime = atrPctl >= 90 ? "EXTREME" : atrPctl >= 70 ? "HIGH" : atrPctl >= 30 ? "NORMAL" : "LOW"
color  atrClr    = atrPctl >= 90 ? C_ORANGE : atrPctl >= 70 ? C_YELLOW : atrPctl >= 30 ? C_NEUTRAL : C_CYAN

[bbMid, bbUp, bbLow] = ta.bb(close, i_bbLen, i_bbMult)
float bbWidth = bbMid != 0 ? (bbUp - bbLow) / bbMid * 100 : 0.0
int bbwRank = 0
for i = 1 to i_bbwPctlLB
    if bbWidth > nz(bbWidth[i])
        bbwRank += 1
float bbwPctl = i_bbwPctlLB > 0 ? bbwRank / i_bbwPctlLB * 100 : 50.0

string bbwStr = bbwPctl < 10 ? "COMPRESSED" : bbwPctl < 30 ? "NARROW" : bbwPctl > 90 ? "EXPANDED" : bbwPctl > 70 ? "WIDE" : "NORMAL"
color  bbwClr = bbwPctl < 10 ? C_CYAN : bbwPctl < 30 ? color.new(C_CYAN, 40) : bbwPctl > 90 ? C_ORANGE : bbwPctl > 70 ? C_YELLOW : C_NEUTRAL

// ============================================================================
// COMPOSITE TREND SCORE
// ============================================================================
int wStructural = i_useWeighted ? 2 : 1
int wConfirm    = 1
int trendScore = 0

// Structural (2x)
trendScore += emaBullish ? wStructural : emaBearish ? -wStructural : 0
if not na(d200Ema)
    trendScore += close > d200Ema ? wStructural : close < d200Ema ? -wStructural : 0
if i_useHTF and not na(h4_50Ema)
    trendScore += close > h4_50Ema ? wStructural : close < h4_50Ema ? -wStructural : 0

// Confirmation (1x)
if adxVal > 20
    trendScore += diPlus > diMinus ? wConfirm : diMinus > diPlus ? -wConfirm : 0
trendScore += rsiVal > 55 ? wConfirm : rsiVal < 45 ? -wConfirm : 0
trendScore += macdAboveSignal ? wConfirm : -wConfirm
if i_useOBV
    trendScore += obvSlope > 0 ? wConfirm : obvSlope < 0 ? -wConfirm : 0
trendScore += emaSlopePos ? wConfirm : emaSlopeNeg ? -wConfirm : 0

int maxScore = 3 * wStructural + 5 * wConfirm
float scoreAvg5  = ta.sma(trendScore, 5)
string scoreTrend = trendScore > scoreAvg5 + 0.5 ? "IMPROVING" : trendScore < scoreAvg5 - 0.5 ? "FADING" : "STABLE"
color scoreTrendClr = scoreTrend == "IMPROVING" ? C_BULL : scoreTrend == "FADING" ? C_BEAR : C_NEUTRAL

float dist200 = not na(d200Ema) and d200Ema != 0 ? (close - d200Ema) / d200Ema * 100 : 0.0
color dist200Clr = dist200 > 5 ? C_BULL : dist200 > 0 ? color.new(C_BULL, 40) : dist200 < -5 ? C_BEAR : dist200 < 0 ? color.new(C_BEAR, 40) : C_NEUTRAL

// ============================================================================
// PLOTS
// ============================================================================
plot(i_showEma ? emaFast : na, "Fast EMA", color=C_BULL, linewidth=1)
plot(i_showEma ? emaMid  : na, "Mid EMA",  color=C_YELLOW, linewidth=1)
plot(i_showEma ? emaSlow : na, "Slow EMA", color=C_BEAR, linewidth=1)

p_fast = plot(i_showEma and i_showCloud ? emaFast : na, "Fast (fill)", color=color(na), display=display.none)
p_slow = plot(i_showEma and i_showCloud ? emaSlow : na, "Slow (fill)", color=color(na), display=display.none)
fill(p_fast, p_slow, color=emaBullish ? C_CLOUD_BULL : emaBearish ? C_CLOUD_BEAR : C_CLOUD_MIX)

plot(i_showD200 ? d200Ema : na, "Daily 200 EMA", color=C_200_EMA, linewidth=2, style=plot.style_line)
plot(i_showD50  ? d50Ema  : na, "Daily 50 EMA",  color=C_D50_EMA, linewidth=2, style=plot.style_stepline)
plot(i_show4H50 ? h4_50Ema : na, "4H 50 EMA", color=C_4H50_EMA, linewidth=1, style=plot.style_stepline)
plot(i_show1H50 ? h1_50Ema : na, "1H 50 EMA", color=C_1H50_EMA, linewidth=1, style=plot.style_stepline)

// ============================================================================
// 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
        "Bottom Left" => position.bottom_left
        => position.top_right

    string cellSize = switch i_dashSize
        "Tiny" => size.tiny
        "Small" => size.small
        "Normal" => size.normal
        "Large" => size.large
        => size.small

    var table dash = table.new(tblPos, 3, 20,
         bgcolor      = C_DASH_BG,
         border_width = 0,
         border_color = color.new(#FFFFFF, 85),
         frame_width  = 1,
         frame_color  = color.new(#FFFFFF, 80))

    // === Row 0: Header ===
    table.cell(dash, 0, 0, "  Trend Compass - 15m  ", text_color=#FFFFFF,
         bgcolor=C_DASH_HEADER, text_size=cellSize, text_halign=text.align_center)
    table.cell(dash, 1, 0, "", bgcolor=C_DASH_HEADER, text_size=cellSize)
    table.cell(dash, 2, 0, "", bgcolor=C_DASH_HEADER, text_size=cellSize)
    table.merge_cells(dash, 0, 0, 2, 0)

    table.cell(dash, 0, 1, "Phase", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
    table.cell(dash, 1, 1, trendPhase, text_color=phaseClr, text_size=cellSize, text_halign=text.align_right)

    string tsStr = (trendScore > 0 ? "+" : "") + str.tostring(trendScore) + "/" + str.tostring(maxScore)
    table.cell(dash, 0, 2, "Score", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
    table.cell(dash, 1, 2, tsStr, text_color=biasColor(trendScore, maxScore), text_size=cellSize, text_halign=text.align_right)
    table.cell(dash, 2, 2, biasLabel(trendScore, maxScore), text_color=biasColor(trendScore, maxScore), text_size=cellSize, text_halign=text.align_right)

    // Momentum, EMA Stack, ADX, etc...
    table.cell(dash, 0, 3, "Momentum", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
    table.cell(dash, 1, 3, scoreTrend, text_color=scoreTrendClr, text_size=cellSize, text_halign=text.align_right)

    table.cell(dash, 0, 4, "EMA Stack", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
    table.cell(dash, 1, 4, emaStackStr, text_color=emaStackClr, text_size=cellSize, text_halign=text.align_right)

    table.cell(dash, 0, 5, "ADX", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
    table.cell(dash, 1, 5, f1(adxVal), text_color=adxClr, text_size=cellSize, text_halign=text.align_right)
    
    table.cell(dash, 0, 6, "ADX Slope", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
    table.cell(dash, 1, 6, adxSlopeStr, text_color=adxSlopeClr, text_size=cellSize, text_halign=text.align_right)
    
    // (Abridged Standard Rows)
    table.cell(dash, 0, 13, "ATR", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
    table.cell(dash, 1, 13, atrRegime, text_color=atrClr, text_size=cellSize, text_halign=text.align_right)

    table.cell(dash, 0, 14, "BB Width", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
    table.cell(dash, 1, 14, bbwStr, text_color=bbwClr, text_size=cellSize, text_halign=text.align_right)

    // 15m HTF Specific Block
    string d200Str = d200Bullish ? "ABOVE" : d200Bearish ? "BELOW" : "N/A"
    table.cell(dash, 0, 15, "D 200 EMA", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
    table.cell(dash, 1, 15, d200Str, text_color=(d200Bullish ? C_BULL : C_BEAR), text_size=cellSize, text_halign=text.align_right)
    table.cell(dash, 2, 15, (dist200 >= 0 ? "+" : "") + f1(dist200) + "%", text_color=dist200Clr, text_size=cellSize, text_halign=text.align_right)

    string d50Str = not na(d50Ema) ? (close > d50Ema ? "ABOVE" : "BELOW") : "N/A"
    table.cell(dash, 0, 16, "D 50 EMA", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
    table.cell(dash, 1, 16, d50Str, text_color=(close > d50Ema ? C_BULL : C_BEAR), text_size=cellSize, text_halign=text.align_right)

    string h4_50Str = h4_50Bullish ? "ABOVE" : h4_50Bearish ? "BELOW" : "N/A"
    table.cell(dash, 0, 17, "4H 50 EMA", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
    table.cell(dash, 1, 17, h4_50Str, text_color=(h4_50Bullish ? C_BULL : C_BEAR), text_size=cellSize, text_halign=text.align_right)

    string h1_50Str = h1_50Bullish ? "ABOVE" : h1_50Bearish ? "BELOW" : "N/A"
    table.cell(dash, 0, 18, "1H 50 EMA", text_color=C_DASH_TEXT, text_size=cellSize, text_halign=text.align_left)
    table.cell(dash, 1, 18, h1_50Str, text_color=(h1_50Bullish ? C_BULL : C_BEAR), text_size=cellSize, text_halign=text.align_right)

Overview

Trend Compass 15m calculates a triple exponential moving average ribbon, higher-timeframe reference EMAs from 1H, 4H, and Daily periods, RSI and MACD divergences, On-Balance Volume trends, and ATR volatility metrics. The indicator classifies current conditions into six trend phases and calculates a composite weighted trend score, displaying data on an integrated heads-up dashboard panel along with optional Ichimoku and Fibonacci levels. Higher-timeframe values on the unclosed higher-timeframe bar repaint until that bar closes.

Details

Published by

chris-c-thomas on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Trend, Moving averages, Divergence, Screeners & dashboards

Markets

Stocks, Indices

Timeframes

M15-H1

Risk Notes

  • Signals can change on the live bar or after it closes (repainting)

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
Fast EMAint10i_emaFastLen
Mid EMAint21i_emaMidLen
Slow EMAint50i_emaSlowLen
Show EMA Ribbonbooltruei_showEma
Show Cloud Fillbooltruei_showCloud
Enable HTF Confirmationbooltruei_useHTF
Show 1H 50 EMAbooltruei_show1H50
Show 4H 50 EMAbooltruei_show4H50
Show Daily 50 EMAbooltruei_showD50
Show Daily 200 EMAbooltruei_showD200
ADX Lengthint14i_adxLen
ADX Slope Lookbackint5i_adxSlopeLB
Show all 40 settings
SettingTypeDefaultVariable
EMA Slope Lookbackint5i_emaSlopeLB
ADX Slope Thresholdfloat0.3i_adxSlopeThr
RSI Lengthint14i_rsiLen
Enable RSI Divergencebooltruei_rsiDiv
Divergence Pivot Left Barsint3i_rsiPivotL
Divergence Pivot Right Barsint2i_rsiPivotR
MACD Fastint12i_macdFast
MACD Slowint26i_macdSlow
MACD Signalint9i_macdSignal
Enable MACD Divergencebooltruei_macdDiv
MACD Div Pivot Leftint3i_macdPivotL
MACD Div Pivot Rightint2i_macdPivotR
Enable OBV Analysisbooltruei_useOBV
OBV EMA Lengthint21i_obvEmaLen
Relative Volume Lookbackint20i_relVolLen
ATR Lengthint14i_atrLen
ATR Percentile Lookbackint390i_atrPctlLB
BB Lengthint20i_bbLen
BB Multiplierfloat2.0i_bbMult
BB Width Percentile Lookbackint390i_bbwPctlLB
Show 52-Week High/Lowbooltruei_show52w
Show Prior Day H/L/Cbooltruei_showPrDay
Show Prior Week H/L/Cbooltruei_showPrWk
Show Fibonacci Levelsboolfalsei_showFib
Fibonacci Lookback Barsint260i_fibLB
Show Ichimoku Cloudboolfalsei_showIchi
Tenkan-sen (Conversion)int9i_ichiTenkan
Kijun-sen (Base)int26i_ichiKijun
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

218 stars

TradersPost Trading Checklist is an overlay indicator that evaluates a suite of popular technical conditions for long and short trade setups. It checks session-anchored VWAP, fast and slow EMA alignments and crosses, 200 SMA, RSI overbought or oversold conditi

Indicator v6 Screeners & dashboardsTrend
Updated 3 months ago · None stated GitHub repository of TradersPost Trading Checklist
92 stars

This indicator plots five moving averages of a chosen source on the price chart, defaulting to EMA lengths 10, 20, 30, 40 and 50. The user can switch the average type to SMA, WMA or HMA and recolor each line. Wide translucent glows sit under the lines, and sof

Indicator v5 TrendMoving averages
Updated 8 weeks ago · Custom licence GitHub repository of Moon Dev Rainbow MA Ribbon 10-50

EMA Ribbon Overlay

by iamrichardD
57 stars

Calculates exponential moving averages for fast, pivot, and slow lengths along with optional 50, 100, and 200 simple moving average context lines. It visualizes bullish and bearish ribbon fills on the main price chart alongside MACD calculations to generate ea

Indicator v6 TrendMoving averages
Updated 1 year ago · Apache-2.0 GitHub repository of EMA Ribbon Overlay
53 stars

This indicator analyzes historical candle direction and price range over a configurable lookback window to gauge market trend. It categorizes trend direction using candle count, price range sum, or both. Signals are filtered using a Simple Moving Average to en

Indicator v6 TrendOscillators & momentum
Updated 2 months ago · MPL-2.0 GitHub repository of Candle Counter + MA + RSI + Mode
43 stars

This indicator evaluates market momentum and risk by calculating trend metrics and volatility stats, displaying them in an on-chart table pane. It measures short-term and long-term trend strength via two MACD configurations built on three customizable EMA leng

Indicator v5 TrendVolatility & bands
Updated 1 year ago · None stated GitHub repository of Trend Strength & Volatility

5 EMAs

by hirawatt
11 stars

5 EMAs calculates and plots five Exponential Moving Averages derived from five configurable timeframes directly onto the main price chart. It also plots the high and low lines fetched from an additional custom timeframe, accompanied by a small on-screen table

Indicator v4 Moving averagesTrend Repaints
Updated 2 years ago · MIT GitHub repository of 5 EMAs

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