backtest-kit-docs / documents /cli_readme.html
tripolskypetr's picture
docs
edafffa
Raw History Blame Contribute Delete
136 kB
<!DOCTYPE html><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"> &amp;&amp; </span><span class="hl-1">npm</span><span class="hl-2"> </span><span class="hl-3">install</span><span class="hl-2"> &amp;&amp; </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">&#39;backtest-kit&#39;</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">&#39;ccxt&#39;</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">&#39;binance&#39;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">&#39;feb-2024&#39;</span><span class="hl-2">, </span><span class="hl-5">interval:</span><span class="hl-2"> </span><span class="hl-3">&#39;1m&#39;</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">&#39;2024-02-01&#39;</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">&#39;2024-02-29&#39;</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">&#39;my-strategy&#39;</span><span class="hl-2">, </span><span class="hl-5">interval:</span><span class="hl-2"> </span><span class="hl-3">&#39;15m&#39;</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">=&gt;</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">&quot;scripts&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2"> </span><span class="hl-16">&quot;backtest&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx @backtest-kit/cli --backtest ./src/index.mjs&quot;</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-16">&quot;paper&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx @backtest-kit/cli --paper ./src/index.mjs&quot;</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-16">&quot;start&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx @backtest-kit/cli --live ./src/index.mjs&quot;</span><br/><span class="hl-2"> },</span><br/><span class="hl-2"> </span><span class="hl-16">&quot;dependencies&quot;</span><span class="hl-2">: { </span><span class="hl-16">&quot;@backtest-kit/cli&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;latest&quot;</span><span class="hl-2">, </span><span class="hl-16">&quot;backtest-kit&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;latest&quot;</span><span class="hl-2">, </span><span class="hl-16">&quot;ccxt&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;latest&quot;</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&amp;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 &amp; 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 &amp; 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>Sweep</strong></td>
<td><code>--sweep</code></td>
<td>Grid sweep over a crowd-ideas feed: profit-before-stop corridor + raw author tracks, axes from a positional JSON config</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>&quot;BTCUSDT&quot;</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>&quot;1m, 15m, 30m, 4h&quot;</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>&quot;signal-open&quot;</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">&quot;scripts&quot;</span><span class="hl-2">: { </span><span class="hl-16">&quot;backtest&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx @backtest-kit/cli --backtest --symbol ETHUSDT --strategy my-strategy --exchange binance --frame feb-2024 --cacheInterval </span><span class="hl-13">\&quot;</span><span class="hl-3">1m, 15m, 1h, 4h</span><span class="hl-13">\&quot;</span><span class="hl-3"> ./src/index.mjs&quot;</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">&quot;scripts&quot;</span><span class="hl-2">: { </span><span class="hl-16">&quot;paper&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx @backtest-kit/cli --paper --symbol BTCUSDT ./src/index.mjs&quot;</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">&quot;scripts&quot;</span><span class="hl-2">: { </span><span class="hl-16">&quot;start&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx @backtest-kit/cli --live --ui --telegram --symbol BTCUSDT ./src/index.mjs&quot;</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>&quot;BTCUSDT&quot;</code>)</td>
</tr>
<tr>
<td><code>--cacheInterval</code></td>
<td>string</td>
<td>Intervals to pre-cache (default <code>&quot;1m, 15m, 30m, 4h&quot;</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/&lt;output&gt;.json</code> and exit</td>
</tr>
<tr>
<td><code>--markdown</code></td>
<td>boolean</td>
<td>Save <code>Walker.getReport()</code> as <code>./dump/&lt;output&gt;.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">&quot;scripts&quot;</span><span class="hl-2">: { </span><span class="hl-16">&quot;main&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx @backtest-kit/cli --main ./tools/fetch_fear_and_greed.ts&quot;</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/&lt;mode&gt;.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">&quot;backtest-kit&quot;</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">&quot;ccxt&quot;</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">&quot;binance&quot;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">&quot;feb-2026&quot;</span><span class="hl-2">, </span><span class="hl-5">interval:</span><span class="hl-2"> </span><span class="hl-3">&quot;1m&quot;</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">&quot;2026-02-01&quot;</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">&quot;2026-02-28&quot;</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">&quot;my-strategy&quot;</span><span class="hl-2">, </span><span class="hl-5">interval:</span><span class="hl-2"> </span><span class="hl-3">&quot;15m&quot;</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">=&gt;</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">&quot;BTCUSDT&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;ETHUSDT&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;SOLUSDT&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;BNBUSDT&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;XRPUSDT&quot;</span><span class="hl-2">]) {</span><br/><span class="hl-2"> </span><span class="hl-0">// optional: await warmCandles({ exchangeName: &quot;binance&quot;, interval: &quot;1m&quot;, 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">&quot;my-strategy&quot;</span><span class="hl-2">, </span><span class="hl-5">exchangeName:</span><span class="hl-2"> </span><span class="hl-3">&quot;binance&quot;</span><span class="hl-2">, </span><span class="hl-5">frameName:</span><span class="hl-2"> </span><span class="hl-3">&quot;feb-2026&quot;</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>Utilities that don't run a strategy. They share one convention, explained once here and referenced below.</p>
<blockquote>
<p><strong>The <code>&lt;mode&gt;.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/&lt;mode&gt;.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>&lt;mode&gt;.module.ts</code> shape (pine / editor / dump / pnldebug / brokerdebug / sweep)</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">&quot;backtest-kit&quot;</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">&quot;ccxt&quot;</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">&quot;my-exchange&quot;</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">=&gt;</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">=&gt;</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">=&gt;</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">=&gt;</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">&quot;2025-09-24T12:00:00.000Z&quot;</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>&quot;BTCUSDT&quot;</code>)</td>
</tr>
<tr>
<td><code>--timeframe</code></td>
<td>string</td>
<td>Candle interval (default <code>&quot;15m&quot;</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>&lt;pine-dir&gt;/dump/{output}.json</code></td>
</tr>
<tr>
<td><code>--jsonl</code></td>
<td>boolean</td>
<td>Write plots as JSONL to <code>&lt;pine-dir&gt;/dump/{output}.jsonl</code></td>
</tr>
<tr>
<td><code>--markdown</code></td>
<td>boolean</td>
<td>Write Markdown table to <code>&lt;pine-dir&gt;/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>&lt;pine-dir&gt;/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">&quot;2026-02-28T00:00:00.000Z&quot;</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>&quot;BTCUSDT&quot;</code>)</td>
</tr>
<tr>
<td><code>--timeframe</code></td>
<td>string</td>
<td>Candle interval (default <code>&quot;15m&quot;</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">&quot;2025-02-25&quot;</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>&quot;BTCUSDT&quot;</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/&lt;output&gt;.{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>&quot;signal-open&quot;</code>)</td>
</tr>
<tr>
<td><code>--symbol</code></td>
<td>string</td>
<td>Trading pair (default <code>&quot;BTCUSDT&quot;</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="πŸŽ›οΈ-sweep---sweep" class="tsd-anchor"></a><h3 class="tsd-anchor-link">πŸŽ›οΈ Sweep (<code>--sweep</code>)<a href="#πŸŽ›οΈ-sweep---sweep" 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>A <strong>grid sweep</strong> over a feed of crowd trading ideas: the grid axes come from a positional JSON config of the consumer β€” from a small feasibility probe to a full parameter search. Without a config the engine defaults apply. Authors are graded by ONE binary outcome β€” <strong>profit-before-stop</strong>: walking each point's own hold window candle by candle, an idea is a HIT when a fixation (the profit lock if lock &gt; 0, OR the trailing arm level) fires BEFORE the hard stop, a MISS when the hard stop fires first, the window times out, or the candles run out. Prints a Markdown report with the corridor share, the ranking winners, and the <strong>raw author tracks</strong> (ideas/hits/hitRate per grading rule) β€” the engine grades every author but bans none; who to trust is userspace.</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">--sweep</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-3">./assets/tv-ideas.normalized.jsonl</span><br/><span class="hl-0"># β†’ engine-default grid; add a config to shape the sweep:</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">--sweep</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-3">./assets/tv-ideas.normalized.jsonl</span><span class="hl-2"> </span><span class="hl-3">./assets/probe.config.json</span>
</code><button type="button">Copy</button></pre>
<details>
<summary>Sweep flags, input format & behavior</summary>
<table>
<thead>
<tr>
<th>Flag</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--sweep</code></td>
<td>boolean</td>
<td>Enable the grid sweep</td>
</tr>
<tr>
<td><code>--symbol</code></td>
<td>string</td>
<td>Trading pair to simulate (default <code>&quot;BTCUSDT&quot;</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>sweep_{SYMBOL}_{TIMESTAMP}</code>)</td>
</tr>
<tr>
<td><code>--json</code></td>
<td>boolean</td>
<td>Save the full <code>ISweepResult</code> to <code>./dump/&lt;output&gt;.json</code></td>
</tr>
<tr>
<td><code>--markdown</code></td>
<td>boolean</td>
<td>Save the summary report to <code>./dump/&lt;output&gt;.md</code></td>
</tr>
<tr>
<td><code>--verbose</code></td>
<td>boolean</td>
<td>Log every sweep lifecycle callback to the console</td>
</tr>
</tbody>
</table>
<p><strong>Positionals:</strong> path to an ideas <code>.jsonl</code> file (required) β€” one idea per line, exact shape <code>{ &quot;id&quot;: number, &quot;ts&quot;: number, &quot;symbol&quot;: string, &quot;direction&quot;: &quot;LONG&quot;|&quot;SHORT&quot;|&quot;NEUTRAL&quot;, &quot;author&quot;: string }</code> β€” and an <strong>optional config <code>.json</code></strong> with the shape <code>{ &quot;gridAxes&quot;?: ISweepGridAxes, &quot;reportOrder&quot;?: &quot;sharpe&quot;|&quot;sortino&quot;|&quot;pnl&quot;|&quot;recovery&quot; }</code>. Both files are validated <strong>before any work starts</strong> β€” a structure mismatch (including an unknown config key) aborts the run with an error naming the field. No config β†’ an empty object β†’ the engine defaults. Ideas of other symbols are filtered out by the engine, so one shared feed serves any <code>--symbol</code>.</p>
<p>Under the hood: one candle pass per idea to the grid's longest hold (lazy chunked fetch through the exchange, persist cache first), flood dedupe (one idea per author per direction per 8h), EVERY author traded (the engine grades every author on the raw per-author track <strong>inside each point's own hold window</strong> and bans none β€” userspace filters on the continuous <code>hitRate</code>), production slot semantics (one open position per author), time-based Sharpe/Sortino over daily equity buckets. The result is a single report bucket β€” <code>result.reports.reports</code> (grid point reports, sorted by <code>reportOrder</code>), <code>result.reports.best</code> (the four ranking winners), and <code>result.reports.tracks</code> (author tracks). Each track line is self-contained (<code>{holdMinutes, profitLockPercent, hardStopPercent, trailingTakePercent, author, ideas, hits, hitRate}</code>) for grep/jq without a join. With <code>--verbose</code> every lifecycle callback (<code>onIdeas</code>, <code>onProfiles</code>, <code>onAuthorsTrained</code>, <code>onGridPoint</code>, <code>onRanking</code>, <code>onDone</code>) is logged to the console as it fires, so long runs show progress. Exchange via <code>sweep.module</code> (see convention above).</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>&lt;entry-dir&gt;/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 &amp; 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"> &amp;&amp; </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">&#39;/.env&#39;</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">&#39;/.env&#39;</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 &quot;utils&quot;</code></td>
<td><code>&lt;cwd&gt;/utils/index.ts</code> (or <code>.js</code>/<code>.mjs</code>/<code>.cjs</code>)</td>
</tr>
<tr>
<td><code>import { calcRSI } from &quot;math/rsi&quot;</code></td>
<td><code>&lt;cwd&gt;/math/rsi.ts</code></td>
</tr>
<tr>
<td><code>import { research } from &quot;logic&quot;</code></td>
<td><code>&lt;cwd&gt;/logic/index.ts</code></td>
</tr>
<tr>
<td><code>import { X } from &quot;logic/contract/ResearchResponse.contract&quot;</code></td>
<td><code>&lt;cwd&gt;/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">&quot;compilerOptions&quot;</span><span class="hl-2">: {</span><br/><span class="hl-2"> </span><span class="hl-16">&quot;moduleResolution&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;bundler&quot;</span><span class="hl-2">,</span><br/><span class="hl-2"> </span><span class="hl-16">&quot;paths&quot;</span><span class="hl-2">: { </span><span class="hl-16">&quot;logic&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;./logic/index.ts&quot;</span><span class="hl-2">], </span><span class="hl-16">&quot;logic/*&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;./logic/*&quot;</span><span class="hl-2">], </span><span class="hl-16">&quot;math&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;./math/index.ts&quot;</span><span class="hl-2">], </span><span class="hl-16">&quot;math/*&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;./math/*&quot;</span><span class="hl-2">], </span><span class="hl-16">&quot;utils&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;./utils/index.ts&quot;</span><span class="hl-2">], </span><span class="hl-16">&quot;utils/*&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;./utils/*&quot;</span><span class="hl-2">] }</span><br/><span class="hl-2"> },</span><br/><span class="hl-2"> </span><span class="hl-16">&quot;include&quot;</span><span class="hl-2">: [</span><span class="hl-3">&quot;./logic&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;./math&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;./utils&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;./content&quot;</span><span class="hl-2">, </span><span class="hl-3">&quot;./modules&quot;</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">&#39;backtest-kit&#39;</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">&#39;./exchange.mjs&#39;</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">&#39;sell&#39;</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">&#39;buy&#39;</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&lt;IBroker&gt;</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 &amp; 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">&#39;@backtest-kit/mongo&#39;</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">=&gt;</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">=&gt;</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 &quot;@my-org/brokers&quot;; import &quot;@my-org/signals&quot;;</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">&quot;./stubs/ccxt.stub.cjs&quot;</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">&quot;./stubs/ccxt.stub.cjs&quot;</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 () =&gt; ({...})`</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">=&gt;</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">&quot;nanoid&quot;</span><span class="hl-2">), </span><span class="hl-3">&quot;p-limit&quot;</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">&quot;p-limit&quot;</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(&quot;ccxt&quot;)</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> &amp; <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">&quot;/icon/btc.png&quot;</span><span class="hl-2">, </span><span class="hl-5">logo:</span><span class="hl-2"> </span><span class="hl-3">&quot;/icon/128/btc.png&quot;</span><span class="hl-2">, </span><span class="hl-5">symbol:</span><span class="hl-2"> </span><span class="hl-3">&quot;BTCUSDT&quot;</span><span class="hl-2">, </span><span class="hl-5">displayName:</span><span class="hl-2"> </span><span class="hl-3">&quot;Bitcoin&quot;</span><span class="hl-2">, </span><span class="hl-5">color:</span><span class="hl-2"> </span><span class="hl-3">&quot;#F7931A&quot;</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">&quot;Bitcoin β€” the first and most popular cryptocurrency&quot;</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">&quot;/icon/eth.png&quot;</span><span class="hl-2">, </span><span class="hl-5">logo:</span><span class="hl-2"> </span><span class="hl-3">&quot;/icon/128/eth.png&quot;</span><span class="hl-2">, </span><span class="hl-5">symbol:</span><span class="hl-2"> </span><span class="hl-3">&quot;ETHUSDT&quot;</span><span class="hl-2">, </span><span class="hl-5">displayName:</span><span class="hl-2"> </span><span class="hl-3">&quot;Ethereum&quot;</span><span class="hl-2">, </span><span class="hl-5">color:</span><span class="hl-2"> </span><span class="hl-3">&quot;#6F42C1&quot;</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">&quot;Ethereum β€” a blockchain platform for smart contracts&quot;</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>order_sync</code></td>
<td><code>false</code></td>
<td>Live order fill / exit confirmations from exchange sync</td>
</tr>
<tr>
<td><code>order_check</code></td>
<td><code>false</code></td>
<td>Live order ping / pending 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&lt;string&gt;</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">&quot;backtest-kit&quot;</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">&#39;@backtest-kit/cli&#39;</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">&#39;backtest&#39;</span><span class="hl-2">, { </span><span class="hl-5">entryPoint:</span><span class="hl-2"> </span><span class="hl-3">&#39;./src/index.mjs&#39;</span><span class="hl-2">, </span><span class="hl-5">symbol:</span><span class="hl-2"> </span><span class="hl-3">&#39;ETHUSDT&#39;</span><span class="hl-2">, </span><span class="hl-5">frame:</span><span class="hl-2"> </span><span class="hl-3">&#39;feb-2024&#39;</span><span class="hl-2">, </span><span class="hl-5">cacheInterval:</span><span class="hl-2"> [</span><span class="hl-3">&#39;1m&#39;</span><span class="hl-2">,</span><span class="hl-3">&#39;15m&#39;</span><span class="hl-2">,</span><span class="hl-3">&#39;1h&#39;</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">&#39;paper&#39;</span><span class="hl-2">, { </span><span class="hl-5">entryPoint:</span><span class="hl-2"> </span><span class="hl-3">&#39;./src/index.mjs&#39;</span><span class="hl-2">, </span><span class="hl-5">symbol:</span><span class="hl-2"> </span><span class="hl-3">&#39;BTCUSDT&#39;</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">&#39;live&#39;</span><span class="hl-2">, { </span><span class="hl-5">entryPoint:</span><span class="hl-2"> </span><span class="hl-3">&#39;./src/index.mjs&#39;</span><span class="hl-2">, </span><span class="hl-5">symbol:</span><span class="hl-2"> </span><span class="hl-3">&#39;BTCUSDT&#39;</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>&quot;Should be called only once&quot;</code>. <code>mode</code>: <code>&quot;backtest&quot; | &quot;paper&quot; | &quot;live&quot;</code>.</p>
<p><strong>Backtest:</strong> <code>entryPoint</code>, <code>symbol</code> (<code>&quot;BTCUSDT&quot;</code>), <code>strategy</code> (first registered), <code>exchange</code> (first registered), <code>frame</code> (first registered), <code>cacheInterval</code> (<code>[&quot;1m&quot;,&quot;15m&quot;,&quot;30m&quot;,&quot;1h&quot;,&quot;4h&quot;]</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>&quot;BTCUSDT&quot;</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">&quot;scripts&quot;</span><span class="hl-2">: { </span><span class="hl-16">&quot;backtest&quot;</span><span class="hl-2">: </span><span class="hl-3">&quot;npx @backtest-kit/cli --backtest --ui --telegram ./src/index.mjs&quot;</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="#πŸŽ›οΈ-sweep---sweep"><span>πŸŽ›οΈ <wbr/>Sweep (--sweep)</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 &amp; 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 &amp; 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 &amp; 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>