Climax Detector w Traps

atcsam

Market Structure Expert
Plus
Climax Detector w/ Traps

A unified climax, exhaustion, and trap detection framework for Thinkorswim
TXE6FWl.png

This study is the modern evolution of something I built years ago called A Better Better Volume with VSA. That original project started as a simple attempt to improve the classic Better Volume indicator by adding relative volume, wick behavior, and basic YTC‑style reference levels. Over time, as I kept layering in VSA concepts, range/spread context, anomaly handling, and early trap logic, the tool gradually shifted from “better volume bars” into a full microstructure framework.

The Climax Detector w/ Traps is the result of that evolution. It takes the core ideas from the original study — volume context, climax footprints, structural reference lines — and expands them into a unified engine that reads exhaustion, pressure, trap formation, and structural turning points with far more precision. If you used the old version, this is the same philosophy, just taken much further and built on a cleaner, more deterministic foundation.

Overview

This release introduces the Climax Detector w/ Traps, a consolidated microstructure engine designed to identify:

  • Buying climaxes
  • Selling climaxes
  • Bull traps
  • Bear traps
  • Micro traps
  • Climax traps
  • Chop zones
  • Pressure direction
It focuses on tape pressure, exhaustion, climax footprints, and trap formation, using a unified logic stack that merges VSA, YTC, wick‑climax, and microstate behavior.

1. What This Script Does

The Climax Detector w/ Traps is a hybrid microstructure model that reads:

A. Climax Events

The engine detects climaxes using multiple independent subsystems:

  • Terminal Wyckoff BC / SC
  • Strict wick climax
  • Loose wick climax
  • Gap‑failure exhaustion
  • Shape‑based climaxes
  • Two‑bar YTC climaxes
  • Hybrid VSA climaxes
All climax types are normalized into:

  • ClimaxBC (Buying Climax → Weakness)
  • ClimaxSC (Selling Climax → Strength)
This produces a single, clean, deterministic climax signal regardless of which subsystem triggered it.

📌 YTC Lines (Expanded Explanation)

YTC Lines are one of the most important components of this script. Every time a Buying Climax (BC) or Selling Climax (SC) is detected, the engine automatically generates a set of YTC reference lines:

  • YTC High Line
  • YTC Low Line
  • YTC Mid Line (1/3 retracement)
These lines are anchored to the bar that triggered the climax and remain active until a new climax replaces them.

What YTC Lines Represent

YTC lines represent the same structural reference levels used by professional traders — the high, low, and midpoint of a climax bar. These levels are widely used in institutional trading to identify:

  • absorption
  • continuation
  • failure points
  • trap behavior
The script formalizes these levels into a consistent, visual framework. They function as microstructure anchors that the market reacts to repeatedly.

Why They Matter

Climax‑anchored levels are often used by professionals because they:

  • define the true pivot of the climax
  • show where trapped traders must exit
  • reveal where continuation trades trigger
  • identify fade zones
  • provide natural stop‑loss locations
  • act as breakout confirmation levels
  • help determine whether a trap is forming or failing
In practice, price interacts with these climax‑derived levels far more reliably than traditional pivots or moving averages.

How I Use Them

1. Entry


  • Break above SC → long continuation
  • Break below BC → short continuation
  • Rejection at the mid‑line → fade entry
2. Exit

  • Profit targets at the opposite YTC band
  • Exiting when price returns to the climax origin
3. Stop Placement

  • Stops placed beyond the YTC high/low
  • Stops tightened when price re‑enters the YTC zone
4. Trap Confirmation

  • Bull Trap → price fails at BC YTC lines
  • Bear Trap → price fails at SC YTC lines
5. Chop Detection

The script uses YTC high/low overlap to determine:

  • true chop
  • breakout from chop
  • suppression of false signals
YTC overlap does a great job catching real chop because it’s based on structural BC/SC levels, not just low volatility or flat indicators.

YTC lines turn climax detection into a complete trading framework, not just a signal.

B. Trap Detection


The trap engine uses:

  • exhaustion polarity
  • microstate collapse
  • wick‑climax alignment
  • YTC exhaustion
  • micro‑trap patterns
  • climax‑trap overrides
It outputs:

  • Bull Trap
  • Bear Trap
  • Micro Bull Trap
  • Micro Bear Trap
  • Climax Trap
Traps are not guesses — they are based on exhaustion + collapse, the same logic used by Wyckoff operators and tape readers.

C. Tape Pressure

Tape pressure is derived from:

  • climaxes
  • exhaustion
  • microstate flips
  • wick‑climax direction
  • YTC polarity
  • trend context
Outputs:

  • tapeUpPressure
  • tapeDnPressure
This is the modern equivalent of “strength vs weakness” on the tape.

D. Special Candle States

The engine classifies:

  • BuyClimax
  • SellClimax
  • SpinTop
  • BullPin
  • BearPin
  • LV Up
  • LV Down
These appear as [2] [1] [0] labels for the last three bars.

E. Chop Zone Detection

A structural overlap model using YTC high/low bands:

  • Detects true chop
  • Identifies breakout from chop
  • Suppresses false signals
  • Provides clean regime context
Outputs:

  • Chop
  • Bull Breakout (Clears Upper Band)
  • Bear Breakout (Clears Lower Band)
2. Multi-Layer Confirmation

This engine combines:

  • volume
  • spread
  • wick dominance
  • displacement
  • volatility
  • microstate behavior
  • YTC exhaustion
  • shape analysis
  • gap enhancers
  • trend context
  • climax memory
This produces far fewer false positives and much cleaner trap detection.

3. Key Features

✔ Unified Climax Engine

✔ Dual‑Fire Trap Engine

✔ Micro Traps

✔ Climax Traps

✔ Tape Pressure

✔ Special Candle Labels

✔ Chop Zone Logic

Example 1 — Bull Trap (Severity 3 of 5)

This example shows a Bull Trap with a severity rating of 3 out of 5. Notice how price continues to rise while volume decreases, signaling weakening participation behind the move. The trap forms when buyers chase the rising price into fading volume and diminishing pressure, creating the conditions for a reversal once the structure collapses back inside the prior range.

