Sun, Moon, and Stars (SMS) For ThinkOrSwim

chewie76

Well-known member
VIP
VIP Enthusiast
🌌 Sun, Moon, and Stars Indicator (SMS) includes Upper and Lower Indicator
The SMS Indicator is built around a simple idea: price behaves like it’s moving within a gravity system.

There are three main forces at play:
  • ☀️ Sun (primary force – trend & gravity core)
  • 🌙 Moon (secondary force – structure & timing)
  • ✨ Stars (early warning signals)
  • 🌌 Milky Way (long-term directional bias)
In the below picture, there are three examples of buying signals, the best buy signals are to wait for the stars (points) to appear, then buy at the cross of the yellow TriggerLine. Take profit at the opposite Moon cloud, or if price breaks below the TriggerLine. Below is a full description. Below the description is the code for both the upper and lower chart. The upper chart includes a bell curve label at the top that shows you how long price stays within the sun bands. This gives you trend sentiment by seeing which side is larger than the other. It is by default counting the last 200 bars, which can be changed in the settings.

All features of this indicator can be turned ON/OFF in the settings.
1775590891988.png


☀️ The Sun – Primary Gravity Force
At the center of everything is the Sun gravity line (the thick yellow/green/red line).
This is the core trend engine.
  • It’s a smoothed price equilibrium (Hull-based), acting like gravity
  • Price naturally pulls back toward it
  • Color tells direction:
    • Green → uptrend
    • Red → downtrend
Sun Bands (The Real Power)
Around the Sun are expanding volatility-based bands:
  • Inner Yellow Band (Gravity Core Zone)
    → Where price spends most of its time
  • Outer Bands (Orbit Levels)
    → Measure how far price has stretched:
    • +1 / -1 → normal extension
    • +2 / -2 → strong extension
    • +3 / -3 → extreme (Event Horizon)
Key Concepts:
  • Gravity is inevitable → the farther price moves away, the stronger the pull back
  • Band Contraction (Squeeze) → signals a big move is coming
  • Event Horizon (outside outer bands) → rare exhaustion zones



🌙 The Moon – Secondary Force (Structure & Confirmation)

The Moon bands (gray clouds) act as a secondary gravity system.
  • Tighter and more responsive than the Sun
  • Helps define trade structure and timing
  • Works best when aligned with the Sun
Components:
  • Moon Midline (white line) → short-term balance
  • Inner/Outer Gray Bands → volatility envelopes
  • Moon Trend Cloud (green/red) → directional bias (optional)
How to Use:
  • Confirms direction after a setup
  • Helps identify break points and continuation zones
  • Acts as dynamic support/resistance



✨ The Stars – Your Early Signal
The Stars are the first thing you want to see.

They mark price extremes where reversals or setups are likely forming.
  • ⭐ Star Buy → price is stretched to the downside
  • ⭐ Star Sell → price is stretched to the upside
These are NOT entry signals by themselves — they are alerts.




🎯 The Trigger Line – Your Entry

Once you see a Star, the next step is simple:
  • Wait for price to break the yellow Trigger Line
Trade Logic:
  • ⭐ → First signal (attention)
  • Break of Trigger Line → Entry

This keeps you from catching falling knives or shorting too early.



🌌 The Milky Way – Long-Term Force
The Milky Way = 180 EMA + 250 EMA

This is your macro directional bias.
  • Acts as a third layer of gravity
  • Helps filter trades:
    • Above → favor longs
    • Below → favor shorts
Think of it as the bigger orbit system everything else is moving within.




🔁Full Trade Setup (Simple Version)
Best Buy Setup:
  1. Price was in the blue (lower extreme) zone
  2. ⭐ Star Buy prints
  3. Price breaks above Trigger Line or Moon line
  4. Target:
    • Opposite Moon band
    • Or Sun band



Best Sell Setup:
  1. Price was in the red (upper extreme) zone
  2. ⭐ Star Sell prints
  3. Price breaks below Trigger Line or Moon line
  4. Target:
    • Opposite Moon band
    • Or Sun band



📊 Bonus Features
  • Bell Curve Labels → show how often price lives in each zone (last 200 bars)
  • Squeeze Dots → early warning of expansion
  • Breakout Arrows → post-squeeze moves
  • Divergence Lines → added confluence
  • Event Horizon Alerts → rare extreme conditions



🧠 Core Philosophy
“Expansion is opportunity—gravity is inevitable.”
“The farther price drifts, the stronger gravity pulls.”



🔑 Final Thought
This indicator is not about predicting — it’s about positioning around extremes and letting gravity do the work.
  • The Sun tells you where price should go
  • The Moon helps guide how it gets there
  • The Stars tell you when to pay attention
  • The Milky Way reminds you of the bigger picture


UPPER INDICATOR:
Code:
# Sun_Moon_and_Stars_Indicator (SMS)
# The center yellow band is the Sun's gravity core.  Price is attracted to it. It indicates trend direction.
# The outer shaded bands the Sun's outer orbit and escape orbit gravity levels.
# When the Sun's bands are converging and squeezing together, it is an early indication that a big move will occur.
# The Event Horizon is when price extends outside the bands, which is a rare occurance.
# The smaller range Moon bands are a powerful combination when they work together, and are optional in then settings.
# The Moon trend (Green and Red cloud) indicate which direction you should trade.  It's optional in the settings.
# The Stars indicate extended levels. They are early indicators.above  They and are optional in the settings.
# The Milky Way lines are standard 180 and 250 EMA lines.  They are optional in the settings.
# The Label is a bell curve that shows the percentage of the time that price is within these zones.
# Time is set to measure the last 200 bars by default, but can be adjusted in the settings.
# The white dots on the Sun's gravity line indicate a squeeze. Squeeze length can be adjusted in the settings.
# Best buys are after price was in the blue zone and you see the stars, then it breaks above the  Moon line or triggerline.
# Good exit target is the opposite Moon line or Sun band.
# Best sells are after price was in the red zone and you see the stars, then it breaks below the Moon line or triggerline.
# Good exit target is the opposite Moon line or Sun band.
# Includes divergence lines.
# “Expansion is opportunity—gravity is inevitable.”
# “The farther price drifts, the stronger gravity pulls.”
# Created by Chewie 3/26/2026

declare upper;

# =========================
# INPUTS
# =========================
input Colorbar = yes;
input showSunSignals = yes;
input Moon_bands = yes;
input Moon_Trend = yes;
input showStars = yes;
input MilkyWay = yes;
input enable_EH_Alert = yes;
input SqueezeDot = yes;
input SqueezeLength = 50;
input GravityLength = 200;
input length = 25;
input bandMult = 2.0;
input GravityZone = 0.38;
input Sun_Zone = 0.3;


# =========================
# COLORS
# =========================
DefineGlobalColor("Yellow", CreateColor(110, 110, 0));

DefineGlobalColor("softDarkRed", CreateColor(90, 0, 0));
DefineGlobalColor("softRed", CreateColor(140, 40, 80));
DefineGlobalColor("Red", CreateColor(175, 60, 190));
DefineGlobalColor("Magenta", CreateColor(255, 0, 255));

DefineGlobalColor("SoftDarkBlue", CreateColor(0, 40, 100));
DefineGlobalColor("SoftBlue", CreateColor(30, 90, 200));
DefineGlobalColor("Blue", CreateColor(70, 160, 255));
DefineGlobalColor("Cyan", CreateColor(0, 200, 255));

# =========================
# GRAVITY LENGTH PLOT
# =========================

def targetBar = HighestAll(BarNumber()) - GravityLength;
AddVerticalLine(BarNumber() == targetBar, GravityLength + " Bars Ago", Color.YELLOW);

# =========================
# PRICE + MID (GRAVITY)
# =========================
def ohlc4 = (open + high + low + close) / 4;
def mid =   HullMovingAvg((high + low) / 2, 160);   #ExpAverage(ohlc4, length);

# =========================
# VOLATILITY ENGINE
# =========================
def spread = ohlc4 / mid;
def dev = StDev(spread, length);
def vol = ExpAverage(dev, length);

