
Every expression evaluates to one of these types:
Number: a single numeric value at each time index (most common).
Series: a time-aligned numeric series (what charts plot).
Boolean (logical): internally represented as 1 or 0 (still numeric so it can be charted and composed).
Null / NaN: invalid or unavailable value at a timestamp (propagates in a predictable way).
Core rule: everything becomes a number per bar. Even conditions like BTC.rsi > 70 produce a numeric output series of 1s and 0s.
All computations are evaluated per timestamp on the chart’s timeline.
If you reference BTC.rsi and ETH.macd, the engine aligns them by timestamp.
If one series has no value at a timestamp, that point becomes null (or is dropped if the consumer requires strict continuity).
Rolling functions require enough history; early bars are null until n bars exist.
Rule: the engine will never “peek” forward. A value at time t can only depend on values at t and earlier.
A variable reference has the semantic form:
ASSET.operation Examples: BTC.close, ETH.volume, SOL.rsi
Semantics:
ASSET resolves to an instrument in the view context (or global symbol list).
operation resolves through the operation registry (built-in indicators + formula operations).
The result is a series (or a scalar derived from a series if the operation is scalar-by-design).
Supported literal types:
Numbers: 2, 2.5, 0.7233
Function arguments can mix literals and references.
Semantics: literals are treated as a constant series over time when combined with series.
Example: BTC.close / 2 means “divide each bar of BTC.close by 2.”
Operators act pointwise over time.
Arithmetic:
+ - * /
Comparisons (return 1 or 0):
> < >= <= == !=
Logical (return 1 or 0):
AND OR
Precedence (highest → lowest):
Parentheses ( )
Unary -x (negation)
* /
+ -
Comparisons > < >= <= == !=
AND
OR
Semantics:
Arithmetic combines numbers/series pointwise.
Comparisons produce a binary series (1/0).
Logical operators treat non-zero as true.
Missing values propagate in a conservative way.
Arithmetic: if either side is null → result null.
Comparisons: if either side is null → result null (not false).
Logical: if either side is null → result null, unless the logic can be determined without it (optional engine optimization, but default is conservative).
This prevents “fake certainty” near boundaries, early rolling windows, or missing markets.
Functions are pure and deterministic. They do not mutate state.
General form:
functionName(arg1, arg2, ...)
Arguments can be:
series references (BTC.rsi)
nested expressions (normalize(BTC.volume / ETH.volume))
literals (30)
Rules:
Functions validate arity (number of args) and types.
If the function is rolling and requires n, then first n-1 outputs are null.
These functions combine multiple inputs and return a single output series.
composite(A, B, C, ...)Meaning:
Normalize each input to a comparable range (engine-defined, typically 0–100 or z-scored).
Take the equal-weight average.
Use when you want “overall score” behavior.
weighted(w1:A, w2:B, w3:C, ...)Meaning:
Each wi is a literal weight.
Engine normalizes weights so they sum to 1 (unless strict mode is enabled).
Inputs are normalized before weighting (unless the function defines otherwise).
Use when BTC should dominate, but you still want diversification.
correlation(X, Y)Meaning:
Pearson correlation over the maximal overlapping history window (or default window).
Returns a series (or scalar depending on implementation; in SEIOO you typically want a series).
rollingCorrelation(X, Y, n)Meaning:
For each time t, correlation is computed over [t-n+1 ... t].
Early bars are null until n points exist.
normalize(X)Meaning:
Transform X into a comparable scale so it can be combined with unrelated units (price vs RSI vs volume).
Typically outputs 0–100 or a standard score depending on the configured normalizer.
Rule: normalization is a semantic promise: “this number is now comparable to other normalized numbers.”
Many built-in indicators produce raw values (Layer 1) and can optionally produce signals (Layer 2) scaled into a fixed range (often 0–10).
Semantic idea:
BTC.rsi is a value series.
A signal form (if exposed) represents interpretive scoring using a normalizer (threshold/sigmoid/linear).
If your UI has a “signal mode,” the meaning is: “convert raw numeric indicator into an interpretable score on a common scale.”
A formula is rejected if it attempts to do anything outside the grammar:
No arbitrary identifiers beyond allowed tokens
No property access except ASSET.operation
No loops, assignments, imports, or JavaScript constructs
No side effects
No filesystem, network, or runtime access
Meaning: the DSL is not a programming language, it is a financial expression language.
A practical guide to writing correct expressions.
BTC.close
ETH.volume
SOL.rsiBTC.close - BTC.open
(BTC.high + BTC.low) / 2
BTC.volume / ETH.volumeBTC.rsi > 70
BTC.rsi < 30 OR ETH.rsi < 30
BTC.rsi > 50 AND BTC.volume > normalize(ETH.volume)These return a 1/0 series that you can chart or feed into other logic.
normalize(BTC.volume)
rollingCorrelation(BTC.close, ETH.close, 30)
composite(BTC.rsi, ETH.macd, SOL.volume)
weighted(0.6:BTC.rsi, 0.3:ETH.rsi, 0.1:SOL.rsi)composite(
normalize(BTC.volume),
normalize(ETH.volume),
normalize(SOL.volume)
)(BTC.rsi > 70) AND (rollingCorrelation(BTC.close, ETH.close, 30) > 0.8)Market breadth style composite
composite(BTC.rsi, ETH.rsi, SOL.rsi, XRP.rsi)Risk-on / risk-off proxy
rollingCorrelation(BTC.close, NASDAQ.close, 60)Condition gating
(BTC.rsi > 55) AND (BTC.volume > normalize(BTC.volume))If two users run the same formula on the same data and timeframe, they must get the same result. If an expression could introduce ambiguity, the engine rejects it rather than guessing.