tYGEwXh.png


Chart w Studies

Example 2 — Bear Trap Setup (Early Warning Signal)

This chart shows a Bear Trap Setup, which acts as an early warning or “heads‑up” that downside pressure is weakening. Even though price pushes lower, the internal conditions don’t support continuation — volume behavior, wick structure, and microstate compression all point to sellers losing commitment. A Bear Trap Setup does not confirm a trap by itself, but it highlights the exact moment when the market becomes vulnerable to a bullish reversal if collapse conditions flip.

sRaF3Fh.png



Climax Detector w Traps study

Ruby:
# Climax Detector w/ Traps
# atcsam — 04/20/2026
# Playing it forward

# Real Credit: thinkScript community

# Core inspirations and source references:
# - Verbatim translation of the TradeStation code from:
#   http://emini-watch.com/free-stuff/volume-indicator/  (Better Volume)
#
# - YTC (Lance Beggs) — Converted from:
#   https://www.tradingview.com/script/5fSgjYoM-YTC-Candlestick-Sentiment/
#
# - hiVolume indicator:
#   source: http://tinboot.blogspot.com
#   author: Allen Everhart
#
# - Advanced Volume Study for Volume Spread Analysis:
#   [email protected]


############################################
# INPUTS
############################################
input proMode = {default Moderate, Aggressive, Conservative};
input showYTCLines = yes;
input currentOnlyYTC = yes;
input ytcLookback = 20;
input choplabel = yes;
input tape = yes;
input pro  = yes;
input ShowArrows = no;

##########
# Label Position
#################
input LabelPos = {default "Bottom Left", "Top Left", "Top Right"};
def pos =
    if LabelPos == LabelPos."Top Left" then 0 else
    if LabelPos == LabelPos."Top Right" then 1 else
    if LabelPos == LabelPos."Bottom Left" then 2 else
    3;

###############
# CORE SERIES 
###############
def o = open;
def h = high;
def l = low;
def c = close;
def v = volume;

def spread = h - l;
def body   = AbsValue(c - o);
def range  = spread;

def isBull = c >= o;
def isBear = c < o;

def upperWick = h - Max(o, c);
def lowerWick = Min(o, c) - l;

def bodyPct    = if range != 0 then 100 * body / range else 0;
def upWickPct  = if range != 0 then 100 * upperWick / range else 0;
def lowWickPct = if range != 0 then 100 * lowerWick / range else 0;

############################################
# MICROSTATE ENGINE (OVERWATCH CORE ONLY)
############################################
input zCloudLevel = 1.0;
input atrLength = 15;
input dsLen = 5;

def price = close;
def atr = Average(TrueRange(high, close, low), atrLength);

# Z-score on price
def zMean50 = Average(price, 50);
def zDev50  = StDev(price, 50);
def z50     = if zDev50 != 0 then (price - zMean50) / zDev50 else 0;

def zMean30 = Average(price, 30);
def zDev30  = StDev(price, 30);
def z30     = if zDev30 != 0 then (price - zMean30) / zDev30 else 0;

def zMean20 = Average(price, 20);
def zDev20  = StDev(price, 20);
def z20     = if zDev20 != 0 then (price - zMean20) / zDev20 else 0;

def zMean10 = Average(price, 10);
def zDev10  = StDev(price, 10);
def z10     = if zDev10 != 0 then (price - zMean10) / zDev10 else 0;

def zScore_ms =
    if AbsValue(z50) > 3 then z10 else
    if AbsValue(z50) > 2 then z20 else
    if AbsValue(z50) > 1.5 then z30 else
    z50;

# Curved dome
def zStretch = Max(0, AbsValue(zScore_ms) - zCloudLevel);
def zCurve = Power(zStretch, 1.5);

def atrMean = Average(atr, 20);
def volWeight = if atrMean != 0 then atr / atrMean else 1;

def zExpandRaw = zCurve * atr * volWeight;
def zExpand = ExpAverage(zExpandRaw, 3);

# DOOM core
def domeThickness = zExpand;
def domeSmooth = ExpAverage(domeThickness, 5);
def domeROC = domeSmooth - domeSmooth[3];

def domeNorm =
    if HighestAll(domeSmooth) == LowestAll(domeSmooth)
    then 0
    else 100 * (domeSmooth - LowestAll(domeSmooth)) /
         (HighestAll(domeSmooth) - LowestAll(domeSmooth));

def domeCompression = domeROC < 0;
def domeExpansion   = domeROC > 0;

# Vol burst
def volBurst = domeROC > domeSmooth * 0.15;

# Directional commitment & stability (dsNorm)
def barRange_ms   = high - low;
def bodySize_ms   = AbsValue(close - open);
def upperWick_ms  = high - Max(open, close);
def lowerWick_ms  = Min(open, close) - low;

def safeRange_ms  = if barRange_ms == 0 then 0.0001 else barRange_ms;
def bodyFrac_ms = bodySize_ms / safeRange_ms;

def bullBar_ms = close > open;
def bearBar_ms = close < open;

def followThroughUp_ms   = bullBar_ms and close > close[1];
def followThroughDown_ms = bearBar_ms and close < close[1];

def overlap_ms =
    (high <= high[1] and low >= low[1]) or
    (high[1] <= high and low[1] >= low);

def wickAlignedBull_ms = bullBar_ms and lowerWick_ms > upperWick_ms;
def wickAlignedBear_ms = bearBar_ms and upperWick_ms > lowerWick_ms;

def dcRaw_ms =
    (if bodyFrac_ms > 0.5 then 1 else 0) +
    (if followThroughUp_ms or followThroughDown_ms then 1 else 0) +
    (if wickAlignedBull_ms or wickAlignedBear_ms then 1 else 0) +
    (if !overlap_ms then 1 else 0);

def dcNorm_ms = dcRaw_ms / 4;

