Guide

Why Your Pine Script Is Slow and How to Fix It

Loop timeouts, laggy charts, and the dreaded runtime errors almost always trace to a handful of culprits. Here is how to find them and fix them.

10 min read

A slow Pine Script shows up in a few ways: the chart takes a long time to load, edits feel sluggish, or you hit an outright error like a loop timeout or a memory limit. The good news is that Pine Script performance problems are not mysterious. They come from a short list of causes, and once you know what to look for, most are quick to fix.

The root reason performance matters so much in Pine Script is the execution model: your script runs once per bar, so anything expensive is paid again on every bar of history. A cost that looks trivial on one bar becomes thousands of times larger across a full chart. Keep that multiplier in mind as we go through the culprits.

CulpritWhy it hurtsFix
request.security in a loop or repeatedEach call is expensive; repeats multiplyRequest once, store the result
Heavy per-bar loopsRuns every bar, 500ms cap per barUse built-in series functions
Recreating drawings every barObject churn and memory pressureUpdate existing objects, cap counts
Large or growing collectionsRebuilt or scanned each barCap size, use reducers

The profiler: measure before you guess

TradingView includes a Pine profiler that shows how much time each part of your script consumes. Turn it on and it annotates your code with the relative cost of each line, which tells you where the time actually goes instead of where you assume it goes. Always profile before optimizing; the slow line is often not the one you would have guessed, and time spent optimizing a cheap line is wasted.

PineScripter flags expensive patterns as it writes, before they become a timeout

Culprit one: redundant request.security calls

Requesting data from another symbol or timeframe is one of the most expensive things a script can do, and the classic mistake is calling request.security multiple times for the same data, or calling it inside a loop when the result does not change per iteration. Request each piece of data once, store it in a variable, and reuse it.

pine
//@version=6
indicator("Request once, reuse", overlay = true)

// SLOW: three separate requests for the same higher timeframe
// htfHigh = request.security(syminfo.tickerid, "D", high)
// htfLow  = request.security(syminfo.tickerid, "D", low)
// htfClose= request.security(syminfo.tickerid, "D", close)

// FAST: one request returning a tuple
[htfHigh, htfLow, htfClose] = request.security(syminfo.tickerid, "D",
     [high, low, close])

plot(htfClose)

Bundling several values into one request with a tuple turns three expensive calls into one. For scanners that legitimately need many symbols, the same discipline plus the memory limits apply, covered in dynamic requests.

Culprit two: loops that should be series functions

New users often write a for loop to sum or average recent values. Pine already has that built in, and the built-in is both faster and clearer. Any loop on a single bar is capped at 500 milliseconds, and since it runs every bar, a heavy loop is the most common cause of an outright timeout. Reach for math.sum, ta.highest, array.avg, and friends before writing a loop.

pine
//@version=6
indicator("Prefer built-ins over loops")

// SLOW: manual loop over 100 bars, every bar
sumManual = 0.0
for i = 0 to 99
    sumManual += close[i]
avgManual = sumManual / 100

// FAST: built-in, optimized internally
avgBuiltin = ta.sma(close, 100)

plot(avgBuiltin)

Culprit three: drawing-object churn

Creating new lines, labels, or boxes on every bar generates enormous object churn and pushes against the drawing-object caps. Instead of deleting and recreating a drawing each bar, create it once and update its properties, or only draw when something actually changes. Remember drawings also cannot extend more than 500 bars into the future or 9,999 into the past, so sprawling annotation schemes hit walls on top of being slow. The collection side of this is in arrays, matrices, and maps.

Culprit four: rebuilding state every bar

If you rebuild a large array or recompute a big aggregate from scratch on every bar, you are throwing away work you already did. Use var to persist state and update it incrementally, pushing one new value and dropping one old value rather than rebuilding the whole window. The persistence patterns are in the var and varip guide.

A practical optimization order

Work in this order. First, profile to find the real hotspot. Second, collapse redundant request.security calls into single tuple requests. Third, replace hand-written loops with built-in series functions. Fourth, stop recreating drawings and persist state with var. Fifth, cap the size of any collection and lean on built-in reducers. Following that order fixes the vast majority of slow scripts without touching the actual strategy logic.

Most of these anti-patterns are exactly what generic AI produces, because it writes Pine Script the way it writes ordinary code: loops everywhere, no awareness of the per-bar multiplier, redundant requests. PineScripter writes with the execution model in mind and flags expensive patterns as it goes, so the code you get is closer to optimal from the start. If you have a script that times out or lags, you can hand it to PineScripter to profile and rework, or read how it works first.


Disclaimer: PineScripter is a coding tool for Pine Script development. It does not provide financial advice and does not guarantee trading profits. Always backtest strategies thoroughly and understand the risks before live trading.