New

Complete Suite: all 11 tools. 5 new Premium desks inside.All 11 tools $557.98 one by one

$557.98 one by one $97 once Save $460.98Get the SuiteGet it

Pine Script v5 to v6 converter & repaint checker

Paste a v5 Pine Script to convert it to v6, see every change, and keep v5's behaviour unless you choose v6.

Updated 23 October 2026. Runs in your browser; nothing you type is uploaded.

Sample scriptRepaint risk on line 5

Try a worked example

Uses when=, transp=, 250 / 100, a ta.* call after and, profit with limit, and no margin settings.

Converted to v6: 3 edits applied · 4 decisions (4 set to keep v5)

Behaves as your v5 script did. Switch any decision below to v6.

Why:Line 1Line 2Line 6Line 8Line 9Line 11Line 12

v5 line 1, removed: //@version=5
v6 line 1, added: //@version=6
v5 line 2, removed: strategy("v5 RSI breakout (worked example)", overlay = true)
v6 line 2, added: strategy("v5 RSI breakout (worked example)", overlay = true, margin_long = 0, margin_short = 0)
basis = ta.sma(close, 20)
basis = ta.sma(close, 20)
rsi = ta.rsi(close, 14)
rsi = ta.rsi(close, 14)
v5 line 6, removed: riskTicks = 250 / 100
v6 line 6, added: riskTicks = int(250 / 100)
v5 line 8, removed: longSignal = close > basis and ta.crossover(rsi, 50)
v6 line 8, added: crossover_8 = ta.crossover(rsi, 50)
v5 line 9, removed: plot(basis, "Basis", color = color.orange, transp = 30)
v6 line 9, added: longSignal = close > basis and crossover_8
v6 line 10, added: plot(basis, "Basis", color = color.new(color.orange, 30))
v5 line 11, removed: strategy.entry("Long", strategy.long, when = longSignal)
v6 line 12, added: if longSignal
v5 line 12, removed: strategy.exit("Exit", "Long", profit = 400, limit = close * 1.03, loss = riskTicks * 100)
v6 line 13, added: strategy.entry("Long", strategy.long)
v6 line 14, added: strategy.exit("Exit", "Long", limit = close * 1.03, loss = riskTicks * 100)
v5 line 1, removed: //@version=5
v5 line 2, removed: strategy("v5 RSI breakout (worked example)", overlay = true)
v6 line 1, added: //@version=6
v6 line 2, added: strategy("v5 RSI breakout (worked example)", overlay = true, margin_long = 0, margin_short = 0)
basis = ta.sma(close, 20)
rsi = ta.rsi(close, 14)
v5 line 6, removed: riskTicks = 250 / 100
v6 line 6, added: riskTicks = int(250 / 100)
v5 line 8, removed: longSignal = close > basis and ta.crossover(rsi, 50)
v5 line 9, removed: plot(basis, "Basis", color = color.orange, transp = 30)
v6 line 8, added: crossover_8 = ta.crossover(rsi, 50)
v6 line 9, added: longSignal = close > basis and crossover_8
v6 line 10, added: plot(basis, "Basis", color = color.new(color.orange, 30))
v5 line 11, removed: strategy.entry("Long", strategy.long, when = longSignal)
v5 line 12, removed: strategy.exit("Exit", "Long", profit = 400, limit = close * 1.03, loss = riskTicks * 100)
v6 line 12, added: if longSignal
v6 line 13, added: strategy.entry("Long", strategy.long)
v6 line 14, added: strategy.exit("Exit", "Long", limit = close * 1.03, loss = riskTicks * 100)
Show the full v6 code
//@version=6
strategy("v5 RSI breakout (worked example)", overlay = true, margin_long = 0, margin_short = 0)

basis = ta.sma(close, 20)
rsi = ta.rsi(close, 14)
riskTicks = int(250 / 100)

crossover_8 = ta.crossover(rsi, 50)
longSignal = close > basis and crossover_8
plot(basis, "Basis", color = color.new(color.orange, 30))

if longSignal
    strategy.entry("Long", strategy.long)