def ema3_ms = ExpAverage(close, 3);
def ema3Slope_ms = ema3_ms - ema3_ms[1];
def mom3_ms = close - close[3];

def dirBar_ms   = if close > open then 1 else if close < open then -1 else 0;
def dirSlope_ms = if ema3Slope_ms > 0 then 1 else if ema3Slope_ms < 0 then -1 else 0;
def dirMom_ms   = if mom3_ms > 0 then 1 else if mom3_ms < 0 then -1 else 0;

def dirBarSum_ms   = Sum(dirBar_ms, dsLen);
def dirSlopeSum_ms = Sum(dirSlope_ms, dsLen);
def dirMomSum_ms   = Sum(dirMom_ms, dsLen);

def dsBar_ms   = AbsValue(dirBarSum_ms)   / dsLen;
def dsSlope_ms = AbsValue(dirSlopeSum_ms) / dsLen;
def dsMom_ms   = AbsValue(dirMomSum_ms)   / dsLen;

def dsNormRaw_ms = (dsBar_ms + dsSlope_ms + dsMom_ms) / 3;

rec dcSmooth_ms =
    if IsNaN(dcSmooth_ms[1]) then dcNorm_ms
    else dcSmooth_ms[1] + 0.2 * (dcNorm_ms - dcSmooth_ms[1]);

rec dsSmooth_ms =
    if IsNaN(dsSmooth_ms[1]) then dsNormRaw_ms
    else dsSmooth_ms[1] + 0.2 * (dsNormRaw_ms - dsSmooth_ms[1]);

def dsNorm = Max(0, Min(1, dsSmooth_ms));

# DOOM-L → microState
def doomL_norm     = domeNorm;
def doomL_compress = domeCompression;
def doomL_expand   = domeExpansion;
def doomL_roc      = domeROC;
def doomL_smooth   = domeSmooth;

def doomL_state =
    if doomL_compress and !volBurst and dsNorm >= 0.5 then 0 else
    if doomL_compress and  volBurst and dsNorm >= 0.5 then 1 else
    if doomL_expand   and !volBurst and dsNorm >= 0.5 then 2 else
    if doomL_expand   and  volBurst and dsNorm >= 0.5 then 3 else
    0;

def microState = doomL_state;

# MicroState helpers for traps
def msContracting = microState == 0 or microState == 1;
def msExpanding   = microState == 2 or microState == 3;

def msFlipDown =
    (microState == 0 or microState == 1) and
    (microState[1] == 2 or microState[1] == 3);

def msFlipUp =
    (microState == 2 or microState == 3) and
    (microState[1] == 0 or microState[1] == 1);

############################################
# GEOMETRY / DOJI / SPINNER (UNIFIED)
############################################
def isDoji = bodyPct <= 10;

def isMicroBody = bodyPct > 10 and bodyPct < 35;

def isWickBalanced =
    upWickPct > 20 and
    lowWickPct > 20 and
    AbsValue(upWickPct - lowWickPct) <= 20;

def isSpinner = isMicroBody and isWickBalanced;

def upperW = high - Max(open, close);
def lowerW = Min(open, close) - low;

def upperPct = if spread > 0 then upperW / spread else 0;
def lowerPct = if spread > 0 then lowerW / spread else 0;

def isHighWave =
    bodyPct <= 20 and
    upperPct >= 0.35 and
    lowerPct >= 0.35;

def isDojiFamily =
    isDoji or
    isSpinner or
    isHighWave;

############################################
# ADAPTIVE LENGTHS
############################################
def vsaLen =
    if proMode == proMode.Aggressive then 10
    else if proMode == proMode.Conservative then 60
    else 20;

def volLen = vsaLen;
def sprLen = vsaLen;
def rngLen = vsaLen;

############################################
# RELATIVE VOLUME (RVOL 3, 5, 10)
############################################
def avgV3  = Average(v, 3);
def avgV5  = Average(v, 5);
def avgV10 = Average(v, 10);

def rv3  = if avgV3  != 0 then v / avgV3  else 1;
def rv5  = if avgV5  != 0 then v / avgV5  else 1;
def rv10 = if avgV10 != 0 then v / avgV10 else 1;

############################################
# MODE SENSITIVITY MULTIPLIERS
############################################
def zMult =
    if proMode == proMode.Aggressive then 1.4
    else if proMode == proMode.Conservative then 0.7
    else 1.0;

def vrocMult =
    if proMode == proMode.Aggressive then 1.5
    else if proMode == proMode.Conservative then 0.75
    else 1.0;

############################################
# VOLUME ROC
############################################
def vroc    = if v[1] != 0 then (v - v[1]) / v[1] else 0;
def vrocAdj = vroc * vrocMult;

############################################
# Z-SCORE + EULER NORMALIZATION
############################################
def volMean = Average(v, volLen);
def volStd  = StDev(v, volLen);
def sprMean = Average(spread, sprLen);
def sprStd  = StDev(spread, sprLen);
def rngMean = Average(range, rngLen);
def rngStd  = StDev(range, rngLen);

def volNorm = (1 - Exp(-AbsValue((v - volMean) / Max(volStd, 0.0001)))) * zMult;
def sprNorm = (1 - Exp(-AbsValue((spread - sprMean) / Max(sprStd, 0.0001)))) * zMult;
def rngNorm = (1 - Exp(-AbsValue((range - rngMean) / Max(rngStd, 0.0001)))) * zMult;

############################################
# PRICE Z-SCORE (FOR GAP ENHANCERS)
############################################
def priceMean = Average(c, vsaLen);
def priceStd  = StDev(c, vsaLen);
def priceZ    = if priceStd != 0 then (c - priceMean) / priceStd else 0;

############################################
# BASIC PATTERNS
############################################
def isBullPin = isBull and bodyPct <= 33 and lowWickPct >= 60;
def isBearPin = isBear and bodyPct <= 33 and upWickPct >= 60;

