TTM Squeeze Format, Scan, Watchlist, Label For ThinkOrSwim

BenTen

Administrative
Staff member
Staff
VIP
For those of you who enjoy using the TTM Squeeze & Momentum indicator, this should be a handy addition to your ThinkorSwim chart setup. The script will add the status of the TTM Squeeze for stocks on your watchlist via a new column. You can select whichever timeframe you would like to scan for TTM squeeze.

Stocks usually break out of consolidations and by having the TTM Squeeze on your watchlist it helps to alert when they do.

Here is what each signal and labels represent:
  • Bright Red: The stock is in Buy zone
  • Dark Red: Market compression is currently in this time period. The number reveals the amount of dots on your TTM Squeeze indicator.
  • Dark Green: The squeeze has fired. The number notes how many dots (up to 5) and whether the squeeze signaled Long or Short.
  • Black: No squeeze on this time frame

YexBBcI.png


thinkScript Code

Code:
# Squeeze watchlist column
#   Red Background   - Squeeze is building in the time period - number indicates how many dots
#      w/ white "B"  - Squeeze is building, stock is within "buy zone"
#   Green Background - Squeeze has fired - number indicates how many dots (up to 5 dots)
#                      and whether Squeeze fired L(ong) or S(hort)
#   Black Background - No Squeeze in play

# Original code by Eric Purdy of Simpler Trading 2017
# Modified code by Rich Stratmann to put in background colors and shorten column values
# Updates by dmccuskey
#   - change background colors to aid content scanning
#   - add "B" to time frames which are in the Buy Zone - between 8 & 21 EMA
#   - change "0" to " " <space> so that the content doesn't show when row is highlighted

#ToS Share Code: https://tos.mx/IPrEhH

def sqz = !TTM_Squeeze().SqueezeAlert;
def direction = TTM_Squeeze()>TTM_Squeeze()[1];
def count = if sqz and !sqz[1] then 1 else count[1]+1;
def isFired = if !sqz and sqz[1] then 1 else 0;
def firedCount = if isFired then 1 else firedCount[1]+1;
def firedDirection = if isFired then direction else firedDirection[1];

def sumIsFired = sum(isFired,5);
def isFiredDir = sumIsFired && firedDirection;

# look for close buy zone
def ema8 = reference movAvgExponential(length=8);
def ema21 = reference movAvgExponential(length=21);
def currPrice = close();
def highVal = Max(ema8, ema21);
def lowVal = Min(ema8, ema21);
def inBuyZone = currPrice >= lowVal && currPrice <= highVal;

def sqzBuy = sqz && inBuyZone;
def sqzNoBuy = sqz && !inBuyZone;

addLabel(yes, Concat(if sqzBuy then "B " else "", if sqz then "" + count else if sumIsFired then “” + firedCount + if firedDirection then ” L” else ” S” else “ ”), if sqzBuy then color.white else color.black);

AssignBackgroundColor(if sqzNoBuy then  CreateColor(170, 6, 0) else if sqzBuy then color.red else if sumIsFired then CreateColor(28, 105, 3) else color.black);

Shareable Link

https://tos.mx/uXplYi

Credits:
 
Last edited:

BC1106

New member
2019 Donor
VIP
Code:
def squeeze = if(reference BollingerBands()."upperband" - KeltnerChannels()."Upper_Band")<0 then 1 else 0;

def insqueeze = if squeeze then insqueeze[1] + 1 else 0;

def fired = if !squeeze then fired[1]+1 else 0;

def direction = if (fired == 1 , if( TTM_Squeeze() > TTM_Squeeze()[1] , 1 , 0 ),  direction[1]);

plot result = if insqueeze > 0 then insqueeze else if fired >0 and fired <8 then fired else 0;

AddLabel(yes, Concat(if insqueeze>0 then "Squeeze: " else if fired >0 and fired <8 then Concat("FIRED: ", if direction then "? " else "? ") else "", result), if insqueeze>0 then color.white else color.black);

AssignBackgroundColor( if insqueeze>0 then color.red else if fired>0 and fired <8 then color.green else color.black);

