User-defined types
Group related values into reusable records with fields and methods.
User-defined types let a script give a name and behaviour to a group of related values. They are useful when several values always travel together: a price zone, a signal result, a risk plan, or the running state of a pattern.
Declare a type
Use type with a type annotation for every field:
type Zone {
low: float
high: float
touches: int
}Create a value by supplying every field inside a handler or function:
fn makeZone(low: float, high: float) -> Zone {
return Zone {
low: low
high: high
touches: 0
}
}Types are nominal. Two types with the same fields are still different types, which prevents accidentally passing one kind of record where another was expected.
Add methods
Methods keep operations beside the data they work on. The current value is available as self:
type Zone {
low: float
high: float
touches: int
fn width() -> float {
return self.high - self.low
}
fn contains(price: float) -> bool {
return price >= self.low && price <= self.high
}
fn recordTouch() {
self.touches += 1
}
}Read-only methods return information without changing the value:
let inside = activeZone.contains(chart.close ?? 0.0)
let width = activeZone.width()A method that assigns to self updates a state or local value in place:
state activeZone = Zone { low: 0.0, high: 0.0, touches: 0 }
on chart.close {
if activeZone.contains(chart.close ?? 0.0) {
activeZone.recordTouch()
}
}Store typed values in collections
Types make arrays and rolling buffers much easier to understand:
type Signal {
price: float
strength: float
bullish: bool
}
state recent = Signal[](50)
on chart.close {
if recent.len >= recent.capacity {
recent.shift()
}
recent.push(Signal {
price: chart.close ?? 0.0
strength: math.abs((chart.close ?? 0.0) - (chart.open ?? 0.0))
bullish: (chart.close ?? 0.0) >= (chart.open ?? 0.0)
})
}Understand copying
An ordinary assignment or function argument copies a user-defined value. Changing the copy does not change the original. Methods are the exception: self refers to the value on which the method was called, so a mutating method can update a state or local variable.
Array iteration also gives you a value copy. To change an array element, write the updated value back by index:
state zones = Zone[](32)
for i, zone in zones {
if zone.contains(price) {
zones[i] = Zone {
low: zone.low
high: zone.high
touches: zone.touches + 1
}
}
}This explicit write-back makes it clear which stored element changes.
When to introduce a type
Create a type when it improves the model of the script, not merely to shorten a few lines. Strong candidates include:
- A result with several related outputs.
- Persistent state with its own update rules.
- Items stored in an array or map.
- Logic that becomes clearer as methods such as
contains,reset, orsize.
For a one-off pair of local values, plain let bindings are usually simpler.