backtest-kit 1.13.4 → 2.0.1
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/README.md +8 -8
- package/build/index.cjs +880 -398
- package/build/index.mjs +831 -353
- package/package.json +1 -1
- package/types.d.ts +484 -196
package/build/index.cjs
CHANGED
|
@@ -541,14 +541,21 @@ const breakevenSubject = new functoolsKit.Subject();
|
|
|
541
541
|
*/
|
|
542
542
|
const riskSubject = new functoolsKit.Subject();
|
|
543
543
|
/**
|
|
544
|
-
*
|
|
544
|
+
* Schedule ping emitter for scheduled signal monitoring events.
|
|
545
545
|
* Emits every minute when a scheduled signal is being monitored (waiting for activation).
|
|
546
546
|
* Allows users to track scheduled signal lifecycle and implement custom cancellation logic.
|
|
547
547
|
*/
|
|
548
|
-
const
|
|
548
|
+
const schedulePingSubject = new functoolsKit.Subject();
|
|
549
|
+
/**
|
|
550
|
+
* Active ping emitter for active pending signal monitoring events.
|
|
551
|
+
* Emits every minute when an active pending signal is being monitored.
|
|
552
|
+
* Allows users to track active signal lifecycle and implement custom dynamic management logic.
|
|
553
|
+
*/
|
|
554
|
+
const activePingSubject = new functoolsKit.Subject();
|
|
549
555
|
|
|
550
556
|
var emitters = /*#__PURE__*/Object.freeze({
|
|
551
557
|
__proto__: null,
|
|
558
|
+
activePingSubject: activePingSubject,
|
|
552
559
|
breakevenSubject: breakevenSubject,
|
|
553
560
|
doneBacktestSubject: doneBacktestSubject,
|
|
554
561
|
doneLiveSubject: doneLiveSubject,
|
|
@@ -558,11 +565,11 @@ var emitters = /*#__PURE__*/Object.freeze({
|
|
|
558
565
|
partialLossSubject: partialLossSubject,
|
|
559
566
|
partialProfitSubject: partialProfitSubject,
|
|
560
567
|
performanceEmitter: performanceEmitter,
|
|
561
|
-
pingSubject: pingSubject,
|
|
562
568
|
progressBacktestEmitter: progressBacktestEmitter,
|
|
563
569
|
progressOptimizerEmitter: progressOptimizerEmitter,
|
|
564
570
|
progressWalkerEmitter: progressWalkerEmitter,
|
|
565
571
|
riskSubject: riskSubject,
|
|
572
|
+
schedulePingSubject: schedulePingSubject,
|
|
566
573
|
signalBacktestEmitter: signalBacktestEmitter,
|
|
567
574
|
signalEmitter: signalEmitter,
|
|
568
575
|
signalLiveEmitter: signalLiveEmitter,
|
|
@@ -773,11 +780,16 @@ class ClientExchange {
|
|
|
773
780
|
const whenTimestamp = this.params.execution.context.when.getTime();
|
|
774
781
|
const sinceTimestamp = since.getTime();
|
|
775
782
|
const filteredData = allData.filter((candle) => candle.timestamp >= sinceTimestamp && candle.timestamp <= whenTimestamp);
|
|
776
|
-
|
|
777
|
-
|
|
783
|
+
// Apply distinct by timestamp to remove duplicates
|
|
784
|
+
const uniqueData = Array.from(new Map(filteredData.map((candle) => [candle.timestamp, candle])).values());
|
|
785
|
+
if (filteredData.length !== uniqueData.length) {
|
|
786
|
+
this.params.logger.warn(`ClientExchange Removed ${filteredData.length - uniqueData.length} duplicate candles by timestamp`);
|
|
778
787
|
}
|
|
779
|
-
|
|
780
|
-
|
|
788
|
+
if (uniqueData.length < limit) {
|
|
789
|
+
this.params.logger.warn(`ClientExchange Expected ${limit} candles, got ${uniqueData.length}`);
|
|
790
|
+
}
|
|
791
|
+
await CALL_CANDLE_DATA_CALLBACKS_FN(this, symbol, interval, since, limit, uniqueData);
|
|
792
|
+
return uniqueData;
|
|
781
793
|
}
|
|
782
794
|
/**
|
|
783
795
|
* Fetches future candles forwards from execution context time.
|
|
@@ -826,11 +838,16 @@ class ClientExchange {
|
|
|
826
838
|
// Filter candles to strictly match the requested range
|
|
827
839
|
const sinceTimestamp = since.getTime();
|
|
828
840
|
const filteredData = allData.filter((candle) => candle.timestamp >= sinceTimestamp && candle.timestamp <= endTime);
|
|
829
|
-
|
|
830
|
-
|
|
841
|
+
// Apply distinct by timestamp to remove duplicates
|
|
842
|
+
const uniqueData = Array.from(new Map(filteredData.map((candle) => [candle.timestamp, candle])).values());
|
|
843
|
+
if (filteredData.length !== uniqueData.length) {
|
|
844
|
+
this.params.logger.warn(`ClientExchange getNextCandles: Removed ${filteredData.length - uniqueData.length} duplicate candles by timestamp`);
|
|
845
|
+
}
|
|
846
|
+
if (uniqueData.length < limit) {
|
|
847
|
+
this.params.logger.warn(`ClientExchange getNextCandles: Expected ${limit} candles, got ${uniqueData.length}`);
|
|
831
848
|
}
|
|
832
|
-
await CALL_CANDLE_DATA_CALLBACKS_FN(this, symbol, interval, since, limit,
|
|
833
|
-
return
|
|
849
|
+
await CALL_CANDLE_DATA_CALLBACKS_FN(this, symbol, interval, since, limit, uniqueData);
|
|
850
|
+
return uniqueData;
|
|
834
851
|
}
|
|
835
852
|
/**
|
|
836
853
|
* Calculates VWAP (Volume Weighted Average Price) from last N 1m candles.
|
|
@@ -3406,14 +3423,14 @@ const ACTIVATE_SCHEDULED_SIGNAL_FN = async (self, scheduled, activationTimestamp
|
|
|
3406
3423
|
await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, activationTime, self.params.execution.context.backtest);
|
|
3407
3424
|
return result;
|
|
3408
3425
|
};
|
|
3409
|
-
const
|
|
3426
|
+
const CALL_SCHEDULE_PING_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, scheduled, timestamp, backtest) => {
|
|
3410
3427
|
await ExecutionContextService.runInContext(async () => {
|
|
3411
3428
|
const publicSignal = TO_PUBLIC_SIGNAL(scheduled);
|
|
3412
|
-
// Call system
|
|
3413
|
-
await self.params.
|
|
3414
|
-
// Call user
|
|
3415
|
-
if (self.params.callbacks?.
|
|
3416
|
-
await self.params.callbacks.
|
|
3429
|
+
// Call system onSchedulePing callback first (emits to pingSubject)
|
|
3430
|
+
await self.params.onSchedulePing(self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, publicSignal, self.params.execution.context.backtest, timestamp);
|
|
3431
|
+
// Call user onSchedulePing callback only if signal is still active (not cancelled, not activated)
|
|
3432
|
+
if (self.params.callbacks?.onSchedulePing) {
|
|
3433
|
+
await self.params.callbacks.onSchedulePing(self.params.execution.context.symbol, publicSignal, new Date(timestamp), self.params.execution.context.backtest);
|
|
3417
3434
|
}
|
|
3418
3435
|
}, {
|
|
3419
3436
|
when: new Date(timestamp),
|
|
@@ -3422,7 +3439,33 @@ const CALL_PING_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symb
|
|
|
3422
3439
|
});
|
|
3423
3440
|
}), {
|
|
3424
3441
|
fallback: (error) => {
|
|
3425
|
-
const message = "ClientStrategy
|
|
3442
|
+
const message = "ClientStrategy CALL_SCHEDULE_PING_CALLBACKS_FN thrown";
|
|
3443
|
+
const payload = {
|
|
3444
|
+
error: functoolsKit.errorData(error),
|
|
3445
|
+
message: functoolsKit.getErrorMessage(error),
|
|
3446
|
+
};
|
|
3447
|
+
backtest$1.loggerService.warn(message, payload);
|
|
3448
|
+
console.warn(message, payload);
|
|
3449
|
+
errorEmitter.next(error);
|
|
3450
|
+
},
|
|
3451
|
+
});
|
|
3452
|
+
const CALL_ACTIVE_PING_CALLBACKS_FN = functoolsKit.trycatch(beginTime(async (self, symbol, pending, timestamp, backtest) => {
|
|
3453
|
+
await ExecutionContextService.runInContext(async () => {
|
|
3454
|
+
const publicSignal = TO_PUBLIC_SIGNAL(pending);
|
|
3455
|
+
// Call system onActivePing callback first (emits to activePingSubject)
|
|
3456
|
+
await self.params.onActivePing(self.params.execution.context.symbol, self.params.method.context.strategyName, self.params.method.context.exchangeName, publicSignal, self.params.execution.context.backtest, timestamp);
|
|
3457
|
+
// Call user onActivePing callback only if signal is still active (not closed)
|
|
3458
|
+
if (self.params.callbacks?.onActivePing) {
|
|
3459
|
+
await self.params.callbacks.onActivePing(self.params.execution.context.symbol, publicSignal, new Date(timestamp), self.params.execution.context.backtest);
|
|
3460
|
+
}
|
|
3461
|
+
}, {
|
|
3462
|
+
when: new Date(timestamp),
|
|
3463
|
+
symbol: symbol,
|
|
3464
|
+
backtest: backtest,
|
|
3465
|
+
});
|
|
3466
|
+
}), {
|
|
3467
|
+
fallback: (error) => {
|
|
3468
|
+
const message = "ClientStrategy CALL_ACTIVE_PING_CALLBACKS_FN thrown";
|
|
3426
3469
|
const payload = {
|
|
3427
3470
|
error: functoolsKit.errorData(error),
|
|
3428
3471
|
message: functoolsKit.getErrorMessage(error),
|
|
@@ -3794,10 +3837,10 @@ const CALL_BREAKEVEN_CLEAR_FN = functoolsKit.trycatch(beginTime(async (self, sym
|
|
|
3794
3837
|
});
|
|
3795
3838
|
const RETURN_SCHEDULED_SIGNAL_ACTIVE_FN = async (self, scheduled, currentPrice) => {
|
|
3796
3839
|
const currentTime = self.params.execution.context.when.getTime();
|
|
3797
|
-
await
|
|
3840
|
+
await CALL_SCHEDULE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, currentTime, self.params.execution.context.backtest);
|
|
3798
3841
|
const pnl = toProfitLossDto(scheduled, currentPrice);
|
|
3799
3842
|
const result = {
|
|
3800
|
-
action: "
|
|
3843
|
+
action: "waiting",
|
|
3801
3844
|
signal: TO_PUBLIC_SIGNAL(scheduled),
|
|
3802
3845
|
currentPrice: currentPrice,
|
|
3803
3846
|
strategyName: self.params.method.context.strategyName,
|
|
@@ -3924,6 +3967,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
3924
3967
|
let percentTp = 0;
|
|
3925
3968
|
let percentSl = 0;
|
|
3926
3969
|
const currentTime = self.params.execution.context.when.getTime();
|
|
3970
|
+
await CALL_ACTIVE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentTime, self.params.execution.context.backtest);
|
|
3927
3971
|
// Calculate percentage of path to TP/SL for partial fill/loss callbacks
|
|
3928
3972
|
{
|
|
3929
3973
|
if (signal.position === "long") {
|
|
@@ -4129,7 +4173,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
4129
4173
|
const averagePrice = GET_AVG_PRICE_FN(recentCandles);
|
|
4130
4174
|
// КРИТИЧНО: Проверяем был ли сигнал отменен пользователем через cancel()
|
|
4131
4175
|
if (self._cancelledSignal) {
|
|
4132
|
-
// Сигнал был отменен через cancel() в
|
|
4176
|
+
// Сигнал был отменен через cancel() в onSchedulePing
|
|
4133
4177
|
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user");
|
|
4134
4178
|
return { activated: false, cancelled: true, activationIndex: i, result };
|
|
4135
4179
|
}
|
|
@@ -4186,7 +4230,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
4186
4230
|
result: null,
|
|
4187
4231
|
};
|
|
4188
4232
|
}
|
|
4189
|
-
await
|
|
4233
|
+
await CALL_SCHEDULE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, candle.timestamp, true);
|
|
4190
4234
|
}
|
|
4191
4235
|
return {
|
|
4192
4236
|
activated: false,
|
|
@@ -4212,6 +4256,7 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
4212
4256
|
const startIndex = Math.max(0, i - (candlesCount - 1));
|
|
4213
4257
|
const recentCandles = candles.slice(startIndex, i + 1);
|
|
4214
4258
|
const averagePrice = GET_AVG_PRICE_FN(recentCandles);
|
|
4259
|
+
await CALL_ACTIVE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentCandleTimestamp, true);
|
|
4215
4260
|
let shouldClose = false;
|
|
4216
4261
|
let closeReason;
|
|
4217
4262
|
// Check time expiration FIRST (КРИТИЧНО!)
|
|
@@ -4757,7 +4802,15 @@ class ClientStrategy {
|
|
|
4757
4802
|
return result;
|
|
4758
4803
|
}
|
|
4759
4804
|
if (activated) {
|
|
4760
|
-
|
|
4805
|
+
// КРИТИЧНО: activationIndex - индекс свечи активации в массиве candles
|
|
4806
|
+
// BacktestLogicPrivateService включил буфер в начало массива, поэтому перед activationIndex достаточно свечей
|
|
4807
|
+
// PROCESS_PENDING_SIGNAL_CANDLES_FN пропустит первые bufferCandlesCount свечей для VWAP
|
|
4808
|
+
// Чтобы обработка началась со СЛЕДУЮЩЕЙ свечи после активации (activationIndex + 1),
|
|
4809
|
+
// нужно взять срез начиная с (activationIndex + 1 - bufferCandlesCount)
|
|
4810
|
+
// Это даст буфер ИЗ scheduled фазы + свеча после активации как первая обрабатываемая
|
|
4811
|
+
const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
|
|
4812
|
+
const sliceStart = Math.max(0, activationIndex + 1 - bufferCandlesCount);
|
|
4813
|
+
const remainingCandles = candles.slice(sliceStart);
|
|
4761
4814
|
if (remainingCandles.length === 0) {
|
|
4762
4815
|
const candlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT;
|
|
4763
4816
|
const recentCandles = candles.slice(Math.max(0, activationIndex - (candlesCount - 1)), activationIndex + 1);
|
|
@@ -4773,35 +4826,23 @@ class ClientStrategy {
|
|
|
4773
4826
|
const lastCandleTimestamp = candles[candles.length - 1].timestamp;
|
|
4774
4827
|
const elapsedTime = lastCandleTimestamp - scheduled.scheduledAt;
|
|
4775
4828
|
if (elapsedTime < maxTimeToWait) {
|
|
4776
|
-
//
|
|
4777
|
-
//
|
|
4778
|
-
|
|
4779
|
-
const
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
signal: TO_PUBLIC_SIGNAL(scheduled),
|
|
4794
|
-
currentPrice: lastPrice,
|
|
4795
|
-
percentSl: 0,
|
|
4796
|
-
percentTp: 0,
|
|
4797
|
-
pnl,
|
|
4798
|
-
strategyName: this.params.method.context.strategyName,
|
|
4799
|
-
exchangeName: this.params.method.context.exchangeName,
|
|
4800
|
-
frameName: this.params.method.context.frameName,
|
|
4801
|
-
symbol: this.params.execution.context.symbol,
|
|
4802
|
-
backtest: this.params.execution.context.backtest,
|
|
4803
|
-
};
|
|
4804
|
-
return result; // Cast to IStrategyBacktestResult (which includes Active)
|
|
4829
|
+
// EDGE CASE: backtest() called with partial candle data (should never happen in production)
|
|
4830
|
+
// In real backtest flow this won't happen as we process all candles at once
|
|
4831
|
+
// This indicates incorrect usage of backtest() - throw error instead of returning partial result
|
|
4832
|
+
const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
|
|
4833
|
+
// For scheduled signal that has NOT activated: buffer + wait time only (no lifetime yet)
|
|
4834
|
+
const requiredCandlesCount = bufferCandlesCount + GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES + 1;
|
|
4835
|
+
throw new Error(functoolsKit.str.newline(`ClientStrategy backtest: Insufficient candle data for scheduled signal (not yet activated). ` +
|
|
4836
|
+
`Signal scheduled at ${new Date(scheduled.scheduledAt).toISOString()}, ` +
|
|
4837
|
+
`last candle at ${new Date(lastCandleTimestamp).toISOString()}. ` +
|
|
4838
|
+
`Elapsed: ${Math.floor(elapsedTime / 60000)}min of ${GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES}min wait time. ` +
|
|
4839
|
+
`Provided ${candles.length} candles, but need at least ${requiredCandlesCount} candles. ` +
|
|
4840
|
+
`\nBreakdown: ` +
|
|
4841
|
+
`${bufferCandlesCount} buffer (VWAP) + ` +
|
|
4842
|
+
`${GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES} wait (for activation) = ${requiredCandlesCount} total. ` +
|
|
4843
|
+
`\nBuffer explanation: VWAP calculation requires ${GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT} candles, ` +
|
|
4844
|
+
`so first ${bufferCandlesCount} candles are skipped during scheduled phase processing. ` +
|
|
4845
|
+
`Provide complete candle range: [scheduledAt - ${bufferCandlesCount}min, scheduledAt + ${GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES}min].`));
|
|
4805
4846
|
}
|
|
4806
4847
|
// Timeout reached - cancel the scheduled signal
|
|
4807
4848
|
const candlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT;
|
|
@@ -4831,9 +4872,29 @@ class ClientStrategy {
|
|
|
4831
4872
|
if (closedResult) {
|
|
4832
4873
|
return closedResult;
|
|
4833
4874
|
}
|
|
4875
|
+
// Signal didn't close during candle processing - check if we have enough data
|
|
4834
4876
|
const lastCandles = candles.slice(-GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT);
|
|
4835
4877
|
const lastPrice = GET_AVG_PRICE_FN(lastCandles);
|
|
4836
4878
|
const closeTimestamp = lastCandles[lastCandles.length - 1].timestamp;
|
|
4879
|
+
const signalTime = signal.pendingAt;
|
|
4880
|
+
const maxTimeToWait = signal.minuteEstimatedTime * 60 * 1000;
|
|
4881
|
+
const elapsedTime = closeTimestamp - signalTime;
|
|
4882
|
+
// Check if we actually reached time expiration or just ran out of candles
|
|
4883
|
+
if (elapsedTime < maxTimeToWait) {
|
|
4884
|
+
// EDGE CASE: backtest() called with insufficient candle data
|
|
4885
|
+
const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
|
|
4886
|
+
const requiredCandlesCount = signal.minuteEstimatedTime + bufferCandlesCount + 1;
|
|
4887
|
+
throw new Error(functoolsKit.str.newline(`ClientStrategy backtest: Insufficient candle data for pending signal. ` +
|
|
4888
|
+
`Signal opened at ${new Date(signal.pendingAt).toISOString()}, ` +
|
|
4889
|
+
`last candle at ${new Date(closeTimestamp).toISOString()}. ` +
|
|
4890
|
+
`Elapsed: ${Math.floor(elapsedTime / 60000)}min of ${signal.minuteEstimatedTime}min required. ` +
|
|
4891
|
+
`Provided ${candles.length} candles, but need at least ${requiredCandlesCount} candles. ` +
|
|
4892
|
+
`\nBreakdown: ${signal.minuteEstimatedTime} candles for signal lifetime + ${bufferCandlesCount} buffer candles. ` +
|
|
4893
|
+
`\nBuffer explanation: VWAP calculation requires ${GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT} candles, ` +
|
|
4894
|
+
`so first ${bufferCandlesCount} candles are skipped to ensure accurate price averaging. ` +
|
|
4895
|
+
`Provide complete candle range: [pendingAt - ${bufferCandlesCount}min, pendingAt + ${signal.minuteEstimatedTime}min].`));
|
|
4896
|
+
}
|
|
4897
|
+
// Time actually expired - close with time_expired
|
|
4837
4898
|
return await CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN(this, signal, lastPrice, "time_expired", closeTimestamp);
|
|
4838
4899
|
}
|
|
4839
4900
|
/**
|
|
@@ -5622,6 +5683,9 @@ class ClientStrategy {
|
|
|
5622
5683
|
const RISK_METHOD_NAME_GET_DATA = "RiskUtils.getData";
|
|
5623
5684
|
const RISK_METHOD_NAME_GET_REPORT = "RiskUtils.getReport";
|
|
5624
5685
|
const RISK_METHOD_NAME_DUMP = "RiskUtils.dump";
|
|
5686
|
+
const RISK_METHOD_NAME_CHECK_SIGNAL = "MergeRisk.checkSignal";
|
|
5687
|
+
const RISK_METHOD_NAME_ADD_SIGNAL = "MergeRisk.addSignal";
|
|
5688
|
+
const RISK_METHOD_NAME_REMOVE_SIGNAL = "MergeRisk.removeSignal";
|
|
5625
5689
|
/**
|
|
5626
5690
|
* Composite risk management class that combines multiple risk profiles.
|
|
5627
5691
|
*
|
|
@@ -5677,7 +5741,7 @@ class MergeRisk {
|
|
|
5677
5741
|
* @returns Promise resolving to true if all risks approve, false if any risk rejects
|
|
5678
5742
|
*/
|
|
5679
5743
|
async checkSignal(params) {
|
|
5680
|
-
backtest$1.loggerService.info(
|
|
5744
|
+
backtest$1.loggerService.info(RISK_METHOD_NAME_CHECK_SIGNAL, {
|
|
5681
5745
|
params,
|
|
5682
5746
|
});
|
|
5683
5747
|
for (const [riskName, risk] of Object.entries(this._riskMap)) {
|
|
@@ -5701,7 +5765,7 @@ class MergeRisk {
|
|
|
5701
5765
|
* @returns Promise that resolves when all risks have registered the signal
|
|
5702
5766
|
*/
|
|
5703
5767
|
async addSignal(symbol, context, positionData) {
|
|
5704
|
-
backtest$1.loggerService.info(
|
|
5768
|
+
backtest$1.loggerService.info(RISK_METHOD_NAME_ADD_SIGNAL, {
|
|
5705
5769
|
symbol,
|
|
5706
5770
|
context,
|
|
5707
5771
|
});
|
|
@@ -5718,7 +5782,7 @@ class MergeRisk {
|
|
|
5718
5782
|
* @returns Promise that resolves when all risks have removed the signal
|
|
5719
5783
|
*/
|
|
5720
5784
|
async removeSignal(symbol, context) {
|
|
5721
|
-
backtest$1.loggerService.info(
|
|
5785
|
+
backtest$1.loggerService.info(RISK_METHOD_NAME_REMOVE_SIGNAL, {
|
|
5722
5786
|
symbol,
|
|
5723
5787
|
context,
|
|
5724
5788
|
});
|
|
@@ -6001,15 +6065,15 @@ const CREATE_KEY_FN$k = (symbol, strategyName, exchangeName, frameName, backtest
|
|
|
6001
6065
|
return parts.join(":");
|
|
6002
6066
|
};
|
|
6003
6067
|
/**
|
|
6004
|
-
* Creates a callback function for emitting ping events to pingSubject.
|
|
6068
|
+
* Creates a callback function for emitting schedule ping events to pingSubject.
|
|
6005
6069
|
*
|
|
6006
6070
|
* Called by ClientStrategy when a scheduled signal is being monitored every minute.
|
|
6007
6071
|
* Emits PingContract event to all subscribers and calls ActionCoreService.
|
|
6008
6072
|
*
|
|
6009
6073
|
* @param self - Reference to StrategyConnectionService instance
|
|
6010
|
-
* @returns Callback function for ping events
|
|
6074
|
+
* @returns Callback function for schedule ping events
|
|
6011
6075
|
*/
|
|
6012
|
-
const
|
|
6076
|
+
const CREATE_COMMIT_SCHEDULE_PING_FN = (self) => async (symbol, strategyName, exchangeName, data, backtest, timestamp) => {
|
|
6013
6077
|
const event = {
|
|
6014
6078
|
symbol,
|
|
6015
6079
|
strategyName,
|
|
@@ -6018,8 +6082,29 @@ const CREATE_COMMIT_PING_FN = (self) => async (symbol, strategyName, exchangeNam
|
|
|
6018
6082
|
backtest,
|
|
6019
6083
|
timestamp,
|
|
6020
6084
|
};
|
|
6021
|
-
await
|
|
6022
|
-
await self.actionCoreService.
|
|
6085
|
+
await schedulePingSubject.next(event);
|
|
6086
|
+
await self.actionCoreService.pingScheduled(backtest, event, { strategyName, exchangeName, frameName: data.frameName });
|
|
6087
|
+
};
|
|
6088
|
+
/**
|
|
6089
|
+
* Creates a callback function for emitting active ping events.
|
|
6090
|
+
*
|
|
6091
|
+
* Called by ClientStrategy when an active pending signal is being monitored every minute.
|
|
6092
|
+
* Placeholder for future activePingSubject implementation.
|
|
6093
|
+
*
|
|
6094
|
+
* @param self - Reference to StrategyConnectionService instance
|
|
6095
|
+
* @returns Callback function for active ping events
|
|
6096
|
+
*/
|
|
6097
|
+
const CREATE_COMMIT_ACTIVE_PING_FN = (self) => async (symbol, strategyName, exchangeName, data, backtest, timestamp) => {
|
|
6098
|
+
const event = {
|
|
6099
|
+
symbol,
|
|
6100
|
+
strategyName,
|
|
6101
|
+
exchangeName,
|
|
6102
|
+
data,
|
|
6103
|
+
backtest,
|
|
6104
|
+
timestamp,
|
|
6105
|
+
};
|
|
6106
|
+
await activePingSubject.next(event);
|
|
6107
|
+
await self.actionCoreService.pingActive(backtest, event, { strategyName, exchangeName, frameName: data.frameName });
|
|
6023
6108
|
};
|
|
6024
6109
|
/**
|
|
6025
6110
|
* Creates a callback function for emitting init events.
|
|
@@ -6116,7 +6201,8 @@ class StrategyConnectionService {
|
|
|
6116
6201
|
getSignal,
|
|
6117
6202
|
callbacks,
|
|
6118
6203
|
onInit: CREATE_COMMIT_INIT_FN(this),
|
|
6119
|
-
|
|
6204
|
+
onSchedulePing: CREATE_COMMIT_SCHEDULE_PING_FN(this),
|
|
6205
|
+
onActivePing: CREATE_COMMIT_ACTIVE_PING_FN(this),
|
|
6120
6206
|
onDispose: CREATE_COMMIT_DISPOSE_FN(this),
|
|
6121
6207
|
});
|
|
6122
6208
|
});
|
|
@@ -7509,8 +7595,8 @@ const CALL_SIGNAL_BACKTEST_CALLBACK_FN = functoolsKit.trycatch(async (self, even
|
|
|
7509
7595
|
});
|
|
7510
7596
|
/** Wrapper to call breakeven callback with error handling */
|
|
7511
7597
|
const CALL_BREAKEVEN_CALLBACK_FN = functoolsKit.trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
7512
|
-
if (self.params.callbacks?.
|
|
7513
|
-
await self.params.callbacks.
|
|
7598
|
+
if (self.params.callbacks?.onBreakevenAvailable) {
|
|
7599
|
+
await self.params.callbacks.onBreakevenAvailable(event, self.params.actionName, strategyName, frameName, backtest);
|
|
7514
7600
|
}
|
|
7515
7601
|
}, {
|
|
7516
7602
|
fallback: (error) => {
|
|
@@ -7526,8 +7612,8 @@ const CALL_BREAKEVEN_CALLBACK_FN = functoolsKit.trycatch(async (self, event, str
|
|
|
7526
7612
|
});
|
|
7527
7613
|
/** Wrapper to call partialProfit callback with error handling */
|
|
7528
7614
|
const CALL_PARTIAL_PROFIT_CALLBACK_FN = functoolsKit.trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
7529
|
-
if (self.params.callbacks?.
|
|
7530
|
-
await self.params.callbacks.
|
|
7615
|
+
if (self.params.callbacks?.onPartialProfitAvailable) {
|
|
7616
|
+
await self.params.callbacks.onPartialProfitAvailable(event, self.params.actionName, strategyName, frameName, backtest);
|
|
7531
7617
|
}
|
|
7532
7618
|
}, {
|
|
7533
7619
|
fallback: (error) => {
|
|
@@ -7543,8 +7629,8 @@ const CALL_PARTIAL_PROFIT_CALLBACK_FN = functoolsKit.trycatch(async (self, event
|
|
|
7543
7629
|
});
|
|
7544
7630
|
/** Wrapper to call partialLoss callback with error handling */
|
|
7545
7631
|
const CALL_PARTIAL_LOSS_CALLBACK_FN = functoolsKit.trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
7546
|
-
if (self.params.callbacks?.
|
|
7547
|
-
await self.params.callbacks.
|
|
7632
|
+
if (self.params.callbacks?.onPartialLossAvailable) {
|
|
7633
|
+
await self.params.callbacks.onPartialLossAvailable(event, self.params.actionName, strategyName, frameName, backtest);
|
|
7548
7634
|
}
|
|
7549
7635
|
}, {
|
|
7550
7636
|
fallback: (error) => {
|
|
@@ -7558,14 +7644,31 @@ const CALL_PARTIAL_LOSS_CALLBACK_FN = functoolsKit.trycatch(async (self, event,
|
|
|
7558
7644
|
errorEmitter.next(error);
|
|
7559
7645
|
},
|
|
7560
7646
|
});
|
|
7561
|
-
/** Wrapper to call ping callback with error handling */
|
|
7562
|
-
const
|
|
7563
|
-
if (self.params.callbacks?.
|
|
7564
|
-
await self.params.callbacks.
|
|
7647
|
+
/** Wrapper to call scheduled ping callback with error handling */
|
|
7648
|
+
const CALL_PING_SCHEDULED_CALLBACK_FN = functoolsKit.trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
7649
|
+
if (self.params.callbacks?.onPingScheduled) {
|
|
7650
|
+
await self.params.callbacks.onPingScheduled(event, self.params.actionName, strategyName, frameName, backtest);
|
|
7565
7651
|
}
|
|
7566
7652
|
}, {
|
|
7567
7653
|
fallback: (error) => {
|
|
7568
|
-
const message = "ClientAction
|
|
7654
|
+
const message = "ClientAction CALL_PING_SCHEDULED_CALLBACK_FN thrown";
|
|
7655
|
+
const payload = {
|
|
7656
|
+
error: functoolsKit.errorData(error),
|
|
7657
|
+
message: functoolsKit.getErrorMessage(error),
|
|
7658
|
+
};
|
|
7659
|
+
backtest$1.loggerService.warn(message, payload);
|
|
7660
|
+
console.warn(message, payload);
|
|
7661
|
+
errorEmitter.next(error);
|
|
7662
|
+
},
|
|
7663
|
+
});
|
|
7664
|
+
/** Wrapper to call active ping callback with error handling */
|
|
7665
|
+
const CALL_PING_ACTIVE_CALLBACK_FN = functoolsKit.trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
7666
|
+
if (self.params.callbacks?.onPingActive) {
|
|
7667
|
+
await self.params.callbacks.onPingActive(event, self.params.actionName, strategyName, frameName, backtest);
|
|
7668
|
+
}
|
|
7669
|
+
}, {
|
|
7670
|
+
fallback: (error) => {
|
|
7671
|
+
const message = "ClientAction CALL_PING_ACTIVE_CALLBACK_FN thrown";
|
|
7569
7672
|
const payload = {
|
|
7570
7673
|
error: functoolsKit.errorData(error),
|
|
7571
7674
|
message: functoolsKit.getErrorMessage(error),
|
|
@@ -7640,6 +7743,7 @@ const CREATE_HANDLER_FN = (self) => {
|
|
|
7640
7743
|
self.params.strategyName,
|
|
7641
7744
|
self.params.frameName,
|
|
7642
7745
|
self.params.actionName,
|
|
7746
|
+
self.params.backtest,
|
|
7643
7747
|
]);
|
|
7644
7748
|
}
|
|
7645
7749
|
return self.params.handler;
|
|
@@ -7831,8 +7935,8 @@ class ClientAction {
|
|
|
7831
7935
|
/**
|
|
7832
7936
|
* Handles breakeven events when stop-loss is moved to entry price.
|
|
7833
7937
|
*/
|
|
7834
|
-
async
|
|
7835
|
-
this.params.logger.debug("ClientAction
|
|
7938
|
+
async breakevenAvailable(event) {
|
|
7939
|
+
this.params.logger.debug("ClientAction breakevenAvailable", {
|
|
7836
7940
|
actionName: this.params.actionName,
|
|
7837
7941
|
strategyName: this.params.strategyName,
|
|
7838
7942
|
frameName: this.params.frameName,
|
|
@@ -7841,8 +7945,8 @@ class ClientAction {
|
|
|
7841
7945
|
await this.waitForInit();
|
|
7842
7946
|
}
|
|
7843
7947
|
// Call handler method if defined
|
|
7844
|
-
if (this._handlerInstance?.
|
|
7845
|
-
await this._handlerInstance.
|
|
7948
|
+
if (this._handlerInstance?.breakevenAvailable) {
|
|
7949
|
+
await this._handlerInstance.breakevenAvailable(event);
|
|
7846
7950
|
}
|
|
7847
7951
|
// Call callback if defined
|
|
7848
7952
|
await CALL_BREAKEVEN_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
@@ -7851,8 +7955,8 @@ class ClientAction {
|
|
|
7851
7955
|
/**
|
|
7852
7956
|
* Handles partial profit level events (10%, 20%, 30%, etc).
|
|
7853
7957
|
*/
|
|
7854
|
-
async
|
|
7855
|
-
this.params.logger.debug("ClientAction
|
|
7958
|
+
async partialProfitAvailable(event) {
|
|
7959
|
+
this.params.logger.debug("ClientAction partialProfitAvailable", {
|
|
7856
7960
|
actionName: this.params.actionName,
|
|
7857
7961
|
strategyName: this.params.strategyName,
|
|
7858
7962
|
frameName: this.params.frameName,
|
|
@@ -7861,8 +7965,8 @@ class ClientAction {
|
|
|
7861
7965
|
await this.waitForInit();
|
|
7862
7966
|
}
|
|
7863
7967
|
// Call handler method if defined
|
|
7864
|
-
if (this._handlerInstance?.
|
|
7865
|
-
await this._handlerInstance.
|
|
7968
|
+
if (this._handlerInstance?.partialProfitAvailable) {
|
|
7969
|
+
await this._handlerInstance.partialProfitAvailable(event);
|
|
7866
7970
|
}
|
|
7867
7971
|
// Call callback if defined
|
|
7868
7972
|
await CALL_PARTIAL_PROFIT_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
@@ -7871,8 +7975,8 @@ class ClientAction {
|
|
|
7871
7975
|
/**
|
|
7872
7976
|
* Handles partial loss level events (-10%, -20%, -30%, etc).
|
|
7873
7977
|
*/
|
|
7874
|
-
async
|
|
7875
|
-
this.params.logger.debug("ClientAction
|
|
7978
|
+
async partialLossAvailable(event) {
|
|
7979
|
+
this.params.logger.debug("ClientAction partialLossAvailable", {
|
|
7876
7980
|
actionName: this.params.actionName,
|
|
7877
7981
|
strategyName: this.params.strategyName,
|
|
7878
7982
|
frameName: this.params.frameName,
|
|
@@ -7881,18 +7985,18 @@ class ClientAction {
|
|
|
7881
7985
|
await this.waitForInit();
|
|
7882
7986
|
}
|
|
7883
7987
|
// Call handler method if defined
|
|
7884
|
-
if (this._handlerInstance?.
|
|
7885
|
-
await this._handlerInstance.
|
|
7988
|
+
if (this._handlerInstance?.partialLossAvailable) {
|
|
7989
|
+
await this._handlerInstance.partialLossAvailable(event);
|
|
7886
7990
|
}
|
|
7887
7991
|
// Call callback if defined
|
|
7888
7992
|
await CALL_PARTIAL_LOSS_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
7889
7993
|
}
|
|
7890
7994
|
;
|
|
7891
7995
|
/**
|
|
7892
|
-
* Handles ping events during scheduled signal monitoring.
|
|
7996
|
+
* Handles scheduled ping events during scheduled signal monitoring.
|
|
7893
7997
|
*/
|
|
7894
|
-
async
|
|
7895
|
-
this.params.logger.debug("ClientAction
|
|
7998
|
+
async pingScheduled(event) {
|
|
7999
|
+
this.params.logger.debug("ClientAction pingScheduled", {
|
|
7896
8000
|
actionName: this.params.actionName,
|
|
7897
8001
|
strategyName: this.params.strategyName,
|
|
7898
8002
|
frameName: this.params.frameName,
|
|
@@ -7901,11 +8005,31 @@ class ClientAction {
|
|
|
7901
8005
|
await this.waitForInit();
|
|
7902
8006
|
}
|
|
7903
8007
|
// Call handler method if defined
|
|
7904
|
-
if (this._handlerInstance?.
|
|
7905
|
-
await this._handlerInstance.
|
|
8008
|
+
if (this._handlerInstance?.pingScheduled) {
|
|
8009
|
+
await this._handlerInstance.pingScheduled(event);
|
|
7906
8010
|
}
|
|
7907
8011
|
// Call callback if defined
|
|
7908
|
-
await
|
|
8012
|
+
await CALL_PING_SCHEDULED_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
8013
|
+
}
|
|
8014
|
+
;
|
|
8015
|
+
/**
|
|
8016
|
+
* Handles active ping events during active pending signal monitoring.
|
|
8017
|
+
*/
|
|
8018
|
+
async pingActive(event) {
|
|
8019
|
+
this.params.logger.debug("ClientAction pingActive", {
|
|
8020
|
+
actionName: this.params.actionName,
|
|
8021
|
+
strategyName: this.params.strategyName,
|
|
8022
|
+
frameName: this.params.frameName,
|
|
8023
|
+
});
|
|
8024
|
+
if (!this._handlerInstance) {
|
|
8025
|
+
await this.waitForInit();
|
|
8026
|
+
}
|
|
8027
|
+
// Call handler method if defined
|
|
8028
|
+
if (this._handlerInstance?.pingActive) {
|
|
8029
|
+
await this._handlerInstance.pingActive(event);
|
|
8030
|
+
}
|
|
8031
|
+
// Call callback if defined
|
|
8032
|
+
await CALL_PING_ACTIVE_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
7909
8033
|
}
|
|
7910
8034
|
;
|
|
7911
8035
|
/**
|
|
@@ -8073,13 +8197,13 @@ class ActionConnectionService {
|
|
|
8073
8197
|
* @param backtest - Whether running in backtest mode
|
|
8074
8198
|
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
8075
8199
|
*/
|
|
8076
|
-
this.
|
|
8077
|
-
this.loggerService.log("actionConnectionService
|
|
8200
|
+
this.breakevenAvailable = async (event, backtest, context) => {
|
|
8201
|
+
this.loggerService.log("actionConnectionService breakevenAvailable", {
|
|
8078
8202
|
backtest,
|
|
8079
8203
|
context,
|
|
8080
8204
|
});
|
|
8081
8205
|
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8082
|
-
await action.
|
|
8206
|
+
await action.breakevenAvailable(event);
|
|
8083
8207
|
};
|
|
8084
8208
|
/**
|
|
8085
8209
|
* Routes partialProfit event to appropriate ClientAction instance.
|
|
@@ -8088,13 +8212,13 @@ class ActionConnectionService {
|
|
|
8088
8212
|
* @param backtest - Whether running in backtest mode
|
|
8089
8213
|
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
8090
8214
|
*/
|
|
8091
|
-
this.
|
|
8092
|
-
this.loggerService.log("actionConnectionService
|
|
8215
|
+
this.partialProfitAvailable = async (event, backtest, context) => {
|
|
8216
|
+
this.loggerService.log("actionConnectionService partialProfitAvailable", {
|
|
8093
8217
|
backtest,
|
|
8094
8218
|
context,
|
|
8095
8219
|
});
|
|
8096
8220
|
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8097
|
-
await action.
|
|
8221
|
+
await action.partialProfitAvailable(event);
|
|
8098
8222
|
};
|
|
8099
8223
|
/**
|
|
8100
8224
|
* Routes partialLoss event to appropriate ClientAction instance.
|
|
@@ -8103,28 +8227,43 @@ class ActionConnectionService {
|
|
|
8103
8227
|
* @param backtest - Whether running in backtest mode
|
|
8104
8228
|
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
8105
8229
|
*/
|
|
8106
|
-
this.
|
|
8107
|
-
this.loggerService.log("actionConnectionService
|
|
8230
|
+
this.partialLossAvailable = async (event, backtest, context) => {
|
|
8231
|
+
this.loggerService.log("actionConnectionService partialLossAvailable", {
|
|
8108
8232
|
backtest,
|
|
8109
8233
|
context,
|
|
8110
8234
|
});
|
|
8111
8235
|
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8112
|
-
await action.
|
|
8236
|
+
await action.partialLossAvailable(event);
|
|
8113
8237
|
};
|
|
8114
8238
|
/**
|
|
8115
|
-
* Routes ping event to appropriate ClientAction instance.
|
|
8239
|
+
* Routes scheduled ping event to appropriate ClientAction instance.
|
|
8116
8240
|
*
|
|
8117
|
-
* @param event -
|
|
8241
|
+
* @param event - Scheduled ping event data
|
|
8118
8242
|
* @param backtest - Whether running in backtest mode
|
|
8119
8243
|
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
8120
8244
|
*/
|
|
8121
|
-
this.
|
|
8122
|
-
this.loggerService.log("actionConnectionService
|
|
8245
|
+
this.pingScheduled = async (event, backtest, context) => {
|
|
8246
|
+
this.loggerService.log("actionConnectionService pingScheduled", {
|
|
8123
8247
|
backtest,
|
|
8124
8248
|
context,
|
|
8125
8249
|
});
|
|
8126
8250
|
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8127
|
-
await action.
|
|
8251
|
+
await action.pingScheduled(event);
|
|
8252
|
+
};
|
|
8253
|
+
/**
|
|
8254
|
+
* Routes active ping event to appropriate ClientAction instance.
|
|
8255
|
+
*
|
|
8256
|
+
* @param event - Active ping event data
|
|
8257
|
+
* @param backtest - Whether running in backtest mode
|
|
8258
|
+
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
8259
|
+
*/
|
|
8260
|
+
this.pingActive = async (event, backtest, context) => {
|
|
8261
|
+
this.loggerService.log("actionConnectionService pingActive", {
|
|
8262
|
+
backtest,
|
|
8263
|
+
context,
|
|
8264
|
+
});
|
|
8265
|
+
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8266
|
+
await action.pingActive(event);
|
|
8128
8267
|
};
|
|
8129
8268
|
/**
|
|
8130
8269
|
* Routes riskRejection event to appropriate ClientAction instance.
|
|
@@ -9188,81 +9327,102 @@ class ActionCoreService {
|
|
|
9188
9327
|
* Routes breakeven event to all registered actions for the strategy.
|
|
9189
9328
|
*
|
|
9190
9329
|
* Retrieves action list from strategy schema (IStrategySchema.actions)
|
|
9191
|
-
* and invokes the
|
|
9330
|
+
* and invokes the breakevenAvailable handler on each ClientAction instance sequentially.
|
|
9192
9331
|
*
|
|
9193
9332
|
* @param backtest - Whether running in backtest mode (true) or live mode (false)
|
|
9194
9333
|
* @param event - Breakeven milestone data (stop-loss moved to entry price)
|
|
9195
9334
|
* @param context - Strategy execution context with strategyName, exchangeName, frameName
|
|
9196
9335
|
*/
|
|
9197
|
-
this.
|
|
9198
|
-
this.loggerService.log("actionCoreService
|
|
9336
|
+
this.breakevenAvailable = async (backtest, event, context) => {
|
|
9337
|
+
this.loggerService.log("actionCoreService breakevenAvailable", {
|
|
9199
9338
|
context,
|
|
9200
9339
|
});
|
|
9201
9340
|
await this.validate(context);
|
|
9202
9341
|
const { actions = [] } = this.strategySchemaService.get(context.strategyName);
|
|
9203
9342
|
for (const actionName of actions) {
|
|
9204
|
-
await this.actionConnectionService.
|
|
9343
|
+
await this.actionConnectionService.breakevenAvailable(event, backtest, { actionName, ...context });
|
|
9205
9344
|
}
|
|
9206
9345
|
};
|
|
9207
9346
|
/**
|
|
9208
9347
|
* Routes partial profit event to all registered actions for the strategy.
|
|
9209
9348
|
*
|
|
9210
9349
|
* Retrieves action list from strategy schema (IStrategySchema.actions)
|
|
9211
|
-
* and invokes the
|
|
9350
|
+
* and invokes the partialProfitAvailable handler on each ClientAction instance sequentially.
|
|
9212
9351
|
*
|
|
9213
9352
|
* @param backtest - Whether running in backtest mode (true) or live mode (false)
|
|
9214
9353
|
* @param event - Profit milestone data with level (10%, 20%, etc.) and price
|
|
9215
9354
|
* @param context - Strategy execution context with strategyName, exchangeName, frameName
|
|
9216
9355
|
*/
|
|
9217
|
-
this.
|
|
9218
|
-
this.loggerService.log("actionCoreService
|
|
9356
|
+
this.partialProfitAvailable = async (backtest, event, context) => {
|
|
9357
|
+
this.loggerService.log("actionCoreService partialProfitAvailable", {
|
|
9219
9358
|
context,
|
|
9220
9359
|
});
|
|
9221
9360
|
await this.validate(context);
|
|
9222
9361
|
const { actions = [] } = this.strategySchemaService.get(context.strategyName);
|
|
9223
9362
|
for (const actionName of actions) {
|
|
9224
|
-
await this.actionConnectionService.
|
|
9363
|
+
await this.actionConnectionService.partialProfitAvailable(event, backtest, { actionName, ...context });
|
|
9225
9364
|
}
|
|
9226
9365
|
};
|
|
9227
9366
|
/**
|
|
9228
9367
|
* Routes partial loss event to all registered actions for the strategy.
|
|
9229
9368
|
*
|
|
9230
9369
|
* Retrieves action list from strategy schema (IStrategySchema.actions)
|
|
9231
|
-
* and invokes the
|
|
9370
|
+
* and invokes the partialLossAvailable handler on each ClientAction instance sequentially.
|
|
9232
9371
|
*
|
|
9233
9372
|
* @param backtest - Whether running in backtest mode (true) or live mode (false)
|
|
9234
9373
|
* @param event - Loss milestone data with level (-10%, -20%, etc.) and price
|
|
9235
9374
|
* @param context - Strategy execution context with strategyName, exchangeName, frameName
|
|
9236
9375
|
*/
|
|
9237
|
-
this.
|
|
9238
|
-
this.loggerService.log("actionCoreService
|
|
9376
|
+
this.partialLossAvailable = async (backtest, event, context) => {
|
|
9377
|
+
this.loggerService.log("actionCoreService partialLossAvailable", {
|
|
9239
9378
|
context,
|
|
9240
9379
|
});
|
|
9241
9380
|
await this.validate(context);
|
|
9242
9381
|
const { actions = [] } = this.strategySchemaService.get(context.strategyName);
|
|
9243
9382
|
for (const actionName of actions) {
|
|
9244
|
-
await this.actionConnectionService.
|
|
9383
|
+
await this.actionConnectionService.partialLossAvailable(event, backtest, { actionName, ...context });
|
|
9245
9384
|
}
|
|
9246
9385
|
};
|
|
9247
9386
|
/**
|
|
9248
|
-
* Routes ping event to all registered actions for the strategy.
|
|
9387
|
+
* Routes scheduled ping event to all registered actions for the strategy.
|
|
9249
9388
|
*
|
|
9250
9389
|
* Retrieves action list from strategy schema (IStrategySchema.actions)
|
|
9251
|
-
* and invokes the
|
|
9390
|
+
* and invokes the pingScheduled handler on each ClientAction instance sequentially.
|
|
9252
9391
|
* Called every minute during scheduled signal monitoring.
|
|
9253
9392
|
*
|
|
9254
9393
|
* @param backtest - Whether running in backtest mode (true) or live mode (false)
|
|
9255
9394
|
* @param event - Scheduled signal monitoring data
|
|
9256
9395
|
* @param context - Strategy execution context with strategyName, exchangeName, frameName
|
|
9257
9396
|
*/
|
|
9258
|
-
this.
|
|
9259
|
-
this.loggerService.log("actionCoreService
|
|
9397
|
+
this.pingScheduled = async (backtest, event, context) => {
|
|
9398
|
+
this.loggerService.log("actionCoreService pingScheduled", {
|
|
9399
|
+
context,
|
|
9400
|
+
});
|
|
9401
|
+
await this.validate(context);
|
|
9402
|
+
const { actions = [] } = this.strategySchemaService.get(context.strategyName);
|
|
9403
|
+
for (const actionName of actions) {
|
|
9404
|
+
await this.actionConnectionService.pingScheduled(event, backtest, { actionName, ...context });
|
|
9405
|
+
}
|
|
9406
|
+
};
|
|
9407
|
+
/**
|
|
9408
|
+
* Routes active ping event to all registered actions for the strategy.
|
|
9409
|
+
*
|
|
9410
|
+
* Retrieves action list from strategy schema (IStrategySchema.actions)
|
|
9411
|
+
* and invokes the pingActive handler on each ClientAction instance sequentially.
|
|
9412
|
+
* Called every minute during active pending signal monitoring.
|
|
9413
|
+
*
|
|
9414
|
+
* @param backtest - Whether running in backtest mode (true) or live mode (false)
|
|
9415
|
+
* @param event - Active pending signal monitoring data
|
|
9416
|
+
* @param context - Strategy execution context with strategyName, exchangeName, frameName
|
|
9417
|
+
*/
|
|
9418
|
+
this.pingActive = async (backtest, event, context) => {
|
|
9419
|
+
this.loggerService.log("actionCoreService pingActive", {
|
|
9260
9420
|
context,
|
|
9261
9421
|
});
|
|
9262
9422
|
await this.validate(context);
|
|
9263
9423
|
const { actions = [] } = this.strategySchemaService.get(context.strategyName);
|
|
9264
9424
|
for (const actionName of actions) {
|
|
9265
|
-
await this.actionConnectionService.
|
|
9425
|
+
await this.actionConnectionService.pingActive(event, backtest, { actionName, ...context });
|
|
9266
9426
|
}
|
|
9267
9427
|
};
|
|
9268
9428
|
/**
|
|
@@ -10381,7 +10541,11 @@ class LiveLogicPrivateService {
|
|
|
10381
10541
|
await functoolsKit.sleep(TICK_TTL);
|
|
10382
10542
|
continue;
|
|
10383
10543
|
}
|
|
10384
|
-
|
|
10544
|
+
if (result.action === "waiting") {
|
|
10545
|
+
await functoolsKit.sleep(TICK_TTL);
|
|
10546
|
+
continue;
|
|
10547
|
+
}
|
|
10548
|
+
// Yield opened, closed, cancelled results
|
|
10385
10549
|
yield result;
|
|
10386
10550
|
// Check if strategy should stop after signal is closed
|
|
10387
10551
|
if (result.action === "closed") {
|
|
@@ -12316,6 +12480,12 @@ var _a$1, _b$1;
|
|
|
12316
12480
|
const MARKDOWN_METHOD_NAME_ENABLE = "MarkdownUtils.enable";
|
|
12317
12481
|
const MARKDOWN_METHOD_NAME_DISABLE = "MarkdownUtils.disable";
|
|
12318
12482
|
const MARKDOWN_METHOD_NAME_USE_ADAPTER = "MarkdownAdapter.useMarkdownAdapter";
|
|
12483
|
+
const MARKDOWN_METHOD_NAME_FILE_DUMP = "MarkdownFileAdapter.dump";
|
|
12484
|
+
const MARKDOWN_METHOD_NAME_FOLDER_DUMP = "MarkdownFolderAdapter.dump";
|
|
12485
|
+
const MARKDOWN_METHOD_NAME_WRITE_DATA = "MarkdownAdapter.writeData";
|
|
12486
|
+
const MARKDOWN_METHOD_NAME_USE_MD = "MarkdownAdapter.useMd";
|
|
12487
|
+
const MARKDOWN_METHOD_NAME_USE_JSONL = "MarkdownAdapter.useJsonl";
|
|
12488
|
+
const MARKDOWN_METHOD_NAME_USE_DUMMY = "MarkdownAdapter.useDummy";
|
|
12319
12489
|
const WAIT_FOR_INIT_SYMBOL$1 = Symbol("wait-for-init");
|
|
12320
12490
|
const WRITE_SAFE_SYMBOL$1 = Symbol("write-safe");
|
|
12321
12491
|
/**
|
|
@@ -12409,7 +12579,7 @@ class MarkdownFileBase {
|
|
|
12409
12579
|
* @throws Error if stream not initialized or write timeout exceeded
|
|
12410
12580
|
*/
|
|
12411
12581
|
async dump(data, options) {
|
|
12412
|
-
backtest$1.loggerService.debug(
|
|
12582
|
+
backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_FILE_DUMP, {
|
|
12413
12583
|
markdownName: this.markdownName,
|
|
12414
12584
|
options,
|
|
12415
12585
|
});
|
|
@@ -12491,7 +12661,7 @@ class MarkdownFolderBase {
|
|
|
12491
12661
|
* @throws Error if directory creation or file write fails
|
|
12492
12662
|
*/
|
|
12493
12663
|
async dump(content, options) {
|
|
12494
|
-
backtest$1.loggerService.debug(
|
|
12664
|
+
backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_FOLDER_DUMP, {
|
|
12495
12665
|
markdownName: this.markdownName,
|
|
12496
12666
|
options,
|
|
12497
12667
|
});
|
|
@@ -12731,7 +12901,7 @@ class MarkdownAdapter extends MarkdownUtils {
|
|
|
12731
12901
|
* @internal - Use service-specific dump methods instead (e.g., Backtest.dump)
|
|
12732
12902
|
*/
|
|
12733
12903
|
async writeData(markdownName, content, options) {
|
|
12734
|
-
backtest$1.loggerService.debug(
|
|
12904
|
+
backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_WRITE_DATA, {
|
|
12735
12905
|
markdownName,
|
|
12736
12906
|
options,
|
|
12737
12907
|
});
|
|
@@ -12746,7 +12916,7 @@ class MarkdownAdapter extends MarkdownUtils {
|
|
|
12746
12916
|
* Each dump creates a separate .md file.
|
|
12747
12917
|
*/
|
|
12748
12918
|
useMd() {
|
|
12749
|
-
backtest$1.loggerService.debug(
|
|
12919
|
+
backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_USE_MD);
|
|
12750
12920
|
this.useMarkdownAdapter(MarkdownFolderBase);
|
|
12751
12921
|
}
|
|
12752
12922
|
/**
|
|
@@ -12755,7 +12925,7 @@ class MarkdownAdapter extends MarkdownUtils {
|
|
|
12755
12925
|
* All dumps append to a single .jsonl file per markdown type.
|
|
12756
12926
|
*/
|
|
12757
12927
|
useJsonl() {
|
|
12758
|
-
backtest$1.loggerService.debug(
|
|
12928
|
+
backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_USE_JSONL);
|
|
12759
12929
|
this.useMarkdownAdapter(MarkdownFileBase);
|
|
12760
12930
|
}
|
|
12761
12931
|
/**
|
|
@@ -12763,7 +12933,7 @@ class MarkdownAdapter extends MarkdownUtils {
|
|
|
12763
12933
|
* All future markdown writes will be no-ops.
|
|
12764
12934
|
*/
|
|
12765
12935
|
useDummy() {
|
|
12766
|
-
backtest$1.loggerService.debug(
|
|
12936
|
+
backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_USE_DUMMY);
|
|
12767
12937
|
this.useMarkdownAdapter(MarkdownDummy);
|
|
12768
12938
|
}
|
|
12769
12939
|
}
|
|
@@ -13426,6 +13596,98 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
13426
13596
|
this._eventList.pop();
|
|
13427
13597
|
}
|
|
13428
13598
|
}
|
|
13599
|
+
/**
|
|
13600
|
+
* Adds a scheduled event to the storage.
|
|
13601
|
+
*
|
|
13602
|
+
* @param data - Scheduled tick result
|
|
13603
|
+
*/
|
|
13604
|
+
addScheduledEvent(data) {
|
|
13605
|
+
this._eventList.unshift({
|
|
13606
|
+
timestamp: data.signal.scheduledAt,
|
|
13607
|
+
action: "scheduled",
|
|
13608
|
+
symbol: data.signal.symbol,
|
|
13609
|
+
signalId: data.signal.id,
|
|
13610
|
+
position: data.signal.position,
|
|
13611
|
+
note: data.signal.note,
|
|
13612
|
+
currentPrice: data.currentPrice,
|
|
13613
|
+
priceOpen: data.signal.priceOpen,
|
|
13614
|
+
priceTakeProfit: data.signal.priceTakeProfit,
|
|
13615
|
+
priceStopLoss: data.signal.priceStopLoss,
|
|
13616
|
+
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
13617
|
+
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
13618
|
+
totalExecuted: data.signal.totalExecuted,
|
|
13619
|
+
});
|
|
13620
|
+
// Trim queue if exceeded MAX_EVENTS
|
|
13621
|
+
if (this._eventList.length > MAX_EVENTS$6) {
|
|
13622
|
+
this._eventList.pop();
|
|
13623
|
+
}
|
|
13624
|
+
}
|
|
13625
|
+
/**
|
|
13626
|
+
* Adds a waiting event to the storage.
|
|
13627
|
+
* Replaces the last waiting event with the same signalId.
|
|
13628
|
+
*
|
|
13629
|
+
* @param data - Waiting tick result
|
|
13630
|
+
*/
|
|
13631
|
+
addWaitingEvent(data) {
|
|
13632
|
+
const newEvent = {
|
|
13633
|
+
timestamp: Date.now(),
|
|
13634
|
+
action: "waiting",
|
|
13635
|
+
symbol: data.signal.symbol,
|
|
13636
|
+
signalId: data.signal.id,
|
|
13637
|
+
position: data.signal.position,
|
|
13638
|
+
note: data.signal.note,
|
|
13639
|
+
currentPrice: data.currentPrice,
|
|
13640
|
+
priceOpen: data.signal.priceOpen,
|
|
13641
|
+
priceTakeProfit: data.signal.priceTakeProfit,
|
|
13642
|
+
priceStopLoss: data.signal.priceStopLoss,
|
|
13643
|
+
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
13644
|
+
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
13645
|
+
totalExecuted: data.signal.totalExecuted,
|
|
13646
|
+
percentTp: data.percentTp,
|
|
13647
|
+
percentSl: data.percentSl,
|
|
13648
|
+
pnl: data.pnl.pnlPercentage,
|
|
13649
|
+
};
|
|
13650
|
+
// Find the last waiting event with the same signalId
|
|
13651
|
+
const lastWaitingIndex = this._eventList.findLastIndex((event) => event.action === "waiting" && event.signalId === data.signal.id);
|
|
13652
|
+
// Replace the last waiting event with the same signalId
|
|
13653
|
+
if (lastWaitingIndex !== -1) {
|
|
13654
|
+
this._eventList[lastWaitingIndex] = newEvent;
|
|
13655
|
+
return;
|
|
13656
|
+
}
|
|
13657
|
+
// If no previous waiting event found, add new event
|
|
13658
|
+
this._eventList.unshift(newEvent);
|
|
13659
|
+
// Trim queue if exceeded MAX_EVENTS
|
|
13660
|
+
if (this._eventList.length > MAX_EVENTS$6) {
|
|
13661
|
+
this._eventList.pop();
|
|
13662
|
+
}
|
|
13663
|
+
}
|
|
13664
|
+
/**
|
|
13665
|
+
* Adds a cancelled event to the storage.
|
|
13666
|
+
*
|
|
13667
|
+
* @param data - Cancelled tick result
|
|
13668
|
+
*/
|
|
13669
|
+
addCancelledEvent(data) {
|
|
13670
|
+
this._eventList.unshift({
|
|
13671
|
+
timestamp: data.closeTimestamp,
|
|
13672
|
+
action: "cancelled",
|
|
13673
|
+
symbol: data.signal.symbol,
|
|
13674
|
+
signalId: data.signal.id,
|
|
13675
|
+
position: data.signal.position,
|
|
13676
|
+
note: data.signal.note,
|
|
13677
|
+
currentPrice: data.currentPrice,
|
|
13678
|
+
priceOpen: data.signal.priceOpen,
|
|
13679
|
+
priceTakeProfit: data.signal.priceTakeProfit,
|
|
13680
|
+
priceStopLoss: data.signal.priceStopLoss,
|
|
13681
|
+
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
13682
|
+
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
13683
|
+
totalExecuted: data.signal.totalExecuted,
|
|
13684
|
+
cancelReason: data.reason,
|
|
13685
|
+
});
|
|
13686
|
+
// Trim queue if exceeded MAX_EVENTS
|
|
13687
|
+
if (this._eventList.length > MAX_EVENTS$6) {
|
|
13688
|
+
this._eventList.pop();
|
|
13689
|
+
}
|
|
13690
|
+
}
|
|
13429
13691
|
/**
|
|
13430
13692
|
* Calculates statistical data from live trading events (Controller).
|
|
13431
13693
|
* Returns null for any unsafe numeric values (NaN, Infinity, etc).
|
|
@@ -13683,6 +13945,12 @@ class LiveMarkdownService {
|
|
|
13683
13945
|
if (data.action === "idle") {
|
|
13684
13946
|
storage.addIdleEvent(data.currentPrice);
|
|
13685
13947
|
}
|
|
13948
|
+
else if (data.action === "scheduled") {
|
|
13949
|
+
storage.addScheduledEvent(data);
|
|
13950
|
+
}
|
|
13951
|
+
else if (data.action === "waiting") {
|
|
13952
|
+
storage.addWaitingEvent(data);
|
|
13953
|
+
}
|
|
13686
13954
|
else if (data.action === "opened") {
|
|
13687
13955
|
storage.addOpenedEvent(data);
|
|
13688
13956
|
}
|
|
@@ -13692,6 +13960,9 @@ class LiveMarkdownService {
|
|
|
13692
13960
|
else if (data.action === "closed") {
|
|
13693
13961
|
storage.addClosedEvent(data);
|
|
13694
13962
|
}
|
|
13963
|
+
else if (data.action === "cancelled") {
|
|
13964
|
+
storage.addCancelledEvent(data);
|
|
13965
|
+
}
|
|
13695
13966
|
};
|
|
13696
13967
|
/**
|
|
13697
13968
|
* Gets statistical data from all live trading events for a symbol-strategy pair.
|
|
@@ -18118,7 +18389,7 @@ const CREATE_COMMIT_PROFIT_FN = (self) => async (symbol, strategyName, exchangeN
|
|
|
18118
18389
|
timestamp,
|
|
18119
18390
|
};
|
|
18120
18391
|
await partialProfitSubject.next(event);
|
|
18121
|
-
await self.actionCoreService.
|
|
18392
|
+
await self.actionCoreService.partialProfitAvailable(backtest, event, { strategyName, exchangeName, frameName });
|
|
18122
18393
|
};
|
|
18123
18394
|
/**
|
|
18124
18395
|
* Creates a callback function for emitting loss events to partialLossSubject.
|
|
@@ -18142,7 +18413,7 @@ const CREATE_COMMIT_LOSS_FN = (self) => async (symbol, strategyName, exchangeNam
|
|
|
18142
18413
|
timestamp,
|
|
18143
18414
|
};
|
|
18144
18415
|
await partialLossSubject.next(event);
|
|
18145
|
-
await self.actionCoreService.
|
|
18416
|
+
await self.actionCoreService.partialLossAvailable(backtest, event, { strategyName, exchangeName, frameName });
|
|
18146
18417
|
};
|
|
18147
18418
|
/**
|
|
18148
18419
|
* Connection service for partial profit/loss tracking.
|
|
@@ -19291,7 +19562,7 @@ const CREATE_COMMIT_BREAKEVEN_FN = (self) => async (symbol, strategyName, exchan
|
|
|
19291
19562
|
timestamp,
|
|
19292
19563
|
};
|
|
19293
19564
|
await breakevenSubject.next(event);
|
|
19294
|
-
await self.actionCoreService.
|
|
19565
|
+
await self.actionCoreService.breakevenAvailable(backtest, event, { strategyName, exchangeName, frameName });
|
|
19295
19566
|
};
|
|
19296
19567
|
/**
|
|
19297
19568
|
* Connection service for breakeven tracking.
|
|
@@ -21376,6 +21647,43 @@ class LiveReportService {
|
|
|
21376
21647
|
if (data.action === "idle") {
|
|
21377
21648
|
await Report.writeData("live", baseEvent, searchOptions);
|
|
21378
21649
|
}
|
|
21650
|
+
else if (data.action === "scheduled") {
|
|
21651
|
+
await Report.writeData("live", {
|
|
21652
|
+
...baseEvent,
|
|
21653
|
+
signalId: data.signal?.id,
|
|
21654
|
+
position: data.signal?.position,
|
|
21655
|
+
note: data.signal?.note,
|
|
21656
|
+
priceOpen: data.signal?.priceOpen,
|
|
21657
|
+
priceTakeProfit: data.signal?.priceTakeProfit,
|
|
21658
|
+
priceStopLoss: data.signal?.priceStopLoss,
|
|
21659
|
+
originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
|
|
21660
|
+
originalPriceStopLoss: data.signal?.originalPriceStopLoss,
|
|
21661
|
+
totalExecuted: data.signal?.totalExecuted,
|
|
21662
|
+
scheduledAt: data.signal?.scheduledAt,
|
|
21663
|
+
minuteEstimatedTime: data.signal?.minuteEstimatedTime,
|
|
21664
|
+
}, { ...searchOptions, signalId: data.signal?.id });
|
|
21665
|
+
}
|
|
21666
|
+
else if (data.action === "waiting") {
|
|
21667
|
+
await Report.writeData("live", {
|
|
21668
|
+
...baseEvent,
|
|
21669
|
+
signalId: data.signal?.id,
|
|
21670
|
+
position: data.signal?.position,
|
|
21671
|
+
note: data.signal?.note,
|
|
21672
|
+
priceOpen: data.signal?.priceOpen,
|
|
21673
|
+
priceTakeProfit: data.signal?.priceTakeProfit,
|
|
21674
|
+
priceStopLoss: data.signal?.priceStopLoss,
|
|
21675
|
+
originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
|
|
21676
|
+
originalPriceStopLoss: data.signal?.originalPriceStopLoss,
|
|
21677
|
+
totalExecuted: data.signal?.totalExecuted,
|
|
21678
|
+
scheduledAt: data.signal?.scheduledAt,
|
|
21679
|
+
minuteEstimatedTime: data.signal?.minuteEstimatedTime,
|
|
21680
|
+
percentTp: data.percentTp,
|
|
21681
|
+
percentSl: data.percentSl,
|
|
21682
|
+
pnl: data.pnl.pnlPercentage,
|
|
21683
|
+
pnlPriceOpen: data.pnl.priceOpen,
|
|
21684
|
+
pnlPriceClose: data.pnl.priceClose,
|
|
21685
|
+
}, { ...searchOptions, signalId: data.signal?.id });
|
|
21686
|
+
}
|
|
21379
21687
|
else if (data.action === "opened") {
|
|
21380
21688
|
await Report.writeData("live", {
|
|
21381
21689
|
...baseEvent,
|
|
@@ -21442,6 +21750,24 @@ class LiveReportService {
|
|
|
21442
21750
|
closeTime: data.closeTimestamp,
|
|
21443
21751
|
}, { ...searchOptions, signalId: data.signal?.id });
|
|
21444
21752
|
}
|
|
21753
|
+
else if (data.action === "cancelled") {
|
|
21754
|
+
await Report.writeData("live", {
|
|
21755
|
+
...baseEvent,
|
|
21756
|
+
signalId: data.signal?.id,
|
|
21757
|
+
position: data.signal?.position,
|
|
21758
|
+
note: data.signal?.note,
|
|
21759
|
+
priceOpen: data.signal?.priceOpen,
|
|
21760
|
+
priceTakeProfit: data.signal?.priceTakeProfit,
|
|
21761
|
+
priceStopLoss: data.signal?.priceStopLoss,
|
|
21762
|
+
originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
|
|
21763
|
+
originalPriceStopLoss: data.signal?.originalPriceStopLoss,
|
|
21764
|
+
totalExecuted: data.signal?.totalExecuted,
|
|
21765
|
+
scheduledAt: data.signal?.scheduledAt,
|
|
21766
|
+
minuteEstimatedTime: data.signal?.minuteEstimatedTime,
|
|
21767
|
+
cancelReason: data.reason,
|
|
21768
|
+
closeTime: data.closeTimestamp,
|
|
21769
|
+
}, { ...searchOptions, signalId: data.signal?.id });
|
|
21770
|
+
}
|
|
21445
21771
|
};
|
|
21446
21772
|
/**
|
|
21447
21773
|
* Subscribes to live signal emitter to receive tick events.
|
|
@@ -22631,23 +22957,23 @@ const backtest = {
|
|
|
22631
22957
|
init();
|
|
22632
22958
|
var backtest$1 = backtest;
|
|
22633
22959
|
|
|
22634
|
-
const GET_TIMEFRAME_METHOD_NAME = "get.
|
|
22960
|
+
const GET_TIMEFRAME_METHOD_NAME = "get.getBacktestTimeframe";
|
|
22635
22961
|
/**
|
|
22636
22962
|
* Retrieves current backtest timeframe for given symbol.
|
|
22637
22963
|
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
22638
22964
|
* @returns Promise resolving to array of Date objects representing tick timestamps
|
|
22639
22965
|
* @throws Error if called outside of backtest execution context
|
|
22640
22966
|
*/
|
|
22641
|
-
async function
|
|
22967
|
+
async function getBacktestTimeframe(symbol) {
|
|
22642
22968
|
backtest$1.loggerService.info(GET_TIMEFRAME_METHOD_NAME, { symbol });
|
|
22643
22969
|
if (!ExecutionContextService.hasContext()) {
|
|
22644
|
-
throw new Error("
|
|
22970
|
+
throw new Error("getBacktestTimeframe requires an execution context");
|
|
22645
22971
|
}
|
|
22646
22972
|
if (!MethodContextService.hasContext()) {
|
|
22647
|
-
throw new Error("
|
|
22973
|
+
throw new Error("getBacktestTimeframe requires a method context");
|
|
22648
22974
|
}
|
|
22649
22975
|
if (!backtest$1.executionContextService.context.backtest) {
|
|
22650
|
-
throw new Error("
|
|
22976
|
+
throw new Error("getBacktestTimeframe can only be used during backtest execution");
|
|
22651
22977
|
}
|
|
22652
22978
|
return await backtest$1.frameCoreService.getTimeframe(symbol, backtest$1.methodContextService.context.frameName);
|
|
22653
22979
|
}
|
|
@@ -22847,14 +23173,14 @@ async function validate(args = {}) {
|
|
|
22847
23173
|
return await validateInternal(args);
|
|
22848
23174
|
}
|
|
22849
23175
|
|
|
22850
|
-
const GET_STRATEGY_METHOD_NAME = "get.
|
|
22851
|
-
const GET_EXCHANGE_METHOD_NAME = "get.
|
|
22852
|
-
const GET_FRAME_METHOD_NAME = "get.
|
|
22853
|
-
const GET_WALKER_METHOD_NAME = "get.
|
|
22854
|
-
const GET_SIZING_METHOD_NAME = "get.
|
|
22855
|
-
const GET_RISK_METHOD_NAME = "get.
|
|
22856
|
-
const GET_OPTIMIZER_METHOD_NAME = "get.
|
|
22857
|
-
const GET_ACTION_METHOD_NAME = "get.
|
|
23176
|
+
const GET_STRATEGY_METHOD_NAME = "get.getStrategySchema";
|
|
23177
|
+
const GET_EXCHANGE_METHOD_NAME = "get.getExchangeSchema";
|
|
23178
|
+
const GET_FRAME_METHOD_NAME = "get.getFrameSchema";
|
|
23179
|
+
const GET_WALKER_METHOD_NAME = "get.getWalkerSchema";
|
|
23180
|
+
const GET_SIZING_METHOD_NAME = "get.getSizingSchema";
|
|
23181
|
+
const GET_RISK_METHOD_NAME = "get.getRiskSchema";
|
|
23182
|
+
const GET_OPTIMIZER_METHOD_NAME = "get.getOptimizerSchema";
|
|
23183
|
+
const GET_ACTION_METHOD_NAME = "get.getActionSchema";
|
|
22858
23184
|
/**
|
|
22859
23185
|
* Retrieves a registered strategy schema by name.
|
|
22860
23186
|
*
|
|
@@ -22869,7 +23195,7 @@ const GET_ACTION_METHOD_NAME = "get.getAction";
|
|
|
22869
23195
|
* console.log(strategy.getSignal); // async function
|
|
22870
23196
|
* ```
|
|
22871
23197
|
*/
|
|
22872
|
-
function
|
|
23198
|
+
function getStrategySchema(strategyName) {
|
|
22873
23199
|
backtest$1.loggerService.log(GET_STRATEGY_METHOD_NAME, {
|
|
22874
23200
|
strategyName,
|
|
22875
23201
|
});
|
|
@@ -22890,7 +23216,7 @@ function getStrategy(strategyName) {
|
|
|
22890
23216
|
* console.log(exchange.formatPrice); // async function
|
|
22891
23217
|
* ```
|
|
22892
23218
|
*/
|
|
22893
|
-
function
|
|
23219
|
+
function getExchangeSchema(exchangeName) {
|
|
22894
23220
|
backtest$1.loggerService.log(GET_EXCHANGE_METHOD_NAME, {
|
|
22895
23221
|
exchangeName,
|
|
22896
23222
|
});
|
|
@@ -22912,7 +23238,7 @@ function getExchange(exchangeName) {
|
|
|
22912
23238
|
* console.log(frame.endDate); // Date object
|
|
22913
23239
|
* ```
|
|
22914
23240
|
*/
|
|
22915
|
-
function
|
|
23241
|
+
function getFrameSchema(frameName) {
|
|
22916
23242
|
backtest$1.loggerService.log(GET_FRAME_METHOD_NAME, {
|
|
22917
23243
|
frameName,
|
|
22918
23244
|
});
|
|
@@ -22935,7 +23261,7 @@ function getFrame(frameName) {
|
|
|
22935
23261
|
* console.log(walker.metric); // "sharpeRatio"
|
|
22936
23262
|
* ```
|
|
22937
23263
|
*/
|
|
22938
|
-
function
|
|
23264
|
+
function getWalkerSchema(walkerName) {
|
|
22939
23265
|
backtest$1.loggerService.log(GET_WALKER_METHOD_NAME, {
|
|
22940
23266
|
walkerName,
|
|
22941
23267
|
});
|
|
@@ -22957,7 +23283,7 @@ function getWalker(walkerName) {
|
|
|
22957
23283
|
* console.log(sizing.maxPositionPercentage); // 10
|
|
22958
23284
|
* ```
|
|
22959
23285
|
*/
|
|
22960
|
-
function
|
|
23286
|
+
function getSizingSchema(sizingName) {
|
|
22961
23287
|
backtest$1.loggerService.log(GET_SIZING_METHOD_NAME, {
|
|
22962
23288
|
sizingName,
|
|
22963
23289
|
});
|
|
@@ -22978,7 +23304,7 @@ function getSizing(sizingName) {
|
|
|
22978
23304
|
* console.log(risk.validations); // Array of validation functions
|
|
22979
23305
|
* ```
|
|
22980
23306
|
*/
|
|
22981
|
-
function
|
|
23307
|
+
function getRiskSchema(riskName) {
|
|
22982
23308
|
backtest$1.loggerService.log(GET_RISK_METHOD_NAME, {
|
|
22983
23309
|
riskName,
|
|
22984
23310
|
});
|
|
@@ -23001,7 +23327,7 @@ function getRisk(riskName) {
|
|
|
23001
23327
|
* console.log(optimizer.getPrompt); // async function
|
|
23002
23328
|
* ```
|
|
23003
23329
|
*/
|
|
23004
|
-
function
|
|
23330
|
+
function getOptimizerSchema(optimizerName) {
|
|
23005
23331
|
backtest$1.loggerService.log(GET_OPTIMIZER_METHOD_NAME, {
|
|
23006
23332
|
optimizerName,
|
|
23007
23333
|
});
|
|
@@ -23022,7 +23348,7 @@ function getOptimizer(optimizerName) {
|
|
|
23022
23348
|
* console.log(action.callbacks); // Optional lifecycle callbacks
|
|
23023
23349
|
* ```
|
|
23024
23350
|
*/
|
|
23025
|
-
function
|
|
23351
|
+
function getActionSchema(actionName) {
|
|
23026
23352
|
backtest$1.loggerService.log(GET_ACTION_METHOD_NAME, {
|
|
23027
23353
|
actionName,
|
|
23028
23354
|
});
|
|
@@ -23036,6 +23362,8 @@ const FORMAT_PRICE_METHOD_NAME = "exchange.formatPrice";
|
|
|
23036
23362
|
const FORMAT_QUANTITY_METHOD_NAME = "exchange.formatQuantity";
|
|
23037
23363
|
const GET_DATE_METHOD_NAME = "exchange.getDate";
|
|
23038
23364
|
const GET_MODE_METHOD_NAME = "exchange.getMode";
|
|
23365
|
+
const GET_SYMBOL_METHOD_NAME = "exchange.getSymbol";
|
|
23366
|
+
const GET_CONTEXT_METHOD_NAME = "exchange.getContext";
|
|
23039
23367
|
const HAS_TRADE_CONTEXT_METHOD_NAME = "exchange.hasTradeContext";
|
|
23040
23368
|
const GET_ORDER_BOOK_METHOD_NAME = "exchange.getOrderBook";
|
|
23041
23369
|
/**
|
|
@@ -23218,6 +23546,48 @@ async function getMode() {
|
|
|
23218
23546
|
const { backtest: bt } = backtest$1.executionContextService.context;
|
|
23219
23547
|
return bt ? "backtest" : "live";
|
|
23220
23548
|
}
|
|
23549
|
+
/**
|
|
23550
|
+
* Gets the current trading symbol from execution context.
|
|
23551
|
+
*
|
|
23552
|
+
* @returns Promise resolving to the current trading symbol (e.g., "BTCUSDT")
|
|
23553
|
+
* @throws Error if execution context is not active
|
|
23554
|
+
*
|
|
23555
|
+
* @example
|
|
23556
|
+
* ```typescript
|
|
23557
|
+
* const symbol = await getSymbol();
|
|
23558
|
+
* console.log(symbol); // "BTCUSDT"
|
|
23559
|
+
* ```
|
|
23560
|
+
*/
|
|
23561
|
+
async function getSymbol() {
|
|
23562
|
+
backtest$1.loggerService.info(GET_SYMBOL_METHOD_NAME);
|
|
23563
|
+
if (!ExecutionContextService.hasContext()) {
|
|
23564
|
+
throw new Error("getSymbol requires an execution context");
|
|
23565
|
+
}
|
|
23566
|
+
const { symbol } = backtest$1.executionContextService.context;
|
|
23567
|
+
return symbol;
|
|
23568
|
+
}
|
|
23569
|
+
/**
|
|
23570
|
+
* Gets the current method context.
|
|
23571
|
+
*
|
|
23572
|
+
* Returns the context object from the method context service, which contains
|
|
23573
|
+
* information about the current method execution environment.
|
|
23574
|
+
*
|
|
23575
|
+
* @returns Promise resolving to the current method context object
|
|
23576
|
+
* @throws Error if method context is not active
|
|
23577
|
+
*
|
|
23578
|
+
* @example
|
|
23579
|
+
* ```typescript
|
|
23580
|
+
* const context = await getContext();
|
|
23581
|
+
* console.log(context); // { ...method context data... }
|
|
23582
|
+
* ```
|
|
23583
|
+
*/
|
|
23584
|
+
async function getContext() {
|
|
23585
|
+
backtest$1.loggerService.info(GET_CONTEXT_METHOD_NAME);
|
|
23586
|
+
if (!MethodContextService.hasContext()) {
|
|
23587
|
+
throw new Error("getContext requires a method context");
|
|
23588
|
+
}
|
|
23589
|
+
return backtest$1.methodContextService.context;
|
|
23590
|
+
}
|
|
23221
23591
|
/**
|
|
23222
23592
|
* Fetches order book for a trading pair from the registered exchange.
|
|
23223
23593
|
*
|
|
@@ -23254,52 +23624,12 @@ async function getOrderBook(symbol, depth) {
|
|
|
23254
23624
|
return await backtest$1.exchangeConnectionService.getOrderBook(symbol, depth);
|
|
23255
23625
|
}
|
|
23256
23626
|
|
|
23257
|
-
const
|
|
23258
|
-
const
|
|
23259
|
-
const
|
|
23260
|
-
const
|
|
23261
|
-
const
|
|
23262
|
-
const
|
|
23263
|
-
const BREAKEVEN_METHOD_NAME = "strategy.breakeven";
|
|
23264
|
-
/**
|
|
23265
|
-
* Stops the strategy from generating new signals.
|
|
23266
|
-
*
|
|
23267
|
-
* Sets internal flag to prevent strategy from opening new signals.
|
|
23268
|
-
* Current active signal (if any) will complete normally.
|
|
23269
|
-
* Backtest/Live mode will stop at the next safe point (idle state or after signal closes).
|
|
23270
|
-
*
|
|
23271
|
-
* Automatically detects backtest/live mode from execution context.
|
|
23272
|
-
*
|
|
23273
|
-
* @param symbol - Trading pair symbol
|
|
23274
|
-
* @param strategyName - Strategy name to stop
|
|
23275
|
-
* @returns Promise that resolves when stop flag is set
|
|
23276
|
-
*
|
|
23277
|
-
* @example
|
|
23278
|
-
* ```typescript
|
|
23279
|
-
* import { stop } from "backtest-kit";
|
|
23280
|
-
*
|
|
23281
|
-
* // Stop strategy after some condition
|
|
23282
|
-
* await stop("BTCUSDT", "my-strategy");
|
|
23283
|
-
* ```
|
|
23284
|
-
*/
|
|
23285
|
-
async function stop(symbol) {
|
|
23286
|
-
backtest$1.loggerService.info(STOP_METHOD_NAME, {
|
|
23287
|
-
symbol,
|
|
23288
|
-
});
|
|
23289
|
-
if (!ExecutionContextService.hasContext()) {
|
|
23290
|
-
throw new Error("stop requires an execution context");
|
|
23291
|
-
}
|
|
23292
|
-
if (!MethodContextService.hasContext()) {
|
|
23293
|
-
throw new Error("stop requires a method context");
|
|
23294
|
-
}
|
|
23295
|
-
const { backtest: isBacktest } = backtest$1.executionContextService.context;
|
|
23296
|
-
const { exchangeName, frameName, strategyName } = backtest$1.methodContextService.context;
|
|
23297
|
-
await backtest$1.strategyCoreService.stop(isBacktest, symbol, {
|
|
23298
|
-
exchangeName,
|
|
23299
|
-
frameName,
|
|
23300
|
-
strategyName,
|
|
23301
|
-
});
|
|
23302
|
-
}
|
|
23627
|
+
const CANCEL_METHOD_NAME = "strategy.commitCancel";
|
|
23628
|
+
const PARTIAL_PROFIT_METHOD_NAME = "strategy.commitPartialProfit";
|
|
23629
|
+
const PARTIAL_LOSS_METHOD_NAME = "strategy.commitPartialLoss";
|
|
23630
|
+
const TRAILING_STOP_METHOD_NAME = "strategy.commitTrailingStop";
|
|
23631
|
+
const TRAILING_PROFIT_METHOD_NAME = "strategy.commitTrailingTake";
|
|
23632
|
+
const BREAKEVEN_METHOD_NAME = "strategy.commitBreakeven";
|
|
23303
23633
|
/**
|
|
23304
23634
|
* Cancels the scheduled signal without stopping the strategy.
|
|
23305
23635
|
*
|
|
@@ -23322,7 +23652,7 @@ async function stop(symbol) {
|
|
|
23322
23652
|
* await cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
|
|
23323
23653
|
* ```
|
|
23324
23654
|
*/
|
|
23325
|
-
async function
|
|
23655
|
+
async function commitCancel(symbol, cancelId) {
|
|
23326
23656
|
backtest$1.loggerService.info(CANCEL_METHOD_NAME, {
|
|
23327
23657
|
symbol,
|
|
23328
23658
|
cancelId,
|
|
@@ -23364,7 +23694,7 @@ async function cancel(symbol, cancelId) {
|
|
|
23364
23694
|
* }
|
|
23365
23695
|
* ```
|
|
23366
23696
|
*/
|
|
23367
|
-
async function
|
|
23697
|
+
async function commitPartialProfit(symbol, percentToClose) {
|
|
23368
23698
|
backtest$1.loggerService.info(PARTIAL_PROFIT_METHOD_NAME, {
|
|
23369
23699
|
symbol,
|
|
23370
23700
|
percentToClose,
|
|
@@ -23407,7 +23737,7 @@ async function partialProfit(symbol, percentToClose) {
|
|
|
23407
23737
|
* }
|
|
23408
23738
|
* ```
|
|
23409
23739
|
*/
|
|
23410
|
-
async function
|
|
23740
|
+
async function commitPartialLoss(symbol, percentToClose) {
|
|
23411
23741
|
backtest$1.loggerService.info(PARTIAL_LOSS_METHOD_NAME, {
|
|
23412
23742
|
symbol,
|
|
23413
23743
|
percentToClose,
|
|
@@ -23466,7 +23796,7 @@ async function partialLoss(symbol, percentToClose) {
|
|
|
23466
23796
|
* // success3 = true (ACCEPTED: newDistance = 10% - 7% = 3%, newSL = 97 > 95, better protection)
|
|
23467
23797
|
* ```
|
|
23468
23798
|
*/
|
|
23469
|
-
async function
|
|
23799
|
+
async function commitTrailingStop(symbol, percentShift, currentPrice) {
|
|
23470
23800
|
backtest$1.loggerService.info(TRAILING_STOP_METHOD_NAME, {
|
|
23471
23801
|
symbol,
|
|
23472
23802
|
percentShift,
|
|
@@ -23525,7 +23855,7 @@ async function trailingStop(symbol, percentShift, currentPrice) {
|
|
|
23525
23855
|
* // success3 = true (ACCEPTED: newDistance = 10% - 5% = 5%, newTP = 105 < 107, more conservative)
|
|
23526
23856
|
* ```
|
|
23527
23857
|
*/
|
|
23528
|
-
async function
|
|
23858
|
+
async function commitTrailingTake(symbol, percentShift, currentPrice) {
|
|
23529
23859
|
backtest$1.loggerService.info(TRAILING_PROFIT_METHOD_NAME, {
|
|
23530
23860
|
symbol,
|
|
23531
23861
|
percentShift,
|
|
@@ -23566,7 +23896,7 @@ async function trailingTake(symbol, percentShift, currentPrice) {
|
|
|
23566
23896
|
* }
|
|
23567
23897
|
* ```
|
|
23568
23898
|
*/
|
|
23569
|
-
async function
|
|
23899
|
+
async function commitBreakeven(symbol) {
|
|
23570
23900
|
backtest$1.loggerService.info(BREAKEVEN_METHOD_NAME, {
|
|
23571
23901
|
symbol,
|
|
23572
23902
|
});
|
|
@@ -23582,6 +23912,47 @@ async function breakeven(symbol) {
|
|
|
23582
23912
|
return await backtest$1.strategyCoreService.breakeven(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
|
|
23583
23913
|
}
|
|
23584
23914
|
|
|
23915
|
+
const STOP_METHOD_NAME = "control.stop";
|
|
23916
|
+
/**
|
|
23917
|
+
* Stops the strategy from generating new signals.
|
|
23918
|
+
*
|
|
23919
|
+
* Sets internal flag to prevent strategy from opening new signals.
|
|
23920
|
+
* Current active signal (if any) will complete normally.
|
|
23921
|
+
* Backtest/Live mode will stop at the next safe point (idle state or after signal closes).
|
|
23922
|
+
*
|
|
23923
|
+
* Automatically detects backtest/live mode from execution context.
|
|
23924
|
+
*
|
|
23925
|
+
* @param symbol - Trading pair symbol
|
|
23926
|
+
* @param strategyName - Strategy name to stop
|
|
23927
|
+
* @returns Promise that resolves when stop flag is set
|
|
23928
|
+
*
|
|
23929
|
+
* @example
|
|
23930
|
+
* ```typescript
|
|
23931
|
+
* import { stop } from "backtest-kit";
|
|
23932
|
+
*
|
|
23933
|
+
* // Stop strategy after some condition
|
|
23934
|
+
* await stop("BTCUSDT", "my-strategy");
|
|
23935
|
+
* ```
|
|
23936
|
+
*/
|
|
23937
|
+
async function stop(symbol) {
|
|
23938
|
+
backtest$1.loggerService.info(STOP_METHOD_NAME, {
|
|
23939
|
+
symbol,
|
|
23940
|
+
});
|
|
23941
|
+
if (!ExecutionContextService.hasContext()) {
|
|
23942
|
+
throw new Error("stop requires an execution context");
|
|
23943
|
+
}
|
|
23944
|
+
if (!MethodContextService.hasContext()) {
|
|
23945
|
+
throw new Error("stop requires a method context");
|
|
23946
|
+
}
|
|
23947
|
+
const { backtest: isBacktest } = backtest$1.executionContextService.context;
|
|
23948
|
+
const { exchangeName, frameName, strategyName } = backtest$1.methodContextService.context;
|
|
23949
|
+
await backtest$1.strategyCoreService.stop(isBacktest, symbol, {
|
|
23950
|
+
exchangeName,
|
|
23951
|
+
frameName,
|
|
23952
|
+
strategyName,
|
|
23953
|
+
});
|
|
23954
|
+
}
|
|
23955
|
+
|
|
23585
23956
|
/**
|
|
23586
23957
|
* Sets custom logger implementation for the framework.
|
|
23587
23958
|
*
|
|
@@ -23732,14 +24103,14 @@ function getDefaultColumns() {
|
|
|
23732
24103
|
return DEFAULT_COLUMNS;
|
|
23733
24104
|
}
|
|
23734
24105
|
|
|
23735
|
-
const ADD_STRATEGY_METHOD_NAME = "add.
|
|
23736
|
-
const ADD_EXCHANGE_METHOD_NAME = "add.
|
|
23737
|
-
const ADD_FRAME_METHOD_NAME = "add.
|
|
23738
|
-
const ADD_WALKER_METHOD_NAME = "add.
|
|
23739
|
-
const ADD_SIZING_METHOD_NAME = "add.
|
|
23740
|
-
const ADD_RISK_METHOD_NAME = "add.
|
|
23741
|
-
const ADD_OPTIMIZER_METHOD_NAME = "add.
|
|
23742
|
-
const ADD_ACTION_METHOD_NAME = "add.
|
|
24106
|
+
const ADD_STRATEGY_METHOD_NAME = "add.addStrategySchema";
|
|
24107
|
+
const ADD_EXCHANGE_METHOD_NAME = "add.addExchangeSchema";
|
|
24108
|
+
const ADD_FRAME_METHOD_NAME = "add.addFrameSchema";
|
|
24109
|
+
const ADD_WALKER_METHOD_NAME = "add.addWalkerSchema";
|
|
24110
|
+
const ADD_SIZING_METHOD_NAME = "add.addSizingSchema";
|
|
24111
|
+
const ADD_RISK_METHOD_NAME = "add.addRiskSchema";
|
|
24112
|
+
const ADD_OPTIMIZER_METHOD_NAME = "add.addOptimizerSchema";
|
|
24113
|
+
const ADD_ACTION_METHOD_NAME = "add.addActionSchema";
|
|
23743
24114
|
/**
|
|
23744
24115
|
* Registers a trading strategy in the framework.
|
|
23745
24116
|
*
|
|
@@ -23774,7 +24145,7 @@ const ADD_ACTION_METHOD_NAME = "add.addAction";
|
|
|
23774
24145
|
* });
|
|
23775
24146
|
* ```
|
|
23776
24147
|
*/
|
|
23777
|
-
function
|
|
24148
|
+
function addStrategySchema(strategySchema) {
|
|
23778
24149
|
backtest$1.loggerService.info(ADD_STRATEGY_METHOD_NAME, {
|
|
23779
24150
|
strategySchema,
|
|
23780
24151
|
});
|
|
@@ -23816,7 +24187,7 @@ function addStrategy(strategySchema) {
|
|
|
23816
24187
|
* });
|
|
23817
24188
|
* ```
|
|
23818
24189
|
*/
|
|
23819
|
-
function
|
|
24190
|
+
function addExchangeSchema(exchangeSchema) {
|
|
23820
24191
|
backtest$1.loggerService.info(ADD_EXCHANGE_METHOD_NAME, {
|
|
23821
24192
|
exchangeSchema,
|
|
23822
24193
|
});
|
|
@@ -23853,7 +24224,7 @@ function addExchange(exchangeSchema) {
|
|
|
23853
24224
|
* });
|
|
23854
24225
|
* ```
|
|
23855
24226
|
*/
|
|
23856
|
-
function
|
|
24227
|
+
function addFrameSchema(frameSchema) {
|
|
23857
24228
|
backtest$1.loggerService.info(ADD_FRAME_METHOD_NAME, {
|
|
23858
24229
|
frameSchema,
|
|
23859
24230
|
});
|
|
@@ -23897,7 +24268,7 @@ function addFrame(frameSchema) {
|
|
|
23897
24268
|
* });
|
|
23898
24269
|
* ```
|
|
23899
24270
|
*/
|
|
23900
|
-
function
|
|
24271
|
+
function addWalkerSchema(walkerSchema) {
|
|
23901
24272
|
backtest$1.loggerService.info(ADD_WALKER_METHOD_NAME, {
|
|
23902
24273
|
walkerSchema,
|
|
23903
24274
|
});
|
|
@@ -23956,7 +24327,7 @@ function addWalker(walkerSchema) {
|
|
|
23956
24327
|
* });
|
|
23957
24328
|
* ```
|
|
23958
24329
|
*/
|
|
23959
|
-
function
|
|
24330
|
+
function addSizingSchema(sizingSchema) {
|
|
23960
24331
|
backtest$1.loggerService.info(ADD_SIZING_METHOD_NAME, {
|
|
23961
24332
|
sizingSchema,
|
|
23962
24333
|
});
|
|
@@ -24024,7 +24395,7 @@ function addSizing(sizingSchema) {
|
|
|
24024
24395
|
* });
|
|
24025
24396
|
* ```
|
|
24026
24397
|
*/
|
|
24027
|
-
function
|
|
24398
|
+
function addRiskSchema(riskSchema) {
|
|
24028
24399
|
backtest$1.loggerService.info(ADD_RISK_METHOD_NAME, {
|
|
24029
24400
|
riskSchema,
|
|
24030
24401
|
});
|
|
@@ -24118,7 +24489,7 @@ function addRisk(riskSchema) {
|
|
|
24118
24489
|
* });
|
|
24119
24490
|
* ```
|
|
24120
24491
|
*/
|
|
24121
|
-
function
|
|
24492
|
+
function addOptimizerSchema(optimizerSchema) {
|
|
24122
24493
|
backtest$1.loggerService.info(ADD_OPTIMIZER_METHOD_NAME, {
|
|
24123
24494
|
optimizerSchema,
|
|
24124
24495
|
});
|
|
@@ -24193,7 +24564,7 @@ function addOptimizer(optimizerSchema) {
|
|
|
24193
24564
|
* });
|
|
24194
24565
|
* ```
|
|
24195
24566
|
*/
|
|
24196
|
-
function
|
|
24567
|
+
function addActionSchema(actionSchema) {
|
|
24197
24568
|
backtest$1.loggerService.info(ADD_ACTION_METHOD_NAME, {
|
|
24198
24569
|
actionSchema,
|
|
24199
24570
|
});
|
|
@@ -24201,14 +24572,14 @@ function addAction(actionSchema) {
|
|
|
24201
24572
|
backtest$1.actionSchemaService.register(actionSchema.actionName, actionSchema);
|
|
24202
24573
|
}
|
|
24203
24574
|
|
|
24204
|
-
const METHOD_NAME_OVERRIDE_STRATEGY = "function.override.
|
|
24205
|
-
const METHOD_NAME_OVERRIDE_EXCHANGE = "function.override.
|
|
24206
|
-
const METHOD_NAME_OVERRIDE_FRAME = "function.override.
|
|
24207
|
-
const METHOD_NAME_OVERRIDE_WALKER = "function.override.
|
|
24208
|
-
const METHOD_NAME_OVERRIDE_SIZING = "function.override.
|
|
24209
|
-
const METHOD_NAME_OVERRIDE_RISK = "function.override.
|
|
24210
|
-
const METHOD_NAME_OVERRIDE_OPTIMIZER = "function.override.
|
|
24211
|
-
const METHOD_NAME_OVERRIDE_ACTION = "function.override.
|
|
24575
|
+
const METHOD_NAME_OVERRIDE_STRATEGY = "function.override.overrideStrategySchema";
|
|
24576
|
+
const METHOD_NAME_OVERRIDE_EXCHANGE = "function.override.overrideExchangeSchema";
|
|
24577
|
+
const METHOD_NAME_OVERRIDE_FRAME = "function.override.overrideFrameSchema";
|
|
24578
|
+
const METHOD_NAME_OVERRIDE_WALKER = "function.override.overrideWalkerSchema";
|
|
24579
|
+
const METHOD_NAME_OVERRIDE_SIZING = "function.override.overrideSizingSchema";
|
|
24580
|
+
const METHOD_NAME_OVERRIDE_RISK = "function.override.overrideRiskSchema";
|
|
24581
|
+
const METHOD_NAME_OVERRIDE_OPTIMIZER = "function.override.overrideOptimizerSchema";
|
|
24582
|
+
const METHOD_NAME_OVERRIDE_ACTION = "function.override.overrideActionSchema";
|
|
24212
24583
|
/**
|
|
24213
24584
|
* Overrides an existing trading strategy in the framework.
|
|
24214
24585
|
*
|
|
@@ -24229,7 +24600,7 @@ const METHOD_NAME_OVERRIDE_ACTION = "function.override.overrideAction";
|
|
|
24229
24600
|
* });
|
|
24230
24601
|
* ```
|
|
24231
24602
|
*/
|
|
24232
|
-
async function
|
|
24603
|
+
async function overrideStrategySchema(strategySchema) {
|
|
24233
24604
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_STRATEGY, {
|
|
24234
24605
|
strategySchema,
|
|
24235
24606
|
});
|
|
@@ -24257,7 +24628,7 @@ async function overrideStrategy(strategySchema) {
|
|
|
24257
24628
|
* });
|
|
24258
24629
|
* ```
|
|
24259
24630
|
*/
|
|
24260
|
-
async function
|
|
24631
|
+
async function overrideExchangeSchema(exchangeSchema) {
|
|
24261
24632
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_EXCHANGE, {
|
|
24262
24633
|
exchangeSchema,
|
|
24263
24634
|
});
|
|
@@ -24285,7 +24656,7 @@ async function overrideExchange(exchangeSchema) {
|
|
|
24285
24656
|
* });
|
|
24286
24657
|
* ```
|
|
24287
24658
|
*/
|
|
24288
|
-
async function
|
|
24659
|
+
async function overrideFrameSchema(frameSchema) {
|
|
24289
24660
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_FRAME, {
|
|
24290
24661
|
frameSchema,
|
|
24291
24662
|
});
|
|
@@ -24314,7 +24685,7 @@ async function overrideFrame(frameSchema) {
|
|
|
24314
24685
|
* });
|
|
24315
24686
|
* ```
|
|
24316
24687
|
*/
|
|
24317
|
-
async function
|
|
24688
|
+
async function overrideWalkerSchema(walkerSchema) {
|
|
24318
24689
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_WALKER, {
|
|
24319
24690
|
walkerSchema,
|
|
24320
24691
|
});
|
|
@@ -24346,7 +24717,7 @@ async function overrideWalker(walkerSchema) {
|
|
|
24346
24717
|
* });
|
|
24347
24718
|
* ```
|
|
24348
24719
|
*/
|
|
24349
|
-
async function
|
|
24720
|
+
async function overrideSizingSchema(sizingSchema) {
|
|
24350
24721
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_SIZING, {
|
|
24351
24722
|
sizingSchema,
|
|
24352
24723
|
});
|
|
@@ -24373,7 +24744,7 @@ async function overrideSizing(sizingSchema) {
|
|
|
24373
24744
|
* });
|
|
24374
24745
|
* ```
|
|
24375
24746
|
*/
|
|
24376
|
-
async function
|
|
24747
|
+
async function overrideRiskSchema(riskSchema) {
|
|
24377
24748
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_RISK, {
|
|
24378
24749
|
riskSchema,
|
|
24379
24750
|
});
|
|
@@ -24407,7 +24778,7 @@ async function overrideRisk(riskSchema) {
|
|
|
24407
24778
|
* });
|
|
24408
24779
|
* ```
|
|
24409
24780
|
*/
|
|
24410
|
-
async function
|
|
24781
|
+
async function overrideOptimizerSchema(optimizerSchema) {
|
|
24411
24782
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_OPTIMIZER, {
|
|
24412
24783
|
optimizerSchema,
|
|
24413
24784
|
});
|
|
@@ -24474,7 +24845,7 @@ async function overrideOptimizer(optimizerSchema) {
|
|
|
24474
24845
|
* });
|
|
24475
24846
|
* ```
|
|
24476
24847
|
*/
|
|
24477
|
-
async function
|
|
24848
|
+
async function overrideActionSchema(actionSchema) {
|
|
24478
24849
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_ACTION, {
|
|
24479
24850
|
actionSchema,
|
|
24480
24851
|
});
|
|
@@ -24482,13 +24853,13 @@ async function overrideAction(actionSchema) {
|
|
|
24482
24853
|
return backtest$1.actionSchemaService.override(actionSchema.actionName, actionSchema);
|
|
24483
24854
|
}
|
|
24484
24855
|
|
|
24485
|
-
const LIST_EXCHANGES_METHOD_NAME = "list.
|
|
24486
|
-
const LIST_STRATEGIES_METHOD_NAME = "list.
|
|
24487
|
-
const LIST_FRAMES_METHOD_NAME = "list.
|
|
24488
|
-
const LIST_WALKERS_METHOD_NAME = "list.
|
|
24489
|
-
const LIST_SIZINGS_METHOD_NAME = "list.
|
|
24490
|
-
const LIST_RISKS_METHOD_NAME = "list.
|
|
24491
|
-
const LIST_OPTIMIZERS_METHOD_NAME = "list.
|
|
24856
|
+
const LIST_EXCHANGES_METHOD_NAME = "list.listExchangeSchema";
|
|
24857
|
+
const LIST_STRATEGIES_METHOD_NAME = "list.listStrategySchema";
|
|
24858
|
+
const LIST_FRAMES_METHOD_NAME = "list.listFrameSchema";
|
|
24859
|
+
const LIST_WALKERS_METHOD_NAME = "list.listWalkerSchema";
|
|
24860
|
+
const LIST_SIZINGS_METHOD_NAME = "list.listSizingSchema";
|
|
24861
|
+
const LIST_RISKS_METHOD_NAME = "list.listRiskSchema";
|
|
24862
|
+
const LIST_OPTIMIZERS_METHOD_NAME = "list.listOptimizerSchema";
|
|
24492
24863
|
/**
|
|
24493
24864
|
* Returns a list of all registered exchange schemas.
|
|
24494
24865
|
*
|
|
@@ -24514,7 +24885,7 @@ const LIST_OPTIMIZERS_METHOD_NAME = "list.listOptimizers";
|
|
|
24514
24885
|
* // [{ exchangeName: "binance", note: "Binance cryptocurrency exchange", ... }]
|
|
24515
24886
|
* ```
|
|
24516
24887
|
*/
|
|
24517
|
-
async function
|
|
24888
|
+
async function listExchangeSchema() {
|
|
24518
24889
|
backtest$1.loggerService.log(LIST_EXCHANGES_METHOD_NAME);
|
|
24519
24890
|
return await backtest$1.exchangeValidationService.list();
|
|
24520
24891
|
}
|
|
@@ -24548,7 +24919,7 @@ async function listExchanges() {
|
|
|
24548
24919
|
* // [{ strategyName: "my-strategy", note: "Simple moving average...", ... }]
|
|
24549
24920
|
* ```
|
|
24550
24921
|
*/
|
|
24551
|
-
async function
|
|
24922
|
+
async function listStrategySchema() {
|
|
24552
24923
|
backtest$1.loggerService.log(LIST_STRATEGIES_METHOD_NAME);
|
|
24553
24924
|
return await backtest$1.strategyValidationService.list();
|
|
24554
24925
|
}
|
|
@@ -24577,7 +24948,7 @@ async function listStrategies() {
|
|
|
24577
24948
|
* // [{ frameName: "1d-backtest", note: "One day backtest...", ... }]
|
|
24578
24949
|
* ```
|
|
24579
24950
|
*/
|
|
24580
|
-
async function
|
|
24951
|
+
async function listFrameSchema() {
|
|
24581
24952
|
backtest$1.loggerService.log(LIST_FRAMES_METHOD_NAME);
|
|
24582
24953
|
return await backtest$1.frameValidationService.list();
|
|
24583
24954
|
}
|
|
@@ -24607,7 +24978,7 @@ async function listFrames() {
|
|
|
24607
24978
|
* // [{ walkerName: "llm-prompt-optimizer", note: "Compare LLM...", ... }]
|
|
24608
24979
|
* ```
|
|
24609
24980
|
*/
|
|
24610
|
-
async function
|
|
24981
|
+
async function listWalkerSchema() {
|
|
24611
24982
|
backtest$1.loggerService.log(LIST_WALKERS_METHOD_NAME);
|
|
24612
24983
|
return await backtest$1.walkerValidationService.list();
|
|
24613
24984
|
}
|
|
@@ -24646,7 +25017,7 @@ async function listWalkers() {
|
|
|
24646
25017
|
* // ]
|
|
24647
25018
|
* ```
|
|
24648
25019
|
*/
|
|
24649
|
-
async function
|
|
25020
|
+
async function listSizingSchema() {
|
|
24650
25021
|
backtest$1.loggerService.log(LIST_SIZINGS_METHOD_NAME);
|
|
24651
25022
|
return await backtest$1.sizingValidationService.list();
|
|
24652
25023
|
}
|
|
@@ -24682,7 +25053,7 @@ async function listSizings() {
|
|
|
24682
25053
|
* // ]
|
|
24683
25054
|
* ```
|
|
24684
25055
|
*/
|
|
24685
|
-
async function
|
|
25056
|
+
async function listRiskSchema() {
|
|
24686
25057
|
backtest$1.loggerService.log(LIST_RISKS_METHOD_NAME);
|
|
24687
25058
|
return await backtest$1.riskValidationService.list();
|
|
24688
25059
|
}
|
|
@@ -24722,7 +25093,7 @@ async function listRisks() {
|
|
|
24722
25093
|
* // [{ optimizerName: "llm-strategy-generator", note: "Generates...", ... }]
|
|
24723
25094
|
* ```
|
|
24724
25095
|
*/
|
|
24725
|
-
async function
|
|
25096
|
+
async function listOptimizerSchema() {
|
|
24726
25097
|
backtest$1.loggerService.log(LIST_OPTIMIZERS_METHOD_NAME);
|
|
24727
25098
|
return await backtest$1.optimizerValidationService.list();
|
|
24728
25099
|
}
|
|
@@ -24757,8 +25128,10 @@ const LISTEN_BREAKEVEN_METHOD_NAME = "event.listenBreakeven";
|
|
|
24757
25128
|
const LISTEN_BREAKEVEN_ONCE_METHOD_NAME = "event.listenBreakevenOnce";
|
|
24758
25129
|
const LISTEN_RISK_METHOD_NAME = "event.listenRisk";
|
|
24759
25130
|
const LISTEN_RISK_ONCE_METHOD_NAME = "event.listenRiskOnce";
|
|
24760
|
-
const
|
|
24761
|
-
const
|
|
25131
|
+
const LISTEN_SCHEDULE_PING_METHOD_NAME = "event.listenSchedulePing";
|
|
25132
|
+
const LISTEN_SCHEDULE_PING_ONCE_METHOD_NAME = "event.listenSchedulePingOnce";
|
|
25133
|
+
const LISTEN_ACTIVE_PING_METHOD_NAME = "event.listenActivePing";
|
|
25134
|
+
const LISTEN_ACTIVE_PING_ONCE_METHOD_NAME = "event.listenActivePingOnce";
|
|
24762
25135
|
/**
|
|
24763
25136
|
* Subscribes to all signal events with queued async processing.
|
|
24764
25137
|
*
|
|
@@ -25457,7 +25830,7 @@ function listenValidation(fn) {
|
|
|
25457
25830
|
* unsubscribe();
|
|
25458
25831
|
* ```
|
|
25459
25832
|
*/
|
|
25460
|
-
function
|
|
25833
|
+
function listenPartialProfitAvailable(fn) {
|
|
25461
25834
|
backtest$1.loggerService.log(LISTEN_PARTIAL_PROFIT_METHOD_NAME);
|
|
25462
25835
|
return partialProfitSubject.subscribe(functoolsKit.queued(async (event) => fn(event)));
|
|
25463
25836
|
}
|
|
@@ -25491,7 +25864,7 @@ function listenPartialProfit(fn) {
|
|
|
25491
25864
|
* cancel();
|
|
25492
25865
|
* ```
|
|
25493
25866
|
*/
|
|
25494
|
-
function
|
|
25867
|
+
function listenPartialProfitAvailableOnce(filterFn, fn) {
|
|
25495
25868
|
backtest$1.loggerService.log(LISTEN_PARTIAL_PROFIT_ONCE_METHOD_NAME);
|
|
25496
25869
|
return partialProfitSubject.filter(filterFn).once(fn);
|
|
25497
25870
|
}
|
|
@@ -25519,7 +25892,7 @@ function listenPartialProfitOnce(filterFn, fn) {
|
|
|
25519
25892
|
* unsubscribe();
|
|
25520
25893
|
* ```
|
|
25521
25894
|
*/
|
|
25522
|
-
function
|
|
25895
|
+
function listenPartialLossAvailable(fn) {
|
|
25523
25896
|
backtest$1.loggerService.log(LISTEN_PARTIAL_LOSS_METHOD_NAME);
|
|
25524
25897
|
return partialLossSubject.subscribe(functoolsKit.queued(async (event) => fn(event)));
|
|
25525
25898
|
}
|
|
@@ -25553,7 +25926,7 @@ function listenPartialLoss(fn) {
|
|
|
25553
25926
|
* cancel();
|
|
25554
25927
|
* ```
|
|
25555
25928
|
*/
|
|
25556
|
-
function
|
|
25929
|
+
function listenPartialLossAvailableOnce(filterFn, fn) {
|
|
25557
25930
|
backtest$1.loggerService.log(LISTEN_PARTIAL_LOSS_ONCE_METHOD_NAME);
|
|
25558
25931
|
return partialLossSubject.filter(filterFn).once(fn);
|
|
25559
25932
|
}
|
|
@@ -25583,7 +25956,7 @@ function listenPartialLossOnce(filterFn, fn) {
|
|
|
25583
25956
|
* unsubscribe();
|
|
25584
25957
|
* ```
|
|
25585
25958
|
*/
|
|
25586
|
-
function
|
|
25959
|
+
function listenBreakevenAvailable(fn) {
|
|
25587
25960
|
backtest$1.loggerService.log(LISTEN_BREAKEVEN_METHOD_NAME);
|
|
25588
25961
|
return breakevenSubject.subscribe(functoolsKit.queued(async (event) => fn(event)));
|
|
25589
25962
|
}
|
|
@@ -25617,7 +25990,7 @@ function listenBreakeven(fn) {
|
|
|
25617
25990
|
* cancel();
|
|
25618
25991
|
* ```
|
|
25619
25992
|
*/
|
|
25620
|
-
function
|
|
25993
|
+
function listenBreakevenAvailableOnce(filterFn, fn) {
|
|
25621
25994
|
backtest$1.loggerService.log(LISTEN_BREAKEVEN_ONCE_METHOD_NAME);
|
|
25622
25995
|
return breakevenSubject.filter(filterFn).once(fn);
|
|
25623
25996
|
}
|
|
@@ -25713,9 +26086,9 @@ function listenRiskOnce(filterFn, fn) {
|
|
|
25713
26086
|
* unsubscribe();
|
|
25714
26087
|
* ```
|
|
25715
26088
|
*/
|
|
25716
|
-
function
|
|
25717
|
-
backtest$1.loggerService.log(
|
|
25718
|
-
return
|
|
26089
|
+
function listenSchedulePing(fn) {
|
|
26090
|
+
backtest$1.loggerService.log(LISTEN_SCHEDULE_PING_METHOD_NAME);
|
|
26091
|
+
return schedulePingSubject.subscribe(functoolsKit.queued(async (event) => fn(event)));
|
|
25719
26092
|
}
|
|
25720
26093
|
/**
|
|
25721
26094
|
* Subscribes to filtered ping events with one-time execution.
|
|
@@ -25747,9 +26120,74 @@ function listenPing(fn) {
|
|
|
25747
26120
|
* cancel();
|
|
25748
26121
|
* ```
|
|
25749
26122
|
*/
|
|
25750
|
-
function
|
|
25751
|
-
backtest$1.loggerService.log(
|
|
25752
|
-
return
|
|
26123
|
+
function listenSchedulePingOnce(filterFn, fn) {
|
|
26124
|
+
backtest$1.loggerService.log(LISTEN_SCHEDULE_PING_ONCE_METHOD_NAME);
|
|
26125
|
+
return schedulePingSubject.filter(filterFn).once(fn);
|
|
26126
|
+
}
|
|
26127
|
+
/**
|
|
26128
|
+
* Subscribes to active ping events with queued async processing.
|
|
26129
|
+
*
|
|
26130
|
+
* Listens for active pending signal monitoring events emitted every minute.
|
|
26131
|
+
* Useful for tracking active signal lifecycle and implementing dynamic management logic.
|
|
26132
|
+
*
|
|
26133
|
+
* Events are processed sequentially in order received, even if callback is async.
|
|
26134
|
+
* Uses queued wrapper to prevent concurrent execution of the callback.
|
|
26135
|
+
*
|
|
26136
|
+
* @param fn - Callback function to handle active ping events
|
|
26137
|
+
* @returns Unsubscribe function to stop listening
|
|
26138
|
+
*
|
|
26139
|
+
* @example
|
|
26140
|
+
* ```typescript
|
|
26141
|
+
* import { listenActivePing } from "./function/event";
|
|
26142
|
+
*
|
|
26143
|
+
* const unsubscribe = listenActivePing((event) => {
|
|
26144
|
+
* console.log(`[${event.backtest ? "Backtest" : "Live"}] Active Ping`);
|
|
26145
|
+
* console.log(`Symbol: ${event.symbol}, Strategy: ${event.strategyName}`);
|
|
26146
|
+
* console.log(`Signal ID: ${event.data.id}, Position: ${event.data.position}`);
|
|
26147
|
+
* console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
|
|
26148
|
+
* });
|
|
26149
|
+
*
|
|
26150
|
+
* // Later: stop listening
|
|
26151
|
+
* unsubscribe();
|
|
26152
|
+
* ```
|
|
26153
|
+
*/
|
|
26154
|
+
function listenActivePing(fn) {
|
|
26155
|
+
backtest$1.loggerService.log(LISTEN_ACTIVE_PING_METHOD_NAME);
|
|
26156
|
+
return activePingSubject.subscribe(functoolsKit.queued(async (event) => fn(event)));
|
|
26157
|
+
}
|
|
26158
|
+
/**
|
|
26159
|
+
* Subscribes to filtered active ping events with one-time execution.
|
|
26160
|
+
*
|
|
26161
|
+
* Listens for events matching the filter predicate, then executes callback once
|
|
26162
|
+
* and automatically unsubscribes. Useful for waiting for specific active ping conditions.
|
|
26163
|
+
*
|
|
26164
|
+
* @param filterFn - Predicate to filter which events trigger the callback
|
|
26165
|
+
* @param fn - Callback function to handle the filtered event (called only once)
|
|
26166
|
+
* @returns Unsubscribe function to cancel the listener before it fires
|
|
26167
|
+
*
|
|
26168
|
+
* @example
|
|
26169
|
+
* ```typescript
|
|
26170
|
+
* import { listenActivePingOnce } from "./function/event";
|
|
26171
|
+
*
|
|
26172
|
+
* // Wait for first active ping on BTCUSDT
|
|
26173
|
+
* listenActivePingOnce(
|
|
26174
|
+
* (event) => event.symbol === "BTCUSDT",
|
|
26175
|
+
* (event) => console.log("First BTCUSDT active ping received")
|
|
26176
|
+
* );
|
|
26177
|
+
*
|
|
26178
|
+
* // Wait for active ping in backtest mode
|
|
26179
|
+
* const cancel = listenActivePingOnce(
|
|
26180
|
+
* (event) => event.backtest === true,
|
|
26181
|
+
* (event) => console.log("Backtest active ping received at", new Date(event.timestamp))
|
|
26182
|
+
* );
|
|
26183
|
+
*
|
|
26184
|
+
* // Cancel if needed before event fires
|
|
26185
|
+
* cancel();
|
|
26186
|
+
* ```
|
|
26187
|
+
*/
|
|
26188
|
+
function listenActivePingOnce(filterFn, fn) {
|
|
26189
|
+
backtest$1.loggerService.log(LISTEN_ACTIVE_PING_ONCE_METHOD_NAME);
|
|
26190
|
+
return activePingSubject.filter(filterFn).once(fn);
|
|
25753
26191
|
}
|
|
25754
26192
|
|
|
25755
26193
|
const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
@@ -25819,7 +26257,7 @@ const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
|
25819
26257
|
* // ./dump/strategy/{uuid}/06_llm_output.md (final signal)
|
|
25820
26258
|
* ```
|
|
25821
26259
|
*/
|
|
25822
|
-
async function
|
|
26260
|
+
async function dumpSignalData(signalId, history, signal, outputDir = "./dump/strategy") {
|
|
25823
26261
|
backtest$1.loggerService.info(DUMP_SIGNAL_METHOD_NAME, {
|
|
25824
26262
|
signalId,
|
|
25825
26263
|
history,
|
|
@@ -25839,11 +26277,12 @@ const BACKTEST_METHOD_NAME_GET_STATUS = "BacktestUtils.getStatus";
|
|
|
25839
26277
|
const BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL = "BacktestUtils.getPendingSignal";
|
|
25840
26278
|
const BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL = "BacktestUtils.getScheduledSignal";
|
|
25841
26279
|
const BACKTEST_METHOD_NAME_GET_BREAKEVEN = "BacktestUtils.getBreakeven";
|
|
25842
|
-
const
|
|
25843
|
-
const
|
|
25844
|
-
const
|
|
25845
|
-
const
|
|
25846
|
-
const
|
|
26280
|
+
const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
|
|
26281
|
+
const BACKTEST_METHOD_NAME_CANCEL = "BacktestUtils.commitCancel";
|
|
26282
|
+
const BACKTEST_METHOD_NAME_PARTIAL_PROFIT = "BacktestUtils.commitPartialProfit";
|
|
26283
|
+
const BACKTEST_METHOD_NAME_PARTIAL_LOSS = "BacktestUtils.commitPartialLoss";
|
|
26284
|
+
const BACKTEST_METHOD_NAME_TRAILING_STOP = "BacktestUtils.commitTrailingStop";
|
|
26285
|
+
const BACKTEST_METHOD_NAME_TRAILING_PROFIT = "BacktestUtils.commitTrailingTake";
|
|
25847
26286
|
const BACKTEST_METHOD_NAME_GET_DATA = "BacktestUtils.getData";
|
|
25848
26287
|
/**
|
|
25849
26288
|
* Internal task function that runs backtest and handles completion.
|
|
@@ -26367,14 +26806,14 @@ class BacktestUtils {
|
|
|
26367
26806
|
* @example
|
|
26368
26807
|
* ```typescript
|
|
26369
26808
|
* // Cancel scheduled signal with custom ID
|
|
26370
|
-
* await Backtest.
|
|
26809
|
+
* await Backtest.commitCancel("BTCUSDT", "my-strategy", {
|
|
26371
26810
|
* exchangeName: "binance",
|
|
26372
26811
|
* frameName: "frame1",
|
|
26373
26812
|
* strategyName: "my-strategy"
|
|
26374
26813
|
* }, "manual-cancel-001");
|
|
26375
26814
|
* ```
|
|
26376
26815
|
*/
|
|
26377
|
-
this.
|
|
26816
|
+
this.commitCancel = async (symbol, context, cancelId) => {
|
|
26378
26817
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_CANCEL, {
|
|
26379
26818
|
symbol,
|
|
26380
26819
|
context,
|
|
@@ -26412,7 +26851,7 @@ class BacktestUtils {
|
|
|
26412
26851
|
* @example
|
|
26413
26852
|
* ```typescript
|
|
26414
26853
|
* // Close 30% of LONG position at profit
|
|
26415
|
-
* const success = await Backtest.
|
|
26854
|
+
* const success = await Backtest.commitPartialProfit("BTCUSDT", 30, 45000, {
|
|
26416
26855
|
* exchangeName: "binance",
|
|
26417
26856
|
* frameName: "frame1",
|
|
26418
26857
|
* strategyName: "my-strategy"
|
|
@@ -26422,7 +26861,7 @@ class BacktestUtils {
|
|
|
26422
26861
|
* }
|
|
26423
26862
|
* ```
|
|
26424
26863
|
*/
|
|
26425
|
-
this.
|
|
26864
|
+
this.commitPartialProfit = async (symbol, percentToClose, currentPrice, context) => {
|
|
26426
26865
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_PARTIAL_PROFIT, {
|
|
26427
26866
|
symbol,
|
|
26428
26867
|
percentToClose,
|
|
@@ -26461,7 +26900,7 @@ class BacktestUtils {
|
|
|
26461
26900
|
* @example
|
|
26462
26901
|
* ```typescript
|
|
26463
26902
|
* // Close 40% of LONG position at loss
|
|
26464
|
-
* const success = await Backtest.
|
|
26903
|
+
* const success = await Backtest.commitPartialLoss("BTCUSDT", 40, 38000, {
|
|
26465
26904
|
* exchangeName: "binance",
|
|
26466
26905
|
* frameName: "frame1",
|
|
26467
26906
|
* strategyName: "my-strategy"
|
|
@@ -26471,7 +26910,7 @@ class BacktestUtils {
|
|
|
26471
26910
|
* }
|
|
26472
26911
|
* ```
|
|
26473
26912
|
*/
|
|
26474
|
-
this.
|
|
26913
|
+
this.commitPartialLoss = async (symbol, percentToClose, currentPrice, context) => {
|
|
26475
26914
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_PARTIAL_LOSS, {
|
|
26476
26915
|
symbol,
|
|
26477
26916
|
percentToClose,
|
|
@@ -26519,7 +26958,7 @@ class BacktestUtils {
|
|
|
26519
26958
|
* // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
|
|
26520
26959
|
*
|
|
26521
26960
|
* // First call: tighten by 5%
|
|
26522
|
-
* await Backtest.
|
|
26961
|
+
* await Backtest.commitTrailingStop("BTCUSDT", -5, 102, {
|
|
26523
26962
|
* exchangeName: "binance",
|
|
26524
26963
|
* frameName: "frame1",
|
|
26525
26964
|
* strategyName: "my-strategy"
|
|
@@ -26527,15 +26966,15 @@ class BacktestUtils {
|
|
|
26527
26966
|
* // newDistance = 10% - 5% = 5%, newSL = 95
|
|
26528
26967
|
*
|
|
26529
26968
|
* // Second call: try weaker protection (smaller percentShift)
|
|
26530
|
-
* await Backtest.
|
|
26969
|
+
* await Backtest.commitTrailingStop("BTCUSDT", -3, 102, context);
|
|
26531
26970
|
* // SKIPPED: newSL=97 < 95 (worse protection, larger % absorbs smaller)
|
|
26532
26971
|
*
|
|
26533
26972
|
* // Third call: stronger protection (larger percentShift)
|
|
26534
|
-
* await Backtest.
|
|
26973
|
+
* await Backtest.commitTrailingStop("BTCUSDT", -7, 102, context);
|
|
26535
26974
|
* // ACCEPTED: newDistance = 10% - 7% = 3%, newSL = 97 > 95 (better protection)
|
|
26536
26975
|
* ```
|
|
26537
26976
|
*/
|
|
26538
|
-
this.
|
|
26977
|
+
this.commitTrailingStop = async (symbol, percentShift, currentPrice, context) => {
|
|
26539
26978
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_TRAILING_STOP, {
|
|
26540
26979
|
symbol,
|
|
26541
26980
|
percentShift,
|
|
@@ -26583,7 +27022,7 @@ class BacktestUtils {
|
|
|
26583
27022
|
* // LONG: entry=100, originalTP=110, distance=10%, currentPrice=102
|
|
26584
27023
|
*
|
|
26585
27024
|
* // First call: bring TP closer by 3%
|
|
26586
|
-
* await Backtest.
|
|
27025
|
+
* await Backtest.commitTrailingTake("BTCUSDT", -3, 102, {
|
|
26587
27026
|
* exchangeName: "binance",
|
|
26588
27027
|
* frameName: "frame1",
|
|
26589
27028
|
* strategyName: "my-strategy"
|
|
@@ -26591,15 +27030,15 @@ class BacktestUtils {
|
|
|
26591
27030
|
* // newDistance = 10% - 3% = 7%, newTP = 107
|
|
26592
27031
|
*
|
|
26593
27032
|
* // Second call: try to move TP further (less conservative)
|
|
26594
|
-
* await Backtest.
|
|
27033
|
+
* await Backtest.commitTrailingTake("BTCUSDT", 2, 102, context);
|
|
26595
27034
|
* // SKIPPED: newTP=112 > 107 (less conservative, larger % absorbs smaller)
|
|
26596
27035
|
*
|
|
26597
27036
|
* // Third call: even more conservative
|
|
26598
|
-
* await Backtest.
|
|
27037
|
+
* await Backtest.commitTrailingTake("BTCUSDT", -5, 102, context);
|
|
26599
27038
|
* // ACCEPTED: newDistance = 10% - 5% = 5%, newTP = 105 < 107 (more conservative)
|
|
26600
27039
|
* ```
|
|
26601
27040
|
*/
|
|
26602
|
-
this.
|
|
27041
|
+
this.commitTrailingTake = async (symbol, percentShift, currentPrice, context) => {
|
|
26603
27042
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_TRAILING_PROFIT, {
|
|
26604
27043
|
symbol,
|
|
26605
27044
|
percentShift,
|
|
@@ -26632,7 +27071,7 @@ class BacktestUtils {
|
|
|
26632
27071
|
*
|
|
26633
27072
|
* @example
|
|
26634
27073
|
* ```typescript
|
|
26635
|
-
* const moved = await Backtest.
|
|
27074
|
+
* const moved = await Backtest.commitBreakeven(
|
|
26636
27075
|
* "BTCUSDT",
|
|
26637
27076
|
* 112,
|
|
26638
27077
|
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "1h" }
|
|
@@ -26640,22 +27079,22 @@ class BacktestUtils {
|
|
|
26640
27079
|
* console.log(moved); // true (SL moved to entry price)
|
|
26641
27080
|
* ```
|
|
26642
27081
|
*/
|
|
26643
|
-
this.
|
|
26644
|
-
backtest$1.loggerService.info(
|
|
27082
|
+
this.commitBreakeven = async (symbol, currentPrice, context) => {
|
|
27083
|
+
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_BREAKEVEN, {
|
|
26645
27084
|
symbol,
|
|
26646
27085
|
currentPrice,
|
|
26647
27086
|
context,
|
|
26648
27087
|
});
|
|
26649
|
-
backtest$1.strategyValidationService.validate(context.strategyName,
|
|
26650
|
-
backtest$1.exchangeValidationService.validate(context.exchangeName,
|
|
27088
|
+
backtest$1.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_BREAKEVEN);
|
|
27089
|
+
backtest$1.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_BREAKEVEN);
|
|
26651
27090
|
{
|
|
26652
27091
|
const { riskName, riskList, actions } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
26653
27092
|
riskName &&
|
|
26654
|
-
backtest$1.riskValidationService.validate(riskName,
|
|
27093
|
+
backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_BREAKEVEN);
|
|
26655
27094
|
riskList &&
|
|
26656
|
-
riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName,
|
|
27095
|
+
riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_BREAKEVEN));
|
|
26657
27096
|
actions &&
|
|
26658
|
-
actions.forEach((actionName) => backtest$1.actionValidationService.validate(actionName,
|
|
27097
|
+
actions.forEach((actionName) => backtest$1.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_BREAKEVEN));
|
|
26659
27098
|
}
|
|
26660
27099
|
return await backtest$1.strategyCoreService.breakeven(true, symbol, currentPrice, context);
|
|
26661
27100
|
};
|
|
@@ -26827,11 +27266,12 @@ const LIVE_METHOD_NAME_GET_STATUS = "LiveUtils.getStatus";
|
|
|
26827
27266
|
const LIVE_METHOD_NAME_GET_PENDING_SIGNAL = "LiveUtils.getPendingSignal";
|
|
26828
27267
|
const LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL = "LiveUtils.getScheduledSignal";
|
|
26829
27268
|
const LIVE_METHOD_NAME_GET_BREAKEVEN = "LiveUtils.getBreakeven";
|
|
26830
|
-
const
|
|
26831
|
-
const
|
|
26832
|
-
const
|
|
26833
|
-
const
|
|
26834
|
-
const
|
|
27269
|
+
const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
|
|
27270
|
+
const LIVE_METHOD_NAME_CANCEL = "LiveUtils.commitCancel";
|
|
27271
|
+
const LIVE_METHOD_NAME_PARTIAL_PROFIT = "LiveUtils.commitPartialProfit";
|
|
27272
|
+
const LIVE_METHOD_NAME_PARTIAL_LOSS = "LiveUtils.commitPartialLoss";
|
|
27273
|
+
const LIVE_METHOD_NAME_TRAILING_STOP = "LiveUtils.commitTrailingStop";
|
|
27274
|
+
const LIVE_METHOD_NAME_TRAILING_PROFIT = "LiveUtils.commitTrailingTake";
|
|
26835
27275
|
/**
|
|
26836
27276
|
* Internal task function that runs live trading and handles completion.
|
|
26837
27277
|
* Consumes live trading results and updates instance state flags.
|
|
@@ -27321,14 +27761,14 @@ class LiveUtils {
|
|
|
27321
27761
|
* @example
|
|
27322
27762
|
* ```typescript
|
|
27323
27763
|
* // Cancel scheduled signal in live trading with custom ID
|
|
27324
|
-
* await Live.
|
|
27764
|
+
* await Live.commitCancel("BTCUSDT", "my-strategy", {
|
|
27325
27765
|
* exchangeName: "binance",
|
|
27326
27766
|
* frameName: "",
|
|
27327
27767
|
* strategyName: "my-strategy"
|
|
27328
27768
|
* }, "manual-cancel-001");
|
|
27329
27769
|
* ```
|
|
27330
27770
|
*/
|
|
27331
|
-
this.
|
|
27771
|
+
this.commitCancel = async (symbol, context, cancelId) => {
|
|
27332
27772
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_CANCEL, {
|
|
27333
27773
|
symbol,
|
|
27334
27774
|
context,
|
|
@@ -27367,7 +27807,7 @@ class LiveUtils {
|
|
|
27367
27807
|
* @example
|
|
27368
27808
|
* ```typescript
|
|
27369
27809
|
* // Close 30% of LONG position at profit
|
|
27370
|
-
* const success = await Live.
|
|
27810
|
+
* const success = await Live.commitPartialProfit("BTCUSDT", 30, 45000, {
|
|
27371
27811
|
* exchangeName: "binance",
|
|
27372
27812
|
* strategyName: "my-strategy"
|
|
27373
27813
|
* });
|
|
@@ -27376,7 +27816,7 @@ class LiveUtils {
|
|
|
27376
27816
|
* }
|
|
27377
27817
|
* ```
|
|
27378
27818
|
*/
|
|
27379
|
-
this.
|
|
27819
|
+
this.commitPartialProfit = async (symbol, percentToClose, currentPrice, context) => {
|
|
27380
27820
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_PARTIAL_PROFIT, {
|
|
27381
27821
|
symbol,
|
|
27382
27822
|
percentToClose,
|
|
@@ -27416,7 +27856,7 @@ class LiveUtils {
|
|
|
27416
27856
|
* @example
|
|
27417
27857
|
* ```typescript
|
|
27418
27858
|
* // Close 40% of LONG position at loss
|
|
27419
|
-
* const success = await Live.
|
|
27859
|
+
* const success = await Live.commitPartialLoss("BTCUSDT", 40, 38000, {
|
|
27420
27860
|
* exchangeName: "binance",
|
|
27421
27861
|
* strategyName: "my-strategy"
|
|
27422
27862
|
* });
|
|
@@ -27425,7 +27865,7 @@ class LiveUtils {
|
|
|
27425
27865
|
* }
|
|
27426
27866
|
* ```
|
|
27427
27867
|
*/
|
|
27428
|
-
this.
|
|
27868
|
+
this.commitPartialLoss = async (symbol, percentToClose, currentPrice, context) => {
|
|
27429
27869
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_PARTIAL_LOSS, {
|
|
27430
27870
|
symbol,
|
|
27431
27871
|
percentToClose,
|
|
@@ -27474,22 +27914,22 @@ class LiveUtils {
|
|
|
27474
27914
|
* // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
|
|
27475
27915
|
*
|
|
27476
27916
|
* // First call: tighten by 5%
|
|
27477
|
-
* const success1 = await Live.
|
|
27917
|
+
* const success1 = await Live.commitTrailingStop("BTCUSDT", -5, 102, {
|
|
27478
27918
|
* exchangeName: "binance",
|
|
27479
27919
|
* strategyName: "my-strategy"
|
|
27480
27920
|
* });
|
|
27481
27921
|
* // success1 = true, newDistance = 10% - 5% = 5%, newSL = 95
|
|
27482
27922
|
*
|
|
27483
27923
|
* // Second call: try weaker protection (smaller percentShift)
|
|
27484
|
-
* const success2 = await Live.
|
|
27924
|
+
* const success2 = await Live.commitTrailingStop("BTCUSDT", -3, 102, context);
|
|
27485
27925
|
* // success2 = false (SKIPPED: newSL=97 < 95, worse protection, larger % absorbs smaller)
|
|
27486
27926
|
*
|
|
27487
27927
|
* // Third call: stronger protection (larger percentShift)
|
|
27488
|
-
* const success3 = await Live.
|
|
27928
|
+
* const success3 = await Live.commitTrailingStop("BTCUSDT", -7, 102, context);
|
|
27489
27929
|
* // success3 = true (ACCEPTED: newDistance = 10% - 7% = 3%, newSL = 97 > 95, better protection)
|
|
27490
27930
|
* ```
|
|
27491
27931
|
*/
|
|
27492
|
-
this.
|
|
27932
|
+
this.commitTrailingStop = async (symbol, percentShift, currentPrice, context) => {
|
|
27493
27933
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_TRAILING_STOP, {
|
|
27494
27934
|
symbol,
|
|
27495
27935
|
percentShift,
|
|
@@ -27538,22 +27978,22 @@ class LiveUtils {
|
|
|
27538
27978
|
* // LONG: entry=100, originalTP=110, distance=10%, currentPrice=102
|
|
27539
27979
|
*
|
|
27540
27980
|
* // First call: bring TP closer by 3%
|
|
27541
|
-
* const success1 = await Live.
|
|
27981
|
+
* const success1 = await Live.commitTrailingTake("BTCUSDT", -3, 102, {
|
|
27542
27982
|
* exchangeName: "binance",
|
|
27543
27983
|
* strategyName: "my-strategy"
|
|
27544
27984
|
* });
|
|
27545
27985
|
* // success1 = true, newDistance = 10% - 3% = 7%, newTP = 107
|
|
27546
27986
|
*
|
|
27547
27987
|
* // Second call: try to move TP further (less conservative)
|
|
27548
|
-
* const success2 = await Live.
|
|
27988
|
+
* const success2 = await Live.commitTrailingTake("BTCUSDT", 2, 102, context);
|
|
27549
27989
|
* // success2 = false (SKIPPED: newTP=112 > 107, less conservative, larger % absorbs smaller)
|
|
27550
27990
|
*
|
|
27551
27991
|
* // Third call: even more conservative
|
|
27552
|
-
* const success3 = await Live.
|
|
27992
|
+
* const success3 = await Live.commitTrailingTake("BTCUSDT", -5, 102, context);
|
|
27553
27993
|
* // success3 = true (ACCEPTED: newDistance = 10% - 5% = 5%, newTP = 105 < 107, more conservative)
|
|
27554
27994
|
* ```
|
|
27555
27995
|
*/
|
|
27556
|
-
this.
|
|
27996
|
+
this.commitTrailingTake = async (symbol, percentShift, currentPrice, context) => {
|
|
27557
27997
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_TRAILING_PROFIT, {
|
|
27558
27998
|
symbol,
|
|
27559
27999
|
percentShift,
|
|
@@ -27587,7 +28027,7 @@ class LiveUtils {
|
|
|
27587
28027
|
*
|
|
27588
28028
|
* @example
|
|
27589
28029
|
* ```typescript
|
|
27590
|
-
* const moved = await Live.
|
|
28030
|
+
* const moved = await Live.commitBreakeven(
|
|
27591
28031
|
* "BTCUSDT",
|
|
27592
28032
|
* 112,
|
|
27593
28033
|
* { strategyName: "my-strategy", exchangeName: "binance" }
|
|
@@ -27595,19 +28035,19 @@ class LiveUtils {
|
|
|
27595
28035
|
* console.log(moved); // true (SL moved to entry price)
|
|
27596
28036
|
* ```
|
|
27597
28037
|
*/
|
|
27598
|
-
this.
|
|
27599
|
-
backtest$1.loggerService.info(
|
|
28038
|
+
this.commitBreakeven = async (symbol, currentPrice, context) => {
|
|
28039
|
+
backtest$1.loggerService.info(LIVE_METHOD_NAME_BREAKEVEN, {
|
|
27600
28040
|
symbol,
|
|
27601
28041
|
currentPrice,
|
|
27602
28042
|
context,
|
|
27603
28043
|
});
|
|
27604
|
-
backtest$1.strategyValidationService.validate(context.strategyName,
|
|
27605
|
-
backtest$1.exchangeValidationService.validate(context.exchangeName,
|
|
28044
|
+
backtest$1.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_BREAKEVEN);
|
|
28045
|
+
backtest$1.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_BREAKEVEN);
|
|
27606
28046
|
{
|
|
27607
28047
|
const { riskName, riskList, actions } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
27608
|
-
riskName && backtest$1.riskValidationService.validate(riskName,
|
|
27609
|
-
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName,
|
|
27610
|
-
actions && actions.forEach((actionName) => backtest$1.actionValidationService.validate(actionName,
|
|
28048
|
+
riskName && backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_BREAKEVEN);
|
|
28049
|
+
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_BREAKEVEN));
|
|
28050
|
+
actions && actions.forEach((actionName) => backtest$1.actionValidationService.validate(actionName, LIVE_METHOD_NAME_BREAKEVEN));
|
|
27611
28051
|
}
|
|
27612
28052
|
return await backtest$1.strategyCoreService.breakeven(false, symbol, currentPrice, {
|
|
27613
28053
|
strategyName: context.strategyName,
|
|
@@ -29435,10 +29875,15 @@ class ExchangeInstance {
|
|
|
29435
29875
|
const whenTimestamp = when.getTime();
|
|
29436
29876
|
const sinceTimestamp = since.getTime();
|
|
29437
29877
|
const filteredData = allData.filter((candle) => candle.timestamp >= sinceTimestamp && candle.timestamp <= whenTimestamp);
|
|
29438
|
-
|
|
29439
|
-
|
|
29878
|
+
// Apply distinct by timestamp to remove duplicates
|
|
29879
|
+
const uniqueData = Array.from(new Map(filteredData.map((candle) => [candle.timestamp, candle])).values());
|
|
29880
|
+
if (filteredData.length !== uniqueData.length) {
|
|
29881
|
+
backtest$1.loggerService.warn(`ExchangeInstance Removed ${filteredData.length - uniqueData.length} duplicate candles by timestamp`);
|
|
29440
29882
|
}
|
|
29441
|
-
|
|
29883
|
+
if (uniqueData.length < limit) {
|
|
29884
|
+
backtest$1.loggerService.warn(`ExchangeInstance Expected ${limit} candles, got ${uniqueData.length}`);
|
|
29885
|
+
}
|
|
29886
|
+
return uniqueData;
|
|
29442
29887
|
};
|
|
29443
29888
|
/**
|
|
29444
29889
|
* Calculates VWAP (Volume Weighted Average Price) from last N 1m candles.
|
|
@@ -30617,10 +31062,11 @@ const METHOD_NAME_INIT = "ActionBase.init";
|
|
|
30617
31062
|
const METHOD_NAME_EVENT = "ActionBase.event";
|
|
30618
31063
|
const METHOD_NAME_SIGNAL_LIVE = "ActionBase.signalLive";
|
|
30619
31064
|
const METHOD_NAME_SIGNAL_BACKTEST = "ActionBase.signalBacktest";
|
|
30620
|
-
const
|
|
30621
|
-
const
|
|
30622
|
-
const
|
|
30623
|
-
const
|
|
31065
|
+
const METHOD_NAME_BREAKEVEN_AVAILABLE = "ActionBase.breakevenAvailable";
|
|
31066
|
+
const METHOD_NAME_PARTIAL_PROFIT_AVAILABLE = "ActionBase.partialProfitAvailable";
|
|
31067
|
+
const METHOD_NAME_PARTIAL_LOSS_AVAILABLE = "ActionBase.partialLossAvailable";
|
|
31068
|
+
const METHOD_NAME_PING_SCHEDULED = "ActionBase.pingScheduled";
|
|
31069
|
+
const METHOD_NAME_PING_ACTIVE = "ActionBase.pingActive";
|
|
30624
31070
|
const METHOD_NAME_RISK_REJECTION = "ActionBase.riskRejection";
|
|
30625
31071
|
const METHOD_NAME_DISPOSE = "ActionBase.dispose";
|
|
30626
31072
|
const DEFAULT_SOURCE = "default";
|
|
@@ -30651,10 +31097,11 @@ const DEFAULT_SOURCE = "default";
|
|
|
30651
31097
|
* - signal() - Called on every tick/candle (all modes)
|
|
30652
31098
|
* - signalLive() - Called only in live mode
|
|
30653
31099
|
* - signalBacktest() - Called only in backtest mode
|
|
30654
|
-
* -
|
|
30655
|
-
* -
|
|
30656
|
-
* -
|
|
30657
|
-
* -
|
|
31100
|
+
* - breakevenAvailable() - Called when SL moved to entry
|
|
31101
|
+
* - partialProfitAvailable() - Called on profit milestones (10%, 20%, etc.)
|
|
31102
|
+
* - partialLossAvailable() - Called on loss milestones (-10%, -20%, etc.)
|
|
31103
|
+
* - pingScheduled() - Called every minute during scheduled signal monitoring
|
|
31104
|
+
* - pingActive() - Called every minute during active pending signal monitoring
|
|
30658
31105
|
* - riskRejection() - Called when signal rejected by risk management
|
|
30659
31106
|
*
|
|
30660
31107
|
* @example
|
|
@@ -30695,7 +31142,7 @@ const DEFAULT_SOURCE = "default";
|
|
|
30695
31142
|
* }
|
|
30696
31143
|
*
|
|
30697
31144
|
* // Register the action
|
|
30698
|
-
*
|
|
31145
|
+
* addActionSchema({
|
|
30699
31146
|
* actionName: "telegram-notifier",
|
|
30700
31147
|
* handler: TelegramNotifier
|
|
30701
31148
|
* });
|
|
@@ -30737,11 +31184,13 @@ class ActionBase {
|
|
|
30737
31184
|
* @param strategyName - Strategy identifier this action is attached to
|
|
30738
31185
|
* @param frameName - Timeframe identifier this action is attached to
|
|
30739
31186
|
* @param actionName - Action identifier
|
|
31187
|
+
* @param backtest - If running in backtest
|
|
30740
31188
|
*/
|
|
30741
|
-
constructor(strategyName, frameName, actionName) {
|
|
31189
|
+
constructor(strategyName, frameName, actionName, backtest) {
|
|
30742
31190
|
this.strategyName = strategyName;
|
|
30743
31191
|
this.frameName = frameName;
|
|
30744
31192
|
this.actionName = actionName;
|
|
31193
|
+
this.backtest = backtest;
|
|
30745
31194
|
}
|
|
30746
31195
|
/**
|
|
30747
31196
|
* Initializes the action handler.
|
|
@@ -30865,7 +31314,7 @@ class ActionBase {
|
|
|
30865
31314
|
* Called once per signal when price moves far enough to cover fees and slippage.
|
|
30866
31315
|
* Breakeven threshold: (CC_PERCENT_SLIPPAGE + CC_PERCENT_FEE) * 2 + CC_BREAKEVEN_THRESHOLD
|
|
30867
31316
|
*
|
|
30868
|
-
* Triggered by: ActionCoreService.
|
|
31317
|
+
* Triggered by: ActionCoreService.breakevenAvailable() via BreakevenConnectionService
|
|
30869
31318
|
* Source: breakevenSubject.next() in CREATE_COMMIT_BREAKEVEN_FN callback
|
|
30870
31319
|
* Frequency: Once per signal when threshold reached
|
|
30871
31320
|
*
|
|
@@ -30875,7 +31324,7 @@ class ActionBase {
|
|
|
30875
31324
|
*
|
|
30876
31325
|
* @example
|
|
30877
31326
|
* ```typescript
|
|
30878
|
-
* async
|
|
31327
|
+
* async breakevenAvailable(event: BreakevenContract) {
|
|
30879
31328
|
* await this.telegram.send(
|
|
30880
31329
|
* `[${event.strategyName}] Breakeven reached! ` +
|
|
30881
31330
|
* `Signal: ${event.data.side} @ ${event.currentPrice}`
|
|
@@ -30883,8 +31332,8 @@ class ActionBase {
|
|
|
30883
31332
|
* }
|
|
30884
31333
|
* ```
|
|
30885
31334
|
*/
|
|
30886
|
-
|
|
30887
|
-
backtest$1.loggerService.info(
|
|
31335
|
+
breakevenAvailable(event, source = DEFAULT_SOURCE) {
|
|
31336
|
+
backtest$1.loggerService.info(METHOD_NAME_BREAKEVEN_AVAILABLE, {
|
|
30888
31337
|
event,
|
|
30889
31338
|
source,
|
|
30890
31339
|
});
|
|
@@ -30895,7 +31344,7 @@ class ActionBase {
|
|
|
30895
31344
|
* Called once per profit level per signal (deduplicated).
|
|
30896
31345
|
* Use to track profit milestones and adjust position management.
|
|
30897
31346
|
*
|
|
30898
|
-
* Triggered by: ActionCoreService.
|
|
31347
|
+
* Triggered by: ActionCoreService.partialProfitAvailable() via PartialConnectionService
|
|
30899
31348
|
* Source: partialProfitSubject.next() in CREATE_COMMIT_PROFIT_FN callback
|
|
30900
31349
|
* Frequency: Once per profit level per signal
|
|
30901
31350
|
*
|
|
@@ -30905,7 +31354,7 @@ class ActionBase {
|
|
|
30905
31354
|
*
|
|
30906
31355
|
* @example
|
|
30907
31356
|
* ```typescript
|
|
30908
|
-
* async
|
|
31357
|
+
* async partialProfitAvailable(event: PartialProfitContract) {
|
|
30909
31358
|
* await this.telegram.send(
|
|
30910
31359
|
* `[${event.strategyName}] Profit ${event.level}% reached! ` +
|
|
30911
31360
|
* `Current price: ${event.currentPrice}`
|
|
@@ -30914,8 +31363,8 @@ class ActionBase {
|
|
|
30914
31363
|
* }
|
|
30915
31364
|
* ```
|
|
30916
31365
|
*/
|
|
30917
|
-
|
|
30918
|
-
backtest$1.loggerService.info(
|
|
31366
|
+
partialProfitAvailable(event, source = DEFAULT_SOURCE) {
|
|
31367
|
+
backtest$1.loggerService.info(METHOD_NAME_PARTIAL_PROFIT_AVAILABLE, {
|
|
30919
31368
|
event,
|
|
30920
31369
|
source,
|
|
30921
31370
|
});
|
|
@@ -30926,7 +31375,7 @@ class ActionBase {
|
|
|
30926
31375
|
* Called once per loss level per signal (deduplicated).
|
|
30927
31376
|
* Use to track loss milestones and implement risk management actions.
|
|
30928
31377
|
*
|
|
30929
|
-
* Triggered by: ActionCoreService.
|
|
31378
|
+
* Triggered by: ActionCoreService.partialLossAvailable() via PartialConnectionService
|
|
30930
31379
|
* Source: partialLossSubject.next() in CREATE_COMMIT_LOSS_FN callback
|
|
30931
31380
|
* Frequency: Once per loss level per signal
|
|
30932
31381
|
*
|
|
@@ -30936,7 +31385,7 @@ class ActionBase {
|
|
|
30936
31385
|
*
|
|
30937
31386
|
* @example
|
|
30938
31387
|
* ```typescript
|
|
30939
|
-
* async
|
|
31388
|
+
* async partialLossAvailable(event: PartialLossContract) {
|
|
30940
31389
|
* await this.telegram.send(
|
|
30941
31390
|
* `[${event.strategyName}] Loss ${event.level}% reached! ` +
|
|
30942
31391
|
* `Current price: ${event.currentPrice}`
|
|
@@ -30945,37 +31394,66 @@ class ActionBase {
|
|
|
30945
31394
|
* }
|
|
30946
31395
|
* ```
|
|
30947
31396
|
*/
|
|
30948
|
-
|
|
30949
|
-
backtest$1.loggerService.info(
|
|
31397
|
+
partialLossAvailable(event, source = DEFAULT_SOURCE) {
|
|
31398
|
+
backtest$1.loggerService.info(METHOD_NAME_PARTIAL_LOSS_AVAILABLE, {
|
|
30950
31399
|
event,
|
|
30951
31400
|
source,
|
|
30952
31401
|
});
|
|
30953
31402
|
}
|
|
30954
31403
|
/**
|
|
30955
|
-
* Handles ping events during scheduled signal monitoring.
|
|
31404
|
+
* Handles scheduled ping events during scheduled signal monitoring.
|
|
30956
31405
|
*
|
|
30957
31406
|
* Called every minute while a scheduled signal is waiting for activation.
|
|
30958
31407
|
* Use to monitor pending signals and track wait time.
|
|
30959
31408
|
*
|
|
30960
|
-
* Triggered by: ActionCoreService.
|
|
30961
|
-
* Source:
|
|
31409
|
+
* Triggered by: ActionCoreService.pingScheduled() via StrategyConnectionService
|
|
31410
|
+
* Source: schedulePingSubject.next() in CREATE_COMMIT_SCHEDULE_PING_FN callback
|
|
30962
31411
|
* Frequency: Every minute while scheduled signal is waiting
|
|
30963
31412
|
*
|
|
30964
|
-
* Default implementation: Logs ping event.
|
|
31413
|
+
* Default implementation: Logs scheduled ping event.
|
|
30965
31414
|
*
|
|
30966
31415
|
* @param event - Scheduled signal monitoring data with symbol, strategy info, signal data, timestamp
|
|
30967
31416
|
*
|
|
30968
31417
|
* @example
|
|
30969
31418
|
* ```typescript
|
|
30970
|
-
*
|
|
31419
|
+
* pingScheduled(event: SchedulePingContract) {
|
|
30971
31420
|
* const waitTime = Date.now() - event.data.timestampScheduled;
|
|
30972
31421
|
* const waitMinutes = Math.floor(waitTime / 60000);
|
|
30973
31422
|
* console.log(`Scheduled signal waiting ${waitMinutes} minutes`);
|
|
30974
31423
|
* }
|
|
30975
31424
|
* ```
|
|
30976
31425
|
*/
|
|
30977
|
-
|
|
30978
|
-
backtest$1.loggerService.info(
|
|
31426
|
+
pingScheduled(event, source = DEFAULT_SOURCE) {
|
|
31427
|
+
backtest$1.loggerService.info(METHOD_NAME_PING_SCHEDULED, {
|
|
31428
|
+
event,
|
|
31429
|
+
source,
|
|
31430
|
+
});
|
|
31431
|
+
}
|
|
31432
|
+
/**
|
|
31433
|
+
* Handles active ping events during active pending signal monitoring.
|
|
31434
|
+
*
|
|
31435
|
+
* Called every minute while a pending signal is active (position open).
|
|
31436
|
+
* Use to monitor active positions and track lifecycle.
|
|
31437
|
+
*
|
|
31438
|
+
* Triggered by: ActionCoreService.pingActive() via StrategyConnectionService
|
|
31439
|
+
* Source: activePingSubject.next() in CREATE_COMMIT_ACTIVE_PING_FN callback
|
|
31440
|
+
* Frequency: Every minute while pending signal is active
|
|
31441
|
+
*
|
|
31442
|
+
* Default implementation: Logs active ping event.
|
|
31443
|
+
*
|
|
31444
|
+
* @param event - Active pending signal monitoring data with symbol, strategy info, signal data, timestamp
|
|
31445
|
+
*
|
|
31446
|
+
* @example
|
|
31447
|
+
* ```typescript
|
|
31448
|
+
* pingActive(event: ActivePingContract) {
|
|
31449
|
+
* const holdTime = Date.now() - event.data.pendingAt;
|
|
31450
|
+
* const holdMinutes = Math.floor(holdTime / 60000);
|
|
31451
|
+
* console.log(`Active signal holding ${holdMinutes} minutes`);
|
|
31452
|
+
* }
|
|
31453
|
+
* ```
|
|
31454
|
+
*/
|
|
31455
|
+
pingActive(event, source = DEFAULT_SOURCE) {
|
|
31456
|
+
backtest$1.loggerService.info(METHOD_NAME_PING_ACTIVE, {
|
|
30979
31457
|
event,
|
|
30980
31458
|
source,
|
|
30981
31459
|
});
|
|
@@ -31123,51 +31601,59 @@ exports.ReportBase = ReportBase;
|
|
|
31123
31601
|
exports.Risk = Risk;
|
|
31124
31602
|
exports.Schedule = Schedule;
|
|
31125
31603
|
exports.Walker = Walker;
|
|
31126
|
-
exports.
|
|
31127
|
-
exports.
|
|
31128
|
-
exports.
|
|
31129
|
-
exports.
|
|
31130
|
-
exports.
|
|
31131
|
-
exports.
|
|
31132
|
-
exports.
|
|
31133
|
-
exports.
|
|
31134
|
-
exports.
|
|
31135
|
-
exports.
|
|
31136
|
-
exports.
|
|
31604
|
+
exports.addActionSchema = addActionSchema;
|
|
31605
|
+
exports.addExchangeSchema = addExchangeSchema;
|
|
31606
|
+
exports.addFrameSchema = addFrameSchema;
|
|
31607
|
+
exports.addOptimizerSchema = addOptimizerSchema;
|
|
31608
|
+
exports.addRiskSchema = addRiskSchema;
|
|
31609
|
+
exports.addSizingSchema = addSizingSchema;
|
|
31610
|
+
exports.addStrategySchema = addStrategySchema;
|
|
31611
|
+
exports.addWalkerSchema = addWalkerSchema;
|
|
31612
|
+
exports.commitBreakeven = commitBreakeven;
|
|
31613
|
+
exports.commitCancel = commitCancel;
|
|
31614
|
+
exports.commitPartialLoss = commitPartialLoss;
|
|
31615
|
+
exports.commitPartialProfit = commitPartialProfit;
|
|
31616
|
+
exports.commitTrailingStop = commitTrailingStop;
|
|
31617
|
+
exports.commitTrailingTake = commitTrailingTake;
|
|
31618
|
+
exports.dumpSignalData = dumpSignalData;
|
|
31137
31619
|
exports.emitters = emitters;
|
|
31138
31620
|
exports.formatPrice = formatPrice;
|
|
31139
31621
|
exports.formatQuantity = formatQuantity;
|
|
31140
31622
|
exports.get = get;
|
|
31141
|
-
exports.
|
|
31623
|
+
exports.getActionSchema = getActionSchema;
|
|
31142
31624
|
exports.getAveragePrice = getAveragePrice;
|
|
31625
|
+
exports.getBacktestTimeframe = getBacktestTimeframe;
|
|
31143
31626
|
exports.getCandles = getCandles;
|
|
31144
31627
|
exports.getColumns = getColumns;
|
|
31145
31628
|
exports.getConfig = getConfig;
|
|
31146
|
-
exports.
|
|
31629
|
+
exports.getContext = getContext;
|
|
31147
31630
|
exports.getDate = getDate;
|
|
31148
31631
|
exports.getDefaultColumns = getDefaultColumns;
|
|
31149
31632
|
exports.getDefaultConfig = getDefaultConfig;
|
|
31150
|
-
exports.
|
|
31151
|
-
exports.
|
|
31633
|
+
exports.getExchangeSchema = getExchangeSchema;
|
|
31634
|
+
exports.getFrameSchema = getFrameSchema;
|
|
31152
31635
|
exports.getMode = getMode;
|
|
31153
|
-
exports.
|
|
31636
|
+
exports.getOptimizerSchema = getOptimizerSchema;
|
|
31154
31637
|
exports.getOrderBook = getOrderBook;
|
|
31155
|
-
exports.
|
|
31156
|
-
exports.
|
|
31157
|
-
exports.
|
|
31158
|
-
exports.
|
|
31638
|
+
exports.getRiskSchema = getRiskSchema;
|
|
31639
|
+
exports.getSizingSchema = getSizingSchema;
|
|
31640
|
+
exports.getStrategySchema = getStrategySchema;
|
|
31641
|
+
exports.getSymbol = getSymbol;
|
|
31642
|
+
exports.getWalkerSchema = getWalkerSchema;
|
|
31159
31643
|
exports.hasTradeContext = hasTradeContext;
|
|
31160
31644
|
exports.lib = backtest;
|
|
31161
|
-
exports.
|
|
31162
|
-
exports.
|
|
31163
|
-
exports.
|
|
31164
|
-
exports.
|
|
31165
|
-
exports.
|
|
31166
|
-
exports.
|
|
31167
|
-
exports.
|
|
31645
|
+
exports.listExchangeSchema = listExchangeSchema;
|
|
31646
|
+
exports.listFrameSchema = listFrameSchema;
|
|
31647
|
+
exports.listOptimizerSchema = listOptimizerSchema;
|
|
31648
|
+
exports.listRiskSchema = listRiskSchema;
|
|
31649
|
+
exports.listSizingSchema = listSizingSchema;
|
|
31650
|
+
exports.listStrategySchema = listStrategySchema;
|
|
31651
|
+
exports.listWalkerSchema = listWalkerSchema;
|
|
31652
|
+
exports.listenActivePing = listenActivePing;
|
|
31653
|
+
exports.listenActivePingOnce = listenActivePingOnce;
|
|
31168
31654
|
exports.listenBacktestProgress = listenBacktestProgress;
|
|
31169
|
-
exports.
|
|
31170
|
-
exports.
|
|
31655
|
+
exports.listenBreakevenAvailable = listenBreakevenAvailable;
|
|
31656
|
+
exports.listenBreakevenAvailableOnce = listenBreakevenAvailableOnce;
|
|
31171
31657
|
exports.listenDoneBacktest = listenDoneBacktest;
|
|
31172
31658
|
exports.listenDoneBacktestOnce = listenDoneBacktestOnce;
|
|
31173
31659
|
exports.listenDoneLive = listenDoneLive;
|
|
@@ -31177,15 +31663,15 @@ exports.listenDoneWalkerOnce = listenDoneWalkerOnce;
|
|
|
31177
31663
|
exports.listenError = listenError;
|
|
31178
31664
|
exports.listenExit = listenExit;
|
|
31179
31665
|
exports.listenOptimizerProgress = listenOptimizerProgress;
|
|
31180
|
-
exports.
|
|
31181
|
-
exports.
|
|
31182
|
-
exports.
|
|
31183
|
-
exports.
|
|
31666
|
+
exports.listenPartialLossAvailable = listenPartialLossAvailable;
|
|
31667
|
+
exports.listenPartialLossAvailableOnce = listenPartialLossAvailableOnce;
|
|
31668
|
+
exports.listenPartialProfitAvailable = listenPartialProfitAvailable;
|
|
31669
|
+
exports.listenPartialProfitAvailableOnce = listenPartialProfitAvailableOnce;
|
|
31184
31670
|
exports.listenPerformance = listenPerformance;
|
|
31185
|
-
exports.listenPing = listenPing;
|
|
31186
|
-
exports.listenPingOnce = listenPingOnce;
|
|
31187
31671
|
exports.listenRisk = listenRisk;
|
|
31188
31672
|
exports.listenRiskOnce = listenRiskOnce;
|
|
31673
|
+
exports.listenSchedulePing = listenSchedulePing;
|
|
31674
|
+
exports.listenSchedulePingOnce = listenSchedulePingOnce;
|
|
31189
31675
|
exports.listenSignal = listenSignal;
|
|
31190
31676
|
exports.listenSignalBacktest = listenSignalBacktest;
|
|
31191
31677
|
exports.listenSignalBacktestOnce = listenSignalBacktestOnce;
|
|
@@ -31197,22 +31683,18 @@ exports.listenWalker = listenWalker;
|
|
|
31197
31683
|
exports.listenWalkerComplete = listenWalkerComplete;
|
|
31198
31684
|
exports.listenWalkerOnce = listenWalkerOnce;
|
|
31199
31685
|
exports.listenWalkerProgress = listenWalkerProgress;
|
|
31200
|
-
exports.
|
|
31201
|
-
exports.
|
|
31202
|
-
exports.
|
|
31203
|
-
exports.
|
|
31204
|
-
exports.
|
|
31205
|
-
exports.
|
|
31206
|
-
exports.
|
|
31207
|
-
exports.
|
|
31208
|
-
exports.partialLoss = partialLoss;
|
|
31209
|
-
exports.partialProfit = partialProfit;
|
|
31686
|
+
exports.overrideActionSchema = overrideActionSchema;
|
|
31687
|
+
exports.overrideExchangeSchema = overrideExchangeSchema;
|
|
31688
|
+
exports.overrideFrameSchema = overrideFrameSchema;
|
|
31689
|
+
exports.overrideOptimizerSchema = overrideOptimizerSchema;
|
|
31690
|
+
exports.overrideRiskSchema = overrideRiskSchema;
|
|
31691
|
+
exports.overrideSizingSchema = overrideSizingSchema;
|
|
31692
|
+
exports.overrideStrategySchema = overrideStrategySchema;
|
|
31693
|
+
exports.overrideWalkerSchema = overrideWalkerSchema;
|
|
31210
31694
|
exports.roundTicks = roundTicks;
|
|
31211
31695
|
exports.set = set;
|
|
31212
31696
|
exports.setColumns = setColumns;
|
|
31213
31697
|
exports.setConfig = setConfig;
|
|
31214
31698
|
exports.setLogger = setLogger;
|
|
31215
31699
|
exports.stop = stop;
|
|
31216
|
-
exports.trailingStop = trailingStop;
|
|
31217
|
-
exports.trailingTake = trailingTake;
|
|
31218
31700
|
exports.validate = validate;
|