strategy.exit("Exit", "Long", limit = close * 1.03, loss = riskTicks * 100)

Needs your decision

Line 2: strategy() doesn't set margin_long or margin_short

v6 defaults to 100% margin, with margin calls; v5 used 0%.

See line 2 in the diff
Why, and what each choice does

v5 strategies defaulted to 0% margin, which never checks the funds available. v6 defaults to 100%, which can bring margin calls the v5 backtest never had. Setting 0 keeps the v5 backtest. Set your broker's values if you want a realistic one. TradingView's guide on this

Your v5
strategy("v5 RSI breakout (worked example)", overlay = true)
Keep v5: margin_long = 0, margin_short = 0 (keeps v5 behaviour)
strategy("v5 RSI breakout (worked example)", overlay = true, margin_long = 0, margin_short = 0)
Line 6: 250 / 100 divides whole numbers

v6 keeps the fraction: 5 / 2 is 2.5, where v5 gave 2.

See line 6 in the diff
Why, and what each choice does

v5 dropped the fraction when it divided two whole-number constants (5 / 2 was 2). v6 keeps it (2.5). int() keeps v5's result. If v5's whole number was a mistake, use v6's. TradingView's guide on this

Your v5
riskTicks = 250 / 100
Keep v5: int(250 / 100) (keeps v5 behaviour)
riskTicks = int(250 / 100)
Line 8: ta.crossover() on the right of and / or

v6 skips this call on bars where the left side already decides.

See line 8 in the diff
Why, and what each choice does

v5 evaluated both sides of and / or on every bar. v6 skips the right side when the left side already decides, so ta.crossover() misses bars and its history goes wrong. Computing it on the line before keeps v5's result. TradingView's guide on this

Your v5
longSignal = close > basis and ta.crossover(rsi, 50)
Keep v5: compute the call on the line before and use its result (keeps v5 behaviour)
crossover_8 = ta.crossover(rsi, 50)
longSignal = close > basis and crossover_8
Line 12: strategy.exit() has both profit and limit

v6 exits at whichever level comes first; v5 used limit only.

See line 12 in the diff
Why, and what each choice does

v5 used limit and ignored profit when both were given. v6 uses whichever of the two levels the price reaches first, so the exits can move. Removing profit keeps what v5 did. TradingView's guide on this

Your v5
strategy.exit("Exit", "Long", profit = 400, limit = close * 1.03, loss = riskTicks * 100)
Keep v5: remove profit (keeps v5 behaviour)
strategy.exit("Exit", "Long", limit = close * 1.03, loss = riskTicks * 100)
Converted automatically (3)
Notes: no edit needed (1)
  • Past 9,000 orders, v6 drops the oldest instead of stopping. A v5 strategy stopped with an error past 9,000 orders (outside Deep Backtesting). v6 keeps running and drops the oldest ones from its results, so strategy.closedtrades.* on those returns na. strategy.closedtrades.first_index gives the first one kept. TradingView's guide on this

Next: paste it into TradingView's Pine Editor, save, and add it to the chart. An error? Find its message below, or let Pine Fixer fix it. How to add a script

v5 RSI breakout (worked example), Pine v5 strategy

No repaint flags found · 7 Pine v6 migration items

Static repaint checks passed, but the script uses legacy Pine syntax or an older //@version header that needs updating for Pine Script v6.

Critical repaint flags

0

0 caution flags to verify

Bar-close gate

Not found

barstate.isconfirmed or a bar-close alert

Higher-timeframe calls

0

request.security() calls, of a 40 limit

Plots and alerts

1

Plots, of a 64 limit; 0 alerts

Repaint and lookahead

No lookahead leaks, unshifted request.security() calls or realtime-only signal branches found in this snippet.

Want TradingView indicators whose Pine Script v6 source already passes every check?

Every desk in Pine Pro Library ships as readable .pine v6 source with confirmed higher-timeframe requests and barstate.isconfirmed event logging.

Share this calculator

Post on XPost to Reddit
Embed or cite it on your site

