OpenScriptv0.5.0Documentation
GitHub

Types

The value types of OpenScript, the series, input and constant qualifiers, arrays, drawing objects and declaration handles, and the annotations that name them.

On this page
  1. The type list
  2. Value types
  3. Qualifiers: when a value is known
  4. Arrays
  5. Drawing objects
  6. Declaration handles
  7. Calls that return nothing
  8. User types
  9. Type annotations
  10. How a name gets its type
  11. Conversions
  12. Type errors you are likely to meet

Every value in an OpenScript script has a type, and the compiler knows it before the first bar runs. This page lists every type in version 1, the three qualifiers that say when a value is known (fixed when the script compiles, fixed by the settings dialog, or new on every bar), and the annotations you can write to state a type yourself.

Types matter because OpenScript never converts one into another behind your back. "count: " + 5 does not quietly become "count: 5", and a number is never a condition. Knowing the list below turns most compile errors into something you can predict.

version 1
study("Typed helpers", overlay = true)

lookback = input(20, "Lookback", min = 2, max = 200)   // number, fixed by the settings dialog
tint     = input(aqua, "Colour")                        // color, fixed by the settings dialog

fn band(src: series number, len: number, mult: number = 2) =>
    sma(src, len) + mult * stdev(src, len)

var highs: array<number> = []                          // an array of numbers
var marks: array<label> = []                           // an array of drawing objects

upper = band(close, lookback)                          // series number
push(highs, high)
if size(highs) > lookback
    shift(highs)

if close > upper
    push(marks, draw.label(time, high, "Above " + text(upper, 2)))
if size(marks) > 10
    draw.delete(element(marks, 0))
    shift(marks)

plot(upper, "Upper band", tint)
plot(max(highs), "Highest kept high", silver)

The type list

TypeHoldsWritten asCan be annotated
numberOne finite number42, 3.14, 0xFF, 1_000Yes
stringText, as Unicode code points"BUY", 'BUY'Yes
booltrue or falsetrue, falseYes
colorRed, green, blue and alpha (opacity)aqua, #ff8800, #ff880080Yes
noneThe absent valuenoneNo, it belongs to every type
series TOne T per barNo literalYes, as series number and so on
array<T>An ordered, resizable list of one type[1, 2, 3]Yes
line, label, box, polyline, tableAn object the script creates and keepsReturned by the drawing calls and table()Yes
plot, fill, levelA declaration handle, fixed before bar 0Returned by plot(), plotCandles(), fill() and level()No

There is no integer type and no date type, and you cannot declare a type of your own in version 1; the word type is reserved for that later. Some library tables show a parameter as any: that is a library function accepting several types, not a type you can write.

Value types

number

A number is a 64-bit floating point value that is always finite. It is the only numeric type, so a length, a bar count, a lot size and a price are all number, and no conversion between them exists to get wrong.

Where a whole number is required, such as a lookback length, an array index or a loop step, a fractional value is refused rather than rounded for you. The compiler reports OS3004 when it can see the value; a value computed on a bar stops the script when that bar runs (OS4003 for a length, OS4004 for an array index). Round it yourself with floor() or round(), where a reader can see which way it goes.

An operation with no finite result, such as 1 / 0 or sqrt(-1), gives none instead of infinity or not-a-number.

len = input(21, "Slow length", min = 2, max = 500)
halfLen = floor(len / 2)          // 10, not 10.5, and the choice is visible
plot(sma(close, len), "Slow", orange)
plot(sma(close, halfLen), "Fast", aqua)

Number literals:

LiteralValue
42, 3.14, .5Decimal, a leading digit is optional
1_000_000Underscores group digits and mean nothing
2.5e-4An exponent
0xFFHexadecimal, 255
010Ten. There is no octal form

Time is a number too: milliseconds since 1 January 1970, in UTC. That is why time can be subtracted, compared and stored like any number, and why the date functions exist to read it as a calendar.

string

Text, as a sequence of Unicode code points. Double and single quotes mean the same thing, so a string holding one kind needs no escape: 'He said "go"'. The escapes are \\, \", \', \n, \t, \r, \0 and \uXXXX with four hex digits; any other is OS1005. A string cannot run past the end of its line (OS1004); join pieces with + across lines instead.

+ joins two strings, and <, <=, >, >= compare them by code point, the same on every machine. Everything else is in the string functions.

panel = table("Symbol", 1, 1, position = "bottomRight")
symbolLine = "NIFTY" + " " + text(close, 2)
if bar.isLast
    cell(panel, 0, 0, symbolLine)

bool

