backtest-kit 2.2.14 → 2.2.15

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
@@ -2829,6 +2829,114 @@ const INTERVAL_MINUTES$3 = {
2829
2829
  "1h": 60,
2830
2830
  };
2831
2831
  const TIMEOUT_SYMBOL = Symbol('timeout');
2832
+ /**
2833
+ * Calls onCommit callback with strategy commit event.
2834
+ *
2835
+ * Wraps the callback in trycatch to prevent errors from breaking the flow.
2836
+ * Used by ClientStrategy methods that modify signal state (partial, trailing, breakeven, cancel, close).
2837
+ *
2838
+ * @param self - ClientStrategy instance
2839
+ * @param event - Strategy commit event to emit
2840
+ */
2841
+ const CALL_COMMIT_FN = functoolsKit.trycatch(async (self, event) => {
2842
+ await self.params.onCommit(event);
2843
+ }, {
2844
+ fallback: (error) => {
2845
+ const message = "ClientStrategy CALL_COMMIT_FN thrown";
2846
+ const payload = {
2847
+ error: functoolsKit.errorData(error),
2848
+ message: functoolsKit.getErrorMessage(error),
2849
+ };
2850
+ bt.loggerService.warn(message, payload);
2851
+ console.warn(message, payload);
2852
+ errorEmitter.next(error);
2853
+ },
2854
+ });
2855
+ /**
2856
+ * Processes queued commit events with proper execution context timestamp.
2857
+ *
2858
+ * Commit events from partialProfit, partialLoss, breakeven, trailingStop, trailingTake
2859
+ * are queued in _commitQueue and processed here with correct timestamp from
2860
+ * execution context (tick's when or backtest candle timestamp).
2861
+ *
2862
+ * @param self - ClientStrategy instance
2863
+ * @param timestamp - Timestamp from execution context
2864
+ */
2865
+ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
2866
+ if (self._commitQueue.length === 0) {
2867
+ return;
2868
+ }
2869
+ const queue = self._commitQueue;
2870
+ self._commitQueue = [];
2871
+ for (const commit of queue) {
2872
+ switch (commit.action) {
2873
+ case "partial-profit":
2874
+ await CALL_COMMIT_FN(self, {
2875
+ action: "partial-profit",
2876
+ symbol: commit.symbol,
2877
+ strategyName: self.params.strategyName,
2878
+ exchangeName: self.params.exchangeName,
2879
+ frameName: self.params.frameName,
2880
+ backtest: commit.backtest,
2881
+ percentToClose: commit.percentToClose,
2882
+ currentPrice: commit.currentPrice,
2883
+ timestamp,
2884
+ });
2885
+ break;
2886
+ case "partial-loss":
2887
+ await CALL_COMMIT_FN(self, {
2888
+ action: "partial-loss",
2889
+ symbol: commit.symbol,
2890
+ strategyName: self.params.strategyName,
2891
+ exchangeName: self.params.exchangeName,
2892
+ frameName: self.params.frameName,
2893
+ backtest: commit.backtest,
2894
+ percentToClose: commit.percentToClose,
2895
+ currentPrice: commit.currentPrice,
2896
+ timestamp,
2897
+ });
2898
+ break;
2899
+ case "breakeven":
2900
+ await CALL_COMMIT_FN(self, {
2901
+ action: "breakeven",
2902
+ symbol: commit.symbol,
2903
+ strategyName: self.params.strategyName,
2904
+ exchangeName: self.params.exchangeName,
2905
+ frameName: self.params.frameName,
2906
+ backtest: commit.backtest,
2907
+ currentPrice: commit.currentPrice,
2908
+ timestamp,
2909
+ });
2910
+ break;
2911
+ case "trailing-stop":
2912
+ await CALL_COMMIT_FN(self, {
2913
+ action: "trailing-stop",
2914
+ symbol: commit.symbol,
2915
+ strategyName: self.params.strategyName,
2916
+ exchangeName: self.params.exchangeName,
2917
+ frameName: self.params.frameName,
2918
+ backtest: commit.backtest,
2919
+ percentShift: commit.percentShift,
2920
+ currentPrice: commit.currentPrice,
2921
+ timestamp,
2922
+ });
2923
+ break;
2924
+ case "trailing-take":
2925
+ await CALL_COMMIT_FN(self, {
2926
+ action: "trailing-take",
2927
+ symbol: commit.symbol,
2928
+ strategyName: self.params.strategyName,
2929
+ exchangeName: self.params.exchangeName,
2930
+ frameName: self.params.frameName,
2931
+ backtest: commit.backtest,
2932
+ percentShift: commit.percentShift,
2933
+ currentPrice: commit.currentPrice,
2934
+ timestamp,
2935
+ });
2936
+ break;
2937
+ }
2938
+ }
2939
+ };
2832
2940
  /**
2833
2941
  * Converts internal signal to public API format.
2834
2942
  *
@@ -4775,6 +4883,8 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
4775
4883
  };
4776
4884
  }
4777
4885
  await CALL_SCHEDULE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, candle.timestamp, true);
4886
+ // Process queued commit events with candle timestamp
4887
+ await PROCESS_COMMIT_QUEUE_FN(self, candle.timestamp);
4778
4888
  }
4779
4889
  return {
4780
4890
  activated: false,
@@ -4900,6 +5010,8 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
4900
5010
  }
4901
5011
  }
4902
5012
  }
5013
+ // Process queued commit events with candle timestamp
5014
+ await PROCESS_COMMIT_QUEUE_FN(self, currentCandleTimestamp);
4903
5015
  }
4904
5016
  return null;
4905
5017
  };
@@ -4939,6 +5051,8 @@ class ClientStrategy {
4939
5051
  this._scheduledSignal = null;
4940
5052
  this._cancelledSignal = null;
4941
5053
  this._closedSignal = null;
5054
+ /** Queue for commit events to be processed in tick()/backtest() with proper timestamp */
5055
+ this._commitQueue = [];
4942
5056
  /**
4943
5057
  * Initializes strategy state by loading persisted signal from disk.
4944
5058
  *
@@ -5186,6 +5300,8 @@ class ClientStrategy {
5186
5300
  });
5187
5301
  // Получаем текущее время в начале tick для консистентности
5188
5302
  const currentTime = this.params.execution.context.when.getTime();
5303
+ // Process queued commit events with proper timestamp
5304
+ await PROCESS_COMMIT_QUEUE_FN(this, currentTime);
5189
5305
  // Early return if strategy was stopped
5190
5306
  if (this._isStopped) {
5191
5307
  const currentPrice = await this.params.exchange.getAveragePrice(this.params.execution.context.symbol);
@@ -5576,6 +5692,7 @@ class ClientStrategy {
5576
5692
  cancelId,
5577
5693
  });
5578
5694
  // Save cancelled signal for next tick to emit cancelled event
5695
+ const hadScheduledSignal = this._scheduledSignal !== null;
5579
5696
  if (this._scheduledSignal) {
5580
5697
  this._cancelledSignal = Object.assign({}, this._scheduledSignal, {
5581
5698
  cancelId,
@@ -5583,9 +5700,35 @@ class ClientStrategy {
5583
5700
  this._scheduledSignal = null;
5584
5701
  }
5585
5702
  if (backtest) {
5703
+ // Emit commit event only if signal was actually cancelled
5704
+ if (hadScheduledSignal) {
5705
+ await CALL_COMMIT_FN(this, {
5706
+ action: "cancel-scheduled",
5707
+ symbol,
5708
+ strategyName: this.params.strategyName,
5709
+ exchangeName: this.params.exchangeName,
5710
+ frameName: this.params.frameName,
5711
+ backtest,
5712
+ cancelId,
5713
+ timestamp: this.params.execution.context.when.getTime(),
5714
+ });
5715
+ }
5586
5716
  return;
5587
5717
  }
5588
5718
  await PersistScheduleAdapter.writeScheduleData(this._scheduledSignal, symbol, this.params.method.context.strategyName, this.params.method.context.exchangeName);
5719
+ // Emit commit event only if signal was actually cancelled
5720
+ if (hadScheduledSignal) {
5721
+ await CALL_COMMIT_FN(this, {
5722
+ action: "cancel-scheduled",
5723
+ symbol,
5724
+ strategyName: this.params.strategyName,
5725
+ exchangeName: this.params.exchangeName,
5726
+ frameName: this.params.frameName,
5727
+ backtest,
5728
+ cancelId,
5729
+ timestamp: this.params.execution.context.when.getTime(),
5730
+ });
5731
+ }
5589
5732
  }
5590
5733
  /**
5591
5734
  * Closes the pending signal without stopping the strategy.
@@ -5615,6 +5758,7 @@ class ClientStrategy {
5615
5758
  closeId,
5616
5759
  });
5617
5760
  // Save closed signal for next tick to emit closed event
5761
+ const hadPendingSignal = this._pendingSignal !== null;
5618
5762
  if (this._pendingSignal) {
5619
5763
  this._closedSignal = Object.assign({}, this._pendingSignal, {
5620
5764
  closeId,
@@ -5622,9 +5766,35 @@ class ClientStrategy {
5622
5766
  this._pendingSignal = null;
5623
5767
  }
5624
5768
  if (backtest) {
5769
+ // Emit commit event only if signal was actually closed
5770
+ if (hadPendingSignal) {
5771
+ await CALL_COMMIT_FN(this, {
5772
+ action: "close-pending",
5773
+ symbol,
5774
+ strategyName: this.params.strategyName,
5775
+ exchangeName: this.params.exchangeName,
5776
+ frameName: this.params.frameName,
5777
+ backtest,
5778
+ closeId,
5779
+ timestamp: this.params.execution.context.when.getTime(),
5780
+ });
5781
+ }
5625
5782
  return;
5626
5783
  }
5627
5784
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, symbol, this.params.strategyName, this.params.exchangeName);
5785
+ // Emit commit event only if signal was actually closed
5786
+ if (hadPendingSignal) {
5787
+ await CALL_COMMIT_FN(this, {
5788
+ action: "close-pending",
5789
+ symbol,
5790
+ strategyName: this.params.strategyName,
5791
+ exchangeName: this.params.exchangeName,
5792
+ frameName: this.params.frameName,
5793
+ backtest,
5794
+ closeId,
5795
+ timestamp: this.params.execution.context.when.getTime(),
5796
+ });
5797
+ }
5628
5798
  }
5629
5799
  /**
5630
5800
  * Executes partial close at profit level (moving toward TP).
@@ -5748,6 +5918,14 @@ class ClientStrategy {
5748
5918
  if (!backtest) {
5749
5919
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.execution.context.symbol, this.params.strategyName, this.params.exchangeName);
5750
5920
  }
5921
+ // Queue commit event for processing in tick()/backtest() with proper timestamp
5922
+ this._commitQueue.push({
5923
+ action: "partial-profit",
5924
+ symbol,
5925
+ backtest,
5926
+ percentToClose,
5927
+ currentPrice,
5928
+ });
5751
5929
  return true;
5752
5930
  }
5753
5931
  /**
@@ -5872,6 +6050,14 @@ class ClientStrategy {
5872
6050
  if (!backtest) {
5873
6051
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.execution.context.symbol, this.params.strategyName, this.params.exchangeName);
5874
6052
  }
6053
+ // Queue commit event for processing in tick()/backtest() with proper timestamp
6054
+ this._commitQueue.push({
6055
+ action: "partial-loss",
6056
+ symbol,
6057
+ backtest,
6058
+ percentToClose,
6059
+ currentPrice,
6060
+ });
5875
6061
  return true;
5876
6062
  }
5877
6063
  /**
@@ -5987,6 +6173,13 @@ class ClientStrategy {
5987
6173
  if (!backtest) {
5988
6174
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.execution.context.symbol, this.params.strategyName, this.params.exchangeName);
5989
6175
  }
6176
+ // Queue commit event for processing in tick()/backtest() with proper timestamp
6177
+ this._commitQueue.push({
6178
+ action: "breakeven",
6179
+ symbol,
6180
+ backtest,
6181
+ currentPrice,
6182
+ });
5990
6183
  return true;
5991
6184
  }
5992
6185
  /**
@@ -6166,6 +6359,14 @@ class ClientStrategy {
6166
6359
  if (!backtest) {
6167
6360
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.execution.context.symbol, this.params.strategyName, this.params.exchangeName);
6168
6361
  }
6362
+ // Queue commit event for processing in tick()/backtest() with proper timestamp
6363
+ this._commitQueue.push({
6364
+ action: "trailing-stop",
6365
+ symbol,
6366
+ backtest,
6367
+ percentShift,
6368
+ currentPrice,
6369
+ });
6169
6370
  return true;
6170
6371
  }
6171
6372
  /**
@@ -6331,6 +6532,14 @@ class ClientStrategy {
6331
6532
  if (!backtest) {
6332
6533
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.execution.context.symbol, this.params.strategyName, this.params.exchangeName);
6333
6534
  }
6535
+ // Queue commit event for processing in tick()/backtest() with proper timestamp
6536
+ this._commitQueue.push({
6537
+ action: "trailing-take",
6538
+ symbol,
6539
+ backtest,
6540
+ percentShift,
6541
+ currentPrice,
6542
+ });
6334
6543
  return true;
6335
6544
  }
6336
6545
  }
@@ -6843,6 +7052,31 @@ const CREATE_COMMIT_INIT_FN = (self) => functoolsKit.trycatch(async (symbol, str
6843
7052
  },
6844
7053
  defaultValue: null,
6845
7054
  });
7055
+ /**
7056
+ * Creates a callback function for emitting commit events to strategyCommitSubject.
7057
+ *
7058
+ * Called by ClientStrategy when strategy management actions are executed
7059
+ * (partialProfit, partialLoss, trailingStop, trailingTake, breakeven, cancelScheduled, closePending).
7060
+ * Emits StrategyCommitContract event to all subscribers.
7061
+ *
7062
+ * @param self - Reference to StrategyConnectionService instance
7063
+ * @returns Callback function for commit events
7064
+ */
7065
+ const CREATE_COMMIT_FN = (self) => functoolsKit.trycatch(async (event) => {
7066
+ await strategyCommitSubject.next(event);
7067
+ }, {
7068
+ fallback: (error) => {
7069
+ const message = "StrategyConnectionService CREATE_COMMIT_FN thrown";
7070
+ const payload = {
7071
+ error: functoolsKit.errorData(error),
7072
+ message: functoolsKit.getErrorMessage(error),
7073
+ };
7074
+ bt.loggerService.warn(message, payload);
7075
+ console.warn(message, payload);
7076
+ errorEmitter.next(error);
7077
+ },
7078
+ defaultValue: null,
7079
+ });
6846
7080
  /**
6847
7081
  * Creates a callback function for emitting dispose events.
6848
7082
  *
@@ -6937,6 +7171,7 @@ class StrategyConnectionService {
6937
7171
  onSchedulePing: CREATE_COMMIT_SCHEDULE_PING_FN(this),
6938
7172
  onActivePing: CREATE_COMMIT_ACTIVE_PING_FN(this),
6939
7173
  onDispose: CREATE_COMMIT_DISPOSE_FN(this),
7174
+ onCommit: CREATE_COMMIT_FN(),
6940
7175
  });
6941
7176
  });
6942
7177
  /**
@@ -10223,26 +10458,6 @@ const CREATE_KEY_FN$h = (context) => {
10223
10458
  parts.push(context.frameName);
10224
10459
  return parts.join(":");
10225
10460
  };
10226
- /**
10227
- * Broadcasts StrategyCommitContract event to strategyCommitSubject.
10228
- *
10229
- * @param event - The signal commit event to broadcast
10230
- */
10231
- const CALL_STRATEGY_COMMIT_FN = functoolsKit.trycatch(async (event) => {
10232
- await strategyCommitSubject.next(event);
10233
- }, {
10234
- fallback: (error) => {
10235
- const message = "StrategyCoreService CALL_STRATEGY_COMMIT_FN thrown";
10236
- const payload = {
10237
- error: functoolsKit.errorData(error),
10238
- message: functoolsKit.getErrorMessage(error),
10239
- };
10240
- bt.loggerService.warn(message, payload);
10241
- console.warn(message, payload);
10242
- errorEmitter.next(error);
10243
- },
10244
- defaultValue: null,
10245
- });
10246
10461
  /**
10247
10462
  * Global service for strategy operations with execution context injection.
10248
10463
  *
@@ -10475,19 +10690,7 @@ class StrategyCoreService {
10475
10690
  cancelId,
10476
10691
  });
10477
10692
  await this.validate(context);
10478
- const result = await this.strategyConnectionService.cancelScheduled(backtest, symbol, context, cancelId);
10479
- {
10480
- await CALL_STRATEGY_COMMIT_FN({
10481
- action: "cancel-scheduled",
10482
- symbol,
10483
- strategyName: context.strategyName,
10484
- exchangeName: context.exchangeName,
10485
- frameName: context.frameName,
10486
- backtest,
10487
- cancelId,
10488
- });
10489
- }
10490
- return result;
10693
+ return await this.strategyConnectionService.cancelScheduled(backtest, symbol, context, cancelId);
10491
10694
  };
10492
10695
  /**
10493
10696
  * Closes the pending signal without stopping the strategy.
@@ -10514,19 +10717,7 @@ class StrategyCoreService {
10514
10717
  closeId,
10515
10718
  });
10516
10719
  await this.validate(context);
10517
- const result = await this.strategyConnectionService.closePending(backtest, symbol, context, closeId);
10518
- {
10519
- await CALL_STRATEGY_COMMIT_FN({
10520
- action: "close-pending",
10521
- symbol,
10522
- strategyName: context.strategyName,
10523
- exchangeName: context.exchangeName,
10524
- frameName: context.frameName,
10525
- backtest,
10526
- closeId,
10527
- });
10528
- }
10529
- return result;
10720
+ return await this.strategyConnectionService.closePending(backtest, symbol, context, closeId);
10530
10721
  };
10531
10722
  /**
10532
10723
  * Disposes the ClientStrategy instance for the given context.
@@ -10607,20 +10798,7 @@ class StrategyCoreService {
10607
10798
  backtest,
10608
10799
  });
10609
10800
  await this.validate(context);
10610
- const result = await this.strategyConnectionService.partialProfit(backtest, symbol, percentToClose, currentPrice, context);
10611
- if (result) {
10612
- await CALL_STRATEGY_COMMIT_FN({
10613
- action: "partial-profit",
10614
- symbol,
10615
- strategyName: context.strategyName,
10616
- exchangeName: context.exchangeName,
10617
- frameName: context.frameName,
10618
- backtest,
10619
- percentToClose,
10620
- currentPrice,
10621
- });
10622
- }
10623
- return result;
10801
+ return await this.strategyConnectionService.partialProfit(backtest, symbol, percentToClose, currentPrice, context);
10624
10802
  };
10625
10803
  /**
10626
10804
  * Executes partial close at loss level (moving toward SL).
@@ -10661,20 +10839,7 @@ class StrategyCoreService {
10661
10839
  backtest,
10662
10840
  });
10663
10841
  await this.validate(context);
10664
- const result = await this.strategyConnectionService.partialLoss(backtest, symbol, percentToClose, currentPrice, context);
10665
- if (result) {
10666
- await CALL_STRATEGY_COMMIT_FN({
10667
- action: "partial-loss",
10668
- symbol,
10669
- strategyName: context.strategyName,
10670
- exchangeName: context.exchangeName,
10671
- frameName: context.frameName,
10672
- backtest,
10673
- percentToClose,
10674
- currentPrice,
10675
- });
10676
- }
10677
- return result;
10842
+ return await this.strategyConnectionService.partialLoss(backtest, symbol, percentToClose, currentPrice, context);
10678
10843
  };
10679
10844
  /**
10680
10845
  * Adjusts the trailing stop-loss distance for an active pending signal.
@@ -10713,20 +10878,7 @@ class StrategyCoreService {
10713
10878
  backtest,
10714
10879
  });
10715
10880
  await this.validate(context);
10716
- const result = await this.strategyConnectionService.trailingStop(backtest, symbol, percentShift, currentPrice, context);
10717
- if (result) {
10718
- await CALL_STRATEGY_COMMIT_FN({
10719
- action: "trailing-stop",
10720
- symbol,
10721
- strategyName: context.strategyName,
10722
- exchangeName: context.exchangeName,
10723
- frameName: context.frameName,
10724
- backtest,
10725
- percentShift,
10726
- currentPrice,
10727
- });
10728
- }
10729
- return result;
10881
+ return await this.strategyConnectionService.trailingStop(backtest, symbol, percentShift, currentPrice, context);
10730
10882
  };
10731
10883
  /**
10732
10884
  * Adjusts the trailing take-profit distance for an active pending signal.
@@ -10761,20 +10913,7 @@ class StrategyCoreService {
10761
10913
  backtest,
10762
10914
  });
10763
10915
  await this.validate(context);
10764
- const result = await this.strategyConnectionService.trailingTake(backtest, symbol, percentShift, currentPrice, context);
10765
- if (result) {
10766
- await CALL_STRATEGY_COMMIT_FN({
10767
- action: "trailing-take",
10768
- symbol,
10769
- strategyName: context.strategyName,
10770
- exchangeName: context.exchangeName,
10771
- frameName: context.frameName,
10772
- backtest,
10773
- percentShift,
10774
- currentPrice,
10775
- });
10776
- }
10777
- return result;
10916
+ return await this.strategyConnectionService.trailingTake(backtest, symbol, percentShift, currentPrice, context);
10778
10917
  };
10779
10918
  /**
10780
10919
  * Moves stop-loss to breakeven when price reaches threshold.
@@ -10804,19 +10943,7 @@ class StrategyCoreService {
10804
10943
  backtest,
10805
10944
  });
10806
10945
  await this.validate(context);
10807
- const result = await this.strategyConnectionService.breakeven(backtest, symbol, currentPrice, context);
10808
- if (result) {
10809
- await CALL_STRATEGY_COMMIT_FN({
10810
- action: "breakeven",
10811
- symbol,
10812
- strategyName: context.strategyName,
10813
- exchangeName: context.exchangeName,
10814
- frameName: context.frameName,
10815
- backtest,
10816
- currentPrice,
10817
- });
10818
- }
10819
- return result;
10946
+ return await this.strategyConnectionService.breakeven(backtest, symbol, currentPrice, context);
10820
10947
  };
10821
10948
  }
10822
10949
  }
@@ -23549,22 +23676,6 @@ class RiskReportService {
23549
23676
  }
23550
23677
  }
23551
23678
 
23552
- /**
23553
- * Extracts execution context timestamp for strategy event logging.
23554
- *
23555
- * @param self - The StrategyReportService instance to extract context from
23556
- * @returns Object containing ISO 8601 formatted timestamp, or empty string if no context
23557
- * @internal
23558
- */
23559
- const GET_EXECUTION_CONTEXT_FN$1 = (self) => {
23560
- if (ExecutionContextService.hasContext()) {
23561
- const { when } = self.executionContextService.context;
23562
- return { when: when.toISOString() };
23563
- }
23564
- return {
23565
- when: "",
23566
- };
23567
- };
23568
23679
  /**
23569
23680
  * Service for persisting strategy management events to JSON report files.
23570
23681
  *
@@ -23580,41 +23691,23 @@ const GET_EXECUTION_CONTEXT_FN$1 = (self) => {
23580
23691
  * - Events are written via Report.writeData() with "strategy" category
23581
23692
  * - Call unsubscribe() to disable event logging
23582
23693
  *
23583
- * @example
23584
- * ```typescript
23585
- * // Service is typically used internally by strategy management classes
23586
- * strategyReportService.subscribe();
23587
- *
23588
- * // Events are logged automatically when strategy actions occur
23589
- * await strategyReportService.partialProfit("BTCUSDT", 50, 50100, false, {
23590
- * strategyName: "my-strategy",
23591
- * exchangeName: "binance",
23592
- * frameName: "1h"
23593
- * });
23594
- *
23595
- * strategyReportService.unsubscribe();
23596
- * ```
23597
- *
23598
23694
  * @see StrategyMarkdownService for in-memory event accumulation and markdown report generation
23599
23695
  * @see Report for the underlying persistence mechanism
23600
23696
  */
23601
23697
  class StrategyReportService {
23602
23698
  constructor() {
23603
23699
  this.loggerService = inject(TYPES.loggerService);
23604
- this.executionContextService = inject(TYPES.executionContextService);
23605
23700
  this.strategyCoreService = inject(TYPES.strategyCoreService);
23606
23701
  /**
23607
23702
  * Logs a cancel-scheduled event when a scheduled signal is cancelled.
23608
23703
  *
23609
- * Retrieves the scheduled signal from StrategyCoreService and writes
23610
- * the cancellation event to the report file.
23611
- *
23612
23704
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23613
23705
  * @param isBacktest - Whether this is a backtest or live trading event
23614
23706
  * @param context - Strategy context with strategyName, exchangeName, frameName
23707
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23615
23708
  * @param cancelId - Optional identifier for the cancellation reason
23616
23709
  */
23617
- this.cancelScheduled = async (symbol, isBacktest, context, cancelId) => {
23710
+ this.cancelScheduled = async (symbol, isBacktest, context, timestamp, cancelId) => {
23618
23711
  this.loggerService.log("strategyReportService cancelScheduled", {
23619
23712
  symbol,
23620
23713
  isBacktest,
@@ -23623,7 +23716,6 @@ class StrategyReportService {
23623
23716
  if (!this.subscribe.hasValue()) {
23624
23717
  return;
23625
23718
  }
23626
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23627
23719
  const scheduledRow = await this.strategyCoreService.getScheduledSignal(isBacktest, symbol, {
23628
23720
  exchangeName: context.exchangeName,
23629
23721
  strategyName: context.strategyName,
@@ -23632,10 +23724,12 @@ class StrategyReportService {
23632
23724
  if (!scheduledRow) {
23633
23725
  return;
23634
23726
  }
23727
+ const createdAt = new Date(timestamp).toISOString();
23635
23728
  await Report.writeData("strategy", {
23636
23729
  action: "cancel-scheduled",
23637
23730
  cancelId,
23638
23731
  symbol,
23732
+ timestamp,
23639
23733
  createdAt,
23640
23734
  }, {
23641
23735
  signalId: scheduledRow.id,
@@ -23649,15 +23743,13 @@ class StrategyReportService {
23649
23743
  /**
23650
23744
  * Logs a close-pending event when a pending signal is closed.
23651
23745
  *
23652
- * Retrieves the pending signal from StrategyCoreService and writes
23653
- * the close event to the report file.
23654
- *
23655
23746
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23656
23747
  * @param isBacktest - Whether this is a backtest or live trading event
23657
23748
  * @param context - Strategy context with strategyName, exchangeName, frameName
23749
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23658
23750
  * @param closeId - Optional identifier for the close reason
23659
23751
  */
23660
- this.closePending = async (symbol, isBacktest, context, closeId) => {
23752
+ this.closePending = async (symbol, isBacktest, context, timestamp, closeId) => {
23661
23753
  this.loggerService.log("strategyReportService closePending", {
23662
23754
  symbol,
23663
23755
  isBacktest,
@@ -23666,7 +23758,6 @@ class StrategyReportService {
23666
23758
  if (!this.subscribe.hasValue()) {
23667
23759
  return;
23668
23760
  }
23669
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23670
23761
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
23671
23762
  exchangeName: context.exchangeName,
23672
23763
  strategyName: context.strategyName,
@@ -23675,10 +23766,12 @@ class StrategyReportService {
23675
23766
  if (!pendingRow) {
23676
23767
  return;
23677
23768
  }
23769
+ const createdAt = new Date(timestamp).toISOString();
23678
23770
  await Report.writeData("strategy", {
23679
23771
  action: "close-pending",
23680
23772
  closeId,
23681
23773
  symbol,
23774
+ timestamp,
23682
23775
  createdAt,
23683
23776
  }, {
23684
23777
  signalId: pendingRow.id,
@@ -23692,15 +23785,14 @@ class StrategyReportService {
23692
23785
  /**
23693
23786
  * Logs a partial-profit event when a portion of the position is closed at profit.
23694
23787
  *
23695
- * Records the percentage closed and current price when partial profit-taking occurs.
23696
- *
23697
23788
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23698
23789
  * @param percentToClose - Percentage of position to close (0-100)
23699
23790
  * @param currentPrice - Current market price at time of partial close
23700
23791
  * @param isBacktest - Whether this is a backtest or live trading event
23701
23792
  * @param context - Strategy context with strategyName, exchangeName, frameName
23793
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23702
23794
  */
23703
- this.partialProfit = async (symbol, percentToClose, currentPrice, isBacktest, context) => {
23795
+ this.partialProfit = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp) => {
23704
23796
  this.loggerService.log("strategyReportService partialProfit", {
23705
23797
  symbol,
23706
23798
  percentToClose,
@@ -23710,7 +23802,6 @@ class StrategyReportService {
23710
23802
  if (!this.subscribe.hasValue()) {
23711
23803
  return;
23712
23804
  }
23713
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23714
23805
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
23715
23806
  exchangeName: context.exchangeName,
23716
23807
  strategyName: context.strategyName,
@@ -23719,11 +23810,13 @@ class StrategyReportService {
23719
23810
  if (!pendingRow) {
23720
23811
  return;
23721
23812
  }
23813
+ const createdAt = new Date(timestamp).toISOString();
23722
23814
  await Report.writeData("strategy", {
23723
23815
  action: "partial-profit",
23724
23816
  percentToClose,
23725
23817
  currentPrice,
23726
23818
  symbol,
23819
+ timestamp,
23727
23820
  createdAt,
23728
23821
  }, {
23729
23822
  signalId: pendingRow.id,
@@ -23737,15 +23830,14 @@ class StrategyReportService {
23737
23830
  /**
23738
23831
  * Logs a partial-loss event when a portion of the position is closed at loss.
23739
23832
  *
23740
- * Records the percentage closed and current price when partial loss-cutting occurs.
23741
- *
23742
23833
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23743
23834
  * @param percentToClose - Percentage of position to close (0-100)
23744
23835
  * @param currentPrice - Current market price at time of partial close
23745
23836
  * @param isBacktest - Whether this is a backtest or live trading event
23746
23837
  * @param context - Strategy context with strategyName, exchangeName, frameName
23838
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23747
23839
  */
23748
- this.partialLoss = async (symbol, percentToClose, currentPrice, isBacktest, context) => {
23840
+ this.partialLoss = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp) => {
23749
23841
  this.loggerService.log("strategyReportService partialLoss", {
23750
23842
  symbol,
23751
23843
  percentToClose,
@@ -23755,7 +23847,6 @@ class StrategyReportService {
23755
23847
  if (!this.subscribe.hasValue()) {
23756
23848
  return;
23757
23849
  }
23758
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23759
23850
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
23760
23851
  exchangeName: context.exchangeName,
23761
23852
  strategyName: context.strategyName,
@@ -23764,11 +23855,13 @@ class StrategyReportService {
23764
23855
  if (!pendingRow) {
23765
23856
  return;
23766
23857
  }
23858
+ const createdAt = new Date(timestamp).toISOString();
23767
23859
  await Report.writeData("strategy", {
23768
23860
  action: "partial-loss",
23769
23861
  percentToClose,
23770
23862
  currentPrice,
23771
23863
  symbol,
23864
+ timestamp,
23772
23865
  createdAt,
23773
23866
  }, {
23774
23867
  signalId: pendingRow.id,
@@ -23782,15 +23875,14 @@ class StrategyReportService {
23782
23875
  /**
23783
23876
  * Logs a trailing-stop event when the stop-loss is adjusted.
23784
23877
  *
23785
- * Records the percentage shift and current price when trailing stop moves.
23786
- *
23787
23878
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23788
23879
  * @param percentShift - Percentage the stop-loss was shifted
23789
23880
  * @param currentPrice - Current market price at time of adjustment
23790
23881
  * @param isBacktest - Whether this is a backtest or live trading event
23791
23882
  * @param context - Strategy context with strategyName, exchangeName, frameName
23883
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23792
23884
  */
23793
- this.trailingStop = async (symbol, percentShift, currentPrice, isBacktest, context) => {
23885
+ this.trailingStop = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp) => {
23794
23886
  this.loggerService.log("strategyReportService trailingStop", {
23795
23887
  symbol,
23796
23888
  percentShift,
@@ -23800,7 +23892,6 @@ class StrategyReportService {
23800
23892
  if (!this.subscribe.hasValue()) {
23801
23893
  return;
23802
23894
  }
23803
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23804
23895
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
23805
23896
  exchangeName: context.exchangeName,
23806
23897
  strategyName: context.strategyName,
@@ -23809,11 +23900,13 @@ class StrategyReportService {
23809
23900
  if (!pendingRow) {
23810
23901
  return;
23811
23902
  }
23903
+ const createdAt = new Date(timestamp).toISOString();
23812
23904
  await Report.writeData("strategy", {
23813
23905
  action: "trailing-stop",
23814
23906
  percentShift,
23815
23907
  currentPrice,
23816
23908
  symbol,
23909
+ timestamp,
23817
23910
  createdAt,
23818
23911
  }, {
23819
23912
  signalId: pendingRow.id,
@@ -23827,15 +23920,14 @@ class StrategyReportService {
23827
23920
  /**
23828
23921
  * Logs a trailing-take event when the take-profit is adjusted.
23829
23922
  *
23830
- * Records the percentage shift and current price when trailing take-profit moves.
23831
- *
23832
23923
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23833
23924
  * @param percentShift - Percentage the take-profit was shifted
23834
23925
  * @param currentPrice - Current market price at time of adjustment
23835
23926
  * @param isBacktest - Whether this is a backtest or live trading event
23836
23927
  * @param context - Strategy context with strategyName, exchangeName, frameName
23928
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23837
23929
  */
23838
- this.trailingTake = async (symbol, percentShift, currentPrice, isBacktest, context) => {
23930
+ this.trailingTake = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp) => {
23839
23931
  this.loggerService.log("strategyReportService trailingTake", {
23840
23932
  symbol,
23841
23933
  percentShift,
@@ -23845,7 +23937,6 @@ class StrategyReportService {
23845
23937
  if (!this.subscribe.hasValue()) {
23846
23938
  return;
23847
23939
  }
23848
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23849
23940
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
23850
23941
  exchangeName: context.exchangeName,
23851
23942
  strategyName: context.strategyName,
@@ -23854,11 +23945,13 @@ class StrategyReportService {
23854
23945
  if (!pendingRow) {
23855
23946
  return;
23856
23947
  }
23948
+ const createdAt = new Date(timestamp).toISOString();
23857
23949
  await Report.writeData("strategy", {
23858
23950
  action: "trailing-take",
23859
23951
  percentShift,
23860
23952
  currentPrice,
23861
23953
  symbol,
23954
+ timestamp,
23862
23955
  createdAt,
23863
23956
  }, {
23864
23957
  signalId: pendingRow.id,
@@ -23872,14 +23965,13 @@ class StrategyReportService {
23872
23965
  /**
23873
23966
  * Logs a breakeven event when the stop-loss is moved to entry price.
23874
23967
  *
23875
- * Records the current price when breakeven protection is activated.
23876
- *
23877
23968
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23878
23969
  * @param currentPrice - Current market price at time of breakeven activation
23879
23970
  * @param isBacktest - Whether this is a backtest or live trading event
23880
23971
  * @param context - Strategy context with strategyName, exchangeName, frameName
23972
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23881
23973
  */
23882
- this.breakeven = async (symbol, currentPrice, isBacktest, context) => {
23974
+ this.breakeven = async (symbol, currentPrice, isBacktest, context, timestamp) => {
23883
23975
  this.loggerService.log("strategyReportService breakeven", {
23884
23976
  symbol,
23885
23977
  currentPrice,
@@ -23888,7 +23980,6 @@ class StrategyReportService {
23888
23980
  if (!this.subscribe.hasValue()) {
23889
23981
  return;
23890
23982
  }
23891
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23892
23983
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
23893
23984
  exchangeName: context.exchangeName,
23894
23985
  strategyName: context.strategyName,
@@ -23897,10 +23988,12 @@ class StrategyReportService {
23897
23988
  if (!pendingRow) {
23898
23989
  return;
23899
23990
  }
23991
+ const createdAt = new Date(timestamp).toISOString();
23900
23992
  await Report.writeData("strategy", {
23901
23993
  action: "breakeven",
23902
23994
  currentPrice,
23903
23995
  symbol,
23996
+ timestamp,
23904
23997
  createdAt,
23905
23998
  }, {
23906
23999
  signalId: pendingRow.id,
@@ -23927,49 +24020,49 @@ class StrategyReportService {
23927
24020
  exchangeName: event.exchangeName,
23928
24021
  frameName: event.frameName,
23929
24022
  strategyName: event.strategyName,
23930
- }, event.cancelId));
24023
+ }, event.timestamp, event.cancelId));
23931
24024
  const unClosePending = strategyCommitSubject
23932
24025
  .filter(({ action }) => action === "close-pending")
23933
24026
  .connect(async (event) => await this.closePending(event.symbol, event.backtest, {
23934
24027
  exchangeName: event.exchangeName,
23935
24028
  frameName: event.frameName,
23936
24029
  strategyName: event.strategyName,
23937
- }, event.closeId));
24030
+ }, event.timestamp, event.closeId));
23938
24031
  const unPartialProfit = strategyCommitSubject
23939
24032
  .filter(({ action }) => action === "partial-profit")
23940
24033
  .connect(async (event) => await this.partialProfit(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
23941
24034
  exchangeName: event.exchangeName,
23942
24035
  frameName: event.frameName,
23943
24036
  strategyName: event.strategyName,
23944
- }));
24037
+ }, event.timestamp));
23945
24038
  const unPartialLoss = strategyCommitSubject
23946
24039
  .filter(({ action }) => action === "partial-loss")
23947
24040
  .connect(async (event) => await this.partialLoss(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
23948
24041
  exchangeName: event.exchangeName,
23949
24042
  frameName: event.frameName,
23950
24043
  strategyName: event.strategyName,
23951
- }));
24044
+ }, event.timestamp));
23952
24045
  const unTrailingStop = strategyCommitSubject
23953
24046
  .filter(({ action }) => action === "trailing-stop")
23954
24047
  .connect(async (event) => await this.trailingStop(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
23955
24048
  exchangeName: event.exchangeName,
23956
24049
  frameName: event.frameName,
23957
24050
  strategyName: event.strategyName,
23958
- }));
24051
+ }, event.timestamp));
23959
24052
  const unTrailingTake = strategyCommitSubject
23960
24053
  .filter(({ action }) => action === "trailing-take")
23961
24054
  .connect(async (event) => await this.trailingTake(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
23962
24055
  exchangeName: event.exchangeName,
23963
24056
  frameName: event.frameName,
23964
24057
  strategyName: event.strategyName,
23965
- }));
24058
+ }, event.timestamp));
23966
24059
  const unBreakeven = strategyCommitSubject
23967
24060
  .filter(({ action }) => action === "breakeven")
23968
24061
  .connect(async (event) => await this.breakeven(event.symbol, event.currentPrice, event.backtest, {
23969
24062
  exchangeName: event.exchangeName,
23970
24063
  frameName: event.frameName,
23971
24064
  strategyName: event.strategyName,
23972
- }));
24065
+ }, event.timestamp));
23973
24066
  const disposeFn = functoolsKit.compose(() => unCancelSchedule(), () => unClosePending(), () => unPartialProfit(), () => unPartialLoss(), () => unTrailingStop(), () => unTrailingTake(), () => unBreakeven());
23974
24067
  return () => {
23975
24068
  disposeFn();
@@ -23991,22 +24084,6 @@ class StrategyReportService {
23991
24084
  }
23992
24085
  }
23993
24086
 
23994
- /**
23995
- * Extracts execution context timestamp for strategy event logging.
23996
- *
23997
- * @param self - The StrategyMarkdownService instance to extract context from
23998
- * @returns Object containing ISO 8601 formatted timestamp, or empty string if no context
23999
- * @internal
24000
- */
24001
- const GET_EXECUTION_CONTEXT_FN = (self) => {
24002
- if (ExecutionContextService.hasContext()) {
24003
- const { when } = self.executionContextService.context;
24004
- return { when: when.toISOString() };
24005
- }
24006
- return {
24007
- when: "",
24008
- };
24009
- };
24010
24087
  /**
24011
24088
  * Creates a unique key for memoizing ReportStorage instances.
24012
24089
  *
@@ -24254,7 +24331,6 @@ class ReportStorage {
24254
24331
  class StrategyMarkdownService {
24255
24332
  constructor() {
24256
24333
  this.loggerService = inject(TYPES.loggerService);
24257
- this.executionContextService = inject(TYPES.executionContextService);
24258
24334
  this.strategyCoreService = inject(TYPES.strategyCoreService);
24259
24335
  /**
24260
24336
  * Memoized factory for ReportStorage instances.
@@ -24268,15 +24344,13 @@ class StrategyMarkdownService {
24268
24344
  /**
24269
24345
  * Records a cancel-scheduled event when a scheduled signal is cancelled.
24270
24346
  *
24271
- * Retrieves the scheduled signal from StrategyCoreService and stores
24272
- * the cancellation event in the appropriate ReportStorage.
24273
- *
24274
24347
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24275
24348
  * @param isBacktest - Whether this is a backtest or live trading event
24276
24349
  * @param context - Strategy context with strategyName, exchangeName, frameName
24350
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24277
24351
  * @param cancelId - Optional identifier for the cancellation reason
24278
24352
  */
24279
- this.cancelScheduled = async (symbol, isBacktest, context, cancelId) => {
24353
+ this.cancelScheduled = async (symbol, isBacktest, context, timestamp, cancelId) => {
24280
24354
  this.loggerService.log("strategyMarkdownService cancelScheduled", {
24281
24355
  symbol,
24282
24356
  isBacktest,
@@ -24285,7 +24359,6 @@ class StrategyMarkdownService {
24285
24359
  if (!this.subscribe.hasValue()) {
24286
24360
  return;
24287
24361
  }
24288
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24289
24362
  const scheduledRow = await this.strategyCoreService.getScheduledSignal(isBacktest, symbol, {
24290
24363
  exchangeName: context.exchangeName,
24291
24364
  strategyName: context.strategyName,
@@ -24294,9 +24367,10 @@ class StrategyMarkdownService {
24294
24367
  if (!scheduledRow) {
24295
24368
  return;
24296
24369
  }
24370
+ const createdAt = new Date(timestamp).toISOString();
24297
24371
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24298
24372
  storage.addEvent({
24299
- timestamp: Date.now(),
24373
+ timestamp,
24300
24374
  symbol,
24301
24375
  strategyName: context.strategyName,
24302
24376
  exchangeName: context.exchangeName,
@@ -24311,15 +24385,13 @@ class StrategyMarkdownService {
24311
24385
  /**
24312
24386
  * Records a close-pending event when a pending signal is closed.
24313
24387
  *
24314
- * Retrieves the pending signal from StrategyCoreService and stores
24315
- * the close event in the appropriate ReportStorage.
24316
- *
24317
24388
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24318
24389
  * @param isBacktest - Whether this is a backtest or live trading event
24319
24390
  * @param context - Strategy context with strategyName, exchangeName, frameName
24391
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24320
24392
  * @param closeId - Optional identifier for the close reason
24321
24393
  */
24322
- this.closePending = async (symbol, isBacktest, context, closeId) => {
24394
+ this.closePending = async (symbol, isBacktest, context, timestamp, closeId) => {
24323
24395
  this.loggerService.log("strategyMarkdownService closePending", {
24324
24396
  symbol,
24325
24397
  isBacktest,
@@ -24328,7 +24400,6 @@ class StrategyMarkdownService {
24328
24400
  if (!this.subscribe.hasValue()) {
24329
24401
  return;
24330
24402
  }
24331
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24332
24403
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
24333
24404
  exchangeName: context.exchangeName,
24334
24405
  strategyName: context.strategyName,
@@ -24337,9 +24408,10 @@ class StrategyMarkdownService {
24337
24408
  if (!pendingRow) {
24338
24409
  return;
24339
24410
  }
24411
+ const createdAt = new Date(timestamp).toISOString();
24340
24412
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24341
24413
  storage.addEvent({
24342
- timestamp: Date.now(),
24414
+ timestamp,
24343
24415
  symbol,
24344
24416
  strategyName: context.strategyName,
24345
24417
  exchangeName: context.exchangeName,
@@ -24354,15 +24426,14 @@ class StrategyMarkdownService {
24354
24426
  /**
24355
24427
  * Records a partial-profit event when a portion of the position is closed at profit.
24356
24428
  *
24357
- * Stores the percentage closed and current price when partial profit-taking occurs.
24358
- *
24359
24429
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24360
24430
  * @param percentToClose - Percentage of position to close (0-100)
24361
24431
  * @param currentPrice - Current market price at time of partial close
24362
24432
  * @param isBacktest - Whether this is a backtest or live trading event
24363
24433
  * @param context - Strategy context with strategyName, exchangeName, frameName
24434
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24364
24435
  */
24365
- this.partialProfit = async (symbol, percentToClose, currentPrice, isBacktest, context) => {
24436
+ this.partialProfit = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp) => {
24366
24437
  this.loggerService.log("strategyMarkdownService partialProfit", {
24367
24438
  symbol,
24368
24439
  percentToClose,
@@ -24372,7 +24443,6 @@ class StrategyMarkdownService {
24372
24443
  if (!this.subscribe.hasValue()) {
24373
24444
  return;
24374
24445
  }
24375
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24376
24446
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
24377
24447
  exchangeName: context.exchangeName,
24378
24448
  strategyName: context.strategyName,
@@ -24381,9 +24451,10 @@ class StrategyMarkdownService {
24381
24451
  if (!pendingRow) {
24382
24452
  return;
24383
24453
  }
24454
+ const createdAt = new Date(timestamp).toISOString();
24384
24455
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24385
24456
  storage.addEvent({
24386
- timestamp: Date.now(),
24457
+ timestamp,
24387
24458
  symbol,
24388
24459
  strategyName: context.strategyName,
24389
24460
  exchangeName: context.exchangeName,
@@ -24399,15 +24470,14 @@ class StrategyMarkdownService {
24399
24470
  /**
24400
24471
  * Records a partial-loss event when a portion of the position is closed at loss.
24401
24472
  *
24402
- * Stores the percentage closed and current price when partial loss-cutting occurs.
24403
- *
24404
24473
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24405
24474
  * @param percentToClose - Percentage of position to close (0-100)
24406
24475
  * @param currentPrice - Current market price at time of partial close
24407
24476
  * @param isBacktest - Whether this is a backtest or live trading event
24408
24477
  * @param context - Strategy context with strategyName, exchangeName, frameName
24478
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24409
24479
  */
24410
- this.partialLoss = async (symbol, percentToClose, currentPrice, isBacktest, context) => {
24480
+ this.partialLoss = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp) => {
24411
24481
  this.loggerService.log("strategyMarkdownService partialLoss", {
24412
24482
  symbol,
24413
24483
  percentToClose,
@@ -24417,7 +24487,6 @@ class StrategyMarkdownService {
24417
24487
  if (!this.subscribe.hasValue()) {
24418
24488
  return;
24419
24489
  }
24420
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24421
24490
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
24422
24491
  exchangeName: context.exchangeName,
24423
24492
  strategyName: context.strategyName,
@@ -24426,9 +24495,10 @@ class StrategyMarkdownService {
24426
24495
  if (!pendingRow) {
24427
24496
  return;
24428
24497
  }
24498
+ const createdAt = new Date(timestamp).toISOString();
24429
24499
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24430
24500
  storage.addEvent({
24431
- timestamp: Date.now(),
24501
+ timestamp,
24432
24502
  symbol,
24433
24503
  strategyName: context.strategyName,
24434
24504
  exchangeName: context.exchangeName,
@@ -24444,15 +24514,14 @@ class StrategyMarkdownService {
24444
24514
  /**
24445
24515
  * Records a trailing-stop event when the stop-loss is adjusted.
24446
24516
  *
24447
- * Stores the percentage shift and current price when trailing stop moves.
24448
- *
24449
24517
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24450
24518
  * @param percentShift - Percentage the stop-loss was shifted
24451
24519
  * @param currentPrice - Current market price at time of adjustment
24452
24520
  * @param isBacktest - Whether this is a backtest or live trading event
24453
24521
  * @param context - Strategy context with strategyName, exchangeName, frameName
24522
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24454
24523
  */
24455
- this.trailingStop = async (symbol, percentShift, currentPrice, isBacktest, context) => {
24524
+ this.trailingStop = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp) => {
24456
24525
  this.loggerService.log("strategyMarkdownService trailingStop", {
24457
24526
  symbol,
24458
24527
  percentShift,
@@ -24462,7 +24531,6 @@ class StrategyMarkdownService {
24462
24531
  if (!this.subscribe.hasValue()) {
24463
24532
  return;
24464
24533
  }
24465
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24466
24534
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
24467
24535
  exchangeName: context.exchangeName,
24468
24536
  strategyName: context.strategyName,
@@ -24471,9 +24539,10 @@ class StrategyMarkdownService {
24471
24539
  if (!pendingRow) {
24472
24540
  return;
24473
24541
  }
24542
+ const createdAt = new Date(timestamp).toISOString();
24474
24543
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24475
24544
  storage.addEvent({
24476
- timestamp: Date.now(),
24545
+ timestamp,
24477
24546
  symbol,
24478
24547
  strategyName: context.strategyName,
24479
24548
  exchangeName: context.exchangeName,
@@ -24489,15 +24558,14 @@ class StrategyMarkdownService {
24489
24558
  /**
24490
24559
  * Records a trailing-take event when the take-profit is adjusted.
24491
24560
  *
24492
- * Stores the percentage shift and current price when trailing take-profit moves.
24493
- *
24494
24561
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24495
24562
  * @param percentShift - Percentage the take-profit was shifted
24496
24563
  * @param currentPrice - Current market price at time of adjustment
24497
24564
  * @param isBacktest - Whether this is a backtest or live trading event
24498
24565
  * @param context - Strategy context with strategyName, exchangeName, frameName
24566
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24499
24567
  */
24500
- this.trailingTake = async (symbol, percentShift, currentPrice, isBacktest, context) => {
24568
+ this.trailingTake = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp) => {
24501
24569
  this.loggerService.log("strategyMarkdownService trailingTake", {
24502
24570
  symbol,
24503
24571
  percentShift,
@@ -24507,7 +24575,6 @@ class StrategyMarkdownService {
24507
24575
  if (!this.subscribe.hasValue()) {
24508
24576
  return;
24509
24577
  }
24510
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24511
24578
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
24512
24579
  exchangeName: context.exchangeName,
24513
24580
  strategyName: context.strategyName,
@@ -24516,9 +24583,10 @@ class StrategyMarkdownService {
24516
24583
  if (!pendingRow) {
24517
24584
  return;
24518
24585
  }
24586
+ const createdAt = new Date(timestamp).toISOString();
24519
24587
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24520
24588
  storage.addEvent({
24521
- timestamp: Date.now(),
24589
+ timestamp,
24522
24590
  symbol,
24523
24591
  strategyName: context.strategyName,
24524
24592
  exchangeName: context.exchangeName,
@@ -24534,14 +24602,13 @@ class StrategyMarkdownService {
24534
24602
  /**
24535
24603
  * Records a breakeven event when the stop-loss is moved to entry price.
24536
24604
  *
24537
- * Stores the current price when breakeven protection is activated.
24538
- *
24539
24605
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24540
24606
  * @param currentPrice - Current market price at time of breakeven activation
24541
24607
  * @param isBacktest - Whether this is a backtest or live trading event
24542
24608
  * @param context - Strategy context with strategyName, exchangeName, frameName
24609
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24543
24610
  */
24544
- this.breakeven = async (symbol, currentPrice, isBacktest, context) => {
24611
+ this.breakeven = async (symbol, currentPrice, isBacktest, context, timestamp) => {
24545
24612
  this.loggerService.log("strategyMarkdownService breakeven", {
24546
24613
  symbol,
24547
24614
  currentPrice,
@@ -24550,7 +24617,6 @@ class StrategyMarkdownService {
24550
24617
  if (!this.subscribe.hasValue()) {
24551
24618
  return;
24552
24619
  }
24553
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24554
24620
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
24555
24621
  exchangeName: context.exchangeName,
24556
24622
  strategyName: context.strategyName,
@@ -24559,9 +24625,10 @@ class StrategyMarkdownService {
24559
24625
  if (!pendingRow) {
24560
24626
  return;
24561
24627
  }
24628
+ const createdAt = new Date(timestamp).toISOString();
24562
24629
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24563
24630
  storage.addEvent({
24564
- timestamp: Date.now(),
24631
+ timestamp,
24565
24632
  symbol,
24566
24633
  strategyName: context.strategyName,
24567
24634
  exchangeName: context.exchangeName,
@@ -24703,49 +24770,49 @@ class StrategyMarkdownService {
24703
24770
  exchangeName: event.exchangeName,
24704
24771
  frameName: event.frameName,
24705
24772
  strategyName: event.strategyName,
24706
- }, event.cancelId));
24773
+ }, event.timestamp, event.cancelId));
24707
24774
  const unClosePending = strategyCommitSubject
24708
24775
  .filter(({ action }) => action === "close-pending")
24709
24776
  .connect(async (event) => await this.closePending(event.symbol, event.backtest, {
24710
24777
  exchangeName: event.exchangeName,
24711
24778
  frameName: event.frameName,
24712
24779
  strategyName: event.strategyName,
24713
- }, event.closeId));
24780
+ }, event.timestamp, event.closeId));
24714
24781
  const unPartialProfit = strategyCommitSubject
24715
24782
  .filter(({ action }) => action === "partial-profit")
24716
24783
  .connect(async (event) => await this.partialProfit(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
24717
24784
  exchangeName: event.exchangeName,
24718
24785
  frameName: event.frameName,
24719
24786
  strategyName: event.strategyName,
24720
- }));
24787
+ }, event.timestamp));
24721
24788
  const unPartialLoss = strategyCommitSubject
24722
24789
  .filter(({ action }) => action === "partial-loss")
24723
24790
  .connect(async (event) => await this.partialLoss(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
24724
24791
  exchangeName: event.exchangeName,
24725
24792
  frameName: event.frameName,
24726
24793
  strategyName: event.strategyName,
24727
- }));
24794
+ }, event.timestamp));
24728
24795
  const unTrailingStop = strategyCommitSubject
24729
24796
  .filter(({ action }) => action === "trailing-stop")
24730
24797
  .connect(async (event) => await this.trailingStop(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
24731
24798
  exchangeName: event.exchangeName,
24732
24799
  frameName: event.frameName,
24733
24800
  strategyName: event.strategyName,
24734
- }));
24801
+ }, event.timestamp));
24735
24802
  const unTrailingTake = strategyCommitSubject
24736
24803
  .filter(({ action }) => action === "trailing-take")
24737
24804
  .connect(async (event) => await this.trailingTake(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
24738
24805
  exchangeName: event.exchangeName,
24739
24806
  frameName: event.frameName,
24740
24807
  strategyName: event.strategyName,
24741
- }));
24808
+ }, event.timestamp));
24742
24809
  const unBreakeven = strategyCommitSubject
24743
24810
  .filter(({ action }) => action === "breakeven")
24744
24811
  .connect(async (event) => await this.breakeven(event.symbol, event.currentPrice, event.backtest, {
24745
24812
  exchangeName: event.exchangeName,
24746
24813
  frameName: event.frameName,
24747
24814
  strategyName: event.strategyName,
24748
- }));
24815
+ }, event.timestamp));
24749
24816
  const disposeFn = functoolsKit.compose(() => unCancelSchedule(), () => unClosePending(), () => unPartialProfit(), () => unPartialLoss(), () => unTrailingStop(), () => unTrailingTake(), () => unBreakeven());
24750
24817
  return () => {
24751
24818
  disposeFn();
@@ -33114,12 +33181,6 @@ class NotificationInstance {
33114
33181
  createdAt: data.createdAt,
33115
33182
  });
33116
33183
  }
33117
- // Sort signal notifications by createdAt (newest first)
33118
- this._notifications.sort((a, b) => {
33119
- const aCreatedAt = "createdAt" in a ? a.createdAt : 0;
33120
- const bCreatedAt = "createdAt" in b ? b.createdAt : 0;
33121
- return bCreatedAt - aCreatedAt;
33122
- });
33123
33184
  };
33124
33185
  /**
33125
33186
  * Processes partial profit events.
@@ -33138,6 +33199,7 @@ class NotificationInstance {
33138
33199
  currentPrice: data.currentPrice,
33139
33200
  priceOpen: data.data.priceOpen,
33140
33201
  position: data.data.position,
33202
+ createdAt: data.timestamp,
33141
33203
  });
33142
33204
  };
33143
33205
  /**
@@ -33157,6 +33219,7 @@ class NotificationInstance {
33157
33219
  currentPrice: data.currentPrice,
33158
33220
  priceOpen: data.data.priceOpen,
33159
33221
  position: data.data.position,
33222
+ createdAt: data.timestamp,
33160
33223
  });
33161
33224
  };
33162
33225
  /**
@@ -33175,6 +33238,7 @@ class NotificationInstance {
33175
33238
  currentPrice: data.currentPrice,
33176
33239
  priceOpen: data.data.priceOpen,
33177
33240
  position: data.data.position,
33241
+ createdAt: data.timestamp,
33178
33242
  });
33179
33243
  };
33180
33244
  /**
@@ -33185,64 +33249,69 @@ class NotificationInstance {
33185
33249
  this._addNotification({
33186
33250
  type: "partial_profit.commit",
33187
33251
  id: CREATE_KEY_FN(),
33188
- timestamp: Date.now(),
33252
+ timestamp: data.timestamp,
33189
33253
  backtest: data.backtest,
33190
33254
  symbol: data.symbol,
33191
33255
  strategyName: data.strategyName,
33192
33256
  exchangeName: data.exchangeName,
33193
33257
  percentToClose: data.percentToClose,
33194
33258
  currentPrice: data.currentPrice,
33259
+ createdAt: data.timestamp,
33195
33260
  });
33196
33261
  }
33197
33262
  else if (data.action === "partial-loss") {
33198
33263
  this._addNotification({
33199
33264
  type: "partial_loss.commit",
33200
33265
  id: CREATE_KEY_FN(),
33201
- timestamp: Date.now(),
33266
+ timestamp: data.timestamp,
33202
33267
  backtest: data.backtest,
33203
33268
  symbol: data.symbol,
33204
33269
  strategyName: data.strategyName,
33205
33270
  exchangeName: data.exchangeName,
33206
33271
  percentToClose: data.percentToClose,
33207
33272
  currentPrice: data.currentPrice,
33273
+ createdAt: data.timestamp,
33208
33274
  });
33209
33275
  }
33210
33276
  else if (data.action === "breakeven") {
33211
33277
  this._addNotification({
33212
33278
  type: "breakeven.commit",
33213
33279
  id: CREATE_KEY_FN(),
33214
- timestamp: Date.now(),
33280
+ timestamp: data.timestamp,
33215
33281
  backtest: data.backtest,
33216
33282
  symbol: data.symbol,
33217
33283
  strategyName: data.strategyName,
33218
33284
  exchangeName: data.exchangeName,
33219
33285
  currentPrice: data.currentPrice,
33286
+ createdAt: data.timestamp,
33220
33287
  });
33221
33288
  }
33222
33289
  else if (data.action === "trailing-stop") {
33223
33290
  this._addNotification({
33224
33291
  type: "trailing_stop.commit",
33225
33292
  id: CREATE_KEY_FN(),
33226
- timestamp: Date.now(),
33293
+ timestamp: data.timestamp,
33227
33294
  backtest: data.backtest,
33228
33295
  symbol: data.symbol,
33229
33296
  strategyName: data.strategyName,
33230
33297
  exchangeName: data.exchangeName,
33231
33298
  percentShift: data.percentShift,
33232
33299
  currentPrice: data.currentPrice,
33300
+ createdAt: data.timestamp,
33233
33301
  });
33234
33302
  }
33235
33303
  else if (data.action === "trailing-take") {
33236
33304
  this._addNotification({
33237
33305
  type: "trailing_take.commit",
33238
33306
  id: CREATE_KEY_FN(),
33239
- timestamp: Date.now(),
33307
+ timestamp: data.timestamp,
33240
33308
  backtest: data.backtest,
33241
33309
  symbol: data.symbol,
33242
33310
  strategyName: data.strategyName,
33243
33311
  exchangeName: data.exchangeName,
33244
33312
  percentShift: data.percentShift,
33245
33313
  currentPrice: data.currentPrice,
33314
+ createdAt: data.timestamp,
33246
33315
  });
33247
33316
  }
33248
33317
  };
@@ -33263,6 +33332,7 @@ class NotificationInstance {
33263
33332
  activePositionCount: data.activePositionCount,
33264
33333
  currentPrice: data.currentPrice,
33265
33334
  pendingSignal: data.pendingSignal,
33335
+ createdAt: data.timestamp,
33266
33336
  });
33267
33337
  };
33268
33338
  /**
@@ -33272,7 +33342,6 @@ class NotificationInstance {
33272
33342
  this._addNotification({
33273
33343
  type: "error.info",
33274
33344
  id: CREATE_KEY_FN(),
33275
- timestamp: Date.now(),
33276
33345
  error: functoolsKit.errorData(error),
33277
33346
  message: functoolsKit.getErrorMessage(error),
33278
33347
  backtest: false,
@@ -33285,7 +33354,6 @@ class NotificationInstance {
33285
33354
  this._addNotification({
33286
33355
  type: "error.critical",
33287
33356
  id: CREATE_KEY_FN(),
33288
- timestamp: Date.now(),
33289
33357
  error: functoolsKit.errorData(error),
33290
33358
  message: functoolsKit.getErrorMessage(error),
33291
33359
  backtest: false,
@@ -33298,7 +33366,6 @@ class NotificationInstance {
33298
33366
  this._addNotification({
33299
33367
  type: "error.validation",
33300
33368
  id: CREATE_KEY_FN(),
33301
- timestamp: Date.now(),
33302
33369
  error: functoolsKit.errorData(error),
33303
33370
  message: functoolsKit.getErrorMessage(error),
33304
33371
  backtest: false,