const fs = require("node:fs"); const path = require("node:path"); const { performance } = require("node:perf_hooks"); const wasm = require("./node/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();