Free on any site, blog, Notion page or Substack: paste this code to show the live calculator. The credit line is optional; keep it, edit it, add rel="nofollow" or remove it.

<iframe src="https://proea.app/embed/tools/pine-script-checker" title="Pine Script v5 to v6 converter & repaint checker" loading="lazy" referrerpolicy="strict-origin-when-cross-origin" style="display:block;width:100%;height:820px;border:0;border-radius:12px"></iframe>
<p style="font-size:12px">Pine Script v5 to v6 converter & repaint checker by <a href="https://proea.app/en/tools/pine-script-checker?utm_source=embed&utm_medium=referral&utm_campaign=pine-script-checker">ProEA</a></p>

Writing about it instead? Cite the method and link the page:

The math: safe HTF = request.security(symbol, tf, expr[1], lookahead = barmerge.lookahead_on)

Pine Script v6 compiler errors, as TradingView prints them

Compiled on TradingView on 23 October 2026 from the “Before” code; your names and types will differ. Paste your script above and the checker marks the line.

Error CE10173Cannot assign a value of the "simple na" type to the "signal" variable. The variable is declared with the "const bool" type.

A bool in v6 is only true or false; it can no longer hold na.

Before
bool signal = na
Pine v6
bool signal = false
Error CE10120The "strategy.entry" function does not have an argument with the name "when"

v6 removed when= from the strategy.* order functions. The condition moves to an if block.

Before
strategy.entry("L", strategy.long, when = go)
Pine v6
if go
    strategy.entry("L", strategy.long)

The full fix, with this code in the checker

Error CE10120The "plot" function does not have an argument with the name "transp"

v6 removed transp= (v5 only hid it). Transparency now goes inside the colour.

Before
plot(ma, color = color.blue, transp = 70)
Pine v6
plot(ma, color = color.new(color.blue, 70))

The full fix, with this code in the checker

Error CE10123Cannot call "operator ?:" with argument "expr0"="strategy.position_size". An argument of "series float" type was used but a "const bool" is expected.

A number no longer counts as true or false in v6. Compare it, or wrap it in bool().

Before
bgcolor(strategy.position_size ? color.green : na)
Pine v6
bgcolor(strategy.position_size != 0 ? color.green : na)
Error CE10123Cannot call "na" with argument "x"="b". An argument of "series bool" type was used but a "simple float" is expected.

na(), nz() and fixnan() no longer take a bool, because a v6 bool is never na; nz() gives the same message with "nz" and "source". The test can go.

Before
b = close > open
if na(b)
    b := false
Pine v6
b = close > open
// b is never na in v6: nothing to replace
Error CE10290Cannot use the history-referencing operator on fields of user-defined types. Reference the history of the object first by enclosing it in parentheses, and then request the field, e.g. "(object[1]).field" instead of "object.field[1]".

In v6, [] goes on the object, then you read the field.

Before
plot(myBox.top[1])
Pine v6
plot((myBox[1]).top)
Error CE10040The "plot()" function cannot have a "linewidth" value less than 1. Use a value of 1 or greater in the function call.

v6 needs a linewidth of 1 or more in plot() and hline(). v5 drew anything below 1 at width 1, so 1 draws the same.

Before
plot(ma, linewidth = 0)
Pine v6
plot(ma, linewidth = 1)
Error CE10072Two or more arguments are passed to the "color" parameter. You can pass only one argument.

v6 rejects the same parameter passed twice, once by position and once by name. v5 only warned and used the first value, so keep that one to draw what v5 drew.

Before
plot(ma, "MA", color.blue, color = color.red)
Pine v6
plot(ma, "MA", color.blue)
Warning CW10002The "ta.crossover()" call inside the conditional expression might not execute on every bar, which can cause inconsistent calculations because the function depends on historical results. For consistency, assign the call's result to a global variable and use that variable in the expression instead.

v6 skips the right side of and / or when the left side already decides, so a ta.* call there misses bars. It compiles, and calculates wrong.

Before
if close > open and ta.crossover(rsi, 50)
Pine v6
xUp = ta.crossover(rsi, 50)
if close > open and xUp
Error CE10271Could not find function or function reference 'study'

