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
package/types/index.d.ts
CHANGED
|
@@ -132,6 +132,14 @@ export interface SideBreakdownEntry {
|
|
|
132
132
|
avgR: number;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
export interface BenchmarkStats {
|
|
136
|
+
alpha: number | null;
|
|
137
|
+
beta: number | null;
|
|
138
|
+
correlation: number | null;
|
|
139
|
+
informationRatio: number | null;
|
|
140
|
+
trackingError: number | null;
|
|
141
|
+
}
|
|
142
|
+
|
|
135
143
|
/** Aggregate performance metrics returned by `backtest()`. */
|
|
136
144
|
export interface BacktestMetrics {
|
|
137
145
|
/** Count of completed positions included in the aggregate metrics. */
|
|
@@ -146,6 +154,12 @@ export interface BacktestMetrics {
|
|
|
146
154
|
avgR: number;
|
|
147
155
|
/** Daily Sharpe ratio alias for quick access. */
|
|
148
156
|
sharpe: number;
|
|
157
|
+
/** Annualized Sharpe ratio derived from the configured interval or bar spacing. */
|
|
158
|
+
sharpeAnnualized: number;
|
|
159
|
+
/** Annualized Sortino ratio derived from the configured interval or bar spacing. */
|
|
160
|
+
sortinoAnnualized: number;
|
|
161
|
+
/** Number of periods per year used for annualized metrics. */
|
|
162
|
+
annualizationPeriods: number;
|
|
149
163
|
sharpePerTrade: number;
|
|
150
164
|
sortinoPerTrade: number;
|
|
151
165
|
/** Maximum drawdown percent alias. */
|
|
@@ -169,6 +183,7 @@ export interface BacktestMetrics {
|
|
|
169
183
|
/** Daily Sharpe ratio computed from realized equity changes. */
|
|
170
184
|
sharpeDaily: number;
|
|
171
185
|
sortinoDaily: number;
|
|
186
|
+
benchmark: BenchmarkStats;
|
|
172
187
|
/** Long/short breakdown grouped by completed position side. */
|
|
173
188
|
sideBreakdown: {
|
|
174
189
|
long: SideBreakdownEntry;
|
|
@@ -659,6 +674,7 @@ export function optimize(options: {
|
|
|
659
674
|
export function backtestPortfolio(options: {
|
|
660
675
|
systems: PortfolioSystem[];
|
|
661
676
|
equity?: number;
|
|
677
|
+
interval?: string;
|
|
662
678
|
allocation?: "equal" | "weight";
|
|
663
679
|
collectEqSeries?: boolean;
|
|
664
680
|
collectReplay?: boolean;
|
|
@@ -684,7 +700,16 @@ export function buildMetrics(input: {
|
|
|
684
700
|
candles: Candle[];
|
|
685
701
|
estBarMs: number;
|
|
686
702
|
eqSeries?: EquityPoint[];
|
|
703
|
+
interval?: string;
|
|
704
|
+
benchmarkReturns?: number[];
|
|
687
705
|
}): BacktestMetrics;
|
|
706
|
+
export function benchmarkStats(
|
|
707
|
+
strategyReturns: number[],
|
|
708
|
+
benchmarkReturns: number[]
|
|
709
|
+
): BenchmarkStats;
|
|
710
|
+
export function clampFinite(value: unknown, fallback?: number): number;
|
|
711
|
+
export const BIG_NUMBER: number;
|
|
712
|
+
export function periodsPerYear(interval?: string, estBarMs?: number): number;
|
|
688
713
|
|
|
689
714
|
export class LlmSignal {
|
|
690
715
|
constructor(options: LlmSignalOptions);
|
package/types/live.d.ts
CHANGED
|
@@ -84,6 +84,8 @@ export interface StoredState {
|
|
|
84
84
|
savedAt: number;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
export const LIVE_EVENTS: string[];
|
|
88
|
+
|
|
87
89
|
export class EventBus extends import("node:events").EventEmitter {
|
|
88
90
|
emitEvent(event: string, payload?: Record<string, unknown>): true;
|
|
89
91
|
onAny(handler: (input: { event: string; payload: Record<string, unknown> }) => void): () => void;
|
|
@@ -395,3 +397,100 @@ export function createLiveOrchestrator(options: {
|
|
|
395
397
|
}): LiveOrchestrator;
|
|
396
398
|
|
|
397
399
|
export type { SignalResult };
|
|
400
|
+
|
|
401
|
+
// ── TradingSession / SessionManager ──────────────────────────────────────────
|
|
402
|
+
|
|
403
|
+
export interface TradingSessionOptions {
|
|
404
|
+
id?: string;
|
|
405
|
+
symbol: string;
|
|
406
|
+
interval?: string;
|
|
407
|
+
broker: BrokerAdapter;
|
|
408
|
+
mode?: "paper" | "live";
|
|
409
|
+
equity?: number;
|
|
410
|
+
riskPct?: number;
|
|
411
|
+
maxDailyLossPct?: number;
|
|
412
|
+
maxPositionPct?: number;
|
|
413
|
+
qtyStep?: number;
|
|
414
|
+
minQty?: number;
|
|
415
|
+
maxLeverage?: number;
|
|
416
|
+
eventBus?: EventBus;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export interface SessionPlaceOrderOptions {
|
|
420
|
+
side: "long" | "short" | "buy" | "sell";
|
|
421
|
+
type?: "market" | "limit" | "stop" | "stop_limit";
|
|
422
|
+
qty?: number;
|
|
423
|
+
riskPct?: number;
|
|
424
|
+
stop?: number;
|
|
425
|
+
target?: number;
|
|
426
|
+
rr?: number;
|
|
427
|
+
limitPrice?: number;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export interface SessionStatus {
|
|
431
|
+
id: string;
|
|
432
|
+
symbol: string;
|
|
433
|
+
interval: string;
|
|
434
|
+
mode: "paper" | "live";
|
|
435
|
+
running: boolean;
|
|
436
|
+
equity: number;
|
|
437
|
+
dayPnl: number;
|
|
438
|
+
lastPrice: number | null;
|
|
439
|
+
positions: BrokerPosition[];
|
|
440
|
+
openOrders: OrderReceipt[];
|
|
441
|
+
risk: {
|
|
442
|
+
halted: boolean;
|
|
443
|
+
haltReason: string | null;
|
|
444
|
+
dayPnl: number;
|
|
445
|
+
dayTrades: number;
|
|
446
|
+
[key: string]: unknown;
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export class TradingSession {
|
|
451
|
+
constructor(options: TradingSessionOptions);
|
|
452
|
+
readonly id: string;
|
|
453
|
+
readonly symbol: string;
|
|
454
|
+
readonly interval: string;
|
|
455
|
+
readonly mode: "paper" | "live";
|
|
456
|
+
readonly eventBus: EventBus;
|
|
457
|
+
equity: number;
|
|
458
|
+
lastPrice: number | null;
|
|
459
|
+
running: boolean;
|
|
460
|
+
candleBuffer: import("./index.d.ts").Candle[];
|
|
461
|
+
static liveAllowed(): boolean;
|
|
462
|
+
start(): Promise<void>;
|
|
463
|
+
stop(options?: { flatten?: boolean }): Promise<void>;
|
|
464
|
+
pushBar(bar: import("./index.d.ts").Candle): Promise<void>;
|
|
465
|
+
placeOrder(options: SessionPlaceOrderOptions): Promise<OrderReceipt>;
|
|
466
|
+
closePosition(symbol?: string): Promise<OrderReceipt | null>;
|
|
467
|
+
flatten(): Promise<void>;
|
|
468
|
+
cancelOrder(orderId: string): Promise<void>;
|
|
469
|
+
getAccount(): Promise<AccountInfo>;
|
|
470
|
+
getPositions(): Promise<BrokerPosition[]>;
|
|
471
|
+
recentEvents(limit?: number): Array<{ event: string; payload: unknown; t: number }>;
|
|
472
|
+
getStatus(): SessionStatus;
|
|
473
|
+
refresh(): Promise<SessionStatus>;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export interface SessionManagerOptions {
|
|
477
|
+
brokerFactory?: (options: Record<string, unknown>) => BrokerAdapter;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
export interface CreateSessionOptions extends Partial<TradingSessionOptions> {
|
|
481
|
+
id: string;
|
|
482
|
+
symbol: string;
|
|
483
|
+
confirmLive?: boolean;
|
|
484
|
+
broker?: BrokerAdapter;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export class SessionManager {
|
|
488
|
+
constructor(options?: SessionManagerOptions);
|
|
489
|
+
create(options: CreateSessionOptions): Promise<TradingSession>;
|
|
490
|
+
get(id: string): TradingSession | null;
|
|
491
|
+
list(): TradingSession[];
|
|
492
|
+
remove(id: string, options?: { flatten?: boolean }): Promise<void>;
|
|
493
|
+
haltAll(): Promise<void>;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
export function createSessionManager(options?: SessionManagerOptions): SessionManager;
|
package/types/mcp.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { SessionManager } from "./live.d.ts";
|
|
3
|
+
|
|
4
|
+
export function createServer(): McpServer;
|
|
5
|
+
export function startStdioServer(): Promise<McpServer>;
|
|
6
|
+
|
|
7
|
+
export interface McpToolHandler {
|
|
8
|
+
description: string;
|
|
9
|
+
handler(args: Record<string, unknown>): Promise<unknown>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const mcpTools: Record<string, McpToolHandler>;
|
|
13
|
+
export const researchTools: Record<string, McpToolHandler>;
|
|
14
|
+
export const liveTools: Record<string, McpToolHandler>;
|
|
15
|
+
|
|
16
|
+
/** The shared SessionManager instance used by the MCP server process. */
|
|
17
|
+
export const sessionManager: SessionManager;
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# tradelab 2026 Roadmap — Overview & Sequencing
|
|
2
|
-
|
|
3
|
-
> **For agentic workers:** Each subsystem below has its own plan file. Use
|
|
4
|
-
> `superpowers:subagent-driven-development` (recommended) or
|
|
5
|
-
> `superpowers:executing-plans` to implement one plan at a time, task-by-task.
|
|
6
|
-
> All step lists use checkbox (`- [ ]`) syntax for tracking.
|
|
7
|
-
|
|
8
|
-
**Goal:** Turn tradelab from a strong single-run backtester into an AI-native,
|
|
9
|
-
statistically-defensible research + execution platform for quants, simple
|
|
10
|
-
traders, and autonomous agents.
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## The 8 subsystems
|
|
15
|
-
|
|
16
|
-
| # | Plan file | What it delivers | Depends on |
|
|
17
|
-
| --- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ----------- |
|
|
18
|
-
| 1 | [2026-01-metrics-correctness.md](2026-01-metrics-correctness.md) | Annualized Sharpe/Sortino, finite-clamped metrics JSON, benchmark alpha/beta/IR | — |
|
|
19
|
-
| 2 | [2026-02-indicator-library.md](2026-02-indicator-library.md) | `tradelab/ta` namespace: RSI, MACD, Bollinger, VWAP, Supertrend, Donchian, Keltner, stochastics | — |
|
|
20
|
-
| 3 | [2026-03-overfitting-toolkit.md](2026-03-overfitting-toolkit.md) | CPCV, PBO, Deflated Sharpe, Monte Carlo bands, sweep haircut | 1 |
|
|
21
|
-
| 4 | [2026-04-async-signals-seeding.md](2026-04-async-signals-seeding.md) | `async signal()` with per-bar budget + cache + no-lookahead guard, `LlmSignal`, configurable RNG seed | — |
|
|
22
|
-
| 5 | [2026-05-mcp-server.md](2026-05-mcp-server.md) | `tradelab/mcp` server exposing data/backtest/walk-forward/metrics as agent tools | 1, 4 (soft) |
|
|
23
|
-
| 6 | [2026-06-parallel-param-sweep.md](2026-06-parallel-param-sweep.md) | Worker-pool param sweep + `optimize()` API | — |
|
|
24
|
-
| 7 | [2026-07-funding-carry-costs.md](2026-07-funding-carry-costs.md) | Funding/borrow/overnight carry in the cost model | — |
|
|
25
|
-
| 8 | [2026-08-live-dashboard.md](2026-08-live-dashboard.md) | Local realtime dashboard for `LiveEngine`/`LiveOrchestrator` | — |
|
|
26
|
-
|
|
27
|
-
### Dependency graph
|
|
28
|
-
|
|
29
|
-
```
|
|
30
|
-
1 metrics ──► 3 overfitting
|
|
31
|
-
1 metrics ──► 5 mcp (soft: nicer tool output)
|
|
32
|
-
4 async ────► 5 mcp (soft: agent-driven backtests)
|
|
33
|
-
2, 6, 7, 8 are independent
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### Recommended execution order
|
|
37
|
-
|
|
38
|
-
1. **Plan 1 (metrics)** — small, corrects existing bugs, unblocks 3 and 5.
|
|
39
|
-
2. **Plan 2 (indicators)** — independent, high user value, unblocks NL strategies later.
|
|
40
|
-
3. **Plan 4 (async signals + seed)** — engine change; do before MCP so agents can drive live.
|
|
41
|
-
4. **Plan 5 (MCP)** — the 2026 headline; sits on top of 1 + 4.
|
|
42
|
-
5. **Plan 3 (overfitting)** — the quant moat; needs clean metrics.
|
|
43
|
-
6. **Plans 6, 7, 8** — parallelizable, any order.
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Shared conventions (all plans assume these)
|
|
48
|
-
|
|
49
|
-
**Runtime:** Node `>=18`, ESM (`"type": "module"`). No transpile step for `src/`.
|
|
50
|
-
The CJS build is generated by `npm run build` (esbuild) from `src/`.
|
|
51
|
-
|
|
52
|
-
**Tests:** `node:test` + `node:assert/strict`. Run a single file with:
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
node --test test/<name>.test.js
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Run everything with `npm test` (`node --test`). New test files live under `test/`
|
|
59
|
-
mirroring `src/` layout (e.g. `src/metrics/finite.js` → `test/metrics/finite.test.js`).
|
|
60
|
-
There is no test runner config — discovery is by filename.
|
|
61
|
-
|
|
62
|
-
**Lint/format before commit:**
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
npm run lint
|
|
66
|
-
npm run format:check
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
**Commit style:** match existing history — `feat:`, `fix:`, `docs:`, `perf:`,
|
|
70
|
-
`test:`. Every commit message MUST end with the trailer:
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**Public exports:** add new top-level exports in [src/index.js](../../../src/index.js).
|
|
77
|
-
New subpath entrypoints (`tradelab/ta`, `tradelab/mcp`) require an entry in the
|
|
78
|
-
`exports` map of [package.json](../../../package.json) AND a matching CJS bundle
|
|
79
|
-
in [scripts/build-cjs.mjs](../../../scripts/build-cjs.mjs).
|
|
80
|
-
|
|
81
|
-
**Canonical result shape (do not break it):** every engine returns
|
|
82
|
-
`{ symbol, interval, range, trades, positions, openPositions, metrics, eqSeries, replay }`.
|
|
83
|
-
`buildMetrics` is the single source of truth for `metrics`. Plans that add metrics
|
|
84
|
-
fields ADD keys; they never rename or remove existing ones (dashboards depend on them).
|
|
85
|
-
|
|
86
|
-
**The signal contract (do not break it):** `signal(context)` receives
|
|
87
|
-
`{ candles, index, bar, equity, openPosition, pendingOrder }` and returns `null`
|
|
88
|
-
or `{ side, entry?, stop, rr|takeProfit, ... }`. Two engines call it independently:
|
|
89
|
-
the standalone loop in [src/engine/backtest.js](../../../src/engine/backtest.js) and
|
|
90
|
-
the shared [src/engine/barSystemRunner.js](../../../src/engine/barSystemRunner.js)
|
|
91
|
-
(used by portfolio). The live path uses
|
|
92
|
-
[src/live/engine/liveEngine.js](../../../src/live/engine/liveEngine.js). Plan 4
|
|
93
|
-
touches all three call sites.
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## Out of scope for this roadmap
|
|
98
|
-
|
|
99
|
-
- Natural-language → signal compiler (separate spec; depends on Plan 2 + a strategy schema).
|
|
100
|
-
- New broker adapters / options / fundamentals data sources.
|
|
101
|
-
- L2 microstructure simulator.
|