Repaints MTF PPO Sniper Heatmap For ThinkOrSwim

Repaints

Ricky_005

New member
Plus
thinkorswim_H6rr1EC6ab.png

thinkorswim_isJv2SttK3.png

thinkorswim_0HuUr3UT1B.png


thinkorswim_Lk4VUNnrMH.png


MTF PPO Heatmap — It won’t predict the future, but it will expose your bad decisions faster.” :LOL:

This is a gift from me to the useThinkScript community.

I’ve spent a lot of time building, breaking, fixing, and tightening this until it behaved the way I want a real “at-a-glance” market read to behave. The goal is simple: stop guessing what timeframe is driving price, and see the PPO state across multiple timeframes in one clean heatmap.

This study is a Multi-Timeframe Standard PPO Heatmap built around PPO histogram behavior:

  • Histogram (DELTA) = MAC − SIG
  • Bull vs Bear is the sign of the histogram (above/below zero)
  • Strength vs Weakening is the histogram’s momentum (rising/falling)
So instead of staring at one PPO and hoping it represents “the market,” you get up to 8 rows of stacked timeframe context. It’s designed to be compact, readable, and fast enough to actually run.


What You Get​

1) A clean multi-timeframe PPO “state” heatmap (up to 8 rows)​

Rows ladder up automatically from your chart timeframe (ex: 1d → 2d → 3d → 4d → 1w → 1mo → 3mo → 1yr).

2) 4-Color or 2-Color heatmap mode (plots only)​

4-Color = direction + momentum shading

  • BrightGreen: bull strengthening
  • DarkGreen: bull weakening
  • DarkRed: bear weakening
  • BrightRed: bear strengthening
2-Color = momentum-only

  • Green when histogram rising
  • Red when histogram falling
Labels stay 4-color by design (so they always tell the full story).

3) Optional zero-line cross markers + forecast markers​

  • Confirmed markers fire on the exact cross bar (MAC crosses 0).
  • Forecast markers can show when a cross is projected within a user-set window.

4) Labels that don’t lie to you​

NaN-safe logic is included so you don’t get fake arrows when data is unavailable (common on higher aggregations or limited history symbols). Prediction math is last-bar gated to reduce load.


Performance / Practical Controls​

  • rowCount (3–8 rows) lets you cut rows for speed and clarity.
  • Rows that aren’t enabled or aren’t distinct do not plot (no useless placeholders).
  • Base chart support is time-based up to Quarterly (3 months). Year is used only as an upper ladder row.


“Repaint?” — Quick clarification​

This study does not use ZigZag, pivots, or any future-bar lookahead logic. It’s straight PPO math (MAC, Signal, Histogram) and state coloring.

Like all indicators, values update until the bar is closed.
And because this is MTF, Thinkorswim will also update higher-timeframe values while that higher-timeframe candle is still forming. So on a lower timeframe chart, the higher rows can appear to “shift” until that higher timeframe bar closes. A lot of people call that repainting, but it’s really just normal MTF behavior.

Bottom line: nothing “changes after the higher-timeframe bar is closed.”
Treat higher-timeframe rows/markers as confirmed on that timeframe’s close.
Also, the optional forecast/projection features are estimates by design and will update as momentum changes.



How I Use It​

This isn’t meant to be “the signal.” It’s meant to be your market posture dashboard:

  • Are the higher timeframes aligned or fighting?
  • Is momentum expanding or fading?
  • Is the move real, or just lower-TF noise?
When this thing lights up across multiple rows, you’re not guessing anymore—you’re reading the tape with context.


Give It a Real Test​

Drop it on different symbols, different timeframes, and watch how it behaves around:

  • trend starts
  • pullbacks
  • consolidations
  • reversals
  • fakeouts
If you find improvements, optimizations, or cleaner ways to do parts of it—post them. I’m putting this out here to contribute, and I’m absolutely open to other sharp eyes making it better.

“Use it, tweak it, improve it — and if you make it better, post it back so the whole forum levels up.”

Hope you all enjoy it,
Tricky Rick



Code:
declare lower;
#===================================================================
# ThinkOrSwim Study: MTF PPO Heatmap (EMA-based)
# Author: Richard Campbell AKA Tricky Rick
# Created: January 03, 2026
# 2026 Richard Campbell. All rights reserved.
#===================================================================
# USER GUIDE
#
# What this study is:
# - A compact, multi-timeframe PPO “state” heatmap (up to 8 rows).
# - Each row represents a progressively higher aggregation than the chart timeframe.
# - The heatmap state is driven by PPO Histogram behavior:
#     • Histogram (DELTA) = (MAC - SIG)
#     • Bull/Bear is Histogram sign (above/below 0)
#     • Strength vs weakening is Histogram momentum (change in Histogram per bar)
#
# Core PPO settings:
# - fastLen / slowLen / signalLen:
#   PPO lengths used on every enabled timeframe row.
#   Note: shorter signalLen generally reduces lag/smear (tighter regime changes),
#   but can increase noise on faster timeframes.
# - ppoAvgType:
#   MA type used for Fast, Slow, AND the Signal line (all share the same AvgType).
# - priceSource:
#   Selects the OHLC input used to compute PPO on every timeframe row.
#
# Heatmap plot color mode (PLOTS ONLY):
# - heatColorMode ("4-Color" / "2-Color"):
#   Controls ONLY the heatmap block colors (HeatTF plots).
#   IMPORTANT: Labels are NOT affected by this toggle and remain 4-color (BG/DG/DR/BR).
#
#   • 4-Color (default) = direction + momentum shading:
#       BrightGreen = Histogram > 0 AND rising (bull strengthening)
#       DarkGreen   = Histogram > 0 AND falling (bull weakening)
#       BrightRed   = Histogram < 0 AND falling or flat (bear strengthening / more negative)
#       DarkRed     = Histogram < 0 AND rising (bear weakening / rising toward 0)
#       Neutral     = Histogram == 0 OR values unavailable (NaN)
#
#   • 2-Color = momentum-only (simplified):
#       BrightGreen = Histogram rising (st == 2 OR st == -1)
#       BrightRed   = Histogram falling (st == 1 OR st == -2)
#       Neutral     = Histogram == 0 OR values unavailable (NaN)
#
# Heatmap rendering style:
# - heatPaint (Squares / LineVsSquares / Horizontal / Points)
#   Default is LineVsSquares (so you don’t have to keep switching it back after reloads).
#
# Row control (performance + clarity):
# - rowCount ("3 Rows" … "8 Rows"):
#   Limits how many timeframe rows are enabled.
#   NOTE: TF1–TF3 are always eligible by design (rowCount minimum is 3).
#         rowCount controls only TF4–TF8.
#
# - Disabled rows:
#   • are gated to Double.NaN and do not plot (no placeholders / no gray “empty” blocks)
#   • aggregation price series calls are conditionally gated so they are intended not to run
#     when a row is disabled (ThinkScript can still pre-evaluate some expressions internally,
#     but outputs remain NaN and the row does not print).
#
# - Non-distinct rows:
#   If a mapped higher TF collapses to the same aggregation as the prior row (P3 == P2, etc.),
#   that row is treated as “not distinct” and is not shown.
#
# Labels (two independent sections):
# - showHeaderLabels:
#   Toggles ONLY the header/title + settings/spec labels.
# - showLabels:
#   Toggles ONLY the TF1–TF8 timeframe row labels (and also gates histogram prediction math).
#
# Label display modes:
# - showSignalNumbers:
#   Shows numeric MAC / SIG / HIST, plus optional prediction (" > Nc").
# - showSignalDirections:
#   Shows direction symbols + sign (+/-/?), plus optional prediction (" > Nc").
# - TF-only mode (both modes OFF):
#   Shows timeframe text only, plus a small tag indicating PPO MAC relative to the 0 line:
#     "+  " = MAC >= 0
#     "-  " = MAC <  0
#     "?  " = MAC is NaN / unavailable on this bar (insufficient data / warm-up)
#   Optional prediction can also be shown in TF-only mode.
#   NOTE: The TF +/-/? tag is shown ONLY in TF-only mode (it is hidden when Numbers or Directions is ON).
#
# IMPORTANT precedence:
# - If BOTH Numbers and Directions are ON, Numbers mode takes priority (Directions is ignored).
#
# NaN / “insufficient bars” behavior (important):
# - On some symbols/timeframes (new IPOs, limited history, very high TFs, early bars), MAC/SIG/HIST can be NaN
#   until enough bars exist to compute the moving averages.
# - The label logic intentionally uses NaN checks for slopes/sign so you do NOT get misleading arrows:
#     • Without NaN checks, NaN comparisons can print a fake down "\" (because (NaN > 0) is false).
#     • With NaN checks, slope arrows print "?" when values are unavailable.
# - In Numbers mode, numeric fields may display "NaN" text when the underlying value is unavailable.
#
# Histogram prediction (to zero-cross):
# - showHistoPrediction:
#   Master toggle for HISTOGRAM (MAC-SIG) projection math + printing (" > Nc").
# - Predictions run ONLY when:
#   • showLabels is ON, and
#   • showHistoPrediction is ON,
#   • and ONLY on the last visible bar (labels are last-bar gated).
#
# Last-bar gating:
# - Header labels, TF labels, and histogram prediction math are computed/printed ONLY on the last visible bar to reduce load.
#
# Zero-cross markers (MAC crosses 0):
# - showZeroCrossMarkers:
#   CONFIRMED markers plot ONLY on the actual cross bar:
#     • Blue   = cross ABOVE 0
#     • Yellow = cross BELOW 0
#
# Zero-cross forecast (MAC projected to cross 0 soon):
# - showZeroCrossForecast:
#   Forecast markers plot every bar while “in-range”:
#     • Blue-ish  = projected cross ABOVE (currently below 0, rising)
#     • Yellow-ish= projected cross BELOW (currently above 0, falling)
# - forecastBars:
#   Sets the look-ahead window (1–7 bars) for when a forecast marker is allowed to print.
#
# Base timeframe support (fail-fast behavior):
# - The base chart aggregation supports standard time-based charts from 1m up to Quarterly (3 months).
# - If the base chart aggregation is not one of the explicitly supported timeframes,
#   the study will stop with an "N/A: Unsupported base aggregation" message.
# - YEAR is used only as a ladder mapping ABOVE Quarterly (as a higher row); it is not supported as a base chart.
#
# Notes:
# - Neutral (dark gray) can appear when:
#     • state resolves to 0 (histogram exactly 0), OR
#     • values are unavailable (NaN) due to warm-up/insufficient data.
# - Depending on symbol history and platform data limits, very high mapped rows (e.g., Year) may not always be usable.
#===================================================================