study() was renamed indicator() in v5.

Before
study("My script")
Pine v6
indicator("My script")

The full fix, with this code in the checker

Error CE10271Could not find function or function reference 'security'

security() became request.security() in v5. Take the higher timeframe's previous bar with lookahead on, so it doesn't repaint.

Before
security(syminfo.tickerid, "D", close)
Pine v6
request.security(syminfo.tickerid, "D", close[1], lookahead = barmerge.lookahead_on)

The full fix, with this code in the checker

Every Pine Script error with its fix (69 messages): Cannot use "plot" in local scope, Undeclared identifier, Syntax error at input, Mismatched input and the rest, each compiled on TradingView.

Read the full guide

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 indexing close[1] lets every intraday bar on history read the finished daily close hours before it actually prints. The non-repainting pattern pairs expr[1] with barmerge.lookahead_on.
  • Realtime vs historical branching: Conditions that test barstate.isrealtime, barstate.islast, barstate.ishistory, or varip state 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 = true or calc_on_order_fills = true in a strategy() 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 = cond moves into an if cond block. transp = 70 folds into color.new(c, 70); TradingView's own converter drops it, and the plot turns opaque. A number used as a condition gets bool(), and timeframe.period == "D" becomes "1D". Each edit is made only where its conditions hold. For example, when = stays a decision if another argument calls a ta.* function.
  • Your decision: v6 runs the same code differently. 250 / 100 is 2.5, not 2. and / or skip a ta.* call on some bars. strategy.exit() uses whichever of profit and limit the price reaches first. Strategies default to 100% margin, and a for loop 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: In strategy.entry() and strategy.exit(), pass your JSON string to alert_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. With alert.freq_once_per_bar or alert.freq_all, gate the call on barstate.isconfirmed for the same result.
Method and privacy

Method: Converts in your browser by reading the code token by token, never with a line-by-line regex. The rules come from TradingView's v6 migration guide (read 2026-10-22). Some edits keep v5's meaning exactly and are made automatically: when= into an if block, transp= into color.new(), a number used as true/false into bool(), "D" into "1D", a repeated argument, a linewidth below 1, and a bool declared na. Others are listed as decisions, with v5's behaviour kept by default where an exact edit exists: whole-number division, a ta.* call after and / or, strategy.exit pairs, default margin, for-loop ends and dynamic requests. A line it can't read is left as written. The repaint checks are static: they read request.security() offsets and lookahead, calc_on_every_tick, calc_on_order_fills, barstate branching and varip. Neither replaces compiling in the Pine Editor or a Bar Replay test.

Calculations run entirely in your browser — nothing you type is uploaded. Your last numbers stay in this browser so you can pick up where you left off; Forget them clears them. A link with your numbers carries them after the # sign, which browsers do not send to servers, and the page takes them out of the address before any analytics loads. Copy and download actions save your calculation locally. The site may record coarse tool actions under its privacy policy, without inputs, results, file contents or filenames.

Common questions

How does this checker detect Pine Script repainting?

It scans your code for the five static patterns that cause historical bars to differ from live bars: request.security() with barmerge.lookahead_on and no [1] offset, unshifted higher-timeframe expressions, calc_on_every_tick = true, calc_on_order_fills = true, and signal logic that branches on barstate.isrealtime, barstate.islast, or varip.

How to tell if a TradingView indicator repaints (20-minute audit)

What is the confirmed non-repainting request.security() pattern in Pine v6?

Index the higher-timeframe expression with [1] inside request.security() and pair it with lookahead = barmerge.lookahead_on — for example, request.security(syminfo.tickerid, "D", high[1], lookahead = barmerge.lookahead_on). That locks the value to the last completed higher-timeframe bar on both historical and live bars.

What changes when migrating from Pine Script v4 or v5 to v6?

