millionchart
فارسی

Values and types

The kinds of value a script works with. Numbers, text, true and false, colours, na, series and history, tuples, arrays, maps, records and enums.

Every expression has a value. This page lists the kinds of value and how to write them. Edition 1 does not make you declare types: the engine works out what a value is. You may write a type after a name when it helps the reader. In edition 1 a written type is documentation: the engine does not check it, so a type that does not match the value is not an error.

Numbers, text, true and false#

Cuprel
indicator "Plain values"
    edition = 1

let count = 1_000
let ratio = 0.25
let big = 1.5e3
let name = "Gold"
let other = "single quotes work too"
let flag = close > open
plot(count + ratio + big, name = "Number")
plot(if flag then 1 else 0, name = "Flag")
  • A number is one kind. 1, 1.5, .5, 1_000 (an underscore groups digits) and 1.5e3 are all numbers, and 7 / 2 is 3.5. There is no whole-number division; use math.floor(7 / 2) for 3.
  • Text is written in double or single quotes. A text can hold the other kind of quote. Triple quotes hold several lines. Text and number formatting has the escapes and the formatted text f"...".
  • true and false are the two truth values. A comparison gives one.

Colours#

A colour is #rrggbb or #rrggbbaa (the last pair is opacity: ff solid and 00 invisible), a named colour such as color.red, or one made by a function:

Cuprel
indicator "Colours"
    edition = 1
    display = price

plot(close, name = "Named", color = color.orange)
plot(open, name = "Hex", color = #2962ff)
plot(high, name = "With transparency", color = #ff980080)
plot(low, name = "Made", color = color.fade(color.purple, 40))
plot(hl2, name = "From parts", color = color.rgb(0, 150, 136))

color.fade(color, percent) takes a transparency from 0 (solid) to 100 (invisible). color.rgb(r, g, b) takes three numbers from 0 to 255.

Not a number: na#

na means "no value". It is a legal value, not an error: an average has it on its first bars, and close[5] has it on the first five bars. Arithmetic with na gives na. A comparison with na is never true. na, errors and arithmetic is about it.

Series: a value on every bar#

The script runs once for every bar, so every name is really a series: one value per bar. You read the value on the current bar by writing the name, and the value n bars ago by writing name[n]. This is how you compare a bar with the one before it.

Cuprel
indicator "Change from the bar before"
    edition = 1

let change = close - close[1]
let two_back = close[2]
let rising = change > 0 and change[1] > 0
plot(change, name = "Change", style = histogram)
plot(if rising then 1 else 0, name = "Two rising bars")

bar.index is the number of the bar, counting from 0. x[n] is only for history. n is a count of bars back; it may be an input or any number. On the first n bars there is no such bar and the result is na. The brackets never mean "the n-th element of a list": for a list use .at(n).

Cuprel · error E3173
indicator "Not a list element"
    edition = 1

let levels = [close, open, high]
plot(levels[1])

Tuples#

A function can return several values at once as a tuple, and you take them apart with a pattern. _ throws a position away.

Cuprel
indicator "Band"
    edition = 1
    display = price

fn band(length: int): (float, float)
    let middle = tech.sma(close, length)
    let spread = tech.stdev(close, length) * 2
    return (middle + spread, middle - spread)
let (upper, lower) = band(20)
let (_, floor_only) = band(50)
plot(upper, name = "Upper", color = color.red)
plot(lower, name = "Lower", color = color.green)
plot(floor_only, name = "Lower, 50 bars", color = color.gray)

Arrays and maps#

An array is an ordered list, a map holds values by key. Both are created empty or from a literal, and are read and changed with methods.

Cuprel
indicator "Last closes"
    edition = 1

state closes: array<float> = []
closes.push(close)
if closes.size() > 5
    closes.shift()
let items = [high, low, close]
plot(closes.avg(), name = "Average of the last 5 closes")
plot(items.at(0) - items.at(1), name = "Range")

[a, b, c] is an array of the three values; [0.0; 4] is four copies of 0.0. Useful methods are .size(), .at(i), .set(i, v), .push(v), .shift() (drops the oldest), .first(), .last(), .avg(), .sum(), .min() and .max(). Indexes count from 0. Choices and loops shows how to walk an array.

Cuprel
indicator "Streak counter"
    edition = 1

state seen: map<int, int> = map<int, int>()
state run: int = 0
if close > close[1]
    run += 1
else
    if run > 0
        seen.set(run, (seen.at(run) ?? 0) + 1)
    run = 0
plot(run, name = "Current streak", style = histogram)
plot(seen.size(), name = "Different streak lengths seen")

A map's methods are .set(key, value), .at(key) (na when the key is missing; an array's .at(i) outside the array stops the script instead), .contains(key), .keys(), .size() and .remove(key). An array or a map is limited in size; see Limits and budgets.

Records and enums#

A type groups named fields. Build one by naming its fields; a field with = has a default.

Cuprel
indicator "A record"
    edition = 1

type Level
    price: float
    strength: int = 1
let level_ = Level(price = close, strength = 3)
plot(level_.price * level_.strength, name = "Weighted price")

An enum is a short list of names. Use it with match to cover every case:

Cuprel
indicator "An enum"
    edition = 1

enum Direction
    up
    down
let direction = if close > open then Direction.up else Direction.down
let sign = match direction
    Direction.up => 1
    Direction.down => -1
plot(sign, name = "Sign", style = histogram)

You may attach a text to a member (fast = "Fast") and use the enum as the type of an input, to give the settings panel a dropdown. See Inputs.

See also: Variables, Functions, types and enums, na, errors and arithmetic.