DocsSoma NodeLanguage Reference

Language Reference

Complete reference for the Soma scripting language — declarations, types, operators, control flow, and indexing semantics. Soma is a line-oriented language where all values are either a scalar or a Float64Array time-series. Comments start with #.

Declarations

Declarations must appear at the top of the script, before any executable statements. The parser collects them to determine how many input handles to render on the node widget and which output channels to expose downstream.

inputinput <name>

Declares a named input channel. The name must match the edge handle that wires an upstream series into this node. At runtime the value is a Float64Array of bar values in chronological order (index 0 = oldest bar).

input prices
outputoutput <name>

Declares a named output array. The runtime pre-allocates a Float64Array of the same length as the primary input. Assign values inside loops via output[i] = expr. Unassigned positions remain NaN. The node renders one output handle per declared output.

output upper
paramparam <name> as <type> [= default]

Declares a user-configurable parameter. Type must be int, float, or bool. The optional default is a numeric literal. Params are exposed in the node's property panel and passed to the script as read-only scalars.

param period as int = 20
letlet <name> [as <type>] = <expr>

Declares a local variable in the current scope. Type is inferred from the right-hand expression unless an explicit type annotation is given. Variables declared inside a block (for/if/while) are scoped to that block. Re-assignment is allowed.

let alpha = 2.0 / (period + 1)

Type System

Soma has a minimal type system. All time-series data is stored as Float64Array. Scalars are JS numbers. The type annotation on let and param controls how the compiler treats the variable — it does not change the underlying JS representation.

TypeContextJS storageNotes
numinput / outputFloat64ArrayDefault type for input/output channels.
num[]input / outputFloat64ArrayAlias for num.
intparam / letnumber (truncated)Integer. Values are Math.trunc() at runtime.
floatparam / letnumberFloating-point scalar.
boolparam / letnumber (0 or 1)Boolean. true = 1, false = 0.
int[]letFloat64ArrayTyped local array. Elements treated as integers.
float[]letFloat64ArrayTyped local float array.
bool[]letFloat64ArrayTyped local boolean array (0/1 values).
NaN semantics: Uninitialized output positions and warm-up bars from indicators like sma() are represented as NaN. Use prune(x) to strip NaN values, or isnan(x) to test element-wise.

Operators

All binary operators work element-wise when either operand is a Float64Array. Scalar-array broadcasting is supported: the scalar is applied to every element. The result length is the shorter of the two operand lengths.

Arithmetic
+Addition
-Subtraction (also unary negation: -x)
*Multiplication
/Division
%Modulo
^Exponentiation (right-associative)
Comparison
>Greater than
<Less than
>=Greater than or equal
<=Less than or equal
==Equal
!=Not equal
Logical
&& / andLogical AND
|| / orLogical OR
! / notLogical NOT (unary)
Bitwise
&Bitwise AND
|Bitwise OR
~Bitwise NOT (unary)
>>Right shift
<<Left shift
Assignment
=Simple assignment
+=Add and assign
-=Subtract and assign
*=Multiply and assign
/=Divide and assign
%=Modulo and assign
Special
cond ? a : bTernary conditional
a ?? bNull-coalesce: b when a is NaN or null
arr[n]Array index (see Indexing below)

Indexing Semantics

Array indexing uses a look-back convention inside a for loop body:

prices[0]

The current bar's value (same as v in for (i, v) in prices).

prices[1]

The previous bar's value.

prices[n]

The value n bars back from the current bar.

Outside a loop, arr[n] uses ordinary 0-based indexing from the start of the array. Output arrays are indexed by absolute bar position: output[i] = value writes to bar i.

Control Flow

for

Iterates over a Float64Array. Two forms: with index (for (i, v) in arr) or value-only (for v in arr). The loop body executes once per element in chronological order. Supports break and continue.

# With index and value
for (i, v) in prices {
  output[i] = v * 2
}

# Value-only (no index needed)
for v in prices {
  print(v)
}
while

Loops while the condition is truthy (non-zero). Useful for manual rolling calculations inside a for body. Supports break and continue.

let j = 0
while j < period {
  let diff = prices[j] - mean
  sum = sum + diff * diff
  j = j + 1
}
if / else

Conditional branch. Both branches are optional — you may write a bare if without else. The condition is truthy when non-zero. Blocks are delimited by { }.

if i < period - 1 { continue }

if value > upper_band {
  signal[i] = 1
} else {
  signal[i] = 0
}
break / continue

break exits the innermost for or while loop immediately. continue skips the remainder of the current iteration and advances to the next.