############################################
# VOLUME CLASSIFICATION (USING RVOL + Z)
############################################
def isLowVol   = rv3 < 0.95 and rv5 < 0.95 and volNorm < 0.65;
def isHighVol  = rv3 > 1.8 or rv5 > 1.7 or volNorm > 0.7;
def isUltraVol = rv3 > 2.3 and rv5 > 2.0 and rv10 > 1.7 and volNorm > 0.75;

############################################
# GAPS (ENHANCERS)
############################################
def gapUp   = o > h[1];
def gapDown = o < l[1];

############################################
# SMART PIN BARS
############################################
def bullPinShape = isBull and bodyPct <= 33 and lowWickPct >= 60;
def bearPinShape = isBear and bodyPct <= 33 and upWickPct >= 60;

def LV_BullPin = bullPinShape and volNorm <= 0.25;
def LV_BearPin = bearPinShape and volNorm <= 0.25;

############################################
# FAT-TAIL EVENT ENGINE (CONTEXT)
############################################
input kurtLen = 20;
input kurtSensitivity = 2.5;

def volMeanK = Average(v, kurtLen);
def volStDevK = StDev(v, kurtLen);
def volKurtosis = if volStDevK != 0 then (v - volMeanK) / volStDevK else 0;

def sprSafeK = if spread == 0 then 0.0001 else spread;
def churnK = v / sprSafeK;
def churnMeanK = Average(churnK, kurtLen);
def churnStDevK = StDev(churnK, kurtLen);
def churnZK = if churnStDevK != 0 then (churnK - churnMeanK) / churnStDevK else 0;

def priceVelocityK = c - c[5];
def priceVolatilityK = Average(TrueRange(h, c, l), 5);
def sharpeMomentumK = if priceVolatilityK != 0 then priceVelocityK / priceVolatilityK else 0;

def isFatTailEvent =
    volKurtosis > kurtSensitivity and
    churnZK > 1.0;

############################################
# CLASSIC VSA CONTEXT (NOT TRAPS)
############################################
def StoppingVol =
    isBear and
    isHighVol and
    rngNorm > 0.6 and
    c > c[1];

def NoDemand =
    isBull and
    isLowVol and
    spread < sprMean and
    c <= c[1];

def NoSupply =
    isBear and
    isLowVol and
    spread < sprMean and
    c >= c[1];

############################################
# TREND CONTEXT
############################################
def trendMA   = ExpAverage(c, vsaLen);
def inUpTrend = c > trendMA;
def inDnTrend = c < trendMA;

############################################
# VSA ENGINE — HYBRID (STRICT + LOOSE)
############################################
def sprAvg = Average(spread, 20);
def sprATR = ATR(14);

def volAvg20    = Average(v, 20);
def volAvg50    = Average(v, 50);
def volStDev50  = StDev(v, 50);
def volZ        = if volStDev50 != 0 then (v - volAvg50) / volStDev50 else 0;

# STRICT VOLUME SPIKE
def strictVol =
    v > volAvg20 * 1.8 or
    v > volAvg50 * 2.2 or
    volZ > 2.0;

# STRICT SPREAD SPIKE
def strictSpread =
    spread > sprAvg * 1.5 and
    spread > sprATR * 1.2;

# LOOSE VOLUME + SPREAD
def looseVol    = rv3 > 1.6 and rv5 > 1.4;
def looseSpread = spread > sprAvg * 1.1;

# STRICT REJECTION
def rejectUp_strict   = c < h - (spread * 0.25);
def rejectDown_strict = c > l + (spread * 0.25);

# LOOSE REJECTION
def rejectUp_loose   = c < h - (spread * 0.15);
def rejectDown_loose = c > l + (spread * 0.15);

# HYBRID REJECTION
def rejectUp   = rejectUp_strict or rejectUp_loose;
def rejectDown = rejectDown_strict or rejectDown_loose;

# TREND ACCELERATION FILTER
def mom   = c - c[1];
def momUp = mom > 0 and mom > mom[1];
def momDn = mom < 0 and mom < mom[1];

def trendAccelUp = momUp and spread > sprAvg;
def trendAccelDn = momDn and spread > sprAvg;

# ============================================================
# TERMINAL WYCKOFF BC / SC
# ============================================================
def volOK = isHighVol or isUltraVol;

def upTrend =
    c > c[1] and
    c[1] > c[2] and
    c[2] > c[3];

def downTrend =
    c < c[1] and
    c[1] < c[2] and
    c[2] < c[3];

def displacementUp   = c >= h - 0.25 * spread;
def displacementDown = c <= l + 0.25 * spread;

def absorptionUp   = upWickPct >= 40;
def absorptionDown = lowWickPct >= 40;

def wideSpread = spread >= 1.75 * sprATR and spread >= 1.5 * sprAvg;

def vsa_SC =
    wideSpread and
    displacementDown and
    volOK and
    downTrend and
    !isDojiFamily and
    !absorptionDown;

def vsa_BC =
    wideSpread and
    displacementUp and
    volOK and
    upTrend and
    !isDojiFamily and
    !absorptionUp;

def BC = vsa_BC;
def SC = vsa_SC;

#===============================================
# NON‑TERMINAL WICK‑CLIMAX ENGINE
# ================================================
def confirmUp =
    c[1] < c or
    v[1] < v or
    h[1] < h;

def confirmDown =
    c[1] > c or
    v[1] < v or
    l < l[1];

# STRICT WICK‑CLIMAX
def WCUp_strict =
    isBear and
    volOK and
    strictSpread and
    lowWickPct >= 40 and
    rejectDown_strict and
    !trendAccelDn and
    confirmDown and
    !isDojiFamily and
    !vsa_SC and
    !vsa_BC;

def WCDown_strict =
    isBull and
    volOK and
    strictSpread and
    upWickPct >= 40 and
    rejectUp_strict and
    !trendAccelUp and
    confirmUp and
    !isDojiFamily and
    !vsa_SC and
    !vsa_BC;

# LOOSE WICK‑CLIMAX
def WCUp_loose =
    isBear and
    volOK and
    looseSpread and
    lowWickPct >= 40 and
    rejectDown and
    !isDojiFamily and
    !vsa_SC and
    !vsa_BC;

