On this page
What you'll build

- In a squeeze
- No squeeze (label)
- No squeeze (dot)
- Type
- Indicator
- Runs as
- Chart labels
- Code
- 531 lines
- Video
- 21 minutes
- Level
- Beginner
In this tutorial I build a multi-time frame (MTF) Squeeze indicator for ThinkOrSwim that shows the default Squeeze on every timeframe at a glance. Each timeframe gets a label on the chart, from M for monthly down to 1m, red when that timeframe is in a squeeze and dark green when it isn't.
The indicator rebuilds the Squeeze from Bollinger Band and Keltner Channel source code, because the built-in function tends to give false squeeze signals inside multi-time frame code. It's for Squeeze traders who check several timeframes, and for anyone who wants a reusable multi-time frame template in thinkScript.
Key takeaways
- The MTF Squeeze indicator for ThinkOrSwim plots 18 labels, from monthly (M) down to 1-minute (1m). A label is red when that timeframe is in a squeeze and dark green when it isn't.
- It uses ThinkOrSwim's default Squeeze settings: length 20, nK 1.5, nBB 2 and an alert line of 1.
- A timeframe is in a squeeze when nBB times the standard deviation, divided by nK times the average true range, is below 1. That means the Bollinger Bands are inside the Keltner Channels.
- The indicator rebuilds the Squeeze from source code because the built-in Squeeze function tends to give false squeeze signals inside an if/else block that uses aggregation periods.
- ThinkOrSwim can only read timeframes equal to or higher than your chart's, so on a 5-minute chart the 1m to 4m labels don't appear.
What it shows
What the MTF Squeeze Indicator Shows
The MTF Squeeze indicator is a row of chart labels, one per timeframe, that shows whether each timeframe is in a squeeze. It saves you from flipping through charts: one glance tells you which timeframes have the default squeeze.
Each label is red when that timeframe has a squeeze and dark green when it doesn't. The free download covers 18 timeframes. The table lists each label with the aggregation period and the period string the code uses for it.
| Label | Timeframe | Aggregation check | Price period |
|---|---|---|---|
| M | Monthly | AggregationPeriod.Month | period="Month" |
| W | Weekly | AggregationPeriod.week | period="week" |
| 4D | 4-day | AggregationPeriod.four_days | period="4 days" |
| 3D | 3-day | AggregationPeriod.three_days | period="3 days" |
| 2D | 2-day | AggregationPeriod.two_days | period="2 days" |
| D | Daily | AggregationPeriod.day | period="day" |
| 4h | 4-hour | AggregationPeriod.four_hours | period="4 Hours" |
| 2h | 2-hour | AggregationPeriod.two_hours | period="2 Hours" |
| 1h | 1-hour | AggregationPeriod.hour | period="1 Hour" |
| 30m | 30-minute | AggregationPeriod.Thirty_Min | period="30 Min" |
| 20m | 20-minute | AggregationPeriod.Twenty_Min | period="20 Min" |
| 15m | 15-minute | AggregationPeriod.Fifteen_Min | period="15 Min" |
| 10m | 10-minute | AggregationPeriod.Ten_Min | period="10 Min" |
| 5m | 5-minute | AggregationPeriod.Five_Min | period="5 Min" |
| 4m | 4-minute | AggregationPeriod.Four_Min | period="4 Min" |
| 3m | 3-minute | AggregationPeriod.Three_Min | period="3 Min" |
| 2m | 2-minute | AggregationPeriod.Two_Min | period="2 Min" |
| 1m | 1-minute | AggregationPeriod.Min | period="1 Min" |
Labels only appear for timeframes equal to or higher than your chart's. ThinkOrSwim can read higher timeframes from the chart you're on, but nothing lower. On a 5-minute chart you'll see 5m and up, and you won't know whether the 1, 2, 3 or 4-minute charts have a squeeze. The higher labels also need enough candle data on your chart to calculate.
How to Read the Labels
A red MTF Squeeze label tells you that timeframe is in a squeeze with the default settings. It doesn't tell you which way price breaks or when. Open the red timeframes and read the setup on those charts.
Before you trade a squeeze, decide where your stop goes and size the position so a stop-out costs a small, fixed share of your account. To find squeezes across many stocks instead of one chart, use our free Squeeze dots scan or build a Squeeze dashboard for a watchlist.
The MTF Squeeze on real charts
Drawn from real intraday bars with the code on this page, through the Oct 20, 2026 close. The labels use the indicator's own math (length 20, nK 1.5, nBB 2, alert line 1) on each timeframe our data has: monthly, weekly, daily, 4-hour, 2-hour, 1-hour, 30, 20, 15, 10 and 5-minute. Hourly and longer intraday bars are built from 30-minute bars starting at 9:30; 10 and 20-minute bars from 5-minute bars. The 4D, 3D, 2D and 1 to 4-minute labels are gray because those bars are not in our data.
XLI: the most timeframes in a squeeze on Oct 20

