How do I scan for a candlestick pattern (engulfing, pin bar) in thinkorswim?
Updated August 27, 2026
Short answer
thinkorswim ships around 58 candlestick pattern studies (Engulfing, Hammer, Harami, ShootingStar and so on) and you use them as study filters, not as the pattern filter, which is for classic chart patterns. Go to the Scan tab, Stock Hacker, Add filter, Study, pick the pattern, then pick which plot you're testing since most of them have separate Bullish and Bearish plots. If the built-in definition is stricter than yours (most require a preceding trend), write the candle rule yourself in a custom filter instead.
Use the study filter, not the pattern filter
Easy trap. Add filter > Pattern is for classic chart patterns, the head and shoulders, wedges, that family. Its results are only updated hourly and you can only have one pattern filter in a scan. Candlesticks live somewhere else entirely.
Candlesticks are studies. Scan tab > Stock Hacker > Add filter > Study, then in the study dropdown start typing the pattern name.
There are about 58 of them in the library, split into three groups. Seventeen that work both ways (Engulfing, Harami, HaramiCross, Doji, Marubozu, BeltHold, Kicking, ThreeLineStrike and friends), twenty bullish only (Hammer, InvertedHammer, MorningStar, PiercingLine, ThreeWhiteSoldiers, ThreeOutsideUp), and twenty one bearish only (ShootingStar, HangingMan, DarkCloudCover, EveningStar, ThreeBlackCrows).
Pick the right plot, or you get the wrong direction
Engulfing has two plots, Bullish and Bearish. So does most of the two-way group. After you select the study the filter row lets you choose which plot to evaluate, and the comparison should be set so a true plot counts as a hit. Leave it on the default and you may be scanning for bearish engulfing while thinking you’re doing the opposite.
Hammer only has a Bullish plot, so there’s nothing to choose there.
Why the built-ins find fewer names than you expect
Every one of these studies has a trend setup input, and that’s the part nobody reads. The built-in Engulfing doesn’t just look at two candles. It also demands a run of falling closes before the pattern. Here’s the shape of it, from the shipped study:
input length = 20;
input trendSetup = 3;
def BodyMax = Max(open, close);
def BodyMin = Min(open, close);
def IsEngulfing = BodyMax > BodyMax[1] and BodyMin < BodyMin[1];
def IsWhite = open < close;
def IsBlack = open > close;
def IsPrevDoji = IsDoji(length)[1];
plot Bullish = IsDescending(close, trendSetup)[1] and # three lower closes first
(IsBlack[1] or IsPrevDoji) and
IsWhite and
IsEngulfing;
IsDescending(close, trendSetup)[1] is the gate. Three consecutive lower closes going into the pattern. Plenty of textbook-looking engulfing candles fail that, which is why your eyes find them on the chart and the scan doesn’t. Hammer adds two more inputs on top, body factor and shadow factor, which decide how short the body and how long the shadow have to be.
You can lower trend setup to 1 in the filter’s input parameters and immediately get more hits. If you want no trend requirement at all, write your own.
Rolling your own: bullish engulfing
Pick Custom in the study dropdown, then the thinkScript Editor tab in the Scanner Custom Filter window. Define the pattern explicitly, because “engulfing” means at least three different things depending on who you ask. This version is body over body, which is the common one.
# Bullish engulfing, body-over-body definition, no trend requirement
def priorRed = close[1] < open[1]; # previous candle closed down
def todayGreen = close > open; # this candle closed up
def engulfs = close > open[1] and open < close[1]; # this body covers the previous body
plot scan = priorRed and todayGreen and engulfs;
If you want the stricter “whole range engulfed” version, swap that third line for high > high[1] and low < low[1]. Far fewer hits.
One plot, and only one. A custom scan filter using standard thinkScript syntax has to have exactly one plot statement or it won’t work.
Rolling your own: hammer / bullish pin bar
Same idea, but here the definition really is arbitrary and you should own the numbers rather than inherit them.
# Hammer / bullish pin bar: long lower wick, small body near the top, little upper wick
def rng = high - low;
def bodySize = AbsValue(close - open);
def lowerWick = Min(open, close) - low;
def upperWick = high - Max(open, close);
plot scan = rng > 0
and lowerWick >= 2 * bodySize # the 2 is the whole pattern, tune this first
and bodySize <= 0.30 * rng
and upperWick <= 0.15 * rng;
The rng > 0 guard matters more than it looks. A candle where high equals low (a symbol that didn’t trade in that bar) would otherwise divide your logic into nonsense, and thin premarket names produce those constantly.
Raise the 2 to 3 and you get proper long-tailed pin bars and a much shorter list. Drop it to 1.5 and you’ll drown.
When the pattern is actually real
Set the filter’s Aggregation Period to Day and you’re scanning the current, still forming daily bar. Run that scan at 11am and close is just wherever the stock happens to be trading right now. A candle that looks like a perfect hammer at lunch can close as a plain red bar. The scan wasn’t wrong, the bar hadn’t finished.
So daily pattern scans are an after-the-close job. Run it at 4:15pm or later and the hits are real. Run it mid-session and treat everything as provisional.
If you want to scan for a pattern that completed on the previous bar rather than the current one, offset everything by one: close[1] > open[1] and so on. That difference, and how it interacts with within, is covered in within vs offset.
If your pattern scan returns nothing at all
Before you rewrite the logic, check the aggregation period on the filter and check what Scan in is set to. Those two account for most empty pattern scans, and the full order to check things in is on the empty scan page.
Quickest sanity check going: take the exact same code, add it to a chart as a study with plot scan painted as points, and look at a symbol you know had the pattern last week. If it doesn’t paint on the chart, the scan was never going to find it either.