Indicator Cookbook
Ready-to-use indicator recipes. Each recipe includes complete code and an explanation of the technique used.
Trend Indicators
SuperTrend with Dynamic Coloring
ATR-based trailing stop that flips with trend changes. Green = uptrend, red = downtrend.
typescript
import { Script, input, plotColor, color } from '@chartlabs/script-sdk';
export default class SuperTrend extends Script {
period = input.number('ATR Period', 10, { min: 1, max: 100 });
factor = input.number('Factor', 3.0, { min: 0.5, max: 10, step: 0.5 });
calculate() {
const st = this.supertrend(this.period, this.factor);
plotColor('SuperTrend', st.value,
[color.green, color.red],
st.direction === 1 ? 0 : 1,
{ width: 2 }
);
}
}Chandelier Exit
ATR-based trailing stops from the highest high (long) or lowest low (short).
typescript
import { Script, input, plot, color } from '@chartlabs/script-sdk';
export default class ChandelierExit extends Script {
period = input.number('Period', 22, { min: 1, max: 100 });
mult = input.number('ATR Mult', 3.0, { min: 1, max: 10, step: 0.5 });
calculate() {
const atrVal = this.atr(this.period);
const longStop = this.highest('high', this.period) - atrVal * this.mult;
const shortStop = this.lowest('low', this.period) + atrVal * this.mult;
plot('Long Stop', longStop, { color: color.green, width: 1, overlay: true });
plot('Short Stop', shortStop, { color: color.red, width: 1, overlay: true });
}
}Moving Average Ribbon
Multiple EMAs showing trend direction and strength.
typescript
import { Script, input, plot, color } from '@chartlabs/script-sdk';
export default class MARibbon extends Script {
source = input.source('Source', 'close');
calculate() {
const periods = [8, 13, 21, 34, 55, 89];
const colors = [color.cyan, color.blue, color.purple, color.orange, color.red, color.maroon];
for (let i = 0; i < periods.length; i++) {
plot('EMA' + periods[i], this.ema(this.source, periods[i]), {
color: colors[i], width: 1, overlay: true
});
}
}
}Donchian Channel
Highest high / lowest low channel.
typescript
import { Script, input, plot, color } from '@chartlabs/script-sdk';
export default class DonchianChannel extends Script {
length = input.number('Period', 20, { min: 1, max: 200 });
calculate() {
const upper = this.highest('high', this.length);
const lower = this.lowest('low', this.length);
const mid = (upper + lower) / 2;
plot('Upper', upper, { color: color.blue, width: 1, overlay: true });
plot('Middle', mid, { color: color.gray, width: 1, overlay: true, style: 'dashed' });
plot('Lower', lower, { color: color.blue, width: 1, overlay: true });
}
}Momentum Indicators
MACD Enhanced (4-Color Histogram)
typescript
import { Script, input, plot, plotHistogram, hline, color } from '@chartlabs/script-sdk';
export default class MACDEnhanced extends Script {
fast = input.number('Fast', 12, { min: 1, max: 200 });
slow = input.number('Slow', 26, { min: 1, max: 500 });
sig = input.number('Signal', 9, { min: 1, max: 100 });
calculate() {
const m = this.macd('close', this.fast, this.slow, this.sig);
plot('MACD', m.macd, { color: color.blue, width: 2 });
plot('Signal', m.signal, { color: color.orange });
hline('Zero', 0, { color: color.gray, style: 'dashed' });
const hist = m.histogram;
const prev = this.bar > 0 ? this.getPlot('MACD', 1) - this.getPlot('Signal', 1) : 0;
let c;
if (hist >= 0) { c = hist > prev ? '#16a34a' : '#86efac'; }
else { c = hist < prev ? '#dc2626' : '#fca5a5'; }
plotHistogram('Hist', hist, { color: c });
}
}Awesome Oscillator
Difference between 5-period and 34-period SMA of the midpoint, colored by momentum.
typescript
import { Script, plotHistogram, color } from '@chartlabs/script-sdk';
export default class AwesomeOscillator extends Script {
calculate() {
const ao = this.sma('hl2', 5) - this.sma('hl2', 34);
const prev = this.bar > 0 ? this.getPlot('AO', 1) : ao;
plotHistogram('AO', ao, { color: ao > prev ? color.green : color.red });
}
}CCI with Zones
typescript
import { Script, input, plotColor, hline, color } from '@chartlabs/script-sdk';
export default class CCIZones extends Script {
length = input.number('Period', 20, { min: 1, max: 200 });
calculate() {
const val = this.cci('hlc3', this.length);
plotColor('CCI', val,
[color.green, color.red, color.gray],
val > 100 ? 0 : val < -100 ? 1 : 2,
{ width: 2 }
);
hline('Upper', 100, { color: color.red, style: 'dashed' });
hline('Lower', -100, { color: color.green, style: 'dashed' });
hline('Zero', 0, { color: color.gray, style: 'dotted' });
}
}Volume Indicators
On Balance Volume with Signal
typescript
import { Script, input, plot, color } from '@chartlabs/script-sdk';
export default class OBVSignal extends Script {
signalLen = input.number('Signal Period', 21, { min: 1, max: 100 });
calculate() {
const obvVal = this.obv();
plot('OBV', obvVal, { color: color.blue, width: 2 });
// Signal line: SMA of OBV
const signal = this.sma(obvVal, this.signalLen);
plot('Signal', signal, { color: color.orange, width: 1 });
}
}Volume-Weighted Price vs SMA
Shows when volume-weighted average diverges from simple average.
typescript
import { Script, input, plot, plotHistogram, color } from '@chartlabs/script-sdk';
export default class VWMAvsSMA extends Script {
length = input.number('Period', 20, { min: 1, max: 200 });
calculate() {
const vwmaVal = this.vwma('close', this.length);
const smaVal = this.sma('close', this.length);
const diff = vwmaVal - smaVal;
plotHistogram('Diff', diff, {
color: diff > 0 ? color.green : color.red
});
}
}Volatility Indicators
Bollinger Bandwidth
Measures how wide Bollinger Bands are. Low values indicate squeeze.
typescript
import { Script, input, plot, hline, color } from '@chartlabs/script-sdk';
export default class BollingerBandwidth extends Script {
length = input.number('Period', 20, { min: 2, max: 200 });
mult = input.number('Multiplier', 2.0, { min: 0.5, max: 5, step: 0.5 });
calculate() {
const bb = this.bbands('close', this.length, this.mult);
const bandwidth = (bb.upper - bb.lower) / bb.middle * 100;
plot('Bandwidth', bandwidth, { color: color.purple, width: 2 });
}
}ATR Percent
ATR as a percentage of price for cross-instrument comparison.
typescript
import { Script, input, plot, color } from '@chartlabs/script-sdk';
export default class ATRPercent extends Script {
length = input.number('Period', 14, { min: 1, max: 100 });
calculate() {
const atrVal = this.atr(this.length);
const atrPct = this.close > 0 ? (atrVal / this.close) * 100 : 0;
plot('ATR%', atrPct, { color: color.orange, width: 2 });
}
}Composite Indicators
Trend Strength Dashboard
Combines ADX, RSI, and MFI into a single composite signal.
typescript
import { Script, input, plotHistogram, plot, hline, color } from '@chartlabs/script-sdk';
export default class TrendDashboard extends Script {
period = input.number('Period', 14, { min: 1, max: 50 });
calculate() {
const adxVal = this.adx(this.period);
const rsiVal = this.rsi('close', this.period);
const mfiVal = this.mfi(this.period);
const trending = adxVal > 25 ? 1 : 0;
const bullMomentum = rsiVal > 50 ? 1 : -1;
const volumeBull = mfiVal > 50 ? 1 : -1;
const score = trending * (bullMomentum + volumeBull);
plotHistogram('Score', score, {
color: score > 0 ? color.green : score < 0 ? color.red : color.gray
});
plot('ADX', adxVal, { color: color.purple });
hline('Trend', 25, { color: color.gray, style: 'dashed' });
}
}Keltner Channel
EMA-based channel using ATR for width.
typescript
import { Script, input, plot, color } from '@chartlabs/script-sdk';
export default class KeltnerChannel extends Script {
length = input.number('Period', 20, { min: 1, max: 200 });
mult = input.number('ATR Mult', 2.0, { min: 0.5, max: 5, step: 0.5 });
calculate() {
const mid = this.ema('close', this.length);
const atrVal = this.atr(this.length);
plot('Upper', mid + atrVal * this.mult, { color: color.blue, overlay: true });
plot('Middle', mid, { color: color.gray, overlay: true });
plot('Lower', mid - atrVal * this.mult, { color: color.blue, overlay: true });
}
}Rate of Change Oscillator
typescript
import { Script, input, plotColor, hline, color } from '@chartlabs/script-sdk';
export default class ROCOscillator extends Script {
length = input.number('Period', 12, { min: 1, max: 200 });
calculate() {
const rocVal = this.roc('close', this.length);
plotColor('ROC', rocVal,
[color.green, color.red],
rocVal >= 0 ? 0 : 1,
{ width: 2 }
);
hline('Zero', 0, { color: color.gray, style: 'dashed' });
}
}