def squeeze = if(reference BollingerBands()."upperband" - KeltnerChannels()."Upper_Band")<0 then 1 else 0;

def insqueeze = if squeeze then insqueeze[1] + 1 else 0;

def fired = if !squeeze then fired[1]+1 else 0;

def direction = if (fired == 1 , if( TTM_Squeeze() > TTM_Squeeze()[1] , 1 , 0 ),  direction[1]);

plot result = if insqueeze > 0 then insqueeze else if fired >0 and fired <8 then fired else 0;

AddLabel(yes, Concat(if insqueeze>0 then "Squeeze: " else if fired >0 and fired <8 then Concat("FIRED: ", if direction then "? " else "? ") else "", result), if insqueeze>0 then color.white else color.black);

AssignBackgroundColor( if insqueeze>0 then color.red else if fired>0 and fired <8 then color.green else color.black);

This is the code i use for my watchlist column, very similar to yours in fact it may be the exact same, but Im not 100% sure since i know nothing about coding. But visually its slightly different and you can customize to whatever timeframe you want, it just wont say if it fired long or short.
 

dvorakm

New member
2019 Donor
VIP
Does anyone know how to program a scan using the TTM Squeeze indicator that will indicate that a squeeze has fired LONG within 15 bars with NO loss of momentum?

Thanks,
Mike D.
 

tomsk

Well-known member
VIP
Saw some discussions on squeeze watchlists on some other threads, thought I'd ahre what I have.
Here's a version from my files

Code:
# TTM Squeeze Watchlist
# TSL
# 11.13.2019

input price = close;
input length = 20;
input nK = 1.5;
input nBB = 2.0;
input alertLine = 1.0;

def squeezeDots = TTM_Squeeze(price, length, nK, nBB, alertLine).SqueezeAlert;
def alertCount = if squeezeDots[1] == 0 and squeezeDots == 1 then 1 
                 else if squeezeDots == 1 then alertCount[1] + 1 
                 else 0;
plot data = alertCount;
data.SetDefaultColor(Color.BLACK);

def squeezeHistogram = TTM_Squeeze(price, length, nK, nBB, alertLine).Histogram;
AssignBackgroundColor(if squeezeHistogram >= 0 
                      then if squeezeHistogram > squeezeHistogram[1] then Color.CYAN else Color.BLUE
                      else if squeezeHistogram < squeezeHistogram[1] then Color.RED else Color.YELLOW);
 

Rojo Grande

Member
VIP
Hello Ben, In the original TTM squeeze, it's possible to change the length from the default of 20 to your liking. Can adjustable length be added to this study?

