Update version numbers across Rust, Python, and documentation files to 1.1.0. Enhance the .gitignore to include macOS dSYM files and plans directory. Introduce new dependencies in the Rust core library and update the README to reflect recent performance benchmarks and backtesting engine capabilities. Add new artifacts to the benchmarks manifest and improve documentation for the backtesting engine API.
ferro-ta WASM
WebAssembly bindings for the ferro-ta technical analysis library.
Install from npm
Once published, install the Node.js build from npm:
npm install ferro-ta-wasm
const { sma, ema, wma, rsi, adx, mfi, bbands, atr, obv, macd } = require('ferro-ta-wasm');
const close = new Float64Array([44.34, 44.09, 44.15, 43.61, 44.33, 44.83, 45.10]);
const smaOut = sma(close, 3);
console.log('SMA:', Array.from(smaOut));
Decision: We chose WebAssembly (wasm-bindgen / wasm-pack) as the second binding because it runs in browsers and Node.js without any native addons, and shares zero unsafe FFI surface with the Python build. Node.js users get a pure-JS entry point; browser users get the same
.wasmfile.
Available Indicators
| Category | Function | Parameters | Returns |
|---|---|---|---|
| Overlap | sma |
close: Float64Array, timeperiod: number |
Float64Array |
| Overlap | ema |
close: Float64Array, timeperiod: number |
Float64Array |
| Overlap | wma |
close: Float64Array, timeperiod: number |
Float64Array |
| Overlap | bbands |
close, timeperiod, nbdevup, nbdevdn |
Array[upper, middle, lower] |
| Momentum | rsi |
close: Float64Array, timeperiod: number |
Float64Array |
| Momentum | adx |
high, low, close: Float64Array, timeperiod |
Float64Array |
| Momentum | macd |
close, fastperiod, slowperiod, signalperiod |
Array[macd, signal, hist] |
| Momentum | mom |
close: Float64Array, timeperiod: number |
Float64Array |
| Momentum | stochf |
high, low, close, fastk_period, fastd_period |
Array[fastk, fastd] |
| Volatility | atr |
high, low, close: Float64Array, timeperiod |
Float64Array |
| Volume | obv |
close: Float64Array, volume: Float64Array |
Float64Array |
| Volume | mfi |
high, low, close, volume: Float64Array, timeperiod |
Float64Array |
Adding more indicators
WASM exports live in src/lib.rs and can either implement logic directly or delegate to ferro_ta_core.
To add a new indicator:
- Add a
#[wasm_bindgen]export insrc/lib.rs(prefer delegating toferro_ta_corewhere possible). - Add at least two
#[wasm_bindgen_test]tests covering output length and a known value. - Update this README table.
- Run
wasm-pack test --nodeto verify.
Prerequisites
# Install Rust (if not already present)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Install wasm-pack
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
# OR via cargo:
cargo install wasm-pack
Build
cd wasm/
wasm-pack build --target nodejs --out-dir pkg
This produces a pkg/ directory containing:
ferro_ta_wasm.js— JavaScript glue codeferro_ta_wasm_bg.wasm— compiled WebAssembly binaryferro_ta_wasm.d.ts— TypeScript declarations
For a browser build:
wasm-pack build --target web --out-dir pkg-web
Usage (Node.js)
const { sma, ema, wma, rsi, adx, mfi, bbands, atr, obv, macd } = require('./pkg/ferro_ta_wasm.js');
const close = new Float64Array([44.34, 44.09, 44.15, 43.61, 44.33, 44.83, 45.10]);
// Simple Moving Average (period 3)
const smaOut = sma(close, 3);
console.log('SMA:', Array.from(smaOut)); // [ NaN, NaN, 44.193, ... ]
// RSI (period 5)
const rsiOut = rsi(close, 5);
console.log('RSI:', Array.from(rsiOut));
// WMA (period 5)
const wmaOut = wma(close, 5);
console.log('WMA:', Array.from(wmaOut));
// Bollinger Bands (period 5, ±2σ) — returns [upper, middle, lower]
const [upper, middle, lower] = bbands(close, 5, 2.0, 2.0);
console.log('BBANDS upper:', Array.from(upper));
// MACD (fast=3, slow=5, signal=2) — returns [macd_line, signal_line, histogram]
const [macdLine, signalLine, histogram] = macd(close, 3, 5, 2);
console.log('MACD:', Array.from(macdLine));
console.log('Signal:', Array.from(signalLine));
console.log('Histogram:', Array.from(histogram));
// ATR (period 3)
const high = new Float64Array([45.0, 46.0, 47.0, 46.0, 45.0, 44.0, 45.0]);
const low = new Float64Array([43.0, 44.0, 45.0, 44.0, 43.0, 42.0, 43.0]);
const atrOut = atr(high, low, close, 3);
console.log('ATR:', Array.from(atrOut));
// ADX (period 3)
const adxOut = adx(high, low, close, 3);
console.log('ADX:', Array.from(adxOut));
// OBV
const volume = new Float64Array([1000, 1200, 900, 1500, 800, 600, 700]);
const obvOut = obv(close, volume);
console.log('OBV:', Array.from(obvOut));
// MFI (period 3)
const mfiOut = mfi(high, low, close, volume, 3);
console.log('MFI:', Array.from(mfiOut));
Usage (Browser)
<script type="module">
import init, { sma, macd } from './pkg-web/ferro_ta_wasm.js';
await init(); // loads the .wasm binary
const close = new Float64Array([44.34, 44.09, 44.15, 43.61, 44.33]);
const smaOut = sma(close, 3);
console.log('SMA:', Array.from(smaOut));
// MACD
const [macdLine, signal, hist] = macd(close, 3, 5, 2);
console.log('MACD line:', Array.from(macdLine));
</script>
Run Tests
cd wasm/
wasm-pack test --node
CI Artifact
Every CI run on main builds the WASM package and uploads it as a GitHub Actions
artifact named wasm-pkg. To download the latest pre-built package without building
from source:
- Go to the Actions tab.
- Open the latest successful CI run.
- Download the
wasm-pkgartifact from the Artifacts section. - Unzip and use
pkg/ferro_ta_wasm.jsdirectly in your project.
Limitations
- Only 12 indicators are currently exposed (SMA, EMA, WMA, BBANDS, RSI, ADX, MACD, MOM, STOCHF, ATR, OBV, MFI).
Additional indicators will be added following the same pattern in
src/lib.rs. - Large arrays (> 10M bars) may be slow due to JS↔WASM memory copies. For high-throughput use cases prefer the Python (PyO3) binding.
- WASM does not support multi-threading natively in browsers (SharedArrayBuffer requires COOP/COEP headers).