backtest-kit 1.11.8 → 1.11.10

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/build/index.cjs CHANGED
@@ -422,6 +422,13 @@ const GLOBAL_CONFIG = {
422
422
  * Default: 10 minutes
423
423
  */
424
424
  CC_ORDER_BOOK_TIME_OFFSET_MINUTES: 10,
425
+ /**
426
+ * Maximum depth levels for order book fetching.
427
+ * Specifies how many price levels to fetch from both bids and asks.
428
+ *
429
+ * Default: 20 levels
430
+ */
431
+ CC_ORDER_BOOK_MAX_DEPTH_LEVELS: 1000,
425
432
  };
426
433
  const DEFAULT_CONFIG = Object.freeze({ ...GLOBAL_CONFIG });
427
434
 
@@ -891,17 +898,23 @@ class ClientExchange {
891
898
  /**
892
899
  * Fetches order book for a trading pair.
893
900
  *
901
+ * Calculates time range based on execution context time (when) and
902
+ * CC_ORDER_BOOK_TIME_OFFSET_MINUTES, then delegates to the exchange
903
+ * schema implementation which may use or ignore the time range.
904
+ *
894
905
  * @param symbol - Trading pair symbol
906
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
895
907
  * @returns Promise resolving to order book data
896
908
  * @throws Error if getOrderBook is not implemented
897
909
  */
898
- async getOrderBook(symbol) {
910
+ async getOrderBook(symbol, depth = GLOBAL_CONFIG.CC_ORDER_BOOK_MAX_DEPTH_LEVELS) {
899
911
  this.params.logger.debug("ClientExchange getOrderBook", {
900
912
  symbol,
913
+ depth,
901
914
  });
902
915
  const to = new Date(this.params.execution.context.when.getTime());
903
916
  const from = new Date(to.getTime() - GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * 60 * 1000);
904
- return await this.params.getOrderBook(symbol, from, to);
917
+ return await this.params.getOrderBook(symbol, depth, from, to);
905
918
  }
906
919
  }
907
920
 
@@ -929,8 +942,13 @@ const DEFAULT_FORMAT_PRICE_FN$1 = async (_symbol, price) => {
929
942
  /**
930
943
  * Default implementation for getOrderBook.
931
944
  * Throws an error indicating the method is not implemented.
945
+ *
946
+ * @param _symbol - Trading pair symbol (unused)
947
+ * @param _depth - Maximum depth levels (unused)
948
+ * @param _from - Start of time range (unused - can be ignored in live implementations)
949
+ * @param _to - End of time range (unused - can be ignored in live implementations)
932
950
  */
933
- const DEFAULT_GET_ORDER_BOOK_FN$1 = async (_symbol, _from, _to) => {
951
+ const DEFAULT_GET_ORDER_BOOK_FN$1 = async (_symbol, _depth, _from, _to) => {
934
952
  throw new Error(`getOrderBook is not implemented for this exchange`);
935
953
  };
936
954
  /**
@@ -1071,15 +1089,19 @@ class ExchangeConnectionService {
1071
1089
  * Fetches order book for a trading pair using configured exchange.
1072
1090
  *
1073
1091
  * Routes to exchange determined by methodContextService.context.exchangeName.
1092
+ * The ClientExchange will calculate time range and pass it to the schema
1093
+ * implementation, which may use (backtest) or ignore (live) the parameters.
1074
1094
  *
1075
1095
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
1096
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
1076
1097
  * @returns Promise resolving to order book data
1077
1098
  */
1078
- this.getOrderBook = async (symbol) => {
1099
+ this.getOrderBook = async (symbol, depth) => {
1079
1100
  this.loggerService.log("exchangeConnectionService getOrderBook", {
1080
1101
  symbol,
1102
+ depth,
1081
1103
  });
1082
- return await this.getExchange(this.methodContextService.context.exchangeName).getOrderBook(symbol);
1104
+ return await this.getExchange(this.methodContextService.context.exchangeName).getOrderBook(symbol, depth);
1083
1105
  };
1084
1106
  }
1085
1107
  }
@@ -2231,6 +2253,48 @@ const toPlainString = (content) => {
2231
2253
  return text.trim();
2232
2254
  };
2233
2255
 
2256
+ /**
2257
+ * Wraps a function to execute it outside of the current execution context if one exists.
2258
+ *
2259
+ * This utility ensures that the wrapped function runs in isolation from any existing
2260
+ * ExecutionContext, preventing context leakage and unintended context sharing between
2261
+ * async operations.
2262
+ *
2263
+ * @template T - Function type with any parameters and return type
2264
+ * @param {T} run - The function to be wrapped and executed outside of context
2265
+ * @returns {Function} A curried function that accepts the original function's parameters
2266
+ * and executes it outside of the current context if one exists
2267
+ *
2268
+ * @example
2269
+ * ```ts
2270
+ * const myFunction = async (param: string) => {
2271
+ * // This code will run outside of any ExecutionContext
2272
+ * return param.toUpperCase();
2273
+ * };
2274
+ *
2275
+ * const wrappedFunction = beginTime(myFunction);
2276
+ * const result = wrappedFunction('hello'); // Returns 'HELLO'
2277
+ * ```
2278
+ *
2279
+ * @example
2280
+ * ```ts
2281
+ * // Usage with trycatch wrapper
2282
+ * const safeFunction = trycatch(
2283
+ * beginTime(async (id: number) => {
2284
+ * // Function body runs isolated from parent context
2285
+ * return await fetchData(id);
2286
+ * })
2287
+ * );
2288
+ * ```
2289
+ */
2290
+ const beginTime = (run) => (...args) => {
2291
+ let fn = () => run(...args);
2292
+ if (ExecutionContextService.hasContext()) {
2293
+ fn = ExecutionContextService.runOutOfContext(fn);
2294
+ }
2295
+ return fn();
2296
+ };
2297
+
2234
2298
  const INTERVAL_MINUTES$3 = {
2235
2299
  "1m": 1,
2236
2300
  "3m": 3,
@@ -3330,7 +3394,7 @@ const ACTIVATE_SCHEDULED_SIGNAL_FN = async (self, scheduled, activationTimestamp
3330
3394
  await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, activationTime, self.params.execution.context.backtest);
3331
3395
  return result;
3332
3396
  };
3333
- const CALL_PING_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, scheduled, timestamp, backtest) => {
3397
+ const CALL_PING_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, scheduled, timestamp, backtest) => {
3334
3398
  await ExecutionContextService.runInContext(async () => {
3335
3399
  const publicSignal = TO_PUBLIC_SIGNAL(scheduled);
3336
3400
  // Call system onPing callback first (emits to pingSubject)
@@ -3344,7 +3408,7 @@ const CALL_PING_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, schedu
3344
3408
  symbol: symbol,
3345
3409
  backtest: backtest,
3346
3410
  });
3347
- }, {
3411
+ }), {
3348
3412
  fallback: (error) => {
3349
3413
  const message = "ClientStrategy CALL_PING_CALLBACKS_FN thrown";
3350
3414
  const payload = {
@@ -3356,7 +3420,7 @@ const CALL_PING_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, schedu
3356
3420
  errorEmitter.next(error);
3357
3421
  },
3358
3422
  });
3359
- const CALL_ACTIVE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3423
+ const CALL_ACTIVE_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3360
3424
  await ExecutionContextService.runInContext(async () => {
3361
3425
  if (self.params.callbacks?.onActive) {
3362
3426
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
@@ -3367,7 +3431,7 @@ const CALL_ACTIVE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, sign
3367
3431
  symbol: symbol,
3368
3432
  backtest: backtest,
3369
3433
  });
3370
- }, {
3434
+ }), {
3371
3435
  fallback: (error) => {
3372
3436
  const message = "ClientStrategy CALL_ACTIVE_CALLBACKS_FN thrown";
3373
3437
  const payload = {
@@ -3379,7 +3443,7 @@ const CALL_ACTIVE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, sign
3379
3443
  errorEmitter.next(error);
3380
3444
  },
3381
3445
  });
3382
- const CALL_SCHEDULE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3446
+ const CALL_SCHEDULE_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3383
3447
  await ExecutionContextService.runInContext(async () => {
3384
3448
  if (self.params.callbacks?.onSchedule) {
3385
3449
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
@@ -3390,7 +3454,7 @@ const CALL_SCHEDULE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, si
3390
3454
  symbol: symbol,
3391
3455
  backtest: backtest,
3392
3456
  });
3393
- }, {
3457
+ }), {
3394
3458
  fallback: (error) => {
3395
3459
  const message = "ClientStrategy CALL_SCHEDULE_CALLBACKS_FN thrown";
3396
3460
  const payload = {
@@ -3402,7 +3466,7 @@ const CALL_SCHEDULE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, si
3402
3466
  errorEmitter.next(error);
3403
3467
  },
3404
3468
  });
3405
- const CALL_CANCEL_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3469
+ const CALL_CANCEL_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3406
3470
  await ExecutionContextService.runInContext(async () => {
3407
3471
  if (self.params.callbacks?.onCancel) {
3408
3472
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
@@ -3413,7 +3477,7 @@ const CALL_CANCEL_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, sign
3413
3477
  symbol: symbol,
3414
3478
  backtest: backtest,
3415
3479
  });
3416
- }, {
3480
+ }), {
3417
3481
  fallback: (error) => {
3418
3482
  const message = "ClientStrategy CALL_CANCEL_CALLBACKS_FN thrown";
3419
3483
  const payload = {
@@ -3425,7 +3489,7 @@ const CALL_CANCEL_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, sign
3425
3489
  errorEmitter.next(error);
3426
3490
  },
3427
3491
  });
