Price-Volume Pattern Detector For ThinkOrSwim

justAnotherTrader

Active member
VIP
VIP Enthusiast
Shared Chart Link: http://tos.mx/!YAXsXIxY MUST follow these instructions for loading shared links.

LiDO1ln.png


Case Study: I want you to notice the little yellow Bullish Exhaustion bubble. The volume there was decreasing 3 bars in a row. If you look up at the price chart the price was increasing over the same 3 bars. This divergence triggered the bubble, telling us the volume wasnt keeping up with the price


Okay guys, this is a work in progress but I felt there was enough here to post about it. This is my volume pattern recognizer. It uses the idea of volume pressure from this post here with a bit of a twist and some pattern seeking behaviors.

First of all note the different colors our histogram can take:
  • Black : This means that the trading volume was not significant AND the candle body sized was less than half of the ATR
    • This helps us ignore some data points and help us to focus on volume that matters. This is an adjustable paramater
  • Cyan: This means that volume was significant but the body size was small, this is a could indicator of a possible pivot point. Notice the image above
  • Green/Red: If the price action exceeded our body size requirements then you will see our best estimate of buyers vs sellers

Next note the line. That line is actually a twist on the Chaikin Money Flow Line that works like this:
  • If CMF > .1 (indicating strong positive money flow) then the CMF line will be above the volume
    • If the slope of CMF is positive then the line will be green, otherwise red
** This gives us an idea of the short term trend based off our volume and pairs well with the chart

Next you have our bubbles for our context pattern scenario analysis:
  • Bearish Exhaustion: If price is decreasing but volume is also decreasing
  • Bullish Exhaustion: If Price is increasing but volume is decreasing
  • Bear Rev + Vol Surge: Bearish reversal with volume increase-decrease-increase
  • Bear Rev + Rising Vol: Bearish reversal with increasing volume
  • (The same for the bullish side)
  • ... I plan on adding more patterns later

I hope you enjoy. Happy Trading!

Code:
# === 3-Bar Price / Volume Pattern Detector ===
# Shows buying/selling pressure with separate volume histograms
# Patterns shown with bubbles only

########################################
# ========= USER INPUTS ===============
########################################
input showPlot               = yes;  # master on/off
input showBubbles            = yes;  # show pattern bubbles
input lineWeight             = 3;
input minBarsNeeded          = 4;    # Minimum bars needed for patterns
input atr_length             = 14;   # ATR period length
input BodyToATR_Threshold    = 0.5;  # Body must be this % of ATR to color by control
input volZScoreThres         = 1.2;  # Z-score threshold for volume significance
input volLookback            = 20;   # Lookback period for volume z-score calculation
input volDeclineThreshold    = 0.95; # Volume must decline to this % of bar[2] for exhaustion signals
input volIncreaseThreshold   = 1.05; # Volume must increase by this % between bars for surge signals

########################################
# ======== PATTERN DETECTION ==========
########################################
# Check if we have enough bars for pattern detection
def hasEnoughBars = BarNumber() >= minBarsNeeded;

# --- Price Patterns ---
# Compare bar 0 vs bar 1, bar 1 vs bar 2, bar 2 vs bar 3:
def lh_ll_0 = if hasEnoughBars then high < high[1] and low < low[1] else no;
def hh_hl_0 = if hasEnoughBars then high > high[1] and low > low[1] else no;
def lh_ll_1 = if hasEnoughBars then high[1] < high[2] and low[1] < low[2] else no;
def hh_hl_1 = if hasEnoughBars then high[1] > high[2] and low[1] > low[2] else no;
def lh_ll_2 = if hasEnoughBars then high[2] < high[3] and low[2] < low[3] else no;
def hh_hl_2 = if hasEnoughBars then high[2] > high[3] and low[2] > low[3] else no;

# "2-down then 1-up" reversal
def priceRevUp  = lh_ll_2 and lh_ll_1 and hh_hl_0;
# "2-up then 1-down" reversal
def priceRevDn  = hh_hl_2 and hh_hl_1 and lh_ll_0;

# Price trend patterns (for divergence detection)
def priceUp3 = if hasEnoughBars then close > close[1] and close[1] > close[2] else no;
def priceDn3 = if hasEnoughBars then close < close[1] and close[1] < close[2] else no;

########################################
# ========== VOLUME PATTERNS ==========
########################################
# Three‐bar volume ↑↑↑ with percentage threshold
def volInc3 = if hasEnoughBars then
              volume > volume[1] * volIncreaseThreshold and
              volume[1] > volume[2] * volIncreaseThreshold and
              (volume / volume[2]) > volIncreaseThreshold * volIncreaseThreshold # Ensure significant increase
              else no;
                    
