Guide

What the Pine Script v6 Converter Fixes and What It Misses

The Pine Editor can convert a v5 script to v6 automatically. Here is exactly what it handles, what it cannot, and how to verify the result.

13 min read

TradingView ships a converter for this. You open the Pine Editor, use the Manage script menu, choose Convert code to v6, and the editor rewrites the mechanical parts of your v5 source. It is genuinely useful, and it is also where a lot of people stop, which is the problem. The converter fixes syntax. It cannot decide what your code was supposed to mean.

Short answer

The Pine Editor's "Convert code to v6" command automatically handles most mechanical v5-to-v6 changes, such as removing the deprecated transp parameter. It requires a v5 script that already compiles, and TradingView documents that in rare cases the converted script can still contain compilation errors. It cannot resolve changes that depend on intent, including three-state boolean logic that relied on na, mixed relative and absolute strategy.exit() parameters, and behavior differences introduced by v6 defaults such as 100 percent margin and dynamic requests.

Key facts

  • The Pine Editor highlights the //@version=5 annotation in yellow and offers "Convert code to v6" through the Manage script dropdown menu.
  • A script can be converted only if its v5 code compiles successfully, so a broken v5 script must be fixed before conversion.
  • TradingView states that in rare cases automatic conversion can produce a v6 script with compilation errors, which are then highlighted in the editor for manual resolution.
  • The converter removes the transp argument wherever it appears, because v6 removes that parameter from bgcolor(), fill(), plot(), plotarrow(), plotchar(), and plotshape().
  • In v6 the dynamic_requests parameter of indicator(), strategy(), and library() defaults to true, and the compiler disables the feature automatically when a script does not need it.
  • v6 changes strategy behavior that the converter cannot judge for you: the default long and short margin percentage is 100, and strategy.exit() now evaluates related relative and absolute parameters together instead of always prioritizing the absolute one.
  • timeframe.period now always includes a multiplier, so a daily chart reports "1D" rather than "D", which breaks direct string comparisons written for v5.
ChangeConverter handles itWhy
transp parameter removalYesMechanical deletion of a removed argument
Version annotationYesDirect substitution
Numeric to bool castingPartlyA cast is mechanical; the intent behind it is not
Three-state bool using naNoRequires choosing a new type to represent the states
Removed when parameterPartlyRestructuring into if blocks can change readability
Margin default changeNoA behavior default, not a syntax error
strategy.exit() parameter pairsNoRequires deciding which price level you intend
timeframe.period comparisonsNoA string literal in your logic, not invalid syntax
Dynamic request behaviorNoDepends on whether your requests should be dynamic

Run the converter properly: baseline first

The converter has one hard prerequisite that people skip. It only works on a v5 script that compiles. That constraint is useful rather than annoying, because it gives you a clean baseline: if an error appears after conversion, it belongs to the version change and not to a pre-existing problem you had not noticed.

So the sequence is: confirm the v5 script compiles, save or duplicate the original source, then convert the copy. Keeping the v5 original is not ceremony. Several v6 changes alter behavior without producing an error, and the only way to detect those is to compare the new output against the old one. If you convert in place and close the tab, that comparison is gone.

Resist the urge to add features in the same session. A migration is much easier to reason about when every difference in output has exactly one possible explanation. Convert, verify, commit that as a known-good v6 baseline, and only then start improving the script.

What the converter genuinely handles

Mechanical substitutions are its strength. The clearest example is transp: v6 removes that parameter from bgcolor(), fill(), plot(), plotarrow(), plotchar(), and plotshape(), and the converter strips the argument wherever it encounters it. That is a safe, unambiguous edit.

It also handles the version annotation itself and a range of straightforward syntax updates where there is only one correct v6 form. For a simple indicator that plots a couple of moving averages, the converter will frequently produce a working v6 script with no further work required, and it is reasonable to trust it for that class of script after a visual check.

The useful mental model is that the converter is a syntax translator, not a code reviewer. Anywhere v5 had exactly one valid v6 equivalent, expect the converter to get it right. Anywhere v5 was ambiguous, or where v6 changed a default rather than a syntax rule, expect to make the decision yourself.