true or false, and nothing else. A bool is not a number: 0 is not false, 1 is not true, and "" is not false. A condition must be a bool or none; anything else is OS2011. To turn a condition into a number, write cond ? 1 : 0, or count it over a window with count().

upBar = close > open
plot(count(upBar, 20), "Up bars in the last 20")

color

Red, green, blue and alpha, where alpha is the opacity. Write one of the nineteen named colours such as aqua or orange, a hex literal #rrggbb or #rrggbbaa, or build one with rgb(), rgba(), fade(), withAlpha() or mix(). Two colours are equal when all four channels match.

tint = close > open ? lime : fade(red, 40)
barColor(tint)

See Colors for every named colour and function.

none

The absent value, written none. It is the only value of its own type, and it also belongs to every other type, so a series number can hold none on any bar and a string name can hold it too. It is what a moving average holds before it has enough bars, what close[1] is on the first bar, and what a division by zero gives.

It passes through arithmetic and ordered comparison, == and != always answer true or false, and a condition that is none takes the false branch. See Absent values and the functions isNone() and orElse().

Qualifiers: when a value is known

A type says what a value is. A qualifier says when it is settled, and some arguments accept only a value settled before the first bar.

QualifierSettledExamples
ConstantWhen the script compilesLiterals, arithmetic over literals, named colours, math.pi, math.e, and rgb(), rgba(), fade(), withAlpha(), alpha(), mix() over constants
InputBefore bar 0, by the settings dialogA call to input(), and a name bound directly to one
SeriesAfresh on every barclose, bar.index, ema(close, 9), any name computed from bar data

Each level can stand wherever a later one is accepted: a constant works anywhere an input or a series does, and an input works anywhere a series does. The reverse is refused.

PlaceAcceptsRefused with
Options of study() and strategy()A constant, or an input used as it isOS3003
Arguments marked "Fixed before the first bar" in a reference table, such as a table's corner or a marker's shapeA constant, or an input used as it isOS3003
limits(loops = ..., history = ...)A literal number onlyOS3015
Everything elseAny of the three
corner = input("topLeft", "Corner", options = ["topLeft", "topRight", "bottomLeft", "bottomRight"])
panel  = table("Last close", 1, 2, position = corner)   // an input is fixed before bar 0
if bar.isLast
    cell(panel, 0, 0, "Close")
    cell(panel, 0, 1, text(close, 2))

Series and broadcast

A series is the per-bar history of a value: series number is one number per bar. Read bare, it gives this bar's value; [n] gives the value n bars back.

A plain value used where a series is expected is broadcast: treated as that same value on every bar. A series used where a plain value is expected means this bar's value. That is why ema(close, 9) works whether the length is a literal, an input or a series. Broadcast is the only automatic conversion in the language, and it changes no value.

A value has history, and accepts [n], in four cases:

  1. It is a built-in series, such as open, close, volume, time or bar.index.
  2. It is a name declared at the top level of the file.
  3. It is a call to a function that returns a series, such as ema(close, 9).
  4. It is a series parameter of your own function, where [n] reads the history of whatever the caller passed.

Anything else is OS2004. The facts in the chart namespace are the clearest case: they are fixed for the whole run, so chart.tickSize[1] asks a question with no different answer and is refused.

body = close - open                              // top level, so it has history
plot(body - body[1], "Change in body size")

An input() is a single value, with one exception: a source input such as input(close, "Source") returns a series number, because what the user picks is a series.

Arrays

array<T> is an ordered, resizable list whose elements share one type. The element type is number, string, bool, color, or an object type: line, label, box, polyline or table.

RuleDetail
Not an element typeA series, a declaration handle or another array: array<array<number>> is OS2019
Empty literalTakes its type from an annotation or from the first push, unshift, insert or set into it; with neither it is OS2015
Mixed literal["RSI", 14] is OS2013; keep two arrays side by side
AssignmentCopies the reference, not the contents: two names, one array. copy() makes an independent array
==True only for the same array. arrayEqual() compares contents
Index out of rangeOS4004
SizeAt most 1,000,000 elements (OS5002)
var window: array<number> = []
push(window, close)
if size(window) > 20
    shift(window)
plot(avg(window), "Mean of the last 20 closes", aqua)

A function with more than one output returns an array<number> of this bar's outputs, in the order its reference entry documents. The array is never absent and never changes length; each element has its own warmup and is none until it is ready.

m = macd(close, 12, 26, 9)
plot(m[0], "MACD", aqua)
plot(m[1], "Signal", orange)
plot(m[2], "Histogram", gray, style = "histogram")

See Collections for every array function.

Drawing objects

