Values, series and na
The rules of Flowscript on one page: values and series, na, expressions, functions, folds, enums and match, phases and libraries.
Values#
| Kind | Example |
|---|---|
| Number | 1, 0.5, 1e-3 |
| Condition | true, false or na |
| Text | "ETH" |
| Record | { usd: 100, side: "long" } |
| List | [1, 2, 3] |
Everything is a series#
Every const is a series: one value per bar. close is today's close; close[1] is the bar before; close[5] five bars back. Arithmetic works bar by bar, so (high + low) / 2 is a series of midpoints.
Durations are seconds: seconds(30), minutes(15), hours(6).
na: not known yet#
na means a value isn't known yet: an indicator still warming up, a division by zero, a bar with no data.
- It spreads:
na + 1isna,na > 3isna, and!naisna. - A trigger never fires on
na. - Test for it with
isNa(x); replace it withnz(x, 0).
So write the condition you mean: close > ema(close, 200) is na (not false) for the first 200 bars.
Expressions#
- Arithmetic
+ - * / %, comparisons< <= > >= == !=(they don't chain: write1 < x && x < 3), logic&& || !. c ? a : b, andif (c) a else b.- Math:
abs,min,max,clamp,ln,exp,sqrt,pow,floor,ceil,round,sign. - Time:
bars.time(market, interval)gives each bar's start;hour,minute,dayofweekread it in UTC, andnyTime,londonTime,tokyoTimeand others give a city's clock with daylight saving.inWeekdaySession(nyTime(t), 930, 1600)is the US stock session.
Functions and folds#
fn mid(a: Series<number>, b: Series<number>): Series<number> { return (a + b) / 2; }
const m = mid(high, low);
const doubled = map(xs, (x) => x * 2);There are no loops and no variables to change. State that carries from bar to bar is a fold: a seed and a step from the last state and the new value.
const streak = fold(close, 0, (st, c) => isNa(c) ? st : (c > c[1] ? st + 1 : 0));Guard every fold step with isNa(x) ? st : …: one missing bar would otherwise turn the state na forever.
Enums and match#
enum Regime { hot, quiet, normal }
const close = bars.close(market, interval);
const vol = stddev(change(ln(close)), 24);
const regime = vol > 0.02 ? Regime.hot : (vol < 0.005 ? Regime.quiet : Regime.normal);
metric("size", match (regime) { hot => 0, quiet => 1, _ => 0.5 });A match must cover every variant or end in _. A swap's side and a source's cases are enums too.
Phases#
phases Trade { phase Flat { … } phase Long { … } } is a state machine: inside each phase, when … => Phase moves to another, at most once a bar. It's the clearest way to write "wait for a setup, then enter, then manage the position".
Higher timeframes#
On a 15-minute run, bars.close(market, "4h") is the last closed four-hour bar's close, held until the next one closes. It never repaints. higher("4h", close, c => ema(c, 20)) runs a calculation on the four-hour bars themselves.
Libraries#
Keep functions you reuse in a library (save_library) and import "mylib"; after the uses line. A strategy keeps the library version it was saved with until you save it again.
Exact integers#
Numbers are 64-bit floats. For token amounts that need every digit, the u256 functions carry exact 256-bit integers (u256Add, u256Mul, u256Units(1.1, 18) and more). Overflow and division by zero are na, and an order with an na argument is refused before it's signed.