# Three‐bar volume ↓↓↓ with percentage threshold
def volDec3 = if hasEnoughBars then
              volume < volume[1] * volDeclineThreshold and
              volume[1] < volume[2] * volDeclineThreshold and
              (volume / volume[2]) < volDeclineThreshold * volDeclineThreshold # Ensure significant decline
              else no;
                    
# Three‐bar volume ↑↓↑
def volIncDecInc = if hasEnoughBars then
                   volume > volume[1] * volIncreaseThreshold and
                   volume[1] < volume[2] * volDeclineThreshold
                   else no;

########################################
# ========== VOLUME Z-SCORE ===========
########################################
# Calculate volume Z-score (how many standard deviations from mean)
def volAvg = Average(volume, volLookback);
def volSD = StDev(volume, volLookback);
def volZ = if volSD != 0 then (volume - volAvg) / volSD else 0;

# Define volume significance conditions
def highVolume = volZ > volZScoreThres;
def lowVolume = volZ <= volZScoreThres;

########################################
# ========== BUYER/SELLER CONTROL ====
########################################
# Calculate ATR
def atr = Average(TrueRange(high, close, low), atr_length);

# Calculate candle body and compare to ATR
def body = AbsValue(close - open);
def bodyToATR = body / atr;
def significantBody = bodyToATR > BodyToATR_Threshold;
def insignificantBody = !significantBody;

# Check for significant bodies in previous candles
def body_1 = AbsValue(close[1] - open[1]);
def bodyToATR_1 = body_1 / atr;
def significantBody_1 = bodyToATR_1 > BodyToATR_Threshold;

def body_2 = AbsValue(close[2] - open[2]);
def bodyToATR_2 = body_2 / atr;
def significantBody_2 = bodyToATR_2 > BodyToATR_Threshold;

# Calculate buyer/seller control percentages
def totalRange = high - low;
def buyerControl = if totalRange != 0 then (close - low) / totalRange else 0.5;
def sellerControl = 1 - buyerControl;

# Calculate buying and selling volume components
def buyingVolume = volume * buyerControl;
def sellingVolume = volume * sellerControl;

########################################
# ============ PATTERN LOGIC ==========
########################################
# Basic price + volume patterns
def condRevUp_IncVol      = priceRevUp and volInc3 and significantBody_1 and significantBody_2;
def condRevUp_IncDecInc   = priceRevUp and volIncDecInc and significantBody_1 and significantBody_2;
def condRevDn_IncVol      = priceRevDn and volInc3 and significantBody_1 and significantBody_2;
def condRevDn_IncDecInc   = priceRevDn and volIncDecInc and significantBody_1 and significantBody_2;

# Divergence patterns with significant body requirement
def bullishExhaustion = priceUp3 and volDec3 and significantBody_1 and significantBody_2; 
def bearishExhaustion = priceDn3 and volDec3 and significantBody_1 and significantBody_2; 

########################################
# ============= LABELS ===============
########################################
# Add informative context labels
AddLabel(yes, "Volume Z-Score: " + Round(volZ, 1),
         if highVolume then Color.CYAN else if lowVolume then Color.DARK_GRAY else Color.WHITE);

AddLabel(yes, "Body/ATR: " + Round(bodyToATR, 2),
         if significantBody then Color.WHITE else Color.GRAY);
        
AddLabel(yes, "Buyer Control: " + Round(buyerControl * 100, 0) + "%",
         if buyerControl > 0.7 then Color.GREEN else if buyerControl < 0.3 then Color.RED else Color.WHITE);

# Volume trend labels
AddLabel(volInc3, "Volume Surging", Color.CYAN);
AddLabel(volDec3, "Volume Fading", Color.DARK_GRAY);
AddLabel(volIncDecInc, "Volume Spike", Color.MAGENTA);

########################################
# ============== BUBBLES ==============
########################################
# Add bubbles at the bottom/top of the chart for pattern detection
def bubbleY = if !showBubbles then Double.NaN else (low - (0.05 * (high - low)));
def bubbleYTop = if !showBubbles then Double.NaN else (high + (0.05 * (high - low)));

# Bullish reversal with increasing volume
AddChartBubble(
    condRevUp_IncVol,
    bubbleY,
    "Bull Rev + Rising Vol",
    Color.CYAN,
    no
);

# Bullish reversal with volume increase-decrease-increase
AddChartBubble(
    condRevUp_IncDecInc,
    bubbleY,
    "Bull Rev + Vol Surge",
    Color.GREEN,
    no
);

# Bearish reversal with increasing volume
AddChartBubble(
    condRevDn_IncVol,
    bubbleYTop,
    "Bear Rev + Rising Vol",
    Color.MAGENTA,
    yes
);

