# ThinkScript Architect - Dynamic Pivots (Traditional Only)
# Author: TheInevstorToolkit.com
# Description: Plots Traditional Pivots (P, S1-S5, R1-R5) and Midpoints.
# Feature: Levels are hidden until the price breaches the previous level ("Next Level Up" logic).
# Updated: Anchored Labels to Last Price + Offset (stable position) instead of Chart Edge.
declare upper;
# --- Inputs ---
input timeFrame = {default Auto, Day, Week, Month, Quarter, Year};
input lookBack = 2; # Number of periods back to show
input showLabels = yes; # Show bubbles at the end of the lines
input bubbleOffset = 10; # Distance (in bars) from the last candle to the bubbles
# --- Globals ---
# Change this color in the "Globals" section of the settings
DefineGlobalColor("PivotColor", Color.WHITE);
# --- Timeframe Logic ---
def aggPeriod;
switch (timeFrame) {
case Auto:
aggPeriod = if GetAggregationPeriod() <= AggregationPeriod.FIFTEEN_MIN then AggregationPeriod.DAY else AggregationPeriod.WEEK;
case Day:
aggPeriod = AggregationPeriod.DAY;
case Week:
aggPeriod = AggregationPeriod.WEEK;
case Month:
aggPeriod = AggregationPeriod.MONTH;
case Quarter:
aggPeriod = AggregationPeriod.QUARTER;
case Year:
aggPeriod = AggregationPeriod.YEAR;
}
# --- Data Fetch ---
# Fetch High, Low, Close of the PREVIOUS completed period for calculation
def h = high(period = aggPeriod)[1];
def l = low(period = aggPeriod)[1];
def c = close(period = aggPeriod)[1];
# Fetch CURRENT developing high/low for the Visibility Logic
def periodHigh = high(period = aggPeriod);
def periodLow = low(period = aggPeriod);
# --- Robust New Period Detection ---
# 1. Logic: Detect changes in the Timeframe's Date components.
def periodChange = if aggPeriod == AggregationPeriod.DAY then GetDay() != GetDay()[1]
else if aggPeriod == AggregationPeriod.WEEK then GetWeek() != GetWeek()[1]
else if aggPeriod == AggregationPeriod.MONTH then GetMonth() != GetMonth()[1]
else if aggPeriod == AggregationPeriod.QUARTER then Floor((GetMonth() - 1) / 3) != Floor((GetMonth()[1] - 1) / 3)
else if aggPeriod == AggregationPeriod.YEAR then GetYear() != GetYear()[1]
else GetDay() != GetDay()[1];
# 2. Safety: Force 'Yes' on the first bar to start the cycle, otherwise use the detection.
def isNewPeriod = if BarNumber() == 1 then yes else periodChange;
# --- NaN-Proof Counter ---
rec periodCounter = CompoundValue(1, if isNewPeriod then periodCounter[1] + 1 else periodCounter[1], 1);
# --- Lookback Logic ---
# We must find the counter value at the LAST VALID BAR (with close data), not the expansion area.
def lastValidBar = HighestAll(if !IsNaN(close) then BarNumber() else Double.NaN);
def totalValidPeriods = HighestAll(if BarNumber() == lastValidBar then periodCounter else 0);
# Compare current counter to the capped total
def withinLookBack = periodCounter >= (totalValidPeriods - lookBack + 1);
# --- Pivot Calculations (Traditional) ---
def PP = (h + l + c) / 3;
def R1 = PP * 2 - l;
def S1 = PP * 2 - h;
def R2 = PP + (h - l);
def S2 = PP - (h - l);
def R3 = PP * 2 + (h - 2 * l);
def S3 = PP * 2 - (2 * h - l);
def R4 = PP * 3 + (h - 3 * l);
def S4 = PP * 3 - (3 * h - l);
def R5 = PP * 4 + (h - 4 * l);
def S5 = PP * 4 - (4 * h - l);
# Midpoints
def P_R1 = (PP + R1) / 2;
def R1_R2 = (R1 + R2) / 2;
def R2_R3 = (R2 + R3) / 2;
def R3_R4 = (R3 + R4) / 2;
def R4_R5 = (R4 + R5) / 2;
def P_S1 = (PP + S1) / 2;
def S1_S2 = (S1 + S2) / 2;
def S2_S3 = (S2 + S3) / 2;
def S3_S4 = (S3 + S4) / 2;
def S4_S5 = (S4 + S5) / 2;
# --- Visibility Logic (The "Unlock" System) ---
def instantRLevel =
if !IsNaN(R4) and periodHigh >= R4 then 5
else if !IsNaN(R3) and periodHigh >= R3 then 4
else if !IsNaN(R2) and periodHigh >= R2 then 3
else if !IsNaN(R1) and periodHigh >= R1 then 2
else 1;
def instantSLevel =
if !IsNaN(S4) and periodLow <= S4 then 5
else if !IsNaN(S3) and periodLow <= S3 then 4
else if !IsNaN(S2) and periodLow <= S2 then 3
else if !IsNaN(S1) and periodLow <= S1 then 2
else 1;
# State Manager
# On New Period: Adopt the Instant Level immediately (don't force reset to 1 if we gapped).
# On Same Period: Keep the highest level reached so far (Max).
rec activeRLevel = CompoundValue(1,
if isNewPeriod then instantRLevel
else Max(activeRLevel[1], instantRLevel), 1);
rec activeSLevel = CompoundValue(1,
if isNewPeriod then instantSLevel
else Max(activeSLevel[1], instantSLevel), 1);
# --- Plot Definitions ---
# Logic: Within Lookback AND Unlocked AND Not in Expansion Area (!IsNaN(close))
def validPlot = withinLookBack and !IsNaN(close);
plot PPlot = if validPlot then PP else Double.NaN;
plot R1Plot = if validPlot and activeRLevel >= 1 then R1 else Double.NaN;
plot R2Plot = if validPlot and activeRLevel >= 2 then R2 else Double.NaN;
plot R3Plot = if validPlot and activeRLevel >= 3 then R3 else Double.NaN;
plot R4Plot = if validPlot and activeRLevel >= 4 then R4 else Double.NaN;
plot R5Plot = if validPlot and activeRLevel >= 5 then R5 else Double.NaN;
plot P_R1Plot = if validPlot and activeRLevel >= 1 then P_R1 else Double.NaN;
plot R1_R2Plot = if validPlot and activeRLevel >= 2 then R1_R2 else Double.NaN;
plot R2_R3Plot = if validPlot and activeRLevel >= 3 then R2_R3 else Double.NaN;
plot R3_R4Plot = if validPlot and activeRLevel >= 4 then R3_R4 else Double.NaN;
plot R4_R5Plot = if validPlot and activeRLevel >= 5 then R4_R5 else Double.NaN;
plot S1Plot = if validPlot and activeSLevel >= 1 then S1 else Double.NaN;
plot S2Plot = if validPlot and activeSLevel >= 2 then S2 else Double.NaN;
plot S3Plot = if validPlot and activeSLevel >= 3 then S3 else Double.NaN;
plot S4Plot = if validPlot and activeSLevel >= 4 then S4 else Double.NaN;
plot S5Plot = if validPlot and activeSLevel >= 5 then S5 else Double.NaN;
plot P_S1Plot = if validPlot and activeSLevel >= 1 then P_S1 else Double.NaN;
plot S1_S2Plot = if validPlot and activeSLevel >= 2 then S1_S2 else Double.NaN;
plot S2_S3Plot = if validPlot and activeSLevel >= 3 then S2_S3 else Double.NaN;
plot S3_S4Plot = if validPlot and activeSLevel >= 4 then S3_S4 else Double.NaN;
plot S4_S5Plot = if validPlot and activeSLevel >= 5 then S4_S5 else Double.NaN;
# --- Styling ---
PPlot.SetDefaultColor(GlobalColor("PivotColor"));
PPlot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
PPlot.SetStyle(Curve.FIRM);
# Resistance
R1Plot.SetDefaultColor(GlobalColor("PivotColor")); R1Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); R1Plot.SetStyle(Curve.FIRM);
R2Plot.SetDefaultColor(GlobalColor("PivotColor")); R2Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); R2Plot.SetStyle(Curve.FIRM);
R3Plot.SetDefaultColor(GlobalColor("PivotColor")); R3Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); R3Plot.SetStyle(Curve.FIRM);
R4Plot.SetDefaultColor(GlobalColor("PivotColor")); R4Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); R4Plot.SetStyle(Curve.FIRM);
R5Plot.SetDefaultColor(GlobalColor("PivotColor")); R5Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); R5Plot.SetStyle(Curve.FIRM);
# Support
S1Plot.SetDefaultColor(GlobalColor("PivotColor")); S1Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); S1Plot.SetStyle(Curve.FIRM);
S2Plot.SetDefaultColor(GlobalColor("PivotColor")); S2Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); S2Plot.SetStyle(Curve.FIRM);
S3Plot.SetDefaultColor(GlobalColor("PivotColor")); S3Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); S3Plot.SetStyle(Curve.FIRM);
S4Plot.SetDefaultColor(GlobalColor("PivotColor")); S4Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); S4Plot.SetStyle(Curve.FIRM);
S5Plot.SetDefaultColor(GlobalColor("PivotColor")); S5Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); S5Plot.SetStyle(Curve.FIRM);
# Midpoints (Dotted)
P_R1Plot.SetDefaultColor(GlobalColor("PivotColor")); P_R1Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); P_R1Plot.SetStyle(Curve.SHORT_DASH);
R1_R2Plot.SetDefaultColor(GlobalColor("PivotColor")); R1_R2Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); R1_R2Plot.SetStyle(Curve.SHORT_DASH);
R2_R3Plot.SetDefaultColor(GlobalColor("PivotColor")); R2_R3Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); R2_R3Plot.SetStyle(Curve.SHORT_DASH);
R3_R4Plot.SetDefaultColor(GlobalColor("PivotColor")); R3_R4Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); R3_R4Plot.SetStyle(Curve.SHORT_DASH);
R4_R5Plot.SetDefaultColor(GlobalColor("PivotColor")); R4_R5Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); R4_R5Plot.SetStyle(Curve.SHORT_DASH);
P_S1Plot.SetDefaultColor(GlobalColor("PivotColor")); P_S1Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); P_S1Plot.SetStyle(Curve.SHORT_DASH);
S1_S2Plot.SetDefaultColor(GlobalColor("PivotColor")); S1_S2Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); S1_S2Plot.SetStyle(Curve.SHORT_DASH);
S2_S3Plot.SetDefaultColor(GlobalColor("PivotColor")); S2_S3Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); S2_S3Plot.SetStyle(Curve.SHORT_DASH);
S3_S4Plot.SetDefaultColor(GlobalColor("PivotColor")); S3_S4Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); S3_S4Plot.SetStyle(Curve.SHORT_DASH);
S4_S5Plot.SetDefaultColor(GlobalColor("PivotColor")); S4_S5Plot.SetPaintingStrategy(PaintingStrategy.HORIZONTAL); S4_S5Plot.SetStyle(Curve.SHORT_DASH);
# --- Labels & Bubbles ---
# 1. Descriptive Header: explicit Timeframe Name
def isLastPeriod = BarNumber() == lastValidBar;
AddLabel(showLabels and !IsNaN(PPlot) and isLastPeriod,
if aggPeriod == AggregationPeriod.DAY then "Daily Pivots"
else if aggPeriod == AggregationPeriod.WEEK then "Weekly Pivots"
else if aggPeriod == AggregationPeriod.MONTH then "Monthly Pivots"
else if aggPeriod == AggregationPeriod.QUARTER then "Quarterly Pivots"
else if aggPeriod == AggregationPeriod.YEAR then "Yearly Pivots"
else "Pivots",
GlobalColor("PivotColor"));
# 2. Axis Bubbles (Relative to Price)
# We anchor bubbles to (LastValidBar + bubbleOffset).
# This keeps them stable relative to the price candles, even if you drag the chart.
def bubbleLoc = BarNumber() == (lastValidBar + bubbleOffset);
# Helper to carry the value from the last valid candle to the bubble location
def lastPP = HighestAll(if BarNumber() == lastValidBar then PP else Double.NaN);
def lastR1 = HighestAll(if BarNumber() == lastValidBar then R1 else Double.NaN);
def lastR2 = HighestAll(if BarNumber() == lastValidBar then R2 else Double.NaN);
def lastR3 = HighestAll(if BarNumber() == lastValidBar then R3 else Double.NaN);
def lastR4 = HighestAll(if BarNumber() == lastValidBar then R4 else Double.NaN);
def lastR5 = HighestAll(if BarNumber() == lastValidBar then R5 else Double.NaN);
def lastS1 = HighestAll(if BarNumber() == lastValidBar then S1 else Double.NaN);
def lastS2 = HighestAll(if BarNumber() == lastValidBar then S2 else Double.NaN);
def lastS3 = HighestAll(if BarNumber() == lastValidBar then S3 else Double.NaN);
def lastS4 = HighestAll(if BarNumber() == lastValidBar then S4 else Double.NaN);
def lastS5 = HighestAll(if BarNumber() == lastValidBar then S5 else Double.NaN);
# We check !IsNaN(last...) to ensure we only draw bubbles for unlocked levels
AddChartBubble(showLabels and !IsNaN(lastPP) and bubbleLoc, lastPP, "Pivot", GlobalColor("PivotColor"), yes);
AddChartBubble(showLabels and !IsNaN(lastR1) and bubbleLoc, lastR1, "R1", GlobalColor("PivotColor"), yes);
AddChartBubble(showLabels and !IsNaN(lastR2) and bubbleLoc, lastR2, "R2", GlobalColor("PivotColor"), yes);
AddChartBubble(showLabels and !IsNaN(lastR3) and bubbleLoc, lastR3, "R3", GlobalColor("PivotColor"), yes);
AddChartBubble(showLabels and !IsNaN(lastR4) and bubbleLoc, lastR4, "R4", GlobalColor("PivotColor"), yes);
AddChartBubble(showLabels and !IsNaN(lastR5) and bubbleLoc, lastR5, "R5", GlobalColor("PivotColor"), yes);
AddChartBubble(showLabels and !IsNaN(lastS1) and bubbleLoc, lastS1, "S1", GlobalColor("PivotColor"), no);
AddChartBubble(showLabels and !IsNaN(lastS2) and bubbleLoc, lastS2, "S2", GlobalColor("PivotColor"), no);
AddChartBubble(showLabels and !IsNaN(lastS3) and bubbleLoc, lastS3, "S3", GlobalColor("PivotColor"), no);
AddChartBubble(showLabels and !IsNaN(lastS4) and bubbleLoc, lastS4, "S4", GlobalColor("PivotColor"), no);
AddChartBubble(showLabels and !IsNaN(lastS5) and bubbleLoc, lastS5, "S5", GlobalColor("PivotColor"), no);