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/dist/cjs/ta.cjs
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/ta/index.js
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
atr: () => atr,
|
|
24
|
+
bollinger: () => bollinger,
|
|
25
|
+
detectFVG: () => detectFVG,
|
|
26
|
+
donchian: () => donchian,
|
|
27
|
+
ema: () => ema,
|
|
28
|
+
keltner: () => keltner,
|
|
29
|
+
lastSwing: () => lastSwing,
|
|
30
|
+
macd: () => macd,
|
|
31
|
+
rsi: () => rsi,
|
|
32
|
+
stochastic: () => stochastic,
|
|
33
|
+
structureState: () => structureState,
|
|
34
|
+
supertrend: () => supertrend,
|
|
35
|
+
swingHigh: () => swingHigh,
|
|
36
|
+
swingLow: () => swingLow,
|
|
37
|
+
vwap: () => vwap
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/utils/indicators.js
|
|
42
|
+
function ema(values, period = 14) {
|
|
43
|
+
if (!values?.length) return [];
|
|
44
|
+
const lookback = Math.max(1, period | 0);
|
|
45
|
+
const output = new Array(values.length);
|
|
46
|
+
let warmupSum = 0;
|
|
47
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
48
|
+
const value = values[index];
|
|
49
|
+
if (!Number.isFinite(value)) {
|
|
50
|
+
output[index] = index === 0 ? 0 : output[index - 1];
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (index < lookback) {
|
|
54
|
+
warmupSum += value;
|
|
55
|
+
output[index] = index === lookback - 1 ? warmupSum / lookback : value;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const smoothing = 2 / (lookback + 1);
|
|
59
|
+
output[index] = value * smoothing + output[index - 1] * (1 - smoothing);
|
|
60
|
+
}
|
|
61
|
+
return output;
|
|
62
|
+
}
|
|
63
|
+
function swingHigh(bars, index, left = 2, right = 2) {
|
|
64
|
+
if (index < left || index + right >= bars.length) return false;
|
|
65
|
+
const high = bars[index].high;
|
|
66
|
+
for (let cursor = index - left; cursor <= index + right; cursor += 1) {
|
|
67
|
+
if (cursor !== index && bars[cursor].high >= high) return false;
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
function swingLow(bars, index, left = 2, right = 2) {
|
|
72
|
+
if (index < left || index + right >= bars.length) return false;
|
|
73
|
+
const low = bars[index].low;
|
|
74
|
+
for (let cursor = index - left; cursor <= index + right; cursor += 1) {
|
|
75
|
+
if (cursor !== index && bars[cursor].low <= low) return false;
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
function detectFVG(bars, index) {
|
|
80
|
+
if (index < 2) return null;
|
|
81
|
+
const first = bars[index - 2];
|
|
82
|
+
const third = bars[index];
|
|
83
|
+
if (first.high < third.low) {
|
|
84
|
+
return {
|
|
85
|
+
type: "bull",
|
|
86
|
+
top: first.high,
|
|
87
|
+
bottom: third.low,
|
|
88
|
+
mid: (first.high + third.low) / 2
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (first.low > third.high) {
|
|
92
|
+
return {
|
|
93
|
+
type: "bear",
|
|
94
|
+
top: third.high,
|
|
95
|
+
bottom: first.low,
|
|
96
|
+
mid: (third.high + first.low) / 2
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
function lastSwing(bars, index, direction) {
|
|
102
|
+
for (let cursor = index - 1; cursor >= 0; cursor -= 1) {
|
|
103
|
+
if (direction === "up" && swingLow(bars, cursor)) {
|
|
104
|
+
return { idx: cursor, price: bars[cursor].low };
|
|
105
|
+
}
|
|
106
|
+
if (direction === "down" && swingHigh(bars, cursor)) {
|
|
107
|
+
return { idx: cursor, price: bars[cursor].high };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
function structureState(bars, index) {
|
|
113
|
+
return {
|
|
114
|
+
lastLow: lastSwing(bars, index, "up"),
|
|
115
|
+
lastHigh: lastSwing(bars, index, "down")
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function atr(bars, period = 14) {
|
|
119
|
+
if (!bars?.length || period <= 0) return [];
|
|
120
|
+
const trueRanges = new Array(bars.length);
|
|
121
|
+
for (let index = 0; index < bars.length; index += 1) {
|
|
122
|
+
if (index === 0) {
|
|
123
|
+
trueRanges[index] = bars[index].high - bars[index].low;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const high = bars[index].high;
|
|
127
|
+
const low = bars[index].low;
|
|
128
|
+
const previousClose = bars[index - 1].close;
|
|
129
|
+
trueRanges[index] = Math.max(
|
|
130
|
+
high - low,
|
|
131
|
+
Math.abs(high - previousClose),
|
|
132
|
+
Math.abs(low - previousClose)
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
const output = new Array(trueRanges.length);
|
|
136
|
+
let previousAtr;
|
|
137
|
+
for (let index = 0; index < trueRanges.length; index += 1) {
|
|
138
|
+
if (index < period) {
|
|
139
|
+
output[index] = void 0;
|
|
140
|
+
if (index === period - 1) {
|
|
141
|
+
let seed = 0;
|
|
142
|
+
for (let cursor = 0; cursor < period; cursor += 1) {
|
|
143
|
+
seed += trueRanges[cursor];
|
|
144
|
+
}
|
|
145
|
+
previousAtr = seed / period;
|
|
146
|
+
output[index] = previousAtr;
|
|
147
|
+
}
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
previousAtr = (previousAtr * (period - 1) + trueRanges[index]) / period;
|
|
151
|
+
output[index] = previousAtr;
|
|
152
|
+
}
|
|
153
|
+
return output;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/ta/oscillators.js
|
|
157
|
+
function rsi(closes, period = 14) {
|
|
158
|
+
const out = new Array(closes.length).fill(void 0);
|
|
159
|
+
if (closes.length <= period) return out;
|
|
160
|
+
let gainSum = 0;
|
|
161
|
+
let lossSum = 0;
|
|
162
|
+
for (let i = 1; i <= period; i += 1) {
|
|
163
|
+
const change = closes[i] - closes[i - 1];
|
|
164
|
+
if (change >= 0) gainSum += change;
|
|
165
|
+
else lossSum -= change;
|
|
166
|
+
}
|
|
167
|
+
let avgGain = gainSum / period;
|
|
168
|
+
let avgLoss = lossSum / period;
|
|
169
|
+
out[period] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
|
|
170
|
+
for (let i = period + 1; i < closes.length; i += 1) {
|
|
171
|
+
const change = closes[i] - closes[i - 1];
|
|
172
|
+
const gain = change > 0 ? change : 0;
|
|
173
|
+
const loss = change < 0 ? -change : 0;
|
|
174
|
+
avgGain = (avgGain * (period - 1) + gain) / period;
|
|
175
|
+
avgLoss = (avgLoss * (period - 1) + loss) / period;
|
|
176
|
+
out[i] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
|
|
177
|
+
}
|
|
178
|
+
return out;
|
|
179
|
+
}
|
|
180
|
+
function macd(closes, fast = 12, slow = 26, signalPeriod = 9) {
|
|
181
|
+
const emaFast = ema(closes, fast);
|
|
182
|
+
const emaSlow = ema(closes, slow);
|
|
183
|
+
const macdLine = closes.map((_, i) => emaFast[i] - emaSlow[i]);
|
|
184
|
+
const signalLine = ema(macdLine, signalPeriod);
|
|
185
|
+
const histogram = macdLine.map((v, i) => v - signalLine[i]);
|
|
186
|
+
return { macd: macdLine, signal: signalLine, histogram };
|
|
187
|
+
}
|
|
188
|
+
function stochastic(bars, kPeriod = 14, dPeriod = 3) {
|
|
189
|
+
const k = new Array(bars.length).fill(void 0);
|
|
190
|
+
for (let i = kPeriod - 1; i < bars.length; i += 1) {
|
|
191
|
+
let hh = -Infinity;
|
|
192
|
+
let ll = Infinity;
|
|
193
|
+
for (let j = i - kPeriod + 1; j <= i; j += 1) {
|
|
194
|
+
if (bars[j].high > hh) hh = bars[j].high;
|
|
195
|
+
if (bars[j].low < ll) ll = bars[j].low;
|
|
196
|
+
}
|
|
197
|
+
const range = hh - ll;
|
|
198
|
+
k[i] = range === 0 ? 0 : (bars[i].close - ll) / range * 100;
|
|
199
|
+
}
|
|
200
|
+
const d = new Array(bars.length).fill(void 0);
|
|
201
|
+
for (let i = 0; i < bars.length; i += 1) {
|
|
202
|
+
if (i < kPeriod - 1 + dPeriod - 1) continue;
|
|
203
|
+
let sum = 0;
|
|
204
|
+
for (let j = i - dPeriod + 1; j <= i; j += 1) sum += k[j];
|
|
205
|
+
d[i] = sum / dPeriod;
|
|
206
|
+
}
|
|
207
|
+
return { k, d };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/ta/channels.js
|
|
211
|
+
function rollingMean(values, period, i) {
|
|
212
|
+
let sum = 0;
|
|
213
|
+
for (let j = i - period + 1; j <= i; j += 1) sum += values[j];
|
|
214
|
+
return sum / period;
|
|
215
|
+
}
|
|
216
|
+
function bollinger(closes, period = 20, mult = 2) {
|
|
217
|
+
const middle = new Array(closes.length).fill(void 0);
|
|
218
|
+
const upper = new Array(closes.length).fill(void 0);
|
|
219
|
+
const lower = new Array(closes.length).fill(void 0);
|
|
220
|
+
for (let i = period - 1; i < closes.length; i += 1) {
|
|
221
|
+
const avg = rollingMean(closes, period, i);
|
|
222
|
+
let variance = 0;
|
|
223
|
+
for (let j = i - period + 1; j <= i; j += 1) variance += (closes[j] - avg) ** 2;
|
|
224
|
+
const sd = Math.sqrt(variance / period);
|
|
225
|
+
middle[i] = avg;
|
|
226
|
+
upper[i] = avg + mult * sd;
|
|
227
|
+
lower[i] = avg - mult * sd;
|
|
228
|
+
}
|
|
229
|
+
return { middle, upper, lower };
|
|
230
|
+
}
|
|
231
|
+
function donchian(bars, period = 20) {
|
|
232
|
+
const upper = new Array(bars.length).fill(void 0);
|
|
233
|
+
const lower = new Array(bars.length).fill(void 0);
|
|
234
|
+
const middle = new Array(bars.length).fill(void 0);
|
|
235
|
+
for (let i = period - 1; i < bars.length; i += 1) {
|
|
236
|
+
let hh = -Infinity;
|
|
237
|
+
let ll = Infinity;
|
|
238
|
+
for (let j = i - period + 1; j <= i; j += 1) {
|
|
239
|
+
if (bars[j].high > hh) hh = bars[j].high;
|
|
240
|
+
if (bars[j].low < ll) ll = bars[j].low;
|
|
241
|
+
}
|
|
242
|
+
upper[i] = hh;
|
|
243
|
+
lower[i] = ll;
|
|
244
|
+
middle[i] = (hh + ll) / 2;
|
|
245
|
+
}
|
|
246
|
+
return { upper, lower, middle };
|
|
247
|
+
}
|
|
248
|
+
function keltner(bars, emaPeriod = 20, atrPeriod = 14, mult = 2) {
|
|
249
|
+
const closes = bars.map((b) => b.close);
|
|
250
|
+
const mid = ema(closes, emaPeriod);
|
|
251
|
+
const range = atr(bars, atrPeriod);
|
|
252
|
+
const upper = new Array(bars.length).fill(void 0);
|
|
253
|
+
const lower = new Array(bars.length).fill(void 0);
|
|
254
|
+
const middle = new Array(bars.length).fill(void 0);
|
|
255
|
+
for (let i = 0; i < bars.length; i += 1) {
|
|
256
|
+
if (range[i] === void 0) continue;
|
|
257
|
+
middle[i] = mid[i];
|
|
258
|
+
upper[i] = mid[i] + mult * range[i];
|
|
259
|
+
lower[i] = mid[i] - mult * range[i];
|
|
260
|
+
}
|
|
261
|
+
return { upper, lower, middle };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/ta/trend.js
|
|
265
|
+
function supertrend(bars, period = 10, mult = 3) {
|
|
266
|
+
const range = atr(bars, period);
|
|
267
|
+
const line = new Array(bars.length).fill(void 0);
|
|
268
|
+
const direction = new Array(bars.length).fill(void 0);
|
|
269
|
+
let prevUpper = Infinity;
|
|
270
|
+
let prevLower = -Infinity;
|
|
271
|
+
let prevDir = 1;
|
|
272
|
+
for (let i = 0; i < bars.length; i += 1) {
|
|
273
|
+
if (range[i] === void 0) continue;
|
|
274
|
+
const mid = (bars[i].high + bars[i].low) / 2;
|
|
275
|
+
const basicUpper = mid + mult * range[i];
|
|
276
|
+
const basicLower = mid - mult * range[i];
|
|
277
|
+
const close = bars[i].close;
|
|
278
|
+
const prevClose = i > 0 ? bars[i - 1].close : close;
|
|
279
|
+
const upper = basicUpper < prevUpper || prevClose > prevUpper ? basicUpper : prevUpper;
|
|
280
|
+
const lower = basicLower > prevLower || prevClose < prevLower ? basicLower : prevLower;
|
|
281
|
+
let dir = prevDir;
|
|
282
|
+
if (prevDir === 1 && close < lower) dir = -1;
|
|
283
|
+
else if (prevDir === -1 && close > upper) dir = 1;
|
|
284
|
+
line[i] = dir === 1 ? lower : upper;
|
|
285
|
+
direction[i] = dir;
|
|
286
|
+
prevUpper = upper;
|
|
287
|
+
prevLower = lower;
|
|
288
|
+
prevDir = dir;
|
|
289
|
+
}
|
|
290
|
+
return { line, direction };
|
|
291
|
+
}
|
|
292
|
+
function dayKeyUTC(timeMs) {
|
|
293
|
+
const d = new Date(timeMs);
|
|
294
|
+
return d.getUTCFullYear() * 1e4 + (d.getUTCMonth() + 1) * 100 + d.getUTCDate();
|
|
295
|
+
}
|
|
296
|
+
function vwap(bars) {
|
|
297
|
+
const out = new Array(bars.length).fill(void 0);
|
|
298
|
+
let currentDay = null;
|
|
299
|
+
let cumPV = 0;
|
|
300
|
+
let cumV = 0;
|
|
301
|
+
let cumTP = 0;
|
|
302
|
+
let count = 0;
|
|
303
|
+
for (let i = 0; i < bars.length; i += 1) {
|
|
304
|
+
const day = dayKeyUTC(bars[i].time);
|
|
305
|
+
if (day !== currentDay) {
|
|
306
|
+
currentDay = day;
|
|
307
|
+
cumPV = 0;
|
|
308
|
+
cumV = 0;
|
|
309
|
+
cumTP = 0;
|
|
310
|
+
count = 0;
|
|
311
|
+
}
|
|
312
|
+
const tp = (bars[i].high + bars[i].low + bars[i].close) / 3;
|
|
313
|
+
const vol = Number.isFinite(bars[i].volume) ? bars[i].volume : 0;
|
|
314
|
+
cumPV += tp * vol;
|
|
315
|
+
cumV += vol;
|
|
316
|
+
cumTP += tp;
|
|
317
|
+
count += 1;
|
|
318
|
+
out[i] = cumV > 0 ? cumPV / cumV : cumTP / count;
|
|
319
|
+
}
|
|
320
|
+
return out;
|
|
321
|
+
}
|
|
322
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
323
|
+
0 && (module.exports = {
|
|
324
|
+
atr,
|
|
325
|
+
bollinger,
|
|
326
|
+
detectFVG,
|
|
327
|
+
donchian,
|
|
328
|
+
ema,
|
|
329
|
+
keltner,
|
|
330
|
+
lastSwing,
|
|
331
|
+
macd,
|
|
332
|
+
rsi,
|
|
333
|
+
stochastic,
|
|
334
|
+
structureState,
|
|
335
|
+
supertrend,
|
|
336
|
+
swingHigh,
|
|
337
|
+
swingLow,
|
|
338
|
+
vwap
|
|
339
|
+
});
|
package/docs/README.md
CHANGED
|
@@ -1,78 +1,44 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Documentation
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Use this page to choose the right guide. If you are new to tradelab, read the first three links in order.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- [Data, reporting, and CLI](data-reporting-cli.md)
|
|
7
|
-
- [Live trading](live-trading.md)
|
|
8
|
-
- [Strategy examples](examples.md)
|
|
9
|
-
- [API reference](api-reference.md)
|
|
5
|
+
## Start Here
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
1. [Backtesting](backtest-engine.md) - the `signal()` contract, result shape, costs, portfolio runs, walk-forward validation, and parameter sweeps.
|
|
8
|
+
2. [Data, reporting, and CLI](data-reporting-cli.md) - Yahoo data, CSV files, cache helpers, exported reports, and terminal commands.
|
|
9
|
+
3. [Live trading](live-trading.md) - paper mode, broker adapters, persisted state, multi-system orchestration, and the local dashboard.
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
| ------------------------------------------ | ------------------------------------------------- |
|
|
15
|
-
| Run one strategy on one dataset | [Backtest engine](backtest-engine.md) |
|
|
16
|
-
| Load Yahoo or CSV data | [Data, reporting, and CLI](data-reporting-cli.md) |
|
|
17
|
-
| Export reports or machine-readable results | [Data, reporting, and CLI](data-reporting-cli.md) |
|
|
18
|
-
| Run multiple symbols together | [Backtest engine](backtest-engine.md) |
|
|
19
|
-
| Run walk-forward validation | [Backtest engine](backtest-engine.md) |
|
|
20
|
-
| Run one strategy live or in paper mode | [Live trading](live-trading.md) |
|
|
21
|
-
| Run multiple live systems together | [Live trading](live-trading.md) |
|
|
22
|
-
| See complete strategy patterns | [Strategy examples](examples.md) |
|
|
23
|
-
| Check the exact public exports | [API reference](api-reference.md) |
|
|
11
|
+
## Reference
|
|
24
12
|
|
|
25
|
-
|
|
13
|
+
- [API reference](api-reference.md) - public exports by module.
|
|
14
|
+
- [Research tools](research.md) - Monte Carlo, deflated Sharpe, PBO, and CPCV.
|
|
15
|
+
- [MCP server](mcp.md) - `tradelab-mcp` setup and tool list.
|
|
16
|
+
- [Strategy examples](examples.md) - complete strategy patterns you can adapt.
|
|
26
17
|
|
|
27
|
-
|
|
18
|
+
## Common Paths
|
|
28
19
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
20
|
+
| If you want to... | Read |
|
|
21
|
+
| --------------------------------- | --------------------------------------------------------- |
|
|
22
|
+
| Run one strategy on OHLCV candles | [Backtesting](backtest-engine.md) |
|
|
23
|
+
| Load Yahoo or CSV data | [Data, reporting, and CLI](data-reporting-cli.md) |
|
|
24
|
+
| Export HTML, CSV, or JSON | [Data, reporting, and CLI](data-reporting-cli.md) |
|
|
25
|
+
| Combine several systems | [Backtesting](backtest-engine.md#portfolio-backtests) |
|
|
26
|
+
| Test parameter stability | [Backtesting](backtest-engine.md#walk-forward-validation) |
|
|
27
|
+
| Run a local paper session | [Live trading](live-trading.md) |
|
|
28
|
+
| Connect an MCP client | [MCP server](mcp.md) |
|
|
29
|
+
| Check exact function names | [API reference](api-reference.md) |
|
|
35
30
|
|
|
36
|
-
|
|
31
|
+
## Package Scope
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
tradelab is built for strategy research and operational dry-runs:
|
|
39
34
|
|
|
40
|
-
|
|
35
|
+
- candle and tick backtests
|
|
36
|
+
- shared-capital portfolio simulation
|
|
37
|
+
- realistic cost assumptions
|
|
38
|
+
- walk-forward validation and overfitting checks
|
|
39
|
+
- paper and live execution through broker adapters
|
|
40
|
+
- local reports and machine-readable exports
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
It is not an exchange simulator. It does not try to model full market depth, queue priority, latency, or venue-specific microstructure.
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
2. Run `backtest()`
|
|
46
|
-
3. Inspect `result.metrics` and `result.positions`
|
|
47
|
-
4. Export HTML, CSV, or JSON if needed
|
|
48
|
-
|
|
49
|
-
### Multi-symbol workflow
|
|
50
|
-
|
|
51
|
-
1. Prepare one candle array per symbol
|
|
52
|
-
2. Run `backtestPortfolio()`
|
|
53
|
-
3. Review combined `metrics`, `positions`, and `eqSeries`
|
|
54
|
-
|
|
55
|
-
### Validation workflow
|
|
56
|
-
|
|
57
|
-
1. Build a `signalFactory(params)`
|
|
58
|
-
2. Create parameter sets
|
|
59
|
-
3. Run `walkForwardOptimize()`
|
|
60
|
-
4. Review per-window winners before trusting the aggregate result
|
|
61
|
-
|
|
62
|
-
### Live execution workflow
|
|
63
|
-
|
|
64
|
-
1. Build a `signal()` used in backtest first
|
|
65
|
-
2. Wire it into `LiveEngine` with a broker or `PaperEngine`
|
|
66
|
-
3. Persist state with `JsonFileStorage`
|
|
67
|
-
4. Start with `tradelab paper` or `tradelab live --paper`
|
|
68
|
-
5. Inspect persisted state with `tradelab status`
|
|
69
|
-
|
|
70
|
-
## Documentation map
|
|
71
|
-
|
|
72
|
-
- [Backtest engine](backtest-engine.md): strategy inputs, engine options, result shape, portfolio mode, walk-forward mode
|
|
73
|
-
- [Data, reporting, and CLI](data-reporting-cli.md): data loading, cache behavior, exports, terminal usage
|
|
74
|
-
- [Live trading](live-trading.md): live engine, broker adapters, paper mode, orchestration, lifecycle, and state
|
|
75
|
-
- [Strategy examples](examples.md): mean reversion, breakout, sentiment, LLM, and portfolio research patterns
|
|
76
|
-
- [API reference](api-reference.md): compact export index
|
|
77
|
-
|
|
78
|
-
<small>[Back to README.md](../README.md)</small>
|
|
44
|
+
[Back to README](../README.md)
|