@backtest-kit/cli 13.6.0 โ 14.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +552 -1586
- package/build/index.cjs +4 -4
- package/build/index.mjs +4 -4
- package/docker/package.json +8 -8
- package/package.json +15 -15
- package/template/project/package.mustache +7 -7
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# ๐ @backtest-kit/cli
|
|
4
4
|
|
|
5
|
-
> Zero-boilerplate CLI for
|
|
5
|
+
> Zero-boilerplate CLI for [backtest-kit](https://www.npmjs.com/package/backtest-kit). 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.
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
|
|
@@ -10,1898 +10,864 @@
|
|
|
10
10
|
[](https://npmjs.org/package/@backtest-kit/cli)
|
|
11
11
|
[]()
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
๐ **[Docs](https://backtest-kit.github.io/documents/article_07_ai_news_trading_signals.html)** ยท ๐ **[Reference implementation](https://github.com/tripolskypetr/backtest-kit/tree/master/example)** ยท ๐ **[GitHub](https://github.com/tripolskypetr/backtest-kit)**
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
> **New here?** The fastest real setup is to clone the [reference implementation](https://github.com/tripolskypetr/backtest-kit/tree/master/example) โ a working news-sentiment AI trading system with LLM forecasting, multi-timeframe data, and a documented February 2026 backtest. Start there, not from scratch.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
---
|
|
18
18
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
Minimal scaffold โ all boilerplate stays inside @backtest-kit/cli:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
npx @backtest-kit/cli --init --output backtest-kit-project
|
|
25
|
-
cd backtest-kit-project
|
|
26
|
-
npm install
|
|
27
|
-
npm start -- --help
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## ๐ค Philosophy
|
|
31
|
-
|
|
32
|
-
`@backtest-kit/cli` is designed to do **two things well** โ and the same tool covers both.
|
|
33
|
-
|
|
34
|
-
**1. The lightest possible runner for a solo quant on day one.**
|
|
35
|
-
|
|
36
|
-
You write a strategy file, point the CLI at it, and you're trading. No DI container to learn, no project scaffold to fight, no infrastructure code to copy-paste. One developer, one strategy, one command:
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npx @backtest-kit/cli --init
|
|
40
|
-
npx @backtest-kit/cli --backtest ./content/feb_2026.strategy/index.ts
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
That's the whole onboarding. The first day you have an idea, you can backtest it. The first week you have an edge, you can paper-trade it. The first month you have a P&L, you can run it live โ same CLI, different flag.
|
|
44
|
-
|
|
45
|
-
**2. Built-in monorepo tooling for when the business takes off.**
|
|
46
|
-
|
|
47
|
-
The moment you start making money is the worst possible moment to rewrite your stack in another language. So the CLI is also a monorepo-grade runner from day one โ even if you don't use it that way at first.
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
monorepo/
|
|
51
|
-
โโโ content/
|
|
52
|
-
โ โโโ feb_2026.strategy/
|
|
53
|
-
โ โโโ feb_2026.strategy.ts # strategy production code
|
|
54
|
-
โ โโโ feb_2026.test.ts # developer playground
|
|
55
|
-
โโโ packages/
|
|
56
|
-
โ โโโ shared-broker/ # shared broker code
|
|
57
|
-
โ โโโ shared-signals/ # common indicators (RSI, MACD)
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
As a result: you used to backtest your first idea is the same tool you use to run a desk of strategies in production. No rewrite, no language switch, no framework migration when the business scales โ only more files in the monorepo.
|
|
62
|
-
|
|
63
|
-
## โจ Features
|
|
64
|
-
|
|
65
|
-
- ๐ **Zero Config**: Run `npx @backtest-kit/cli --backtest ./strategy.mjs` โ no boilerplate needed
|
|
66
|
-
- ๐ **Four Modes**: Backtest on historical data, walker A/B comparison, paper trade on live prices, or deploy live bots
|
|
67
|
-
- ๐พ **Auto Candle Cache**: Warms OHLCV cache for all required intervals before backtest starts
|
|
68
|
-
- ๐ **Web Dashboard**: Launch `@backtest-kit/ui` with a single `--ui` flag
|
|
69
|
-
- ๐ฌ **Telegram Alerts**: Send formatted trade notifications with charts via `--telegram`
|
|
70
|
-
- ๐ **Default Binance**: CCXT Binance exchange schema registered automatically when none is provided
|
|
71
|
-
- ๐งฉ **Module Hooks**: Drop a `live.module.mjs`, `paper.module.mjs`, or `backtest.module.mjs` to register a `Broker` adapter. No manual wiring needed.
|
|
72
|
-
- ๐๏ธ **Transactional Live Orders**: Broker adapter intercepts every trade mutation before internal state changes โ exchange rejection rolls back the operation atomically.
|
|
73
|
-
- ๐ **Pluggable Logger**: Override the built-in logger with `setLogger()` from your strategy module
|
|
74
|
-
- ๐ **Graceful Shutdown**: SIGINT stops the active run and cleans up all subscriptions safely
|
|
75
|
-
|
|
76
|
-
## ๐ What It Does
|
|
77
|
-
|
|
78
|
-
`@backtest-kit/cli` wraps the `backtest-kit` engine and resolves all scaffolding automatically:
|
|
79
|
-
|
|
80
|
-
| Mode | Command Line Args | Description |
|
|
81
|
-
|------------------|----------------------------|----------------------------------------------|
|
|
82
|
-
| **Backtest** | `--backtest` | Run strategy on historical candle data |
|
|
83
|
-
| **Walker** | `--walker` | A/B compare multiple strategies on the same historical data |
|
|
84
|
-
| **Paper** | `--paper` | Live prices, no real orders |
|
|
85
|
-
| **Live** | `--live` | Real trades via exchange API |
|
|
86
|
-
| **Main** | `--main` | Run a custom entry point with a prepared environment, no trading harness |
|
|
87
|
-
| **UI Dashboard** | `--ui` | Web dashboard at `http://localhost:60050` |
|
|
88
|
-
| **Telegram** | `--telegram` | Trade notifications with price charts |
|
|
89
|
-
| **PineScript** | `--pine` | Run a local `.pine` indicator against exchange data |
|
|
90
|
-
| **Pine Editor** | `--editor` | Open the visual Pine Script editor in the browser |
|
|
91
|
-
| **Candle Dump** | `--dump` | Fetch and save raw OHLCV candles to a file |
|
|
92
|
-
| **PnL Debug** | `--pnldebug` | Simulate per-minute PnL for a given entry price and direction |
|
|
93
|
-
| **Broker Debug** | `--brokerdebug` | Fire a single broker commit against the live broker adapter |
|
|
94
|
-
| **Flush** | `--flush` | Delete report/log/markdown/agent folders from strategy dump dir |
|
|
95
|
-
| **Init Project** | `--init` | Scaffold a new backtest-kit project |
|
|
96
|
-
|
|
97
|
-
## ๐ Installation
|
|
98
|
-
|
|
99
|
-
Add `@backtest-kit/cli` to your project and wire it up in `package.json` scripts:
|
|
19
|
+
## ๐ Quick Start
|
|
100
20
|
|
|
101
21
|
```bash
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
```json
|
|
106
|
-
{
|
|
107
|
-
"scripts": {
|
|
108
|
-
"backtest": "npx @backtest-kit/cli --backtest ./src/index.mjs",
|
|
109
|
-
"paper": "npx @backtest-kit/cli --paper ./src/index.mjs",
|
|
110
|
-
"start": "npx @backtest-kit/cli --live ./src/index.mjs"
|
|
111
|
-
},
|
|
112
|
-
"dependencies": {
|
|
113
|
-
"@backtest-kit/cli": "latest",
|
|
114
|
-
"backtest-kit": "latest",
|
|
115
|
-
"ccxt": "latest"
|
|
116
|
-
}
|
|
117
|
-
}
|
|
22
|
+
# Scaffold a project (boilerplate stays inside the CLI; docs auto-fetched)
|
|
23
|
+
npx @backtest-kit/cli --init --output backtest-kit-project
|
|
24
|
+
cd backtest-kit-project && npm install && npm start -- --help
|
|
118
25
|
```
|
|
119
26
|
|
|
120
|
-
|
|
27
|
+
The whole onboarding is: write a strategy file that registers schemas via `backtest-kit`, point the CLI at it, choose a flag.
|
|
121
28
|
|
|
122
29
|
```bash
|
|
123
|
-
npx @backtest-kit/cli --backtest ./
|
|
30
|
+
npx @backtest-kit/cli --backtest ./content/feb_2026.strategy/index.ts --symbol BTCUSDT
|
|
124
31
|
```
|
|
125
32
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
Create your strategy entry point (`src/index.mjs`). The file registers schemas via `backtest-kit` โ `@backtest-kit/cli` is only the runner:
|
|
33
|
+
<details>
|
|
34
|
+
<summary>The strategy entry point (the CLI is only the runner)</summary>
|
|
129
35
|
|
|
130
36
|
```javascript
|
|
131
|
-
// src/index.mjs
|
|
37
|
+
// src/index.mjs โ registers schemas via backtest-kit; @backtest-kit/cli just runs it
|
|
132
38
|
import { addStrategySchema, addExchangeSchema, addFrameSchema } from 'backtest-kit';
|
|
133
39
|
import ccxt from 'ccxt';
|
|
134
40
|
|
|
135
|
-
// Register exchange
|
|
136
41
|
addExchangeSchema({
|
|
137
42
|
exchangeName: 'binance',
|
|
138
43
|
getCandles: async (symbol, interval, since, limit) => {
|
|
139
44
|
const exchange = new ccxt.binance();
|
|
140
45
|
const ohlcv = await exchange.fetchOHLCV(symbol, interval, since.getTime(), limit);
|
|
141
|
-
return ohlcv.map(([timestamp, open, high, low, close, volume]) =>
|
|
142
|
-
timestamp, open, high, low, close, volume
|
|
143
|
-
}));
|
|
46
|
+
return ohlcv.map(([timestamp, open, high, low, close, volume]) =>
|
|
47
|
+
({ timestamp, open, high, low, close, volume }));
|
|
144
48
|
},
|
|
145
49
|
formatPrice: (symbol, price) => price.toFixed(2),
|
|
146
50
|
formatQuantity: (symbol, quantity) => quantity.toFixed(8),
|
|
147
51
|
});
|
|
148
52
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
frameName: 'feb-2024',
|
|
152
|
-
interval: '1m',
|
|
153
|
-
startDate: new Date('2024-02-01'),
|
|
154
|
-
endDate: new Date('2024-02-29'),
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// Register strategy
|
|
158
|
-
addStrategySchema({
|
|
159
|
-
strategyName: 'my-strategy',
|
|
160
|
-
interval: '15m',
|
|
161
|
-
getSignal: async (symbol) => {
|
|
162
|
-
// return signal or null
|
|
163
|
-
return null;
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
Run a backtest:
|
|
169
|
-
|
|
170
|
-
```bash
|
|
171
|
-
npm run backtest -- --symbol BTCUSDT
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
Run with UI dashboard and Telegram:
|
|
175
|
-
|
|
176
|
-
```bash
|
|
177
|
-
npm run backtest -- --symbol BTCUSDT --ui --telegram
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
Run live trading:
|
|
181
|
-
|
|
182
|
-
```bash
|
|
183
|
-
npm start -- --symbol BTCUSDT --ui
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
## ๐๏ธ CLI Flags
|
|
187
|
-
|
|
188
|
-
| Command Line Args | Type | Description |
|
|
189
|
-
|---------------------------|---------|--------------------------------------------------------------------|
|
|
190
|
-
| `--backtest` | boolean | Run historical backtest (default: `false`) |
|
|
191
|
-
| `--walker` | boolean | Run Walker A/B strategy comparison (default: `false`) |
|
|
192
|
-
| `--paper` | boolean | Paper trading (live prices, no orders) (default: `false`) |
|
|
193
|
-
| `--live` | boolean | Run live trading (default: `false`) |
|
|
194
|
-
| `--main` | boolean | Run a custom entry point with a prepared environment, no trading harness (default: `false`) |
|
|
195
|
-
| `--ui` | boolean | Start web UI dashboard (default: `false`) |
|
|
196
|
-
| `--telegram` | boolean | Enable Telegram notifications (default: `false`) |
|
|
197
|
-
| `--verbose` | boolean | Log each candle fetch (default: `false`) |
|
|
198
|
-
| `--noCache` | boolean | Skip candle cache warming before backtest (default: `false`) |
|
|
199
|
-
| `--noFlush` | boolean | Skip removing report/log/markdown/agent folders before backtest run (default: `false`) |
|
|
200
|
-
| `--symbol` | string | Trading pair (default: `"BTCUSDT"`) |
|
|
201
|
-
| `--strategy` | string | Strategy name (default: first registered) |
|
|
202
|
-
| `--exchange` | string | Exchange name (default: first registered) |
|
|
203
|
-
| `--frame` | string | Backtest frame name (default: first registered) |
|
|
204
|
-
| `--cacheInterval` | string | Intervals to pre-cache before backtest (default: `"1m, 15m, 30m, 4h"`) |
|
|
205
|
-
| `--brokerdebug` | boolean | Fire a single broker commit against the live broker adapter (default: `false`) |
|
|
206
|
-
| `--commit` | string | Commit type for `--brokerdebug` (default: `"signal-open"`) |
|
|
207
|
-
|
|
208
|
-
**Positional argument (required):** path to your strategy entry point file (set once in `package.json` scripts).
|
|
209
|
-
|
|
210
|
-
```json
|
|
211
|
-
{
|
|
212
|
-
"scripts": {
|
|
213
|
-
"backtest": "npx @backtest-kit/cli --backtest ./src/index.mjs"
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
## ๐ Execution Modes
|
|
219
|
-
|
|
220
|
-
### Backtest
|
|
221
|
-
|
|
222
|
-
Runs the strategy against historical candle data using a registered `FrameSchema`.
|
|
223
|
-
|
|
224
|
-
```json
|
|
225
|
-
{
|
|
226
|
-
"scripts": {
|
|
227
|
-
"backtest": "npx @backtest-kit/cli --backtest --symbol ETHUSDT --strategy my-strategy --exchange binance --frame feb-2024 --cacheInterval \"1m, 15m, 1h, 4h\" ./src/index.mjs"
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
```bash
|
|
233
|
-
npm run backtest
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
Before running, the CLI removes the `report`, `log`, `markdown`, and `agent` folders from the strategy's `dump/` directory, then warms the candle cache for every interval in `--cacheInterval`. On the next run, cached data is used directly โ no API calls needed. Pass `--noCache` to skip cache warming, `--noFlush` to keep existing output folders.
|
|
237
|
-
|
|
238
|
-
### Paper Trading
|
|
239
|
-
|
|
240
|
-
Connects to the live exchange but does not place real orders. Identical code path to live โ safe for strategy validation.
|
|
241
|
-
|
|
242
|
-
```json
|
|
243
|
-
{
|
|
244
|
-
"scripts": {
|
|
245
|
-
"paper": "npx @backtest-kit/cli --paper --symbol BTCUSDT ./src/index.mjs"
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
```
|
|
53
|
+
addFrameSchema({ frameName: 'feb-2024', interval: '1m',
|
|
54
|
+
startDate: new Date('2024-02-01'), endDate: new Date('2024-02-29') });
|
|
249
55
|
|
|
250
|
-
|
|
251
|
-
|
|
56
|
+
addStrategySchema({ strategyName: 'my-strategy', interval: '15m',
|
|
57
|
+
getSignal: async (symbol) => null }); // return a signal or null
|
|
252
58
|
```
|
|
253
59
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
Deploys a real trading bot. Requires exchange API keys configured in your `.env` or environment.
|
|
60
|
+
Wire it into `package.json` once and the positional path never changes:
|
|
257
61
|
|
|
258
62
|
```json
|
|
259
63
|
{
|
|
260
64
|
"scripts": {
|
|
261
|
-
"
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
```bash
|
|
267
|
-
npm start
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
### Walker โ A/B Strategy Comparison
|
|
271
|
-
|
|
272
|
-
Runs the same historical period against multiple strategy files and prints a ranked comparison report. Use it to pick the best variant before deploying to backtest or live.
|
|
273
|
-
|
|
274
|
-
```json
|
|
275
|
-
{
|
|
276
|
-
"scripts": {
|
|
277
|
-
"walker": "npx @backtest-kit/cli --walker --symbol BTCUSDT --noCache ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts"
|
|
278
|
-
}
|
|
65
|
+
"backtest": "npx @backtest-kit/cli --backtest ./src/index.mjs",
|
|
66
|
+
"paper": "npx @backtest-kit/cli --paper ./src/index.mjs",
|
|
67
|
+
"start": "npx @backtest-kit/cli --live ./src/index.mjs"
|
|
68
|
+
},
|
|
69
|
+
"dependencies": { "@backtest-kit/cli": "latest", "backtest-kit": "latest", "ccxt": "latest" }
|
|
279
70
|
}
|
|
280
71
|
```
|
|
281
72
|
|
|
282
73
|
```bash
|
|
283
|
-
npm run
|
|
74
|
+
npm run backtest -- --symbol BTCUSDT --ui --telegram # add integrations with flags
|
|
284
75
|
```
|
|
285
76
|
|
|
286
|
-
|
|
77
|
+
</details>
|
|
287
78
|
|
|
288
|
-
|
|
79
|
+
---
|
|
289
80
|
|
|
290
|
-
|
|
81
|
+
## ๐ค Philosophy โ React vs Next.js, but for trading
|
|
291
82
|
|
|
292
|
-
|
|
293
|
-
|------|------|-------------|
|
|
294
|
-
| `--walker` | boolean | Enable Walker comparison mode |
|
|
295
|
-
| `--symbol` | string | Trading pair (default: `"BTCUSDT"`) |
|
|
296
|
-
| `--cacheInterval` | string | Intervals to pre-cache (default: `"1m, 15m, 30m, 4h"`) |
|
|
297
|
-
| `--noCache` | boolean | Skip candle cache warming (default: `false`) |
|
|
298
|
-
| `--noFlush` | boolean | Skip removing report/log/markdown/agent folders before walker run (default: `false`) |
|
|
299
|
-
| `--verbose` | boolean | Log each candle fetch and strategy progress (default: `false`) |
|
|
300
|
-
| `--output` | string | Output file base name (default: `walker_{SYMBOL}_{TIMESTAMP}`) |
|
|
301
|
-
| `--json` | boolean | Save results as JSON to `./dump/<output>.json` |
|
|
302
|
-
| `--markdown` | boolean | Save report as Markdown to `./dump/<output>.md` |
|
|
83
|
+
`@backtest-kit/cli` does **two things well with one tool**.
|
|
303
84
|
|
|
304
|
-
**
|
|
85
|
+
**1. The lightest runner for a solo quant on day one.** Write a strategy, point the CLI at it, you're trading. No DI container to learn, no scaffold to fight, no infra to copy-paste. The day you have an idea you can backtest it; the week you have an edge you can paper-trade it; the month you have a P&L you can run it live โ same CLI, different flag.
|
|
305
86
|
|
|
306
|
-
-
|
|
307
|
-
- `--json` โ save `Walker.getData()` result as JSON and exit
|
|
308
|
-
- `--markdown` โ save `Walker.getReport()` as `.md` file and exit
|
|
87
|
+
**2. A monorepo-grade runner for when the business takes off.** 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 `.env`, 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.
|
|
309
88
|
|
|
310
|
-
|
|
89
|
+
---
|
|
311
90
|
|
|
312
|
-
|
|
91
|
+
## ๐บ๏ธ Mode matrix
|
|
313
92
|
|
|
314
|
-
|
|
315
|
-
npx @backtest-kit/cli --walker \
|
|
316
|
-
--symbol BTCUSDT \
|
|
317
|
-
--noCache \
|
|
318
|
-
--markdown \
|
|
319
|
-
--output feb_2026_comparison \
|
|
320
|
-
./content/feb_2026_v1.strategy.ts \
|
|
321
|
-
./content/feb_2026_v2.strategy.ts \
|
|
322
|
-
./content/feb_2026_v3.strategy.ts
|
|
323
|
-
# โ ./dump/feb_2026_comparison.md
|
|
324
|
-
```
|
|
93
|
+
Every invocation is **one mode** (a primary flag) + a positional strategy/entry path + optional modifiers. `--ui` and `--telegram` are integrations that attach to any trading mode.
|
|
325
94
|
|
|
326
|
-
|
|
95
|
+
| Mode | Flag | What it does |
|
|
96
|
+
|------|------|--------------|
|
|
97
|
+
| **Backtest** | `--backtest` | Run a strategy on historical candle data (uses a `FrameSchema`) |
|
|
98
|
+
| **Paper** | `--paper` | Live prices, no real orders โ identical code path to live |
|
|
99
|
+
| **Live** | `--live` | Real trades via exchange API |
|
|
100
|
+
| **Walker** | `--walker` | A/B-compare multiple strategies on the same history, ranked report |
|
|
101
|
+
| **Main** | `--main` | Run a custom entry point with the full environment prepared, **no** trading harness |
|
|
102
|
+
| **Pine** | `--pine` | Run a local `.pine` indicator against exchange data |
|
|
103
|
+
| **Editor** | `--editor` | Open the visual Pine Script editor in the browser |
|
|
104
|
+
| **Candle Dump** | `--dump` | Fetch & save raw OHLCV candles to a file |
|
|
105
|
+
| **PnL Debug** | `--pnldebug` | Simulate per-minute PnL for a given entry price & direction |
|
|
106
|
+
| **Broker Debug** | `--brokerdebug` | Fire a single broker commit against the live adapter |
|
|
107
|
+
| **Flush** | `--flush` | Delete report/log/markdown/agent folders from a strategy dump dir |
|
|
108
|
+
| **Init** | `--init` | Scaffold a new project |
|
|
109
|
+
| **Docker** | `--docker` | Scaffold a self-contained Docker workspace |
|
|
110
|
+
| *modifiers* | `--ui` ยท `--telegram` ยท `--entry` | Web dashboard ยท Telegram alerts ยท fan out one strategy across many symbols |
|
|
327
111
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
```json
|
|
331
|
-
{
|
|
332
|
-
"scripts": {
|
|
333
|
-
"main": "npx @backtest-kit/cli --main ./tools/fetch_fear_and_greed.ts"
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
```bash
|
|
339
|
-
npm run main
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
**Main-specific flags:**
|
|
112
|
+
<details>
|
|
113
|
+
<summary>Complete core flag reference</summary>
|
|
343
114
|
|
|
344
115
|
| Flag | Type | Description |
|
|
345
116
|
|------|------|-------------|
|
|
346
|
-
| `--
|
|
347
|
-
| `--
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
The `--entry` flag is a modifier โ combine it with exactly one of `--backtest` / `--live` / `--paper` / `--walker`. One positional: the path to your entry file.
|
|
364
|
-
|
|
365
|
-
```bash
|
|
366
|
-
npx @backtest-kit/cli --backtest --entry ./src/multi-symbol.mjs
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### Example: backtest a strategy on five symbols at once
|
|
370
|
-
|
|
371
|
-
```javascript
|
|
372
|
-
// src/multi-symbol.mjs
|
|
373
|
-
import {
|
|
374
|
-
addExchangeSchema,
|
|
375
|
-
addFrameSchema,
|
|
376
|
-
addStrategySchema,
|
|
377
|
-
Backtest,
|
|
378
|
-
warmCandles,
|
|
379
|
-
} from "backtest-kit";
|
|
380
|
-
import ccxt from "ccxt";
|
|
381
|
-
|
|
382
|
-
addExchangeSchema({
|
|
383
|
-
exchangeName: "binance",
|
|
384
|
-
getCandles: async (symbol, interval, since, limit) => {
|
|
385
|
-
const exchange = new ccxt.binance();
|
|
386
|
-
const ohlcv = await exchange.fetchOHLCV(symbol, interval, since.getTime(), limit);
|
|
387
|
-
return ohlcv.map(([timestamp, open, high, low, close, volume]) => ({
|
|
388
|
-
timestamp, open, high, low, close, volume,
|
|
389
|
-
}));
|
|
390
|
-
},
|
|
391
|
-
formatPrice: (symbol, price) => price.toFixed(2),
|
|
392
|
-
formatQuantity: (symbol, quantity) => quantity.toFixed(8),
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
addFrameSchema({
|
|
396
|
-
frameName: "feb-2026",
|
|
397
|
-
interval: "1m",
|
|
398
|
-
startDate: new Date("2026-02-01"),
|
|
399
|
-
endDate: new Date("2026-02-28"),
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
addStrategySchema({
|
|
403
|
-
strategyName: "my-strategy",
|
|
404
|
-
interval: "15m",
|
|
405
|
-
getSignal: async (symbol) => null,
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
// Decide the symbol set yourself โ from a UI, database, API, or just a list.
|
|
409
|
-
const symbols = ["BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT", "XRPUSDT"];
|
|
410
|
-
|
|
411
|
-
for (const symbol of symbols) {
|
|
412
|
-
|
|
413
|
-
//
|
|
414
|
-
// Optional
|
|
415
|
-
//
|
|
416
|
-
// await warmCandles({
|
|
417
|
-
// exchangeName: "binance,
|
|
418
|
-
// from: new Date("2026-01-01T00:00:00Z"),
|
|
419
|
-
// to: new Date("2026-01-31T23:59:59Z"),
|
|
420
|
-
// interval: "1m",
|
|
421
|
-
// symbol,
|
|
422
|
-
// })
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
Backtest.background(symbol, {
|
|
426
|
-
strategyName: "my-strategy",
|
|
427
|
-
exchangeName: "binance",
|
|
428
|
-
frameName: "feb-2026",
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
```
|
|
117
|
+
| `--backtest` | boolean | Run historical backtest (default `false`) |
|
|
118
|
+
| `--walker` | boolean | Run Walker A/B comparison (default `false`) |
|
|
119
|
+
| `--paper` | boolean | Paper trading โ live prices, no orders (default `false`) |
|
|
120
|
+
| `--live` | boolean | Run live trading (default `false`) |
|
|
121
|
+
| `--main` | boolean | Custom entry point, no trading harness (default `false`) |
|
|
122
|
+
| `--ui` | boolean | Start web UI dashboard (default `false`) |
|
|
123
|
+
| `--telegram` | boolean | Enable Telegram notifications (default `false`) |
|
|
124
|
+
| `--verbose` | boolean | Log each candle fetch (default `false`) |
|
|
125
|
+
| `--noCache` | boolean | Skip candle cache warming before backtest (default `false`) |
|
|
126
|
+
| `--noFlush` | boolean | Skip removing report/log/markdown/agent folders before run (default `false`) |
|
|
127
|
+
| `--symbol` | string | Trading pair (default `"BTCUSDT"`) |
|
|
128
|
+
| `--strategy` | string | Strategy name (default: first registered) |
|
|
129
|
+
| `--exchange` | string | Exchange name (default: first registered) |
|
|
130
|
+
| `--frame` | string | Backtest frame name (default: first registered) |
|
|
131
|
+
| `--cacheInterval` | string | Intervals to pre-cache (default `"1m, 15m, 30m, 4h"`) |
|
|
132
|
+
| `--brokerdebug` | boolean | Fire a single broker commit against the live adapter (default `false`) |
|
|
133
|
+
| `--commit` | string | Commit type for `--brokerdebug` (default `"signal-open"`) |
|
|
432
134
|
|
|
433
|
-
|
|
135
|
+
**Positional argument (required):** path to your strategy entry point file โ set once in `package.json` scripts. Tool-specific flags (`--pine`, `--dump`, `--pnldebug`, `--docker`, โฆ) are documented in their sections below.
|
|
434
136
|
|
|
435
|
-
|
|
137
|
+
</details>
|
|
436
138
|
|
|
437
|
-
|
|
139
|
+
---
|
|
438
140
|
|
|
439
|
-
|
|
141
|
+
## ๐ Trading modes
|
|
440
142
|
|
|
441
|
-
|
|
143
|
+
The four modes that actually run strategies share one engine and one set of guarantees โ only the clock and the order routing differ.
|
|
442
144
|
|
|
443
|
-
|
|
444
|
-
process.chdir(path.dirname(entryPoint)) // cwd โ strategy directory
|
|
445
|
-
dotenv.config({ path: rootDir + '/.env' }) // load root .env first
|
|
446
|
-
dotenv.config({ path: strategyDir + '/.env', override: true }) // strategy .env overrides
|
|
447
|
-
```
|
|
145
|
+
### Backtest ยท Paper ยท Live
|
|
448
146
|
|
|
449
|
-
|
|
147
|
+
<details>
|
|
148
|
+
<summary>How each behaves</summary>
|
|
450
149
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
```
|
|
454
|
-
monorepo/
|
|
455
|
-
โโโ package.json # root scripts (one per strategy)
|
|
456
|
-
โโโ .env # shared API keys (exchange, Telegram, etc.)
|
|
457
|
-
โโโ strategies/
|
|
458
|
-
โโโ oct_2025/
|
|
459
|
-
โ โโโ index.mjs # entry point โ registers exchange/frame/strategy schemas
|
|
460
|
-
โ โโโ .env # overrides root .env for this strategy
|
|
461
|
-
โ โโโ modules (optional)
|
|
462
|
-
โ | โโโ live.module.mjs # broker adapter for --live mode (optional)
|
|
463
|
-
โ | โโโ paper.module.mjs # broker adapter for --paper mode (optional)
|
|
464
|
-
โ | โโโ backtest.module.mjs # broker adapter for --backtest mode (optional)
|
|
465
|
-
โ โโโ template/ # custom Mustache templates (optional)
|
|
466
|
-
โ โโโ dump/ # auto-created: candle cache + backtest reports
|
|
467
|
-
โโโ dec_2025/
|
|
468
|
-
โโโ index.mjs
|
|
469
|
-
โโโ .env
|
|
470
|
-
โโโ dump/
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
### Root `package.json`
|
|
150
|
+
**Backtest** (`--backtest`) โ runs against historical candles via a registered `FrameSchema`. Before running, the CLI removes the `report`, `log`, `markdown`, and `agent` folders from the strategy's `dump/` dir, then warms the candle cache for every interval in `--cacheInterval`; subsequent runs reuse the cache with no API calls. `--noCache` skips warming, `--noFlush` keeps output folders.
|
|
474
151
|
|
|
475
152
|
```json
|
|
476
|
-
{
|
|
477
|
-
"scripts": {
|
|
478
|
-
"backtest:oct": "npx @backtest-kit/cli --backtest ./strategies/oct_2025/index.mjs",
|
|
479
|
-
"backtest:dec": "npx @backtest-kit/cli --backtest ./strategies/dec_2025/index.mjs"
|
|
480
|
-
},
|
|
481
|
-
"dependencies": {
|
|
482
|
-
"@backtest-kit/cli": "latest",
|
|
483
|
-
"backtest-kit": "latest",
|
|
484
|
-
"ccxt": "latest"
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
```bash
|
|
490
|
-
npm run backtest:oct
|
|
491
|
-
npm run backtest:dec
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
### Isolated Resources Per Strategy
|
|
495
|
-
|
|
496
|
-
| Resource | Path (relative to strategy dir) | Isolated |
|
|
497
|
-
|--------------------------|-----------------------------------|------------------|
|
|
498
|
-
| Candle cache | `./dump/data/candle/` | โ
per-strategy |
|
|
499
|
-
| Backtest reports | `./dump/` | โ
per-strategy |
|
|
500
|
-
| Broker module (live) | `./modules/live.module.mjs` | โ
per-strategy |
|
|
501
|
-
| Broker module (paper) | `./modules/paper.module.mjs` | โ
per-strategy |
|
|
502
|
-
| Broker module (backtest) | `./modules/backtest.module.mjs` | โ
per-strategy |
|
|
503
|
-
| Config module (walker) | `./modules/walker.module.mjs` | โ
loaded once |
|
|
504
|
-
| Telegram templates | `./template/*.mustache` | โ
per-strategy |
|
|
505
|
-
| Environment variables | `./.env` (overrides root) | โ
per-strategy |
|
|
506
|
-
|
|
507
|
-
Each strategy run produces its own `dump/` directory, making it straightforward to compare results across time periods โ both by inspection and by pointing an AI agent at a specific strategy folder.
|
|
508
|
-
|
|
509
|
-
## ๐ Shared Import Aliases
|
|
510
|
-
|
|
511
|
-
`@backtest-kit/cli` automatically turns every **top-level folder** in `process.cwd()` into a bare import alias available inside any strategy file. No configuration needed โ just create the folder.
|
|
512
|
-
|
|
513
|
-
### How It Works
|
|
514
|
-
|
|
515
|
-
When the CLI loads a strategy file, it scans the current working directory for subdirectories and registers each one as an import alias. The alias name is the folder name. Both barrel imports and deep subpath imports are supported:
|
|
516
|
-
|
|
517
|
-
| Import | Resolves to |
|
|
518
|
-
|--------|-------------|
|
|
519
|
-
| `import { fn } from "utils"` | `<cwd>/utils/index.ts` (or `.js`, `.mjs`, `.cjs`) |
|
|
520
|
-
| `import { calcRSI } from "math/rsi"` | `<cwd>/math/rsi.ts` |
|
|
521
|
-
| `import { research } from "logic"` | `<cwd>/logic/index.ts` |
|
|
522
|
-
| `import { ResearchResponseContract } from "logic/contract/ResearchResponse.contract"` | `<cwd>/logic/contract/ResearchResponse.contract.ts` |
|
|
523
|
-
|
|
524
|
-
### Project Structure
|
|
525
|
-
|
|
526
|
-
```
|
|
527
|
-
my-project/
|
|
528
|
-
โโโ utils/ โ import { formatDate } from "utils"
|
|
529
|
-
โ โโโ index.ts
|
|
530
|
-
โโโ math/ โ import { calcRSI } from "math/rsi"
|
|
531
|
-
โ โโโ rsi.ts
|
|
532
|
-
โโโ logic/ โ import { research } from "logic"
|
|
533
|
-
โ โโโ index.ts โ barrel
|
|
534
|
-
โ โโโ contract/
|
|
535
|
-
โ โโโ ResearchResponse.contract.ts โ import { ... } from "logic/contract/ResearchResponse.contract"
|
|
536
|
-
โโโ content/
|
|
537
|
-
โโโ feb_2026.strategy.ts โ uses all three aliases freely
|
|
538
|
-
โโโ mar_2026.strategy.ts โ same aliases, no duplication
|
|
153
|
+
{ "scripts": { "backtest": "npx @backtest-kit/cli --backtest --symbol ETHUSDT --strategy my-strategy --exchange binance --frame feb-2024 --cacheInterval \"1m, 15m, 1h, 4h\" ./src/index.mjs" } }
|
|
539
154
|
```
|
|
540
155
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
### TypeScript Support
|
|
544
|
-
|
|
545
|
-
Add a matching `paths` entry to your `tsconfig.json` so the editor resolves the aliases:
|
|
156
|
+
**Paper** (`--paper`) โ connects to the live exchange but places no real orders. **Identical code path to live** โ the safe way to validate a strategy.
|
|
546
157
|
|
|
547
158
|
```json
|
|
548
|
-
{
|
|
549
|
-
"compilerOptions": {
|
|
550
|
-
"moduleResolution": "bundler",
|
|
551
|
-
"paths": {
|
|
552
|
-
"logic": ["./logic/index.ts"],
|
|
553
|
-
"logic/*": ["./logic/*"],
|
|
554
|
-
"math": ["./math/index.ts"],
|
|
555
|
-
"math/*": ["./math/*"],
|
|
556
|
-
"utils": ["./utils/index.ts"],
|
|
557
|
-
"utils/*": ["./utils/*"]
|
|
558
|
-
}
|
|
559
|
-
},
|
|
560
|
-
"include": [
|
|
561
|
-
"./logic",
|
|
562
|
-
"./math",
|
|
563
|
-
"./utils",
|
|
564
|
-
"./content",
|
|
565
|
-
"./modules",
|
|
566
|
-
],
|
|
567
|
-
}
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
## ๐ Integrations
|
|
571
|
-
|
|
572
|
-
### Web Dashboard (`--ui`)
|
|
573
|
-
|
|
574
|
-
Starts `@backtest-kit/ui` server. Access the interactive dashboard at:
|
|
575
|
-
|
|
576
|
-
```
|
|
577
|
-
http://localhost:60050
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
Customize host/port via environment variables `CC_WWWROOT_HOST` and `CC_WWWROOT_PORT`.
|
|
581
|
-
|
|
582
|
-
#### Symbol List (`symbol.config`)
|
|
583
|
-
|
|
584
|
-
By default the UI shows all symbols from the exchange. To restrict or reorder the list, create a `config/symbol.config` file in your strategy directory (next to the entry point).
|
|
585
|
-
|
|
586
|
-
**Resolution order โ first match wins:**
|
|
587
|
-
|
|
588
|
-
| Priority | Path | Notes |
|
|
589
|
-
|----------|------|-------|
|
|
590
|
-
| 1 | `{strategyDir}/config/symbol.config` | per-strategy override (cwd after `chdir`) |
|
|
591
|
-
| 2 | `{projectRoot}/config/symbol.config` | project-root override (cwd where `npx` was invoked) |
|
|
592
|
-
| 3 | `@backtest-kit/cli/config/symbol.config` | built-in default shipped with the package |
|
|
593
|
-
|
|
594
|
-
Supported file formats (`.ts`, `.cjs`, `.mjs`, `.js` tried automatically):
|
|
595
|
-
|
|
596
|
-
```ts
|
|
597
|
-
// config/symbol.config.ts
|
|
598
|
-
export const symbol_list = [
|
|
599
|
-
{
|
|
600
|
-
icon: "/icon/btc.png",
|
|
601
|
-
logo: "/icon/128/btc.png",
|
|
602
|
-
symbol: "BTCUSDT",
|
|
603
|
-
displayName: "Bitcoin",
|
|
604
|
-
color: "#F7931A",
|
|
605
|
-
priority: 50,
|
|
606
|
-
description: "Bitcoin - the first and most popular cryptocurrency",
|
|
607
|
-
},
|
|
608
|
-
{
|
|
609
|
-
icon: "/icon/eth.png",
|
|
610
|
-
logo: "/icon/128/eth.png",
|
|
611
|
-
symbol: "ETHUSDT",
|
|
612
|
-
displayName: "Ethereum",
|
|
613
|
-
color: "#6F42C1",
|
|
614
|
-
priority: 50,
|
|
615
|
-
description: "Ethereum - a blockchain platform for smart contracts",
|
|
616
|
-
},
|
|
617
|
-
];
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
#### Notification Filter (`notification.config`)
|
|
621
|
-
|
|
622
|
-
Controls which notification categories are shown in the UI dashboard. Create a `config/notification.config` file in your strategy directory to override the defaults.
|
|
623
|
-
|
|
624
|
-
**Resolution order โ first match wins:**
|
|
625
|
-
|
|
626
|
-
| Priority | Path | Notes |
|
|
627
|
-
|----------|------|-------|
|
|
628
|
-
| 1 | `{strategyDir}/config/notification.config` | per-strategy override (cwd after `chdir`) |
|
|
629
|
-
| 2 | `{projectRoot}/config/notification.config` | project-root override (cwd where `npx` was invoked) |
|
|
630
|
-
| 3 | `@backtest-kit/cli/config/notification.config` | built-in default shipped with the package |
|
|
631
|
-
|
|
632
|
-
**Default values (built-in):**
|
|
633
|
-
|
|
634
|
-
| Key | Default | Description |
|
|
635
|
-
|-----|---------|-------------|
|
|
636
|
-
| `signal` | `true` | Signal lifecycle: opened, scheduled, closed, cancelled |
|
|
637
|
-
| `risk` | `true` | Risk manager rejection notifications |
|
|
638
|
-
| `info` | `true` | Informational messages attached to an active signal |
|
|
639
|
-
| `breakeven` | `true` | Breakeven level reached |
|
|
640
|
-
| `common_error` | `true` | Non-fatal runtime errors |
|
|
641
|
-
| `critical_error` | `true` | Fatal errors that terminate the session |
|
|
642
|
-
| `validation_error` | `true` | Strategy config / input validation errors |
|
|
643
|
-
| `strategy_commit` | `true` | All committed actions (partial close, DCA, trailing, etc.) |
|
|
644
|
-
| `partial_loss` | `false` | Partial loss level reached (before commit) |
|
|
645
|
-
| `partial_profit` | `false` | Partial profit level reached (before commit) |
|
|
646
|
-
| `signal_sync` | `false` | Live order fill / exit confirmations from exchange sync |
|
|
647
|
-
|
|
648
|
-
```js
|
|
649
|
-
// config/notification.config.ts
|
|
650
|
-
export default {
|
|
651
|
-
signal: true,
|
|
652
|
-
risk: true,
|
|
653
|
-
info: true,
|
|
654
|
-
breakeven: true,
|
|
655
|
-
common_error: true,
|
|
656
|
-
critical_error: true,
|
|
657
|
-
validation_error: true,
|
|
658
|
-
strategy_commit: true,
|
|
659
|
-
partial_loss: false,
|
|
660
|
-
partial_profit: false,
|
|
661
|
-
signal_sync: false,
|
|
662
|
-
};
|
|
663
|
-
```
|
|
664
|
-
|
|
665
|
-
### Telegram Notifications (`--telegram`)
|
|
666
|
-
|
|
667
|
-
Sends formatted HTML messages with 1m / 15m / 1h price charts to your Telegram channel for every position event: opened, closed, scheduled, cancelled, risk rejection, partial profit/loss, trailing stop/take, and breakeven.
|
|
668
|
-
|
|
669
|
-
Requires `CC_TELEGRAM_TOKEN` and `CC_TELEGRAM_CHANNEL` in your environment.
|
|
670
|
-
|
|
671
|
-
#### Telegram Message Adapter (`telegram.config`)
|
|
672
|
-
|
|
673
|
-
By default messages are rendered from Mustache templates (`template/*.mustache`). To override rendering programmatically, create a `config/telegram.config` file and export an object with any subset of `get*Markdown` methods. Each method receives the event payload and must return a `Promise<string>` with the Markdown message body.
|
|
674
|
-
|
|
675
|
-
Resolution order is the same as other configs (strategy dir โ project root โ package default).
|
|
676
|
-
|
|
677
|
-
```ts
|
|
678
|
-
// config/telegram.config.ts
|
|
679
|
-
import {
|
|
680
|
-
IStrategyTickResultOpened,
|
|
681
|
-
IStrategyTickResultClosed,
|
|
682
|
-
RiskContract,
|
|
683
|
-
} from "backtest-kit";
|
|
684
|
-
|
|
685
|
-
export default {
|
|
686
|
-
async getOpenedMarkdown(event: IStrategyTickResultOpened): Promise<string> {
|
|
687
|
-
return `**Opened** ${event.symbol} at ${event.priceOpen}`;
|
|
688
|
-
},
|
|
689
|
-
async getClosedMarkdown(event: IStrategyTickResultClosed): Promise<string> {
|
|
690
|
-
return `**Closed** ${event.symbol} at ${event.priceClosed}`;
|
|
691
|
-
},
|
|
692
|
-
async getRiskMarkdown(event: RiskContract): Promise<string> {
|
|
693
|
-
return `**Risk rejected** ${event.symbol}`;
|
|
694
|
-
},
|
|
695
|
-
};
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
All methods are optional โ unimplemented ones fall back to the Mustache template.
|
|
699
|
-
|
|
700
|
-
| Method | Event type |
|
|
701
|
-
|--------|------------|
|
|
702
|
-
| `getOpenedMarkdown` | `IStrategyTickResultOpened` |
|
|
703
|
-
| `getClosedMarkdown` | `IStrategyTickResultClosed` |
|
|
704
|
-
| `getScheduledMarkdown` | `IStrategyTickResultScheduled` |
|
|
705
|
-
| `getCancelledMarkdown` | `IStrategyTickResultCancelled` |
|
|
706
|
-
| `getRiskMarkdown` | `RiskContract` |
|
|
707
|
-
| `getPartialProfitMarkdown` | `PartialProfitCommit` |
|
|
708
|
-
| `getPartialLossMarkdown` | `PartialLossCommit` |
|
|
709
|
-
| `getBreakevenMarkdown` | `BreakevenCommit` |
|
|
710
|
-
| `getTrailingTakeMarkdown` | `TrailingTakeCommit` |
|
|
711
|
-
| `getTrailingStopMarkdown` | `TrailingStopCommit` |
|
|
712
|
-
| `getAverageBuyMarkdown` | `AverageBuyCommit` |
|
|
713
|
-
| `getSignalOpenMarkdown` | `SignalOpenContract` |
|
|
714
|
-
| `getSignalCloseMarkdown` | `SignalCloseContract` |
|
|
715
|
-
| `getCancelScheduledMarkdown` | `CancelScheduledCommit` |
|
|
716
|
-
| `getClosePendingMarkdown` | `ClosePendingCommit` |
|
|
717
|
-
| `getSignalInfoMarkdown` | `SignalInfoContract` |
|
|
718
|
-
|
|
719
|
-
## ๐งฉ Module Hooks (Broker Adapter)
|
|
720
|
-
|
|
721
|
-
The CLI supports **mode-specific module files** that are loaded as side-effect imports before the strategy starts. Each file is expected to call `Broker.useBrokerAdapter()` from `backtest-kit` to register a broker adapter.
|
|
722
|
-
|
|
723
|
-
| Command Line Args | Module file | Loaded before |
|
|
724
|
-
|-------------------|---------------------------------|-----------------------------|
|
|
725
|
-
| `--live` | `./modules/live.module.mjs` | `Live.background()` |
|
|
726
|
-
| `--paper` | `./modules/paper.module.mjs` | `Live.background()` (paper) |
|
|
727
|
-
| `--backtest` | `./modules/backtest.module.mjs` | `Backtest.background()` |
|
|
728
|
-
| `--walker` | `./modules/walker.module.mjs` | `Walker.background()` |
|
|
729
|
-
| `--main` | `./modules/main.module.mjs` | the custom entry point |
|
|
730
|
-
| `--brokerdebug` | `./modules/brokerdebug.module.mjs` | broker commit test |
|
|
731
|
-
|
|
732
|
-
> File is resolved relative to `cwd` (the strategy directory). All of `.mjs`, `.cjs`, `.ts` extensions are tried automatically. Missing module is a soft warning โ not an error.
|
|
733
|
-
|
|
734
|
-
### How It Works
|
|
735
|
-
|
|
736
|
-
The module file is a side-effect import. When the CLI loads it, your code runs and registers the adapter. From that point on, `backtest-kit` intercepts every trade-mutating call through the adapter **before** updating internal state โ if the adapter throws, the position state is never changed.
|
|
737
|
-
|
|
738
|
-
```javascript
|
|
739
|
-
// live.module.mjs
|
|
740
|
-
import { Broker } from 'backtest-kit';
|
|
741
|
-
import { myExchange } from './exchange.mjs';
|
|
742
|
-
|
|
743
|
-
class MyBroker {
|
|
744
|
-
async onSignalOpenCommit({ symbol, priceOpen, direction }) {
|
|
745
|
-
await myExchange.openPosition(symbol, direction, priceOpen);
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
async onSignalCloseCommit({ symbol, priceClosed }) {
|
|
749
|
-
await myExchange.closePosition(symbol, priceClosed);
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
async onPartialProfitCommit({ symbol, cost, currentPrice }) {
|
|
753
|
-
await myExchange.createOrder({
|
|
754
|
-
symbol,
|
|
755
|
-
side: 'sell',
|
|
756
|
-
quantity: cost / currentPrice,
|
|
757
|
-
});
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
async onAverageBuyCommit({ symbol, cost, currentPrice }) {
|
|
761
|
-
await myExchange.createOrder({
|
|
762
|
-
symbol,
|
|
763
|
-
side: 'buy',
|
|
764
|
-
quantity: cost / currentPrice,
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
Broker.useBrokerAdapter(MyBroker);
|
|
770
|
-
|
|
771
|
-
Broker.enable();
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
### Available Broker Hooks
|
|
775
|
-
|
|
776
|
-
| Method | Payload type | Triggered on |
|
|
777
|
-
|--------------------------|------------------------------|---------------------------|
|
|
778
|
-
| `onSignalOpenCommit` | `BrokerSignalOpenPayload` | Position activation |
|
|
779
|
-
| `onSignalCloseCommit` | `BrokerSignalClosePayload` | SL / TP / manual close |
|
|
780
|
-
| `onPartialProfitCommit` | `BrokerPartialProfitPayload` | PP |
|
|
781
|
-
| `onPartialLossCommit` | `BrokerPartialLossPayload` | PL |
|
|
782
|
-
| `onTrailingStopCommit` | `BrokerTrailingStopPayload` | SL adjustment |
|
|
783
|
-
| `onTrailingTakeCommit` | `BrokerTrailingTakePayload` | TP adjustment |
|
|
784
|
-
| `onBreakevenCommit` | `BrokerBreakevenPayload` | SL moved to entry |
|
|
785
|
-
| `onAverageBuyCommit` | `BrokerAverageBuyPayload` | DCA entry |
|
|
786
|
-
|
|
787
|
-
All methods are optional. Unimplemented hooks are silently skipped. In backtest mode all broker calls are skipped automatically โ no adapter code runs during backtests.
|
|
788
|
-
|
|
789
|
-
### TypeScript
|
|
790
|
-
|
|
791
|
-
```typescript
|
|
792
|
-
import { Broker, IBroker, BrokerSignalOpenPayload, BrokerSignalClosePayload } from 'backtest-kit';
|
|
793
|
-
|
|
794
|
-
class MyBroker implements Partial<IBroker> {
|
|
795
|
-
async onSignalOpenCommit(payload: BrokerSignalOpenPayload) {
|
|
796
|
-
// place open order on exchange
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
async onSignalCloseCommit(payload: BrokerSignalClosePayload) {
|
|
800
|
-
// place close order on exchange
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
Broker.useBrokerAdapter(MyBroker);
|
|
805
|
-
|
|
806
|
-
Broker.enable();
|
|
807
|
-
```
|
|
808
|
-
|
|
809
|
-
## โ๏ธ Setup Hook (`config/setup.config`)
|
|
810
|
-
|
|
811
|
-
`@backtest-kit/cli` loads a `{projectRoot}/config/setup.config` file once before any module hooks or strategy code run. Use it to perform one-time initialization that must happen before the first persistence call โ registering a custom storage backend, configuring a logger, seeding global state, or anything else the process needs before `backtest-kit` starts.
|
|
812
|
-
|
|
813
|
-
**Important:** When `setup.config` is present, the CLI skips its own default adapter registration โ it does **not** call `usePersist()` / `useLocal()` / `useMemory()` for any of the persistence slots. This means your config takes full ownership of the persistence layer: whatever adapters you register in `{projectRoot}/config/setup.config` are the ones `backtest-kit` uses, with no interference from the CLI defaults.
|
|
814
|
-
|
|
815
|
-
### Example: MongoDB + Redis persistence via `@backtest-kit/mongo`
|
|
816
|
-
|
|
817
|
-
The most common use-case is swapping the default file-based persistence for a production-grade backend. Install `@backtest-kit/mongo` and call `setup()` โ it registers all 15 persistence adapters in one call and reads connection parameters from environment variables:
|
|
818
|
-
|
|
819
|
-
```bash
|
|
820
|
-
npm install @backtest-kit/mongo
|
|
821
|
-
```
|
|
822
|
-
|
|
823
|
-
```ts
|
|
824
|
-
// config/setup.config.ts
|
|
825
|
-
import { setup } from '@backtest-kit/mongo';
|
|
826
|
-
|
|
827
|
-
setup();
|
|
828
|
-
```
|
|
829
|
-
|
|
830
|
-
```env
|
|
831
|
-
# .env
|
|
832
|
-
CC_MONGO_CONNECTION_STRING=mongodb://localhost:27017/backtest-kit
|
|
833
|
-
CC_REDIS_HOST=127.0.0.1
|
|
834
|
-
CC_REDIS_PORT=6379
|
|
835
|
-
```
|
|
836
|
-
|
|
837
|
-
Or pass connection parameters explicitly:
|
|
838
|
-
|
|
839
|
-
```ts
|
|
840
|
-
// config/setup.config.ts
|
|
841
|
-
import { setup } from '@backtest-kit/mongo';
|
|
842
|
-
|
|
843
|
-
setup({
|
|
844
|
-
CC_MONGO_CONNECTION_STRING: 'mongodb://mongo:27017/mydb',
|
|
845
|
-
CC_REDIS_HOST: 'redis',
|
|
846
|
-
CC_REDIS_PORT: 6379,
|
|
847
|
-
CC_REDIS_PASSWORD: 'secret',
|
|
848
|
-
});
|
|
849
|
-
```
|
|
850
|
-
|
|
851
|
-
No changes to strategy code are needed โ `setup()` wires up the adapters transparently before `backtest-kit` makes its first persistence call.
|
|
852
|
-
|
|
853
|
-
## ๐งฉ Module Loader (`config/loader.config`)
|
|
854
|
-
|
|
855
|
-
`@backtest-kit/cli` loads a `{projectRoot}/config/loader.config` file **after** `setup.config` but **before** any strategy or module code runs. Unlike `setup.config` (which is loaded for its side effects), `loader.config` exports a function that the CLI explicitly `await`s. Use it whenever you need to **wait for an async dependency** to be ready before the backtest starts.
|
|
856
|
-
|
|
857
|
-
```bash
|
|
858
|
-
monorepo/
|
|
859
|
-
โโโ packages/
|
|
860
|
-
โ โโโ shared-broker/ # shared broker code
|
|
861
|
-
โ โโโ shared-signals/ # common indicators (RSI, MACD)
|
|
862
|
-
โ โโโ shared-db/ # mongodb wiring
|
|
863
|
-
โ โโโ strategy-momentum/ # strategy code
|
|
864
|
-
โ โโโ strategy-mean-reversion/
|
|
865
|
-
```
|
|
866
|
-
|
|
867
|
-
**When to use it:**
|
|
868
|
-
|
|
869
|
-
- **Wire microfrontends in a monorepo** โ resolve and pre-load sibling packages, register cross-package services, or hydrate a shared DI container before strategies import from neighboring workspaces.
|
|
870
|
-
- **Wait for a database connection** โ open a Mongo/Postgres/Redis connection and verify it's reachable before the first persistence call, so the backtest fails fast instead of mid-run.
|
|
871
|
-
- **Warm up caches or external APIs** โ pre-fetch reference data (instruments list, calendar, fee tables) so the strategy's first tick doesn't pay the round-trip cost.
|
|
872
|
-
- **Run schema migrations** โ apply any pending migrations to the persistence backend before signals start flowing.
|
|
873
|
-
|
|
874
|
-
`loader.config` supports exactly one of two export styles โ **never both at once**. If both are present, the `default` export wins and the named `loader` is ignored.
|
|
875
|
-
|
|
876
|
-
```ts
|
|
877
|
-
// config/loader.config.ts โ default export (preferred, ESM style)
|
|
878
|
-
export default async () => {
|
|
879
|
-
await mongoose.connect(process.env.CC_MONGO_CONNECTION_STRING!);
|
|
880
|
-
await redis.ping();
|
|
881
|
-
};
|
|
882
|
-
```
|
|
883
|
-
|
|
884
|
-
```ts
|
|
885
|
-
// config/loader.config.ts โ named export
|
|
886
|
-
export const loader = async () => {
|
|
887
|
-
await mongoose.connect(process.env.CC_MONGO_CONNECTION_STRING!);
|
|
888
|
-
await redis.ping();
|
|
889
|
-
};
|
|
890
|
-
```
|
|
891
|
-
|
|
892
|
-
### Example: wait for MongoDB before running a backtest
|
|
893
|
-
|
|
894
|
-
`@backtest-kit/mongo`'s `setup()` registers the adapters synchronously but doesn't block until the connection is established. If your backtest depends on data that must be present in Mongo before the first signal fires, use `loader.config` to gate the run on a real connection:
|
|
895
|
-
|
|
896
|
-
```ts
|
|
897
|
-
// config/setup.config.ts
|
|
898
|
-
import { setup } from '@backtest-kit/mongo';
|
|
899
|
-
|
|
900
|
-
setup();
|
|
901
|
-
```
|
|
902
|
-
|
|
903
|
-
```ts
|
|
904
|
-
// config/loader.config.ts
|
|
905
|
-
import mongoose from 'mongoose';
|
|
906
|
-
|
|
907
|
-
export default async () => {
|
|
908
|
-
await mongoose.connect(process.env.CC_MONGO_CONNECTION_STRING!);
|
|
909
|
-
console.log('mongo connection verified, starting backtest');
|
|
910
|
-
};
|
|
159
|
+
{ "scripts": { "paper": "npx @backtest-kit/cli --paper --symbol BTCUSDT ./src/index.mjs" } }
|
|
911
160
|
```
|
|
912
161
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
When `backtest-kit` strategies live in one workspace and shared services (broker adapters, signal feeds, dashboards) live in sibling workspaces, `loader.config` is the place to wire them together before the runner starts:
|
|
916
|
-
|
|
917
|
-
```ts
|
|
918
|
-
// config/loader.config.ts
|
|
919
|
-
import "@my-org/brokers";
|
|
920
|
-
import "@my-org/signals";
|
|
921
|
-
```
|
|
922
|
-
|
|
923
|
-
The `@my-org` alias should be declared in `config/alias.config`.
|
|
924
|
-
|
|
925
|
-
## ๐ Import Aliases (`config/alias.config`)
|
|
926
|
-
|
|
927
|
-
`@backtest-kit/cli` lets you override any nodejs module import โ without touching the strategy code. Drop a `config/alias.config` file in your project root and export a mapping from module name to replacement module.
|
|
928
|
-
|
|
929
|
-
The alias table is loaded once (on the first `import` call) from `{projectRoot}/config/alias.config` and applied globally to every subsequent module load via `require`/ `import`.
|
|
930
|
-
|
|
931
|
-
**Use cases:**
|
|
932
|
-
|
|
933
|
-
- Replace a heavy dependency with a lighter stub for backtesting
|
|
934
|
-
- Swap any external api for a mock during CI runs
|
|
935
|
-
|
|
936
|
-
```ts
|
|
937
|
-
// config/alias.config.ts โ named export
|
|
938
|
-
export const ccxt = require("./stubs/ccxt.stub.cjs");
|
|
939
|
-
```
|
|
940
|
-
|
|
941
|
-
```js
|
|
942
|
-
// config/alias.config.cjs โ default export
|
|
943
|
-
module.exports = {
|
|
944
|
-
ccxt: require("./stubs/ccxt.stub.cjs"),
|
|
945
|
-
};
|
|
946
|
-
```
|
|
947
|
-
|
|
948
|
-
```js
|
|
949
|
-
// config/alias.config.mjs โ default export
|
|
950
|
-
import ccxtStub from "./stubs/ccxt.stub.mjs";
|
|
951
|
-
|
|
952
|
-
export default {
|
|
953
|
-
ccxt: ccxtStub,
|
|
954
|
-
};
|
|
955
|
-
```
|
|
956
|
-
|
|
957
|
-
Alias config may export an **async factory** instead of a plain mapping. The CLI `await`s it before any strategy code runs, so you can resolve ESM modules dynamically and hand the live bindings to the alias table:
|
|
958
|
-
|
|
959
|
-
```ts
|
|
960
|
-
// config/alias.config.ts โ async factory (default export)
|
|
961
|
-
export default async () => ({
|
|
962
|
-
// `nanoid` is ESM-only โ `require("nanoid")` would throw,
|
|
963
|
-
// so pull it in with a dynamic import and alias it.
|
|
964
|
-
nanoid: await import("nanoid"),
|
|
965
|
-
});
|
|
966
|
-
```
|
|
967
|
-
|
|
968
|
-
```ts
|
|
969
|
-
// config/alias.config.ts โ async factory (named `loader` export)
|
|
970
|
-
export const loader = async () => ({
|
|
971
|
-
"p-limit": await import("p-limit"),
|
|
972
|
-
});
|
|
973
|
-
```
|
|
974
|
-
|
|
975
|
-
Both export styles are supported โ `export default` and `export const loader` โ but **never both at once**; if both are present the `default` export wins. The factory must resolve to the same `{ moduleName: replacement }` shape as the object form. The CLI awaits it before loading the first strategy module, so strategy code keeps calling a plain `require("nanoid")` and transparently gets the ESM binding.
|
|
976
|
-
|
|
977
|
-
When strategy code calls `require("ccxt")`, the loader checks `IMPORT_ALIAS` first. If a key matches, the mapped value is returned instead of the real module โ no monkey-patching of `node_modules` needed.
|
|
978
|
-
|
|
979
|
-
**Important:** It is **not** per-strategy โ it applies to all modules loaded in the current process.
|
|
980
|
-
|
|
981
|
-
## ๐ฆ Supported Entry Point Formats
|
|
982
|
-
|
|
983
|
-
`@backtest-kit/cli` automatically detects the format of your strategy file and loads it with the appropriate runtime โ no flags or configuration required.
|
|
984
|
-
|
|
985
|
-
| Format | Extension | Runtime | Use Case |
|
|
986
|
-
|--------|-----------|---------|----------|
|
|
987
|
-
| **TypeScript** | `.ts` | [`tsx`](https://tsx.is/) via `tsImport()` | TypeScript strategies with cross-imports (ESM โ CJS) |
|
|
988
|
-
| **ES Module** | `.mjs` | Native `import()` | Modern JavaScript with top-level `await` and ESM syntax |
|
|
989
|
-
| **CommonJS** | `.cjs` | Native `require()` | Legacy or dual-package strategies |
|
|
990
|
-
|
|
991
|
-
### TypeScript (`.ts`)
|
|
992
|
-
|
|
993
|
-
Run TypeScript strategy files directly โ no `tsc` compilation step needed. Powered by `tsx`, which handles cross-format imports transparently:
|
|
162
|
+
**Live** (`--live`) โ deploys a real bot. Requires exchange API keys in `.env`. Combine with `--ui --telegram` for a monitored deployment.
|
|
994
163
|
|
|
995
164
|
```json
|
|
996
|
-
{
|
|
997
|
-
"scripts": {
|
|
998
|
-
"backtest": "npx @backtest-kit/cli --backtest ./src/index.ts"
|
|
999
|
-
},
|
|
1000
|
-
"dependencies": {
|
|
1001
|
-
"@backtest-kit/cli": "latest",
|
|
1002
|
-
"backtest-kit": "latest",
|
|
1003
|
-
"tsx": "latest"
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
165
|
+
{ "scripts": { "start": "npx @backtest-kit/cli --live --ui --telegram --symbol BTCUSDT ./src/index.mjs" } }
|
|
1006
166
|
```
|
|
1007
167
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
Standard ESM format. Supports top-level `await`, named exports, and `import` syntax:
|
|
1011
|
-
|
|
1012
|
-
```json
|
|
1013
|
-
{
|
|
1014
|
-
"scripts": {
|
|
1015
|
-
"backtest": "npx @backtest-kit/cli --backtest ./src/index.mjs"
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
```
|
|
168
|
+
</details>
|
|
1019
169
|
|
|
1020
|
-
###
|
|
170
|
+
### Walker โ A/B strategy comparison
|
|
1021
171
|
|
|
1022
|
-
|
|
172
|
+
Runs the same historical period against multiple strategy files and prints a ranked report. Use it to pick the best variant before deploying.
|
|
1023
173
|
|
|
1024
|
-
```
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
174
|
+
```bash
|
|
175
|
+
npx @backtest-kit/cli --walker --symbol BTCUSDT --noCache --markdown --output feb_2026_comparison \
|
|
176
|
+
./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
|
|
177
|
+
# โ ./dump/feb_2026_comparison.md
|
|
1030
178
|
```
|
|
1031
179
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
`@backtest-kit/cli` can execute any local `.pine` file against a real exchange and print the results as a Markdown table โ no TradingView account required.
|
|
180
|
+
<details>
|
|
181
|
+
<summary>Walker flags, output modes & behavior</summary>
|
|
1035
182
|
|
|
1036
|
-
|
|
183
|
+
Each positional argument is a separate strategy entry point. Before loading them the CLI removes the `report`/`log`/`markdown`/`agent` folders from each entry point's `dump/` (skip with `--noFlush`). All files load **without changing `process.cwd()`** โ `.env` is read from the working directory only. After loading, `addWalkerSchema` 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 `Date.now()` with a warning.
|
|
1037
184
|
|
|
1038
185
|
| Flag | Type | Description |
|
|
1039
186
|
|------|------|-------------|
|
|
1040
|
-
| `--
|
|
1041
|
-
| `--symbol` | string | Trading pair (default
|
|
1042
|
-
| `--
|
|
1043
|
-
| `--
|
|
1044
|
-
| `--
|
|
1045
|
-
| `--
|
|
1046
|
-
| `--output` | string | Output file base name
|
|
1047
|
-
| `--json` | boolean |
|
|
1048
|
-
| `--
|
|
1049
|
-
| `--markdown` | boolean | Write Markdown table to `<pine-dir>/dump/{output}.md` |
|
|
1050
|
-
|
|
1051
|
-
**Important:** `limit` must cover indicator warmup bars โ rows before warmup completes will show `N/A`
|
|
187
|
+
| `--walker` | boolean | Enable Walker comparison |
|
|
188
|
+
| `--symbol` | string | Trading pair (default `"BTCUSDT"`) |
|
|
189
|
+
| `--cacheInterval` | string | Intervals to pre-cache (default `"1m, 15m, 30m, 4h"`) |
|
|
190
|
+
| `--noCache` | boolean | Skip candle cache warming |
|
|
191
|
+
| `--noFlush` | boolean | Skip removing output folders before the run |
|
|
192
|
+
| `--verbose` | boolean | Log each candle fetch and strategy progress |
|
|
193
|
+
| `--output` | string | Output file base name (default `walker_{SYMBOL}_{TIMESTAMP}`) |
|
|
194
|
+
| `--json` | boolean | Save `Walker.getData()` as JSON to `./dump/<output>.json` and exit |
|
|
195
|
+
| `--markdown` | boolean | Save `Walker.getReport()` as `./dump/<output>.md` and exit |
|
|
1052
196
|
|
|
1053
|
-
**
|
|
197
|
+
**Output:** no flag โ print Markdown report to stdout; `--json` / `--markdown` โ save and exit. **Module hook:** `./modules/walker.module` loads automatically before the comparison (`.ts`/`.mjs`/`.cjs` tried in order).
|
|
1054
198
|
|
|
1055
|
-
|
|
199
|
+
</details>
|
|
1056
200
|
|
|
1057
|
-
|
|
201
|
+
### Main โ custom entry point, no trading harness
|
|
1058
202
|
|
|
1059
|
-
|
|
203
|
+
Runs a single entry point with the full CLI environment prepared (`.env`, `config/setup.config`, `config/loader.config`, `./modules/main.module`, cwd changed to the entry-point folder, graceful shutdown wired) โ but **never** starts a trading harness. Use it to bootstrap the environment for a quick action, e.g. calling a 3rd-party API with automatic `.env` import.
|
|
1060
204
|
|
|
1061
|
-
|
|
1062
|
-
|
|
205
|
+
<details>
|
|
206
|
+
<summary>Main behavior & flags</summary>
|
|
1063
207
|
|
|
1064
|
-
|
|
1065
|
-
my-project/
|
|
1066
|
-
โโโ math/
|
|
1067
|
-
โ โโโ impulse_trend_15m.pine โ indicator
|
|
1068
|
-
โ โโโ modules/
|
|
1069
|
-
โ โโโ pine.module.ts โ loaded first (next to .pine file)
|
|
1070
|
-
โโโ modules/
|
|
1071
|
-
โ โโโ pine.module.ts โ fallback (project root)
|
|
1072
|
-
โโโ package.json
|
|
1073
|
-
```
|
|
208
|
+
Unlike the trading modes it does not call `Backtest/Live/Walker.background`, pick a symbol, warm the cache, or resolve a strategy/exchange/frame โ the entry point decides what to run. Exactly **one** positional entry point is required (`Entry point is required` otherwise). `process.cwd()` changes to the entry-point directory and its local `.env` overrides the root `.env`.
|
|
1074
209
|
|
|
1075
|
-
|
|
210
|
+
Although the CLI starts nothing itself, any `Backtest`/`Live`/`Walker` run **your** entry point launches is still managed: the process exits once `listenDone*` reports completion, the first `Ctrl+C` stops every active run via `*.list()`/`*.stop()`, a second force-quits. `./modules/main.module` loads automatically before the entry point.
|
|
1076
211
|
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
addExchangeSchema({
|
|
1083
|
-
exchangeName: "my-exchange",
|
|
1084
|
-
getCandles: async (symbol, interval, since, limit) => {
|
|
1085
|
-
const exchange = new ccxt.bybit({ enableRateLimit: true });
|
|
1086
|
-
const ohlcv = await exchange.fetchOHLCV(symbol, interval, since.getTime(), limit);
|
|
1087
|
-
return ohlcv.map(([timestamp, open, high, low, close, volume]) => ({
|
|
1088
|
-
timestamp, open, high, low, close, volume,
|
|
1089
|
-
}));
|
|
1090
|
-
},
|
|
1091
|
-
formatPrice: (symbol, price) => price.toFixed(2),
|
|
1092
|
-
formatQuantity: (symbol, quantity) => quantity.toFixed(8),
|
|
1093
|
-
});
|
|
1094
|
-
```
|
|
1095
|
-
|
|
1096
|
-
### Environment variables (`.env`)
|
|
1097
|
-
|
|
1098
|
-
Before loading `pine.module`, the CLI loads `.env` files in the same order as for strategy modules โ project root first, then the `.pine` file directory (overrides root):
|
|
1099
|
-
|
|
1100
|
-
```
|
|
1101
|
-
my-project/
|
|
1102
|
-
โโโ math/
|
|
1103
|
-
โ โโโ .env โ loaded second (overrides root)
|
|
1104
|
-
โ โโโ impulse_trend_15m.pine
|
|
1105
|
-
โโโ .env โ loaded first
|
|
1106
|
-
โโโ package.json
|
|
1107
|
-
```
|
|
1108
|
-
|
|
1109
|
-
Use this to store API keys without hardcoding them:
|
|
1110
|
-
|
|
1111
|
-
```env
|
|
1112
|
-
# .env
|
|
1113
|
-
BYBIT_API_KEY=xxx
|
|
1114
|
-
BYBIT_API_SECRET=yyy
|
|
1115
|
-
```
|
|
1116
|
-
|
|
1117
|
-
```typescript
|
|
1118
|
-
// modules/pine.module.ts
|
|
1119
|
-
addExchangeSchema({
|
|
1120
|
-
exchangeName: "my-exchange",
|
|
1121
|
-
getCandles: async (symbol, interval, since, limit) => {
|
|
1122
|
-
const exchange = new ccxt.bybit({
|
|
1123
|
-
apiKey: process.env.BYBIT_API_KEY,
|
|
1124
|
-
secret: process.env.BYBIT_API_SECRET,
|
|
1125
|
-
enableRateLimit: true,
|
|
1126
|
-
});
|
|
1127
|
-
// ...
|
|
1128
|
-
},
|
|
1129
|
-
});
|
|
1130
|
-
```
|
|
1131
|
-
|
|
1132
|
-
Then run:
|
|
1133
|
-
|
|
1134
|
-
```bash
|
|
1135
|
-
npx @backtest-kit/cli --pine ./math/impulse_trend_15m.pine \
|
|
1136
|
-
--exchange my-exchange \
|
|
1137
|
-
--symbol BTCUSDT \
|
|
1138
|
-
--timeframe 15m \
|
|
1139
|
-
--limit 180 \
|
|
1140
|
-
--when "2025-09-24T12:00:00.000Z"
|
|
1141
|
-
```
|
|
1142
|
-
|
|
1143
|
-
Or add it to `package.json`:
|
|
1144
|
-
|
|
1145
|
-
```json
|
|
1146
|
-
{
|
|
1147
|
-
"scripts": {
|
|
1148
|
-
"pine": "npx @backtest-kit/cli --pine ./math/impulse_trend_15m.pine --symbol BTCUSDT --timeframe 15m --limit 180"
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
```
|
|
1152
|
-
|
|
1153
|
-
```bash
|
|
1154
|
-
npm run pine
|
|
1155
|
-
```
|
|
1156
|
-
|
|
1157
|
-
### PineScript Requirements
|
|
1158
|
-
|
|
1159
|
-
The CLI reads all `plot()` calls that use `display=display.data_window` as output columns. Every other `plot()` is ignored. Name each output plot explicitly:
|
|
1160
|
-
|
|
1161
|
-
```pine
|
|
1162
|
-
//@version=5
|
|
1163
|
-
indicator("MyIndicator", overlay=true)
|
|
1164
|
-
|
|
1165
|
-
// ... computation ...
|
|
1166
|
-
|
|
1167
|
-
plot(close, "Close", display=display.data_window)
|
|
1168
|
-
plot(position, "Position", display=display.data_window)
|
|
1169
|
-
```
|
|
1170
|
-
|
|
1171
|
-
The column names in the output Markdown table are taken directly from those plot names โ no manual schema definition needed.
|
|
1172
|
-
|
|
1173
|
-
### Output
|
|
1174
|
-
|
|
1175
|
-
The CLI prints a Markdown table to stdout:
|
|
1176
|
-
|
|
1177
|
-
```
|
|
1178
|
-
# PineScript Technical Analysis Dump
|
|
1179
|
-
|
|
1180
|
-
**Signal ID**: CLI execution 2025-09-24T12:00:00.000Z
|
|
1181
|
-
|
|
1182
|
-
| Close | Position | timestamp |
|
|
1183
|
-
| --- | --- | --- |
|
|
1184
|
-
| 112871.28 | -1.0000 | 2025-09-22T15:00:00.000Z |
|
|
1185
|
-
| 112666.69 | -1.0000 | 2025-09-22T15:15:00.000Z |
|
|
1186
|
-
| 112736.00 | 0.0000 | 2025-09-22T18:30:00.000Z |
|
|
1187
|
-
| 112653.90 | 1.0000 | 2025-09-22T22:15:00.000Z |
|
|
1188
|
-
```
|
|
1189
|
-
|
|
1190
|
-
Save to `./math/dump/impulse_trend_15m.md` (uses `.pine` file name automatically, dump is created next to the `.pine` file):
|
|
1191
|
-
|
|
1192
|
-
```bash
|
|
1193
|
-
npx @backtest-kit/cli --pine ./math/impulse_trend_15m.pine --markdown
|
|
1194
|
-
```
|
|
1195
|
-
|
|
1196
|
-
Override the output name with `--output`:
|
|
1197
|
-
|
|
1198
|
-
```bash
|
|
1199
|
-
npx @backtest-kit/cli --pine ./math/impulse_trend_15m.pine --jsonl --output feb2026_bb
|
|
1200
|
-
# โ ./math/dump/feb2026_bb.jsonl
|
|
1201
|
-
```
|
|
1202
|
-
|
|
1203
|
-
Print to stdout (no flag):
|
|
1204
|
-
|
|
1205
|
-
```bash
|
|
1206
|
-
npx @backtest-kit/cli --pine ./math/impulse_trend_15m.pine
|
|
1207
|
-
```
|
|
1208
|
-
|
|
1209
|
-
## ๐จ Visual Pine Script Editor
|
|
1210
|
-
|
|
1211
|
-

|
|
1212
|
-
|
|
1213
|
-
`@backtest-kit/cli` ships a browser-based Pine Script editor powered by `@backtest-kit/ui`. It lets you write, run, and iterate on indicators interactively โ with a live chart that updates as you hit **โถ Run**
|
|
1214
|
-
|
|
1215
|
-
### Usage
|
|
212
|
+
| Flag | Type | Description |
|
|
213
|
+
|------|------|-------------|
|
|
214
|
+
| `--main` | boolean | Enable Main mode |
|
|
215
|
+
| `--noFlush` | boolean | Skip removing output folders before the run |
|
|
1216
216
|
|
|
1217
|
-
```
|
|
1218
|
-
npx @backtest-kit/cli --
|
|
217
|
+
```json
|
|
218
|
+
{ "scripts": { "main": "npx @backtest-kit/cli --main ./tools/fetch_fear_and_greed.ts" } }
|
|
1219
219
|
```
|
|
1220
220
|
|
|
1221
|
-
|
|
221
|
+
</details>
|
|
1222
222
|
|
|
1223
|
-
|
|
1224
|
-
2. Start the `@backtest-kit/ui` server on `http://localhost:60050` (or `CC_WWWROOT_PORT`)
|
|
1225
|
-
3. Open `http://localhost:{CC_WWWROOT_PORT}?pine=1` automatically in your default browser
|
|
223
|
+
### Parallel multi-symbol (`--entry`)
|
|
1226
224
|
|
|
1227
|
-
|
|
225
|
+
> **Power-user modifier โ skip unless needed.** The standard flow runs one symbol from `--symbol`. Use `--entry` to fan one strategy out across many symbols at once, or to drive `*.background()` from a UI / DB / API.
|
|
1228
226
|
|
|
1229
|
-
|
|
227
|
+
`--entry` is a modifier โ combine it with exactly one of `--backtest`/`--live`/`--paper`/`--walker`, plus one positional entry file. The CLI does only the boilerplate (`Setup`, providers, the matching `./modules/<mode>.module`, SIGINT that stops every active run, `shutdown()` once `listenDone*` reports all runs complete); **you** pick the symbol set, warm cache, and call `*.background()`.
|
|
1230
228
|
|
|
1231
|
-
|
|
229
|
+
<details>
|
|
230
|
+
<summary>Example โ backtest one strategy across five symbols</summary>
|
|
1232
231
|
|
|
1233
|
-
```
|
|
1234
|
-
//
|
|
1235
|
-
import { addExchangeSchema } from "backtest-kit";
|
|
232
|
+
```javascript
|
|
233
|
+
// src/multi-symbol.mjs
|
|
234
|
+
import { addExchangeSchema, addFrameSchema, addStrategySchema, Backtest, warmCandles } from "backtest-kit";
|
|
1236
235
|
import ccxt from "ccxt";
|
|
1237
236
|
|
|
1238
|
-
addExchangeSchema({
|
|
1239
|
-
exchangeName: "my-exchange",
|
|
237
|
+
addExchangeSchema({ exchangeName: "binance",
|
|
1240
238
|
getCandles: async (symbol, interval, since, limit) => {
|
|
1241
|
-
const exchange = new ccxt.
|
|
239
|
+
const exchange = new ccxt.binance();
|
|
1242
240
|
const ohlcv = await exchange.fetchOHLCV(symbol, interval, since.getTime(), limit);
|
|
1243
|
-
return ohlcv.map(([timestamp, open, high, low, close, volume]) =>
|
|
1244
|
-
timestamp, open, high, low, close, volume
|
|
1245
|
-
}));
|
|
241
|
+
return ohlcv.map(([timestamp, open, high, low, close, volume]) =>
|
|
242
|
+
({ timestamp, open, high, low, close, volume }));
|
|
1246
243
|
},
|
|
1247
|
-
formatPrice: (
|
|
1248
|
-
formatQuantity: (symbol, quantity) => quantity.toFixed(8),
|
|
1249
|
-
});
|
|
1250
|
-
```
|
|
244
|
+
formatPrice: (s, p) => p.toFixed(2), formatQuantity: (s, q) => q.toFixed(8) });
|
|
1251
245
|
|
|
1252
|
-
|
|
246
|
+
addFrameSchema({ frameName: "feb-2026", interval: "1m",
|
|
247
|
+
startDate: new Date("2026-02-01"), endDate: new Date("2026-02-28") });
|
|
248
|
+
addStrategySchema({ strategyName: "my-strategy", interval: "15m", getSignal: async () => null });
|
|
1253
249
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
### `package.json` script
|
|
1260
|
-
|
|
1261
|
-
```json
|
|
1262
|
-
{
|
|
1263
|
-
"scripts": {
|
|
1264
|
-
"editor": "npx @backtest-kit/cli --editor"
|
|
1265
|
-
}
|
|
250
|
+
// Decide the symbol set yourself โ UI, database, API, or a list.
|
|
251
|
+
for (const symbol of ["BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT", "XRPUSDT"]) {
|
|
252
|
+
// optional: await warmCandles({ exchangeName: "binance", interval: "1m", symbol, from, to });
|
|
253
|
+
Backtest.background(symbol, { strategyName: "my-strategy", exchangeName: "binance", frameName: "feb-2026" });
|
|
1266
254
|
}
|
|
1267
255
|
```
|
|
1268
256
|
|
|
1269
257
|
```bash
|
|
1270
|
-
|
|
258
|
+
npx @backtest-kit/cli --backtest --entry ./src/multi-symbol.mjs
|
|
1271
259
|
```
|
|
1272
260
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
`@backtest-kit/cli` can fetch raw OHLCV candles from any registered exchange and save them to a file โ no strategy file required.
|
|
1276
|
-
|
|
1277
|
-
### CLI Flags
|
|
261
|
+
The same shape works for `--live --entry` / `--paper --entry` (call `Live.background()` per symbol with your broker adapter).
|
|
1278
262
|
|
|
1279
|
-
|
|
1280
|
-
|------|------|-------------|
|
|
1281
|
-
| `--dump` | boolean | Enable candle dump mode |
|
|
1282
|
-
| `--symbol` | string | Trading pair (default: `"BTCUSDT"`) |
|
|
1283
|
-
| `--timeframe` | string | Candle interval (default: `"15m"`) |
|
|
1284
|
-
| `--limit` | string | Number of candles to fetch (default: `250`) |
|
|
1285
|
-
| `--when` | string | End date for candle window โ ISO 8601 or Unix ms (default: now) |
|
|
1286
|
-
| `--exchange` | string | Exchange name (default: first registered, falls back to CCXT Binance) |
|
|
1287
|
-
| `--output` | string | Output file base name without extension (default: `{SYMBOL}_{LIMIT}_{TIMEFRAME}_{TIMESTAMP}`) |
|
|
1288
|
-
| `--json` | boolean | Write candles as a JSON array to `./dump/{output}.json` |
|
|
1289
|
-
| `--jsonl` | boolean | Write candles as JSONL (one row per line) to `./dump/{output}.jsonl` |
|
|
263
|
+
</details>
|
|
1290
264
|
|
|
1291
|
-
|
|
265
|
+
---
|
|
1292
266
|
|
|
1293
|
-
|
|
267
|
+
## ๐ ๏ธ Tooling modes
|
|
1294
268
|
|
|
1295
|
-
|
|
269
|
+
Five utilities that don't run a strategy. They share one convention, explained once here and referenced below.
|
|
1296
270
|
|
|
1297
|
-
The CLI
|
|
1298
|
-
|
|
1299
|
-
```
|
|
1300
|
-
my-project/
|
|
1301
|
-
โโโ modules/
|
|
1302
|
-
โ โโโ dump.module.ts โ exchange registration
|
|
1303
|
-
โโโ dump/ โ auto-created: candle output files
|
|
1304
|
-
โโโ package.json
|
|
1305
|
-
```
|
|
271
|
+
> **The `<mode>.module` convention.** By default the CLI auto-registers CCXT Binance. To use a different exchange (custom API keys, rate limits, a non-spot market), drop a `modules/<mode>.module.ts` that calls `addExchangeSchema` from `backtest-kit`. The CLI loads it automatically before running, trying `.ts`/`.mjs`/`.cjs`; it's searched **next to the target file first, then in the project root**. `.env` is loaded root-first then the target-file dir (override), so API keys stay out of code.
|
|
1306
272
|
|
|
1307
|
-
|
|
273
|
+
<details>
|
|
274
|
+
<summary>The shared <code><mode>.module.ts</code> shape (pine / editor / dump / pnldebug / brokerdebug)</summary>
|
|
1308
275
|
|
|
1309
276
|
```typescript
|
|
1310
|
-
// modules/
|
|
277
|
+
// modules/pine.module.ts (same shape for editor/dump/pnldebug.module; brokerdebug registers a Broker instead)
|
|
1311
278
|
import { addExchangeSchema } from "backtest-kit";
|
|
1312
279
|
import ccxt from "ccxt";
|
|
1313
280
|
|
|
1314
281
|
addExchangeSchema({
|
|
1315
282
|
exchangeName: "my-exchange",
|
|
1316
283
|
getCandles: async (symbol, interval, since, limit) => {
|
|
1317
|
-
const exchange = new ccxt.bybit({
|
|
284
|
+
const exchange = new ccxt.bybit({
|
|
285
|
+
apiKey: process.env.BYBIT_API_KEY, secret: process.env.BYBIT_API_SECRET, enableRateLimit: true,
|
|
286
|
+
});
|
|
1318
287
|
const ohlcv = await exchange.fetchOHLCV(symbol, interval, since.getTime(), limit);
|
|
1319
|
-
return ohlcv.map(([timestamp, open, high, low, close, volume]) =>
|
|
1320
|
-
timestamp, open, high, low, close, volume
|
|
1321
|
-
}));
|
|
288
|
+
return ohlcv.map(([timestamp, open, high, low, close, volume]) =>
|
|
289
|
+
({ timestamp, open, high, low, close, volume }));
|
|
1322
290
|
},
|
|
1323
|
-
formatPrice: (
|
|
1324
|
-
formatQuantity: (symbol, quantity) => quantity.toFixed(8),
|
|
291
|
+
formatPrice: (s, p) => p.toFixed(2), formatQuantity: (s, q) => q.toFixed(8),
|
|
1325
292
|
});
|
|
1326
293
|
```
|
|
1327
294
|
|
|
1328
|
-
|
|
295
|
+
```env
|
|
296
|
+
# .env (loaded root-first, then next to the target file)
|
|
297
|
+
BYBIT_API_KEY=xxx
|
|
298
|
+
BYBIT_API_SECRET=yyy
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
</details>
|
|
1329
302
|
|
|
1330
|
-
|
|
303
|
+
### ๐ฒ Pine โ run local PineScript (`--pine`)
|
|
304
|
+
|
|
305
|
+
Executes any local `.pine` file against a real exchange and prints results as a Markdown table โ no TradingView account. Reads every `plot()` that uses `display=display.data_window` as an output column (others ignored); column names come straight from the plot names.
|
|
1331
306
|
|
|
1332
307
|
```bash
|
|
1333
|
-
npx @backtest-kit/cli --
|
|
308
|
+
npx @backtest-kit/cli --pine ./math/impulse_trend_15m.pine --symbol BTCUSDT --timeframe 15m --limit 180 --when "2025-09-24T12:00:00.000Z"
|
|
1334
309
|
```
|
|
1335
310
|
|
|
1336
|
-
|
|
311
|
+
<details>
|
|
312
|
+
<summary>Pine flags, requirements & output</summary>
|
|
1337
313
|
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
314
|
+
| Flag | Type | Description |
|
|
315
|
+
|------|------|-------------|
|
|
316
|
+
| `--pine` | boolean | Enable PineScript mode |
|
|
317
|
+
| `--symbol` | string | Trading pair (default `"BTCUSDT"`) |
|
|
318
|
+
| `--timeframe` | string | Candle interval (default `"15m"`) |
|
|
319
|
+
| `--limit` | string | Candles to fetch (default `250`) |
|
|
320
|
+
| `--when` | string | End date โ ISO 8601 or Unix ms (default now) |
|
|
321
|
+
| `--exchange` | string | Exchange (default: first registered, falls back to CCXT Binance) |
|
|
322
|
+
| `--output` | string | Output base name (default: `.pine` file name) |
|
|
323
|
+
| `--json` | boolean | Write plots as JSON array to `<pine-dir>/dump/{output}.json` |
|
|
324
|
+
| `--jsonl` | boolean | Write plots as JSONL to `<pine-dir>/dump/{output}.jsonl` |
|
|
325
|
+
| `--markdown` | boolean | Write Markdown table to `<pine-dir>/dump/{output}.md` |
|
|
1341
326
|
|
|
1342
|
-
|
|
327
|
+
`--limit` must cover indicator warmup bars โ rows before warmup show `N/A`. Positional: path to the `.pine` file. Exchange via `pine.module` (see convention above). Required plot form:
|
|
1343
328
|
|
|
1344
|
-
```
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
329
|
+
```pine
|
|
330
|
+
//@version=5
|
|
331
|
+
indicator("MyIndicator", overlay=true)
|
|
332
|
+
plot(close, "Close", display=display.data_window)
|
|
333
|
+
plot(position, "Position", display=display.data_window)
|
|
1349
334
|
```
|
|
1350
335
|
|
|
1351
|
-
|
|
336
|
+
Output (stdout, or `--markdown`/`--json`/`--jsonl` to `<pine-dir>/dump/`):
|
|
1352
337
|
|
|
1353
|
-
```json
|
|
1354
|
-
{
|
|
1355
|
-
"scripts": {
|
|
1356
|
-
"dump": "npx @backtest-kit/cli --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl"
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
338
|
```
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
339
|
+
| Close | Position | timestamp |
|
|
340
|
+
| --- | --- | --- |
|
|
341
|
+
| 112871.28 | -1.0000 | 2025-09-22T15:00:00.000Z |
|
|
342
|
+
| 112736.00 | 0.0000 | 2025-09-22T18:30:00.000Z |
|
|
343
|
+
| 112653.90 | 1.0000 | 2025-09-22T22:15:00.000Z |
|
|
1363
344
|
```
|
|
1364
345
|
|
|
1365
|
-
|
|
346
|
+
</details>
|
|
1366
347
|
|
|
1367
|
-
|
|
348
|
+
### ๐จ Editor โ visual Pine Script editor (`--editor`)
|
|
1368
349
|
|
|
1369
|
-
|
|
350
|
+

|
|
1370
351
|
|
|
1371
|
-
|
|
1372
|
-
|------|------|-------------|
|
|
1373
|
-
| `--pnldebug` | boolean | Enable PnL debug mode |
|
|
1374
|
-
| `--priceopen` | number | Entry price (required) |
|
|
1375
|
-
| `--direction` | string | `long` or `short` (default: `long`) |
|
|
1376
|
-
| `--when` | string | Start timestamp โ ISO 8601 or Unix ms (default: now) |
|
|
1377
|
-
| `--minutes` | string | Number of 1m candles to simulate (default: `60`) |
|
|
1378
|
-
| `--symbol` | string | Trading pair (default: `"BTCUSDT"`) |
|
|
1379
|
-
| `--exchange` | string | Exchange name (default: first registered, falls back to CCXT Binance) |
|
|
1380
|
-
| `--output` | string | Output file base name (default: `{SYMBOL}_{DIRECTION}_{PRICEOPEN}_{TIMESTAMP}`) |
|
|
1381
|
-
| `--json` | boolean | Save results as JSON array to `./dump/<output>.json` |
|
|
1382
|
-
| `--jsonl` | boolean | Save results as JSONL to `./dump/<output>.jsonl` |
|
|
1383
|
-
| `--markdown` | boolean | Save results as Markdown table to `./dump/<output>.md` |
|
|
1384
|
-
|
|
1385
|
-
### Output columns
|
|
1386
|
-
|
|
1387
|
-
| Column | Description |
|
|
1388
|
-
|--------|-------------|
|
|
1389
|
-
| `min` | Minute offset from start (1-based) |
|
|
1390
|
-
| `timestamp` | Candle timestamp (ISO 8601) |
|
|
1391
|
-
| `close` | Candle close price |
|
|
1392
|
-
| `pnl%` | Running PnL vs entry price (signed %) |
|
|
1393
|
-
| `peak%` | Highest PnL reached so far (always โฅ 0) |
|
|
1394
|
-
| `drawdown%` | Lowest PnL reached so far (always โค 0) |
|
|
352
|
+
A browser-based Pine Script editor (powered by `@backtest-kit/ui`) with a live chart that updates on **โถ Run**.
|
|
1395
353
|
|
|
1396
|
-
|
|
354
|
+
```bash
|
|
355
|
+
npx @backtest-kit/cli --editor # โ http://localhost:60050?pine=1 opens automatically
|
|
356
|
+
```
|
|
1397
357
|
|
|
1398
|
-
|
|
358
|
+
<details>
|
|
359
|
+
<summary>Editor behavior & exchange</summary>
|
|
1399
360
|
|
|
1400
|
-
|
|
1401
|
-
// modules/pnldebug.module.ts
|
|
1402
|
-
import { addExchangeSchema } from "backtest-kit";
|
|
1403
|
-
import ccxt from "ccxt";
|
|
361
|
+
The CLI loads `./modules/editor.module` if present (register your exchange, same as `pine.module`), starts the `@backtest-kit/ui` server on `CC_WWWROOT_PORT` (default `60050`), and opens the editor in your browser. **Ctrl+C** stops it. Env: `CC_WWWROOT_HOST` (default `0.0.0.0`), `CC_WWWROOT_PORT` (default `60050`).
|
|
1404
362
|
|
|
1405
|
-
|
|
1406
|
-
exchangeName: "my-exchange",
|
|
1407
|
-
getCandles: async (symbol, interval, since, limit) => {
|
|
1408
|
-
const exchange = new ccxt.bybit({ enableRateLimit: true });
|
|
1409
|
-
const ohlcv = await exchange.fetchOHLCV(symbol, interval, since.getTime(), limit);
|
|
1410
|
-
return ohlcv.map(([timestamp, open, high, low, close, volume]) => ({
|
|
1411
|
-
timestamp, open, high, low, close, volume,
|
|
1412
|
-
}));
|
|
1413
|
-
},
|
|
1414
|
-
formatPrice: (symbol, price) => price.toFixed(2),
|
|
1415
|
-
formatQuantity: (symbol, quantity) => quantity.toFixed(8),
|
|
1416
|
-
});
|
|
1417
|
-
```
|
|
363
|
+
</details>
|
|
1418
364
|
|
|
1419
|
-
###
|
|
365
|
+
### ๐พ Candle Dump (`--dump`)
|
|
1420
366
|
|
|
1421
|
-
|
|
367
|
+
Fetch raw OHLCV candles from any registered exchange and save them โ no strategy file required. `dump/` is created in the current working directory.
|
|
1422
368
|
|
|
1423
369
|
```bash
|
|
1424
|
-
npx @backtest-kit/cli --
|
|
370
|
+
npx @backtest-kit/cli --dump --symbol BTCUSDT --timeframe 15m --limit 500 --when "2026-02-28T00:00:00.000Z" --jsonl --output feb2026_btc
|
|
371
|
+
# โ ./dump/feb2026_btc.jsonl
|
|
1425
372
|
```
|
|
1426
373
|
|
|
1427
|
-
|
|
374
|
+
<details>
|
|
375
|
+
<summary>Dump flags</summary>
|
|
1428
376
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
377
|
+
| Flag | Type | Description |
|
|
378
|
+
|------|------|-------------|
|
|
379
|
+
| `--dump` | boolean | Enable candle dump |
|
|
380
|
+
| `--symbol` | string | Trading pair (default `"BTCUSDT"`) |
|
|
381
|
+
| `--timeframe` | string | Candle interval (default `"15m"`) |
|
|
382
|
+
| `--limit` | string | Candles to fetch (default `250`) |
|
|
383
|
+
| `--when` | string | End date โ ISO 8601 or Unix ms (default now) |
|
|
384
|
+
| `--exchange` | string | Exchange (default: first registered, falls back to CCXT Binance) |
|
|
385
|
+
| `--output` | string | Output base name (default `{SYMBOL}_{LIMIT}_{TIMEFRAME}_{TIMESTAMP}`) |
|
|
386
|
+
| `--json` | boolean | Write candles as JSON array to `./dump/{output}.json` |
|
|
387
|
+
| `--jsonl` | boolean | Write candles as JSONL to `./dump/{output}.jsonl` |
|
|
1433
388
|
|
|
1434
|
-
|
|
389
|
+
Exchange via `dump.module` (see convention above), searched in the current working directory. No flag โ print to stdout.
|
|
1435
390
|
|
|
1436
|
-
|
|
1437
|
-
npx @backtest-kit/cli --pnldebug --priceopen 64069.50 --direction short --when "2025-02-25" --minutes 120 \
|
|
1438
|
-
--jsonl --output feb25_short_debug
|
|
1439
|
-
# โ ./dump/feb25_short_debug.jsonl
|
|
1440
|
-
```
|
|
391
|
+
</details>
|
|
1441
392
|
|
|
1442
|
-
|
|
393
|
+
### ๐ PnL Debug (`--pnldebug`)
|
|
1443
394
|
|
|
1444
|
-
|
|
1445
|
-
{
|
|
1446
|
-
"scripts": {
|
|
1447
|
-
"pnldebug": "npx @backtest-kit/cli --pnldebug --symbol BTCUSDT --priceopen 64069.50 --direction short --when \"2025-02-25\" --minutes 120"
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
|
-
```
|
|
395
|
+
Simulate a hypothetical position minute by minute โ running PnL, peak profit, max drawdown per candle โ without placing trades or loading a strategy.
|
|
1451
396
|
|
|
1452
397
|
```bash
|
|
1453
|
-
|
|
398
|
+
npx @backtest-kit/cli --pnldebug --symbol BTCUSDT --priceopen 64069.50 --direction short --when "2025-02-25" --minutes 120
|
|
1454
399
|
```
|
|
1455
400
|
|
|
1456
|
-
|
|
401
|
+
<details>
|
|
402
|
+
<summary>PnL Debug flags, columns & sample output</summary>
|
|
403
|
+
|
|
404
|
+
| Flag | Type | Description |
|
|
405
|
+
|------|------|-------------|
|
|
406
|
+
| `--pnldebug` | boolean | Enable PnL debug |
|
|
407
|
+
| `--priceopen` | number | Entry price (**required**) |
|
|
408
|
+
| `--direction` | string | `long` or `short` (default `long`) |
|
|
409
|
+
| `--when` | string | Start timestamp โ ISO 8601 or Unix ms (default now) |
|
|
410
|
+
| `--minutes` | string | Number of 1m candles to simulate (default `60`) |
|
|
411
|
+
| `--symbol` | string | Trading pair (default `"BTCUSDT"`) |
|
|
412
|
+
| `--exchange` | string | Exchange (default: first registered, falls back to CCXT Binance) |
|
|
413
|
+
| `--output` | string | Output base name (default `{SYMBOL}_{DIRECTION}_{PRICEOPEN}_{TIMESTAMP}`) |
|
|
414
|
+
| `--json` / `--jsonl` / `--markdown` | boolean | Save to `./dump/<output>.{json,jsonl,md}` |
|
|
415
|
+
|
|
416
|
+
Columns: `min` (1-based offset), `timestamp`, `close`, `pnl%` (signed, vs entry), `peak%` (highest so far, โฅ0), `drawdown%` (lowest so far, โค0). Exchange via `pnldebug.module` (convention above).
|
|
1457
417
|
|
|
1458
418
|
```
|
|
1459
419
|
Symbol: BTCUSDT | Direction: short | PriceOpen: 64069.50 | From: 2025-02-25T00:00:00.000Z | Minutes: 120
|
|
1460
|
-
|
|
1461
420
|
min | timestamp | close | pnl% | peak% | drawdown%
|
|
1462
|
-
-----------------------------------------------------------------------------------
|
|
1463
421
|
1 | 2025-02-25T00:01:00.000Z | 64020.10 | +0.08% | +0.08% | 0.00%
|
|
1464
422
|
2 | 2025-02-25T00:02:00.000Z | 64105.30 | -0.06% | +0.08% | -0.06%
|
|
1465
|
-
...
|
|
1466
423
|
120 | 2025-02-25T02:00:00.000Z | 63200.00 | +1.36% | +1.36% | -0.06%
|
|
1467
424
|
```
|
|
1468
425
|
|
|
1469
|
-
|
|
426
|
+
</details>
|
|
427
|
+
|
|
428
|
+
### ๐ Broker Debug (`--brokerdebug`)
|
|
1470
429
|
|
|
1471
|
-
|
|
430
|
+
Fire a single broker commit against your live adapter without a full strategy โ verify your `brokerdebug.module` wires exchange calls correctly before waiting hours for a real signal.
|
|
1472
431
|
|
|
1473
|
-
|
|
432
|
+
```bash
|
|
433
|
+
npx @backtest-kit/cli --brokerdebug --commit signal-open --symbol BTCUSDT
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
<details>
|
|
437
|
+
<summary>Broker Debug flags, commit types & how it works</summary>
|
|
1474
438
|
|
|
1475
439
|
| Flag | Type | Description |
|
|
1476
440
|
|------|------|-------------|
|
|
1477
|
-
| `--brokerdebug` | boolean | Enable broker debug
|
|
1478
|
-
| `--commit` | string | Commit type to fire (default
|
|
1479
|
-
| `--symbol` | string | Trading pair (default
|
|
1480
|
-
| `--exchange` | string | Exchange
|
|
441
|
+
| `--brokerdebug` | boolean | Enable broker debug |
|
|
442
|
+
| `--commit` | string | Commit type to fire (default `"signal-open"`) |
|
|
443
|
+
| `--symbol` | string | Trading pair (default `"BTCUSDT"`) |
|
|
444
|
+
| `--exchange` | string | Exchange (default: first registered) |
|
|
1481
445
|
|
|
1482
|
-
|
|
446
|
+
`--commit` values โ hook: `signal-open`โ`onSignalOpenCommit`, `signal-close`โ`onSignalCloseCommit`, `partial-profit`โ`onPartialProfitCommit`, `partial-loss`โ`onPartialLossCommit`, `average-buy`โ`onAverageBuyCommit`, `trailing-stop`โ`onTrailingStopCommit`, `trailing-take`โ`onTrailingTakeCommit`, `breakeven`โ`onBreakevenCommit`.
|
|
1483
447
|
|
|
1484
|
-
|
|
1485
|
-
|-------|-------------|
|
|
1486
|
-
| `signal-open` | `onSignalOpenCommit` |
|
|
1487
|
-
| `signal-close` | `onSignalCloseCommit` |
|
|
1488
|
-
| `partial-profit` | `onPartialProfitCommit` |
|
|
1489
|
-
| `partial-loss` | `onPartialLossCommit` |
|
|
1490
|
-
| `average-buy` | `onAverageBuyCommit` |
|
|
1491
|
-
| `trailing-stop` | `onTrailingStopCommit` |
|
|
1492
|
-
| `trailing-take` | `onTrailingTakeCommit` |
|
|
1493
|
-
| `breakeven` | `onBreakevenCommit` |
|
|
448
|
+
The CLI loads `./modules/brokerdebug.module`, fetches the last candle for `--symbol`, derives a synthetic payload from `currentPrice` (TP = +2%, SL = โ2%), and calls the selected hook once; exits `0` on success. The module registers a `Broker` adapter (`Broker.useBrokerAdapter(...)` + `Broker.enable()`), not an exchange.
|
|
1494
449
|
|
|
1495
|
-
|
|
450
|
+
</details>
|
|
1496
451
|
|
|
1497
|
-
|
|
452
|
+
### ๐๏ธ Flush (`--flush`)
|
|
1498
453
|
|
|
1499
|
-
|
|
454
|
+
Delete generated output folders from one or more strategy dump dirs **without** touching cached candle data.
|
|
1500
455
|
|
|
1501
|
-
|
|
456
|
+
```bash
|
|
457
|
+
npx @backtest-kit/cli --flush ./content/feb_2026.strategy/modules/backtest.module.ts ./content/mar_2026.strategy/modules/backtest.module.ts
|
|
458
|
+
```
|
|
1502
459
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
import { Broker } from 'backtest-kit';
|
|
1506
|
-
import { myExchange } from './exchange.mjs';
|
|
460
|
+
<details>
|
|
461
|
+
<summary>What flush removes</summary>
|
|
1507
462
|
|
|
1508
|
-
|
|
1509
|
-
async onSignalOpenCommit({ symbol, priceOpen, position }) {
|
|
1510
|
-
await myExchange.openPosition(symbol, position, priceOpen);
|
|
1511
|
-
}
|
|
1512
|
-
// ... other hooks
|
|
1513
|
-
}
|
|
463
|
+
For each positional entry point the CLI resolves its directory and removes from `<entry-dir>/dump/`: `report` (backtest `.jsonl`), `log` (`log.jsonl`), `markdown` (exported reports), `agent` (agent outlines). Candle cache (`dump/data/`) and AI forecast outlines (`dump/outline/`) are **not** removed.
|
|
1514
464
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
465
|
+
</details>
|
|
466
|
+
|
|
467
|
+
---
|
|
1518
468
|
|
|
1519
|
-
|
|
469
|
+
## ๐๏ธ Project & monorepo
|
|
470
|
+
|
|
471
|
+
### Scaffolding (`--init`)
|
|
472
|
+
|
|
473
|
+
Bootstraps a ready-to-use project with an example strategy, an example Pine indicator, an AI-agent `CLAUDE.md`, and documentation fetched automatically. The target dir must not exist or be empty.
|
|
1520
474
|
|
|
1521
475
|
```bash
|
|
1522
|
-
npx @backtest-kit/cli --
|
|
1523
|
-
npx @backtest-kit/cli --brokerdebug --commit partial-profit --symbol ETHUSDT --timeframe 1h
|
|
476
|
+
npx @backtest-kit/cli --init --output my-trading-bot # โ ./my-trading-bot/
|
|
1524
477
|
```
|
|
1525
478
|
|
|
1526
|
-
|
|
479
|
+
<details>
|
|
480
|
+
<summary>Generated structure & automatic docs fetch</summary>
|
|
1527
481
|
|
|
1528
|
-
|
|
482
|
+
```
|
|
483
|
+
backtest-kit-project/
|
|
484
|
+
โโโ package.json # pre-configured with all backtest-kit deps
|
|
485
|
+
โโโ CLAUDE.md # AI-agent guide for writing strategies
|
|
486
|
+
โโโ content/feb_2026.strategy.ts # example strategy entry point
|
|
487
|
+
โโโ math/feb_2026.pine # example PineScript indicator
|
|
488
|
+
โโโ modules/{dump,pine}.module.ts # exchange schemas for --dump / --pine
|
|
489
|
+
โโโ report/feb_2026.md # example research report
|
|
490
|
+
โโโ docs/{...}.md + docs/lib/ # guides + fetched library READMEs
|
|
491
|
+
โโโ scripts/fetch_docs.mjs # downloads library READMEs into docs/lib/
|
|
492
|
+
```
|
|
1529
493
|
|
|
1530
|
-
|
|
494
|
+
After scaffolding the CLI runs `scripts/fetch_docs.mjs`, downloading the latest READMEs for `backtest-kit`, `@backtest-kit/graph`, `@backtest-kit/pinets`, `@backtest-kit/cli`, `garch`, `volume-anomaly`, `agent-swarm-kit`, `functools-kit` into `docs/lib/`. Re-run anytime with `node ./scripts/fetch_docs.mjs` or `npm run sync:lib`.
|
|
1531
495
|
|
|
1532
|
-
|
|
1533
|
-
|------|------|-------------|
|
|
1534
|
-
| `--flush` | boolean | Enable flush mode |
|
|
496
|
+
</details>
|
|
1535
497
|
|
|
1536
|
-
|
|
498
|
+
### Docker (`--docker`)
|
|
1537
499
|
|
|
1538
|
-
|
|
1539
|
-
|--------|----------|
|
|
1540
|
-
| `report` | Backtest report files (`.jsonl`) |
|
|
1541
|
-
| `log` | Run logs (`log.jsonl`) |
|
|
1542
|
-
| `markdown` | Exported Markdown reports |
|
|
1543
|
-
| `agent` | Agent outline files |
|
|
500
|
+
Scaffolds a self-contained Docker workspace with `docker-compose.yaml` and a strategy entry point, for zero-downtime live trading.
|
|
1544
501
|
|
|
1545
|
-
|
|
502
|
+
```bash
|
|
503
|
+
npx @backtest-kit/cli --docker && cd backtest-kit-docker
|
|
504
|
+
MODE=live SYMBOL=TRXUSDT STRATEGY_FILE=./content/feb_2026/feb_2026.strategy.ts docker-compose up -d
|
|
505
|
+
```
|
|
1546
506
|
|
|
1547
|
-
|
|
507
|
+
<details>
|
|
508
|
+
<summary>Two launch modes & environment variables</summary>
|
|
1548
509
|
|
|
1549
|
-
|
|
510
|
+
**1. `command:` in `docker-compose.yaml`** โ pin mode and flags directly; the entrypoint forwards all args to the CLI unchanged:
|
|
1550
511
|
|
|
1551
|
-
```
|
|
1552
|
-
|
|
512
|
+
```yaml
|
|
513
|
+
command: [--live, --symbol, TRXUSDT, --strategy, feb_2026_strategy, --exchange, ccxt-exchange, ./content/feb_2026/feb_2026.strategy.ts, --ui]
|
|
1553
514
|
```
|
|
1554
515
|
|
|
1555
|
-
|
|
516
|
+
**2. Inline env vars** โ `MODE` + `STRATEGY_FILE` on the command line, no file edits:
|
|
1556
517
|
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
518
|
+
| Variable | Required | Default | Description |
|
|
519
|
+
|----------|----------|---------|-------------|
|
|
520
|
+
| `MODE` | yes | โ | `backtest` \| `live` \| `paper` \| `walker` |
|
|
521
|
+
| `STRATEGY_FILE` | yes | โ | Path to entry point (relative to `working_dir`) |
|
|
522
|
+
| `SYMBOL` | no | `BTCUSDT` | Trading pair |
|
|
523
|
+
| `STRATEGY` / `EXCHANGE` / `FRAME` | no | first registered | Names |
|
|
524
|
+
| `UI` / `TELEGRAM` / `VERBOSE` / `NO_CACHE` / `NO_FLUSH` / `ENTRY` | no | โ | Any non-empty value enables the matching flag |
|
|
525
|
+
|
|
526
|
+
</details>
|
|
527
|
+
|
|
528
|
+
### Monorepo cwd resolution
|
|
529
|
+
|
|
530
|
+
When the CLI loads an entry point it **changes the working directory to that file's location**, so every relative path (`dump/`, `modules/`, `template/`) resolves inside that strategy's folder. Each strategy gets its own `.env`, broker modules, templates, and dump dir โ so the same tool scales from one strategy to a desk of them.
|
|
531
|
+
|
|
532
|
+
<details>
|
|
533
|
+
<summary>How it works + isolated resources</summary>
|
|
534
|
+
|
|
535
|
+
`ResolveService` runs, before executing your entry point:
|
|
536
|
+
|
|
537
|
+
```
|
|
538
|
+
process.chdir(path.dirname(entryPoint)) // cwd โ strategy directory
|
|
539
|
+
dotenv.config({ path: rootDir + '/.env' }) // root .env first
|
|
540
|
+
dotenv.config({ path: strategyDir + '/.env', override: true }) // strategy .env overrides
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
```
|
|
544
|
+
monorepo/
|
|
545
|
+
โโโ package.json # root scripts (one per strategy)
|
|
546
|
+
โโโ .env # shared API keys
|
|
547
|
+
โโโ strategies/
|
|
548
|
+
โโโ oct_2025/
|
|
549
|
+
โ โโโ index.mjs # registers exchange/frame/strategy schemas
|
|
550
|
+
โ โโโ .env # overrides root .env for this strategy
|
|
551
|
+
โ โโโ modules/{live,paper,backtest}.module.mjs # broker adapters (optional)
|
|
552
|
+
โ โโโ template/ # custom Mustache templates (optional)
|
|
553
|
+
โ โโโ dump/ # auto-created: candle cache + reports
|
|
554
|
+
โโโ dec_2025/ โฆ
|
|
1561
555
|
```
|
|
1562
556
|
|
|
1563
|
-
|
|
557
|
+
| Resource | Path (relative to strategy dir) | Isolated |
|
|
558
|
+
|----------|----------------------------------|----------|
|
|
559
|
+
| Candle cache | `./dump/data/candle/` | โ
per-strategy |
|
|
560
|
+
| Backtest reports | `./dump/` | โ
per-strategy |
|
|
561
|
+
| Broker module (live/paper/backtest) | `./modules/{live,paper,backtest}.module.mjs` | โ
per-strategy |
|
|
562
|
+
| Config module (walker) | `./modules/walker.module.mjs` | โ
loaded once |
|
|
563
|
+
| Telegram templates | `./template/*.mustache` | โ
per-strategy |
|
|
564
|
+
| Environment variables | `./.env` (overrides root) | โ
per-strategy |
|
|
565
|
+
|
|
566
|
+
Each run produces its own `dump/` โ easy to compare results across periods, by inspection or by pointing an AI agent at a specific folder.
|
|
567
|
+
|
|
568
|
+
</details>
|
|
569
|
+
|
|
570
|
+
### Folder-based import aliases
|
|
571
|
+
|
|
572
|
+
Every **top-level folder** in `process.cwd()` 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.
|
|
573
|
+
|
|
574
|
+
<details>
|
|
575
|
+
<summary>Resolution table, structure & tsconfig</summary>
|
|
576
|
+
|
|
577
|
+
| Import | Resolves to |
|
|
578
|
+
|--------|-------------|
|
|
579
|
+
| `import { fn } from "utils"` | `<cwd>/utils/index.ts` (or `.js`/`.mjs`/`.cjs`) |
|
|
580
|
+
| `import { calcRSI } from "math/rsi"` | `<cwd>/math/rsi.ts` |
|
|
581
|
+
| `import { research } from "logic"` | `<cwd>/logic/index.ts` |
|
|
582
|
+
| `import { X } from "logic/contract/ResearchResponse.contract"` | `<cwd>/logic/contract/ResearchResponse.contract.ts` |
|
|
583
|
+
|
|
584
|
+
Both barrel and deep-subpath imports are supported. Add a matching `paths` entry to `tsconfig.json` so the editor resolves them:
|
|
1564
585
|
|
|
1565
586
|
```json
|
|
1566
587
|
{
|
|
1567
|
-
"
|
|
1568
|
-
"
|
|
1569
|
-
|
|
588
|
+
"compilerOptions": {
|
|
589
|
+
"moduleResolution": "bundler",
|
|
590
|
+
"paths": { "logic": ["./logic/index.ts"], "logic/*": ["./logic/*"], "math": ["./math/index.ts"], "math/*": ["./math/*"], "utils": ["./utils/index.ts"], "utils/*": ["./utils/*"] }
|
|
591
|
+
},
|
|
592
|
+
"include": ["./logic", "./math", "./utils", "./content", "./modules"]
|
|
1570
593
|
}
|
|
1571
594
|
```
|
|
1572
595
|
|
|
1573
|
-
|
|
1574
|
-
npm run flush
|
|
1575
|
-
```
|
|
596
|
+
</details>
|
|
1576
597
|
|
|
1577
|
-
|
|
598
|
+
### Entry point formats
|
|
1578
599
|
|
|
1579
|
-
|
|
600
|
+
The CLI auto-detects the format and loads it with the right runtime โ no flags. `.ts` via [`tsx`](https://tsx.is/) `tsImport()` (handles ESMโCJS cross-imports, no `tsc` step), `.mjs` via native `import()` (top-level `await`, ESM), `.cjs` via native `require()` (legacy/dual-package). Add `tsx` to deps for `.ts` strategies.
|
|
1580
601
|
|
|
1581
|
-
|
|
602
|
+
---
|
|
1582
603
|
|
|
1583
|
-
|
|
1584
|
-
|------|------|-------------|
|
|
1585
|
-
| `--init` | boolean | Scaffold a new project |
|
|
1586
|
-
| `--output` | string | Target directory name (default: `backtest-kit-project`) |
|
|
604
|
+
## ๐ Broker adapter โ transactional live orders
|
|
1587
605
|
|
|
1588
|
-
|
|
606
|
+
Mode-specific module files register a `Broker` adapter via side-effect import before the strategy starts. From then on, `backtest-kit` intercepts **every** trade-mutating call through the adapter *before* 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.
|
|
1589
607
|
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
608
|
+
| Mode flag | Module file | Loaded before |
|
|
609
|
+
|-----------|-------------|----------------|
|
|
610
|
+
| `--live` | `./modules/live.module.mjs` | `Live.background()` |
|
|
611
|
+
| `--paper` | `./modules/paper.module.mjs` | `Live.background()` (paper) |
|
|
612
|
+
| `--backtest` | `./modules/backtest.module.mjs` | `Backtest.background()` |
|
|
613
|
+
| `--walker` | `./modules/walker.module.mjs` | `Walker.background()` |
|
|
614
|
+
| `--main` | `./modules/main.module.mjs` | the custom entry point |
|
|
615
|
+
| `--brokerdebug` | `./modules/brokerdebug.module.mjs` | the broker commit test |
|
|
1593
616
|
|
|
1594
|
-
|
|
617
|
+
> Resolved relative to `cwd` (the strategy dir); `.mjs`/`.cjs`/`.ts` tried automatically. A missing module is a soft warning, not an error.
|
|
1595
618
|
|
|
1596
|
-
|
|
619
|
+
<details>
|
|
620
|
+
<summary>Adapter example & hook reference</summary>
|
|
1597
621
|
|
|
1598
|
-
```
|
|
1599
|
-
|
|
622
|
+
```javascript
|
|
623
|
+
// live.module.mjs
|
|
624
|
+
import { Broker } from 'backtest-kit';
|
|
625
|
+
import { myExchange } from './exchange.mjs';
|
|
626
|
+
|
|
627
|
+
class MyBroker {
|
|
628
|
+
async onSignalOpenCommit({ symbol, priceOpen, direction }) { await myExchange.openPosition(symbol, direction, priceOpen); }
|
|
629
|
+
async onSignalCloseCommit({ symbol, priceClosed }) { await myExchange.closePosition(symbol, priceClosed); }
|
|
630
|
+
async onPartialProfitCommit({ symbol, cost, currentPrice }) { await myExchange.createOrder({ symbol, side: 'sell', quantity: cost / currentPrice }); }
|
|
631
|
+
async onAverageBuyCommit({ symbol, cost, currentPrice }) { await myExchange.createOrder({ symbol, side: 'buy', quantity: cost / currentPrice }); }
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
Broker.useBrokerAdapter(MyBroker);
|
|
635
|
+
Broker.enable();
|
|
1600
636
|
```
|
|
1601
637
|
|
|
1602
|
-
|
|
638
|
+
| Method | Payload type | Triggered on |
|
|
639
|
+
|--------|--------------|--------------|
|
|
640
|
+
| `onSignalOpenCommit` | `BrokerSignalOpenPayload` | Position activation |
|
|
641
|
+
| `onSignalCloseCommit` | `BrokerSignalClosePayload` | SL / TP / manual close |
|
|
642
|
+
| `onPartialProfitCommit` | `BrokerPartialProfitPayload` | Partial profit |
|
|
643
|
+
| `onPartialLossCommit` | `BrokerPartialLossPayload` | Partial loss |
|
|
644
|
+
| `onTrailingStopCommit` | `BrokerTrailingStopPayload` | SL adjustment |
|
|
645
|
+
| `onTrailingTakeCommit` | `BrokerTrailingTakePayload` | TP adjustment |
|
|
646
|
+
| `onBreakevenCommit` | `BrokerBreakevenPayload` | SL moved to entry |
|
|
647
|
+
| `onAverageBuyCommit` | `BrokerAverageBuyPayload` | DCA entry |
|
|
1603
648
|
|
|
1604
|
-
|
|
649
|
+
All methods are optional; unimplemented hooks are silently skipped. TypeScript: implement `Partial<IBroker>` with typed payloads (`BrokerSignalOpenPayload`, etc.).
|
|
1605
650
|
|
|
1606
|
-
|
|
651
|
+
</details>
|
|
1607
652
|
|
|
1608
|
-
|
|
1609
|
-
backtest-kit-project/
|
|
1610
|
-
โโโ package.json # pre-configured with all backtest-kit dependencies
|
|
1611
|
-
โโโ .gitignore
|
|
1612
|
-
โโโ CLAUDE.md # AI-agent guide for writing strategies
|
|
1613
|
-
โโโ content/
|
|
1614
|
-
โ โโโ feb_2026.strategy.ts # example strategy entry point
|
|
1615
|
-
โโโ docs/
|
|
1616
|
-
โ โโโ lib/ # fetched automatically (see below)
|
|
1617
|
-
โ โโโ backtest_actions.md
|
|
1618
|
-
โ โโโ backtest_graph_pattern.md
|
|
1619
|
-
โ โโโ backtest_logging_jsonl.md
|
|
1620
|
-
โ โโโ backtest_pinets_usage.md
|
|
1621
|
-
โ โโโ backtest_risk_async.md
|
|
1622
|
-
โ โโโ backtest_strategy_structure.md
|
|
1623
|
-
โ โโโ pine_debug.md
|
|
1624
|
-
โ โโโ pine_indicator_warmup.md
|
|
1625
|
-
โโโ math/
|
|
1626
|
-
โ โโโ feb_2026.pine # example PineScript indicator
|
|
1627
|
-
โโโ modules/
|
|
1628
|
-
โ โโโ dump.module.ts # exchange schema for --dump mode
|
|
1629
|
-
โ โโโ pine.module.ts # exchange schema for --pine mode
|
|
1630
|
-
โโโ report/
|
|
1631
|
-
โ โโโ feb_2026.md # example strategy research report
|
|
1632
|
-
โโโ scripts/
|
|
1633
|
-
โโโ fetch_docs.mjs # utility: downloads library READMEs into docs/lib/
|
|
1634
|
-
```
|
|
653
|
+
---
|
|
1635
654
|
|
|
1636
|
-
|
|
655
|
+
## โ๏ธ Configuration files (`config/*`)
|
|
1637
656
|
|
|
1638
|
-
|
|
657
|
+
Loaded from `{projectRoot}/config/`. The three runtime configs load in order โ **`setup.config` โ `loader.config` โ `alias.config`** โ before any strategy or module code. The UI/Telegram configs resolve **strategy dir โ project root โ package default** (first match wins) and accept `.ts`/`.cjs`/`.mjs`/`.js`.
|
|
1639
658
|
|
|
1640
|
-
|
|
1641
|
-
|------|--------|
|
|
1642
|
-
| `backtest-kit.md` | `backtest-kit` README |
|
|
1643
|
-
| `backtest-kit__graph.md` | `@backtest-kit/graph` README |
|
|
1644
|
-
| `backtest-kit__pinets.md` | `@backtest-kit/pinets` README |
|
|
1645
|
-
| `backtest-kit__cli.md` | `@backtest-kit/cli` README |
|
|
1646
|
-
| `garch.md` | `garch` README |
|
|
1647
|
-
| `volume-anomaly.md` | `volume-anomaly` README |
|
|
1648
|
-
| `agent-swarm-kit.md` | `agent-swarm-kit` README |
|
|
1649
|
-
| `functools-kit.md` | `functools-kit` README |
|
|
659
|
+
### `setup.config` โ persistence & one-time init
|
|
1650
660
|
|
|
1651
|
-
|
|
661
|
+
Loaded once before any persistence call. **When present, the CLI skips its own default adapter registration** โ your config takes full ownership of the persistence layer.
|
|
1652
662
|
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
node ./scripts/fetch_docs.mjs
|
|
1656
|
-
```
|
|
663
|
+
<details>
|
|
664
|
+
<summary>MongoDB + Redis via @backtest-kit/mongo</summary>
|
|
1657
665
|
|
|
1658
|
-
|
|
666
|
+
`setup()` registers all 15 persistence adapters in one call, reading connection params from env (or passed explicitly):
|
|
1659
667
|
|
|
1660
|
-
```
|
|
1661
|
-
|
|
668
|
+
```ts
|
|
669
|
+
// config/setup.config.ts
|
|
670
|
+
import { setup } from '@backtest-kit/mongo';
|
|
671
|
+
setup(); // or setup({ CC_MONGO_CONNECTION_STRING, CC_REDIS_HOST, CC_REDIS_PORT, CC_REDIS_PASSWORD })
|
|
1662
672
|
```
|
|
1663
673
|
|
|
1664
|
-
|
|
674
|
+
```env
|
|
675
|
+
CC_MONGO_CONNECTION_STRING=mongodb://localhost:27017/backtest-kit
|
|
676
|
+
CC_REDIS_HOST=127.0.0.1
|
|
677
|
+
CC_REDIS_PORT=6379
|
|
678
|
+
```
|
|
1665
679
|
|
|
1666
|
-
|
|
680
|
+
No strategy-code changes โ adapters are wired transparently before the first persistence call.
|
|
1667
681
|
|
|
1668
|
-
|
|
682
|
+
</details>
|
|
1669
683
|
|
|
1670
|
-
|
|
1671
|
-
|------|------|-------------|
|
|
1672
|
-
| `--docker` | boolean | Scaffold a Docker workspace |
|
|
1673
|
-
| `--output` | string | Target directory name (default: `backtest-kit-docker`) |
|
|
684
|
+
### `loader.config` โ async startup gate
|
|
1674
685
|
|
|
1675
|
-
|
|
686
|
+
Loaded **after** `setup.config`, **before** strategy/module code. Unlike `setup.config` (side-effect import), it exports a function the CLI `await`s โ use it to wait for an async dependency before the run starts.
|
|
1676
687
|
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
```
|
|
688
|
+
<details>
|
|
689
|
+
<summary>When to use it, export styles & examples</summary>
|
|
1680
690
|
|
|
1681
|
-
|
|
691
|
+
**Use it to:** 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.
|
|
1682
692
|
|
|
1683
|
-
|
|
693
|
+
Exactly one export style โ **never both** (if both present, `default` wins):
|
|
1684
694
|
|
|
1685
|
-
```
|
|
1686
|
-
|
|
695
|
+
```ts
|
|
696
|
+
// config/loader.config.ts โ default export (preferred)
|
|
697
|
+
export default async () => { await mongoose.connect(process.env.CC_MONGO_CONNECTION_STRING!); await redis.ping(); };
|
|
698
|
+
// โ or named export
|
|
699
|
+
export const loader = async () => { /* โฆ */ };
|
|
1687
700
|
```
|
|
1688
701
|
|
|
1689
|
-
|
|
702
|
+
`@backtest-kit/mongo`'s `setup()` registers adapters synchronously but doesn't block on the connection; gate the run on a real connection here. To stitch microfrontends: `import "@my-org/brokers"; import "@my-org/signals";` (the `@my-org` alias is declared in `alias.config`).
|
|
1690
703
|
|
|
1691
|
-
|
|
704
|
+
</details>
|
|
1692
705
|
|
|
1693
|
-
|
|
706
|
+
### `alias.config` โ override any module import
|
|
1694
707
|
|
|
1695
|
-
|
|
708
|
+
Override any Node module import without touching strategy code. Loaded once on the first `import` and applied globally โ e.g. replace a heavy dependency with a stub for backtesting, or swap an external API for a mock in CI.
|
|
1696
709
|
|
|
1697
|
-
|
|
710
|
+
<details>
|
|
711
|
+
<summary>Formats & async factory</summary>
|
|
1698
712
|
|
|
1699
|
-
```
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
- --strategy
|
|
1705
|
-
- feb_2026_strategy
|
|
1706
|
-
- --exchange
|
|
1707
|
-
- ccxt-exchange
|
|
1708
|
-
- ./content/feb_2026/feb_2026.strategy.ts
|
|
1709
|
-
- --ui
|
|
713
|
+
```ts
|
|
714
|
+
// config/alias.config.ts โ named export
|
|
715
|
+
export const ccxt = require("./stubs/ccxt.stub.cjs");
|
|
716
|
+
// config/alias.config.cjs โ default export
|
|
717
|
+
module.exports = { ccxt: require("./stubs/ccxt.stub.cjs") };
|
|
1710
718
|
```
|
|
1711
719
|
|
|
1712
|
-
|
|
720
|
+
It may also export an **async factory** the CLI `await`s before strategy code runs โ handy for ESM-only modules that `require()` would throw on:
|
|
1713
721
|
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
MODE=live SYMBOL=TRXUSDT STRATEGY_FILE=./content/feb_2026/feb_2026.strategy.ts docker-compose up -d
|
|
722
|
+
```ts
|
|
723
|
+
// async factory (default export); or `export const loader = async () => ({...})`
|
|
724
|
+
export default async () => ({ nanoid: await import("nanoid"), "p-limit": await import("p-limit") });
|
|
1718
725
|
```
|
|
1719
726
|
|
|
1720
|
-
|
|
1721
|
-
|----------|----------|---------|-------------|
|
|
1722
|
-
| `MODE` | yes | โ | `backtest` \| `live` \| `paper` \| `walker` |
|
|
1723
|
-
| `STRATEGY_FILE` | yes | โ | Path to strategy entry point (relative to `working_dir`) |
|
|
1724
|
-
| `SYMBOL` | no | `BTCUSDT` | Trading pair |
|
|
1725
|
-
| `STRATEGY` | no | first registered | Strategy name |
|
|
1726
|
-
| `EXCHANGE` | no | first registered | Exchange name |
|
|
1727
|
-
| `FRAME` | no | first registered | Frame name (backtest only) |
|
|
1728
|
-
| `UI` | no | โ | Any non-empty value enables `--ui` |
|
|
1729
|
-
| `TELEGRAM` | no | โ | Any non-empty value enables `--telegram` |
|
|
1730
|
-
| `VERBOSE` | no | โ | Any non-empty value enables `--verbose` |
|
|
1731
|
-
| `NO_CACHE` | no | โ | Any non-empty value enables `--noCache` |
|
|
1732
|
-
| `NO_FLUSH` | no | โ | Any non-empty value enables `--noFlush` |
|
|
1733
|
-
| `ENTRY` | no | โ | Any non-empty value enables multiple symbols from userspace |
|
|
727
|
+
Both styles supported, never both at once (`default` wins). When strategy code calls `require("ccxt")`, the loader checks the alias table first โ no monkey-patching of `node_modules`. Applies to **all** modules in the process (not per-strategy).
|
|
1734
728
|
|
|
1735
|
-
|
|
729
|
+
</details>
|
|
1736
730
|
|
|
1737
|
-
|
|
731
|
+
### `symbol.config` & `notification.config` โ UI dashboard
|
|
1738
732
|
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
CC_TELEGRAM_TOKEN=your_bot_token_here
|
|
1742
|
-
CC_TELEGRAM_CHANNEL=-100123456789
|
|
733
|
+
<details>
|
|
734
|
+
<summary>symbol.config โ restrict/reorder the UI symbol list</summary>
|
|
1743
735
|
|
|
1744
|
-
|
|
1745
|
-
CC_WWWROOT_HOST=0.0.0.0
|
|
1746
|
-
CC_WWWROOT_PORT=60050
|
|
736
|
+
By default the UI shows all exchange symbols. Override with a `config/symbol.config` (resolution: strategy dir โ project root โ package default):
|
|
1747
737
|
|
|
1748
|
-
|
|
1749
|
-
|
|
738
|
+
```ts
|
|
739
|
+
// config/symbol.config.ts
|
|
740
|
+
export const symbol_list = [
|
|
741
|
+
{ icon: "/icon/btc.png", logo: "/icon/128/btc.png", symbol: "BTCUSDT", displayName: "Bitcoin", color: "#F7931A", priority: 50, description: "Bitcoin โ the first and most popular cryptocurrency" },
|
|
742
|
+
{ icon: "/icon/eth.png", logo: "/icon/128/eth.png", symbol: "ETHUSDT", displayName: "Ethereum", color: "#6F42C1", priority: 50, description: "Ethereum โ a blockchain platform for smart contracts" },
|
|
743
|
+
];
|
|
1750
744
|
```
|
|
1751
745
|
|
|
1752
|
-
|
|
1753
|
-
|------------------------|-------------|---------------------------------------|
|
|
1754
|
-
| `CC_TELEGRAM_TOKEN` | โ | Telegram bot token (from @BotFather) |
|
|
1755
|
-
| `CC_TELEGRAM_CHANNEL` | โ | Telegram channel or chat ID |
|
|
1756
|
-
| `CC_WWWROOT_HOST` | `0.0.0.0` | UI server bind address |
|
|
1757
|
-
| `CC_WWWROOT_PORT` | `60050` | UI server port |
|
|
1758
|
-
| `CC_QUICKCHART_HOST` | โ | Self-hosted QuickChart instance URL |
|
|
746
|
+
</details>
|
|
1759
747
|
|
|
1760
|
-
|
|
748
|
+
<details>
|
|
749
|
+
<summary>notification.config โ which notification categories the UI shows</summary>
|
|
1761
750
|
|
|
1762
|
-
|
|
751
|
+
Defaults (override per strategy):
|
|
1763
752
|
|
|
1764
|
-
|
|
|
1765
|
-
|
|
1766
|
-
|
|
|
1767
|
-
|
|
|
1768
|
-
|
|
|
1769
|
-
|
|
|
753
|
+
| Key | Default | Description |
|
|
754
|
+
|-----|---------|-------------|
|
|
755
|
+
| `signal` | `true` | Signal lifecycle: opened, scheduled, closed, cancelled |
|
|
756
|
+
| `risk` | `true` | Risk manager rejections |
|
|
757
|
+
| `info` | `true` | Informational messages on an active signal |
|
|
758
|
+
| `breakeven` | `true` | Breakeven level reached |
|
|
759
|
+
| `common_error` | `true` | Non-fatal runtime errors |
|
|
760
|
+
| `critical_error` | `true` | Fatal errors that terminate the session |
|
|
761
|
+
| `validation_error` | `true` | Config / input validation errors |
|
|
762
|
+
| `strategy_commit` | `true` | All committed actions (partial close, DCA, trailing, โฆ) |
|
|
763
|
+
| `partial_loss` | `false` | Partial loss level reached (before commit) |
|
|
764
|
+
| `partial_profit` | `false` | Partial profit level reached (before commit) |
|
|
765
|
+
| `signal_sync` | `false` | Live order fill / exit confirmations from exchange sync |
|
|
1770
766
|
|
|
1771
|
-
|
|
767
|
+
```js
|
|
768
|
+
// config/notification.config.ts
|
|
769
|
+
export default { signal: true, risk: true, info: true, breakeven: true, common_error: true, critical_error: true, validation_error: true, strategy_commit: true, partial_loss: false, partial_profit: false, signal_sync: false };
|
|
770
|
+
```
|
|
1772
771
|
|
|
1773
|
-
|
|
772
|
+
</details>
|
|
1774
773
|
|
|
1775
|
-
|
|
774
|
+
### `telegram.config` โ programmatic message rendering
|
|
1776
775
|
|
|
1777
|
-
|
|
776
|
+
<details>
|
|
777
|
+
<summary>Override Mustache rendering with get*Markdown methods</summary>
|
|
1778
778
|
|
|
1779
|
-
|
|
1780
|
-
import { run } from '@backtest-kit/cli';
|
|
779
|
+
By default messages render from Mustache templates (`template/*.mustache`). Export an object with any subset of `get*Markdown` methods (each gets the event payload, returns `Promise<string>`); unimplemented ones fall back to the template.
|
|
1781
780
|
|
|
1782
|
-
|
|
781
|
+
```ts
|
|
782
|
+
// config/telegram.config.ts
|
|
783
|
+
import { IStrategyTickResultOpened, IStrategyTickResultClosed, RiskContract } from "backtest-kit";
|
|
784
|
+
export default {
|
|
785
|
+
async getOpenedMarkdown(e: IStrategyTickResultOpened) { return `**Opened** ${e.symbol} at ${e.priceOpen}`; },
|
|
786
|
+
async getClosedMarkdown(e: IStrategyTickResultClosed) { return `**Closed** ${e.symbol} at ${e.priceClosed}`; },
|
|
787
|
+
async getRiskMarkdown(e: RiskContract) { return `**Risk rejected** ${e.symbol}`; },
|
|
788
|
+
};
|
|
1783
789
|
```
|
|
1784
790
|
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
| `args` | Mode-specific options (all optional โ same defaults as CLI) |
|
|
791
|
+
Methods โ event types: `getOpenedMarkdown`/`getClosedMarkdown`/`getScheduledMarkdown`/`getCancelledMarkdown` (`IStrategyTickResult*`), `getRiskMarkdown` (`RiskContract`), `getPartialProfitMarkdown`/`getPartialLossMarkdown`/`getBreakevenMarkdown`/`getTrailingTakeMarkdown`/`getTrailingStopMarkdown`/`getAverageBuyMarkdown` (the matching `*Commit`), `getSignalOpenMarkdown`/`getSignalCloseMarkdown` (`SignalOpen/CloseContract`), `getCancelScheduledMarkdown`/`getClosePendingMarkdown` (`*Commit`), `getSignalInfoMarkdown` (`SignalInfoContract`).
|
|
792
|
+
|
|
793
|
+
</details>
|
|
1789
794
|
|
|
1790
|
-
|
|
795
|
+
---
|
|
1791
796
|
|
|
1792
|
-
|
|
797
|
+
## ๐ Integrations
|
|
798
|
+
|
|
799
|
+
### Web dashboard (`--ui`)
|
|
1793
800
|
|
|
1794
|
-
|
|
801
|
+
Starts the `@backtest-kit/ui` server at `http://localhost:60050` (host/port via `CC_WWWROOT_HOST` / `CC_WWWROOT_PORT`). Restrict the symbol list with `symbol.config` and notification categories with `notification.config` (above).
|
|
1795
802
|
|
|
1796
|
-
|
|
1797
|
-
|-------|------|-------------|
|
|
1798
|
-
| `entryPoint` | `string` | Path to strategy entry point file |
|
|
1799
|
-
| `symbol` | `string` | Trading pair (default: `"BTCUSDT"`) |
|
|
1800
|
-
| `strategy` | `string` | Strategy name (default: first registered) |
|
|
1801
|
-
| `exchange` | `string` | Exchange name (default: first registered) |
|
|
1802
|
-
| `frame` | `string` | Frame name (default: first registered) |
|
|
1803
|
-
| `cacheInterval` | `CandleInterval[]` | Intervals to pre-cache (default: `["1m","15m","30m","1h","4h"]`) |
|
|
1804
|
-
| `noCache` | `boolean` | Skip candle cache warming (default: `false`) |
|
|
1805
|
-
| `noFlush` | `boolean` | Skip removing report/log/markdown/agent folders before the run (default: `false`) |
|
|
1806
|
-
| `verbose` | `boolean` | Log each candle fetch (default: `false`) |
|
|
803
|
+
### Telegram (`--telegram`)
|
|
1807
804
|
|
|
1808
|
-
|
|
805
|
+
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 `CC_TELEGRAM_TOKEN` and `CC_TELEGRAM_CHANNEL`. Customize per-event rendering with `telegram.config` (above).
|
|
1809
806
|
|
|
1810
|
-
|
|
1811
|
-
|-------|------|-------------|
|
|
1812
|
-
| `entryPoint` | `string` | Path to strategy entry point file |
|
|
1813
|
-
| `symbol` | `string` | Trading pair (default: `"BTCUSDT"`) |
|
|
1814
|
-
| `strategy` | `string` | Strategy name (default: first registered) |
|
|
1815
|
-
| `exchange` | `string` | Exchange name (default: first registered) |
|
|
1816
|
-
| `verbose` | `boolean` | Log each candle fetch (default: `false`) |
|
|
807
|
+
---
|
|
1817
808
|
|
|
1818
|
-
|
|
809
|
+
## ๐งช Programmatic API โ `run(mode, args)`
|
|
1819
810
|
|
|
1820
|
-
|
|
811
|
+
Use the CLI as a library โ call `run()` from your own script, no child process or flag parsing.
|
|
1821
812
|
|
|
1822
813
|
```typescript
|
|
1823
814
|
import { run } from '@backtest-kit/cli';
|
|
1824
815
|
|
|
1825
|
-
await run('backtest', {
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
frame: 'feb-2024',
|
|
1829
|
-
cacheInterval: ['1m', '15m', '1h'],
|
|
1830
|
-
verbose: true,
|
|
1831
|
-
});
|
|
816
|
+
await run('backtest', { entryPoint: './src/index.mjs', symbol: 'ETHUSDT', frame: 'feb-2024', cacheInterval: ['1m','15m','1h'], verbose: true });
|
|
817
|
+
await run('paper', { entryPoint: './src/index.mjs', symbol: 'BTCUSDT' });
|
|
818
|
+
await run('live', { entryPoint: './src/index.mjs', symbol: 'BTCUSDT', verbose: true });
|
|
1832
819
|
```
|
|
1833
820
|
|
|
1834
|
-
|
|
821
|
+
<details>
|
|
822
|
+
<summary>Payload fields (call once per process)</summary>
|
|
1835
823
|
|
|
1836
|
-
|
|
1837
|
-
import { run } from '@backtest-kit/cli';
|
|
824
|
+
`run()` can be called **only once per process** โ a second call throws `"Should be called only once"`. `mode`: `"backtest" | "paper" | "live"`.
|
|
1838
825
|
|
|
1839
|
-
|
|
1840
|
-
entryPoint: './src/index.mjs',
|
|
1841
|
-
symbol: 'BTCUSDT',
|
|
1842
|
-
});
|
|
1843
|
-
```
|
|
826
|
+
**Backtest:** `entryPoint`, `symbol` (`"BTCUSDT"`), `strategy` (first registered), `exchange` (first registered), `frame` (first registered), `cacheInterval` (`["1m","15m","30m","1h","4h"]`), `noCache` (`false`), `noFlush` (`false`), `verbose` (`false`).
|
|
1844
827
|
|
|
1845
|
-
**Live
|
|
828
|
+
**Paper / Live:** `entryPoint`, `symbol` (`"BTCUSDT"`), `strategy` (first registered), `exchange` (first registered), `verbose` (`false`).
|
|
1846
829
|
|
|
1847
|
-
|
|
1848
|
-
import { run } from '@backtest-kit/cli';
|
|
830
|
+
</details>
|
|
1849
831
|
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
832
|
+
---
|
|
833
|
+
|
|
834
|
+
## ๐ Environment variables
|
|
835
|
+
|
|
836
|
+
```env
|
|
837
|
+
CC_TELEGRAM_TOKEN=your_bot_token_here # required for --telegram (from @BotFather)
|
|
838
|
+
CC_TELEGRAM_CHANNEL=-100123456789 # required for --telegram (channel/chat ID)
|
|
839
|
+
CC_WWWROOT_HOST=0.0.0.0 # UI bind address (default 0.0.0.0)
|
|
840
|
+
CC_WWWROOT_PORT=60050 # UI port (default 60050)
|
|
841
|
+
CC_QUICKCHART_HOST= # optional self-hosted QuickChart URL
|
|
1855
842
|
```
|
|
1856
843
|
|
|
1857
|
-
|
|
844
|
+
<details>
|
|
845
|
+
<summary>Default behaviors (when a schema isn't registered)</summary>
|
|
1858
846
|
|
|
1859
|
-
|
|
847
|
+
| Component | Default | Note |
|
|
848
|
+
|-----------|---------|------|
|
|
849
|
+
| **Exchange** | CCXT Binance (`default_exchange`) | warns; **does not support order book in backtest** โ register a custom exchange with snapshot storage if your strategy calls `getOrderBook()` in backtest |
|
|
850
|
+
| **Frame** | February 2024 (`default_frame`) | warns |
|
|
851
|
+
| **Symbol** | `BTCUSDT` | โ |
|
|
852
|
+
| **Cache intervals** | `1m, 15m, 30m, 4h` | used if `--cacheInterval` not given; skip with `--noCache` |
|
|
1860
853
|
|
|
1861
|
-
|
|
854
|
+
</details>
|
|
1862
855
|
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
import { serve } from '@backtest-kit/ui';
|
|
1867
|
-
|
|
1868
|
-
setLogger({ log: console.log, ... });
|
|
1869
|
-
Storage.enable();
|
|
1870
|
-
Notification.enable();
|
|
1871
|
-
Report.enable();
|
|
1872
|
-
Markdown.disable();
|
|
1873
|
-
|
|
1874
|
-
// ... parse CLI args manually
|
|
1875
|
-
// ... register exchange schema
|
|
1876
|
-
// ... warm candle cache
|
|
1877
|
-
// ... set up Telegram bot
|
|
1878
|
-
// ... handle SIGINT gracefully
|
|
1879
|
-
// ... load and run backtest
|
|
1880
|
-
```
|
|
856
|
+
---
|
|
857
|
+
|
|
858
|
+
## ๐ก Why @backtest-kit/cli
|
|
1881
859
|
|
|
1882
|
-
|
|
860
|
+
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:
|
|
1883
861
|
|
|
1884
862
|
```json
|
|
1885
863
|
{ "scripts": { "backtest": "npx @backtest-kit/cli --backtest --ui --telegram ./src/index.mjs" } }
|
|
1886
864
|
```
|
|
1887
865
|
|
|
1888
|
-
|
|
1889
|
-
npm run backtest
|
|
1890
|
-
```
|
|
1891
|
-
|
|
1892
|
-
**Benefits:**
|
|
1893
|
-
|
|
1894
|
-
- ๐ From zero to running backtest in seconds
|
|
1895
|
-
- ๐พ Automatic candle cache warming with retry logic
|
|
1896
|
-
- ๐ Production-ready web dashboard out of the box
|
|
1897
|
-
- ๐ฌ Telegram notifications with price charts โ no chart code needed
|
|
1898
|
-
- ๐ Graceful shutdown on SIGINT โ no hanging processes
|
|
1899
|
-
- ๐ Works with any `backtest-kit` strategy file as-is
|
|
1900
|
-
- ๐งฉ Broker adapter hooks via side-effect module files โ no CLI internals to touch
|
|
866
|
+
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 `setLogger()` from your strategy module ยท works with any `backtest-kit` strategy as-is ยท broker hooks via side-effect modules (no CLI internals to touch).
|
|
1901
867
|
|
|
1902
868
|
## ๐ค Contribute
|
|
1903
869
|
|
|
1904
|
-
Fork/PR on [GitHub](https://github.com/tripolskypetr/backtest-kit).
|
|
870
|
+
Fork / PR on [GitHub](https://github.com/tripolskypetr/backtest-kit).
|
|
1905
871
|
|
|
1906
872
|
## ๐ License
|
|
1907
873
|
|