# =========================
# SUN GRAVITY BANDS
# =========================

def b0u = mid * (1 + vol * bandMult * GravityZone);
def b0d = mid * (1 - vol * bandMult * GravityZone);
def b1u = mid * (1 + vol * bandMult);
def b1d = mid * (1 - vol * bandMult);
def b15u = mid * (1 + vol * bandMult * 1.5);
def b15d = mid * (1 - vol * bandMult * 1.5);
def b2u = mid * (1 + vol * bandMult * 2);
def b2d = mid * (1 - vol * bandMult * 2);
def b25u = mid * (1 + vol * bandMult * 2.5);
def b25d = mid * (1 - vol * bandMult * 2.5);
def b3u = mid * (1 + vol * bandMult * 3);
def b3d = mid * (1 - vol * bandMult * 3);

# =========================
# GRAVITY ENGINE
# =========================
def bandUnit = vol * bandMult;
def gravityRaw = (ohlc4 - mid) / (mid * bandUnit);
def gravityBand = gravityRaw;
def gravitySmooth = ExpAverage(gravityBand, 5);

# =========================
# EXHAUSTION CONDITIONS
# =========================
def extremeHigh = ohlc4 > b2u;
def extremeLow  = ohlc4 < b2d;
def revertShort = extremeHigh and gravitySmooth > 0;
def revertLong  = extremeLow and gravitySmooth < 0;

# =========================
# TREND DIRECTION
# =========================
rec dir =
    if mid > mid[1] then 1
    else if mid < mid[1] then -1
    else dir[1];

# =========================
# BELL CURVE DISTRIBUTION
# =========================

def zone3Up   = ohlc4 > b3u;
def zone2Up   = ohlc4 > b2u and ohlc4 <= b3u;
def zone1Up   = ohlc4 > b1u and ohlc4 <= b2u;
def zoneMid   = ohlc4 <= b1u and ohlc4 >= b1d;
def zone1Down = ohlc4 < b1d and ohlc4 >= b2d;
def zone2Down = ohlc4 < b2d and ohlc4 >= b3d;
def zone3Down = ohlc4 < b3d;

def p3Up   = Round(100 * Sum(zone3Up,   GravityLength) / GravityLength, 1);
def p2Up   = Round(100 * Sum(zone2Up,   GravityLength) / GravityLength, 1);
def p1Up   = Round(100 * Sum(zone1Up,   GravityLength) / GravityLength, 1);
def pMid   = Round(100 * Sum(zoneMid,   GravityLength) / GravityLength, 1);
def p1Down = Round(100 * Sum(zone1Down, GravityLength) / GravityLength, 1);
def p2Down = Round(100 * Sum(zone2Down, GravityLength) / GravityLength, 1);
def p3Down = Round(100 * Sum(zone3Down, GravityLength) / GravityLength, 1);

# =========================
# PLOTS
# =========================
plot Gravity = mid;

plot U0 = b0u;
plot D0 = b0d;
plot U1 = b1u;
plot D1 = b1d;
plot U15 = b15u;
plot D15 = b15d;
plot U2 = b2u;
plot D2 = b2d;
plot U25 = b25u;
plot D25 = b25d;
plot U3 = b3u;
plot D3 = b3d;

Gravity.AssignValueColor(if dir == 1 then Color.GREEN else Color.RED);
Gravity.SetLineWeight(3);

U0.SetDefaultColor(GlobalColor("Yellow"));
D0.SetDefaultColor(GlobalColor("Yellow"));
U1.SetDefaultColor(GlobalColor("SoftRed"));
U15.SetDefaultColor(GlobalColor("SoftRed"));
U2.SetDefaultColor(GlobalColor("Red"));
U25.SetDefaultColor(GlobalColor("magenta"));
U3.SetDefaultColor(Color.MAGENTA);
D1.SetDefaultColor(GlobalColor("SoftBlue"));
D15.SetDefaultColor(GlobalColor("SoftBlue"));
D2.SetDefaultColor(GlobalColor("Blue"));
D25.SetDefaultColor(GlobalColor("cyan"));
D3.SetDefaultColor(Color.CYAN);

# =========================
# SUN GRAVITY CLOUDS
# =========================

def atrLengthg = 50;
def smoothATR = 20;
def basis = HullMovingAvg((high + low) / 2, 160);

# === VOLATILITY ===
def rawATRg = Average(TrueRange(high, close, low), atrLengthg);
def atrg = ExpAverage(rawATRg, smoothATR);

# === SUN GRAVITY BANDS ===
def mu1 = basis + atrg * Sun_Zone;
def ml1 = basis - atrg * Sun_Zone;

AddCloud(mu1, ml1, Color.YELLOW, Color.YELLOW);
AddCloud(U0, D0, GlobalColor("Yellow"), GlobalColor("Yellow"));

AddCloud(U1, U15, GlobalColor("softDarkRed"), GlobalColor("softDarkRed"));
AddCloud(U15, U2, GlobalColor("softRed"), GlobalColor("softRed"));
AddCloud(U2, U25, GlobalColor("Red"), GlobalColor("Red"));
AddCloud(U25, U3, GlobalColor("Magenta"), GlobalColor("Magenta"));

AddCloud(D1, D15, GlobalColor("softDarkBlue"), GlobalColor("softDarkBlue"));
AddCloud(D15, D2, GlobalColor("softBlue"), GlobalColor("softBlue"));
AddCloud(D2, D25, GlobalColor("BLUE"), GlobalColor("BLUE"));
AddCloud(D25, D3, GlobalColor("cyan"), GlobalColor("cyan"));

# SUN SIGNALS
plot BuySunSignal = if showSunSignals and revertLong then low - ATR(60) / 2 else Double.NaN;
BuySunSignal.SetPaintingStrategy(PaintingStrategy.POINTS);
BuySunSignal.SetDefaultColor(Color.CYAN);
BuySunSignal.SetLineWeight(1);

plot SellSunSignal = if showSunSignals and revertShort then high + ATR(60) / 2 else Double.NaN;
SellSunSignal.SetPaintingStrategy(PaintingStrategy.POINTS);
SellSunSignal.SetDefaultColor(Color.MAGENTA);
SellSunSignal.SetLineWeight(2);

# =========================
# PRICE COLOR
# =========================
AssignPriceColor(
    if !Colorbar then Color.CURRENT
    else if high > U3 then Color.MAGENTA
    else if low < D3 then Color.CYAN
    else if close > U0 then Color.GREEN
    else if close < D0 then Color.RED
    else Color.YELLOW);

#----------------------------
# TRIGGERLINE
#----------------------------
def price        = close;
def smaLength    = 8;
def TatrLength   = 15;
def regLength    = 21;
def atrMult      = 1.25;   # Used to build the Keltner mid channel
def valS               = Average(price, smaLength);
def Tatr               = Average(TrueRange(high, close, low), TatrLength);
def average_true_range = Tatr;
def Upper_Band         = valS + atrMult * average_true_range;
def Lower_Band         = valS - atrMult * average_true_range;
def mid1               = (Upper_Band - Lower_Band) / 2 + Lower_Band;
def regMid             = Inertia(mid1, regLength);

plot TRIGGERLINE = regMid;
TRIGGERLINE.SetDefaultColor(Color.YELLOW);
TRIGGERLINE.SetLineWeight(3);
TRIGGERLINE.HideTitle();
TRIGGERLINE.HideBubble();

# =========================
# LABELS
# =========================

# ZONE PERCENTAGE LABELS
AddLabel(yes, "+3: " + p3Up + "%", Color.MAGENTA);
AddLabel(yes, "+2: " + p2Up + "%", GlobalColor("Red"));
AddLabel(yes, "+1: " + p1Up + "%", GlobalColor("softRed"));
AddLabel(yes, "CENTER: " + pMid + "%", GlobalColor("Yellow"));
AddLabel(yes, "-1: " + p1Down + "%", GlobalColor("softBlue"));
AddLabel(yes, "-2: " + p2Down + "%", GlobalColor("Blue"));
AddLabel(yes, "-3: " + p3Down + "%", Color.CYAN);

