OpenScriptv0.8.0Documentation
ReferenceGitHub

OS9xxx Import

Every OS9 code, reported by the importer when it turns a script written in another chart language into OpenScript. An error marks a statement kept as a comment for you to translate by hand; a warning marks a statement translated with a stated difference in meaning.

On this page
  1. How to read these
  2. Errors: statements kept as a comment or not translated
  3. Warnings: translated with a stated difference

This page covers the OS9xxx codes of OpenScript (also called OpenAlgo Script): the findings of the importer. The importer is importScript, a function in the openalgo-script package that reads a script written in the version-annotated chart dialect, versions 5 and 6, and writes it as OpenScript. Its rule is that a translation that changes what a script means is worse than none, so it never approximates. An error is a statement it kept as a comment, because it could not translate it with its meaning, and you translate that statement by hand. A warning is a statement it did translate, whose meaning differs from the original in the way the message states. The importer is a library function for a host or an editor to call, and the /trading page does not offer it: current OpenAlgo releases ship library 0.5.0, and the importer arrived in 0.6.0.

How to read these

The importer takes the text of a script and returns two things: the OpenScript it wrote, and its findings.

import { importScript } from "openalgo-script";

const { source, findings } = importScript(text, { name: "breakout.txt" });
for (const f of findings) console.log(f.code, f.severity, f.span.line, f.message);
  • The findings point into the script you imported. Each one is an ordinary diagnostic, with a code, a severity, a message and a fix, and its line and column are in the text you gave the importer, not in the OpenScript it wrote, so the caret lands under what the original author wrote.
  • What comes back compiles. The importer compiles its own output before it returns it, so source is a program whenever it is not empty. It is the empty string when nothing could be translated at all, as for OS9001, and for a script with no declaration or a library (OS9004).
  • A statement it did not translate is kept as a comment. The unit is the whole statement at the top level, because a block with one line missing would mean something the original did not. Each of its source lines is written out behind a marker naming the code:
// not translated (OS9003): rank = ta.percentrank(close, 20)

Read the errors first, because each one is a statement you have to write yourself, and one refusal can hold back the statements after it (OS9005). Then read the warnings, and compare the first bars of the translated study with the original before you rely on it. Each entry below shows a short script in the source dialect, as plain text, beside the OpenScript it ends up as. Importing a script walks through a whole import.

Errors: statements kept as a comment or not translated

OS9001 Version not read by the importer

Errorimport

The importer reads scripts written for version 5 or 6 of the source dialect, and this one found.

What it means

A script in the source dialect opens with an annotation comment naming the dialect version it was written for, //@version=5 or //@version=6, above its first line of code. The importer's table of built-ins and every rule it applies are written for those two versions. An earlier version names its built-ins differently and follows rules the importer does not model, so reading it as version 5 would give a translation that compiles and means something else. The whole script is refused: the source that comes back is empty, and this is the only finding. A script with no annotation is refused the same way, and the message says it declares no version.

Bring the script up to version 5 or 6 and import it again, or translate it by hand.

How to fix it

Bring the script up to version 5 or 6 in the source dialect, or translate it by hand, and import it again.

Script to import

//@version=4
study("Range")
plot(high - low)

Translation

version 1

study("Range")
plot(high - low, "Range")

OS9002 Construct with no equivalent

Errorimport

The importer cannot translate construct, so the statement holding it was kept as a comment.

What it means

