User-defined types
Give related values a name and behaviour with records, methods and typed collections.
User-defined types give a name and behaviour to values that travel together: a price zone, a signal, a risk plan or the running state of a pattern.
Declare a type
type lists every field with its type:
type Zone {
low: float
high: float
touches: int
}Create a value by giving every field, one per line or on one line:
Zone { low: 1.0, high: 2.0, touches: 0 }. Types are nominal: two types with the
same fields are still different types.
Methods
Methods keep operations next to the data. The current value is self:
script "Zone touches"
data chart = subscribe(data.ohlcv)
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
}
}
state zone = Zone { low: 0.0, high: 0.0, touches: 0 }
state boxes = entities.boxPool(max: 1)
on chart.close {
let hi = ta.highest(chart.high, 50)
let lo = ta.lowest(chart.low, 50)
if zone.width() == 0.0 && hi != null && lo != null {
// A zone at the bottom fifth of the 50-bar range.
zone = Zone { low: lo, high: lo + (hi - lo) * 0.2, touches: 0 }
}
if zone.contains(chart.low ?? 0.0) {
zone.recordTouch()
}
}
on chart.update {
let t = chart.time
if chart.isLast && t != null && zone.width() > 0.0 {
let label = str.format("{0} touches", zone.touches)
boxes.get("zone").set(t - time.bars(50), zone.high, t, zone.low, color: #26A69A22, extend: extend.right, text: label)
}
}Read-only methods return information; a method that assigns to self updates the
state or local it was called on.
Typed collections
Types make collections easy to read:
script "Recent signals"
data chart = subscribe(data.ohlcv)
type Signal {
price: float
strength: float
bullish: bool
}
state recent = Signal[](50)
pane countPane = pane(title: "Bullish of the last 50", height: 0.2)
plot bullish = plot.histogram(title: "Bullish", color: color.green, on: countPane)
on chart.close {
if recent.len >= recent.capacity {
recent.shift()
}
let open = chart.open ?? 0.0
let close = chart.close ?? 0.0
recent.push(Signal { price: close, strength: math.abs(close - open), bullish: close >= open })
let ups = recent.filter(|s| s.bullish)
bullish.plot(float(ups.len))
}Copying
Assignment and function arguments copy a record; changing the copy leaves the
original alone. Methods are the exception: self is the value the method was called
on. Iterating an array also gives copies, so write a changed element back by index:
script "Write back"
data chart = subscribe(data.ohlcv)
type Level {
price: float
hits: int
}
state levels = Level[](8)
on chart.close {
let close = chart.close ?? 0.0
if levels.len == 0 {
levels.push(Level { price: close, hits: 0 })
}
for i, level in levels {
if math.abs(close - level.price) < context.tickSize * 10.0 {
levels[i] = Level { price: level.price, hits: level.hits + 1 }
}
}
}When to introduce a type
Create a type when it improves the model of the script: a result with several
related parts, state with its own update rules, items stored in a collection, or
logic that reads better as methods such as contains or reset. For a one-off pair
of values, two let bindings are simpler.