Assert(GetAggregationPeriod() <= AggregationPeriod.QUARTER,
       "N/A: This study only supports time-based charts up to 3 months (Quarterly).");

#---------------------------------------------------------------------------------------------
# Core PPO inputs – standard single-engine PPO used across all 8 timeframes
#---------------------------------------------------------------------------------------------
input fastLen     = 12;
input slowLen     = 26;
input signalLen   = 9;
input ppoAvgType  = AverageType.EXPONENTIAL;  # SIMPLE / EXPONENTIAL / WILDERS / HULL / WEIGHTED
input priceSource = {default Close, Open, High, Low, HL2, HLC3, OHLC4};

input HeatMapControls = {default "<<<<<< Heatmap Settings >>>>>>"};
# Row-count toggle: 3–7 rows or full 8
input rowCount = {
    default "8 Rows",
    "7 Rows",
    "6 Rows",
    "5 Rows",
    "4 Rows",
    "3 Rows"
};

# Decode dropdown → numeric row count (3–8)
def rows =
    if    rowCount == rowCount."8 Rows" then 8
    else if rowCount == rowCount."7 Rows" then 7
    else if rowCount == rowCount."6 Rows" then 6
    else if rowCount == rowCount."5 Rows" then 5
    else if rowCount == rowCount."4 Rows" then 4
    else 3;

# Row-count booleans (rowCount minimum is 3 Rows)
def useTF2 = yes;
def useTF3 = yes;
def useTF4 = rows >= 4;
def useTF5 = rows >= 5;
def useTF6 = rows >= 6;
def useTF7 = rows >= 7;
def useTF8 = rows >= 8;

# Heatmap color mode:
# - 4-Color = direction + momentum shading (BG/DG/DR/BR)
# - 2-Color = momentum-only (Histogram rising = Green, falling = Red)
input heatColorMode = {default "4-Color", "2-Color"};
def use2Color = heatColorMode == heatColorMode."2-Color";

# Heatmap rendering style
input heatPaint = {Squares, default LineVsSquares, Horizontal, Points};

def heatPS =
    if heatPaint == heatPaint.LineVsSquares then PaintingStrategy.LINE_VS_SQUARES
    else if heatPaint == heatPaint.Squares then PaintingStrategy.SQUARES
    else if heatPaint == heatPaint.Horizontal then PaintingStrategy.HORIZONTAL
    else PaintingStrategy.POINTS;

input PredictionSettings = {default "<<<< Zero Prediction Settings >>>>"};
input showZeroCrossMarkers = yes; # ON/OFF
def zxOff = 0.35;    # vertical offset so markers sit on top of the row
input showZeroCrossForecast = no;
def zxFoff = 0.02;   # separate offset so forecast can't cover confirmed markers
input forecastBars = { default "1 Bar", "2 Bars", "3 Bars", "4 Bars", "5 Bars", "6 Bars", "7 Bars" };
def minProjSlope = 0.000001;

input LabelSetting = {default "<<<<<<<< Label Settings >>>>>>>>"};
#---------------------------------------------------------------------------------------------
# Label controls
#---------------------------------------------------------------------------------------------
input showHeaderLabels      = yes;  # Title + settings/spec header labels only
input showLabels            = yes;  # TF1–TF8 timeframe row labels (and projection gating)
input TfLabelLocation = {default "Top-Left", "Top-Right", "Bottom-Left", "Bottom-Right"};
input showSignalDirections  = no;   # direction-only view ("\ /  + / - / ?  > Nc")
input showSignalNumbers     = no;   # numeric view ("mac / sig | hist > Nc")
input showHistoPrediction   = yes;  # Enables/disables HISTOGRAM (MAC-SIG) projection-to-zero math + label printing (" > Nc").

#---------------------------------------------------------------------------------------------
# Global colors — PPO state
#---------------------------------------------------------------------------------------------
DefineGlobalColor("BrightGreen", CreateColor(0, 255, 0));
DefineGlobalColor("DarkGreen",   CreateColor(15, 121, 25));
DefineGlobalColor("BrightRed",   CreateColor(255, 0, 0));
DefineGlobalColor("DarkRed",     CreateColor(170, 0, 0));
DefineGlobalColor("Neutral",     Color.DARK_GRAY);

#------------------------------------------
# Global colors (Crossings and Predictions)
#------------------------------------------
DefineGlobalColor("ZxCrossUp",      CreateColor(2,71,254)); # Lighter Blue
DefineGlobalColor("ZxCrossDown",    CreateColor(255,255,0)); # Yellow

DefineGlobalColor("ZxForecastUp",   CreateColor(21,96,189)); # Blue
DefineGlobalColor("ZxForecastDown", CreateColor(250,218,94)); # Darker Yellow

#---------------------------------------------------------------------------------------------
# Multi-timeframe ladder – base aggregation (P1) and mapped higher timeframes (P2–P8)
#---------------------------------------------------------------------------------------------
def P1 = GetAggregationPeriod();

def P1_isKnown =
     P1 == AggregationPeriod.MIN
  or P1 == AggregationPeriod.TWO_MIN
  or P1 == AggregationPeriod.THREE_MIN
  or P1 == AggregationPeriod.FOUR_MIN
  or P1 == AggregationPeriod.FIVE_MIN
  or P1 == AggregationPeriod.TEN_MIN
  or P1 == AggregationPeriod.FIFTEEN_MIN
  or P1 == AggregationPeriod.TWENTY_MIN
  or P1 == AggregationPeriod.THIRTY_MIN
  or P1 == AggregationPeriod.HOUR
  or P1 == AggregationPeriod.TWO_HOURS
  or P1 == AggregationPeriod.FOUR_HOURS
  or P1 == AggregationPeriod.DAY
  or P1 == AggregationPeriod.TWO_DAYS
  or P1 == AggregationPeriod.THREE_DAYS
  or P1 == AggregationPeriod.FOUR_DAYS
  or P1 == AggregationPeriod.WEEK
  or P1 == AggregationPeriod.MONTH
  or P1 == AggregationPeriod.QUARTER;

Assert(P1_isKnown,
       "N/A: Unsupported base aggregation. Use standard time-based charts (1m–3mo / Quarterly).");

script NextTF {
    input tf = 0;

    plot out =
        if tf == AggregationPeriod.MIN
         or tf == AggregationPeriod.TWO_MIN
         or tf == AggregationPeriod.THREE_MIN
         or tf == AggregationPeriod.FOUR_MIN
        then AggregationPeriod.FIVE_MIN
        else if tf == AggregationPeriod.FIVE_MIN then AggregationPeriod.TEN_MIN
        else if tf == AggregationPeriod.TEN_MIN then AggregationPeriod.FIFTEEN_MIN
        else if tf == AggregationPeriod.FIFTEEN_MIN then AggregationPeriod.TWENTY_MIN
        else if tf == AggregationPeriod.TWENTY_MIN then AggregationPeriod.THIRTY_MIN
        else if tf == AggregationPeriod.THIRTY_MIN then AggregationPeriod.HOUR
        else if tf == AggregationPeriod.HOUR then AggregationPeriod.TWO_HOURS
        else if tf == AggregationPeriod.TWO_HOURS then AggregationPeriod.FOUR_HOURS
        else if tf == AggregationPeriod.FOUR_HOURS then AggregationPeriod.DAY
        else if tf == AggregationPeriod.DAY then AggregationPeriod.TWO_DAYS
        else if tf == AggregationPeriod.TWO_DAYS then AggregationPeriod.THREE_DAYS
        else if tf == AggregationPeriod.THREE_DAYS then AggregationPeriod.FOUR_DAYS
        else if tf == AggregationPeriod.FOUR_DAYS then AggregationPeriod.WEEK
        else if tf == AggregationPeriod.WEEK then AggregationPeriod.MONTH
        else if tf == AggregationPeriod.MONTH then AggregationPeriod.QUARTER
        else if tf == AggregationPeriod.QUARTER then AggregationPeriod.YEAR
        else AggregationPeriod.YEAR;
}

def P2 = NextTF(P1).out;
def P3 = NextTF(P2).out;
def P4 = NextTF(P3).out;
def P5 = NextTF(P4).out;
def P6 = NextTF(P5).out;
def P7 = NextTF(P6).out;
def P8 = NextTF(P7).out;

#---------------------------------------------------------------------------------------------
# Effective TF enable flags – require both rowCount AND a distinct higher timeframe
#---------------------------------------------------------------------------------------------
def tf2Enabled = useTF2 and P2 != P1;
def tf3Enabled = useTF3 and P3 != P2;
def tf4Enabled = useTF4 and P4 != P3;
def tf5Enabled = useTF5 and P5 != P4;
def tf6Enabled = useTF6 and P6 != P5;
def tf7Enabled = useTF7 and P7 != P6;
def tf8Enabled = useTF8 and P8 != P7;

#---------------------------------------------------------------------------------------------
# LAST BAR GATING (labels + projections)
#---------------------------------------------------------------------------------------------
def inBar = !IsNaN(close);
def lastBar = !IsNaN(close) and IsNaN(close[-1]);

def showHdr = showHeaderLabels and lastBar;
def showTF  = showLabels and lastBar;

# Histogram projection gate (label-only): run ONLY when TF labels are printing (last-bar gated).
def useProj = showTF and showHistoPrediction;