def EVENT_HORIZON_S = high > U3;
def EVENT_HORIZON_B = low < D3;
def ESCAPE_ORBIT_S = close < U3 and close > U2;
def ESCAPE_ORBIT_B = close > D3 and close < D2;
def OUTER_ORBIT_S = close < U2 and close > U1;
def OUTER_ORBIT_B = close > D2 and close < D1;
def GRAVITY_ZONE = close < U1 and close > D1;

# GRAVITY LABEL #
AddLabel(
    yes,
    if EVENT_HORIZON_S then "Gravity: " + Round(gravityRaw, 2) + ": EVENT_HORIZON_SELL"
    else if EVENT_HORIZON_B then "Gravity: " + Round(gravityRaw, 2) + ": EVENT_HORIZON_BUY"
    else if ESCAPE_ORBIT_S then "Gravity: " + Round(gravityRaw, 2) + ": ESCAPE_ORBIT_SELL"
    else if ESCAPE_ORBIT_B then "Gravity: " + Round(gravityRaw, 2) + ": ESCAPE_ORBIT_BUY"
    else if OUTER_ORBIT_S then "Gravity: " + Round(gravityRaw, 2) + ": OUTER_ORBIT_SELL"
    else if OUTER_ORBIT_B then "Gravity: " + Round(gravityRaw, 2) + ": OUTER_ORBIT_BUY"
    else if GRAVITY_ZONE then "Gravity: " + Round(gravityRaw, 2) + ": CENTER_ZONE" else "",
    if EVENT_HORIZON_S then Color.MAGENTA
    else if EVENT_HORIZON_B then Color.CYAN
    else if ESCAPE_ORBIT_S then GlobalColor("Red")
    else if ESCAPE_ORBIT_B then GlobalColor("Blue")
    else if OUTER_ORBIT_S then GlobalColor("softRed")
    else if OUTER_ORBIT_B then GlobalColor("softBlue")
    else if GRAVITY_ZONE then GlobalColor("Yellow")
    else Color.CURRENT);


# =========================
#MOON GRAVITY
# =========================

def lengthi = 24;
def h_param = 8.0;
def r_param = 2.0;
def mult_inner = 1.3;
def mult_outer = 1.8;
def mult_X = 2.3;
def mult_X2 = 2.9;
def srci = (high + low + close) / 3;

def numerator =
    fold k1 = 0 to lengthi
    with acc1 = 0
    do acc1 +
        srci[k1] *
        Power(1 + Sqr(k1) / (2 * r_param * Sqr(h_param)),
            -r_param);

def denominator =
    fold k2 = 0 to lengthi
    with acc2 = 0
    do acc2 +
        Power(1 + Sqr(k2) / (2 * r_param * Sqr(h_param)),
            -r_param);

def Moon = numerator / denominator;
def errorSum =
    fold k3 = 0 to lengthi
    with acc3 = 0
    do acc3 + AbsValue(srci[k3] - Moon);

def meanDeviation = errorSum / lengthi;
def volatility = (meanDeviation + ATR(lengthi)) / 2;

# =========================
# MOON BANDS
# =========================
def MoonUP = Moon + volatility * mult_inner;
def MoonDN = Moon - volatility * mult_inner;
def MoonUP2 = Moon + volatility * mult_outer;
def MoonDN2 = Moon - volatility * mult_outer;
def MoonUPX = if Moon_bands then Moon + volatility * mult_X else Double.NaN;
def MoonDNX = if Moon_bands then Moon - volatility * mult_X else Double.NaN;

# =========================
# PLOTS
# =========================

plot Moon_Band_High = if Moon_bands then MoonUP else Double.NaN;
plot Moon_Band_Mid = if Moon_bands then Moon else Double.NaN;
plot Moon_Band_Low = if Moon_bands then MoonDN else Double.NaN;
plot MoonUPX2 = if Moon_bands then Moon + volatility * mult_X2 else Double.NaN;
plot MoonDNX2 = if Moon_bands then Moon - volatility * mult_X2 else Double.NaN;

Moon_Band_High.SetDefaultColor(Color.LIGHT_GRAY);
Moon_Band_Mid.SetDefaultColor(Color.WHITE);
Moon_Band_Low.SetDefaultColor(Color.LIGHT_GRAY);
MoonUPX2.SetDefaultColor(Color.GRAY);
MoonDNX2.SetDefaultColor(Color.GRAY);
Moon_Band_Mid.SetLineWeight(2);

AddCloud(MoonDN2, Moon_Band_Low, Color.DARK_GRAY, Color.DARK_GRAY);
AddCloud(MoonUP2, Moon_Band_High, Color.DARK_GRAY, Color.DARK_GRAY);
AddCloud(MoonDN2, MoonDNX, Color.GRAY, Color.GRAY);
AddCloud(MoonUP2, MoonUPX, Color.GRAY, Color.GRAY);
AddCloud(MoonDNX, MoonDNX2, Color.LIGHT_GRAY, Color.LIGHT_GRAY);
AddCloud(MoonUPX, MoonUPX2, Color.LIGHT_GRAY, Color.LIGHT_GRAY);

def Star_Sell = high > MoonUP2 and high > U15;
def Star_Buy = low < MoonDN2 and low < D15;

# STAR SIGNALS
plot StarBuySignal = if showStars and Star_Buy then low - ATR(60) / 2 else Double.NaN;
StarBuySignal.SetPaintingStrategy(PaintingStrategy.POINTS);
StarBuySignal.SetDefaultColor(Color.WHITE);
StarBuySignal.SetLineWeight(1);

plot StarSellSignal = if showStars and Star_Sell then high + ATR(60) / 2 else Double.NaN;
StarSellSignal.SetPaintingStrategy(PaintingStrategy.POINTS);
StarSellSignal.SetDefaultColor(Color.WHITE);
StarSellSignal.SetLineWeight(1);

# ALERTS #
def EHS = EVENT_HORIZON_S and !EVENT_HORIZON_S[1];
def EHB = EVENT_HORIZON_B and !EVENT_HORIZON_B[1];
Alert(enable_EH_Alert and EHS, "Event Horizon SELL (First Touch)", Alert.BAR, Sound.Ding);
Alert(enable_EH_Alert and EHB, "Event Horizon BUY (First Touch)", Alert.BAR, Sound.Ding);


# MOON TREND

def bandwidth = 10;

script MoonTrend{
    input src      = close;
    input len      = 25;
    input bw       = 10;
    input atrVal   = 0.0;
    input bandMult = 0.0;
    input side     = 0;

    def adjSrc;
    if side == 1 {
        adjSrc = src + (atrVal * bandMult);
    } else if side == -1 {
        adjSrc = src - (atrVal * bandMult);
    } else {
        adjSrc = src;}
    def sumW   = fold i0 = 0 to len with sw   = 0 do sw   + Exp(-0.5 * (i0 / bw) * (i0 / bw));
    def sumWX  = fold i1 = 0 to len with swx  = 0 do swx  + Exp(-0.5 * (i1 / bw) * (i1 / bw)) * i1;
    def sumWY  = fold i2 = 0 to len with swy  = 0 do swy  + Exp(-0.5 * (i2 / bw) * (i2 / bw)) * GetValue(adjSrc, i2);
    def sumWXY = fold i3 = 0 to len with swxy = 0 do swxy + Exp(-0.5 * (i3 / bw) * (i3 / bw)) * i3 * GetValue(adjSrc, i3);
    def sumWX2 = fold i4 = 0 to len with swx2 = 0 do swx2 + Exp(-0.5 * (i4 / bw) * (i4 / bw)) * i4 * i4;
    def meanX = sumWX / sumW;
    def meanY = sumWY / sumW;
    def beta  = (sumWXY - meanX * meanY * sumW) / (sumWX2 - meanX * meanX * sumW);
    def alpha = meanY - beta * meanX;
    plot result = alpha + beta * (len / 2);}

def atr = ATR(220);
def MTND  = MoonTrend(close, length, bandwidth, atr, 0, 0);

# ============================================================
# PLOTS
# ============================================================

