tradelab 1.0.1 → 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 +112 -0
- package/README.md +188 -328
- package/bin/tradelab-mcp.js +7 -0
- package/bin/tradelab.js +29 -0
- package/dist/cjs/data.cjs +149 -26
- package/dist/cjs/index.cjs +1917 -1005
- package/dist/cjs/live.cjs +536 -25
- package/dist/cjs/ta.cjs +339 -0
- package/docs/README.md +32 -66
- package/docs/api-reference.md +283 -112
- package/docs/backtest-engine.md +210 -252
- package/docs/data-reporting-cli.md +114 -156
- package/docs/examples.md +6 -6
- package/docs/live-trading.md +263 -92
- package/docs/mcp.md +285 -0
- package/docs/research.md +157 -0
- package/examples/liveDashboard.js +33 -0
- package/examples/llmSignal.js +33 -0
- package/examples/mcpLiveTrading.js +77 -0
- package/examples/optimize.js +25 -0
- package/package.json +26 -4
- package/src/engine/asyncSignal.js +28 -0
- package/src/engine/backtest.js +13 -1
- package/src/engine/backtestAsync.js +27 -0
- package/src/engine/backtestTicks.js +13 -2
- package/src/engine/barSystemRunner.js +96 -41
- package/src/engine/execution.js +39 -0
- package/src/engine/grid.js +15 -0
- package/src/engine/llmSignal.js +84 -0
- package/src/engine/optimize.js +110 -0
- package/src/engine/optimizeWorker.js +67 -0
- package/src/engine/portfolio.js +4 -1
- package/src/engine/walkForward.js +1 -0
- package/src/index.js +9 -0
- package/src/live/dashboard/server.js +179 -0
- package/src/live/engine/liveEngine.js +2 -2
- package/src/live/engine/paperEngine.js +5 -0
- package/src/live/index.js +3 -0
- package/src/live/session.js +402 -0
- package/src/mcp/liveTools.js +179 -0
- package/src/mcp/schemas.js +167 -0
- package/src/mcp/server.js +35 -0
- package/src/mcp/tools.js +265 -0
- package/src/metrics/annualize.js +32 -0
- package/src/metrics/benchmark.js +55 -0
- package/src/metrics/buildMetrics.js +34 -13
- package/src/metrics/finite.js +17 -0
- package/src/research/combinations.js +18 -0
- package/src/research/cpcv.js +47 -0
- package/src/research/deflatedSharpe.js +35 -0
- package/src/research/index.js +6 -0
- package/src/research/monteCarlo.js +88 -0
- package/src/research/pbo.js +69 -0
- package/src/research/stats.js +78 -0
- package/src/strategies/builtins.js +96 -0
- package/src/strategies/index.js +30 -0
- package/src/ta/channels.js +67 -0
- package/src/ta/index.js +16 -0
- package/src/ta/oscillators.js +70 -0
- package/src/ta/trend.js +78 -0
- package/src/utils/random.js +33 -0
- package/templates/dashboard.html +661 -0
- package/types/index.d.ts +179 -0
- package/types/live.d.ts +114 -0
- package/types/mcp.d.ts +17 -0
- package/types/ta.d.ts +45 -0
package/README.md
CHANGED
|
@@ -1,278 +1,229 @@
|
|
|
1
|
-
|
|
2
|
-
<img src="https://i.imgur.com/HGvvQbq.png" width="420" alt="tradelab logo" />
|
|
1
|
+
# tradelab
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
A Node.js toolkit for testing, validating, and operating trading strategies.
|
|
5
4
|
|
|
6
|
-
|
|
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)
|
|
5
|
+
tradelab gives you one `signal()` contract across research and execution:
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
- run candle or tick backtests
|
|
8
|
+
- model slippage, commissions, borrow, carry, and funding
|
|
9
|
+
- validate parameters with walk-forward tests and research statistics
|
|
10
|
+
- combine multiple systems into a shared-capital portfolio
|
|
11
|
+
- move the same strategy into paper or live execution
|
|
12
|
+
- export reports, metrics, and trade ledgers
|
|
13
|
+
- expose research tools through an MCP server
|
|
19
14
|
|
|
20
15
|
```bash
|
|
21
16
|
npm install tradelab
|
|
22
17
|
```
|
|
23
18
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
## Table of contents
|
|
27
|
-
|
|
28
|
-
- [What it includes](#what-it-includes)
|
|
29
|
-
- [Quick start](#quick-start)
|
|
30
|
-
- [Loading historical data](#loading-historical-data)
|
|
31
|
-
- [Core concepts](#core-concepts)
|
|
32
|
-
- [Portfolio mode](#portfolio-mode)
|
|
33
|
-
- [Walk-forward optimization](#walk-forward-optimization)
|
|
34
|
-
- [Tick backtests](#tick-backtests)
|
|
35
|
-
- [Live trading](#live-trading)
|
|
36
|
-
- [Execution and cost modeling](#execution-and-cost-modeling)
|
|
37
|
-
- [Exports and reporting](#exports-and-reporting)
|
|
38
|
-
- [CLI](#cli)
|
|
39
|
-
- [Examples](#examples)
|
|
40
|
-
- [Documentation](#documentation)
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## What it includes
|
|
45
|
-
|
|
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 |
|
|
56
|
-
|
|
57
|
-
---
|
|
19
|
+
Requires Node.js 18 or newer.
|
|
58
20
|
|
|
59
|
-
## Quick
|
|
60
|
-
|
|
61
|
-
If you already have candles, `backtest()` is the main entry point.
|
|
21
|
+
## Quick Start
|
|
62
22
|
|
|
63
23
|
```js
|
|
64
|
-
import { backtest, ema, exportBacktestArtifacts } from "tradelab";
|
|
24
|
+
import { backtest, getHistoricalCandles, ema, exportBacktestArtifacts } from "tradelab";
|
|
25
|
+
|
|
26
|
+
const candles = await getHistoricalCandles({
|
|
27
|
+
source: "yahoo",
|
|
28
|
+
symbol: "SPY",
|
|
29
|
+
interval: "1d",
|
|
30
|
+
period: "2y",
|
|
31
|
+
cache: true,
|
|
32
|
+
});
|
|
65
33
|
|
|
66
34
|
const result = backtest({
|
|
67
35
|
candles,
|
|
68
|
-
symbol: "
|
|
69
|
-
interval: "
|
|
70
|
-
range: "60d",
|
|
36
|
+
symbol: "SPY",
|
|
37
|
+
interval: "1d",
|
|
71
38
|
equity: 10_000,
|
|
72
39
|
riskPct: 1,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
40
|
+
warmupBars: 50,
|
|
41
|
+
costs: {
|
|
42
|
+
slippageBps: 1,
|
|
43
|
+
commissionBps: 0.5,
|
|
44
|
+
},
|
|
45
|
+
signal({ candles: history, bar }) {
|
|
46
|
+
const closes = history.map((c) => c.close);
|
|
77
47
|
const fast = ema(closes, 10);
|
|
78
48
|
const slow = ema(closes, 30);
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
if (fast[last - 1] <= slow[last - 1] && fast[last] > slow[last]) {
|
|
82
|
-
const entry = history[last].close;
|
|
83
|
-
const stop = Math.min(...history.slice(-15).map((bar) => bar.low));
|
|
84
|
-
const risk = entry - stop;
|
|
85
|
-
if (risk <= 0) return null;
|
|
49
|
+
const i = closes.length - 1;
|
|
86
50
|
|
|
87
|
-
|
|
51
|
+
if (fast[i - 1] <= slow[i - 1] && fast[i] > slow[i]) {
|
|
52
|
+
return { side: "long", stop: bar.close * 0.97, rr: 2 };
|
|
88
53
|
}
|
|
89
54
|
|
|
90
55
|
return null;
|
|
91
56
|
},
|
|
92
57
|
});
|
|
93
58
|
|
|
59
|
+
console.log(result.metrics);
|
|
94
60
|
exportBacktestArtifacts({ result, outDir: "./output" });
|
|
95
61
|
```
|
|
96
62
|
|
|
97
|
-
|
|
63
|
+
Start with `result.metrics` for the summary and `result.positions` for completed trades. Use `trades` when you need every realized leg, including partial exits.
|
|
98
64
|
|
|
99
|
-
|
|
65
|
+
## What You Can Build
|
|
100
66
|
|
|
101
|
-
|
|
67
|
+
| Goal | API or command |
|
|
68
|
+
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
69
|
+
| Backtest one strategy | `backtest({ candles, signal })` |
|
|
70
|
+
| Backtest an async strategy | `backtestAsync({ candles, signal })` |
|
|
71
|
+
| Replay tick or quote data | `backtestTicks({ ticks, signal })` |
|
|
72
|
+
| Run several systems together | `backtestPortfolio({ systems })` |
|
|
73
|
+
| Test parameter stability | `walkForwardOptimize(options)` |
|
|
74
|
+
| Run a parallel parameter sweep | `optimize({ signalModulePath, parameterSets })` |
|
|
75
|
+
| Use indicators | `import { rsi, macd, vwap } from "tradelab/ta"` |
|
|
76
|
+
| Check overfitting risk | `research.monteCarlo`, `research.deflatedSharpe` |
|
|
77
|
+
| Run in paper or live mode | `LiveEngine`, `LiveOrchestrator`, `tradelab paper` |
|
|
78
|
+
| Watch a live run locally | `createDashboardServer({ source })` — equity curve, KPI strip, controls |
|
|
79
|
+
| Let MCP clients run research tools | `tradelab-mcp` — `run_backtest`, `walk_forward`, `analyze_robustness`, `optimize_strategy`, `compare_strategies`, `candle_stats` |
|
|
80
|
+
| Let MCP agents trade (paper/live) | `tradelab-mcp` — `create_session`, `feed_price`, `place_order`, bracket orders, `halt_all` kill-switch (see [docs/mcp.md](docs/mcp.md)) |
|
|
81
|
+
| Export reports and machine data | `exportBacktestArtifacts`, `exportMetricsJSON` |
|
|
102
82
|
|
|
103
|
-
|
|
83
|
+
## The Signal Contract
|
|
104
84
|
|
|
105
|
-
|
|
106
|
-
import { getHistoricalCandles, backtest } from "tradelab";
|
|
85
|
+
Your strategy is a function. Return `null` to do nothing, or return a trade signal.
|
|
107
86
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
interval: "1d",
|
|
112
|
-
period: "2y",
|
|
113
|
-
cache: true, // reuses local copy on repeated runs
|
|
114
|
-
});
|
|
87
|
+
```js
|
|
88
|
+
function signal({ candles, index, bar, equity, openPosition, pendingOrder }) {
|
|
89
|
+
if (openPosition || index < 50) return null;
|
|
115
90
|
|
|
116
|
-
|
|
91
|
+
return {
|
|
92
|
+
side: "long",
|
|
93
|
+
entry: bar.close, // optional; defaults to current close
|
|
94
|
+
stop: bar.close - 2,
|
|
95
|
+
rr: 2, // take profit at 2R
|
|
96
|
+
};
|
|
97
|
+
}
|
|
117
98
|
```
|
|
118
99
|
|
|
119
|
-
|
|
100
|
+
Common signal fields:
|
|
120
101
|
|
|
121
|
-
|
|
102
|
+
| Field | Meaning |
|
|
103
|
+
| --------------------------- | --------------------------------------------------- |
|
|
104
|
+
| `side` | `long`, `short`, `buy`, or `sell` |
|
|
105
|
+
| `entry` | Entry price. Defaults to the current close |
|
|
106
|
+
| `stop` | Required stop level for sizing and risk |
|
|
107
|
+
| `takeProfit` | Explicit target price |
|
|
108
|
+
| `rr` | Builds target from risk when `takeProfit` is absent |
|
|
109
|
+
| `qty` or `size` | Fixed size override |
|
|
110
|
+
| `riskPct` or `riskFraction` | Per-trade risk override |
|
|
122
111
|
|
|
123
|
-
|
|
112
|
+
## Data
|
|
124
113
|
|
|
125
|
-
|
|
114
|
+
Use `getHistoricalCandles()` for Yahoo Finance, CSV files, and cached datasets.
|
|
126
115
|
|
|
127
116
|
```js
|
|
128
|
-
const
|
|
129
|
-
source: "
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// ... optional column mapping
|
|
135
|
-
},
|
|
117
|
+
const yahoo = await getHistoricalCandles({
|
|
118
|
+
source: "yahoo",
|
|
119
|
+
symbol: "QQQ",
|
|
120
|
+
interval: "1d",
|
|
121
|
+
period: "1y",
|
|
122
|
+
cache: true,
|
|
136
123
|
});
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
If your CSV already uses standard OHLCV column names, no mapping is needed at all.
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
## Core concepts
|
|
144
|
-
|
|
145
|
-
### The signal function
|
|
146
124
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
// return null to skip
|
|
152
|
-
// return a signal to enter
|
|
153
|
-
return {
|
|
154
|
-
side: "long", // "long" | "short" | "buy" | "sell"
|
|
155
|
-
entry: bar.close, // defaults to current close if omitted
|
|
156
|
-
stop: bar.close - 2,
|
|
157
|
-
rr: 2, // target = entry + (entry - stop) * rr
|
|
158
|
-
};
|
|
159
|
-
}
|
|
125
|
+
const csv = await getHistoricalCandles({
|
|
126
|
+
source: "csv",
|
|
127
|
+
csvPath: "./data/btc.csv",
|
|
128
|
+
});
|
|
160
129
|
```
|
|
161
130
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
### Key backtest options
|
|
165
|
-
|
|
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 |
|
|
176
|
-
|
|
177
|
-
### Result shape
|
|
131
|
+
Candles are normalized to:
|
|
178
132
|
|
|
179
133
|
```js
|
|
180
134
|
{
|
|
181
|
-
|
|
182
|
-
trades, // every realized leg, including partial exits
|
|
183
|
-
positions, // completed positions - start here for analysis
|
|
184
|
-
metrics, // winRate, profitFactor, maxDrawdown, sharpe, ...
|
|
185
|
-
eqSeries, // [{ time, timestamp, equity }] - equity curve
|
|
186
|
-
replay, // visualization frames and events
|
|
135
|
+
(time, open, high, low, close, volume);
|
|
187
136
|
}
|
|
188
137
|
```
|
|
189
138
|
|
|
190
|
-
|
|
191
|
-
|
|
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?
|
|
196
|
-
|
|
197
|
-
---
|
|
198
|
-
|
|
199
|
-
## Portfolio mode
|
|
139
|
+
## Costs
|
|
200
140
|
|
|
201
|
-
|
|
141
|
+
Cost assumptions belong in the run, not in post-processing.
|
|
202
142
|
|
|
203
143
|
```js
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
144
|
+
const result = backtest({
|
|
145
|
+
candles,
|
|
146
|
+
signal,
|
|
147
|
+
costs: {
|
|
148
|
+
slippageBps: 2,
|
|
149
|
+
spreadBps: 1,
|
|
150
|
+
commissionBps: 1,
|
|
151
|
+
minCommission: 1,
|
|
152
|
+
carry: {
|
|
153
|
+
longAnnualBps: 500,
|
|
154
|
+
shortAnnualBps: 800,
|
|
155
|
+
},
|
|
156
|
+
funding: {
|
|
157
|
+
rateBps: 10,
|
|
158
|
+
intervalMs: 8 * 60 * 60 * 1000,
|
|
159
|
+
anchorMs: 0,
|
|
160
|
+
},
|
|
161
|
+
},
|
|
212
162
|
});
|
|
213
163
|
```
|
|
214
164
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
---
|
|
165
|
+
`exit.financing` is included on closed trades when carry or funding applies. It is already deducted from `exit.pnl` and aggregate metrics.
|
|
218
166
|
|
|
219
|
-
##
|
|
167
|
+
## Validation
|
|
220
168
|
|
|
221
|
-
Use
|
|
169
|
+
Use a normal backtest to build the strategy. Use validation tools before trusting it.
|
|
222
170
|
|
|
223
171
|
```js
|
|
224
|
-
import { walkForwardOptimize } from "tradelab";
|
|
172
|
+
import { walkForwardOptimize, grid } from "tradelab";
|
|
225
173
|
|
|
226
174
|
const wf = walkForwardOptimize({
|
|
227
175
|
candles,
|
|
228
|
-
mode: "anchored",
|
|
229
176
|
trainBars: 180,
|
|
230
177
|
testBars: 60,
|
|
231
|
-
|
|
178
|
+
mode: "anchored",
|
|
232
179
|
scoreBy: "profitFactor",
|
|
233
|
-
parameterSets:
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
180
|
+
parameterSets: grid({
|
|
181
|
+
fast: [8, 10, 12],
|
|
182
|
+
slow: [21, 30, 50],
|
|
183
|
+
rr: [1.5, 2, 3],
|
|
184
|
+
}),
|
|
237
185
|
signalFactory(params) {
|
|
238
|
-
return
|
|
186
|
+
return createEmaSignal(params);
|
|
239
187
|
},
|
|
240
188
|
});
|
|
241
|
-
```
|
|
242
|
-
|
|
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
189
|
|
|
247
|
-
|
|
190
|
+
console.log(wf.metrics);
|
|
191
|
+
console.log(wf.bestParamsSummary);
|
|
192
|
+
```
|
|
248
193
|
|
|
249
|
-
|
|
194
|
+
For larger sweeps, use `optimize()` with a strategy module:
|
|
250
195
|
|
|
251
196
|
```js
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
197
|
+
const out = await optimize({
|
|
198
|
+
candles,
|
|
199
|
+
interval: "1d",
|
|
200
|
+
signalModulePath: new URL("./strategy.js", import.meta.url).pathname,
|
|
201
|
+
parameterSets: grid({ fast: [8, 10], slow: [30, 50] }),
|
|
202
|
+
scoreBy: "sharpeAnnualized",
|
|
258
203
|
});
|
|
259
204
|
```
|
|
260
205
|
|
|
261
|
-
|
|
206
|
+
## Portfolio Backtests
|
|
262
207
|
|
|
263
|
-
|
|
208
|
+
`backtestPortfolio()` runs multiple systems against shared capital. Capital is locked only when an order fills, so later systems size against what is still available.
|
|
264
209
|
|
|
265
|
-
|
|
210
|
+
```js
|
|
211
|
+
const portfolio = backtestPortfolio({
|
|
212
|
+
equity: 100_000,
|
|
213
|
+
interval: "1d",
|
|
214
|
+
maxDailyLossPct: 3,
|
|
215
|
+
systems: [
|
|
216
|
+
{ symbol: "SPY", candles: spy, signal: spySignal, weight: 2 },
|
|
217
|
+
{ symbol: "QQQ", candles: qqq, signal: qqqSignal, weight: 1 },
|
|
218
|
+
],
|
|
219
|
+
});
|
|
220
|
+
```
|
|
266
221
|
|
|
267
|
-
`
|
|
222
|
+
Portfolio equity points include `lockedCapital` and `availableCapital`.
|
|
268
223
|
|
|
269
|
-
|
|
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`
|
|
224
|
+
## Live and Paper Runs
|
|
274
225
|
|
|
275
|
-
|
|
226
|
+
The live package uses the same signal shape as backtests.
|
|
276
227
|
|
|
277
228
|
```js
|
|
278
229
|
import { LiveEngine, PaperEngine, JsonFileStorage } from "tradelab/live";
|
|
@@ -281,172 +232,81 @@ const engine = new LiveEngine({
|
|
|
281
232
|
id: "aapl-1m",
|
|
282
233
|
symbol: "AAPL",
|
|
283
234
|
interval: "1m",
|
|
235
|
+
mode: "polling",
|
|
284
236
|
broker: new PaperEngine({ equity: 25_000 }),
|
|
285
237
|
storage: new JsonFileStorage({ baseDir: "./output/live-state" }),
|
|
286
|
-
signal
|
|
287
|
-
if (openPosition) return null;
|
|
288
|
-
return { side: "long", stop: bar.close - 1, rr: 2 };
|
|
289
|
-
},
|
|
238
|
+
signal,
|
|
290
239
|
});
|
|
291
240
|
|
|
292
241
|
await engine.start();
|
|
293
242
|
```
|
|
294
243
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
---
|
|
298
|
-
|
|
299
|
-
## Execution and cost modeling
|
|
300
|
-
|
|
301
|
-
```js
|
|
302
|
-
const result = backtest({
|
|
303
|
-
candles,
|
|
304
|
-
signal,
|
|
305
|
-
costs: {
|
|
306
|
-
slippageBps: 2,
|
|
307
|
-
spreadBps: 1,
|
|
308
|
-
slippageByKind: {
|
|
309
|
-
market: 3,
|
|
310
|
-
limit: 0.5,
|
|
311
|
-
stop: 4,
|
|
312
|
-
},
|
|
313
|
-
commissionBps: 1,
|
|
314
|
-
commissionPerUnit: 0,
|
|
315
|
-
commissionPerOrder: 1,
|
|
316
|
-
minCommission: 1,
|
|
317
|
-
},
|
|
318
|
-
});
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
- Slippage is applied in the trade direction
|
|
322
|
-
- Spread is modeled as half-spread paid on entry and exit
|
|
323
|
-
- Commission can be percentage-based, per-unit, per-order, or mixed
|
|
324
|
-
- `minCommission` floors the fee per fill
|
|
325
|
-
|
|
326
|
-
> Leaving costs at zero is the most common cause of inflated backtests. Set them from the start.
|
|
327
|
-
|
|
328
|
-
---
|
|
329
|
-
|
|
330
|
-
## Exports and reporting
|
|
331
|
-
|
|
332
|
-
```js
|
|
333
|
-
import { exportBacktestArtifacts } from "tradelab";
|
|
244
|
+
Run the same flow from the terminal:
|
|
334
245
|
|
|
335
|
-
|
|
336
|
-
|
|
246
|
+
```bash
|
|
247
|
+
tradelab paper --symbol AAPL --interval 1m --mode polling --once true
|
|
248
|
+
tradelab live --config ./live-portfolio.json --paper
|
|
337
249
|
```
|
|
338
250
|
|
|
339
|
-
|
|
340
|
-
|
|
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 |
|
|
347
|
-
|
|
348
|
-
For programmatic pipelines, `exportMetricsJSON` is usually the most useful format to build on.
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
## CLI
|
|
353
|
-
|
|
354
|
-
The package ships a `tradelab` binary. Best for quick iteration, smoke tests, and trying the package before wiring it into application code.
|
|
355
|
-
|
|
356
|
-
```bash
|
|
357
|
-
# Backtest from Yahoo
|
|
358
|
-
npx tradelab backtest --source yahoo --symbol SPY --interval 1d --period 1y
|
|
251
|
+
Add `--dashboard --dashboardPort 4317` to open a local Server-Sent Events dashboard.
|
|
359
252
|
|
|
360
|
-
|
|
361
|
-
npx tradelab backtest --source csv --csvPath ./data/btc.csv --strategy buy-hold --holdBars 3
|
|
253
|
+
## MCP Server
|
|
362
254
|
|
|
363
|
-
|
|
364
|
-
npx tradelab portfolio \
|
|
365
|
-
--csvPaths ./data/spy.csv,./data/qqq.csv \
|
|
366
|
-
--symbols SPY,QQQ \
|
|
367
|
-
--strategy buy-hold
|
|
255
|
+
`tradelab-mcp` exposes research and live-trading tools over stdio to any MCP-capable agent (Claude Desktop, Cursor, etc.). See [docs/mcp.md](docs/mcp.md) for the full tool reference and agent trading guide.
|
|
368
256
|
|
|
369
|
-
|
|
370
|
-
npx tradelab walk-forward \
|
|
371
|
-
--source yahoo --symbol QQQ --interval 1d --period 2y \
|
|
372
|
-
--trainBars 180 --testBars 60 --mode anchored
|
|
257
|
+
**Research tools:** `list_strategies`, `fetch_candles`, `run_backtest`, `walk_forward`, `analyze_robustness`, `optimize_strategy`, `compare_strategies`, `candle_stats`
|
|
373
258
|
|
|
374
|
-
|
|
375
|
-
npx tradelab paper --symbol AAPL --interval 1m --mode polling --once true
|
|
259
|
+
**Agent trading tools (paper by default; live gated):** `create_session`, `list_sessions`, `session_status`, `feed_price`, `place_order`, `close_position`, `flatten`, `cancel_order`, `account`, `positions`, `recent_events`, `attach_strategy`, `halt_all`
|
|
376
260
|
|
|
377
|
-
|
|
378
|
-
npx tradelab live --config ./live-portfolio.json --paper --mode polling --once true
|
|
261
|
+
Paper trading needs no credentials. Live trading requires `TRADELAB_ALLOW_LIVE=true` and `confirmLive: true` plus a credentialed broker. `halt_all` is an emergency kill-switch that flattens all positions and stops every session.
|
|
379
262
|
|
|
380
|
-
|
|
381
|
-
npx tradelab status --dir ./output/live-state
|
|
263
|
+
Use it from any MCP client that can launch a stdio server:
|
|
382
264
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
265
|
+
```json
|
|
266
|
+
{
|
|
267
|
+
"mcpServers": {
|
|
268
|
+
"tradelab": {
|
|
269
|
+
"command": "npx",
|
|
270
|
+
"args": ["-y", "tradelab", "tradelab-mcp"]
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
386
274
|
```
|
|
387
275
|
|
|
388
|
-
|
|
389
|
-
|
|
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`.
|
|
391
|
-
|
|
392
|
-
---
|
|
393
|
-
|
|
394
|
-
## Examples
|
|
276
|
+
## CLI
|
|
395
277
|
|
|
396
278
|
```bash
|
|
397
|
-
|
|
398
|
-
|
|
279
|
+
tradelab backtest --source yahoo --symbol SPY --interval 1d --period 1y
|
|
280
|
+
tradelab portfolio --csvPaths ./spy.csv,./qqq.csv --symbols SPY,QQQ
|
|
281
|
+
tradelab walk-forward --source yahoo --symbol QQQ --interval 1d --period 2y
|
|
282
|
+
tradelab status --dir ./output/live-state
|
|
399
283
|
```
|
|
400
284
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
---
|
|
285
|
+
## Documentation
|
|
404
286
|
|
|
405
|
-
|
|
287
|
+
- [Docs home](docs/README.md)
|
|
288
|
+
- [Backtesting](docs/backtest-engine.md)
|
|
289
|
+
- [Data, reporting, and CLI](docs/data-reporting-cli.md)
|
|
290
|
+
- [Live trading](docs/live-trading.md)
|
|
291
|
+
- [MCP server](docs/mcp.md)
|
|
292
|
+
- [Research tools](docs/research.md)
|
|
293
|
+
- [Strategy examples](docs/examples.md)
|
|
294
|
+
- [API reference](docs/api-reference.md)
|
|
406
295
|
|
|
407
|
-
|
|
296
|
+
## Module Entry Points
|
|
408
297
|
|
|
409
298
|
```js
|
|
410
|
-
import { backtest, getHistoricalCandles
|
|
411
|
-
import {
|
|
412
|
-
import { LiveEngine, PaperEngine } from "tradelab/live";
|
|
299
|
+
import { backtest, getHistoricalCandles } from "tradelab";
|
|
300
|
+
import { rsi, macd, vwap } from "tradelab/ta";
|
|
301
|
+
import { LiveEngine, PaperEngine, TradingSession, SessionManager } from "tradelab/live";
|
|
413
302
|
```
|
|
414
303
|
|
|
415
|
-
|
|
304
|
+
CommonJS is supported for the main, data, live, and TA entry points:
|
|
416
305
|
|
|
417
306
|
```js
|
|
418
|
-
const { backtest
|
|
419
|
-
const { fetchHistorical } = require("tradelab/data");
|
|
420
|
-
const { LiveEngine, PaperEngine } = require("tradelab/live");
|
|
307
|
+
const { backtest } = require("tradelab");
|
|
421
308
|
```
|
|
422
309
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
## Documentation
|
|
426
|
-
|
|
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 |
|
|
434
|
-
|
|
435
|
-
---
|
|
436
|
-
|
|
437
|
-
## Common mistakes
|
|
438
|
-
|
|
439
|
-
- Using unsorted candles or mixed intervals in a single series
|
|
440
|
-
- Reading `trades` as if they were always full positions - use `positions` for top-line analysis
|
|
441
|
-
- Leaving costs at zero and overestimating edge
|
|
442
|
-
- Trusting one backtest without out-of-sample validation
|
|
443
|
-
- Debugging a strategy with `strict: false` when lookahead is possible
|
|
444
|
-
|
|
445
|
-
---
|
|
446
|
-
|
|
447
|
-
## Notes
|
|
310
|
+
## License
|
|
448
311
|
|
|
449
|
-
|
|
450
|
-
- Yahoo downloads are cached under `output/data` by default
|
|
451
|
-
- CommonJS and ESM are both supported
|
|
452
|
-
- Live adapters support broker execution workflows, but this is still not an exchange microstructure simulator
|
|
312
|
+
MIT
|