#---------------------------------------------------------------------------------------------
# OHLC component gating based on priceSource (perf win)
#---------------------------------------------------------------------------------------------
def needOpen  = priceSource == priceSource.Open  or priceSource == priceSource.OHLC4;
def needHigh  = priceSource == priceSource.High  or priceSource == priceSource.HL2
             or priceSource == priceSource.HLC3  or priceSource == priceSource.OHLC4;
def needLow   = priceSource == priceSource.Low   or priceSource == priceSource.HL2
             or priceSource == priceSource.HLC3  or priceSource == priceSource.OHLC4;
def needClose = priceSource == priceSource.Close or priceSource == priceSource.HLC3
             or priceSource == priceSource.OHLC4;

#---------------------------------------------------------------------------------------------
# Price handles – per-timeframe OHLC
#---------------------------------------------------------------------------------------------
# Only enabled TFs + only required OHLC components make aggregation calls.
#-------------------------------------------------------------------------
# TF1 (base chart series — no aggregation call needed)
def o1 = if needOpen  then open  else Double.NaN;
def h1 = if needHigh  then high  else Double.NaN;
def l1 = if needLow   then low   else Double.NaN;
def c1 = if needClose then close else Double.NaN;

# TF2–TF8
def o2 = if tf2Enabled and needOpen  then open(period = P2)  else Double.NaN;
def h2 = if tf2Enabled and needHigh  then high(period = P2)  else Double.NaN;
def l2 = if tf2Enabled and needLow   then low(period = P2)   else Double.NaN;
def c2 = if tf2Enabled and needClose then close(period = P2) else Double.NaN;

def o3 = if tf3Enabled and needOpen  then open(period = P3)  else Double.NaN;
def h3 = if tf3Enabled and needHigh  then high(period = P3)  else Double.NaN;
def l3 = if tf3Enabled and needLow   then low(period = P3)   else Double.NaN;
def c3 = if tf3Enabled and needClose then close(period = P3) else Double.NaN;

def o4 = if tf4Enabled and needOpen  then open(period = P4)  else Double.NaN;
def h4 = if tf4Enabled and needHigh  then high(period = P4)  else Double.NaN;
def l4 = if tf4Enabled and needLow   then low(period = P4)   else Double.NaN;
def c4 = if tf4Enabled and needClose then close(period = P4) else Double.NaN;

def o5 = if tf5Enabled and needOpen  then open(period = P5)  else Double.NaN;
def h5 = if tf5Enabled and needHigh  then high(period = P5)  else Double.NaN;
def l5 = if tf5Enabled and needLow   then low(period = P5)   else Double.NaN;
def c5 = if tf5Enabled and needClose then close(period = P5) else Double.NaN;

def o6 = if tf6Enabled and needOpen  then open(period = P6)  else Double.NaN;
def h6 = if tf6Enabled and needHigh  then high(period = P6)  else Double.NaN;
def l6 = if tf6Enabled and needLow   then low(period = P6)   else Double.NaN;
def c6 = if tf6Enabled and needClose then close(period = P6) else Double.NaN;

def o7 = if tf7Enabled and needOpen  then open(period = P7)  else Double.NaN;
def h7 = if tf7Enabled and needHigh  then high(period = P7)  else Double.NaN;
def l7 = if tf7Enabled and needLow   then low(period = P7)   else Double.NaN;
def c7 = if tf7Enabled and needClose then close(period = P7) else Double.NaN;

def o8 = if tf8Enabled and needOpen  then open(period = P8)  else Double.NaN;
def h8 = if tf8Enabled and needHigh  then high(period = P8)  else Double.NaN;
def l8 = if tf8Enabled and needLow   then low(period = P8)   else Double.NaN;
def c8 = if tf8Enabled and needClose then close(period = P8) else Double.NaN;

# Shared priceSource selector used for all 8 timeframes
script SelectPrice {
    input o = 0.0;
    input h = 0.0;
    input l = 0.0;
    input c = 0.0;
    input priceSource = {default Close, Open, High, Low, HL2, HLC3, OHLC4};

    def src =
        if priceSource == priceSource.Close then c
        else if priceSource == priceSource.Open then o
        else if priceSource == priceSource.High then h
        else if priceSource == priceSource.Low then l
        else if priceSource == priceSource.HL2 then (h + l) / 2
        else if priceSource == priceSource.HLC3 then (h + l + c) / 3
        else (o + h + l + c) / 4;  # OHLC4

    plot out = src;
}

def src1 = SelectPrice(o1, h1, l1, c1, priceSource).out;
def src2 = SelectPrice(o2, h2, l2, c2, priceSource).out;
def src3 = SelectPrice(o3, h3, l3, c3, priceSource).out;
def src4 = SelectPrice(o4, h4, l4, c4, priceSource).out;
def src5 = SelectPrice(o5, h5, l5, c5, priceSource).out;
def src6 = SelectPrice(o6, h6, l6, c6, priceSource).out;
def src7 = SelectPrice(o7, h7, l7, c7, priceSource).out;
def src8 = SelectPrice(o8, h8, l8, c8, priceSource).out;

#---------------------------------------------------------------------------------------------
# PPO core – MAC line only
#---------------------------------------------------------------------------------------------
script StdMacOnly {
    input price   = close;
    input fastLen = 12;
    input slowLen = 26;
    input avgType = AverageType.EXPONENTIAL;
    def f = MovingAverage(avgType, price, fastLen);
    def s = MovingAverage(avgType, price, slowLen);
    plot macOut = if !IsNaN(s) and s != 0 then 100 * (f - s) / s else Double.NaN;
}

#---------------------------------------------------------------------------------------------
# Per-timeframe PPO values – MAC, Signal, Histogram, slopes, and 4-state strength flags
#---------------------------------------------------------------------------------------------
def mac1 = StdMacOnly(src1, fastLen, slowLen, ppoAvgType).macOut;
def sig1 = MovingAverage(ppoAvgType, mac1, signalLen);
def del1 = mac1 - sig1;
def ms1  = mac1 - mac1[1];
def ss1  = sig1 - sig1[1];
def d1s  = del1 - del1[1];
def st1  =
    if      del1 > 0 and d1s >= 0 then  2
    else if del1 > 0 and d1s <  0 then  1
    else if del1 < 0 and d1s <= 0 then -2
    else if del1 < 0 and d1s >  0 then -1
    else 0;

def mac2 = if tf2Enabled then StdMacOnly(src2, fastLen, slowLen, ppoAvgType).macOut else Double.NaN;
def sig2 = if tf2Enabled then MovingAverage(ppoAvgType, mac2, signalLen) else Double.NaN;
def del2 = mac2 - sig2;
def ms2  = mac2 - mac2[1];
def ss2  = sig2 - sig2[1];
def d2s  = del2 - del2[1];
def st2  =
    if      del2 > 0 and d2s >= 0 then  2
    else if del2 > 0 and d2s <  0 then  1
    else if del2 < 0 and d2s <= 0 then -2
    else if del2 < 0 and d2s >  0 then -1
    else 0;

def mac3 = if tf3Enabled then StdMacOnly(src3, fastLen, slowLen, ppoAvgType).macOut else Double.NaN;
def sig3 = if tf3Enabled then MovingAverage(ppoAvgType, mac3, signalLen) else Double.NaN;
def del3 = mac3 - sig3;
def ms3  = mac3 - mac3[1];
def ss3  = sig3 - sig3[1];
def d3s  = del3 - del3[1];
def st3  =
    if      del3 > 0 and d3s >= 0 then  2
    else if del3 > 0 and d3s <  0 then  1
    else if del3 < 0 and d3s <= 0 then -2
    else if del3 < 0 and d3s >  0 then -1
    else 0;

def mac4 = if tf4Enabled then StdMacOnly(src4, fastLen, slowLen, ppoAvgType).macOut else Double.NaN;
def sig4 = if tf4Enabled then MovingAverage(ppoAvgType, mac4, signalLen) else Double.NaN;
def del4 = mac4 - sig4;
def ms4  = mac4 - mac4[1];
def ss4  = sig4 - sig4[1];
def d4s  = del4 - del4[1];
def st4  =
    if      del4 > 0 and d4s >= 0 then  2
    else if del4 > 0 and d4s <  0 then  1
    else if del4 < 0 and d4s <= 0 then -2
    else if del4 < 0 and d4s >  0 then -1
    else 0;

def mac5 = if tf5Enabled then StdMacOnly(src5, fastLen, slowLen, ppoAvgType).macOut else Double.NaN;
def sig5 = if tf5Enabled then MovingAverage(ppoAvgType, mac5, signalLen) else Double.NaN;
def del5 = mac5 - sig5;
def ms5  = mac5 - mac5[1];
def ss5  = sig5 - sig5[1];
def d5s  = del5 - del5[1];
def st5  =
    if      del5 > 0 and d5s >= 0 then  2
    else if del5 > 0 and d5s <  0 then  1
    else if del5 < 0 and d5s <= 0 then -2
    else if del5 < 0 and d5s >  0 then -1
    else 0;

def mac6 = if tf6Enabled then StdMacOnly(src6, fastLen, slowLen, ppoAvgType).macOut else Double.NaN;
def sig6 = if tf6Enabled then MovingAverage(ppoAvgType, mac6, signalLen) else Double.NaN;
def del6 = mac6 - sig6;
def ms6  = mac6 - mac6[1];
def ss6  = sig6 - sig6[1];
def d6s  = del6 - del6[1];
def st6  =
    if      del6 > 0 and d6s >= 0 then  2
    else if del6 > 0 and d6s <  0 then  1
    else if del6 < 0 and d6s <= 0 then -2
    else if del6 < 0 and d6s >  0 then -1
    else 0;

