Spaces:
Running
Running
| <html class="default" lang="en" data-base=".."><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>cli/readme | backtest-kit</title><meta name="description" content="Documentation for backtest-kit"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script><script async src="../assets/hierarchy.js" id="tsd-hierarchy-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><div class="table-cell" id="tsd-search"><div class="field"><label for="tsd-search-field" class="tsd-widget tsd-toolbar-icon search no-caption"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></label><input type="text" id="tsd-search-field" aria-label="Search"/></div><div class="field"><div id="tsd-toolbar-links"></div></div><ul class="results"><li class="state loading">Preparing search index...</li><li class="state failure">The search index is not available</li></ul><a href="../index.html" class="title">backtest-kit</a></div><div class="table-cell" id="tsd-widgets"><a href="#" class="tsd-widget tsd-toolbar-icon menu no-caption" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb"><li><a href="../modules.html">backtest-kit</a></li><li><a href="cli_readme.html">cli/readme</a></li></ul></div><div class="tsd-panel tsd-typography"><img src="https://github.com/tripolskypetr/backtest-kit/raw/refs/heads/master/assets/square_compasses.svg" height="45px" align="right"> | |
| <a id="📟-backtest-kitcli" class="tsd-anchor"></a><h1 class="tsd-anchor-link">📟 @backtest-kit/cli<a href="#📟-backtest-kitcli" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h1><blockquote> | |
| <p>Zero-boilerplate CLI for <a href="https://www.npmjs.com/package/backtest-kit">backtest-kit</a>. Point it at a strategy file, pick a mode, and it handles exchange connectivity, candle caching, the web dashboard, Telegram alerts, and graceful shutdown for you — no setup code.</p> | |
| </blockquote> | |
| <p><img src="https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot16.png" alt="screenshot"></p> | |
| <p><a href="https://deepwiki.com/tripolskypetr/backtest-kit"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a> | |
| <a href="https://npmjs.org/package/@backtest-kit/cli"><img src="https://img.shields.io/npm/v/@backtest-kit/cli.svg?style=flat-square" alt="npm"></a> | |
| <a href=""><img src="https://img.shields.io/badge/TypeScript-5.0+-blue" alt="TypeScript"></a></p> | |
| <p>📚 <strong><a href="https://backtest-kit.github.io/documents/article_07_ai_news_trading_signals.html">Docs</a></strong> · 🌟 <strong><a href="https://github.com/tripolskypetr/backtest-kit/tree/master/example">Reference implementation</a></strong> · 🐙 <strong><a href="https://github.com/tripolskypetr/backtest-kit">GitHub</a></strong></p> | |
| <blockquote> | |
| <p><strong>New here?</strong> The fastest real setup is to clone the <a href="https://github.com/tripolskypetr/backtest-kit/tree/master/example">reference implementation</a> — a working news-sentiment AI trading system with LLM forecasting, multi-timeframe data, and a documented February 2026 backtest. Start there, not from scratch.</p> | |
| </blockquote> | |
| <hr> | |
| <a id="🚀-quick-start" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🚀 Quick Start<a href="#🚀-quick-start" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><pre><code class="bash"><span class="hl-0"># Scaffold a project (boilerplate stays inside the CLI; docs auto-fetched)</span><br/><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/cli</span><span class="hl-2"> </span><span class="hl-4">--init</span><span class="hl-2"> </span><span class="hl-4">--output</span><span class="hl-2"> </span><span class="hl-3">backtest-kit-project</span><br/><span class="hl-1">cd</span><span class="hl-2"> </span><span class="hl-3">backtest-kit-project</span><span class="hl-2"> && </span><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">install</span><span class="hl-2"> && </span><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">start</span><span class="hl-2"> </span><span class="hl-4">--</span><span class="hl-2"> </span><span class="hl-4">--help</span> | |
| </code><button type="button">Copy</button></pre> | |
| <p>The whole onboarding is: write a strategy file that registers schemas via <code>backtest-kit</code>, point the CLI at it, choose a flag.</p> | |
| <pre><code class="bash"><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/cli</span><span class="hl-2"> </span><span class="hl-4">--backtest</span><span class="hl-2"> </span><span class="hl-3">./content/feb_2026.strategy/index.ts</span><span class="hl-2"> </span><span class="hl-4">--symbol</span><span class="hl-2"> </span><span class="hl-3">BTCUSDT</span> | |
| </code><button type="button">Copy</button></pre> | |
| <details> | |
| <summary>The strategy entry point (the CLI is only the runner)</summary> | |
| <pre><code class="javascript"><span class="hl-0">// src/index.mjs — registers schemas via backtest-kit; @backtest-kit/cli just runs it</span><br/><span class="hl-6">import</span><span class="hl-2"> { </span><span class="hl-5">addStrategySchema</span><span class="hl-2">, </span><span class="hl-5">addExchangeSchema</span><span class="hl-2">, </span><span class="hl-5">addFrameSchema</span><span class="hl-2"> } </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">'backtest-kit'</span><span class="hl-2">;</span><br/><span class="hl-6">import</span><span class="hl-2"> </span><span class="hl-5">ccxt</span><span class="hl-2"> </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">'ccxt'</span><span class="hl-2">;</span><br/><br/><span class="hl-1">addExchangeSchema</span><span class="hl-2">({</span><br/><span class="hl-2"> </span><span class="hl-5">exchangeName:</span><span class="hl-2"> </span><span class="hl-3">'binance'</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-1">getCandles</span><span class="hl-5">:</span><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> (</span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">interval</span><span class="hl-2">, </span><span class="hl-5">since</span><span class="hl-2">, </span><span class="hl-5">limit</span><span class="hl-2">) </span><span class="hl-4">=></span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-7">exchange</span><span class="hl-2"> = </span><span class="hl-4">new</span><span class="hl-2"> </span><span class="hl-5">ccxt</span><span class="hl-2">.</span><span class="hl-1">binance</span><span class="hl-2">();</span><br/><span class="hl-2"> </span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-7">ohlcv</span><span class="hl-2"> = </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-5">exchange</span><span class="hl-2">.</span><span class="hl-1">fetchOHLCV</span><span class="hl-2">(</span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">interval</span><span class="hl-2">, </span><span class="hl-5">since</span><span class="hl-2">.</span><span class="hl-1">getTime</span><span class="hl-2">(), </span><span class="hl-5">limit</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-6">return</span><span class="hl-2"> </span><span class="hl-5">ohlcv</span><span class="hl-2">.</span><span class="hl-1">map</span><span class="hl-2">(([</span><span class="hl-5">timestamp</span><span class="hl-2">, </span><span class="hl-5">open</span><span class="hl-2">, </span><span class="hl-5">high</span><span class="hl-2">, </span><span class="hl-5">low</span><span class="hl-2">, </span><span class="hl-5">close</span><span class="hl-2">, </span><span class="hl-5">volume</span><span class="hl-2">]) </span><span class="hl-4">=></span><br/><span class="hl-2"> ({ </span><span class="hl-5">timestamp</span><span class="hl-2">, </span><span class="hl-5">open</span><span class="hl-2">, </span><span class="hl-5">high</span><span class="hl-2">, </span><span class="hl-5">low</span><span class="hl-2">, </span><span class="hl-5">close</span><span class="hl-2">, </span><span class="hl-5">volume</span><span class="hl-2"> }));</span><br/><span class="hl-2"> },</span><br/><span class="hl-2"> </span><span class="hl-1">formatPrice</span><span class="hl-5">:</span><span class="hl-2"> (</span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">price</span><span class="hl-2">) </span><span class="hl-4">=></span><span class="hl-2"> </span><span class="hl-5">price</span><span class="hl-2">.</span><span class="hl-1">toFixed</span><span class="hl-2">(</span><span class="hl-8">2</span><span class="hl-2">),</span><br/><span class="hl-2"> </span><span class="hl-1">formatQuantity</span><span class="hl-5">:</span><span class="hl-2"> (</span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">quantity</span><span class="hl-2">) </span><span class="hl-4">=></span><span class="hl-2"> </span><span class="hl-5">quantity</span><span class="hl-2">.</span><span class="hl-1">toFixed</span><span class="hl-2">(</span><span class="hl-8">8</span><span class="hl-2">),</span><br/><span class="hl-2">});</span><br/><br/><span class="hl-1">addFrameSchema</span><span class="hl-2">({ </span><span class="hl-5">frameName:</span><span class="hl-2"> </span><span class="hl-3">'feb-2024'</span><span class="hl-2">, </span><span class="hl-5">interval:</span><span class="hl-2"> </span><span class="hl-3">'1m'</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-5">startDate:</span><span class="hl-2"> </span><span class="hl-4">new</span><span class="hl-2"> </span><span class="hl-1">Date</span><span class="hl-2">(</span><span class="hl-3">'2024-02-01'</span><span class="hl-2">), </span><span class="hl-5">endDate:</span><span class="hl-2"> </span><span class="hl-4">new</span><span class="hl-2"> </span><span class="hl-1">Date</span><span class="hl-2">(</span><span class="hl-3">'2024-02-29'</span><span class="hl-2">) });</span><br/><br/><span class="hl-1">addStrategySchema</span><span class="hl-2">({ </span><span class="hl-5">strategyName:</span><span class="hl-2"> </span><span class="hl-3">'my-strategy'</span><span class="hl-2">, </span><span class="hl-5">interval:</span><span class="hl-2"> </span><span class="hl-3">'15m'</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-1">getSignal</span><span class="hl-5">:</span><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> (</span><span class="hl-5">symbol</span><span class="hl-2">) </span><span class="hl-4">=></span><span class="hl-2"> </span><span class="hl-4">null</span><span class="hl-2"> }); </span><span class="hl-0">// return a signal or null</span> | |
| </code><button type="button">Copy</button></pre> | |
| <p>Wire it into <code>package.json</code> once and the positional path never changes:</p> | |
| <pre><code class="json"><span class="hl-2">{</span><br/><span class="hl-2"> </span><span class="hl-16">"scripts"</span><span class="hl-2">: {</span><br/><span class="hl-2"> </span><span class="hl-16">"backtest"</span><span class="hl-2">: </span><span class="hl-3">"npx @backtest-kit/cli --backtest ./src/index.mjs"</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-16">"paper"</span><span class="hl-2">: </span><span class="hl-3">"npx @backtest-kit/cli --paper ./src/index.mjs"</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-16">"start"</span><span class="hl-2">: </span><span class="hl-3">"npx @backtest-kit/cli --live ./src/index.mjs"</span><br/><span class="hl-2"> },</span><br/><span class="hl-2"> </span><span class="hl-16">"dependencies"</span><span class="hl-2">: { </span><span class="hl-16">"@backtest-kit/cli"</span><span class="hl-2">: </span><span class="hl-3">"latest"</span><span class="hl-2">, </span><span class="hl-16">"backtest-kit"</span><span class="hl-2">: </span><span class="hl-3">"latest"</span><span class="hl-2">, </span><span class="hl-16">"ccxt"</span><span class="hl-2">: </span><span class="hl-3">"latest"</span><span class="hl-2"> }</span><br/><span class="hl-2">}</span> | |
| </code><button type="button">Copy</button></pre> | |
| <pre><code class="bash"><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">run</span><span class="hl-2"> </span><span class="hl-3">backtest</span><span class="hl-2"> </span><span class="hl-4">--</span><span class="hl-2"> </span><span class="hl-4">--symbol</span><span class="hl-2"> </span><span class="hl-3">BTCUSDT</span><span class="hl-2"> </span><span class="hl-4">--ui</span><span class="hl-2"> </span><span class="hl-4">--telegram</span><span class="hl-2"> </span><span class="hl-0"># add integrations with flags</span> | |
| </code><button type="button">Copy</button></pre> | |
| </details> | |
| <hr> | |
| <a id="🤔-philosophy--react-vs-nextjs-but-for-trading" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🤔 Philosophy — React vs Next.js, but for trading<a href="#🤔-philosophy--react-vs-nextjs-but-for-trading" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p><code>@backtest-kit/cli</code> does <strong>two things well with one tool</strong>.</p> | |
| <p><strong>1. The lightest runner for a solo quant on day one.</strong> Write a strategy, point the CLI at it, you're trading. No DI container to learn, no scaffold to fight, no infra to copy-paste. The day you have an idea you can backtest it; the week you have an edge you can paper-trade it; the month you have a P&L you can run it live — same CLI, different flag.</p> | |
| <p><strong>2. A monorepo-grade runner for when the business takes off.</strong> The moment you start making money is the worst moment to rewrite your stack. So the CLI is monorepo-ready from day one even if you don't use it that way at first: per-strategy <code>.env</code>, per-strategy broker modules, folder-based import aliases, isolated dump dirs. The tool you backtested your first idea with is the tool that runs a desk of strategies in production — no rewrite, no language switch, only more files.</p> | |
| <hr> | |
| <a id="🗺️-mode-matrix" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🗺️ Mode matrix<a href="#🗺️-mode-matrix" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Every invocation is <strong>one mode</strong> (a primary flag) + a positional strategy/entry path + optional modifiers. <code>--ui</code> and <code>--telegram</code> are integrations that attach to any trading mode.</p> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Mode</th> | |
| <th>Flag</th> | |
| <th>What it does</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><strong>Backtest</strong></td> | |
| <td><code>--backtest</code></td> | |
| <td>Run a strategy on historical candle data (uses a <code>FrameSchema</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Paper</strong></td> | |
| <td><code>--paper</code></td> | |
| <td>Live prices, no real orders — identical code path to live</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Live</strong></td> | |
| <td><code>--live</code></td> | |
| <td>Real trades via exchange API</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Walker</strong></td> | |
| <td><code>--walker</code></td> | |
| <td>A/B-compare multiple strategies on the same history, ranked report</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Main</strong></td> | |
| <td><code>--main</code></td> | |
| <td>Run a custom entry point with the full environment prepared, <strong>no</strong> trading harness</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Pine</strong></td> | |
| <td><code>--pine</code></td> | |
| <td>Run a local <code>.pine</code> indicator against exchange data</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Editor</strong></td> | |
| <td><code>--editor</code></td> | |
| <td>Open the visual Pine Script editor in the browser</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Candle Dump</strong></td> | |
| <td><code>--dump</code></td> | |
| <td>Fetch & save raw OHLCV candles to a file</td> | |
| </tr> | |
| <tr> | |
| <td><strong>PnL Debug</strong></td> | |
| <td><code>--pnldebug</code></td> | |
| <td>Simulate per-minute PnL for a given entry price & direction</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Broker Debug</strong></td> | |
| <td><code>--brokerdebug</code></td> | |
| <td>Fire a single broker commit against the live adapter</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Flush</strong></td> | |
| <td><code>--flush</code></td> | |
| <td>Delete report/log/markdown/agent folders from a strategy dump dir</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Init</strong></td> | |
| <td><code>--init</code></td> | |
| <td>Scaffold a new project</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Docker</strong></td> | |
| <td><code>--docker</code></td> | |
| <td>Scaffold a self-contained Docker workspace</td> | |
| </tr> | |
| <tr> | |
| <td><em>modifiers</em></td> | |
| <td><code>--ui</code> · <code>--telegram</code> · <code>--entry</code></td> | |
| <td>Web dashboard · Telegram alerts · fan out one strategy across many symbols</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <details> | |
| <summary>Complete core flag reference</summary> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Flag</th> | |
| <th>Type</th> | |
| <th>Description</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>--backtest</code></td> | |
| <td>boolean</td> | |
| <td>Run historical backtest (default <code>false</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--walker</code></td> | |
| <td>boolean</td> | |
| <td>Run Walker A/B comparison (default <code>false</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--paper</code></td> | |
| <td>boolean</td> | |
| <td>Paper trading — live prices, no orders (default <code>false</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--live</code></td> | |
| <td>boolean</td> | |
| <td>Run live trading (default <code>false</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--main</code></td> | |
| <td>boolean</td> | |
| <td>Custom entry point, no trading harness (default <code>false</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--ui</code></td> | |
| <td>boolean</td> | |
| <td>Start web UI dashboard (default <code>false</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--telegram</code></td> | |
| <td>boolean</td> | |
| <td>Enable Telegram notifications (default <code>false</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--verbose</code></td> | |
| <td>boolean</td> | |
| <td>Log each candle fetch (default <code>false</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--noCache</code></td> | |
| <td>boolean</td> | |
| <td>Skip candle cache warming before backtest (default <code>false</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--noFlush</code></td> | |
| <td>boolean</td> | |
| <td>Skip removing report/log/markdown/agent folders before run (default <code>false</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--symbol</code></td> | |
| <td>string</td> | |
| <td>Trading pair (default <code>"BTCUSDT"</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--strategy</code></td> | |
| <td>string</td> | |
| <td>Strategy name (default: first registered)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--exchange</code></td> | |
| <td>string</td> | |
| <td>Exchange name (default: first registered)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--frame</code></td> | |
| <td>string</td> | |
| <td>Backtest frame name (default: first registered)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--cacheInterval</code></td> | |
| <td>string</td> | |
| <td>Intervals to pre-cache (default <code>"1m, 15m, 30m, 4h"</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--brokerdebug</code></td> | |
| <td>boolean</td> | |
| <td>Fire a single broker commit against the live adapter (default <code>false</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--commit</code></td> | |
| <td>string</td> | |
| <td>Commit type for <code>--brokerdebug</code> (default <code>"signal-open"</code>)</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <p><strong>Positional argument (required):</strong> path to your strategy entry point file — set once in <code>package.json</code> scripts. Tool-specific flags (<code>--pine</code>, <code>--dump</code>, <code>--pnldebug</code>, <code>--docker</code>, …) are documented in their sections below.</p> | |
| </details> | |
| <hr> | |
| <a id="📈-trading-modes" class="tsd-anchor"></a><h2 class="tsd-anchor-link">📈 Trading modes<a href="#📈-trading-modes" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>The four modes that actually run strategies share one engine and one set of guarantees — only the clock and the order routing differ.</p> | |
| <a id="backtest-·-paper-·-live" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Backtest · Paper · Live<a href="#backtest-·-paper-·-live" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><details> | |
| <summary>How each behaves</summary> | |
| <p><strong>Backtest</strong> (<code>--backtest</code>) — runs against historical candles via a registered <code>FrameSchema</code>. Before running, the CLI removes the <code>report</code>, <code>log</code>, <code>markdown</code>, and <code>agent</code> folders from the strategy's <code>dump/</code> dir, then warms the candle cache for every interval in <code>--cacheInterval</code>; subsequent runs reuse the cache with no API calls. <code>--noCache</code> skips warming, <code>--noFlush</code> keeps output folders.</p> | |
| <pre><code class="json"><span class="hl-2">{ </span><span class="hl-16">"scripts"</span><span class="hl-2">: { </span><span class="hl-16">"backtest"</span><span class="hl-2">: </span><span class="hl-3">"npx @backtest-kit/cli --backtest --symbol ETHUSDT --strategy my-strategy --exchange binance --frame feb-2024 --cacheInterval </span><span class="hl-13">\"</span><span class="hl-3">1m, 15m, 1h, 4h</span><span class="hl-13">\"</span><span class="hl-3"> ./src/index.mjs"</span><span class="hl-2"> } }</span> | |
| </code><button type="button">Copy</button></pre> | |
| <p><strong>Paper</strong> (<code>--paper</code>) — connects to the live exchange but places no real orders. <strong>Identical code path to live</strong> — the safe way to validate a strategy.</p> | |
| <pre><code class="json"><span class="hl-2">{ </span><span class="hl-16">"scripts"</span><span class="hl-2">: { </span><span class="hl-16">"paper"</span><span class="hl-2">: </span><span class="hl-3">"npx @backtest-kit/cli --paper --symbol BTCUSDT ./src/index.mjs"</span><span class="hl-2"> } }</span> | |
| </code><button type="button">Copy</button></pre> | |
| <p><strong>Live</strong> (<code>--live</code>) — deploys a real bot. Requires exchange API keys in <code>.env</code>. Combine with <code>--ui --telegram</code> for a monitored deployment.</p> | |
| <pre><code class="json"><span class="hl-2">{ </span><span class="hl-16">"scripts"</span><span class="hl-2">: { </span><span class="hl-16">"start"</span><span class="hl-2">: </span><span class="hl-3">"npx @backtest-kit/cli --live --ui --telegram --symbol BTCUSDT ./src/index.mjs"</span><span class="hl-2"> } }</span> | |
| </code><button type="button">Copy</button></pre> | |
| </details> | |
| <a id="walker--ab-strategy-comparison" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Walker — A/B strategy comparison<a href="#walker--ab-strategy-comparison" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Runs the same historical period against multiple strategy files and prints a ranked report. Use it to pick the best variant before deploying.</p> | |
| <pre><code class="bash"><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/cli</span><span class="hl-2"> </span><span class="hl-4">--walker</span><span class="hl-2"> </span><span class="hl-4">--symbol</span><span class="hl-2"> </span><span class="hl-3">BTCUSDT</span><span class="hl-2"> </span><span class="hl-4">--noCache</span><span class="hl-2"> </span><span class="hl-4">--markdown</span><span class="hl-2"> </span><span class="hl-4">--output</span><span class="hl-2"> </span><span class="hl-3">feb_2026_comparison</span><span class="hl-2"> </span><span class="hl-13">\</span><br/><span class="hl-2"> </span><span class="hl-3">./content/feb_2026_v1.strategy.ts</span><span class="hl-2"> </span><span class="hl-3">./content/feb_2026_v2.strategy.ts</span><span class="hl-2"> </span><span class="hl-3">./content/feb_2026_v3.strategy.ts</span><br/><span class="hl-0"># → ./dump/feb_2026_comparison.md</span> | |
| </code><button type="button">Copy</button></pre> | |
| <details> | |
| <summary>Walker flags, output modes & behavior</summary> | |
| <p>Each positional argument is a separate strategy entry point. Before loading them the CLI removes the <code>report</code>/<code>log</code>/<code>markdown</code>/<code>agent</code> folders from each entry point's <code>dump/</code> (skip with <code>--noFlush</code>). All files load <strong>without changing <code>process.cwd()</code></strong> — <code>.env</code> is read from the working directory only. After loading, <code>addWalkerSchema</code> is called automatically using the exchange and frame registered by the strategy files. If no frame is registered, the CLI falls back to the last 31 days from <code>Date.now()</code> with a warning.</p> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Flag</th> | |
| <th>Type</th> | |
| <th>Description</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>--walker</code></td> | |
| <td>boolean</td> | |
| <td>Enable Walker comparison</td> | |
| </tr> | |
| <tr> | |
| <td><code>--symbol</code></td> | |
| <td>string</td> | |
| <td>Trading pair (default <code>"BTCUSDT"</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--cacheInterval</code></td> | |
| <td>string</td> | |
| <td>Intervals to pre-cache (default <code>"1m, 15m, 30m, 4h"</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--noCache</code></td> | |
| <td>boolean</td> | |
| <td>Skip candle cache warming</td> | |
| </tr> | |
| <tr> | |
| <td><code>--noFlush</code></td> | |
| <td>boolean</td> | |
| <td>Skip removing output folders before the run</td> | |
| </tr> | |
| <tr> | |
| <td><code>--verbose</code></td> | |
| <td>boolean</td> | |
| <td>Log each candle fetch and strategy progress</td> | |
| </tr> | |
| <tr> | |
| <td><code>--output</code></td> | |
| <td>string</td> | |
| <td>Output file base name (default <code>walker_{SYMBOL}_{TIMESTAMP}</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--json</code></td> | |
| <td>boolean</td> | |
| <td>Save <code>Walker.getData()</code> as JSON to <code>./dump/<output>.json</code> and exit</td> | |
| </tr> | |
| <tr> | |
| <td><code>--markdown</code></td> | |
| <td>boolean</td> | |
| <td>Save <code>Walker.getReport()</code> as <code>./dump/<output>.md</code> and exit</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <p><strong>Output:</strong> no flag → print Markdown report to stdout; <code>--json</code> / <code>--markdown</code> → save and exit. <strong>Module hook:</strong> <code>./modules/walker.module</code> loads automatically before the comparison (<code>.ts</code>/<code>.mjs</code>/<code>.cjs</code> tried in order).</p> | |
| </details> | |
| <a id="main--custom-entry-point-no-trading-harness" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Main — custom entry point, no trading harness<a href="#main--custom-entry-point-no-trading-harness" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Runs a single entry point with the full CLI environment prepared (<code>.env</code>, <code>config/setup.config</code>, <code>config/loader.config</code>, <code>./modules/main.module</code>, cwd changed to the entry-point folder, graceful shutdown wired) — but <strong>never</strong> starts a trading harness. Use it to bootstrap the environment for a quick action, e.g. calling a 3rd-party API with automatic <code>.env</code> import.</p> | |
| <details> | |
| <summary>Main behavior & flags</summary> | |
| <p>Unlike the trading modes it does not call <code>Backtest/Live/Walker.background</code>, pick a symbol, warm the cache, or resolve a strategy/exchange/frame — the entry point decides what to run. Exactly <strong>one</strong> positional entry point is required (<code>Entry point is required</code> otherwise). <code>process.cwd()</code> changes to the entry-point directory and its local <code>.env</code> overrides the root <code>.env</code>.</p> | |
| <p>Although the CLI starts nothing itself, any <code>Backtest</code>/<code>Live</code>/<code>Walker</code> run <strong>your</strong> entry point launches is still managed: the process exits once <code>listenDone*</code> reports completion, the first <code>Ctrl+C</code> stops every active run via <code>*.list()</code>/<code>*.stop()</code>, a second force-quits. <code>./modules/main.module</code> loads automatically before the entry point.</p> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Flag</th> | |
| <th>Type</th> | |
| <th>Description</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>--main</code></td> | |
| <td>boolean</td> | |
| <td>Enable Main mode</td> | |
| </tr> | |
| <tr> | |
| <td><code>--noFlush</code></td> | |
| <td>boolean</td> | |
| <td>Skip removing output folders before the run</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <pre><code class="json"><span class="hl-2">{ </span><span class="hl-16">"scripts"</span><span class="hl-2">: { </span><span class="hl-16">"main"</span><span class="hl-2">: </span><span class="hl-3">"npx @backtest-kit/cli --main ./tools/fetch_fear_and_greed.ts"</span><span class="hl-2"> } }</span> | |
| </code><button type="button">Copy</button></pre> | |
| </details> | |
| <a id="parallel-multi-symbol---entry" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Parallel multi-symbol (<code>--entry</code>)<a href="#parallel-multi-symbol---entry" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><blockquote> | |
| <p><strong>Power-user modifier — skip unless needed.</strong> The standard flow runs one symbol from <code>--symbol</code>. Use <code>--entry</code> to fan one strategy out across many symbols at once, or to drive <code>*.background()</code> from a UI / DB / API.</p> | |
| </blockquote> | |
| <p><code>--entry</code> is a modifier — combine it with exactly one of <code>--backtest</code>/<code>--live</code>/<code>--paper</code>/<code>--walker</code>, plus one positional entry file. The CLI does only the boilerplate (<code>Setup</code>, providers, the matching <code>./modules/<mode>.module</code>, SIGINT that stops every active run, <code>shutdown()</code> once <code>listenDone*</code> reports all runs complete); <strong>you</strong> pick the symbol set, warm cache, and call <code>*.background()</code>.</p> | |
| <details> | |
| <summary>Example — backtest one strategy across five symbols</summary> | |
| <pre><code class="javascript"><span class="hl-0">// src/multi-symbol.mjs</span><br/><span class="hl-6">import</span><span class="hl-2"> { </span><span class="hl-5">addExchangeSchema</span><span class="hl-2">, </span><span class="hl-5">addFrameSchema</span><span class="hl-2">, </span><span class="hl-5">addStrategySchema</span><span class="hl-2">, </span><span class="hl-5">Backtest</span><span class="hl-2">, </span><span class="hl-5">warmCandles</span><span class="hl-2"> } </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">"backtest-kit"</span><span class="hl-2">;</span><br/><span class="hl-6">import</span><span class="hl-2"> </span><span class="hl-5">ccxt</span><span class="hl-2"> </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">"ccxt"</span><span class="hl-2">;</span><br/><br/><span class="hl-1">addExchangeSchema</span><span class="hl-2">({ </span><span class="hl-5">exchangeName:</span><span class="hl-2"> </span><span class="hl-3">"binance"</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-1">getCandles</span><span class="hl-5">:</span><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> (</span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">interval</span><span class="hl-2">, </span><span class="hl-5">since</span><span class="hl-2">, </span><span class="hl-5">limit</span><span class="hl-2">) </span><span class="hl-4">=></span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-7">exchange</span><span class="hl-2"> = </span><span class="hl-4">new</span><span class="hl-2"> </span><span class="hl-5">ccxt</span><span class="hl-2">.</span><span class="hl-1">binance</span><span class="hl-2">();</span><br/><span class="hl-2"> </span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-7">ohlcv</span><span class="hl-2"> = </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-5">exchange</span><span class="hl-2">.</span><span class="hl-1">fetchOHLCV</span><span class="hl-2">(</span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">interval</span><span class="hl-2">, </span><span class="hl-5">since</span><span class="hl-2">.</span><span class="hl-1">getTime</span><span class="hl-2">(), </span><span class="hl-5">limit</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-6">return</span><span class="hl-2"> </span><span class="hl-5">ohlcv</span><span class="hl-2">.</span><span class="hl-1">map</span><span class="hl-2">(([</span><span class="hl-5">timestamp</span><span class="hl-2">, </span><span class="hl-5">open</span><span class="hl-2">, </span><span class="hl-5">high</span><span class="hl-2">, </span><span class="hl-5">low</span><span class="hl-2">, </span><span class="hl-5">close</span><span class="hl-2">, </span><span class="hl-5">volume</span><span class="hl-2">]) </span><span class="hl-4">=></span><br/><span class="hl-2"> ({ </span><span class="hl-5">timestamp</span><span class="hl-2">, </span><span class="hl-5">open</span><span class="hl-2">, </span><span class="hl-5">high</span><span class="hl-2">, </span><span class="hl-5">low</span><span class="hl-2">, </span><span class="hl-5">close</span><span class="hl-2">, </span><span class="hl-5">volume</span><span class="hl-2"> }));</span><br/><span class="hl-2"> },</span><br/><span class="hl-2"> </span><span class="hl-1">formatPrice</span><span class="hl-5">:</span><span class="hl-2"> (</span><span class="hl-5">s</span><span class="hl-2">, </span><span class="hl-5">p</span><span class="hl-2">) </span><span class="hl-4">=></span><span class="hl-2"> </span><span class="hl-5">p</span><span class="hl-2">.</span><span class="hl-1">toFixed</span><span class="hl-2">(</span><span class="hl-8">2</span><span class="hl-2">), </span><span class="hl-1">formatQuantity</span><span class="hl-5">:</span><span class="hl-2"> (</span><span class="hl-5">s</span><span class="hl-2">, </span><span class="hl-5">q</span><span class="hl-2">) </span><span class="hl-4">=></span><span class="hl-2"> </span><span class="hl-5">q</span><span class="hl-2">.</span><span class="hl-1">toFixed</span><span class="hl-2">(</span><span class="hl-8">8</span><span class="hl-2">) });</span><br/><br/><span class="hl-1">addFrameSchema</span><span class="hl-2">({ </span><span class="hl-5">frameName:</span><span class="hl-2"> </span><span class="hl-3">"feb-2026"</span><span class="hl-2">, </span><span class="hl-5">interval:</span><span class="hl-2"> </span><span class="hl-3">"1m"</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-5">startDate:</span><span class="hl-2"> </span><span class="hl-4">new</span><span class="hl-2"> </span><span class="hl-1">Date</span><span class="hl-2">(</span><span class="hl-3">"2026-02-01"</span><span class="hl-2">), </span><span class="hl-5">endDate:</span><span class="hl-2"> </span><span class="hl-4">new</span><span class="hl-2"> </span><span class="hl-1">Date</span><span class="hl-2">(</span><span class="hl-3">"2026-02-28"</span><span class="hl-2">) });</span><br/><span class="hl-1">addStrategySchema</span><span class="hl-2">({ </span><span class="hl-5">strategyName:</span><span class="hl-2"> </span><span class="hl-3">"my-strategy"</span><span class="hl-2">, </span><span class="hl-5">interval:</span><span class="hl-2"> </span><span class="hl-3">"15m"</span><span class="hl-2">, </span><span class="hl-1">getSignal</span><span class="hl-5">:</span><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> () </span><span class="hl-4">=></span><span class="hl-2"> </span><span class="hl-4">null</span><span class="hl-2"> });</span><br/><br/><span class="hl-0">// Decide the symbol set yourself — UI, database, API, or a list.</span><br/><span class="hl-6">for</span><span class="hl-2"> (</span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-7">symbol</span><span class="hl-2"> </span><span class="hl-4">of</span><span class="hl-2"> [</span><span class="hl-3">"BTCUSDT"</span><span class="hl-2">, </span><span class="hl-3">"ETHUSDT"</span><span class="hl-2">, </span><span class="hl-3">"SOLUSDT"</span><span class="hl-2">, </span><span class="hl-3">"BNBUSDT"</span><span class="hl-2">, </span><span class="hl-3">"XRPUSDT"</span><span class="hl-2">]) {</span><br/><span class="hl-2"> </span><span class="hl-0">// optional: await warmCandles({ exchangeName: "binance", interval: "1m", symbol, from, to });</span><br/><span class="hl-2"> </span><span class="hl-5">Backtest</span><span class="hl-2">.</span><span class="hl-1">background</span><span class="hl-2">(</span><span class="hl-5">symbol</span><span class="hl-2">, { </span><span class="hl-5">strategyName:</span><span class="hl-2"> </span><span class="hl-3">"my-strategy"</span><span class="hl-2">, </span><span class="hl-5">exchangeName:</span><span class="hl-2"> </span><span class="hl-3">"binance"</span><span class="hl-2">, </span><span class="hl-5">frameName:</span><span class="hl-2"> </span><span class="hl-3">"feb-2026"</span><span class="hl-2"> });</span><br/><span class="hl-2">}</span> | |
| </code><button type="button">Copy</button></pre> | |
| <pre><code class="bash"><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/cli</span><span class="hl-2"> </span><span class="hl-4">--backtest</span><span class="hl-2"> </span><span class="hl-4">--entry</span><span class="hl-2"> </span><span class="hl-3">./src/multi-symbol.mjs</span> | |
| </code><button type="button">Copy</button></pre> | |
| <p>The same shape works for <code>--live --entry</code> / <code>--paper --entry</code> (call <code>Live.background()</code> per symbol with your broker adapter).</p> | |
| </details> | |
| <hr> | |
| <a id="🛠️-tooling-modes" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🛠️ Tooling modes<a href="#🛠️-tooling-modes" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Five utilities that don't run a strategy. They share one convention, explained once here and referenced below.</p> | |
| <blockquote> | |
| <p><strong>The <code><mode>.module</code> convention.</strong> By default the CLI auto-registers CCXT Binance. To use a different exchange (custom API keys, rate limits, a non-spot market), drop a <code>modules/<mode>.module.ts</code> that calls <code>addExchangeSchema</code> from <code>backtest-kit</code>. The CLI loads it automatically before running, trying <code>.ts</code>/<code>.mjs</code>/<code>.cjs</code>; it's searched <strong>next to the target file first, then in the project root</strong>. <code>.env</code> is loaded root-first then the target-file dir (override), so API keys stay out of code.</p> | |
| </blockquote> | |
| <details> | |
| <summary>The shared <code><mode>.module.ts</code> shape (pine / editor / dump / pnldebug / brokerdebug)</summary> | |
| <pre><code class="typescript"><span class="hl-0">// modules/pine.module.ts (same shape for editor/dump/pnldebug.module; brokerdebug registers a Broker instead)</span><br/><span class="hl-6">import</span><span class="hl-2"> { </span><span class="hl-5">addExchangeSchema</span><span class="hl-2"> } </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">"backtest-kit"</span><span class="hl-2">;</span><br/><span class="hl-6">import</span><span class="hl-2"> </span><span class="hl-5">ccxt</span><span class="hl-2"> </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">"ccxt"</span><span class="hl-2">;</span><br/><br/><span class="hl-1">addExchangeSchema</span><span class="hl-2">({</span><br/><span class="hl-2"> </span><span class="hl-5">exchangeName:</span><span class="hl-2"> </span><span class="hl-3">"my-exchange"</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-1">getCandles</span><span class="hl-5">:</span><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> (</span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">interval</span><span class="hl-2">, </span><span class="hl-5">since</span><span class="hl-2">, </span><span class="hl-5">limit</span><span class="hl-2">) </span><span class="hl-4">=></span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-7">exchange</span><span class="hl-2"> = </span><span class="hl-4">new</span><span class="hl-2"> </span><span class="hl-5">ccxt</span><span class="hl-2">.</span><span class="hl-1">bybit</span><span class="hl-2">({</span><br/><span class="hl-2"> </span><span class="hl-5">apiKey:</span><span class="hl-2"> </span><span class="hl-5">process</span><span class="hl-2">.</span><span class="hl-5">env</span><span class="hl-2">.</span><span class="hl-7">BYBIT_API_KEY</span><span class="hl-2">, </span><span class="hl-5">secret:</span><span class="hl-2"> </span><span class="hl-5">process</span><span class="hl-2">.</span><span class="hl-5">env</span><span class="hl-2">.</span><span class="hl-7">BYBIT_API_SECRET</span><span class="hl-2">, </span><span class="hl-5">enableRateLimit:</span><span class="hl-2"> </span><span class="hl-4">true</span><span class="hl-2">,</span><br/><span class="hl-2"> });</span><br/><span class="hl-2"> </span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-7">ohlcv</span><span class="hl-2"> = </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-5">exchange</span><span class="hl-2">.</span><span class="hl-1">fetchOHLCV</span><span class="hl-2">(</span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">interval</span><span class="hl-2">, </span><span class="hl-5">since</span><span class="hl-2">.</span><span class="hl-1">getTime</span><span class="hl-2">(), </span><span class="hl-5">limit</span><span class="hl-2">);</span><br/><span class="hl-2"> </span><span class="hl-6">return</span><span class="hl-2"> </span><span class="hl-5">ohlcv</span><span class="hl-2">.</span><span class="hl-1">map</span><span class="hl-2">(([</span><span class="hl-5">timestamp</span><span class="hl-2">, </span><span class="hl-5">open</span><span class="hl-2">, </span><span class="hl-5">high</span><span class="hl-2">, </span><span class="hl-5">low</span><span class="hl-2">, </span><span class="hl-5">close</span><span class="hl-2">, </span><span class="hl-5">volume</span><span class="hl-2">]) </span><span class="hl-4">=></span><br/><span class="hl-2"> ({ </span><span class="hl-5">timestamp</span><span class="hl-2">, </span><span class="hl-5">open</span><span class="hl-2">, </span><span class="hl-5">high</span><span class="hl-2">, </span><span class="hl-5">low</span><span class="hl-2">, </span><span class="hl-5">close</span><span class="hl-2">, </span><span class="hl-5">volume</span><span class="hl-2"> }));</span><br/><span class="hl-2"> },</span><br/><span class="hl-2"> </span><span class="hl-1">formatPrice</span><span class="hl-5">:</span><span class="hl-2"> (</span><span class="hl-5">s</span><span class="hl-2">, </span><span class="hl-5">p</span><span class="hl-2">) </span><span class="hl-4">=></span><span class="hl-2"> </span><span class="hl-5">p</span><span class="hl-2">.</span><span class="hl-1">toFixed</span><span class="hl-2">(</span><span class="hl-8">2</span><span class="hl-2">), </span><span class="hl-1">formatQuantity</span><span class="hl-5">:</span><span class="hl-2"> (</span><span class="hl-5">s</span><span class="hl-2">, </span><span class="hl-5">q</span><span class="hl-2">) </span><span class="hl-4">=></span><span class="hl-2"> </span><span class="hl-5">q</span><span class="hl-2">.</span><span class="hl-1">toFixed</span><span class="hl-2">(</span><span class="hl-8">8</span><span class="hl-2">),</span><br/><span class="hl-2">});</span> | |
| </code><button type="button">Copy</button></pre> | |
| <pre><code class="env"># .env (loaded root-first, then next to the target file) | |
| BYBIT_API_KEY=xxx | |
| BYBIT_API_SECRET=yyy | |
| </code><button type="button">Copy</button></pre> | |
| </details> | |
| <a id="🌲-pine--run-local-pinescript---pine" class="tsd-anchor"></a><h3 class="tsd-anchor-link">🌲 Pine — run local PineScript (<code>--pine</code>)<a href="#🌲-pine--run-local-pinescript---pine" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Executes any local <code>.pine</code> file against a real exchange and prints results as a Markdown table — no TradingView account. Reads every <code>plot()</code> that uses <code>display=display.data_window</code> as an output column (others ignored); column names come straight from the plot names.</p> | |
| <pre><code class="bash"><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/cli</span><span class="hl-2"> </span><span class="hl-4">--pine</span><span class="hl-2"> </span><span class="hl-3">./math/impulse_trend_15m.pine</span><span class="hl-2"> </span><span class="hl-4">--symbol</span><span class="hl-2"> </span><span class="hl-3">BTCUSDT</span><span class="hl-2"> </span><span class="hl-4">--timeframe</span><span class="hl-2"> </span><span class="hl-3">15m</span><span class="hl-2"> </span><span class="hl-4">--limit</span><span class="hl-2"> </span><span class="hl-8">180</span><span class="hl-2"> </span><span class="hl-4">--when</span><span class="hl-2"> </span><span class="hl-3">"2025-09-24T12:00:00.000Z"</span> | |
| </code><button type="button">Copy</button></pre> | |
| <details> | |
| <summary>Pine flags, requirements & output</summary> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Flag</th> | |
| <th>Type</th> | |
| <th>Description</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>--pine</code></td> | |
| <td>boolean</td> | |
| <td>Enable PineScript mode</td> | |
| </tr> | |
| <tr> | |
| <td><code>--symbol</code></td> | |
| <td>string</td> | |
| <td>Trading pair (default <code>"BTCUSDT"</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--timeframe</code></td> | |
| <td>string</td> | |
| <td>Candle interval (default <code>"15m"</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--limit</code></td> | |
| <td>string</td> | |
| <td>Candles to fetch (default <code>250</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--when</code></td> | |
| <td>string</td> | |
| <td>End date — ISO 8601 or Unix ms (default now)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--exchange</code></td> | |
| <td>string</td> | |
| <td>Exchange (default: first registered, falls back to CCXT Binance)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--output</code></td> | |
| <td>string</td> | |
| <td>Output base name (default: <code>.pine</code> file name)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--json</code></td> | |
| <td>boolean</td> | |
| <td>Write plots as JSON array to <code><pine-dir>/dump/{output}.json</code></td> | |
| </tr> | |
| <tr> | |
| <td><code>--jsonl</code></td> | |
| <td>boolean</td> | |
| <td>Write plots as JSONL to <code><pine-dir>/dump/{output}.jsonl</code></td> | |
| </tr> | |
| <tr> | |
| <td><code>--markdown</code></td> | |
| <td>boolean</td> | |
| <td>Write Markdown table to <code><pine-dir>/dump/{output}.md</code></td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <p><code>--limit</code> must cover indicator warmup bars — rows before warmup show <code>N/A</code>. Positional: path to the <code>.pine</code> file. Exchange via <code>pine.module</code> (see convention above). Required plot form:</p> | |
| <pre><code class="pine">//@version=5 | |
| indicator("MyIndicator", overlay=true) | |
| plot(close, "Close", display=display.data_window) | |
| plot(position, "Position", display=display.data_window) | |
| </code><button type="button">Copy</button></pre> | |
| <p>Output (stdout, or <code>--markdown</code>/<code>--json</code>/<code>--jsonl</code> to <code><pine-dir>/dump/</code>):</p> | |
| <pre><code><span class="hl-2">| </span><span class="hl-5">Close</span><span class="hl-2"> | </span><span class="hl-5">Position</span><span class="hl-2"> | </span><span class="hl-5">timestamp</span><span class="hl-2"> |</span><br/><span class="hl-2">| --- | --- | --- |</span><br/><span class="hl-2">| </span><span class="hl-8">112871.28</span><span class="hl-2"> | -</span><span class="hl-8">1.0000</span><span class="hl-2"> | </span><span class="hl-8">2025</span><span class="hl-2">-</span><span class="hl-8">09</span><span class="hl-2">-22</span><span class="hl-9">T15</span><span class="hl-2">:</span><span class="hl-8">00</span><span class="hl-2">:</span><span class="hl-8">00</span><span class="hl-2">.000</span><span class="hl-7">Z</span><span class="hl-2"> |</span><br/><span class="hl-2">| </span><span class="hl-8">112736.00</span><span class="hl-2"> | </span><span class="hl-8">0.0000</span><span class="hl-2"> | </span><span class="hl-8">2025</span><span class="hl-2">-</span><span class="hl-8">09</span><span class="hl-2">-22</span><span class="hl-9">T18</span><span class="hl-2">:</span><span class="hl-8">30</span><span class="hl-2">:</span><span class="hl-8">00</span><span class="hl-2">.000</span><span class="hl-7">Z</span><span class="hl-2"> |</span><br/><span class="hl-2">| </span><span class="hl-8">112653.90</span><span class="hl-2"> | </span><span class="hl-8">1.0000</span><span class="hl-2"> | </span><span class="hl-8">2025</span><span class="hl-2">-</span><span class="hl-8">09</span><span class="hl-2">-22</span><span class="hl-9">T22</span><span class="hl-2">:</span><span class="hl-8">15</span><span class="hl-2">:</span><span class="hl-8">00</span><span class="hl-2">.000</span><span class="hl-7">Z</span><span class="hl-2"> |</span> | |
| </code><button>Copy</button></pre> | |
| </details> | |
| <a id="🎨-editor--visual-pine-script-editor---editor" class="tsd-anchor"></a><h3 class="tsd-anchor-link">🎨 Editor — visual Pine Script editor (<code>--editor</code>)<a href="#🎨-editor--visual-pine-script-editor---editor" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p><img src="https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot32.png" alt="pine"></p> | |
| <p>A browser-based Pine Script editor (powered by <code>@backtest-kit/ui</code>) with a live chart that updates on <strong>▶ Run</strong>.</p> | |
| <pre><code class="bash"><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/cli</span><span class="hl-2"> </span><span class="hl-4">--editor</span><span class="hl-2"> </span><span class="hl-0"># → http://localhost:60050?pine=1 opens automatically</span> | |
| </code><button type="button">Copy</button></pre> | |
| <details> | |
| <summary>Editor behavior & exchange</summary> | |
| <p>The CLI loads <code>./modules/editor.module</code> if present (register your exchange, same as <code>pine.module</code>), starts the <code>@backtest-kit/ui</code> server on <code>CC_WWWROOT_PORT</code> (default <code>60050</code>), and opens the editor in your browser. <strong>Ctrl+C</strong> stops it. Env: <code>CC_WWWROOT_HOST</code> (default <code>0.0.0.0</code>), <code>CC_WWWROOT_PORT</code> (default <code>60050</code>).</p> | |
| </details> | |
| <a id="💾-candle-dump---dump" class="tsd-anchor"></a><h3 class="tsd-anchor-link">💾 Candle Dump (<code>--dump</code>)<a href="#💾-candle-dump---dump" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Fetch raw OHLCV candles from any registered exchange and save them — no strategy file required. <code>dump/</code> is created in the current working directory.</p> | |
| <pre><code class="bash"><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/cli</span><span class="hl-2"> </span><span class="hl-4">--dump</span><span class="hl-2"> </span><span class="hl-4">--symbol</span><span class="hl-2"> </span><span class="hl-3">BTCUSDT</span><span class="hl-2"> </span><span class="hl-4">--timeframe</span><span class="hl-2"> </span><span class="hl-3">15m</span><span class="hl-2"> </span><span class="hl-4">--limit</span><span class="hl-2"> </span><span class="hl-8">500</span><span class="hl-2"> </span><span class="hl-4">--when</span><span class="hl-2"> </span><span class="hl-3">"2026-02-28T00:00:00.000Z"</span><span class="hl-2"> </span><span class="hl-4">--jsonl</span><span class="hl-2"> </span><span class="hl-4">--output</span><span class="hl-2"> </span><span class="hl-3">feb2026_btc</span><br/><span class="hl-0"># → ./dump/feb2026_btc.jsonl</span> | |
| </code><button type="button">Copy</button></pre> | |
| <details> | |
| <summary>Dump flags</summary> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Flag</th> | |
| <th>Type</th> | |
| <th>Description</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>--dump</code></td> | |
| <td>boolean</td> | |
| <td>Enable candle dump</td> | |
| </tr> | |
| <tr> | |
| <td><code>--symbol</code></td> | |
| <td>string</td> | |
| <td>Trading pair (default <code>"BTCUSDT"</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--timeframe</code></td> | |
| <td>string</td> | |
| <td>Candle interval (default <code>"15m"</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--limit</code></td> | |
| <td>string</td> | |
| <td>Candles to fetch (default <code>250</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--when</code></td> | |
| <td>string</td> | |
| <td>End date — ISO 8601 or Unix ms (default now)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--exchange</code></td> | |
| <td>string</td> | |
| <td>Exchange (default: first registered, falls back to CCXT Binance)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--output</code></td> | |
| <td>string</td> | |
| <td>Output base name (default <code>{SYMBOL}_{LIMIT}_{TIMEFRAME}_{TIMESTAMP}</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--json</code></td> | |
| <td>boolean</td> | |
| <td>Write candles as JSON array to <code>./dump/{output}.json</code></td> | |
| </tr> | |
| <tr> | |
| <td><code>--jsonl</code></td> | |
| <td>boolean</td> | |
| <td>Write candles as JSONL to <code>./dump/{output}.jsonl</code></td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <p>Exchange via <code>dump.module</code> (see convention above), searched in the current working directory. No flag → print to stdout.</p> | |
| </details> | |
| <a id="🐞-pnl-debug---pnldebug" class="tsd-anchor"></a><h3 class="tsd-anchor-link">🐞 PnL Debug (<code>--pnldebug</code>)<a href="#🐞-pnl-debug---pnldebug" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Simulate a hypothetical position minute by minute — running PnL, peak profit, max drawdown per candle — without placing trades or loading a strategy.</p> | |
| <pre><code class="bash"><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/cli</span><span class="hl-2"> </span><span class="hl-4">--pnldebug</span><span class="hl-2"> </span><span class="hl-4">--symbol</span><span class="hl-2"> </span><span class="hl-3">BTCUSDT</span><span class="hl-2"> </span><span class="hl-4">--priceopen</span><span class="hl-2"> </span><span class="hl-8">64069.50</span><span class="hl-2"> </span><span class="hl-4">--direction</span><span class="hl-2"> </span><span class="hl-3">short</span><span class="hl-2"> </span><span class="hl-4">--when</span><span class="hl-2"> </span><span class="hl-3">"2025-02-25"</span><span class="hl-2"> </span><span class="hl-4">--minutes</span><span class="hl-2"> </span><span class="hl-8">120</span> | |
| </code><button type="button">Copy</button></pre> | |
| <details> | |
| <summary>PnL Debug flags, columns & sample output</summary> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Flag</th> | |
| <th>Type</th> | |
| <th>Description</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>--pnldebug</code></td> | |
| <td>boolean</td> | |
| <td>Enable PnL debug</td> | |
| </tr> | |
| <tr> | |
| <td><code>--priceopen</code></td> | |
| <td>number</td> | |
| <td>Entry price (<strong>required</strong>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--direction</code></td> | |
| <td>string</td> | |
| <td><code>long</code> or <code>short</code> (default <code>long</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--when</code></td> | |
| <td>string</td> | |
| <td>Start timestamp — ISO 8601 or Unix ms (default now)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--minutes</code></td> | |
| <td>string</td> | |
| <td>Number of 1m candles to simulate (default <code>60</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--symbol</code></td> | |
| <td>string</td> | |
| <td>Trading pair (default <code>"BTCUSDT"</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--exchange</code></td> | |
| <td>string</td> | |
| <td>Exchange (default: first registered, falls back to CCXT Binance)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--output</code></td> | |
| <td>string</td> | |
| <td>Output base name (default <code>{SYMBOL}_{DIRECTION}_{PRICEOPEN}_{TIMESTAMP}</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--json</code> / <code>--jsonl</code> / <code>--markdown</code></td> | |
| <td>boolean</td> | |
| <td>Save to <code>./dump/<output>.{json,jsonl,md}</code></td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <p>Columns: <code>min</code> (1-based offset), <code>timestamp</code>, <code>close</code>, <code>pnl%</code> (signed, vs entry), <code>peak%</code> (highest so far, ≥0), <code>drawdown%</code> (lowest so far, ≤0). Exchange via <code>pnldebug.module</code> (convention above).</p> | |
| <pre><code><span class="hl-9">Symbol</span><span class="hl-2">: </span><span class="hl-7">BTCUSDT</span><span class="hl-2"> | </span><span class="hl-9">Direction</span><span class="hl-2">: </span><span class="hl-5">short</span><span class="hl-2"> | </span><span class="hl-9">PriceOpen</span><span class="hl-2">: </span><span class="hl-8">64069.50</span><span class="hl-2"> | </span><span class="hl-9">From</span><span class="hl-2">: </span><span class="hl-8">2025</span><span class="hl-2">-</span><span class="hl-8">02</span><span class="hl-2">-25</span><span class="hl-9">T00</span><span class="hl-2">:</span><span class="hl-8">00</span><span class="hl-2">:</span><span class="hl-8">00</span><span class="hl-2">.000</span><span class="hl-7">Z</span><span class="hl-2"> | </span><span class="hl-9">Minutes</span><span class="hl-2">: </span><span class="hl-8">120</span><br/><span class="hl-2"> </span><span class="hl-5">min</span><span class="hl-2"> | </span><span class="hl-5">timestamp</span><span class="hl-2"> | </span><span class="hl-5">close</span><span class="hl-2"> | </span><span class="hl-5">pnl</span><span class="hl-2">% | </span><span class="hl-5">peak</span><span class="hl-2">% | </span><span class="hl-5">drawdown</span><span class="hl-2">%</span><br/><span class="hl-2"> </span><span class="hl-8">1</span><span class="hl-2"> | </span><span class="hl-8">2025</span><span class="hl-2">-</span><span class="hl-8">02</span><span class="hl-2">-25</span><span class="hl-9">T00</span><span class="hl-2">:</span><span class="hl-8">01</span><span class="hl-2">:</span><span class="hl-8">00</span><span class="hl-2">.000</span><span class="hl-7">Z</span><span class="hl-2"> | </span><span class="hl-8">64020.10</span><span class="hl-2"> | +</span><span class="hl-8">0.08</span><span class="hl-2">% | +</span><span class="hl-8">0.08</span><span class="hl-2">% | </span><span class="hl-8">0.00</span><span class="hl-2">%</span><br/><span class="hl-2"> </span><span class="hl-8">2</span><span class="hl-2"> | </span><span class="hl-8">2025</span><span class="hl-2">-</span><span class="hl-8">02</span><span class="hl-2">-25</span><span class="hl-9">T00</span><span class="hl-2">:</span><span class="hl-8">02</span><span class="hl-2">:</span><span class="hl-8">00</span><span class="hl-2">.000</span><span class="hl-7">Z</span><span class="hl-2"> | </span><span class="hl-8">64105.30</span><span class="hl-2"> | -</span><span class="hl-8">0.06</span><span class="hl-2">% | +</span><span class="hl-8">0.08</span><span class="hl-2">% | -</span><span class="hl-8">0.06</span><span class="hl-2">%</span><br/><span class="hl-2"> </span><span class="hl-8">120</span><span class="hl-2"> | </span><span class="hl-8">2025</span><span class="hl-2">-</span><span class="hl-8">02</span><span class="hl-2">-25</span><span class="hl-9">T02</span><span class="hl-2">:</span><span class="hl-8">00</span><span class="hl-2">:</span><span class="hl-8">00</span><span class="hl-2">.000</span><span class="hl-7">Z</span><span class="hl-2"> | </span><span class="hl-8">63200.00</span><span class="hl-2"> | +</span><span class="hl-8">1.36</span><span class="hl-2">% | +</span><span class="hl-8">1.36</span><span class="hl-2">% | -</span><span class="hl-8">0.06</span><span class="hl-2">%</span> | |
| </code><button>Copy</button></pre> | |
| </details> | |
| <a id="🐛-broker-debug---brokerdebug" class="tsd-anchor"></a><h3 class="tsd-anchor-link">🐛 Broker Debug (<code>--brokerdebug</code>)<a href="#🐛-broker-debug---brokerdebug" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Fire a single broker commit against your live adapter without a full strategy — verify your <code>brokerdebug.module</code> wires exchange calls correctly before waiting hours for a real signal.</p> | |
| <pre><code class="bash"><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/cli</span><span class="hl-2"> </span><span class="hl-4">--brokerdebug</span><span class="hl-2"> </span><span class="hl-4">--commit</span><span class="hl-2"> </span><span class="hl-3">signal-open</span><span class="hl-2"> </span><span class="hl-4">--symbol</span><span class="hl-2"> </span><span class="hl-3">BTCUSDT</span> | |
| </code><button type="button">Copy</button></pre> | |
| <details> | |
| <summary>Broker Debug flags, commit types & how it works</summary> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Flag</th> | |
| <th>Type</th> | |
| <th>Description</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>--brokerdebug</code></td> | |
| <td>boolean</td> | |
| <td>Enable broker debug</td> | |
| </tr> | |
| <tr> | |
| <td><code>--commit</code></td> | |
| <td>string</td> | |
| <td>Commit type to fire (default <code>"signal-open"</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--symbol</code></td> | |
| <td>string</td> | |
| <td>Trading pair (default <code>"BTCUSDT"</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--exchange</code></td> | |
| <td>string</td> | |
| <td>Exchange (default: first registered)</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <p><code>--commit</code> values → hook: <code>signal-open</code>→<code>onSignalOpenCommit</code>, <code>signal-close</code>→<code>onSignalCloseCommit</code>, <code>partial-profit</code>→<code>onPartialProfitCommit</code>, <code>partial-loss</code>→<code>onPartialLossCommit</code>, <code>average-buy</code>→<code>onAverageBuyCommit</code>, <code>trailing-stop</code>→<code>onTrailingStopCommit</code>, <code>trailing-take</code>→<code>onTrailingTakeCommit</code>, <code>breakeven</code>→<code>onBreakevenCommit</code>.</p> | |
| <p>The CLI loads <code>./modules/brokerdebug.module</code>, fetches the last candle for <code>--symbol</code>, derives a synthetic payload from <code>currentPrice</code> (TP = +2%, SL = −2%), and calls the selected hook once; exits <code>0</code> on success. The module registers a <code>Broker</code> adapter (<code>Broker.useBrokerAdapter(...)</code> + <code>Broker.enable()</code>), not an exchange.</p> | |
| </details> | |
| <a id="🗑️-flush---flush" class="tsd-anchor"></a><h3 class="tsd-anchor-link">🗑️ Flush (<code>--flush</code>)<a href="#🗑️-flush---flush" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Delete generated output folders from one or more strategy dump dirs <strong>without</strong> touching cached candle data.</p> | |
| <pre><code class="bash"><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/cli</span><span class="hl-2"> </span><span class="hl-4">--flush</span><span class="hl-2"> </span><span class="hl-3">./content/feb_2026.strategy/modules/backtest.module.ts</span><span class="hl-2"> </span><span class="hl-3">./content/mar_2026.strategy/modules/backtest.module.ts</span> | |
| </code><button type="button">Copy</button></pre> | |
| <details> | |
| <summary>What flush removes</summary> | |
| <p>For each positional entry point the CLI resolves its directory and removes from <code><entry-dir>/dump/</code>: <code>report</code> (backtest <code>.jsonl</code>), <code>log</code> (<code>log.jsonl</code>), <code>markdown</code> (exported reports), <code>agent</code> (agent outlines). Candle cache (<code>dump/data/</code>) and AI forecast outlines (<code>dump/outline/</code>) are <strong>not</strong> removed.</p> | |
| </details> | |
| <hr> | |
| <a id="🗂️-project--monorepo" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🗂️ Project & monorepo<a href="#🗂️-project--monorepo" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><a id="scaffolding---init" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Scaffolding (<code>--init</code>)<a href="#scaffolding---init" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Bootstraps a ready-to-use project with an example strategy, an example Pine indicator, an AI-agent <code>CLAUDE.md</code>, and documentation fetched automatically. The target dir must not exist or be empty.</p> | |
| <pre><code class="bash"><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/cli</span><span class="hl-2"> </span><span class="hl-4">--init</span><span class="hl-2"> </span><span class="hl-4">--output</span><span class="hl-2"> </span><span class="hl-3">my-trading-bot</span><span class="hl-2"> </span><span class="hl-0"># → ./my-trading-bot/</span> | |
| </code><button type="button">Copy</button></pre> | |
| <details> | |
| <summary>Generated structure & automatic docs fetch</summary> | |
| <pre><code><span class="hl-5">backtest</span><span class="hl-2">-</span><span class="hl-5">kit</span><span class="hl-2">-</span><span class="hl-5">project</span><span class="hl-2">/</span><br/><span class="hl-2">├── </span><span class="hl-5">package</span><span class="hl-2">.</span><span class="hl-5">json</span><span class="hl-2"> # </span><span class="hl-5">pre</span><span class="hl-2">-</span><span class="hl-5">configured</span><span class="hl-2"> </span><span class="hl-6">with</span><span class="hl-2"> </span><span class="hl-5">all</span><span class="hl-2"> </span><span class="hl-5">backtest</span><span class="hl-2">-</span><span class="hl-5">kit</span><span class="hl-2"> </span><span class="hl-5">deps</span><br/><span class="hl-2">├── </span><span class="hl-7">CLAUDE</span><span class="hl-2">.</span><span class="hl-5">md</span><span class="hl-2"> # </span><span class="hl-7">AI</span><span class="hl-2">-</span><span class="hl-5">agent</span><span class="hl-2"> </span><span class="hl-5">guide</span><span class="hl-2"> </span><span class="hl-5">for</span><span class="hl-2"> </span><span class="hl-5">writing</span><span class="hl-2"> </span><span class="hl-5">strategies</span><br/><span class="hl-2">├── </span><span class="hl-5">content</span><span class="hl-2">/</span><span class="hl-5">feb_2026</span><span class="hl-2">.</span><span class="hl-5">strategy</span><span class="hl-2">.</span><span class="hl-5">ts</span><span class="hl-2"> # </span><span class="hl-5">example</span><span class="hl-2"> </span><span class="hl-5">strategy</span><span class="hl-2"> </span><span class="hl-5">entry</span><span class="hl-2"> </span><span class="hl-5">point</span><br/><span class="hl-2">├── </span><span class="hl-5">math</span><span class="hl-2">/</span><span class="hl-5">feb_2026</span><span class="hl-2">.</span><span class="hl-5">pine</span><span class="hl-2"> # </span><span class="hl-5">example</span><span class="hl-2"> </span><span class="hl-5">PineScript</span><span class="hl-2"> </span><span class="hl-5">indicator</span><br/><span class="hl-2">├── </span><span class="hl-5">modules</span><span class="hl-2">/{</span><span class="hl-5">dump</span><span class="hl-2">,</span><span class="hl-5">pine</span><span class="hl-2">}.</span><span class="hl-5">module</span><span class="hl-2">.</span><span class="hl-5">ts</span><span class="hl-2"> # </span><span class="hl-5">exchange</span><span class="hl-2"> </span><span class="hl-5">schemas</span><span class="hl-2"> </span><span class="hl-5">for</span><span class="hl-2"> --</span><span class="hl-5">dump</span><span class="hl-2"> / --</span><span class="hl-5">pine</span><br/><span class="hl-2">├── </span><span class="hl-5">report</span><span class="hl-2">/</span><span class="hl-5">feb_2026</span><span class="hl-2">.</span><span class="hl-5">md</span><span class="hl-2"> # </span><span class="hl-5">example</span><span class="hl-2"> </span><span class="hl-5">research</span><span class="hl-2"> </span><span class="hl-5">report</span><br/><span class="hl-2">├── </span><span class="hl-5">docs</span><span class="hl-2">/{...}.</span><span class="hl-5">md</span><span class="hl-2"> + </span><span class="hl-5">docs</span><span class="hl-2">/</span><span class="hl-5">lib</span><span class="hl-2">/ # </span><span class="hl-5">guides</span><span class="hl-2"> + </span><span class="hl-5">fetched</span><span class="hl-2"> </span><span class="hl-5">library</span><span class="hl-2"> </span><span class="hl-5">READMEs</span><br/><span class="hl-2">└── </span><span class="hl-5">scripts</span><span class="hl-2">/</span><span class="hl-5">fetch_docs</span><span class="hl-2">.</span><span class="hl-5">mjs</span><span class="hl-2"> # </span><span class="hl-5">downloads</span><span class="hl-2"> </span><span class="hl-5">library</span><span class="hl-2"> </span><span class="hl-5">READMEs</span><span class="hl-2"> </span><span class="hl-5">into</span><span class="hl-2"> </span><span class="hl-5">docs</span><span class="hl-2">/</span><span class="hl-5">lib</span><span class="hl-2">/</span> | |
| </code><button>Copy</button></pre> | |
| <p>After scaffolding the CLI runs <code>scripts/fetch_docs.mjs</code>, downloading the latest READMEs for <code>backtest-kit</code>, <code>@backtest-kit/graph</code>, <code>@backtest-kit/pinets</code>, <code>@backtest-kit/cli</code>, <code>garch</code>, <code>volume-anomaly</code>, <code>agent-swarm-kit</code>, <code>functools-kit</code> into <code>docs/lib/</code>. Re-run anytime with <code>node ./scripts/fetch_docs.mjs</code> or <code>npm run sync:lib</code>.</p> | |
| </details> | |
| <a id="docker---docker" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Docker (<code>--docker</code>)<a href="#docker---docker" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Scaffolds a self-contained Docker workspace with <code>docker-compose.yaml</code> and a strategy entry point, for zero-downtime live trading.</p> | |
| <pre><code class="bash"><span class="hl-1">npx</span><span class="hl-2"> </span><span class="hl-3">@backtest-kit/cli</span><span class="hl-2"> </span><span class="hl-4">--docker</span><span class="hl-2"> && </span><span class="hl-1">cd</span><span class="hl-2"> </span><span class="hl-3">backtest-kit-docker</span><br/><span class="hl-5">MODE</span><span class="hl-2">=</span><span class="hl-3">live</span><span class="hl-2"> </span><span class="hl-5">SYMBOL</span><span class="hl-2">=</span><span class="hl-3">TRXUSDT</span><span class="hl-2"> </span><span class="hl-5">STRATEGY_FILE</span><span class="hl-2">=</span><span class="hl-3">./content/feb_2026/feb_2026.strategy.ts</span><span class="hl-2"> </span><span class="hl-1">docker-compose</span><span class="hl-2"> </span><span class="hl-3">up</span><span class="hl-2"> </span><span class="hl-4">-d</span> | |
| </code><button type="button">Copy</button></pre> | |
| <details> | |
| <summary>Two launch modes & environment variables</summary> | |
| <p><strong>1. <code>command:</code> in <code>docker-compose.yaml</code></strong> — pin mode and flags directly; the entrypoint forwards all args to the CLI unchanged:</p> | |
| <pre><code class="yaml"><span class="hl-14">command</span><span class="hl-2">: [</span><span class="hl-15">--live</span><span class="hl-2">, </span><span class="hl-15">--symbol</span><span class="hl-2">, </span><span class="hl-15">TRXUSDT</span><span class="hl-2">, </span><span class="hl-15">--strategy</span><span class="hl-2">, </span><span class="hl-15">feb_2026_strategy</span><span class="hl-2">, </span><span class="hl-15">--exchange</span><span class="hl-2">, </span><span class="hl-15">ccxt-exchange</span><span class="hl-2">, </span><span class="hl-15">./content/feb_2026/feb_2026.strategy.ts</span><span class="hl-2">, </span><span class="hl-15">--ui</span><span class="hl-2">]</span> | |
| </code><button type="button">Copy</button></pre> | |
| <p><strong>2. Inline env vars</strong> — <code>MODE</code> + <code>STRATEGY_FILE</code> on the command line, no file edits:</p> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Variable</th> | |
| <th>Required</th> | |
| <th>Default</th> | |
| <th>Description</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>MODE</code></td> | |
| <td>yes</td> | |
| <td>—</td> | |
| <td><code>backtest</code> | <code>live</code> | <code>paper</code> | <code>walker</code></td> | |
| </tr> | |
| <tr> | |
| <td><code>STRATEGY_FILE</code></td> | |
| <td>yes</td> | |
| <td>—</td> | |
| <td>Path to entry point (relative to <code>working_dir</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>SYMBOL</code></td> | |
| <td>no</td> | |
| <td><code>BTCUSDT</code></td> | |
| <td>Trading pair</td> | |
| </tr> | |
| <tr> | |
| <td><code>STRATEGY</code> / <code>EXCHANGE</code> / <code>FRAME</code></td> | |
| <td>no</td> | |
| <td>first registered</td> | |
| <td>Names</td> | |
| </tr> | |
| <tr> | |
| <td><code>UI</code> / <code>TELEGRAM</code> / <code>VERBOSE</code> / <code>NO_CACHE</code> / <code>NO_FLUSH</code> / <code>ENTRY</code></td> | |
| <td>no</td> | |
| <td>—</td> | |
| <td>Any non-empty value enables the matching flag</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </details> | |
| <a id="monorepo-cwd-resolution" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Monorepo cwd resolution<a href="#monorepo-cwd-resolution" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>When the CLI loads an entry point it <strong>changes the working directory to that file's location</strong>, so every relative path (<code>dump/</code>, <code>modules/</code>, <code>template/</code>) resolves inside that strategy's folder. Each strategy gets its own <code>.env</code>, broker modules, templates, and dump dir — so the same tool scales from one strategy to a desk of them.</p> | |
| <details> | |
| <summary>How it works + isolated resources</summary> | |
| <p><code>ResolveService</code> runs, before executing your entry point:</p> | |
| <pre><code><span class="hl-5">process</span><span class="hl-2">.</span><span class="hl-1">chdir</span><span class="hl-2">(</span><span class="hl-5">path</span><span class="hl-2">.</span><span class="hl-1">dirname</span><span class="hl-2">(</span><span class="hl-5">entryPoint</span><span class="hl-2">)) </span><span class="hl-0">// cwd → strategy directory</span><br/><span class="hl-5">dotenv</span><span class="hl-2">.</span><span class="hl-1">config</span><span class="hl-2">({ </span><span class="hl-5">path:</span><span class="hl-2"> </span><span class="hl-5">rootDir</span><span class="hl-2"> + </span><span class="hl-3">'/.env'</span><span class="hl-2"> }) </span><span class="hl-0">// root .env first</span><br/><span class="hl-5">dotenv</span><span class="hl-2">.</span><span class="hl-1">config</span><span class="hl-2">({ </span><span class="hl-5">path:</span><span class="hl-2"> </span><span class="hl-5">strategyDir</span><span class="hl-2"> + </span><span class="hl-3">'/.env'</span><span class="hl-2">, </span><span class="hl-5">override:</span><span class="hl-2"> </span><span class="hl-4">true</span><span class="hl-2"> }) </span><span class="hl-0">// strategy .env overrides</span> | |
| </code><button>Copy</button></pre> | |
| <pre><code><span class="hl-5">monorepo</span><span class="hl-2">/</span><br/><span class="hl-2">├── </span><span class="hl-5">package</span><span class="hl-2">.</span><span class="hl-5">json</span><span class="hl-2"> # </span><span class="hl-5">root</span><span class="hl-2"> </span><span class="hl-1">scripts</span><span class="hl-2"> (</span><span class="hl-5">one</span><span class="hl-2"> </span><span class="hl-5">per</span><span class="hl-2"> </span><span class="hl-5">strategy</span><span class="hl-2">)</span><br/><span class="hl-2">├── .</span><span class="hl-5">env</span><span class="hl-2"> # </span><span class="hl-5">shared</span><span class="hl-2"> </span><span class="hl-7">API</span><span class="hl-2"> </span><span class="hl-5">keys</span><br/><span class="hl-2">└── </span><span class="hl-5">strategies</span><span class="hl-2">/</span><br/><span class="hl-2"> ├── </span><span class="hl-5">oct_2025</span><span class="hl-2">/</span><br/><span class="hl-2"> │ ├── </span><span class="hl-5">index</span><span class="hl-2">.</span><span class="hl-5">mjs</span><span class="hl-2"> # </span><span class="hl-5">registers</span><span class="hl-2"> </span><span class="hl-5">exchange</span><span class="hl-2">/</span><span class="hl-5">frame</span><span class="hl-2">/</span><span class="hl-5">strategy</span><span class="hl-2"> </span><span class="hl-5">schemas</span><br/><span class="hl-2"> │ ├── .</span><span class="hl-5">env</span><span class="hl-2"> # </span><span class="hl-5">overrides</span><span class="hl-2"> </span><span class="hl-5">root</span><span class="hl-2"> .</span><span class="hl-5">env</span><span class="hl-2"> </span><span class="hl-5">for</span><span class="hl-2"> </span><span class="hl-4">this</span><span class="hl-2"> </span><span class="hl-5">strategy</span><br/><span class="hl-2"> │ ├── </span><span class="hl-5">modules</span><span class="hl-2">/{</span><span class="hl-5">live</span><span class="hl-2">,</span><span class="hl-5">paper</span><span class="hl-2">,</span><span class="hl-5">backtest</span><span class="hl-2">}.</span><span class="hl-5">module</span><span class="hl-2">.</span><span class="hl-5">mjs</span><span class="hl-2"> # </span><span class="hl-5">broker</span><span class="hl-2"> </span><span class="hl-1">adapters</span><span class="hl-2"> (</span><span class="hl-5">optional</span><span class="hl-2">)</span><br/><span class="hl-2"> │ ├── </span><span class="hl-5">template</span><span class="hl-2">/ # </span><span class="hl-5">custom</span><span class="hl-2"> </span><span class="hl-5">Mustache</span><span class="hl-2"> </span><span class="hl-1">templates</span><span class="hl-2"> (</span><span class="hl-5">optional</span><span class="hl-2">)</span><br/><span class="hl-2"> │ └── </span><span class="hl-5">dump</span><span class="hl-2">/ # </span><span class="hl-5">auto</span><span class="hl-2">-</span><span class="hl-9">created</span><span class="hl-2">: </span><span class="hl-5">candle</span><span class="hl-2"> </span><span class="hl-5">cache</span><span class="hl-2"> + </span><span class="hl-5">reports</span><br/><span class="hl-2"> └── </span><span class="hl-5">dec_2025</span><span class="hl-2">/ …</span> | |
| </code><button>Copy</button></pre> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Resource</th> | |
| <th>Path (relative to strategy dir)</th> | |
| <th>Isolated</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td>Candle cache</td> | |
| <td><code>./dump/data/candle/</code></td> | |
| <td>✅ per-strategy</td> | |
| </tr> | |
| <tr> | |
| <td>Backtest reports</td> | |
| <td><code>./dump/</code></td> | |
| <td>✅ per-strategy</td> | |
| </tr> | |
| <tr> | |
| <td>Broker module (live/paper/backtest)</td> | |
| <td><code>./modules/{live,paper,backtest}.module.mjs</code></td> | |
| <td>✅ per-strategy</td> | |
| </tr> | |
| <tr> | |
| <td>Config module (walker)</td> | |
| <td><code>./modules/walker.module.mjs</code></td> | |
| <td>✅ loaded once</td> | |
| </tr> | |
| <tr> | |
| <td>Telegram templates</td> | |
| <td><code>./template/*.mustache</code></td> | |
| <td>✅ per-strategy</td> | |
| </tr> | |
| <tr> | |
| <td>Environment variables</td> | |
| <td><code>./.env</code> (overrides root)</td> | |
| <td>✅ per-strategy</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <p>Each run produces its own <code>dump/</code> — easy to compare results across periods, by inspection or by pointing an AI agent at a specific folder.</p> | |
| </details> | |
| <a id="folder-based-import-aliases" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Folder-based import aliases<a href="#folder-based-import-aliases" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Every <strong>top-level folder</strong> in <code>process.cwd()</code> automatically becomes a bare import alias inside any strategy file — no config, just create the folder. Extract shared utilities, indicators, or AI-agent logic into named folders and reuse them across strategies without relative-path hell.</p> | |
| <details> | |
| <summary>Resolution table, structure & tsconfig</summary> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Import</th> | |
| <th>Resolves to</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>import { fn } from "utils"</code></td> | |
| <td><code><cwd>/utils/index.ts</code> (or <code>.js</code>/<code>.mjs</code>/<code>.cjs</code>)</td> | |
| </tr> | |
| <tr> | |
| <td><code>import { calcRSI } from "math/rsi"</code></td> | |
| <td><code><cwd>/math/rsi.ts</code></td> | |
| </tr> | |
| <tr> | |
| <td><code>import { research } from "logic"</code></td> | |
| <td><code><cwd>/logic/index.ts</code></td> | |
| </tr> | |
| <tr> | |
| <td><code>import { X } from "logic/contract/ResearchResponse.contract"</code></td> | |
| <td><code><cwd>/logic/contract/ResearchResponse.contract.ts</code></td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <p>Both barrel and deep-subpath imports are supported. Add a matching <code>paths</code> entry to <code>tsconfig.json</code> so the editor resolves them:</p> | |
| <pre><code class="json"><span class="hl-2">{</span><br/><span class="hl-2"> </span><span class="hl-16">"compilerOptions"</span><span class="hl-2">: {</span><br/><span class="hl-2"> </span><span class="hl-16">"moduleResolution"</span><span class="hl-2">: </span><span class="hl-3">"bundler"</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-16">"paths"</span><span class="hl-2">: { </span><span class="hl-16">"logic"</span><span class="hl-2">: [</span><span class="hl-3">"./logic/index.ts"</span><span class="hl-2">], </span><span class="hl-16">"logic/*"</span><span class="hl-2">: [</span><span class="hl-3">"./logic/*"</span><span class="hl-2">], </span><span class="hl-16">"math"</span><span class="hl-2">: [</span><span class="hl-3">"./math/index.ts"</span><span class="hl-2">], </span><span class="hl-16">"math/*"</span><span class="hl-2">: [</span><span class="hl-3">"./math/*"</span><span class="hl-2">], </span><span class="hl-16">"utils"</span><span class="hl-2">: [</span><span class="hl-3">"./utils/index.ts"</span><span class="hl-2">], </span><span class="hl-16">"utils/*"</span><span class="hl-2">: [</span><span class="hl-3">"./utils/*"</span><span class="hl-2">] }</span><br/><span class="hl-2"> },</span><br/><span class="hl-2"> </span><span class="hl-16">"include"</span><span class="hl-2">: [</span><span class="hl-3">"./logic"</span><span class="hl-2">, </span><span class="hl-3">"./math"</span><span class="hl-2">, </span><span class="hl-3">"./utils"</span><span class="hl-2">, </span><span class="hl-3">"./content"</span><span class="hl-2">, </span><span class="hl-3">"./modules"</span><span class="hl-2">]</span><br/><span class="hl-2">}</span> | |
| </code><button type="button">Copy</button></pre> | |
| </details> | |
| <a id="entry-point-formats" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Entry point formats<a href="#entry-point-formats" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>The CLI auto-detects the format and loads it with the right runtime — no flags. <code>.ts</code> via <a href="https://tsx.is/"><code>tsx</code></a> <code>tsImport()</code> (handles ESM↔CJS cross-imports, no <code>tsc</code> step), <code>.mjs</code> via native <code>import()</code> (top-level <code>await</code>, ESM), <code>.cjs</code> via native <code>require()</code> (legacy/dual-package). Add <code>tsx</code> to deps for <code>.ts</code> strategies.</p> | |
| <hr> | |
| <a id="🔌-broker-adapter--transactional-live-orders" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🔌 Broker adapter — transactional live orders<a href="#🔌-broker-adapter--transactional-live-orders" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Mode-specific module files register a <code>Broker</code> adapter via side-effect import before the strategy starts. From then on, <code>backtest-kit</code> intercepts <strong>every</strong> trade-mutating call through the adapter <em>before</em> updating internal state — if the adapter throws, the position state is never changed (atomic rollback, retried next tick). No manual wiring; in backtest mode no adapter is called at all.</p> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Mode flag</th> | |
| <th>Module file</th> | |
| <th>Loaded before</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>--live</code></td> | |
| <td><code>./modules/live.module.mjs</code></td> | |
| <td><code>Live.background()</code></td> | |
| </tr> | |
| <tr> | |
| <td><code>--paper</code></td> | |
| <td><code>./modules/paper.module.mjs</code></td> | |
| <td><code>Live.background()</code> (paper)</td> | |
| </tr> | |
| <tr> | |
| <td><code>--backtest</code></td> | |
| <td><code>./modules/backtest.module.mjs</code></td> | |
| <td><code>Backtest.background()</code></td> | |
| </tr> | |
| <tr> | |
| <td><code>--walker</code></td> | |
| <td><code>./modules/walker.module.mjs</code></td> | |
| <td><code>Walker.background()</code></td> | |
| </tr> | |
| <tr> | |
| <td><code>--main</code></td> | |
| <td><code>./modules/main.module.mjs</code></td> | |
| <td>the custom entry point</td> | |
| </tr> | |
| <tr> | |
| <td><code>--brokerdebug</code></td> | |
| <td><code>./modules/brokerdebug.module.mjs</code></td> | |
| <td>the broker commit test</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <blockquote> | |
| <p>Resolved relative to <code>cwd</code> (the strategy dir); <code>.mjs</code>/<code>.cjs</code>/<code>.ts</code> tried automatically. A missing module is a soft warning, not an error.</p> | |
| </blockquote> | |
| <details> | |
| <summary>Adapter example & hook reference</summary> | |
| <pre><code class="javascript"><span class="hl-0">// live.module.mjs</span><br/><span class="hl-6">import</span><span class="hl-2"> { </span><span class="hl-5">Broker</span><span class="hl-2"> } </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">'backtest-kit'</span><span class="hl-2">;</span><br/><span class="hl-6">import</span><span class="hl-2"> { </span><span class="hl-5">myExchange</span><span class="hl-2"> } </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">'./exchange.mjs'</span><span class="hl-2">;</span><br/><br/><span class="hl-4">class</span><span class="hl-2"> </span><span class="hl-10">MyBroker</span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> </span><span class="hl-1">onSignalOpenCommit</span><span class="hl-2">({ </span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">priceOpen</span><span class="hl-2">, </span><span class="hl-5">direction</span><span class="hl-2"> }) { </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-5">myExchange</span><span class="hl-2">.</span><span class="hl-1">openPosition</span><span class="hl-2">(</span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">direction</span><span class="hl-2">, </span><span class="hl-5">priceOpen</span><span class="hl-2">); }</span><br/><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> </span><span class="hl-1">onSignalCloseCommit</span><span class="hl-2">({ </span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">priceClosed</span><span class="hl-2"> }) { </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-5">myExchange</span><span class="hl-2">.</span><span class="hl-1">closePosition</span><span class="hl-2">(</span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">priceClosed</span><span class="hl-2">); }</span><br/><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> </span><span class="hl-1">onPartialProfitCommit</span><span class="hl-2">({ </span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">cost</span><span class="hl-2">, </span><span class="hl-5">currentPrice</span><span class="hl-2"> }) { </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-5">myExchange</span><span class="hl-2">.</span><span class="hl-1">createOrder</span><span class="hl-2">({ </span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">side:</span><span class="hl-2"> </span><span class="hl-3">'sell'</span><span class="hl-2">, </span><span class="hl-5">quantity:</span><span class="hl-2"> </span><span class="hl-5">cost</span><span class="hl-2"> / </span><span class="hl-5">currentPrice</span><span class="hl-2"> }); }</span><br/><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> </span><span class="hl-1">onAverageBuyCommit</span><span class="hl-2">({ </span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">cost</span><span class="hl-2">, </span><span class="hl-5">currentPrice</span><span class="hl-2"> }) { </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-5">myExchange</span><span class="hl-2">.</span><span class="hl-1">createOrder</span><span class="hl-2">({ </span><span class="hl-5">symbol</span><span class="hl-2">, </span><span class="hl-5">side:</span><span class="hl-2"> </span><span class="hl-3">'buy'</span><span class="hl-2">, </span><span class="hl-5">quantity:</span><span class="hl-2"> </span><span class="hl-5">cost</span><span class="hl-2"> / </span><span class="hl-5">currentPrice</span><span class="hl-2"> }); }</span><br/><span class="hl-2">}</span><br/><br/><span class="hl-5">Broker</span><span class="hl-2">.</span><span class="hl-1">useBrokerAdapter</span><span class="hl-2">(</span><span class="hl-5">MyBroker</span><span class="hl-2">);</span><br/><span class="hl-5">Broker</span><span class="hl-2">.</span><span class="hl-1">enable</span><span class="hl-2">();</span> | |
| </code><button type="button">Copy</button></pre> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Method</th> | |
| <th>Payload type</th> | |
| <th>Triggered on</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>onSignalOpenCommit</code></td> | |
| <td><code>BrokerSignalOpenPayload</code></td> | |
| <td>Position activation</td> | |
| </tr> | |
| <tr> | |
| <td><code>onSignalCloseCommit</code></td> | |
| <td><code>BrokerSignalClosePayload</code></td> | |
| <td>SL / TP / manual close</td> | |
| </tr> | |
| <tr> | |
| <td><code>onPartialProfitCommit</code></td> | |
| <td><code>BrokerPartialProfitPayload</code></td> | |
| <td>Partial profit</td> | |
| </tr> | |
| <tr> | |
| <td><code>onPartialLossCommit</code></td> | |
| <td><code>BrokerPartialLossPayload</code></td> | |
| <td>Partial loss</td> | |
| </tr> | |
| <tr> | |
| <td><code>onTrailingStopCommit</code></td> | |
| <td><code>BrokerTrailingStopPayload</code></td> | |
| <td>SL adjustment</td> | |
| </tr> | |
| <tr> | |
| <td><code>onTrailingTakeCommit</code></td> | |
| <td><code>BrokerTrailingTakePayload</code></td> | |
| <td>TP adjustment</td> | |
| </tr> | |
| <tr> | |
| <td><code>onBreakevenCommit</code></td> | |
| <td><code>BrokerBreakevenPayload</code></td> | |
| <td>SL moved to entry</td> | |
| </tr> | |
| <tr> | |
| <td><code>onAverageBuyCommit</code></td> | |
| <td><code>BrokerAverageBuyPayload</code></td> | |
| <td>DCA entry</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <p>All methods are optional; unimplemented hooks are silently skipped. TypeScript: implement <code>Partial<IBroker></code> with typed payloads (<code>BrokerSignalOpenPayload</code>, etc.).</p> | |
| </details> | |
| <hr> | |
| <a id="⚙️-configuration-files-config" class="tsd-anchor"></a><h2 class="tsd-anchor-link">⚙️ Configuration files (<code>config/*</code>)<a href="#⚙️-configuration-files-config" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Loaded from <code>{projectRoot}/config/</code>. The three runtime configs load in order — <strong><code>setup.config</code> → <code>loader.config</code> → <code>alias.config</code></strong> — before any strategy or module code. The UI/Telegram configs resolve <strong>strategy dir → project root → package default</strong> (first match wins) and accept <code>.ts</code>/<code>.cjs</code>/<code>.mjs</code>/<code>.js</code>.</p> | |
| <a id="setupconfig--persistence--one-time-init" class="tsd-anchor"></a><h3 class="tsd-anchor-link"><code>setup.config</code> — persistence & one-time init<a href="#setupconfig--persistence--one-time-init" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Loaded once before any persistence call. <strong>When present, the CLI skips its own default adapter registration</strong> — your config takes full ownership of the persistence layer.</p> | |
| <details> | |
| <summary>MongoDB + Redis via @backtest-kit/mongo</summary> | |
| <p><code>setup()</code> registers all 15 persistence adapters in one call, reading connection params from env (or passed explicitly):</p> | |
| <pre><code class="ts"><span class="hl-0">// config/setup.config.ts</span><br/><span class="hl-6">import</span><span class="hl-2"> { </span><span class="hl-5">setup</span><span class="hl-2"> } </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">'@backtest-kit/mongo'</span><span class="hl-2">;</span><br/><span class="hl-1">setup</span><span class="hl-2">(); </span><span class="hl-0">// or setup({ CC_MONGO_CONNECTION_STRING, CC_REDIS_HOST, CC_REDIS_PORT, CC_REDIS_PASSWORD })</span> | |
| </code><button type="button">Copy</button></pre> | |
| <pre><code class="env">CC_MONGO_CONNECTION_STRING=mongodb://localhost:27017/backtest-kit | |
| CC_REDIS_HOST=127.0.0.1 | |
| CC_REDIS_PORT=6379 | |
| </code><button type="button">Copy</button></pre> | |
| <p>No strategy-code changes — adapters are wired transparently before the first persistence call.</p> | |
| </details> | |
| <a id="loaderconfig--async-startup-gate" class="tsd-anchor"></a><h3 class="tsd-anchor-link"><code>loader.config</code> — async startup gate<a href="#loaderconfig--async-startup-gate" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Loaded <strong>after</strong> <code>setup.config</code>, <strong>before</strong> strategy/module code. Unlike <code>setup.config</code> (side-effect import), it exports a function the CLI <code>await</code>s — use it to wait for an async dependency before the run starts.</p> | |
| <details> | |
| <summary>When to use it, export styles & examples</summary> | |
| <p><strong>Use it to:</strong> wire microfrontends in a monorepo (pre-load sibling packages, hydrate a shared DI container); wait for a DB connection so the backtest fails fast instead of mid-run; warm caches / external APIs (instruments, calendar, fee tables); run schema migrations before signals flow.</p> | |
| <p>Exactly one export style — <strong>never both</strong> (if both present, <code>default</code> wins):</p> | |
| <pre><code class="ts"><span class="hl-0">// config/loader.config.ts — default export (preferred)</span><br/><span class="hl-6">export</span><span class="hl-2"> </span><span class="hl-6">default</span><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> () </span><span class="hl-4">=></span><span class="hl-2"> { </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-5">mongoose</span><span class="hl-2">.</span><span class="hl-1">connect</span><span class="hl-2">(</span><span class="hl-5">process</span><span class="hl-2">.</span><span class="hl-5">env</span><span class="hl-2">.</span><span class="hl-7">CC_MONGO_CONNECTION_STRING</span><span class="hl-2">!); </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-5">redis</span><span class="hl-2">.</span><span class="hl-1">ping</span><span class="hl-2">(); };</span><br/><span class="hl-0">// — or named export</span><br/><span class="hl-6">export</span><span class="hl-2"> </span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-1">loader</span><span class="hl-2"> = </span><span class="hl-4">async</span><span class="hl-2"> () </span><span class="hl-4">=></span><span class="hl-2"> { </span><span class="hl-0">/* … */</span><span class="hl-2"> };</span> | |
| </code><button type="button">Copy</button></pre> | |
| <p><code>@backtest-kit/mongo</code>'s <code>setup()</code> registers adapters synchronously but doesn't block on the connection; gate the run on a real connection here. To stitch microfrontends: <code>import "@my-org/brokers"; import "@my-org/signals";</code> (the <code>@my-org</code> alias is declared in <code>alias.config</code>).</p> | |
| </details> | |
| <a id="aliasconfig--override-any-module-import" class="tsd-anchor"></a><h3 class="tsd-anchor-link"><code>alias.config</code> — override any module import<a href="#aliasconfig--override-any-module-import" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Override any Node module import without touching strategy code. Loaded once on the first <code>import</code> and applied globally — e.g. replace a heavy dependency with a stub for backtesting, or swap an external API for a mock in CI.</p> | |
| <details> | |
| <summary>Formats & async factory</summary> | |
| <pre><code class="ts"><span class="hl-0">// config/alias.config.ts — named export</span><br/><span class="hl-6">export</span><span class="hl-2"> </span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-7">ccxt</span><span class="hl-2"> = </span><span class="hl-1">require</span><span class="hl-2">(</span><span class="hl-3">"./stubs/ccxt.stub.cjs"</span><span class="hl-2">);</span><br/><span class="hl-0">// config/alias.config.cjs — default export</span><br/><span class="hl-10">module</span><span class="hl-2">.</span><span class="hl-10">exports</span><span class="hl-2"> = { </span><span class="hl-5">ccxt:</span><span class="hl-2"> </span><span class="hl-1">require</span><span class="hl-2">(</span><span class="hl-3">"./stubs/ccxt.stub.cjs"</span><span class="hl-2">) };</span> | |
| </code><button type="button">Copy</button></pre> | |
| <p>It may also export an <strong>async factory</strong> the CLI <code>await</code>s before strategy code runs — handy for ESM-only modules that <code>require()</code> would throw on:</p> | |
| <pre><code class="ts"><span class="hl-0">// async factory (default export); or `export const loader = async () => ({...})`</span><br/><span class="hl-6">export</span><span class="hl-2"> </span><span class="hl-6">default</span><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> () </span><span class="hl-4">=></span><span class="hl-2"> ({ </span><span class="hl-5">nanoid:</span><span class="hl-2"> </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-4">import</span><span class="hl-2">(</span><span class="hl-3">"nanoid"</span><span class="hl-2">), </span><span class="hl-3">"p-limit"</span><span class="hl-5">:</span><span class="hl-2"> </span><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-4">import</span><span class="hl-2">(</span><span class="hl-3">"p-limit"</span><span class="hl-2">) });</span> | |
| </code><button type="button">Copy</button></pre> | |
| <p>Both styles supported, never both at once (<code>default</code> wins). When strategy code calls <code>require("ccxt")</code>, the loader checks the alias table first — no monkey-patching of <code>node_modules</code>. Applies to <strong>all</strong> modules in the process (not per-strategy).</p> | |
| </details> | |
| <a id="symbolconfig--notificationconfig--ui-dashboard" class="tsd-anchor"></a><h3 class="tsd-anchor-link"><code>symbol.config</code> & <code>notification.config</code> — UI dashboard<a href="#symbolconfig--notificationconfig--ui-dashboard" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><details> | |
| <summary>symbol.config — restrict/reorder the UI symbol list</summary> | |
| <p>By default the UI shows all exchange symbols. Override with a <code>config/symbol.config</code> (resolution: strategy dir → project root → package default):</p> | |
| <pre><code class="ts"><span class="hl-0">// config/symbol.config.ts</span><br/><span class="hl-6">export</span><span class="hl-2"> </span><span class="hl-4">const</span><span class="hl-2"> </span><span class="hl-7">symbol_list</span><span class="hl-2"> = [</span><br/><span class="hl-2"> { </span><span class="hl-5">icon:</span><span class="hl-2"> </span><span class="hl-3">"/icon/btc.png"</span><span class="hl-2">, </span><span class="hl-5">logo:</span><span class="hl-2"> </span><span class="hl-3">"/icon/128/btc.png"</span><span class="hl-2">, </span><span class="hl-5">symbol:</span><span class="hl-2"> </span><span class="hl-3">"BTCUSDT"</span><span class="hl-2">, </span><span class="hl-5">displayName:</span><span class="hl-2"> </span><span class="hl-3">"Bitcoin"</span><span class="hl-2">, </span><span class="hl-5">color:</span><span class="hl-2"> </span><span class="hl-3">"#F7931A"</span><span class="hl-2">, </span><span class="hl-5">priority:</span><span class="hl-2"> </span><span class="hl-8">50</span><span class="hl-2">, </span><span class="hl-5">description:</span><span class="hl-2"> </span><span class="hl-3">"Bitcoin — the first and most popular cryptocurrency"</span><span class="hl-2"> },</span><br/><span class="hl-2"> { </span><span class="hl-5">icon:</span><span class="hl-2"> </span><span class="hl-3">"/icon/eth.png"</span><span class="hl-2">, </span><span class="hl-5">logo:</span><span class="hl-2"> </span><span class="hl-3">"/icon/128/eth.png"</span><span class="hl-2">, </span><span class="hl-5">symbol:</span><span class="hl-2"> </span><span class="hl-3">"ETHUSDT"</span><span class="hl-2">, </span><span class="hl-5">displayName:</span><span class="hl-2"> </span><span class="hl-3">"Ethereum"</span><span class="hl-2">, </span><span class="hl-5">color:</span><span class="hl-2"> </span><span class="hl-3">"#6F42C1"</span><span class="hl-2">, </span><span class="hl-5">priority:</span><span class="hl-2"> </span><span class="hl-8">50</span><span class="hl-2">, </span><span class="hl-5">description:</span><span class="hl-2"> </span><span class="hl-3">"Ethereum — a blockchain platform for smart contracts"</span><span class="hl-2"> },</span><br/><span class="hl-2">];</span> | |
| </code><button type="button">Copy</button></pre> | |
| </details> | |
| <details> | |
| <summary>notification.config — which notification categories the UI shows</summary> | |
| <p>Defaults (override per strategy):</p> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Key</th> | |
| <th>Default</th> | |
| <th>Description</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><code>signal</code></td> | |
| <td><code>true</code></td> | |
| <td>Signal lifecycle: opened, scheduled, closed, cancelled</td> | |
| </tr> | |
| <tr> | |
| <td><code>risk</code></td> | |
| <td><code>true</code></td> | |
| <td>Risk manager rejections</td> | |
| </tr> | |
| <tr> | |
| <td><code>info</code></td> | |
| <td><code>true</code></td> | |
| <td>Informational messages on an active signal</td> | |
| </tr> | |
| <tr> | |
| <td><code>breakeven</code></td> | |
| <td><code>true</code></td> | |
| <td>Breakeven level reached</td> | |
| </tr> | |
| <tr> | |
| <td><code>common_error</code></td> | |
| <td><code>true</code></td> | |
| <td>Non-fatal runtime errors</td> | |
| </tr> | |
| <tr> | |
| <td><code>critical_error</code></td> | |
| <td><code>true</code></td> | |
| <td>Fatal errors that terminate the session</td> | |
| </tr> | |
| <tr> | |
| <td><code>validation_error</code></td> | |
| <td><code>true</code></td> | |
| <td>Config / input validation errors</td> | |
| </tr> | |
| <tr> | |
| <td><code>strategy_commit</code></td> | |
| <td><code>true</code></td> | |
| <td>All committed actions (partial close, DCA, trailing, …)</td> | |
| </tr> | |
| <tr> | |
| <td><code>partial_loss</code></td> | |
| <td><code>false</code></td> | |
| <td>Partial loss level reached (before commit)</td> | |
| </tr> | |
| <tr> | |
| <td><code>partial_profit</code></td> | |
| <td><code>false</code></td> | |
| <td>Partial profit level reached (before commit)</td> | |
| </tr> | |
| <tr> | |
| <td><code>signal_sync</code></td> | |
| <td><code>false</code></td> | |
| <td>Live order fill / exit confirmations from exchange sync</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| <pre><code class="js"><span class="hl-0">// config/notification.config.ts</span><br/><span class="hl-6">export</span><span class="hl-2"> </span><span class="hl-6">default</span><span class="hl-2"> { </span><span class="hl-5">signal:</span><span class="hl-2"> </span><span class="hl-4">true</span><span class="hl-2">, </span><span class="hl-5">risk:</span><span class="hl-2"> </span><span class="hl-4">true</span><span class="hl-2">, </span><span class="hl-5">info:</span><span class="hl-2"> </span><span class="hl-4">true</span><span class="hl-2">, </span><span class="hl-5">breakeven:</span><span class="hl-2"> </span><span class="hl-4">true</span><span class="hl-2">, </span><span class="hl-5">common_error:</span><span class="hl-2"> </span><span class="hl-4">true</span><span class="hl-2">, </span><span class="hl-5">critical_error:</span><span class="hl-2"> </span><span class="hl-4">true</span><span class="hl-2">, </span><span class="hl-5">validation_error:</span><span class="hl-2"> </span><span class="hl-4">true</span><span class="hl-2">, </span><span class="hl-5">strategy_commit:</span><span class="hl-2"> </span><span class="hl-4">true</span><span class="hl-2">, </span><span class="hl-5">partial_loss:</span><span class="hl-2"> </span><span class="hl-4">false</span><span class="hl-2">, </span><span class="hl-5">partial_profit:</span><span class="hl-2"> </span><span class="hl-4">false</span><span class="hl-2">, </span><span class="hl-5">signal_sync:</span><span class="hl-2"> </span><span class="hl-4">false</span><span class="hl-2"> };</span> | |
| </code><button type="button">Copy</button></pre> | |
| </details> | |
| <a id="telegramconfig--programmatic-message-rendering" class="tsd-anchor"></a><h3 class="tsd-anchor-link"><code>telegram.config</code> — programmatic message rendering<a href="#telegramconfig--programmatic-message-rendering" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><details> | |
| <summary>Override Mustache rendering with get*Markdown methods</summary> | |
| <p>By default messages render from Mustache templates (<code>template/*.mustache</code>). Export an object with any subset of <code>get*Markdown</code> methods (each gets the event payload, returns <code>Promise<string></code>); unimplemented ones fall back to the template.</p> | |
| <pre><code class="ts"><span class="hl-0">// config/telegram.config.ts</span><br/><span class="hl-6">import</span><span class="hl-2"> { </span><span class="hl-5">IStrategyTickResultOpened</span><span class="hl-2">, </span><span class="hl-5">IStrategyTickResultClosed</span><span class="hl-2">, </span><span class="hl-5">RiskContract</span><span class="hl-2"> } </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">"backtest-kit"</span><span class="hl-2">;</span><br/><span class="hl-6">export</span><span class="hl-2"> </span><span class="hl-6">default</span><span class="hl-2"> {</span><br/><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> </span><span class="hl-1">getOpenedMarkdown</span><span class="hl-2">(</span><span class="hl-5">e</span><span class="hl-2">: </span><span class="hl-10">IStrategyTickResultOpened</span><span class="hl-2">) { </span><span class="hl-6">return</span><span class="hl-2"> </span><span class="hl-3">`**Opened** </span><span class="hl-4">${</span><span class="hl-5">e</span><span class="hl-11">.</span><span class="hl-5">symbol</span><span class="hl-4">}</span><span class="hl-3"> at </span><span class="hl-4">${</span><span class="hl-5">e</span><span class="hl-11">.</span><span class="hl-5">priceOpen</span><span class="hl-4">}</span><span class="hl-3">`</span><span class="hl-2">; },</span><br/><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> </span><span class="hl-1">getClosedMarkdown</span><span class="hl-2">(</span><span class="hl-5">e</span><span class="hl-2">: </span><span class="hl-10">IStrategyTickResultClosed</span><span class="hl-2">) { </span><span class="hl-6">return</span><span class="hl-2"> </span><span class="hl-3">`**Closed** </span><span class="hl-4">${</span><span class="hl-5">e</span><span class="hl-11">.</span><span class="hl-5">symbol</span><span class="hl-4">}</span><span class="hl-3"> at </span><span class="hl-4">${</span><span class="hl-5">e</span><span class="hl-11">.</span><span class="hl-5">priceClosed</span><span class="hl-4">}</span><span class="hl-3">`</span><span class="hl-2">; },</span><br/><span class="hl-2"> </span><span class="hl-4">async</span><span class="hl-2"> </span><span class="hl-1">getRiskMarkdown</span><span class="hl-2">(</span><span class="hl-5">e</span><span class="hl-2">: </span><span class="hl-10">RiskContract</span><span class="hl-2">) { </span><span class="hl-6">return</span><span class="hl-2"> </span><span class="hl-3">`**Risk rejected** </span><span class="hl-4">${</span><span class="hl-5">e</span><span class="hl-11">.</span><span class="hl-5">symbol</span><span class="hl-4">}</span><span class="hl-3">`</span><span class="hl-2">; },</span><br/><span class="hl-2">};</span> | |
| </code><button type="button">Copy</button></pre> | |
| <p>Methods → event types: <code>getOpenedMarkdown</code>/<code>getClosedMarkdown</code>/<code>getScheduledMarkdown</code>/<code>getCancelledMarkdown</code> (<code>IStrategyTickResult*</code>), <code>getRiskMarkdown</code> (<code>RiskContract</code>), <code>getPartialProfitMarkdown</code>/<code>getPartialLossMarkdown</code>/<code>getBreakevenMarkdown</code>/<code>getTrailingTakeMarkdown</code>/<code>getTrailingStopMarkdown</code>/<code>getAverageBuyMarkdown</code> (the matching <code>*Commit</code>), <code>getSignalOpenMarkdown</code>/<code>getSignalCloseMarkdown</code> (<code>SignalOpen/CloseContract</code>), <code>getCancelScheduledMarkdown</code>/<code>getClosePendingMarkdown</code> (<code>*Commit</code>), <code>getSignalInfoMarkdown</code> (<code>SignalInfoContract</code>).</p> | |
| </details> | |
| <hr> | |
| <a id="🔔-integrations" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🔔 Integrations<a href="#🔔-integrations" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><a id="web-dashboard---ui" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Web dashboard (<code>--ui</code>)<a href="#web-dashboard---ui" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Starts the <code>@backtest-kit/ui</code> server at <code>http://localhost:60050</code> (host/port via <code>CC_WWWROOT_HOST</code> / <code>CC_WWWROOT_PORT</code>). Restrict the symbol list with <code>symbol.config</code> and notification categories with <code>notification.config</code> (above).</p> | |
| <a id="telegram---telegram" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Telegram (<code>--telegram</code>)<a href="#telegram---telegram" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h3><p>Sends formatted HTML messages with 1m / 15m / 1h price charts for every position event — opened, closed, scheduled, cancelled, risk rejection, partial profit/loss, trailing stop/take, breakeven. Requires <code>CC_TELEGRAM_TOKEN</code> and <code>CC_TELEGRAM_CHANNEL</code>. Customize per-event rendering with <code>telegram.config</code> (above).</p> | |
| <hr> | |
| <a id="🧪-programmatic-api--runmode-args" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🧪 Programmatic API — <code>run(mode, args)</code><a href="#🧪-programmatic-api--runmode-args" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Use the CLI as a library — call <code>run()</code> from your own script, no child process or flag parsing.</p> | |
| <pre><code class="typescript"><span class="hl-6">import</span><span class="hl-2"> { </span><span class="hl-5">run</span><span class="hl-2"> } </span><span class="hl-6">from</span><span class="hl-2"> </span><span class="hl-3">'@backtest-kit/cli'</span><span class="hl-2">;</span><br/><br/><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-1">run</span><span class="hl-2">(</span><span class="hl-3">'backtest'</span><span class="hl-2">, { </span><span class="hl-5">entryPoint:</span><span class="hl-2"> </span><span class="hl-3">'./src/index.mjs'</span><span class="hl-2">, </span><span class="hl-5">symbol:</span><span class="hl-2"> </span><span class="hl-3">'ETHUSDT'</span><span class="hl-2">, </span><span class="hl-5">frame:</span><span class="hl-2"> </span><span class="hl-3">'feb-2024'</span><span class="hl-2">, </span><span class="hl-5">cacheInterval:</span><span class="hl-2"> [</span><span class="hl-3">'1m'</span><span class="hl-2">,</span><span class="hl-3">'15m'</span><span class="hl-2">,</span><span class="hl-3">'1h'</span><span class="hl-2">], </span><span class="hl-5">verbose:</span><span class="hl-2"> </span><span class="hl-4">true</span><span class="hl-2"> });</span><br/><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-1">run</span><span class="hl-2">(</span><span class="hl-3">'paper'</span><span class="hl-2">, { </span><span class="hl-5">entryPoint:</span><span class="hl-2"> </span><span class="hl-3">'./src/index.mjs'</span><span class="hl-2">, </span><span class="hl-5">symbol:</span><span class="hl-2"> </span><span class="hl-3">'BTCUSDT'</span><span class="hl-2"> });</span><br/><span class="hl-6">await</span><span class="hl-2"> </span><span class="hl-1">run</span><span class="hl-2">(</span><span class="hl-3">'live'</span><span class="hl-2">, { </span><span class="hl-5">entryPoint:</span><span class="hl-2"> </span><span class="hl-3">'./src/index.mjs'</span><span class="hl-2">, </span><span class="hl-5">symbol:</span><span class="hl-2"> </span><span class="hl-3">'BTCUSDT'</span><span class="hl-2">, </span><span class="hl-5">verbose:</span><span class="hl-2"> </span><span class="hl-4">true</span><span class="hl-2"> });</span> | |
| </code><button type="button">Copy</button></pre> | |
| <details> | |
| <summary>Payload fields (call once per process)</summary> | |
| <p><code>run()</code> can be called <strong>only once per process</strong> — a second call throws <code>"Should be called only once"</code>. <code>mode</code>: <code>"backtest" | "paper" | "live"</code>.</p> | |
| <p><strong>Backtest:</strong> <code>entryPoint</code>, <code>symbol</code> (<code>"BTCUSDT"</code>), <code>strategy</code> (first registered), <code>exchange</code> (first registered), <code>frame</code> (first registered), <code>cacheInterval</code> (<code>["1m","15m","30m","1h","4h"]</code>), <code>noCache</code> (<code>false</code>), <code>noFlush</code> (<code>false</code>), <code>verbose</code> (<code>false</code>).</p> | |
| <p><strong>Paper / Live:</strong> <code>entryPoint</code>, <code>symbol</code> (<code>"BTCUSDT"</code>), <code>strategy</code> (first registered), <code>exchange</code> (first registered), <code>verbose</code> (<code>false</code>).</p> | |
| </details> | |
| <hr> | |
| <a id="🌍-environment-variables" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🌍 Environment variables<a href="#🌍-environment-variables" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><pre><code class="env">CC_TELEGRAM_TOKEN=your_bot_token_here # required for --telegram (from @BotFather) | |
| CC_TELEGRAM_CHANNEL=-100123456789 # required for --telegram (channel/chat ID) | |
| CC_WWWROOT_HOST=0.0.0.0 # UI bind address (default 0.0.0.0) | |
| CC_WWWROOT_PORT=60050 # UI port (default 60050) | |
| CC_QUICKCHART_HOST= # optional self-hosted QuickChart URL | |
| </code><button type="button">Copy</button></pre> | |
| <details> | |
| <summary>Default behaviors (when a schema isn't registered)</summary> | |
| <table> | |
| <thead> | |
| <tr> | |
| <th>Component</th> | |
| <th>Default</th> | |
| <th>Note</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr> | |
| <td><strong>Exchange</strong></td> | |
| <td>CCXT Binance (<code>default_exchange</code>)</td> | |
| <td>warns; <strong>does not support order book in backtest</strong> — register a custom exchange with snapshot storage if your strategy calls <code>getOrderBook()</code> in backtest</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Frame</strong></td> | |
| <td>February 2024 (<code>default_frame</code>)</td> | |
| <td>warns</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Symbol</strong></td> | |
| <td><code>BTCUSDT</code></td> | |
| <td>—</td> | |
| </tr> | |
| <tr> | |
| <td><strong>Cache intervals</strong></td> | |
| <td><code>1m, 15m, 30m, 4h</code></td> | |
| <td>used if <code>--cacheInterval</code> not given; skip with <code>--noCache</code></td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </details> | |
| <hr> | |
| <a id="💡-why-backtest-kitcli" class="tsd-anchor"></a><h2 class="tsd-anchor-link">💡 Why @backtest-kit/cli<a href="#💡-why-backtest-kitcli" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Instead of writing infrastructure for every project — manual logger/storage/notification setup, CLI arg parsing, exchange registration, cache warming, Telegram bot, SIGINT handling, run wiring — the whole thing is one script:</p> | |
| <pre><code class="json"><span class="hl-2">{ </span><span class="hl-16">"scripts"</span><span class="hl-2">: { </span><span class="hl-16">"backtest"</span><span class="hl-2">: </span><span class="hl-3">"npx @backtest-kit/cli --backtest --ui --telegram ./src/index.mjs"</span><span class="hl-2"> } }</span> | |
| </code><button type="button">Copy</button></pre> | |
| <p>Zero to running backtest in seconds · automatic candle-cache warming with retry · production web dashboard out of the box · Telegram alerts with charts (no chart code) · graceful SIGINT shutdown (no hanging processes) · pluggable logger — override the built-in one with <code>setLogger()</code> from your strategy module · works with any <code>backtest-kit</code> strategy as-is · broker hooks via side-effect modules (no CLI internals to touch).</p> | |
| <a id="🤝-contribute" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🤝 Contribute<a href="#🤝-contribute" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Fork / PR on <a href="https://github.com/tripolskypetr/backtest-kit">GitHub</a>.</p> | |
| <a id="📜-license" class="tsd-anchor"></a><h2 class="tsd-anchor-link">📜 License<a href="#📜-license" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24" aria-hidden="true"><use href="../assets/icons.svg#icon-anchor"></use></svg></a></h2><p>MIT © <a href="https://github.com/tripolskypetr">tripolskypetr</a></p> | |
| </div></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><h3><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-external" name="external"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>External</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div><details open class="tsd-accordion tsd-page-navigation"><summary class="tsd-accordion-summary"><h3><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg>On This Page</h3></summary><div class="tsd-accordion-details"><a href="#📟-backtest-kitcli"><span>📟 @backtest-<wbr/>kit/cli</span></a><ul><li><a href="#🚀-quick-start"><span>🚀 <wbr/>Quick <wbr/>Start</span></a></li><li><a href="#🤔-philosophy--react-vs-nextjs-but-for-trading"><span>🤔 <wbr/>Philosophy — <wbr/>React vs <wbr/>Next.js, but for trading</span></a></li><li><a href="#🗺️-mode-matrix"><span>🗺️ <wbr/>Mode matrix</span></a></li><li><a href="#📈-trading-modes"><span>📈 <wbr/>Trading modes</span></a></li><li><ul><li><a href="#backtest-·-paper-·-live"><span>Backtest · <wbr/>Paper · <wbr/>Live</span></a></li><li><a href="#walker--ab-strategy-comparison"><span>Walker — <wbr/>A/<wbr/>B strategy comparison</span></a></li><li><a href="#main--custom-entry-point-no-trading-harness"><span>Main — custom entry point, no trading harness</span></a></li><li><a href="#parallel-multi-symbol---entry"><span>Parallel multi-<wbr/>symbol (--entry)</span></a></li></ul></li><li><a href="#🛠️-tooling-modes"><span>🛠️ <wbr/>Tooling modes</span></a></li><li><ul><li><a href="#🌲-pine--run-local-pinescript---pine"><span>🌲 <wbr/>Pine — run local <wbr/>Pine<wbr/>Script (--pine)</span></a></li><li><a href="#🎨-editor--visual-pine-script-editor---editor"><span>🎨 <wbr/>Editor — visual <wbr/>Pine <wbr/>Script editor (--editor)</span></a></li><li><a href="#💾-candle-dump---dump"><span>💾 <wbr/>Candle <wbr/>Dump (--dump)</span></a></li><li><a href="#🐞-pnl-debug---pnldebug"><span>🐞 <wbr/>Pn<wbr/>L <wbr/>Debug (--pnldebug)</span></a></li><li><a href="#🐛-broker-debug---brokerdebug"><span>🐛 <wbr/>Broker <wbr/>Debug (--brokerdebug)</span></a></li><li><a href="#🗑️-flush---flush"><span>🗑️ <wbr/>Flush (--flush)</span></a></li></ul></li><li><a href="#🗂️-project--monorepo"><span>🗂️ <wbr/>Project & monorepo</span></a></li><li><ul><li><a href="#scaffolding---init"><span>Scaffolding (--init)</span></a></li><li><a href="#docker---docker"><span>Docker (--docker)</span></a></li><li><a href="#monorepo-cwd-resolution"><span>Monorepo cwd resolution</span></a></li><li><a href="#folder-based-import-aliases"><span>Folder-<wbr/>based import aliases</span></a></li><li><a href="#entry-point-formats"><span>Entry point formats</span></a></li></ul></li><li><a href="#🔌-broker-adapter--transactional-live-orders"><span>🔌 <wbr/>Broker adapter — transactional live orders</span></a></li><li><a href="#⚙️-configuration-files-config"><span>⚙️ <wbr/>Configuration files (config/*)</span></a></li><li><ul><li><a href="#setupconfig--persistence--one-time-init"><span>setup.config — persistence & one-<wbr/>time init</span></a></li><li><a href="#loaderconfig--async-startup-gate"><span>loader.config — async startup gate</span></a></li><li><a href="#aliasconfig--override-any-module-import"><span>alias.config — override any module import</span></a></li><li><a href="#symbolconfig--notificationconfig--ui-dashboard"><span>symbol.config & notification.config — UI dashboard</span></a></li><li><a href="#telegramconfig--programmatic-message-rendering"><span>telegram.config — programmatic message rendering</span></a></li></ul></li><li><a href="#🔔-integrations"><span>🔔 <wbr/>Integrations</span></a></li><li><ul><li><a href="#web-dashboard---ui"><span>Web dashboard (--ui)</span></a></li><li><a href="#telegram---telegram"><span>Telegram (--telegram)</span></a></li></ul></li><li><a href="#🧪-programmatic-api--runmode-args"><span>🧪 <wbr/>Programmatic API — run(mode, args)</span></a></li><li><a href="#🌍-environment-variables"><span>🌍 <wbr/>Environment variables</span></a></li><li><a href="#💡-why-backtest-kitcli"><span>💡 <wbr/>Why @backtest-<wbr/>kit/cli</span></a></li><li><a href="#🤝-contribute"><span>🤝 <wbr/>Contribute</span></a></li><li><a href="#📜-license"><span>📜 <wbr/>License</span></a></li></ul></div></details></div><div class="site-menu"><nav class="tsd-navigation"><a href="../modules.html">backtest-kit</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer><p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer><div class="overlay"></div></body></html> | |
| <!-- Yandex.Metrika counter --> | |
| <script type="text/javascript"> | |
| (function(m,e,t,r,i,k,a){ | |
| m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)}; | |
| m[i].l=1*new Date(); | |
| for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }} | |
| k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a) | |
| })(window, document,'script','https://mc.yandex.ru/metrika/tag.js?id=105455585', 'ym'); | |
| ym(105455585, 'init', {ssr:true, webvisor:true, clickmap:true, ecommerce:"dataLayer", accurateTrackBounce:true, trackLinks:true}); | |
| </script> | |
| <noscript><div><img src="https://mc.yandex.ru/watch/105455585" style="position:absolute; left:-9999px;" alt="" /></div></noscript> | |
| <!-- /Yandex.Metrika counter --> | |
| <!-- Google tag (gtag.js) --> | |
| <script async src="https://www.googletagmanager.com/gtag/js?id=G-3MQZEBBDDR"></script> | |
| <script> | |
| window.dataLayer = window.dataLayer || []; | |
| function gtag(){dataLayer.push(arguments);} | |
| gtag('js', new Date()); | |
| gtag('config', 'G-3MQZEBBDDR'); | |
| </script> | |