Skip to main content

Tutorial 1: Getting started with ti-engine

Leverage Rust-powered speed from JavaScript — compute indicators directly on plain arrays.

This tutorial is the first in a series:


🎯 What you’ll learn

  • Install and initialize ti-engine
  • Understand the single vs bulk API style
  • Compute:
    • Simple Moving Average (SMA)
    • Moving Constant Bands (generic Bollinger-like bands)
    • Relative Strength Index (RSI)
    • Average True Range (ATR)
  • Handle window alignment and common pitfalls

📦 Prerequisites

Install ti-engine:

# npm
npm install ti-engine

# yarn
yarn add ti-engine

# pnpm
pnpm add ti-engine

Initialize (required in browsers; a no-op in Node for parity):

import init from "ti-engine";
await init();

🧱 Library structure (quick recap)

  • Namespaces group indicators by families:
    • movingAverage, momentumIndicators, candleIndicators, otherIndicators, trendIndicators, strengthIndicators, volatilityIndicators, correlationIndicators, chartTrends
  • Each indicator namespace typically exposes:
    • single: full-window, scalar/tuple output
    • bulk: rolling windows, vector output (length L - N + 1 for window N)

Enums you’ll use frequently:

  • MovingAverageType: Simple, Smoothed, Exponential
  • ConstantModelType: SimpleMovingAverage, SmoothedMovingAverage, ExponentialMovingAverage, SimpleMovingMedian, SimpleMovingMode
  • DeviationModel: StandardDeviation, MeanAbsoluteDeviation, MedianAbsoluteDeviation, ModeAbsoluteDeviation, UlcerIndex

📥 Data

We’ll use a constant array for Close prices. For ATR, we’ll derive simple High/Low arrays from Close just for demonstration (use real OHLC in practice).

// Close prices
const close = [
6037.59, 5970.84, 5906.94, 5881.63, 5868.55, 5942.47, 5975.38, 5909.03,
5918.25, 5827.04, 5836.22, 5842.91, 5949.91, 5937.34, 5996.66, 6049.24,
6086.37, 6118.71, 6101.24, 6012.28, 6067.70, 6039.31, 6071.17, 6040.53,
5994.57, 6037.88, 6061.48, 6083.57, 6025.99, 6066.44, 6068.50, 6051.97,
6115.07, 6114.63, 6129.58, 6144.15, 6117.52, 6013.13, 5983.25, 5955.25,
5956.06, 5861.57, 5954.50, 5849.72, 5778.15, 5842.63, 5738.52, 5770.20,
5614.56, 5572.07, 5599.30, 5521.52, 5638.94
];

// Synthetic High/Low for ATR demo (replace with real OHLC in practice)
const high = close.map(v => v + 20);
const low = close.map(v => v - 20);

✅ Example 1: Simple Moving Average (SMA)

Helpers live under movingAverage. Use MovingAverageType to pick Simple/Smoothed/Exponential.

import init, { movingAverage, MovingAverageType } from "ti-engine";
await init();

// Single: average over the full array
const smaAll = movingAverage.single.movingAverage(close, MovingAverageType.Simple);
console.log("Full-series SMA:", smaAll);

// Bulk: rolling SMA with period=20
const periodSMA = 20;
const smaSeries = movingAverage.bulk.movingAverage(close, MovingAverageType.Simple, periodSMA);
console.log(`SMA(${periodSMA}) series length:`, smaSeries.length);
// Alignment tip: smaSeries[0] corresponds to close[0..19] and anchors at close[19]

✅ Example 2: Moving Constant Bands (generic Bollinger-like bands)

Bands around a moving “constant” (e.g., EMA) using a deviation model (e.g., StdDev). Outputs are [lower, middle, upper].

import init, {
candleIndicators,
ConstantModelType,
DeviationModel
} from "ti-engine";
await init();

const periodBands = 20;
const constantModel = ConstantModelType.ExponentialMovingAverage;
const deviationModel = DeviationModel.StandardDeviation;
const deviationMultiplier = 2.0;

// Single (latest window)
const latestWindow = close.slice(-periodBands);
const [lower1, middle1, upper1] = candleIndicators.single.movingConstantBands(
latestWindow,
constantModel,
deviationModel,
deviationMultiplier
);
console.log("Latest MCB (single):", lower1, middle1, upper1);

// Bulk (rolling across the whole series)
const bands = candleIndicators.bulk.movingConstantBands(
close,
constantModel,
deviationModel,
deviationMultiplier,
periodBands
);
// bands[i] => [lower, middle, upper] for window close[i..i+periodBands-1], anchored at close[i+periodBands-1]
const [lowerLast, middleLast, upperLast] = bands[bands.length - 1];
console.log("Last MCB (bulk):", lowerLast, middleLast, upperLast);

✅ Example 3: Relative Strength Index (RSI)

Use momentumIndicators with a ConstantModelType (SmoothedMovingAverage is the common default in many RSI formulations).

import init, { momentumIndicators, ConstantModelType } from "ti-engine";
await init();

const periodRSI = 14;
const rsiSeries = momentumIndicators.bulk.relativeStrengthIndex(
close,
ConstantModelType.SmoothedMovingAverage,
periodRSI
);
console.log(`RSI(${periodRSI}) length:`, rsiSeries.length);

// Latest RSI using single on the last window
const rsiLatest = momentumIndicators.single.relativeStrengthIndex(
close.slice(-periodRSI),
ConstantModelType.SmoothedMovingAverage
);
console.log("Latest RSI (single):", rsiLatest);

✅ Example 4: Average True Range (ATR)

ATR measures average true range of movement; provide aligned arrays for previous close, high, and low. You can choose the averaging model (e.g., ExponentialMovingAverage) and a window.

import init, { otherIndicators, ConstantModelType } from "ti-engine";
await init();

const periodATR = 14;
const atrSeries = otherIndicators.bulk.averageTrueRange(
close, // previous closes
high,
low,
ConstantModelType.ExponentialMovingAverage,
periodATR
);
console.log(`ATR(${periodATR}) length:`, atrSeries.length);
console.log("Latest ATR:", atrSeries[atrSeries.length - 1]);

// Note: atrSeries[0] corresponds to close/high/low window [0..periodATR-1], anchored at index periodATR-1

🛠️ Alignment and windows

  • Bulk outputs shrink: length = L - N + 1 for a window N over L inputs.
  • For i from 0 to L - N:
    • bulkOut[i] corresponds to input slice inputs[i..i+N-1]
    • “Anchors” at inputs[i + N - 1] (the right edge of each rolling window)

🧪 Single vs Bulk — when to choose

  • single: one full-window value (e.g., latest signal, dashboard, streaming tick updates)
  • bulk: historical series over rolling windows (e.g., chart overlays, backtests, feature engineering)

⚙️ Tips

  • Always await init() in browsers (it loads the WebAssembly module).
  • Use number[] or Float64Array. For large series, consider chunking to amortize crossings.
  • Consistency matters: arrays for multi-input indicators (e.g., ATR) must be the same length.

✅ Next step

Happy analyzing! 🦀📈