plot MoonTrend = if Moon_trend then MTND else double.nan;
MoonTrend.assignValueColor(if MoonTrend > Moon_Band_Mid then color.red else color.green);
AddCloud(Moon_Band_Mid, MoonTrend, Color.DARK_GREEN, Color.DARK_RED);

# =========================
# SQUEEZE DETECTION
# =========================
def b2Width      = b2u - b2d;
def sqLow50      = Lowest(b2Width, 50);
def sqHigh50     = Highest(b2Width, 50);
def squeezeRange = sqHigh50 - sqLow50;
def tolerance    = sqLow50 * 0.001;
def squeezeOn    = b2Width <= sqLow50 + tolerance;
def wideningNow  = b2Width > b2Width[1];

rec inSqueeze =
    if squeezeOn and !wideningNow then 1
    else if inSqueeze[1] and !wideningNow then 1
    else 0;

# =========================
# DIRECTIONAL BIAS (vs TriggerLine)
# =========================
def bullishSqueeze = inSqueeze and close > regMid;
def bearishSqueeze = inSqueeze and close <= regMid;

# =========================
# SQUEEZE DOTS
# =========================
plot SqueezeDotBull = if SqueezeDot and bullishSqueeze then mid else Double.NaN;
SqueezeDotBull.SetPaintingStrategy(PaintingStrategy.POINTS);
SqueezeDotBull.SetDefaultColor(Color.green);
SqueezeDotBull.SetLineWeight(5);

plot SqueezeDotBear = if SqueezeDot and bearishSqueeze then mid else Double.NaN;
SqueezeDotBear.SetPaintingStrategy(PaintingStrategy.POINTS);
SqueezeDotBear.SetDefaultColor(Color.red);
SqueezeDotBear.SetLineWeight(5);


# =========================
# POST-SQUEEZE BREAKOUT ARROW
# =========================
def justExited   = !inSqueeze and inSqueeze[1];
def breakoutUp   = justExited and close > b0u;
def breakoutDown = justExited and close < b0d;

plot SqBreakUp = if breakoutUp then mid - (b2Width * 0.15) else Double.NaN;
SqBreakUp.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
SqBreakUp.SetDefaultColor(Color.GREEN);
SqBreakUp.SetLineWeight(3);

plot SqBreakDn = if breakoutDown then mid + (b2Width * 0.15) else Double.NaN;
SqBreakDn.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
SqBreakDn.SetDefaultColor(Color.RED);
SqBreakDn.SetLineWeight(3);


# =========================
# MILKY WAY LINES
# =========================
input lengthAvgEXP = 180;
input lengthAvgEXP2 = 250;
def displace = 0;
def pricebb = close;
input averageType2 = AverageType.EXPONENTIAL;

plot MilkyWay1 = if Milkyway then MovingAverage(averageType2, data = pricebb[-displace], length = lengthAvgEXP) else double.nan;
plot MilkyWay2 = if Milkyway then MovingAverage(averageType2, data = pricebb[-displace], length = lengthAvgEXP2) else double.nan;

MilkyWay2.setdefaultColor(createcolor(255,51,51));
#.AssignValueColor(if close > AvgExp2 then Color.GREEN else Color.RED);
MilkyWay2.SetLineWeight(3);
MilkyWay2.HideTitle();
MilkyWay2.HideBubble();
MilkyWay1.setdefaultColor(color.dark_orange);
#AvgExp2.AssignValueColor(if close > AvgExp2 then Color.UPTICK else Color.DOWNTICK);
MilkyWay1.SetLineWeight(3);
MilkyWay1.HideTitle();
MilkyWay1.HideBubble();

AddCloud(MilkyWay1, MilkyWay2, Color.dark_green, Color.dark_red);




# =========================
# DIVERGENCE
# =========================
def smoothOsc    = 1;
def osc = ExpAverage(gravityRaw, smoothOsc);

input divergenceLength = 55; #hint divergenceLength: The number of bars used to calculate divergences.
def overBought = 1; #hint overbought: The overbought level value.
def overSold = -1; #hint oversold: The oversold level value.
def HALF = 0;
input divergenceType = {default regular, reverse}; #hint divergenceType: The type of divergence. A regular divergence is when price is making higher highs (or lower lows), while the indicator is making lower highs (or higher lows). A reverse divergence (also called a hidden divergence) is when the indicator is making higher highs (or lower lows), while price is making lower highs (or higher lows).
#Hint: The output of this indicator is for informational and educational use only, is not an investment recommendation or advice, and should not be relied upon in making the decision to buy or sell a security or pursue a particular investment strategy.

def xDownBars;
def xUpBars;
def xDowns;
def xUps;
def hiBars;
def loBars;
def pivotTop;
def pivotBottom;
def hiInd;
def loInd;
def hiPrice;
def loPrice;
plot bearish;
plot bullish;

def K = osc;

def ind;
ind = K;

# Bearish
pivotTop =
if
    divergenceType == divergenceType.regular
then
    ind[1] > overBought and ind[1] == Highest(ind, divergenceLength + 1)
else
    ind[1] >= 50 and
    ind[1] == Highest(ind, divergenceLength + 1) and
    ind[1] == Highest(ind, divergenceLength + 1)[-divergenceLength + 1];

if pivotTop
then {
    hiBars = 1;
    hiInd = ind[1];
    hiPrice = Max(high[2], Max(high[1], high[0]));
}
else {
    hiBars = hiBars[1] + 1;
    hiInd = hiInd[1];
    hiPrice = hiPrice[1];
}

if ind[1] crosses below overBought
then {
    xDownBars = 1;
    xDowns = xDowns[1] + 1;
}
else {
    xDownBars = xDownBars[1] + 1;
    xDowns = if pivotTop[1] then 0 else xDowns[1];
}

def bearCond;
switch (divergenceType) {
case regular:
    bearCond =
    ind[1] >= HALF and
    ind < ind[1] and
    high[1] == Highest(high, divergenceLength + 1) and
    hiBars[1] > xDownBars[1] and
    xDowns == 1 and
    close < close[1] and
    hiPrice[1] < high[1] and
    hiInd[1] > ind[1];
case reverse:
    bearCond =
    ind[1] >= HALF and
    ind < ind[1] and
#    high[1] == Highest(high, divergenceLength) and
#    hiBars[1] > xDownBars[1] and
#    xDowns == 1 and
    close < close[1] and
    hiPrice[1] > high[1] and hiPrice[1] > high and
    hiInd[1] < ind[1];
}

bearish =
    if
        bearCond
    then
        high[1]
    else
        Double.NaN;
;

bearish.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
bearish.SetDefaultColor(Color.GRAY);
bearish.SetLineWeight(3);
bearish.HideTitle();
bearish.HideBubble();

def countBear = if bearCond[-1] then countBear[1] + 1 else countBear[1];
def recentBear = countBear == HighestAll(countBear);
def secHigh = HighestAll(if bearCond[-1] and recentBear then ind else Double.NaN);
#def firstHigh = highestAll(if bearCond and recentBear and ind[1] == secHigh then hiInd[1] else double.NaN);
def FH_bar = HighestAll(if recentBear and bearCond[-1] and ind == secHigh then GetValue(BarNumber(), hiBars) else Double.NaN);

plot bearTrendline =
if recentBear and
     bearCond[-1] and ind == secHigh
then
    Max(high[1], high[0])
else
#    if pivotTop and hiInd == firstHigh
    if
        FH_bar == BarNumber()
    then
        high
    else
        Double.NaN;
bearTrendline.EnableApproximation();
bearTrendline.SetDefaultColor(Color.RED);
bearTrendline.SetLineWeight(3);
bearTrendline.HideBubble();
bearTrendline.HideTitle();

#Bullish
pivotBottom =
if
    divergenceType == divergenceType.regular
then
    ind[1] < overSold and ind[1] == Lowest(ind, divergenceLength + 1)
else
    ind[1] <= 50 and
    ind[1] == Lowest(ind, divergenceLength + 1) and
    ind[1] == Lowest(ind, divergenceLength + 1)[-divergenceLength + 1];