Booleans: the change that needs your judgment

v6 tightened boolean handling in two ways. Numeric values no longer implicitly cast to bool, and a bool can no longer hold na. The first is mechanical: wrapping a numeric expression in bool() restores compilation. The second is not mechanical at all, because it removes a state your code may have been using deliberately.

In v5, a bool had three possible values: true, false, and na. That third state was often used to mean "not determined yet", such as a direction flag before any position exists. In v6 the undefined branch of a conditional returns false instead of na, so the "not yet known" case and the "known to be false" case collapse into the same value. A converter cannot fix this, because both of your original states are still representable, just not by a single boolean.

The correct fix is to choose a type that can express every state you actually need. An integer with values such as -1, 0, and 1, or an enum, restores the distinction explicitly. TradingView's migration guide walks through precisely this example with a position-direction flag. This is the change most likely to alter behavior silently, so inspect the first bars of the dataset where no state has been established yet.

pine
// v5: three states, where na meant "no position yet"
// bool isLong = if strategy.position_size > 0
//     true
// else if strategy.position_size < 0
//     false

// v6: model the three states explicitly
int tradeDirection = if strategy.position_size > 0
    1
else if strategy.position_size < 0
    -1
else
    0

The integer version is longer and better. It says what the third state means instead of relying on na, which makes the later switch or comparison readable to anyone who opens the file in six months.

Behavior changes that produce no error at all

These are the dangerous ones, because a clean compile feels like success. The v6 default long and short margin percentage is 100, and strategies now enforce position-size limits rather than ignoring available funds. A v5 strategy that opened positions larger than its capital will behave differently, and TradingView's own migration example shows a strategy producing margin calls in v6 that did not exist in v5 on the same chart.

strategy.exit() also changed. In v5, when a call supplied both a relative parameter such as profit and an absolute one such as limit for the same price level, the absolute value always won. In v6 the command evaluates both and uses whichever the market price is expected to reach first. TradingView documents an example where this causes exits at the entry price rather than at the intended target. The converter cannot choose for you, because both parameters are still valid; you have to decide which definition you meant.

Two smaller items belong in the same category. timeframe.period now always includes a multiplier, so a v5 comparison against "D" silently stops matching on a daily chart and must become "1D". And division of two integer constants can now produce a fractional result, so code that relied on truncation needs an explicit int(), math.floor(), math.ceil(), or math.round(). None of these raise an error. All of them change numbers.

Requests: a default that changes execution

In v6, dynamic_requests defaults to true on indicator(), strategy(), and library(), and the compiler turns the feature off automatically when a script does not need it. This unlocks genuinely useful patterns, such as calling request.security() inside a loop over a list of symbols, which v5 required an explicit opt-in to allow.

The migration caveat is that dynamic and non-dynamic requests can differ in some cases, for example when the result of one request.security() call feeds the expression argument of another. TradingView notes that a valid v5 script without dynamic requests can behave differently after conversion even when nothing about the requests was edited. If you need the old behavior for comparison, you can set dynamic_requests = false explicitly.

There is a converse trap worth knowing. If a v5 script explicitly set dynamic_requests = false and called request functions inside loops or conditional blocks through user-defined functions, the guidance is to remove that explicit argument during conversion so the v6 script uses dynamic requests automatically. Keeping the false value can turn previously working wrapped calls into a compilation error.

A verification pass that catches what the converter cannot

Verify in three stages, in order: compile, structure, behavior. Compiling is binary and quick. Structure means confirming that every input still appears with the same label and default, plots sit in the expected pane with the expected titles, and alert conditions still reference the intended series. Behavior means comparing output against the v5 original.

For the behavior stage, choose bars deliberately rather than glancing at the chart. Look at the earliest bars, where history is thin and where the removed boolean na state used to live. Look at a normal historical section. Look at the most recent bars, where realtime calculation and request timing matter. For a strategy, record the original Strategy Tester settings first, because initial capital, order size, commission, and margin are all part of the test environment and a difference in any of them invalidates the comparison.