def WCDown_loose =
    isBull and
    volOK and
    looseSpread and
    upWickPct >= 40 and
    rejectUp and
    !isDojiFamily and
    !vsa_SC and
    !vsa_BC;

# GAP FAILURE ENHANCERS
def gapFailUp   = gapUp and rejectDown;
def gapFailDown = gapDown and rejectUp;

# FINAL HYBRID WICK‑CLIMAX OUTPUTS
def WCDown =
    if WCDown_strict then 1
    else if WCDown_loose then 1
    else if (isBull and gapFailUp and isFatTailEvent and upWickPct >= 40 and !isDojiFamily and !vsa_BC and !vsa_SC) then 1
    else 0;

def WCUp =
    if WCUp_strict then 1
    else if WCUp_loose then 1
    else if (isBear and gapFailDown and isFatTailEvent and lowWickPct >= 40 and !isDojiFamily and !vsa_BC and !vsa_SC) then 1
    else 0;

def GapExhaustUp   = WCUp   and (isBear and gapFailDown and isFatTailEvent);
def GapExhaustDown = WCDown and (isBull and gapFailUp   and isFatTailEvent);

######################
# TWO-BAR YTC ENGINE
######################
def useYTC = yes;

def vRange = Max(h - l, TickSize());

def vValue1;
def vValue2;

if c > o then {
    vValue1 = (vRange / (2 * vRange + o - c)) * v;
    vValue2 = v - vValue1;
} else if c < o then {
    vValue1 = ((vRange + c - o) / (2 * vRange + c - o)) * v;
    vValue2 = v - vValue1;
} else {
    vValue1 = 0.5 * v;
    vValue2 = v - vValue1;
}

def vValue3 = AbsValue(vValue1 + vValue2);

def hi2 = Highest(h, 2);
def lo2 = Lowest(l, 2);
def range2 = Max(hi2 - lo2, TickSize());

def vValue14 = if useYTC then (vValue1 + vValue1[1]) * range2 else 0;
def vValue15 = if useYTC then (vValue1 + vValue1[1] - vValue2 - vValue2[1]) * range2 else 0;
def vValue16 = if useYTC then (vValue2 + vValue2[1]) * range2 else 0;
def vValue17 = if useYTC then (vValue2 + vValue2[1] - vValue1 - vValue1[1]) * range2 else 0;

def vValue18 = if useYTC then (vValue1 + vValue1[1]) / range2 else 0;
def vValue19 = if useYTC then (vValue1 + vValue1[1] - vValue2 - vValue2[1]) / range2 else 0;
def vValue20 = if useYTC then (vValue2 + vValue2[1]) / range2 else 0;
def vValue21 = if useYTC then (vValue2 + vValue2[1] - vValue1 - vValue1[1]) / range2 else 0;

def vCondition13 = useYTC and vValue15 == Highest(vValue15, ytcLookback) and c > o and c[1] > o[1];
def vCondition18 = useYTC and vValue20 == Lowest(vValue20, ytcLookback) and c > o and c[1] > o[1];
def vCondition19 = useYTC and vValue21 == Lowest(vValue21, ytcLookback) and c > o and c[1] > o[1];

def vCondition14 = useYTC and vValue16 == Highest(vValue16, ytcLookback) and c < o and c[1] < o[1];
def vCondition15 = useYTC and vValue17 == Highest(vValue17, ytcLookback) and c < o and c[1] < o[1];
def vCondition16 = useYTC and vValue18 == Lowest(vValue18, ytcLookback) and c < o and c[1] < o[1];
def vCondition17 = useYTC and vValue19 == Lowest(vValue19, ytcLookback) and c < o and c[1] < o[1];

def TwoBarClimaxUp   = vCondition13 or vCondition18 or vCondition19;
def TwoBarClimaxDown = vCondition14 or vCondition15 or vCondition16 or vCondition17;

#########################
# SHAPE-BASED CLIMAX
#########################
def strongUpperWick1 = upperWick > range * 0.40;
def strongLowerWick1 = lowerWick > range * 0.40;

def upperWickDominant = upperWick > lowerWick * 1.3;
def lowerWickDominant = lowerWick > upperWick * 1.3;
def avgRange1 = Average(range, 20);
def smallBody1 = body < range * 0.30;

def rangeExpanded1 = range > avgRange1 * 1.30;

def trendUp1   = c > trendMA;
def trendDown1 = c < trendMA;

def bearShapeClimax_clean =
    strongUpperWick1 and
    smallBody1 and
    rangeExpanded1 and
    trendUp1;

def bullShapeClimax_clean =
    strongLowerWick1 and
    smallBody1 and
    rangeExpanded1 and
    trendDown1;

def ShapeBC =
    isBull and
    upperWickDominant and
    smallBody1 and
    wideSpread and
    trendUp1;

def ShapeSC =
    isBear and
    lowerWickDominant and
    smallBody1 and
    wideSpread and
    trendDown1;

################################
# TRAP DUAL-FIRE LOGIC
################################
def TrapUp =
    (WCUp and TwoBarClimaxDown) or
    (TwoBarClimaxDown and WCUp) or
    WCUp or
    TwoBarClimaxDown;

def TrapDown =
    (WCDown and TwoBarClimaxUp) or
    (TwoBarClimaxUp and WCDown) or
    WCDown or
    TwoBarClimaxUp;

###########################
# FINAL YTC EVENT STACK
###########################
def YTC_SC_Event =
    if SC then 1
    else if TwoBarClimaxDown then 1
    else if ShapeSC then 1
    else 0;

def YTC_BC_Event =
    if BC then 1
    else if TwoBarClimaxUp then 1
    else if ShapeBC then 1
    else 0;

def YTC_SC = YTC_SC_Event;
def YTC_BC = YTC_BC_Event;

def ClimaxSC = YTC_SC;
def ClimaxBC = YTC_BC;

##################
# YTC LINES
##################

def bn = BarNumber();
def NA = Double.NaN;