if pivotBottom
then {
    loBars = 1;
    loInd = ind[1];
    loPrice = Min(low[2], Min(low[1], low[0]));
}
else {
    loBars = loBars[1] + 1;
    loInd = loInd[1];
    loPrice = loPrice[1];
}

if ind[1] crosses above overSold
then {
    xUpBars = 1;
    xUps = xUps[1] + 1;
}
else {
    xUpBars = xUpBars[1] + 1;
    xUps = if pivotBottom[1] then 0 else xUps[1];
}

def bullCond;
switch (divergenceType){
case regular:
    bullCond =
    ind[1] <= HALF and
    ind > ind[1] and 
    low[1] == Lowest(low, divergenceLength + 1) and
    loBars[1] > xUpBars[1] and
    xUps == 1 and
    close > close[1] and
    loPrice[1] > low[1] and
    loInd[1] < ind[1];
case reverse:
    bullCond =
    ind[1] <= HALF and
    ind > ind[1] and 
#    low[1] == Lowest(low, divergenceLength) and
#    loBars[1] > xUpBars[1] and
#    xUps == 1 and
    close > close[1] and
    loPrice[1] < low[1] and loPrice[1] < low and
    loInd[1] > ind[1];
}

bullish =
    if
        bullCond
    then
        low[1]
    else
        Double.NaN;

bullish.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
bullish.SetDefaultColor(Color.GRAY);
bullish.SetLineWeight(3);
bullish.HideTitle();
bullish.HideBubble();

def countBull = if bullCond[-1] then countBull[1] + 1 else countBull[1];
def recentBull = countBull == HighestAll(countBull);
def secLow = HighestAll(if bullCond[-1] and recentBull then ind else Double.NaN);
#def firstLow = highestAll(if bullCond and recentBull and ind[1] == secLow then loInd[1] else double.NaN);
def FL_bar = HighestAll(if recentBull and bullCond[-1] and ind == secLow then GetValue(BarNumber(), loBars) else Double.NaN);

plot bullTrendline =
    if
        recentBull and bullCond[-1] and ind == secLow
    then
        Min(low[1], low[0])
    else
        if
#            pivotBottom and loInd == firstLow
            FL_bar == BarNumber()
        then
            low[0]
        else
            Double.NaN;
bullTrendline.EnableApproximation();
bullTrendline.SetDefaultColor(Color.GREEN);
bullTrendline.SetLineWeight(3);
bullTrendline.HideBubble();
bullTrendline.HideTitle();
LOWER INDICATOR: includes divergence for both sun and moon lines.
Code:
# =========================================================
# Sun_Moon_and_Stars_Oscillator
# Lower indicator companion to the Sun_Moon_and_Stars_Indicator
# "The farther price drifts, the stronger gravity pulls."
# Created by Chewie 3/26/2026
# =========================================================

declare lower;

# =========================
# INPUTS
# =========================
input length       = 25;
input bandMult     = 2.0;
input GravityZone  = 0.38;
input GravityLength = 200;
input showSunSignals  = yes;
input showStars = yes;
input showTrigger  = yes;
input smoothOsc    = 1; 

# =========================
# COLORS
# =========================

DefineGlobalColor("Yellow",      CreateColor(170, 170,  0));
DefineGlobalColor("softDarkRed", CreateColor( 90,   0,   0));
DefineGlobalColor("softRed",     CreateColor(140,  40, 80));
DefineGlobalColor("Red",         CreateColor(175,  60, 190));
DefineGlobalColor("Magenta",     CreateColor(255, 0, 255));
DefineGlobalColor("SoftDarkBlue", CreateColor(  0,  40, 100));
DefineGlobalColor("SoftBlue",    CreateColor( 30, 90, 200));
DefineGlobalColor("Blue",        CreateColor( 70, 160, 255));
DefineGlobalColor("Cyan",        CreateColor(0, 200, 255));

# =========================
# SUN GRAVITY ENGINE
# =========================
def ohlc4   = (open + high + low + close) / 4;
def mid     = HullMovingAvg((high + low) / 2, 160);
def spread  = ohlc4 / mid;
def dev     = StDev(spread, length);
def vol     = ExpAverage(dev, length);
def bandUnit = vol * bandMult;

# Core gravity value
def gravityRaw    = (ohlc4 - mid) / (mid * bandUnit);
def gravitySmooth = ExpAverage(gravityRaw, smoothOsc);

# =========================
# TRIGGER LINE
# =========================
def price          = close;
def smaLength      = 8;
def TatrLength     = 15;
def regLength      = 21;
def atrMult        = 1.25;
def valS           = Average(price, smaLength);
def Tatr           = Average(TrueRange(high, close, low), TatrLength);
def Upper_Band     = valS + atrMult * Tatr;
def Lower_Band     = valS - atrMult * Tatr;
def mid1           = (Upper_Band - Lower_Band) / 2 + Lower_Band;
def regMid         = Inertia(mid1, regLength);
def triggerGravity = (regMid - mid) / (mid * bandUnit);

# =========================
# TREND DIRECTION
# =========================
rec dir =
    if mid > mid[1] then  1
    else if mid < mid[1] then -1
    else dir[1];

# =========================
# GRAVITY ZONE THRESHOLDS
# =========================
# ±GravityZone  = inner gravity core
# ±1            = outer orbit
# ±1.5          = outer orbit edge
# ±2            = escape orbit
# ±2.5          = escape orbit edge
# ±3            = event horizon

def gz  = GravityZone;     # inner core boundary

# =========================
# GRAVITY ZONE CLASSIFICATION
# =========================
def inEventHorizonUp  = gravitySmooth >  3;
def inEscapeUp2       = gravitySmooth >  2   and gravitySmooth <=  3;
def inEscapeUp15      = gravitySmooth >  1.5 and gravitySmooth <=  2;
def inOuterUp         = gravitySmooth >  1   and gravitySmooth <=  1.5;
def inGravityZoneUp   = gravitySmooth >  gz  and gravitySmooth <=  1;
def inCore            = gravitySmooth >= -gz  and gravitySmooth <=  gz;
def inGravityZoneDn   = gravitySmooth < -gz  and gravitySmooth >= -1;
def inOuterDn         = gravitySmooth < -1   and gravitySmooth >= -1.5;
def inEscapeDn15      = gravitySmooth < -1.5 and gravitySmooth >= -2;
def inEscapeDn2       = gravitySmooth < -2   and gravitySmooth >= -3;
def inEventHorizonDn  = gravitySmooth < -3;

# =========================
# GRAVITY OSCILLATOR PLOT
# =========================
plot GravOsc = gravitySmooth;
GravOsc.SetLineWeight(2);
GravOsc.AssignValueColor(
    if inEventHorizonUp  then Color.MAGENTA
    else if inEscapeUp2  then CreateColor(175,  60, 190)
    else if inEscapeUp15 then CreateColor(140,  40, 90)
    else if inOuterUp    then CreateColor(100,  20, 20)
    else if inGravityZoneUp then GlobalColor("Yellow")
    else if inCore          then CreateColor(255, 255,  0)
    else if inGravityZoneDn then GlobalColor("Yellow")
    else if inOuterDn    then CreateColor( 30, 70, 160)
    else if inEscapeDn15 then CreateColor( 60, 110, 190)
    else if inEscapeDn2  then CreateColor( 90, 170, 230)
    else if inEventHorizonDn then Color.CYAN
    else Color.GRAY);

# =========================
# TRIGGER LINE
# =========================
plot TriggerOsc = if showTrigger then triggerGravity else Double.NaN;
TriggerOsc.SetDefaultColor(Color.yellow);
TriggerOsc.SetLineWeight(2);
TriggerOsc.SetPaintingStrategy(PaintingStrategy.LINE);


# =========================
# ZERO LINE  (gravity core — color = trend direction)
# =========================
plot ZeroLine = 0;
ZeroLine.SetLineWeight(2);
ZeroLine.AssignValueColor(if dir == 1 then Color.GREEN else Color.RED);