3428
- const CALL_OPEN_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, signal, priceOpen, timestamp, backtest) => {
3492
+ const CALL_OPEN_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, signal, priceOpen, timestamp, backtest) => {
3429
3493
  await ExecutionContextService.runInContext(async () => {
3430
3494
  if (self.params.callbacks?.onOpen) {
3431
3495
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
@@ -3436,7 +3500,7 @@ const CALL_OPEN_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, signal
3436
3500
  symbol: symbol,
3437
3501
  backtest: backtest,
3438
3502
  });
3439
- }, {
3503
+ }), {
3440
3504
  fallback: (error) => {
3441
3505
  const message = "ClientStrategy CALL_OPEN_CALLBACKS_FN thrown";
3442
3506
  const payload = {
@@ -3448,7 +3512,7 @@ const CALL_OPEN_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, signal
3448
3512
  errorEmitter.next(error);
3449
3513
  },
3450
3514
  });
3451
- const CALL_CLOSE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3515
+ const CALL_CLOSE_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3452
3516
  await ExecutionContextService.runInContext(async () => {
3453
3517
  if (self.params.callbacks?.onClose) {
3454
3518
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
@@ -3459,7 +3523,7 @@ const CALL_CLOSE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, signa
3459
3523
  symbol: symbol,
3460
3524
  backtest: backtest,
3461
3525
  });
3462
- }, {
3526
+ }), {
3463
3527
  fallback: (error) => {
3464
3528
  const message = "ClientStrategy CALL_CLOSE_CALLBACKS_FN thrown";
3465
3529
  const payload = {
@@ -3471,7 +3535,7 @@ const CALL_CLOSE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, signa
3471
3535
  errorEmitter.next(error);
3472
3536
  },
3473
3537
  });
3474
- const CALL_TICK_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, result, timestamp, backtest) => {
3538
+ const CALL_TICK_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, result, timestamp, backtest) => {
3475
3539
  await ExecutionContextService.runInContext(async () => {
3476
3540
  if (self.params.callbacks?.onTick) {
3477
3541
  await self.params.callbacks.onTick(self.params.execution.context.symbol, result, self.params.execution.context.backtest);
@@ -3481,7 +3545,7 @@ const CALL_TICK_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, result
3481
3545
  symbol: symbol,
3482
3546
  backtest: backtest,
3483
3547
  });
3484
- }, {
3548
+ }), {
3485
3549
  fallback: (error) => {
3486
3550
  const message = "ClientStrategy CALL_TICK_CALLBACKS_FN thrown";
3487
3551
  const payload = {
@@ -3493,7 +3557,7 @@ const CALL_TICK_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, result
3493
3557
  errorEmitter.next(error);
3494
3558
  },
3495
3559
  });
3496
- const CALL_IDLE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, currentPrice, timestamp, backtest) => {
3560
+ const CALL_IDLE_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, currentPrice, timestamp, backtest) => {
3497
3561
  await ExecutionContextService.runInContext(async () => {
3498
3562
  if (self.params.callbacks?.onIdle) {
3499
3563
  await self.params.callbacks.onIdle(self.params.execution.context.symbol, currentPrice, self.params.execution.context.backtest);
@@ -3503,7 +3567,7 @@ const CALL_IDLE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, curren
3503
3567
  symbol: symbol,
3504
3568
  backtest: backtest,
3505
3569
  });
3506
- }, {
3570
+ }), {
3507
3571
  fallback: (error) => {
3508
3572
  const message = "ClientStrategy CALL_IDLE_CALLBACKS_FN thrown";
3509
3573
  const payload = {
@@ -3515,7 +3579,7 @@ const CALL_IDLE_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, curren
3515
3579
  errorEmitter.next(error);
3516
3580
  },
3517
3581
  });
