IPM-Liquidity-divergences-MAIN (Upper)
# Use this on your main price chart-Copy and paste from declare Upper (Entire code)

declare upper;

input fastLength = 5;
input slowLength = 21;
input atrLength = 14;
input volumeLength = 20;
input smoothLength = 5;
input absorptionThreshold = 1.20;
input signalThreshold = 15.0;
input showSignals = yes;
input paintBars = no;

# ------------------------------------------------------------
# PRICE STRUCTURE
# ------------------------------------------------------------
def c = close;
def o = open;
def h = high;
def l = low;
def v = volume;

def barRange = Max(h - l, TickSize());
def trueRange = TrueRange(h, c, l);
def atr = Average(trueRange, atrLength);

# ------------------------------------------------------------
# PRESSURE & IMBALANCE
# ------------------------------------------------------------
def closePos = if barRange != 0 then (c - l) / barRange else 0.5;
def buyPressureRaw = v * closePos;
def sellPressureRaw = v * (1 - closePos);

def buyPressure = ExpAverage(buyPressureRaw, fastLength);
def sellPressure = ExpAverage(sellPressureRaw, fastLength);

def pressureDelta = buyPressure - sellPressure;
def pressureTotal = buyPressure + sellPressure;
def imbalancePct = if pressureTotal != 0 then 100 * pressureDelta / pressureTotal else 0;
def imbalanceSmoothed = ExpAverage(imbalancePct, smoothLength);

# ------------------------------------------------------------
# ABSORPTION ENGINE
# ------------------------------------------------------------
def priceChange = c - c[1];
def absPriceChange = AbsValue(priceChange);
def normalizedPriceMove = if atr != 0 then absPriceChange / atr else 0;

def avgVolume = Average(v, volumeLength);
def volumeRatio = if avgVolume != 0 then v / avgVolume else 1;

def sellDominant = sellPressure > buyPressure;
def buyDominant = buyPressure > sellPressure;

def sellPressureRatio = if pressureTotal != 0 then sellPressure / pressureTotal else 0;
def buyPressureRatio = if pressureTotal != 0 then buyPressure / pressureTotal else 0;

def bearishEffort = sellPressureRatio * volumeRatio;
def bullishEffort = buyPressureRatio * volumeRatio;

def bullishAbsorptionRaw = if priceChange < 0 and sellDominant then (bearishEffort / Max(normalizedPriceMove, 0.01)) else 0;
def bearishAbsorptionRaw = if priceChange > 0 and buyDominant then (bullishEffort / Max(normalizedPriceMove, 0.01)) else 0;

def bullishAbsorption = ExpAverage(bullishAbsorptionRaw, smoothLength);
def bearishAbsorption = ExpAverage(bearishAbsorptionRaw, smoothLength);

def bullishWeightRaw = if priceChange <= 0 then (AbsValue(imbalancePct) * volumeRatio * bullishAbsorption) else 0;
def bearishWeightRaw = if priceChange >= 0 then (AbsValue(imbalancePct) * volumeRatio * bearishAbsorption) else 0;

def bullishWeight = ExpAverage(bullishWeightRaw, smoothLength);
def bearishWeight = ExpAverage(bearishWeightRaw, smoothLength);

# ------------------------------------------------------------
# SIGNAL LOGIC (Circle Trigger)
# ------------------------------------------------------------
def imbalanceRising = imbalanceSmoothed > imbalanceSmoothed[1];
def imbalanceFalling = imbalanceSmoothed < imbalanceSmoothed[1];

def bullishDivergence = priceChange < 0 and imbalanceRising;
def bearishDivergence = priceChange > 0 and imbalanceFalling;

def bullishAbsorptionSignal = bullishDivergence and bullishAbsorption > absorptionThreshold and bullishWeight > signalThreshold;
def bearishAbsorptionSignal = bearishDivergence and bearishAbsorption > absorptionThreshold and bearishWeight > signalThreshold;

# ------------------------------------------------------------
# PLOTS (Circles on Price)
# ------------------------------------------------------------

plot BullCircle = if showSignals and bullishAbsorptionSignal then l - (atr * 0.5) else Double.NaN;
BullCircle.SetPaintingStrategy(PaintingStrategy.POINTS);
BullCircle.SetDefaultColor(Color.GREEN);
BullCircle.SetLineWeight(5);

plot BearCircle = if showSignals and bearishAbsorptionSignal then h + (atr * 0.5) else Double.NaN;
BearCircle.SetPaintingStrategy(PaintingStrategy.POINTS);
BearCircle.SetDefaultColor(Color.RED);
BearCircle.SetLineWeight(5);

# Optional Bar Coloring
AssignPriceColor(if !paintBars then Color.CURRENT else if bullishAbsorptionSignal then Color.GREEN else if bearishAbsorptionSignal then Color.RED else Color.CURRENT);