Of the 44 stocks and ETFs we checked, XLI had the most timeframes in a squeeze at the Oct 20, 2026 close: D, 4h, 2h, 1h, 30m, 15m, 5m. Not in a squeeze: M, W, 20m, 10m. A stack of red labels says the stock is coiled on several clocks at once; it says nothing about which way it breaks.
The 14 ETFs on one screen

By the numbers
30 well-known stocks and 14 ETFs at the Oct 20, 2026 close, 11 timeframes each.
Install it in ThinkOrSwim
Download the Multi-Time Frame Squeeze indicator for ThinkOrSwim. The download is a .zip with one study file, TI_MTF_SqueezeSTUDY.ts, the same code as the Code tab on this page. It plots 18 labels from monthly to 1-minute using the default Squeeze settings. If you haven't added a custom study before, here's how to import an indicator into your ThinkOrSwim platform.
How to Build It in ThinkOrSwim
Building the MTF Squeeze takes one monthly block of code, then a copy of that block for every other timeframe.
- Click the Studies icon, click Create, and give the study a name.
- Add a header comment that credits the Blogspot source code with its link.
- Define the global variables: length, alert line, nK, nBB and the rest.
- Write the monthly block: the declarations, the if/else, and 2 labels.
- Click Apply, then check the M label against the built-in Squeeze on a monthly chart.
- Copy the block into a text editor, replace
monthwith the next timeframe, fix the label text and the period string, and paste it back. Repeat down to 1 minute.
If you'd rather start from the finished file, download it and follow our guide on how to import a study into ThinkOrSwim.
How the code works
Why the Indicator Rebuilds the Squeeze From Source Code
The built-in Squeeze function in ThinkOrSwim tends to give false squeeze signals inside multi-time frame code. That happens when you reference it inside an if/else block that also uses aggregation periods. It doesn't return the squeeze alert value you'd expect, so the indicator doesn't work the way you want.
ThinkOrSwim's own documentation describes the Squeeze as a measure of two indicators and their relationship: the Bollinger Bands and the Keltner Channels. The squeeze is on when the Bollinger Bands fall inside the Keltner Channels. The Squeeze's source code is hidden, but the Bollinger Band and Keltner Channel source code isn't, so you can rebuild it from those pieces.
To speed things along, I used code that a member sent us from another thinkScript author's Blogspot, which recreates the Squeeze. If you use that code, leave the author's credit and Blogspot link in your header. Our download keeps it on the first lines.
The math comes down to one ratio. Bollinger Band width is a multiple of the standard deviation (nBB), and Keltner Channel width is a multiple of the average true range (nK). The code divides the first by the second and compares the result with the alert line of 1. Below 1, the Bollinger Bands are inside the Keltner Channels, and that timeframe is in a squeeze.
The Multi-Time Frame Framework
The MTF Squeeze is built on the drag-and-drop multi-time frame module from our MTF DMI indicator. Because that code was written to be reusable, I could lift the same framework and drop in the squeeze math. You could do the same for EMA crossovers or your own set of indicators.
- Declare first. Each timeframe's variables are declared above its if/else block.
- Check the timeframe. The if tests whether the chart's aggregation period is less than or equal to the timeframe you want to measure, then assigns the values.
- Default to 0. The else sets every variable to 0 to keep things simple.
- Prefix every name. Each variable starts with its timeframe, like
monthPrice, so a find-and-replace turns the monthly block into the weekly one.
If you only need one higher timeframe, such as a daily level on an intraday chart, our tutorial on plotting higher time frame indicators with aggregation periods covers that simpler case.
How the Code Works
The MTF Squeeze code is a short global section followed by 18 blocks that each follow the same pattern. Once you understand the monthly block, you understand all of them.
Step 1: Global Variables
Global variables are the settings that don't change from one timeframe to the next. These are the default values of the built-in Squeeze.
This step’s code is free with an account. Create one or sign in.
To change the compression, change nk here. To set a value from the studies menu, change its def to input.
I renamed the source's nBB factor to nBB so the code is easier to copy and paste later.
Step 2: Declare the Monthly Variables
Each monthly variable is declared once, above the if/else, so both branches can assign it.
This step’s code is free with an account. Create one or sign in.
The download adds monthAggregationPeriod, a flag that hides the label on charts that can't read the monthly timeframe.
Step 3: The Monthly If Block
The if block checks that the chart is monthly or lower, then calculates the squeeze on monthly bars. Inside it, you assign values without def, because the variables already exist.
This step’s code is free with an account. Create one or sign in.
period="Month" makes each price read monthly bars.
The source code called an average true range function, and ThinkOrSwim reported no such function. My workaround was to break it out as the average of TrueRange over the length, with the monthly high, close and low.
Step 4: The Else Block
The else block gives every monthly variable a value of 0. Until each one has a value here, ThinkOrSwim shows an error.
This step’s code is free with an account. Create one or sign in.
With that done, one error is left: at least one plot should be defined. The labels fix that.
Step 5: The Labels
AddLabel takes a condition, the text and a color. One label shows when there's a monthly squeeze and the other shows when there isn't.
This step’s code is free with an account. Create one or sign in.
In the video I used black for no squeeze. Dark green, which the download uses, is easier to see on a black chart background.
Our AddLabel tutorial covers static and dynamic labels in more depth.
Step 6: Copy the Block to Every Timeframe
Copying the monthly block is where the naming pays off. I paste it into Sublime Text, press Ctrl+H, and replace month with week.
Then change the label text from "M" to "W". That's the one piece that's easy to forget: in the video, my weekly label showed up as a second M until I fixed it.
Some timeframes need a different period string. The aggregation check uses AggregationPeriod.four_days, but inside period I use "4 days" with the number.
This step’s code is free with an account. Create one or sign in.
That's how ThinkOrSwim's own examples write it, and it tends to work without glitches. I've had problems with other spellings there in the past.
Full thinkScript code
Create a free account or sign in to copy all 531 lines and download the file.
Examples
Testing It Against the Built-in Squeeze
The MTF Squeeze labels should match the built-in Squeeze study on each timeframe's own chart, so I checked them that way as I built them in the video.
- Monthly, no squeeze. The first chart had no monthly squeeze, and the built-in Squeeze on the monthly chart agreed. Coca-Cola had no monthly squeeze either.
- Monthly squeeze. On a symbol that did have a monthly squeeze, the M label turned red.
- Weekly. After I added the weekly block, that chart showed a squeeze on the monthly and none on the weekly.
- 4-day. The 4D label showed no squeeze, and the 4-day chart confirmed it.
Customize and troubleshoot
Free Download vs the Member Version
The free MTF Squeeze indicator tracks one compression, the default mid compression that the built-in Squeeze uses. Volatility Box members have a second version in Module 9 of the Squeeze Course that takes the code one step further.
| Feature | Free download (built in this tutorial) | Member version (Module 9) |
|---|---|---|
| Compressions | 1, the default mid compression | 3, fast, mid and slow |
| Squeeze colors | Red | Gray for fast, red for mid, yellow for slow |
| No squeeze | Dark green | Black in the video |
| Changing colors | Edit the AddLabel lines | Pick colors in the studies menu |
In the video, I changed the member version's no-squeeze color from black to dark green in the studies menu, clicked Apply, and every label without a squeeze turned dark green. Colors you can set from the menu also help if you're colorblind.
Customizing and Stacking the Labels
The MTF Squeeze code is straightforward to extend because every timeframe block has the same shape.
Add the Fast and Slow Compressions
- More compressions. Add an nK factor 2 and an nK factor 3, then repeat the squeeze code with a 2 and a 3 version of each variable.
- Inputs instead of fixed values. Change any global
deftoinputto set it from the studies menu. - Your own colors. Change
color.redandcolor.dark_greenin theAddLabellines.
Stack Them With the MTF Squeeze Histogram
Volatility Box members who also have the MTF Squeeze Histogram indicator, from Module 8 of the Squeeze Course, can load both indicators, and the two sets of labels plot side by side. The fastest way I know to stack them is to click the hamburger menu on the chart, choose Detach, and shrink the window until the two sets of labels stack on top of one another.
If that makes the chart too skinny, a flex grid gives you more control over how wide each chart is.
Common Problems
Most MTF Squeeze problems come from the chart's timeframe, a missing value in the else block, or a label name.
- Lower-timeframe labels are missing. That's by design. On a 5-minute chart, labels below 5 minutes can't calculate. Drop to a 1-minute chart to see all 18, and load enough days of data for the higher timeframes to calculate.
- "At least one plot should be defined." The study needs output. Add the
AddLabellines. - "No such function" on the ATR line. Use
Average(TrueRange(high, close, low), length)with the timeframe's period on each price. - Two labels with the same letter. You copied a block and didn't change the label text.
- Errors on variables in a new block. Every declared variable needs a value in both the if and the else.
For other errors, our tutorial on how to debug thinkScript code walks through tracking them down.
Video: Multi-Time Frame (MTF) Squeeze Indicator, built line by line
21 minutes on YouTube.
Watch on YouTube
Questions
What is a multi-time frame Squeeze indicator?
It shows whether the Squeeze is on for several timeframes at once, so you don't have to flip through charts. Our free MTF Squeeze indicator plots 18 labels on a ThinkOrSwim chart, from monthly down to 1-minute. Each label is red when that timeframe is in a squeeze and dark green when it isn't.
How do I add the MTF Squeeze indicator to ThinkOrSwim?
Download the study file or copy the code from the Code tab. In ThinkOrSwim, click the Studies icon, click Create, paste in the code, name the study, and click OK, then Apply. The labels appear on the chart. Use a 1-minute chart to see all 18 timeframes, with enough candle data loaded for the higher ones to calculate.
Why don't I see the lower timeframe labels on my chart?
ThinkOrSwim can only read timeframes equal to or higher than the chart you're on. On a 5-minute chart, the indicator shows 5m and every higher timeframe, and it hides the 1, 2, 3 and 4-minute labels. Higher timeframes also need enough candle data on the chart to calculate.
Why not use the built-in Squeeze function for multiple timeframes?
When you reference the built-in Squeeze function inside an if/else block that uses aggregation periods, it tends to give false squeeze signals because it doesn't return the squeeze alert you'd expect. The MTF Squeeze rebuilds the squeeze from standard deviation and average true range instead, which works the way you'd expect.
What settings does the MTF Squeeze indicator use?
It uses the built-in Squeeze defaults: a length of 20, an nK factor of 1.5, an nBB factor of 2 and an alert line of 1. They're global variables at the top of the code. Change def to input on any of them if you want to set it from the studies menu.
How does the indicator decide a timeframe is in a squeeze?
For each timeframe it multiplies the standard deviation of the close by nBB and the average true range by nK, then divides the first by the second. If the result is below the alert line of 1, the Bollinger Bands are inside the Keltner Channels, and that timeframe's label turns red.
How do I add the fast and slow compressions?
Add an nK factor 2 and an nK factor 3 to the global variables, then repeat the squeeze code with a 2 and a 3 version of each variable. Volatility Box members have a finished version in Module 9 of the Squeeze Course, with all 3 compressions and label colors you can change in the studies menu.
Can I use this code for other multi-time frame indicators?
Yes. The framework comes from our MTF DMI indicator and works for things like EMA crossovers or your own indicators. Declare the variables above an if/else, check the aggregation period in the if, set everything to 0 in the else, and prefix each name with its timeframe so find-and-replace builds the next block.
Goes with
Get each new indicator by email
One email on Sundays with the week's new tool and its code. A free account also unlocks every .txt download.
No credit card. Unsubscribe anytime.
Fair Value Gap
False Breakout Scanner
Heiken Ashi Trend



Stock Volatility Box
Futures Volatility Box
ORB Setups
Squeeze Setups
TTM Squeeze Scanner
TTM Squeeze Backtester
TTM Squeeze Histogram Backtester
TTM Squeeze Dashboard
Trend Squeeze
Create a TTM Squeeze Watchlist in Seconds