# =========================
# INNER GRAVITY ZONE LINES  (±GravityZone)
# =========================
plot GZoneUp = gz;
plot GZoneDn = -gz;
GZoneUp.SetDefaultColor(GlobalColor("Yellow"));
GZoneDn.SetDefaultColor(GlobalColor("Yellow"));
GZoneUp.SetLineWeight(1);
GZoneDn.SetLineWeight(1);

# =========================
# GRAVITY LEVEL LINES
# =========================
plot Lp1  =  1;
plot Lm1  = -1;
plot Lp15 =  1.5;
plot Lm15 = -1.5;
plot Lp2  =  2;
plot Lm2  = -2;
plot Lp25 =  2.5;
plot Lm25 = -2.5;
plot Lp3  =  3;
plot Lm3  = -3;

Lp1.SetDefaultColor(GlobalColor("softRed"));
Lm1.SetDefaultColor(GlobalColor("SoftBlue"));
Lp15.SetDefaultColor(GlobalColor("softRed"));
Lm15.SetDefaultColor(GlobalColor("SoftBlue"));
Lp2.SetDefaultColor(GlobalColor("Red"));
Lm2.SetDefaultColor(GlobalColor("Blue"));
Lp25.SetDefaultColor(GlobalColor("Magenta"));
Lm25.SetDefaultColor(GlobalColor("Cyan"));
Lp3.SetDefaultColor(Color.MAGENTA);
Lm3.SetDefaultColor(Color.CYAN);

AddCloud(Lp1, Lp15, GlobalColor("softDarkRed"), GlobalColor("softDarkRed"));
AddCloud(Lp15, Lp2, GlobalColor("softRed"), GlobalColor("softRed"));
AddCloud(Lp2, Lp25, GlobalColor("Red"), GlobalColor("Red"));
AddCloud(Lp25, Lp3, GlobalColor("Magenta"), GlobalColor("Magenta"));

AddCloud(Lm1, Lm15, GlobalColor("softDarkBlue"), GlobalColor("softDarkBlue"));
AddCloud(Lm15, Lm2, GlobalColor("softBlue"), GlobalColor("softBlue"));
AddCloud(Lm2, Lm25, GlobalColor("BLUE"), GlobalColor("BLUE"));
AddCloud(Lm25, Lm3, GlobalColor("cyan"), GlobalColor("cyan"));

Lp1.SetLineWeight(1);
Lm1.SetLineWeight(1);
Lp15.SetLineWeight(1);
Lm15.SetLineWeight(1);
Lp2.SetLineWeight(1);
Lm2.SetLineWeight(1);
Lp25.SetLineWeight(1);
Lm25.SetLineWeight(1);
Lp3.SetLineWeight(1);
Lm3.SetLineWeight(1);

# =========================
# SUN SIGNALS
# =========================
def extremeHigh = ohlc4 > mid * (1 + vol * bandMult * 2);
def extremeLow  = ohlc4 < mid * (1 - vol * bandMult * 2);
def revertShort = extremeHigh and gravitySmooth > 0;
def revertLong  = extremeLow  and gravitySmooth < 0;

plot BuySunSignal = if showSunSignals and revertLong
    then gravitySmooth - 0.15
    else Double.NaN;
BuySunSignal.SetPaintingStrategy(PaintingStrategy.POINTS);
BuySunSignal.SetDefaultColor(Color.CYAN);
BuySunSignal.SetLineWeight(1);

plot SellSunSignal = if showSunSignals and revertShort
    then gravitySmooth + 0.15
    else Double.NaN;
SellSunSignal.SetPaintingStrategy(PaintingStrategy.POINTS);
SellSunSignal.SetDefaultColor(Color.MAGENTA);
SellSunSignal.SetLineWeight(1);

# =========================
#MOON GRAVITY
# =========================

def lengthi = 24;
def h_param = 8.0;
def r_param = 2.0;
def mult_inner = 1.8;
def srci = (high + low + close) / 3;

def numerator =
    fold k1 = 0 to lengthi
    with acc1 = 0
    do acc1 +
        srci[k1] *
        Power(
            1 + Sqr(k1) / (2 * r_param * Sqr(h_param)),
            -r_param);

def denominator =
    fold k2 = 0 to lengthi
    with acc2 = 0
    do acc2 +
        Power(
            1 + Sqr(k2) / (2 * r_param * Sqr(h_param)),
            -r_param);

def Moon = numerator / denominator;

def errorSum =
    fold k3 = 0 to lengthi
    with acc3 = 0
    do acc3 + AbsValue(srci[k3] - Moon);

def meanDeviation = errorSum / lengthi;
def volatility = (meanDeviation + ATR(lengthi)) / 2;

# =========================
# MOON LEVELS
# =========================
def MoonUP = Moon + volatility * mult_inner;
def MoonDN = Moon - volatility * mult_inner;

# =========================
# STAR SIGNALS
# =========================

def b15u  = mid * (1 + vol * bandMult * 1.5);
def b15d  = mid * (1 - vol * bandMult * 1.5);
def Moon_Band_Sell = high > MoonUP and high > b15u;
def Moon_Band_Buy  = low  < MoonDN and low  < b15d;

plot StarBuySignal = if showStars and Moon_Band_Buy
    then gravitySmooth - 0.30
    else Double.NaN;
StarBuySignal.SetPaintingStrategy(PaintingStrategy.POINTS);
StarBuySignal.SetDefaultColor(Color.white);
StarBuySignal.SetLineWeight(1);

plot StarSellSignal = if showStars and Moon_Band_Sell
    then gravitySmooth + 0.30
    else Double.NaN;
StarSellSignal.SetPaintingStrategy(PaintingStrategy.POINTS);
StarSellSignal.SetDefaultColor(Color.white);
StarSellSignal.SetLineWeight(1);


# =========================
# LABELS
# =========================
def gravNow   = gravitySmooth;
def trigNow   = triggerGravity;
def crossUp   = gravNow > trigNow and gravNow[1] <= trigNow[1];
def crossDown = gravNow < trigNow and gravNow[1] >= trigNow[1];

AddLabel(yes,
    if inEventHorizonUp  then "OSC: " + Round(gravNow, 2) + "  EVENT HORIZON"
    else if inEscapeUp2  then "OSC: " + Round(gravNow, 2) + "  ESCAPE ORBIT +"
    else if inEscapeUp15 then "OSC: " + Round(gravNow, 2) + "  OUTER ORBIT +"
    else if inOuterUp    then "OSC: " + Round(gravNow, 2) + "  OUTER ORBIT"
    else if inGravityZoneUp then "OSC: " + Round(gravNow, 2) + "  GRAVITY ZONE +"
    else if inCore          then "OSC: " + Round(gravNow, 2) + "  CORE"
    else if inGravityZoneDn then "OSC: " + Round(gravNow, 2) + "  GRAVITY ZONE -"
    else if inOuterDn    then "OSC: " + Round(gravNow, 2) + "  OUTER ORBIT -"
    else if inEscapeDn15 then "OSC: " + Round(gravNow, 2) + "  OUTER ORBIT -"
    else if inEscapeDn2  then "OSC: " + Round(gravNow, 2) + "  ESCAPE ORBIT -"
    else if inEventHorizonDn then "OSC: " + Round(gravNow, 2) + "  EVENT HORIZON"
    else "",
    if inEventHorizonUp  then Color.MAGENTA
    else if inEscapeUp2  then GlobalColor("Red")
    else if inEscapeUp15 then GlobalColor("softRed")
    else if inOuterUp    then GlobalColor("softRed")
    else if inGravityZoneUp then GlobalColor("Yellow")
    else if inCore          then Color.GRAY
    else if inGravityZoneDn then GlobalColor("Yellow")
    else if inOuterDn    then GlobalColor("SoftBlue")
    else if inEscapeDn15 then GlobalColor("SoftBlue")
    else if inEscapeDn2  then GlobalColor("Blue")
    else if inEventHorizonDn then Color.CYAN
    else Color.GRAY);