Some forms of the source dialect have no OpenScript spelling that keeps their meaning: an if or a switch used as a value, a tuple other than the four the importer unpacks (from the source dialect's MACD, Bollinger band, supertrend and directional movement built-ins), a type, a method, an import, an array literal, a loop over a collection, and an equality test against na. A few more are written in forms the importer's reader does not accept. The message names the form in a few words, such as "an if used as a value" or "a comparison with na", and the whole statement holding it is kept as a comment.

Translate the statement from its comment. An if used as a value is usually a ternary, as in the example: side = close > open ? 1 : -1. For a comparison with na, decide what the original meant before you write it: the source dialect answers it false on every bar, so the original never took that branch, while isNone() is true wherever the value is absent.

How to fix it

Translate that statement by hand from the comment that holds it: the rest of the script was translated around it.

Script to import

//@version=5
indicator("Side")
side = if close > open
    1
else
    -1
plot(side)

Translation

version 1

study("Side")
side = close > open ? 1 : -1
plot(side, "Side")

OS9003 Built-in with no mapping

Errorimport

name is not one of the built-ins the importer maps, so the statement holding it was kept as a comment.

What it means

The importer translates a built-in only where its table says which OpenScript call computes the same thing and how the arguments line up. A built-in with no row in that table, or a name the source script never declares, is not guessed at: a function with a similar name can differ in its arguments, its warmup or its arithmetic, and a translation built on the wrong one would draw a line that looks right and is not. The message names the built-in as the source writes it, namespace included, and the whole statement holding it is kept as a comment.

Find the OpenScript call that does the same job in the reference, check how it treats its first bars and absent values, and write the statement by hand. The example's built-in becomes percentRank(), which the importer's table does not hold.

How to fix it

Find the OpenScript library call that does the same job, check how it treats its first bars and absent values, and write the statement by hand.

Script to import

//@version=5
indicator("Range position")
rank = ta.percentrank(close, 20)
plot(rank)

Translation

version 1

study("Range position")
rank = percentRank(close, 20)
plot(rank, "Rank")

OS9004 Declaration the importer cannot place

Errorimport

The importer writes the declaration from one indicator or strategy call at the top level, and this script found.

What it means

An OpenScript file has exactly one declaration, study() or strategy(), and it decides whether the file may place orders. The importer writes it from the source's indicator or strategy call and from nothing else, because a declaration it invented would make that decision for you. A script with no declaration, and a library, which has no declaration an OpenScript file can carry, are refused whole: the source that comes back is empty and this is the only finding. A second declaration is kept as a comment, and the first one is used.

Give the source script exactly one indicator or strategy declaration at the top level, and import it again.

How to fix it

Give the source script exactly one indicator or strategy declaration at the top level, and import it again.

Script to import

//@version=5
basis = ta.sma(close, 20)
plot(basis)

Translation

version 1

study("Basis")
basis = sma(close, 20)
plot(basis, "Basis")

OS9005 Reads a statement that was not translated

Errorimport

name is declared by the statement at line line, which was not translated, so this statement that reads it was kept as a comment too.

What it means

A statement kept as a comment declares nothing in the output, so a later statement that reads one of its names would not compile. Rather than hand back a file that fails, the importer keeps each such statement as a comment too, in source order, and the message names the line that started the chain. One refusal can therefore produce several findings: in the example, the built-in with no mapping on line 3 is OS9003, and the average on line 4 that reads its result is OS9005, as is the plot of that average.

Translate the statement at the line the message names first, then the statements that read it, which were kept only because of it.

How to fix it

Translate the statement at line line by hand first, then this one, which was kept as a comment only because it reads name.

Script to import

//@version=5
indicator("Rank")
rank = ta.percentrank(close, 20)
smooth = ta.sma(rank, 5)
plot(smooth)

Translation

version 1

study("Rank")
rank = percentRank(close, 20)
smooth = sma(rank, 5)
plot(smooth, "Smooth")

OS9006 Argument with no equivalent

Errorimport

argument changes what call computes or trades, and OpenScript has no equivalent the importer can write.

What it means

Some arguments change the numbers a script produces or the orders it sends, and OpenScript has nothing the importer can write for them: a declaration's timeframe, a strategy's margin or a pyramiding above one, a limit price on an entry, a trailing exit, a quantity on a close, and a quantity on an entry in a strategy that sizes its orders in anything but units, because the source dialect counts that quantity in contracts and OpenScript in the declaration's own unit. Left out in silence, any of them would give a translation that computes or trades something else.

On the declaration or an input, the argument is left out and the call is kept, because the rest of the script needs them, so the translation runs without that argument's meaning until you put it back. Any other call carrying one is kept as a comment. Decide what OpenScript should do in its place: the example reads the daily timeframe the original declared with req.timeframe().

How to fix it

Translate the call by hand, deciding what OpenScript should do in place of argument, or remove argument from the source script if the script does not depend on it.

Script to import

//@version=5
indicator("Daily basis", timeframe = "D")
plot(ta.sma(close, 20))

Translation

version 1

study("Daily basis")
plot(req.timeframe("1D", sma(close, 20)), "Basis")

OS9012 The translation does not compile

Errorimport

The OpenScript compiler refused the translation of this statement with code, so the statement was kept as a comment.

What it means

The importer compiles its own output before it returns it, so what it hands back always compiles. A statement can translate piece by piece and still be refused as a whole, because the importer does not work out the types of the source script: version 5 of the source dialect lets a number stand where a condition is expected, and the source dialect lets a name inside a block keep a history and lets values of two types meet where OpenScript refuses. The importer learns of these from the compiler, keeps the statement as a comment, and compiles again. The message names the code the compiler raised.

Look that code up on its range page and translate the statement in the form its fix describes. In the example the compiler raised OS2011, a condition that is not a bool, and the fix writes the test out: volume > 0.

How to fix it

Look up code in this catalogue and translate the statement by hand in the form that code's fix describes.

Script to import

//@version=5
indicator("Traded")
traded = volume ? 1 : 0
plot(traded)

Translation

version 1

study("Traded")
traded = volume > 0 ? 1 : 0
plot(traded, "Traded")

Warnings: translated with a stated difference

OS9007 Warmup and absent bars follow OpenScript

Warningimport

call became target here and wherever else the script calls it, and target follows OpenScript's own warmup, arithmetic and handling of absent values, which the original is not guaranteed to share.

What it means

A windowed built-in, such as an average, an RSI or a highest high, is translated to the OpenScript call that computes the same quantity. What the two languages may not share is the detail: the first bar a value appears on, how a seeded average is seeded, the order the arithmetic is done in, and what an absent value inside the window does, which OpenScript carries through where the source dialect skips it. Over data with no absent values, and after the first bars, the two compute the same quantity, though the last digits can differ. On the first bars, and around a gap, the translation follows OpenScript's rules, which Warmup and Absent values describe. The warning is given once per built-in, at its first call.

Nothing needs to change unless the script depends on its first bars. Compare them with the original, and where they matter, write the value as an explicit recurrence held in a var, seeded the way the original seeds it.

How to fix it

Compare the first bars of the translation with the original, and where the script depends on them, write the value as an explicit recurrence held in var, seeded the way the original seeds it.

Script to import

//@version=5
indicator("Average")
plot(ta.ema(close, 20))

Translation

version 1

study("Average")
plot(ema(close, 20), "Average")

OS9008 Whole-number division in version 5

Warningimport

Both sides of this division are whole-number constants, which version 5 of the source dialect divides without a fractional part, and OpenScript keeps the fraction.

What it means

Version 5 of the source dialect divides two whole-number constants without a fractional part, so 7 / 2 is 3 there. Version 6 and OpenScript both keep the fraction, 3.5. A constant here is a whole-number literal, or a name the script sets once to one and never changes. The importer writes the division as OpenScript does and tells you, rather than guessing which result the author relied on. A version 6 script never raises this warning.

If the original relied on the whole-number result, wrap the division in trunc(), as the example does; otherwise leave it as it is.

How to fix it

If the script relied on the whole-number result, wrap the translated division in trunc(); otherwise leave it as it is.

Script to import

//@version=5
indicator("Half")
half = 7 / 2
plot(close * half)

Translation

version 1

study("Half")
half = trunc(7 / 2)
plot(close * half, "Half")

OS9009 Presentation argument left out

Warningimport

argument changes how call is displayed or what it may allocate, not what it computes, and OpenScript has no equivalent, so the translation leaves it out.

What it means

Many arguments in the source dialect are about the picture rather than the numbers: a line that tracks the price, a label size, a legend entry, a count of drawings to keep, a plot style OpenScript does not offer. Leaving one out changes how the chart looks and nothing the script computes or trades, so the call is translated without it, and the argument is reported rather than dropped in silence. The message names the argument and the call.

Nothing needs to change unless the look mattered. Where it did, restyle the translated call with the options OpenScript has, such as its colour, width and style. See Plots.

How to fix it

Nothing needs to change unless the display mattered; where it did, restyle the translated call with the arguments OpenScript does have, such as its colour, width and style.

Script to import

//@version=5
indicator("Close")
plot(close, "Close", trackprice = true)

Translation

version 1

study("Close")
plot(close, "Close")

OS9010 Order placed under OpenScript's order model

Warningimport

call was translated into OpenScript's order calls here and wherever else the script uses it, and OpenScript decides when an order fills, how a reversal and a repeated entry are handled and when an exit level starts to apply by its own order model.

What it means

An order call is translated into OpenScript's order calls as closely as the two order models allow. An entry becomes a close of any opposite position followed by the entry, guarded so that it never adds to a position already held on its side, which is what the source dialect does at its default pyramiding. An exit that names its entry is guarded by that entry's side, and a profit or loss given in ticks is multiplied by chart.tickSize. What still differs is the model: an OpenScript exit sets its level when it is called rather than when its entry fills, and a repeated entry past the pyramiding limit is refused rather than ignored. The warning is given once per order call, at its first use.

Backtest the translation beside the original and compare the two trade lists before you rely on it, and adjust the order calls by hand where they part. A strategy that names no capital is given the source dialect's starting capital of 1,000,000, as the example shows, because OpenScript's own default is 100,000 and an order sized as a percentage would otherwise be a tenth of the size. See Orders and Backtesting.

How to fix it

Backtest the translation beside the original and compare the two trade lists before relying on it, and adjust the order calls by hand where they part.

Script to import

//@version=5
strategy("Cross")
fast = ta.sma(close, 10)
slow = ta.sma(close, 30)
if ta.crossover(fast, slow)
    strategy.entry("Long", strategy.long)

Translation

version 1

strategy("Cross", capital = 1000000)
fast = sma(close, 10)
slow = sma(close, 30)
if crossUp(fast, slow)
    if pos.size < 0
        close()
    if pos.size <= 0
        buy(tag = "Long")

OS9011 Equality over two absent values

Warningimport

Both sides of operator can be absent on the same bar, and OpenScript counts two absent values as equal, which the original is not guaranteed to do.

What it means

OpenScript's equality is total: none == none is true and none != none is false, so a test for absence can be written as a comparison. The source dialect does not count two absent values as equal. Where both sides of == or != can be absent on the same bar, which is every warmup bar of two averages, the translation and the original can take different branches on exactly those bars.

If the original relied on two absent values never comparing equal, guard the comparison with isNone(), as the example does: not isNone(fast) and fast == slow. See Absent values.

Related. Importing a script, Reading an error, OS2xxx Names and types, Absent values, Warmup, JavaScript library

How to fix it

If the original relied on two absent values never comparing equal, guard the translated comparison with isNone() on either side.

Script to import

//@version=5
indicator("Touch")
fast = ta.sma(close, 5)
slow = ta.sma(close, 20)
plot(fast == slow ? 1 : 0)

Translation

version 1

study("Touch")
fast = sma(close, 5)
slow = sma(close, 20)
plot(not isNone(fast) and fast == slow ? 1 : 0, "Touch")