Some changes stop a script compiling: a bool can no longer be na, numbers no longer act as true/false, the when = parameter is gone from strategy orders, and transp, which v5 only hid, is gone. Others compile and quietly change behaviour: 3 / 2 is now 1.5, and / or skip the right side when the left decides it (so a ta.* call there loses bars), timeframe.period reads "1D" not "D", strategy.exit pairs profit with limit and loss with stop, and strategies default to 100% margin. This converter names each one on its line, links TradingView's section on it, and keeps v5's behaviour unless you choose v6. v4 scripts go to v5 first.

Pine Script v6 migration guide

Does TradingView have a built-in Pine Script v6 converter?

Yes. In the Pine Editor, Manage script, then Convert code to v6, converts a v5 script that compiles; a v4 script goes to v5 first. TradingView's guide says that converter removes any transp argument, which turns those plots opaque, and it doesn't list what it changed. This page keeps the transparency in color.new(), shows every change in a diff, and lists each place where v6 runs a line differently, with v5's behaviour kept unless you choose v6. Either way, compile the result in the Pine Editor.

Is my script uploaded when I convert it?

No. The conversion runs in your browser, and the page sends your code nowhere, not even to TradingView. To compile the result, paste it into the Pine Editor yourself.

What is the current version of Pine Script?

Pine Script v6, declared with //@version=6 on the first line. Scripts written for v5 still run on TradingView, but new language features only arrive in v6.

How to add a Pine Script to TradingView

Can I open a .pine file directly in this tool?

Yes. Click Open .pine / .txt file above the editor to load any local .pine file into your browser, inspect its source code, and run the repaint and v6 readiness checks without uploading the file to a server.

How to open a .pine file on Windows, Mac, or TradingView

Does passing static checks mean an indicator will never repaint live?

No. Static checks catch the most common code-level lookahead and barstate traps, but dynamic request loops, pivot confirmation lags, or intrabar order fills still need a chart reload test and a forward paper-trading check.

How do I fix 'Cannot assign a value of the na type to the bool variable' in Pine Script v6?

In v6 a bool is only true or false, never na, so the compiler stops on bool signal = na. Start it as false (bool signal = false), or keep the missing state in a separate variable. na(), nz() and fixnan() no longer accept a bool either; compare numbers explicitly (x != 0) where v5 treated them as true/false.

Every v6 compiler message, as TradingView prints it

How do I fix 'does not have an argument with the name when' or 'transp' in Pine Script v6?

Pine Script v6 removed the when= parameter from strategy.entry(), strategy.exit(), and strategy.close()—wrap the order call inside an if condition block instead. For transp= in plot(), bgcolor(), or fill(), replace transp = 70 with color = color.new(baseColor, 70).

What do 'historical offset is beyond the historical buffer's limit' and 'too many unique request.*() calls' mean?

The first means a series was asked for more history than its buffer keeps (at most 5,000 bars; 10,000 for open, high, low, close and time): set max_bars_back in indicator() or strategy(), or on that series. The second means more than 40 unique request.*() calls (64 for Pine v6 scripts on Professional plans such as Ultimate): fetch several values from one symbol and timeframe in a single tuple request.security() call.

Why does a TradingView webhook fail with 400 Bad Request on {{strategy.order.action}}?

TradingView only sends the application/json header when the rendered alert payload is valid JSON. Unquoted string placeholders such as {{strategy.order.action}}, {{strategy.market_position}}, or {{ticker}}, trailing commas, or curly quotes make the payload invalid JSON so TradingView sends text/plain instead.

Fix, build or get finished Pine v6

  • Pine Fixer An AI skill pack for 45 compiler-verified Pine v6 failure classes, with a repaint clinic. $9.99 once.
  • Pine Starter Stack Prompt Vault, Builder Lite and Pine Fixer in one zip. $19.99 once.
  • Pine Pro Library 19 TradingView desks with their full Pine v6 source and every update. $49 once.

Goes deeper

Limits and trading risk

These tools calculate from your inputs, supported historical records or explicitly labelled teaching examples. They do not verify a live account, predict future returns or confirm that a broker or prop-firm rule has been met. Missing costs, floating losses and changed market conditions can materially change outcomes. Trading can lose the capital committed to it; check the stated method and your account specifications.

Read the risk disclosure