Language Syntax
TDSL is statically typed and supports structs, enums, persistent series buffers, and compiles to high-performance Rust.
Primitive Types
| Function / Property | Description |
|---|---|
int | Integer number (32-bit). |
double | Double-precision float. Default for prices and calculations. |
bool | Boolean: true or false. |
string | Text string. |
datetime | Point in time, stored as Unix timestamp (f64). |
timeframe | Chart timeframe constant: Timeframe.M1, Timeframe.H1, Timeframe.H4, Timeframe.D1, etc. |
color | RGBA color shown as a colour picker in the UI. |
enum | User-defined enumeration — shown as a dropdown in the UI. |
int count = 50;
double price = 1.23456;
bool is_long = true;
string label = "Upper Band";
datetime last_alert = 0.0;
timeframe tf = Timeframe.H4;System Data
Access market data length and engine-level orchestration variables.
| Function / Property | Type | Description |
|---|---|---|
candle_count() | double | Returns the total number of bars available in the current history. |
Chart.resume_index | int | The index of the first bar that needs calculation (0 on first run, >0 on new ticks). |
calculate() {
// Always loop from the last stable index to the latest candle
for (int i = Chart.resume_index; i < candle_count(); i++) {
// ... calculate ...
}
}Series Buffers
A series array is a DMA-backed persistent buffer that survives between calculate() calls. Each element corresponds to one bar.
series double upper[];
series double lower[];
calculate() {
int len = candle_count();
upper[] = create_array(len);
lower[] = create_array(len);
// Array-style: absolute bar index
upper[i] = mean + (2.0 * std_dev);
// Series-style: offset from current bar (0 = latest)
double prev_upper = upper(1);
double two_ago = upper(2);
}arr(0) reads relative to the most recent bar. arr[i] reads by absolute bar index.Structs & Returns
TDSL supports two ways to return data from an indicator: Named Structs (recommended for clarity) or Anonymous Tuples.
Option 1: Named Structs
Fields map to names in the display block (e.g. MyStruct.value).
display {
outputs: 2
IndicatorResult.value { type: "line"; color: "#3b82f6"; name: "Value"; };
IndicatorResult.signal { type: "line"; color: "#ef4444"; name: "Signal"; };
}
struct IndicatorResult {
double value;
double signal;
}
series double val_buf[];
series double sig_buf[];
calculate() {
int len = candle_count();
val_buf[] = create_array(len); sig_buf[] = create_array(len);
for (int i = Chart.resume_index; i < len; i++) {
val_buf[i] = close(0, i) * 1.05;
sig_buf[i] = close(0, i) * 0.95;
}
return IndicatorResult { value: val_buf, signal: sig_buf };
}Option 2: Anonymous Tuples (Buffer Arrays)
Return a simple array of buffers using [ ]. These map to buffer[index] in the display block.
display {
outputs: 3
buffer[0] { type: "line"; color: "#10b981"; name: "Upper"; };
buffer[1] { type: "line"; color: "#f59e0b"; name: "Middle"; };
buffer[2] { type: "line"; color: "#ef4444"; name: "Lower"; };
}
calculate() {
int len = candle_count();
series double up[]; series double mid[]; series double lo[];
up[] = create_array(len); mid[] = create_array(len); lo[] = create_array(len);
for (int i = Chart.resume_index; i < len; i++) {
mid[i] = close(0, i);
up[i] = mid[i] + 100 * Symbol.Point();
lo[i] = mid[i] - 100 * Symbol.Point();
}
return [up, mid, lo];
}[ ]) are only supported in standalone Indicators. When building Strategies or sub-indicator proxies, you must return data using Named Structs.Logic & Control Flow
TDSL supports standard C-style comparison operators and loop control statements for filtering data and managing execution flow.
Comparison Operators
| Function / Property | Parameters | Returns | Description |
|---|---|---|---|
== | — | void | Equal to. |
!= | — | void | Not equal to. |
> | — | void | Greater than. |
< | — | void | Less than. |
>= | — | void | Greater than or equal to. |
<= | — | void | Less than or equal to. |
Control Statements
// Continue and Break in loops
for (int i = 0; i < Position.Count(); i = i + 1) {
Position.SetIndex(i);
if (Position.Symbol() == "EURUSD") { continue; } // Skip specific symbol
if (Position.Magic() != 12345) { continue; } // Only process my magic
Trade.Close(Position.Ticket());
}
// Complex logic gates
if (Account.Balance() > 1000 && !Position.Exists()) {
Trade.Buy(Symbol(), 0.1);
} else if (Account.Equity() < 500 || Account.IsConnected() == false) {
Trade.CloseAll();
}Enums
Enums appear as dropdown selectors in the parameter panel.
enum Source { Close = 0, Open = 1, High = 2, Low = 3 }
parameters { Source src = Source.Close; int period = 14; }
calculate() {
double src_data[];
if (src == Source.Close) { src_data = close(); }
if (src == Source.High) { src_data = high(); }
}Parameters Block
parameters {
string _General_ = "=== General Settings ===";
int period = 14; // Smoothing Period
double factor = 2.0; // Deviation Factor
string _Risk_ = "=== Risk Settings ==="; // Risk Management
double lot_size = 0.1; // Fixed Lot Size
bool use_sl = true; // Enable Stop Loss
}State & Persistence
state { }Script lifetimeSerialised and restored on reload. Use for values that must survive indicator resets.staticScript lifetimeThread-safe persistent value. Survives tick-to-tick.global varInit() callModule-scope variable, reset on initialisation.state { datetime lastAlertTime = 0.0; int tradeCount = 0; }
static double lastBar = 0.0;
bool newbar() {
static datetime last;
datetime cur = time(0);
if (last != cur) { last = cur; return true; }
return false;
}Display Configuration
display {
separate_window: false;
outputs: 3
buffer[0] { type: "line"; color: "#c42626ff"; name: "Upper Band"; };
buffer[1] { type: "line"; color: "#b0aaaaff"; name: "Middle Band"; };
buffer[2] { type: "line"; color: "#50c426ff"; name: "Lower Band"; };
fill { buffer_a: 0; buffer_b: 1; color: "rgba(220,38,38,0.05)"; };
}"line"Continuous line connecting bar values."histogram"Vertical bar from zero to the value."arrows"Directional arrow glyph at each bar."dots"Single dot plotted at each bar."candles"Full OHLC candle from four buffers.Control Flow
if (rsi_val < 30.0) { signal = 1; }
else if (rsi_val > 70.0) { signal = -1; }
for (int i = Chart.resume_index; i < candle_count(); i = i + 1) {
result[i] = closes_array[i];
}
while (count < max) { count = count + 1; }
double lot = use_fixed ? fixed_lot : Symbol.ToLots(risk_pct, sl_pips);