def cdbn = CompoundValue(1, if YTC_SC then bn else cdbn[1], 0);

def WhiteH = CompoundValue(1,
    if YTC_SC and showYTCLines and useYTC then h
    else WhiteH[1],
    NA);

plot WhiteHL =
    if useYTC and showYTCLines and currentOnlyYTC and bn >= HighestAll(cdbn) then WhiteH
    else if useYTC and showYTCLines and !currentOnlyYTC then WhiteH
    else NA;
WhiteHL.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
WhiteHL.SetDefaultColor(Color.WHITE);

def WhiteYTC = CompoundValue(1,
    if YTC_SC then (h - (h - l) / 3)
    else WhiteYTC[1],
    NA);

plot WhiteYTCL =
    if useYTC and showYTCLines and currentOnlyYTC and bn >= HighestAll(cdbn) then WhiteYTC
    else if useYTC and showYTCLines and !currentOnlyYTC then WhiteYTC
    else NA;
WhiteYTCL.SetPaintingStrategy(PaintingStrategy.DASHES);
WhiteYTCL.SetDefaultColor(Color.WHITE);

def WhiteL = CompoundValue(1,
    if YTC_SC then l
    else WhiteL[1],
    NA);

plot WhiteLL =
    if useYTC and showYTCLines and currentOnlyYTC and bn >= HighestAll(cdbn) then WhiteL
    else NA;
WhiteLL.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
WhiteLL.SetDefaultColor(Color.MAGENTA);

def rlbn = CompoundValue(1, if YTC_BC then bn else rlbn[1], 0);

def RedYTC = CompoundValue(1,
    if YTC_BC and showYTCLines and useYTC then ((h - l) / 3 + l)
    else RedYTC[1],
    NA);

plot RedYTCL =
    if useYTC and showYTCLines and currentOnlyYTC and bn >= HighestAll(rlbn) then RedYTC
    else if useYTC and showYTCLines and !currentOnlyYTC then RedYTC
    else NA;
RedYTCL.SetPaintingStrategy(PaintingStrategy.DASHES);
RedYTCL.SetDefaultColor(Color.LIGHT_RED);

def RedYTCLow = CompoundValue(1,
    if YTC_BC then l
    else RedYTCLow[1],
    NA);

def RedH = CompoundValue(1,
    if YTC_BC and showYTCLines and useYTC then h
    else RedH[1],
    NA);

plot RedYTCLowL =
    if useYTC and showYTCLines and currentOnlyYTC and bn >= HighestAll(rlbn) then RedYTCLow
    else if useYTC and showYTCLines and !currentOnlyYTC then RedYTCLow
    else NA;
RedYTCLowL.SetPaintingStrategy(PaintingStrategy.HORIZONTAL);
RedYTCLowL.SetDefaultColor(Color.RED);

plot RedHL =
    if useYTC and showYTCLines and currentOnlyYTC and bn >= HighestAll(rlbn) then RedH
    else if useYTC and showYTCLines and !currentOnlyYTC and (RedYTCLow < WhiteH) then RedH
    else NA;
RedHL.SetPaintingStrategy(PaintingStrategy.DASHES);
RedHL.SetDefaultColor(Color.YELLOW);

#################
# CHOP ZONE
#################

# True structural overlap (must match YTC logic)
def overlap = RedYTCLow <= WhiteHL and RedHL >= WhiteLL;

# Chop zone boundaries
def chopZoneHigh = Max(RedHL, WhiteHL);
def chopZoneLow  = Min(RedYTCLow, WhiteLL);

# Price inside the overlap zone
def insideOverlap = close <= chopZoneHigh and close >= chopZoneLow;

# Core chop state
def chopCore = overlap and insideOverlap;

# Hard breakout logic (candle must CLEAR the zone)
def bullBreakout = low > chopZoneHigh;   # full clearance above
def bearBreakout = high < chopZoneLow;   # full clearance below

def chopBreakout = bullBreakout or bearBreakout;

# Final chop state
def chop = chopCore and !chopBreakout;

AddLabel(choplabel and chop, "Chop", Color.YELLOW, pos);

############################################
# MARKER TOGGLES
############################################
input showVSAClimax   = yes;
input showNoDemand    = yes;
input showNoSupply    = yes;
input showStoppingVol = yes;
input showGapExhaust  = yes;
input showWeaknessBar = yes;

############################################
# GAP EXHAUST FLAG
############################################
def GapExhaust = GapExhaustUp or GapExhaustDown;

def weaknessBar = NoDemand;

plot VSAClimaxDownArrow = ShowArrows and showVSAClimax and BC;
VSAClimaxDownArrow.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
VSAClimaxDownArrow.SetLineWeight(3);
VSAClimaxDownArrow.AssignValueColor(Color.RED);
VSAClimaxDownArrow.HideBubble();
VSAClimaxDownArrow.HideTitle();

plot VSAClimaxUpArrow = ShowArrows and showVSAClimax and SC;
VSAClimaxUpArrow.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
VSAClimaxUpArrow.SetLineWeight(3);
VSAClimaxUpArrow.AssignValueColor(Color.WHITE);
VSAClimaxUpArrow.HideBubble();
VSAClimaxUpArrow.HideTitle();
####### ad on_
plot WickClimaxDownArrow = ShowArrows and showVSAClimax and WCDown;
WickClimaxDownArrow.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
WickClimaxDownArrow.AssignValueColor(Color.YELLOW);
WickClimaxDownArrow.SetLineWeight(2);

plot WickClimaxUpArrow = ShowArrows and showVSAClimax and WCUp;
WickClimaxUpArrow.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
WickClimaxUpArrow.AssignValueColor(Color.YELLOW);
WickClimaxUpArrow.SetLineWeight(2);

####################
# MAP TO TAPE TERMS
####################

def isBuyClimax  = ClimaxBC;  # Buying Climax (weakness)
def isSellClimax = ClimaxSC;  # Selling Climax (strength)

def anyStrength =
    ClimaxSC or
    (NoSupply and inUpTrend) or
    (isSpinner and inUpTrend);

