# ==========================================
# Title: M&T Stoplight LOWER (Dashboard)
#Original Author: TheInvestorToolkit.com
# Description: Plots the Stoplight Signals in a separate pane with dynamic labels
# Ported By: ThinkScript Architect
# ==========================================
declare lower;
# ==========================================
# 1. INPUTS
# ==========================================
input priceSource = close;
input maType = {default "HWEMA", "SMA", "EMA", "Hull"};
input maLength = 20;
input proxyLength = 20;
input useMtf1 = yes;
input agg1 = AggregationPeriod.DAY;
input useMtf2 = yes;
input agg2 = AggregationPeriod.QUARTER;
input useMtf3 = yes;
input agg3 = AggregationPeriod.YEAR;
input label_offset = 0;
# ==========================================
# 2. MA CALCULATIONS
# ==========================================
def maVal = if maType == maType.SMA then Average(priceSource, maLength)
else if maType == maType.EMA then ExpAverage(priceSource, maLength)
else if maType == maType.Hull then HullMovingAvg(priceSource, maLength)
else HullMovingAvg(WMA(priceSource, maLength), maLength);
def maUp = maVal > maVal[1];
def maDn = maVal < maVal[1];
def isPmaBull = close > maVal and maUp;
def isPmaBear = close < maVal and maDn;
def isPmaWarnBull = close < maVal and !maDn;
def isPmaWarnBear = close > maVal and !maUp;
# ==========================================
# 3. PROXY CALCULATIONS
# ==========================================
def logClose = Log(close);
def rocSum = fold i = 1 to proxyLength with s = 0 do s + (logClose - logClose[i]);
def rocAverage = rocSum / proxyLength;
def proxyBull = rocAverage >= 0;
def proxyRising = rocAverage > rocAverage[1];
# ==========================================
# 4. MTF OPEN CALCULATIONS
# ==========================================
def open1 = open(period = agg1);
def open2 = open(period = agg2);
def open3 = open(period = agg3);
# ==========================================
# 5. VISUALIZATION (DASHBOARD ROWS)
# ==========================================
def y_pma = 1;
def y_slope = 2;
def y_proxy = 3;
def y_tf1 = 4;
def y_tf2 = 5;
def y_tf3 = 6;
# ROW: PtoMA
# Fix: Added !IsNaN(close) check to prevent plotting in the "Future"
plot DotPma = if !IsNaN(close) then y_pma else Double.NaN;
DotPma.SetPaintingStrategy(PaintingStrategy.POINTS);
DotPma.SetLineWeight(5);
DotPma.AssignValueColor(
if isPmaBull then Color.GREEN
else if isPmaBear then Color.RED
else if isPmaWarnBull then Color.YELLOW
else if isPmaWarnBear then Color.ORANGE
else Color.GRAY
);
# ROW: Slope
plot DotSlope = if !IsNaN(close) then y_slope else Double.NaN;
DotSlope.SetPaintingStrategy(PaintingStrategy.POINTS);
DotSlope.SetLineWeight(5);
DotSlope.AssignValueColor(
if maUp then Color.GREEN
else if maDn then Color.RED
else Color.GRAY
);
# ROW: Proxy Heatmap
plot DotProxy = if !IsNaN(close) then y_proxy else Double.NaN;
DotProxy.SetPaintingStrategy(PaintingStrategy.POINTS);
DotProxy.SetLineWeight(5);
DotProxy.AssignValueColor(
if proxyBull and proxyRising then Color.GREEN
else if proxyBull and !proxyRising then Color.DARK_GREEN
else if !proxyBull and !proxyRising then Color.RED
else Color.DARK_RED
);
# ROW: MTF 1
plot DotMtf1 = if useMtf1 and !IsNaN(close) then y_tf1 else Double.NaN;
DotMtf1.SetPaintingStrategy(PaintingStrategy.POINTS);
DotMtf1.SetLineWeight(5);
DotMtf1.AssignValueColor(if close > open1 then Color.GREEN else Color.RED);
# ROW: MTF 2
plot DotMtf2 = if useMtf2 and !IsNaN(close) then y_tf2 else Double.NaN;
DotMtf2.SetPaintingStrategy(PaintingStrategy.POINTS);
DotMtf2.SetLineWeight(5);
DotMtf2.AssignValueColor(if close > open2 then Color.GREEN else Color.RED);
# ROW: MTF 3
plot DotMtf3 = if useMtf3 and !IsNaN(close) then y_tf3 else Double.NaN;
DotMtf3.SetPaintingStrategy(PaintingStrategy.POINTS);
DotMtf3.SetLineWeight(5);
DotMtf3.AssignValueColor(if close > open3 then Color.GREEN else Color.RED);
# ==========================================
# 6. DYNAMIC LABELS (USING BUBBLES)
# ==========================================
def bar = BarNumber();
def lastBar = HighestAll(if !IsNaN(close) then bar else Double.NaN);
def isLastBar = bar == lastBar;
# --- PtoMA Bubble ---
def pma_dist = close - maVal;
def pma_perc = (close - maVal) / maVal * 100;
AddChartBubble(isLastBar, y_pma,
"PtoMA: " + (if isPmaBull then "Strong Bull" else if isPmaWarnBear then "Weak Bear" else if isPmaBear then "Strong Bear" else if isPmaWarnBull then "Weak Bull" else "Neutral") +
" (" + AsText(pma_dist, "%.2f") + " | " + (if pma_perc > 0 then "+" else "") + AsText(pma_perc, "%.2f") + "%)",
if isPmaBull then Color.GREEN else if isPmaWarnBear then Color.ORANGE else if isPmaBear then Color.RED else if isPmaWarnBull then Color.YELLOW else Color.GRAY,
yes);
# --- Slope Bubble ---
AddChartBubble(isLastBar, y_slope,
"Slope: " + (if maUp then "Bullish" else if maDn then "Bearish" else "Neutral"),
if maUp then Color.GREEN else if maDn then Color.RED else Color.GRAY,
yes);
# --- Proxy Bubble ---
AddChartBubble(isLastBar, y_proxy,
"Buying/Selling Proxy: " + (if proxyBull then (if proxyRising then "Bullish (Strong)" else "Bullish (Fading)") else (if !proxyRising then "Bearish (Strong)" else "Bearish (Fading)")),
if proxyBull and proxyRising then Color.GREEN else if proxyBull and !proxyRising then Color.DARK_GREEN else if !proxyBull and !proxyRising then Color.RED else Color.DARK_RED,
yes);
# --- MTF1 Bubble ---
def mtf1_bull = close > open1;
def mtf1_dist = close - open1;
def mtf1_perc = (close - open1) / open1 * 100;
AddChartBubble(useMtf1 and isLastBar, y_tf1,
(if agg1 == AggregationPeriod.YEAR then "Y" else if agg1 == AggregationPeriod.QUARTER then "3M" else if agg1 == AggregationPeriod.MONTH then "M" else if agg1 == AggregationPeriod.WEEK then "W" else if agg1 == AggregationPeriod.DAY then "D" else "Intra") +
" Open: " + (if mtf1_bull then "Bullish" else "Bearish") + " (" + AsText(mtf1_dist, "%.2f") + " | " + (if mtf1_perc > 0 then "+" else "") + AsText(mtf1_perc, "%.2f") + "%)",
if mtf1_bull then Color.GREEN else Color.RED,
yes);
# --- MTF2 Bubble ---
def mtf2_bull = close > open2;
def mtf2_dist = close - open2;
def mtf2_perc = (close - open2) / open2 * 100;
AddChartBubble(useMtf2 and isLastBar, y_tf2,
(if agg2 == AggregationPeriod.YEAR then "Y" else if agg2 == AggregationPeriod.QUARTER then "3M" else if agg2 == AggregationPeriod.MONTH then "M" else if agg2 == AggregationPeriod.WEEK then "W" else if agg2 == AggregationPeriod.DAY then "D" else "Intra") +
" Open: " + (if mtf2_bull then "Bullish" else "Bearish") + " (" + AsText(mtf2_dist, "%.2f") + " | " + (if mtf2_perc > 0 then "+" else "") + AsText(mtf2_perc, "%.2f") + "%)",
if mtf2_bull then Color.GREEN else Color.RED,
yes);
# --- MTF3 Bubble ---
def mtf3_bull = close > open3;
def mtf3_dist = close - open3;
def mtf3_perc = (close - open3) / open3 * 100;
AddChartBubble(useMtf3 and isLastBar, y_tf3,
(if agg3 == AggregationPeriod.YEAR then "Y" else if agg3 == AggregationPeriod.QUARTER then "3M" else if agg3 == AggregationPeriod.MONTH then "M" else if agg3 == AggregationPeriod.WEEK then "W" else if agg3 == AggregationPeriod.DAY then "D" else "Intra") +
" Open: " + (if mtf3_bull then "Bullish" else "Bearish") + " (" + AsText(mtf3_dist, "%.2f") + " | " + (if mtf3_perc > 0 then "+" else "") + AsText(mtf3_perc, "%.2f") + "%)",
if mtf3_bull then Color.GREEN else Color.RED,
yes);