input debug = yes;
# =========================
# Three fixed window sets
# =========================
input fdiShort = 50;
input fdiMed = 100;
input fdiLong = 150;
input vrShort = 10;
input vrMed = 20;
input vrLong = 30;
input rsqShort = 20;
input rsqMed = 50;
input rsqLong = 80;
# =========================
# Efficiency Ratio (ER)
# =========================
def chgER = AbsValue(close - close[20]);
def noiseER = Sum(AbsValue(close - close[1]), 20);
def ER = if noiseER != 0 then chgER / noiseER else 0;
# =========================
# FDI (short/med/long)
# =========================
def HHs = Highest(high, fdiShort);
def LLs = Lowest(low, fdiShort);
def Rs = HHs - LLs;
def Ss = Sum(AbsValue(close - close[1]), fdiShort);
def FDI_s_raw = if Rs != 0 then Log(Ss / Rs) / Log(fdiShort) else 1.5;
def FDI_s = Min(2.0, Max(1.0, FDI_s_raw));
def HHm = Highest(high, fdiMed);
def LLm = Lowest(low, fdiMed);
def Rm = HHm - LLm;
def Sm = Sum(AbsValue(close - close[1]), fdiMed);
def FDI_m_raw = if Rm != 0 then Log(Sm / Rm) / Log(fdiMed) else 1.5;
def FDI_m = Min(2.0, Max(1.0, FDI_m_raw));
def HHl = Highest(high, fdiLong);
def LLl = Lowest(low, fdiLong);
def Rl = HHl - LLl;
def Sl = Sum(AbsValue(close - close[1]), fdiLong);
def FDI_l_raw = if Rl != 0 then Log(Sl / Rl) / Log(fdiLong) else 1.5;
def FDI_l = Min(2.0, Max(1.0, FDI_l_raw));
# =========================
# VR (short/med/long)
# =========================
def r1 = close - close[1];
def rNs = close - close[vrShort];
def var1s = Average(r1 * r1, vrShort);
def varNs = Average(rNs * rNs, vrShort);
def VR_s_raw = if var1s != 0 then varNs / (vrShort * var1s) else 1;
def VR_s = Min(2.0, Max(0.5, VR_s_raw));
def rNm = close - close[vrMed];
def var1m = Average(r1 * r1, vrMed);
def varNm = Average(rNm * rNm, vrMed);
def VR_m_raw = if var1m != 0 then varNm / (vrMed * var1m) else 1;
def VR_m = Min(2.0, Max(0.5, VR_m_raw));
def rNl = close - close[vrLong];
def var1l = Average(r1 * r1, vrLong);
def varNl = Average(rNl * rNl, vrLong);
def VR_l_raw = if var1l != 0 then varNl / (vrLong * var1l) else 1;
def VR_l = Min(2.0, Max(0.5, VR_l_raw));
# =========================
# RSQ (short/med/long)
# =========================
def avgS = Average(close, rsqShort);
def devS = close - avgS;
def varS = Average(devS * devS, rsqShort);
def covS = Average(devS * (close - close[1]), rsqShort);
def RSQ_s = if varS != 0 then Sqr(covS / varS) else 0;
def avgM = Average(close, rsqMed);
def devM = close - avgM;
def varM = Average(devM * devM, rsqMed);
def covM = Average(devM * (close - close[1]), rsqMed);
def RSQ_m = if varM != 0 then Sqr(covM / varM) else 0;
def avgL = Average(close, rsqLong);
def devL = close - avgL;
def varL = Average(devL * devL, rsqLong);
def covL = Average(devL * (close - close[1]), rsqLong);
def RSQ_l = if varL != 0 then Sqr(covL / varL) else 0;
# =========================
# Option A: TrendScore – RangeScore selection
# =========================
# SHORT window scores
def trendScore_s =
(ER > 0.30) +
(FDI_s < 1.55) +
(RSQ_s > 0.30) +
(VR_s > 1.05);
def rangeScore_s =
(ER < 0.20) +
(FDI_s > 1.60) +
(RSQ_s < 0.20) +
(VR_s < 0.95);
def signal_s = trendScore_s - rangeScore_s;
# MEDIUM window scores
def trendScore_m =
(ER > 0.30) +
(FDI_m < 1.55) +
(RSQ_m > 0.30) +
(VR_m > 1.05);
def rangeScore_m =
(ER < 0.20) +
(FDI_m > 1.60) +
(RSQ_m < 0.20) +
(VR_m < 0.95);
def signal_m = trendScore_m - rangeScore_m;
# LONG window scores
def trendScore_l =
(ER > 0.30) +
(FDI_l < 1.55) +
(RSQ_l > 0.30) +
(VR_l > 1.05);
def rangeScore_l =
(ER < 0.20) +
(FDI_l > 1.60) +
(RSQ_l < 0.20) +
(VR_l < 0.95);
def signal_l = trendScore_l - rangeScore_l;
# =========================
# Window Selection
# =========================
def useShort = signal_s > signal_m and signal_s > signal_l;
def useMed = signal_m > signal_s and signal_m > signal_l;
def useLong = signal_l > signal_s and signal_l > signal_m;
# Tie-breaker: MED if equal
def FDI = if useShort then FDI_s else if useMed then FDI_m else FDI_l;
def VR = if useShort then VR_s else if useMed then VR_m else VR_l;
def RSQ = if useShort then RSQ_s else if useMed then RSQ_m else RSQ_l;
# =========================
# Slope
# =========================
def slope = close - close[1];
# =========================
# Trend / Range Scores (final)
# =========================
def trendScore =
(ER > 0.30) +
(FDI < 1.55) +
(RSQ > 0.30) +
(VR > 1.05);
def rangeScore =
(ER < 0.20) +
(FDI > 1.60) +
(RSQ < 0.20) +
(VR < 0.95);
def regime =
if trendScore >= 3 then 1
else if rangeScore >= 3 then -1
else 0;
# =========================
# Strength / Quality
# =========================
def strengthRaw =
(ER * 25) +
((2 - FDI) * 25) +
(RSQ * 25) +
(VR * 25);
def trendStrength = Max(0, Min(100, strengthRaw));
def qualityRaw =
(ER * 20) +
((2 - FDI) * 20) +
(RSQ * 30) +
(VR * 20) +
(AbsValue(slope) * 10);
def trendQuality = Max(0, Min(100, qualityRaw));
# =========================
# Directional Label
# =========================
def trendDir = if slope > 0 then 1 else -1;
AddLabel(yes,
if regime == 1 then
(if trendDir == 1 then "TREND ↑ | Strength " + Round(trendStrength,2) + " | Quality " + Round(trendQuality,2)
else "TREND ↓ | Strength " + Round(trendStrength,2) + " | Quality " + Round(trendQuality,2))
else if regime == -1 then
"RANGE | Strength " + Round(trendStrength,2) + " | Quality " + Round(trendQuality,2)
else
"TRANSITION | Strength " + Round(trendStrength,2) + " | Quality " + Round(trendQuality,2),
if regime == 1 then
(if trendDir == 1 then Color.GREEN else Color.RED)
else if regime == -1 then
Color.YELLOW
else
Color.YELLOW
);
# =========================
# Debug Labels
# =========================
AddLabel(debug, "FDI S/M/L: " + Round(FDI_s,2) + " / " + Round(FDI_m,2) + " / " + Round(FDI_l,2), Color.WHITE);
AddLabel(debug, "VR S/M/L: " + Round(VR_s,2) + " / " + Round(VR_m,2) + " / " + Round(VR_l,2), Color.WHITE);
AddLabel(debug, "RSQ S/M/L: " + Round(RSQ_s,2) + " / " + Round(RSQ_m,2) + " / " + Round(RSQ_l,2), Color.WHITE);
AddLabel(debug, "Signal S/M/L: " + signal_s + " / " + signal_m + " / " + signal_l, Color.WHITE);
AddLabel(debug, "Using: " + (if useShort then "SHORT" else if useMed then "MEDIUM" else "LONG"), Color.CYAN);