DayTraderScripts
← Back to blog
thinkorswimthinkScriptscannerstock scannerliquidity sweep

Thinkorswim Scanner Scripts: How Custom Scans Really Work (and Where They Break)

A practitioner's guide to thinkScript scanner scripts in Thinkorswim — scan vs study vs watchlist column, the platform's real limitations, and a worked liquidity-sweep scan concept.

DayTraderScripts Desk·Jul 17, 2026·5 min read

Thinkorswim's scanner is one of the most powerful free screening engines retail traders have access to — and one of the most misunderstood. The same thinkScript language drives charts, scans, and watchlist columns, but the three contexts execute differently, and code that works perfectly on a chart can silently return garbage in a scan.

Here's how thinkorswim scanner scripts actually execute, the hard limitations you can't code around, and a worked example of turning a discretionary concept — the liquidity sweep — into a scannable condition.

Scan vs. Study vs. Watchlist Column: Same Language, Three Engines

thinkScript runs in three main contexts, and the differences are not cosmetic:

  • Chart study. Runs across every visible bar. You get plots, labels, and full recursive history. This is where you prototype, because you can see what your logic is doing bar by bar.
  • Scan (Study Filter). Runs across the entire scannable universe, but each symbol is evaluated only on the last bar of the chosen aggregation. Your script must reduce to a single boolean plot — true means the symbol passes. There is no visual output, no labels, no second chances.
  • Watchlist column. Runs per-symbol on the last bar like a scan, but returns a displayed value (and optionally a background color) instead of a filter. Watchlist columns update on a refresh cycle, not tick-by-tick — treat them as "recent," not "live."

The workflow that saves you pain: build it as a chart study first, verify the signal bars visually, then strip it down to a single boolean plot for the scan. Scan scripts that were never charted are scan scripts that were never tested.

The Limitations You Cannot Code Around

Thinkorswim's scanner has hard walls. Knowing them up front saves hours:

  1. Last-bar evaluation. A scan answers one question: "is the condition true on the most recent bar?" You can reference history within the script (close[5], Highest(high, 20)[1]), so "did X happen in the last N bars" is expressible via lookbacks like Sum(condition, 10) > 0. But you cannot plot, count across the full chart, or return anything richer than pass/fail.
  2. No float data. thinkScript has no function that returns share float. Float rotation, float percentage, low-float filters — none of it is expressible in a scan script. The stock hacker's built-in fundamental filters (market cap, shares outstanding) are the closest you get, set outside the script. Anyone selling you a "float rotation thinkScript scanner" is selling something the language cannot do.
  3. Aggregation rules. A scan script runs on one aggregation, chosen in the scanner UI — and unlike chart studies, scan scripts cannot request a second aggregation period. No comparing the 5-minute close to the daily 200-SMA inside one script. The standard workaround is stacking multiple study filters in the same scan, each on its own aggregation. Also: intraday scans have a limited historical window, so a 200-period lookback on a 1-minute aggregation may not have the data it needs.
  4. No cross-symbol references. Scans evaluate each symbol in isolation. Comparisons against SPY, sector ETFs, or another ticker via a second symbol reference are unavailable in study filters.
  5. Recursion needs care. Recursive variables work in scans, but must be initialized with CompoundValue to behave predictably; uninitialized recursion that looks fine on a chart can misfire in the scan engine.
  6. Universe and refresh limits. Scans return up to 500 results, and custom-study scans refresh on a cycle measured in minutes, not milliseconds. A thinkScript scan is a discovery tool, not an execution trigger.

Design around these instead of fighting them: do timeframe confluence with stacked filters, do float filtering with the built-in fundamentals, and treat every scan hit as a candidate to be verified on the chart.

Worked Example: A Liquidity-Sweep Scan Concept

A liquidity sweep is price running the stops beyond an obvious level — a prior low, usually — then snapping back, revealing that the break was a stop hunt rather than real distribution. On a chart you see it as a wick spearing below support and closing back above it. Here's how the concept reduces to scannable conditions.

The concept, stated as rules on the current bar:

  1. A meaningful level existed: the lowest low of the prior 20 bars, excluding the current bar — def sweepLevel = Lowest(low[1], 20);
  2. The current bar swept it: low < sweepLevel — price traded below the level.
  3. The sweep failed: close > sweepLevel — the bar closed back above. The wick took the stops; the close rejected the breakdown.
  4. Participation was real: volume > 1.5 * Average(volume, 20) — sweeps on dead volume are noise, not fuel.
  5. The rejection was decisive: the close finished in the upper half of the bar's range — close > (high + low) / 2.

The final scan plot is just the conjunction:

plot signal = low < sweepLevel and close > sweepLevel
    and volume > 1.5 * Average(volume, 20)
    and close > (high + low) / 2;

Run it on a 5- or 15-minute aggregation during regular hours, stack a daily-aggregation filter on top for trend context (say, close above the daily 20-EMA), and add a built-in volume filter (average daily volume above 1M) so you're only sweeping liquid names. Note what the last-bar rule means here: the scan fires only while the sweep bar is the current bar, so this is a scan you run live and re-run — or widen with a Sum(..., 3) > 0 lookback to catch sweeps from the last few bars at the cost of some staleness.

That's the honest version of this scan — and also its honest ceiling. Bid/ask context, session-level filtering, and multi-timeframe confirmation on the same filter are beyond what a study filter can express. Our Liquidity Sweep Scanner packages the charting-side companion study and the pre-built scan stack together, so the scan finds candidates and the chart study shows you which historical bars actually qualified — which is the verification step most scan users skip.

Debugging Scans That Return Nothing (or Everything)

  • Chart it first. Paste the scan logic into a chart study, plot the boolean, and count signals over the last month. Zero chart signals means the logic is wrong; plenty of chart signals but zero scan hits means a context problem (aggregation, data window, initialization).
  • Loosen one condition at a time. Comment out conditions until results appear; the last one you removed is the filter doing the killing.
  • Check the aggregation math. A 20-bar lookback on a daily aggregation is a month; on a 1-minute aggregation it's 20 minutes. The same script means different things on different aggregations.
  • Beware IsNaN. Conditions comparing against NaN silently evaluate false. Guard derived values, especially early-bar recursive ones.

Bottom Line

Thinkorswim scans are boolean questions asked of the last bar, one symbol at a time, on one aggregation. Respect those constraints — no float data, no cross-symbol logic, stacked filters for timeframe confluence — and the scanner becomes a genuinely sharp discovery tool. Prototype on the chart, reduce to one plot, verify every hit visually before it becomes a trade.

Educational content, not financial advice. Trading involves substantial risk of loss.

Keep reading