tradelab 1.2.0 → 1.2.1

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 CHANGED
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.2.1] - 2026-06-27
9
+
10
+ ### Fixed
11
+
12
+ - `backtestPortfolio()` now reports aggregate `metrics.finalEquity` correctly when `collectEqSeries: false`.
13
+ - `PaperEngine` now rejects market orders that have no price reference instead of filling them at zero.
14
+ - `TradingSession` now clears staged brackets when an async entry is rejected or canceled, and staged brackets are matched by order id/client order id instead of a loose string check.
15
+ - MCP `attach_strategy` now evaluates built-in strategies with the normal backtest signal context and auto-places returned order intents from `feed_price`.
16
+ - `research.monteCarlo()` now rejects non-positive iteration counts instead of returning empty bands and `NaN` probability fields.
17
+ - Public live types now include `TradingSessionOptions.confirmLive` and optional dashboard `source.refresh()`.
18
+
8
19
  ## [1.2.0] - 2026-06-26
9
20
 
10
21
  ### Added
package/README.md CHANGED
@@ -1,4 +1,17 @@
1
- # tradelab
1
+ <div align="center">
2
+ <img src="https://i.imgur.com/HGvvQbq.png" width="420" alt="tradelab logo" />
3
+
4
+ <p><strong>A Node.js backtesting toolkit for serious trading strategy research.</strong></p>
5
+
6
+ [![npm version](https://img.shields.io/npm/v/tradelab?color=0f172a&label=npm&logo=npm)](https://www.npmjs.com/package/tradelab)
7
+ [![GitHub](https://img.shields.io/badge/github-ishsharm0/tradelab-0f172a?logo=github)](https://github.com/ishsharm0/tradelab)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-0f172a)](https://github.com/ishsharm0/tradelab/blob/main/LICENSE)
9
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-0f172a?logo=node.js)](https://nodejs.org)
10
+ [![TypeScript](https://img.shields.io/badge/TypeScript-ready-0f172a?logo=typescript)](https://github.com/ishsharm0/tradelab/blob/main/types/index.d.ts)
11
+
12
+ </div>
13
+
14
+ ---
2
15
 
3
16
  A Node.js toolkit for testing, validating, and operating trading strategies.
4
17
 
@@ -3329,10 +3329,11 @@ function backtestPortfolio({
3329
3329
  const allCandles = systems.flatMap((system) => system.candles || []);
3330
3330
  const orderedCandles = [...allCandles].sort((left, right) => left.time - right.time);
3331
3331
  const metricsInterval = interval ?? systems[0]?.interval;
3332
+ const finalState = portfolioState(runners, equity);
3332
3333
  const metrics = buildMetrics({
3333
3334
  closed: trades,
3334
3335
  equityStart: equity,
3335
- equityFinal: eqSeries.length ? eqSeries[eqSeries.length - 1].equity : equity,
3336
+ equityFinal: eqSeries.length ? eqSeries[eqSeries.length - 1].equity : finalState.markedEquity,
3336
3337
  candles: orderedCandles,
3337
3338
  estBarMs: estimateBarMs(orderedCandles),
3338
3339
  eqSeries,
@@ -3967,13 +3968,17 @@ function monteCarlo({
3967
3968
  if (!Array.isArray(tradePnls) || tradePnls.length === 0) {
3968
3969
  throw new Error("monteCarlo() requires a non-empty tradePnls array");
3969
3970
  }
3971
+ const runCount = Math.floor(Number(iterations));
3972
+ if (!Number.isFinite(runCount) || runCount < 1) {
3973
+ throw new Error("monteCarlo() requires positive iterations");
3974
+ }
3970
3975
  const rng = makeRng(seed);
3971
3976
  const n = tradePnls.length;
3972
3977
  const block = Math.max(1, Math.floor(blockSize));
3973
3978
  const finals = [];
3974
3979
  const drawdowns = [];
3975
3980
  const pathSamples = Array.from({ length: n + 1 }, () => []);
3976
- for (let it = 0; it < iterations; it += 1) {
3981
+ for (let it = 0; it < runCount; it += 1) {
3977
3982
  const path7 = [equityStart];
3978
3983
  let equity = equityStart;
3979
3984
  let filled = 0;
@@ -4005,7 +4010,7 @@ function monteCarlo({
4005
4010
  p95: percentile2(sorted, 0.95)
4006
4011
  });
4007
4012
  return {
4008
- iterations,
4013
+ iterations: runCount,
4009
4014
  blockSize: block,
4010
4015
  finalEquity: bands(sortedFinals),
4011
4016
  maxDrawdown: bands(sortedDd),
package/dist/cjs/live.cjs CHANGED
@@ -2301,15 +2301,20 @@ var PaperEngine = class extends BrokerAdapter {
2301
2301
  _recordOrder(order) {
2302
2302
  this.orderHistory.set(order.orderId, { ...order });
2303
2303
  }
2304
+ _rejectOrder(order, reason) {
2305
+ order.status = "rejected";
2306
+ order.rejectReason = reason;
2307
+ this._recordOrder(order);
2308
+ this.openOrders.delete(order.orderId);
2309
+ const receipt = cloneOrder(order);
2310
+ this.emit("order:rejected", receipt);
2311
+ return receipt;
2312
+ }
2304
2313
  _fillOrder(order, fillPrice, kind = "market", fillTime = Date.now()) {
2305
2314
  const side = normalizeOrderSide(order.side);
2306
2315
  const qty = Math.max(0, asNumber(order.qty, 0));
2307
2316
  if (!(qty > 0)) {
2308
- order.status = "rejected";
2309
- order.rejectReason = "invalid quantity";
2310
- this._recordOrder(order);
2311
- this.emit("order:rejected", cloneOrder(order));
2312
- return cloneOrder(order);
2317
+ return this._rejectOrder(order, "invalid quantity");
2313
2318
  }
2314
2319
  const sideForFill = side === "buy" ? "long" : "short";
2315
2320
  const filled = applyFill(fillPrice, sideForFill, {
@@ -2417,17 +2422,16 @@ var PaperEngine = class extends BrokerAdapter {
2417
2422
  rejectReason: void 0
2418
2423
  };
2419
2424
  if (!(normalized.qty > 0)) {
2420
- normalized.status = "rejected";
2421
- normalized.rejectReason = "invalid quantity";
2422
- this._recordOrder(normalized);
2423
- this.emit("order:rejected", cloneOrder(normalized));
2424
- return cloneOrder(normalized);
2425
+ return this._rejectOrder(normalized, "invalid quantity");
2425
2426
  }
2426
2427
  this._recordOrder(normalized);
2427
2428
  this.emit("order:submitted", cloneOrder(normalized));
2428
2429
  if (normalized.type === "market") {
2429
2430
  const mark = this.lastPrices.get(normalized.symbol);
2430
- const fillPrice = mark ?? normalized.limitPrice ?? normalized.stopPrice ?? 0;
2431
+ const fillPrice = mark ?? normalized.limitPrice ?? normalized.stopPrice;
2432
+ if (!Number.isFinite(fillPrice)) {
2433
+ return this._rejectOrder(normalized, "no price available for market order");
2434
+ }
2431
2435
  return this._fillOrder(normalized, fillPrice, "market");
2432
2436
  }
2433
2437
  this.openOrders.set(normalized.orderId, normalized);
@@ -3480,6 +3484,14 @@ function oppositeSide2(side) {
3480
3484
  function toBrokerSide(side) {
3481
3485
  return side === "long" || side === "buy" ? "buy" : "sell";
3482
3486
  }
3487
+ function matchesOrderRef(reference, order) {
3488
+ if (!reference || !order) return false;
3489
+ if (reference.orderId && order.orderId && reference.orderId === order.orderId) return true;
3490
+ if (reference.clientOrderId && order.clientOrderId && reference.clientOrderId === order.clientOrderId) {
3491
+ return true;
3492
+ }
3493
+ return false;
3494
+ }
3483
3495
  var TradingSession = class _TradingSession {
3484
3496
  constructor({
3485
3497
  id,
@@ -3541,13 +3553,26 @@ var TradingSession = class _TradingSession {
3541
3553
  _wireBrokerEvents() {
3542
3554
  this.broker.on?.("order:filled", (order) => this._onBrokerFillSync(order));
3543
3555
  this.broker.on?.("order:submitted", (order) => this._record("order:submitted", order));
3544
- this.broker.on?.("order:canceled", (order) => this._record("order:canceled", order));
3556
+ this.broker.on?.(
3557
+ "order:canceled",
3558
+ (order) => this._onBrokerTerminalOrderSync("order:canceled", order)
3559
+ );
3560
+ this.broker.on?.(
3561
+ "order:rejected",
3562
+ (order) => this._onBrokerTerminalOrderSync("order:rejected", order)
3563
+ );
3545
3564
  this.broker.on?.("equity:update", (acct) => this._record("equity:update", acct));
3546
3565
  }
3566
+ _onBrokerTerminalOrderSync(event, order) {
3567
+ this._record(event, order);
3568
+ if (matchesOrderRef(this._pendingBracket, order)) {
3569
+ this._pendingBracket = null;
3570
+ }
3571
+ }
3547
3572
  // Sync event handler — fire-and-forget async OCO work via a stored promise
3548
3573
  _onBrokerFillSync(order) {
3549
3574
  this._record("order:filled", order);
3550
- if (this._pendingBracket && String(order.clientOrderId || "").includes("-entry-")) {
3575
+ if (matchesOrderRef(this._pendingBracket, order)) {
3551
3576
  const staged = this._pendingBracket;
3552
3577
  this._pendingBracket = null;
3553
3578
  this._pendingCancelPromise = Promise.resolve(
@@ -3622,19 +3647,31 @@ var TradingSession = class _TradingSession {
3622
3647
  }
3623
3648
  size = roundStep(size, this.qtyStep);
3624
3649
  if (!(size >= this.minQty)) throw new Error(`sized below minQty (${size})`);
3650
+ const entryClientOrderId = `${this.id}-entry-${Date.now()}`;
3625
3651
  const receipt = await this.broker.submitOrder({
3626
3652
  symbol: this.symbol,
3627
3653
  side: toBrokerSide(side),
3628
3654
  type,
3629
3655
  qty: size,
3630
3656
  limitPrice: type === "limit" ? limitPrice : void 0,
3631
- clientOrderId: `${this.id}-entry-${Date.now()}`
3657
+ clientOrderId: entryClientOrderId
3632
3658
  });
3633
3659
  if (Number.isFinite(stop) || Number.isFinite(target) || Number.isFinite(rr)) {
3634
3660
  if (receipt.status === "filled") {
3635
3661
  await this._attachBracket({ side, size, stop, target, rr, entryRef, receipt });
3662
+ } else if (receipt.status !== "rejected") {
3663
+ this._pendingBracket = {
3664
+ side,
3665
+ size,
3666
+ stop,
3667
+ target,
3668
+ rr,
3669
+ entryRef,
3670
+ orderId: receipt.orderId,
3671
+ clientOrderId: receipt.clientOrderId || entryClientOrderId
3672
+ };
3636
3673
  } else {
3637
- this._pendingBracket = { side, size, stop, target, rr, entryRef };
3674
+ this._pendingBracket = null;
3638
3675
  }
3639
3676
  }
3640
3677
  await this.refresh();
@@ -49,7 +49,7 @@ console.log(engine.getStatus());
49
49
  await engine.stop();
50
50
  ```
51
51
 
52
- `PaperEngine` implements the broker interface in memory. Use it first for CLI runs, dashboard checks, and strategy wiring.
52
+ `PaperEngine` implements the broker interface in memory. Use it first for CLI runs, dashboard checks, and strategy wiring. Market orders need a price reference, so call `pushBar()`, `simulateBar()`, or the MCP `feed_price` tool before submitting one.
53
53
 
54
54
  ## Signal Contract
55
55
 
package/docs/mcp.md CHANGED
@@ -129,7 +129,7 @@ A typical autonomous paper-trading loop:
129
129
  4. Call `session_status` any time for a snapshot of positions, orders, equity, and risk state.
130
130
  5. Call `flatten` or `halt_all` to emergency-close everything.
131
131
 
132
- If you attach a strategy with `attach_strategy`, `feed_price` will auto-evaluate it each bar and place orders when the session is flat.
132
+ If you attach a strategy with `attach_strategy`, `feed_price` will auto-evaluate it each bar and place orders when the session is flat. Attached strategies receive the same `{ candles, index, bar, equity, openPosition, pendingOrder }` context as `backtest()`, and returned order intents default to a market order unless `type` is set.
133
133
 
134
134
  ## Typical Research Flow
135
135
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tradelab",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Backtesting toolkit for Node.js with strategy simulation, historical data loading, and report generation",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.cjs",
@@ -309,10 +309,11 @@ export function backtestPortfolio({
309
309
  const allCandles = systems.flatMap((system) => system.candles || []);
310
310
  const orderedCandles = [...allCandles].sort((left, right) => left.time - right.time);
311
311
  const metricsInterval = interval ?? systems[0]?.interval;
312
+ const finalState = portfolioState(runners, equity);
312
313
  const metrics = buildMetrics({
313
314
  closed: trades,
314
315
  equityStart: equity,
315
- equityFinal: eqSeries.length ? eqSeries[eqSeries.length - 1].equity : equity,
316
+ equityFinal: eqSeries.length ? eqSeries[eqSeries.length - 1].equity : finalState.markedEquity,
316
317
  candles: orderedCandles,
317
318
  estBarMs: estimateBarMs(orderedCandles),
318
319
  eqSeries,
@@ -216,15 +216,21 @@ export class PaperEngine extends BrokerAdapter {
216
216
  this.orderHistory.set(order.orderId, { ...order });
217
217
  }
218
218
 
219
+ _rejectOrder(order, reason) {
220
+ order.status = "rejected";
221
+ order.rejectReason = reason;
222
+ this._recordOrder(order);
223
+ this.openOrders.delete(order.orderId);
224
+ const receipt = cloneOrder(order);
225
+ this.emit("order:rejected", receipt);
226
+ return receipt;
227
+ }
228
+
219
229
  _fillOrder(order, fillPrice, kind = "market", fillTime = Date.now()) {
220
230
  const side = normalizeOrderSide(order.side);
221
231
  const qty = Math.max(0, asNumber(order.qty, 0));
222
232
  if (!(qty > 0)) {
223
- order.status = "rejected";
224
- order.rejectReason = "invalid quantity";
225
- this._recordOrder(order);
226
- this.emit("order:rejected", cloneOrder(order));
227
- return cloneOrder(order);
233
+ return this._rejectOrder(order, "invalid quantity");
228
234
  }
229
235
 
230
236
  const sideForFill = side === "buy" ? "long" : "short";
@@ -346,11 +352,7 @@ export class PaperEngine extends BrokerAdapter {
346
352
  };
347
353
 
348
354
  if (!(normalized.qty > 0)) {
349
- normalized.status = "rejected";
350
- normalized.rejectReason = "invalid quantity";
351
- this._recordOrder(normalized);
352
- this.emit("order:rejected", cloneOrder(normalized));
353
- return cloneOrder(normalized);
355
+ return this._rejectOrder(normalized, "invalid quantity");
354
356
  }
355
357
 
356
358
  this._recordOrder(normalized);
@@ -358,7 +360,10 @@ export class PaperEngine extends BrokerAdapter {
358
360
 
359
361
  if (normalized.type === "market") {
360
362
  const mark = this.lastPrices.get(normalized.symbol);
361
- const fillPrice = mark ?? normalized.limitPrice ?? normalized.stopPrice ?? 0;
363
+ const fillPrice = mark ?? normalized.limitPrice ?? normalized.stopPrice;
364
+ if (!Number.isFinite(fillPrice)) {
365
+ return this._rejectOrder(normalized, "no price available for market order");
366
+ }
362
367
  return this._fillOrder(normalized, fillPrice, "market");
363
368
  }
364
369
 
@@ -12,6 +12,19 @@ function toBrokerSide(side) {
12
12
  return side === "long" || side === "buy" ? "buy" : "sell";
13
13
  }
14
14
 
15
+ function matchesOrderRef(reference, order) {
16
+ if (!reference || !order) return false;
17
+ if (reference.orderId && order.orderId && reference.orderId === order.orderId) return true;
18
+ if (
19
+ reference.clientOrderId &&
20
+ order.clientOrderId &&
21
+ reference.clientOrderId === order.clientOrderId
22
+ ) {
23
+ return true;
24
+ }
25
+ return false;
26
+ }
27
+
15
28
  export class TradingSession {
16
29
  constructor({
17
30
  id,
@@ -79,16 +92,28 @@ export class TradingSession {
79
92
  // Forward broker fills/cancels onto the session bus, and run OCO bracket logic.
80
93
  this.broker.on?.("order:filled", (order) => this._onBrokerFillSync(order));
81
94
  this.broker.on?.("order:submitted", (order) => this._record("order:submitted", order));
82
- this.broker.on?.("order:canceled", (order) => this._record("order:canceled", order));
95
+ this.broker.on?.("order:canceled", (order) =>
96
+ this._onBrokerTerminalOrderSync("order:canceled", order)
97
+ );
98
+ this.broker.on?.("order:rejected", (order) =>
99
+ this._onBrokerTerminalOrderSync("order:rejected", order)
100
+ );
83
101
  this.broker.on?.("equity:update", (acct) => this._record("equity:update", acct));
84
102
  }
85
103
 
104
+ _onBrokerTerminalOrderSync(event, order) {
105
+ this._record(event, order);
106
+ if (matchesOrderRef(this._pendingBracket, order)) {
107
+ this._pendingBracket = null;
108
+ }
109
+ }
110
+
86
111
  // Sync event handler — fire-and-forget async OCO work via a stored promise
87
112
  _onBrokerFillSync(order) {
88
113
  this._record("order:filled", order);
89
114
 
90
115
  // Resting entry order (e.g. a limit) just filled — attach its staged bracket.
91
- if (this._pendingBracket && String(order.clientOrderId || "").includes("-entry-")) {
116
+ if (matchesOrderRef(this._pendingBracket, order)) {
92
117
  const staged = this._pendingBracket;
93
118
  this._pendingBracket = null;
94
119
  // simulateBar may still be iterating orders, so schedule attach without awaiting.
@@ -175,21 +200,33 @@ export class TradingSession {
175
200
  size = roundStep(size, this.qtyStep);
176
201
  if (!(size >= this.minQty)) throw new Error(`sized below minQty (${size})`);
177
202
 
203
+ const entryClientOrderId = `${this.id}-entry-${Date.now()}`;
178
204
  const receipt = await this.broker.submitOrder({
179
205
  symbol: this.symbol,
180
206
  side: toBrokerSide(side),
181
207
  type,
182
208
  qty: size,
183
209
  limitPrice: type === "limit" ? limitPrice : undefined,
184
- clientOrderId: `${this.id}-entry-${Date.now()}`,
210
+ clientOrderId: entryClientOrderId,
185
211
  });
186
212
 
187
213
  // Stage bracket if needed — market orders fill synchronously in PaperEngine
188
214
  if (Number.isFinite(stop) || Number.isFinite(target) || Number.isFinite(rr)) {
189
215
  if (receipt.status === "filled") {
190
216
  await this._attachBracket({ side, size, stop, target, rr, entryRef, receipt });
217
+ } else if (receipt.status !== "rejected") {
218
+ this._pendingBracket = {
219
+ side,
220
+ size,
221
+ stop,
222
+ target,
223
+ rr,
224
+ entryRef,
225
+ orderId: receipt.orderId,
226
+ clientOrderId: receipt.clientOrderId || entryClientOrderId,
227
+ };
191
228
  } else {
192
- this._pendingBracket = { side, size, stop, target, rr, entryRef };
229
+ this._pendingBracket = null;
193
230
  }
194
231
  }
195
232
 
@@ -10,6 +10,33 @@ function requireSession(sessionId) {
10
10
  return s;
11
11
  }
12
12
 
13
+ function strategyContext(session) {
14
+ const candles = session.candleBuffer;
15
+ const bar = candles[candles.length - 1] ?? null;
16
+ const status = session.getStatus();
17
+ return {
18
+ candles,
19
+ index: candles.length - 1,
20
+ bar,
21
+ equity: status.equity,
22
+ openPosition: status.positions[0] ?? null,
23
+ pendingOrder: null,
24
+ };
25
+ }
26
+
27
+ function signalToOrder(signal) {
28
+ return {
29
+ side: signal.side ?? signal.direction ?? signal.action,
30
+ type: signal.type ?? "market",
31
+ qty: signal.qty ?? signal.size,
32
+ riskPct: signal.riskPct,
33
+ stop: signal.stop ?? signal.stopLoss ?? signal.sl,
34
+ target: signal.target ?? signal.takeProfit ?? signal.tp,
35
+ rr: signal.rr ?? signal._rr,
36
+ limitPrice: signal.limitPrice ?? signal.limit ?? signal.entry ?? signal.price,
37
+ };
38
+ }
39
+
13
40
  export { manager as sessionManager };
14
41
 
15
42
  export const liveTools = {
@@ -71,9 +98,9 @@ export const liveTools = {
71
98
  // If a strategy is attached and session is flat, evaluate it
72
99
  if (session._strategy && session.getStatus().positions.length === 0) {
73
100
  try {
74
- const signal = session._strategy(session.candleBuffer, session.getStatus());
75
- if (signal && signal.side && signal.type) {
76
- await session.placeOrder(signal).catch(() => {});
101
+ const signal = session._strategy(strategyContext(session));
102
+ if (signal && (signal.side || signal.direction || signal.action)) {
103
+ await session.placeOrder(signalToOrder(signal)).catch(() => {});
77
104
  }
78
105
  } catch {
79
106
  // strategy errors are non-fatal
@@ -160,11 +187,7 @@ export const liveTools = {
160
187
  const session = requireSession(sessionId);
161
188
  const factory = getStrategy(strategy);
162
189
  const signal = factory(params);
163
- // Wrap: accept (candleBuffer, status) and call signal with the buffer
164
- session._strategy = (candleBuffer) => {
165
- if (!candleBuffer || candleBuffer.length === 0) return null;
166
- return signal(candleBuffer[candleBuffer.length - 1], candleBuffer);
167
- };
190
+ session._strategy = signal;
168
191
  return { ok: true, strategy, params };
169
192
  },
170
193
  },
@@ -35,6 +35,10 @@ export function monteCarlo({
35
35
  if (!Array.isArray(tradePnls) || tradePnls.length === 0) {
36
36
  throw new Error("monteCarlo() requires a non-empty tradePnls array");
37
37
  }
38
+ const runCount = Math.floor(Number(iterations));
39
+ if (!Number.isFinite(runCount) || runCount < 1) {
40
+ throw new Error("monteCarlo() requires positive iterations");
41
+ }
38
42
  const rng = makeRng(seed);
39
43
  const n = tradePnls.length;
40
44
  const block = Math.max(1, Math.floor(blockSize));
@@ -43,7 +47,7 @@ export function monteCarlo({
43
47
  const drawdowns = [];
44
48
  const pathSamples = Array.from({ length: n + 1 }, () => []);
45
49
 
46
- for (let it = 0; it < iterations; it += 1) {
50
+ for (let it = 0; it < runCount; it += 1) {
47
51
  const path = [equityStart];
48
52
  let equity = equityStart;
49
53
  let filled = 0;
@@ -78,7 +82,7 @@ export function monteCarlo({
78
82
  });
79
83
 
80
84
  return {
81
- iterations,
85
+ iterations: runCount,
82
86
  blockSize: block,
83
87
  finalEquity: bands(sortedFinals),
84
88
  maxDrawdown: bands(sortedDd),
package/types/live.d.ts CHANGED
@@ -341,7 +341,8 @@ export interface DashboardServer {
341
341
  export function createDashboardServer(options: {
342
342
  source: {
343
343
  eventBus: EventBus;
344
- getStatus?: () => Record<string, unknown>;
344
+ getStatus?: () => unknown;
345
+ refresh?: () => Promise<unknown>;
345
346
  };
346
347
  port?: number;
347
348
  maxBuffer?: number;
@@ -414,6 +415,7 @@ export interface TradingSessionOptions {
414
415
  minQty?: number;
415
416
  maxLeverage?: number;
416
417
  eventBus?: EventBus;
418
+ confirmLive?: boolean;
417
419
  }
418
420
 
419
421
  export interface SessionPlaceOrderOptions {