# Bearish reversal with volume increase-decrease-increase
AddChartBubble(
    condRevDn_IncDecInc,
    bubbleYTop,
    "Bear Rev + Vol Surge",
    Color.ORANGE,
    yes
);

# Bullish exhaustion (divergence - price up but volume declining)
AddChartBubble(
    bullishExhaustion,
    bubbleYTop,
    "Bullish Exhaustion",
    Color.YELLOW,
    yes
);

# Bearish exhaustion (divergence - price down but volume declining)
AddChartBubble(
    bearishExhaustion,
    bubbleY,
    "Bearish Exhaustion",
    CreateColor(255, 165, 0), # Orange
    no
);

########################################
# =========== TWISTED CMF =============
########################################
# Chaikin Money Flow calculation
def mfMultiplier = if high != low then ((close - low) - (high - close)) / (high - low) else 0;
def mfVolume = mfMultiplier * volume;
def cmf = Average(mfVolume, 20) / Average(volume, 20);
def cmfSlope = cmf - cmf[1];

# Add CMF value label
AddLabel(yes, "CMF: " + Round(cmf, 2),
         if cmf > 0.1 then (if cmfSlope >= 0 then Color.GREEN else Color.RED)
         else Color.DARK_GRAY);

# Twisted CMF line - shows at volume level when CMF > 0.1, otherwise at zero
plot CMFLine = if cmf > 0.1 then volume else 0;
CMFLine.AssignValueColor(if cmfSlope >= 0 then Color.GREEN else Color.RED);
CMFLine.SetPaintingStrategy(PaintingStrategy.LINE);
CMFLine.SetLineWeight(2);

########################################
# ========= VOLUME HISTOGRAMS =========
########################################
# Selling volume histogram
plot SV = if showPlot and hasEnoughBars then sellingVolume else Double.NaN;
SV.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
SV.SetLineWeight(lineWeight);
SV.AssignValueColor(
    # First check body significance, then volume levels for all cases
    if insignificantBody then
        if highVolume then Color.CYAN else Color.DARK_GRAY
    else # Significant body
        if sellerControl >= 0.9 then Color.DARK_RED
        else if sellerControl >= 0.8 then CreateColor(180, 0, 0)
        else if sellerControl >= 0.7 then CreateColor(160, 0, 0)
        else if sellerControl >= 0.6 then CreateColor(140, 0, 0)
        else CreateColor(120, 0, 0)
);

# Buying volume histogram
plot BV = if showPlot and hasEnoughBars then buyingVolume else Double.NaN;
BV.SetPaintingStrategy(PaintingStrategy.HISTOGRAM);
BV.SetLineWeight(lineWeight);
BV.AssignValueColor(
    # First check body significance, then volume levels for all cases
    if insignificantBody then
        if highVolume then Color.CYAN else Color.DARK_GRAY
    else # Significant body
        if buyerControl >= 0.9 then Color.DARK_GREEN
        else if buyerControl >= 0.8 then CreateColor(0, 180, 0)
        else if buyerControl >= 0.7 then CreateColor(0, 160, 0)
        else if buyerControl >= 0.6 then CreateColor(0, 140, 0)
        else CreateColor(0, 120, 0)
);

########################################
# =========== VWAP COMPARISON =========
########################################
# Calculate VWAP
def vwap = VWAP();

# Calculate distance from close to VWAP
def closeToVWAP = close - vwap;
def closeToVWAP_Normalized = AbsValue(closeToVWAP) / atr;

# Determine position relative to VWAP using same body threshold
def aboveVWAP_Significant = closeToVWAP > 0 and closeToVWAP_Normalized > BodyToATR_Threshold;
def belowVWAP_Significant = closeToVWAP < 0 and closeToVWAP_Normalized > BodyToATR_Threshold;
def nearVWAP = !aboveVWAP_Significant and !belowVWAP_Significant;

# Prepare label text
def vwapPct = (closeToVWAP / vwap) * 100;
AddLabel(yes,
    "VWAP: " + if aboveVWAP_Significant then
                  "+$" + Round(AbsValue(closeToVWAP), 2) + " (" + Round(AbsValue(vwapPct), 1) + "%)" + " Above"
               else if belowVWAP_Significant then
                  "-$" + Round(AbsValue(closeToVWAP), 2) + " (" + Round(AbsValue(vwapPct), 1) + "%)" + " Below"
               else
                  "Near ±" + Round(BodyToATR_Threshold * atr, 2),
    if aboveVWAP_Significant then Color.GREEN
    else if belowVWAP_Significant then Color.RED
    else Color.GRAY
);
 

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
476 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