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.cjs +334 -259
- package/build/index.mjs +334 -259
- package/package.json +1 -1
- package/types.d.ts +128 -151
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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,
|