Files
ferro-ta/wasm/bench.js
T
Pratik Bhadane 53566b9d82 feat: expand rust parity, wasm exports, and api conformance
Move several hot Python analysis paths to Rust-backed helpers. This adds Rust implementations for backtest strategy signal generation and the core portfolio loop, options and futures payoff aggregation, Greeks aggregation, ratio calculation, trade extraction, chunked close-only indicator runs, and forward-fill helpers. Wire the Python analysis and data modules to prefer these paths, and add coverage for the new batch fast path.

Expand the WASM package to export WMA, ADX, and MFI from ferro_ta_core, refresh the Node examples, benchmarks, and README, and add a Node-vs-Python conformance test so the browser and node surface stays aligned with the main Python package.

Introduce a generated cross-surface API manifest in docs/, along with scripts to rebuild and verify it from source exports. Enforce manifest freshness in the Python and WASM CI workflows so release candidates catch surface drift before push.
2026-03-24 14:28:51 +05:30

119 lines
3.4 KiB
JavaScript

const fs = require("node:fs");
const path = require("node:path");
const { performance } = require("node:perf_hooks");
const wasm = require("./pkg/ferro_ta_wasm.js");
function parseArgs(argv) {
const args = { bars: 100000, json: null };
for (let idx = 0; idx < argv.length; idx += 1) {
const token = argv[idx];
if (token === "--bars") {
args.bars = Number(argv[idx + 1]);
idx += 1;
} else if (token === "--json") {
args.json = argv[idx + 1];
idx += 1;
}
}
return args;
}
function makeSeries(length) {
const close = new Float64Array(length);
const high = new Float64Array(length);
const low = new Float64Array(length);
const volume = new Float64Array(length);
let value = 100.0;
for (let idx = 0; idx < length; idx += 1) {
value += Math.sin(idx / 13.0) * 0.35 + Math.cos(idx / 29.0) * 0.18;
close[idx] = value;
high[idx] = value + 1.25;
low[idx] = value - 1.10;
volume[idx] = 1000.0 + Math.abs(Math.sin(idx / 7.0) * 300.0) + (idx % 100);
}
return { close, high, low, volume };
}
function timeMin(fn, rounds = 7) {
fn();
let best = Number.POSITIVE_INFINITY;
for (let round = 0; round < rounds; round += 1) {
const started = performance.now();
fn();
best = Math.min(best, performance.now() - started);
}
return best;
}
function runBenchmark({ bars }) {
const { close, high, low, volume } = makeSeries(bars);
const cases = [
["SMA", () => wasm.sma(close, 20)],
["EMA", () => wasm.ema(close, 20)],
["WMA", () => wasm.wma(close, 20)],
["RSI", () => wasm.rsi(close, 14)],
["ADX", () => wasm.adx(high, low, close, 14)],
["MFI", () => wasm.mfi(high, low, close, volume, 14)],
["ATR", () => wasm.atr(high, low, close, 14)],
["BBANDS", () => wasm.bbands(close, 20, 2.0, 2.0)],
];
const results = cases.map(([name, fn]) => {
const elapsedMs = timeMin(fn);
return {
indicator: name,
elapsed_ms: Number(elapsedMs.toFixed(4)),
ns_per_bar: Number(((elapsedMs * 1e6) / bars).toFixed(2)),
million_bars_per_second: Number((((bars / 1e6) / (elapsedMs / 1000))).toFixed(2)),
};
});
return {
metadata: {
suite: "wasm",
runtime: {
generated_at_utc: new Date().toISOString(),
node_version: process.version,
platform: process.platform,
arch: process.arch,
},
dataset: {
bars,
},
},
results,
};
}
function printResults(payload) {
const bars = payload.metadata.dataset.bars;
console.log(`WASM Benchmark: ${bars} bars`);
console.log("----------------------------------------------------------------");
console.log(
`${"Indicator".padEnd(12)}${"Elapsed (ms)".padStart(14)}${"ns/bar".padStart(12)}${"M bars/s".padStart(12)}`
);
console.log("----------------------------------------------------------------");
for (const row of payload.results) {
console.log(
`${row.indicator.padEnd(12)}${row.elapsed_ms.toFixed(2).padStart(14)}${row.ns_per_bar
.toFixed(2)
.padStart(12)}${row.million_bars_per_second.toFixed(2).padStart(12)}`
);
}
}
function main() {
const args = parseArgs(process.argv.slice(2));
const payload = runBenchmark(args);
printResults(payload);
if (args.json) {
const outputPath = path.resolve(args.json);
fs.writeFileSync(outputPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
console.log(`\nWrote JSON results to ${outputPath}`);
}
}
main();