def mac7 = if tf7Enabled then StdMacOnly(src7, fastLen, slowLen, ppoAvgType).macOut else Double.NaN;
def sig7 = if tf7Enabled then MovingAverage(ppoAvgType, mac7, signalLen) else Double.NaN;
def del7 = mac7 - sig7;
def ms7  = mac7 - mac7[1];
def ss7  = sig7 - sig7[1];
def d7s  = del7 - del7[1];
def st7  =
    if      del7 > 0 and d7s >= 0 then  2
    else if del7 > 0 and d7s <  0 then  1
    else if del7 < 0 and d7s <= 0 then -2
    else if del7 < 0 and d7s >  0 then -1
    else 0;

def mac8 = if tf8Enabled then StdMacOnly(src8, fastLen, slowLen, ppoAvgType).macOut else Double.NaN;
def sig8 = if tf8Enabled then MovingAverage(ppoAvgType, mac8, signalLen) else Double.NaN;
def del8 = mac8 - sig8;
def ms8  = mac8 - mac8[1];
def ss8  = sig8 - sig8[1];
def d8s  = del8 - del8[1];
def st8  =
    if      del8 > 0 and d8s >= 0 then  2
    else if del8 > 0 and d8s <  0 then  1
    else if del8 < 0 and d8s <= 0 then -2
    else if del8 < 0 and d8s >  0 then -1
    else 0;

#---------------------------------------------------------------------------------------------
# Projection engine – estimates bars until PPO histogram crosses 0 (decay-based)
#---------------------------------------------------------------------------------------------
script ProjToZero {
    input d        = 0.0;       # PPO histogram (MAC - SIG)
    input minSlope = 0.000001;  # minimum decay slope to accept
    def absD  = AbsValue(d);
    def curve = Power(absD, 0.9);
    def nl    = if d > 0 then curve else if d < 0 then -curve else 0;
    def slope = nl - nl[1];
    def flipped = (nl[1] > 0 and nl < 0) or (nl[1] < 0 and nl > 0);
    def suppress = flipped[1];
    def isDecaying = if suppress then no else ((nl > 0 and slope < 0) or (nl < 0 and slope > 0));
    def slopeDec   = (nl > 0 and slope <= 0) or (nl < 0 and slope >= 0);
    def projBars   = if isDecaying and slopeDec and AbsValue(slope) > minSlope then -nl / slope else Double.NaN;
    plot clean = if projBars >= 0.01 and projBars <= 30 then projBars else Double.NaN;
}

# Projections run only on last bar when labels are printing
def pz1 = if useProj then ProjToZero(del1).clean else Double.NaN;
def pz2 = if useProj and tf2Enabled then ProjToZero(del2).clean else Double.NaN;
def pz3 = if useProj and tf3Enabled then ProjToZero(del3).clean else Double.NaN;
def pz4 = if useProj and tf4Enabled then ProjToZero(del4).clean else Double.NaN;
def pz5 = if useProj and tf5Enabled then ProjToZero(del5).clean else Double.NaN;
def pz6 = if useProj and tf6Enabled then ProjToZero(del6).clean else Double.NaN;
def pz7 = if useProj and tf7Enabled then ProjToZero(del7).clean else Double.NaN;
def pz8 = if useProj and tf8Enabled then ProjToZero(del8).clean else Double.NaN;

#---------------------------------------------------------------------------------------------
# ZERO-CROSS: CONFIRMED MARKERS + FORECAST (single instance only)
#---------------------------------------------------------------------------------------------
# Zero-cross markers (PPO MAC crosses 0)
#   Blue   = cross ABOVE 0 (confirmed)
#   Yellow = cross BELOW 0 (confirmed)
# --------------------------------------

def zx1Up = showZeroCrossMarkers and inBar and Crosses(mac1, 0, CrossingDirection.ABOVE);
def zx1Dn = showZeroCrossMarkers and inBar and Crosses(mac1, 0, CrossingDirection.BELOW);
plot Zx1  = if (zx1Up or zx1Dn) then 8 + zxOff else Double.NaN;

def zx2Up = showZeroCrossMarkers and inBar and tf2Enabled and Crosses(mac2, 0, CrossingDirection.ABOVE);
def zx2Dn = showZeroCrossMarkers and inBar and tf2Enabled and Crosses(mac2, 0, CrossingDirection.BELOW);
plot Zx2  = if (zx2Up or zx2Dn) then 7 + zxOff else Double.NaN;

def zx3Up = showZeroCrossMarkers and inBar and tf3Enabled and Crosses(mac3, 0, CrossingDirection.ABOVE);
def zx3Dn = showZeroCrossMarkers and inBar and tf3Enabled and Crosses(mac3, 0, CrossingDirection.BELOW);
plot Zx3  = if (zx3Up or zx3Dn) then 6 + zxOff else Double.NaN;

def zx4Up = showZeroCrossMarkers and inBar and tf4Enabled and Crosses(mac4, 0, CrossingDirection.ABOVE);
def zx4Dn = showZeroCrossMarkers and inBar and tf4Enabled and Crosses(mac4, 0, CrossingDirection.BELOW);
plot Zx4  = if (zx4Up or zx4Dn) then 5 + zxOff else Double.NaN;

def zx5Up = showZeroCrossMarkers and inBar and tf5Enabled and Crosses(mac5, 0, CrossingDirection.ABOVE);
def zx5Dn = showZeroCrossMarkers and inBar and tf5Enabled and Crosses(mac5, 0, CrossingDirection.BELOW);
plot Zx5  = if (zx5Up or zx5Dn) then 4 + zxOff else Double.NaN;

def zx6Up = showZeroCrossMarkers and inBar and tf6Enabled and Crosses(mac6, 0, CrossingDirection.ABOVE);
def zx6Dn = showZeroCrossMarkers and inBar and tf6Enabled and Crosses(mac6, 0, CrossingDirection.BELOW);
plot Zx6  = if (zx6Up or zx6Dn) then 3 + zxOff else Double.NaN;

def zx7Up = showZeroCrossMarkers and inBar and tf7Enabled and Crosses(mac7, 0, CrossingDirection.ABOVE);
def zx7Dn = showZeroCrossMarkers and inBar and tf7Enabled and Crosses(mac7, 0, CrossingDirection.BELOW);
plot Zx7  = if (zx7Up or zx7Dn) then 2 + zxOff else Double.NaN;

def zx8Up = showZeroCrossMarkers and inBar and tf8Enabled and Crosses(mac8, 0, CrossingDirection.ABOVE);
def zx8Dn = showZeroCrossMarkers and inBar and tf8Enabled and Crosses(mac8, 0, CrossingDirection.BELOW);
plot Zx8  = if (zx8Up or zx8Dn) then 1 + zxOff else Double.NaN;

Zx1.SetPaintingStrategy(PaintingStrategy.TRIANGLES); Zx1.SetLineWeight(5);
Zx2.SetPaintingStrategy(PaintingStrategy.TRIANGLES); Zx2.SetLineWeight(5);
Zx3.SetPaintingStrategy(PaintingStrategy.TRIANGLES); Zx3.SetLineWeight(5);
Zx4.SetPaintingStrategy(PaintingStrategy.TRIANGLES); Zx4.SetLineWeight(5);
Zx5.SetPaintingStrategy(PaintingStrategy.TRIANGLES); Zx5.SetLineWeight(5);
Zx6.SetPaintingStrategy(PaintingStrategy.TRIANGLES); Zx6.SetLineWeight(5);
Zx7.SetPaintingStrategy(PaintingStrategy.TRIANGLES); Zx7.SetLineWeight(5);
Zx8.SetPaintingStrategy(PaintingStrategy.TRIANGLES); Zx8.SetLineWeight(5);

Zx1.AssignValueColor(if zx1Up then GlobalColor("ZxCrossUp") else GlobalColor("ZxCrossDown"));
Zx2.AssignValueColor(if zx2Up then GlobalColor("ZxCrossUp") else GlobalColor("ZxCrossDown"));
Zx3.AssignValueColor(if zx3Up then GlobalColor("ZxCrossUp") else GlobalColor("ZxCrossDown"));
Zx4.AssignValueColor(if zx4Up then GlobalColor("ZxCrossUp") else GlobalColor("ZxCrossDown"));
Zx5.AssignValueColor(if zx5Up then GlobalColor("ZxCrossUp") else GlobalColor("ZxCrossDown"));
Zx6.AssignValueColor(if zx6Up then GlobalColor("ZxCrossUp") else GlobalColor("ZxCrossDown"));
Zx7.AssignValueColor(if zx7Up then GlobalColor("ZxCrossUp") else GlobalColor("ZxCrossDown"));
Zx8.AssignValueColor(if zx8Up then GlobalColor("ZxCrossUp") else GlobalColor("ZxCrossDown"));

#---------------------------------------------------------------------------------------------
# Zero-cross FORECAST (PPO MAC projected to cross 0 soon)
#---------------------------------------------------------------------------------------------
#   Blue   = projected cross ABOVE (currently below 0, rising)
#   Yellow = projected cross BELOW (currently above 0, falling)
#   Prints EVERY bar while in-range (no pulse)
#-------------------------------------------------------------

def zWin =
    if forecastBars == forecastBars."1 Bar" then 1
    else if forecastBars == forecastBars."2 Bars" then 2
    else if forecastBars == forecastBars."3 Bars" then 3
    else if forecastBars == forecastBars."4 Bars" then 4
    else if forecastBars == forecastBars."5 Bars" then 5
    else if forecastBars == forecastBars."6 Bars" then 6
    else 7;

# NOTE: Avoid scripts-with-recursion. Do held-slope per TF (stable in TOS).
def bOK = BarNumber() > 1;

