20260602.1 · Server-run grants unification
minor- Server-run access moved to unified grant ledger flow for script-level runtime control.
- Run authorization path decoupled from older invite-only subscription tables.
Complete syntax and runtime guide for Tradeiz YeScript — declarations, built-ins, plotting, strategy execution, bytecode internals, and server-run behavior.
YeScript is Tradeiz's chart-native DSL. Source compiles into bytecode and executes per bar in a sandboxed VM. The same script model is used for browser backtest and server-run execution.
strategy("EMA + RSI", initial_capital=10000, commission=0.001)
var fast = ta.ema(close, 12)
var slow = ta.ema(close, 26)
var r = ta.rsi(close, 14)
if ta.crossOver(fast, slow) and r < 65
trade.open_long("ema_long", 1)
if ta.crossUnder(fast, slow)
trade.close_all()
Every .yes script starts with exactly one declaration on the first effective line.
| Declaration | Description |
|---|---|
indicator("Name") | Indicator script. Default pane behavior comes from host routing and indicator options. |
indicator("Name", overlay=true) | Indicator overlaying price context. |
strategy("Name", initial_capital=..., commission=..., default_qty=...) | Strategy script that can emit trade intents and backtest metrics. |
library("Name") | Reusable function package for import-oriented workflows. |
Lexer supports single-line and block comments.
// slash comment
# hash comment
var x = 1 // inline
/* multi-line comment
still ignored by compiler */
| Type | Examples | Notes |
|---|---|---|
number | 42, 3.14, -0.5 | Primary numeric type for series/math. |
string | "BTCUSDT", "#2196F3" | Text, identifiers, colors, labels. |
bool | true, false | Control and trigger conditions. |
na | na | Missing value marker, propagates through many calculations. |
array | [1, 2, 3] | Runtime array object for arr.*. |
map | map.new() | Runtime map object for map.*. |
| Name | Description |
|---|---|
open, high, low, close, volume | Current bar OHLCV fields from host data. |
bar_index | Zero-based current bar index. |
time | Current bar timestamp (seconds, UTC context). |
Series can be referenced with bracket offsets.
close[0] // current close
close[1] // previous bar close
high[5] // high from five bars ago
volume[2] // volume from two bars ago
Out-of-range history returns na.
| Form | Behavior |
|---|---|
var x = expr | Mutable variable in script scope. |
const x = expr | Immutable after declaration. |
state x = expr | Initialized once; persistent across bars. |
x = expr | Assignment-style declaration/update when allowed by analyzer rules. |
var regime = close > ta.ema(close, 200) ? "bull" : "bear"
var trigger = ta.crossOver(close, ta.sma(close, 20)) and not na(close[1])
Indentation-based blocks are used throughout.
if close > open
log.info("bull bar")
else if close < open
log.info("bear bar")
else
log.info("doji")
for i = 0 to 10 by 1
if i == 5
continue
var total = 0
for p in [close, close[1], close[2]]
total = total + p
User functions are declared with fn and can return values.
fn spread(srcHigh, srcLow)
return srcHigh - srcLow
var s = spread(high, low)
return is valid inside function scope and supports expression returns.
Library declaration and import syntax supported by parser/analyzer:
library("shared_stats")
fn zscore(src, len)
var m = ta.sma(src, len)
return (src - m) / ta.stdev(len)
use "shared_stats"
import (zscore, signal) from "shared_stats"
For production workflows, keep library code reusable and side-effect free.
Input descriptors are captured per script and can be overridden by host-provided values.
| Function | Signature | Return |
|---|---|---|
input.int | input.int(defval, title) | integer |
input.float | input.float(defval, title) | number |
input.bool | input.bool(defval, title) | boolean |
input.string | input.string(defval, title) | string |
input.source | input.source(defval, title) | string |
input.timeframe | input.timeframe(defval, title) | string |
input.color | input.color(defval, title) | string |
input.symbol | input.symbol(defval, title) | string |
| Function | Description |
|---|---|
log.info(msg, ...) | Informational log entry for current bar. |
log.warn(msg, ...) | Warning log entry for current bar. |
log.error(msg, ...) | Error log entry for current bar. |
| Function/const | Signature |
|---|---|
math.abs | math.abs(x) |
math.max, math.min | math.max(a, b), math.min(a, b) |
math.round, math.floor, math.ceil | single-number transforms |
math.sqrt, math.pow, math.log, math.exp | core numeric transforms |
math.sin, math.cos, math.tan | trigonometric |
math.asin, math.acos, math.atan | inverse trigonometric |
math.log10, math.log2, math.sign, math.random | misc numeric helpers |
math.avg(...), math.sum(...) | variadic aggregations |
math.to_degrees, math.to_radians | angle conversion |
math.PI, math.E | numeric constants |
| Function | Signature |
|---|---|
str.length | str.length(s) |
str.contains | str.contains(s, sub) |
str.to_upper, str.to_lower | case conversion |
str.replace | str.replace(s, old, next) (global replacement) |
str.substring | str.substring(s, start, end?) |
str.starts_with, str.ends_with | prefix/suffix checks |
str.to_string | str.to_string(value) |
str.format | str.format("x={0}", x) |
| Function | Signature | Notes |
|---|---|---|
arr.new | arr.new(size, fill) | create array |
arr.length | arr.length(a) | canonical length name |
arr.get, arr.set | arr.get(a, idx), arr.set(a, idx, val) | random access |
arr.push, arr.pop, arr.shift, arr.unshift | queue/stack ops | mutating |
arr.includes | arr.includes(a, value) | membership check |
arr.sum, arr.avg, arr.max, arr.min | numeric aggregation | non-numeric ignored in numeric reducers |
arr.sort, arr.reverse, arr.slice, arr.join | structural transforms | sort/reverse return copied arrays |
| Function | Signature |
|---|---|
map.new | map.new() |
map.get, map.set, map.has | key/value read-write checks |
map.keys, map.values, map.size | introspection helpers |
map.remove, map.clear | deletion helpers |
| Property | Description |
|---|---|
sym.ticker | Host symbol/ticker (default fallback: BTCUSDT). |
sym.exchange | Host exchange id/name. |
sym.kind | Host market kind. |
sym.description | Description field (currently empty placeholder if host does not inject). |
| Property/function | Description |
|---|---|
tf.period | Current timeframe period string from host context. |
tf.multiplier | Current timeframe multiplier from host context. |
tf.data(tf, expr) | Pass-through placeholder hook for multi-timeframe extension. |
| Property | Range/description |
|---|---|
time.hour, time.minute, time.second | UTC components of current bar datetime. |
time.dayofweek, time.dayofmonth, time.month, time.year | UTC calendar breakdown. |
time.is_monday, time.is_friday, time.is_weekend | Boolean day selectors. |
Color helpers include constructors and constants:
color.new("#2196F3", 90)
color.rgb(33, 150, 243, 200)
color.red color.green color.blue color.orange color.purple color.yellow
color.white color.black color.gray color.aqua color.lime color.fuchsia
color.silver color.teal color.navy color.maroon color.olive
This table reflects VM/runtime built-ins plus codegen TA opcode mappings. Canonical cross names are ta.crossOver / ta.crossUnder; lowercase aliases ta.crossover / ta.crossunder are also accepted.
| # | Function | Typical signature | Return |
|---|---|---|---|
| 1 | ta.sma | ta.sma(length) or ta.sma(close, length) | number |
| 2 | ta.ema | ta.ema(length) or ta.ema(close, length) | number |
| 3 | ta.rsi | ta.rsi(length) or ta.rsi(close, length) | number |
| 4 | ta.atr | ta.atr(length) | number |
| 5 | ta.wma | ta.wma(length) or ta.wma(close, length) | number |
| 6 | ta.highest | ta.highest(length) or ta.highest(close, length) | number |
| 7 | ta.lowest | ta.lowest(length) or ta.lowest(close, length) | number |
| 8 | ta.change | ta.change() or ta.change(length) | number |
| 9 | ta.crossOver | ta.crossOver(a, b) | bool |
| 10 | ta.crossUnder | ta.crossUnder(a, b) | bool |
| 11 | ta.vwma | ta.vwma(length) | number |
| 12 | ta.rma | ta.rma(length) | number |
| 13 | ta.hma | ta.hma(length) | number |
| 14 | ta.swma | ta.swma() | number |
| 15 | ta.alma | ta.alma(length, offset?, sigma?) | number |
| 16 | ta.dema | ta.dema(length) | number |
| 17 | ta.tema | ta.tema(length) | number |
| 18 | ta.macd | ta.macd(fast?, slow?, signal?) | tuple |
| 19 | ta.stoch | ta.stoch(k?, d?, smooth?) | tuple |
| 20 | ta.cci | ta.cci(length?) | number |
| 21 | ta.mfi | ta.mfi(length?) | number |
| 22 | ta.roc | ta.roc(length?) | number |
| 23 | ta.mom | ta.mom(length?) | number |
| 24 | ta.willr | ta.willr(length?) | number |
| 25 | ta.obv | ta.obv() | number |
| 26 | ta.bb | ta.bb(length?, mult?) | tuple |
| 27 | ta.kc | ta.kc(length?, mult?, atr_len?) | tuple |
| 28 | ta.supertrend | ta.supertrend(factor?, atr_len?) | tuple |
| 29 | ta.stdev | ta.stdev(length) | number |
| 30 | ta.variance | ta.variance(length) | number |
| 31 | ta.tr | ta.tr() | number |
| 32 | ta.dmi | ta.dmi(length?, adx_len?) | tuple |
| 33 | ta.sar | ta.sar(start?, inc?, max?) | number |
| 34 | ta.pivot_high | ta.pivot_high(left, right) | number |
| 35 | ta.pivot_low | ta.pivot_low(left, right) | number |
| 36 | ta.cum | ta.cum() | number |
| 37 | ta.rising | ta.rising(length) | bool |
| 38 | ta.falling | ta.falling(length) | bool |
| 39 | ta.bars_since | ta.bars_since(cond) | number |
| 40 | ta.pct_rank | ta.pct_rank(length) | number |
| 41 | ta.linreg | ta.linreg(length, offset?) | number |
| 42 | ta.valuewhen | ta.valuewhen(cond, source, n?) | number |
| 43 | ta.highestbars | ta.highestbars(length) | number |
| 44 | ta.lowestbars | ta.lowestbars(length) | number |
| 45 | ta.median | ta.median(length) | number |
| 46 | ta.percentile | ta.percentile(length, pct?) | number |
| 47 | ta.correlation | ta.correlation(length) | number |
Available only in strategy() scripts.
Paper Trading: trade.open_long and trade.open_short add quantity to the same merged account position as your manual trades on that symbol (same leverage and margin mode).
| Function/property | Signature | Description |
|---|---|---|
trade.open_long | trade.open_long(id, qty) | emit open-long intent |
trade.open_short | trade.open_short(id, qty) | emit open-short intent |
trade.close_long | trade.close_long(id) | emit close-long intent |
trade.close_short | trade.close_short(id) | emit close-short intent |
trade.exit | trade.exit(id) | emit exit intent |
trade.close_all | trade.close_all() | flatten all positions |
trade.cancel | trade.cancel(id) | cancel single pending order |
trade.cancel_all | trade.cancel_all() | cancel all pending orders |
trade.limit | trade.limit(id, qty, price) | emit limit-order intent |
trade.stop | trade.stop(id, qty, price) | emit stop-order intent |
trade.position_size | property | current position size |
trade.avg_price | property | average entry price |
trade.equity | property | current equity snapshot |
trade.open_profit | property | unrealized PnL |
trade.side | property | long, short, or flat side label |
In chart/backtest mode, only the strategy's simulated position exists. trade.close_long, trade.close_short, trade.exit, trade.close_all, and close_position close that strategy position only—the semantics are clear.
In server-run Paper Trading, futures positions are merged per user, symbol, leverage, and margin mode into one ti_positions row. There is no source field—strategy fills and manual fills share the same account record.
When the strategy closes, it closes the merged net position, not just the slice the strategy opened. If you manually opened the same symbol on the same side, the strategy close can reduce or flatten your manual position too.
This is intentional product behavior, not a bug. See also: Paper Trading.
Existing trade.* APIs and legacy buy/sell/close_position statements still compile unchanged—no paper-trading-specific syntax was added. By default, server runs emit signals only; trade intents reach the run owner's Paper Trading account only when you explicitly enable simulated orders for that run at creation time.
Closes operate on the account merged net position for that symbol and side (full explanation under trade.* Close functions above). Manual positions in the same direction are included. This is intentional product behavior, not a defect.
Codes returned to the worker/run UI when an intent is not executed:
| Code | Description |
|---|---|
strategy_pending_intent_not_supported | Limit, stop, or cancel intents are not supported for server-run Paper Trading. |
strategy_intent_not_executable | Intent could not be mapped to a market open/close action. |
strategy_position_not_found | Close intent but no matching VM position, or no matching Paper Trading position on the account. |
strategy_intent_limit_exceeded | More than 16 trade intents on the same bar. |
paper_trading_not_enabled_for_run | This server run was not created with simulated-order consent (paper_trading_enabled=false). |
feature_not_in_plan | Run owner lacks yescript.paper_trading entitlement. |
strategy_order_rate_limited | More than 120 strategy orders for this user in the current minute. |
strategy_order_rate_limit_unavailable | Rate-limit store unavailable; retry later. |
entitlement_unavailable | Could not verify entitlements. |
paper_order_unavailable | Paper Trading service unreachable. |
strategy_run_not_active | Server run is not in starting/running/idle state. |
strategy_market_not_supported | Paper Trading routing applies to futures server runs only. |
paper_order_not_filled | Market order submitted but received no fill. |
paper_order_rejected | Paper Trading rejected the order (4xx upstream). |
| Function | Signature | Notes |
|---|---|---|
plot | plot(value, label?, color?, pane?) | line series |
plotHistogram | plotHistogram(value, label?, color?, pane?) | histogram series |
hline | hline(price, label?, color?, pane?) | horizontal line |
bgcolor | bgcolor(color, label?, opacity?) | background tint |
mark | mark(value, style?, location?, color?, size?, text?) | shape marker (canonical; not plotshape) |
tint | tint(color) | bar tint |
arrow | arrow(value, colorUp?, colorDown?) | directional arrow |
candle | candle(open, high, low, close, color?) | custom candle draw command |
fill | fill(plot1, plot2, color?, opacity?) | area fill between named plots |
Statement form:
alert("Price crossed above resistance")
Legacy strategy statements remain available and compile to trade opcodes.
buy(0.01)
buy("BTCUSDT", 0.01)
sell(0.01)
close_position("BTCUSDT")
close_position follows the same close semantics as trade.close_long and trade.close_all. In backtest it closes the strategy position only. In Paper Trading it closes the merged account net position, which can include manual positions on the same symbol and side—see Close functions under trade.*.
Compile and execution pipeline:
source (.yes)
-> lexer -> parser -> analyzer -> codegen
-> bytecode (magic: YESC, version: 2)
-> VM.executeBar()
-> { plotCommands, tradeIntents, alertIntents, logEntries }
| Opcode group | Examples |
|---|---|
| Stack/vars | PUSH_CONST, LOAD_VAR, STORE_STATE |
| Arithmetic/logical | ADD, DIV, EQ, AND |
| Data access | GET_OPEN, GET_TIME, GET_SERIES_AT |
| TA | CALL_SMA, CALL_RSI, CALL_CROSS_OVER, CALL_BUILTIN |
| Drawing | DRAW_PLOT, DRAW_MARK, DRAW_FILL |
| Trade/alert | TRADE_BUY, TRADE_CLOSE, ALERT_FIRE |
Known constraints and doc-vs-VM differences (source-of-truth: compiler/vm runtime):
Changelog
Version history highlights for syntax/runtime milestones. Release labels use YYYYMMDD.N style to align with API/platform docs.
YeScript syntax and runtime releases use calendar versions in the form YYYYMMDD.N — date stamp plus a same-day increment. Breaking language or bytecode changes bump the date or reset N; additive APIs and runtime improvements ship as regular increments on the release date.
major — Breaking DSL keywords, bytecode format, or incompatible runtime semantics.minor — New built-ins, community workflows, or materially expanded script capabilities.patch — Server-run, subscription, or documentation-only adjustments without language changes.