backtest-kit 6.11.0 → 6.12.0
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 +1154 -98
- package/build/index.mjs +1152 -99
- package/package.json +2 -2
- package/types.d.ts +944 -194
package/build/index.cjs
CHANGED
|
@@ -6593,7 +6593,7 @@ const RETURN_IDLE_FN = async (self, currentPrice) => {
|
|
|
6593
6593
|
await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, currentTime, self.params.execution.context.backtest);
|
|
6594
6594
|
return result;
|
|
6595
6595
|
};
|
|
6596
|
-
const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePrice, closeTimestamp, reason, cancelId) => {
|
|
6596
|
+
const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePrice, closeTimestamp, reason, cancelId, cancelNote) => {
|
|
6597
6597
|
self.params.logger.info("ClientStrategy backtest scheduled signal cancelled", {
|
|
6598
6598
|
symbol: self.params.execution.context.symbol,
|
|
6599
6599
|
signalId: scheduled.id,
|
|
@@ -6618,7 +6618,7 @@ const CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN = async (self, scheduled, averagePr
|
|
|
6618
6618
|
totalPartials: scheduled._partial?.length ?? 0,
|
|
6619
6619
|
originalPriceOpen: scheduled.priceOpen,
|
|
6620
6620
|
pnl: toProfitLossDto(scheduled, averagePrice),
|
|
6621
|
-
note: scheduled.note,
|
|
6621
|
+
note: cancelNote ?? scheduled.note,
|
|
6622
6622
|
});
|
|
6623
6623
|
}
|
|
6624
6624
|
await CALL_CANCEL_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
@@ -6785,7 +6785,7 @@ const CLOSE_USER_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, closedSignal, aver
|
|
|
6785
6785
|
totalPartials: closedSignal._partial?.length ?? 0,
|
|
6786
6786
|
originalPriceOpen: closedSignal.priceOpen,
|
|
6787
6787
|
pnl: toProfitLossDto(closedSignal, averagePrice),
|
|
6788
|
-
note: closedSignal.note,
|
|
6788
|
+
note: closedSignal.closeNote ?? closedSignal.note,
|
|
6789
6789
|
});
|
|
6790
6790
|
await CALL_CLOSE_CALLBACKS_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
6791
6791
|
await CALL_PARTIAL_CLEAR_FN(self, self.params.execution.context.symbol, closedSignal, averagePrice, closeTimestamp, self.params.execution.context.backtest);
|
|
@@ -6832,7 +6832,8 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
|
|
|
6832
6832
|
if (self._cancelledSignal) {
|
|
6833
6833
|
// Сигнал был отменен через cancel() в onSchedulePing
|
|
6834
6834
|
const cancelId = self._cancelledSignal.cancelId;
|
|
6835
|
-
const
|
|
6835
|
+
const cancelNote = self._cancelledSignal.cancelNote;
|
|
6836
|
+
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user", cancelId, cancelNote);
|
|
6836
6837
|
return { outcome: "cancelled", result };
|
|
6837
6838
|
}
|
|
6838
6839
|
// КРИТИЧНО: Проверяем был ли сигнал активирован пользователем через activateScheduled()
|
|
@@ -6886,7 +6887,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
|
|
|
6886
6887
|
totalPartials: activatedSignal._partial?.length ?? 0,
|
|
6887
6888
|
originalPriceOpen: activatedSignal.priceOpen,
|
|
6888
6889
|
pnl: toProfitLossDto(activatedSignal, averagePrice),
|
|
6889
|
-
note: activatedSignal.note,
|
|
6890
|
+
note: activatedSignal.activateNote ?? activatedSignal.note,
|
|
6890
6891
|
});
|
|
6891
6892
|
return { outcome: "pending" };
|
|
6892
6893
|
}
|
|
@@ -6918,7 +6919,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles, fra
|
|
|
6918
6919
|
pendingAt: publicSignalForCommit.pendingAt,
|
|
6919
6920
|
totalEntries: publicSignalForCommit.totalEntries,
|
|
6920
6921
|
totalPartials: publicSignalForCommit.totalPartials,
|
|
6921
|
-
note: publicSignalForCommit.note,
|
|
6922
|
+
note: activatedSignal.activateNote ?? publicSignalForCommit.note,
|
|
6922
6923
|
});
|
|
6923
6924
|
await CALL_OPEN_CALLBACKS_FN(self, self.params.execution.context.symbol, pendingSignal, pendingSignal.priceOpen, candle.timestamp, self.params.execution.context.backtest);
|
|
6924
6925
|
await CALL_BACKTEST_SCHEDULE_OPEN_FN(self, self.params.execution.context.symbol, pendingSignal, candle.timestamp, self.params.execution.context.backtest);
|
|
@@ -8039,6 +8040,44 @@ class ClientStrategy {
|
|
|
8039
8040
|
const currentPnl = toProfitLossDto(this._pendingSignal, currentPrice);
|
|
8040
8041
|
return Math.max(0, currentPnl.pnlCost - this._pendingSignal._fall.pnlCost);
|
|
8041
8042
|
}
|
|
8043
|
+
/**
|
|
8044
|
+
* Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
|
|
8045
|
+
*
|
|
8046
|
+
* Measures the total swing from the stored `_peak.pnlPercentage` to the stored `_fall.pnlPercentage`.
|
|
8047
|
+
* Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
|
|
8048
|
+
*
|
|
8049
|
+
* Returns null if no pending signal exists.
|
|
8050
|
+
*
|
|
8051
|
+
* @param symbol - Trading pair symbol
|
|
8052
|
+
* @param currentPrice - Current market price
|
|
8053
|
+
* @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
|
|
8054
|
+
*/
|
|
8055
|
+
async getMaxDrawdownDistancePnlPercentage(symbol, currentPrice) {
|
|
8056
|
+
this.params.logger.debug("ClientStrategy getMaxDrawdownDistancePnlPercentage", { symbol, currentPrice });
|
|
8057
|
+
if (!this._pendingSignal) {
|
|
8058
|
+
return null;
|
|
8059
|
+
}
|
|
8060
|
+
return Math.max(0, this._pendingSignal._peak.pnlPercentage - this._pendingSignal._fall.pnlPercentage);
|
|
8061
|
+
}
|
|
8062
|
+
/**
|
|
8063
|
+
* Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
|
|
8064
|
+
*
|
|
8065
|
+
* Measures the total swing from the stored `_peak.pnlCost` to the stored `_fall.pnlCost`.
|
|
8066
|
+
* Computed as: max(0, peakPnlCost - fallPnlCost).
|
|
8067
|
+
*
|
|
8068
|
+
* Returns null if no pending signal exists.
|
|
8069
|
+
*
|
|
8070
|
+
* @param symbol - Trading pair symbol
|
|
8071
|
+
* @param currentPrice - Current market price
|
|
8072
|
+
* @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
|
|
8073
|
+
*/
|
|
8074
|
+
async getMaxDrawdownDistancePnlCost(symbol, currentPrice) {
|
|
8075
|
+
this.params.logger.debug("ClientStrategy getMaxDrawdownDistancePnlCost", { symbol, currentPrice });
|
|
8076
|
+
if (!this._pendingSignal) {
|
|
8077
|
+
return null;
|
|
8078
|
+
}
|
|
8079
|
+
return Math.max(0, this._pendingSignal._peak.pnlCost - this._pendingSignal._fall.pnlCost);
|
|
8080
|
+
}
|
|
8042
8081
|
/**
|
|
8043
8082
|
* Performs a single tick of strategy execution.
|
|
8044
8083
|
*
|
|
@@ -8103,7 +8142,7 @@ class ClientStrategy {
|
|
|
8103
8142
|
totalPartials: cancelledSignal._partial?.length ?? 0,
|
|
8104
8143
|
originalPriceOpen: cancelledSignal.priceOpen,
|
|
8105
8144
|
pnl: toProfitLossDto(cancelledSignal, currentPrice),
|
|
8106
|
-
note: cancelledSignal.note,
|
|
8145
|
+
note: cancelledSignal.cancelNote ?? cancelledSignal.note,
|
|
8107
8146
|
});
|
|
8108
8147
|
// Call onCancel callback
|
|
8109
8148
|
await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, currentTime, this.params.execution.context.backtest);
|
|
@@ -8157,7 +8196,7 @@ class ClientStrategy {
|
|
|
8157
8196
|
totalPartials: closedSignal._partial?.length ?? 0,
|
|
8158
8197
|
originalPriceOpen: closedSignal.priceOpen,
|
|
8159
8198
|
pnl: toProfitLossDto(closedSignal, currentPrice),
|
|
8160
|
-
note: closedSignal.note,
|
|
8199
|
+
note: closedSignal.closeNote ?? closedSignal.note,
|
|
8161
8200
|
});
|
|
8162
8201
|
// Call onClose callback
|
|
8163
8202
|
await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, currentTime, this.params.execution.context.backtest);
|
|
@@ -8239,7 +8278,7 @@ class ClientStrategy {
|
|
|
8239
8278
|
totalPartials: activatedSignal._partial?.length ?? 0,
|
|
8240
8279
|
originalPriceOpen: activatedSignal.priceOpen,
|
|
8241
8280
|
pnl: toProfitLossDto(activatedSignal, currentPrice),
|
|
8242
|
-
note: activatedSignal.note,
|
|
8281
|
+
note: activatedSignal.activateNote ?? activatedSignal.note,
|
|
8243
8282
|
});
|
|
8244
8283
|
return await RETURN_IDLE_FN(this, currentPrice);
|
|
8245
8284
|
}
|
|
@@ -8270,7 +8309,7 @@ class ClientStrategy {
|
|
|
8270
8309
|
pendingAt: publicSignalForCommit.pendingAt,
|
|
8271
8310
|
totalEntries: publicSignalForCommit.totalEntries,
|
|
8272
8311
|
totalPartials: publicSignalForCommit.totalPartials,
|
|
8273
|
-
note: publicSignalForCommit.note,
|
|
8312
|
+
note: activatedSignal.activateNote ?? publicSignalForCommit.note,
|
|
8274
8313
|
});
|
|
8275
8314
|
// Call onOpen callback
|
|
8276
8315
|
await CALL_OPEN_CALLBACKS_FN(this, this.params.execution.context.symbol, pendingSignal, currentPrice, currentTime, this.params.execution.context.backtest);
|
|
@@ -8406,7 +8445,7 @@ class ClientStrategy {
|
|
|
8406
8445
|
totalPartials: cancelledSignal._partial?.length ?? 0,
|
|
8407
8446
|
originalPriceOpen: cancelledSignal.priceOpen,
|
|
8408
8447
|
pnl: toProfitLossDto(cancelledSignal, currentPrice),
|
|
8409
|
-
note: cancelledSignal.note,
|
|
8448
|
+
note: cancelledSignal.cancelNote ?? cancelledSignal.note,
|
|
8410
8449
|
});
|
|
8411
8450
|
await CALL_CANCEL_CALLBACKS_FN(this, this.params.execution.context.symbol, cancelledSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
|
|
8412
8451
|
const cancelledResult = {
|
|
@@ -8461,7 +8500,7 @@ class ClientStrategy {
|
|
|
8461
8500
|
totalPartials: closedSignal._partial?.length ?? 0,
|
|
8462
8501
|
originalPriceOpen: closedSignal.priceOpen,
|
|
8463
8502
|
pnl: toProfitLossDto(closedSignal, currentPrice),
|
|
8464
|
-
note: closedSignal.note,
|
|
8503
|
+
note: closedSignal.closeNote ?? closedSignal.note,
|
|
8465
8504
|
});
|
|
8466
8505
|
await CALL_CLOSE_CALLBACKS_FN(this, this.params.execution.context.symbol, closedSignal, currentPrice, closeTimestamp, this.params.execution.context.backtest);
|
|
8467
8506
|
// КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
|
|
@@ -8645,7 +8684,8 @@ class ClientStrategy {
|
|
|
8645
8684
|
* // Strategy continues, can generate new signals
|
|
8646
8685
|
* ```
|
|
8647
8686
|
*/
|
|
8648
|
-
async cancelScheduled(symbol, backtest,
|
|
8687
|
+
async cancelScheduled(symbol, backtest, payload) {
|
|
8688
|
+
const cancelId = payload.id;
|
|
8649
8689
|
this.params.logger.debug("ClientStrategy cancelScheduled", {
|
|
8650
8690
|
symbol,
|
|
8651
8691
|
hasScheduledSignal: this._scheduledSignal !== null,
|
|
@@ -8657,6 +8697,7 @@ class ClientStrategy {
|
|
|
8657
8697
|
if (this._scheduledSignal) {
|
|
8658
8698
|
this._cancelledSignal = Object.assign({}, this._scheduledSignal, {
|
|
8659
8699
|
cancelId,
|
|
8700
|
+
cancelNote: payload.note,
|
|
8660
8701
|
});
|
|
8661
8702
|
this._scheduledSignal = null;
|
|
8662
8703
|
}
|
|
@@ -8688,7 +8729,8 @@ class ClientStrategy {
|
|
|
8688
8729
|
* // Scheduled signal becomes pending signal immediately
|
|
8689
8730
|
* ```
|
|
8690
8731
|
*/
|
|
8691
|
-
async activateScheduled(symbol, backtest,
|
|
8732
|
+
async activateScheduled(symbol, backtest, payload) {
|
|
8733
|
+
const activateId = payload.id;
|
|
8692
8734
|
this.params.logger.debug("ClientStrategy activateScheduled", {
|
|
8693
8735
|
symbol,
|
|
8694
8736
|
hasScheduledSignal: this._scheduledSignal !== null,
|
|
@@ -8706,6 +8748,7 @@ class ClientStrategy {
|
|
|
8706
8748
|
if (this._scheduledSignal) {
|
|
8707
8749
|
this._activatedSignal = Object.assign({}, this._scheduledSignal, {
|
|
8708
8750
|
activateId,
|
|
8751
|
+
activateNote: payload.note,
|
|
8709
8752
|
});
|
|
8710
8753
|
this._scheduledSignal = null;
|
|
8711
8754
|
}
|
|
@@ -8737,7 +8780,8 @@ class ClientStrategy {
|
|
|
8737
8780
|
* // Strategy continues, can generate new signals
|
|
8738
8781
|
* ```
|
|
8739
8782
|
*/
|
|
8740
|
-
async closePending(symbol, backtest,
|
|
8783
|
+
async closePending(symbol, backtest, payload) {
|
|
8784
|
+
const closeId = payload.id;
|
|
8741
8785
|
this.params.logger.debug("ClientStrategy closePending", {
|
|
8742
8786
|
symbol,
|
|
8743
8787
|
hasPendingSignal: this._pendingSignal !== null,
|
|
@@ -8748,6 +8792,7 @@ class ClientStrategy {
|
|
|
8748
8792
|
if (this._pendingSignal) {
|
|
8749
8793
|
this._closedSignal = Object.assign({}, this._pendingSignal, {
|
|
8750
8794
|
closeId,
|
|
8795
|
+
closeNote: payload.note,
|
|
8751
8796
|
});
|
|
8752
8797
|
this._pendingSignal = null;
|
|
8753
8798
|
}
|
|
@@ -11238,6 +11283,48 @@ class StrategyConnectionService {
|
|
|
11238
11283
|
const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
|
|
11239
11284
|
return await strategy.getPositionHighestMaxDrawdownPnlCost(symbol, currentPrice);
|
|
11240
11285
|
};
|
|
11286
|
+
/**
|
|
11287
|
+
* Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
|
|
11288
|
+
*
|
|
11289
|
+
* Resolves current price via priceMetaService and delegates to
|
|
11290
|
+
* ClientStrategy.getMaxDrawdownDistancePnlPercentage().
|
|
11291
|
+
* Returns null if no pending signal exists.
|
|
11292
|
+
*
|
|
11293
|
+
* @param backtest - Whether running in backtest mode
|
|
11294
|
+
* @param symbol - Trading pair symbol
|
|
11295
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
11296
|
+
* @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
|
|
11297
|
+
*/
|
|
11298
|
+
this.getMaxDrawdownDistancePnlPercentage = async (backtest, symbol, context) => {
|
|
11299
|
+
this.loggerService.log("strategyConnectionService getMaxDrawdownDistancePnlPercentage", {
|
|
11300
|
+
symbol,
|
|
11301
|
+
context,
|
|
11302
|
+
});
|
|
11303
|
+
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
11304
|
+
const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
|
|
11305
|
+
return await strategy.getMaxDrawdownDistancePnlPercentage(symbol, currentPrice);
|
|
11306
|
+
};
|
|
11307
|
+
/**
|
|
11308
|
+
* Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
|
|
11309
|
+
*
|
|
11310
|
+
* Resolves current price via priceMetaService and delegates to
|
|
11311
|
+
* ClientStrategy.getMaxDrawdownDistancePnlCost().
|
|
11312
|
+
* Returns null if no pending signal exists.
|
|
11313
|
+
*
|
|
11314
|
+
* @param backtest - Whether running in backtest mode
|
|
11315
|
+
* @param symbol - Trading pair symbol
|
|
11316
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
11317
|
+
* @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
|
|
11318
|
+
*/
|
|
11319
|
+
this.getMaxDrawdownDistancePnlCost = async (backtest, symbol, context) => {
|
|
11320
|
+
this.loggerService.log("strategyConnectionService getMaxDrawdownDistancePnlCost", {
|
|
11321
|
+
symbol,
|
|
11322
|
+
context,
|
|
11323
|
+
});
|
|
11324
|
+
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
11325
|
+
const currentPrice = await this.priceMetaService.getCurrentPrice(symbol, context, backtest);
|
|
11326
|
+
return await strategy.getMaxDrawdownDistancePnlCost(symbol, currentPrice);
|
|
11327
|
+
};
|
|
11241
11328
|
/**
|
|
11242
11329
|
* Disposes the ClientStrategy instance for the given context.
|
|
11243
11330
|
*
|
|
@@ -11297,17 +11384,17 @@ class StrategyConnectionService {
|
|
|
11297
11384
|
* @param backtest - Whether running in backtest mode
|
|
11298
11385
|
* @param symbol - Trading pair symbol
|
|
11299
11386
|
* @param ctx - Context with strategyName, exchangeName, frameName
|
|
11300
|
-
* @param
|
|
11387
|
+
* @param payload - Optional commit payload with id and note
|
|
11301
11388
|
* @returns Promise that resolves when scheduled signal is cancelled
|
|
11302
11389
|
*/
|
|
11303
|
-
this.cancelScheduled = async (backtest, symbol, context,
|
|
11390
|
+
this.cancelScheduled = async (backtest, symbol, context, payload = {}) => {
|
|
11304
11391
|
this.loggerService.log("strategyConnectionService cancelScheduled", {
|
|
11305
11392
|
symbol,
|
|
11306
11393
|
context,
|
|
11307
|
-
|
|
11394
|
+
payload,
|
|
11308
11395
|
});
|
|
11309
11396
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
11310
|
-
await strategy.cancelScheduled(symbol, backtest,
|
|
11397
|
+
await strategy.cancelScheduled(symbol, backtest, payload);
|
|
11311
11398
|
};
|
|
11312
11399
|
/**
|
|
11313
11400
|
* Closes the pending signal without stopping the strategy.
|
|
@@ -11322,17 +11409,17 @@ class StrategyConnectionService {
|
|
|
11322
11409
|
* @param backtest - Whether running in backtest mode
|
|
11323
11410
|
* @param symbol - Trading pair symbol
|
|
11324
11411
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
11325
|
-
* @param
|
|
11412
|
+
* @param payload - Optional commit payload with id and note
|
|
11326
11413
|
* @returns Promise that resolves when pending signal is closed
|
|
11327
11414
|
*/
|
|
11328
|
-
this.closePending = async (backtest, symbol, context,
|
|
11415
|
+
this.closePending = async (backtest, symbol, context, payload = {}) => {
|
|
11329
11416
|
this.loggerService.log("strategyConnectionService closePending", {
|
|
11330
11417
|
symbol,
|
|
11331
11418
|
context,
|
|
11332
|
-
|
|
11419
|
+
payload,
|
|
11333
11420
|
});
|
|
11334
11421
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
11335
|
-
await strategy.closePending(symbol, backtest,
|
|
11422
|
+
await strategy.closePending(symbol, backtest, payload);
|
|
11336
11423
|
};
|
|
11337
11424
|
/**
|
|
11338
11425
|
* Checks whether `partialProfit` would succeed without executing it.
|
|
@@ -11611,7 +11698,7 @@ class StrategyConnectionService {
|
|
|
11611
11698
|
* @param backtest - Whether running in backtest mode
|
|
11612
11699
|
* @param symbol - Trading pair symbol
|
|
11613
11700
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
11614
|
-
* @param
|
|
11701
|
+
* @param payload - Optional commit payload with id and note
|
|
11615
11702
|
* @returns Promise that resolves when activation flag is set
|
|
11616
11703
|
*
|
|
11617
11704
|
* @example
|
|
@@ -11621,19 +11708,19 @@ class StrategyConnectionService {
|
|
|
11621
11708
|
* false,
|
|
11622
11709
|
* "BTCUSDT",
|
|
11623
11710
|
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "" },
|
|
11624
|
-
* "manual-activation"
|
|
11711
|
+
* { id: "manual-activation" }
|
|
11625
11712
|
* );
|
|
11626
11713
|
* ```
|
|
11627
11714
|
*/
|
|
11628
|
-
this.activateScheduled = async (backtest, symbol, context,
|
|
11715
|
+
this.activateScheduled = async (backtest, symbol, context, payload = {}) => {
|
|
11629
11716
|
this.loggerService.log("strategyConnectionService activateScheduled", {
|
|
11630
11717
|
symbol,
|
|
11631
11718
|
context,
|
|
11632
11719
|
backtest,
|
|
11633
|
-
|
|
11720
|
+
payload,
|
|
11634
11721
|
});
|
|
11635
11722
|
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
11636
|
-
return await strategy.activateScheduled(symbol, backtest,
|
|
11723
|
+
return await strategy.activateScheduled(symbol, backtest, payload);
|
|
11637
11724
|
};
|
|
11638
11725
|
/**
|
|
11639
11726
|
* Checks whether `averageBuy` would succeed without executing it.
|
|
@@ -14668,18 +14755,18 @@ class StrategyCoreService {
|
|
|
14668
14755
|
* @param backtest - Whether running in backtest mode
|
|
14669
14756
|
* @param symbol - Trading pair symbol
|
|
14670
14757
|
* @param ctx - Context with strategyName, exchangeName, frameName
|
|
14671
|
-
* @param
|
|
14758
|
+
* @param payload - Optional commit payload with id and note
|
|
14672
14759
|
* @returns Promise that resolves when scheduled signal is cancelled
|
|
14673
14760
|
*/
|
|
14674
|
-
this.cancelScheduled = async (backtest, symbol, context,
|
|
14761
|
+
this.cancelScheduled = async (backtest, symbol, context, payload = {}) => {
|
|
14675
14762
|
this.loggerService.log("strategyCoreService cancelScheduled", {
|
|
14676
14763
|
symbol,
|
|
14677
14764
|
context,
|
|
14678
14765
|
backtest,
|
|
14679
|
-
|
|
14766
|
+
payload,
|
|
14680
14767
|
});
|
|
14681
14768
|
await this.validate(context);
|
|
14682
|
-
return await this.strategyConnectionService.cancelScheduled(backtest, symbol, context,
|
|
14769
|
+
return await this.strategyConnectionService.cancelScheduled(backtest, symbol, context, payload);
|
|
14683
14770
|
};
|
|
14684
14771
|
/**
|
|
14685
14772
|
* Closes the pending signal without stopping the strategy.
|
|
@@ -14695,18 +14782,18 @@ class StrategyCoreService {
|
|
|
14695
14782
|
* @param backtest - Whether running in backtest mode
|
|
14696
14783
|
* @param symbol - Trading pair symbol
|
|
14697
14784
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
14698
|
-
* @param
|
|
14785
|
+
* @param payload - Optional commit payload with id and note
|
|
14699
14786
|
* @returns Promise that resolves when pending signal is closed
|
|
14700
14787
|
*/
|
|
14701
|
-
this.closePending = async (backtest, symbol, context,
|
|
14788
|
+
this.closePending = async (backtest, symbol, context, payload = {}) => {
|
|
14702
14789
|
this.loggerService.log("strategyCoreService closePending", {
|
|
14703
14790
|
symbol,
|
|
14704
14791
|
context,
|
|
14705
14792
|
backtest,
|
|
14706
|
-
|
|
14793
|
+
payload,
|
|
14707
14794
|
});
|
|
14708
14795
|
await this.validate(context);
|
|
14709
|
-
return await this.strategyConnectionService.closePending(backtest, symbol, context,
|
|
14796
|
+
return await this.strategyConnectionService.closePending(backtest, symbol, context, payload);
|
|
14710
14797
|
};
|
|
14711
14798
|
/**
|
|
14712
14799
|
* Disposes the ClientStrategy instance for the given context.
|
|
@@ -15051,7 +15138,7 @@ class StrategyCoreService {
|
|
|
15051
15138
|
* @param backtest - Whether running in backtest mode
|
|
15052
15139
|
* @param symbol - Trading pair symbol
|
|
15053
15140
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
15054
|
-
* @param
|
|
15141
|
+
* @param payload - Optional commit payload with id and note
|
|
15055
15142
|
* @returns Promise that resolves when activation flag is set
|
|
15056
15143
|
*
|
|
15057
15144
|
* @example
|
|
@@ -15061,19 +15148,19 @@ class StrategyCoreService {
|
|
|
15061
15148
|
* false,
|
|
15062
15149
|
* "BTCUSDT",
|
|
15063
15150
|
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "" },
|
|
15064
|
-
* "manual-activation"
|
|
15151
|
+
* { id: "manual-activation" }
|
|
15065
15152
|
* );
|
|
15066
15153
|
* ```
|
|
15067
15154
|
*/
|
|
15068
|
-
this.activateScheduled = async (backtest, symbol, context,
|
|
15155
|
+
this.activateScheduled = async (backtest, symbol, context, payload = {}) => {
|
|
15069
15156
|
this.loggerService.log("strategyCoreService activateScheduled", {
|
|
15070
15157
|
symbol,
|
|
15071
15158
|
context,
|
|
15072
15159
|
backtest,
|
|
15073
|
-
|
|
15160
|
+
payload,
|
|
15074
15161
|
});
|
|
15075
15162
|
await this.validate(context);
|
|
15076
|
-
return await this.strategyConnectionService.activateScheduled(backtest, symbol, context,
|
|
15163
|
+
return await this.strategyConnectionService.activateScheduled(backtest, symbol, context, payload);
|
|
15077
15164
|
};
|
|
15078
15165
|
/**
|
|
15079
15166
|
* Checks whether `averageBuy` would succeed without executing it.
|
|
@@ -15475,6 +15562,44 @@ class StrategyCoreService {
|
|
|
15475
15562
|
await this.validate(context);
|
|
15476
15563
|
return await this.strategyConnectionService.getPositionHighestMaxDrawdownPnlCost(backtest, symbol, context);
|
|
15477
15564
|
};
|
|
15565
|
+
/**
|
|
15566
|
+
* Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
|
|
15567
|
+
*
|
|
15568
|
+
* Delegates to StrategyConnectionService.getMaxDrawdownDistancePnlPercentage().
|
|
15569
|
+
* Returns null if no pending signal exists.
|
|
15570
|
+
*
|
|
15571
|
+
* @param backtest - Whether running in backtest mode
|
|
15572
|
+
* @param symbol - Trading pair symbol
|
|
15573
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
15574
|
+
* @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
|
|
15575
|
+
*/
|
|
15576
|
+
this.getMaxDrawdownDistancePnlPercentage = async (backtest, symbol, context) => {
|
|
15577
|
+
this.loggerService.log("strategyCoreService getMaxDrawdownDistancePnlPercentage", {
|
|
15578
|
+
symbol,
|
|
15579
|
+
context,
|
|
15580
|
+
});
|
|
15581
|
+
await this.validate(context);
|
|
15582
|
+
return await this.strategyConnectionService.getMaxDrawdownDistancePnlPercentage(backtest, symbol, context);
|
|
15583
|
+
};
|
|
15584
|
+
/**
|
|
15585
|
+
* Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
|
|
15586
|
+
*
|
|
15587
|
+
* Delegates to StrategyConnectionService.getMaxDrawdownDistancePnlCost().
|
|
15588
|
+
* Returns null if no pending signal exists.
|
|
15589
|
+
*
|
|
15590
|
+
* @param backtest - Whether running in backtest mode
|
|
15591
|
+
* @param symbol - Trading pair symbol
|
|
15592
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
15593
|
+
* @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
|
|
15594
|
+
*/
|
|
15595
|
+
this.getMaxDrawdownDistancePnlCost = async (backtest, symbol, context) => {
|
|
15596
|
+
this.loggerService.log("strategyCoreService getMaxDrawdownDistancePnlCost", {
|
|
15597
|
+
symbol,
|
|
15598
|
+
context,
|
|
15599
|
+
});
|
|
15600
|
+
await this.validate(context);
|
|
15601
|
+
return await this.strategyConnectionService.getMaxDrawdownDistancePnlCost(backtest, symbol, context);
|
|
15602
|
+
};
|
|
15478
15603
|
}
|
|
15479
15604
|
}
|
|
15480
15605
|
|
|
@@ -29074,7 +29199,7 @@ class StrategyReportService {
|
|
|
29074
29199
|
/**
|
|
29075
29200
|
* Logs a cancel-scheduled event when a scheduled signal is cancelled.
|
|
29076
29201
|
*/
|
|
29077
|
-
this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, cancelId) => {
|
|
29202
|
+
this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, cancelId, note) => {
|
|
29078
29203
|
this.loggerService.log("strategyReportService cancelScheduled", {
|
|
29079
29204
|
symbol,
|
|
29080
29205
|
isBacktest,
|
|
@@ -29087,6 +29212,7 @@ class StrategyReportService {
|
|
|
29087
29212
|
await ReportWriter.writeData("strategy", {
|
|
29088
29213
|
action: "cancel-scheduled",
|
|
29089
29214
|
cancelId,
|
|
29215
|
+
note,
|
|
29090
29216
|
symbol,
|
|
29091
29217
|
timestamp,
|
|
29092
29218
|
createdAt,
|
|
@@ -29108,7 +29234,7 @@ class StrategyReportService {
|
|
|
29108
29234
|
/**
|
|
29109
29235
|
* Logs a close-pending event when a pending signal is closed.
|
|
29110
29236
|
*/
|
|
29111
|
-
this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, closeId) => {
|
|
29237
|
+
this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, totalPartials, closeId, note) => {
|
|
29112
29238
|
this.loggerService.log("strategyReportService closePending", {
|
|
29113
29239
|
symbol,
|
|
29114
29240
|
isBacktest,
|
|
@@ -29121,6 +29247,7 @@ class StrategyReportService {
|
|
|
29121
29247
|
await ReportWriter.writeData("strategy", {
|
|
29122
29248
|
action: "close-pending",
|
|
29123
29249
|
closeId,
|
|
29250
|
+
note,
|
|
29124
29251
|
symbol,
|
|
29125
29252
|
timestamp,
|
|
29126
29253
|
createdAt,
|
|
@@ -29370,7 +29497,7 @@ class StrategyReportService {
|
|
|
29370
29497
|
/**
|
|
29371
29498
|
* Logs an activate-scheduled event when a scheduled signal is activated early.
|
|
29372
29499
|
*/
|
|
29373
|
-
this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId) => {
|
|
29500
|
+
this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId, note) => {
|
|
29374
29501
|
this.loggerService.log("strategyReportService activateScheduled", {
|
|
29375
29502
|
symbol,
|
|
29376
29503
|
currentPrice,
|
|
@@ -29384,6 +29511,7 @@ class StrategyReportService {
|
|
|
29384
29511
|
await ReportWriter.writeData("strategy", {
|
|
29385
29512
|
action: "activate-scheduled",
|
|
29386
29513
|
activateId,
|
|
29514
|
+
note,
|
|
29387
29515
|
currentPrice,
|
|
29388
29516
|
symbol,
|
|
29389
29517
|
timestamp,
|
|
@@ -29477,14 +29605,14 @@ class StrategyReportService {
|
|
|
29477
29605
|
exchangeName: event.exchangeName,
|
|
29478
29606
|
frameName: event.frameName,
|
|
29479
29607
|
strategyName: event.strategyName,
|
|
29480
|
-
}, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.cancelId));
|
|
29608
|
+
}, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.cancelId, event.note));
|
|
29481
29609
|
const unClosePending = strategyCommitSubject
|
|
29482
29610
|
.filter(({ action }) => action === "close-pending")
|
|
29483
29611
|
.connect(async (event) => await this.closePending(event.symbol, event.backtest, {
|
|
29484
29612
|
exchangeName: event.exchangeName,
|
|
29485
29613
|
frameName: event.frameName,
|
|
29486
29614
|
strategyName: event.strategyName,
|
|
29487
|
-
}, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.closeId));
|
|
29615
|
+
}, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.closeId, event.note));
|
|
29488
29616
|
const unPartialProfit = strategyCommitSubject
|
|
29489
29617
|
.filter(({ action }) => action === "partial-profit")
|
|
29490
29618
|
.connect(async (event) => await this.partialProfit(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
|
|
@@ -29526,7 +29654,7 @@ class StrategyReportService {
|
|
|
29526
29654
|
exchangeName: event.exchangeName,
|
|
29527
29655
|
frameName: event.frameName,
|
|
29528
29656
|
strategyName: event.strategyName,
|
|
29529
|
-
}, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId));
|
|
29657
|
+
}, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId, event.note));
|
|
29530
29658
|
const unAverageBuy = strategyCommitSubject
|
|
29531
29659
|
.filter(({ action }) => action === "average-buy")
|
|
29532
29660
|
.connect(async (event) => await this.averageBuy(event.symbol, event.currentPrice, event.effectivePriceOpen, event.totalEntries, event.backtest, {
|
|
@@ -30065,8 +30193,9 @@ class StrategyMarkdownService {
|
|
|
30065
30193
|
* @param context - Strategy context with strategyName, exchangeName, frameName
|
|
30066
30194
|
* @param timestamp - Timestamp from StrategyCommitContract (execution context time)
|
|
30067
30195
|
* @param cancelId - Optional identifier for the cancellation reason
|
|
30196
|
+
* @param note - Optional note from commit payload
|
|
30068
30197
|
*/
|
|
30069
|
-
this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, cancelId) => {
|
|
30198
|
+
this.cancelScheduled = async (symbol, isBacktest, context, timestamp, signalId, pnl, cancelId, note) => {
|
|
30070
30199
|
this.loggerService.log("strategyMarkdownService cancelScheduled", {
|
|
30071
30200
|
symbol,
|
|
30072
30201
|
isBacktest,
|
|
@@ -30087,6 +30216,7 @@ class StrategyMarkdownService {
|
|
|
30087
30216
|
action: "cancel-scheduled",
|
|
30088
30217
|
pnl,
|
|
30089
30218
|
cancelId,
|
|
30219
|
+
note,
|
|
30090
30220
|
createdAt,
|
|
30091
30221
|
backtest: isBacktest,
|
|
30092
30222
|
});
|
|
@@ -30099,8 +30229,9 @@ class StrategyMarkdownService {
|
|
|
30099
30229
|
* @param context - Strategy context with strategyName, exchangeName, frameName
|
|
30100
30230
|
* @param timestamp - Timestamp from StrategyCommitContract (execution context time)
|
|
30101
30231
|
* @param closeId - Optional identifier for the close reason
|
|
30232
|
+
* @param note - Optional note from commit payload
|
|
30102
30233
|
*/
|
|
30103
|
-
this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, closeId) => {
|
|
30234
|
+
this.closePending = async (symbol, isBacktest, context, timestamp, signalId, pnl, closeId, note) => {
|
|
30104
30235
|
this.loggerService.log("strategyMarkdownService closePending", {
|
|
30105
30236
|
symbol,
|
|
30106
30237
|
isBacktest,
|
|
@@ -30121,6 +30252,7 @@ class StrategyMarkdownService {
|
|
|
30121
30252
|
action: "close-pending",
|
|
30122
30253
|
pnl,
|
|
30123
30254
|
closeId,
|
|
30255
|
+
note,
|
|
30124
30256
|
createdAt,
|
|
30125
30257
|
backtest: isBacktest,
|
|
30126
30258
|
});
|
|
@@ -30419,8 +30551,9 @@ class StrategyMarkdownService {
|
|
|
30419
30551
|
* @param scheduledAt - Signal creation timestamp in milliseconds
|
|
30420
30552
|
* @param pendingAt - Pending timestamp in milliseconds
|
|
30421
30553
|
* @param activateId - Optional identifier for the activation reason
|
|
30554
|
+
* @param note - Optional note from commit payload
|
|
30422
30555
|
*/
|
|
30423
|
-
this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId) => {
|
|
30556
|
+
this.activateScheduled = async (symbol, currentPrice, isBacktest, context, timestamp, signalId, pnl, totalPartials, position, priceOpen, priceTakeProfit, priceStopLoss, originalPriceTakeProfit, originalPriceStopLoss, scheduledAt, pendingAt, totalEntries, originalPriceOpen, activateId, note) => {
|
|
30424
30557
|
this.loggerService.log("strategyMarkdownService activateScheduled", {
|
|
30425
30558
|
symbol,
|
|
30426
30559
|
currentPrice,
|
|
@@ -30443,6 +30576,7 @@ class StrategyMarkdownService {
|
|
|
30443
30576
|
pnl,
|
|
30444
30577
|
totalPartials,
|
|
30445
30578
|
activateId,
|
|
30579
|
+
note,
|
|
30446
30580
|
currentPrice,
|
|
30447
30581
|
createdAt,
|
|
30448
30582
|
backtest: isBacktest,
|
|
@@ -30647,14 +30781,14 @@ class StrategyMarkdownService {
|
|
|
30647
30781
|
exchangeName: event.exchangeName,
|
|
30648
30782
|
frameName: event.frameName,
|
|
30649
30783
|
strategyName: event.strategyName,
|
|
30650
|
-
}, event.timestamp, event.signalId, event.pnl, event.cancelId));
|
|
30784
|
+
}, event.timestamp, event.signalId, event.pnl, event.cancelId, event.note));
|
|
30651
30785
|
const unClosePending = strategyCommitSubject
|
|
30652
30786
|
.filter(({ action }) => action === "close-pending")
|
|
30653
30787
|
.connect(async (event) => await this.closePending(event.symbol, event.backtest, {
|
|
30654
30788
|
exchangeName: event.exchangeName,
|
|
30655
30789
|
frameName: event.frameName,
|
|
30656
30790
|
strategyName: event.strategyName,
|
|
30657
|
-
}, event.timestamp, event.signalId, event.pnl, event.closeId));
|
|
30791
|
+
}, event.timestamp, event.signalId, event.pnl, event.closeId, event.note));
|
|
30658
30792
|
const unPartialProfit = strategyCommitSubject
|
|
30659
30793
|
.filter(({ action }) => action === "partial-profit")
|
|
30660
30794
|
.connect(async (event) => await this.partialProfit(event.symbol, event.percentToClose, event.currentPrice, event.backtest, {
|
|
@@ -30696,7 +30830,7 @@ class StrategyMarkdownService {
|
|
|
30696
30830
|
exchangeName: event.exchangeName,
|
|
30697
30831
|
frameName: event.frameName,
|
|
30698
30832
|
strategyName: event.strategyName,
|
|
30699
|
-
}, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId));
|
|
30833
|
+
}, event.timestamp, event.signalId, event.pnl, event.totalPartials, event.position, event.priceOpen, event.priceTakeProfit, event.priceStopLoss, event.originalPriceTakeProfit, event.originalPriceStopLoss, event.scheduledAt, event.pendingAt, event.totalEntries, event.originalPriceOpen, event.activateId, event.note));
|
|
30700
30834
|
const unAverageBuy = strategyCommitSubject
|
|
30701
30835
|
.filter(({ action }) => action === "average-buy")
|
|
30702
30836
|
.connect(async (event) => await this.averageBuy(event.symbol, event.currentPrice, event.effectivePriceOpen, event.totalEntries, event.backtest, {
|
|
@@ -35181,6 +35315,8 @@ const GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE_METHOD_NAME = "strateg
|
|
|
35181
35315
|
const GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST_METHOD_NAME = "strategy.getPositionHighestProfitDistancePnlCost";
|
|
35182
35316
|
const GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE_METHOD_NAME = "strategy.getPositionHighestMaxDrawdownPnlPercentage";
|
|
35183
35317
|
const GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST_METHOD_NAME = "strategy.getPositionHighestMaxDrawdownPnlCost";
|
|
35318
|
+
const GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE_METHOD_NAME = "strategy.getMaxDrawdownDistancePnlPercentage";
|
|
35319
|
+
const GET_MAX_DRAWDOWN_DISTANCE_PNL_COST_METHOD_NAME = "strategy.getMaxDrawdownDistancePnlCost";
|
|
35184
35320
|
const GET_POSITION_ENTRY_OVERLAP_METHOD_NAME = "strategy.getPositionEntryOverlap";
|
|
35185
35321
|
const GET_POSITION_PARTIAL_OVERLAP_METHOD_NAME = "strategy.getPositionPartialOverlap";
|
|
35186
35322
|
const HAS_NO_PENDING_SIGNAL_METHOD_NAME = "strategy.hasNoPendingSignal";
|
|
@@ -35196,7 +35332,7 @@ const HAS_NO_SCHEDULED_SIGNAL_METHOD_NAME = "strategy.hasNoScheduledSignal";
|
|
|
35196
35332
|
*
|
|
35197
35333
|
* @param symbol - Trading pair symbol
|
|
35198
35334
|
* @param strategyName - Strategy name
|
|
35199
|
-
* @param
|
|
35335
|
+
* @param payload - Optional commit payload with id and note
|
|
35200
35336
|
* @returns Promise that resolves when scheduled signal is cancelled
|
|
35201
35337
|
*
|
|
35202
35338
|
* @example
|
|
@@ -35204,13 +35340,13 @@ const HAS_NO_SCHEDULED_SIGNAL_METHOD_NAME = "strategy.hasNoScheduledSignal";
|
|
|
35204
35340
|
* import { commitCancelScheduled } from "backtest-kit";
|
|
35205
35341
|
*
|
|
35206
35342
|
* // Cancel scheduled signal with custom ID
|
|
35207
|
-
* await commitCancelScheduled("BTCUSDT", "manual-cancel-001");
|
|
35343
|
+
* await commitCancelScheduled("BTCUSDT", { id: "manual-cancel-001" });
|
|
35208
35344
|
* ```
|
|
35209
35345
|
*/
|
|
35210
|
-
async function commitCancelScheduled(symbol,
|
|
35346
|
+
async function commitCancelScheduled(symbol, payload = {}) {
|
|
35211
35347
|
backtest.loggerService.info(CANCEL_SCHEDULED_METHOD_NAME, {
|
|
35212
35348
|
symbol,
|
|
35213
|
-
|
|
35349
|
+
payload,
|
|
35214
35350
|
});
|
|
35215
35351
|
if (!ExecutionContextService.hasContext()) {
|
|
35216
35352
|
throw new Error("commitCancelScheduled requires an execution context");
|
|
@@ -35220,7 +35356,7 @@ async function commitCancelScheduled(symbol, cancelId) {
|
|
|
35220
35356
|
}
|
|
35221
35357
|
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
35222
35358
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
35223
|
-
await backtest.strategyCoreService.cancelScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName },
|
|
35359
|
+
await backtest.strategyCoreService.cancelScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
|
|
35224
35360
|
}
|
|
35225
35361
|
/**
|
|
35226
35362
|
* Closes the pending signal without stopping the strategy.
|
|
@@ -35232,7 +35368,7 @@ async function commitCancelScheduled(symbol, cancelId) {
|
|
|
35232
35368
|
* Automatically detects backtest/live mode from execution context.
|
|
35233
35369
|
*
|
|
35234
35370
|
* @param symbol - Trading pair symbol
|
|
35235
|
-
* @param
|
|
35371
|
+
* @param payload - Optional commit payload with id and note
|
|
35236
35372
|
* @returns Promise that resolves when pending signal is closed
|
|
35237
35373
|
*
|
|
35238
35374
|
* @example
|
|
@@ -35240,13 +35376,13 @@ async function commitCancelScheduled(symbol, cancelId) {
|
|
|
35240
35376
|
* import { commitClosePending } from "backtest-kit";
|
|
35241
35377
|
*
|
|
35242
35378
|
* // Close pending signal with custom ID
|
|
35243
|
-
* await commitClosePending("BTCUSDT", "manual-close-001");
|
|
35379
|
+
* await commitClosePending("BTCUSDT", { id: "manual-close-001" });
|
|
35244
35380
|
* ```
|
|
35245
35381
|
*/
|
|
35246
|
-
async function commitClosePending(symbol,
|
|
35382
|
+
async function commitClosePending(symbol, payload = {}) {
|
|
35247
35383
|
backtest.loggerService.info(CLOSE_PENDING_METHOD_NAME, {
|
|
35248
35384
|
symbol,
|
|
35249
|
-
|
|
35385
|
+
payload,
|
|
35250
35386
|
});
|
|
35251
35387
|
if (!ExecutionContextService.hasContext()) {
|
|
35252
35388
|
throw new Error("commitClosePending requires an execution context");
|
|
@@ -35256,7 +35392,7 @@ async function commitClosePending(symbol, closeId) {
|
|
|
35256
35392
|
}
|
|
35257
35393
|
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
35258
35394
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
35259
|
-
await backtest.strategyCoreService.closePending(isBacktest, symbol, { exchangeName, frameName, strategyName },
|
|
35395
|
+
await backtest.strategyCoreService.closePending(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
|
|
35260
35396
|
}
|
|
35261
35397
|
/**
|
|
35262
35398
|
* Executes partial close at profit level (moving toward TP).
|
|
@@ -35719,7 +35855,7 @@ async function commitBreakeven(symbol) {
|
|
|
35719
35855
|
* Automatically detects backtest/live mode from execution context.
|
|
35720
35856
|
*
|
|
35721
35857
|
* @param symbol - Trading pair symbol
|
|
35722
|
-
* @param
|
|
35858
|
+
* @param payload - Optional commit payload with id and note
|
|
35723
35859
|
* @returns Promise that resolves when activation flag is set
|
|
35724
35860
|
*
|
|
35725
35861
|
* @example
|
|
@@ -35727,13 +35863,13 @@ async function commitBreakeven(symbol) {
|
|
|
35727
35863
|
* import { commitActivateScheduled } from "backtest-kit";
|
|
35728
35864
|
*
|
|
35729
35865
|
* // Activate scheduled signal early with custom ID
|
|
35730
|
-
* await commitActivateScheduled("BTCUSDT", "manual-activate-001");
|
|
35866
|
+
* await commitActivateScheduled("BTCUSDT", { id: "manual-activate-001" });
|
|
35731
35867
|
* ```
|
|
35732
35868
|
*/
|
|
35733
|
-
async function commitActivateScheduled(symbol,
|
|
35869
|
+
async function commitActivateScheduled(symbol, payload = {}) {
|
|
35734
35870
|
backtest.loggerService.info(ACTIVATE_SCHEDULED_METHOD_NAME, {
|
|
35735
35871
|
symbol,
|
|
35736
|
-
|
|
35872
|
+
payload,
|
|
35737
35873
|
});
|
|
35738
35874
|
if (!ExecutionContextService.hasContext()) {
|
|
35739
35875
|
throw new Error("commitActivateScheduled requires an execution context");
|
|
@@ -35743,7 +35879,7 @@ async function commitActivateScheduled(symbol, activateId) {
|
|
|
35743
35879
|
}
|
|
35744
35880
|
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
35745
35881
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
35746
|
-
await backtest.strategyCoreService.activateScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName },
|
|
35882
|
+
await backtest.strategyCoreService.activateScheduled(isBacktest, symbol, { exchangeName, frameName, strategyName }, payload);
|
|
35747
35883
|
}
|
|
35748
35884
|
/**
|
|
35749
35885
|
* Adds a new DCA entry to the active pending signal.
|
|
@@ -36931,6 +37067,64 @@ async function getPositionHighestMaxDrawdownPnlCost(symbol) {
|
|
|
36931
37067
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
36932
37068
|
return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
|
|
36933
37069
|
}
|
|
37070
|
+
/**
|
|
37071
|
+
* Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
|
|
37072
|
+
*
|
|
37073
|
+
* Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
|
|
37074
|
+
* Returns null if no pending signal exists.
|
|
37075
|
+
*
|
|
37076
|
+
* @param symbol - Trading pair symbol
|
|
37077
|
+
* @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
|
|
37078
|
+
*
|
|
37079
|
+
* @example
|
|
37080
|
+
* ```typescript
|
|
37081
|
+
* import { getMaxDrawdownDistancePnlPercentage } from "backtest-kit";
|
|
37082
|
+
*
|
|
37083
|
+
* const dist = await getMaxDrawdownDistancePnlPercentage("BTCUSDT");
|
|
37084
|
+
* // e.g. 3.5 (peak was +3.5% above trough)
|
|
37085
|
+
* ```
|
|
37086
|
+
*/
|
|
37087
|
+
async function getMaxDrawdownDistancePnlPercentage(symbol) {
|
|
37088
|
+
backtest.loggerService.info(GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE_METHOD_NAME, { symbol });
|
|
37089
|
+
if (!ExecutionContextService.hasContext()) {
|
|
37090
|
+
throw new Error("getMaxDrawdownDistancePnlPercentage requires an execution context");
|
|
37091
|
+
}
|
|
37092
|
+
if (!MethodContextService.hasContext()) {
|
|
37093
|
+
throw new Error("getMaxDrawdownDistancePnlPercentage requires a method context");
|
|
37094
|
+
}
|
|
37095
|
+
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
37096
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
37097
|
+
return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(isBacktest, symbol, { exchangeName, frameName, strategyName });
|
|
37098
|
+
}
|
|
37099
|
+
/**
|
|
37100
|
+
* Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
|
|
37101
|
+
*
|
|
37102
|
+
* Computed as: max(0, peakPnlCost - fallPnlCost).
|
|
37103
|
+
* Returns null if no pending signal exists.
|
|
37104
|
+
*
|
|
37105
|
+
* @param symbol - Trading pair symbol
|
|
37106
|
+
* @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
|
|
37107
|
+
*
|
|
37108
|
+
* @example
|
|
37109
|
+
* ```typescript
|
|
37110
|
+
* import { getMaxDrawdownDistancePnlCost } from "backtest-kit";
|
|
37111
|
+
*
|
|
37112
|
+
* const dist = await getMaxDrawdownDistancePnlCost("BTCUSDT");
|
|
37113
|
+
* // e.g. 7.2 (peak was $7.2 above trough)
|
|
37114
|
+
* ```
|
|
37115
|
+
*/
|
|
37116
|
+
async function getMaxDrawdownDistancePnlCost(symbol) {
|
|
37117
|
+
backtest.loggerService.info(GET_MAX_DRAWDOWN_DISTANCE_PNL_COST_METHOD_NAME, { symbol });
|
|
37118
|
+
if (!ExecutionContextService.hasContext()) {
|
|
37119
|
+
throw new Error("getMaxDrawdownDistancePnlCost requires an execution context");
|
|
37120
|
+
}
|
|
37121
|
+
if (!MethodContextService.hasContext()) {
|
|
37122
|
+
throw new Error("getMaxDrawdownDistancePnlCost requires a method context");
|
|
37123
|
+
}
|
|
37124
|
+
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
37125
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
37126
|
+
return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(isBacktest, symbol, { exchangeName, frameName, strategyName });
|
|
37127
|
+
}
|
|
36934
37128
|
/**
|
|
36935
37129
|
* Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
|
|
36936
37130
|
* Use this to prevent duplicate DCA entries at the same price area.
|
|
@@ -38604,6 +38798,8 @@ const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE =
|
|
|
38604
38798
|
const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "BacktestUtils.getPositionHighestProfitDistancePnlCost";
|
|
38605
38799
|
const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "BacktestUtils.getPositionHighestMaxDrawdownPnlPercentage";
|
|
38606
38800
|
const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "BacktestUtils.getPositionHighestMaxDrawdownPnlCost";
|
|
38801
|
+
const BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE = "BacktestUtils.getMaxDrawdownDistancePnlPercentage";
|
|
38802
|
+
const BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST = "BacktestUtils.getMaxDrawdownDistancePnlCost";
|
|
38607
38803
|
const BACKTEST_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "BacktestUtils.getPositionEntryOverlap";
|
|
38608
38804
|
const BACKTEST_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "BacktestUtils.getPositionPartialOverlap";
|
|
38609
38805
|
const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
|
|
@@ -39985,6 +40181,62 @@ class BacktestUtils {
|
|
|
39985
40181
|
}
|
|
39986
40182
|
return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(true, symbol, context);
|
|
39987
40183
|
};
|
|
40184
|
+
/**
|
|
40185
|
+
* Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
|
|
40186
|
+
*
|
|
40187
|
+
* Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
|
|
40188
|
+
* Returns null if no pending signal exists.
|
|
40189
|
+
*
|
|
40190
|
+
* @param symbol - Trading pair symbol
|
|
40191
|
+
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
40192
|
+
* @returns peak-to-trough PnL percentage distance (≥ 0) or null if no active position
|
|
40193
|
+
*/
|
|
40194
|
+
this.getMaxDrawdownDistancePnlPercentage = async (symbol, context) => {
|
|
40195
|
+
backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE, {
|
|
40196
|
+
symbol,
|
|
40197
|
+
context,
|
|
40198
|
+
});
|
|
40199
|
+
backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
|
|
40200
|
+
backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
|
|
40201
|
+
{
|
|
40202
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
40203
|
+
riskName &&
|
|
40204
|
+
backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
|
|
40205
|
+
riskList &&
|
|
40206
|
+
riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
|
|
40207
|
+
actions &&
|
|
40208
|
+
actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
|
|
40209
|
+
}
|
|
40210
|
+
return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(true, symbol, context);
|
|
40211
|
+
};
|
|
40212
|
+
/**
|
|
40213
|
+
* Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
|
|
40214
|
+
*
|
|
40215
|
+
* Computed as: max(0, peakPnlCost - fallPnlCost).
|
|
40216
|
+
* Returns null if no pending signal exists.
|
|
40217
|
+
*
|
|
40218
|
+
* @param symbol - Trading pair symbol
|
|
40219
|
+
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
40220
|
+
* @returns peak-to-trough PnL cost distance (≥ 0) or null if no active position
|
|
40221
|
+
*/
|
|
40222
|
+
this.getMaxDrawdownDistancePnlCost = async (symbol, context) => {
|
|
40223
|
+
backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST, {
|
|
40224
|
+
symbol,
|
|
40225
|
+
context,
|
|
40226
|
+
});
|
|
40227
|
+
backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
|
|
40228
|
+
backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
|
|
40229
|
+
{
|
|
40230
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
40231
|
+
riskName &&
|
|
40232
|
+
backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
|
|
40233
|
+
riskList &&
|
|
40234
|
+
riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
|
|
40235
|
+
actions &&
|
|
40236
|
+
actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
|
|
40237
|
+
}
|
|
40238
|
+
return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(true, symbol, context);
|
|
40239
|
+
};
|
|
39988
40240
|
/**
|
|
39989
40241
|
* Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
|
|
39990
40242
|
* Use this to prevent duplicate DCA entries at the same price area.
|
|
@@ -40119,7 +40371,7 @@ class BacktestUtils {
|
|
|
40119
40371
|
* @param symbol - Trading pair symbol
|
|
40120
40372
|
* @param strategyName - Strategy name
|
|
40121
40373
|
* @param context - Execution context with exchangeName and frameName
|
|
40122
|
-
* @param
|
|
40374
|
+
* @param payload - Optional commit payload with id and note
|
|
40123
40375
|
* @returns Promise that resolves when scheduled signal is cancelled
|
|
40124
40376
|
*
|
|
40125
40377
|
* @example
|
|
@@ -40129,14 +40381,14 @@ class BacktestUtils {
|
|
|
40129
40381
|
* exchangeName: "binance",
|
|
40130
40382
|
* frameName: "frame1",
|
|
40131
40383
|
* strategyName: "my-strategy"
|
|
40132
|
-
* }, "manual-cancel-001");
|
|
40384
|
+
* }, { id: "manual-cancel-001" });
|
|
40133
40385
|
* ```
|
|
40134
40386
|
*/
|
|
40135
|
-
this.commitCancelScheduled = async (symbol, context,
|
|
40387
|
+
this.commitCancelScheduled = async (symbol, context, payload = {}) => {
|
|
40136
40388
|
backtest.loggerService.info(BACKTEST_METHOD_NAME_CANCEL_SCHEDULED, {
|
|
40137
40389
|
symbol,
|
|
40138
40390
|
context,
|
|
40139
|
-
|
|
40391
|
+
payload,
|
|
40140
40392
|
});
|
|
40141
40393
|
backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_CANCEL_SCHEDULED);
|
|
40142
40394
|
backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_CANCEL_SCHEDULED);
|
|
@@ -40149,7 +40401,7 @@ class BacktestUtils {
|
|
|
40149
40401
|
actions &&
|
|
40150
40402
|
actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_CANCEL_SCHEDULED));
|
|
40151
40403
|
}
|
|
40152
|
-
await backtest.strategyCoreService.cancelScheduled(true, symbol, context,
|
|
40404
|
+
await backtest.strategyCoreService.cancelScheduled(true, symbol, context, payload);
|
|
40153
40405
|
};
|
|
40154
40406
|
/**
|
|
40155
40407
|
* Closes the pending signal without stopping the strategy.
|
|
@@ -40160,7 +40412,7 @@ class BacktestUtils {
|
|
|
40160
40412
|
*
|
|
40161
40413
|
* @param symbol - Trading pair symbol
|
|
40162
40414
|
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
40163
|
-
* @param
|
|
40415
|
+
* @param payload - Optional commit payload with id and note
|
|
40164
40416
|
* @returns Promise that resolves when pending signal is closed
|
|
40165
40417
|
*
|
|
40166
40418
|
* @example
|
|
@@ -40170,14 +40422,14 @@ class BacktestUtils {
|
|
|
40170
40422
|
* exchangeName: "binance",
|
|
40171
40423
|
* strategyName: "my-strategy",
|
|
40172
40424
|
* frameName: "1m"
|
|
40173
|
-
* }, "manual-close-001");
|
|
40425
|
+
* }, { id: "manual-close-001" });
|
|
40174
40426
|
* ```
|
|
40175
40427
|
*/
|
|
40176
|
-
this.commitClosePending = async (symbol, context,
|
|
40428
|
+
this.commitClosePending = async (symbol, context, payload = {}) => {
|
|
40177
40429
|
backtest.loggerService.info(BACKTEST_METHOD_NAME_CLOSE_PENDING, {
|
|
40178
40430
|
symbol,
|
|
40179
40431
|
context,
|
|
40180
|
-
|
|
40432
|
+
payload,
|
|
40181
40433
|
});
|
|
40182
40434
|
backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_CLOSE_PENDING);
|
|
40183
40435
|
backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_CLOSE_PENDING);
|
|
@@ -40190,7 +40442,7 @@ class BacktestUtils {
|
|
|
40190
40442
|
actions &&
|
|
40191
40443
|
actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_CLOSE_PENDING));
|
|
40192
40444
|
}
|
|
40193
|
-
await backtest.strategyCoreService.closePending(true, symbol, context,
|
|
40445
|
+
await backtest.strategyCoreService.closePending(true, symbol, context, payload);
|
|
40194
40446
|
};
|
|
40195
40447
|
/**
|
|
40196
40448
|
* Executes partial close at profit level (moving toward TP).
|
|
@@ -40826,7 +41078,7 @@ class BacktestUtils {
|
|
|
40826
41078
|
*
|
|
40827
41079
|
* @param symbol - Trading pair symbol
|
|
40828
41080
|
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
40829
|
-
* @param
|
|
41081
|
+
* @param payload - Optional commit payload with id and note
|
|
40830
41082
|
* @returns Promise that resolves when activation flag is set
|
|
40831
41083
|
*
|
|
40832
41084
|
* @example
|
|
@@ -40836,14 +41088,14 @@ class BacktestUtils {
|
|
|
40836
41088
|
* strategyName: "my-strategy",
|
|
40837
41089
|
* exchangeName: "binance",
|
|
40838
41090
|
* frameName: "1h"
|
|
40839
|
-
* }, "manual-activate-001");
|
|
41091
|
+
* }, { id: "manual-activate-001" });
|
|
40840
41092
|
* ```
|
|
40841
41093
|
*/
|
|
40842
|
-
this.commitActivateScheduled = async (symbol, context,
|
|
41094
|
+
this.commitActivateScheduled = async (symbol, context, payload = {}) => {
|
|
40843
41095
|
backtest.loggerService.info(BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED, {
|
|
40844
41096
|
symbol,
|
|
40845
41097
|
context,
|
|
40846
|
-
|
|
41098
|
+
payload,
|
|
40847
41099
|
});
|
|
40848
41100
|
backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED);
|
|
40849
41101
|
backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED);
|
|
@@ -40856,7 +41108,7 @@ class BacktestUtils {
|
|
|
40856
41108
|
actions &&
|
|
40857
41109
|
actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED));
|
|
40858
41110
|
}
|
|
40859
|
-
await backtest.strategyCoreService.activateScheduled(true, symbol, context,
|
|
41111
|
+
await backtest.strategyCoreService.activateScheduled(true, symbol, context, payload);
|
|
40860
41112
|
};
|
|
40861
41113
|
/**
|
|
40862
41114
|
* Adds a new DCA entry to the active pending signal.
|
|
@@ -41114,6 +41366,8 @@ const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE = "Li
|
|
|
41114
41366
|
const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "LiveUtils.getPositionHighestProfitDistancePnlCost";
|
|
41115
41367
|
const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "LiveUtils.getPositionHighestMaxDrawdownPnlPercentage";
|
|
41116
41368
|
const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "LiveUtils.getPositionHighestMaxDrawdownPnlCost";
|
|
41369
|
+
const LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE = "LiveUtils.getMaxDrawdownDistancePnlPercentage";
|
|
41370
|
+
const LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST = "LiveUtils.getMaxDrawdownDistancePnlCost";
|
|
41117
41371
|
const LIVE_METHOD_NAME_GET_POSITION_ENTRY_OVERLAP = "LiveUtils.getPositionEntryOverlap";
|
|
41118
41372
|
const LIVE_METHOD_NAME_GET_POSITION_PARTIAL_OVERLAP = "LiveUtils.getPositionPartialOverlap";
|
|
41119
41373
|
const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
|
|
@@ -42638,6 +42892,70 @@ class LiveUtils {
|
|
|
42638
42892
|
frameName: "",
|
|
42639
42893
|
});
|
|
42640
42894
|
};
|
|
42895
|
+
/**
|
|
42896
|
+
* Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
|
|
42897
|
+
*
|
|
42898
|
+
* Computed as: max(0, peakPnlPercentage - fallPnlPercentage).
|
|
42899
|
+
* Returns null if no pending signal exists.
|
|
42900
|
+
*
|
|
42901
|
+
* @param symbol - Trading pair symbol
|
|
42902
|
+
* @param context - Execution context with strategyName and exchangeName
|
|
42903
|
+
* @returns peak-to-trough PnL percentage distance (≥ 0) or null if no active position
|
|
42904
|
+
*/
|
|
42905
|
+
this.getMaxDrawdownDistancePnlPercentage = async (symbol, context) => {
|
|
42906
|
+
backtest.loggerService.info(LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE, {
|
|
42907
|
+
symbol,
|
|
42908
|
+
context,
|
|
42909
|
+
});
|
|
42910
|
+
backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
|
|
42911
|
+
backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
|
|
42912
|
+
{
|
|
42913
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
42914
|
+
riskName &&
|
|
42915
|
+
backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
|
|
42916
|
+
riskList &&
|
|
42917
|
+
riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
|
|
42918
|
+
actions &&
|
|
42919
|
+
actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
|
|
42920
|
+
}
|
|
42921
|
+
return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(false, symbol, {
|
|
42922
|
+
strategyName: context.strategyName,
|
|
42923
|
+
exchangeName: context.exchangeName,
|
|
42924
|
+
frameName: "",
|
|
42925
|
+
});
|
|
42926
|
+
};
|
|
42927
|
+
/**
|
|
42928
|
+
* Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
|
|
42929
|
+
*
|
|
42930
|
+
* Computed as: max(0, peakPnlCost - fallPnlCost).
|
|
42931
|
+
* Returns null if no pending signal exists.
|
|
42932
|
+
*
|
|
42933
|
+
* @param symbol - Trading pair symbol
|
|
42934
|
+
* @param context - Execution context with strategyName and exchangeName
|
|
42935
|
+
* @returns peak-to-trough PnL cost distance (≥ 0) or null if no active position
|
|
42936
|
+
*/
|
|
42937
|
+
this.getMaxDrawdownDistancePnlCost = async (symbol, context) => {
|
|
42938
|
+
backtest.loggerService.info(LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST, {
|
|
42939
|
+
symbol,
|
|
42940
|
+
context,
|
|
42941
|
+
});
|
|
42942
|
+
backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
|
|
42943
|
+
backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
|
|
42944
|
+
{
|
|
42945
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
42946
|
+
riskName &&
|
|
42947
|
+
backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
|
|
42948
|
+
riskList &&
|
|
42949
|
+
riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
|
|
42950
|
+
actions &&
|
|
42951
|
+
actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
|
|
42952
|
+
}
|
|
42953
|
+
return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(false, symbol, {
|
|
42954
|
+
strategyName: context.strategyName,
|
|
42955
|
+
exchangeName: context.exchangeName,
|
|
42956
|
+
frameName: "",
|
|
42957
|
+
});
|
|
42958
|
+
};
|
|
42641
42959
|
/**
|
|
42642
42960
|
* Checks whether the current price falls within the tolerance zone of any existing DCA entry level.
|
|
42643
42961
|
* Use this to prevent duplicate DCA entries at the same price area.
|
|
@@ -42779,7 +43097,7 @@ class LiveUtils {
|
|
|
42779
43097
|
* @param symbol - Trading pair symbol
|
|
42780
43098
|
* @param strategyName - Strategy name
|
|
42781
43099
|
* @param context - Execution context with exchangeName and frameName
|
|
42782
|
-
* @param
|
|
43100
|
+
* @param payload - Optional commit payload with id and note
|
|
42783
43101
|
* @returns Promise that resolves when scheduled signal is cancelled
|
|
42784
43102
|
*
|
|
42785
43103
|
* @example
|
|
@@ -42789,14 +43107,14 @@ class LiveUtils {
|
|
|
42789
43107
|
* exchangeName: "binance",
|
|
42790
43108
|
* frameName: "",
|
|
42791
43109
|
* strategyName: "my-strategy"
|
|
42792
|
-
* }, "manual-cancel-001");
|
|
43110
|
+
* }, { id: "manual-cancel-001" });
|
|
42793
43111
|
* ```
|
|
42794
43112
|
*/
|
|
42795
|
-
this.commitCancelScheduled = async (symbol, context,
|
|
43113
|
+
this.commitCancelScheduled = async (symbol, context, payload = {}) => {
|
|
42796
43114
|
backtest.loggerService.info(LIVE_METHOD_NAME_CANCEL_SCHEDULED, {
|
|
42797
43115
|
symbol,
|
|
42798
43116
|
context,
|
|
42799
|
-
|
|
43117
|
+
payload,
|
|
42800
43118
|
});
|
|
42801
43119
|
backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_CANCEL_SCHEDULED);
|
|
42802
43120
|
backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_CANCEL_SCHEDULED);
|
|
@@ -42813,7 +43131,7 @@ class LiveUtils {
|
|
|
42813
43131
|
strategyName: context.strategyName,
|
|
42814
43132
|
exchangeName: context.exchangeName,
|
|
42815
43133
|
frameName: "",
|
|
42816
|
-
},
|
|
43134
|
+
}, payload);
|
|
42817
43135
|
};
|
|
42818
43136
|
/**
|
|
42819
43137
|
* Closes the pending signal without stopping the strategy.
|
|
@@ -42824,7 +43142,7 @@ class LiveUtils {
|
|
|
42824
43142
|
*
|
|
42825
43143
|
* @param symbol - Trading pair symbol
|
|
42826
43144
|
* @param context - Execution context with strategyName and exchangeName
|
|
42827
|
-
* @param
|
|
43145
|
+
* @param payload - Optional commit payload with id and note
|
|
42828
43146
|
* @returns Promise that resolves when pending signal is closed
|
|
42829
43147
|
*
|
|
42830
43148
|
* @example
|
|
@@ -42833,14 +43151,14 @@ class LiveUtils {
|
|
|
42833
43151
|
* await Live.commitClose("BTCUSDT", {
|
|
42834
43152
|
* exchangeName: "binance",
|
|
42835
43153
|
* strategyName: "my-strategy"
|
|
42836
|
-
* }, "manual-close-001");
|
|
43154
|
+
* }, { id: "manual-close-001" });
|
|
42837
43155
|
* ```
|
|
42838
43156
|
*/
|
|
42839
|
-
this.commitClosePending = async (symbol, context,
|
|
43157
|
+
this.commitClosePending = async (symbol, context, payload = {}) => {
|
|
42840
43158
|
backtest.loggerService.info(LIVE_METHOD_NAME_CLOSE_PENDING, {
|
|
42841
43159
|
symbol,
|
|
42842
43160
|
context,
|
|
42843
|
-
|
|
43161
|
+
payload,
|
|
42844
43162
|
});
|
|
42845
43163
|
backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_CLOSE_PENDING);
|
|
42846
43164
|
backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_CLOSE_PENDING);
|
|
@@ -42857,7 +43175,7 @@ class LiveUtils {
|
|
|
42857
43175
|
strategyName: context.strategyName,
|
|
42858
43176
|
exchangeName: context.exchangeName,
|
|
42859
43177
|
frameName: "",
|
|
42860
|
-
},
|
|
43178
|
+
}, payload);
|
|
42861
43179
|
};
|
|
42862
43180
|
/**
|
|
42863
43181
|
* Executes partial close at profit level (moving toward TP).
|
|
@@ -43651,7 +43969,7 @@ class LiveUtils {
|
|
|
43651
43969
|
*
|
|
43652
43970
|
* @param symbol - Trading pair symbol
|
|
43653
43971
|
* @param context - Execution context with strategyName and exchangeName
|
|
43654
|
-
* @param
|
|
43972
|
+
* @param payload - Optional commit payload with id and note
|
|
43655
43973
|
* @returns Promise that resolves when activation flag is set
|
|
43656
43974
|
*
|
|
43657
43975
|
* @example
|
|
@@ -43660,14 +43978,14 @@ class LiveUtils {
|
|
|
43660
43978
|
* await Live.commitActivateScheduled("BTCUSDT", {
|
|
43661
43979
|
* strategyName: "my-strategy",
|
|
43662
43980
|
* exchangeName: "binance"
|
|
43663
|
-
* }, "manual-activate-001");
|
|
43981
|
+
* }, { id: "manual-activate-001" });
|
|
43664
43982
|
* ```
|
|
43665
43983
|
*/
|
|
43666
|
-
this.commitActivateScheduled = async (symbol, context,
|
|
43984
|
+
this.commitActivateScheduled = async (symbol, context, payload = {}) => {
|
|
43667
43985
|
backtest.loggerService.info(LIVE_METHOD_NAME_ACTIVATE_SCHEDULED, {
|
|
43668
43986
|
symbol,
|
|
43669
43987
|
context,
|
|
43670
|
-
|
|
43988
|
+
payload,
|
|
43671
43989
|
});
|
|
43672
43990
|
backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_ACTIVATE_SCHEDULED);
|
|
43673
43991
|
backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_ACTIVATE_SCHEDULED);
|
|
@@ -43684,7 +44002,7 @@ class LiveUtils {
|
|
|
43684
44002
|
strategyName: context.strategyName,
|
|
43685
44003
|
exchangeName: context.exchangeName,
|
|
43686
44004
|
frameName: "",
|
|
43687
|
-
},
|
|
44005
|
+
}, payload);
|
|
43688
44006
|
};
|
|
43689
44007
|
/**
|
|
43690
44008
|
* Adds a new DCA entry to the active pending signal.
|
|
@@ -49755,6 +50073,741 @@ class MaxDrawdownUtils {
|
|
|
49755
50073
|
*/
|
|
49756
50074
|
const MaxDrawdown = new MaxDrawdownUtils();
|
|
49757
50075
|
|
|
50076
|
+
const REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT = "ReflectUtils.getPositionPnlPercent";
|
|
50077
|
+
const REFLECT_METHOD_NAME_GET_POSITION_PNL_COST = "ReflectUtils.getPositionPnlCost";
|
|
50078
|
+
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE = "ReflectUtils.getPositionHighestProfitPrice";
|
|
50079
|
+
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP = "ReflectUtils.getPositionHighestProfitTimestamp";
|
|
50080
|
+
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestPnlPercentage";
|
|
50081
|
+
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST = "ReflectUtils.getPositionHighestPnlCost";
|
|
50082
|
+
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN = "ReflectUtils.getPositionHighestProfitBreakeven";
|
|
50083
|
+
const REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES = "ReflectUtils.getPositionDrawdownMinutes";
|
|
50084
|
+
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES = "ReflectUtils.getPositionHighestProfitMinutes";
|
|
50085
|
+
const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES = "ReflectUtils.getPositionMaxDrawdownMinutes";
|
|
50086
|
+
const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE = "ReflectUtils.getPositionMaxDrawdownPrice";
|
|
50087
|
+
const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP = "ReflectUtils.getPositionMaxDrawdownTimestamp";
|
|
50088
|
+
const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE = "ReflectUtils.getPositionMaxDrawdownPnlPercentage";
|
|
50089
|
+
const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST = "ReflectUtils.getPositionMaxDrawdownPnlCost";
|
|
50090
|
+
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestProfitDistancePnlPercentage";
|
|
50091
|
+
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST = "ReflectUtils.getPositionHighestProfitDistancePnlCost";
|
|
50092
|
+
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestMaxDrawdownPnlPercentage";
|
|
50093
|
+
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST = "ReflectUtils.getPositionHighestMaxDrawdownPnlCost";
|
|
50094
|
+
const REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE = "ReflectUtils.getMaxDrawdownDistancePnlPercentage";
|
|
50095
|
+
const REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST = "ReflectUtils.getMaxDrawdownDistancePnlCost";
|
|
50096
|
+
/**
|
|
50097
|
+
* Utility class for real-time position reflection: PNL, peak profit, and drawdown queries.
|
|
50098
|
+
*
|
|
50099
|
+
* Provides unified access to strategyCoreService position state methods with logging
|
|
50100
|
+
* and full validation (strategy, exchange, frame, risk, actions).
|
|
50101
|
+
* Works for both live and backtest modes via the `backtest` parameter.
|
|
50102
|
+
* Exported as singleton instance for convenient usage.
|
|
50103
|
+
*
|
|
50104
|
+
* @example
|
|
50105
|
+
* ```typescript
|
|
50106
|
+
* import { Reflect } from "backtest-kit";
|
|
50107
|
+
*
|
|
50108
|
+
* // Get current unrealized PNL percentage
|
|
50109
|
+
* const pnl = await Reflect.getPositionPnlPercent(
|
|
50110
|
+
* "BTCUSDT",
|
|
50111
|
+
* 45000,
|
|
50112
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50113
|
+
* );
|
|
50114
|
+
* console.log(`PNL: ${pnl}%`);
|
|
50115
|
+
*
|
|
50116
|
+
* // Get peak profit reached
|
|
50117
|
+
* const peakPnl = await Reflect.getPositionHighestPnlPercentage(
|
|
50118
|
+
* "BTCUSDT",
|
|
50119
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50120
|
+
* );
|
|
50121
|
+
* console.log(`Peak PNL: ${peakPnl}%`);
|
|
50122
|
+
* ```
|
|
50123
|
+
*/
|
|
50124
|
+
class ReflectUtils {
|
|
50125
|
+
constructor() {
|
|
50126
|
+
/**
|
|
50127
|
+
* Returns the unrealized PNL percentage for the current pending signal at currentPrice.
|
|
50128
|
+
*
|
|
50129
|
+
* Accounts for partial closes, DCA entries, slippage and fees.
|
|
50130
|
+
* Returns null if no pending signal exists.
|
|
50131
|
+
*
|
|
50132
|
+
* @param symbol - Trading pair symbol
|
|
50133
|
+
* @param currentPrice - Current market price
|
|
50134
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50135
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50136
|
+
* @returns Promise resolving to PNL percentage or null
|
|
50137
|
+
*
|
|
50138
|
+
* @example
|
|
50139
|
+
* ```typescript
|
|
50140
|
+
* const pnl = await Reflect.getPositionPnlPercent(
|
|
50141
|
+
* "BTCUSDT",
|
|
50142
|
+
* 45000,
|
|
50143
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50144
|
+
* );
|
|
50145
|
+
* console.log(`PNL: ${pnl}%`);
|
|
50146
|
+
* ```
|
|
50147
|
+
*/
|
|
50148
|
+
this.getPositionPnlPercent = async (symbol, currentPrice, context, backtest$1 = false) => {
|
|
50149
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT, { symbol, currentPrice, context });
|
|
50150
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
|
|
50151
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
|
|
50152
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
|
|
50153
|
+
{
|
|
50154
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50155
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT);
|
|
50156
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT));
|
|
50157
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_PNL_PERCENT));
|
|
50158
|
+
}
|
|
50159
|
+
return await backtest.strategyCoreService.getPositionPnlPercent(backtest$1, symbol, currentPrice, context);
|
|
50160
|
+
};
|
|
50161
|
+
/**
|
|
50162
|
+
* Returns the unrealized PNL in dollars for the current pending signal at currentPrice.
|
|
50163
|
+
*
|
|
50164
|
+
* Calculated as: pnlPercentage / 100 × totalInvestedCost.
|
|
50165
|
+
* Accounts for partial closes, DCA entries, slippage and fees.
|
|
50166
|
+
* Returns null if no pending signal exists.
|
|
50167
|
+
*
|
|
50168
|
+
* @param symbol - Trading pair symbol
|
|
50169
|
+
* @param currentPrice - Current market price
|
|
50170
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50171
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50172
|
+
* @returns Promise resolving to PNL in dollars or null
|
|
50173
|
+
*
|
|
50174
|
+
* @example
|
|
50175
|
+
* ```typescript
|
|
50176
|
+
* const pnlCost = await Reflect.getPositionPnlCost(
|
|
50177
|
+
* "BTCUSDT",
|
|
50178
|
+
* 45000,
|
|
50179
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50180
|
+
* );
|
|
50181
|
+
* console.log(`PNL: $${pnlCost}`);
|
|
50182
|
+
* ```
|
|
50183
|
+
*/
|
|
50184
|
+
this.getPositionPnlCost = async (symbol, currentPrice, context, backtest$1 = false) => {
|
|
50185
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_PNL_COST, { symbol, currentPrice, context });
|
|
50186
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
|
|
50187
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
|
|
50188
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
|
|
50189
|
+
{
|
|
50190
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50191
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST);
|
|
50192
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST));
|
|
50193
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_PNL_COST));
|
|
50194
|
+
}
|
|
50195
|
+
return await backtest.strategyCoreService.getPositionPnlCost(backtest$1, symbol, currentPrice, context);
|
|
50196
|
+
};
|
|
50197
|
+
/**
|
|
50198
|
+
* Returns the best price reached in the profit direction during this position's life.
|
|
50199
|
+
*
|
|
50200
|
+
* Returns null if no pending signal exists.
|
|
50201
|
+
*
|
|
50202
|
+
* @param symbol - Trading pair symbol
|
|
50203
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50204
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50205
|
+
* @returns Promise resolving to price or null
|
|
50206
|
+
*
|
|
50207
|
+
* @example
|
|
50208
|
+
* ```typescript
|
|
50209
|
+
* const peakPrice = await Reflect.getPositionHighestProfitPrice(
|
|
50210
|
+
* "BTCUSDT",
|
|
50211
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50212
|
+
* );
|
|
50213
|
+
* console.log(`Peak price: ${peakPrice}`);
|
|
50214
|
+
* ```
|
|
50215
|
+
*/
|
|
50216
|
+
this.getPositionHighestProfitPrice = async (symbol, context, backtest$1 = false) => {
|
|
50217
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE, { symbol, context });
|
|
50218
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
|
|
50219
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
|
|
50220
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
|
|
50221
|
+
{
|
|
50222
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50223
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE);
|
|
50224
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE));
|
|
50225
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE));
|
|
50226
|
+
}
|
|
50227
|
+
return await backtest.strategyCoreService.getPositionHighestProfitPrice(backtest$1, symbol, context);
|
|
50228
|
+
};
|
|
50229
|
+
/**
|
|
50230
|
+
* Returns the timestamp when the best profit price was recorded during this position's life.
|
|
50231
|
+
*
|
|
50232
|
+
* Returns null if no pending signal exists.
|
|
50233
|
+
*
|
|
50234
|
+
* @param symbol - Trading pair symbol
|
|
50235
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50236
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50237
|
+
* @returns Promise resolving to timestamp in milliseconds or null
|
|
50238
|
+
*
|
|
50239
|
+
* @example
|
|
50240
|
+
* ```typescript
|
|
50241
|
+
* const ts = await Reflect.getPositionHighestProfitTimestamp(
|
|
50242
|
+
* "BTCUSDT",
|
|
50243
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50244
|
+
* );
|
|
50245
|
+
* console.log(`Peak at: ${new Date(ts).toISOString()}`);
|
|
50246
|
+
* ```
|
|
50247
|
+
*/
|
|
50248
|
+
this.getPositionHighestProfitTimestamp = async (symbol, context, backtest$1 = false) => {
|
|
50249
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP, { symbol, context });
|
|
50250
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
|
|
50251
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
|
|
50252
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
|
|
50253
|
+
{
|
|
50254
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50255
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP);
|
|
50256
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP));
|
|
50257
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP));
|
|
50258
|
+
}
|
|
50259
|
+
return await backtest.strategyCoreService.getPositionHighestProfitTimestamp(backtest$1, symbol, context);
|
|
50260
|
+
};
|
|
50261
|
+
/**
|
|
50262
|
+
* Returns the PnL percentage at the moment the best profit price was recorded during this position's life.
|
|
50263
|
+
*
|
|
50264
|
+
* Returns null if no pending signal exists.
|
|
50265
|
+
*
|
|
50266
|
+
* @param symbol - Trading pair symbol
|
|
50267
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50268
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50269
|
+
* @returns Promise resolving to PnL percentage or null
|
|
50270
|
+
*
|
|
50271
|
+
* @example
|
|
50272
|
+
* ```typescript
|
|
50273
|
+
* const peakPnl = await Reflect.getPositionHighestPnlPercentage(
|
|
50274
|
+
* "BTCUSDT",
|
|
50275
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50276
|
+
* );
|
|
50277
|
+
* console.log(`Peak PNL: ${peakPnl}%`);
|
|
50278
|
+
* ```
|
|
50279
|
+
*/
|
|
50280
|
+
this.getPositionHighestPnlPercentage = async (symbol, context, backtest$1 = false) => {
|
|
50281
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE, { symbol, context });
|
|
50282
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
|
|
50283
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
|
|
50284
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
|
|
50285
|
+
{
|
|
50286
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50287
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE);
|
|
50288
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE));
|
|
50289
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE));
|
|
50290
|
+
}
|
|
50291
|
+
return await backtest.strategyCoreService.getPositionHighestPnlPercentage(backtest$1, symbol, context);
|
|
50292
|
+
};
|
|
50293
|
+
/**
|
|
50294
|
+
* Returns the PnL cost (in quote currency) at the moment the best profit price was recorded during this position's life.
|
|
50295
|
+
*
|
|
50296
|
+
* Returns null if no pending signal exists.
|
|
50297
|
+
*
|
|
50298
|
+
* @param symbol - Trading pair symbol
|
|
50299
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50300
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50301
|
+
* @returns Promise resolving to PnL cost in quote currency or null
|
|
50302
|
+
*
|
|
50303
|
+
* @example
|
|
50304
|
+
* ```typescript
|
|
50305
|
+
* const peakCost = await Reflect.getPositionHighestPnlCost(
|
|
50306
|
+
* "BTCUSDT",
|
|
50307
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50308
|
+
* );
|
|
50309
|
+
* console.log(`Peak PNL: $${peakCost}`);
|
|
50310
|
+
* ```
|
|
50311
|
+
*/
|
|
50312
|
+
this.getPositionHighestPnlCost = async (symbol, context, backtest$1 = false) => {
|
|
50313
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST, { symbol, context });
|
|
50314
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
|
|
50315
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
|
|
50316
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
|
|
50317
|
+
{
|
|
50318
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50319
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST);
|
|
50320
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST));
|
|
50321
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST));
|
|
50322
|
+
}
|
|
50323
|
+
return await backtest.strategyCoreService.getPositionHighestPnlCost(backtest$1, symbol, context);
|
|
50324
|
+
};
|
|
50325
|
+
/**
|
|
50326
|
+
* Returns whether breakeven was mathematically reachable at the highest profit price.
|
|
50327
|
+
*
|
|
50328
|
+
* Returns null if no pending signal exists.
|
|
50329
|
+
*
|
|
50330
|
+
* @param symbol - Trading pair symbol
|
|
50331
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50332
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50333
|
+
* @returns Promise resolving to true if breakeven was reachable at peak, false otherwise, or null
|
|
50334
|
+
*
|
|
50335
|
+
* @example
|
|
50336
|
+
* ```typescript
|
|
50337
|
+
* const wasReachable = await Reflect.getPositionHighestProfitBreakeven(
|
|
50338
|
+
* "BTCUSDT",
|
|
50339
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50340
|
+
* );
|
|
50341
|
+
* console.log(`Breakeven reachable at peak: ${wasReachable}`);
|
|
50342
|
+
* ```
|
|
50343
|
+
*/
|
|
50344
|
+
this.getPositionHighestProfitBreakeven = async (symbol, context, backtest$1 = false) => {
|
|
50345
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN, { symbol, context });
|
|
50346
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
|
|
50347
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
|
|
50348
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
|
|
50349
|
+
{
|
|
50350
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50351
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN);
|
|
50352
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN));
|
|
50353
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN));
|
|
50354
|
+
}
|
|
50355
|
+
return await backtest.strategyCoreService.getPositionHighestProfitBreakeven(backtest$1, symbol, context);
|
|
50356
|
+
};
|
|
50357
|
+
/**
|
|
50358
|
+
* Returns the number of minutes elapsed since the highest profit price was recorded.
|
|
50359
|
+
*
|
|
50360
|
+
* Returns null if no pending signal exists.
|
|
50361
|
+
*
|
|
50362
|
+
* @param symbol - Trading pair symbol
|
|
50363
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50364
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50365
|
+
* @returns Promise resolving to minutes since highest profit price was recorded, or null
|
|
50366
|
+
*
|
|
50367
|
+
* @example
|
|
50368
|
+
* ```typescript
|
|
50369
|
+
* const minutes = await Reflect.getPositionDrawdownMinutes(
|
|
50370
|
+
* "BTCUSDT",
|
|
50371
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50372
|
+
* );
|
|
50373
|
+
* console.log(`Pulling back from peak for ${minutes} minutes`);
|
|
50374
|
+
* ```
|
|
50375
|
+
*/
|
|
50376
|
+
this.getPositionDrawdownMinutes = async (symbol, context, backtest$1 = false) => {
|
|
50377
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES, { symbol, context });
|
|
50378
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
|
|
50379
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
|
|
50380
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
|
|
50381
|
+
{
|
|
50382
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50383
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES);
|
|
50384
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES));
|
|
50385
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES));
|
|
50386
|
+
}
|
|
50387
|
+
return await backtest.strategyCoreService.getPositionDrawdownMinutes(backtest$1, symbol, context);
|
|
50388
|
+
};
|
|
50389
|
+
/**
|
|
50390
|
+
* Returns the number of minutes elapsed since the highest profit price was recorded.
|
|
50391
|
+
*
|
|
50392
|
+
* Alias for getPositionDrawdownMinutes — measures how long the position has been
|
|
50393
|
+
* pulling back from its peak profit level.
|
|
50394
|
+
* Returns null if no pending signal exists.
|
|
50395
|
+
*
|
|
50396
|
+
* @param symbol - Trading pair symbol
|
|
50397
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50398
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50399
|
+
* @returns Promise resolving to minutes since last profit peak or null
|
|
50400
|
+
*
|
|
50401
|
+
* @example
|
|
50402
|
+
* ```typescript
|
|
50403
|
+
* const minutes = await Reflect.getPositionHighestProfitMinutes(
|
|
50404
|
+
* "BTCUSDT",
|
|
50405
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50406
|
+
* );
|
|
50407
|
+
* console.log(`Pulling back from peak for ${minutes} minutes`);
|
|
50408
|
+
* ```
|
|
50409
|
+
*/
|
|
50410
|
+
this.getPositionHighestProfitMinutes = async (symbol, context, backtest$1 = false) => {
|
|
50411
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES, { symbol, context });
|
|
50412
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
|
|
50413
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
|
|
50414
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
|
|
50415
|
+
{
|
|
50416
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50417
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES);
|
|
50418
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES));
|
|
50419
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES));
|
|
50420
|
+
}
|
|
50421
|
+
return await backtest.strategyCoreService.getPositionHighestProfitMinutes(backtest$1, symbol, context);
|
|
50422
|
+
};
|
|
50423
|
+
/**
|
|
50424
|
+
* Returns the number of minutes elapsed since the worst loss price was recorded.
|
|
50425
|
+
*
|
|
50426
|
+
* Measures how long ago the deepest drawdown point occurred.
|
|
50427
|
+
* Zero when called at the exact moment the trough was set.
|
|
50428
|
+
* Returns null if no pending signal exists.
|
|
50429
|
+
*
|
|
50430
|
+
* @param symbol - Trading pair symbol
|
|
50431
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50432
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50433
|
+
* @returns Promise resolving to minutes since last drawdown trough or null
|
|
50434
|
+
*
|
|
50435
|
+
* @example
|
|
50436
|
+
* ```typescript
|
|
50437
|
+
* const minutes = await Reflect.getPositionMaxDrawdownMinutes(
|
|
50438
|
+
* "BTCUSDT",
|
|
50439
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50440
|
+
* );
|
|
50441
|
+
* console.log(`Drawdown trough was ${minutes} minutes ago`);
|
|
50442
|
+
* ```
|
|
50443
|
+
*/
|
|
50444
|
+
this.getPositionMaxDrawdownMinutes = async (symbol, context, backtest$1 = false) => {
|
|
50445
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES, { symbol, context });
|
|
50446
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
|
|
50447
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
|
|
50448
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
|
|
50449
|
+
{
|
|
50450
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50451
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES);
|
|
50452
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES));
|
|
50453
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES));
|
|
50454
|
+
}
|
|
50455
|
+
return await backtest.strategyCoreService.getPositionMaxDrawdownMinutes(backtest$1, symbol, context);
|
|
50456
|
+
};
|
|
50457
|
+
/**
|
|
50458
|
+
* Returns the worst price reached in the loss direction during this position's life.
|
|
50459
|
+
*
|
|
50460
|
+
* Returns null if no pending signal exists.
|
|
50461
|
+
*
|
|
50462
|
+
* @param symbol - Trading pair symbol
|
|
50463
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50464
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50465
|
+
* @returns Promise resolving to price or null
|
|
50466
|
+
*
|
|
50467
|
+
* @example
|
|
50468
|
+
* ```typescript
|
|
50469
|
+
* const troughPrice = await Reflect.getPositionMaxDrawdownPrice(
|
|
50470
|
+
* "BTCUSDT",
|
|
50471
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50472
|
+
* );
|
|
50473
|
+
* console.log(`Worst price: ${troughPrice}`);
|
|
50474
|
+
* ```
|
|
50475
|
+
*/
|
|
50476
|
+
this.getPositionMaxDrawdownPrice = async (symbol, context, backtest$1 = false) => {
|
|
50477
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE, { symbol, context });
|
|
50478
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
|
|
50479
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
|
|
50480
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
|
|
50481
|
+
{
|
|
50482
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50483
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE);
|
|
50484
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE));
|
|
50485
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PRICE));
|
|
50486
|
+
}
|
|
50487
|
+
return await backtest.strategyCoreService.getPositionMaxDrawdownPrice(backtest$1, symbol, context);
|
|
50488
|
+
};
|
|
50489
|
+
/**
|
|
50490
|
+
* Returns the timestamp when the worst loss price was recorded during this position's life.
|
|
50491
|
+
*
|
|
50492
|
+
* Returns null if no pending signal exists.
|
|
50493
|
+
*
|
|
50494
|
+
* @param symbol - Trading pair symbol
|
|
50495
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50496
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50497
|
+
* @returns Promise resolving to timestamp in milliseconds or null
|
|
50498
|
+
*
|
|
50499
|
+
* @example
|
|
50500
|
+
* ```typescript
|
|
50501
|
+
* const ts = await Reflect.getPositionMaxDrawdownTimestamp(
|
|
50502
|
+
* "BTCUSDT",
|
|
50503
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50504
|
+
* );
|
|
50505
|
+
* console.log(`Worst drawdown at: ${new Date(ts).toISOString()}`);
|
|
50506
|
+
* ```
|
|
50507
|
+
*/
|
|
50508
|
+
this.getPositionMaxDrawdownTimestamp = async (symbol, context, backtest$1 = false) => {
|
|
50509
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP, { symbol, context });
|
|
50510
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
|
|
50511
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
|
|
50512
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
|
|
50513
|
+
{
|
|
50514
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50515
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP);
|
|
50516
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP));
|
|
50517
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_TIMESTAMP));
|
|
50518
|
+
}
|
|
50519
|
+
return await backtest.strategyCoreService.getPositionMaxDrawdownTimestamp(backtest$1, symbol, context);
|
|
50520
|
+
};
|
|
50521
|
+
/**
|
|
50522
|
+
* Returns the PnL percentage at the moment the worst loss price was recorded during this position's life.
|
|
50523
|
+
*
|
|
50524
|
+
* Returns null if no pending signal exists.
|
|
50525
|
+
*
|
|
50526
|
+
* @param symbol - Trading pair symbol
|
|
50527
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50528
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50529
|
+
* @returns Promise resolving to PnL percentage or null
|
|
50530
|
+
*
|
|
50531
|
+
* @example
|
|
50532
|
+
* ```typescript
|
|
50533
|
+
* const worstPnl = await Reflect.getPositionMaxDrawdownPnlPercentage(
|
|
50534
|
+
* "BTCUSDT",
|
|
50535
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50536
|
+
* );
|
|
50537
|
+
* console.log(`Worst PNL: ${worstPnl}%`);
|
|
50538
|
+
* ```
|
|
50539
|
+
*/
|
|
50540
|
+
this.getPositionMaxDrawdownPnlPercentage = async (symbol, context, backtest$1 = false) => {
|
|
50541
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE, { symbol, context });
|
|
50542
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
|
|
50543
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
|
|
50544
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
|
|
50545
|
+
{
|
|
50546
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50547
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE);
|
|
50548
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE));
|
|
50549
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_PERCENTAGE));
|
|
50550
|
+
}
|
|
50551
|
+
return await backtest.strategyCoreService.getPositionMaxDrawdownPnlPercentage(backtest$1, symbol, context);
|
|
50552
|
+
};
|
|
50553
|
+
/**
|
|
50554
|
+
* Returns the PnL cost (in quote currency) at the moment the worst loss price was recorded during this position's life.
|
|
50555
|
+
*
|
|
50556
|
+
* Returns null if no pending signal exists.
|
|
50557
|
+
*
|
|
50558
|
+
* @param symbol - Trading pair symbol
|
|
50559
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50560
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50561
|
+
* @returns Promise resolving to PnL cost in quote currency or null
|
|
50562
|
+
*
|
|
50563
|
+
* @example
|
|
50564
|
+
* ```typescript
|
|
50565
|
+
* const worstCost = await Reflect.getPositionMaxDrawdownPnlCost(
|
|
50566
|
+
* "BTCUSDT",
|
|
50567
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50568
|
+
* );
|
|
50569
|
+
* console.log(`Worst PNL: $${worstCost}`);
|
|
50570
|
+
* ```
|
|
50571
|
+
*/
|
|
50572
|
+
this.getPositionMaxDrawdownPnlCost = async (symbol, context, backtest$1 = false) => {
|
|
50573
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST, { symbol, context });
|
|
50574
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
|
|
50575
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
|
|
50576
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
|
|
50577
|
+
{
|
|
50578
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50579
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST);
|
|
50580
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST));
|
|
50581
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_PNL_COST));
|
|
50582
|
+
}
|
|
50583
|
+
return await backtest.strategyCoreService.getPositionMaxDrawdownPnlCost(backtest$1, symbol, context);
|
|
50584
|
+
};
|
|
50585
|
+
/**
|
|
50586
|
+
* Returns the distance in PnL percentage between the current price and the highest profit peak.
|
|
50587
|
+
*
|
|
50588
|
+
* Result is ≥ 0. Returns null if no pending signal exists.
|
|
50589
|
+
*
|
|
50590
|
+
* @param symbol - Trading pair symbol
|
|
50591
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50592
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50593
|
+
* @returns Promise resolving to drawdown distance in PnL% (≥ 0) or null
|
|
50594
|
+
*
|
|
50595
|
+
* @example
|
|
50596
|
+
* ```typescript
|
|
50597
|
+
* const distance = await Reflect.getPositionHighestProfitDistancePnlPercentage(
|
|
50598
|
+
* "BTCUSDT",
|
|
50599
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50600
|
+
* );
|
|
50601
|
+
* console.log(`Dropped ${distance}% from peak`);
|
|
50602
|
+
* ```
|
|
50603
|
+
*/
|
|
50604
|
+
this.getPositionHighestProfitDistancePnlPercentage = async (symbol, context, backtest$1 = false) => {
|
|
50605
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE, { symbol, context });
|
|
50606
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
|
|
50607
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
|
|
50608
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
|
|
50609
|
+
{
|
|
50610
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50611
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE);
|
|
50612
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE));
|
|
50613
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_PERCENTAGE));
|
|
50614
|
+
}
|
|
50615
|
+
return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlPercentage(backtest$1, symbol, context);
|
|
50616
|
+
};
|
|
50617
|
+
/**
|
|
50618
|
+
* Returns the distance in PnL cost between the current price and the highest profit peak.
|
|
50619
|
+
*
|
|
50620
|
+
* Result is ≥ 0. Returns null if no pending signal exists.
|
|
50621
|
+
*
|
|
50622
|
+
* @param symbol - Trading pair symbol
|
|
50623
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50624
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50625
|
+
* @returns Promise resolving to drawdown distance in PnL cost (≥ 0) or null
|
|
50626
|
+
*
|
|
50627
|
+
* @example
|
|
50628
|
+
* ```typescript
|
|
50629
|
+
* const distance = await Reflect.getPositionHighestProfitDistancePnlCost(
|
|
50630
|
+
* "BTCUSDT",
|
|
50631
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50632
|
+
* );
|
|
50633
|
+
* console.log(`Dropped $${distance} from peak`);
|
|
50634
|
+
* ```
|
|
50635
|
+
*/
|
|
50636
|
+
this.getPositionHighestProfitDistancePnlCost = async (symbol, context, backtest$1 = false) => {
|
|
50637
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST, { symbol, context });
|
|
50638
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
|
|
50639
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
|
|
50640
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
|
|
50641
|
+
{
|
|
50642
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50643
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST);
|
|
50644
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST));
|
|
50645
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_DISTANCE_PNL_COST));
|
|
50646
|
+
}
|
|
50647
|
+
return await backtest.strategyCoreService.getPositionHighestProfitDistancePnlCost(backtest$1, symbol, context);
|
|
50648
|
+
};
|
|
50649
|
+
/**
|
|
50650
|
+
* Returns the distance in PnL percentage between the current price and the worst drawdown trough.
|
|
50651
|
+
*
|
|
50652
|
+
* Result is ≥ 0. Returns null if no pending signal exists.
|
|
50653
|
+
*
|
|
50654
|
+
* @param symbol - Trading pair symbol
|
|
50655
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50656
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50657
|
+
* @returns Promise resolving to recovery distance from worst drawdown trough in PnL% (≥ 0) or null
|
|
50658
|
+
*
|
|
50659
|
+
* @example
|
|
50660
|
+
* ```typescript
|
|
50661
|
+
* const distance = await Reflect.getPositionHighestMaxDrawdownPnlPercentage(
|
|
50662
|
+
* "BTCUSDT",
|
|
50663
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50664
|
+
* );
|
|
50665
|
+
* console.log(`${distance}% above worst trough`);
|
|
50666
|
+
* ```
|
|
50667
|
+
*/
|
|
50668
|
+
this.getPositionHighestMaxDrawdownPnlPercentage = async (symbol, context, backtest$1 = false) => {
|
|
50669
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE, { symbol, context });
|
|
50670
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
|
|
50671
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
|
|
50672
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
|
|
50673
|
+
{
|
|
50674
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50675
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE);
|
|
50676
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE));
|
|
50677
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_PERCENTAGE));
|
|
50678
|
+
}
|
|
50679
|
+
return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlPercentage(backtest$1, symbol, context);
|
|
50680
|
+
};
|
|
50681
|
+
/**
|
|
50682
|
+
* Returns the distance in PnL cost between the current price and the worst drawdown trough.
|
|
50683
|
+
*
|
|
50684
|
+
* Result is ≥ 0. Returns null if no pending signal exists.
|
|
50685
|
+
*
|
|
50686
|
+
* @param symbol - Trading pair symbol
|
|
50687
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50688
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50689
|
+
* @returns Promise resolving to recovery distance from worst drawdown trough in PnL cost (≥ 0) or null
|
|
50690
|
+
*
|
|
50691
|
+
* @example
|
|
50692
|
+
* ```typescript
|
|
50693
|
+
* const distance = await Reflect.getPositionHighestMaxDrawdownPnlCost(
|
|
50694
|
+
* "BTCUSDT",
|
|
50695
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50696
|
+
* );
|
|
50697
|
+
* console.log(`$${distance} above worst trough`);
|
|
50698
|
+
* ```
|
|
50699
|
+
*/
|
|
50700
|
+
this.getPositionHighestMaxDrawdownPnlCost = async (symbol, context, backtest$1 = false) => {
|
|
50701
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST, { symbol, context });
|
|
50702
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
|
|
50703
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
|
|
50704
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
|
|
50705
|
+
{
|
|
50706
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50707
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST);
|
|
50708
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST));
|
|
50709
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_MAX_DRAWDOWN_PNL_COST));
|
|
50710
|
+
}
|
|
50711
|
+
return await backtest.strategyCoreService.getPositionHighestMaxDrawdownPnlCost(backtest$1, symbol, context);
|
|
50712
|
+
};
|
|
50713
|
+
/**
|
|
50714
|
+
* Returns the peak-to-trough PnL percentage distance between the position's highest profit and deepest drawdown.
|
|
50715
|
+
*
|
|
50716
|
+
* Result is ≥ 0. Returns null if no pending signal exists.
|
|
50717
|
+
*
|
|
50718
|
+
* @param symbol - Trading pair symbol
|
|
50719
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50720
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50721
|
+
* @returns Promise resolving to peak-to-trough PnL percentage distance (≥ 0) or null
|
|
50722
|
+
*
|
|
50723
|
+
* @example
|
|
50724
|
+
* ```typescript
|
|
50725
|
+
* const distance = await Reflect.getMaxDrawdownDistancePnlPercentage(
|
|
50726
|
+
* "BTCUSDT",
|
|
50727
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50728
|
+
* );
|
|
50729
|
+
* console.log(`Peak-to-trough: ${distance}%`);
|
|
50730
|
+
* ```
|
|
50731
|
+
*/
|
|
50732
|
+
this.getMaxDrawdownDistancePnlPercentage = async (symbol, context, backtest$1 = false) => {
|
|
50733
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE, { symbol, context });
|
|
50734
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
|
|
50735
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
|
|
50736
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
|
|
50737
|
+
{
|
|
50738
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50739
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE);
|
|
50740
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
|
|
50741
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_PERCENTAGE));
|
|
50742
|
+
}
|
|
50743
|
+
return await backtest.strategyCoreService.getMaxDrawdownDistancePnlPercentage(backtest$1, symbol, context);
|
|
50744
|
+
};
|
|
50745
|
+
/**
|
|
50746
|
+
* Returns the peak-to-trough PnL cost distance between the position's highest profit and deepest drawdown.
|
|
50747
|
+
*
|
|
50748
|
+
* Result is ≥ 0. Returns null if no pending signal exists.
|
|
50749
|
+
*
|
|
50750
|
+
* @param symbol - Trading pair symbol
|
|
50751
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
50752
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
50753
|
+
* @returns Promise resolving to peak-to-trough PnL cost distance (≥ 0) or null
|
|
50754
|
+
*
|
|
50755
|
+
* @example
|
|
50756
|
+
* ```typescript
|
|
50757
|
+
* const distance = await Reflect.getMaxDrawdownDistancePnlCost(
|
|
50758
|
+
* "BTCUSDT",
|
|
50759
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50760
|
+
* );
|
|
50761
|
+
* console.log(`Peak-to-trough: $${distance}`);
|
|
50762
|
+
* ```
|
|
50763
|
+
*/
|
|
50764
|
+
this.getMaxDrawdownDistancePnlCost = async (symbol, context, backtest$1 = false) => {
|
|
50765
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST, { symbol, context });
|
|
50766
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
|
|
50767
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
|
|
50768
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
|
|
50769
|
+
{
|
|
50770
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
50771
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST);
|
|
50772
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
|
|
50773
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_MAX_DRAWDOWN_DISTANCE_PNL_COST));
|
|
50774
|
+
}
|
|
50775
|
+
return await backtest.strategyCoreService.getMaxDrawdownDistancePnlCost(backtest$1, symbol, context);
|
|
50776
|
+
};
|
|
50777
|
+
}
|
|
50778
|
+
}
|
|
50779
|
+
/**
|
|
50780
|
+
* Singleton instance of ReflectUtils for convenient position state queries.
|
|
50781
|
+
*
|
|
50782
|
+
* @example
|
|
50783
|
+
* ```typescript
|
|
50784
|
+
* import { Reflect } from "backtest-kit";
|
|
50785
|
+
*
|
|
50786
|
+
* // Real-time PNL
|
|
50787
|
+
* const pnl = await Reflect.getPositionPnlPercent(
|
|
50788
|
+
* "BTCUSDT",
|
|
50789
|
+
* 45000,
|
|
50790
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50791
|
+
* );
|
|
50792
|
+
* console.log(`PNL: ${pnl}%`);
|
|
50793
|
+
*
|
|
50794
|
+
* // Peak profit
|
|
50795
|
+
* const peakPnl = await Reflect.getPositionHighestPnlPercentage(
|
|
50796
|
+
* "BTCUSDT",
|
|
50797
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50798
|
+
* );
|
|
50799
|
+
* console.log(`Peak PNL: ${peakPnl}%`);
|
|
50800
|
+
*
|
|
50801
|
+
* // Drawdown from peak
|
|
50802
|
+
* const drawdown = await Reflect.getPositionHighestProfitDistancePnlPercentage(
|
|
50803
|
+
* "BTCUSDT",
|
|
50804
|
+
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "frame1" }
|
|
50805
|
+
* );
|
|
50806
|
+
* console.log(`Dropped ${drawdown}% from peak`);
|
|
50807
|
+
* ```
|
|
50808
|
+
*/
|
|
50809
|
+
const Reflect$1 = new ReflectUtils();
|
|
50810
|
+
|
|
49758
50811
|
/**
|
|
49759
50812
|
* Utility class containing predefined trading constants for take-profit and stop-loss levels.
|
|
49760
50813
|
*
|
|
@@ -55864,6 +56917,7 @@ exports.PersistSignalAdapter = PersistSignalAdapter;
|
|
|
55864
56917
|
exports.PersistStorageAdapter = PersistStorageAdapter;
|
|
55865
56918
|
exports.Position = Position;
|
|
55866
56919
|
exports.PositionSize = PositionSize;
|
|
56920
|
+
exports.Reflect = Reflect$1;
|
|
55867
56921
|
exports.Report = Report;
|
|
55868
56922
|
exports.ReportBase = ReportBase;
|
|
55869
56923
|
exports.ReportWriter = ReportWriter;
|
|
@@ -55922,6 +56976,8 @@ exports.getDefaultConfig = getDefaultConfig;
|
|
|
55922
56976
|
exports.getEffectivePriceOpen = getEffectivePriceOpen;
|
|
55923
56977
|
exports.getExchangeSchema = getExchangeSchema;
|
|
55924
56978
|
exports.getFrameSchema = getFrameSchema;
|
|
56979
|
+
exports.getMaxDrawdownDistancePnlCost = getMaxDrawdownDistancePnlCost;
|
|
56980
|
+
exports.getMaxDrawdownDistancePnlPercentage = getMaxDrawdownDistancePnlPercentage;
|
|
55925
56981
|
exports.getMode = getMode;
|
|
55926
56982
|
exports.getNextCandles = getNextCandles;
|
|
55927
56983
|
exports.getOrderBook = getOrderBook;
|