tradelab 0.4.0 → 1.0.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 +121 -52
- package/bin/tradelab.js +340 -49
- package/dist/cjs/data.cjs +210 -155
- package/dist/cjs/index.cjs +1782 -274
- package/dist/cjs/live.cjs +3350 -0
- package/docs/README.md +26 -9
- package/docs/api-reference.md +89 -26
- package/docs/backtest-engine.md +74 -60
- package/docs/data-reporting-cli.md +66 -36
- package/docs/examples.md +275 -0
- package/docs/live-trading.md +186 -0
- package/examples/yahooEmaCross.js +1 -6
- package/package.json +18 -3
- package/src/data/csv.js +24 -14
- package/src/data/index.js +1 -5
- package/src/data/yahoo.js +6 -19
- package/src/engine/backtest.js +137 -144
- package/src/engine/backtestTicks.js +481 -0
- package/src/engine/barSystemRunner.js +1027 -0
- package/src/engine/execution.js +11 -39
- package/src/engine/portfolio.js +237 -66
- package/src/engine/walkForward.js +132 -13
- package/src/index.js +3 -11
- package/src/live/broker/alpaca.js +254 -0
- package/src/live/broker/binance.js +351 -0
- package/src/live/broker/coinbase.js +339 -0
- package/src/live/broker/interactiveBrokers.js +123 -0
- package/src/live/broker/interface.js +74 -0
- package/src/live/clock.js +56 -0
- package/src/live/engine/candleAggregator.js +154 -0
- package/src/live/engine/liveEngine.js +694 -0
- package/src/live/engine/paperEngine.js +453 -0
- package/src/live/engine/riskManager.js +185 -0
- package/src/live/engine/stateManager.js +112 -0
- package/src/live/events.js +48 -0
- package/src/live/feed/brokerFeed.js +35 -0
- package/src/live/feed/interface.js +28 -0
- package/src/live/feed/pollingFeed.js +105 -0
- package/src/live/index.js +27 -0
- package/src/live/logger.js +82 -0
- package/src/live/orchestrator.js +133 -0
- package/src/live/storage/interface.js +36 -0
- package/src/live/storage/jsonFileStorage.js +112 -0
- package/src/metrics/buildMetrics.js +103 -100
- package/src/reporting/exportBacktestArtifacts.js +1 -4
- package/src/reporting/exportTradesCsv.js +2 -7
- package/src/reporting/renderHtmlReport.js +8 -13
- package/src/utils/indicators.js +1 -2
- package/src/utils/positionSizing.js +16 -2
- package/src/utils/time.js +4 -12
- package/templates/report.html +23 -9
- package/templates/report.js +83 -69
- package/types/index.d.ts +98 -4
- package/types/live.d.ts +382 -0
package/README.md
CHANGED
|
@@ -3,19 +3,19 @@
|
|
|
3
3
|
|
|
4
4
|
<p><strong>A Node.js backtesting toolkit for serious trading strategy research.</strong></p>
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
[](https://www.npmjs.com/package/tradelab)
|
|
7
|
+
[](https://github.com/ishsharm0/tradelab)
|
|
8
|
+
[](https://github.com/ishsharm0/tradelab/blob/main/LICENSE)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](https://github.com/ishsharm0/tradelab/blob/main/types/index.d.ts)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
-
**tradelab** handles
|
|
16
|
+
**tradelab** handles strategy research and execution workflows in one package.
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Use it for backtests, portfolio and walk-forward validation, and live or paper execution through broker adapters while keeping the same `signal()` contract.
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
npm install tradelab
|
|
@@ -31,6 +31,8 @@ npm install tradelab
|
|
|
31
31
|
- [Core concepts](#core-concepts)
|
|
32
32
|
- [Portfolio mode](#portfolio-mode)
|
|
33
33
|
- [Walk-forward optimization](#walk-forward-optimization)
|
|
34
|
+
- [Tick backtests](#tick-backtests)
|
|
35
|
+
- [Live trading](#live-trading)
|
|
34
36
|
- [Execution and cost modeling](#execution-and-cost-modeling)
|
|
35
37
|
- [Exports and reporting](#exports-and-reporting)
|
|
36
38
|
- [CLI](#cli)
|
|
@@ -41,15 +43,16 @@ npm install tradelab
|
|
|
41
43
|
|
|
42
44
|
## What it includes
|
|
43
45
|
|
|
44
|
-
| Area
|
|
45
|
-
|
|
46
|
-
| **Engine**
|
|
47
|
-
| **Portfolio**
|
|
48
|
-
| **Walk-forward**
|
|
49
|
-
| **
|
|
50
|
-
| **
|
|
51
|
-
| **
|
|
52
|
-
| **
|
|
46
|
+
| Area | What you get |
|
|
47
|
+
| ------------------ | ---------------------------------------------------------------------------------------- |
|
|
48
|
+
| **Engine** | Candle and tick backtests with position sizing, exits, replay capture, and cost models |
|
|
49
|
+
| **Portfolio** | Multi-system shared-capital simulation with live capital locking and daily loss halts |
|
|
50
|
+
| **Walk-forward** | Rolling and anchored train/test validation with parameter search and stability summaries |
|
|
51
|
+
| **Live execution** | Live and paper engines with broker adapters, state persistence, and orchestration |
|
|
52
|
+
| **Data** | Yahoo Finance downloads, CSV import, and local cache helpers |
|
|
53
|
+
| **Costs** | Slippage, spread, and commission modeling |
|
|
54
|
+
| **Exports** | HTML reports, metrics JSON, and trade CSV |
|
|
55
|
+
| **Dev experience** | TypeScript definitions, ESM/CJS support, CLI for quick runs |
|
|
53
56
|
|
|
54
57
|
---
|
|
55
58
|
|
|
@@ -107,7 +110,7 @@ const candles = await getHistoricalCandles({
|
|
|
107
110
|
symbol: "SPY",
|
|
108
111
|
interval: "1d",
|
|
109
112
|
period: "2y",
|
|
110
|
-
cache: true,
|
|
113
|
+
cache: true, // reuses local copy on repeated runs
|
|
111
114
|
});
|
|
112
115
|
|
|
113
116
|
const result = backtest({ candles, symbol: "SPY", interval: "1d", range: "2y", signal });
|
|
@@ -160,16 +163,16 @@ The minimum viable signal is just `side`, `stop`, and `rr`. Start there and add
|
|
|
160
163
|
|
|
161
164
|
### Key backtest options
|
|
162
165
|
|
|
163
|
-
| Option
|
|
164
|
-
|
|
165
|
-
| `equity`
|
|
166
|
-
| `riskPct`
|
|
167
|
-
| `warmupBars`
|
|
168
|
-
| `flattenAtClose`
|
|
169
|
-
| `costs`
|
|
170
|
-
| `strict`
|
|
171
|
-
| `collectEqSeries` | Enables equity curve output
|
|
172
|
-
| `collectReplay`
|
|
166
|
+
| Option | Purpose |
|
|
167
|
+
| ----------------- | -------------------------------------------- |
|
|
168
|
+
| `equity` | Starting equity (default `10000`) |
|
|
169
|
+
| `riskPct` | Percent of equity risked per trade |
|
|
170
|
+
| `warmupBars` | Bars skipped before signal evaluation starts |
|
|
171
|
+
| `flattenAtClose` | Forces end-of-day exit when enabled |
|
|
172
|
+
| `costs` | Slippage, spread, and commission model |
|
|
173
|
+
| `strict` | Throws on lookahead access |
|
|
174
|
+
| `collectEqSeries` | Enables equity curve output |
|
|
175
|
+
| `collectReplay` | Enables visualization payload |
|
|
173
176
|
|
|
174
177
|
### Result shape
|
|
175
178
|
|
|
@@ -177,19 +180,19 @@ The minimum viable signal is just `side`, `stop`, and `rr`. Start there and add
|
|
|
177
180
|
{
|
|
178
181
|
symbol, interval, range,
|
|
179
182
|
trades, // every realized leg, including partial exits
|
|
180
|
-
positions, // completed positions
|
|
183
|
+
positions, // completed positions - start here for analysis
|
|
181
184
|
metrics, // winRate, profitFactor, maxDrawdown, sharpe, ...
|
|
182
|
-
eqSeries, // [{ time, timestamp, equity }]
|
|
185
|
+
eqSeries, // [{ time, timestamp, equity }] - equity curve
|
|
183
186
|
replay, // visualization frames and events
|
|
184
187
|
}
|
|
185
188
|
```
|
|
186
189
|
|
|
187
190
|
**First checks after any run:**
|
|
188
191
|
|
|
189
|
-
- `metrics.trades`
|
|
190
|
-
- `metrics.profitFactor`
|
|
191
|
-
- `metrics.maxDrawdown`
|
|
192
|
-
- `metrics.sideBreakdown`
|
|
192
|
+
- `metrics.trades` - enough sample size to trust the numbers?
|
|
193
|
+
- `metrics.profitFactor` - do winners beat losers gross of costs?
|
|
194
|
+
- `metrics.maxDrawdown` - is the equity path survivable?
|
|
195
|
+
- `metrics.sideBreakdown` - does one side carry the whole result?
|
|
193
196
|
|
|
194
197
|
---
|
|
195
198
|
|
|
@@ -209,25 +212,26 @@ const result = backtestPortfolio({
|
|
|
209
212
|
});
|
|
210
213
|
```
|
|
211
214
|
|
|
212
|
-
|
|
215
|
+
Weights now act as default per-system allocation caps rather than pre-funded sleeves. Capital is locked only when a fill happens, `eqSeries` includes `lockedCapital` and `availableCapital`, later systems size against remaining live capital, and `maxDailyLossPct` on `backtestPortfolio()` can halt the whole book for the rest of the day.
|
|
213
216
|
|
|
214
217
|
---
|
|
215
218
|
|
|
216
219
|
## Walk-forward optimization
|
|
217
220
|
|
|
218
|
-
Use `walkForwardOptimize()` when one in-sample backtest is not enough. It
|
|
221
|
+
Use `walkForwardOptimize()` when one in-sample backtest is not enough. It supports rolling and anchored train/test windows across the full candle history.
|
|
219
222
|
|
|
220
223
|
```js
|
|
221
224
|
import { walkForwardOptimize } from "tradelab";
|
|
222
225
|
|
|
223
226
|
const wf = walkForwardOptimize({
|
|
224
227
|
candles,
|
|
228
|
+
mode: "anchored",
|
|
225
229
|
trainBars: 180,
|
|
226
230
|
testBars: 60,
|
|
227
231
|
stepBars: 60,
|
|
228
232
|
scoreBy: "profitFactor",
|
|
229
233
|
parameterSets: [
|
|
230
|
-
{ fast: 8,
|
|
234
|
+
{ fast: 8, slow: 21, rr: 2 },
|
|
231
235
|
{ fast: 10, slow: 30, rr: 2 },
|
|
232
236
|
],
|
|
233
237
|
signalFactory(params) {
|
|
@@ -236,7 +240,59 @@ const wf = walkForwardOptimize({
|
|
|
236
240
|
});
|
|
237
241
|
```
|
|
238
242
|
|
|
239
|
-
Each window picks the best parameter set in training, then runs it blind on the test slice. The `windows` array
|
|
243
|
+
Each window picks the best parameter set in training, then runs it blind on the test slice. The `windows` array now includes out-of-sample trade count, profitability, and a per-window stability score. `bestParamsSummary` reports how stable the winners were across the full run.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Tick backtests
|
|
248
|
+
|
|
249
|
+
Use `backtestTicks()` when you want event-driven fills on tick or quote data without changing the result shape used by metrics, exports, or replay.
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
import { backtestTicks } from "tradelab";
|
|
253
|
+
|
|
254
|
+
const result = backtestTicks({
|
|
255
|
+
ticks,
|
|
256
|
+
queueFillProbability: 0.35,
|
|
257
|
+
signal,
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Market entries fill on the next tick, limit orders can fill at the touch with configurable queue probability, and stop exits use the existing cost model with stop-specific slippage if you provide it in `costs.slippageByKind.stop`.
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Live trading
|
|
266
|
+
|
|
267
|
+
`tradelab/live` provides the live stack:
|
|
268
|
+
|
|
269
|
+
- `LiveEngine` for single-system live/paper execution
|
|
270
|
+
- `LiveOrchestrator` for multi-system execution with shared broker state
|
|
271
|
+
- `PaperEngine` implementing the broker interface for deterministic simulation
|
|
272
|
+
- broker adapters for Alpaca, Binance, Coinbase, and Interactive Brokers
|
|
273
|
+
- JSON state/trade/equity persistence via `JsonFileStorage`
|
|
274
|
+
|
|
275
|
+
Use the same signal contract from backtesting in live mode:
|
|
276
|
+
|
|
277
|
+
```js
|
|
278
|
+
import { LiveEngine, PaperEngine, JsonFileStorage } from "tradelab/live";
|
|
279
|
+
|
|
280
|
+
const engine = new LiveEngine({
|
|
281
|
+
id: "aapl-1m",
|
|
282
|
+
symbol: "AAPL",
|
|
283
|
+
interval: "1m",
|
|
284
|
+
broker: new PaperEngine({ equity: 25_000 }),
|
|
285
|
+
storage: new JsonFileStorage({ baseDir: "./output/live-state" }),
|
|
286
|
+
signal({ bar, openPosition }) {
|
|
287
|
+
if (openPosition) return null;
|
|
288
|
+
return { side: "long", stop: bar.close - 1, rr: 2 };
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
await engine.start();
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
See [docs/live-trading.md](docs/live-trading.md) for API and CLI workflows.
|
|
240
296
|
|
|
241
297
|
---
|
|
242
298
|
|
|
@@ -282,12 +338,12 @@ exportBacktestArtifacts({ result, outDir: "./output" });
|
|
|
282
338
|
|
|
283
339
|
Or use the narrower helpers:
|
|
284
340
|
|
|
285
|
-
| Helper
|
|
286
|
-
|
|
287
|
-
| `exportHtmlReport(options)`
|
|
288
|
-
| `renderHtmlReport(options)`
|
|
289
|
-
| `exportTradesCsv(trades, options)` | Flat trade ledger for spreadsheets or pandas
|
|
290
|
-
| `exportMetricsJSON(options)`
|
|
341
|
+
| Helper | Output |
|
|
342
|
+
| ---------------------------------- | ----------------------------------------------------- |
|
|
343
|
+
| `exportHtmlReport(options)` | Interactive HTML report written to disk |
|
|
344
|
+
| `renderHtmlReport(options)` | HTML report returned as a string |
|
|
345
|
+
| `exportTradesCsv(trades, options)` | Flat trade ledger for spreadsheets or pandas |
|
|
346
|
+
| `exportMetricsJSON(options)` | Machine-readable metrics for dashboards or automation |
|
|
291
347
|
|
|
292
348
|
For programmatic pipelines, `exportMetricsJSON` is usually the most useful format to build on.
|
|
293
349
|
|
|
@@ -313,7 +369,16 @@ npx tradelab portfolio \
|
|
|
313
369
|
# Walk-forward validation
|
|
314
370
|
npx tradelab walk-forward \
|
|
315
371
|
--source yahoo --symbol QQQ --interval 1d --period 2y \
|
|
316
|
-
--trainBars 180 --testBars 60
|
|
372
|
+
--trainBars 180 --testBars 60 --mode anchored
|
|
373
|
+
|
|
374
|
+
# Live paper engine (single system)
|
|
375
|
+
npx tradelab paper --symbol AAPL --interval 1m --mode polling --once true
|
|
376
|
+
|
|
377
|
+
# Live orchestrator from config
|
|
378
|
+
npx tradelab live --config ./live-portfolio.json --paper --mode polling --once true
|
|
379
|
+
|
|
380
|
+
# Inspect persisted live state
|
|
381
|
+
npx tradelab status --dir ./output/live-state
|
|
317
382
|
|
|
318
383
|
# Prefetch and cache data
|
|
319
384
|
npx tradelab prefetch --symbol SPY --interval 1d --period 1y
|
|
@@ -322,7 +387,7 @@ npx tradelab import-csv --csvPath ./data/spy.csv --symbol SPY --interval 1d
|
|
|
322
387
|
|
|
323
388
|
**Built-in strategies:** `ema-cross` · `buy-hold`
|
|
324
389
|
|
|
325
|
-
You can also point `--strategy` at a local module that exports `default(args)`, `createSignal(args)`, or `signal`.
|
|
390
|
+
You can also point `--strategy` at a local module that exports `default(args)`, `createSignal(args)`, or `signal` for `backtest`, or `signalFactory(params, args)` plus `parameterSets`/`createParameterSets(args)` for `walk-forward`.
|
|
326
391
|
|
|
327
392
|
---
|
|
328
393
|
|
|
@@ -344,6 +409,7 @@ The examples are a good place to start if you want something runnable before wir
|
|
|
344
409
|
```js
|
|
345
410
|
import { backtest, getHistoricalCandles, ema } from "tradelab";
|
|
346
411
|
import { fetchHistorical } from "tradelab/data";
|
|
412
|
+
import { LiveEngine, PaperEngine } from "tradelab/live";
|
|
347
413
|
```
|
|
348
414
|
|
|
349
415
|
### CommonJS
|
|
@@ -351,24 +417,27 @@ import { fetchHistorical } from "tradelab/data";
|
|
|
351
417
|
```js
|
|
352
418
|
const { backtest, getHistoricalCandles, ema } = require("tradelab");
|
|
353
419
|
const { fetchHistorical } = require("tradelab/data");
|
|
420
|
+
const { LiveEngine, PaperEngine } = require("tradelab/live");
|
|
354
421
|
```
|
|
355
422
|
|
|
356
423
|
---
|
|
357
424
|
|
|
358
425
|
## Documentation
|
|
359
426
|
|
|
360
|
-
| Guide
|
|
361
|
-
|
|
362
|
-
| [Backtest engine](docs/backtest-engine.md)
|
|
363
|
-
| [Data, reporting, and CLI](docs/data-reporting-cli.md) | Data loading, cache behavior, exports, CLI reference
|
|
364
|
-
| [
|
|
427
|
+
| Guide | What it covers |
|
|
428
|
+
| ------------------------------------------------------ | ------------------------------------------------------------------------------ |
|
|
429
|
+
| [Backtest engine](docs/backtest-engine.md) | Signal contract, all options, result shape, portfolio mode, walk-forward |
|
|
430
|
+
| [Data, reporting, and CLI](docs/data-reporting-cli.md) | Data loading, cache behavior, exports, CLI reference |
|
|
431
|
+
| [Live trading](docs/live-trading.md) | Live engine, broker adapters, paper mode, orchestration, and state persistence |
|
|
432
|
+
| [Strategy examples](docs/examples.md) | Mean reversion, breakout, sentiment, LLM, and portfolio strategy patterns |
|
|
433
|
+
| [API reference](docs/api-reference.md) | Compact index of every public export |
|
|
365
434
|
|
|
366
435
|
---
|
|
367
436
|
|
|
368
437
|
## Common mistakes
|
|
369
438
|
|
|
370
439
|
- Using unsorted candles or mixed intervals in a single series
|
|
371
|
-
- Reading `trades` as if they were always full positions
|
|
440
|
+
- Reading `trades` as if they were always full positions - use `positions` for top-line analysis
|
|
372
441
|
- Leaving costs at zero and overestimating edge
|
|
373
442
|
- Trusting one backtest without out-of-sample validation
|
|
374
443
|
- Debugging a strategy with `strict: false` when lookahead is possible
|
|
@@ -380,4 +449,4 @@ const { fetchHistorical } = require("tradelab/data");
|
|
|
380
449
|
- Node `18+` is required
|
|
381
450
|
- Yahoo downloads are cached under `output/data` by default
|
|
382
451
|
- CommonJS and ESM are both supported
|
|
383
|
-
-
|
|
452
|
+
- Live adapters support broker execution workflows, but this is still not an exchange microstructure simulator
|