# TF1
def m1_has2   = bOK and !IsNaN(mac1) and !IsNaN(mac1[1]);
def m1_rawSl  = if m1_has2 and mac1 != mac1[1] then mac1 - mac1[1] else Double.NaN;
def m1_slHold = CompoundValue(1, if !IsNaN(m1_rawSl) then m1_rawSl else m1_slHold[1], 0.0);
def pb1       = if showZeroCrossForecast and inBar and m1_has2 and AbsValue(m1_slHold) > minProjSlope then -mac1 / m1_slHold else Double.NaN;
def f1Up      = showZeroCrossForecast and inBar and !IsNaN(pb1) and pb1 > 0 and pb1 <= zWin and mac1 < 0 and m1_slHold > 0;
def f1Dn      = showZeroCrossForecast and inBar and !IsNaN(pb1) and pb1 > 0 and pb1 <= zWin and mac1 > 0 and m1_slHold < 0;
plot ZxF1     = if (f1Up or f1Dn) then 8 + zxFoff else Double.NaN;

# TF2
def m2_has2   = bOK and tf2Enabled and !IsNaN(mac2) and !IsNaN(mac2[1]);
def m2_rawSl  = if m2_has2 and mac2 != mac2[1] then mac2 - mac2[1] else Double.NaN;
def m2_slHold = CompoundValue(1, if !IsNaN(m2_rawSl) then m2_rawSl else m2_slHold[1], 0.0);
def pb2       = if showZeroCrossForecast and inBar and tf2Enabled and m2_has2 and AbsValue(m2_slHold) > minProjSlope then -mac2 / m2_slHold else Double.NaN;
def f2Up      = showZeroCrossForecast and inBar and tf2Enabled and !IsNaN(pb2) and pb2 > 0 and pb2 <= zWin and mac2 < 0 and m2_slHold > 0;
def f2Dn      = showZeroCrossForecast and inBar and tf2Enabled and !IsNaN(pb2) and pb2 > 0 and pb2 <= zWin and mac2 > 0 and m2_slHold < 0;
plot ZxF2     = if (f2Up or f2Dn) then 7 + zxFoff else Double.NaN;

# TF3
def m3_has2   = bOK and tf3Enabled and !IsNaN(mac3) and !IsNaN(mac3[1]);
def m3_rawSl  = if m3_has2 and mac3 != mac3[1] then mac3 - mac3[1] else Double.NaN;
def m3_slHold = CompoundValue(1, if !IsNaN(m3_rawSl) then m3_rawSl else m3_slHold[1], 0.0);
def pb3       = if showZeroCrossForecast and inBar and tf3Enabled and m3_has2 and AbsValue(m3_slHold) > minProjSlope then -mac3 / m3_slHold else Double.NaN;
def f3Up      = showZeroCrossForecast and inBar and tf3Enabled and !IsNaN(pb3) and pb3 > 0 and pb3 <= zWin and mac3 < 0 and m3_slHold > 0;
def f3Dn      = showZeroCrossForecast and inBar and tf3Enabled and !IsNaN(pb3) and pb3 > 0 and pb3 <= zWin and mac3 > 0 and m3_slHold < 0;
plot ZxF3     = if (f3Up or f3Dn) then 6 + zxFoff else Double.NaN;

# TF4
def m4_has2   = bOK and tf4Enabled and !IsNaN(mac4) and !IsNaN(mac4[1]);
def m4_rawSl  = if m4_has2 and mac4 != mac4[1] then mac4 - mac4[1] else Double.NaN;
def m4_slHold = CompoundValue(1, if !IsNaN(m4_rawSl) then m4_rawSl else m4_slHold[1], 0.0);
def pb4       = if showZeroCrossForecast and inBar and tf4Enabled and m4_has2 and AbsValue(m4_slHold) > minProjSlope then -mac4 / m4_slHold else Double.NaN;
def f4Up      = showZeroCrossForecast and inBar and tf4Enabled and !IsNaN(pb4) and pb4 > 0 and pb4 <= zWin and mac4 < 0 and m4_slHold > 0;
def f4Dn      = showZeroCrossForecast and inBar and tf4Enabled and !IsNaN(pb4) and pb4 > 0 and pb4 <= zWin and mac4 > 0 and m4_slHold < 0;
plot ZxF4     = if (f4Up or f4Dn) then 5 + zxFoff else Double.NaN;

# TF5
def m5_has2   = bOK and tf5Enabled and !IsNaN(mac5) and !IsNaN(mac5[1]);
def m5_rawSl  = if m5_has2 and mac5 != mac5[1] then mac5 - mac5[1] else Double.NaN;
def m5_slHold = CompoundValue(1, if !IsNaN(m5_rawSl) then m5_rawSl else m5_slHold[1], 0.0);
def pb5       = if showZeroCrossForecast and inBar and tf5Enabled and m5_has2 and AbsValue(m5_slHold) > minProjSlope then -mac5 / m5_slHold else Double.NaN;
def f5Up      = showZeroCrossForecast and inBar and tf5Enabled and !IsNaN(pb5) and pb5 > 0 and pb5 <= zWin and mac5 < 0 and m5_slHold > 0;
def f5Dn      = showZeroCrossForecast and inBar and tf5Enabled and !IsNaN(pb5) and pb5 > 0 and pb5 <= zWin and mac5 > 0 and m5_slHold < 0;
plot ZxF5     = if (f5Up or f5Dn) then 4 + zxFoff else Double.NaN;

# TF6
def m6_has2   = bOK and tf6Enabled and !IsNaN(mac6) and !IsNaN(mac6[1]);
def m6_rawSl  = if m6_has2 and mac6 != mac6[1] then mac6 - mac6[1] else Double.NaN;
def m6_slHold = CompoundValue(1, if !IsNaN(m6_rawSl) then m6_rawSl else m6_slHold[1], 0.0);
def pb6       = if showZeroCrossForecast and inBar and tf6Enabled and m6_has2 and AbsValue(m6_slHold) > minProjSlope then -mac6 / m6_slHold else Double.NaN;
def f6Up      = showZeroCrossForecast and inBar and tf6Enabled and !IsNaN(pb6) and pb6 > 0 and pb6 <= zWin and mac6 < 0 and m6_slHold > 0;
def f6Dn      = showZeroCrossForecast and inBar and tf6Enabled and !IsNaN(pb6) and pb6 > 0 and pb6 <= zWin and mac6 > 0 and m6_slHold < 0;
plot ZxF6     = if (f6Up or f6Dn) then 3 + zxFoff else Double.NaN;

# TF7
def m7_has2   = bOK and tf7Enabled and !IsNaN(mac7) and !IsNaN(mac7[1]);
def m7_rawSl  = if m7_has2 and mac7 != mac7[1] then mac7 - mac7[1] else Double.NaN;
def m7_slHold = CompoundValue(1, if !IsNaN(m7_rawSl) then m7_rawSl else m7_slHold[1], 0.0);
def pb7       = if showZeroCrossForecast and inBar and tf7Enabled and m7_has2 and AbsValue(m7_slHold) > minProjSlope then -mac7 / m7_slHold else Double.NaN;
def f7Up      = showZeroCrossForecast and inBar and tf7Enabled and !IsNaN(pb7) and pb7 > 0 and pb7 <= zWin and mac7 < 0 and m7_slHold > 0;
def f7Dn      = showZeroCrossForecast and inBar and tf7Enabled and !IsNaN(pb7) and pb7 > 0 and pb7 <= zWin and mac7 > 0 and m7_slHold < 0;
plot ZxF7     = if (f7Up or f7Dn) then 2 + zxFoff else Double.NaN;

# TF8
def m8_has2   = bOK and tf8Enabled and !IsNaN(mac8) and !IsNaN(mac8[1]);
def m8_rawSl  = if m8_has2 and mac8 != mac8[1] then mac8 - mac8[1] else Double.NaN;
def m8_slHold = CompoundValue(1, if !IsNaN(m8_rawSl) then m8_rawSl else m8_slHold[1], 0.0);
def pb8       = if showZeroCrossForecast and inBar and tf8Enabled and m8_has2 and AbsValue(m8_slHold) > minProjSlope then -mac8 / m8_slHold else Double.NaN;
def f8Up      = showZeroCrossForecast and inBar and tf8Enabled and !IsNaN(pb8) and pb8 > 0 and pb8 <= zWin and mac8 < 0 and m8_slHold > 0;
def f8Dn      = showZeroCrossForecast and inBar and tf8Enabled and !IsNaN(pb8) and pb8 > 0 and pb8 <= zWin and mac8 > 0 and m8_slHold < 0;
plot ZxF8     = if (f8Up or f8Dn) then 1 + zxFoff else Double.NaN;

ZxF1.SetPaintingStrategy(PaintingStrategy.TRIANGLES); ZxF1.SetLineWeight(1);
ZxF2.SetPaintingStrategy(PaintingStrategy.TRIANGLES); ZxF2.SetLineWeight(1);
ZxF3.SetPaintingStrategy(PaintingStrategy.TRIANGLES); ZxF3.SetLineWeight(1);
ZxF4.SetPaintingStrategy(PaintingStrategy.TRIANGLES); ZxF4.SetLineWeight(1);
ZxF5.SetPaintingStrategy(PaintingStrategy.TRIANGLES); ZxF5.SetLineWeight(1);
ZxF6.SetPaintingStrategy(PaintingStrategy.TRIANGLES); ZxF6.SetLineWeight(1);
ZxF7.SetPaintingStrategy(PaintingStrategy.TRIANGLES); ZxF7.SetLineWeight(1);
ZxF8.SetPaintingStrategy(PaintingStrategy.TRIANGLES); ZxF8.SetLineWeight(1);

ZxF1.AssignValueColor(if f1Up then GlobalColor("ZxForecastUp") else GlobalColor("ZxForecastDown"));
ZxF2.AssignValueColor(if f2Up then GlobalColor("ZxForecastUp") else GlobalColor("ZxForecastDown"));
ZxF3.AssignValueColor(if f3Up then GlobalColor("ZxForecastUp") else GlobalColor("ZxForecastDown"));
ZxF4.AssignValueColor(if f4Up then GlobalColor("ZxForecastUp") else GlobalColor("ZxForecastDown"));
ZxF5.AssignValueColor(if f5Up then GlobalColor("ZxForecastUp") else GlobalColor("ZxForecastDown"));
ZxF6.AssignValueColor(if f6Up then GlobalColor("ZxForecastUp") else GlobalColor("ZxForecastDown"));
ZxF7.AssignValueColor(if f7Up then GlobalColor("ZxForecastUp") else GlobalColor("ZxForecastDown"));
ZxF8.AssignValueColor(if f8Up then GlobalColor("ZxForecastUp") else GlobalColor("ZxForecastDown"));

