backtest-kit 6.14.0 → 6.16.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 +889 -32
- package/build/index.mjs +885 -33
- package/package.json +1 -1
- package/types.d.ts +513 -3
package/build/index.cjs
CHANGED
|
@@ -734,6 +734,11 @@ const schedulePingSubject = new functoolsKit.Subject();
|
|
|
734
734
|
* Allows users to track active signal lifecycle and implement custom dynamic management logic.
|
|
735
735
|
*/
|
|
736
736
|
const activePingSubject = new functoolsKit.Subject();
|
|
737
|
+
/**
|
|
738
|
+
* Idle ping emitter for strategy idle state events.
|
|
739
|
+
* Emits every tick when there is no pending or scheduled signal being monitored.
|
|
740
|
+
*/
|
|
741
|
+
const idlePingSubject = new functoolsKit.Subject();
|
|
737
742
|
/**
|
|
738
743
|
* Strategy management signal emitter.
|
|
739
744
|
* Emits when strategy management actions are executed:
|
|
@@ -785,6 +790,7 @@ var emitters = /*#__PURE__*/Object.freeze({
|
|
|
785
790
|
errorEmitter: errorEmitter,
|
|
786
791
|
exitEmitter: exitEmitter,
|
|
787
792
|
highestProfitSubject: highestProfitSubject,
|
|
793
|
+
idlePingSubject: idlePingSubject,
|
|
788
794
|
maxDrawdownSubject: maxDrawdownSubject,
|
|
789
795
|
partialLossSubject: partialLossSubject,
|
|
790
796
|
partialProfitSubject: partialProfitSubject,
|
|
@@ -6048,6 +6054,27 @@ const CALL_ACTIVE_PING_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (sel
|
|
|
6048
6054
|
errorEmitter.next(error);
|
|
6049
6055
|
},
|
|
6050
6056
|
});
|
|
6057
|
+
const CALL_IDLE_PING_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, timestamp, backtest, currentPrice) => {
|
|
6058
|
+
await ExecutionContextService.runInContext(async () => {
|
|
6059
|
+
// Call system onIdlePing callback (emits to idlePingSubject)
|
|
6060
|
+
await self.params.onIdlePing(self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, currentPrice, self.params.execution.context.backtest, timestamp);
|
|
6061
|
+
}, {
|
|
6062
|
+
when: new Date(timestamp),
|
|
6063
|
+
symbol: symbol,
|
|
6064
|
+
backtest: backtest,
|
|
6065
|
+
});
|
|
6066
|
+
}), {
|
|
6067
|
+
fallback: (error, self) => {
|
|
6068
|
+
const message = "ClientStrategy CALL_IDLE_PING_CALLBACKS_FN thrown";
|
|
6069
|
+
const payload = {
|
|
6070
|
+
error: functoolsKit.errorData(error),
|
|
6071
|
+
message: functoolsKit.getErrorMessage(error),
|
|
6072
|
+
};
|
|
6073
|
+
self.params.logger.warn(message, payload);
|
|
6074
|
+
console.warn(message, payload);
|
|
6075
|
+
errorEmitter.next(error);
|
|
6076
|
+
},
|
|
6077
|
+
});
|
|
6051
6078
|
const CALL_ACTIVE_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, signal, currentPrice, timestamp, backtest) => {
|
|
6052
6079
|
await ExecutionContextService.runInContext(async () => {
|
|
6053
6080
|
if (self.params.callbacks?.onActive) {
|
|
@@ -6703,6 +6730,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice, backt
|
|
|
6703
6730
|
};
|
|
6704
6731
|
const RETURN_IDLE_FN = async (self, currentPrice) => {
|
|
6705
6732
|
const currentTime = self.params.execution.context.when.getTime();
|
|
6733
|
+
await CALL_IDLE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, currentTime, self.params.execution.context.backtest, currentPrice);
|
|
6706
6734
|
await CALL_IDLE_CALLBACKS_FN(self, self.params.execution.context.symbol, currentPrice, currentTime, self.params.execution.context.backtest);
|
|
6707
6735
|
const result = {
|
|
6708
6736
|
action: "idle",
|
|
@@ -7983,6 +8011,40 @@ class ClientStrategy {
|
|
|
7983
8011
|
}
|
|
7984
8012
|
return Math.floor((timestamp - this._pendingSignal._peak.timestamp) / 60000);
|
|
7985
8013
|
}
|
|
8014
|
+
/**
|
|
8015
|
+
* Returns the number of minutes the position has been active since it opened.
|
|
8016
|
+
*
|
|
8017
|
+
* Computed as elapsed minutes since `pendingAt` (the moment the signal was activated).
|
|
8018
|
+
* Returns null if no pending signal exists.
|
|
8019
|
+
*
|
|
8020
|
+
* @param symbol - Trading pair symbol
|
|
8021
|
+
* @param timestamp - Current Unix timestamp in milliseconds
|
|
8022
|
+
* @returns Promise resolving to active minutes (≥ 0) or null
|
|
8023
|
+
*/
|
|
8024
|
+
async getPositionActiveMinutes(symbol, timestamp) {
|
|
8025
|
+
this.params.logger.debug("ClientStrategy getPositionActiveMinutes", { symbol });
|
|
8026
|
+
if (!this._pendingSignal) {
|
|
8027
|
+
return null;
|
|
8028
|
+
}
|
|
8029
|
+
return Math.floor((timestamp - this._pendingSignal.pendingAt) / 60000);
|
|
8030
|
+
}
|
|
8031
|
+
/**
|
|
8032
|
+
* Returns the number of minutes the scheduled signal has been waiting for activation.
|
|
8033
|
+
*
|
|
8034
|
+
* Computed as elapsed minutes since `scheduledAt` (the moment the scheduled signal was created).
|
|
8035
|
+
* Returns null if no scheduled signal exists.
|
|
8036
|
+
*
|
|
8037
|
+
* @param symbol - Trading pair symbol
|
|
8038
|
+
* @param timestamp - Current Unix timestamp in milliseconds
|
|
8039
|
+
* @returns Promise resolving to waiting minutes (≥ 0) or null
|
|
8040
|
+
*/
|
|
8041
|
+
async getPositionWaitingMinutes(symbol, timestamp) {
|
|
8042
|
+
this.params.logger.debug("ClientStrategy getPositionWaitingMinutes", { symbol });
|
|
8043
|
+
if (!this._scheduledSignal) {
|
|
8044
|
+
return null;
|
|
8045
|
+
}
|
|
8046
|
+
return Math.floor((timestamp - this._scheduledSignal.scheduledAt) / 60000);
|
|
8047
|
+
}
|
|
7986
8048
|
/**
|
|
7987
8049
|
* Returns the number of minutes elapsed since the highest profit price was recorded.
|
|
7988
8050
|
*
|
|
@@ -10341,11 +10403,44 @@ const CREATE_COMMIT_SCHEDULE_PING_FN = (self) => functoolsKit.trycatch(async (sy
|
|
|
10341
10403
|
},
|
|
10342
10404
|
defaultValue: null,
|
|
10343
10405
|
});
|
|
10406
|
+
/**
|
|
10407
|
+
* Creates a callback function for emitting idle ping events.
|
|
10408
|
+
*
|
|
10409
|
+
* Called by ClientStrategy when no active or scheduled signals are present.
|
|
10410
|
+
*
|
|
10411
|
+
* @param self - Reference to StrategyConnectionService instance
|
|
10412
|
+
* @returns Callback function for idle ping events
|
|
10413
|
+
*/
|
|
10414
|
+
const CREATE_COMMIT_IDLE_PING_FN = (self) => functoolsKit.trycatch(async (symbol, strategyName, exchangeName, currentPrice, backtest, timestamp) => {
|
|
10415
|
+
const frameName = self.methodContextService.context.frameName;
|
|
10416
|
+
const event = {
|
|
10417
|
+
symbol,
|
|
10418
|
+
strategyName,
|
|
10419
|
+
exchangeName,
|
|
10420
|
+
frameName,
|
|
10421
|
+
currentPrice,
|
|
10422
|
+
backtest,
|
|
10423
|
+
timestamp,
|
|
10424
|
+
};
|
|
10425
|
+
await idlePingSubject.next(event);
|
|
10426
|
+
await self.actionCoreService.pingIdle(backtest, event, { strategyName, exchangeName, frameName });
|
|
10427
|
+
}, {
|
|
10428
|
+
fallback: (error) => {
|
|
10429
|
+
const message = "StrategyConnectionService CREATE_COMMIT_IDLE_PING_FN thrown";
|
|
10430
|
+
const payload = {
|
|
10431
|
+
error: functoolsKit.errorData(error),
|
|
10432
|
+
message: functoolsKit.getErrorMessage(error),
|
|
10433
|
+
};
|
|
10434
|
+
self.loggerService.warn(message, payload);
|
|
10435
|
+
console.warn(message, payload);
|
|
10436
|
+
errorEmitter.next(error);
|
|
10437
|
+
},
|
|
10438
|
+
defaultValue: null,
|
|
10439
|
+
});
|
|
10344
10440
|
/**
|
|
10345
10441
|
* Creates a callback function for emitting active ping events.
|
|
10346
10442
|
*
|
|
10347
10443
|
* Called by ClientStrategy when an active pending signal is being monitored every minute.
|
|
10348
|
-
* Placeholder for future activePingSubject implementation.
|
|
10349
10444
|
*
|
|
10350
10445
|
* @param self - Reference to StrategyConnectionService instance
|
|
10351
10446
|
* @returns Callback function for active ping events
|
|
@@ -10592,6 +10687,7 @@ class StrategyConnectionService {
|
|
|
10592
10687
|
onInit: CREATE_COMMIT_INIT_FN(this),
|
|
10593
10688
|
onSchedulePing: CREATE_COMMIT_SCHEDULE_PING_FN(this),
|
|
10594
10689
|
onActivePing: CREATE_COMMIT_ACTIVE_PING_FN(this),
|
|
10690
|
+
onIdlePing: CREATE_COMMIT_IDLE_PING_FN(this),
|
|
10595
10691
|
onDispose: CREATE_COMMIT_DISPOSE_FN(this),
|
|
10596
10692
|
onCommit: CREATE_COMMIT_FN(this),
|
|
10597
10693
|
onSignalSync: CREATE_SYNC_FN(this, strategyName, exchangeName, frameName, backtest),
|
|
@@ -11084,6 +11180,46 @@ class StrategyConnectionService {
|
|
|
11084
11180
|
const timestamp = await this.timeMetaService.getTimestamp(symbol, context, backtest);
|
|
11085
11181
|
return await strategy.getPositionCountdownMinutes(symbol, timestamp);
|
|
11086
11182
|
};
|
|
11183
|
+
/**
|
|
11184
|
+
* Returns the number of minutes the position has been active since it opened.
|
|
11185
|
+
*
|
|
11186
|
+
* Delegates to ClientStrategy.getPositionActiveMinutes().
|
|
11187
|
+
* Returns null if no pending signal exists.
|
|
11188
|
+
*
|
|
11189
|
+
* @param backtest - Whether running in backtest mode
|
|
11190
|
+
* @param symbol - Trading pair symbol
|
|
11191
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
11192
|
+
* @returns Promise resolving to active minutes (≥ 0) or null
|
|
11193
|
+
*/
|
|
11194
|
+
this.getPositionActiveMinutes = async (backtest, symbol, context) => {
|
|
11195
|
+
this.loggerService.log("strategyConnectionService getPositionActiveMinutes", {
|
|
11196
|
+
symbol,
|
|
11197
|
+
context,
|
|
11198
|
+
});
|
|
11199
|
+
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
11200
|
+
const timestamp = await this.timeMetaService.getTimestamp(symbol, context, backtest);
|
|
11201
|
+
return await strategy.getPositionActiveMinutes(symbol, timestamp);
|
|
11202
|
+
};
|
|
11203
|
+
/**
|
|
11204
|
+
* Returns the number of minutes the scheduled signal has been waiting for activation.
|
|
11205
|
+
*
|
|
11206
|
+
* Delegates to ClientStrategy.getPositionWaitingMinutes().
|
|
11207
|
+
* Returns null if no scheduled signal exists.
|
|
11208
|
+
*
|
|
11209
|
+
* @param backtest - Whether running in backtest mode
|
|
11210
|
+
* @param symbol - Trading pair symbol
|
|
11211
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
11212
|
+
* @returns Promise resolving to waiting minutes (≥ 0) or null
|
|
11213
|
+
*/
|
|
11214
|
+
this.getPositionWaitingMinutes = async (backtest, symbol, context) => {
|
|
11215
|
+
this.loggerService.log("strategyConnectionService getPositionWaitingMinutes", {
|
|
11216
|
+
symbol,
|
|
11217
|
+
context,
|
|
11218
|
+
});
|
|
11219
|
+
const strategy = this.getStrategy(symbol, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
11220
|
+
const timestamp = await this.timeMetaService.getTimestamp(symbol, context, backtest);
|
|
11221
|
+
return await strategy.getPositionWaitingMinutes(symbol, timestamp);
|
|
11222
|
+
};
|
|
11087
11223
|
/**
|
|
11088
11224
|
* Returns the best price reached in the profit direction during this position's life.
|
|
11089
11225
|
*
|
|
@@ -13030,6 +13166,34 @@ const CALL_PING_SCHEDULED_FN = functoolsKit.trycatch(async (event, self) => {
|
|
|
13030
13166
|
},
|
|
13031
13167
|
defaultValue: null,
|
|
13032
13168
|
});
|
|
13169
|
+
/**
|
|
13170
|
+
* Wrapper to call pingIdle method with error capture.
|
|
13171
|
+
*/
|
|
13172
|
+
const CALL_PING_IDLE_FN = functoolsKit.trycatch(async (event, self) => {
|
|
13173
|
+
if (!self._target.pingIdle) {
|
|
13174
|
+
return;
|
|
13175
|
+
}
|
|
13176
|
+
if (await self.params.strategy.hasPendingSignal(event.backtest, event.symbol, {
|
|
13177
|
+
strategyName: event.strategyName,
|
|
13178
|
+
exchangeName: event.exchangeName,
|
|
13179
|
+
frameName: event.frameName,
|
|
13180
|
+
})) {
|
|
13181
|
+
return;
|
|
13182
|
+
}
|
|
13183
|
+
return await self._target.pingIdle(event);
|
|
13184
|
+
}, {
|
|
13185
|
+
fallback: (error) => {
|
|
13186
|
+
const message = "ActionProxy.pingIdle thrown";
|
|
13187
|
+
const payload = {
|
|
13188
|
+
error: functoolsKit.errorData(error),
|
|
13189
|
+
message: functoolsKit.getErrorMessage(error),
|
|
13190
|
+
};
|
|
13191
|
+
LOGGER_SERVICE$4.warn(message, payload);
|
|
13192
|
+
console.warn(message, payload);
|
|
13193
|
+
errorEmitter.next(error);
|
|
13194
|
+
},
|
|
13195
|
+
defaultValue: null,
|
|
13196
|
+
});
|
|
13033
13197
|
/**
|
|
13034
13198
|
* Wrapper to call pingActive method with error capture.
|
|
13035
13199
|
*/
|
|
@@ -13268,6 +13432,18 @@ class ActionProxy {
|
|
|
13268
13432
|
async pingActive(event) {
|
|
13269
13433
|
return await CALL_PING_ACTIVE_FN(event, this);
|
|
13270
13434
|
}
|
|
13435
|
+
/**
|
|
13436
|
+
* Handles idle ping events with error capture.
|
|
13437
|
+
*
|
|
13438
|
+
* Wraps the user's pingIdle() method to catch and log any errors.
|
|
13439
|
+
* Called every tick while no signal is pending or scheduled.
|
|
13440
|
+
*
|
|
13441
|
+
* @param event - Idle ping data with symbol, strategy info, current price, timestamp
|
|
13442
|
+
* @returns Promise resolving to user's pingIdle() result or null on error
|
|
13443
|
+
*/
|
|
13444
|
+
async pingIdle(event) {
|
|
13445
|
+
return await CALL_PING_IDLE_FN(event, this);
|
|
13446
|
+
}
|
|
13271
13447
|
/**
|
|
13272
13448
|
* Handles risk rejection events with error capture.
|
|
13273
13449
|
*
|
|
@@ -13455,6 +13631,23 @@ const CALL_PING_SCHEDULED_CALLBACK_FN = functoolsKit.trycatch(async (self, event
|
|
|
13455
13631
|
errorEmitter.next(error);
|
|
13456
13632
|
},
|
|
13457
13633
|
});
|
|
13634
|
+
/** Wrapper to call idle ping callback with error handling */
|
|
13635
|
+
const CALL_PING_IDLE_CALLBACK_FN = functoolsKit.trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
13636
|
+
if (self.params.callbacks?.onPingIdle) {
|
|
13637
|
+
await self.params.callbacks.onPingIdle(event, self.params.actionName, strategyName, frameName, backtest);
|
|
13638
|
+
}
|
|
13639
|
+
}, {
|
|
13640
|
+
fallback: (error, self) => {
|
|
13641
|
+
const message = "ClientAction CALL_PING_IDLE_CALLBACK_FN thrown";
|
|
13642
|
+
const payload = {
|
|
13643
|
+
error: functoolsKit.errorData(error),
|
|
13644
|
+
message: functoolsKit.getErrorMessage(error),
|
|
13645
|
+
};
|
|
13646
|
+
self.params.logger.warn(message, payload);
|
|
13647
|
+
console.warn(message, payload);
|
|
13648
|
+
errorEmitter.next(error);
|
|
13649
|
+
},
|
|
13650
|
+
});
|
|
13458
13651
|
/** Wrapper to call active ping callback with error handling */
|
|
13459
13652
|
const CALL_PING_ACTIVE_CALLBACK_FN = functoolsKit.trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
13460
13653
|
if (self.params.callbacks?.onPingActive) {
|
|
@@ -13821,6 +14014,24 @@ class ClientAction {
|
|
|
13821
14014
|
await CALL_PING_ACTIVE_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
13822
14015
|
}
|
|
13823
14016
|
;
|
|
14017
|
+
/**
|
|
14018
|
+
* Handles idle ping events when no signal is active.
|
|
14019
|
+
*/
|
|
14020
|
+
async pingIdle(event) {
|
|
14021
|
+
this.params.logger.debug("ClientAction pingIdle", {
|
|
14022
|
+
actionName: this.params.actionName,
|
|
14023
|
+
strategyName: this.params.strategyName,
|
|
14024
|
+
frameName: this.params.frameName,
|
|
14025
|
+
});
|
|
14026
|
+
if (!this._handlerInstance) {
|
|
14027
|
+
await this.waitForInit();
|
|
14028
|
+
}
|
|
14029
|
+
// Call handler method if defined
|
|
14030
|
+
await this._handlerInstance?.pingIdle(event);
|
|
14031
|
+
// Call callback if defined
|
|
14032
|
+
await CALL_PING_IDLE_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
14033
|
+
}
|
|
14034
|
+
;
|
|
13824
14035
|
/**
|
|
13825
14036
|
* Handles risk rejection events when signals fail risk validation.
|
|
13826
14037
|
*/
|
|
@@ -14073,6 +14284,21 @@ class ActionConnectionService {
|
|
|
14073
14284
|
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
14074
14285
|
await action.pingActive(event);
|
|
14075
14286
|
};
|
|
14287
|
+
/**
|
|
14288
|
+
* Routes idle ping event to appropriate ClientAction instance.
|
|
14289
|
+
*
|
|
14290
|
+
* @param event - Idle ping event data
|
|
14291
|
+
* @param backtest - Whether running in backtest mode
|
|
14292
|
+
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
14293
|
+
*/
|
|
14294
|
+
this.pingIdle = async (event, backtest, context) => {
|
|
14295
|
+
this.loggerService.log("actionConnectionService pingIdle", {
|
|
14296
|
+
backtest,
|
|
14297
|
+
context,
|
|
14298
|
+
});
|
|
14299
|
+
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
14300
|
+
await action.pingIdle(event);
|
|
14301
|
+
};
|
|
14076
14302
|
/**
|
|
14077
14303
|
* Routes riskRejection event to appropriate ClientAction instance.
|
|
14078
14304
|
*
|
|
@@ -15404,6 +15630,38 @@ class StrategyCoreService {
|
|
|
15404
15630
|
await this.validate(context);
|
|
15405
15631
|
return await this.strategyConnectionService.getPositionCountdownMinutes(backtest, symbol, context);
|
|
15406
15632
|
};
|
|
15633
|
+
/**
|
|
15634
|
+
* Returns the number of minutes the position has been active since it opened.
|
|
15635
|
+
*
|
|
15636
|
+
* @param backtest - Whether running in backtest mode
|
|
15637
|
+
* @param symbol - Trading pair symbol
|
|
15638
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
15639
|
+
* @returns Promise resolving to active minutes (≥ 0) or null
|
|
15640
|
+
*/
|
|
15641
|
+
this.getPositionActiveMinutes = async (backtest, symbol, context) => {
|
|
15642
|
+
this.loggerService.log("strategyCoreService getPositionActiveMinutes", {
|
|
15643
|
+
symbol,
|
|
15644
|
+
context,
|
|
15645
|
+
});
|
|
15646
|
+
await this.validate(context);
|
|
15647
|
+
return await this.strategyConnectionService.getPositionActiveMinutes(backtest, symbol, context);
|
|
15648
|
+
};
|
|
15649
|
+
/**
|
|
15650
|
+
* Returns the number of minutes the scheduled signal has been waiting for activation.
|
|
15651
|
+
*
|
|
15652
|
+
* @param backtest - Whether running in backtest mode
|
|
15653
|
+
* @param symbol - Trading pair symbol
|
|
15654
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
15655
|
+
* @returns Promise resolving to waiting minutes (≥ 0) or null
|
|
15656
|
+
*/
|
|
15657
|
+
this.getPositionWaitingMinutes = async (backtest, symbol, context) => {
|
|
15658
|
+
this.loggerService.log("strategyCoreService getPositionWaitingMinutes", {
|
|
15659
|
+
symbol,
|
|
15660
|
+
context,
|
|
15661
|
+
});
|
|
15662
|
+
await this.validate(context);
|
|
15663
|
+
return await this.strategyConnectionService.getPositionWaitingMinutes(backtest, symbol, context);
|
|
15664
|
+
};
|
|
15407
15665
|
/**
|
|
15408
15666
|
* Returns the best price reached in the profit direction during this position's life.
|
|
15409
15667
|
*
|
|
@@ -16144,6 +16402,27 @@ class ActionCoreService {
|
|
|
16144
16402
|
await this.actionConnectionService.pingActive(event, backtest, { actionName, ...context });
|
|
16145
16403
|
}
|
|
16146
16404
|
};
|
|
16405
|
+
/**
|
|
16406
|
+
* Routes idle ping event to all registered actions for the strategy.
|
|
16407
|
+
*
|
|
16408
|
+
* Retrieves action list from strategy schema (IStrategySchema.actions)
|
|
16409
|
+
* and invokes the pingIdle handler on each ClientAction instance sequentially.
|
|
16410
|
+
* Called every tick when there is no pending or scheduled signal being monitored.
|
|
16411
|
+
*
|
|
16412
|
+
* @param backtest - Whether running in backtest mode (true) or live mode (false)
|
|
16413
|
+
* @param event - Idle state monitoring data
|
|
16414
|
+
* @param context - Strategy execution context with strategyName, exchangeName, frameName
|
|
16415
|
+
*/
|
|
16416
|
+
this.pingIdle = async (backtest, event, context) => {
|
|
16417
|
+
this.loggerService.log("actionCoreService pingIdle", {
|
|
16418
|
+
context,
|
|
16419
|
+
});
|
|
16420
|
+
await this.validate(context);
|
|
16421
|
+
const { actions = [] } = this.strategySchemaService.get(context.strategyName);
|
|
16422
|
+
for (const actionName of actions) {
|
|
16423
|
+
await this.actionConnectionService.pingIdle(event, backtest, { actionName, ...context });
|
|
16424
|
+
}
|
|
16425
|
+
};
|
|
16147
16426
|
/**
|
|
16148
16427
|
* Routes risk rejection event to all registered actions for the strategy.
|
|
16149
16428
|
*
|
|
@@ -32410,7 +32689,7 @@ class NotificationHelperService {
|
|
|
32410
32689
|
const pendingSignal = await this.strategyCoreService.getPendingSignal(backtest, symbol, currentPrice, {
|
|
32411
32690
|
strategyName: context.strategyName,
|
|
32412
32691
|
exchangeName: context.exchangeName,
|
|
32413
|
-
frameName:
|
|
32692
|
+
frameName: context.frameName,
|
|
32414
32693
|
});
|
|
32415
32694
|
if (!pendingSignal) {
|
|
32416
32695
|
throw new Error(`SignalUtils notify No pending signal found symbol=${symbol} `);
|
|
@@ -35550,6 +35829,8 @@ const GET_POSITION_PARTIALS_METHOD_NAME = "strategy.getPositionPartials";
|
|
|
35550
35829
|
const GET_POSITION_ENTRIES_METHOD_NAME = "strategy.getPositionEntries";
|
|
35551
35830
|
const GET_POSITION_ESTIMATE_MINUTES_METHOD_NAME = "strategy.getPositionEstimateMinutes";
|
|
35552
35831
|
const GET_POSITION_COUNTDOWN_MINUTES_METHOD_NAME = "strategy.getPositionCountdownMinutes";
|
|
35832
|
+
const GET_POSITION_ACTIVE_MINUTES_METHOD_NAME = "strategy.getPositionActiveMinutes";
|
|
35833
|
+
const GET_POSITION_WAITING_MINUTES_METHOD_NAME = "strategy.getPositionWaitingMinutes";
|
|
35553
35834
|
const GET_POSITION_HIGHEST_PROFIT_PRICE_METHOD_NAME = "strategy.getPositionHighestProfitPrice";
|
|
35554
35835
|
const GET_POSITION_HIGHEST_PROFIT_TIMESTAMP_METHOD_NAME = "strategy.getPositionHighestProfitTimestamp";
|
|
35555
35836
|
const GET_POSITION_HIGHEST_PNL_PERCENTAGE_METHOD_NAME = "strategy.getPositionHighestPnlPercentage";
|
|
@@ -36850,6 +37131,62 @@ async function getPositionCountdownMinutes(symbol) {
|
|
|
36850
37131
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
36851
37132
|
return await backtest.strategyCoreService.getPositionCountdownMinutes(isBacktest, symbol, { exchangeName, frameName, strategyName });
|
|
36852
37133
|
}
|
|
37134
|
+
/**
|
|
37135
|
+
* Returns the number of minutes the position has been active since it opened.
|
|
37136
|
+
*
|
|
37137
|
+
* Returns null if no pending signal exists.
|
|
37138
|
+
*
|
|
37139
|
+
* @param symbol - Trading pair symbol
|
|
37140
|
+
* @returns Promise resolving to active minutes (≥ 0) or null
|
|
37141
|
+
*
|
|
37142
|
+
* @example
|
|
37143
|
+
* ```typescript
|
|
37144
|
+
* import { getPositionActiveMinutes } from "backtest-kit";
|
|
37145
|
+
*
|
|
37146
|
+
* const minutes = await getPositionActiveMinutes("BTCUSDT");
|
|
37147
|
+
* // e.g. 120 (position has been open for 2 hours)
|
|
37148
|
+
* ```
|
|
37149
|
+
*/
|
|
37150
|
+
async function getPositionActiveMinutes(symbol) {
|
|
37151
|
+
backtest.loggerService.info(GET_POSITION_ACTIVE_MINUTES_METHOD_NAME, { symbol });
|
|
37152
|
+
if (!ExecutionContextService.hasContext()) {
|
|
37153
|
+
throw new Error("getPositionActiveMinutes requires an execution context");
|
|
37154
|
+
}
|
|
37155
|
+
if (!MethodContextService.hasContext()) {
|
|
37156
|
+
throw new Error("getPositionActiveMinutes requires a method context");
|
|
37157
|
+
}
|
|
37158
|
+
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
37159
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
37160
|
+
return await backtest.strategyCoreService.getPositionActiveMinutes(isBacktest, symbol, { exchangeName, frameName, strategyName });
|
|
37161
|
+
}
|
|
37162
|
+
/**
|
|
37163
|
+
* Returns the number of minutes the scheduled signal has been waiting for activation.
|
|
37164
|
+
*
|
|
37165
|
+
* Returns null if no scheduled signal exists.
|
|
37166
|
+
*
|
|
37167
|
+
* @param symbol - Trading pair symbol
|
|
37168
|
+
* @returns Promise resolving to waiting minutes (≥ 0) or null
|
|
37169
|
+
*
|
|
37170
|
+
* @example
|
|
37171
|
+
* ```typescript
|
|
37172
|
+
* import { getPositionWaitingMinutes } from "backtest-kit";
|
|
37173
|
+
*
|
|
37174
|
+
* const minutes = await getPositionWaitingMinutes("BTCUSDT");
|
|
37175
|
+
* // e.g. 15 (scheduled signal has been waiting 15 minutes for activation)
|
|
37176
|
+
* ```
|
|
37177
|
+
*/
|
|
37178
|
+
async function getPositionWaitingMinutes(symbol) {
|
|
37179
|
+
backtest.loggerService.info(GET_POSITION_WAITING_MINUTES_METHOD_NAME, { symbol });
|
|
37180
|
+
if (!ExecutionContextService.hasContext()) {
|
|
37181
|
+
throw new Error("getPositionWaitingMinutes requires an execution context");
|
|
37182
|
+
}
|
|
37183
|
+
if (!MethodContextService.hasContext()) {
|
|
37184
|
+
throw new Error("getPositionWaitingMinutes requires a method context");
|
|
37185
|
+
}
|
|
37186
|
+
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
37187
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
37188
|
+
return await backtest.strategyCoreService.getPositionWaitingMinutes(isBacktest, symbol, { exchangeName, frameName, strategyName });
|
|
37189
|
+
}
|
|
36853
37190
|
/**
|
|
36854
37191
|
* Returns the best price reached in the profit direction during this position's life.
|
|
36855
37192
|
*
|
|
@@ -37656,6 +37993,8 @@ const LISTEN_SCHEDULE_PING_METHOD_NAME = "event.listenSchedulePing";
|
|
|
37656
37993
|
const LISTEN_SCHEDULE_PING_ONCE_METHOD_NAME = "event.listenSchedulePingOnce";
|
|
37657
37994
|
const LISTEN_ACTIVE_PING_METHOD_NAME = "event.listenActivePing";
|
|
37658
37995
|
const LISTEN_ACTIVE_PING_ONCE_METHOD_NAME = "event.listenActivePingOnce";
|
|
37996
|
+
const LISTEN_IDLE_PING_METHOD_NAME = "event.listenIdlePing";
|
|
37997
|
+
const LISTEN_IDLE_PING_ONCE_METHOD_NAME = "event.listenIdlePingOnce";
|
|
37659
37998
|
const LISTEN_STRATEGY_COMMIT_METHOD_NAME = "event.listenStrategyCommit";
|
|
37660
37999
|
const LISTEN_STRATEGY_COMMIT_ONCE_METHOD_NAME = "event.listenStrategyCommitOnce";
|
|
37661
38000
|
const LISTEN_SYNC_METHOD_NAME = "event.listenSync";
|
|
@@ -38831,6 +39170,45 @@ function listenActivePingOnce(filterFn, fn) {
|
|
|
38831
39170
|
};
|
|
38832
39171
|
return disposeFn = listenActivePing(wrappedFn);
|
|
38833
39172
|
}
|
|
39173
|
+
/**
|
|
39174
|
+
* Subscribes to idle ping events with queued async processing.
|
|
39175
|
+
*
|
|
39176
|
+
* Emits every tick when there is no pending or scheduled signal being monitored.
|
|
39177
|
+
*
|
|
39178
|
+
* @param fn - Callback function to handle idle ping events
|
|
39179
|
+
* @returns Unsubscribe function to stop listening
|
|
39180
|
+
*/
|
|
39181
|
+
function listenIdlePing(fn) {
|
|
39182
|
+
backtest.loggerService.log(LISTEN_IDLE_PING_METHOD_NAME);
|
|
39183
|
+
const wrappedFn = async (event) => {
|
|
39184
|
+
if (await functoolsKit.not(backtest.strategyCoreService.hasPendingSignal(event.backtest, event.symbol, {
|
|
39185
|
+
strategyName: event.strategyName,
|
|
39186
|
+
exchangeName: event.exchangeName,
|
|
39187
|
+
frameName: event.frameName,
|
|
39188
|
+
}))) {
|
|
39189
|
+
await fn(event);
|
|
39190
|
+
}
|
|
39191
|
+
};
|
|
39192
|
+
return idlePingSubject.subscribe(functoolsKit.queued(wrappedFn));
|
|
39193
|
+
}
|
|
39194
|
+
/**
|
|
39195
|
+
* Subscribes to filtered idle ping events with one-time execution.
|
|
39196
|
+
*
|
|
39197
|
+
* @param filterFn - Predicate to filter events
|
|
39198
|
+
* @param fn - Callback function to handle the matching event
|
|
39199
|
+
* @returns Unsubscribe function to cancel the listener before it fires
|
|
39200
|
+
*/
|
|
39201
|
+
function listenIdlePingOnce(filterFn, fn) {
|
|
39202
|
+
backtest.loggerService.log(LISTEN_IDLE_PING_ONCE_METHOD_NAME);
|
|
39203
|
+
let disposeFn;
|
|
39204
|
+
const wrappedFn = async (event) => {
|
|
39205
|
+
if (filterFn(event)) {
|
|
39206
|
+
await fn(event);
|
|
39207
|
+
disposeFn && disposeFn();
|
|
39208
|
+
}
|
|
39209
|
+
};
|
|
39210
|
+
return disposeFn = listenIdlePing(wrappedFn);
|
|
39211
|
+
}
|
|
38834
39212
|
/**
|
|
38835
39213
|
* Subscribes to strategy management events with queued async processing.
|
|
38836
39214
|
*
|
|
@@ -39120,6 +39498,8 @@ const BACKTEST_METHOD_NAME_GET_POSITION_PARTIALS = "BacktestUtils.getPositionPar
|
|
|
39120
39498
|
const BACKTEST_METHOD_NAME_GET_POSITION_ENTRIES = "BacktestUtils.getPositionEntries";
|
|
39121
39499
|
const BACKTEST_METHOD_NAME_GET_POSITION_ESTIMATE_MINUTES = "BacktestUtils.getPositionEstimateMinutes";
|
|
39122
39500
|
const BACKTEST_METHOD_NAME_GET_POSITION_COUNTDOWN_MINUTES = "BacktestUtils.getPositionCountdownMinutes";
|
|
39501
|
+
const BACKTEST_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES = "BacktestUtils.getPositionActiveMinutes";
|
|
39502
|
+
const BACKTEST_METHOD_NAME_GET_POSITION_WAITING_MINUTES = "BacktestUtils.getPositionWaitingMinutes";
|
|
39123
39503
|
const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE = "BacktestUtils.getPositionHighestProfitPrice";
|
|
39124
39504
|
const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP = "BacktestUtils.getPositionHighestProfitTimestamp";
|
|
39125
39505
|
const BACKTEST_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE = "BacktestUtils.getPositionHighestPnlPercentage";
|
|
@@ -40075,6 +40455,60 @@ class BacktestUtils {
|
|
|
40075
40455
|
}
|
|
40076
40456
|
return await backtest.strategyCoreService.getPositionCountdownMinutes(true, symbol, context);
|
|
40077
40457
|
};
|
|
40458
|
+
/**
|
|
40459
|
+
* Returns the number of minutes the position has been active since it opened.
|
|
40460
|
+
*
|
|
40461
|
+
* Returns null if no pending signal exists.
|
|
40462
|
+
*
|
|
40463
|
+
* @param symbol - Trading pair symbol
|
|
40464
|
+
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
40465
|
+
* @returns Active minutes (≥ 0), or null if no active position
|
|
40466
|
+
*/
|
|
40467
|
+
this.getPositionActiveMinutes = async (symbol, context) => {
|
|
40468
|
+
backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES, {
|
|
40469
|
+
symbol,
|
|
40470
|
+
context,
|
|
40471
|
+
});
|
|
40472
|
+
backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES);
|
|
40473
|
+
backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES);
|
|
40474
|
+
{
|
|
40475
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
40476
|
+
riskName &&
|
|
40477
|
+
backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES);
|
|
40478
|
+
riskList &&
|
|
40479
|
+
riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES));
|
|
40480
|
+
actions &&
|
|
40481
|
+
actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES));
|
|
40482
|
+
}
|
|
40483
|
+
return await backtest.strategyCoreService.getPositionActiveMinutes(true, symbol, context);
|
|
40484
|
+
};
|
|
40485
|
+
/**
|
|
40486
|
+
* Returns the number of minutes the scheduled signal has been waiting for activation.
|
|
40487
|
+
*
|
|
40488
|
+
* Returns null if no scheduled signal exists.
|
|
40489
|
+
*
|
|
40490
|
+
* @param symbol - Trading pair symbol
|
|
40491
|
+
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
40492
|
+
* @returns Waiting minutes (≥ 0), or null if no scheduled signal
|
|
40493
|
+
*/
|
|
40494
|
+
this.getPositionWaitingMinutes = async (symbol, context) => {
|
|
40495
|
+
backtest.loggerService.info(BACKTEST_METHOD_NAME_GET_POSITION_WAITING_MINUTES, {
|
|
40496
|
+
symbol,
|
|
40497
|
+
context,
|
|
40498
|
+
});
|
|
40499
|
+
backtest.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_GET_POSITION_WAITING_MINUTES);
|
|
40500
|
+
backtest.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_GET_POSITION_WAITING_MINUTES);
|
|
40501
|
+
{
|
|
40502
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
40503
|
+
riskName &&
|
|
40504
|
+
backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_WAITING_MINUTES);
|
|
40505
|
+
riskList &&
|
|
40506
|
+
riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_GET_POSITION_WAITING_MINUTES));
|
|
40507
|
+
actions &&
|
|
40508
|
+
actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_GET_POSITION_WAITING_MINUTES));
|
|
40509
|
+
}
|
|
40510
|
+
return await backtest.strategyCoreService.getPositionWaitingMinutes(true, symbol, context);
|
|
40511
|
+
};
|
|
40078
40512
|
/**
|
|
40079
40513
|
* Returns the best price reached in the profit direction during this position's life.
|
|
40080
40514
|
*
|
|
@@ -41716,6 +42150,8 @@ const LIVE_METHOD_NAME_GET_POSITION_PARTIALS = "LiveUtils.getPositionPartials";
|
|
|
41716
42150
|
const LIVE_METHOD_NAME_GET_POSITION_ENTRIES = "LiveUtils.getPositionEntries";
|
|
41717
42151
|
const LIVE_METHOD_NAME_GET_POSITION_ESTIMATE_MINUTES = "LiveUtils.getPositionEstimateMinutes";
|
|
41718
42152
|
const LIVE_METHOD_NAME_GET_POSITION_COUNTDOWN_MINUTES = "LiveUtils.getPositionCountdownMinutes";
|
|
42153
|
+
const LIVE_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES = "LiveUtils.getPositionActiveMinutes";
|
|
42154
|
+
const LIVE_METHOD_NAME_GET_POSITION_WAITING_MINUTES = "LiveUtils.getPositionWaitingMinutes";
|
|
41719
42155
|
const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_PRICE = "LiveUtils.getPositionHighestProfitPrice";
|
|
41720
42156
|
const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP = "LiveUtils.getPositionHighestProfitTimestamp";
|
|
41721
42157
|
const LIVE_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE = "LiveUtils.getPositionHighestPnlPercentage";
|
|
@@ -42750,6 +43186,68 @@ class LiveUtils {
|
|
|
42750
43186
|
frameName: "",
|
|
42751
43187
|
});
|
|
42752
43188
|
};
|
|
43189
|
+
/**
|
|
43190
|
+
* Returns the number of minutes the position has been active since it opened.
|
|
43191
|
+
*
|
|
43192
|
+
* Returns null if no pending signal exists.
|
|
43193
|
+
*
|
|
43194
|
+
* @param symbol - Trading pair symbol
|
|
43195
|
+
* @param context - Execution context with strategyName and exchangeName
|
|
43196
|
+
* @returns Active minutes (≥ 0), or null if no active position
|
|
43197
|
+
*/
|
|
43198
|
+
this.getPositionActiveMinutes = async (symbol, context) => {
|
|
43199
|
+
backtest.loggerService.info(LIVE_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES, {
|
|
43200
|
+
symbol,
|
|
43201
|
+
context,
|
|
43202
|
+
});
|
|
43203
|
+
backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES);
|
|
43204
|
+
backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES);
|
|
43205
|
+
{
|
|
43206
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
43207
|
+
riskName &&
|
|
43208
|
+
backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES);
|
|
43209
|
+
riskList &&
|
|
43210
|
+
riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES));
|
|
43211
|
+
actions &&
|
|
43212
|
+
actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES));
|
|
43213
|
+
}
|
|
43214
|
+
return await backtest.strategyCoreService.getPositionActiveMinutes(false, symbol, {
|
|
43215
|
+
strategyName: context.strategyName,
|
|
43216
|
+
exchangeName: context.exchangeName,
|
|
43217
|
+
frameName: "",
|
|
43218
|
+
});
|
|
43219
|
+
};
|
|
43220
|
+
/**
|
|
43221
|
+
* Returns the number of minutes the scheduled signal has been waiting for activation.
|
|
43222
|
+
*
|
|
43223
|
+
* Returns null if no scheduled signal exists.
|
|
43224
|
+
*
|
|
43225
|
+
* @param symbol - Trading pair symbol
|
|
43226
|
+
* @param context - Execution context with strategyName and exchangeName
|
|
43227
|
+
* @returns Waiting minutes (≥ 0), or null if no scheduled signal
|
|
43228
|
+
*/
|
|
43229
|
+
this.getPositionWaitingMinutes = async (symbol, context) => {
|
|
43230
|
+
backtest.loggerService.info(LIVE_METHOD_NAME_GET_POSITION_WAITING_MINUTES, {
|
|
43231
|
+
symbol,
|
|
43232
|
+
context,
|
|
43233
|
+
});
|
|
43234
|
+
backtest.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_GET_POSITION_WAITING_MINUTES);
|
|
43235
|
+
backtest.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_GET_POSITION_WAITING_MINUTES);
|
|
43236
|
+
{
|
|
43237
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
43238
|
+
riskName &&
|
|
43239
|
+
backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_WAITING_MINUTES);
|
|
43240
|
+
riskList &&
|
|
43241
|
+
riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, LIVE_METHOD_NAME_GET_POSITION_WAITING_MINUTES));
|
|
43242
|
+
actions &&
|
|
43243
|
+
actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, LIVE_METHOD_NAME_GET_POSITION_WAITING_MINUTES));
|
|
43244
|
+
}
|
|
43245
|
+
return await backtest.strategyCoreService.getPositionWaitingMinutes(false, symbol, {
|
|
43246
|
+
strategyName: context.strategyName,
|
|
43247
|
+
exchangeName: context.exchangeName,
|
|
43248
|
+
frameName: "",
|
|
43249
|
+
});
|
|
43250
|
+
};
|
|
42753
43251
|
/**
|
|
42754
43252
|
* Returns the best price reached in the profit direction during this position's life.
|
|
42755
43253
|
*
|
|
@@ -45668,6 +46166,7 @@ const RECENT_LIVE_ADAPTER_METHOD_NAME_CLEAR = "RecentLiveAdapter.clear";
|
|
|
45668
46166
|
const RECENT_ADAPTER_METHOD_NAME_ENABLE = "RecentAdapter.enable";
|
|
45669
46167
|
const RECENT_ADAPTER_METHOD_NAME_DISABLE = "RecentAdapter.disable";
|
|
45670
46168
|
const RECENT_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL = "RecentAdapter.getLatestSignal";
|
|
46169
|
+
const RECENT_ADAPTER_METHOD_NAME_GET_MINUTES_SINCE_LATEST_SIGNAL = "RecentAdapter.getMinutesSinceLatestSignalCreated";
|
|
45671
46170
|
/**
|
|
45672
46171
|
* Builds a composite storage key from context parts.
|
|
45673
46172
|
* Includes backtest flag as the last segment to prevent live/backtest collisions.
|
|
@@ -45726,6 +46225,23 @@ class RecentPersistBacktestUtils {
|
|
|
45726
46225
|
});
|
|
45727
46226
|
return await PersistRecentAdapter.readRecentData(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
45728
46227
|
};
|
|
46228
|
+
/**
|
|
46229
|
+
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
46230
|
+
* @param timestamp - Current timestamp in milliseconds
|
|
46231
|
+
* @param symbol - Trading pair symbol
|
|
46232
|
+
* @param strategyName - Strategy identifier
|
|
46233
|
+
* @param exchangeName - Exchange identifier
|
|
46234
|
+
* @param frameName - Frame identifier
|
|
46235
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
46236
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
46237
|
+
*/
|
|
46238
|
+
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
46239
|
+
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest);
|
|
46240
|
+
if (!signal) {
|
|
46241
|
+
return null;
|
|
46242
|
+
}
|
|
46243
|
+
return Math.floor((timestamp - signal.timestamp) / (1000 * 60));
|
|
46244
|
+
};
|
|
45729
46245
|
}
|
|
45730
46246
|
}
|
|
45731
46247
|
/**
|
|
@@ -45768,6 +46284,23 @@ class RecentMemoryBacktestUtils {
|
|
|
45768
46284
|
backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
45769
46285
|
return this._signals.get(key) ?? null;
|
|
45770
46286
|
};
|
|
46287
|
+
/**
|
|
46288
|
+
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
46289
|
+
* @param timestamp - Current timestamp in milliseconds
|
|
46290
|
+
* @param symbol - Trading pair symbol
|
|
46291
|
+
* @param strategyName - Strategy identifier
|
|
46292
|
+
* @param exchangeName - Exchange identifier
|
|
46293
|
+
* @param frameName - Frame identifier
|
|
46294
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
46295
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
46296
|
+
*/
|
|
46297
|
+
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
46298
|
+
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest);
|
|
46299
|
+
if (!signal) {
|
|
46300
|
+
return null;
|
|
46301
|
+
}
|
|
46302
|
+
return Math.floor((timestamp - signal.timestamp) / (1000 * 60));
|
|
46303
|
+
};
|
|
45771
46304
|
}
|
|
45772
46305
|
}
|
|
45773
46306
|
/**
|
|
@@ -45811,6 +46344,23 @@ class RecentPersistLiveUtils {
|
|
|
45811
46344
|
});
|
|
45812
46345
|
return await PersistRecentAdapter.readRecentData(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
45813
46346
|
};
|
|
46347
|
+
/**
|
|
46348
|
+
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
46349
|
+
* @param timestamp - Current timestamp in milliseconds
|
|
46350
|
+
* @param symbol - Trading pair symbol
|
|
46351
|
+
* @param strategyName - Strategy identifier
|
|
46352
|
+
* @param exchangeName - Exchange identifier
|
|
46353
|
+
* @param frameName - Frame identifier
|
|
46354
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
46355
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
46356
|
+
*/
|
|
46357
|
+
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
46358
|
+
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest);
|
|
46359
|
+
if (!signal) {
|
|
46360
|
+
return null;
|
|
46361
|
+
}
|
|
46362
|
+
return Math.floor((timestamp - signal.timestamp) / (1000 * 60));
|
|
46363
|
+
};
|
|
45814
46364
|
}
|
|
45815
46365
|
}
|
|
45816
46366
|
/**
|
|
@@ -45853,6 +46403,23 @@ class RecentMemoryLiveUtils {
|
|
|
45853
46403
|
backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
45854
46404
|
return this._signals.get(key) ?? null;
|
|
45855
46405
|
};
|
|
46406
|
+
/**
|
|
46407
|
+
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
46408
|
+
* @param timestamp - Current timestamp in milliseconds
|
|
46409
|
+
* @param symbol - Trading pair symbol
|
|
46410
|
+
* @param strategyName - Strategy identifier
|
|
46411
|
+
* @param exchangeName - Exchange identifier
|
|
46412
|
+
* @param frameName - Frame identifier
|
|
46413
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
46414
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
46415
|
+
*/
|
|
46416
|
+
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
46417
|
+
const signal = await this.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest);
|
|
46418
|
+
if (!signal) {
|
|
46419
|
+
return null;
|
|
46420
|
+
}
|
|
46421
|
+
return Math.floor((timestamp - signal.timestamp) / (1000 * 60));
|
|
46422
|
+
};
|
|
45856
46423
|
}
|
|
45857
46424
|
}
|
|
45858
46425
|
/**
|
|
@@ -45899,6 +46466,32 @@ class RecentBacktestAdapter {
|
|
|
45899
46466
|
});
|
|
45900
46467
|
return await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
45901
46468
|
};
|
|
46469
|
+
/**
|
|
46470
|
+
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
46471
|
+
* Proxies call to the underlying storage adapter.
|
|
46472
|
+
* @param timestamp - Current timestamp in milliseconds
|
|
46473
|
+
* @param symbol - Trading pair symbol
|
|
46474
|
+
* @param strategyName - Strategy identifier
|
|
46475
|
+
* @param exchangeName - Exchange identifier
|
|
46476
|
+
* @param frameName - Frame identifier
|
|
46477
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
46478
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
46479
|
+
*/
|
|
46480
|
+
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
46481
|
+
backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
46482
|
+
symbol,
|
|
46483
|
+
strategyName,
|
|
46484
|
+
exchangeName,
|
|
46485
|
+
frameName,
|
|
46486
|
+
backtest: backtest$1,
|
|
46487
|
+
timestamp,
|
|
46488
|
+
});
|
|
46489
|
+
const signal = await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
46490
|
+
if (!signal) {
|
|
46491
|
+
return null;
|
|
46492
|
+
}
|
|
46493
|
+
return Math.floor((timestamp - signal.timestamp) / (1000 * 60));
|
|
46494
|
+
};
|
|
45902
46495
|
/**
|
|
45903
46496
|
* Sets the storage adapter constructor.
|
|
45904
46497
|
* All future storage operations will use this adapter.
|
|
@@ -45977,6 +46570,32 @@ class RecentLiveAdapter {
|
|
|
45977
46570
|
});
|
|
45978
46571
|
return await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
45979
46572
|
};
|
|
46573
|
+
/**
|
|
46574
|
+
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
46575
|
+
* Proxies call to the underlying storage adapter.
|
|
46576
|
+
* @param timestamp - Current timestamp in milliseconds
|
|
46577
|
+
* @param symbol - Trading pair symbol
|
|
46578
|
+
* @param strategyName - Strategy identifier
|
|
46579
|
+
* @param exchangeName - Exchange identifier
|
|
46580
|
+
* @param frameName - Frame identifier
|
|
46581
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
46582
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
46583
|
+
*/
|
|
46584
|
+
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
46585
|
+
backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
46586
|
+
symbol,
|
|
46587
|
+
strategyName,
|
|
46588
|
+
exchangeName,
|
|
46589
|
+
frameName,
|
|
46590
|
+
backtest: backtest$1,
|
|
46591
|
+
timestamp,
|
|
46592
|
+
});
|
|
46593
|
+
const signal = await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
46594
|
+
if (!signal) {
|
|
46595
|
+
return null;
|
|
46596
|
+
}
|
|
46597
|
+
return Math.floor((timestamp - signal.timestamp) / (1000 * 60));
|
|
46598
|
+
};
|
|
45980
46599
|
/**
|
|
45981
46600
|
* Sets the storage adapter constructor.
|
|
45982
46601
|
* All future storage operations will use this adapter.
|
|
@@ -46085,6 +46704,27 @@ class RecentAdapter {
|
|
|
46085
46704
|
}
|
|
46086
46705
|
return null;
|
|
46087
46706
|
};
|
|
46707
|
+
/**
|
|
46708
|
+
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
46709
|
+
* Searches backtest storage first, then live storage.
|
|
46710
|
+
* @param timestamp - Current timestamp in milliseconds
|
|
46711
|
+
* @param symbol - Trading pair symbol
|
|
46712
|
+
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
46713
|
+
* @returns Whole minutes since the latest signal was created, or null if no signal found
|
|
46714
|
+
* @throws Error if RecentAdapter is not enabled
|
|
46715
|
+
*/
|
|
46716
|
+
this.getMinutesSinceLatestSignalCreated = async (timestamp, symbol, context) => {
|
|
46717
|
+
backtest.loggerService.info(RECENT_ADAPTER_METHOD_NAME_GET_MINUTES_SINCE_LATEST_SIGNAL, {
|
|
46718
|
+
symbol,
|
|
46719
|
+
context,
|
|
46720
|
+
timestamp,
|
|
46721
|
+
});
|
|
46722
|
+
const signal = await this.getLatestSignal(symbol, context);
|
|
46723
|
+
if (!signal) {
|
|
46724
|
+
return null;
|
|
46725
|
+
}
|
|
46726
|
+
return Math.floor((timestamp - signal.timestamp) / (1000 * 60));
|
|
46727
|
+
};
|
|
46088
46728
|
}
|
|
46089
46729
|
}
|
|
46090
46730
|
/**
|
|
@@ -46104,6 +46744,7 @@ const RecentLive = new RecentLiveAdapter();
|
|
|
46104
46744
|
const RecentBacktest = new RecentBacktestAdapter();
|
|
46105
46745
|
|
|
46106
46746
|
const GET_LATEST_SIGNAL_METHOD_NAME = "signal.getLatestSignal";
|
|
46747
|
+
const GET_MINUTES_SINCE_LATEST_SIGNAL_CREATED_METHOD_NAME = "signal.getMinutesSinceLatestSignalCreated";
|
|
46107
46748
|
/**
|
|
46108
46749
|
* Returns the latest signal (pending or closed) for the current strategy context.
|
|
46109
46750
|
*
|
|
@@ -46141,6 +46782,42 @@ async function getLatestSignal(symbol) {
|
|
|
46141
46782
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
46142
46783
|
return await Recent.getLatestSignal(symbol, { exchangeName, frameName, strategyName });
|
|
46143
46784
|
}
|
|
46785
|
+
/**
|
|
46786
|
+
* Returns the number of whole minutes elapsed since the latest signal's creation timestamp.
|
|
46787
|
+
*
|
|
46788
|
+
* Does not distinguish between active and closed signals — measures time since
|
|
46789
|
+
* whichever signal was recorded last. Useful for cooldown logic after a stop-loss.
|
|
46790
|
+
*
|
|
46791
|
+
* Searches backtest storage first, then live storage.
|
|
46792
|
+
* Returns null if no signal exists at all.
|
|
46793
|
+
*
|
|
46794
|
+
* Automatically detects backtest/live mode from execution context.
|
|
46795
|
+
*
|
|
46796
|
+
* @param symbol - Trading pair symbol
|
|
46797
|
+
* @param timestamp - Current timestamp in milliseconds
|
|
46798
|
+
* @returns Promise resolving to whole minutes since the latest signal was created, or null
|
|
46799
|
+
*
|
|
46800
|
+
* @example
|
|
46801
|
+
* ```typescript
|
|
46802
|
+
* import { getMinutesSinceLatestSignalCreated } from "backtest-kit";
|
|
46803
|
+
*
|
|
46804
|
+
* const minutes = await getMinutesSinceLatestSignalCreated("BTCUSDT", Date.now());
|
|
46805
|
+
* if (minutes !== null && minutes < 24 * 60) {
|
|
46806
|
+
* return; // cooldown — skip new signal for 24 hours after last signal
|
|
46807
|
+
* }
|
|
46808
|
+
* ```
|
|
46809
|
+
*/
|
|
46810
|
+
async function getMinutesSinceLatestSignalCreated(symbol, timestamp) {
|
|
46811
|
+
backtest.loggerService.info(GET_MINUTES_SINCE_LATEST_SIGNAL_CREATED_METHOD_NAME, { symbol, timestamp });
|
|
46812
|
+
if (!ExecutionContextService.hasContext()) {
|
|
46813
|
+
throw new Error("getMinutesSinceLatestSignalCreated requires an execution context");
|
|
46814
|
+
}
|
|
46815
|
+
if (!MethodContextService.hasContext()) {
|
|
46816
|
+
throw new Error("getMinutesSinceLatestSignalCreated requires a method context");
|
|
46817
|
+
}
|
|
46818
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
46819
|
+
return await Recent.getMinutesSinceLatestSignalCreated(timestamp, symbol, { exchangeName, frameName, strategyName });
|
|
46820
|
+
}
|
|
46144
46821
|
|
|
46145
46822
|
const DEFAULT_BM25_K1 = 1.5;
|
|
46146
46823
|
const DEFAULT_BM25_B = 0.75;
|
|
@@ -48203,7 +48880,7 @@ const LOGGER_SERVICE$2 = new LoggerService();
|
|
|
48203
48880
|
* Default configuration that enables all report services.
|
|
48204
48881
|
* Used when no specific configuration is provided to enable().
|
|
48205
48882
|
*/
|
|
48206
|
-
const WILDCARD_TARGET$
|
|
48883
|
+
const WILDCARD_TARGET$2 = {
|
|
48207
48884
|
backtest: true,
|
|
48208
48885
|
strategy: true,
|
|
48209
48886
|
breakeven: true,
|
|
@@ -48254,7 +48931,7 @@ class ReportUtils {
|
|
|
48254
48931
|
*
|
|
48255
48932
|
* @returns Cleanup function that unsubscribes from all enabled services
|
|
48256
48933
|
*/
|
|
48257
|
-
this.enable = ({ backtest: bt = false, breakeven = false, heat = false, live = false, partial = false, performance = false, risk = false, schedule = false, walker = false, strategy = false, sync = false, highest_profit = false, max_drawdown = false, } = WILDCARD_TARGET$
|
|
48934
|
+
this.enable = ({ backtest: bt = false, breakeven = false, heat = false, live = false, partial = false, performance = false, risk = false, schedule = false, walker = false, strategy = false, sync = false, highest_profit = false, max_drawdown = false, } = WILDCARD_TARGET$2) => {
|
|
48258
48935
|
LOGGER_SERVICE$2.debug(REPORT_UTILS_METHOD_NAME_ENABLE, {
|
|
48259
48936
|
backtest: bt,
|
|
48260
48937
|
breakeven,
|
|
@@ -48346,7 +49023,7 @@ class ReportUtils {
|
|
|
48346
49023
|
* Report.disable();
|
|
48347
49024
|
* ```
|
|
48348
49025
|
*/
|
|
48349
|
-
this.disable = ({ backtest: bt = false, breakeven = false, heat = false, live = false, partial = false, performance = false, risk = false, schedule = false, walker = false, strategy = false, sync = false, highest_profit = false, max_drawdown = false, } = WILDCARD_TARGET$
|
|
49026
|
+
this.disable = ({ backtest: bt = false, breakeven = false, heat = false, live = false, partial = false, performance = false, risk = false, schedule = false, walker = false, strategy = false, sync = false, highest_profit = false, max_drawdown = false, } = WILDCARD_TARGET$2) => {
|
|
48350
49027
|
LOGGER_SERVICE$2.debug(REPORT_UTILS_METHOD_NAME_DISABLE, {
|
|
48351
49028
|
backtest: bt,
|
|
48352
49029
|
breakeven,
|
|
@@ -48470,7 +49147,7 @@ const LOGGER_SERVICE$1 = new LoggerService();
|
|
|
48470
49147
|
* Default configuration that enables all markdown services.
|
|
48471
49148
|
* Used when no specific configuration is provided to `enable()`.
|
|
48472
49149
|
*/
|
|
48473
|
-
const WILDCARD_TARGET = {
|
|
49150
|
+
const WILDCARD_TARGET$1 = {
|
|
48474
49151
|
backtest: true,
|
|
48475
49152
|
breakeven: true,
|
|
48476
49153
|
heat: true,
|
|
@@ -48521,7 +49198,7 @@ class MarkdownUtils {
|
|
|
48521
49198
|
*
|
|
48522
49199
|
* @returns Cleanup function that unsubscribes from all enabled services
|
|
48523
49200
|
*/
|
|
48524
|
-
this.enable = ({ backtest: bt = false, breakeven = false, heat = false, live = false, partial = false, performance = false, strategy = false, risk = false, schedule = false, walker = false, sync = false, highest_profit = false, max_drawdown = false, } = WILDCARD_TARGET) => {
|
|
49201
|
+
this.enable = ({ backtest: bt = false, breakeven = false, heat = false, live = false, partial = false, performance = false, strategy = false, risk = false, schedule = false, walker = false, sync = false, highest_profit = false, max_drawdown = false, } = WILDCARD_TARGET$1) => {
|
|
48525
49202
|
LOGGER_SERVICE$1.debug(MARKDOWN_METHOD_NAME_ENABLE, {
|
|
48526
49203
|
backtest: bt,
|
|
48527
49204
|
breakeven,
|
|
@@ -48615,7 +49292,7 @@ class MarkdownUtils {
|
|
|
48615
49292
|
* Markdown.disable();
|
|
48616
49293
|
* ```
|
|
48617
49294
|
*/
|
|
48618
|
-
this.disable = ({ backtest: bt = false, breakeven = false, heat = false, live = false, partial = false, performance = false, risk = false, strategy = false, schedule = false, walker = false, sync = false, highest_profit = false, max_drawdown = false, } = WILDCARD_TARGET) => {
|
|
49295
|
+
this.disable = ({ backtest: bt = false, breakeven = false, heat = false, live = false, partial = false, performance = false, risk = false, strategy = false, schedule = false, walker = false, sync = false, highest_profit = false, max_drawdown = false, } = WILDCARD_TARGET$1) => {
|
|
48619
49296
|
LOGGER_SERVICE$1.debug(MARKDOWN_METHOD_NAME_DISABLE, {
|
|
48620
49297
|
backtest: bt,
|
|
48621
49298
|
breakeven,
|
|
@@ -48696,7 +49373,7 @@ class MarkdownUtils {
|
|
|
48696
49373
|
* @param config.highest_profit - Clear highest profit report data
|
|
48697
49374
|
* @param config.max_drawdown - Clear max drawdown report data
|
|
48698
49375
|
*/
|
|
48699
|
-
this.clear = ({ backtest: bt = false, breakeven = false, heat = false, live = false, partial = false, performance = false, risk = false, strategy = false, schedule = false, walker = false, sync = false, highest_profit = false, max_drawdown = false, } = WILDCARD_TARGET) => {
|
|
49376
|
+
this.clear = ({ backtest: bt = false, breakeven = false, heat = false, live = false, partial = false, performance = false, risk = false, strategy = false, schedule = false, walker = false, sync = false, highest_profit = false, max_drawdown = false, } = WILDCARD_TARGET$1) => {
|
|
48700
49377
|
LOGGER_SERVICE$1.debug(MARKDOWN_METHOD_NAME_CLEAR, {
|
|
48701
49378
|
backtest: bt,
|
|
48702
49379
|
breakeven,
|
|
@@ -49468,6 +50145,7 @@ const METHOD_NAME_CREATE_SNAPSHOT = "SessionUtils.createSnapshot";
|
|
|
49468
50145
|
/** List of all global subjects whose listeners should be snapshotted for session isolation */
|
|
49469
50146
|
const SUBJECT_ISOLATION_LIST = [
|
|
49470
50147
|
activePingSubject,
|
|
50148
|
+
idlePingSubject,
|
|
49471
50149
|
backtestScheduleOpenSubject,
|
|
49472
50150
|
breakevenSubject,
|
|
49473
50151
|
doneBacktestSubject,
|
|
@@ -51108,6 +51786,8 @@ const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_TIMESTAMP = "ReflectUtils.
|
|
|
51108
51786
|
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_PERCENTAGE = "ReflectUtils.getPositionHighestPnlPercentage";
|
|
51109
51787
|
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PNL_COST = "ReflectUtils.getPositionHighestPnlCost";
|
|
51110
51788
|
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_BREAKEVEN = "ReflectUtils.getPositionHighestProfitBreakeven";
|
|
51789
|
+
const REFLECT_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES = "ReflectUtils.getPositionActiveMinutes";
|
|
51790
|
+
const REFLECT_METHOD_NAME_GET_POSITION_WAITING_MINUTES = "ReflectUtils.getPositionWaitingMinutes";
|
|
51111
51791
|
const REFLECT_METHOD_NAME_GET_POSITION_DRAWDOWN_MINUTES = "ReflectUtils.getPositionDrawdownMinutes";
|
|
51112
51792
|
const REFLECT_METHOD_NAME_GET_POSITION_HIGHEST_PROFIT_MINUTES = "ReflectUtils.getPositionHighestProfitMinutes";
|
|
51113
51793
|
const REFLECT_METHOD_NAME_GET_POSITION_MAX_DRAWDOWN_MINUTES = "ReflectUtils.getPositionMaxDrawdownMinutes";
|
|
@@ -51382,6 +52062,52 @@ class ReflectUtils {
|
|
|
51382
52062
|
}
|
|
51383
52063
|
return await backtest.strategyCoreService.getPositionHighestProfitBreakeven(backtest$1, symbol, context);
|
|
51384
52064
|
};
|
|
52065
|
+
/**
|
|
52066
|
+
* Returns the number of minutes the position has been active since it opened.
|
|
52067
|
+
*
|
|
52068
|
+
* Returns null if no pending signal exists.
|
|
52069
|
+
*
|
|
52070
|
+
* @param symbol - Trading pair symbol
|
|
52071
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
52072
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
52073
|
+
* @returns Promise resolving to active minutes (≥ 0) or null
|
|
52074
|
+
*/
|
|
52075
|
+
this.getPositionActiveMinutes = async (symbol, context, backtest$1 = false) => {
|
|
52076
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES, { symbol, context });
|
|
52077
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES);
|
|
52078
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES);
|
|
52079
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES);
|
|
52080
|
+
{
|
|
52081
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
52082
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES);
|
|
52083
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES));
|
|
52084
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_ACTIVE_MINUTES));
|
|
52085
|
+
}
|
|
52086
|
+
return await backtest.strategyCoreService.getPositionActiveMinutes(backtest$1, symbol, context);
|
|
52087
|
+
};
|
|
52088
|
+
/**
|
|
52089
|
+
* Returns the number of minutes the scheduled signal has been waiting for activation.
|
|
52090
|
+
*
|
|
52091
|
+
* Returns null if no scheduled signal exists.
|
|
52092
|
+
*
|
|
52093
|
+
* @param symbol - Trading pair symbol
|
|
52094
|
+
* @param context - Execution context with strategyName, exchangeName and frameName
|
|
52095
|
+
* @param backtest - True if backtest mode, false if live mode (default: false)
|
|
52096
|
+
* @returns Promise resolving to waiting minutes (≥ 0) or null
|
|
52097
|
+
*/
|
|
52098
|
+
this.getPositionWaitingMinutes = async (symbol, context, backtest$1 = false) => {
|
|
52099
|
+
backtest.loggerService.info(REFLECT_METHOD_NAME_GET_POSITION_WAITING_MINUTES, { symbol, context });
|
|
52100
|
+
backtest.strategyValidationService.validate(context.strategyName, REFLECT_METHOD_NAME_GET_POSITION_WAITING_MINUTES);
|
|
52101
|
+
backtest.exchangeValidationService.validate(context.exchangeName, REFLECT_METHOD_NAME_GET_POSITION_WAITING_MINUTES);
|
|
52102
|
+
context.frameName && backtest.frameValidationService.validate(context.frameName, REFLECT_METHOD_NAME_GET_POSITION_WAITING_MINUTES);
|
|
52103
|
+
{
|
|
52104
|
+
const { riskName, riskList, actions } = backtest.strategySchemaService.get(context.strategyName);
|
|
52105
|
+
riskName && backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_WAITING_MINUTES);
|
|
52106
|
+
riskList && riskList.forEach((riskName) => backtest.riskValidationService.validate(riskName, REFLECT_METHOD_NAME_GET_POSITION_WAITING_MINUTES));
|
|
52107
|
+
actions && actions.forEach((actionName) => backtest.actionValidationService.validate(actionName, REFLECT_METHOD_NAME_GET_POSITION_WAITING_MINUTES));
|
|
52108
|
+
}
|
|
52109
|
+
return await backtest.strategyCoreService.getPositionWaitingMinutes(backtest$1, symbol, context);
|
|
52110
|
+
};
|
|
51385
52111
|
/**
|
|
51386
52112
|
* Returns the number of minutes elapsed since the highest profit price was recorded.
|
|
51387
52113
|
*
|
|
@@ -53658,6 +54384,23 @@ const StorageLive = new StorageLiveAdapter();
|
|
|
53658
54384
|
*/
|
|
53659
54385
|
const StorageBacktest = new StorageBacktestAdapter();
|
|
53660
54386
|
|
|
54387
|
+
/**
|
|
54388
|
+
* Default configuration that enables all notification types.
|
|
54389
|
+
* Used when no specific configuration is provided to enable().
|
|
54390
|
+
*/
|
|
54391
|
+
const WILDCARD_TARGET = {
|
|
54392
|
+
signal: true,
|
|
54393
|
+
partial_profit: true,
|
|
54394
|
+
partial_loss: true,
|
|
54395
|
+
breakeven: true,
|
|
54396
|
+
strategy_commit: true,
|
|
54397
|
+
signal_sync: true,
|
|
54398
|
+
risk: true,
|
|
54399
|
+
info: true,
|
|
54400
|
+
common_error: true,
|
|
54401
|
+
critical_error: true,
|
|
54402
|
+
validation_error: true,
|
|
54403
|
+
};
|
|
53661
54404
|
/**
|
|
53662
54405
|
* Generates a unique key for notification identification.
|
|
53663
54406
|
* @returns Random string identifier
|
|
@@ -55713,64 +56456,152 @@ class NotificationAdapter {
|
|
|
55713
56456
|
*
|
|
55714
56457
|
* @returns Cleanup function that unsubscribes from all emitters
|
|
55715
56458
|
*/
|
|
55716
|
-
this.enable = functoolsKit.singleshot(() => {
|
|
56459
|
+
this.enable = functoolsKit.singleshot(({ signal = false, info = false, partial_profit = false, partial_loss = false, breakeven = false, strategy_commit = false, signal_sync = false, risk = false, common_error = false, critical_error = false, validation_error = false, } = WILDCARD_TARGET) => {
|
|
55717
56460
|
backtest.loggerService.info(NOTIFICATION_ADAPTER_METHOD_NAME_ENABLE);
|
|
55718
56461
|
let unLive;
|
|
55719
56462
|
let unBacktest;
|
|
55720
56463
|
{
|
|
55721
|
-
const unBacktestSignal = signalBacktestEmitter.subscribe((data) =>
|
|
56464
|
+
const unBacktestSignal = signalBacktestEmitter.subscribe(async (data) => {
|
|
56465
|
+
if (signal) {
|
|
56466
|
+
await NotificationBacktest.handleSignal(data);
|
|
56467
|
+
}
|
|
56468
|
+
});
|
|
55722
56469
|
const unBacktestPartialProfit = partialProfitSubject
|
|
55723
56470
|
.filter(({ backtest }) => backtest)
|
|
55724
|
-
.connect((data) =>
|
|
56471
|
+
.connect(async (data) => {
|
|
56472
|
+
if (partial_profit) {
|
|
56473
|
+
await NotificationBacktest.handlePartialProfit(data);
|
|
56474
|
+
}
|
|
56475
|
+
});
|
|
55725
56476
|
const unBacktestPartialLoss = partialLossSubject
|
|
55726
56477
|
.filter(({ backtest }) => backtest)
|
|
55727
|
-
.connect((data) =>
|
|
56478
|
+
.connect(async (data) => {
|
|
56479
|
+
if (partial_loss) {
|
|
56480
|
+
await NotificationBacktest.handlePartialLoss(data);
|
|
56481
|
+
}
|
|
56482
|
+
});
|
|
55728
56483
|
const unBacktestBreakeven = breakevenSubject
|
|
55729
56484
|
.filter(({ backtest }) => backtest)
|
|
55730
|
-
.connect((data) =>
|
|
56485
|
+
.connect(async (data) => {
|
|
56486
|
+
if (breakeven) {
|
|
56487
|
+
await NotificationBacktest.handleBreakeven(data);
|
|
56488
|
+
}
|
|
56489
|
+
});
|
|
55731
56490
|
const unBacktestStrategyCommit = strategyCommitSubject
|
|
55732
56491
|
.filter(({ backtest }) => backtest)
|
|
55733
|
-
.connect((data) =>
|
|
56492
|
+
.connect(async (data) => {
|
|
56493
|
+
if (strategy_commit) {
|
|
56494
|
+
await NotificationBacktest.handleStrategyCommit(data);
|
|
56495
|
+
}
|
|
56496
|
+
});
|
|
55734
56497
|
const unBacktestSync = syncSubject
|
|
55735
56498
|
.filter(({ backtest }) => backtest)
|
|
55736
|
-
.connect((data) =>
|
|
56499
|
+
.connect(async (data) => {
|
|
56500
|
+
if (signal_sync) {
|
|
56501
|
+
await NotificationBacktest.handleSync(data);
|
|
56502
|
+
}
|
|
56503
|
+
});
|
|
55737
56504
|
const unBacktestRisk = riskSubject
|
|
55738
56505
|
.filter(({ backtest }) => backtest)
|
|
55739
|
-
.connect((data) =>
|
|
55740
|
-
|
|
55741
|
-
|
|
55742
|
-
|
|
56506
|
+
.connect(async (data) => {
|
|
56507
|
+
if (risk) {
|
|
56508
|
+
await NotificationBacktest.handleRisk(data);
|
|
56509
|
+
}
|
|
56510
|
+
});
|
|
56511
|
+
const unBacktestError = errorEmitter.subscribe(async (error) => {
|
|
56512
|
+
if (common_error) {
|
|
56513
|
+
await NotificationBacktest.handleError(error);
|
|
56514
|
+
}
|
|
56515
|
+
});
|
|
56516
|
+
const unBacktestExit = exitEmitter.subscribe(async (error) => {
|
|
56517
|
+
if (critical_error) {
|
|
56518
|
+
await NotificationBacktest.handleCriticalError(error);
|
|
56519
|
+
}
|
|
56520
|
+
});
|
|
56521
|
+
const unBacktestValidation = validationSubject.subscribe(async (error) => {
|
|
56522
|
+
if (validation_error) {
|
|
56523
|
+
await NotificationBacktest.handleValidationError(error);
|
|
56524
|
+
}
|
|
56525
|
+
});
|
|
55743
56526
|
const unBacktestSignalNotify = signalNotifySubject
|
|
55744
56527
|
.filter(({ backtest }) => backtest)
|
|
55745
|
-
.connect((data) =>
|
|
56528
|
+
.connect(async (data) => {
|
|
56529
|
+
if (info) {
|
|
56530
|
+
await NotificationBacktest.handleSignalNotify(data);
|
|
56531
|
+
}
|
|
56532
|
+
});
|
|
55746
56533
|
unBacktest = functoolsKit.compose(() => unBacktestSignal(), () => unBacktestPartialProfit(), () => unBacktestPartialLoss(), () => unBacktestBreakeven(), () => unBacktestStrategyCommit(), () => unBacktestSync(), () => unBacktestRisk(), () => unBacktestError(), () => unBacktestExit(), () => unBacktestValidation(), () => unBacktestSignalNotify());
|
|
55747
56534
|
}
|
|
55748
56535
|
{
|
|
55749
|
-
const unLiveSignal = signalLiveEmitter.subscribe((data) =>
|
|
56536
|
+
const unLiveSignal = signalLiveEmitter.subscribe(async (data) => {
|
|
56537
|
+
if (signal) {
|
|
56538
|
+
await NotificationLive.handleSignal(data);
|
|
56539
|
+
}
|
|
56540
|
+
});
|
|
55750
56541
|
const unLivePartialProfit = partialProfitSubject
|
|
55751
56542
|
.filter(({ backtest }) => !backtest)
|
|
55752
|
-
.connect((data) =>
|
|
56543
|
+
.connect(async (data) => {
|
|
56544
|
+
if (partial_profit) {
|
|
56545
|
+
await NotificationLive.handlePartialProfit(data);
|
|
56546
|
+
}
|
|
56547
|
+
});
|
|
55753
56548
|
const unLivePartialLoss = partialLossSubject
|
|
55754
56549
|
.filter(({ backtest }) => !backtest)
|
|
55755
|
-
.connect((data) =>
|
|
56550
|
+
.connect(async (data) => {
|
|
56551
|
+
if (partial_loss) {
|
|
56552
|
+
await NotificationLive.handlePartialLoss(data);
|
|
56553
|
+
}
|
|
56554
|
+
});
|
|
55756
56555
|
const unLiveBreakeven = breakevenSubject
|
|
55757
56556
|
.filter(({ backtest }) => !backtest)
|
|
55758
|
-
.connect((data) =>
|
|
56557
|
+
.connect(async (data) => {
|
|
56558
|
+
if (breakeven) {
|
|
56559
|
+
await NotificationLive.handleBreakeven(data);
|
|
56560
|
+
}
|
|
56561
|
+
});
|
|
55759
56562
|
const unLiveStrategyCommit = strategyCommitSubject
|
|
55760
56563
|
.filter(({ backtest }) => !backtest)
|
|
55761
|
-
.connect((data) =>
|
|
56564
|
+
.connect(async (data) => {
|
|
56565
|
+
if (strategy_commit) {
|
|
56566
|
+
await NotificationLive.handleStrategyCommit(data);
|
|
56567
|
+
}
|
|
56568
|
+
});
|
|
55762
56569
|
const unLiveSync = syncSubject
|
|
55763
56570
|
.filter(({ backtest }) => !backtest)
|
|
55764
|
-
.connect((data) =>
|
|
56571
|
+
.connect(async (data) => {
|
|
56572
|
+
if (signal_sync) {
|
|
56573
|
+
await NotificationLive.handleSync(data);
|
|
56574
|
+
}
|
|
56575
|
+
});
|
|
55765
56576
|
const unLiveRisk = riskSubject
|
|
55766
56577
|
.filter(({ backtest }) => !backtest)
|
|
55767
|
-
.connect((data) =>
|
|
55768
|
-
|
|
55769
|
-
|
|
55770
|
-
|
|
56578
|
+
.connect(async (data) => {
|
|
56579
|
+
if (risk) {
|
|
56580
|
+
await NotificationLive.handleRisk(data);
|
|
56581
|
+
}
|
|
56582
|
+
});
|
|
56583
|
+
const unLiveError = errorEmitter.subscribe(async (error) => {
|
|
56584
|
+
if (common_error) {
|
|
56585
|
+
await NotificationLive.handleError(error);
|
|
56586
|
+
}
|
|
56587
|
+
});
|
|
56588
|
+
const unLiveExit = exitEmitter.subscribe(async (error) => {
|
|
56589
|
+
if (critical_error) {
|
|
56590
|
+
await NotificationLive.handleCriticalError(error);
|
|
56591
|
+
}
|
|
56592
|
+
});
|
|
56593
|
+
const unLiveValidation = validationSubject.subscribe(async (error) => {
|
|
56594
|
+
if (validation_error) {
|
|
56595
|
+
await NotificationLive.handleValidationError(error);
|
|
56596
|
+
}
|
|
56597
|
+
});
|
|
55771
56598
|
const unLiveSignalNotify = signalNotifySubject
|
|
55772
56599
|
.filter(({ backtest }) => !backtest)
|
|
55773
|
-
.connect((data) =>
|
|
56600
|
+
.connect(async (data) => {
|
|
56601
|
+
if (info) {
|
|
56602
|
+
await NotificationLive.handleSignalNotify(data);
|
|
56603
|
+
}
|
|
56604
|
+
});
|
|
55774
56605
|
unLive = functoolsKit.compose(() => unLiveSignal(), () => unLivePartialProfit(), () => unLivePartialLoss(), () => unLiveBreakeven(), () => unLiveStrategyCommit(), () => unLiveSync(), () => unLiveRisk(), () => unLiveError(), () => unLiveExit(), () => unLiveValidation(), () => unLiveSignalNotify());
|
|
55775
56606
|
}
|
|
55776
56607
|
return () => {
|
|
@@ -57435,6 +58266,7 @@ const METHOD_NAME_PARTIAL_PROFIT_AVAILABLE = "ActionBase.partialProfitAvailable"
|
|
|
57435
58266
|
const METHOD_NAME_PARTIAL_LOSS_AVAILABLE = "ActionBase.partialLossAvailable";
|
|
57436
58267
|
const METHOD_NAME_PING_SCHEDULED = "ActionBase.pingScheduled";
|
|
57437
58268
|
const METHOD_NAME_PING_ACTIVE = "ActionBase.pingActive";
|
|
58269
|
+
const METHOD_NAME_PING_IDLE = "ActionBase.pingIdle";
|
|
57438
58270
|
const METHOD_NAME_RISK_REJECTION = "ActionBase.riskRejection";
|
|
57439
58271
|
const METHOD_NAME_DISPOSE = "ActionBase.dispose";
|
|
57440
58272
|
const DEFAULT_SOURCE = "default";
|
|
@@ -57828,6 +58660,26 @@ class ActionBase {
|
|
|
57828
58660
|
source,
|
|
57829
58661
|
});
|
|
57830
58662
|
}
|
|
58663
|
+
/**
|
|
58664
|
+
* Handles idle ping events when no signal is active.
|
|
58665
|
+
*
|
|
58666
|
+
* Called every tick while no signal is pending or scheduled.
|
|
58667
|
+
* Use to monitor idle strategy state and implement entry condition logic.
|
|
58668
|
+
*
|
|
58669
|
+
* Triggered by: ActionCoreService.pingIdle() via StrategyConnectionService
|
|
58670
|
+
* Source: idlePingSubject.next() in CREATE_COMMIT_IDLE_PING_FN callback
|
|
58671
|
+
* Frequency: Every tick while no signal is pending or scheduled
|
|
58672
|
+
*
|
|
58673
|
+
* Default implementation: Logs idle ping event.
|
|
58674
|
+
*
|
|
58675
|
+
* @param event - Idle ping data with symbol, strategy info, current price, timestamp
|
|
58676
|
+
*/
|
|
58677
|
+
pingIdle(event, source = DEFAULT_SOURCE) {
|
|
58678
|
+
LOGGER_SERVICE.info(METHOD_NAME_PING_IDLE, {
|
|
58679
|
+
event,
|
|
58680
|
+
source,
|
|
58681
|
+
});
|
|
58682
|
+
}
|
|
57831
58683
|
/**
|
|
57832
58684
|
* Handles risk rejection events when signals fail risk validation.
|
|
57833
58685
|
*
|
|
@@ -58271,10 +59123,12 @@ exports.getFrameSchema = getFrameSchema;
|
|
|
58271
59123
|
exports.getLatestSignal = getLatestSignal;
|
|
58272
59124
|
exports.getMaxDrawdownDistancePnlCost = getMaxDrawdownDistancePnlCost;
|
|
58273
59125
|
exports.getMaxDrawdownDistancePnlPercentage = getMaxDrawdownDistancePnlPercentage;
|
|
59126
|
+
exports.getMinutesSinceLatestSignalCreated = getMinutesSinceLatestSignalCreated;
|
|
58274
59127
|
exports.getMode = getMode;
|
|
58275
59128
|
exports.getNextCandles = getNextCandles;
|
|
58276
59129
|
exports.getOrderBook = getOrderBook;
|
|
58277
59130
|
exports.getPendingSignal = getPendingSignal;
|
|
59131
|
+
exports.getPositionActiveMinutes = getPositionActiveMinutes;
|
|
58278
59132
|
exports.getPositionCountdownMinutes = getPositionCountdownMinutes;
|
|
58279
59133
|
exports.getPositionDrawdownMinutes = getPositionDrawdownMinutes;
|
|
58280
59134
|
exports.getPositionEffectivePrice = getPositionEffectivePrice;
|
|
@@ -58303,6 +59157,7 @@ exports.getPositionPartialOverlap = getPositionPartialOverlap;
|
|
|
58303
59157
|
exports.getPositionPartials = getPositionPartials;
|
|
58304
59158
|
exports.getPositionPnlCost = getPositionPnlCost;
|
|
58305
59159
|
exports.getPositionPnlPercent = getPositionPnlPercent;
|
|
59160
|
+
exports.getPositionWaitingMinutes = getPositionWaitingMinutes;
|
|
58306
59161
|
exports.getRawCandles = getRawCandles;
|
|
58307
59162
|
exports.getRiskSchema = getRiskSchema;
|
|
58308
59163
|
exports.getScheduledSignal = getScheduledSignal;
|
|
@@ -58341,6 +59196,8 @@ exports.listenError = listenError;
|
|
|
58341
59196
|
exports.listenExit = listenExit;
|
|
58342
59197
|
exports.listenHighestProfit = listenHighestProfit;
|
|
58343
59198
|
exports.listenHighestProfitOnce = listenHighestProfitOnce;
|
|
59199
|
+
exports.listenIdlePing = listenIdlePing;
|
|
59200
|
+
exports.listenIdlePingOnce = listenIdlePingOnce;
|
|
58344
59201
|
exports.listenMaxDrawdown = listenMaxDrawdown;
|
|
58345
59202
|
exports.listenMaxDrawdownOnce = listenMaxDrawdownOnce;
|
|
58346
59203
|
exports.listenPartialLossAvailable = listenPartialLossAvailable;
|