What this Pine Script checker audits
When a TradingView indicator or strategy looks unusually accurate on historical bars and then loses money live, the cause is almost always in the source code rather than the chart settings. This browser tool inspects any .pine file or pasted snippet across three dimensions before you trust its signals or port it to Pine Script v6.
- Higher-timeframe lookahead leaks: Calling
request.security(syminfo.tickerid, "D", close, lookahead = barmerge.lookahead_on)without indexingclose[1]lets every intraday bar on history read the finished daily close hours before it actually prints. The non-repainting pattern pairsexpr[1]withbarmerge.lookahead_on. - Realtime vs historical branching: Conditions that test
barstate.isrealtime,barstate.islast,barstate.ishistory, orvaripstate inside entry or alert logic behave one way while you watch live ticks and a different way when you reload the chart. - Intrabar strategy flags: Setting
calc_on_every_tick = trueorcalc_on_order_fills = truein astrategy()declaration evaluates every live tick while historical bars only have open, high, low and close.
What the v5 to v6 converter changes, and what it asks you
Paste a //@version=5 script, or open a .pine file, and the converter rewrites it to v6 in your browser. It reads the code token by token, so a // or a bracket inside a string never counts as code. A line it can't read stays exactly as you wrote it. Each change is one of three kinds:
- Converted: v6 removed a way of writing something but kept what it does, so the new form does exactly what the old one did.
when = condmoves into anif condblock.transp = 70folds intocolor.new(c, 70); TradingView's own converter drops it, and the plot turns opaque. A number used as a condition getsbool(), andtimeframe.period == "D"becomes"1D". Each edit is made only where its conditions hold. For example,when =stays a decision if another argument calls ata.*function. - Your decision: v6 runs the same code differently.
250 / 100is 2.5, not 2.and/orskip ata.*call on some bars.strategy.exit()uses whichever of profit and limit the price reaches first. Strategies default to 100% margin, and aforloop re-reads its end. Each starts on the edit that keeps v5's behaviour, and one click switches it to v6's. - Notes: changes with nothing to edit, such as
color.red's new shade or white label text.
v4 scripts go to v5 first, with the Pine Editor's built-in converter. study(), security() and iff() don't compile in v5. v4's iff() also ran both branches where ?: runs only one, so swapping them isn't always safe. Nothing you paste is sent anywhere, not even to TradingView: paste the result into the Pine Editor, whose compiler is the final word.
After fixing static warnings, always run our 20-minute repaint audit on live and replayed bars, or compare your script with the 19 desks in Pine Pro Library and the full Pine Script v6 migration guide.
Pine Script runtime errors and script limits
A script that compiles can still stop on the chart when it reaches one of TradingView's limits. The messages below are TradingView's own, from its Pine Script docs and help centre:
The requested historical offset (X) is beyond the historical buffer's limit (Y).- A series keeps at most 5,000 bars of history (10,000 for open, high, low, close and time). Set max_bars_back in indicator() or strategy(), or call max_bars_back() on that series.
Loop is too long (> 500 ms)- A loop may run for 500 ms on one bar. Cap the loop, keep running totals in var variables instead of rescanning history, or do heavy drawing only on barstate.islast.
The script executes too many unique `request.*()` function calls- 40 unique request.*() calls, or 64 for Pine v6 scripts on Professional plans such as Ultimate. Fetch several values from one symbol and timeframe in one call with a tuple: [o, h, l, c] = request.security(sym, tf, [open, high, low, close]).
The script creates to many plots (x). The limit is 64- 64 plot outputs per script. Send alerts with alert() inside if blocks instead of alertcondition(), and draw fixed levels with line.new() or one table.
- Older boxes, lines or labels vanish from history
- Only the last 50 of each drawing type are kept by default; the most is 500 lines, boxes and labels, and 100 polylines. Raise max_lines_count, max_boxes_count or max_labels_count in indicator() or strategy().
TradingView webhook JSON payload & strategy alert placeholder errors
When connecting a Pine Script v6 strategy or indicator to a webhook receiver, TradingView only sends the Content-Type: application/json header if the rendered alert message is valid JSON. If a single placeholder or comma is malformed, TradingView falls back to text/plain and a receiver that expects JSON rejects it, often with 400 Bad Request:
- Unquoted string placeholders: Placeholders that output text—such as
{{strategy.order.action}}(buy/sell),{{strategy.market_position}}(long/flat/short),{{strategy.prev_market_position}},{{strategy.order.id}}, and{{ticker}}—must be wrapped in straight double quotes:"action": "{{strategy.order.action}}". Numeric placeholders like{{strategy.order.contracts}}and{{strategy.order.price}}can be quoted or unquoted depending on your receiver schema. - Passing dynamic JSON via
alert_message: Instrategy.entry()andstrategy.exit(), pass your JSON string toalert_message = ...and put{{strategy.order.alert_message}}alone in the TradingView Alert Message box so you do not nest JSON inside JSON. - Confirmed bars only:
alert(msg, alert.freq_once_per_bar_close)already fires only when the realtime bar closes, so an intrabar wick cannot send the webhook early. Withalert.freq_once_per_baroralert.freq_all, gate the call onbarstate.isconfirmedfor the same result.