tradelab 1.1.0 → 1.2.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/CHANGELOG.md +46 -0
- package/README.md +185 -388
- package/dist/cjs/index.cjs +31 -9
- package/dist/cjs/live.cjs +409 -7
- package/docs/README.md +32 -66
- package/docs/api-reference.md +269 -144
- package/docs/backtest-engine.md +167 -321
- package/docs/data-reporting-cli.md +114 -156
- package/docs/examples.md +6 -6
- package/docs/live-trading.md +254 -134
- package/docs/mcp.md +244 -23
- package/docs/research.md +99 -45
- package/examples/mcpLiveTrading.js +77 -0
- package/package.json +11 -3
- package/src/engine/optimize.js +25 -1
- package/src/engine/portfolio.js +4 -1
- package/src/live/dashboard/server.js +67 -8
- package/src/live/engine/paperEngine.js +5 -0
- package/src/live/index.js +2 -0
- package/src/live/session.js +402 -0
- package/src/mcp/liveTools.js +179 -0
- package/src/mcp/schemas.js +119 -0
- package/src/mcp/server.js +5 -1
- package/src/mcp/tools.js +125 -2
- package/templates/dashboard.html +595 -108
- package/types/index.d.ts +25 -0
- package/types/live.d.ts +99 -0
- package/types/mcp.d.ts +17 -0
- package/docs/superpowers/plans/2026-00-overview.md +0 -101
- package/docs/superpowers/plans/2026-01-metrics-correctness.md +0 -873
- package/docs/superpowers/plans/2026-02-indicator-library.md +0 -677
- package/docs/superpowers/plans/2026-03-overfitting-toolkit.md +0 -882
- package/docs/superpowers/plans/2026-04-async-signals-seeding.md +0 -981
- package/docs/superpowers/plans/2026-05-mcp-server.md +0 -758
- package/docs/superpowers/plans/2026-06-parallel-param-sweep.md +0 -508
- package/docs/superpowers/plans/2026-07-funding-carry-costs.md +0 -535
- package/docs/superpowers/plans/2026-08-live-dashboard.md +0 -547
- package/docs/superpowers/plans/HANDOFF.md +0 -88
|
@@ -1,35 +1,16 @@
|
|
|
1
|
-
# Data,
|
|
1
|
+
# Data, Reporting, and CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This guide covers data loading, local caches, report exports, and the `tradelab` command.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[Back to docs](README.md)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- local cache helpers
|
|
9
|
-
- export helpers
|
|
10
|
-
- command-line usage for backtest and live workflows
|
|
7
|
+
## Data Loading
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
If you are not bringing your own candles yet, start here.
|
|
15
|
-
|
|
16
|
-
## Choose the right entry point
|
|
17
|
-
|
|
18
|
-
| Use case | Function |
|
|
19
|
-
| --------------------------------------------------------- | ------------------------ |
|
|
20
|
-
| Load data without caring about the source-specific helper | `getHistoricalCandles()` |
|
|
21
|
-
| Fetch directly from Yahoo | `fetchHistorical()` |
|
|
22
|
-
| Load a local CSV file | `loadCandlesFromCSV()` |
|
|
23
|
-
| Reuse saved normalized data | `loadCandlesFromCache()` |
|
|
24
|
-
| Try the package from a terminal first | `tradelab` CLI |
|
|
25
|
-
|
|
26
|
-
## Historical data
|
|
27
|
-
|
|
28
|
-
### `getHistoricalCandles(options)`
|
|
29
|
-
|
|
30
|
-
This is the main data-loading entry point.
|
|
9
|
+
Most workflows should start with `getHistoricalCandles()`.
|
|
31
10
|
|
|
32
11
|
```js
|
|
12
|
+
import { getHistoricalCandles } from "tradelab";
|
|
13
|
+
|
|
33
14
|
const candles = await getHistoricalCandles({
|
|
34
15
|
source: "yahoo",
|
|
35
16
|
symbol: "SPY",
|
|
@@ -39,159 +20,139 @@ const candles = await getHistoricalCandles({
|
|
|
39
20
|
});
|
|
40
21
|
```
|
|
41
22
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
- `yahoo`
|
|
45
|
-
- `csv`
|
|
46
|
-
- `auto`
|
|
47
|
-
|
|
48
|
-
`auto` switches to CSV when `csvPath` or `csv.filePath` is present. Otherwise it uses Yahoo.
|
|
23
|
+
It returns normalized candles:
|
|
49
24
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
| ---------------- | ----------------------------------------------------- |
|
|
56
|
-
| `symbol` | Ticker or Yahoo symbol |
|
|
57
|
-
| `interval` | Candle interval such as `1d` or `5m` |
|
|
58
|
-
| `period` | Lookback period such as `6mo` or `1y` |
|
|
59
|
-
| `includePrePost` | Includes premarket and postmarket data when supported |
|
|
60
|
-
| `cache` | Reuses saved normalized data |
|
|
61
|
-
| `refresh` | Forces a fresh download even if cache exists |
|
|
62
|
-
| `cacheDir` | Overrides the default cache directory |
|
|
25
|
+
```js
|
|
26
|
+
{
|
|
27
|
+
(time, open, high, low, close, volume);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
63
30
|
|
|
64
|
-
|
|
31
|
+
## Sources
|
|
65
32
|
|
|
66
|
-
|
|
33
|
+
| Source | Use it when... |
|
|
34
|
+
| ------- | --------------------------------------------------- |
|
|
35
|
+
| `yahoo` | You want quick market data by symbol |
|
|
36
|
+
| `csv` | You already have a file on disk |
|
|
37
|
+
| `auto` | You want CSV when `csvPath` exists, otherwise Yahoo |
|
|
67
38
|
|
|
68
|
-
###
|
|
39
|
+
### Yahoo
|
|
69
40
|
|
|
70
41
|
```js
|
|
71
42
|
const candles = await getHistoricalCandles({
|
|
72
|
-
source: "
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
lowCol: "low",
|
|
79
|
-
closeCol: "close",
|
|
80
|
-
volumeCol: "volume",
|
|
81
|
-
},
|
|
43
|
+
source: "yahoo",
|
|
44
|
+
symbol: "QQQ",
|
|
45
|
+
interval: "1d",
|
|
46
|
+
period: "1y",
|
|
47
|
+
cache: true,
|
|
48
|
+
refresh: false,
|
|
82
49
|
});
|
|
83
50
|
```
|
|
84
51
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- delimiter
|
|
88
|
-
- header presence
|
|
89
|
-
- column names or indexes
|
|
90
|
-
- start/end date filters
|
|
91
|
-
- custom date parsing
|
|
92
|
-
|
|
93
|
-
If your CSV already uses common OHLCV column names, you often do not need to pass any mapping at all.
|
|
52
|
+
Common options:
|
|
94
53
|
|
|
95
|
-
|
|
54
|
+
| Option | Meaning |
|
|
55
|
+
| ---------------- | --------------------------------------------------------------- |
|
|
56
|
+
| `symbol` | Yahoo symbol |
|
|
57
|
+
| `interval` | `1m`, `5m`, `1d`, `1wk`, and other Yahoo intervals |
|
|
58
|
+
| `period` | `5d`, `60d`, `6mo`, `1y`, `2y`, and similar |
|
|
59
|
+
| `includePrePost` | Include premarket and postmarket candles if Yahoo provides them |
|
|
60
|
+
| `cache` | Reuse a saved normalized file |
|
|
61
|
+
| `refresh` | Download again even if a cache file exists |
|
|
62
|
+
| `cacheDir` | Change where cache files are stored |
|
|
96
63
|
|
|
97
|
-
|
|
64
|
+
The Yahoo helper retries transient failures. If Yahoo is unavailable, use a cached run or switch to CSV for repeatable tests.
|
|
98
65
|
|
|
99
|
-
|
|
100
|
-
- `loadCandlesFromCache(symbol, interval, period, outDir)`
|
|
101
|
-
- `cachedCandlesPath(symbol, interval, period, outDir)`
|
|
102
|
-
|
|
103
|
-
The cache is just normalized candle JSON on disk. It is meant for research convenience, not as a durable database layer.
|
|
104
|
-
|
|
105
|
-
## Common workflows
|
|
106
|
-
|
|
107
|
-
### Yahoo to backtest
|
|
66
|
+
### CSV
|
|
108
67
|
|
|
109
68
|
```js
|
|
110
69
|
const candles = await getHistoricalCandles({
|
|
111
|
-
source: "
|
|
112
|
-
|
|
113
|
-
interval: "1d",
|
|
114
|
-
period: "1y",
|
|
115
|
-
cache: true,
|
|
70
|
+
source: "csv",
|
|
71
|
+
csvPath: "./data/spy.csv",
|
|
116
72
|
});
|
|
117
73
|
```
|
|
118
74
|
|
|
119
|
-
|
|
75
|
+
If your headers use common OHLCV names, no mapping is needed. For custom files, pass column names or indexes:
|
|
120
76
|
|
|
121
77
|
```js
|
|
122
78
|
const candles = await getHistoricalCandles({
|
|
123
79
|
source: "csv",
|
|
124
80
|
csvPath: "./data/spy.csv",
|
|
81
|
+
csv: {
|
|
82
|
+
timeCol: "timestamp",
|
|
83
|
+
openCol: "open_price",
|
|
84
|
+
highCol: "high_price",
|
|
85
|
+
lowCol: "low_price",
|
|
86
|
+
closeCol: "close_price",
|
|
87
|
+
volumeCol: "volume",
|
|
88
|
+
delimiter: ",",
|
|
89
|
+
},
|
|
125
90
|
});
|
|
126
91
|
```
|
|
127
92
|
|
|
128
|
-
|
|
93
|
+
## Cache Helpers
|
|
94
|
+
|
|
95
|
+
The cache is normalized candle JSON on disk. It is useful for repeatable research runs and CI fixtures. It is not a database.
|
|
129
96
|
|
|
130
97
|
```js
|
|
131
|
-
|
|
132
|
-
|
|
98
|
+
import { saveCandlesToCache, loadCandlesFromCache, cachedCandlesPath } from "tradelab";
|
|
99
|
+
|
|
100
|
+
const path = saveCandlesToCache(candles, {
|
|
133
101
|
symbol: "SPY",
|
|
134
102
|
interval: "1d",
|
|
135
103
|
period: "1y",
|
|
136
|
-
cache: true,
|
|
137
|
-
refresh: false,
|
|
138
104
|
});
|
|
139
|
-
```
|
|
140
105
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
### `exportBacktestArtifacts({ result, outDir })`
|
|
144
|
-
|
|
145
|
-
The main bundle export. By default it writes:
|
|
106
|
+
const cached = loadCandlesFromCache("SPY", "1d", "1y");
|
|
107
|
+
```
|
|
146
108
|
|
|
147
|
-
|
|
148
|
-
- trade CSV
|
|
149
|
-
- metrics JSON
|
|
109
|
+
## Reporting
|
|
150
110
|
|
|
151
|
-
|
|
111
|
+
### Write All Artifacts
|
|
152
112
|
|
|
153
|
-
<!-- prettier-ignore -->
|
|
154
113
|
```js
|
|
155
|
-
{
|
|
156
|
-
```
|
|
114
|
+
import { exportBacktestArtifacts } from "tradelab";
|
|
157
115
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
Use this for dashboards, notebooks, or any machine-readable downstream pipeline.
|
|
116
|
+
const files = exportBacktestArtifacts({
|
|
117
|
+
result,
|
|
118
|
+
outDir: "./output",
|
|
119
|
+
});
|
|
163
120
|
|
|
164
|
-
|
|
121
|
+
console.log(files);
|
|
122
|
+
```
|
|
165
123
|
|
|
166
|
-
|
|
124
|
+
Return shape:
|
|
167
125
|
|
|
168
|
-
|
|
126
|
+
```js
|
|
127
|
+
{
|
|
128
|
+
(html, csv, metrics);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
169
131
|
|
|
170
|
-
###
|
|
132
|
+
### Export Only What You Need
|
|
171
133
|
|
|
172
|
-
|
|
173
|
-
|
|
134
|
+
| Helper | Output |
|
|
135
|
+
| ---------------------------------- | ----------------- |
|
|
136
|
+
| `exportMetricsJSON(options)` | Metrics JSON |
|
|
137
|
+
| `exportTradesCsv(trades, options)` | Flat trade ledger |
|
|
138
|
+
| `renderHtmlReport(options)` | HTML string |
|
|
139
|
+
| `exportHtmlReport(options)` | HTML file path |
|
|
174
140
|
|
|
175
|
-
|
|
141
|
+
Use metrics JSON for notebooks, dashboards, or downstream jobs. Use trade CSV for spreadsheet review. Use HTML when a human needs to inspect the run.
|
|
176
142
|
|
|
177
143
|
## CLI
|
|
178
144
|
|
|
179
|
-
The package
|
|
145
|
+
The package installs two binaries:
|
|
180
146
|
|
|
181
|
-
|
|
147
|
+
- `tradelab`
|
|
148
|
+
- `tradelab-mcp`
|
|
182
149
|
|
|
183
|
-
|
|
150
|
+
Use `tradelab` when you want a quick command-line run before writing application code.
|
|
184
151
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
| `tradelab walk-forward` | Run rolling or anchored validation with built-in or local strategy search |
|
|
190
|
-
| `tradelab live` | Run live engine or orchestrator mode |
|
|
191
|
-
| `tradelab paper` | Run live engine in paper broker mode |
|
|
192
|
-
| `tradelab status` | Inspect persisted live namespace state |
|
|
193
|
-
| `tradelab prefetch` | Download and cache Yahoo data |
|
|
194
|
-
| `tradelab import-csv` | Normalize and cache a CSV file |
|
|
152
|
+
```bash
|
|
153
|
+
tradelab --version
|
|
154
|
+
tradelab help
|
|
155
|
+
```
|
|
195
156
|
|
|
196
157
|
### Backtest
|
|
197
158
|
|
|
@@ -200,19 +161,17 @@ tradelab backtest --source yahoo --symbol SPY --interval 1d --period 1y
|
|
|
200
161
|
tradelab backtest --source csv --csvPath ./data/btc.csv --strategy buy-hold --holdBars 3
|
|
201
162
|
```
|
|
202
163
|
|
|
203
|
-
Built-in strategies:
|
|
164
|
+
Built-in CLI strategies:
|
|
204
165
|
|
|
205
166
|
- `ema-cross`
|
|
206
167
|
- `buy-hold`
|
|
207
168
|
|
|
208
|
-
|
|
169
|
+
Local strategy modules can export one of:
|
|
209
170
|
|
|
210
171
|
- `default(args)`
|
|
211
172
|
- `createSignal(args)`
|
|
212
173
|
- `signal`
|
|
213
174
|
|
|
214
|
-
That makes it easy to prototype a strategy file before wiring it into a larger application.
|
|
215
|
-
|
|
216
175
|
### Portfolio
|
|
217
176
|
|
|
218
177
|
```bash
|
|
@@ -222,9 +181,9 @@ tradelab portfolio \
|
|
|
222
181
|
--strategy buy-hold
|
|
223
182
|
```
|
|
224
183
|
|
|
225
|
-
|
|
184
|
+
The CLI portfolio command is intentionally compact. Use the JavaScript API when you need per-system options or custom signal wiring.
|
|
226
185
|
|
|
227
|
-
### Walk-
|
|
186
|
+
### Walk-Forward
|
|
228
187
|
|
|
229
188
|
```bash
|
|
230
189
|
tradelab walk-forward \
|
|
@@ -237,9 +196,9 @@ tradelab walk-forward \
|
|
|
237
196
|
--mode anchored
|
|
238
197
|
```
|
|
239
198
|
|
|
240
|
-
|
|
199
|
+
You can pass `--strategy ./strategy.mjs` for local modules that export `signalFactory(params, args)` and either `parameterSets` or `createParameterSets(args)`.
|
|
241
200
|
|
|
242
|
-
### Live and
|
|
201
|
+
### Live and Paper
|
|
243
202
|
|
|
244
203
|
```bash
|
|
245
204
|
tradelab paper --symbol AAPL --interval 1m --mode polling --once true
|
|
@@ -249,36 +208,35 @@ tradelab live \
|
|
|
249
208
|
--symbol AAPL \
|
|
250
209
|
--interval 1m \
|
|
251
210
|
--broker alpaca \
|
|
252
|
-
--apiKey $APCA_KEY \
|
|
253
|
-
--apiSecret $APCA_SECRET
|
|
254
|
-
|
|
255
|
-
tradelab live --config ./live-portfolio.json --paper --mode polling --once true
|
|
211
|
+
--apiKey "$APCA_KEY" \
|
|
212
|
+
--apiSecret "$APCA_SECRET"
|
|
256
213
|
```
|
|
257
214
|
|
|
258
|
-
|
|
215
|
+
Use a config file for multi-system live runs:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
tradelab live --config ./live-portfolio.json --paper --mode polling
|
|
219
|
+
```
|
|
259
220
|
|
|
260
|
-
|
|
221
|
+
Add a dashboard:
|
|
261
222
|
|
|
262
223
|
```bash
|
|
263
|
-
tradelab
|
|
264
|
-
tradelab status --dir ./output/live-state --namespace my-system
|
|
224
|
+
tradelab paper --symbol AAPL --interval 1m --dashboard --dashboardPort 4317
|
|
265
225
|
```
|
|
266
226
|
|
|
267
|
-
###
|
|
227
|
+
### State
|
|
268
228
|
|
|
269
229
|
```bash
|
|
270
|
-
tradelab
|
|
271
|
-
tradelab
|
|
230
|
+
tradelab status --dir ./output/live-state
|
|
231
|
+
tradelab status --dir ./output/live-state --namespace aapl-1m
|
|
272
232
|
```
|
|
273
233
|
|
|
274
|
-
|
|
234
|
+
State commands read persisted JSON from `JsonFileStorage`; they do not connect to a broker.
|
|
235
|
+
|
|
236
|
+
## MCP Binary
|
|
237
|
+
|
|
238
|
+
`tradelab-mcp` starts the stdio MCP server. Most users run it through an MCP client config rather than typing it directly.
|
|
275
239
|
|
|
276
|
-
|
|
277
|
-
| ---------------------- | ---------------------------------------------------------------------- |
|
|
278
|
-
| Yahoo request errors | enable cache, retry later, or fall back to CSV |
|
|
279
|
-
| Unexpected trade count | `warmupBars`, `flattenAtClose`, and signal frequency |
|
|
280
|
-
| Empty result | candle order, signal logic, and stop/target validity |
|
|
281
|
-
| Confusing CSV import | inspect normalized bars from `loadCandlesFromCSV()` before backtesting |
|
|
282
|
-
| Export confusion | use metrics JSON first if you need programmatic output |
|
|
240
|
+
See [MCP server](mcp.md).
|
|
283
241
|
|
|
284
|
-
|
|
242
|
+
[Back to docs](README.md)
|
package/docs/examples.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Strategy examples
|
|
2
2
|
|
|
3
|
-
<small>[Back to
|
|
3
|
+
<small>[Back to docs](README.md)</small>
|
|
4
4
|
|
|
5
5
|
These are research templates. They show how to wire different kinds of data and execution assumptions into the engine without changing the output pipeline.
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ The five examples cover:
|
|
|
9
9
|
- single-symbol price research
|
|
10
10
|
- tick-level fills
|
|
11
11
|
- external feature overlays
|
|
12
|
-
-
|
|
12
|
+
- precomputed regime filters with walk-forward validation
|
|
13
13
|
- portfolio research with shared capital
|
|
14
14
|
|
|
15
15
|
---
|
|
@@ -157,7 +157,7 @@ The same pattern works for any precomputed field - regime labels, macro scores,
|
|
|
157
157
|
|
|
158
158
|
## 4. Precomputed regime filter with anchored walk-forward
|
|
159
159
|
|
|
160
|
-
|
|
160
|
+
Regime labels work best as precomputed fields, not as slow callers inside the signal function. Build the label once per bar outside the engine, store the result on the candle, then run walk-forward validation on top of it.
|
|
161
161
|
|
|
162
162
|
```js
|
|
163
163
|
import { walkForwardOptimize, getHistoricalCandles, ema } from "tradelab";
|
|
@@ -169,7 +169,7 @@ const candles = await getHistoricalCandles({
|
|
|
169
169
|
period: "3y",
|
|
170
170
|
});
|
|
171
171
|
|
|
172
|
-
//
|
|
172
|
+
// build labels outside the engine - keep signal() synchronous
|
|
173
173
|
const labeled = await Promise.all(
|
|
174
174
|
candles.map(async (bar, index) => ({
|
|
175
175
|
...bar,
|
|
@@ -220,7 +220,7 @@ const wf = walkForwardOptimize({
|
|
|
220
220
|
});
|
|
221
221
|
```
|
|
222
222
|
|
|
223
|
-
Check `wf.bestParamsSummary` for parameter stability across windows. If the winning regime or EMA pair changes every window, the
|
|
223
|
+
Check `wf.bestParamsSummary` for parameter stability across windows. If the winning regime or EMA pair changes every window, the label probably is not adding durable signal.
|
|
224
224
|
|
|
225
225
|
---
|
|
226
226
|
|
|
@@ -272,4 +272,4 @@ const result = backtestPortfolio({
|
|
|
272
272
|
|
|
273
273
|
`result.eqSeries` includes `lockedCapital` and `availableCapital` at each realized equity point. Use those to see how often the portfolio was fully deployed versus sitting partially idle.
|
|
274
274
|
|
|
275
|
-
<small>[Back to
|
|
275
|
+
<small>[Back to docs](README.md)</small>
|