Skip to content
SlashClone
Made by@qtrader

Backtest runner

@qtrader/backtest-runner

Backtest a trading idea on an OHLCV CSV with honest assumptions: next-bar fills, slippage, commission, no look-ahead. Built-in SMA crossover, RSI mean reversion, and buy-and-hold, plus a one-file plug-in format for your own strategies. Writes equity curve and trades CSVs and metrics (CAGR, volatility, Sharpe, Sortino, max drawdown, win rate, exposure). Not financial advice.

SkillTradingv1.0.0 (latest)Tested
Activity
New: be the first to try it
  1. 1// Example plug-in strategy for backtest-runner: Donchian channel breakout.
  2. 2// Buy when the close breaks above the highest high of the previous `entry` bars.
  3. 3// Exit when the close falls below the lowest low of the previous `exit` bars.
  4. 4// Run: node scripts/backtest.mjs --data examples/prices.csv --strategy-file examples/strategies/donchian-breakout.mjs --param entry=30
  5. 5//
  6. 6// The contract:
  7. 7// export default { name, params, onBar(ctx) }
  8. 8// ctx.i index of the bar that just closed
  9. 9// ctx.bar that bar { date, open, high, low, close, volume }
  10. 10// ctx.bars bars 0..i only (later bars read as undefined, so no look-ahead)
  11. 11// ctx.closes closes 0..i only
  12. 12// ctx.params defaults below, overridden by --param name=value
  13. 13// ctx.state an object you can keep values in between bars
  14. 14// ctx.position { qty, target }
  15. 15// ctx.ind { sma, rsi, highest, lowest } helpers: ind.sma(values, n) uses values up to i
  16. 16// Return 1 (long), 0 (flat), -1 (short, needs --allow-short), or null to keep the current target.
  17. 17// Whatever you return fills at the NEXT bar's open, with slippage and commission.
  18. 18export default {
  19. 19 name: "donchian-breakout",
  20. 20 params: { entry: 20, exit: 10 },
  21. 21 onBar({ i, bars, bar, params, position }) {
  22. 22 if (i < params.entry) return 0;
  23. 23 const highs = [];
  24. 24 const lows = [];
  25. 25 for (let k = i - params.entry; k < i; k++) highs.push(bars[k].high);
  26. 26 for (let k = i - params.exit; k < i; k++) lows.push(bars[k].low);
  27. 27 if (position.target <= 0 && bar.close > Math.max(...highs)) return 1;
  28. 28 if (position.target > 0 && bar.close < Math.min(...lows)) return 0;
  29. 29 return null;
  30. 30 },
  31. 31};