AddLabel(showTrigger,
    "TRIG: " + Round(trigNow, 2) +
    (if crossUp   then "  [X UP]"
     else if crossDown then "  [X DN]"
     else ""),
    if crossUp   then Color.GREEN
    else if crossDown then Color.RED
    else Color.YELLOW);


# =========================
# MOON_OSC
# =========================
def mult_in = 0.65;
def mult_mid = 1.95;
def mult_outer = 2.6;

# =========================
# COLORS
# =========================
DefineGlobalColor("Bear", color.red);
DefineGlobalColor("Bull", color.green);

# =========================
# SOURCE
# =========================
def src = (high + low + close) / 3;
def osc = (src - moon) / volatility;

# =========================
# OSCILLATOR PLOT
# =========================
plot Oscillator = osc;
Oscillator.AssignValueColor(
    if osc > 0 then GlobalColor("Bull") else GlobalColor("Bear")
);
Oscillator.SetLineWeight(2);

# =========================
# HISTOGRAM
# =========================
plot GravHist = gravitySmooth;
GravHist.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
GravHist.SetLineWeight(5);
GravHist.AssignValueColor(
    if inEventHorizonUp  then Color.MAGENTA
    else if inEscapeUp2  then CreateColor(175,  60, 190)
    else if inEscapeUp15 then CreateColor(140,  40, 90)
    else if inOuterUp    then CreateColor(100,  20, 20)
    else if inGravityZoneUp then GlobalColor("Yellow")
    else if inCore          then CreateColor(255, 255,  0)
    else if inGravityZoneDn then GlobalColor("Yellow")
    else if inOuterDn    then CreateColor( 30, 70, 160)
    else if inEscapeDn15 then CreateColor( 60, 110, 190)
    else if inEscapeDn2  then CreateColor( 90, 170, 230)
    else if inEventHorizonDn then Color.CYAN
    else CreateColor(50, 50, 50));

# =========================
# SUN DIVERGENCE
# =========================

input divergenceLength = 55; #hint divergenceLength: The number of bars used to calculate divergences.

input divergenceType = {default regular, reverse}; #hint divergenceType: The type of divergence. A regular divergence is when price is making higher highs (or lower lows), while the indicator is making lower highs (or higher lows). A reverse divergence (also called a hidden divergence) is when the indicator is making higher highs (or lower lows), while price is making lower highs (or higher lows).
#Hint: The output of this indicator is for informational and educational use only, is not an investment recommendation or advice, and should not be relied upon in making the decision to buy or sell a security or pursue a particular investment strategy.

def xDownBars;
def xUpBars;
def xDowns;
def xUps;
def hiBars;
def loBars;
def pivotTop;
def pivotBottom;
def hiInd;
def loInd;
def hiPrice;
def loPrice;
plot bearishd;
plot bullishd;

def K = GravOsc;
def Over_Boughta = 1.0;
def Over_Solda = -1.0;

#K.SetDefaultColor(color.white);
#K.SetLineWeight(2);

def ind;
ind = K;

# Bearish
pivotTop =
if
    divergenceType == divergenceType.regular
then
    ind[1] > Over_Boughta and ind[1] == Highest(ind, divergenceLength + 1)
else
    ind[1] >= 50 and
    ind[1] == Highest(ind, divergenceLength + 1) and
    ind[1] == Highest(ind, divergenceLength + 1)[-divergenceLength + 1];

if pivotTop
then {
    hiBars = 1;
    hiInd = ind[1];
    hiPrice = Max(high[2], Max(high[1], high[0]));
}
else {
    hiBars = hiBars[1] + 1;
    hiInd = hiInd[1];
    hiPrice = hiPrice[1];
}

if ind[1] crosses below  Over_Boughta
then {
    xDownBars = 1;
    xDowns = xDowns[1] + 1;
}
else {
    xDownBars = xDownBars[1] + 1;
    xDowns = if pivotTop[1] then 0 else xDowns[1];
}

def bearCond;
switch (divergenceType) {
case regular:
    bearCond =
    ind[1] >= ZeroLine and
    ind < ind[1] and
    high[1] == Highest(high, divergenceLength + 1) and
    hiBars[1] > xDownBars[1] and
    xDowns == 1 and
    close < close[1] and
    hiPrice[1] < high[1] and
    hiInd[1] > ind[1];
case reverse:
    bearCond =
    ind[1] >= ZeroLine and
    ind < ind[1] and
#    high[1] == Highest(high, divergenceLength) and
#    hiBars[1] > xDownBars[1] and
#    xDowns == 1 and
    close < close[1] and
    hiPrice[1] > high[1] and hiPrice[1] > high and
    hiInd[1] < ind[1];
}

bearishd =
    if
        bearCond
    then
        ind[1]
    else
        Double.NaN;
;

bearishd.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
bearishd.SetDefaultColor(Color.RED);
bearishd.SetLineWeight(3);
bearishd.HideTitle();
bearishd.HideBubble();

def countBear = if bearCond[-1] then countBear[1] + 1 else countBear[1];
def recentBear = countBear == HighestAll(countBear);
def secHigh = HighestAll(if bearCond[-1] and recentBear then ind else Double.NaN);
#def firstHigh = highestAll(if bearCond and recentBear and ind[1] == secHigh then hiInd[1] else double.NaN);
def FH_bar = HighestAll(if recentBear and bearCond[-1] and ind == secHigh then GetValue(BarNumber(), hiBars) else Double.NaN);

plot bearTrendline =
if
    recentBear and bearCond[-1] and ind == secHigh
then
    Max(ind[1], ind[0])
else
#    if pivotTop and hiInd == firstHigh
    if
        FH_bar == BarNumber()
    then
        ind
    else
        Double.NaN;
bearTrendline.EnableApproximation();
bearTrendline.SetDefaultColor(Color.RED);
bearTrendline.SetLineWeight(4);
bearTrendline.HideBubble();
bearTrendline.HideTitle();

#Bullish
pivotBottom =
if
    divergenceType == divergenceType.regular
then
    ind[1] < Over_Solda and ind[1] == Lowest(ind, divergenceLength + 1)
else
    ind[1] <= 50 and
    ind[1] == Lowest(ind, divergenceLength + 1) and
    ind[1] == Lowest(ind, divergenceLength + 1)[-divergenceLength + 1];

if pivotBottom
then {
    loBars = 1;
    loInd = ind[1];
    loPrice = Min(low[2], Min(low[1], low[0]));
}
else {
    loBars = loBars[1] + 1;
    loInd = loInd[1];
    loPrice = loPrice[1];
}

if ind[1] crosses above Over_Solda
then {
    xUpBars = 1;
    xUps = xUps[1] + 1;
}
else {
    xUpBars = xUpBars[1] + 1;
    xUps = if pivotBottom[1] then 0 else xUps[1];
}

def bullCond;
switch (divergenceType){
case regular:
    bullCond =
    ind[1] <= ZeroLine and
    ind > ind[1] and 
    low[1] == Lowest(low, divergenceLength + 1) and
    loBars[1] > xUpBars[1] and
    xUps == 1 and
    close > close[1] and
    loPrice[1] > low[1] and
    loInd[1] < ind[1];
case reverse:
    bullCond =
    ind[1] <= ZeroLine and
    ind > ind[1] and 
#    low[1] == Lowest(low, divergenceLength) and
#    loBars[1] > xUpBars[1] and
#    xUps == 1 and
    close > close[1] and
    loPrice[1] < low[1] and loPrice[1] < low and
    loInd[1] > ind[1];
}

bullishd =
    if
        bullCond
    then
        ind[1]
    else
        Double.NaN;

bullishd.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
bullishd.SetDefaultColor(Color.GREEN);
bullishd.SetLineWeight(3);
bullishd.HideTitle();
bullishd.HideBubble();

def countBull = if bullCond[-1] then countBull[1] + 1 else countBull[1];
def recentBull = countBull == HighestAll(countBull);
def secLow = HighestAll(if bullCond[-1] and recentBull then ind else Double.NaN);
#def firstLow = highestAll(if bullCond and recentBull and ind[1] == secLow then loInd[1] else double.NaN);
def FL_bar = HighestAll(if recentBull and bullCond[-1] and ind == secLow then GetValue(BarNumber(), loBars) else Double.NaN);

