Author states:
State-Dependent EMA is an adaptive exponential moving average that adjusts its smoothing coefficient using three measurements: price efficiency, sustained deviation from the filter, and changes in short-term volatility.
Rather than using a fixed EMA length, the indicator calculates a smoothing coefficient between the selected Fast and Slow EMA settings. The coefficient changes as the estimated market state changes, allowing the filter to respond more quickly under some conditions and apply greater smoothing under others.
The indicator includes three state models, an optional volatility penalty, two methods of determining trend direction, a gradient ribbon and a fixed EMA for comparison.
Original Tradingview Script:
https://www.tradingview.com/script/jdVw4YmG-State-Dependent-EMA-BackQuant/
Here is the ThinkOrSwim script:
Code:
# State-Dependent EMA [BackQuant]
# Converted from PineScript v6 to ThinkScript - W. Kalvin
script EffRatio {
input src = close;
input erLen = 20;
def change = src - src[1];
def travel = Average(AbsValue(change), erLen) * erLen;
def displacement = AbsValue(src - src[erLen]);
plot ER = if !IsNaN(travel) and travel > 0.0 then Min(displacement / travel, 1.0) else 0.0;
}
# --- Inputs ---
# Core Calculation
input src = close;
input fastLen = 4;
input slowLen = 40;
input stateMode = {default "Combined", "Efficiency", "Sustained Residual"};
# State Estimation
input erLen = 20;
input residualLen = 4;
input atrLen = 14;
input residualScale = 1.5;
input efficiencyWeight = 0.5;
input residualWeight = 0.5;
input shortVarLen = 10;
input longVarLen = 40;
input variancePenalty = 1.0;
# Plotting and Coloring
input trendMode = {default "Ribbon", "Filter Slope"};
input trendLookback = 2;
input ribbonLen = 9;
input showRibbon = yes;
input showFill = yes;
input showGlow = yes;
input showBaseline = no;
input baselineLen = 20;
input paintCandles = no;
input bgCol = no;
# --- Core Calculations ---
# 1. Base Metrics
def change = src - src[1];
def efficiency = EffRatio(src, erLen);
def shortVol = StDev(change, shortVarLen);
def longVol = StDev(change, longVarLen);
def varianceRatio = if !IsNaN(shortVol) and !IsNaN(longVol) and longVol > 0.0 then shortVol / longVol else 1.0;
def varianceGate = 1.0 / (1.0 + variancePenalty * Max(varianceRatio - 1.0, 0.0));
def fastAlpha = 2.0 / (fastLen + 1.0);
def slowAlpha = 2.0 / (Max(slowLen, fastLen + 1) + 1.0);
def atr = ATR(length = atrLen);
def minTick = TickSize();
# 2. Innovation & Sustained Residual
def innovation = src - src[1];
def persistentResidual = ExpAverage(innovation, residualLen);
def normalizedResidual = AbsValue(persistentResidual) / Max(if IsNaN(atr) or atr == 0 then minTick else atr, minTick);
def residualState = Min(normalizedResidual / residualScale, 1.0);
# 3. State Weighting
def totalWeight = efficiencyWeight + residualWeight;
def combinedState = if totalWeight > 0.0 then (efficiency * efficiencyWeight + residualState * residualWeight) / totalWeight else 0.0;
def rawState = if stateMode == stateMode."Efficiency" then efficiency else if stateMode == stateMode."Sustained Residual" then residualState else combinedState;
def marketState = Max(0.0, Min(1.0, rawState * varianceGate));
# 4. Adaptive Alpha Calculation
def alpha = slowAlpha + (fastAlpha - slowAlpha) * marketState;
# 5. Recursive Main Filter
rec filt = CompoundValue(1, filt[1] + alpha * (src - filt[1]), src);
# Non-recursive projection of filt for AddCloud safety
def filtVal = filt;
# 6. Ribbon & Trend Evaluation
def ribbonRef = ExpAverage(filtVal, ribbonLen);
def bullish = if trendMode == trendMode."Ribbon" then filtVal > ribbonRef else filtVal > filtVal[trendLookback];
def bearish = if trendMode == trendMode."Ribbon" then filtVal < ribbonRef else filtVal < filtVal[trendLookback];
rec trend = if bullish then 1 else if bearish then -1 else trend[1];
# 7. Glow Boundaries & Alerts
def glow = atr * 0.06;
def outerGlow = glow * 2.0;
def bullFlip = trend == 1 and trend[1] == -1;
def bearFlip = trend == -1 and trend[1] == 1;
# ====================================================================
# VISUAL OUTPUTS: PLOTS, CLOUDS, COLORS & ALERTS
# ====================================================================
# Main Filter Line
plot StateEMA = filtVal;
StateEMA.SetLineWeight(3);
StateEMA.AssignValueColor(if trend == 1 then Color.GREEN else if trend == -1 then Color.RED else Color.GRAY);
# Comparison Baseline
plot BaselinePlot = if showBaseline then ExpAverage(src, baselineLen) else Double.NaN;
BaselinePlot.SetDefaultColor(Color.GRAY);
BaselinePlot.SetStyle(Curve.SHORT_DASH);
# Ribbon Line
plot RibbonPlot = if showRibbon then ribbonRef else Double.NaN;
RibbonPlot.AssignValueColor(if trend == 1 then Color.DARK_GREEN else Color.DARK_RED);
# Price Reference Line
plot PriceRef = if showFill then close else Double.NaN;
PriceRef.Hide();
# Clouds & Gradients
AddCloud(
if showFill and close > filtVal then close else Double.NaN,
if showFill and close > filtVal then filtVal else Double.NaN,
Color.DARK_GREEN, Color.DARK_GREEN
);
AddCloud(
if showFill and close <= filtVal then filtVal else Double.NaN,
if showFill and close <= filtVal then close else Double.NaN,
Color.DARK_RED, Color.DARK_RED
);
AddCloud(
if showRibbon then filtVal else Double.NaN,
if showRibbon then ribbonRef else Double.NaN,
Color.GREEN, Color.RED
);
# Glow Boundaries Plots
plot GlowUpper = if showGlow then filtVal + glow else Double.NaN;
plot GlowLower = if showGlow then filtVal - glow else Double.NaN;
plot OuterUpper = if showGlow then filtVal + outerGlow else Double.NaN;
plot OuterLower = if showGlow then filtVal - outerGlow else Double.NaN;
GlowUpper.Hide();
GlowLower.Hide();
OuterUpper.Hide();
OuterLower.Hide();
AddCloud(if showGlow then OuterUpper else Double.NaN, if showGlow then OuterLower else Double.NaN, Color.DARK_GRAY, Color.DARK_GRAY);
AddCloud(if showGlow then GlowUpper else Double.NaN, if showGlow then GlowLower else Double.NaN, Color.GRAY, Color.GRAY);
# Candle & Background Color Rules
AssignPriceColor(if paintCandles then (if trend == 1 then Color.GREEN else if trend == -1 then Color.RED else Color.CURRENT) else Color.CURRENT);
AssignBackgroundColor(if bgCol then (if trend == 1 then Color.DARK_GREEN else if trend == -1 then Color.DARK_RED else Color.CURRENT) else Color.CURRENT);
# Alerts
Alert(bullFlip, "State EMA Turned Bullish", Alert.BAR, Sound.Chimes);
Alert(bearFlip, "State EMA Turned Bearish", Alert.BAR, Sound.Bell);
Last edited by a moderator: