backtest-kit 2.2.13 → 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.mjs CHANGED
@@ -2809,6 +2809,114 @@ const INTERVAL_MINUTES$3 = {
2809
2809
  "1h": 60,
2810
2810
  };
2811
2811
  const TIMEOUT_SYMBOL = Symbol('timeout');
2812
+ /**
2813
+ * Calls onCommit callback with strategy commit event.
2814
+ *
2815
+ * Wraps the callback in trycatch to prevent errors from breaking the flow.
2816
+ * Used by ClientStrategy methods that modify signal state (partial, trailing, breakeven, cancel, close).
2817
+ *
2818
+ * @param self - ClientStrategy instance
2819
+ * @param event - Strategy commit event to emit
2820
+ */
2821
+ const CALL_COMMIT_FN = trycatch(async (self, event) => {
2822
+ await self.params.onCommit(event);
2823
+ }, {
2824
+ fallback: (error) => {
2825
+ const message = "ClientStrategy CALL_COMMIT_FN thrown";
2826
+ const payload = {
2827
+ error: errorData(error),
2828
+ message: getErrorMessage(error),
2829
+ };
2830
+ bt.loggerService.warn(message, payload);
2831
+ console.warn(message, payload);
2832
+ errorEmitter.next(error);
2833
+ },
2834
+ });
2835
+ /**
2836
+ * Processes queued commit events with proper execution context timestamp.
2837
+ *
2838
+ * Commit events from partialProfit, partialLoss, breakeven, trailingStop, trailingTake
2839
+ * are queued in _commitQueue and processed here with correct timestamp from
2840
+ * execution context (tick's when or backtest candle timestamp).
2841
+ *
2842
+ * @param self - ClientStrategy instance
2843
+ * @param timestamp - Timestamp from execution context
2844
+ */
2845
+ const PROCESS_COMMIT_QUEUE_FN = async (self, timestamp) => {
2846
+ if (self._commitQueue.length === 0) {
2847
+ return;
2848
+ }
2849
+ const queue = self._commitQueue;
2850
+ self._commitQueue = [];
2851
+ for (const commit of queue) {
2852
+ switch (commit.action) {
2853
+ case "partial-profit":
2854
+ await CALL_COMMIT_FN(self, {
2855
+ action: "partial-profit",
2856
+ symbol: commit.symbol,
2857
+ strategyName: self.params.strategyName,
2858
+ exchangeName: self.params.exchangeName,
2859
+ frameName: self.params.frameName,
2860
+ backtest: commit.backtest,
2861
+ percentToClose: commit.percentToClose,
2862
+ currentPrice: commit.currentPrice,
2863
+ timestamp,
2864
+ });
2865
+ break;
2866
+ case "partial-loss":
2867
+ await CALL_COMMIT_FN(self, {
2868
+ action: "partial-loss",
2869
+ symbol: commit.symbol,
2870
+ strategyName: self.params.strategyName,
2871
+ exchangeName: self.params.exchangeName,
2872
+ frameName: self.params.frameName,
2873
+ backtest: commit.backtest,
2874
+ percentToClose: commit.percentToClose,
2875
+ currentPrice: commit.currentPrice,
2876
+ timestamp,
2877
+ });
2878
+ break;
2879
+ case "breakeven":
2880
+ await CALL_COMMIT_FN(self, {
2881
+ action: "breakeven",
2882
+ symbol: commit.symbol,
2883
+ strategyName: self.params.strategyName,
2884
+ exchangeName: self.params.exchangeName,
2885
+ frameName: self.params.frameName,
2886
+ backtest: commit.backtest,
2887
+ currentPrice: commit.currentPrice,
2888
+ timestamp,
2889
+ });
2890
+ break;
2891
+ case "trailing-stop":
2892
+ await CALL_COMMIT_FN(self, {
2893
+ action: "trailing-stop",
2894
+ symbol: commit.symbol,
2895
+ strategyName: self.params.strategyName,
2896
+ exchangeName: self.params.exchangeName,
2897
+ frameName: self.params.frameName,
2898
+ backtest: commit.backtest,
2899
+ percentShift: commit.percentShift,
2900
+ currentPrice: commit.currentPrice,
2901
+ timestamp,
2902
+ });
2903
+ break;
2904
+ case "trailing-take":
2905
+ await CALL_COMMIT_FN(self, {
2906
+ action: "trailing-take",
2907
+ symbol: commit.symbol,
2908
+ strategyName: self.params.strategyName,
2909
+ exchangeName: self.params.exchangeName,
2910
+ frameName: self.params.frameName,
2911
+ backtest: commit.backtest,
2912
+ percentShift: commit.percentShift,
2913
+ currentPrice: commit.currentPrice,
2914
+ timestamp,
2915
+ });
2916
+ break;
2917
+ }
2918
+ }
2919
+ };
2812
2920
  /**
2813
2921
  * Converts internal signal to public API format.
2814
2922
  *
@@ -4755,6 +4863,8 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
4755
4863
  };
4756
4864
  }
4757
4865
  await CALL_SCHEDULE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, candle.timestamp, true);
4866
+ // Process queued commit events with candle timestamp
4867
+ await PROCESS_COMMIT_QUEUE_FN(self, candle.timestamp);
4758
4868
  }
4759
4869
  return {
4760
4870
  activated: false,
@@ -4880,6 +4990,8 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
4880
4990
  }
4881
4991
  }
4882
4992
  }
4993
+ // Process queued commit events with candle timestamp
4994
+ await PROCESS_COMMIT_QUEUE_FN(self, currentCandleTimestamp);
4883
4995
  }
4884
4996
  return null;
4885
4997
  };
@@ -4919,6 +5031,8 @@ class ClientStrategy {
4919
5031
  this._scheduledSignal = null;
4920
5032
  this._cancelledSignal = null;
4921
5033
  this._closedSignal = null;
5034
+ /** Queue for commit events to be processed in tick()/backtest() with proper timestamp */
5035
+ this._commitQueue = [];
4922
5036
  /**
4923
5037
  * Initializes strategy state by loading persisted signal from disk.
4924
5038
  *
@@ -5166,6 +5280,8 @@ class ClientStrategy {
5166
5280
  });
5167
5281
  // Получаем текущее время в начале tick для консистентности
5168
5282
  const currentTime = this.params.execution.context.when.getTime();
5283
+ // Process queued commit events with proper timestamp
5284
+ await PROCESS_COMMIT_QUEUE_FN(this, currentTime);
5169
5285
  // Early return if strategy was stopped
5170
5286
  if (this._isStopped) {
5171
5287
  const currentPrice = await this.params.exchange.getAveragePrice(this.params.execution.context.symbol);
@@ -5556,6 +5672,7 @@ class ClientStrategy {
5556
5672
  cancelId,
5557
5673
  });
5558
5674
  // Save cancelled signal for next tick to emit cancelled event
5675
+ const hadScheduledSignal = this._scheduledSignal !== null;
5559
5676
  if (this._scheduledSignal) {
5560
5677
  this._cancelledSignal = Object.assign({}, this._scheduledSignal, {
5561
5678
  cancelId,
@@ -5563,9 +5680,35 @@ class ClientStrategy {
5563
5680
  this._scheduledSignal = null;
5564
5681
  }
5565
5682
  if (backtest) {
5683
+ // Emit commit event only if signal was actually cancelled
5684
+ if (hadScheduledSignal) {
5685
+ await CALL_COMMIT_FN(this, {
5686
+ action: "cancel-scheduled",
5687
+ symbol,
5688
+ strategyName: this.params.strategyName,
5689
+ exchangeName: this.params.exchangeName,
5690
+ frameName: this.params.frameName,
5691
+ backtest,
5692
+ cancelId,
5693
+ timestamp: this.params.execution.context.when.getTime(),
5694
+ });
5695
+ }
5566
5696
  return;
5567
5697
  }
5568
5698
  await PersistScheduleAdapter.writeScheduleData(this._scheduledSignal, symbol, this.params.method.context.strategyName, this.params.method.context.exchangeName);
5699
+ // Emit commit event only if signal was actually cancelled
5700
+ if (hadScheduledSignal) {
5701
+ await CALL_COMMIT_FN(this, {
5702
+ action: "cancel-scheduled",
5703
+ symbol,
5704
+ strategyName: this.params.strategyName,
5705
+ exchangeName: this.params.exchangeName,
5706
+ frameName: this.params.frameName,
5707
+ backtest,
5708
+ cancelId,
5709
+ timestamp: this.params.execution.context.when.getTime(),
5710
+ });
5711
+ }
5569
5712
  }
5570
5713
  /**
5571
5714
  * Closes the pending signal without stopping the strategy.
@@ -5595,6 +5738,7 @@ class ClientStrategy {
5595
5738
  closeId,
5596
5739
  });
5597
5740
  // Save closed signal for next tick to emit closed event
5741
+ const hadPendingSignal = this._pendingSignal !== null;
5598
5742
  if (this._pendingSignal) {
5599
5743
  this._closedSignal = Object.assign({}, this._pendingSignal, {
5600
5744
  closeId,
@@ -5602,9 +5746,35 @@ class ClientStrategy {
5602
5746
  this._pendingSignal = null;
5603
5747
  }
5604
5748
  if (backtest) {
5749
+ // Emit commit event only if signal was actually closed
5750
+ if (hadPendingSignal) {
5751
+ await CALL_COMMIT_FN(this, {
5752
+ action: "close-pending",
5753
+ symbol,
5754
+ strategyName: this.params.strategyName,
5755
+ exchangeName: this.params.exchangeName,
5756
+ frameName: this.params.frameName,
5757
+ backtest,
5758
+ closeId,
5759
+ timestamp: this.params.execution.context.when.getTime(),
5760
+ });
5761
+ }
5605
5762
  return;
5606
5763
  }
5607
5764
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, symbol, this.params.strategyName, this.params.exchangeName);
5765
+ // Emit commit event only if signal was actually closed
5766
+ if (hadPendingSignal) {
5767
+ await CALL_COMMIT_FN(this, {
5768
+ action: "close-pending",
5769
+ symbol,
5770
+ strategyName: this.params.strategyName,
5771
+ exchangeName: this.params.exchangeName,
5772
+ frameName: this.params.frameName,
5773
+ backtest,
5774
+ closeId,
5775
+ timestamp: this.params.execution.context.when.getTime(),
5776
+ });
5777
+ }
5608
5778
  }
5609
5779
  /**
5610
5780
  * Executes partial close at profit level (moving toward TP).
@@ -5728,6 +5898,14 @@ class ClientStrategy {
5728
5898
  if (!backtest) {
5729
5899
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.execution.context.symbol, this.params.strategyName, this.params.exchangeName);
5730
5900
  }
5901
+ // Queue commit event for processing in tick()/backtest() with proper timestamp
5902
+ this._commitQueue.push({
5903
+ action: "partial-profit",
5904
+ symbol,
5905
+ backtest,
5906
+ percentToClose,
5907
+ currentPrice,
5908
+ });
5731
5909
  return true;
5732
5910
  }
5733
5911
  /**
@@ -5852,6 +6030,14 @@ class ClientStrategy {
5852
6030
  if (!backtest) {
5853
6031
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.execution.context.symbol, this.params.strategyName, this.params.exchangeName);
5854
6032
  }
6033
+ // Queue commit event for processing in tick()/backtest() with proper timestamp
6034
+ this._commitQueue.push({
6035
+ action: "partial-loss",
6036
+ symbol,
6037
+ backtest,
6038
+ percentToClose,
6039
+ currentPrice,
6040
+ });
5855
6041
  return true;
5856
6042
  }
5857
6043
  /**
@@ -5967,6 +6153,13 @@ class ClientStrategy {
5967
6153
  if (!backtest) {
5968
6154
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.execution.context.symbol, this.params.strategyName, this.params.exchangeName);
5969
6155
  }
6156
+ // Queue commit event for processing in tick()/backtest() with proper timestamp
6157
+ this._commitQueue.push({
6158
+ action: "breakeven",
6159
+ symbol,
6160
+ backtest,
6161
+ currentPrice,
6162
+ });
5970
6163
  return true;
5971
6164
  }
5972
6165
  /**
@@ -6146,6 +6339,14 @@ class ClientStrategy {
6146
6339
  if (!backtest) {
6147
6340
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.execution.context.symbol, this.params.strategyName, this.params.exchangeName);
6148
6341
  }
6342
+ // Queue commit event for processing in tick()/backtest() with proper timestamp
6343
+ this._commitQueue.push({
6344
+ action: "trailing-stop",
6345
+ symbol,
6346
+ backtest,
6347
+ percentShift,
6348
+ currentPrice,
6349
+ });
6149
6350
  return true;
6150
6351
  }
6151
6352
  /**
@@ -6311,6 +6512,14 @@ class ClientStrategy {
6311
6512
  if (!backtest) {
6312
6513
  await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.execution.context.symbol, this.params.strategyName, this.params.exchangeName);
6313
6514
  }
6515
+ // Queue commit event for processing in tick()/backtest() with proper timestamp
6516
+ this._commitQueue.push({
6517
+ action: "trailing-take",
6518
+ symbol,
6519
+ backtest,
6520
+ percentShift,
6521
+ currentPrice,
6522
+ });
6314
6523
  return true;
6315
6524
  }
6316
6525
  }
@@ -6823,6 +7032,31 @@ const CREATE_COMMIT_INIT_FN = (self) => trycatch(async (symbol, strategyName, ex
6823
7032
  },
6824
7033
  defaultValue: null,
6825
7034
  });
7035
+ /**
7036
+ * Creates a callback function for emitting commit events to strategyCommitSubject.
7037
+ *
7038
+ * Called by ClientStrategy when strategy management actions are executed
7039
+ * (partialProfit, partialLoss, trailingStop, trailingTake, breakeven, cancelScheduled, closePending).
7040
+ * Emits StrategyCommitContract event to all subscribers.
7041
+ *
7042
+ * @param self - Reference to StrategyConnectionService instance
7043
+ * @returns Callback function for commit events
7044
+ */
7045
+ const CREATE_COMMIT_FN = (self) => trycatch(async (event) => {
7046
+ await strategyCommitSubject.next(event);
7047
+ }, {
7048
+ fallback: (error) => {
7049
+ const message = "StrategyConnectionService CREATE_COMMIT_FN thrown";
7050
+ const payload = {
7051
+ error: errorData(error),
7052
+ message: getErrorMessage(error),
7053
+ };
7054
+ bt.loggerService.warn(message, payload);
7055
+ console.warn(message, payload);
7056
+ errorEmitter.next(error);
7057
+ },
7058
+ defaultValue: null,
7059
+ });
6826
7060
  /**
6827
7061
  * Creates a callback function for emitting dispose events.
6828
7062
  *
@@ -6917,6 +7151,7 @@ class StrategyConnectionService {
6917
7151
  onSchedulePing: CREATE_COMMIT_SCHEDULE_PING_FN(this),
6918
7152
  onActivePing: CREATE_COMMIT_ACTIVE_PING_FN(this),
6919
7153
  onDispose: CREATE_COMMIT_DISPOSE_FN(this),
7154
+ onCommit: CREATE_COMMIT_FN(),
6920
7155
  });
6921
7156
  });
6922
7157
  /**
@@ -10203,26 +10438,6 @@ const CREATE_KEY_FN$h = (context) => {
10203
10438
  parts.push(context.frameName);
10204
10439
  return parts.join(":");
10205
10440
  };
10206
- /**
10207
- * Broadcasts StrategyCommitContract event to strategyCommitSubject.
10208
- *
10209
- * @param event - The signal commit event to broadcast
10210
- */
10211
- const CALL_STRATEGY_COMMIT_FN = trycatch(async (event) => {
10212
- await strategyCommitSubject.next(event);
10213
- }, {
10214
- fallback: (error) => {
10215
- const message = "StrategyCoreService CALL_STRATEGY_COMMIT_FN thrown";
10216
- const payload = {
10217
- error: errorData(error),
10218
- message: getErrorMessage(error),
10219
- };
10220
- bt.loggerService.warn(message, payload);
10221
- console.warn(message, payload);
10222
- errorEmitter.next(error);
10223
- },
10224
- defaultValue: null,
10225
- });
10226
10441
  /**
10227
10442
  * Global service for strategy operations with execution context injection.
10228
10443
  *
@@ -10455,19 +10670,7 @@ class StrategyCoreService {
10455
10670
  cancelId,
10456
10671
  });
10457
10672
  await this.validate(context);
10458
- const result = await this.strategyConnectionService.cancelScheduled(backtest, symbol, context, cancelId);
10459
- {
10460
- await CALL_STRATEGY_COMMIT_FN({
10461
- action: "cancel-scheduled",
10462
- symbol,
10463
- strategyName: context.strategyName,
10464
- exchangeName: context.exchangeName,
10465
- frameName: context.frameName,
10466
- backtest,
10467
- cancelId,
10468
- });
10469
- }
10470
- return result;
10673
+ return await this.strategyConnectionService.cancelScheduled(backtest, symbol, context, cancelId);
10471
10674
  };
10472
10675
  /**
10473
10676
  * Closes the pending signal without stopping the strategy.
@@ -10494,19 +10697,7 @@ class StrategyCoreService {
10494
10697
  closeId,
10495
10698
  });
10496
10699
  await this.validate(context);
10497
- const result = await this.strategyConnectionService.closePending(backtest, symbol, context, closeId);
10498
- {
10499
- await CALL_STRATEGY_COMMIT_FN({
10500
- action: "close-pending",
10501
- symbol,
10502
- strategyName: context.strategyName,
10503
- exchangeName: context.exchangeName,
10504
- frameName: context.frameName,
10505
- backtest,
10506
- closeId,
10507
- });
10508
- }
10509
- return result;
10700
+ return await this.strategyConnectionService.closePending(backtest, symbol, context, closeId);
10510
10701
  };
10511
10702
  /**
10512
10703
  * Disposes the ClientStrategy instance for the given context.
@@ -10587,20 +10778,7 @@ class StrategyCoreService {
10587
10778
  backtest,
10588
10779
  });
10589
10780
  await this.validate(context);
10590
- const result = await this.strategyConnectionService.partialProfit(backtest, symbol, percentToClose, currentPrice, context);
10591
- if (result) {
10592
- await CALL_STRATEGY_COMMIT_FN({
10593
- action: "partial-profit",
10594
- symbol,
10595
- strategyName: context.strategyName,
10596
- exchangeName: context.exchangeName,
10597
- frameName: context.frameName,
10598
- backtest,
10599
- percentToClose,
10600
- currentPrice,
10601
- });
10602
- }
10603
- return result;
10781
+ return await this.strategyConnectionService.partialProfit(backtest, symbol, percentToClose, currentPrice, context);
10604
10782
  };
10605
10783
  /**
10606
10784
  * Executes partial close at loss level (moving toward SL).
@@ -10641,20 +10819,7 @@ class StrategyCoreService {
10641
10819
  backtest,
10642
10820
  });
10643
10821
  await this.validate(context);
10644
- const result = await this.strategyConnectionService.partialLoss(backtest, symbol, percentToClose, currentPrice, context);
10645
- if (result) {
10646
- await CALL_STRATEGY_COMMIT_FN({
10647
- action: "partial-loss",
10648
- symbol,
10649
- strategyName: context.strategyName,
10650
- exchangeName: context.exchangeName,
10651
- frameName: context.frameName,
10652
- backtest,
10653
- percentToClose,
10654
- currentPrice,
10655
- });
10656
- }
10657
- return result;
10822
+ return await this.strategyConnectionService.partialLoss(backtest, symbol, percentToClose, currentPrice, context);
10658
10823
  };
10659
10824
  /**
10660
10825
  * Adjusts the trailing stop-loss distance for an active pending signal.
@@ -10693,20 +10858,7 @@ class StrategyCoreService {
10693
10858
  backtest,
10694
10859
  });
10695
10860
  await this.validate(context);
10696
- const result = await this.strategyConnectionService.trailingStop(backtest, symbol, percentShift, currentPrice, context);
10697
- if (result) {
10698
- await CALL_STRATEGY_COMMIT_FN({
10699
- action: "trailing-stop",
10700
- symbol,
10701
- strategyName: context.strategyName,
10702
- exchangeName: context.exchangeName,
10703
- frameName: context.frameName,
10704
- backtest,
10705
- percentShift,
10706
- currentPrice,
10707
- });
10708
- }
10709
- return result;
10861
+ return await this.strategyConnectionService.trailingStop(backtest, symbol, percentShift, currentPrice, context);
10710
10862
  };
10711
10863
  /**
10712
10864
  * Adjusts the trailing take-profit distance for an active pending signal.
@@ -10741,20 +10893,7 @@ class StrategyCoreService {
10741
10893
  backtest,
10742
10894
  });
10743
10895
  await this.validate(context);
10744
- const result = await this.strategyConnectionService.trailingTake(backtest, symbol, percentShift, currentPrice, context);
10745
- if (result) {
10746
- await CALL_STRATEGY_COMMIT_FN({
10747
- action: "trailing-take",
10748
- symbol,
10749
- strategyName: context.strategyName,
10750
- exchangeName: context.exchangeName,
10751
- frameName: context.frameName,
10752
- backtest,
10753
- percentShift,
10754
- currentPrice,
10755
- });
10756
- }
10757
- return result;
10896
+ return await this.strategyConnectionService.trailingTake(backtest, symbol, percentShift, currentPrice, context);
10758
10897
  };
10759
10898
  /**
10760
10899
  * Moves stop-loss to breakeven when price reaches threshold.
@@ -10784,19 +10923,7 @@ class StrategyCoreService {
10784
10923
  backtest,
10785
10924
  });
10786
10925
  await this.validate(context);
10787
- const result = await this.strategyConnectionService.breakeven(backtest, symbol, currentPrice, context);
10788
- if (result) {
10789
- await CALL_STRATEGY_COMMIT_FN({
10790
- action: "breakeven",
10791
- symbol,
10792
- strategyName: context.strategyName,
10793
- exchangeName: context.exchangeName,
10794
- frameName: context.frameName,
10795
- backtest,
10796
- currentPrice,
10797
- });
10798
- }
10799
- return result;
10926
+ return await this.strategyConnectionService.breakeven(backtest, symbol, currentPrice, context);
10800
10927
  };
10801
10928
  }
10802
10929
  }
@@ -23529,22 +23656,6 @@ class RiskReportService {
23529
23656
  }
23530
23657
  }
23531
23658
 
23532
- /**
23533
- * Extracts execution context timestamp for strategy event logging.
23534
- *
23535
- * @param self - The StrategyReportService instance to extract context from
23536
- * @returns Object containing ISO 8601 formatted timestamp, or empty string if no context
23537
- * @internal
23538
- */
23539
- const GET_EXECUTION_CONTEXT_FN$1 = (self) => {
23540
- if (ExecutionContextService.hasContext()) {
23541
- const { when } = self.executionContextService.context;
23542
- return { when: when.toISOString() };
23543
- }
23544
- return {
23545
- when: "",
23546
- };
23547
- };
23548
23659
  /**
23549
23660
  * Service for persisting strategy management events to JSON report files.
23550
23661
  *
@@ -23560,41 +23671,23 @@ const GET_EXECUTION_CONTEXT_FN$1 = (self) => {
23560
23671
  * - Events are written via Report.writeData() with "strategy" category
23561
23672
  * - Call unsubscribe() to disable event logging
23562
23673
  *
23563
- * @example
23564
- * ```typescript
23565
- * // Service is typically used internally by strategy management classes
23566
- * strategyReportService.subscribe();
23567
- *
23568
- * // Events are logged automatically when strategy actions occur
23569
- * await strategyReportService.partialProfit("BTCUSDT", 50, 50100, false, {
23570
- * strategyName: "my-strategy",
23571
- * exchangeName: "binance",
23572
- * frameName: "1h"
23573
- * });
23574
- *
23575
- * strategyReportService.unsubscribe();
23576
- * ```
23577
- *
23578
23674
  * @see StrategyMarkdownService for in-memory event accumulation and markdown report generation
23579
23675
  * @see Report for the underlying persistence mechanism
23580
23676
  */
