Skip to content

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' });
  }
}

ChartLabs Documentation