Pattern matching
Turn continuous values into named cases with match expressions and statements.
match compares one value against ordered cases. It suits turning a continuous value
into a named regime, choosing a style, handling missing data or acting on an enum.
The first matching arm wins, so write the most specific cases first.
Produce a value
A match expression gives each case a result:
script "Move regime"
data chart = subscribe(data.ohlcv)
fn describeMove(changePct: float?) -> string {
return match changePct {
null => "Waiting for data"
>= 2.0 => "Strong rise"
> 0.0 => "Rise"
<= -2.0 => "Strong fall"
< 0.0 => "Fall"
_ => "Flat"
}
}
state hud = entities.labelPool(max: 1, anchor: anchor.topLeft)
on chart.update {
let open = chart.open
let close = chart.close
let change: float? = open != null && close != null && open > 0.0 ? (close / open - 1.0) * 100.0 : null
if chart.isLast {
hud.get("move").set(12.0, 12.0, describeMove(change))
}
}Relational patterns compare the subject with the value after <, <=, > or >=.
Because arms are checked top to bottom, >= 2.0 comes before > 0.0. A match
expression must cover every value; a final _ is the simplest way.
Choose a colour
script "Delta colour"
data chart = subscribe(data.ohlcv)
pane deltaPane = pane(title: "Delta", height: 0.25)
plot bars = plot.histogram(title: "Delta", on: deltaPane)
on chart.close {
let delta = chart.delta()
let volume = chart.volume
let share: float? = delta != null && volume != null && volume > 0.0 ? delta / volume : null
let shade = match share {
null => color.gray
>= 0.3 => color.green
<= -0.3 => color.red
_ => color.yellow
}
bars.plot(delta, color: shade)
}Match statements
Use the statement form to run actions. For one statement an arm needs no braces:
script "Rising streak"
data chart = subscribe(data.ohlcv)
state risingBars = 0
pane streakPane = pane(title: "Rising streak", height: 0.2)
plot streak = plot.histogram(title: "Bars", on: streakPane)
on chart.close {
let move = ta.change(chart.close)
match move {
null => risingBars = 0
> 0.0 => risingBars += 1
_ => risingBars = 0
}
streak.plot(float(risingBars))
}A statement match need not cover every value.
Combine cases
Separate alternatives with |:
script "Session of the day"
data chart = subscribe(data.ohlcv)
fn sessionName(hour: int) -> string {
return match hour {
0 | 1 | 2 | 3 | 4 | 5 => "Overnight"
6 | 7 | 8 | 9 | 10 | 11 => "Morning"
12 | 13 | 14 | 15 | 16 | 17 => "Afternoon"
_ => "Evening"
}
}
state hud = entities.labelPool(max: 1, anchor: anchor.bottomLeft)
on chart.update {
let t = chart.time
if chart.isLast && t != null {
hud.get("session").set(12.0, 12.0, sessionName(time.hour(t)))
}
}Alternatives can mix exact and relational cases: 0.0 | < -3.0 => "Inactive".
Handle missing values first
For a nullable subject, put null in its own arm. Exact and relational patterns
never match null; only null and _ do.
Match calculated values
An exact pattern can be a variable or a calculation. The subject is computed once:
script "Session open"
input openHour = input.int(13, title: "Open hour (UTC)", min: 0, max: 23)
data chart = subscribe(data.ohlcv)
pane phasePane = pane(title: "Phase", height: 0.15)
plot phase = plot.histogram(title: "Phase", on: phasePane)
on chart.close {
let t = chart.time
if t != null {
let value = match time.hour(t) {
openHour => 2.0
> openHour => 1.0
_ => 0.0
}
phase.plot(value)
}
}Exact patterns use plain equality, so they suit integers, strings and enums. Compare prices with relational arms instead.
What patterns do not do
Patterns compare one subject. They do not bind names, destructure records or take
if guards. Read fields into locals first, or compute an extra condition before the
match.
Match or if?
- A match expression calculates one result from several cases of one value.
- A match statement dispatches actions from one value.
ifsuits one or two unrelated conditions.