Appearance
Types and enums
How do you keep values that belong together, such as a swing's price and the bar it came on, in one variable? Declare a type with those fields, make an object of it with .new(), and read each field with a dot. An object is held by reference, so two variables can name the same object, and a change made through one shows in the other.
pine
//@version=6
indicator("Last swing high", overlay = true)
type Swing
float price
int bar
method age(Swing s) => bar_index - s.bar
var Swing last = na // no swing yet
float ph = ta.pivothigh(high, 5, 5)
if not na(ph)
last := Swing.new(ph, bar_index - 5) // a new object for each swing
plot(na(last) ? na : last.price, "Swing high")
plot(na(last) ? na : last.age(), "Age", display = display.data_window)Declaring a type
A declaration starts with type and the type's name, then one field a line, indented under it. Each field has a type and a name, and can have a default after =. A type needs at least one field.
Swing.new(price, bar) makes an object. Pass the fields in the order they're declared, or by name, as in Swing.new(bar = 0, price = close). A field the call leaves out takes its default, or na where it has none. A bool field with no default reads false, since a bool is never na in v6.
Read a field with last.price. Change it with :=, or with += and the other compound assignments: last.price := high. A field can hold any type, another object, an array, a map, a drawing or an enum among them.
TradingView asks for a field's default to be one you can read without running the script: a literal, na, or a built-in name such as close or color.red. Strict Pine refuses a default worked out from other values, such as close * 2, with "has non-literal default", and AmenoScript accepts any expression there.
A field takes no qualifier such as series, and strict Pine refuses one. Every field can change from bar to bar.
Keeping an object from bar to bar
A script runs once a bar, so s = Swing.new(...) makes a new object on every bar. Declare it with var, as in the example, and it's made once, on the first bar, and every later bar reads and changes that same object. A field declared varip, as in varip int ticks = 0, also keeps what each update of a live bar wrote to it, while the object's other fields go back to where the bar started. var and varip has the rules.
Shared or copied
An object variable holds a reference to the object. So b = a gives one object a second name, and a change through b shows through a. The same goes for a function: give it an object and it gets that object, so it can change its caller's fields.
pine
//@version=6
indicator("Shared or copied")
type Level
float price
a = Level.new(100)
b = a // the same object under a second name
c = a.copy() // a second object with a's values
b.price := 120
plot(a.price, "a") // 120: changed through b
plot(c.price, "c") // 100: the copy kept its own.copy() makes a new object with the same field values, one level deep. A field that holds an object, an array or a map holds the same one in the copy, so a change made inside it shows in both. Copy that field too when you need it apart: c.inner := a.inner.copy().
a == b is true only when the two name one object. Two objects with the same values are not equal, so compare their fields when you mean their values.
last[1] is the object as it stood when the bar before closed, with the field values it had then, also one level deep: an object held in one of its fields is today's. Collections says the same of arrays and maps.
Methods
A method is a function you call with a dot on its first argument, as last.age() calls age(last). Declare it with method and give its first parameter your type. A library can export a type together with its methods. Functions covers methods in full.
Enums
An enum names a fixed set of choices, such as a speed setting. Declare it with enum and its name, then one field a line. A field can take a title, a string after =, and a field with none is titled with its own name.
pine
//@version=6
indicator("Speed setting")
enum Speed
fast = "Fast (10)"
slow = "Slow (50)"
Speed speed = input.enum(Speed.slow, "Speed") // a dropdown of the titles
int len = switch speed
Speed.fast => 10
Speed.slow => 50
plot(ta.ema(close, len))Use a field as Speed.fast. A variable, a parameter, a field, an array element or a map key can hold an enum, and na is an enum with no choice yet. == and switch compare them, and str.tostring(Speed.fast) gives the title, Fast (10). input.enum turns the enum into a dropdown of its titles in the script's settings.
WARNING
Here an enum's value is its title. Two fields that share a title read as equal, and an enum compared with a string of its title reads true, which TradingView refuses to compile. Give each field a title of its own, and compare an enum only with its own fields.
Traps
- A copy shares what's inside it.
.copy()copies one level: the copy's nested objects, arrays and maps are the original's. - One object in every slot.
array.new<Swing>(3, Swing.new())puts one object in all three slots, so a change to one changes them all. Make each element with its ownSwing.new()in a loop. - A new object every bar. Without
var, the declaration runs again on each bar and the object starts over. - A field of na reads na.
last.pricewith no object reads na here, with no error, so a missing object can pass unnoticed. Testna(last)first, as the example does.
See also
- Collections, arrays and maps of your objects
- Functions, methods in full
- Inputs,
input.enumamong the other inputs - type and enum, the keywords in the reference
- var and varip, keeping an object across bars and ticks