3518
- const CALL_RISK_ADD_SIGNAL_FN = functoolsKit.trycatch(async (self, symbol, signal, timestamp, backtest) => {
3582
+ const CALL_RISK_ADD_SIGNAL_FN = functoolsKit.trycatch(beginTime(async (self, symbol, signal, timestamp, backtest) => {
3519
3583
  await ExecutionContextService.runInContext(async () => {
3520
3584
  await self.params.risk.addSignal(symbol, {
3521
3585
  strategyName: self.params.method.context.strategyName,
@@ -3535,7 +3599,7 @@ const CALL_RISK_ADD_SIGNAL_FN = functoolsKit.trycatch(async (self, symbol, signa
3535
3599
  symbol: symbol,
3536
3600
  backtest: backtest,
3537
3601
  });
3538
- }, {
3602
+ }), {
3539
3603
  fallback: (error) => {
3540
3604
  const message = "ClientStrategy CALL_RISK_ADD_SIGNAL_FN thrown";
3541
3605
  const payload = {
@@ -3547,7 +3611,7 @@ const CALL_RISK_ADD_SIGNAL_FN = functoolsKit.trycatch(async (self, symbol, signa
3547
3611
  errorEmitter.next(error);
3548
3612
  },
3549
3613
  });
3550
- const CALL_RISK_REMOVE_SIGNAL_FN = functoolsKit.trycatch(async (self, symbol, timestamp, backtest) => {
3614
+ const CALL_RISK_REMOVE_SIGNAL_FN = functoolsKit.trycatch(beginTime(async (self, symbol, timestamp, backtest) => {
3551
3615
  await ExecutionContextService.runInContext(async () => {
3552
3616
  await self.params.risk.removeSignal(symbol, {
3553
3617
  strategyName: self.params.method.context.strategyName,
@@ -3560,7 +3624,7 @@ const CALL_RISK_REMOVE_SIGNAL_FN = functoolsKit.trycatch(async (self, symbol, ti
3560
3624
  symbol: symbol,
3561
3625
  backtest: backtest,
3562
3626
  });
3563
- }, {
3627
+ }), {
3564
3628
  fallback: (error) => {
3565
3629
  const message = "ClientStrategy CALL_RISK_REMOVE_SIGNAL_FN thrown";
3566
3630
  const payload = {
@@ -3572,7 +3636,7 @@ const CALL_RISK_REMOVE_SIGNAL_FN = functoolsKit.trycatch(async (self, symbol, ti
3572
3636
  errorEmitter.next(error);
3573
3637
  },
3574
3638
  });
3575
- const CALL_PARTIAL_CLEAR_FN = functoolsKit.trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3639
+ const CALL_PARTIAL_CLEAR_FN = functoolsKit.trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3576
3640
  await ExecutionContextService.runInContext(async () => {
3577
3641
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
3578
3642
  await self.params.partial.clear(symbol, publicSignal, currentPrice, backtest);
@@ -3581,7 +3645,7 @@ const CALL_PARTIAL_CLEAR_FN = functoolsKit.trycatch(async (self, symbol, signal,
3581
3645
  symbol: symbol,
3582
3646
  backtest: backtest,
3583
3647
  });
3584
- }, {
3648
+ }), {
3585
3649
  fallback: (error) => {
3586
3650
  const message = "ClientStrategy CALL_PARTIAL_CLEAR_FN thrown";
3587
3651
  const payload = {
@@ -3593,7 +3657,7 @@ const CALL_PARTIAL_CLEAR_FN = functoolsKit.trycatch(async (self, symbol, signal,
3593
3657
  errorEmitter.next(error);
3594
3658
  },
3595
3659
  });
3596
- const CALL_RISK_CHECK_SIGNAL_FN = functoolsKit.trycatch(async (self, symbol, pendingSignal, currentPrice, timestamp, backtest) => {
3660
+ const CALL_RISK_CHECK_SIGNAL_FN = functoolsKit.trycatch(beginTime(async (self, symbol, pendingSignal, currentPrice, timestamp, backtest) => {
3597
3661
  return await ExecutionContextService.runInContext(async () => {
3598
3662
  return await self.params.risk.checkSignal({
3599
3663
  pendingSignal: TO_PUBLIC_SIGNAL(pendingSignal),
@@ -3610,7 +3674,7 @@ const CALL_RISK_CHECK_SIGNAL_FN = functoolsKit.trycatch(async (self, symbol, pen
3610
3674
  symbol: symbol,
3611
3675
  backtest: backtest,
3612
3676
  });
3613
- }, {
3677
+ }), {
3614
3678
  defaultValue: false,
3615
3679
  fallback: (error) => {
3616
3680
  const message = "ClientStrategy CALL_RISK_CHECK_SIGNAL_FN thrown";
@@ -3623,7 +3687,7 @@ const CALL_RISK_CHECK_SIGNAL_FN = functoolsKit.trycatch(async (self, symbol, pen
3623
3687
  errorEmitter.next(error);
3624
3688
  },
3625
3689
  });
3626
- const CALL_PARTIAL_PROFIT_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, signal, currentPrice, percentTp, timestamp, backtest) => {
3690
+ const CALL_PARTIAL_PROFIT_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, signal, currentPrice, percentTp, timestamp, backtest) => {
3627
3691
  await ExecutionContextService.runInContext(async () => {
3628
3692
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
3629
3693
  await self.params.partial.profit(symbol, publicSignal, currentPrice, percentTp, backtest, new Date(timestamp));
@@ -3635,7 +3699,7 @@ const CALL_PARTIAL_PROFIT_CALLBACKS_FN = functoolsKit.trycatch(async (self, symb
3635
3699
  symbol: symbol,
3636
3700
  backtest: backtest,
3637
3701
  });
3638
- }, {
3702
+ }), {
3639
3703
  fallback: (error) => {
3640
3704
  const message = "ClientStrategy CALL_PARTIAL_PROFIT_CALLBACKS_FN thrown";
3641
3705
  const payload = {
@@ -3647,7 +3711,7 @@ const CALL_PARTIAL_PROFIT_CALLBACKS_FN = functoolsKit.trycatch(async (self, symb
3647
3711
  errorEmitter.next(error);
3648
3712
  },
3649
3713
  });
3650
- const CALL_PARTIAL_LOSS_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol, signal, currentPrice, percentSl, timestamp, backtest) => {
3714
+ const CALL_PARTIAL_LOSS_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, signal, currentPrice, percentSl, timestamp, backtest) => {
3651
3715
  await ExecutionContextService.runInContext(async () => {
3652
3716
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
3653
3717
  await self.params.partial.loss(symbol, publicSignal, currentPrice, percentSl, backtest, new Date(timestamp));
@@ -3659,7 +3723,7 @@ const CALL_PARTIAL_LOSS_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol
3659
3723
  symbol: symbol,
3660
3724
  backtest: backtest,
3661
3725
  });
3662
- }, {
3726
+ }), {
3663
3727
  fallback: (error) => {
3664
3728
  const message = "ClientStrategy CALL_PARTIAL_LOSS_CALLBACKS_FN thrown";
3665
3729
  const payload = {
@@ -3671,7 +3735,7 @@ const CALL_PARTIAL_LOSS_CALLBACKS_FN = functoolsKit.trycatch(async (self, symbol
3671
3735
  errorEmitter.next(error);
3672
3736
  },
3673
3737
  });
3674
- const CALL_BREAKEVEN_CHECK_FN = functoolsKit.trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3738
+ const CALL_BREAKEVEN_CHECK_FN = functoolsKit.trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3675
3739
  await ExecutionContextService.runInContext(async () => {
3676
3740
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
3677
3741
  const isBreakeven = await self.params.breakeven.check(symbol, publicSignal, currentPrice, backtest, new Date(timestamp));
@@ -3683,7 +3747,7 @@ const CALL_BREAKEVEN_CHECK_FN = functoolsKit.trycatch(async (self, symbol, signa
3683
3747
  symbol: symbol,
3684
3748
  backtest: backtest,
3685
3749
  });
3686
- }, {
3750
+ }), {
3687
3751
  fallback: (error) => {
3688
3752
  const message = "ClientStrategy CALL_BREAKEVEN_CHECK_FN thrown";
3689
3753
  const payload = {
@@ -3695,7 +3759,7 @@ const CALL_BREAKEVEN_CHECK_FN = functoolsKit.trycatch(async (self, symbol, signa
3695
3759
  errorEmitter.next(error);
3696
3760
  },
3697
3761
  });
3698
- const CALL_BREAKEVEN_CLEAR_FN = functoolsKit.trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3762
+ const CALL_BREAKEVEN_CLEAR_FN = functoolsKit.trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3699
3763
  await ExecutionContextService.runInContext(async () => {
3700
3764
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
3701
3765
  await self.params.breakeven.clear(symbol, publicSignal, currentPrice, backtest);
@@ -3704,7 +3768,7 @@ const CALL_BREAKEVEN_CLEAR_FN = functoolsKit.trycatch(async (self, symbol, signa
3704
3768
  symbol: symbol,
3705
3769
  backtest: backtest,
3706
3770
  });
3707
- }, {
3771
+ }), {
3708
3772
  fallback: (error) => {
3709
3773
  const message = "ClientStrategy CALL_BREAKEVEN_CLEAR_FN thrown";
3710
3774
  const payload = {
@@ -7466,23 +7530,29 @@ class ExchangeCoreService {
7466
7530
  /**
7467
7531
  * Fetches order book with execution context.
7468
7532
  *
7533
+ * Sets up execution context with the provided when/backtest parameters.
7534
+ * The exchange implementation will receive time range parameters but may
7535
+ * choose to use them (backtest) or ignore them (live).
7536
+ *
7469
7537
  * @param symbol - Trading pair symbol
7470
7538
  * @param when - Timestamp for context
7471
7539
  * @param backtest - Whether running in backtest mode
7540
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
7472
7541
  * @returns Promise resolving to order book data
7473
7542
  */
7474
- this.getOrderBook = async (symbol, when, backtest) => {
7543
+ this.getOrderBook = async (symbol, when, backtest, depth) => {
7475
7544
  this.loggerService.log("exchangeCoreService getOrderBook", {
7476
7545
  symbol,
7477
7546
  when,
7478
7547
  backtest,
7548
+ depth,
7479
7549
  });
7480
7550
  if (!MethodContextService.hasContext()) {
7481
7551
  throw new Error("exchangeCoreService getOrderBook requires a method context");
7482
7552
  }
7483
7553
  await this.validate(this.methodContextService.context.exchangeName);
7484
7554
  return await ExecutionContextService.runInContext(async () => {
7485
- return await this.exchangeConnectionService.getOrderBook(symbol);
7555
+ return await this.exchangeConnectionService.getOrderBook(symbol, depth);
7486
7556
  }, {
7487
7557
  symbol,
7488
7558
  when,
@@ -21351,6 +21421,7 @@ const FORMAT_QUANTITY_METHOD_NAME = "exchange.formatQuantity";
21351
21421
  const GET_DATE_METHOD_NAME = "exchange.getDate";
21352
21422
  const GET_MODE_METHOD_NAME = "exchange.getMode";
21353
21423
  const HAS_TRADE_CONTEXT_METHOD_NAME = "exchange.hasTradeContext";
21424
+ const GET_ORDER_BOOK_METHOD_NAME = "exchange.getOrderBook";
21354
21425
  /**
21355
21426
  * Checks if trade context is active (execution and method contexts).
21356
21427
  *
@@ -21531,6 +21602,41 @@ async function getMode() {
21531
21602
  const { backtest: bt$1 } = bt.executionContextService.context;
21532
21603
  return bt$1 ? "backtest" : "live";
21533
21604
  }
21605
+ /**
21606
+ * Fetches order book for a trading pair from the registered exchange.
21607
+ *
21608
+ * Uses current execution context to determine timing. The underlying exchange
21609
+ * implementation receives time range parameters but may use them (backtest)
21610
+ * or ignore them (live trading).
21611
+ *
21612
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
21613
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
21614
+ * @returns Promise resolving to order book data
21615
+ * @throws Error if execution or method context is missing
21616
+ *
21617
+ * @example
21618
+ * ```typescript
21619
+ * const orderBook = await getOrderBook("BTCUSDT");
21620
+ * console.log(orderBook.bids); // [{ price: "50000.00", quantity: "0.5" }, ...]
21621
+ * console.log(orderBook.asks); // [{ price: "50001.00", quantity: "0.3" }, ...]
21622
+ *
21623
+ * // Fetch deeper order book
21624
+ * const deepBook = await getOrderBook("BTCUSDT", 100);
21625
+ * ```
21626
+ */
21627
+ async function getOrderBook(symbol, depth) {
21628
+ bt.loggerService.info(GET_ORDER_BOOK_METHOD_NAME, {
21629
+ symbol,
21630
+ depth,
21631
+ });
21632
+ if (!ExecutionContextService.hasContext()) {
21633
+ throw new Error("getOrderBook requires an execution context");
21634
+ }
21635
+ if (!MethodContextService.hasContext()) {
21636
+ throw new Error("getOrderBook requires a method context");
21637
+ }
21638
+ return await bt.exchangeConnectionService.getOrderBook(symbol, depth);
21639
+ }
21534
21640
 
21535
21641
  const STOP_METHOD_NAME = "strategy.stop";
21536
21642
  const CANCEL_METHOD_NAME = "strategy.cancel";
@@ -27131,8 +27237,13 @@ const DEFAULT_FORMAT_PRICE_FN = async (_symbol, price) => {
27131
27237
  /**
27132
27238
  * Default implementation for getOrderBook.
27133
27239
  * Throws an error indicating the method is not implemented.
27240
+ *
27241
+ * @param _symbol - Trading pair symbol (unused)
27242
+ * @param _depth - Maximum depth levels (unused)
27243
+ * @param _from - Start of time range (unused - can be ignored in live implementations)
27244
+ * @param _to - End of time range (unused - can be ignored in live implementations)
27134
27245
  */
27135
- const DEFAULT_GET_ORDER_BOOK_FN = async (_symbol, _from, _to) => {
27246
+ const DEFAULT_GET_ORDER_BOOK_FN = async (_symbol, _depth, _from, _to) => {
27136
27247
  throw new Error(`getOrderBook is not implemented for this exchange`);
27137
27248
  };
27138
27249
  const INTERVAL_MINUTES$1 = {
@@ -27343,7 +27454,12 @@ class ExchangeInstance {
27343
27454
  /**
27344
27455
  * Fetch order book for a trading pair.
27345
27456
  *
27457
+ * Calculates time range using CC_ORDER_BOOK_TIME_OFFSET_MINUTES (default 10 minutes)
27458
+ * and passes it to the exchange schema implementation. The implementation may use
27459
+ * the time range (backtest) or ignore it (live trading).
27460
+ *
27346
27461
  * @param symbol - Trading pair symbol
27462
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
27347
27463
  * @returns Promise resolving to order book data
27348
27464
  * @throws Error if getOrderBook is not implemented
27349
27465
  *
@@ -27352,16 +27468,18 @@ class ExchangeInstance {
27352
27468
  * const instance = new ExchangeInstance("binance");
27353
27469
  * const orderBook = await instance.getOrderBook("BTCUSDT");
27354
27470
  * console.log(orderBook.bids); // [{ price: "50000.00", quantity: "0.5" }, ...]
27471
+ * const deepOrderBook = await instance.getOrderBook("BTCUSDT", 100);
27355
27472
  * ```
27356
27473
  */
27357
- this.getOrderBook = async (symbol) => {
27474
+ this.getOrderBook = async (symbol, depth = GLOBAL_CONFIG.CC_ORDER_BOOK_MAX_DEPTH_LEVELS) => {
27358
27475
  bt.loggerService.info(EXCHANGE_METHOD_NAME_GET_ORDER_BOOK, {
27359
27476
  exchangeName: this.exchangeName,
27360
27477
  symbol,
27478
+ depth,
27361
27479
  });
27362
27480
  const to = new Date(Date.now());
27363
27481
  const from = new Date(to.getTime() - GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * 60 * 1000);
27364
- return await this._methods.getOrderBook(symbol, from, to);
27482
+ return await this._methods.getOrderBook(symbol, depth, from, to);
27365
27483
  };
27366
27484
  const schema = bt.exchangeSchemaService.get(this.exchangeName);
27367
27485
  this._methods = CREATE_EXCHANGE_INSTANCE_FN(schema);
@@ -27453,14 +27571,19 @@ class ExchangeUtils {
27453
27571
  /**
27454
27572
  * Fetch order book for a trading pair.
27455
27573
  *
27574
+ * Delegates to ExchangeInstance which calculates time range and passes it
27575
+ * to the exchange schema implementation. The from/to parameters may be used
27576
+ * (backtest) or ignored (live) depending on the implementation.
27577
+ *
27456
27578
  * @param symbol - Trading pair symbol
27457
27579
  * @param context - Execution context with exchange name
27580
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
27458
27581
  * @returns Promise resolving to order book data
27459
27582
  */
27460
- this.getOrderBook = async (symbol, context) => {
27583
+ this.getOrderBook = async (symbol, context, depth = GLOBAL_CONFIG.CC_ORDER_BOOK_MAX_DEPTH_LEVELS) => {
27461
27584
  bt.exchangeValidationService.validate(context.exchangeName, EXCHANGE_METHOD_NAME_GET_ORDER_BOOK);
27462
27585
  const instance = this._getInstance(context.exchangeName);
27463
- return await instance.getOrderBook(symbol);
27586
+ return await instance.getOrderBook(symbol, depth);
27464
27587
  };
27465
27588
  }
27466
27589
  }
@@ -28455,6 +28578,7 @@ exports.getDate = getDate;
28455
28578
  exports.getDefaultColumns = getDefaultColumns;
28456
28579
  exports.getDefaultConfig = getDefaultConfig;
28457
28580
  exports.getMode = getMode;
28581
+ exports.getOrderBook = getOrderBook;
28458
28582
  exports.hasTradeContext = hasTradeContext;
28459
28583
  exports.lib = backtest;
28460
28584
  exports.listExchanges = listExchanges;
package/build/index.mjs CHANGED
@@ -402,6 +402,13 @@ const GLOBAL_CONFIG = {
402
402
  * Default: 10 minutes
403
403
  */
404
404
  CC_ORDER_BOOK_TIME_OFFSET_MINUTES: 10,
405
+ /**
406
+ * Maximum depth levels for order book fetching.
407
+ * Specifies how many price levels to fetch from both bids and asks.
408
+ *
409
+ * Default: 20 levels
410
+ */
411
+ CC_ORDER_BOOK_MAX_DEPTH_LEVELS: 1000,
405
412
  };
406
413
  const DEFAULT_CONFIG = Object.freeze({ ...GLOBAL_CONFIG });
407
414
 
@@ -871,17 +878,23 @@ class ClientExchange {
871
878
  /**
872
879
  * Fetches order book for a trading pair.
873
880
  *
881
+ * Calculates time range based on execution context time (when) and
882
+ * CC_ORDER_BOOK_TIME_OFFSET_MINUTES, then delegates to the exchange
883
+ * schema implementation which may use or ignore the time range.
884
+ *
874
885
  * @param symbol - Trading pair symbol
886
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
875
887
  * @returns Promise resolving to order book data
876
888
  * @throws Error if getOrderBook is not implemented
877
889
  */
878
- async getOrderBook(symbol) {
890
+ async getOrderBook(symbol, depth = GLOBAL_CONFIG.CC_ORDER_BOOK_MAX_DEPTH_LEVELS) {
879
891
  this.params.logger.debug("ClientExchange getOrderBook", {
880
892
  symbol,
893
+ depth,
881
894
  });
882
895
  const to = new Date(this.params.execution.context.when.getTime());
883
896
  const from = new Date(to.getTime() - GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * 60 * 1000);
884
- return await this.params.getOrderBook(symbol, from, to);
897
+ return await this.params.getOrderBook(symbol, depth, from, to);
885
898
  }
886
899
  }
887
900
 
@@ -909,8 +922,13 @@ const DEFAULT_FORMAT_PRICE_FN$1 = async (_symbol, price) => {
909
922
  /**
910
923
  * Default implementation for getOrderBook.
911
924
  * Throws an error indicating the method is not implemented.
925
+ *
926
+ * @param _symbol - Trading pair symbol (unused)
927
+ * @param _depth - Maximum depth levels (unused)
928
+ * @param _from - Start of time range (unused - can be ignored in live implementations)
929
+ * @param _to - End of time range (unused - can be ignored in live implementations)
912
930
  */
913
- const DEFAULT_GET_ORDER_BOOK_FN$1 = async (_symbol, _from, _to) => {
931
+ const DEFAULT_GET_ORDER_BOOK_FN$1 = async (_symbol, _depth, _from, _to) => {
914
932
  throw new Error(`getOrderBook is not implemented for this exchange`);
915
933
  };
916
934
  /**
@@ -1051,15 +1069,19 @@ class ExchangeConnectionService {
1051
1069
  * Fetches order book for a trading pair using configured exchange.
1052
1070
  *
1053
1071
  * Routes to exchange determined by methodContextService.context.exchangeName.
1072
+ * The ClientExchange will calculate time range and pass it to the schema
1073
+ * implementation, which may use (backtest) or ignore (live) the parameters.
1054
1074
  *
1055
1075
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
1076
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
1056
1077
  * @returns Promise resolving to order book data
1057
1078
  */
1058
- this.getOrderBook = async (symbol) => {
1079
+ this.getOrderBook = async (symbol, depth) => {
1059
1080
  this.loggerService.log("exchangeConnectionService getOrderBook", {
1060
1081
  symbol,
1082
+ depth,
1061
1083
  });
1062
- return await this.getExchange(this.methodContextService.context.exchangeName).getOrderBook(symbol);
1084
+ return await this.getExchange(this.methodContextService.context.exchangeName).getOrderBook(symbol, depth);
1063
1085
  };
1064
1086
  }
1065
1087
  }
@@ -2211,6 +2233,48 @@ const toPlainString = (content) => {
2211
2233
  return text.trim();
2212
2234
  };
2213
2235
 
2236
+ /**
2237
+ * Wraps a function to execute it outside of the current execution context if one exists.
2238
+ *
2239
+ * This utility ensures that the wrapped function runs in isolation from any existing
2240
+ * ExecutionContext, preventing context leakage and unintended context sharing between
2241
+ * async operations.
2242
+ *
2243
+ * @template T - Function type with any parameters and return type
2244
+ * @param {T} run - The function to be wrapped and executed outside of context
2245
+ * @returns {Function} A curried function that accepts the original function's parameters
2246
+ * and executes it outside of the current context if one exists
2247
+ *
2248
+ * @example
2249
+ * ```ts
2250
+ * const myFunction = async (param: string) => {
2251
+ * // This code will run outside of any ExecutionContext
2252
+ * return param.toUpperCase();
2253
+ * };
2254
+ *
2255
+ * const wrappedFunction = beginTime(myFunction);
2256
+ * const result = wrappedFunction('hello'); // Returns 'HELLO'
2257
+ * ```
2258
+ *
2259
+ * @example
2260
+ * ```ts
2261
+ * // Usage with trycatch wrapper
2262
+ * const safeFunction = trycatch(
2263
+ * beginTime(async (id: number) => {
2264
+ * // Function body runs isolated from parent context
2265
+ * return await fetchData(id);
2266
+ * })
2267
+ * );
2268
+ * ```
2269
+ */
2270
+ const beginTime = (run) => (...args) => {
2271
+ let fn = () => run(...args);
2272
+ if (ExecutionContextService.hasContext()) {
2273
+ fn = ExecutionContextService.runOutOfContext(fn);
2274
+ }
2275
+ return fn();
2276
+ };
2277
+
2214
2278
  const INTERVAL_MINUTES$3 = {
2215
2279
  "1m": 1,
2216
2280
  "3m": 3,
@@ -3310,7 +3374,7 @@ const ACTIVATE_SCHEDULED_SIGNAL_FN = async (self, scheduled, activationTimestamp
3310
3374
  await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, activationTime, self.params.execution.context.backtest);
3311
3375
  return result;
3312
3376
  };
3313
- const CALL_PING_CALLBACKS_FN = trycatch(async (self, symbol, scheduled, timestamp, backtest) => {
3377
+ const CALL_PING_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, scheduled, timestamp, backtest) => {
3314
3378
  await ExecutionContextService.runInContext(async () => {
3315
3379
  const publicSignal = TO_PUBLIC_SIGNAL(scheduled);
3316
3380
  // Call system onPing callback first (emits to pingSubject)
@@ -3324,7 +3388,7 @@ const CALL_PING_CALLBACKS_FN = trycatch(async (self, symbol, scheduled, timestam
3324
3388
  symbol: symbol,
3325
3389
  backtest: backtest,
3326
3390
  });
3327
- }, {
3391
+ }), {
3328
3392
  fallback: (error) => {
3329
3393
  const message = "ClientStrategy CALL_PING_CALLBACKS_FN thrown";
3330
3394
  const payload = {
@@ -3336,7 +3400,7 @@ const CALL_PING_CALLBACKS_FN = trycatch(async (self, symbol, scheduled, timestam
3336
3400
  errorEmitter.next(error);
3337
3401
  },
3338
3402
  });
3339
- const CALL_ACTIVE_CALLBACKS_FN = trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3403
+ const CALL_ACTIVE_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3340
3404
  await ExecutionContextService.runInContext(async () => {
3341
3405
  if (self.params.callbacks?.onActive) {
3342
3406
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
@@ -3347,7 +3411,7 @@ const CALL_ACTIVE_CALLBACKS_FN = trycatch(async (self, symbol, signal, currentPr
3347
3411
  symbol: symbol,
3348
3412
  backtest: backtest,
3349
3413
  });
3350
- }, {
3414
+ }), {
3351
3415
  fallback: (error) => {
3352
3416
  const message = "ClientStrategy CALL_ACTIVE_CALLBACKS_FN thrown";
3353
3417
  const payload = {
@@ -3359,7 +3423,7 @@ const CALL_ACTIVE_CALLBACKS_FN = trycatch(async (self, symbol, signal, currentPr
3359
3423
  errorEmitter.next(error);
3360
3424
  },
3361
3425
  });
3362
- const CALL_SCHEDULE_CALLBACKS_FN = trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3426
+ const CALL_SCHEDULE_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3363
3427
  await ExecutionContextService.runInContext(async () => {
3364
3428
  if (self.params.callbacks?.onSchedule) {
3365
3429
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
@@ -3370,7 +3434,7 @@ const CALL_SCHEDULE_CALLBACKS_FN = trycatch(async (self, symbol, signal, current
3370
3434
  symbol: symbol,
3371
3435
  backtest: backtest,
3372
3436
  });
3373
- }, {
3437
+ }), {
3374
3438
  fallback: (error) => {
3375
3439
  const message = "ClientStrategy CALL_SCHEDULE_CALLBACKS_FN thrown";
3376
3440
  const payload = {
@@ -3382,7 +3446,7 @@ const CALL_SCHEDULE_CALLBACKS_FN = trycatch(async (self, symbol, signal, current
3382
3446
  errorEmitter.next(error);
3383
3447
  },
3384
3448
  });
3385
- const CALL_CANCEL_CALLBACKS_FN = trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3449
+ const CALL_CANCEL_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3386
3450
  await ExecutionContextService.runInContext(async () => {
3387
3451
  if (self.params.callbacks?.onCancel) {
3388
3452
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
@@ -3393,7 +3457,7 @@ const CALL_CANCEL_CALLBACKS_FN = trycatch(async (self, symbol, signal, currentPr
3393
3457
  symbol: symbol,
3394
3458
  backtest: backtest,
3395
3459
  });
3396
- }, {
3460
+ }), {
3397
3461
  fallback: (error) => {
3398
3462
  const message = "ClientStrategy CALL_CANCEL_CALLBACKS_FN thrown";
3399
3463
  const payload = {
@@ -3405,7 +3469,7 @@ const CALL_CANCEL_CALLBACKS_FN = trycatch(async (self, symbol, signal, currentPr
3405
3469
  errorEmitter.next(error);
3406
3470
  },
3407
3471
  });
3408
- const CALL_OPEN_CALLBACKS_FN = trycatch(async (self, symbol, signal, priceOpen, timestamp, backtest) => {
3472
+ const CALL_OPEN_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, signal, priceOpen, timestamp, backtest) => {
3409
3473
  await ExecutionContextService.runInContext(async () => {
3410
3474
  if (self.params.callbacks?.onOpen) {
3411
3475
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
@@ -3416,7 +3480,7 @@ const CALL_OPEN_CALLBACKS_FN = trycatch(async (self, symbol, signal, priceOpen,
3416
3480
  symbol: symbol,
3417
3481
  backtest: backtest,
3418
3482
  });
3419
- }, {
3483
+ }), {
3420
3484
  fallback: (error) => {
3421
3485
  const message = "ClientStrategy CALL_OPEN_CALLBACKS_FN thrown";
3422
3486
  const payload = {
@@ -3428,7 +3492,7 @@ const CALL_OPEN_CALLBACKS_FN = trycatch(async (self, symbol, signal, priceOpen,
3428
3492
  errorEmitter.next(error);
3429
3493
  },
3430
3494
  });
3431
- const CALL_CLOSE_CALLBACKS_FN = trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3495
+ const CALL_CLOSE_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3432
3496
  await ExecutionContextService.runInContext(async () => {
3433
3497
  if (self.params.callbacks?.onClose) {
3434
3498
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
@@ -3439,7 +3503,7 @@ const CALL_CLOSE_CALLBACKS_FN = trycatch(async (self, symbol, signal, currentPri
3439
3503
  symbol: symbol,
3440
3504
  backtest: backtest,
3441
3505
  });
3442
- }, {
3506
+ }), {
3443
3507
  fallback: (error) => {
3444
3508
  const message = "ClientStrategy CALL_CLOSE_CALLBACKS_FN thrown";
3445
3509
  const payload = {
@@ -3451,7 +3515,7 @@ const CALL_CLOSE_CALLBACKS_FN = trycatch(async (self, symbol, signal, currentPri
3451
3515
  errorEmitter.next(error);
3452
3516
  },
3453
3517
  });
3454
- const CALL_TICK_CALLBACKS_FN = trycatch(async (self, symbol, result, timestamp, backtest) => {
3518
+ const CALL_TICK_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, result, timestamp, backtest) => {
3455
3519
  await ExecutionContextService.runInContext(async () => {
3456
3520
  if (self.params.callbacks?.onTick) {
3457
3521
  await self.params.callbacks.onTick(self.params.execution.context.symbol, result, self.params.execution.context.backtest);
@@ -3461,7 +3525,7 @@ const CALL_TICK_CALLBACKS_FN = trycatch(async (self, symbol, result, timestamp,
3461
3525
  symbol: symbol,
3462
3526
  backtest: backtest,
3463
3527
  });
3464
- }, {
3528
+ }), {
3465
3529
  fallback: (error) => {
3466
3530
  const message = "ClientStrategy CALL_TICK_CALLBACKS_FN thrown";
3467
3531
  const payload = {
@@ -3473,7 +3537,7 @@ const CALL_TICK_CALLBACKS_FN = trycatch(async (self, symbol, result, timestamp,
3473
3537
  errorEmitter.next(error);
3474
3538
  },
3475
3539
  });
3476
- const CALL_IDLE_CALLBACKS_FN = trycatch(async (self, symbol, currentPrice, timestamp, backtest) => {
3540
+ const CALL_IDLE_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, currentPrice, timestamp, backtest) => {
3477
3541
  await ExecutionContextService.runInContext(async () => {
3478
3542
  if (self.params.callbacks?.onIdle) {
3479
3543
  await self.params.callbacks.onIdle(self.params.execution.context.symbol, currentPrice, self.params.execution.context.backtest);
@@ -3483,7 +3547,7 @@ const CALL_IDLE_CALLBACKS_FN = trycatch(async (self, symbol, currentPrice, times
3483
3547
  symbol: symbol,
3484
3548
  backtest: backtest,
3485
3549
  });
3486
- }, {
3550
+ }), {
3487
3551
  fallback: (error) => {
3488
3552
  const message = "ClientStrategy CALL_IDLE_CALLBACKS_FN thrown";
3489
3553
  const payload = {
@@ -3495,7 +3559,7 @@ const CALL_IDLE_CALLBACKS_FN = trycatch(async (self, symbol, currentPrice, times
3495
3559
  errorEmitter.next(error);
3496
3560
  },
3497
3561
  });
3498
- const CALL_RISK_ADD_SIGNAL_FN = trycatch(async (self, symbol, signal, timestamp, backtest) => {
3562
+ const CALL_RISK_ADD_SIGNAL_FN = trycatch(beginTime(async (self, symbol, signal, timestamp, backtest) => {
3499
3563
  await ExecutionContextService.runInContext(async () => {
3500
3564
  await self.params.risk.addSignal(symbol, {
3501
3565
  strategyName: self.params.method.context.strategyName,
@@ -3515,7 +3579,7 @@ const CALL_RISK_ADD_SIGNAL_FN = trycatch(async (self, symbol, signal, timestamp,
3515
3579
  symbol: symbol,
3516
3580
  backtest: backtest,
3517
3581
  });
3518
- }, {
3582
+ }), {
3519
3583
  fallback: (error) => {
3520
3584
  const message = "ClientStrategy CALL_RISK_ADD_SIGNAL_FN thrown";
3521
3585
  const payload = {
@@ -3527,7 +3591,7 @@ const CALL_RISK_ADD_SIGNAL_FN = trycatch(async (self, symbol, signal, timestamp,
3527
3591
  errorEmitter.next(error);
3528
3592
  },
3529
3593
  });
3530
- const CALL_RISK_REMOVE_SIGNAL_FN = trycatch(async (self, symbol, timestamp, backtest) => {
3594
+ const CALL_RISK_REMOVE_SIGNAL_FN = trycatch(beginTime(async (self, symbol, timestamp, backtest) => {
3531
3595
  await ExecutionContextService.runInContext(async () => {
3532
3596
  await self.params.risk.removeSignal(symbol, {
3533
3597
  strategyName: self.params.method.context.strategyName,
@@ -3540,7 +3604,7 @@ const CALL_RISK_REMOVE_SIGNAL_FN = trycatch(async (self, symbol, timestamp, back
3540
3604
  symbol: symbol,
3541
3605
  backtest: backtest,
3542
3606
  });
3543
- }, {
3607
+ }), {
3544
3608
  fallback: (error) => {
3545
3609
  const message = "ClientStrategy CALL_RISK_REMOVE_SIGNAL_FN thrown";
3546
3610
  const payload = {
@@ -3552,7 +3616,7 @@ const CALL_RISK_REMOVE_SIGNAL_FN = trycatch(async (self, symbol, timestamp, back
3552
3616
  errorEmitter.next(error);
3553
3617
  },
3554
3618
  });
3555
- const CALL_PARTIAL_CLEAR_FN = trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3619
+ const CALL_PARTIAL_CLEAR_FN = trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3556
3620
  await ExecutionContextService.runInContext(async () => {
3557
3621
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
3558
3622
  await self.params.partial.clear(symbol, publicSignal, currentPrice, backtest);
@@ -3561,7 +3625,7 @@ const CALL_PARTIAL_CLEAR_FN = trycatch(async (self, symbol, signal, currentPrice
3561
3625
  symbol: symbol,
3562
3626
  backtest: backtest,
3563
3627
  });
3564
- }, {
3628
+ }), {
3565
3629
  fallback: (error) => {
3566
3630
  const message = "ClientStrategy CALL_PARTIAL_CLEAR_FN thrown";
3567
3631
  const payload = {
@@ -3573,7 +3637,7 @@ const CALL_PARTIAL_CLEAR_FN = trycatch(async (self, symbol, signal, currentPrice
3573
3637
  errorEmitter.next(error);
3574
3638
  },
3575
3639
  });
3576
- const CALL_RISK_CHECK_SIGNAL_FN = trycatch(async (self, symbol, pendingSignal, currentPrice, timestamp, backtest) => {
3640
+ const CALL_RISK_CHECK_SIGNAL_FN = trycatch(beginTime(async (self, symbol, pendingSignal, currentPrice, timestamp, backtest) => {
3577
3641
  return await ExecutionContextService.runInContext(async () => {
3578
3642
  return await self.params.risk.checkSignal({
3579
3643
  pendingSignal: TO_PUBLIC_SIGNAL(pendingSignal),
@@ -3590,7 +3654,7 @@ const CALL_RISK_CHECK_SIGNAL_FN = trycatch(async (self, symbol, pendingSignal, c
3590
3654
  symbol: symbol,
3591
3655
  backtest: backtest,
3592
3656
  });
3593
- }, {
3657
+ }), {
3594
3658
  defaultValue: false,
3595
3659
  fallback: (error) => {
3596
3660
  const message = "ClientStrategy CALL_RISK_CHECK_SIGNAL_FN thrown";
@@ -3603,7 +3667,7 @@ const CALL_RISK_CHECK_SIGNAL_FN = trycatch(async (self, symbol, pendingSignal, c
3603
3667
  errorEmitter.next(error);
3604
3668
  },
3605
3669
  });
3606
- const CALL_PARTIAL_PROFIT_CALLBACKS_FN = trycatch(async (self, symbol, signal, currentPrice, percentTp, timestamp, backtest) => {
3670
+ const CALL_PARTIAL_PROFIT_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, signal, currentPrice, percentTp, timestamp, backtest) => {
3607
3671
  await ExecutionContextService.runInContext(async () => {
3608
3672
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
3609
3673
  await self.params.partial.profit(symbol, publicSignal, currentPrice, percentTp, backtest, new Date(timestamp));
@@ -3615,7 +3679,7 @@ const CALL_PARTIAL_PROFIT_CALLBACKS_FN = trycatch(async (self, symbol, signal, c
3615
3679
  symbol: symbol,
3616
3680
  backtest: backtest,
3617
3681
  });
3618
- }, {
3682
+ }), {
3619
3683
  fallback: (error) => {
3620
3684
  const message = "ClientStrategy CALL_PARTIAL_PROFIT_CALLBACKS_FN thrown";
3621
3685
  const payload = {
@@ -3627,7 +3691,7 @@ const CALL_PARTIAL_PROFIT_CALLBACKS_FN = trycatch(async (self, symbol, signal, c
3627
3691
  errorEmitter.next(error);
3628
3692
  },
3629
3693
  });
3630
- const CALL_PARTIAL_LOSS_CALLBACKS_FN = trycatch(async (self, symbol, signal, currentPrice, percentSl, timestamp, backtest) => {
3694
+ const CALL_PARTIAL_LOSS_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, signal, currentPrice, percentSl, timestamp, backtest) => {
3631
3695
  await ExecutionContextService.runInContext(async () => {
3632
3696
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
3633
3697
  await self.params.partial.loss(symbol, publicSignal, currentPrice, percentSl, backtest, new Date(timestamp));
@@ -3639,7 +3703,7 @@ const CALL_PARTIAL_LOSS_CALLBACKS_FN = trycatch(async (self, symbol, signal, cur
3639
3703
  symbol: symbol,
3640
3704
  backtest: backtest,
3641
3705
  });
3642
- }, {
3706
+ }), {
3643
3707
  fallback: (error) => {
3644
3708
  const message = "ClientStrategy CALL_PARTIAL_LOSS_CALLBACKS_FN thrown";
3645
3709
  const payload = {
@@ -3651,7 +3715,7 @@ const CALL_PARTIAL_LOSS_CALLBACKS_FN = trycatch(async (self, symbol, signal, cur
3651
3715
  errorEmitter.next(error);
3652
3716
  },
3653
3717
  });
3654
- const CALL_BREAKEVEN_CHECK_FN = trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3718
+ const CALL_BREAKEVEN_CHECK_FN = trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3655
3719
  await ExecutionContextService.runInContext(async () => {
3656
3720
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
3657
3721
  const isBreakeven = await self.params.breakeven.check(symbol, publicSignal, currentPrice, backtest, new Date(timestamp));
@@ -3663,7 +3727,7 @@ const CALL_BREAKEVEN_CHECK_FN = trycatch(async (self, symbol, signal, currentPri
3663
3727
  symbol: symbol,
3664
3728
  backtest: backtest,
3665
3729
  });
3666
- }, {
3730
+ }), {
3667
3731
  fallback: (error) => {
3668
3732
  const message = "ClientStrategy CALL_BREAKEVEN_CHECK_FN thrown";
3669
3733
  const payload = {
@@ -3675,7 +3739,7 @@ const CALL_BREAKEVEN_CHECK_FN = trycatch(async (self, symbol, signal, currentPri
3675
3739
  errorEmitter.next(error);
3676
3740
  },
3677
3741
  });
3678
- const CALL_BREAKEVEN_CLEAR_FN = trycatch(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3742
+ const CALL_BREAKEVEN_CLEAR_FN = trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
3679
3743
  await ExecutionContextService.runInContext(async () => {
3680
3744
  const publicSignal = TO_PUBLIC_SIGNAL(signal);
3681
3745
  await self.params.breakeven.clear(symbol, publicSignal, currentPrice, backtest);
@@ -3684,7 +3748,7 @@ const CALL_BREAKEVEN_CLEAR_FN = trycatch(async (self, symbol, signal, currentPri
3684
3748
  symbol: symbol,
3685
3749
  backtest: backtest,
3686
3750
  });
3687
- }, {
3751
+ }), {
3688
3752
  fallback: (error) => {
3689
3753
  const message = "ClientStrategy CALL_BREAKEVEN_CLEAR_FN thrown";
3690
3754
  const payload = {
@@ -7446,23 +7510,29 @@ class ExchangeCoreService {
7446
7510
  /**
7447
7511
  * Fetches order book with execution context.
7448
7512
  *
7513
+ * Sets up execution context with the provided when/backtest parameters.
7514
+ * The exchange implementation will receive time range parameters but may
7515
+ * choose to use them (backtest) or ignore them (live).
7516
+ *
7449
7517
  * @param symbol - Trading pair symbol
7450
7518
  * @param when - Timestamp for context
7451
7519
  * @param backtest - Whether running in backtest mode
7520
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
7452
7521
  * @returns Promise resolving to order book data
7453
7522
  */
7454
- this.getOrderBook = async (symbol, when, backtest) => {
7523
+ this.getOrderBook = async (symbol, when, backtest, depth) => {
7455
7524
  this.loggerService.log("exchangeCoreService getOrderBook", {
7456
7525
  symbol,
7457
7526
  when,
7458
7527
  backtest,
7528
+ depth,
7459
7529
  });
7460
7530
  if (!MethodContextService.hasContext()) {
7461
7531
  throw new Error("exchangeCoreService getOrderBook requires a method context");
7462
7532
  }
7463
7533
  await this.validate(this.methodContextService.context.exchangeName);
7464
7534
  return await ExecutionContextService.runInContext(async () => {
7465
- return await this.exchangeConnectionService.getOrderBook(symbol);
7535
+ return await this.exchangeConnectionService.getOrderBook(symbol, depth);
7466
7536
  }, {
7467
7537
  symbol,
7468
7538
  when,
@@ -21331,6 +21401,7 @@ const FORMAT_QUANTITY_METHOD_NAME = "exchange.formatQuantity";
21331
21401
  const GET_DATE_METHOD_NAME = "exchange.getDate";
21332
21402
  const GET_MODE_METHOD_NAME = "exchange.getMode";
21333
21403
  const HAS_TRADE_CONTEXT_METHOD_NAME = "exchange.hasTradeContext";
21404
+ const GET_ORDER_BOOK_METHOD_NAME = "exchange.getOrderBook";
21334
21405
  /**
21335
21406
  * Checks if trade context is active (execution and method contexts).
21336
21407
  *
@@ -21511,6 +21582,41 @@ async function getMode() {
21511
21582
  const { backtest: bt$1 } = bt.executionContextService.context;
21512
21583
  return bt$1 ? "backtest" : "live";
21513
21584
  }
21585
+ /**
21586
+ * Fetches order book for a trading pair from the registered exchange.
21587
+ *
21588
+ * Uses current execution context to determine timing. The underlying exchange
21589
+ * implementation receives time range parameters but may use them (backtest)
21590
+ * or ignore them (live trading).
21591
+ *
21592
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
21593
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
21594
+ * @returns Promise resolving to order book data
21595
+ * @throws Error if execution or method context is missing
21596
+ *
21597
+ * @example
21598
+ * ```typescript
21599
+ * const orderBook = await getOrderBook("BTCUSDT");
21600
+ * console.log(orderBook.bids); // [{ price: "50000.00", quantity: "0.5" }, ...]
21601
+ * console.log(orderBook.asks); // [{ price: "50001.00", quantity: "0.3" }, ...]
21602
+ *
21603
+ * // Fetch deeper order book
21604
+ * const deepBook = await getOrderBook("BTCUSDT", 100);
21605
+ * ```
21606
+ */
21607
+ async function getOrderBook(symbol, depth) {
21608
+ bt.loggerService.info(GET_ORDER_BOOK_METHOD_NAME, {
21609
+ symbol,
21610
+ depth,
21611
+ });
21612
+ if (!ExecutionContextService.hasContext()) {
21613
+ throw new Error("getOrderBook requires an execution context");
21614
+ }
21615
+ if (!MethodContextService.hasContext()) {
21616
+ throw new Error("getOrderBook requires a method context");
21617
+ }
21618
+ return await bt.exchangeConnectionService.getOrderBook(symbol, depth);
21619
+ }
21514
21620
 
21515
21621
  const STOP_METHOD_NAME = "strategy.stop";
21516
21622
  const CANCEL_METHOD_NAME = "strategy.cancel";
@@ -27111,8 +27217,13 @@ const DEFAULT_FORMAT_PRICE_FN = async (_symbol, price) => {
27111
27217
  /**
27112
27218
  * Default implementation for getOrderBook.
27113
27219
  * Throws an error indicating the method is not implemented.
27220
+ *
27221
+ * @param _symbol - Trading pair symbol (unused)
27222
+ * @param _depth - Maximum depth levels (unused)
27223
+ * @param _from - Start of time range (unused - can be ignored in live implementations)
27224
+ * @param _to - End of time range (unused - can be ignored in live implementations)
27114
27225
  */
27115
- const DEFAULT_GET_ORDER_BOOK_FN = async (_symbol, _from, _to) => {
27226
+ const DEFAULT_GET_ORDER_BOOK_FN = async (_symbol, _depth, _from, _to) => {
27116
27227
  throw new Error(`getOrderBook is not implemented for this exchange`);
27117
27228
  };
27118
27229
  const INTERVAL_MINUTES$1 = {
@@ -27323,7 +27434,12 @@ class ExchangeInstance {
27323
27434
  /**
27324
27435
  * Fetch order book for a trading pair.
27325
27436
  *
27437
+ * Calculates time range using CC_ORDER_BOOK_TIME_OFFSET_MINUTES (default 10 minutes)
27438
+ * and passes it to the exchange schema implementation. The implementation may use
27439
+ * the time range (backtest) or ignore it (live trading).
27440
+ *
27326
27441
  * @param symbol - Trading pair symbol
27442
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
27327
27443
  * @returns Promise resolving to order book data
27328
27444
  * @throws Error if getOrderBook is not implemented
27329
27445
  *
@@ -27332,16 +27448,18 @@ class ExchangeInstance {
27332
27448
  * const instance = new ExchangeInstance("binance");
27333
27449
  * const orderBook = await instance.getOrderBook("BTCUSDT");
27334
27450
  * console.log(orderBook.bids); // [{ price: "50000.00", quantity: "0.5" }, ...]
27451
+ * const deepOrderBook = await instance.getOrderBook("BTCUSDT", 100);
27335
27452
  * ```
27336
27453
  */
27337
- this.getOrderBook = async (symbol) => {
27454
+ this.getOrderBook = async (symbol, depth = GLOBAL_CONFIG.CC_ORDER_BOOK_MAX_DEPTH_LEVELS) => {
27338
27455
  bt.loggerService.info(EXCHANGE_METHOD_NAME_GET_ORDER_BOOK, {
27339
27456
  exchangeName: this.exchangeName,
27340
27457
  symbol,
27458
+ depth,
27341
27459
  });
27342
27460
  const to = new Date(Date.now());
27343
27461
  const from = new Date(to.getTime() - GLOBAL_CONFIG.CC_ORDER_BOOK_TIME_OFFSET_MINUTES * 60 * 1000);
27344
- return await this._methods.getOrderBook(symbol, from, to);
27462
+ return await this._methods.getOrderBook(symbol, depth, from, to);
27345
27463
  };
27346
27464
  const schema = bt.exchangeSchemaService.get(this.exchangeName);
27347
27465
  this._methods = CREATE_EXCHANGE_INSTANCE_FN(schema);
@@ -27433,14 +27551,19 @@ class ExchangeUtils {
27433
27551
  /**
27434
27552
  * Fetch order book for a trading pair.
27435
27553
  *
27554
+ * Delegates to ExchangeInstance which calculates time range and passes it
27555
+ * to the exchange schema implementation. The from/to parameters may be used
27556
+ * (backtest) or ignored (live) depending on the implementation.
27557
+ *
27436
27558
  * @param symbol - Trading pair symbol
27437
27559
  * @param context - Execution context with exchange name
27560
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
27438
27561
  * @returns Promise resolving to order book data
27439
27562
  */
27440
- this.getOrderBook = async (symbol, context) => {
27563
+ this.getOrderBook = async (symbol, context, depth = GLOBAL_CONFIG.CC_ORDER_BOOK_MAX_DEPTH_LEVELS) => {
27441
27564
  bt.exchangeValidationService.validate(context.exchangeName, EXCHANGE_METHOD_NAME_GET_ORDER_BOOK);
27442
27565
  const instance = this._getInstance(context.exchangeName);
27443
- return await instance.getOrderBook(symbol);
27566
+ return await instance.getOrderBook(symbol, depth);
27444
27567
  };
27445
27568
  }
27446
27569
  }
@@ -28386,4 +28509,4 @@ class BreakevenUtils {
28386
28509
  */
28387
28510
  const Breakeven = new BreakevenUtils();
28388
28511
 
28389
- export { Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, Optimizer, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, breakeven, cancel, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getColumns, getConfig, getDate, getDefaultColumns, getDefaultConfig, getMode, hasTradeContext, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenBreakeven, listenBreakevenOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenPing, listenPingOnce, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, partialLoss, partialProfit, setColumns, setConfig, setLogger, stop, trailingStop, trailingTake, validate };
28512
+ export { Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, Optimizer, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, breakeven, cancel, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getColumns, getConfig, getDate, getDefaultColumns, getDefaultConfig, getMode, getOrderBook, hasTradeContext, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenBreakeven, listenBreakevenOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenPing, listenPingOnce, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, partialLoss, partialProfit, setColumns, setConfig, setLogger, stop, trailingStop, trailingTake, validate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backtest-kit",
3
- "version": "1.11.8",
3
+ "version": "1.11.10",
4
4
  "description": "A TypeScript library for trading system backtest",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
package/types.d.ts CHANGED
@@ -460,7 +460,7 @@ interface IExchangeParams extends IExchangeSchema {
460
460
  /** Format price according to exchange precision rules (required, defaults applied) */
461
461
  formatPrice: (symbol: string, price: number) => Promise<string>;
462
462
  /** Fetch order book for a trading pair (required, defaults applied) */
463
- getOrderBook: (symbol: string, from: Date, to: Date) => Promise<IOrderBookData>;
463
+ getOrderBook: (symbol: string, depth: number, from: Date, to: Date) => Promise<IOrderBookData>;
464
464
  }
465
465
  /**
466
466
  * Optional callbacks for exchange data events.
@@ -514,11 +514,25 @@ interface IExchangeSchema {
514
514
  * Optional. If not provided, throws an error when called.
515
515
  *
516
516
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
517
- * @param from - Start of time range for order book snapshot
518
- * @param to - End of time range for order book snapshot
517
+ * @param depth - Maximum depth levels for both bids and asks (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
518
+ * @param from - Start of time range (used in backtest for historical data, can be ignored in live)
519
+ * @param to - End of time range (used in backtest for historical data, can be ignored in live)
519
520
  * @returns Promise resolving to order book data
521
+ *
522
+ * @example
523
+ * ```typescript
524
+ * // Backtest implementation: returns historical order book for the time range
525
+ * const backtestOrderBook = async (symbol: string, depth: number, from: Date, to: Date) => {
526
+ * return await database.getOrderBookSnapshot(symbol, depth, from, to);
527
+ * };
528
+ *
529
+ * // Live implementation: ignores from/to and returns current snapshot
530
+ * const liveOrderBook = async (symbol: string, depth: number, _from: Date, _to: Date) => {
531
+ * return await exchange.fetchOrderBook(symbol, depth);
532
+ * };
533
+ * ```
520
534
  */
521
- getOrderBook?: (symbol: string, from: Date, to: Date) => Promise<IOrderBookData>;
535
+ getOrderBook?: (symbol: string, depth: number, from: Date, to: Date) => Promise<IOrderBookData>;
522
536
  /** Optional lifecycle event callbacks (onCandleData) */
523
537
  callbacks?: Partial<IExchangeCallbacks>;
524
538
  }
@@ -575,9 +589,10 @@ interface IExchange {
575
589
  * Fetch order book for a trading pair.
576
590
  *
577
591
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
592
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
578
593
  * @returns Promise resolving to order book data
579
594
  */
580
- getOrderBook: (symbol: string) => Promise<IOrderBookData>;
595
+ getOrderBook: (symbol: string, depth?: number) => Promise<IOrderBookData>;
581
596
  }
582
597
  /**
583
598
  * Unique exchange identifier.
@@ -2124,6 +2139,13 @@ declare const GLOBAL_CONFIG: {
2124
2139
  * Default: 10 minutes
2125
2140
  */
2126
2141
  CC_ORDER_BOOK_TIME_OFFSET_MINUTES: number;
2142
+ /**
2143
+ * Maximum depth levels for order book fetching.
2144
+ * Specifies how many price levels to fetch from both bids and asks.
2145
+ *
2146
+ * Default: 20 levels
2147
+ */
2148
+ CC_ORDER_BOOK_MAX_DEPTH_LEVELS: number;
2127
2149
  };
2128
2150
  /**
2129
2151
  * Type for global configuration object.
@@ -2227,6 +2249,7 @@ declare function getConfig(): {
2227
2249
  CC_REPORT_SHOW_SIGNAL_NOTE: boolean;
2228
2250
  CC_BREAKEVEN_THRESHOLD: number;
2229
2251
  CC_ORDER_BOOK_TIME_OFFSET_MINUTES: number;
2252
+ CC_ORDER_BOOK_MAX_DEPTH_LEVELS: number;
2230
2253
  };
2231
2254
  /**
2232
2255
  * Retrieves the default configuration object for the framework.
@@ -2260,6 +2283,7 @@ declare function getDefaultConfig(): Readonly<{
2260
2283
  CC_REPORT_SHOW_SIGNAL_NOTE: boolean;
2261
2284
  CC_BREAKEVEN_THRESHOLD: number;
2262
2285
  CC_ORDER_BOOK_TIME_OFFSET_MINUTES: number;
2286
+ CC_ORDER_BOOK_MAX_DEPTH_LEVELS: number;
2263
2287
  }>;
2264
2288
  /**
2265
2289
  * Sets custom column configurations for markdown report generation.
@@ -5360,6 +5384,29 @@ declare function getDate(): Promise<Date>;
5360
5384
  * ```
5361
5385
  */
5362
5386
  declare function getMode(): Promise<"backtest" | "live">;
5387
+ /**
5388
+ * Fetches order book for a trading pair from the registered exchange.
5389
+ *
5390
+ * Uses current execution context to determine timing. The underlying exchange
5391
+ * implementation receives time range parameters but may use them (backtest)
5392
+ * or ignore them (live trading).
5393
+ *
5394
+ * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
5395
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
5396
+ * @returns Promise resolving to order book data
5397
+ * @throws Error if execution or method context is missing
5398
+ *
5399
+ * @example
5400
+ * ```typescript
5401
+ * const orderBook = await getOrderBook("BTCUSDT");
5402
+ * console.log(orderBook.bids); // [{ price: "50000.00", quantity: "0.5" }, ...]
5403
+ * console.log(orderBook.asks); // [{ price: "50001.00", quantity: "0.3" }, ...]
5404
+ *
5405
+ * // Fetch deeper order book
5406
+ * const deepBook = await getOrderBook("BTCUSDT", 100);
5407
+ * ```
5408
+ */
5409
+ declare function getOrderBook(symbol: string, depth?: number): Promise<IOrderBookData>;
5363
5410
 
5364
5411
  /**
5365
5412
  * Dumps signal data and LLM conversation history to markdown files.
@@ -11138,13 +11185,18 @@ declare class ExchangeUtils {
11138
11185
  /**
11139
11186
  * Fetch order book for a trading pair.
11140
11187
  *
11188
+ * Delegates to ExchangeInstance which calculates time range and passes it
11189
+ * to the exchange schema implementation. The from/to parameters may be used
11190
+ * (backtest) or ignored (live) depending on the implementation.
11191
+ *
11141
11192
  * @param symbol - Trading pair symbol
11142
11193
  * @param context - Execution context with exchange name
11194
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
11143
11195
  * @returns Promise resolving to order book data
11144
11196
  */
11145
11197
  getOrderBook: (symbol: string, context: {
11146
11198
  exchangeName: ExchangeName;
11147
- }) => Promise<IOrderBookData>;
11199
+ }, depth?: number) => Promise<IOrderBookData>;
11148
11200
  }
11149
11201
  /**
11150
11202
  * Singleton instance of ExchangeUtils for convenient exchange operations.
@@ -12057,11 +12109,16 @@ declare class ClientExchange implements IExchange {
12057
12109
  /**
12058
12110
  * Fetches order book for a trading pair.
12059
12111
  *
12112
+ * Calculates time range based on execution context time (when) and
12113
+ * CC_ORDER_BOOK_TIME_OFFSET_MINUTES, then delegates to the exchange
12114
+ * schema implementation which may use or ignore the time range.
12115
+ *
12060
12116
  * @param symbol - Trading pair symbol
12117
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
12061
12118
  * @returns Promise resolving to order book data
12062
12119
  * @throws Error if getOrderBook is not implemented
12063
12120
  */
12064
- getOrderBook(symbol: string): Promise<IOrderBookData>;
12121
+ getOrderBook(symbol: string, depth?: number): Promise<IOrderBookData>;
12065
12122
  }
12066
12123
 
12067
12124
  /**
@@ -12158,11 +12215,14 @@ declare class ExchangeConnectionService implements IExchange {
12158
12215
  * Fetches order book for a trading pair using configured exchange.
12159
12216
  *
12160
12217
  * Routes to exchange determined by methodContextService.context.exchangeName.
12218
+ * The ClientExchange will calculate time range and pass it to the schema
12219
+ * implementation, which may use (backtest) or ignore (live) the parameters.
12161
12220
  *
12162
12221
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
12222
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
12163
12223
  * @returns Promise resolving to order book data
12164
12224
  */
12165
- getOrderBook: (symbol: string) => Promise<IOrderBookData>;
12225
+ getOrderBook: (symbol: string, depth?: number) => Promise<IOrderBookData>;
12166
12226
  }
12167
12227
 
12168
12228
  /**
@@ -13222,12 +13282,17 @@ declare class ExchangeCoreService implements TExchange {
13222
13282
  /**
13223
13283
  * Fetches order book with execution context.
13224
13284
  *
13285
+ * Sets up execution context with the provided when/backtest parameters.
13286
+ * The exchange implementation will receive time range parameters but may
13287
+ * choose to use them (backtest) or ignore them (live).
13288
+ *
13225
13289
  * @param symbol - Trading pair symbol
13226
13290
  * @param when - Timestamp for context
13227
13291
  * @param backtest - Whether running in backtest mode
13292
+ * @param depth - Maximum depth levels (default: CC_ORDER_BOOK_MAX_DEPTH_LEVELS)
13228
13293
  * @returns Promise resolving to order book data
13229
13294
  */
13230
- getOrderBook: (symbol: string, when: Date, backtest: boolean) => Promise<IOrderBookData>;
13295
+ getOrderBook: (symbol: string, when: Date, backtest: boolean, depth?: number) => Promise<IOrderBookData>;
13231
13296
  }
13232
13297
 
13233
13298
  /**
@@ -16194,4 +16259,4 @@ declare const backtest: {
16194
16259
  loggerService: LoggerService;
16195
16260
  };
16196
16261
 
16197
- export { Backtest, type BacktestDoneNotification, type BacktestStatisticsModel, type BootstrapNotification, Breakeven, type BreakevenContract, type BreakevenData, Cache, type CandleInterval, type ColumnConfig, type ColumnModel, Constant, type CriticalErrorNotification, type DoneContract, type EntityId, Exchange, ExecutionContextService, type FrameInterval, type GlobalConfig, Heat, type HeatmapStatisticsModel, type IBidData, type ICandleData, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type IMarkdownDumpOptions, type IOptimizerCallbacks, type IOptimizerData, type IOptimizerFetchArgs, type IOptimizerFilterArgs, type IOptimizerRange, type IOptimizerSchema, type IOptimizerSource, type IOptimizerStrategy, type IOptimizerTemplate, type IOrderBookData, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IPublicSignalRow, type IReportDumpOptions, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type IScheduledSignalCancelRow, type IScheduledSignalRow, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStrategyPnL, type IStrategyResult, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultCancelled, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IStrategyTickResultScheduled, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, type InfoErrorNotification, Live, type LiveDoneNotification, type LiveStatisticsModel, Markdown, MarkdownFileBase, MarkdownFolderBase, type MarkdownName, type MessageModel, type MessageRole, MethodContextService, type MetricStats, Notification, type NotificationModel, Optimizer, Partial$1 as Partial, type PartialData, type PartialEvent, type PartialLossContract, type PartialLossNotification, type PartialProfitContract, type PartialProfitNotification, type PartialStatisticsModel, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatisticsModel, PersistBase, PersistBreakevenAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, type PingContract, PositionSize, type ProgressBacktestContract, type ProgressBacktestNotification, type ProgressOptimizerContract, type ProgressWalkerContract, Report, ReportBase, type ReportName, Risk, type RiskContract, type RiskData, type RiskEvent, type RiskRejectionNotification, type RiskStatisticsModel, Schedule, type ScheduleData, type ScheduleStatisticsModel, type ScheduledEvent, type SignalCancelledNotification, type SignalClosedNotification, type SignalData, type SignalInterval, type SignalOpenedNotification, type SignalScheduledNotification, type TMarkdownBase, type TPersistBase, type TPersistBaseCtor, type TReportBase, type TickEvent, type ValidationErrorNotification, Walker, type WalkerCompleteContract, type WalkerContract, type WalkerMetric, type SignalData$1 as WalkerSignalData, type WalkerStatisticsModel, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, breakeven, cancel, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getColumns, getConfig, getDate, getDefaultColumns, getDefaultConfig, getMode, hasTradeContext, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenBreakeven, listenBreakevenOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenPing, listenPingOnce, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, partialLoss, partialProfit, setColumns, setConfig, setLogger, stop, trailingStop, trailingTake, validate };
16262
+ export { Backtest, type BacktestDoneNotification, type BacktestStatisticsModel, type BootstrapNotification, Breakeven, type BreakevenContract, type BreakevenData, Cache, type CandleInterval, type ColumnConfig, type ColumnModel, Constant, type CriticalErrorNotification, type DoneContract, type EntityId, Exchange, ExecutionContextService, type FrameInterval, type GlobalConfig, Heat, type HeatmapStatisticsModel, type IBidData, type ICandleData, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type IMarkdownDumpOptions, type IOptimizerCallbacks, type IOptimizerData, type IOptimizerFetchArgs, type IOptimizerFilterArgs, type IOptimizerRange, type IOptimizerSchema, type IOptimizerSource, type IOptimizerStrategy, type IOptimizerTemplate, type IOrderBookData, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IPublicSignalRow, type IReportDumpOptions, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type IScheduledSignalCancelRow, type IScheduledSignalRow, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStrategyPnL, type IStrategyResult, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultCancelled, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IStrategyTickResultScheduled, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, type InfoErrorNotification, Live, type LiveDoneNotification, type LiveStatisticsModel, Markdown, MarkdownFileBase, MarkdownFolderBase, type MarkdownName, type MessageModel, type MessageRole, MethodContextService, type MetricStats, Notification, type NotificationModel, Optimizer, Partial$1 as Partial, type PartialData, type PartialEvent, type PartialLossContract, type PartialLossNotification, type PartialProfitContract, type PartialProfitNotification, type PartialStatisticsModel, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatisticsModel, PersistBase, PersistBreakevenAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, type PingContract, PositionSize, type ProgressBacktestContract, type ProgressBacktestNotification, type ProgressOptimizerContract, type ProgressWalkerContract, Report, ReportBase, type ReportName, Risk, type RiskContract, type RiskData, type RiskEvent, type RiskRejectionNotification, type RiskStatisticsModel, Schedule, type ScheduleData, type ScheduleStatisticsModel, type ScheduledEvent, type SignalCancelledNotification, type SignalClosedNotification, type SignalData, type SignalInterval, type SignalOpenedNotification, type SignalScheduledNotification, type TMarkdownBase, type TPersistBase, type TPersistBaseCtor, type TReportBase, type TickEvent, type ValidationErrorNotification, Walker, type WalkerCompleteContract, type WalkerContract, type WalkerMetric, type SignalData$1 as WalkerSignalData, type WalkerStatisticsModel, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, breakeven, cancel, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getColumns, getConfig, getDate, getDefaultColumns, getDefaultConfig, getMode, getOrderBook, hasTradeContext, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenBreakeven, listenBreakevenOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenPing, listenPingOnce, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, partialLoss, partialProfit, setColumns, setConfig, setLogger, stop, trailingStop, trailingTake, validate };