23581
23677
  class StrategyReportService {
23582
23678
  constructor() {
23583
23679
  this.loggerService = inject(TYPES.loggerService);
23584
- this.executionContextService = inject(TYPES.executionContextService);
23585
23680
  this.strategyCoreService = inject(TYPES.strategyCoreService);
23586
23681
  /**
23587
23682
  * Logs a cancel-scheduled event when a scheduled signal is cancelled.
23588
23683
  *
23589
- * Retrieves the scheduled signal from StrategyCoreService and writes
23590
- * the cancellation event to the report file.
23591
- *
23592
23684
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23593
23685
  * @param isBacktest - Whether this is a backtest or live trading event
23594
23686
  * @param context - Strategy context with strategyName, exchangeName, frameName
23687
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23595
23688
  * @param cancelId - Optional identifier for the cancellation reason
23596
23689
  */
23597
- this.cancelScheduled = async (symbol, isBacktest, context, cancelId) => {
23690
+ this.cancelScheduled = async (symbol, isBacktest, context, timestamp, cancelId) => {
23598
23691
  this.loggerService.log("strategyReportService cancelScheduled", {
23599
23692
  symbol,
23600
23693
  isBacktest,
@@ -23603,7 +23696,6 @@ class StrategyReportService {
23603
23696
  if (!this.subscribe.hasValue()) {
23604
23697
  return;
23605
23698
  }
23606
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23607
23699
  const scheduledRow = await this.strategyCoreService.getScheduledSignal(isBacktest, symbol, {
23608
23700
  exchangeName: context.exchangeName,
23609
23701
  strategyName: context.strategyName,
@@ -23612,10 +23704,12 @@ class StrategyReportService {
23612
23704
  if (!scheduledRow) {
23613
23705
  return;
23614
23706
  }
23707
+ const createdAt = new Date(timestamp).toISOString();
23615
23708
  await Report.writeData("strategy", {
23616
23709
  action: "cancel-scheduled",
23617
23710
  cancelId,
23618
23711
  symbol,
23712
+ timestamp,
23619
23713
  createdAt,
23620
23714
  }, {
23621
23715
  signalId: scheduledRow.id,
@@ -23629,15 +23723,13 @@ class StrategyReportService {
23629
23723
  /**
23630
23724
  * Logs a close-pending event when a pending signal is closed.
23631
23725
  *
23632
- * Retrieves the pending signal from StrategyCoreService and writes
23633
- * the close event to the report file.
23634
- *
23635
23726
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23636
23727
  * @param isBacktest - Whether this is a backtest or live trading event
23637
23728
  * @param context - Strategy context with strategyName, exchangeName, frameName
23729
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23638
23730
  * @param closeId - Optional identifier for the close reason
23639
23731
  */
23640
- this.closePending = async (symbol, isBacktest, context, closeId) => {
23732
+ this.closePending = async (symbol, isBacktest, context, timestamp, closeId) => {
23641
23733
  this.loggerService.log("strategyReportService closePending", {
23642
23734
  symbol,
23643
23735
  isBacktest,
@@ -23646,7 +23738,6 @@ class StrategyReportService {
23646
23738
  if (!this.subscribe.hasValue()) {
23647
23739
  return;
23648
23740
  }
23649
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23650
23741
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
23651
23742
  exchangeName: context.exchangeName,
23652
23743
  strategyName: context.strategyName,
@@ -23655,10 +23746,12 @@ class StrategyReportService {
23655
23746
  if (!pendingRow) {
23656
23747
  return;
23657
23748
  }
23749
+ const createdAt = new Date(timestamp).toISOString();
23658
23750
  await Report.writeData("strategy", {
23659
23751
  action: "close-pending",
23660
23752
  closeId,
23661
23753
  symbol,
23754
+ timestamp,
23662
23755
  createdAt,
23663
23756
  }, {
23664
23757
  signalId: pendingRow.id,
@@ -23672,15 +23765,14 @@ class StrategyReportService {
23672
23765
  /**
23673
23766
  * Logs a partial-profit event when a portion of the position is closed at profit.
23674
23767
  *
23675
- * Records the percentage closed and current price when partial profit-taking occurs.
23676
- *
23677
23768
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23678
23769
  * @param percentToClose - Percentage of position to close (0-100)
23679
23770
  * @param currentPrice - Current market price at time of partial close
23680
23771
  * @param isBacktest - Whether this is a backtest or live trading event
23681
23772
  * @param context - Strategy context with strategyName, exchangeName, frameName
23773
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23682
23774
  */
23683
- this.partialProfit = async (symbol, percentToClose, currentPrice, isBacktest, context) => {
23775
+ this.partialProfit = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp) => {
23684
23776
  this.loggerService.log("strategyReportService partialProfit", {
23685
23777
  symbol,
23686
23778
  percentToClose,
@@ -23690,7 +23782,6 @@ class StrategyReportService {
23690
23782
  if (!this.subscribe.hasValue()) {
23691
23783
  return;
23692
23784
  }
23693
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23694
23785
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
23695
23786
  exchangeName: context.exchangeName,
23696
23787
  strategyName: context.strategyName,
@@ -23699,11 +23790,13 @@ class StrategyReportService {
23699
23790
  if (!pendingRow) {
23700
23791
  return;
23701
23792
  }
23793
+ const createdAt = new Date(timestamp).toISOString();
23702
23794
  await Report.writeData("strategy", {
23703
23795
  action: "partial-profit",
23704
23796
  percentToClose,
23705
23797
  currentPrice,
23706
23798
  symbol,
23799
+ timestamp,
23707
23800
  createdAt,
23708
23801
  }, {
23709
23802
  signalId: pendingRow.id,
@@ -23717,15 +23810,14 @@ class StrategyReportService {
23717
23810
  /**
23718
23811
  * Logs a partial-loss event when a portion of the position is closed at loss.
23719
23812
  *
23720
- * Records the percentage closed and current price when partial loss-cutting occurs.
23721
- *
23722
23813
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23723
23814
  * @param percentToClose - Percentage of position to close (0-100)
23724
23815
  * @param currentPrice - Current market price at time of partial close
23725
23816
  * @param isBacktest - Whether this is a backtest or live trading event
23726
23817
  * @param context - Strategy context with strategyName, exchangeName, frameName
23818
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23727
23819
  */
23728
- this.partialLoss = async (symbol, percentToClose, currentPrice, isBacktest, context) => {
23820
+ this.partialLoss = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp) => {
23729
23821
  this.loggerService.log("strategyReportService partialLoss", {
23730
23822
  symbol,
23731
23823
  percentToClose,
@@ -23735,7 +23827,6 @@ class StrategyReportService {
23735
23827
  if (!this.subscribe.hasValue()) {
23736
23828
  return;
23737
23829
  }
23738
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23739
23830
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
23740
23831
  exchangeName: context.exchangeName,
23741
23832
  strategyName: context.strategyName,
@@ -23744,11 +23835,13 @@ class StrategyReportService {
23744
23835
  if (!pendingRow) {
23745
23836
  return;
23746
23837
  }
23838
+ const createdAt = new Date(timestamp).toISOString();
23747
23839
  await Report.writeData("strategy", {
23748
23840
  action: "partial-loss",
23749
23841
  percentToClose,
23750
23842
  currentPrice,
23751
23843
  symbol,
23844
+ timestamp,
23752
23845
  createdAt,
23753
23846
  }, {
23754
23847
  signalId: pendingRow.id,
@@ -23762,15 +23855,14 @@ class StrategyReportService {
23762
23855
  /**
23763
23856
  * Logs a trailing-stop event when the stop-loss is adjusted.
23764
23857
  *
23765
- * Records the percentage shift and current price when trailing stop moves.
23766
- *
23767
23858
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23768
23859
  * @param percentShift - Percentage the stop-loss was shifted
23769
23860
  * @param currentPrice - Current market price at time of adjustment
23770
23861
  * @param isBacktest - Whether this is a backtest or live trading event
23771
23862
  * @param context - Strategy context with strategyName, exchangeName, frameName
23863
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23772
23864
  */
23773
- this.trailingStop = async (symbol, percentShift, currentPrice, isBacktest, context) => {
23865
+ this.trailingStop = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp) => {
23774
23866
  this.loggerService.log("strategyReportService trailingStop", {
23775
23867
  symbol,
23776
23868
  percentShift,
@@ -23780,7 +23872,6 @@ class StrategyReportService {
23780
23872
  if (!this.subscribe.hasValue()) {
23781
23873
  return;
23782
23874
  }
23783
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23784
23875
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
23785
23876
  exchangeName: context.exchangeName,
23786
23877
  strategyName: context.strategyName,
@@ -23789,11 +23880,13 @@ class StrategyReportService {
23789
23880
  if (!pendingRow) {
23790
23881
  return;
23791
23882
  }
23883
+ const createdAt = new Date(timestamp).toISOString();
23792
23884
  await Report.writeData("strategy", {
23793
23885
  action: "trailing-stop",
23794
23886
  percentShift,
23795
23887
  currentPrice,
23796
23888
  symbol,
23889
+ timestamp,
23797
23890
  createdAt,
23798
23891
  }, {
23799
23892
  signalId: pendingRow.id,
@@ -23807,15 +23900,14 @@ class StrategyReportService {
23807
23900
  /**
23808
23901
  * Logs a trailing-take event when the take-profit is adjusted.
23809
23902
  *
23810
- * Records the percentage shift and current price when trailing take-profit moves.
23811
- *
23812
23903
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23813
23904
  * @param percentShift - Percentage the take-profit was shifted
23814
23905
  * @param currentPrice - Current market price at time of adjustment
23815
23906
  * @param isBacktest - Whether this is a backtest or live trading event
23816
23907
  * @param context - Strategy context with strategyName, exchangeName, frameName
23908
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23817
23909
  */
23818
- this.trailingTake = async (symbol, percentShift, currentPrice, isBacktest, context) => {
23910
+ this.trailingTake = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp) => {
23819
23911
  this.loggerService.log("strategyReportService trailingTake", {
23820
23912
  symbol,
23821
23913
  percentShift,
@@ -23825,7 +23917,6 @@ class StrategyReportService {
23825
23917
  if (!this.subscribe.hasValue()) {
23826
23918
  return;
23827
23919
  }
23828
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23829
23920
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
23830
23921
  exchangeName: context.exchangeName,
23831
23922
  strategyName: context.strategyName,
@@ -23834,11 +23925,13 @@ class StrategyReportService {
23834
23925
  if (!pendingRow) {
23835
23926
  return;
23836
23927
  }
23928
+ const createdAt = new Date(timestamp).toISOString();
23837
23929
  await Report.writeData("strategy", {
23838
23930
  action: "trailing-take",
23839
23931
  percentShift,
23840
23932
  currentPrice,
23841
23933
  symbol,
23934
+ timestamp,
23842
23935
  createdAt,
23843
23936
  }, {
23844
23937
  signalId: pendingRow.id,
@@ -23852,14 +23945,13 @@ class StrategyReportService {
23852
23945
  /**
23853
23946
  * Logs a breakeven event when the stop-loss is moved to entry price.
23854
23947
  *
23855
- * Records the current price when breakeven protection is activated.
23856
- *
23857
23948
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
23858
23949
  * @param currentPrice - Current market price at time of breakeven activation
23859
23950
  * @param isBacktest - Whether this is a backtest or live trading event
23860
23951
  * @param context - Strategy context with strategyName, exchangeName, frameName
23952
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
23861
23953
  */
23862
- this.breakeven = async (symbol, currentPrice, isBacktest, context) => {
23954
+ this.breakeven = async (symbol, currentPrice, isBacktest, context, timestamp) => {
23863
23955
  this.loggerService.log("strategyReportService breakeven", {
23864
23956
  symbol,
23865
23957
  currentPrice,
@@ -23868,7 +23960,6 @@ class StrategyReportService {
23868
23960
  if (!this.subscribe.hasValue()) {
23869
23961
  return;
23870
23962
  }
23871
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN$1(this);
23872
23963
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
23873
23964
  exchangeName: context.exchangeName,
23874
23965
  strategyName: context.strategyName,
@@ -23877,10 +23968,12 @@ class StrategyReportService {
23877
23968
  if (!pendingRow) {
23878
23969
  return;
23879
23970
  }
23971
+ const createdAt = new Date(timestamp).toISOString();
23880
23972
  await Report.writeData("strategy", {
23881
23973
  action: "breakeven",
23882
23974
  currentPrice,
23883
23975
  symbol,
23976
+ timestamp,
23884
23977
  createdAt,
23885
23978
  }, {
23886
23979
  signalId: pendingRow.id,
@@ -23907,49 +24000,49 @@ class StrategyReportService {
23907
24000
  exchangeName: event.exchangeName,
23908
24001
  frameName: event.frameName,
23909
24002
  strategyName: event.strategyName,
23910
- }, event.cancelId));
24003
+ }, event.timestamp, event.cancelId));
23911
24004
  const unClosePending = strategyCommitSubject
23912
24005
  .filter(({ action }) => action === "close-pending")
23913
24006
  .connect(async (event) => await this.closePending(event.symbol, event.backtest, {
23914
24007
  exchangeName: event.exchangeName,
23915
24008
  frameName: event.frameName,
23916
24009
  strategyName: event.strategyName,
23917
- }, event.closeId));
24010
+ }, event.timestamp, event.closeId));
23918
24011
  const unPartialProfit = strategyCommitSubject
23919
24012
  .filter(({ action }) => action === "partial-profit")
23920
24013
  .connect(async (event) => await this.partialProfit(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
23921
24014
  exchangeName: event.exchangeName,
23922
24015
  frameName: event.frameName,
23923
24016
  strategyName: event.strategyName,
23924
- }));
24017
+ }, event.timestamp));
23925
24018
  const unPartialLoss = strategyCommitSubject
23926
24019
  .filter(({ action }) => action === "partial-loss")
23927
24020
  .connect(async (event) => await this.partialLoss(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
23928
24021
  exchangeName: event.exchangeName,
23929
24022
  frameName: event.frameName,
23930
24023
  strategyName: event.strategyName,
23931
- }));
24024
+ }, event.timestamp));
23932
24025
  const unTrailingStop = strategyCommitSubject
23933
24026
  .filter(({ action }) => action === "trailing-stop")
23934
24027
  .connect(async (event) => await this.trailingStop(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
23935
24028
  exchangeName: event.exchangeName,
23936
24029
  frameName: event.frameName,
23937
24030
  strategyName: event.strategyName,
23938
- }));
24031
+ }, event.timestamp));
23939
24032
  const unTrailingTake = strategyCommitSubject
23940
24033
  .filter(({ action }) => action === "trailing-take")
23941
24034
  .connect(async (event) => await this.trailingTake(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
23942
24035
  exchangeName: event.exchangeName,
23943
24036
  frameName: event.frameName,
23944
24037
  strategyName: event.strategyName,
23945
- }));
24038
+ }, event.timestamp));
23946
24039
  const unBreakeven = strategyCommitSubject
23947
24040
  .filter(({ action }) => action === "breakeven")
23948
24041
  .connect(async (event) => await this.breakeven(event.symbol, event.currentPrice, event.backtest, {
23949
24042
  exchangeName: event.exchangeName,
23950
24043
  frameName: event.frameName,
23951
24044
  strategyName: event.strategyName,
23952
- }));
24045
+ }, event.timestamp));
23953
24046
  const disposeFn = compose(() => unCancelSchedule(), () => unClosePending(), () => unPartialProfit(), () => unPartialLoss(), () => unTrailingStop(), () => unTrailingTake(), () => unBreakeven());
23954
24047
  return () => {
23955
24048
  disposeFn();
@@ -23971,22 +24064,6 @@ class StrategyReportService {
23971
24064
  }
23972
24065
  }
23973
24066
 
23974
- /**
23975
- * Extracts execution context timestamp for strategy event logging.
23976
- *
23977
- * @param self - The StrategyMarkdownService instance to extract context from
23978
- * @returns Object containing ISO 8601 formatted timestamp, or empty string if no context
23979
- * @internal
23980
- */
23981
- const GET_EXECUTION_CONTEXT_FN = (self) => {
23982
- if (ExecutionContextService.hasContext()) {
23983
- const { when } = self.executionContextService.context;
23984
- return { when: when.toISOString() };
23985
- }
23986
- return {
23987
- when: "",
23988
- };
23989
- };
23990
24067
  /**
23991
24068
  * Creates a unique key for memoizing ReportStorage instances.
23992
24069
  *
@@ -24234,7 +24311,6 @@ class ReportStorage {
24234
24311
  class StrategyMarkdownService {
24235
24312
  constructor() {
24236
24313
  this.loggerService = inject(TYPES.loggerService);
24237
- this.executionContextService = inject(TYPES.executionContextService);
24238
24314
  this.strategyCoreService = inject(TYPES.strategyCoreService);
24239
24315
  /**
24240
24316
  * Memoized factory for ReportStorage instances.
@@ -24248,15 +24324,13 @@ class StrategyMarkdownService {
24248
24324
  /**
24249
24325
  * Records a cancel-scheduled event when a scheduled signal is cancelled.
24250
24326
  *
24251
- * Retrieves the scheduled signal from StrategyCoreService and stores
24252
- * the cancellation event in the appropriate ReportStorage.
24253
- *
24254
24327
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24255
24328
  * @param isBacktest - Whether this is a backtest or live trading event
24256
24329
  * @param context - Strategy context with strategyName, exchangeName, frameName
24330
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24257
24331
  * @param cancelId - Optional identifier for the cancellation reason
24258
24332
  */
24259
- this.cancelScheduled = async (symbol, isBacktest, context, cancelId) => {
24333
+ this.cancelScheduled = async (symbol, isBacktest, context, timestamp, cancelId) => {
24260
24334
  this.loggerService.log("strategyMarkdownService cancelScheduled", {
24261
24335
  symbol,
24262
24336
  isBacktest,
@@ -24265,7 +24339,6 @@ class StrategyMarkdownService {
24265
24339
  if (!this.subscribe.hasValue()) {
24266
24340
  return;
24267
24341
  }
24268
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24269
24342
  const scheduledRow = await this.strategyCoreService.getScheduledSignal(isBacktest, symbol, {
24270
24343
  exchangeName: context.exchangeName,
24271
24344
  strategyName: context.strategyName,
@@ -24274,9 +24347,10 @@ class StrategyMarkdownService {
24274
24347
  if (!scheduledRow) {
24275
24348
  return;
24276
24349
  }
24350
+ const createdAt = new Date(timestamp).toISOString();
24277
24351
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24278
24352
  storage.addEvent({
24279
- timestamp: Date.now(),
24353
+ timestamp,
24280
24354
  symbol,
24281
24355
  strategyName: context.strategyName,
24282
24356
  exchangeName: context.exchangeName,
@@ -24291,15 +24365,13 @@ class StrategyMarkdownService {
24291
24365
  /**
24292
24366
  * Records a close-pending event when a pending signal is closed.
24293
24367
  *
24294
- * Retrieves the pending signal from StrategyCoreService and stores
24295
- * the close event in the appropriate ReportStorage.
24296
- *
24297
24368
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24298
24369
  * @param isBacktest - Whether this is a backtest or live trading event
24299
24370
  * @param context - Strategy context with strategyName, exchangeName, frameName
24371
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24300
24372
  * @param closeId - Optional identifier for the close reason
24301
24373
  */
24302
- this.closePending = async (symbol, isBacktest, context, closeId) => {
24374
+ this.closePending = async (symbol, isBacktest, context, timestamp, closeId) => {
24303
24375
  this.loggerService.log("strategyMarkdownService closePending", {
24304
24376
  symbol,
24305
24377
  isBacktest,
@@ -24308,7 +24380,6 @@ class StrategyMarkdownService {
24308
24380
  if (!this.subscribe.hasValue()) {
24309
24381
  return;
24310
24382
  }
24311
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24312
24383
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
24313
24384
  exchangeName: context.exchangeName,
24314
24385
  strategyName: context.strategyName,
@@ -24317,9 +24388,10 @@ class StrategyMarkdownService {
24317
24388
  if (!pendingRow) {
24318
24389
  return;
24319
24390
  }
24391
+ const createdAt = new Date(timestamp).toISOString();
24320
24392
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24321
24393
  storage.addEvent({
24322
- timestamp: Date.now(),
24394
+ timestamp,
24323
24395
  symbol,
24324
24396
  strategyName: context.strategyName,
24325
24397
  exchangeName: context.exchangeName,
@@ -24334,15 +24406,14 @@ class StrategyMarkdownService {
24334
24406
  /**
24335
24407
  * Records a partial-profit event when a portion of the position is closed at profit.
24336
24408
  *
24337
- * Stores the percentage closed and current price when partial profit-taking occurs.
24338
- *
24339
24409
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24340
24410
  * @param percentToClose - Percentage of position to close (0-100)
24341
24411
  * @param currentPrice - Current market price at time of partial close
24342
24412
  * @param isBacktest - Whether this is a backtest or live trading event
24343
24413
  * @param context - Strategy context with strategyName, exchangeName, frameName
24414
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24344
24415
  */
24345
- this.partialProfit = async (symbol, percentToClose, currentPrice, isBacktest, context) => {
24416
+ this.partialProfit = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp) => {
24346
24417
  this.loggerService.log("strategyMarkdownService partialProfit", {
24347
24418
  symbol,
24348
24419
  percentToClose,
@@ -24352,7 +24423,6 @@ class StrategyMarkdownService {
24352
24423
  if (!this.subscribe.hasValue()) {
24353
24424
  return;
24354
24425
  }
24355
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24356
24426
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
24357
24427
  exchangeName: context.exchangeName,
24358
24428
  strategyName: context.strategyName,
@@ -24361,9 +24431,10 @@ class StrategyMarkdownService {
24361
24431
  if (!pendingRow) {
24362
24432
  return;
24363
24433
  }
24434
+ const createdAt = new Date(timestamp).toISOString();
24364
24435
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24365
24436
  storage.addEvent({
24366
- timestamp: Date.now(),
24437
+ timestamp,
24367
24438
  symbol,
24368
24439
  strategyName: context.strategyName,
24369
24440
  exchangeName: context.exchangeName,
@@ -24379,15 +24450,14 @@ class StrategyMarkdownService {
24379
24450
  /**
24380
24451
  * Records a partial-loss event when a portion of the position is closed at loss.
24381
24452
  *
24382
- * Stores the percentage closed and current price when partial loss-cutting occurs.
24383
- *
24384
24453
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24385
24454
  * @param percentToClose - Percentage of position to close (0-100)
24386
24455
  * @param currentPrice - Current market price at time of partial close
24387
24456
  * @param isBacktest - Whether this is a backtest or live trading event
24388
24457
  * @param context - Strategy context with strategyName, exchangeName, frameName
24458
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24389
24459
  */
24390
- this.partialLoss = async (symbol, percentToClose, currentPrice, isBacktest, context) => {
24460
+ this.partialLoss = async (symbol, percentToClose, currentPrice, isBacktest, context, timestamp) => {
24391
24461
  this.loggerService.log("strategyMarkdownService partialLoss", {
24392
24462
  symbol,
24393
24463
  percentToClose,
@@ -24397,7 +24467,6 @@ class StrategyMarkdownService {
24397
24467
  if (!this.subscribe.hasValue()) {
24398
24468
  return;
24399
24469
  }
24400
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24401
24470
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
24402
24471
  exchangeName: context.exchangeName,
24403
24472
  strategyName: context.strategyName,
@@ -24406,9 +24475,10 @@ class StrategyMarkdownService {
24406
24475
  if (!pendingRow) {
24407
24476
  return;
24408
24477
  }
24478
+ const createdAt = new Date(timestamp).toISOString();
24409
24479
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24410
24480
  storage.addEvent({
24411
- timestamp: Date.now(),
24481
+ timestamp,
24412
24482
  symbol,
24413
24483
  strategyName: context.strategyName,
24414
24484
  exchangeName: context.exchangeName,
@@ -24424,15 +24494,14 @@ class StrategyMarkdownService {
24424
24494
  /**
24425
24495
  * Records a trailing-stop event when the stop-loss is adjusted.
24426
24496
  *
24427
- * Stores the percentage shift and current price when trailing stop moves.
24428
- *
24429
24497
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24430
24498
  * @param percentShift - Percentage the stop-loss was shifted
24431
24499
  * @param currentPrice - Current market price at time of adjustment
24432
24500
  * @param isBacktest - Whether this is a backtest or live trading event
24433
24501
  * @param context - Strategy context with strategyName, exchangeName, frameName
24502
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24434
24503
  */
24435
- this.trailingStop = async (symbol, percentShift, currentPrice, isBacktest, context) => {
24504
+ this.trailingStop = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp) => {
24436
24505
  this.loggerService.log("strategyMarkdownService trailingStop", {
24437
24506
  symbol,
24438
24507
  percentShift,
@@ -24442,7 +24511,6 @@ class StrategyMarkdownService {
24442
24511
  if (!this.subscribe.hasValue()) {
24443
24512
  return;
24444
24513
  }
24445
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24446
24514
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
24447
24515
  exchangeName: context.exchangeName,
24448
24516
  strategyName: context.strategyName,
@@ -24451,9 +24519,10 @@ class StrategyMarkdownService {
24451
24519
  if (!pendingRow) {
24452
24520
  return;
24453
24521
  }
24522
+ const createdAt = new Date(timestamp).toISOString();
24454
24523
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24455
24524
  storage.addEvent({
24456
- timestamp: Date.now(),
24525
+ timestamp,
24457
24526
  symbol,
24458
24527
  strategyName: context.strategyName,
24459
24528
  exchangeName: context.exchangeName,
@@ -24469,15 +24538,14 @@ class StrategyMarkdownService {
24469
24538
  /**
24470
24539
  * Records a trailing-take event when the take-profit is adjusted.
24471
24540
  *
24472
- * Stores the percentage shift and current price when trailing take-profit moves.
24473
- *
24474
24541
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24475
24542
  * @param percentShift - Percentage the take-profit was shifted
24476
24543
  * @param currentPrice - Current market price at time of adjustment
24477
24544
  * @param isBacktest - Whether this is a backtest or live trading event
24478
24545
  * @param context - Strategy context with strategyName, exchangeName, frameName
24546
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24479
24547
  */
24480
- this.trailingTake = async (symbol, percentShift, currentPrice, isBacktest, context) => {
24548
+ this.trailingTake = async (symbol, percentShift, currentPrice, isBacktest, context, timestamp) => {
24481
24549
  this.loggerService.log("strategyMarkdownService trailingTake", {
24482
24550
  symbol,
24483
24551
  percentShift,
@@ -24487,7 +24555,6 @@ class StrategyMarkdownService {
24487
24555
  if (!this.subscribe.hasValue()) {
24488
24556
  return;
24489
24557
  }
24490
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24491
24558
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
24492
24559
  exchangeName: context.exchangeName,
24493
24560
  strategyName: context.strategyName,
@@ -24496,9 +24563,10 @@ class StrategyMarkdownService {
24496
24563
  if (!pendingRow) {
24497
24564
  return;
24498
24565
  }
24566
+ const createdAt = new Date(timestamp).toISOString();
24499
24567
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24500
24568
  storage.addEvent({
24501
- timestamp: Date.now(),
24569
+ timestamp,
24502
24570
  symbol,
24503
24571
  strategyName: context.strategyName,
24504
24572
  exchangeName: context.exchangeName,
@@ -24514,14 +24582,13 @@ class StrategyMarkdownService {
24514
24582
  /**
24515
24583
  * Records a breakeven event when the stop-loss is moved to entry price.
24516
24584
  *
24517
- * Stores the current price when breakeven protection is activated.
24518
- *
24519
24585
  * @param symbol - Trading pair symbol (e.g., "BTCUSDT")
24520
24586
  * @param currentPrice - Current market price at time of breakeven activation
24521
24587
  * @param isBacktest - Whether this is a backtest or live trading event
24522
24588
  * @param context - Strategy context with strategyName, exchangeName, frameName
24589
+ * @param timestamp - Timestamp from StrategyCommitContract (execution context time)
24523
24590
  */
24524
- this.breakeven = async (symbol, currentPrice, isBacktest, context) => {
24591
+ this.breakeven = async (symbol, currentPrice, isBacktest, context, timestamp) => {
24525
24592
  this.loggerService.log("strategyMarkdownService breakeven", {
24526
24593
  symbol,
24527
24594
  currentPrice,
@@ -24530,7 +24597,6 @@ class StrategyMarkdownService {
24530
24597
  if (!this.subscribe.hasValue()) {
24531
24598
  return;
24532
24599
  }
24533
- const { when: createdAt } = GET_EXECUTION_CONTEXT_FN(this);
24534
24600
  const pendingRow = await this.strategyCoreService.getPendingSignal(isBacktest, symbol, {
24535
24601
  exchangeName: context.exchangeName,
24536
24602
  strategyName: context.strategyName,
@@ -24539,9 +24605,10 @@ class StrategyMarkdownService {
24539
24605
  if (!pendingRow) {
24540
24606
  return;
24541
24607
  }
24608
+ const createdAt = new Date(timestamp).toISOString();
24542
24609
  const storage = this.getStorage(symbol, context.strategyName, context.exchangeName, context.frameName, isBacktest);
24543
24610
  storage.addEvent({
24544
- timestamp: Date.now(),
24611
+ timestamp,
24545
24612
  symbol,
24546
24613
  strategyName: context.strategyName,
24547
24614
  exchangeName: context.exchangeName,
@@ -24683,49 +24750,49 @@ class StrategyMarkdownService {
24683
24750
  exchangeName: event.exchangeName,
24684
24751
  frameName: event.frameName,
24685
24752
  strategyName: event.strategyName,
24686
- }, event.cancelId));
24753
+ }, event.timestamp, event.cancelId));
24687
24754
  const unClosePending = strategyCommitSubject
24688
24755
  .filter(({ action }) => action === "close-pending")
24689
24756
  .connect(async (event) => await this.closePending(event.symbol, event.backtest, {
24690
24757
  exchangeName: event.exchangeName,
24691
24758
  frameName: event.frameName,
24692
24759
  strategyName: event.strategyName,
24693
- }, event.closeId));
24760
+ }, event.timestamp, event.closeId));
24694
24761
  const unPartialProfit = strategyCommitSubject
24695
24762
  .filter(({ action }) => action === "partial-profit")
24696
24763
  .connect(async (event) => await this.partialProfit(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
24697
24764
  exchangeName: event.exchangeName,
24698
24765
  frameName: event.frameName,
24699
24766
  strategyName: event.strategyName,
24700
- }));
24767
+ }, event.timestamp));
24701
24768
  const unPartialLoss = strategyCommitSubject
24702
24769
  .filter(({ action }) => action === "partial-loss")
24703
24770
  .connect(async (event) => await this.partialLoss(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
24704
24771
  exchangeName: event.exchangeName,
24705
24772
  frameName: event.frameName,
24706
24773
  strategyName: event.strategyName,
24707
- }));
24774
+ }, event.timestamp));
24708
24775
  const unTrailingStop = strategyCommitSubject
24709
24776
  .filter(({ action }) => action === "trailing-stop")
24710
24777
  .connect(async (event) => await this.trailingStop(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
24711
24778
  exchangeName: event.exchangeName,
24712
24779
  frameName: event.frameName,
24713
24780
  strategyName: event.strategyName,
24714
- }));
24781
+ }, event.timestamp));
24715
24782
  const unTrailingTake = strategyCommitSubject
24716
24783
  .filter(({ action }) => action === "trailing-take")
24717
24784
  .connect(async (event) => await this.trailingTake(event.symbol, event.percentShift, event.currentPrice, event.backtest, {
24718
24785
  exchangeName: event.exchangeName,
24719
24786
  frameName: event.frameName,
24720
24787
  strategyName: event.strategyName,
24721
- }));
24788
+ }, event.timestamp));
24722
24789
  const unBreakeven = strategyCommitSubject
24723
24790
  .filter(({ action }) => action === "breakeven")
24724
24791
  .connect(async (event) => await this.breakeven(event.symbol, event.currentPrice, event.backtest, {
24725
24792
  exchangeName: event.exchangeName,
24726
24793
  frameName: event.frameName,
24727
24794
  strategyName: event.strategyName,
24728
- }));
24795
+ }, event.timestamp));
24729
24796
  const disposeFn = compose(() => unCancelSchedule(), () => unClosePending(), () => unPartialProfit(), () => unPartialLoss(), () => unTrailingStop(), () => unTrailingTake(), () => unBreakeven());
24730
24797
  return () => {
24731
24798
  disposeFn();
@@ -31605,6 +31672,7 @@ class StorageBacktestUtils {
31605
31672
  ...tick.signal,
31606
31673
  status: "opened",
31607
31674
  priority: Date.now(),
31675
+ createdAt: lastStorage ? lastStorage.createdAt : tick.createdAt,
31608
31676
  updatedAt: tick.createdAt,
31609
31677
  });
31610
31678
  await this._updateStorage();
@@ -31628,6 +31696,7 @@ class StorageBacktestUtils {
31628
31696
  ...tick.signal,
31629
31697
  status: "closed",
31630
31698
  priority: Date.now(),
31699
+ createdAt: lastStorage ? lastStorage.createdAt : tick.createdAt,
31631
31700
  updatedAt: tick.createdAt,
31632
31701
  });
31633
31702
  await this._updateStorage();
@@ -31651,6 +31720,7 @@ class StorageBacktestUtils {
31651
31720
  ...tick.signal,
31652
31721
  status: "scheduled",
31653
31722
  priority: Date.now(),
31723
+ createdAt: lastStorage ? lastStorage.createdAt : tick.createdAt,
31654
31724
  updatedAt: tick.createdAt,
31655
31725
  });
31656
31726
  await this._updateStorage();
@@ -31674,6 +31744,7 @@ class StorageBacktestUtils {
31674
31744
  ...tick.signal,
31675
31745
  status: "cancelled",
31676
31746
  priority: Date.now(),
31747
+ createdAt: lastStorage ? lastStorage.createdAt : tick.createdAt,
31677
31748
  updatedAt: tick.createdAt,
31678
31749
  });
31679
31750
  await this._updateStorage();
@@ -31770,6 +31841,7 @@ class StorageLiveUtils {
31770
31841
  ...tick.signal,
31771
31842
  status: "opened",
31772
31843
  priority: Date.now(),
31844
+ createdAt: lastStorage ? lastStorage.createdAt : tick.createdAt,
31773
31845
  updatedAt: tick.createdAt,
31774
31846
  });
31775
31847
  await this._updateStorage();
@@ -31793,6 +31865,7 @@ class StorageLiveUtils {
31793
31865
  ...tick.signal,
31794
31866
  status: "closed",
31795
31867
  priority: Date.now(),
31868
+ createdAt: lastStorage ? lastStorage.createdAt : tick.createdAt,
31796
31869
  updatedAt: tick.createdAt,
31797
31870
  });
31798
31871
  await this._updateStorage();
@@ -31816,6 +31889,7 @@ class StorageLiveUtils {
31816
31889
  ...tick.signal,
31817
31890
  status: "scheduled",
31818
31891
  priority: Date.now(),
31892
+ createdAt: lastStorage ? lastStorage.createdAt : tick.createdAt,
31819
31893
  updatedAt: tick.createdAt,
31820
31894
  });
31821
31895
  await this._updateStorage();
@@ -31839,6 +31913,7 @@ class StorageLiveUtils {
31839
31913
  ...tick.signal,
31840
31914
  status: "cancelled",
31841
31915
  priority: Date.now(),
31916
+ createdAt: lastStorage ? lastStorage.createdAt : tick.createdAt,
31842
31917
  updatedAt: tick.createdAt,
31843
31918
  });
31844
31919
  await this._updateStorage();
@@ -33086,12 +33161,6 @@ class NotificationInstance {
33086
33161
  createdAt: data.createdAt,
33087
33162
  });
33088
33163
  }
33089
- // Sort signal notifications by createdAt (newest first)
33090
- this._notifications.sort((a, b) => {
33091
- const aCreatedAt = "createdAt" in a ? a.createdAt : 0;
33092
- const bCreatedAt = "createdAt" in b ? b.createdAt : 0;
33093
- return bCreatedAt - aCreatedAt;
33094
- });
33095
33164
  };
33096
33165
  /**
33097
33166
  * Processes partial profit events.
@@ -33110,6 +33179,7 @@ class NotificationInstance {
33110
33179
  currentPrice: data.currentPrice,
33111
33180
  priceOpen: data.data.priceOpen,
33112
33181
  position: data.data.position,
33182
+ createdAt: data.timestamp,
33113
33183
  });
33114
33184
  };
33115
33185
  /**
@@ -33129,6 +33199,7 @@ class NotificationInstance {
33129
33199
  currentPrice: data.currentPrice,
33130
33200
  priceOpen: data.data.priceOpen,
33131
33201
  position: data.data.position,
33202
+ createdAt: data.timestamp,
33132
33203
  });
33133
33204
  };
33134
33205
  /**
@@ -33147,6 +33218,7 @@ class NotificationInstance {
33147
33218
  currentPrice: data.currentPrice,
33148
33219
  priceOpen: data.data.priceOpen,
33149
33220
  position: data.data.position,
33221
+ createdAt: data.timestamp,
33150
33222
  });
33151
33223
  };
33152
33224
  /**
@@ -33157,64 +33229,69 @@ class NotificationInstance {
33157
33229
  this._addNotification({
33158
33230
  type: "partial_profit.commit",
33159
33231
  id: CREATE_KEY_FN(),
33160
- timestamp: Date.now(),
33232
+ timestamp: data.timestamp,
33161
33233
  backtest: data.backtest,
33162
33234
  symbol: data.symbol,
33163
33235
  strategyName: data.strategyName,
33164
33236
  exchangeName: data.exchangeName,
33165
33237
  percentToClose: data.percentToClose,
33166
33238
  currentPrice: data.currentPrice,
33239
+ createdAt: data.timestamp,
33167
33240
  });
33168
33241
  }
33169
33242
  else if (data.action === "partial-loss") {
33170
33243
  this._addNotification({
33171
33244
  type: "partial_loss.commit",
33172
33245
  id: CREATE_KEY_FN(),
33173
- timestamp: Date.now(),
33246
+ timestamp: data.timestamp,
33174
33247
  backtest: data.backtest,
33175
33248
  symbol: data.symbol,
33176
33249
  strategyName: data.strategyName,
33177
33250
  exchangeName: data.exchangeName,
33178
33251
  percentToClose: data.percentToClose,
33179
33252
  currentPrice: data.currentPrice,
33253
+ createdAt: data.timestamp,
33180
33254
  });
33181
33255
  }
33182
33256
  else if (data.action === "breakeven") {
33183
33257
  this._addNotification({
33184
33258
  type: "breakeven.commit",
33185
33259
  id: CREATE_KEY_FN(),
33186
- timestamp: Date.now(),
33260
+ timestamp: data.timestamp,
33187
33261
  backtest: data.backtest,
33188
33262
  symbol: data.symbol,
33189
33263
  strategyName: data.strategyName,
33190
33264
  exchangeName: data.exchangeName,
33191
33265
  currentPrice: data.currentPrice,
33266
+ createdAt: data.timestamp,
33192
33267
  });
33193
33268
  }
33194
33269
  else if (data.action === "trailing-stop") {
33195
33270
  this._addNotification({
33196
33271
  type: "trailing_stop.commit",
33197
33272
  id: CREATE_KEY_FN(),
33198
- timestamp: Date.now(),
33273
+ timestamp: data.timestamp,
33199
33274
  backtest: data.backtest,
33200
33275
  symbol: data.symbol,
33201
33276
  strategyName: data.strategyName,
33202
33277
  exchangeName: data.exchangeName,
33203
33278
  percentShift: data.percentShift,
33204
33279
  currentPrice: data.currentPrice,
33280
+ createdAt: data.timestamp,
33205
33281
  });
33206
33282
  }
33207
33283
  else if (data.action === "trailing-take") {
33208
33284
  this._addNotification({
33209
33285
  type: "trailing_take.commit",
33210
33286
  id: CREATE_KEY_FN(),
33211
- timestamp: Date.now(),
33287
+ timestamp: data.timestamp,
33212
33288
  backtest: data.backtest,
33213
33289
  symbol: data.symbol,
33214
33290
  strategyName: data.strategyName,
33215
33291
  exchangeName: data.exchangeName,
33216
33292
  percentShift: data.percentShift,
33217
33293
  currentPrice: data.currentPrice,
33294
+ createdAt: data.timestamp,
33218
33295
  });
33219
33296
  }
33220
33297
  };
@@ -33235,6 +33312,7 @@ class NotificationInstance {
33235
33312
  activePositionCount: data.activePositionCount,
33236
33313
  currentPrice: data.currentPrice,
33237
33314
  pendingSignal: data.pendingSignal,
33315
+ createdAt: data.timestamp,
33238
33316
  });
33239
33317
  };
33240
33318
  /**
@@ -33244,7 +33322,6 @@ class NotificationInstance {
33244
33322
  this._addNotification({
33245
33323
  type: "error.info",
33246
33324
  id: CREATE_KEY_FN(),
33247
- timestamp: Date.now(),
33248
33325
  error: errorData(error),
33249
33326
  message: getErrorMessage(error),
33250
33327
  backtest: false,
@@ -33257,7 +33334,6 @@ class NotificationInstance {
33257
33334
  this._addNotification({
33258
33335
  type: "error.critical",
33259
33336
  id: CREATE_KEY_FN(),
33260
- timestamp: Date.now(),
33261
33337
  error: errorData(error),
33262
33338
  message: getErrorMessage(error),
33263
33339
  backtest: false,
@@ -33270,7 +33346,6 @@ class NotificationInstance {
33270
33346
  this._addNotification({
33271
33347
  type: "error.validation",
33272
33348
  id: CREATE_KEY_FN(),
33273
- timestamp: Date.now(),
33274
33349
  error: errorData(error),
33275
33350
  message: getErrorMessage(error),
33276
33351
  backtest: false,