Then write down what you accepted. A short comment above an unusual line, noting that it exists for a v6 compatibility reason, saves the next person from "simplifying" it back into a bug. This is the single highest-value habit in a migration, and it costs one sentence.

Where an AI workflow helps with a migration

The tempting move is to paste an entire v5 script into a general chat assistant and ask for v6. It often produces something plausible, but it makes verification harder rather than easier, because the response may reformat code, rename variables, and restructure conditions alongside the actual migration edits. When you then compare against the v5 original, you cannot tell which differences are the migration and which are the model's taste.

PineScripter is the tool we build, and migration is a good fit for how it works. It retrieves the Pine Script v5 and v6 manuals as context, so a suggested change can reference the documented rule behind it rather than a recollection. Its edits target the specific lines that need to move and appear as a reviewable diff, which is exactly the granularity a migration needs: you want to see that a boolean became an integer and that a when parameter became an if block, not receive a new file.

Be clear about what tooling cannot do. It cannot decide whether your v5 script intended a three-state flag, which price level you meant when you passed both profit and limit, or whether 100 percent margin is right for your test. Those are your decisions. Use the converter for syntax, use a focused workflow to keep the remaining edits small and documented, and use the official migration guide as the authority on every rule.

Line-level edits keep a migration diff readable against the v5 original

Frequently asked questions

How do I convert a Pine Script v5 script to v6?

Open the script in the Pine Editor, click the "Manage script" dropdown menu, and select "Convert code to v6". The Pine Editor highlights a v5 //@version=5 annotation in yellow to indicate a script is eligible. The script must already compile as v5 for conversion to work.

Does the Pine Script v6 converter catch everything?

No. It handles mechanical syntax changes such as removing the deprecated transp parameter, and TradingView notes that in rare cases the converted script can still contain compilation errors. It cannot resolve changes that depend on intent, including three-state boolean logic that used na, mixed relative and absolute strategy.exit() parameters, timeframe.period string comparisons, the 100 percent margin default, or differences in dynamic request behavior.

Why does my converted v6 strategy produce different backtest results?

Several v6 changes alter behavior without raising an error. The default long and short margin percentage is 100 and strategies enforce position-size limits, which can introduce margin calls. strategy.exit() now evaluates related relative and absolute parameters together instead of always prioritizing the absolute value. Integer constant division can return a fractional result. Compare Strategy Tester settings and inspect these specific areas before assuming the migration was wrong.

Why did timeframe.period stop matching after converting to v6?

In v6, timeframe.period always includes a multiplier, so a daily chart reports "1D" instead of "D". A v5 comparison such as timeframe.period == "D" silently stops matching. Update the string literal to include the multiplier.

Should I keep my v5 script after converting?

Yes. Because several v6 changes alter behavior without producing an error, the v5 original is the only reliable reference for comparing plotted values and strategy results. Convert a duplicate, keep the original, and compare output on early bars, a normal historical section, and the most recent bars.

Can I make a v6 script behave like the v5 version?

Partly, and only for specific documented settings. You can set dynamic_requests = false to replicate most previous request behavior, and you can set margin_long and margin_short to 0 to restore the v5 margin defaults. Other changes, such as the removal of the boolean na state, require restructuring the code rather than a setting.

The practical takeaway

Use the converter, then do the part it cannot. It reliably translates syntax with a single correct answer, and it explicitly requires a compiling v5 baseline, which is a gift for debugging. What it leaves you is the set of decisions only you can make: which states a boolean was really tracking, which exit price you meant, and whether v6 defaults suit your test. Convert a duplicate, verify structure and behavior against the v5 original, and document anything unusual you accepted.

For the edits the converter leaves behind, PineScripter is our product and keeps each one as a reviewable line-level change against documented v6 behavior, which is the granularity a migration actually needs.

Sources

Related reading: the full v5 to v6 migration guide, fixing v6 errors after upgrading, everything new in v6, dynamic requests in v6.

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.