line, label, box, polyline and table are runtime objects. The script creates them as bars arrive, keeps them, changes them and deletes them. An object is a reference, like an array: assigning it to a second name gives two names for one object, and == asks whether two names hold the same object.

An object lives from the bar that created it until the bar that deletes it with draw.delete() or draw.deleteAll(). Dropping every name that refers to it does not delete it; the chart keeps drawing it. A table() is never deleted; clear() empties its cells.

var marks: array<label> = []
if crossUp(close, sma(close, 50))
    push(marks, draw.label(time, low, "Up", textColor = lime))
if size(marks) > 20
    draw.delete(element(marks, 0))
    shift(marks)

Declaration handles

plot, fill and level are declaration handles. A call to plot(), plotCandles(), fill() or level() declares a fixed part of the study, a column, a band or a line, once, before bar 0. The handle it returns is that declaration, and it exists only for the compiler.

A handle can be named at the top level and passed to fill(), and nothing else. It cannot be held in a var, stored in an array, passed to or returned from your own function, compared, or used in arithmetic (OS2003). Its type name cannot be written in an annotation (OS2016).

b = bollinger(close, 20, 2)
upper = plot(b[1], "Upper", aqua)
lower = plot(b[2], "Lower", aqua)
fill(upper, lower, color = fade(aqua, 88))
Operationplot, fill, levelline, label, box, polyline, table
Name at the top levelYesYes
Name inside a block or a functionNoYes
Hold in a varNoYes
Hold in an arrayNoYes
Pass to your own function, or return from oneNoYes
Compare with == and !=NoYes, as identity
Read history with [n]NoNo
Arithmetic or a conditionNoNo

Calls that return nothing

A call that acts rather than computes, such as signal(), print(), background() or draw.delete(), returns no value at all. That is not none: there is nothing to keep, so write the call as a statement on its own line.

User types

Version 1 has no user-declared record types. Where a record would hold several fields, keep one array per field and index them together, as draw.polyline() does with its arrays of times and prices.

Type annotations

The compiler infers every type, so annotations are optional. Write one to document intent, or where there is nothing to infer from, as with an empty array or a var that starts as none.

WhereForm
A var declarationvar name: T = initial
A function parametername: T, or name: T = default

An annotation is series in front of a value type, array<T>, or an object type.

newDay = bar.isFirst or not date.isSameDay(time, time[1])
var dayHigh: series number = none
if newDay or high > dayHigh
    dayHigh = high
plot(dayHigh, "High of the day so far", style = "step")
fn band(src: series number = close, len: number = 20) => sma(src, len)

basis = band()
var hits: array<number> = []
var lastSide: color = gray
if close > basis
    push(hits, close)
    lastSide = lime
else if close < basis
    lastSide = red
barColor(lastSide)
plot(size(hits), "Closes above the band so far")

Annotations do not go on a plain assignment (x: number = 5 is a syntax error) or on a function's result: a function's type is inferred from its body. An annotation naming a type that does not exist, such as integer or plot, is OS2016, and a value that does not match its annotation is OS2003.

How a name gets its type

A name's type is fixed by the first assignment that gives it a definite value, and assigning another type later is OS2003, however far apart the two lines are. none carries no type, so var stop = none waits for the first real value to fix it.

var stop = none               // no type yet
if close > open
    stop = low                // number, from here on
plot(stop, "Stop")

A name that never receives a definite value is absent on every bar. Plotting it draws nothing, and the compiler warns with OS8009.

Conversions

There is no implicit conversion between any two types. The conversions are four calls:

CallTakesGives
text(x)Any valuestring; text(none) is "none"
text(x, decimals)numberstring with fixed decimals
toNumber(s)stringnumber, or none when the text is not a number
toBool(x)bool or nonebool; none becomes false

See text(), toNumber() and toBool() on the General page.

Type errors you are likely to meet

CodeMeansUsual fix
OS2003Two types do not mix, or a name changed typeConvert with text, toNumber or toBool, or use a second name
OS2004The value has no historyName it at the top level of the file and read that name
OS2011A condition is not a boolWrite the test out: x > 0, isNone(x), s != ""
OS2012The ternary's arms have different typesMake them agree, or use none for the empty arm
OS2013An array literal mixes typesSplit it into two arrays
OS2015An empty array has no element typeAnnotate it: var hits: array<number> = []
OS2016An annotation names no typeUse number, string, bool, color, array<T> or an object type
OS2019That type cannot be an array elementUse a value or object type
OS3003A value that must be fixed before bar 0 depends on bar dataUse a literal or an input()
OS3004A whole number was requiredWrap it in floor() or round()

Related: Types and values, Absent values, Collections, Objects and methods, Keywords, Operators.