I was looking for that and cannot find it (I am referring to the code in post #1 above) not to tomsk in post #3. I did however find another TTM squeeze alert that I was able to find and change the length, so no need to alter anything on my account. Again, thanks to all who contribute to the best site I know.
 

horserider

Well-known member
VIP
Here's a squeeze count for Watchlist from Mobius, you can adapt this for us on a study if you wish

Code:
# Squeeze Count Watchlist
# Mobius
# 11.09.2015

def Squeeze = BollingerBands().UpperBand < KeltnerChannels().Upper_Band;
def count = if !Squeeze
            then 0
            else if Squeeze
                 then count[1] + 1
                 else count[1];
AddLabel(1, if Squeeze then count else 0,
            if Squeeze
            then Color.Black
            else Color.WHITE);
AssignBackgroundColor(if Squeeze then color.green else color.white);
 

sbtyme

New member
@BenTen - Thanks for this. I'm new to Think Script. I've uploaded the indicator to my watch list, no problem but I don't see how to determine/switch which time frame I want the indicator reflect for the squeeze (ex- 5 min or 30 min or.....).
Thanks in advance.
 
Code:
# This 'Squeeze Watchlist Column' shows when a stock is currently in a squeeze or the number of bars ago that the squeeze ended. The number of bars in a squeeze is shown on a red background using '=?' or '>=13'. When larger than 12 bars, the value is shown as >=13. The bars since the squeeze ended is shown in green as '-? ago' where ? is a maximum of 6.
#The suggested title is 'SQZ'.
# This issued version is based on a 1 minute aggregation that can be changed by editing the code and changing the aggregation at the top of the edit screen. It is meaningful to have the column aggregation match the chart aggregation to avoid confusion.
#Revised 9/29/12

def keltnerChannelATRs = 1.5 ;
def bollingerBandStdDev = 2 ;

# Average True Range
def ATR = AvgTrueRange(high, close, low, 20);

# Standard Deviation
def SDev = stdev(close, 20);

# -- Calculate Bollinger Band Squeeze Indicator --
# for alert
def Denom = (keltnerChannelATRs * ATR);
def BBS_Ind = if (Denom <> 0, ((bollingerBandStdDev * SDev) / Denom), 0);#A squeeze is on if BBS_Ind < 1.0

#Count number of squeeze bars
def Sq_Count_0 = If BBS_Ind < 1.0 then 1 else 0;
def Sq_Count_1 = If BBS_Ind[1] < 1.0 && Sq_Count_0 == 1 then 1 else 0;
def Sq_Count_2 = If BBS_Ind[2] < 1.0  && Sq_Count_1 == 1 then 1 else  0;
def Sq_Count_3 = If BBS_Ind[3] < 1.0 && Sq_Count_2 == 1 then 1 else 0;
def Sq_Count_4 = If BBS_Ind[4] < 1.0 && Sq_Count_3 == 1 then 1 else 0;
def Sq_Count_5 = If BBS_Ind[5] < 1.0 && Sq_Count_4 == 1 then 1 else 0;
def Sq_Count_6 = If BBS_Ind[6] < 1.0 && Sq_Count_5 == 1 then 1 else 0;
def Sq_Count_7 = If BBS_Ind[7] < 1.0 && Sq_Count_6 == 1 then 1 else 0;
def Sq_Count_8 = If BBS_Ind[8] < 1.0 && Sq_Count_7 == 1 then 1 else 0;
def Sq_Count_9 = If BBS_Ind[9] < 1.0 && Sq_Count_8 == 1 then 1 else 0;
def Sq_Count_10 = If BBS_Ind[10] < 1.0 && Sq_Count_9 == 1 then 1 else 0;
def Sq_Count_11 = If BBS_Ind[11] < 1.0 && Sq_Count_10 == 1 then 1 else 0;
def Sq_Count_12 = If BBS_Ind[12] < 1.0 && Sq_Count_11 == 1 then 1 else 0;
def Sq_Count = if BBS_Ind < 1.0 && Sq_Count_0 == 1 then (Sq_Count_0 + Sq_Count_1 + Sq_Count_2 + Sq_Count_3 + Sq_Count_4 + Sq_Count_5 + Sq_Count_6 + Sq_Count_7 + Sq_Count_8 + Sq_Count_9 + Sq_Count_10 + Sq_Count_11 + Sq_Count_12) else 0;

Def Post_Count = if BBS_Ind[1] < 1.0  && BBS_Ind[0] > 1.0 then 1
else if BBS_Ind[2] < 1.0  && BBS_Ind[1] > 1.0 then 2
else if BBS_Ind[3] < 1.0  && BBS_Ind[2] > 1.0 then 3
else if BBS_Ind[4] < 1.0  && BBS_Ind[3] > 1.0 then 4
else if BBS_Ind[5] < 1.0 && BBS_Ind[4] > 1.0  then 5
else if BBS_Ind[6] < 1.0 && BBS_Ind[5] > 1.0 then 6
else Double.nan;

#plot data = post_Count;#Used for testing

AddLabel(yes,if Sq_Count == 13 then Concat(">=" , Sq_Count) else if between(Sq_Count,1,12) then concat("=", Sq_Count) else if between(Post_Count,1,6) then concat("-",Concat(Post_Count," ago")) else "0");

Assignbackgroundcolor (if (Sq_Count >= 13) then color.Dark_red
else if between(Sq_Count,1,12)  then color.red
else if between(Post_Count,1,6) then color.Dark_green
else color.current);


######### EOC ###########
 

BayTrader_93

New member
Hey everyone, I have this code I'm using in my watchlist but I wanted to adjust it so that instead of black backgrounds and color words, I wanted to flip flop it so that the background is colored so that way when the squeeze lines up on multiple time frames, I can see it more clearly. Pretty new to coding, and more specifically think script so just reaching out for some help.

Thanks!

Code:
# Simpler Trading Squeeze watchlist column - Eric Purdy 2017

# Shared Link -    https://tos.mx/5cpgKa
#Start
def sqz = !TTM_Squeeze().SqueezeAlert;
def direction = TTM_Squeeze()>TTM_Squeeze()[1];
def count = if sqz and !sqz[1] then 1 else count[1]+1;
def fired = if !sqz and sqz[1] then 1 else 0;
def firedCount = if fired then 1 else firedCount[1]+1;
def firedDirection = if fired then direction else firedDirection[1];
addLabel(yes, if sqz then “Squeeze:” + count else if sum(fired,5) then “Fired:” + firedCOunt + if firedDirection then ” Long” else ” Short” else “-”, if sqz then color.red else if sum(fired,5) and firedDirection then color.green else color.orange);
#Finish
 

TIGER202020

New member
Ben, thank you for the script and load it for my watchlist. But What's the time frame for the TTM squeeze being used for the watchlist? where can we set up timeframe?

Never mind. it can be modified when column is added.
 
Last edited by a moderator:

Samrock

New member
I use the TTM Squeeze a lot. Can someone make an custom scan to create a watchlist that tells me which stocks with TTM squeeze just turned Red to Yellow within a day and drop off from the watchlist when TTM squeeze turns from light blue to Dark Blue losing momentum.
 

pk1729

Member
VIP
Following code has 4 different scans including 2 you need. Play with all 4 and figure out what you need. Have fun!

Code:
declare lower;

input price = CLOSE;
input length = 20;
input nK = 1.5;
input nBB = 2.0;
input alertLine = 1.0;

plot scan;

def squeezeHistogram = TTM_Squeeze(price, length, nK, nBB, alertLine).Histogram;


def momentumPivotLow = (squeezeHistogram > squeezeHistogram[1] and squeezeHistogram[1] < squeezeHistogram[2]) ;

def momentumPivotHigh = (squeezeHistogram < squeezeHistogram[1] and squeezeHistogram[1] > squeezeHistogram[2]);

#plot barsSincePivot = GetMaxValueOffset(momentumPivot);

def pivotLowFollowThrough = (momentumPivotLow[1] and squeezeHistogram > squeezeHistogram[1]);

def pivotHighFollowThrough = (momentumPivotHigh[1] and squeezeHistogram < squeezeHistogram[1]);

def zeroLinePositiveFollowThrough = ( squeezeHistogram > 0 and squeezeHistogram[1] > 0 and squeezeHistogram[2] < 0 ) ;

def zeroLineNegativeFollowThrough = (squeezeHistogram < 0 and squeezeHistogram[1] < 0 and squeezeHistogram[2] > 0);

# uncomment one to scan for pivot high/low with follow through bar
#scan = pivotHighFollowThrough;
scan = pivotLowFollowThrough;

# uncomment one to scan for zero line positive/negative with follow through bar
#scan = zeroLinePositiveFollowThrough;
#scan = zeroLineNegativeFollowThrough;
 

Samrock

New member
@pk1729 I have something like this, I need a query for the scan the exactly when the Red becomes Yellow so that I can have in my watchlist and create alerts when new stocks gets added and removed.
 

shoecharlie

New member
Any ideas on how to tweak the scanner for the TTM Squeeze to find stocks that have already fired off rather than still being in a red dot squeeze. In laymans terms, a scanner that will tell me when the red dot squeeze changes and has its first green dot.

I am looking for a scanner that works on the 1 hr chart

any suggestions ??
 

wtf_dude

Active member
Here ya go. Paste this into the thinkscript editor for study scan. Won't need to add any conditions. I coded it in

Code:
# Momentum Squeeze
# Mobius
# Added Squeeze Label with directional color
# Label is green when momentum is ascending, red when descending

declare lower;

input lengthe = 20; #hint length: Length for average calculation
input price = close;
input SDmult = 2.0;
input ATRmult = 1.5;

   def K = (Highest(High, lengthe) + Lowest(low, lengthe)) /
               2 + ExpAverage(close, lengthe);
  def Momo = Inertia(price - K / 2, lengthe);

def SD = StDev(close, lengthe);
def Avg = Average(close, lengthe);
def ATR = Average(TrueRange(high, close, low), lengthe);
def SDup = Avg + (SdMult * Sd);
def ATRup = Avg + (AtrMult * ATR);

def Squeeze = SDup < ATRup;
        
def zero = if IsNaN(close) or !IsNaN(Squeeze) then Double.NaN else 0;

Plot breakout = squeeze[1] is true and squeeze is false;
 

shoecharlie

New member
thanks !!! i will try it out.

Thanks wtf_dude for your work !!! but its not exactly what i was looking for .... what i am wanting is when the red dot changes to green .. so like the picture, when the red dots , change to green...

also would be super cool, to have a secondary condition of a scan that said when the momentum histogram color changed from dark green to dark blue (upside).... or from red to yellow (downside)

think you could put something together? is it possible?

w1D8g58.jpg
 

wtf_dude

Active member
@shoecharlie The default setting when you copy paste in a new scanner is for day. You would need to manually change over the D to 1h box in the upper left hand corner. I checked that it's firing off correctly on the day scan

Ok, so did an overhaul since I haven't really seen a more advanced squeeze scanner.
This will give you a scan for:
A breakout either direction, breakout down, breakout up, or trend(color) reversals on bull and bear sides.

Be sure to copy the code in the charts tab to make it a study, don't copy it into the scan tab.
When you add the saved study into the scanner:
Pick the drop down of whatever you want to scan and pick "is true"
Be sure to change the aggregation box to whatever time frame you want

Code:
# AdvancedSqueezeScanner
# Momentum Squeeze open coding by Moebius, based on John Carter
# Scan by WTF_Dude
# Added Squeeze Label with directional color
# Label is green when momentum is ascending, red when descending

declare lower;

input length = 20; #hint length: Length for average calculation
input price = close;
input SDmult = 2.0;
input ATRmult = 1.5;

   def K = (Highest(High, length) + Lowest(low, length)) /
               2 + ExpAverage(close, length);
  def Momo = Inertia(price - K / 2, length);

def SD = StDev(close, length);
def Avg = Average(close, length);
def ATR = Average(TrueRange(high, close, low), length);
def SDup = Avg + (SdMult * Sd);
def ATRup = Avg + (AtrMult * ATR);

def Squeeze = SDup < ATRup;
        
def zero = if IsNaN(close) or !IsNaN(Squeeze) then Double.NaN else 0;

def momobullup = Momo > Momo[1] and Momo > 0;
def momobulldown = Momo > 0 and Momo < Momo[1];
def momobeardown =  Momo > Momo[1] and Momo > 0;
def momobearup = Momo < 0 and Momo > Momo[1];


Plot Breakout = squeeze[1] is true and squeeze is false;
plot BreakoutBull = squeeze[1] is true and squeeze is false and momobullup;
plot BreakoutBear = squeeze[1] is true and squeeze is false and momobulldown;

Plot BullTrendChange = momobulldown is true and momobulldown[1] is false;
Plot BearTrendChange = momobearup is true and momobearup[1] is false;
 

wtf_dude

Active member
@shoecharlie If you're running a scan, you're on the scan tab. If you want to look at your charts, you go to the chart tab. The menu where you pick what studies you want on your chart, there's a button to create a new study, and that's where you paste that code in and give it a name like SqueezeScan. Then when you load a custom study scan you can pick that option
 

Similar threads

Top