#---------------------------------------------------------------------------------------------
# Heatmap rows
#---------------------------------------------------------------------------------------------
plot HeatTF1 = if !inBar then Double.NaN else 8;
plot HeatTF2 = if !inBar or !tf2Enabled then Double.NaN else 7;
plot HeatTF3 = if !inBar or !tf3Enabled then Double.NaN else 6;
plot HeatTF4 = if !inBar or !tf4Enabled then Double.NaN else 5;
plot HeatTF5 = if !inBar or !tf5Enabled then Double.NaN else 4;
plot HeatTF6 = if !inBar or !tf6Enabled then Double.NaN else 3;
plot HeatTF7 = if !inBar or !tf7Enabled then Double.NaN else 2;
plot HeatTF8 = if !inBar or !tf8Enabled then Double.NaN else 1;

HeatTF1.SetPaintingStrategy(heatPS); HeatTF1.SetLineWeight(5);
HeatTF2.SetPaintingStrategy(heatPS); HeatTF2.SetLineWeight(5);
HeatTF3.SetPaintingStrategy(heatPS); HeatTF3.SetLineWeight(5);
HeatTF4.SetPaintingStrategy(heatPS); HeatTF4.SetLineWeight(5);
HeatTF5.SetPaintingStrategy(heatPS); HeatTF5.SetLineWeight(5);
HeatTF6.SetPaintingStrategy(heatPS); HeatTF6.SetLineWeight(5);
HeatTF7.SetPaintingStrategy(heatPS); HeatTF7.SetLineWeight(5);
HeatTF8.SetPaintingStrategy(heatPS); HeatTF8.SetLineWeight(5);

