Below is a complete Thinkorswim upper-chart study using three labels:
How the labels work
A narrowing bullish spread is not immediately bearish, and a narrowing bearish spread is not immediately bullish. Narrowing is a caution signal showing that the existing trend is losing separation. A reversal is more credible only after the EMAs cross and begin widening in the new direction.
- TREND: EMA stack and recent crossover.
- REGIME: spread width and whether it is widening, narrowing, or braiding.
- ACTION: continuation, caution, consolidation, or transition.
How the labels work
| EMA condition | Interpretation |
|---|---|
| 20 EMA above 50 EMA | Bullish directional bias |
| 20 EMA below 50 EMA | Bearish directional bias |
| Wide and widening | Trend is strengthening |
| Wide but stable | Established trend is sustaining |
| Wide and narrowing | Trend remains intact but is weakening |
| Narrow spread | Neutral or transitional |
| Several recent crosses with narrow spread | Braiding and likely consolidation |
A narrowing bullish spread is not immediately bearish, and a narrowing bearish spread is not immediately bullish. Narrowing is a caution signal showing that the existing trend is losing separation. A reversal is more credible only after the EMAs cross and begin widening in the new direction.
Code:
# ============================================================
# EMA 20/50 TREND AND REGIME LABELS
# Thinkorswim Upper Study
# ANTWERKS
# TREND:
# EMA 20 above EMA 50 = bullish
# EMA 20 below EMA 50 = bearish
#
# REGIME:
# Wide and widening = strengthening trend
# Wide but narrowing = trend losing strength
# Narrow = neutral/transition
# Repeated crosses with narrow spread = braiding/consolidation
# ============================================================
declare upper;
# ============================================================
# INPUTS
# ============================================================
input fastEMALength = 20;
input slowEMALength = 50;
input atrLength = 14;
# Spread thresholds normalized by ATR
input narrowSpreadATR = 0.15;
input wideSpreadATR = 0.50;
# Measures whether the EMA spread is widening or narrowing
input spreadSlopeBars = 3;
input spreadChangeTolerance = 0.02;
# Braiding/consolidation detection
input braidLookback = 20;
input minimumCrossesForBraid = 2;
input braidMaximumSpreadATR = 0.25;
# A crossover remains labeled as recent for this many bars
input recentCrossBars = 3;
input showTrendLabel = yes;
input showRegimeLabel = yes;
input showActionLabel = yes;
input showEMAPlots = no;
# ============================================================
# EMA CALCULATIONS
# ============================================================
def ema20 = ExpAverage(close, fastEMALength);
def ema50 = ExpAverage(close, slowEMALength);
def bullishStack = ema20 > ema50;
def bearishStack = ema20 < ema50;
# ============================================================
# ATR-NORMALIZED EMA SPREAD
# ============================================================
def atr = Average(TrueRange(high, close, low), atrLength);
def safeATR =
if atr > 0
then atr
else 0.01;
# Signed spread shows direction
def signedSpreadATR =
(ema20 - ema50) / safeATR;
# Absolute spread shows width
def absoluteSpreadATR =
AbsValue(signedSpreadATR);
# ============================================================
# CROSSOVER DETECTION
# ============================================================
def bullishCross =
ema20 > ema50 and
ema20[1] <= ema50[1];
def bearishCross =
ema20 < ema50 and
ema20[1] >= ema50[1];
def anyCross =
bullishCross or bearishCross;
def recentBullishCross =
Sum(
if bullishCross then 1 else 0,
recentCrossBars
) > 0;
def recentBearishCross =
Sum(
if bearishCross then 1 else 0,
recentCrossBars
) > 0;
# ============================================================
# SPREAD EXPANSION AND CONTRACTION
# ============================================================
def spreadChange =
absoluteSpreadATR -
absoluteSpreadATR[spreadSlopeBars];
def spreadWidening =
spreadChange > spreadChangeTolerance;
def spreadNarrowing =
spreadChange < -spreadChangeTolerance;
def spreadStable =
!spreadWidening and
!spreadNarrowing;
# ============================================================
# SPREAD WIDTH
# ============================================================
def narrowSpread =
absoluteSpreadATR <= narrowSpreadATR;
def wideSpread =
absoluteSpreadATR >= wideSpreadATR;
def moderateSpread =
!narrowSpread and
!wideSpread;
# ============================================================
# BRAIDING / CONSOLIDATION
# ============================================================
def crossCount =
Sum(
if anyCross then 1 else 0,
braidLookback
);
def emaBraiding =
crossCount >= minimumCrossesForBraid and
absoluteSpreadATR <= braidMaximumSpreadATR;
# ============================================================
# TREND STATES
# ============================================================
def strongBullishTrend =
bullishStack and
wideSpread and
spreadWidening;
def bullishTrend =
bullishStack and
!narrowSpread and
!strongBullishTrend;
def weakBullishTrend =
bullishStack and
narrowSpread;
def strongBearishTrend =
bearishStack and
wideSpread and
spreadWidening;
def bearishTrend =
bearishStack and
!narrowSpread and
!strongBearishTrend;
def weakBearishTrend =
bearishStack and
narrowSpread;
# ============================================================
# CAUTION CONDITIONS
# ============================================================
def bullishCaution =
bullishStack and
spreadNarrowing and
!emaBraiding;
def bearishCaution =
bearishStack and
spreadNarrowing and
!emaBraiding;
# ============================================================
# OPTIONAL EMA PLOTS
# ============================================================
plot EMA20Plot =
if showEMAPlots
then ema20
else Double.NaN;
EMA20Plot.SetDefaultColor(Color.GREEN);
EMA20Plot.SetLineWeight(2);
plot EMA50Plot =
if showEMAPlots
then ema50
else Double.NaN;
EMA50Plot.SetDefaultColor(Color.RED);
EMA50Plot.SetLineWeight(2);
# ============================================================
# LABEL 1: TREND
# ============================================================
AddLabel(
showTrendLabel,
if emaBraiding then
"TREND: NEUTRAL"
else if recentBullishCross then
"TREND: BULLISH CROSS"
else if recentBearishCross then
"TREND: BEARISH CROSS"
else if strongBullishTrend then
"TREND: STRONG BULLISH"
else if strongBearishTrend then
"TREND: STRONG BEARISH"
else if bullishStack then
"TREND: BULLISH"
else if bearishStack then
"TREND: BEARISH"
else
"TREND: NEUTRAL",
if emaBraiding then
Color.GRAY
else if recentBullishCross then
Color.GREEN
else if recentBearishCross then
Color.RED
else if strongBullishTrend then
Color.DARK_GREEN
else if strongBearishTrend then
Color.DARK_RED
else if bullishStack then
Color.GREEN
else if bearishStack then
Color.RED
else
Color.GRAY
);
# ============================================================
# LABEL 2: REGIME
# ============================================================
AddLabel(
showRegimeLabel,
if emaBraiding then
"REGIME: BRAIDING / CONSOLIDATION"
else if bullishStack and wideSpread and spreadWidening then
"REGIME: BULLISH EXPANSION"
else if bearishStack and wideSpread and spreadWidening then
"REGIME: BEARISH EXPANSION"
else if bullishStack and spreadNarrowing then
"REGIME: BULLISH NARROWING"
else if bearishStack and spreadNarrowing then
"REGIME: BEARISH NARROWING"
else if narrowSpread then
"REGIME: NARROW / NEUTRAL"
else if wideSpread then
"REGIME: WIDE / SUSTAINING"
else if moderateSpread and spreadWidening then
"REGIME: DEVELOPING"
else
"REGIME: STABLE",
if emaBraiding then
Color.GRAY
else if bullishStack and wideSpread and spreadWidening then
Color.DARK_GREEN
else if bearishStack and wideSpread and spreadWidening then
Color.DARK_RED
else if spreadNarrowing then
Color.YELLOW
else if narrowSpread then
Color.LIGHT_GRAY
else if bullishStack and spreadWidening then
Color.GREEN
else if bearishStack and spreadWidening then
Color.RED
else
Color.GRAY
);
# ============================================================
# LABEL 3: ACTION
# ============================================================
AddLabel(
showActionLabel,
if emaBraiding then
"ACTION: WAIT FOR BREAKOUT"
else if recentBullishCross and spreadWidening then
"ACTION: BULLISH TURN"
else if recentBearishCross and spreadWidening then
"ACTION: BEARISH TURN"
else if strongBullishTrend then
"ACTION: FAVOR LONGS"
else if strongBearishTrend then
"ACTION: FAVOR SHORTS"
else if bullishCaution then
"ACTION: CAUTION — BULL TREND WEAKENING"
else if bearishCaution then
"ACTION: CAUTION — BEAR TREND WEAKENING"
else if weakBullishTrend then
"ACTION: WAIT — WEAK BULLISH EDGE"
else if weakBearishTrend then
"ACTION: WAIT — WEAK BEARISH EDGE"
else if bullishTrend then
"ACTION: BULLISH BIAS"
else if bearishTrend then
"ACTION: BEARISH BIAS"
else
"ACTION: WAIT",
if emaBraiding then
Color.GRAY
else if recentBullishCross and spreadWidening then
Color.GREEN
else if recentBearishCross and spreadWidening then
Color.RED
else if strongBullishTrend then
Color.DARK_GREEN
else if strongBearishTrend then
Color.DARK_RED
else if bullishCaution or bearishCaution then
Color.YELLOW
else if narrowSpread then
Color.GRAY
else if bullishTrend then
Color.GREEN
else if bearishTrend then
Color.RED
else
Color.GRAY
);
# ============================================================
# DATA LABEL
# ============================================================
AddLabel(
no,
"EMA SPREAD: " + Round(signedSpreadATR, 2) + " ATR",
if signedSpreadATR > 0
then Color.GREEN
else if signedSpreadATR < 0
then Color.RED
else Color.GRAY
);
Last edited by a moderator: