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