#---------------------------------------------------------------------------------------------
# Heatmap colors (driven by the SAME 4-state st1–st8):
#---------------------------------------------------------------------------------------------
# - 4-Color: BG/DG/DR/BR (direction + momentum shading)
# - 2-Color: momentum-only (Histogram rising = Green, falling = Red)
#     Green when st == 2 (bull strengthening) OR st == -1 (bear weakening)
#     Red   when st == 1 (bull weakening)     OR st == -2 (bear strengthening)
#------------------------------------------------------------------------------
HeatTF1.AssignValueColor(
    if !use2Color then
        (if st1 == 2 then GlobalColor("BrightGreen")
         else if st1 == 1 then GlobalColor("DarkGreen")
         else if st1 == -1 then GlobalColor("DarkRed")
         else if st1 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
    else
        (if st1 == 2 or st1 == -1 then GlobalColor("BrightGreen")
         else if st1 == 1 or st1 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
);

HeatTF2.AssignValueColor(
    if !use2Color then
        (if st2 == 2 then GlobalColor("BrightGreen")
         else if st2 == 1 then GlobalColor("DarkGreen")
         else if st2 == -1 then GlobalColor("DarkRed")
         else if st2 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
    else
        (if st2 == 2 or st2 == -1 then GlobalColor("BrightGreen")
         else if st2 == 1 or st2 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
);

HeatTF3.AssignValueColor(
    if !use2Color then
        (if st3 == 2 then GlobalColor("BrightGreen")
         else if st3 == 1 then GlobalColor("DarkGreen")
         else if st3 == -1 then GlobalColor("DarkRed")
         else if st3 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
    else
        (if st3 == 2 or st3 == -1 then GlobalColor("BrightGreen")
         else if st3 == 1 or st3 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
);

HeatTF4.AssignValueColor(
    if !use2Color then
        (if st4 == 2 then GlobalColor("BrightGreen")
         else if st4 == 1 then GlobalColor("DarkGreen")
         else if st4 == -1 then GlobalColor("DarkRed")
         else if st4 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
    else
        (if st4 == 2 or st4 == -1 then GlobalColor("BrightGreen")
         else if st4 == 1 or st4 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
);

HeatTF5.AssignValueColor(
    if !use2Color then
        (if st5 == 2 then GlobalColor("BrightGreen")
         else if st5 == 1 then GlobalColor("DarkGreen")
         else if st5 == -1 then GlobalColor("DarkRed")
         else if st5 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
    else
        (if st5 == 2 or st5 == -1 then GlobalColor("BrightGreen")
         else if st5 == 1 or st5 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
);

HeatTF6.AssignValueColor(
    if !use2Color then
        (if st6 == 2 then GlobalColor("BrightGreen")
         else if st6 == 1 then GlobalColor("DarkGreen")
         else if st6 == -1 then GlobalColor("DarkRed")
         else if st6 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
    else
        (if st6 == 2 or st6 == -1 then GlobalColor("BrightGreen")
         else if st6 == 1 or st6 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
);

HeatTF7.AssignValueColor(
    if !use2Color then
        (if st7 == 2 then GlobalColor("BrightGreen")
         else if st7 == 1 then GlobalColor("DarkGreen")
         else if st7 == -1 then GlobalColor("DarkRed")
         else if st7 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
    else
        (if st7 == 2 or st7 == -1 then GlobalColor("BrightGreen")
         else if st7 == 1 or st7 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
);

HeatTF8.AssignValueColor(
    if !use2Color then
        (if st8 == 2 then GlobalColor("BrightGreen")
         else if st8 == 1 then GlobalColor("DarkGreen")
         else if st8 == -1 then GlobalColor("DarkRed")
         else if st8 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
    else
        (if st8 == 2 or st8 == -1 then GlobalColor("BrightGreen")
         else if st8 == 1 or st8 == -2 then GlobalColor("BrightRed")
         else GlobalColor("Neutral"))
);

#---------------------------------------------------------------------------------------------
# Header spec tokens
#---------------------------------------------------------------------------------------------
def ppoAvgToken =
    if      ppoAvgType == AverageType.SIMPLE      then 0
    else if ppoAvgType == AverageType.EXPONENTIAL then 1
    else if ppoAvgType == AverageType.WILDERS     then 2
    else if ppoAvgType == AverageType.HULL        then 3
    else if ppoAvgType == AverageType.WEIGHTED    then 4
    else 5;

def priceToken =
    if      priceSource == priceSource.Close then 0
    else if priceSource == priceSource.Open  then 1
    else if priceSource == priceSource.High  then 2
    else if priceSource == priceSource.Low   then 3
    else if priceSource == priceSource.HL2   then 4
    else if priceSource == priceSource.HLC3  then 5
    else 6;

#---------------------------------------------------------------------------------------------
# Labels (LAST-BAR GATED)
#---------------------------------------------------------------------------------------------
def tfOnly = !showSignalNumbers and !showSignalDirections;

def tfLblLoc =
    if TfLabelLocation == TfLabelLocation."Top-Left" then Location.TOP_LEFT
    else if TfLabelLocation == TfLabelLocation."Top-Right" then Location.TOP_RIGHT
    else if TfLabelLocation == TfLabelLocation."Bottom-Left" then Location.BOTTOM_LEFT
    else Location.BOTTOM_RIGHT;

AddLabel(showHdr, "   PPO HeatMap 1   ", CreateColor(60, 208, 112));

AddLabel(
    showHdr,
      "  " + Round(fastLen, 0)
    + "  |  " + Round(slowLen, 0)
    + "  |  " + Round(signalLen, 0)
    + "   |   "
    + (if priceToken == 0 then "Close"
       else if priceToken == 1 then "Open"
       else if priceToken == 2 then "High"
       else if priceToken == 3 then "Low"
       else if priceToken == 4 then "HL2"
       else if priceToken == 5 then "HLC3"
       else "OHLC4")
    + "   |   "
    + (if ppoAvgToken == 0 then "SMA"
       else if ppoAvgToken == 1 then "EMA"
       else if ppoAvgToken == 2 then "Wilder"
       else if ppoAvgToken == 3 then "HMA"
       else if ppoAvgToken == 4 then "WMA"
       else "AVG")
    + "   ",
    Color.LIGHT_GRAY
);

AddLabel(showHdr and showTF, "  ", CreateColor(36, 36, 36));

#------------------------------------
# TF1 label (NaN-safe arrows: ms/ss -> "?")
#------------------------------------
AddLabel(showTF,
(if P1 == AggregationPeriod.MIN then "  1m   "
 else if P1 == AggregationPeriod.TWO_MIN then "  2m   "
 else if P1 == AggregationPeriod.THREE_MIN then "  3m   "
 else if P1 == AggregationPeriod.FOUR_MIN then "  4m   "
 else if P1 == AggregationPeriod.FIVE_MIN then "  5m   "
 else if P1 == AggregationPeriod.TEN_MIN then "  10m   "
 else if P1 == AggregationPeriod.FIFTEEN_MIN then "  15m   "
 else if P1 == AggregationPeriod.TWENTY_MIN then "  20m   "
 else if P1 == AggregationPeriod.THIRTY_MIN then "  30m   "
 else if P1 == AggregationPeriod.HOUR then "  1h   "
 else if P1 == AggregationPeriod.TWO_HOURS then "  2h   "
 else if P1 == AggregationPeriod.FOUR_HOURS then "  4h   "
 else if P1 == AggregationPeriod.DAY then "  1d   "
 else if P1 == AggregationPeriod.TWO_DAYS then "  2d   "
 else if P1 == AggregationPeriod.THREE_DAYS then "  3d   "
 else if P1 == AggregationPeriod.FOUR_DAYS then "  4d   "
 else if P1 == AggregationPeriod.WEEK then "  1w   "
 else if P1 == AggregationPeriod.MONTH then "  1mo   "
 else if P1 == AggregationPeriod.QUARTER then "  3mo   " else "UNK ")
    + (if tfOnly then (if IsNaN(mac1) then "?  " else if mac1 >= 0 then "+  " else "-  ") else "")
    + (if tfOnly and !IsNaN(pz1) then " > " + Round(pz1, 0) + "c  " else "")
    +
    (if showSignalNumbers then
        (if IsNaN(ms1) then "? " else if ms1 > 0 then "/ " else "\ ")
      + AsText(Round(mac1, 2)) + "   "
      + (if IsNaN(ss1) then "? " else if ss1 > 0 then "/ " else "\ ")
      + AsText(Round(sig1, 2))
      + "  |  " + AsText(Round(del1, 2)) + "  "
      + (if !IsNaN(pz1) then " > " + Round(pz1, 0) + "c " else "")
     else if showSignalDirections then
        (if IsNaN(ms1) then "? " else if ms1 > 0 then "/ " else "\ ")
      + " "
      + (if IsNaN(mac1) then "?  " else if mac1 >= 0 then "+  " else "-  ")
      + (if IsNaN(ss1) then "? " else if ss1 > 0 then "/ " else "\ ")
      + (if !IsNaN(pz1) then "    > " + Round(pz1, 0) + "c    " else "   ")
     else "")
    ,
    if st1 == 2 then GlobalColor("BrightGreen") else if st1 == 1 then GlobalColor("DarkGreen")
    else if st1 == -1 then GlobalColor("DarkRed") else if st1 == -2 then GlobalColor("BrightRed")
    else GlobalColor("Neutral"), location = tfLblLoc
);

#------------------------------------
# TF2 label
#------------------------------------
AddLabel(showTF and tf2Enabled,
(if P2 == AggregationPeriod.FIVE_MIN then "  5m   "
 else if P2 == AggregationPeriod.TEN_MIN then "  10m   "
 else if P2 == AggregationPeriod.FIFTEEN_MIN then "  15m   "
 else if P2 == AggregationPeriod.TWENTY_MIN then "  20m   "
 else if P2 == AggregationPeriod.THIRTY_MIN then "  30m   "
 else if P2 == AggregationPeriod.HOUR then "  1h   "
 else if P2 == AggregationPeriod.TWO_HOURS then "  2h   "
 else if P2 == AggregationPeriod.FOUR_HOURS then "  4h   "
 else if P2 == AggregationPeriod.DAY then "  1d   "
 else if P2 == AggregationPeriod.TWO_DAYS then "  2d   "
 else if P2 == AggregationPeriod.THREE_DAYS then "  3d   "
 else if P2 == AggregationPeriod.FOUR_DAYS then "  4d   "
 else if P2 == AggregationPeriod.WEEK then "  1w   "
 else if P2 == AggregationPeriod.MONTH then "  1mo   "
 else if P2 == AggregationPeriod.QUARTER then "  3mo   "
 else if P2 == AggregationPeriod.YEAR then "  1yr   " else "UNK ")
    + (if tfOnly then (if IsNaN(mac2) then "?  " else if mac2 >= 0 then "+  " else "-  ") else "")
    + (if tfOnly and !IsNaN(pz2) then " > " + Round(pz2, 0) + "c  " else "")
    +
    (if showSignalNumbers then
        (if IsNaN(ms2) then "? " else if ms2 > 0 then "/ " else "\ ")
      + AsText(Round(mac2, 2)) + "   "
      + (if IsNaN(ss2) then "? " else if ss2 > 0 then "/ " else "\ ")
      + AsText(Round(sig2, 2))
      + "  |  " + AsText(Round(del2, 2)) + "  "
      + (if !IsNaN(pz2) then " > " + Round(pz2, 0) + "c " else "")
     else if showSignalDirections then
        (if IsNaN(ms2) then "? " else if ms2 > 0 then "/ " else "\ ")
      + " "
      + (if IsNaN(mac2) then "?  " else if mac2 >= 0 then "+  " else "-  ")
      + (if IsNaN(ss2) then "? " else if ss2 > 0 then "/ " else "\ ")
      + (if !IsNaN(pz2) then "    > " + Round(pz2, 0) + "c    " else "   ")
     else "")
    ,
    if st2 == 2 then GlobalColor("BrightGreen") else if st2 == 1 then GlobalColor("DarkGreen")
    else if st2 == -1 then GlobalColor("DarkRed") else if st2 == -2 then GlobalColor("BrightRed")
    else GlobalColor("Neutral"), location = tfLblLoc
);

#------------------------------------
# TF3 label
#------------------------------------
AddLabel(showTF and tf3Enabled,
(if P3 == AggregationPeriod.TEN_MIN then "  10m   "
 else if P3 == AggregationPeriod.FIFTEEN_MIN then "  15m   "
 else if P3 == AggregationPeriod.TWENTY_MIN then "  20m   "
 else if P3 == AggregationPeriod.THIRTY_MIN then "  30m   "
 else if P3 == AggregationPeriod.HOUR then "  1h   "
 else if P3 == AggregationPeriod.TWO_HOURS then "  2h   "
 else if P3 == AggregationPeriod.FOUR_HOURS then "  4h   "
 else if P3 == AggregationPeriod.DAY then "  1d   "
 else if P3 == AggregationPeriod.TWO_DAYS then "  2d   "
 else if P3 == AggregationPeriod.THREE_DAYS then "  3d   "
 else if P3 == AggregationPeriod.FOUR_DAYS then "  4d   "
 else if P3 == AggregationPeriod.WEEK then "  1w   "
 else if P3 == AggregationPeriod.MONTH then "  1mo   "
 else if P3 == AggregationPeriod.QUARTER then "  3mo   "
 else if P3 == AggregationPeriod.YEAR then "  1yr   " else "UNK ")
    + (if tfOnly then (if IsNaN(mac3) then "?  " else if mac3 >= 0 then "+  " else "-  ") else "")
    + (if tfOnly and !IsNaN(pz3) then " > " + Round(pz3, 0) + "c  " else "")
    +
    (if showSignalNumbers then
        (if IsNaN(ms3) then "? " else if ms3 > 0 then "/ " else "\ ")
      + AsText(Round(mac3, 2)) + "   "
      + (if IsNaN(ss3) then "? " else if ss3 > 0 then "/ " else "\ ")
      + AsText(Round(sig3, 2))
      + "  |  " + AsText(Round(del3, 2)) + "  "
      + (if !IsNaN(pz3) then " > " + Round(pz3, 0) + "c " else "")
     else if showSignalDirections then
        (if IsNaN(ms3) then "? " else if ms3 > 0 then "/ " else "\ ")
      + " "
      + (if IsNaN(mac3) then "?  " else if mac3 >= 0 then "+  " else "-  ")
      + (if IsNaN(ss3) then "? " else if ss3 > 0 then "/ " else "\ ")
      + (if !IsNaN(pz3) then "    > " + Round(pz3, 0) + "c    " else "   ")
     else "")
    ,
    if st3 == 2 then GlobalColor("BrightGreen") else if st3 == 1 then GlobalColor("DarkGreen")
    else if st3 == -1 then GlobalColor("DarkRed") else if st3 == -2 then GlobalColor("BrightRed")
    else GlobalColor("Neutral"), location = tfLblLoc
);

#------------------------------------
# TF4 label
#------------------------------------
AddLabel(showTF and tf4Enabled,
(if P4 == AggregationPeriod.FIFTEEN_MIN then "  15m   "
 else if P4 == AggregationPeriod.TWENTY_MIN then "  20m   "
 else if P4 == AggregationPeriod.THIRTY_MIN then "  30m   "
 else if P4 == AggregationPeriod.HOUR then "  1h   "
 else if P4 == AggregationPeriod.TWO_HOURS then "  2h   "
 else if P4 == AggregationPeriod.FOUR_HOURS then "  4h   "
 else if P4 == AggregationPeriod.DAY then "  1d   "
 else if P4 == AggregationPeriod.TWO_DAYS then "  2d   "
 else if P4 == AggregationPeriod.THREE_DAYS then "  3d   "
 else if P4 == AggregationPeriod.FOUR_DAYS then "  4d   "
 else if P4 == AggregationPeriod.WEEK then "  1w   "
 else if P4 == AggregationPeriod.MONTH then "  1mo   "
 else if P4 == AggregationPeriod.QUARTER then "  3mo   "
 else if P4 == AggregationPeriod.YEAR then "  1yr   " else "UNK ")
    + (if tfOnly then (if IsNaN(mac4) then "?  " else if mac4 >= 0 then "+  " else "-  ") else "")
    + (if tfOnly and !IsNaN(pz4) then " > " + Round(pz4, 0) + "c  " else "")
    +
    (if showSignalNumbers then
        (if IsNaN(ms4) then "? " else if ms4 > 0 then "/ " else "\ ")
      + AsText(Round(mac4, 2)) + "   "
      + (if IsNaN(ss4) then "? " else if ss4 > 0 then "/ " else "\ ")
      + AsText(Round(sig4, 2))
      + "  |  " + AsText(Round(del4, 2)) + "  "
      + (if !IsNaN(pz4) then " > " + Round(pz4, 0) + "c " else "")
     else if showSignalDirections then
        (if IsNaN(ms4) then "? " else if ms4 > 0 then "/ " else "\ ")
      + " "
      + (if IsNaN(mac4) then "?  " else if mac4 >= 0 then "+  " else "-  ")
      + (if IsNaN(ss4) then "? " else if ss4 > 0 then "/ " else "\ ")
      + (if !IsNaN(pz4) then "    > " + Round(pz4, 0) + "c    " else "   ")
     else "")
    ,
    if st4 == 2 then GlobalColor("BrightGreen") else if st4 == 1 then GlobalColor("DarkGreen")
    else if st4 == -1 then GlobalColor("DarkRed") else if st4 == -2 then GlobalColor("BrightRed")
    else GlobalColor("Neutral"), location = tfLblLoc
);

#------------------------------------
# TF5 label
#------------------------------------
AddLabel(showTF and tf5Enabled,
(if P5 == AggregationPeriod.TWENTY_MIN then "  20m   "
 else if P5 == AggregationPeriod.THIRTY_MIN then "  30m   "
 else if P5 == AggregationPeriod.HOUR then "  1h   "
 else if P5 == AggregationPeriod.TWO_HOURS then "  2h   "
 else if P5 == AggregationPeriod.FOUR_HOURS then "  4h   "
 else if P5 == AggregationPeriod.DAY then "  1d   "
 else if P5 == AggregationPeriod.TWO_DAYS then "  2d   "
 else if P5 == AggregationPeriod.THREE_DAYS then "  3d   "
 else if P5 == AggregationPeriod.FOUR_DAYS then "  4d   "
 else if P5 == AggregationPeriod.WEEK then "  1w   "
 else if P5 == AggregationPeriod.MONTH then "  1mo   "
 else if P5 == AggregationPeriod.QUARTER then "  3mo   "
 else if P5 == AggregationPeriod.YEAR then "  1yr   " else "UNK ")
    + (if tfOnly then (if IsNaN(mac5) then "?  " else if mac5 >= 0 then "+  " else "-  ") else "")
    + (if tfOnly and !IsNaN(pz5) then " > " + Round(pz5, 0) + "c  " else "")
    +
    (if showSignalNumbers then
        (if IsNaN(ms5) then "? " else if ms5 > 0 then "/ " else "\ ")
      + AsText(Round(mac5, 2)) + "   "
      + (if IsNaN(ss5) then "? " else if ss5 > 0 then "/ " else "\ ")
      + AsText(Round(sig5, 2))
      + "  |  " + AsText(Round(del5, 2)) + "  "
      + (if !IsNaN(pz5) then " > " + Round(pz5, 0) + "c " else "")
     else if showSignalDirections then
        (if IsNaN(ms5) then "? " else if ms5 > 0 then "/ " else "\ ")
      + " "
      + (if IsNaN(mac5) then "?  " else if mac5 >= 0 then "+  " else "-  ")
      + (if IsNaN(ss5) then "? " else if ss5 > 0 then "/ " else "\ ")
      + (if !IsNaN(pz5) then "    > " + Round(pz5, 0) + "c    " else "   ")
     else "")
    ,
    if st5 == 2 then GlobalColor("BrightGreen") else if st5 == 1 then GlobalColor("DarkGreen")
    else if st5 == -1 then GlobalColor("DarkRed") else if st5 == -2 then GlobalColor("BrightRed")
    else GlobalColor("Neutral"), location = tfLblLoc
);

#------------------------------------
# TF6 label
#------------------------------------
AddLabel(showTF and tf6Enabled,
(if P6 == AggregationPeriod.THIRTY_MIN then "  30m   "
 else if P6 == AggregationPeriod.HOUR then "  1h   "
 else if P6 == AggregationPeriod.TWO_HOURS then "  2h   "
 else if P6 == AggregationPeriod.FOUR_HOURS then "  4h   "
 else if P6 == AggregationPeriod.DAY then "  1d   "
 else if P6 == AggregationPeriod.TWO_DAYS then "  2d   "
 else if P6 == AggregationPeriod.THREE_DAYS then "  3d   "
 else if P6 == AggregationPeriod.FOUR_DAYS then "  4d   "
 else if P6 == AggregationPeriod.WEEK then "  1w   "
 else if P6 == AggregationPeriod.MONTH then "  1mo   "
 else if P6 == AggregationPeriod.QUARTER then "  3mo   "
 else if P6 == AggregationPeriod.YEAR then "  1yr   " else "UNK ")
    + (if tfOnly then (if IsNaN(mac6) then "?  " else if mac6 >= 0 then "+  " else "-  ") else "")
    + (if tfOnly and !IsNaN(pz6) then " > " + Round(pz6, 0) + "c  " else "")
    +
    (if showSignalNumbers then
        (if IsNaN(ms6) then "? " else if ms6 > 0 then "/ " else "\ ")
      + AsText(Round(mac6, 2)) + "   "
      + (if IsNaN(ss6) then "? " else if ss6 > 0 then "/ " else "\ ")
      + AsText(Round(sig6, 2))
      + "  |  " + AsText(Round(del6, 2)) + "  "
      + (if !IsNaN(pz6) then " > " + Round(pz6, 0) + "c " else "")
     else if showSignalDirections then
        (if IsNaN(ms6) then "? " else if ms6 > 0 then "/ " else "\ ")
      + " "
      + (if IsNaN(mac6) then "?  " else if mac6 >= 0 then "+  " else "-  ")
      + (if IsNaN(ss6) then "? " else if ss6 > 0 then "/ " else "\ ")
      + (if !IsNaN(pz6) then "    > " + Round(pz6, 0) + "c    " else "   ")
     else "")
    ,
    if st6 == 2 then GlobalColor("BrightGreen") else if st6 == 1 then GlobalColor("DarkGreen")
    else if st6 == -1 then GlobalColor("DarkRed") else if st6 == -2 then GlobalColor("BrightRed")
    else GlobalColor("Neutral"), location = tfLblLoc
);

#------------------------------------
# TF7 label
#------------------------------------
AddLabel(showTF and tf7Enabled,
(if P7 == AggregationPeriod.HOUR then "  1h   "
 else if P7 == AggregationPeriod.TWO_HOURS then "  2h   "
 else if P7 == AggregationPeriod.FOUR_HOURS then "  4h   "
 else if P7 == AggregationPeriod.DAY then "  1d   "
 else if P7 == AggregationPeriod.TWO_DAYS then "  2d   "
 else if P7 == AggregationPeriod.THREE_DAYS then "  3d   "
 else if P7 == AggregationPeriod.FOUR_DAYS then "  4d   "
 else if P7 == AggregationPeriod.WEEK then "  1w   "
 else if P7 == AggregationPeriod.MONTH then "  1mo   "
 else if P7 == AggregationPeriod.QUARTER then "  3mo   "
 else if P7 == AggregationPeriod.YEAR then "  1yr   " else "UNK ")
    + (if tfOnly then (if IsNaN(mac7) then "?  " else if mac7 >= 0 then "+  " else "-  ") else "")
    + (if tfOnly and !IsNaN(pz7) then " > " + Round(pz7, 0) + "c  " else "")
    +
    (if showSignalNumbers then
        (if IsNaN(ms7) then "? " else if ms7 > 0 then "/ " else "\ ")
      + AsText(Round(mac7, 2)) + "   "
      + (if IsNaN(ss7) then "? " else if ss7 > 0 then "/ " else "\ ")
      + AsText(Round(sig7, 2))
      + "  |  " + AsText(Round(del7, 2)) + "  "
      + (if !IsNaN(pz7) then " > " + Round(pz7, 0) + "c " else "")
     else if showSignalDirections then
        (if IsNaN(ms7) then "? " else if ms7 > 0 then "/ " else "\ ")
      + " "
      + (if IsNaN(mac7) then "?  " else if mac7 >= 0 then "+  " else "-  ")
      + (if IsNaN(ss7) then "? " else if ss7 > 0 then "/ " else "\ ")
      + (if !IsNaN(pz7) then "    > " + Round(pz7, 0) + "c    " else "   ")
     else "")
    ,
    if st7 == 2 then GlobalColor("BrightGreen") else if st7 == 1 then GlobalColor("DarkGreen")
    else if st7 == -1 then GlobalColor("DarkRed") else if st7 == -2 then GlobalColor("BrightRed")
    else GlobalColor("Neutral"), location = tfLblLoc
);

#------------------------------------
# TF8 label
#------------------------------------
AddLabel(showTF and tf8Enabled,
(if P8 == AggregationPeriod.TWO_HOURS then "  2h   "
 else if P8 == AggregationPeriod.FOUR_HOURS then "  4h   "
 else if P8 == AggregationPeriod.DAY then "  1d   "
 else if P8 == AggregationPeriod.TWO_DAYS then "  2d   "
 else if P8 == AggregationPeriod.THREE_DAYS then "  3d   "
 else if P8 == AggregationPeriod.FOUR_DAYS then "  4d   "
 else if P8 == AggregationPeriod.WEEK then "  1w   "
 else if P8 == AggregationPeriod.MONTH then "  1mo   "
 else if P8 == AggregationPeriod.QUARTER then "  3mo   "
 else if P8 == AggregationPeriod.YEAR then "  1yr   " else "UNK ")
    + (if tfOnly then (if IsNaN(mac8) then "?  " else if mac8 >= 0 then "+  " else "-  ") else "")
    + (if tfOnly and !IsNaN(pz8) then " > " + Round(pz8, 0) + "c  " else "")
    +
    (if showSignalNumbers then
        (if IsNaN(ms8) then "? " else if ms8 > 0 then "/ " else "\ ")
      + AsText(Round(mac8, 2)) + "   "
      + (if IsNaN(ss8) then "? " else if ss8 > 0 then "/ " else "\ ")
      + AsText(Round(sig8, 2))
      + "  |  " + AsText(Round(del8, 2)) + "  "
      + (if !IsNaN(pz8) then " > " + Round(pz8, 0) + "c " else "")
     else if showSignalDirections then
        (if IsNaN(ms8) then "? " else if ms8 > 0 then "/ " else "\ ")
      + " "
      + (if IsNaN(mac8) then "?  " else if mac8 >= 0 then "+  " else "-  ")
      + (if IsNaN(ss8) then "? " else if ss8 > 0 then "/ " else "\ ")
      + (if !IsNaN(pz8) then "    > " + Round(pz8, 0) + "c    " else "   ")
     else "")
    ,
    if st8 == 2 then GlobalColor("BrightGreen") else if st8 == 1 then GlobalColor("DarkGreen")
    else if st8 == -1 then GlobalColor("DarkRed") else if st8 == -2 then GlobalColor("BrightRed")
    else GlobalColor("Neutral"), location = tfLblLoc
);
 
Last edited:

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
838 Online
Create Post

Similar threads

Similar threads

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top