npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tradelab

v1.3.1

Published

Backtesting toolkit for Node.js with strategy simulation, historical data loading, and report generation

Downloads

162

Readme

npm version GitHub License: MIT Node.js TypeScript


A Node.js toolkit for testing, validating, and operating trading strategies, built so humans and AI agents work from the same primitives.

One signal() contract runs across research and execution:

  • run candle or tick backtests
  • model slippage, commissions, borrow, carry, and funding
  • validate parameters with walk-forward tests and research statistics
  • combine multiple systems into a shared-capital portfolio
  • move the same strategy into paper or live execution, single or multi-symbol
  • export reports, metrics, and trade ledgers

Agent-native. The tradelab-mcp server exposes 25 tools over stdio, so an AI agent can run the whole loop itself: pull data, run and score backtests, track hypotheses across runs with built-in overfitting guards, then open a paper or live session and place risk-sized bracket orders behind a kill-switch. Agents get the same depth a quant does, not a thin read-only wrapper. See docs/mcp.md.

npm install tradelab

Requires Node.js 18 or newer.

Quick Start

import { backtest, getHistoricalCandles, ema, exportBacktestArtifacts } from "tradelab";

const candles = await getHistoricalCandles({
  source: "yahoo",
  symbol: "SPY",
  interval: "1d",
  period: "2y",
  cache: true,
});

const result = backtest({
  candles,
  symbol: "SPY",
  interval: "1d",
  equity: 10_000,
  riskPct: 1,
  warmupBars: 50,
  costs: {
    slippageBps: 1,
    commissionBps: 0.5,
  },
  signal({ candles: history, bar }) {
    const closes = history.map((c) => c.close);
    const fast = ema(closes, 10);
    const slow = ema(closes, 30);
    const i = closes.length - 1;

    if (fast[i - 1] <= slow[i - 1] && fast[i] > slow[i]) {
      return { side: "long", stop: bar.close * 0.97, rr: 2 };
    }

    return null;
  },
});

console.log(result.metrics);
exportBacktestArtifacts({ result, outDir: "./output" });

Start with result.metrics for the summary and result.positions for completed trades. Use trades when you need every realized leg, including partial exits.

What You Can Build

| Goal | API or command | | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | Backtest one strategy | backtest({ candles, signal }) | | Backtest an async strategy | backtestAsync({ candles, signal }) | | Replay tick or quote data | backtestTicks({ ticks, signal }) | | Run several systems together | backtestPortfolio({ systems }) | | Test parameter stability | walkForwardOptimize(options) | | Run a parallel parameter sweep | optimize({ signalModulePath, parameterSets }) | | Use indicators | import { rsi, macd, vwap } from "tradelab/ta" | | Check overfitting risk | research.monteCarlo, research.deflatedSharpe | | Run in paper or live mode | LiveEngine, LiveOrchestrator, tradelab paper | | Trade multiple symbols in one session | SessionManager.create({ symbols: ["BTC","ETH"] }) with per-symbol pushBar and placeOrder | | Watch a live run locally | createDashboardServer({ source }) with equity curve, KPI strip, controls | | Get notified on fills or risk halts | attachNotifier(session, { onEvent, webhookUrl }) from tradelab/live | | Let MCP clients run research tools | tradelab-mcp with run_backtest, walk_forward, analyze_robustness, optimize_strategy, compare_strategies, candle_stats | | Let MCP agents trade (paper/live) | tradelab-mcp with create_session, feed_price, place_order, bracket orders, halt_all kill-switch (see docs/mcp.md) | | Track strategy research across runs | tradelab-mcp with research_open, research_log, research_recall, research_close (see docs/mcp.md) | | Summarize metrics in plain English | summarize(metrics) returns one plain-English paragraph | | Run a built-in preset from the CLI | tradelab run ema-cross --source yahoo --symbol SPY --period 1y | | Export reports and machine data | exportBacktestArtifacts, exportMetricsJSON |

The Signal Contract

Your strategy is a function. Return null to do nothing, or return a trade signal.

function signal({ candles, index, bar, equity, openPosition, pendingOrder }) {
  if (openPosition || index < 50) return null;

  return {
    side: "long",
    entry: bar.close, // optional; defaults to current close
    stop: bar.close - 2,
    rr: 2, // take profit at 2R
  };
}

Common signal fields:

| Field | Meaning | | --------------------------- | --------------------------------------------------- | | side | long, short, buy, or sell | | entry | Entry price. Defaults to the current close | | stop | Required stop level for sizing and risk | | takeProfit | Explicit target price | | rr | Builds target from risk when takeProfit is absent | | qty or size | Fixed size override | | riskPct or riskFraction | Per-trade risk override |

Data

Use getHistoricalCandles() for Yahoo Finance, CSV files, and cached datasets.

const yahoo = await getHistoricalCandles({
  source: "yahoo",
  symbol: "QQQ",
  interval: "1d",
  period: "1y",
  cache: true,
});

const csv = await getHistoricalCandles({
  source: "csv",
  csvPath: "./data/btc.csv",
});

Candles are normalized to:

{
  (time, open, high, low, close, volume);
}

Costs

Cost assumptions belong in the run, not in post-processing.

const result = backtest({
  candles,
  signal,
  costs: {
    slippageBps: 2,
    spreadBps: 1,
    commissionBps: 1,
    minCommission: 1,
    carry: {
      longAnnualBps: 500,
      shortAnnualBps: 800,
    },
    funding: {
      rateBps: 10,
      intervalMs: 8 * 60 * 60 * 1000,
      anchorMs: 0,
    },
  },
});

exit.financing is included on closed trades when carry or funding applies. It is already deducted from exit.pnl and aggregate metrics.

Validation

Use a normal backtest to build the strategy. Use validation tools before trusting it.

import { walkForwardOptimize, grid } from "tradelab";

const wf = walkForwardOptimize({
  candles,
  trainBars: 180,
  testBars: 60,
  mode: "anchored",
  scoreBy: "profitFactor",
  parameterSets: grid({
    fast: [8, 10, 12],
    slow: [21, 30, 50],
    rr: [1.5, 2, 3],
  }),
  signalFactory(params) {
    return createEmaSignal(params);
  },
});

console.log(wf.metrics);
console.log(wf.bestParamsSummary);

For larger sweeps, use optimize() with a strategy module:

const out = await optimize({
  candles,
  interval: "1d",
  signalModulePath: new URL("./strategy.js", import.meta.url).pathname,
  parameterSets: grid({ fast: [8, 10], slow: [30, 50] }),
  scoreBy: "sharpeAnnualized",
});

Portfolio Backtests

backtestPortfolio() runs multiple systems against shared capital. Capital is locked only when an order fills, so later systems size against what is still available.

const portfolio = backtestPortfolio({
  equity: 100_000,
  interval: "1d",
  maxDailyLossPct: 3,
  systems: [
    { symbol: "SPY", candles: spy, signal: spySignal, weight: 2 },
    { symbol: "QQQ", candles: qqq, signal: qqqSignal, weight: 1 },
  ],
});

Portfolio equity points include lockedCapital and availableCapital.

Live and Paper Runs

The live package uses the same signal shape as backtests.

import { LiveEngine, PaperEngine, JsonFileStorage } from "tradelab/live";

const engine = new LiveEngine({
  id: "aapl-1m",
  symbol: "AAPL",
  interval: "1m",
  mode: "polling",
  broker: new PaperEngine({ equity: 25_000 }),
  storage: new JsonFileStorage({ baseDir: "./output/live-state" }),
  signal,
});

await engine.start();

Run the same flow from the terminal:

tradelab paper --symbol AAPL --interval 1m --mode polling --once true
tradelab live --config ./live-portfolio.json --paper

Add --dashboard --dashboardPort 4317 to open a local Server-Sent Events dashboard.

MCP Server

tradelab-mcp exposes 25 tools over stdio to any MCP-capable agent (Claude Desktop, Cursor, and similar). They cover the full loop an agent needs to work a strategy end to end: research it, validate it, track the search, then trade it. See docs/mcp.md for the full tool reference and agent trading guide.

Research tools: list_strategies, fetch_candles, run_backtest, walk_forward, analyze_robustness, optimize_strategy, compare_strategies, candle_stats

Research loop tools: research_open, research_log, research_recall, research_close for persistent file-backed hypothesis tracking. run_backtest auto-logs when researchId is passed.

Agent trading tools (paper by default; live gated): create_session, list_sessions, session_status, feed_price, place_order, close_position, flatten, cancel_order, account, positions, recent_events, attach_strategy, halt_all

create_session accepts a symbols array for multi-symbol portfolio sessions. Pass symbol to feed_price and place_order to direct bars and orders to a specific instrument.

Paper trading needs no credentials. Live trading requires TRADELAB_ALLOW_LIVE=true and confirmLive: true plus a credentialed broker. halt_all is an emergency kill-switch that flattens all positions and stops every session.

Use it from any MCP client that can launch a stdio server:

{
  "mcpServers": {
    "tradelab": {
      "command": "npx",
      "args": ["-y", "tradelab", "tradelab-mcp"]
    }
  }
}

CLI

tradelab backtest --source yahoo --symbol SPY --interval 1d --period 1y
tradelab portfolio --csvPaths ./spy.csv,./qqq.csv --symbols SPY,QQQ
tradelab walk-forward --source yahoo --symbol QQQ --interval 1d --period 2y
tradelab run ema-cross --source yahoo --symbol SPY --period 1y
tradelab status --dir ./output/live-state

tradelab run <preset> runs a named built-in strategy on Yahoo or CSV data and prints a plain-English summary. Pass --params '{"fast":5,"slow":20}' to override defaults.

Documentation

Module Entry Points

import { backtest, getHistoricalCandles } from "tradelab";
import { rsi, macd, vwap } from "tradelab/ta";
import { LiveEngine, PaperEngine, TradingSession, SessionManager } from "tradelab/live";

CommonJS is supported for the main, data, live, and TA entry points:

const { backtest } = require("tradelab");

License

MIT