DSL Reference
Complete reference for the .algomln strategy language.
AlgoMLN strategies are written in .algomln files — a small, intentionally limited
domain-specific language. It is rules-only: no variables, no loops, no state.
Just conditions and actions. The simplicity is by design — strategies should be readable at a glance.
The language is case-insensitive for keywords. Blank lines and #
comments are allowed anywhere. Indicator periods and quantities must be positive integers.
Strategies compile through a full pipeline: Lexer → Parser → AST → Validator → Runtime Engine → ExecutionTarget. The same compiled strategy runs backtests, paper trading, and live trading — there is no separate live code path.
Grammar
Formal grammar of the .algomln language in EBNF notation:
strategy = rule+
rule = "WHEN" condition NEWLINE action
condition = comparison
| cross_expr
| not_expr
| logical_expr
| position_expr ← parses, not yet evaluated
| time_window ← parses, not yet evaluated
comparison = expr operator expr
operator = "<" | ">" | "<=" | ">=" | "==" | "!="
logical_expr = condition "AND" condition
| condition "OR" condition
not_expr = "NOT" "(" condition ")"
cross_expr = "cross_above" "(" expr "," expr ")"
| "cross_below" "(" expr "," expr ")"
expr = indicator_call | price_field | number
indicator_call = indicator "(" integer ")"
indicator = "ema" | "ma" | "rsi" | "rel_vol" | "atr" | "vwap"
| "bb_upper" | "bb_lower" | "bb_mid"
price_field = "close" | "open" | "high" | "low" | "volume"
| "prev_close" | "prev_open" | "prev_high" | "prev_low"
action = "BUY" integer
| "SELL" integer
| "SELL" "ALL"
Price Fields
These identifiers resolve to values from the current (or previous) candle:
| Field | Description |
|---|---|
close | Current candle close price |
open | Current candle open price |
high | Current candle high price |
low | Current candle low price |
volume | Current candle volume |
prev_close | Previous candle close price |
prev_open | Previous candle open price |
prev_high | Previous candle high price |
prev_low | Previous candle low price |
Indicators
All built-in indicators are pure Rust functions — stateless and deterministic. They use a bounded window provider to stay O(N) regardless of history length. Custom indicators can be registered via the Plugin System.
Indicator calls take the form indicator_name(period) where period must be a positive integer.
ma — Simple Moving Average
Arithmetic mean of the last N close prices.
WHEN close > ma(50) BUY 10
ema — Exponential Moving Average
Exponentially weighted moving average, giving more weight to recent candles. Used for crossover strategies.
WHEN cross_above(ema(20), ema(50)) BUY 10
rsi — Relative Strength Index
Momentum oscillator in the range 0–100. Values below 30 indicate oversold; above 70 indicate overbought. Typical period: 14.
WHEN rsi(14) < 30 BUY 1 WHEN rsi(14) > 70 SELL ALL
atr — Average True Range
Measures market volatility. The true range is the greatest of: current high−low, |current high−prev close|, |current low−prev close|. ATR averages this over N candles.
WHEN atr(14) > 50 BUY 5
vwap — Volume Weighted Average Price
Average price weighted by volume over the given period. Commonly used as a dynamic support/resistance level.
WHEN close > vwap(20) BUY 5
rel_vol — Relative Volume
Current candle volume relative to the average volume over N candles. A value of 2.0 means twice the average volume — useful for detecting breakouts.
WHEN rel_vol(20) > 2 AND close > ma(50) BUY 10
bb_upper / bb_mid / bb_lower — Bollinger Bands
Bollinger Bands consist of a middle band (simple moving average) and upper/lower bands at ±2 standard deviations. All three share the same period parameter and are computed together for efficiency.
| Indicator | Returns |
|---|---|
bb_upper(period) | Middle band + 2 standard deviations |
bb_mid(period) | Simple moving average (same as ma) |
bb_lower(period) | Middle band − 2 standard deviations |
WHEN close < bb_lower(20) BUY 10 WHEN close > bb_upper(20) SELL ALL
Conditions
Conditions follow the WHEN keyword and determine when a rule fires.
Trigger state machine: A rule only fires on a false → true transition.
Once a rule is true, it will not fire again until it first becomes false.
This prevents WHEN close > 0 / BUY 1 from firing on every candle.
Comparison
Compare any two expressions using a standard operator:
| Operator | Meaning |
|---|---|
< | Less than |
> | Greater than |
<= | Less than or equal |
>= | Greater than or equal |
== | Equal |
!= | Not equal |
WHEN close > ma(50) BUY 5 WHEN rsi(14) >= 70 SELL ALL
Cross Detection
Crossovers fire on the exact candle where the fast value crosses the slow value.
The CrossDetector internally stores the previous candle values for each rule and compares
them after evaluation — so all rules within a cycle see consistent previous-candle state.
# Fires when ema(20) crosses above ema(50) WHEN cross_above(ema(20), ema(50)) BUY 10 # Fires when ema(20) crosses below ema(50) WHEN cross_below(ema(20), ema(50)) SELL ALL
Logical — AND / OR / NOT
Combine conditions with AND, OR, or negate with NOT(...).
# AND — both must be true WHEN ema(9) > ema(21) AND rsi(14) < 60 BUY 5 # OR — either is sufficient WHEN rsi(14) > 75 OR close > bb_upper(20) SELL ALL # NOT — negation WHEN NOT (rsi(14) > 70) BUY 2
NOT requires parentheses around its condition: NOT (condition).
Actions
Actions follow the condition on the next line. They instruct the execution engine to place an order.
| Action | Description |
|---|---|
BUY n | Buy n units of the strategy symbol at current close price |
SELL n | Sell n units |
SELL ALL | Close the entire position (sell all held units) |
Quantities (n) must be positive integers. Partial sells greater than held units are clamped to the position size by the PaperBroker.
Examples
RSI Oversold / Overbought
WHEN rsi(14) < 30 BUY 1 WHEN rsi(14) > 70 SELL ALL
EMA Crossover
WHEN cross_above(ema(20), ema(50)) BUY 10 WHEN cross_below(ema(20), ema(50)) SELL ALL
Compound Condition
WHEN ema(9) > ema(21) AND rsi(14) < 60 BUY 5 WHEN rsi(14) > 75 SELL ALL
Bollinger Band Breakout
WHEN close < bb_lower(20) BUY 10 WHEN close > bb_upper(20) SELL ALL
VWAP + Volume Filter
# Only buy if volume is elevated and price is above VWAP WHEN close > vwap(20) AND rel_vol(20) > 1 BUY 5 WHEN close < vwap(20) SELL ALL
Limitations
These features parse successfully but are not yet evaluated at runtime. Using them will not cause an error, but they will have no effect:
| Feature | Status |
|---|---|
| position_expr — position-based conditions (e.g., "if in position") | Parses · Not Evaluated |
| time_window — time-based conditions (e.g., "between 9:30 and 15:30") | Parses · Not Evaluated |
Additional intentional constraints:
- No variables — expressions cannot be assigned to names
- No loops — strategies run one candle at a time, not imperative loops
- Indicator periods must be positive integers — no expressions or variables as periods
- No string literals — the DSL has no string type
These constraints are intentional. The DSL is designed to be readable at a glance — a strategy should be self-evident from a 5-second read. Complex logic belongs in a plugin, not in the DSL itself.