Indicators
Indicators process price history and render visual overlays on the chart. TDSL supports three distinct modes — write from scratch, wrap a native engine, or load an external script.
Three Modes Overview
Mode A
Custom Standard
Write all the math. Full control over every calculation step.
Mode B
Native Fetch
Wraps one of the 30 built-in native engine indicators.
Mode C
Custom Fetch
Loads and wraps another .tbc script from your library.
Mode D
Hybrid Extension
Fetch a base indicator and layer custom secondary math on top.
Mode A — Custom Standard
Write all indicator math directly in TDSL. Use series double[] buffers, populate them in calculate(), and return a typed struct.
Tip
Start your loop at
Chart.resume_index - 1 (or period - 1 on first run) for incremental performance.BollingerBands.tbc — Custom StandardTDSL
about { author: "CoreXTrader"; version: "2.0.0"; }
parameters { int period = 20; double multiplier = 2.0; }
struct BollingerSignal { double upper; double middle; double lower; double buy_signal; double sell_signal; }
display {
separate_window: false; outputs: 5
BollingerSignal.upper { type: "line"; color: "#c42626ff"; name: "Upper"; width: 1; };
BollingerSignal.middle { type: "line"; color: "#b0aaaaff"; name: "Middle"; width: 0.5; };
BollingerSignal.lower { type: "line"; color: "#50c426ff"; name: "Lower"; width: 1; };
BollingerSignal.sell_signal { type: "arrow"; color: "#c42626ff"; arrow_code: 234; };
BollingerSignal.buy_signal { type: "arrow"; color: "#50c426ff"; arrow_code: 233; };
fill { buffer_a: 2; buffer_b: 0; color: "rgba(19,75,228,0.15)"; };
}
series double upper[]; series double middle[]; series double lower[];
series double buy_sig[]; series double sell_sig[];
calculate() {
double closes[] = close();
int len = candle_count();
int start = Chart.resume_index > 0 ? Chart.resume_index - 1 : period - 1;
upper[] = create_array(len); middle[] = create_array(len); lower[] = create_array(len);
buy_sig[] = create_array(len); sell_sig[] = create_array(len);
for (int i = start; i < len; i = i + 1) {
double sum = 0.0;
for (int j = 0; j < period; j = j + 1) { sum = sum + closes[i - j]; }
double mean = sum / period;
middle[i] = mean;
double sq = 0.0;
for (int j = 0; j < period; j = j + 1) { double d = closes[i-j] - mean; sq = sq + (d*d); }
double std_dev = Math.Sqrt(sq / period);
upper[i] = mean + (multiplier * std_dev);
lower[i] = mean - (multiplier * std_dev);
if (i > 0) {
if (closes[i-1] < lower[i-1] && closes[i] > lower[i]) { buy_sig[i] = lower[i]; }
if (closes[i-1] > upper[i-1] && closes[i] < upper[i]) { sell_sig[i] = upper[i]; }
}
}
return BollingerSignal {
upper: upper,
middle: middle,
lower: lower,
sell_signal: sell_sig,
buy_signal: buy_sig,
};
}Mode B — Native Fetch
Wrap a native engine. Use Native.Source, Native.Init, Native.Parameter, and Native.Bind in Init(), then fetch buffers in the proxy .calculate() block.
BollingerNative.tbc — Native FetchTDSL
struct BollingerSignal { double upper; double middle; double lower; }
parameters { int period = 20; double std_dev = 2.0; }
Init() {
Native.Source(i.Bollinger); // or indi.Bollinger
Native.Init(Symbol(), Timeframe);
Native.Parameter(period, std_dev, Price.Close);
Native.Bind(NativeBands);
}
NativeBands.calculate() {
int len = candle_count();
series double mid_buf[]; series double up_buf[]; series double lo_buf[];
mid_buf[] = create_array(len); up_buf[] = create_array(len); lo_buf[] = create_array(len);
// Buffer indices: 0=Middle, 1=Upper, 2=Lower
Native.Fetch(0, mid_buf); Native.Fetch(1, up_buf); Native.Fetch(2, lo_buf);
return BollingerSignal { upper: up_buf, middle: mid_buf, lower: lo_buf };
}Mode C — Custom Fetch
Load any .tbc indicator script and access its output buffers via Custom.Fetch.
Wrapping an external indicatorTDSL
parameters { int Bands_period = 20; double multiplier = 2.0; }
display {
outputs: 2
Bands.upper { type: "line"; color: "#dc2626"; name: "Upper Band"; };
Bands.lower { type: "line"; color: "#16a34a"; name: "Lower Band"; };
}
struct BollingerSignal { double upper; double lower; }
Init() {
Custom.Source("Bands Arrow.tbc");
Custom.Init(Symbol(), Timeframe.H1);
Custom.Parameter(Bands_period, multiplier);
Custom.Bind(Bands);
}
Bands.calculate() {
int len = candle_count();
series double up[]; series double lo[];
up[] = create_array(len); lo[] = create_array(len);
Custom.Fetch(0, up); // buffer 0 → upper band
Custom.Fetch(1, lo); // buffer 1 → lower band
return BollingerSignal { upper: up, lower: lo };
}Mode D — Hybrid Extension
Fetch data from native engines or external scripts, then perform custom secondary calculations (e.g. Volatility Bands derived from a Native EMA).
HybridIndi.tbcTDSL
struct Signal { double value; double vol_adjusted; }
parameters { int period = 50; double mult = 2.0; }
Init() {
// Fetch native EMA
Native.Source(i.EMA); Native.Init(Symbol(), Timeframe);
Native.Parameter(period, Price.Close);
Native.Bind(BaseEMA);
// Fetch custom volatility script
Custom.Source("HV.tbc"); Custom.Init(Symbol(), Timeframe);
Custom.Bind(BaseEMA);
}
BaseEMA.calculate() {
int len = candle_count();
series double ema[]; series double hv[]; series double result[];
ema[] = create_array(len); hv[] = create_array(len); result[] = create_array(len);
// Fetch from BOTH sources
Native.Fetch(0, ema);
Custom.Fetch(0, hv);
// Custom secondary math: Adjust EMA by Historical Volatility
for (int i = Chart.resume_index; i < len; i++) {
result[i] = ema[i] * (1.0 + hv[i] * mult);
}
return Signal { value: ema, vol_adjusted: result };
}Native Buffer Index Map
i.Bollinger0 Middle · 1 Upper · 2 Loweri.MACD0 MACD Line · 1 Signal · 2 Histogrami.RSI0 Value (0–100)i.Stochastic0 %K · 1 %Di.ATR0 Valuei.Ichimoku0 Tenkan · 1 Kijun · 2 Span A · 3 Span B · 4 Chikoui.SAR0 Valuei.Supertrend0 Value · 1 Direction (+1 up / −1 down)i.Pivot0 Pivot · 1 R1 · 2 S1 · 3 R2 · 4 S2 · 5 R3 · 6 S3i.MA0 Value