plot bullTrendline =
    if
        recentBull and bullCond[-1] and ind == secLow
    then
        Min(ind[1], ind[0])
    else
        if
#            pivotBottom and loInd == firstLow
            FL_bar == BarNumber()
        then
            ind[0]
        else
            Double.NaN;
bullTrendline.EnableApproximation();
bullTrendline.SetDefaultColor(Color.GREEN);
bullTrendline.SetLineWeight(4);
bullTrendline.HideBubble();
bullTrendline.HideTitle();



# =========================
# MOON DIVERGENCE
# =========================
def divergenceLength2 = 20; #hint divergenceLength: The number of bars used to calculate divergences.

#input divergenceType = {default regular, reverse}; #hint divergenceType: The type of divergence. A regular divergence is when price is making higher highs (or lower lows), while the indicator is making lower highs (or higher lows). A reverse divergence (also called a hidden divergence) is when the indicator is making higher highs (or lower lows), while price is making lower highs (or higher lows).
#Hint: The output of this indicator is for informational and educational use only, is not an investment recommendation or advice, and should not be relied upon in making the decision to buy or sell a security or pursue a particular investment strategy.

def xDownBars2;
def xUpBars2;
def xDowns2;
def xUps2;
def hiBars2;
def loBars2;
def pivotTop2;
def pivotBottom2;
def hiInd2;
def loInd2;
def hiPrice2;
def loPrice2;
plot bearishd2;
plot bullishd2;

def L = Oscillator;
def Over_Boughta2 = 1.3;
def Over_Solda2 = -1.3;



def ind2;
ind2 = L;

# Bearish
pivotTop2 =
if
    divergenceType == divergenceType.regular
then
    ind2[1] > over_Boughta2 and ind2[1] == Highest(ind2, divergenceLength2 + 1)
else
    ind2[1] >= 50 and
    ind2[1] == highest(ind2, divergenceLength2 + 1) and
    ind2[1] == highest(ind2, divergenceLength2+1)[-divergenceLength2+1];

if pivotTop2
then {
    hiBars2 = 1;
    hiInd2 = ind2[1];
    hiPrice2 = max(high[2], max(high[1], high[0]));
}
else {
    hiBars2 = hiBars2[1] + 1;
    hiInd2 = hiInd2[1];
    hiPrice2 = hiPrice2[1];
}

if ind2[1] crosses below  Over_Boughta2
then {
    xDownBars2 = 1;
    xDowns2 = xDowns2[1] + 1;
}
else {
    xDownBars2 = xDownBars2[1] + 1;
    xDowns2 = if pivotTop2[1] then 0 else xDowns2[1];
}

def bearCond2;
switch (divergenceType) {
case regular:
bearCond2 =
    ind2[1] >= ZeroLine and
    ind2 < ind2[1] and
    high[1] == Highest(high, divergenceLength2 + 1) and
    hiBars2[1] > xDownBars2[1] and
    xDowns2 == 1 and
    close < close[1] and
    hiPrice2[1] < high[1] and
    hiInd2[1] > ind2[1];
case reverse:
bearCond2 =
    ind2[1] >= ZeroLine and
    ind2 < ind2[1] and
#    high[1] == Highest(high, divergenceLength) and
#    hiBars[1] > xDownBars[1] and
#    xDowns == 1 and
    close < close[1] and
    hiPrice2[1] > high[1] and hiPrice2[1] > high and
    hiInd2[1] < ind2[1];}

bearishd2 =
    if
        bearCond2
    then
        ind2[1]
    else
        Double.NaN;;

bearishd2.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
bearishd2.SetDefaultColor(Color.light_gray);
bearishd2.setLineWeight(2);
bearishd2.hideTitle();
bearishd2.hideBubble();

def countBear2 = if bearCond2[-1] then countBear2[1] + 1 else countBear2[1];
def recentBear2 = countBear2 == HighestAll(countBear2);
def secHigh2 = highestAll(if bearCond2[-1] and recentBear2 then ind2 else Double.NaN);
#def firstHigh = highestAll(if bearCond and recentBear and ind[1] == secHigh then hiInd[1] else double.NaN);
def FH_bar2 = highestAll(if recentBear2 and bearCond2[-1] and ind2 == secHigh2 then getvalue(barNumber(), hibars2) else double.NaN);

plot bearTrendline2 =
if
    recentBear2 and bearCond2[-1] and ind2 == secHigh2
then
    max(ind2[1], ind2[0])
else
#    if pivotTop and hiInd == firstHigh
    if
        FH_bar2 == barNumber()
    then
        ind2
    else
        double.NaN;
bearTrendline2.EnableApproximation();
bearTrendline2.setDefaultColor(color.RED);
bearTrendline2.setLineWeight(4);
bearTrendline2.hideBubble();
bearTrendline2.hideTitle();

#Bullish
pivotBottom2 =
if
    divergenceType == divergenceType.regular
then
    ind2[1] < over_Solda2 and ind2[1] == lowest(ind2, divergenceLength2 + 1)
else
    ind2[1] <= 50 and
    ind2[1] == lowest(ind2, divergenceLength2+1) and
    ind2[1] == lowest(ind2, divergenceLength2+1)[-divergenceLength2+1];

if pivotBottom2
then {
    loBars2 = 1;
    loInd2 = ind2[1];
    loPrice2 = min(low[2], min(low[1], low[0]));
}
else {
    loBars2 = loBars2[1] + 1;
    loInd2 = loInd2[1];
    loPrice2 = loPrice2[1];
}

if ind2[1] crosses above over_Solda2
then {
    xUpBars2 = 1;
    xUps2 = xUps2[1] + 1;
}
else {
    xUpBars2 = xUpBars2[1] + 1;
    xUps2 = if pivotBottom2[1] then 0 else xUps2[1];
}

def bullCond2;
switch (divergenceType){
case regular:
bullCond2 =
    ind2[1] <= ZeroLine and
    ind2 > ind2[1] and 
    low[1] == Lowest(low, divergenceLength2 + 1) and
    loBars2[1] > xUpBars2[1] and
    xUps2 == 1 and
    close > close[1] and
    loPrice2[1] > low[1] and
    loInd2[1] < ind2[1];
case reverse:
bullCond2 =
    ind2[1] <= ZeroLine and
    ind2 > ind2[1] and 
#    low[1] == Lowest(low, divergenceLength) and
#    loBars[1] > xUpBars[1] and
#    xUps == 1 and
    close > close[1] and
    loPrice2[1] < low[1] and loPrice2[1] < low and
    loInd2[1] > ind2[1];}

bullishd2 =
    if
        bullCond2
    then
        ind2[1]
    else
        Double.NaN;

bullishd2.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
bullishd2.SetDefaultColor(Color.light_gray);
bullishd2.setLineWeight(2);
bullishd2.HideTitle();
bullishd2.HideBubble();

def countBull2 = if bullCond2[-1] then countBull2[1] + 1 else countBull2[1];
def recentBull2 = countBull2 == HighestAll(countBull2);
def secLow2 = highestAll(if bullCond2[-1] and recentBull2 then ind2 else Double.NaN);
#def firstLow = highestAll(if bullCond and recentBull and ind[1] == secLow then loInd[1] else double.NaN);
def FL_bar2 = highestAll(if recentBull2 and bullCond2[-1] and ind2 == secLow2 then getvalue(barNumber(), lobars2) else double.NaN);

plot bullTrendline2 =
    if
        recentBull2 and bullCond2[-1] and ind2 == secLow2
    then
        min(ind2[1], ind2[0])
    else
        if
#            pivotBottom and loInd == firstLow
            FL_bar2 == barNumber()
        then
            ind2[0]
        else
            double.NaN;
bullTrendline2.EnableApproximation();
bullTrendline2.setDefaultColor(color.GREEN);
bullTrendline2.setLineWeight(4);
bullTrendline2.hideBubble();
bullTrendline2.hideTitle();
 
Last edited by a moderator:

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

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
803 Online
Create Post

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