Types
The value types of ChartScript (int, float, bool, color, string, drawing ids, collections), literals, na, conversions and how numbers behave.
Every value has a type. Most of the time the compiler infers it from what you write (const x = 1.5 is a float), so you only spell a type out when there is nothing to infer from, such as a variable that starts as na. Types catch mistakes early: adding a number to a piece of text, or passing a color where a length is expected, fails when you compile instead of drawing nonsense.
#The basic types
| Type | Holds | Literals | Can be na |
|---|---|---|---|
int | whole numbers | 0, 14, -3 | yes |
float | numbers with a fraction | 1.5, .5, 2., 1e3, 2.5E-4 | yes |
bool | true or false | true, false | no |
color | a color with transparency | #FF8800, #FF880080, color.red | yes |
string | text | "RSI", 'RSI' | yes |
A few details:
- Number literals are decimal. There are no
_separators and no hexadecimal numbers, so0x10is an error (CS1025). - A color literal is
#RRGGBBor#RRGGBBAA. The last pair is the alpha:FFis solid,00is invisible,80is about half see-through. - Strings use double or single quotes and end on the same line. Text between backticks,
`…`, is a template literal: it may span lines and can insert values with${…}. Escapes are\\,\",\',\n,\tand\uXXXX. A string can be at most 40,960 characters. See Strings and formatting. boolis neverna. Anifwithoutelsethat yields a bool givesfalseon bars where it does not run.
#Object and collection types
Besides plain values, a script can hold references to things it creates:
| Type | What it refers to | Created with |
|---|---|---|
label, line, box, table, linefill, polyline | a drawing on the chart | label.new, line.new, … |
chart.point | a point on the chart: time, bar index and price | chart.point.new |
array<T> (also written T[]) | a list | array.new<float>(), array.from(…) |
map<K, V> | a lookup table | map.new<string, float>() |
matrix<T> | a grid of rows and columns | matrix.new<float>(rows, cols) |
| your own types | records with named fields | type Level { … }, see User types, enums and methods |
| enums | a fixed set of named choices | enum Side { … } |
Plots and levels also have handles: const p = plot(…) and const h = hline(…) give values you pass to fill. You do not do anything else with them.
All of these can be na (also written null), meaning "nothing yet". A drawing variable typically starts as na and gets a drawing later:
//@chartscript 1
indicator("Last close label", { overlay: true })
state tag: label = na
if (barstate.islast) {
label.delete(tag) // deleting an na label does nothing, so this is safe
tag = label.new(bar_index, close, str.tostring(close, format.mintick), { style: label.style_label_left })
}
plot(close, "Close", color.muted)#Declaring a type
Put the type after the name, with a colon, when you declare a variable:
const level: float = na // a float that starts empty
const count: int = 0
const note: string = ""
const tint: color = color.new(color.blue, 80)
const closes: array<float> = array.new<float>()
const highs: float[] = array.new<float>() // the same kind of type as array<float>
plot(level)number is accepted as another name for float, and boolean for bool; int stays its own type. The editor writes float and bool.
You must give a type when the value alone does not say it. na has no type, so state x = na is refused (CS2201):
//@chartscript 1
indicator("Untyped na")
state x = na
plot(x)Write state x: float = na instead.
An annotation can also carry a qualifier in front of the type, for the rare cases where you want to say when a value is known: let n: simple int = 5. Function parameters take annotations the same way: function band(src: float, len: simple int = 20) { … }.
#Conversions
ChartScript converts on its own in exactly one direction: an int can be used where a float is expected. Everything else needs an explicit cast.
| You have | You want | Write |
|---|---|---|
int | float | nothing: const x: float = 1 is fine |
float | int | int(x): drops the fraction, toward zero (int(-2.7) is -2) |
| a number | a rounded number | math.round(x), math.floor, math.ceil |
| a number | bool | a comparison: x != 0 |
| anything | string | str.tostring(x) |
string | float | str.tonumber(s) (na if the text is not a number) |
na | a typed na | float(na), int(na), string(na), color(na) |
A typed na is handy in expressions: let nearest = float(na) declares a float that starts empty, the same as let nearest: float = na. For drawing types, use a typed declaration (let tag: label = na); the drawing types have no cast functions.
//@chartscript 1
indicator("Float into int")
const half: int = bar_index / 2 // bar_index / 2 can have a fraction, so it is a float
plot(half)Write const half: int = int(bar_index / 2), or math.floor(bar_index / 2) if you want rounding down for negative numbers too.
Mixing text and numbers also needs a conversion; + on a string only joins strings:
//@chartscript 1
indicator("Text plus number")
const msg = "Close: " + close
plot(close)Write "Close: " + str.tostring(close), or use a template literal: `Close: ${close}`.
#Numbers
- Both
intandfloatare stored as 64-bit floating point numbers. Whole numbers are exact up to about 9 × 10^15. - Dividing two ints keeps the fraction:
7 / 2is3.5, while4 / 2is2(an int). Useint(a / b)when you need whole-number division. Operators has the full rule. %(remainder) takes the sign of the left side:-7 % 3is-1.- Division by zero gives
na, not infinity. So doesx % 0, and any result that would overflow to infinity. - Comparisons of floats are exact.
0.1 + 0.2 == 0.3is false, as in most languages; compare with a tolerance (math.abs(a - b) < 1e-9) or round first. - For display, round to the market's tick size with
math.round_to_mintickor format withformat.mintick.
#Good to know
- A
boolcannot hold "unknown". If you need three states (yes / no / not yet), use anint(1, -1,na) or an enum. - Collections and objects are passed by reference: a function that pushes onto an array you gave it changes your array. Plain values (numbers, strings, colors, bools) are copied.
- Types also decide what you can call:
close.size()fails because a float has no methodsize, the same check that stopsta.sma("abc", 10).