def anyWeakness =
    ClimaxBC or
    (NoDemand and inDnTrend) or
    (isSpinner and inDnTrend);

############################################
# PRESSURE DIRECTION
############################################

def tapeUpPressure =
    ClimaxSC or
    YTC_SC or
    WcUp or
    msFlipUp or
    (inUpTrend and anyStrength);

def tapeDnPressure =
    ClimaxBC or
    YTC_BC or
    wcDown or
    msFlipDown or
    (inDnTrend and anyWeakness);

############################################
# LOW VOLUME LABELS
############################################

def LV_Up   = isLowVol and isBull;
def LV_Down = isLowVol and isBear;

############################################
# SHORT NAMES FOR LAST-3-BAR LABELS
############################################

def txtCode =
    if isBuyClimax then 1 else
    if isSellClimax then 2 else
    if isSpinner then 3 else
    if isBullPin then 4 else
    if isBearPin then 5 else
    if LV_Up then 6 else
    if LV_Down then 7 else
    0;

def txtCode1 = txtCode[1];
def txtCode2 = txtCode[2];

############################################
# COLOR MAP (TAPE LABELS)
############################################

AddLabel(
    tape, "[2]" +
    if txtCode2 == 1 then "BuyClimax"
    else if txtCode2 == 2 then "SellClimax"
    else if txtCode2 == 3 then "SpinTop"
    else if txtCode2 == 4 then "BullPin"
    else if txtCode2 == 5 then "BearPin"
    else if txtCode2 == 6 then "LV Up"
    else if txtCode2 == 7 then "LV Down"
    else " … ",
    if txtCode2 == 1 then Color.RED
    else if txtCode2 == 2 then Color.WHITE
    else if txtCode2 == 3 then Color.MAGENTA
    else if txtCode2 == 4 then Color.GREEN
    else if txtCode2 == 5 then Color.RED
    else if txtCode2 == 6 then Color.LIGHT_RED
    else if txtCode2 == 7 then Color.LIGHT_GREEN
    else Color.LIGHT_GRAY, pos
);

AddLabel(
    tape, "[1]" +
    if txtCode1 == 1 then "BuyClimax"
    else if txtCode1 == 2 then "SellClimax"
    else if txtCode1 == 3 then "SpinTop"
    else if txtCode1 == 4 then "BullPin"
    else if txtCode1 == 5 then "BearPin"
    else if txtCode1 == 6 then "LV Up"
    else if txtCode1 == 7 then "LV Down"
    else " … ",
    if txtCode1 == 1 then Color.RED
    else if txtCode1 == 2 then Color.WHITE
    else if txtCode1 == 3 then Color.MAGENTA
    else if txtCode1 == 4 then Color.GREEN
    else if txtCode1 == 5 then Color.RED
    else if txtCode1 == 6 then Color.LIGHT_RED
    else if txtCode1 == 7 then Color.LIGHT_GREEN
    else Color.LIGHT_GRAY, pos
);

AddLabel(
    tape,
    "[0]" +
    (if txtCode == 1 then "BuyClimax"
     else if txtCode == 2 then "SellClimax"
     else if txtCode == 3 then "SpinTop"
     else if txtCode == 4 then "BullPin"
     else if txtCode == 5 then "BearPin"
     else if txtCode == 6 then "LV Up"
     else if txtCode == 7 then "LV Down"
     else " … "),
    (if txtCode == 1 then Color.RED
     else if txtCode == 2 then Color.WHITE
     else if txtCode == 3 then Color.MAGENTA
     else if txtCode == 4 then Color.GREEN
     else if txtCode == 5 then Color.RED
     else if txtCode == 6 then Color.LIGHT_RED
     else if txtCode == 7 then Color.LIGHT_GREEN
     else Color.LIGHT_GRAY), pos
);

############################################
# MICRO CHURN / COMPRESSION
############################################

def isChurn = isSpinner and isLowVol;
def anyCompression = isChurn;

########################
# EXHAUSTION POLARITY
########################

def bullExhaust = (WcDown or YTC_BC);   # weakness → bull trap path
def bearExhaust = (WcUp   or YTC_SC);   # strength → bear trap path

#################
# TRAP CORE
#################

def bullTrapCore = bullExhaust;
def bearTrapCore = bearExhaust;

def anyTrapCore = bullTrapCore or bearTrapCore;

######################
# MICRO COLLAPSE
######################

def microCollapse = msContracting or msFlipDown or msFlipUp;

#####################
# REAL TRAPS
#####################

def bullTrap = bullTrapCore and microCollapse;
def bearTrap = bearTrapCore and microCollapse;

def anyTrap = bullTrap or bearTrap;

####################
# MICRO TRAPS
####################

def microBullTrap =
    bullTrapCore and microCollapse and
    (
        isBearPin or
        (isSpinner and isBull[1])
    );

def microBearTrap =
    bearTrapCore and microCollapse and
    (
        isBullPin or
        (isSpinner and isBear[1])
    );

def trapTypeMicro =
    if microBullTrap then 1
    else if microBearTrap then 2
    else 0;

############################################
# CLIMAX TRAPS
############################################

def isClimaxTrapBull = bullTrapCore and ClimaxBC;
def isClimaxTrapBear = bearTrapCore and ClimaxSC;

################
# TRAP TYPE
################

def trapType =
    if bullTrap then 1
    else if bearTrap then 2
    else if trapTypeMicro == 1 then 3
    else if trapTypeMicro == 2 then 4
    else if isClimaxTrapBull or isClimaxTrapBear then 5
    else 0;

####################
# CLIMAX MEMORY
####################

input climaxLookback = 6;

def lastBullExhaust = Highest(bearExhaust, climaxLookback) > 0;  # SC = strength
def lastBearExhaust = Highest(bullExhaust, climaxLookback) > 0;  # BC = weakness

############################################
# TRAP SETUP (DIRECTIONAL — BEST GUESS)
############################################

