Skip to content
ChartDeck ChartScript docs Account Open the terminal

Migration tips

A practical workflow for moving a Pine script to ChartScript: import, read the report, fix, compare on a chart, save and publish.

The importer does the mechanical work. What is left is judgment: deciding what a changed meaning should be, and checking that the script draws what you expect. This page is the workflow that works best, followed by the fixes you will need most often.

#The workflow

#1. Gather the files

Have the script's source and the source of every library it imports. The importer converts them together and connects the imports, but it never downloads anything. If the script imports a public library, copy that library's source from its page first, and check its license.

#2. Import everything at once

In Indicators → My scripts, choose Import from Pine. Paste the main script, or better, add all the files with Add files… (up to 20 at once, 5 MB each) and press Convert. You get one report card per file.

#3. Read the report from the top

Start with Blocking: nothing else matters until those items are gone. Then read every Meaning changed item. Not drawn yet and Data substituted tell you what will be missing; Renamed (FYI) can wait. The import report explains each group.

Press Copy report as text before you start fixing, so you keep the list while you work.

#4. Fix in the editor

Click a Line N → M link to open the converted script on that line. Fix it, then check the Problems tab: the editor compiles as you type, so you see straight away whether the fix worked. Common fixes are listed below.

To find every line the importer turned into a comment, search the script for // [import].

#5. Compare on a chart

Use Preview or add the script to a chart on the same market and timeframe you know the original from. Check:

  • the most recent bars first: they are the easiest to compare;
  • the first bars of the chart: averages that start differently converge after a few lengths;
  • any output that uses another timeframe, where the at mode decides what history looks like;
  • labels, lines and boxes: their colors and text.

When a value looks wrong, print it. log.info writes to the editor's Console, one line per call (see Debugging):

ChartScriptOpen in editor
//@chartscript 1
indicator("Check a value", { overlay: true })
const len = input.int(14, "Length")
const avg = ta.sma(close, len)
if (barstate.islast) {
    log.info("SMA on the last bar: " + str.tostring(avg, "#.##"))
}
plot(avg, "SMA")

#6. Save, then publish if you want

Save to My scripts in the import dialog (or Save in the editor) keeps the script with its version history. Publishing is a separate, optional step: read Publishing and licenses first, because the original license still applies to your converted copy.

#Common fixes

#A future leak on a higher timeframe

The original looked into the future on past bars means the original used lookahead on a value that was not known yet. The importer chose { at: request.close }, the last closed bar, which is what you could really have seen live. Keep it. If you wanted the bar as it forms, use { at: request.developing }, which also repaints live but matches history:

ChartScriptOpen in editor
//@chartscript 1
indicator("Weekly high", { overlay: true })
const closedWeek = request.tf("1W", high)  // last closed week
const thisWeek = request.tf("1W", high, { at: request.developing })  // the week so far
plot(closedWeek, "Last week's high", color.gray)
plot(thisWeek, "This week's high so far", color.orange)

#A division that should be whole

… divides two whole numbers: ChartScript keeps the fraction asks you to decide. If the value is a count of bars, an index or a length, wrap it in int(). If it is a price or an average, leave it.

ChartScriptFragmentOpen in editor
const len = input.int(21, "Length")
const half = int(len / 2)  // a length must be whole
plot(ta.wma(close, half), "Half-length WMA")

#A strategy you want to keep watching

Strategy orders are not available yet, and the importer turns order lines into comments. You can keep the signals visible by turning each entry condition into a mark and an alert condition:

ChartScriptOpen in editor
//@chartscript 1
indicator("Breakout signals", { overlay: true })
const len = input.int(20, "Lookback", { minval: 2 })
const upper = ta.highest(high, len)[1]
const lower = ta.lowest(low, len)[1]
const longSignal = ta.crossover(close, upper)
const shortSignal = ta.crossunder(close, lower)
plot(upper, "Upper", color.muted)
plot(lower, "Lower", color.muted)
plotshape(longSignal, "Long", shape.triangleup, location.belowbar, color.up)
plotshape(shortSignal, "Short", shape.triangledown, location.abovebar, color.down)
alert.on(longSignal, "Long signal", "Close broke above the range")
alert.on(shortSignal, "Short signal", "Close broke below the range")

#A missing market

The market … is matched to a ChartDeck market … appears when the script asks for another market by name. Open ChartDeck's market search and check that the VENUE:SYMBOL exists. If the original used a market ChartDeck does not list, choose the nearest one, or let the user pick with input.market:

ChartScriptOpen in editor
//@chartscript 1
indicator("Spread to another market")
const other = input.market("BINANCE:ETHUSDT", "Other market")
const otherClose = request.market(other, "", close, { at: request.developing })  // the same bar as the chart's
plot(close / otherClose, "Ratio")

#Loops written as for (let i = a to b)

Pine's for i = 0 to n - 1 decides when the loop starts whether it counts up or down: if n is 0, it runs once with i = 0 and once with i = -1. The importer keeps that meaning exactly with ChartScript's range loop, for (let i = 0 to n - 1), whenever a bound is not a plain number. When you know the loop should count up and run zero times for an empty range, which is what the original author almost always meant, rewrite it as the usual loop:

ChartScriptFragmentOpen in editor
const n = input.int(5, "Bars")
let total = 0.0
for (let i = 0; i < n; i++) {   // was: for (let i = 0 to n - 1)
    total += close[i]
}
plot(total / n)

Loops whose bounds are both numbers (for i = 0 to 9) are already printed as for (let i = 0; i <= 9; i++). See Loops.

#A renamed variable

If your script used a name ChartScript needs (market, flow, input, timeframe and others), the importer renamed it with a trailing underscore (market_). You can rename it to something clearer in the editor; the report lists every rename.

#The script is too large to save

A script can hold up to 2 MB of code on every plan; comments and blank lines do not count, and the whole text can be up to 5 MB. If a converted script is larger, the save message gives its size and the limit. You can move shared code into a library or remove parts you do not use.

#Colors that look off

Version 4 and 5 colors are kept as the author saw them, so an imported color.red may appear as #FF5252. If you want the script to follow the chart's dark and light themes, replace fixed colors with the theme colors color.up, color.down, color.accent, color.muted, color.text and color.grid. See Colors.

#Habits that save time

  • Convert a script once, fix it in the editor, and keep the fixed version. Re-importing the original later starts from scratch.
  • Fix Blocking items before anything else; one missing library can produce many follow-on errors.
  • Keep the original's license header at the top of the converted script. The importer keeps it word for word.
  • If a non-commercial license is shown, keep the script for your own, non-commercial use.

The ChartScript documentation is in English for now; translations will follow.