TTM Squeeze Pine Script — Code & Strategy Guide
The TTM Squeeze is a volatility and momentum indicator that detects low-volatility periods (squeezes) using Bollinger Bands and Keltner Channels, then tracks the direction and strength of the subsequent price breakout through a momentum oscillator. When the Bollinger Bands contract inside the Keltner Channels, the market enters a squeeze state — a coiled spring suggesting an imminent sharp move. The momentum histogram shows whether bulls or bears are gaining control as the squeeze releases. Developed by John Carter in his 2005 book Mastering the Trade, the TTM Squeeze has become one of the most popular volatility setups on TradingView. In Pine Script v6, the squeeze state is output as a boolean series and momentum as a decimal series, plotted with color-coded histogram bars — cyan for increasing bullish momentum, dark cyan for decreasing bullish, pink for increasing bearish, and purple for decreasing bearish. This guide covers the complete Pine Script v6 implementation, all configurable parameters through BB Length, KC Length, and True Range options, three concrete trading strategies for different market conditions, and the common mistakes that trip up new users.
I have been trading the TTM Squeeze on SPY daily since early 2023. The squeeze-release pattern alone was the trigger for about 40% of my best swing trades that year. But I got burned on false breakouts plenty before I learned to check the momentum histogram color first.
What Is the TTM Squeeze?
The TTM Squeeze is a volatility and momentum indicator that uses the relationship between Bollinger Bands and Keltner Channels to detect compressed volatility, combined with a linear-regression momentum oscillator to measure breakout direction and strength. Unlike plain volatility bands, the TTM Squeeze has a specific setup — when Bollinger Bands narrow to sit inside the Keltner Channels, the market is "squeezing." History shows these low-volatility periods are frequently followed by sharp price expansions. The momentum oscillator tells you which side is building pressure.
History & Inventor
John Carter, a full-time trader and author, introduced the TTM Squeeze in his 2005 book Mastering the Trade. Carter developed the indicator to solve a specific problem: standard volatility band tools tell you whether volatility is expanding or contracting, but they do not tell you when a big move is about to happen. His insight was to overlay two different volatility constructs — standard deviation (Bollinger Bands) and average true range (Keltner Channels) — and watch for the moment when one contracts inside the other. This cross-confirmation of low volatility became the squeeze signal. Carter was an early adopter of combining volatility analysis with momentum measurement in a single pane, and the TTM Squeeze remains one of the most widely used volatility setups in the TradingView community.
How It Works
The indicator runs two separate band calculations simultaneously. First, Bollinger Bands are computed using a simple moving average plus/minus a multiple of standard deviation. Second, Keltner Channels are computed using an exponential moving average plus/minus a multiple of the Average True Range (or the high-low range). When the Bollinger Bands are completely inside the Keltner Channels on every side, the squeeze state is active. The momentum oscillator is calculated using linear regression of price minus a composite midpoint, smoothed over the KC length. Positive values plot above zero; negative below. The color changes based on whether momentum is increasing or decreasing compared to the previous bar — so you see not just the level but the rate of change.
TTM Squeeze Core Formula
Bollinger Band Outer = SMA(Close, bb_length) + (bb_mult × StdDev(Close, bb_length))
Keltner Channel Outer = EMA(Close, kc_length) + (kc_mult × EMA(TR, kc_length))
Squeeze On = (BB_Upper < KC_Upper) AND (BB_Lower > KC_Lower)
E1 = (Highest(High, kc_length) + Lowest(Low, kc_length)) / 2 + SMA(Close, kc_length)
Momentum = LinReg(Close - E1/2, kc_length, 0)
Where:
bb_length = BB lookback (default 20)
bb_mult = BB standard deviation multiplier (default 2.0)
kc_length = KC lookback (default 20)
kc_mult = KC ATR multiplier (default 1.0)
TR = True Range (or high-low, based on useTrueRange flag)What Markets It Suits
Stocks: TTM Squeeze works well on large-cap equities and indices like SPY and QQQ on daily and 4H charts. The squeeze pattern is particularly reliable before earnings releases when volatility naturally compresses.
Crypto: Effective on 4H and daily crypto charts, but expect more frequent squeeze states due to higher baseline volatility. Increase the KC multiplier to 1.2 on BTC/ETH to reduce false squeezes.
Forex: Works on major pairs like EUR/USD and GBP/USD on 4H charts. Forex squeezes tend to have longer duration and more reliable breakouts compared to crypto.
Futures: Popular on ES and NQ futures for intraday squeeze plays. The squeeze pattern before major economic data releases (NFP, FOMC) is especially reliable — the artificial quiet before the storm.
Best Timeframes
The TTM Squeeze produces its most reliable signals on 4H to Daily charts. On 1H charts, the squeeze state triggers roughly 2-3 times more frequently but with lower breakout follow-through — I would estimate about 55% of 1H squeezes lead to a meaningful move, versus roughly 75% on daily. On 15M or lower, squeeze signals become noise: the Bollinger Bands and Keltner Channels shift constantly, creating and releasing squeeze states within a few bars. Stick to 4H and above for swing trades. If you must trade intraday, use 30M as your minimum.
TTM Squeeze Pine Script Code
The code below implements the complete TTM Squeeze indicator in Pine Script v6, including the squeeze detection logic and the color-coded momentum histogram. Copy the entire script, open TradingView, press Alt+P to open the Pine Editor, paste the code, and click Add to Chart. The indicator plots in a separate pane below your price chart — red dot markers on the zero line show squeeze states and the histogram shows momentum direction with four distinct colors.
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify
//@version=6
indicator(title="TTM Squeeze", overlay=false, max_labels_count=500)
p_comm_time_range_to_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
int start_unix_time = na
int end_unix_time = na
int start_time_hour = na
int start_time_minute = na
int end_time_hour = na
int end_time_minute = na
if str.length(time_range) == 11
start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
start_time_minute := math.floor(str.tonumber(str.substring(time_range, 3, 5)))
end_time_hour := math.floor(str.tonumber(str.substring(time_range, 6, 8)))
end_time_minute := math.floor(str.tonumber(str.substring(time_range, 9, 11)))
else if str.length(time_range) == 9
start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
start_time_minute := math.floor(str.tonumber(str.substring(time_range, 2, 4)))
end_time_hour := math.floor(str.tonumber(str.substring(time_range, 5, 7)))
end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
start_unix_time := timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), start_time_hour, start_time_minute, 0)
end_unix_time := timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), end_time_hour, end_time_minute, 0)
[start_unix_time, end_unix_time]
p_comm_time_range_to_start_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
int start_time_hour = na
int start_time_minute = na
if str.length(time_range) == 11
start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
start_time_minute := math.floor(str.tonumber(str.substring(time_range, 3, 5)))
else if str.length(time_range) == 9
start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
start_time_minute := math.floor(str.tonumber(str.substring(time_range, 2, 4)))
timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), start_time_hour, start_time_minute, 0)
p_comm_time_range_to_end_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
int end_time_hour = na
int end_time_minute = na
if str.length(time_range) == 11
end_time_hour := math.floor(str.tonumber(str.substring(time_range, 6, 8)))
end_time_minute := math.floor(str.tonumber(str.substring(time_range, 9, 11)))
else if str.length(time_range) == 9
end_time_hour := math.floor(str.tonumber(str.substring(time_range, 5, 7)))
end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), end_time_hour, end_time_minute, 0)
p_comm_timeframe_to_seconds(simple string tf) =>
float seconds = 0
tf_lower = str.lower(tf)
value = str.tonumber(str.substring(tf_lower, 0, str.length(tf_lower) - 1))
if str.endswith(tf_lower, 's')
seconds := value
else if str.endswith(tf_lower, 'd')
seconds := value * 86400
else if str.endswith(tf_lower, 'w')
seconds := value * 604800
else if str.endswith(tf_lower, 'm')
seconds := value * 2592000
else
seconds := str.tonumber(tf_lower) * 60
seconds
p_custom_sources() =>
[open, high, low, close, volume]
p_ta_ttm(simple int bb_length, simple float bb_mult, simple int kc_length, simple float kc_mult, simple bool use_true_range) =>
bb_value = ta.sma(close, bb_length) + bb_mult * ta.stdev(close, bb_length)
kc_value = ta.ema(close, kc_length) + kc_mult * ta.ema(use_true_range ? ta.tr : (high - low), kc_length)
e1 = (ta.highest(high, kc_length) + ta.lowest(low, kc_length)) / 2 + ta.sma(close, kc_length)
osc = ta.linreg(close - e1 / 2, kc_length, 0)
diff = bb_value - kc_value
[diff < 0, osc]
[p_ind_1_squeezeOn, p_ind_1_momentum] = p_ta_ttm(20, 2, 20, 1, true)
p_ind_1_momentum_color = p_ind_1_momentum[1] < p_ind_1_momentum[0] ? p_ind_1_momentum[0] >= 0 ? color.rgb(0, 255, 255, 0) : color.rgb(204, 0, 204, 0) : p_ind_1_momentum[0] >= 0 ? color.rgb(0, 155, 155, 0) : color.rgb(255, 155, 255, 0)
p_ind_1_squeezeOn_color = p_ind_1_squeezeOn ? color.rgb(242, 54, 69, 0) : color.rgb(76, 175, 80, 0)
plot(p_ind_1_momentum, color=p_ind_1_momentum_color, style=plot.style_histogram, linewidth=2, title="TTM - Momentum")
plot(0, color=p_ind_1_squeezeOn_color, style=plot.style_circles, linewidth=3, title="TTM - Squeeze")
| Visual Element | Color / Style | What It Represents |
|---|---|---|
| Squeeze Dot (Active) | Red circle on zero line | BB bands are inside KC channels — low volatility, coiled state |
| Squeeze Dot (Released) | Green circle on zero line | BB bands have expanded outside KC — volatility is expanding |
| Bullish Momentum (Increasing) | Cyan histogram bar | Positive momentum that is accelerating — strongest long signal |
| Bullish Momentum (Decreasing) | Dark cyan histogram bar | Positive momentum that is slowing — trend may be weakening |
| Bearish Momentum (Increasing) | Pink histogram bar | Negative momentum accelerating — strongest short signal |
| Bearish Momentum (Decreasing) | Purple histogram bar | Negative momentum slowing — potential bullish reversal |
TTM Squeeze Parameters & Tuning Guide
The TTM Squeeze has five configurable parameters that control the sensitivity of both the squeeze detection and the momentum oscillator. The table below shows each input, its default, and the practical working range.
| Parameter | Default | Description | Range |
|---|---|---|---|
| BB Length | 20 | Period for Bollinger Band SMA and standard deviation | 10-30 |
| BB MultFactor | 2.0 | Standard deviation multiplier for Bollinger Bands | 1.5-2.5 |
| KC Length | 20 | Period for Keltner Channel EMA and ATR calculation | 10-30 |
| KC MultFactor | 1.0 | ATR multiplier for Keltner Channel width | 0.8-1.5 |
| Use True Range | Yes | Use True Range for KC (on) or high-low range (off) | On / Off |
Tuning Scenarios
Different trading styles need different TTM Squeeze settings. Here is how I adjust them across three common approaches.
| Scenario | BB Length | BB Mult | KC Length | KC Mult | Use Case |
|---|---|---|---|---|---|
| Scalping | 14 | 1.8 | 14 | 0.8 | 15M crypto pairs |
| Swing | 20 | 2.0 | 20 | 1.0 | 4H stocks / ETF |
| Position | 25 | 2.0 | 25 | 1.2 | Daily forex pairs |
The KC Length parameter has the biggest impact on signal frequency. A shorter KC Length (e.g., 14) makes the Keltner Channels narrower, so the Bollinger Bands more easily exceed them — you get fewer squeeze states and fewer signals overall. A longer KC Length (e.g., 25) widens the channels, making it easier to trigger the squeeze state, but more than half of those squeezes may not lead to a significant breakout. The BB MultFactor is the second-most impactful: reducing it from 2.0 to 1.5 makes the Bollinger Bands narrower, which means they sit inside the Keltner Channels more often — more squeeze signals, lower quality.
Reading TTM Squeeze Signals
Reading the TTM Squeeze is about understanding two things in sequence: the squeeze state (red dot) and the momentum direction (histogram color and position). The table below maps the common signal configurations to their meanings. The critical point is that a red dot alone tells you nothing about direction — you need the histogram to confirm.
| Signal | Condition | Meaning | Reliability (Daily) |
|---|---|---|---|
| Squeeze Forming | BB bands narrowing toward KC bands, red dot appears | Volatility is compressing — a significant move is likely but direction is unknown | Medium |
| Bullish Breakout | Red to green dot switch + cyan histogram above zero | Volatility is expanding upward — bullish momentum accelerating, potential long entry | High |
| Bearish Breakout | Red to green dot switch + pink histogram below zero | Volatility expanding downward — bearish momentum accelerating, potential short entry | High |
| Momentum Weakening | Cyan bar shifts to dark cyan, or pink to purple | The current momentum is decelerating — existing position may need tightening | Medium |
| Momentum Reversal | Histogram crosses zero line from one side to the other | Momentum direction has flipped — potential trend change or strong pullback | Medium |
Common Misread: Red Dot = Immediate Trade Signal
The most common mistake is entering a trade the moment a red dot appears. The squeeze signal only tells you volatility is low — it does not tell you when it will release or which way. Example: SPY spent 11 consecutive daily bars in a squeeze during July 2023. A trader entering at bar 3 would have sat in a flat position for 8 days before the breakout happened. The correct approach is to wait for the squeeze to release (red to green) AND for the momentum histogram to confirm direction. A red dot is a warning light, not a green light.
TTM Squeeze Trading Strategies
Three distinct strategies cover the main ways traders use the TTM Squeeze: directional breakout, momentum continuation, and failed-squeeze reversal. Each approach works in a specific market environment. Applying the wrong one will get you stopped out. Here is exactly how each works.
1. The Squeeze Breakout
Market: Breakout / Low Volatility
The squeeze breakout is the highest-conviction setup in the TTM Squeeze toolkit. After a period of compressed volatility, the release often produces a strong directional move. The key is confirming the direction with the histogram before entering.
Entry Conditions:
- Red squeeze dot has been active for at least 3 consecutive bars (longer squeeze = stronger breakout).
- Squeeze releases — red dot switches to green on the current bar or previous bar.
- Momentum histogram is above zero and shows cyan bars (increasing bullish) for a long entry.
- Momentum histogram is below zero and shows pink bars (increasing bearish) for a short entry.
- Enter at the close of the bar showing the release + confirmed momentum direction.
Exit Conditions:
- Histogram color shifts from increasing to decreasing (cyan to dark cyan, or pink to purple) — exit 50%.
- Histogram crosses back to the zero line from the entry side — exit the remainder.
Stop-Loss:
Place the stop at the most recent swing low (for longs) or swing high (for shorts) within the squeeze zone. If no clear swing point exists, use 1.5x ATR(14) from entry.
Best Combination:
Add ADX indicator as a trend filter: only take the breakout when ADX(14) is above 20 and rising. This avoids trades where the squeeze releases into a directionless market.
2. Momentum Continuation on Re-Squeeze
Market: Trending / Post-Breakout
After a squeeze breakout, the market sometimes re-squeezes briefly before continuing in the original direction. This second leg is often more powerful than the first because late participants are jumping in. I caught one of these on QQQ in October 2023 — the initial squeeze ran about 3%, the re-squeeze lasted 4 bars, and the second leg ran another 5%.
Entry Conditions:
- A squeeze breakout has occurred (red to green) and price moved at least 2x ATR(14) in the breakout direction.
- The squeeze dot turns red again (re-squeeze) while price holds above the previous breakout level (long) or below (short).
- On re-release, the histogram shows the same direction as the initial breakout — do NOT trade a reversal here.
- Enter at the close of the bar when the green dot reappears with confirming histogram.
Exit Conditions:
- Momentum histogram shifts to decreasing color — exit 50%.
- Price closes below the re-squeeze low (long) — full exit.
Stop-Loss:
Place the stop below the re-squeeze low for longs, or above the re-squeeze high for shorts. This is a tighter stop than the initial breakout.
Best Combination:
Add a 50 EMA as a trend filter: only take continuation longs above the 50 EMA and shorts below it. In my experience, this filter improves the re-squeeze win rate from about 55% to around 68%.
3. Failed Squeeze Reversal
Market: Ranging / Choppy
Sometimes the squeeze releases in one direction but quickly reverses — this is a failed squeeze that often leads to a sharp move in the opposite direction. The failed squeeze reversal is a counter-trend setup that works best in ranging markets where breakouts lack conviction.
Entry Conditions:
- Squeeze releases in one direction (e.g., cyan bars above zero after red dot).
- Within 3 bars of release, the histogram crosses back through zero to the opposite side.
- The new momentum bars are the increasing color on the opposite side (pink for short after a failed long breakout).
- Enter at the close of the bar where the histogram confirms the reversal.
Exit Conditions:
- Momentum color shifts from increasing to decreasing — exit 50%.
- Squeeze dot turns red again — full exit.
Stop-Loss:
Stop goes beyond the extreme of the failed breakout bar. If the failed breakout pushed above resistance and reversed, the stop goes above that high.
Best Combination:
Pair with Chaikin Money Flow — only take the failed squeeze reversal when CMF is negative (for long-to-short reversals) or positive (for short-to-long). Volume confirmation doubles the reliability of this setup.
Strategy Comparison
| Strategy | Market Type | Win Rate Range | Best Pair | Risk Level |
|---|---|---|---|---|
| Squeeze Breakout | Breakout | ~55-65% | TTM + ADX | Medium |
| Momentum Continuation | Trending | ~58-68% | TTM + EMA | Medium |
| Failed Squeeze Reversal | Ranging | ~50-60% | TTM + CMF | High |
Win rate ranges are approximate illustrations based on personal observation and backtesting. They vary significantly by market, timeframe, and period.
For educational purposes only. The strategies described above are for informational reference only and do not constitute investment advice. Past performance and backtested win rates do not guarantee future results. Always trade with proper risk management.
TTM Squeeze vs. Bollinger Bands vs. Keltner Channels vs. ADX
TTM Squeeze, plain Bollinger Bands, Keltner Channels, and ADX all measure volatility but in fundamentally different ways and for different purposes. The choice between them depends on whether you want squeeze detection, simple volatility bands, channel tracking, or trend strength. Here is the breakdown.
| Feature | TTM Squeeze | Bollinger Bands | Keltner Channels | ADX |
|---|---|---|---|---|
| Type | Volatility + Momentum | Volatility (StdDev) | Volatility (ATR) | Trend Strength |
| Squeeze Detection | Yes — built in | No (manual via BBW) | No | No |
| Directional Signal | Momentum histogram | Band touches (indirect) | Channel breaks | DI+ / DI- cross |
| Lag | Low-Medium | Low-Medium | Low | Medium |
| Best for | Breakout from consolidation | Mean reversion | Trend following | Trend confirmation |
| Signals per day (Daily) | ~1-3 squeezes | ~2-4 touches | ~1-3 crosses | ~1-2 crossovers |
I reach for the TTM Squeeze when a market is coiling and I want to catch the explosion. Plain Bollinger Bands do not tell you when a squeeze is happening — you would need to monitor Bandwidth separately. Keltner Channels alone give you volatility expansion signals but no squeeze detection. ADX tells you the trend strength but not where volatility is compressing. The TTM Squeeze is unique because it bundles compression detection and directional momentum in one pane. If I am trading a ranging market looking for mean reversion, I use plain BB + RSI instead.
Where TTM falls short is in pure trending markets. Once a strong trend is underway, the indicator mostly shows green dots (no squeeze) with momentum consistently on one side — useful confirmation, but you are better off with a trend-following system using Keltner or moving averages. I switch to KC or EMA when the trend is established and only come back to TTM when I see consolidation again.
Another practical point: on SPY daily, TTM squeezes happen about 8-12 times per year. That is a manageable number of setups. BB touches happen 60+ times per year. If you want fewer, higher-conviction setups, TTM is the better choice. If you want more frequent but less directional signals, stick with plain BB.
Common TTM Squeeze Mistakes & Limitations
Even experienced traders make predictable errors with the TTM Squeeze. Here are the five most common issues I have seen and how to fix each one.
1. Entering on the red dot without waiting for release.
Why it happens: The red dot is visually striking — traders see it and feel the urge to act. The fix: Wait for the red dot to turn green AND for the momentum histogram to confirm direction. A squeeze can last 10+ bars without releasing.
2. Ignoring the histogram color — looking only at the level.
Why it happens: Most oscillators only use position above/below zero. The TTM color adds extra information. The fix: A cyan bar above zero is stronger than a dark cyan bar at the same level. The color shows the rate of change. If the histogram is at +2.0 but dark cyan, momentum is slowing. I missed this distinction for my first three months using the indicator.
3. Using default settings on every timeframe and market.
Why it happens: 20-period defaults are what ship with the code. The fix: On 15M charts, reduce BB Length to 14 and KC Length to 14. On highly volatile crypto, increase the KC MultFactor to 1.2. The default was tuned for daily stock charts.
4. Trading every squeeze release equally.
Why it happens: All red-to-green switches look the same on the indicator. The fix: Prioritize squeezes that lasted at least 5 bars — longer compression tends to produce stronger breakouts. A 2-bar squeeze followed by green dots is often a weak move. Track the squeeze duration mentally or add a counter to your watchlist.
5. Using TTM Squeeze as a standalone system.
Why it happens: The indicator is feature-rich and feels complete. The fix: Always add a trend filter — ADX(14), an EMA, or volume confirmation. The TTM Squeeze tells you when and which direction, but it does not tell you about the broader trend context. A squeeze that releases against the daily trend has a much lower success rate.
How to Generate TTM Squeeze Pine Script in Pineify
Pineify lets you generate a complete TTM Squeeze Pine Script indicator in seconds without writing any code yourself. Just describe what you need and the AI coding agent handles the rest. Here is exactly how it works.
- 1
Open Pineify and select the AI Coding Agent.
Open the coding agent interface — it is the main entry point on the Pineify home screen. No downloads or account setup required to try it.
- 2
Describe your TTM Squeeze indicator.
Type something like: "Generate a TTM Squeeze indicator with BB Length 20, KC Length 20, and momentum histogram." The more specific you are about parameters and colors, the better the output matches your chart setup.
- 3
Copy the generated Pine Script code.
The agent outputs complete, ready-to-run Pine Script v6 code. Use the built-in copy button to grab the full script in one click. No edits needed.
- 4
Open TradingView and paste the code.
Press Alt+P to open the Pine Editor, paste the code, and click Add to Chart. The full TTM Squeeze indicator is now live on your chart.
- 5
Adjust parameters to your trading style.
Open the indicator settings panel in TradingView to tweak the BB Length, KC Length, multipliers, and True Range flag. The generated code exposes all parameters as editable inputs.
TTM Squeeze FAQ
Quick answers to the most common TTM Squeeze questions.
Related Pine Script Indicators
Keltner Channels
Volatility-based channel using ATR — one of the core components of the TTM Squeeze calculation
Bollinger Bands
Standard deviation-based volatility bands — the other half of the squeeze detection system
ADX Indicator
Trend strength measurement that pairs with TTM Squeeze to filter low-quality breakouts
Chaikin Money Flow
Volume-weighted momentum oscillator useful for confirming squeeze breakouts
Stochastic Oscillator
Momentum oscillator that pairs with TTM Squeeze for overbought/oversold confirmation
TTM Squeeze in Seconds
Skip the manual coding. Pineify's AI Coding Agent generates complete, ready-to-use Pine Script TTM Squeeze indicators — including custom BB length, KC settings, and momentum histogram styling — instantly for free.
Try Pineify Free