# Price Memory Trend – LOWER Oscillator
# Assembled by Chewie 2/4/2026
# Lower companion to PMT Bands
declare lower;
# =========================
# INPUTS
# =========================
input memoryStrength = 0.12;
input memoryDecay = 0.96;
input devLen = 50;
input bandMult = 1.6;
input atrLength = 50;
input atrMult = 1.3;
# =========================
# COLORS
# =========================
DefineGlobalColor("Bull", CreateColor(0, 255, 170));
DefineGlobalColor("Bear", CreateColor(255, 0, 255));
DefineGlobalColor("Neutral", Color.GRAY);
# =========================
# PRICE MEMORY ENGINE
# =========================
def price = close;
rec memory =
if BarNumber() == 1 then price
else memory[1] + (price - memory[1]) * memoryStrength;
def base = memory * memoryDecay + price * (1 - memoryDecay);
# =========================
# ADAPTIVE DEVIATION
# =========================
def dev = AbsValue(price - base);
def avgDev = ExpAverage(dev, devLen);
def rawUpper = base + avgDev * bandMult;
def rawLower = base - avgDev * bandMult;
# =========================
# TREND STATE
# =========================
def bullCond1 = price crosses above rawUpper;
def bearCond1 = price crosses below rawLower;
rec trendState =
if BarNumber() == 1 then 0
else if bullCond1 then 1
else if bearCond1 then -1
else trendState[1];
# =========================
# TRAILING CORE
# =========================
rec trailLower =
if BarNumber() == 1 then rawLower
else if trendState == 1 then Max(rawLower, trailLower[1])
else rawLower;
rec trailUpper =
if BarNumber() == 1 then rawUpper
else if trendState == -1 then Min(rawUpper, trailUpper[1])
else rawUpper;
def core =
if trendState == 1 then trailLower
else if trendState == -1 then trailUpper
else base;
# =========================
# NORMALIZED OSCILLATOR
# =========================
def atr = Average(TrueRange(high, close, low), atrLength);
# Distance from PMT core, normalized by ATR
def osc =
if atr != 0 then
(price - core) / (atr * atrMult)
else 0;
# =========================
# PLOTS
# =========================
plot PMT = osc;
PMT.AssignValueColor(
if trendState == 1 then GlobalColor("Bull")
else if trendState == -1 then GlobalColor("Bear")
else GlobalColor("Neutral")
);
PMT.SetLineWeight(3);
plot ZeroLine = 0;
plot one = 1.0;
plot none = -1.0;
plot two = 2.0;
plot ntwo = -2.0;
plot three = 3.0;
plot nthree = -3.0;
ZeroLine.SetDefaultColor(Color.yellow);
one.SetDefaultColor(Color.dark_red);
none.SetDefaultColor(Color.dark_green);
two.SetDefaultColor(Color.red);
ntwo.SetDefaultColor(Color.green);
three.SetDefaultColor(Color.magenta);
nthree.SetDefaultColor(Color.cyan);
# =========================
# BULL / BEAR ZONES
# =========================
AddCloud(PMT, ZeroLine,
GlobalColor("Bull"), Color.CURRENT);
AddCloud(ZeroLine, PMT,
GlobalColor("Bear"), Color.CURRENT);
AddCloud(2, 3, color.current, GlobalColor("Bear"));
AddCloud(-3, -2, color.current, GlobalColor("Bull"));
def bullcore1 = if trendState == 1 then -2 else double.nan;
def bullouter1 = if trendState == 1 then -3 else double.nan;
def bearcore1 = if trendState == -1 then 2 else double.nan;
def bearouter1 = if trendState == -1 then 3 else double.nan;
AddCloud(bullCore1, bullouter1,
color.green, GlobalColor("Bull"));
AddCloud(bearCore1, bearouter1,
GlobalColor("Bear"), color.red);
# DIVERGENCE
input divergenceLength = 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 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 = PMT;
def Over_Boughta = 1.5;
def Over_Solda = -1.5;
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.CYAN);
bearishd.setLineWeight(2);
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.CYAN);
bullishd.setLineWeight(2);
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();