# Raw directional setups from exhaustion memory
def rawSetupBull = bullTrapCore and !anyTrap and lastBearExhaust;   # BC → weakness → bull trap path
def rawSetupBear = bearTrapCore and !anyTrap and lastBullExhaust;   # SC → strength → bear trap path

# Ambiguity conditions
def bothPossible = rawSetupBull and rawSetupBear;
def nonePossible = !rawSetupBull and !rawSetupBear;

# Trend bias for tie‑breaking
def trendBull = inUpTrend;
def trendBear = inDnTrend;

# Candle bias fallback
def candleBull = isBull;
def candleBear = isBear;

# Final directional trap setup (best guess)
def trapSetupBull =
    !anyTrap and (
        rawSetupBull
        or (bothPossible and trendBull)
        or (bothPossible and !trendBull and !trendBear and candleBull)
        or (nonePossible and trendBull)
        or (nonePossible and !trendBull and !trendBear and candleBull)
    );

def trapSetupBear =
    !anyTrap and (
        rawSetupBear
        or (bothPossible and trendBear)
        or (bothPossible and !trendBull and !trendBear and candleBear)
        or (nonePossible and trendBear)
        or (nonePossible and !trendBull and !trendBear and candleBear)
    );



############################################
# TRAP SEVERITY
############################################

def trapSeverity =
    (if anyTrap then 1 else 0) +
    (if trapTypeMicro > 0 then 1 else 0) +
    (if isClimaxTrapBull or isClimaxTrapBear then 1 else 0) +
    (if anyTrapCore then 1 else 0) +
    (if microBullTrap or microBearTrap then 1 else 0) +
    (if isFatTailEvent then 1 else 0);

############################################
# NARRATIVE WINDOWS
############################################

input narrLen = 4;

def strengthWindow    = Highest(anyStrength, narrLen) > 0;
def weaknessWindow    = Highest(anyWeakness, narrLen) > 0;
def compressionWindow = Highest(anyCompression, narrLen) > 0;
def trapWindow        = Highest(anyTrap, narrLen) > 0;

############################################
# PRO CLASS
############################################

def proClass =
    if trapWindow then 4
    else if strengthWindow and !weaknessWindow then 1
    else if weaknessWindow and !strengthWindow then 2
    else if compressionWindow then 3
    else 0;

#########################
# PRO SUBCATEGORY
#########################

def proSubCat =
    if proClass == 1 and StoppingVol then 11
    else if proClass == 1 and NoSupply then 12
    else if proClass == 2 and bullExhaust then 21
    else if proClass == 2 and NoDemand then 22
    else if proClass == 3 and anyCompression then 31
    else if proClass == 4 and trapType == 1 then 41     # Bull Trap
    else if proClass == 4 and trapType == 2 then 42     # Bear Trap
    else if proClass == 4 and trapType == 3 then 43     # Micro Bull Trap
    else if proClass == 4 and trapType == 4 then 44     # Micro Bear Trap
    else if proClass == 4 and trapType == 5 then 45     # Climax Trap
    else if proClass == 4 and trapSetupBull then 46     # Trap Setup Bull
    else if proClass == 4 and trapSetupBear then 47     # Trap Setup Bear
    else if proClass == 4 and anyTrap then 40           # Generic Trap Pattern
    else 0;

############################################
# TRAP FOOTPRINT
############################################

def isTrapFootprint = anyTrapCore and !anyTrap;

############################################
# PRO LABEL
############################################

AddLabel(
    pro,
         (if proClass == 1 then "Strength Building"
     else if proClass == 2 then "Weakness Building"
     else if proClass == 3 then "Compression"
    
     # Trap Setup header ONLY when it's actually a setup
     else if proClass == 4 and (proSubCat == 0 or proSubCat == 46 or proSubCat == 47) then "Trap Setup"
    
     # Confirmed traps get NO prefix
     else if proClass == 4 and (proSubCat == 41 or proSubCat == 42 or proSubCat == 43 or proSubCat == 44 or proSubCat == 45) then ""
    
     else "Neutral")
    +
    (if proSubCat == 11 then " - Stopping Volume"
     else if proSubCat == 12 then " - No Supply"
     else if proSubCat == 21 then " - Buying Climax"
     else if proSubCat == 22 then " - No Demand"
     else if proSubCat == 31 then " - Low RVOL Coil"
     else if proSubCat == 40 then " - Trap Pattern"
    
     # Confirmed traps
     else if proSubCat == 41 then "Bull Trap"
     else if proSubCat == 42 then "Bear Trap"
     else if proSubCat == 43 then "Micro Bull Trap"
     else if proSubCat == 44 then "Micro Bear Trap"
     else if proSubCat == 45 then "Climax Trap"
    
     # Setups
     else if proSubCat == 46 then " - Bull"
     else if proSubCat == 47 then " - Bear"
     else "")
    +
    (if trapType > 0 then "  Sev: " + trapSeverity else "")
    +
    (if isTrapFootprint and trapType == 1 then " - Trap Footprint Bull"
     else if isTrapFootprint and trapType == 2 then " - Trap Footprint Bear"
     else ""),
 (
    if proClass == 4 and proSubCat == 0 then Color.ORANGE
    else if proClass == 4 and (proSubCat == 46 or proSubCat == 47) then Color.CYAN   
    else if proClass == 4 then Color.MAGENTA
    else if proClass == 1 then CreateColor(140, 220, 140)
    else if proClass == 2 then CreateColor(255, 140, 140)
    else if proClass == 3 then Color.YELLOW
    else Color.LIGHT_GRAY), pos
);


###########################
# CLIMAX DEBUG BUBBLES
###########################

input showClimaxBubble = no;

AddChartBubble(
    showClimaxBubble and ClimaxBC,      # Buying Climax (weakness)
    high,
    "BC",
    Color.GREEN,
    yes
);

AddChartBubble(
    showClimaxBubble and ClimaxSC,      # Selling Climax (strength)
    low,
    "SC",
    Color.RED,
    no
);
 

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

Similar threads

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
1432 Online
Create Post

Similar threads

Similar threads

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top