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.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createActivator } from 'di-kit';
|
|
2
2
|
import { scoped } from 'di-scoped';
|
|
3
|
-
import { Subject, trycatch, errorData, getErrorMessage, sleep, memoize, makeExtendable, singleshot, not, retry, randomString, isObject, ToolRegistry, and, resolveDocuments,
|
|
3
|
+
import { Subject, trycatch, errorData, getErrorMessage, sleep, memoize, makeExtendable, singleshot, not, retry, randomString, str, isObject, ToolRegistry, and, resolveDocuments, timeout, TIMEOUT_SYMBOL as TIMEOUT_SYMBOL$1, compose, iterateDocuments, distinctDocuments, queued, singlerun } from 'functools-kit';
|
|
4
4
|
import * as fs from 'fs/promises';
|
|
5
5
|
import fs__default, { mkdir, writeFile } from 'fs/promises';
|
|
6
6
|
import path, { join, dirname } from 'path';
|
|
@@ -521,14 +521,21 @@ const breakevenSubject = new Subject();
|
|
|
521
521
|
*/
|
|
522
522
|
const riskSubject = new Subject();
|
|
523
523
|
/**
|
|
524
|
-
*
|
|
524
|
+
* Schedule ping emitter for scheduled signal monitoring events.
|
|
525
525
|
* Emits every minute when a scheduled signal is being monitored (waiting for activation).
|
|
526
526
|
* Allows users to track scheduled signal lifecycle and implement custom cancellation logic.
|
|
527
527
|
*/
|
|
528
|
-
const
|
|
528
|
+
const schedulePingSubject = new Subject();
|
|
529
|
+
/**
|
|
530
|
+
* Active ping emitter for active pending signal monitoring events.
|
|
531
|
+
* Emits every minute when an active pending signal is being monitored.
|
|
532
|
+
* Allows users to track active signal lifecycle and implement custom dynamic management logic.
|
|
533
|
+
*/
|
|
534
|
+
const activePingSubject = new Subject();
|
|
529
535
|
|
|
530
536
|
var emitters = /*#__PURE__*/Object.freeze({
|
|
531
537
|
__proto__: null,
|
|
538
|
+
activePingSubject: activePingSubject,
|
|
532
539
|
breakevenSubject: breakevenSubject,
|
|
533
540
|
doneBacktestSubject: doneBacktestSubject,
|
|
534
541
|
doneLiveSubject: doneLiveSubject,
|
|
@@ -538,11 +545,11 @@ var emitters = /*#__PURE__*/Object.freeze({
|
|
|
538
545
|
partialLossSubject: partialLossSubject,
|
|
539
546
|
partialProfitSubject: partialProfitSubject,
|
|
540
547
|
performanceEmitter: performanceEmitter,
|
|
541
|
-
pingSubject: pingSubject,
|
|
542
548
|
progressBacktestEmitter: progressBacktestEmitter,
|
|
543
549
|
progressOptimizerEmitter: progressOptimizerEmitter,
|
|
544
550
|
progressWalkerEmitter: progressWalkerEmitter,
|
|
545
551
|
riskSubject: riskSubject,
|
|
552
|
+
schedulePingSubject: schedulePingSubject,
|
|
546
553
|
signalBacktestEmitter: signalBacktestEmitter,
|
|
547
554
|
signalEmitter: signalEmitter,
|
|
548
555
|
signalLiveEmitter: signalLiveEmitter,
|
|
@@ -753,11 +760,16 @@ class ClientExchange {
|
|
|
753
760
|
const whenTimestamp = this.params.execution.context.when.getTime();
|
|
754
761
|
const sinceTimestamp = since.getTime();
|
|
755
762
|
const filteredData = allData.filter((candle) => candle.timestamp >= sinceTimestamp && candle.timestamp <= whenTimestamp);
|
|
756
|
-
|
|
757
|
-
|
|
763
|
+
// Apply distinct by timestamp to remove duplicates
|
|
764
|
+
const uniqueData = Array.from(new Map(filteredData.map((candle) => [candle.timestamp, candle])).values());
|
|
765
|
+
if (filteredData.length !== uniqueData.length) {
|
|
766
|
+
this.params.logger.warn(`ClientExchange Removed ${filteredData.length - uniqueData.length} duplicate candles by timestamp`);
|
|
758
767
|
}
|
|
759
|
-
|
|
760
|
-
|
|
768
|
+
if (uniqueData.length < limit) {
|
|
769
|
+
this.params.logger.warn(`ClientExchange Expected ${limit} candles, got ${uniqueData.length}`);
|
|
770
|
+
}
|
|
771
|
+
await CALL_CANDLE_DATA_CALLBACKS_FN(this, symbol, interval, since, limit, uniqueData);
|
|
772
|
+
return uniqueData;
|
|
761
773
|
}
|
|
762
774
|
/**
|
|
763
775
|
* Fetches future candles forwards from execution context time.
|
|
@@ -806,11 +818,16 @@ class ClientExchange {
|
|
|
806
818
|
// Filter candles to strictly match the requested range
|
|
807
819
|
const sinceTimestamp = since.getTime();
|
|
808
820
|
const filteredData = allData.filter((candle) => candle.timestamp >= sinceTimestamp && candle.timestamp <= endTime);
|
|
809
|
-
|
|
810
|
-
|
|
821
|
+
// Apply distinct by timestamp to remove duplicates
|
|
822
|
+
const uniqueData = Array.from(new Map(filteredData.map((candle) => [candle.timestamp, candle])).values());
|
|
823
|
+
if (filteredData.length !== uniqueData.length) {
|
|
824
|
+
this.params.logger.warn(`ClientExchange getNextCandles: Removed ${filteredData.length - uniqueData.length} duplicate candles by timestamp`);
|
|
825
|
+
}
|
|
826
|
+
if (uniqueData.length < limit) {
|
|
827
|
+
this.params.logger.warn(`ClientExchange getNextCandles: Expected ${limit} candles, got ${uniqueData.length}`);
|
|
811
828
|
}
|
|
812
|
-
await CALL_CANDLE_DATA_CALLBACKS_FN(this, symbol, interval, since, limit,
|
|
813
|
-
return
|
|
829
|
+
await CALL_CANDLE_DATA_CALLBACKS_FN(this, symbol, interval, since, limit, uniqueData);
|
|
830
|
+
return uniqueData;
|
|
814
831
|
}
|
|
815
832
|
/**
|
|
816
833
|
* Calculates VWAP (Volume Weighted Average Price) from last N 1m candles.
|
|
@@ -3386,14 +3403,14 @@ const ACTIVATE_SCHEDULED_SIGNAL_FN = async (self, scheduled, activationTimestamp
|
|
|
3386
3403
|
await CALL_TICK_CALLBACKS_FN(self, self.params.execution.context.symbol, result, activationTime, self.params.execution.context.backtest);
|
|
3387
3404
|
return result;
|
|
3388
3405
|
};
|
|
3389
|
-
const
|
|
3406
|
+
const CALL_SCHEDULE_PING_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, scheduled, timestamp, backtest) => {
|
|
3390
3407
|
await ExecutionContextService.runInContext(async () => {
|
|
3391
3408
|
const publicSignal = TO_PUBLIC_SIGNAL(scheduled);
|
|
3392
|
-
// Call system
|
|
3393
|
-
await self.params.
|
|
3394
|
-
// Call user
|
|
3395
|
-
if (self.params.callbacks?.
|
|
3396
|
-
await self.params.callbacks.
|
|
3409
|
+
// Call system onSchedulePing callback first (emits to pingSubject)
|
|
3410
|
+
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);
|
|
3411
|
+
// Call user onSchedulePing callback only if signal is still active (not cancelled, not activated)
|
|
3412
|
+
if (self.params.callbacks?.onSchedulePing) {
|
|
3413
|
+
await self.params.callbacks.onSchedulePing(self.params.execution.context.symbol, publicSignal, new Date(timestamp), self.params.execution.context.backtest);
|
|
3397
3414
|
}
|
|
3398
3415
|
}, {
|
|
3399
3416
|
when: new Date(timestamp),
|
|
@@ -3402,7 +3419,33 @@ const CALL_PING_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, scheduled
|
|
|
3402
3419
|
});
|
|
3403
3420
|
}), {
|
|
3404
3421
|
fallback: (error) => {
|
|
3405
|
-
const message = "ClientStrategy
|
|
3422
|
+
const message = "ClientStrategy CALL_SCHEDULE_PING_CALLBACKS_FN thrown";
|
|
3423
|
+
const payload = {
|
|
3424
|
+
error: errorData(error),
|
|
3425
|
+
message: getErrorMessage(error),
|
|
3426
|
+
};
|
|
3427
|
+
backtest$1.loggerService.warn(message, payload);
|
|
3428
|
+
console.warn(message, payload);
|
|
3429
|
+
errorEmitter.next(error);
|
|
3430
|
+
},
|
|
3431
|
+
});
|
|
3432
|
+
const CALL_ACTIVE_PING_CALLBACKS_FN = trycatch(beginTime(async (self, symbol, pending, timestamp, backtest) => {
|
|
3433
|
+
await ExecutionContextService.runInContext(async () => {
|
|
3434
|
+
const publicSignal = TO_PUBLIC_SIGNAL(pending);
|
|
3435
|
+
// Call system onActivePing callback first (emits to activePingSubject)
|
|
3436
|
+
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);
|
|
3437
|
+
// Call user onActivePing callback only if signal is still active (not closed)
|
|
3438
|
+
if (self.params.callbacks?.onActivePing) {
|
|
3439
|
+
await self.params.callbacks.onActivePing(self.params.execution.context.symbol, publicSignal, new Date(timestamp), self.params.execution.context.backtest);
|
|
3440
|
+
}
|
|
3441
|
+
}, {
|
|
3442
|
+
when: new Date(timestamp),
|
|
3443
|
+
symbol: symbol,
|
|
3444
|
+
backtest: backtest,
|
|
3445
|
+
});
|
|
3446
|
+
}), {
|
|
3447
|
+
fallback: (error) => {
|
|
3448
|
+
const message = "ClientStrategy CALL_ACTIVE_PING_CALLBACKS_FN thrown";
|
|
3406
3449
|
const payload = {
|
|
3407
3450
|
error: errorData(error),
|
|
3408
3451
|
message: getErrorMessage(error),
|
|
@@ -3774,10 +3817,10 @@ const CALL_BREAKEVEN_CLEAR_FN = trycatch(beginTime(async (self, symbol, signal,
|
|
|
3774
3817
|
});
|
|
3775
3818
|
const RETURN_SCHEDULED_SIGNAL_ACTIVE_FN = async (self, scheduled, currentPrice) => {
|
|
3776
3819
|
const currentTime = self.params.execution.context.when.getTime();
|
|
3777
|
-
await
|
|
3820
|
+
await CALL_SCHEDULE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, currentTime, self.params.execution.context.backtest);
|
|
3778
3821
|
const pnl = toProfitLossDto(scheduled, currentPrice);
|
|
3779
3822
|
const result = {
|
|
3780
|
-
action: "
|
|
3823
|
+
action: "waiting",
|
|
3781
3824
|
signal: TO_PUBLIC_SIGNAL(scheduled),
|
|
3782
3825
|
currentPrice: currentPrice,
|
|
3783
3826
|
strategyName: self.params.method.context.strategyName,
|
|
@@ -3904,6 +3947,7 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
3904
3947
|
let percentTp = 0;
|
|
3905
3948
|
let percentSl = 0;
|
|
3906
3949
|
const currentTime = self.params.execution.context.when.getTime();
|
|
3950
|
+
await CALL_ACTIVE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentTime, self.params.execution.context.backtest);
|
|
3907
3951
|
// Calculate percentage of path to TP/SL for partial fill/loss callbacks
|
|
3908
3952
|
{
|
|
3909
3953
|
if (signal.position === "long") {
|
|
@@ -4109,7 +4153,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
4109
4153
|
const averagePrice = GET_AVG_PRICE_FN(recentCandles);
|
|
4110
4154
|
// КРИТИЧНО: Проверяем был ли сигнал отменен пользователем через cancel()
|
|
4111
4155
|
if (self._cancelledSignal) {
|
|
4112
|
-
// Сигнал был отменен через cancel() в
|
|
4156
|
+
// Сигнал был отменен через cancel() в onSchedulePing
|
|
4113
4157
|
const result = await CANCEL_SCHEDULED_SIGNAL_IN_BACKTEST_FN(self, scheduled, averagePrice, candle.timestamp, "user");
|
|
4114
4158
|
return { activated: false, cancelled: true, activationIndex: i, result };
|
|
4115
4159
|
}
|
|
@@ -4166,7 +4210,7 @@ const PROCESS_SCHEDULED_SIGNAL_CANDLES_FN = async (self, scheduled, candles) =>
|
|
|
4166
4210
|
result: null,
|
|
4167
4211
|
};
|
|
4168
4212
|
}
|
|
4169
|
-
await
|
|
4213
|
+
await CALL_SCHEDULE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, scheduled, candle.timestamp, true);
|
|
4170
4214
|
}
|
|
4171
4215
|
return {
|
|
4172
4216
|
activated: false,
|
|
@@ -4192,6 +4236,7 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
4192
4236
|
const startIndex = Math.max(0, i - (candlesCount - 1));
|
|
4193
4237
|
const recentCandles = candles.slice(startIndex, i + 1);
|
|
4194
4238
|
const averagePrice = GET_AVG_PRICE_FN(recentCandles);
|
|
4239
|
+
await CALL_ACTIVE_PING_CALLBACKS_FN(self, self.params.execution.context.symbol, signal, currentCandleTimestamp, true);
|
|
4195
4240
|
let shouldClose = false;
|
|
4196
4241
|
let closeReason;
|
|
4197
4242
|
// Check time expiration FIRST (КРИТИЧНО!)
|
|
@@ -4737,7 +4782,15 @@ class ClientStrategy {
|
|
|
4737
4782
|
return result;
|
|
4738
4783
|
}
|
|
4739
4784
|
if (activated) {
|
|
4740
|
-
|
|
4785
|
+
// КРИТИЧНО: activationIndex - индекс свечи активации в массиве candles
|
|
4786
|
+
// BacktestLogicPrivateService включил буфер в начало массива, поэтому перед activationIndex достаточно свечей
|
|
4787
|
+
// PROCESS_PENDING_SIGNAL_CANDLES_FN пропустит первые bufferCandlesCount свечей для VWAP
|
|
4788
|
+
// Чтобы обработка началась со СЛЕДУЮЩЕЙ свечи после активации (activationIndex + 1),
|
|
4789
|
+
// нужно взять срез начиная с (activationIndex + 1 - bufferCandlesCount)
|
|
4790
|
+
// Это даст буфер ИЗ scheduled фазы + свеча после активации как первая обрабатываемая
|
|
4791
|
+
const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
|
|
4792
|
+
const sliceStart = Math.max(0, activationIndex + 1 - bufferCandlesCount);
|
|
4793
|
+
const remainingCandles = candles.slice(sliceStart);
|
|
4741
4794
|
if (remainingCandles.length === 0) {
|
|
4742
4795
|
const candlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT;
|
|
4743
4796
|
const recentCandles = candles.slice(Math.max(0, activationIndex - (candlesCount - 1)), activationIndex + 1);
|
|
@@ -4753,35 +4806,23 @@ class ClientStrategy {
|
|
|
4753
4806
|
const lastCandleTimestamp = candles[candles.length - 1].timestamp;
|
|
4754
4807
|
const elapsedTime = lastCandleTimestamp - scheduled.scheduledAt;
|
|
4755
4808
|
if (elapsedTime < maxTimeToWait) {
|
|
4756
|
-
//
|
|
4757
|
-
//
|
|
4758
|
-
|
|
4759
|
-
const
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
signal: TO_PUBLIC_SIGNAL(scheduled),
|
|
4774
|
-
currentPrice: lastPrice,
|
|
4775
|
-
percentSl: 0,
|
|
4776
|
-
percentTp: 0,
|
|
4777
|
-
pnl,
|
|
4778
|
-
strategyName: this.params.method.context.strategyName,
|
|
4779
|
-
exchangeName: this.params.method.context.exchangeName,
|
|
4780
|
-
frameName: this.params.method.context.frameName,
|
|
4781
|
-
symbol: this.params.execution.context.symbol,
|
|
4782
|
-
backtest: this.params.execution.context.backtest,
|
|
4783
|
-
};
|
|
4784
|
-
return result; // Cast to IStrategyBacktestResult (which includes Active)
|
|
4809
|
+
// EDGE CASE: backtest() called with partial candle data (should never happen in production)
|
|
4810
|
+
// In real backtest flow this won't happen as we process all candles at once
|
|
4811
|
+
// This indicates incorrect usage of backtest() - throw error instead of returning partial result
|
|
4812
|
+
const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
|
|
4813
|
+
// For scheduled signal that has NOT activated: buffer + wait time only (no lifetime yet)
|
|
4814
|
+
const requiredCandlesCount = bufferCandlesCount + GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES + 1;
|
|
4815
|
+
throw new Error(str.newline(`ClientStrategy backtest: Insufficient candle data for scheduled signal (not yet activated). ` +
|
|
4816
|
+
`Signal scheduled at ${new Date(scheduled.scheduledAt).toISOString()}, ` +
|
|
4817
|
+
`last candle at ${new Date(lastCandleTimestamp).toISOString()}. ` +
|
|
4818
|
+
`Elapsed: ${Math.floor(elapsedTime / 60000)}min of ${GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES}min wait time. ` +
|
|
4819
|
+
`Provided ${candles.length} candles, but need at least ${requiredCandlesCount} candles. ` +
|
|
4820
|
+
`\nBreakdown: ` +
|
|
4821
|
+
`${bufferCandlesCount} buffer (VWAP) + ` +
|
|
4822
|
+
`${GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES} wait (for activation) = ${requiredCandlesCount} total. ` +
|
|
4823
|
+
`\nBuffer explanation: VWAP calculation requires ${GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT} candles, ` +
|
|
4824
|
+
`so first ${bufferCandlesCount} candles are skipped during scheduled phase processing. ` +
|
|
4825
|
+
`Provide complete candle range: [scheduledAt - ${bufferCandlesCount}min, scheduledAt + ${GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES}min].`));
|
|
4785
4826
|
}
|
|
4786
4827
|
// Timeout reached - cancel the scheduled signal
|
|
4787
4828
|
const candlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT;
|
|
@@ -4811,9 +4852,29 @@ class ClientStrategy {
|
|
|
4811
4852
|
if (closedResult) {
|
|
4812
4853
|
return closedResult;
|
|
4813
4854
|
}
|
|
4855
|
+
// Signal didn't close during candle processing - check if we have enough data
|
|
4814
4856
|
const lastCandles = candles.slice(-GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT);
|
|
4815
4857
|
const lastPrice = GET_AVG_PRICE_FN(lastCandles);
|
|
4816
4858
|
const closeTimestamp = lastCandles[lastCandles.length - 1].timestamp;
|
|
4859
|
+
const signalTime = signal.pendingAt;
|
|
4860
|
+
const maxTimeToWait = signal.minuteEstimatedTime * 60 * 1000;
|
|
4861
|
+
const elapsedTime = closeTimestamp - signalTime;
|
|
4862
|
+
// Check if we actually reached time expiration or just ran out of candles
|
|
4863
|
+
if (elapsedTime < maxTimeToWait) {
|
|
4864
|
+
// EDGE CASE: backtest() called with insufficient candle data
|
|
4865
|
+
const bufferCandlesCount = GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT - 1;
|
|
4866
|
+
const requiredCandlesCount = signal.minuteEstimatedTime + bufferCandlesCount + 1;
|
|
4867
|
+
throw new Error(str.newline(`ClientStrategy backtest: Insufficient candle data for pending signal. ` +
|
|
4868
|
+
`Signal opened at ${new Date(signal.pendingAt).toISOString()}, ` +
|
|
4869
|
+
`last candle at ${new Date(closeTimestamp).toISOString()}. ` +
|
|
4870
|
+
`Elapsed: ${Math.floor(elapsedTime / 60000)}min of ${signal.minuteEstimatedTime}min required. ` +
|
|
4871
|
+
`Provided ${candles.length} candles, but need at least ${requiredCandlesCount} candles. ` +
|
|
4872
|
+
`\nBreakdown: ${signal.minuteEstimatedTime} candles for signal lifetime + ${bufferCandlesCount} buffer candles. ` +
|
|
4873
|
+
`\nBuffer explanation: VWAP calculation requires ${GLOBAL_CONFIG.CC_AVG_PRICE_CANDLES_COUNT} candles, ` +
|
|
4874
|
+
`so first ${bufferCandlesCount} candles are skipped to ensure accurate price averaging. ` +
|
|
4875
|
+
`Provide complete candle range: [pendingAt - ${bufferCandlesCount}min, pendingAt + ${signal.minuteEstimatedTime}min].`));
|
|
4876
|
+
}
|
|
4877
|
+
// Time actually expired - close with time_expired
|
|
4817
4878
|
return await CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN(this, signal, lastPrice, "time_expired", closeTimestamp);
|
|
4818
4879
|
}
|
|
4819
4880
|
/**
|
|
@@ -5602,6 +5663,9 @@ class ClientStrategy {
|
|
|
5602
5663
|
const RISK_METHOD_NAME_GET_DATA = "RiskUtils.getData";
|
|
5603
5664
|
const RISK_METHOD_NAME_GET_REPORT = "RiskUtils.getReport";
|
|
5604
5665
|
const RISK_METHOD_NAME_DUMP = "RiskUtils.dump";
|
|
5666
|
+
const RISK_METHOD_NAME_CHECK_SIGNAL = "MergeRisk.checkSignal";
|
|
5667
|
+
const RISK_METHOD_NAME_ADD_SIGNAL = "MergeRisk.addSignal";
|
|
5668
|
+
const RISK_METHOD_NAME_REMOVE_SIGNAL = "MergeRisk.removeSignal";
|
|
5605
5669
|
/**
|
|
5606
5670
|
* Composite risk management class that combines multiple risk profiles.
|
|
5607
5671
|
*
|
|
@@ -5657,7 +5721,7 @@ class MergeRisk {
|
|
|
5657
5721
|
* @returns Promise resolving to true if all risks approve, false if any risk rejects
|
|
5658
5722
|
*/
|
|
5659
5723
|
async checkSignal(params) {
|
|
5660
|
-
backtest$1.loggerService.info(
|
|
5724
|
+
backtest$1.loggerService.info(RISK_METHOD_NAME_CHECK_SIGNAL, {
|
|
5661
5725
|
params,
|
|
5662
5726
|
});
|
|
5663
5727
|
for (const [riskName, risk] of Object.entries(this._riskMap)) {
|
|
@@ -5681,7 +5745,7 @@ class MergeRisk {
|
|
|
5681
5745
|
* @returns Promise that resolves when all risks have registered the signal
|
|
5682
5746
|
*/
|
|
5683
5747
|
async addSignal(symbol, context, positionData) {
|
|
5684
|
-
backtest$1.loggerService.info(
|
|
5748
|
+
backtest$1.loggerService.info(RISK_METHOD_NAME_ADD_SIGNAL, {
|
|
5685
5749
|
symbol,
|
|
5686
5750
|
context,
|
|
5687
5751
|
});
|
|
@@ -5698,7 +5762,7 @@ class MergeRisk {
|
|
|
5698
5762
|
* @returns Promise that resolves when all risks have removed the signal
|
|
5699
5763
|
*/
|
|
5700
5764
|
async removeSignal(symbol, context) {
|
|
5701
|
-
backtest$1.loggerService.info(
|
|
5765
|
+
backtest$1.loggerService.info(RISK_METHOD_NAME_REMOVE_SIGNAL, {
|
|
5702
5766
|
symbol,
|
|
5703
5767
|
context,
|
|
5704
5768
|
});
|
|
@@ -5981,15 +6045,15 @@ const CREATE_KEY_FN$k = (symbol, strategyName, exchangeName, frameName, backtest
|
|
|
5981
6045
|
return parts.join(":");
|
|
5982
6046
|
};
|
|
5983
6047
|
/**
|
|
5984
|
-
* Creates a callback function for emitting ping events to pingSubject.
|
|
6048
|
+
* Creates a callback function for emitting schedule ping events to pingSubject.
|
|
5985
6049
|
*
|
|
5986
6050
|
* Called by ClientStrategy when a scheduled signal is being monitored every minute.
|
|
5987
6051
|
* Emits PingContract event to all subscribers and calls ActionCoreService.
|
|
5988
6052
|
*
|
|
5989
6053
|
* @param self - Reference to StrategyConnectionService instance
|
|
5990
|
-
* @returns Callback function for ping events
|
|
6054
|
+
* @returns Callback function for schedule ping events
|
|
5991
6055
|
*/
|
|
5992
|
-
const
|
|
6056
|
+
const CREATE_COMMIT_SCHEDULE_PING_FN = (self) => async (symbol, strategyName, exchangeName, data, backtest, timestamp) => {
|
|
5993
6057
|
const event = {
|
|
5994
6058
|
symbol,
|
|
5995
6059
|
strategyName,
|
|
@@ -5998,8 +6062,29 @@ const CREATE_COMMIT_PING_FN = (self) => async (symbol, strategyName, exchangeNam
|
|
|
5998
6062
|
backtest,
|
|
5999
6063
|
timestamp,
|
|
6000
6064
|
};
|
|
6001
|
-
await
|
|
6002
|
-
await self.actionCoreService.
|
|
6065
|
+
await schedulePingSubject.next(event);
|
|
6066
|
+
await self.actionCoreService.pingScheduled(backtest, event, { strategyName, exchangeName, frameName: data.frameName });
|
|
6067
|
+
};
|
|
6068
|
+
/**
|
|
6069
|
+
* Creates a callback function for emitting active ping events.
|
|
6070
|
+
*
|
|
6071
|
+
* Called by ClientStrategy when an active pending signal is being monitored every minute.
|
|
6072
|
+
* Placeholder for future activePingSubject implementation.
|
|
6073
|
+
*
|
|
6074
|
+
* @param self - Reference to StrategyConnectionService instance
|
|
6075
|
+
* @returns Callback function for active ping events
|
|
6076
|
+
*/
|
|
6077
|
+
const CREATE_COMMIT_ACTIVE_PING_FN = (self) => async (symbol, strategyName, exchangeName, data, backtest, timestamp) => {
|
|
6078
|
+
const event = {
|
|
6079
|
+
symbol,
|
|
6080
|
+
strategyName,
|
|
6081
|
+
exchangeName,
|
|
6082
|
+
data,
|
|
6083
|
+
backtest,
|
|
6084
|
+
timestamp,
|
|
6085
|
+
};
|
|
6086
|
+
await activePingSubject.next(event);
|
|
6087
|
+
await self.actionCoreService.pingActive(backtest, event, { strategyName, exchangeName, frameName: data.frameName });
|
|
6003
6088
|
};
|
|
6004
6089
|
/**
|
|
6005
6090
|
* Creates a callback function for emitting init events.
|
|
@@ -6096,7 +6181,8 @@ class StrategyConnectionService {
|
|
|
6096
6181
|
getSignal,
|
|
6097
6182
|
callbacks,
|
|
6098
6183
|
onInit: CREATE_COMMIT_INIT_FN(this),
|
|
6099
|
-
|
|
6184
|
+
onSchedulePing: CREATE_COMMIT_SCHEDULE_PING_FN(this),
|
|
6185
|
+
onActivePing: CREATE_COMMIT_ACTIVE_PING_FN(this),
|
|
6100
6186
|
onDispose: CREATE_COMMIT_DISPOSE_FN(this),
|
|
6101
6187
|
});
|
|
6102
6188
|
});
|
|
@@ -7489,8 +7575,8 @@ const CALL_SIGNAL_BACKTEST_CALLBACK_FN = trycatch(async (self, event, strategyNa
|
|
|
7489
7575
|
});
|
|
7490
7576
|
/** Wrapper to call breakeven callback with error handling */
|
|
7491
7577
|
const CALL_BREAKEVEN_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
7492
|
-
if (self.params.callbacks?.
|
|
7493
|
-
await self.params.callbacks.
|
|
7578
|
+
if (self.params.callbacks?.onBreakevenAvailable) {
|
|
7579
|
+
await self.params.callbacks.onBreakevenAvailable(event, self.params.actionName, strategyName, frameName, backtest);
|
|
7494
7580
|
}
|
|
7495
7581
|
}, {
|
|
7496
7582
|
fallback: (error) => {
|
|
@@ -7506,8 +7592,8 @@ const CALL_BREAKEVEN_CALLBACK_FN = trycatch(async (self, event, strategyName, fr
|
|
|
7506
7592
|
});
|
|
7507
7593
|
/** Wrapper to call partialProfit callback with error handling */
|
|
7508
7594
|
const CALL_PARTIAL_PROFIT_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
7509
|
-
if (self.params.callbacks?.
|
|
7510
|
-
await self.params.callbacks.
|
|
7595
|
+
if (self.params.callbacks?.onPartialProfitAvailable) {
|
|
7596
|
+
await self.params.callbacks.onPartialProfitAvailable(event, self.params.actionName, strategyName, frameName, backtest);
|
|
7511
7597
|
}
|
|
7512
7598
|
}, {
|
|
7513
7599
|
fallback: (error) => {
|
|
@@ -7523,8 +7609,8 @@ const CALL_PARTIAL_PROFIT_CALLBACK_FN = trycatch(async (self, event, strategyNam
|
|
|
7523
7609
|
});
|
|
7524
7610
|
/** Wrapper to call partialLoss callback with error handling */
|
|
7525
7611
|
const CALL_PARTIAL_LOSS_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
7526
|
-
if (self.params.callbacks?.
|
|
7527
|
-
await self.params.callbacks.
|
|
7612
|
+
if (self.params.callbacks?.onPartialLossAvailable) {
|
|
7613
|
+
await self.params.callbacks.onPartialLossAvailable(event, self.params.actionName, strategyName, frameName, backtest);
|
|
7528
7614
|
}
|
|
7529
7615
|
}, {
|
|
7530
7616
|
fallback: (error) => {
|
|
@@ -7538,14 +7624,31 @@ const CALL_PARTIAL_LOSS_CALLBACK_FN = trycatch(async (self, event, strategyName,
|
|
|
7538
7624
|
errorEmitter.next(error);
|
|
7539
7625
|
},
|
|
7540
7626
|
});
|
|
7541
|
-
/** Wrapper to call ping callback with error handling */
|
|
7542
|
-
const
|
|
7543
|
-
if (self.params.callbacks?.
|
|
7544
|
-
await self.params.callbacks.
|
|
7627
|
+
/** Wrapper to call scheduled ping callback with error handling */
|
|
7628
|
+
const CALL_PING_SCHEDULED_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
7629
|
+
if (self.params.callbacks?.onPingScheduled) {
|
|
7630
|
+
await self.params.callbacks.onPingScheduled(event, self.params.actionName, strategyName, frameName, backtest);
|
|
7545
7631
|
}
|
|
7546
7632
|
}, {
|
|
7547
7633
|
fallback: (error) => {
|
|
7548
|
-
const message = "ClientAction
|
|
7634
|
+
const message = "ClientAction CALL_PING_SCHEDULED_CALLBACK_FN thrown";
|
|
7635
|
+
const payload = {
|
|
7636
|
+
error: errorData(error),
|
|
7637
|
+
message: getErrorMessage(error),
|
|
7638
|
+
};
|
|
7639
|
+
backtest$1.loggerService.warn(message, payload);
|
|
7640
|
+
console.warn(message, payload);
|
|
7641
|
+
errorEmitter.next(error);
|
|
7642
|
+
},
|
|
7643
|
+
});
|
|
7644
|
+
/** Wrapper to call active ping callback with error handling */
|
|
7645
|
+
const CALL_PING_ACTIVE_CALLBACK_FN = trycatch(async (self, event, strategyName, frameName, backtest) => {
|
|
7646
|
+
if (self.params.callbacks?.onPingActive) {
|
|
7647
|
+
await self.params.callbacks.onPingActive(event, self.params.actionName, strategyName, frameName, backtest);
|
|
7648
|
+
}
|
|
7649
|
+
}, {
|
|
7650
|
+
fallback: (error) => {
|
|
7651
|
+
const message = "ClientAction CALL_PING_ACTIVE_CALLBACK_FN thrown";
|
|
7549
7652
|
const payload = {
|
|
7550
7653
|
error: errorData(error),
|
|
7551
7654
|
message: getErrorMessage(error),
|
|
@@ -7620,6 +7723,7 @@ const CREATE_HANDLER_FN = (self) => {
|
|
|
7620
7723
|
self.params.strategyName,
|
|
7621
7724
|
self.params.frameName,
|
|
7622
7725
|
self.params.actionName,
|
|
7726
|
+
self.params.backtest,
|
|
7623
7727
|
]);
|
|
7624
7728
|
}
|
|
7625
7729
|
return self.params.handler;
|
|
@@ -7811,8 +7915,8 @@ class ClientAction {
|
|
|
7811
7915
|
/**
|
|
7812
7916
|
* Handles breakeven events when stop-loss is moved to entry price.
|
|
7813
7917
|
*/
|
|
7814
|
-
async
|
|
7815
|
-
this.params.logger.debug("ClientAction
|
|
7918
|
+
async breakevenAvailable(event) {
|
|
7919
|
+
this.params.logger.debug("ClientAction breakevenAvailable", {
|
|
7816
7920
|
actionName: this.params.actionName,
|
|
7817
7921
|
strategyName: this.params.strategyName,
|
|
7818
7922
|
frameName: this.params.frameName,
|
|
@@ -7821,8 +7925,8 @@ class ClientAction {
|
|
|
7821
7925
|
await this.waitForInit();
|
|
7822
7926
|
}
|
|
7823
7927
|
// Call handler method if defined
|
|
7824
|
-
if (this._handlerInstance?.
|
|
7825
|
-
await this._handlerInstance.
|
|
7928
|
+
if (this._handlerInstance?.breakevenAvailable) {
|
|
7929
|
+
await this._handlerInstance.breakevenAvailable(event);
|
|
7826
7930
|
}
|
|
7827
7931
|
// Call callback if defined
|
|
7828
7932
|
await CALL_BREAKEVEN_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
@@ -7831,8 +7935,8 @@ class ClientAction {
|
|
|
7831
7935
|
/**
|
|
7832
7936
|
* Handles partial profit level events (10%, 20%, 30%, etc).
|
|
7833
7937
|
*/
|
|
7834
|
-
async
|
|
7835
|
-
this.params.logger.debug("ClientAction
|
|
7938
|
+
async partialProfitAvailable(event) {
|
|
7939
|
+
this.params.logger.debug("ClientAction partialProfitAvailable", {
|
|
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?.partialProfitAvailable) {
|
|
7949
|
+
await this._handlerInstance.partialProfitAvailable(event);
|
|
7846
7950
|
}
|
|
7847
7951
|
// Call callback if defined
|
|
7848
7952
|
await CALL_PARTIAL_PROFIT_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
@@ -7851,8 +7955,8 @@ class ClientAction {
|
|
|
7851
7955
|
/**
|
|
7852
7956
|
* Handles partial loss level events (-10%, -20%, -30%, etc).
|
|
7853
7957
|
*/
|
|
7854
|
-
async
|
|
7855
|
-
this.params.logger.debug("ClientAction
|
|
7958
|
+
async partialLossAvailable(event) {
|
|
7959
|
+
this.params.logger.debug("ClientAction partialLossAvailable", {
|
|
7856
7960
|
actionName: this.params.actionName,
|
|
7857
7961
|
strategyName: this.params.strategyName,
|
|
7858
7962
|
frameName: this.params.frameName,
|
|
@@ -7861,18 +7965,18 @@ 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?.partialLossAvailable) {
|
|
7969
|
+
await this._handlerInstance.partialLossAvailable(event);
|
|
7866
7970
|
}
|
|
7867
7971
|
// Call callback if defined
|
|
7868
7972
|
await CALL_PARTIAL_LOSS_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
7869
7973
|
}
|
|
7870
7974
|
;
|
|
7871
7975
|
/**
|
|
7872
|
-
* Handles ping events during scheduled signal monitoring.
|
|
7976
|
+
* Handles scheduled ping events during scheduled signal monitoring.
|
|
7873
7977
|
*/
|
|
7874
|
-
async
|
|
7875
|
-
this.params.logger.debug("ClientAction
|
|
7978
|
+
async pingScheduled(event) {
|
|
7979
|
+
this.params.logger.debug("ClientAction pingScheduled", {
|
|
7876
7980
|
actionName: this.params.actionName,
|
|
7877
7981
|
strategyName: this.params.strategyName,
|
|
7878
7982
|
frameName: this.params.frameName,
|
|
@@ -7881,11 +7985,31 @@ 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?.pingScheduled) {
|
|
7989
|
+
await this._handlerInstance.pingScheduled(event);
|
|
7886
7990
|
}
|
|
7887
7991
|
// Call callback if defined
|
|
7888
|
-
await
|
|
7992
|
+
await CALL_PING_SCHEDULED_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
7993
|
+
}
|
|
7994
|
+
;
|
|
7995
|
+
/**
|
|
7996
|
+
* Handles active ping events during active pending signal monitoring.
|
|
7997
|
+
*/
|
|
7998
|
+
async pingActive(event) {
|
|
7999
|
+
this.params.logger.debug("ClientAction pingActive", {
|
|
8000
|
+
actionName: this.params.actionName,
|
|
8001
|
+
strategyName: this.params.strategyName,
|
|
8002
|
+
frameName: this.params.frameName,
|
|
8003
|
+
});
|
|
8004
|
+
if (!this._handlerInstance) {
|
|
8005
|
+
await this.waitForInit();
|
|
8006
|
+
}
|
|
8007
|
+
// Call handler method if defined
|
|
8008
|
+
if (this._handlerInstance?.pingActive) {
|
|
8009
|
+
await this._handlerInstance.pingActive(event);
|
|
8010
|
+
}
|
|
8011
|
+
// Call callback if defined
|
|
8012
|
+
await CALL_PING_ACTIVE_CALLBACK_FN(this, event, this.params.strategyName, this.params.frameName, event.backtest);
|
|
7889
8013
|
}
|
|
7890
8014
|
;
|
|
7891
8015
|
/**
|
|
@@ -8053,13 +8177,13 @@ class ActionConnectionService {
|
|
|
8053
8177
|
* @param backtest - Whether running in backtest mode
|
|
8054
8178
|
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
8055
8179
|
*/
|
|
8056
|
-
this.
|
|
8057
|
-
this.loggerService.log("actionConnectionService
|
|
8180
|
+
this.breakevenAvailable = async (event, backtest, context) => {
|
|
8181
|
+
this.loggerService.log("actionConnectionService breakevenAvailable", {
|
|
8058
8182
|
backtest,
|
|
8059
8183
|
context,
|
|
8060
8184
|
});
|
|
8061
8185
|
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8062
|
-
await action.
|
|
8186
|
+
await action.breakevenAvailable(event);
|
|
8063
8187
|
};
|
|
8064
8188
|
/**
|
|
8065
8189
|
* Routes partialProfit event to appropriate ClientAction instance.
|
|
@@ -8068,13 +8192,13 @@ class ActionConnectionService {
|
|
|
8068
8192
|
* @param backtest - Whether running in backtest mode
|
|
8069
8193
|
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
8070
8194
|
*/
|
|
8071
|
-
this.
|
|
8072
|
-
this.loggerService.log("actionConnectionService
|
|
8195
|
+
this.partialProfitAvailable = async (event, backtest, context) => {
|
|
8196
|
+
this.loggerService.log("actionConnectionService partialProfitAvailable", {
|
|
8073
8197
|
backtest,
|
|
8074
8198
|
context,
|
|
8075
8199
|
});
|
|
8076
8200
|
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8077
|
-
await action.
|
|
8201
|
+
await action.partialProfitAvailable(event);
|
|
8078
8202
|
};
|
|
8079
8203
|
/**
|
|
8080
8204
|
* Routes partialLoss event to appropriate ClientAction instance.
|
|
@@ -8083,28 +8207,43 @@ class ActionConnectionService {
|
|
|
8083
8207
|
* @param backtest - Whether running in backtest mode
|
|
8084
8208
|
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
8085
8209
|
*/
|
|
8086
|
-
this.
|
|
8087
|
-
this.loggerService.log("actionConnectionService
|
|
8210
|
+
this.partialLossAvailable = async (event, backtest, context) => {
|
|
8211
|
+
this.loggerService.log("actionConnectionService partialLossAvailable", {
|
|
8088
8212
|
backtest,
|
|
8089
8213
|
context,
|
|
8090
8214
|
});
|
|
8091
8215
|
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8092
|
-
await action.
|
|
8216
|
+
await action.partialLossAvailable(event);
|
|
8093
8217
|
};
|
|
8094
8218
|
/**
|
|
8095
|
-
* Routes ping event to appropriate ClientAction instance.
|
|
8219
|
+
* Routes scheduled ping event to appropriate ClientAction instance.
|
|
8096
8220
|
*
|
|
8097
|
-
* @param event -
|
|
8221
|
+
* @param event - Scheduled ping event data
|
|
8098
8222
|
* @param backtest - Whether running in backtest mode
|
|
8099
8223
|
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
8100
8224
|
*/
|
|
8101
|
-
this.
|
|
8102
|
-
this.loggerService.log("actionConnectionService
|
|
8225
|
+
this.pingScheduled = async (event, backtest, context) => {
|
|
8226
|
+
this.loggerService.log("actionConnectionService pingScheduled", {
|
|
8103
8227
|
backtest,
|
|
8104
8228
|
context,
|
|
8105
8229
|
});
|
|
8106
8230
|
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8107
|
-
await action.
|
|
8231
|
+
await action.pingScheduled(event);
|
|
8232
|
+
};
|
|
8233
|
+
/**
|
|
8234
|
+
* Routes active ping event to appropriate ClientAction instance.
|
|
8235
|
+
*
|
|
8236
|
+
* @param event - Active ping event data
|
|
8237
|
+
* @param backtest - Whether running in backtest mode
|
|
8238
|
+
* @param context - Execution context with action name, strategy name, exchange name, frame name
|
|
8239
|
+
*/
|
|
8240
|
+
this.pingActive = async (event, backtest, context) => {
|
|
8241
|
+
this.loggerService.log("actionConnectionService pingActive", {
|
|
8242
|
+
backtest,
|
|
8243
|
+
context,
|
|
8244
|
+
});
|
|
8245
|
+
const action = this.getAction(context.actionName, context.strategyName, context.exchangeName, context.frameName, backtest);
|
|
8246
|
+
await action.pingActive(event);
|
|
8108
8247
|
};
|
|
8109
8248
|
/**
|
|
8110
8249
|
* Routes riskRejection event to appropriate ClientAction instance.
|
|
@@ -9168,81 +9307,102 @@ class ActionCoreService {
|
|
|
9168
9307
|
* Routes breakeven event to all registered actions for the strategy.
|
|
9169
9308
|
*
|
|
9170
9309
|
* Retrieves action list from strategy schema (IStrategySchema.actions)
|
|
9171
|
-
* and invokes the
|
|
9310
|
+
* and invokes the breakevenAvailable handler on each ClientAction instance sequentially.
|
|
9172
9311
|
*
|
|
9173
9312
|
* @param backtest - Whether running in backtest mode (true) or live mode (false)
|
|
9174
9313
|
* @param event - Breakeven milestone data (stop-loss moved to entry price)
|
|
9175
9314
|
* @param context - Strategy execution context with strategyName, exchangeName, frameName
|
|
9176
9315
|
*/
|
|
9177
|
-
this.
|
|
9178
|
-
this.loggerService.log("actionCoreService
|
|
9316
|
+
this.breakevenAvailable = async (backtest, event, context) => {
|
|
9317
|
+
this.loggerService.log("actionCoreService breakevenAvailable", {
|
|
9179
9318
|
context,
|
|
9180
9319
|
});
|
|
9181
9320
|
await this.validate(context);
|
|
9182
9321
|
const { actions = [] } = this.strategySchemaService.get(context.strategyName);
|
|
9183
9322
|
for (const actionName of actions) {
|
|
9184
|
-
await this.actionConnectionService.
|
|
9323
|
+
await this.actionConnectionService.breakevenAvailable(event, backtest, { actionName, ...context });
|
|
9185
9324
|
}
|
|
9186
9325
|
};
|
|
9187
9326
|
/**
|
|
9188
9327
|
* Routes partial profit 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 partialProfitAvailable 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 - Profit milestone data with level (10%, 20%, etc.) and price
|
|
9195
9334
|
* @param context - Strategy execution context with strategyName, exchangeName, frameName
|
|
9196
9335
|
*/
|
|
9197
|
-
this.
|
|
9198
|
-
this.loggerService.log("actionCoreService
|
|
9336
|
+
this.partialProfitAvailable = async (backtest, event, context) => {
|
|
9337
|
+
this.loggerService.log("actionCoreService partialProfitAvailable", {
|
|
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.partialProfitAvailable(event, backtest, { actionName, ...context });
|
|
9205
9344
|
}
|
|
9206
9345
|
};
|
|
9207
9346
|
/**
|
|
9208
9347
|
* Routes partial loss 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 partialLossAvailable 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 - Loss 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.partialLossAvailable = async (backtest, event, context) => {
|
|
9357
|
+
this.loggerService.log("actionCoreService partialLossAvailable", {
|
|
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.partialLossAvailable(event, backtest, { actionName, ...context });
|
|
9225
9364
|
}
|
|
9226
9365
|
};
|
|
9227
9366
|
/**
|
|
9228
|
-
* Routes ping event to all registered actions for the strategy.
|
|
9367
|
+
* Routes scheduled ping 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 pingScheduled handler on each ClientAction instance sequentially.
|
|
9232
9371
|
* Called every minute during scheduled signal monitoring.
|
|
9233
9372
|
*
|
|
9234
9373
|
* @param backtest - Whether running in backtest mode (true) or live mode (false)
|
|
9235
9374
|
* @param event - Scheduled signal monitoring data
|
|
9236
9375
|
* @param context - Strategy execution context with strategyName, exchangeName, frameName
|
|
9237
9376
|
*/
|
|
9238
|
-
this.
|
|
9239
|
-
this.loggerService.log("actionCoreService
|
|
9377
|
+
this.pingScheduled = async (backtest, event, context) => {
|
|
9378
|
+
this.loggerService.log("actionCoreService pingScheduled", {
|
|
9379
|
+
context,
|
|
9380
|
+
});
|
|
9381
|
+
await this.validate(context);
|
|
9382
|
+
const { actions = [] } = this.strategySchemaService.get(context.strategyName);
|
|
9383
|
+
for (const actionName of actions) {
|
|
9384
|
+
await this.actionConnectionService.pingScheduled(event, backtest, { actionName, ...context });
|
|
9385
|
+
}
|
|
9386
|
+
};
|
|
9387
|
+
/**
|
|
9388
|
+
* Routes active ping event to all registered actions for the strategy.
|
|
9389
|
+
*
|
|
9390
|
+
* Retrieves action list from strategy schema (IStrategySchema.actions)
|
|
9391
|
+
* and invokes the pingActive handler on each ClientAction instance sequentially.
|
|
9392
|
+
* Called every minute during active pending signal monitoring.
|
|
9393
|
+
*
|
|
9394
|
+
* @param backtest - Whether running in backtest mode (true) or live mode (false)
|
|
9395
|
+
* @param event - Active pending signal monitoring data
|
|
9396
|
+
* @param context - Strategy execution context with strategyName, exchangeName, frameName
|
|
9397
|
+
*/
|
|
9398
|
+
this.pingActive = async (backtest, event, context) => {
|
|
9399
|
+
this.loggerService.log("actionCoreService pingActive", {
|
|
9240
9400
|
context,
|
|
9241
9401
|
});
|
|
9242
9402
|
await this.validate(context);
|
|
9243
9403
|
const { actions = [] } = this.strategySchemaService.get(context.strategyName);
|
|
9244
9404
|
for (const actionName of actions) {
|
|
9245
|
-
await this.actionConnectionService.
|
|
9405
|
+
await this.actionConnectionService.pingActive(event, backtest, { actionName, ...context });
|
|
9246
9406
|
}
|
|
9247
9407
|
};
|
|
9248
9408
|
/**
|
|
@@ -10361,7 +10521,11 @@ class LiveLogicPrivateService {
|
|
|
10361
10521
|
await sleep(TICK_TTL);
|
|
10362
10522
|
continue;
|
|
10363
10523
|
}
|
|
10364
|
-
|
|
10524
|
+
if (result.action === "waiting") {
|
|
10525
|
+
await sleep(TICK_TTL);
|
|
10526
|
+
continue;
|
|
10527
|
+
}
|
|
10528
|
+
// Yield opened, closed, cancelled results
|
|
10365
10529
|
yield result;
|
|
10366
10530
|
// Check if strategy should stop after signal is closed
|
|
10367
10531
|
if (result.action === "closed") {
|
|
@@ -12296,6 +12460,12 @@ var _a$1, _b$1;
|
|
|
12296
12460
|
const MARKDOWN_METHOD_NAME_ENABLE = "MarkdownUtils.enable";
|
|
12297
12461
|
const MARKDOWN_METHOD_NAME_DISABLE = "MarkdownUtils.disable";
|
|
12298
12462
|
const MARKDOWN_METHOD_NAME_USE_ADAPTER = "MarkdownAdapter.useMarkdownAdapter";
|
|
12463
|
+
const MARKDOWN_METHOD_NAME_FILE_DUMP = "MarkdownFileAdapter.dump";
|
|
12464
|
+
const MARKDOWN_METHOD_NAME_FOLDER_DUMP = "MarkdownFolderAdapter.dump";
|
|
12465
|
+
const MARKDOWN_METHOD_NAME_WRITE_DATA = "MarkdownAdapter.writeData";
|
|
12466
|
+
const MARKDOWN_METHOD_NAME_USE_MD = "MarkdownAdapter.useMd";
|
|
12467
|
+
const MARKDOWN_METHOD_NAME_USE_JSONL = "MarkdownAdapter.useJsonl";
|
|
12468
|
+
const MARKDOWN_METHOD_NAME_USE_DUMMY = "MarkdownAdapter.useDummy";
|
|
12299
12469
|
const WAIT_FOR_INIT_SYMBOL$1 = Symbol("wait-for-init");
|
|
12300
12470
|
const WRITE_SAFE_SYMBOL$1 = Symbol("write-safe");
|
|
12301
12471
|
/**
|
|
@@ -12389,7 +12559,7 @@ class MarkdownFileBase {
|
|
|
12389
12559
|
* @throws Error if stream not initialized or write timeout exceeded
|
|
12390
12560
|
*/
|
|
12391
12561
|
async dump(data, options) {
|
|
12392
|
-
backtest$1.loggerService.debug(
|
|
12562
|
+
backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_FILE_DUMP, {
|
|
12393
12563
|
markdownName: this.markdownName,
|
|
12394
12564
|
options,
|
|
12395
12565
|
});
|
|
@@ -12471,7 +12641,7 @@ class MarkdownFolderBase {
|
|
|
12471
12641
|
* @throws Error if directory creation or file write fails
|
|
12472
12642
|
*/
|
|
12473
12643
|
async dump(content, options) {
|
|
12474
|
-
backtest$1.loggerService.debug(
|
|
12644
|
+
backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_FOLDER_DUMP, {
|
|
12475
12645
|
markdownName: this.markdownName,
|
|
12476
12646
|
options,
|
|
12477
12647
|
});
|
|
@@ -12711,7 +12881,7 @@ class MarkdownAdapter extends MarkdownUtils {
|
|
|
12711
12881
|
* @internal - Use service-specific dump methods instead (e.g., Backtest.dump)
|
|
12712
12882
|
*/
|
|
12713
12883
|
async writeData(markdownName, content, options) {
|
|
12714
|
-
backtest$1.loggerService.debug(
|
|
12884
|
+
backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_WRITE_DATA, {
|
|
12715
12885
|
markdownName,
|
|
12716
12886
|
options,
|
|
12717
12887
|
});
|
|
@@ -12726,7 +12896,7 @@ class MarkdownAdapter extends MarkdownUtils {
|
|
|
12726
12896
|
* Each dump creates a separate .md file.
|
|
12727
12897
|
*/
|
|
12728
12898
|
useMd() {
|
|
12729
|
-
backtest$1.loggerService.debug(
|
|
12899
|
+
backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_USE_MD);
|
|
12730
12900
|
this.useMarkdownAdapter(MarkdownFolderBase);
|
|
12731
12901
|
}
|
|
12732
12902
|
/**
|
|
@@ -12735,7 +12905,7 @@ class MarkdownAdapter extends MarkdownUtils {
|
|
|
12735
12905
|
* All dumps append to a single .jsonl file per markdown type.
|
|
12736
12906
|
*/
|
|
12737
12907
|
useJsonl() {
|
|
12738
|
-
backtest$1.loggerService.debug(
|
|
12908
|
+
backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_USE_JSONL);
|
|
12739
12909
|
this.useMarkdownAdapter(MarkdownFileBase);
|
|
12740
12910
|
}
|
|
12741
12911
|
/**
|
|
@@ -12743,7 +12913,7 @@ class MarkdownAdapter extends MarkdownUtils {
|
|
|
12743
12913
|
* All future markdown writes will be no-ops.
|
|
12744
12914
|
*/
|
|
12745
12915
|
useDummy() {
|
|
12746
|
-
backtest$1.loggerService.debug(
|
|
12916
|
+
backtest$1.loggerService.debug(MARKDOWN_METHOD_NAME_USE_DUMMY);
|
|
12747
12917
|
this.useMarkdownAdapter(MarkdownDummy);
|
|
12748
12918
|
}
|
|
12749
12919
|
}
|
|
@@ -13406,6 +13576,98 @@ let ReportStorage$5 = class ReportStorage {
|
|
|
13406
13576
|
this._eventList.pop();
|
|
13407
13577
|
}
|
|
13408
13578
|
}
|
|
13579
|
+
/**
|
|
13580
|
+
* Adds a scheduled event to the storage.
|
|
13581
|
+
*
|
|
13582
|
+
* @param data - Scheduled tick result
|
|
13583
|
+
*/
|
|
13584
|
+
addScheduledEvent(data) {
|
|
13585
|
+
this._eventList.unshift({
|
|
13586
|
+
timestamp: data.signal.scheduledAt,
|
|
13587
|
+
action: "scheduled",
|
|
13588
|
+
symbol: data.signal.symbol,
|
|
13589
|
+
signalId: data.signal.id,
|
|
13590
|
+
position: data.signal.position,
|
|
13591
|
+
note: data.signal.note,
|
|
13592
|
+
currentPrice: data.currentPrice,
|
|
13593
|
+
priceOpen: data.signal.priceOpen,
|
|
13594
|
+
priceTakeProfit: data.signal.priceTakeProfit,
|
|
13595
|
+
priceStopLoss: data.signal.priceStopLoss,
|
|
13596
|
+
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
13597
|
+
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
13598
|
+
totalExecuted: data.signal.totalExecuted,
|
|
13599
|
+
});
|
|
13600
|
+
// Trim queue if exceeded MAX_EVENTS
|
|
13601
|
+
if (this._eventList.length > MAX_EVENTS$6) {
|
|
13602
|
+
this._eventList.pop();
|
|
13603
|
+
}
|
|
13604
|
+
}
|
|
13605
|
+
/**
|
|
13606
|
+
* Adds a waiting event to the storage.
|
|
13607
|
+
* Replaces the last waiting event with the same signalId.
|
|
13608
|
+
*
|
|
13609
|
+
* @param data - Waiting tick result
|
|
13610
|
+
*/
|
|
13611
|
+
addWaitingEvent(data) {
|
|
13612
|
+
const newEvent = {
|
|
13613
|
+
timestamp: Date.now(),
|
|
13614
|
+
action: "waiting",
|
|
13615
|
+
symbol: data.signal.symbol,
|
|
13616
|
+
signalId: data.signal.id,
|
|
13617
|
+
position: data.signal.position,
|
|
13618
|
+
note: data.signal.note,
|
|
13619
|
+
currentPrice: data.currentPrice,
|
|
13620
|
+
priceOpen: data.signal.priceOpen,
|
|
13621
|
+
priceTakeProfit: data.signal.priceTakeProfit,
|
|
13622
|
+
priceStopLoss: data.signal.priceStopLoss,
|
|
13623
|
+
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
13624
|
+
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
13625
|
+
totalExecuted: data.signal.totalExecuted,
|
|
13626
|
+
percentTp: data.percentTp,
|
|
13627
|
+
percentSl: data.percentSl,
|
|
13628
|
+
pnl: data.pnl.pnlPercentage,
|
|
13629
|
+
};
|
|
13630
|
+
// Find the last waiting event with the same signalId
|
|
13631
|
+
const lastWaitingIndex = this._eventList.findLastIndex((event) => event.action === "waiting" && event.signalId === data.signal.id);
|
|
13632
|
+
// Replace the last waiting event with the same signalId
|
|
13633
|
+
if (lastWaitingIndex !== -1) {
|
|
13634
|
+
this._eventList[lastWaitingIndex] = newEvent;
|
|
13635
|
+
return;
|
|
13636
|
+
}
|
|
13637
|
+
// If no previous waiting event found, add new event
|
|
13638
|
+
this._eventList.unshift(newEvent);
|
|
13639
|
+
// Trim queue if exceeded MAX_EVENTS
|
|
13640
|
+
if (this._eventList.length > MAX_EVENTS$6) {
|
|
13641
|
+
this._eventList.pop();
|
|
13642
|
+
}
|
|
13643
|
+
}
|
|
13644
|
+
/**
|
|
13645
|
+
* Adds a cancelled event to the storage.
|
|
13646
|
+
*
|
|
13647
|
+
* @param data - Cancelled tick result
|
|
13648
|
+
*/
|
|
13649
|
+
addCancelledEvent(data) {
|
|
13650
|
+
this._eventList.unshift({
|
|
13651
|
+
timestamp: data.closeTimestamp,
|
|
13652
|
+
action: "cancelled",
|
|
13653
|
+
symbol: data.signal.symbol,
|
|
13654
|
+
signalId: data.signal.id,
|
|
13655
|
+
position: data.signal.position,
|
|
13656
|
+
note: data.signal.note,
|
|
13657
|
+
currentPrice: data.currentPrice,
|
|
13658
|
+
priceOpen: data.signal.priceOpen,
|
|
13659
|
+
priceTakeProfit: data.signal.priceTakeProfit,
|
|
13660
|
+
priceStopLoss: data.signal.priceStopLoss,
|
|
13661
|
+
originalPriceTakeProfit: data.signal.originalPriceTakeProfit,
|
|
13662
|
+
originalPriceStopLoss: data.signal.originalPriceStopLoss,
|
|
13663
|
+
totalExecuted: data.signal.totalExecuted,
|
|
13664
|
+
cancelReason: data.reason,
|
|
13665
|
+
});
|
|
13666
|
+
// Trim queue if exceeded MAX_EVENTS
|
|
13667
|
+
if (this._eventList.length > MAX_EVENTS$6) {
|
|
13668
|
+
this._eventList.pop();
|
|
13669
|
+
}
|
|
13670
|
+
}
|
|
13409
13671
|
/**
|
|
13410
13672
|
* Calculates statistical data from live trading events (Controller).
|
|
13411
13673
|
* Returns null for any unsafe numeric values (NaN, Infinity, etc).
|
|
@@ -13663,6 +13925,12 @@ class LiveMarkdownService {
|
|
|
13663
13925
|
if (data.action === "idle") {
|
|
13664
13926
|
storage.addIdleEvent(data.currentPrice);
|
|
13665
13927
|
}
|
|
13928
|
+
else if (data.action === "scheduled") {
|
|
13929
|
+
storage.addScheduledEvent(data);
|
|
13930
|
+
}
|
|
13931
|
+
else if (data.action === "waiting") {
|
|
13932
|
+
storage.addWaitingEvent(data);
|
|
13933
|
+
}
|
|
13666
13934
|
else if (data.action === "opened") {
|
|
13667
13935
|
storage.addOpenedEvent(data);
|
|
13668
13936
|
}
|
|
@@ -13672,6 +13940,9 @@ class LiveMarkdownService {
|
|
|
13672
13940
|
else if (data.action === "closed") {
|
|
13673
13941
|
storage.addClosedEvent(data);
|
|
13674
13942
|
}
|
|
13943
|
+
else if (data.action === "cancelled") {
|
|
13944
|
+
storage.addCancelledEvent(data);
|
|
13945
|
+
}
|
|
13675
13946
|
};
|
|
13676
13947
|
/**
|
|
13677
13948
|
* Gets statistical data from all live trading events for a symbol-strategy pair.
|
|
@@ -18098,7 +18369,7 @@ const CREATE_COMMIT_PROFIT_FN = (self) => async (symbol, strategyName, exchangeN
|
|
|
18098
18369
|
timestamp,
|
|
18099
18370
|
};
|
|
18100
18371
|
await partialProfitSubject.next(event);
|
|
18101
|
-
await self.actionCoreService.
|
|
18372
|
+
await self.actionCoreService.partialProfitAvailable(backtest, event, { strategyName, exchangeName, frameName });
|
|
18102
18373
|
};
|
|
18103
18374
|
/**
|
|
18104
18375
|
* Creates a callback function for emitting loss events to partialLossSubject.
|
|
@@ -18122,7 +18393,7 @@ const CREATE_COMMIT_LOSS_FN = (self) => async (symbol, strategyName, exchangeNam
|
|
|
18122
18393
|
timestamp,
|
|
18123
18394
|
};
|
|
18124
18395
|
await partialLossSubject.next(event);
|
|
18125
|
-
await self.actionCoreService.
|
|
18396
|
+
await self.actionCoreService.partialLossAvailable(backtest, event, { strategyName, exchangeName, frameName });
|
|
18126
18397
|
};
|
|
18127
18398
|
/**
|
|
18128
18399
|
* Connection service for partial profit/loss tracking.
|
|
@@ -19271,7 +19542,7 @@ const CREATE_COMMIT_BREAKEVEN_FN = (self) => async (symbol, strategyName, exchan
|
|
|
19271
19542
|
timestamp,
|
|
19272
19543
|
};
|
|
19273
19544
|
await breakevenSubject.next(event);
|
|
19274
|
-
await self.actionCoreService.
|
|
19545
|
+
await self.actionCoreService.breakevenAvailable(backtest, event, { strategyName, exchangeName, frameName });
|
|
19275
19546
|
};
|
|
19276
19547
|
/**
|
|
19277
19548
|
* Connection service for breakeven tracking.
|
|
@@ -21356,6 +21627,43 @@ class LiveReportService {
|
|
|
21356
21627
|
if (data.action === "idle") {
|
|
21357
21628
|
await Report.writeData("live", baseEvent, searchOptions);
|
|
21358
21629
|
}
|
|
21630
|
+
else if (data.action === "scheduled") {
|
|
21631
|
+
await Report.writeData("live", {
|
|
21632
|
+
...baseEvent,
|
|
21633
|
+
signalId: data.signal?.id,
|
|
21634
|
+
position: data.signal?.position,
|
|
21635
|
+
note: data.signal?.note,
|
|
21636
|
+
priceOpen: data.signal?.priceOpen,
|
|
21637
|
+
priceTakeProfit: data.signal?.priceTakeProfit,
|
|
21638
|
+
priceStopLoss: data.signal?.priceStopLoss,
|
|
21639
|
+
originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
|
|
21640
|
+
originalPriceStopLoss: data.signal?.originalPriceStopLoss,
|
|
21641
|
+
totalExecuted: data.signal?.totalExecuted,
|
|
21642
|
+
scheduledAt: data.signal?.scheduledAt,
|
|
21643
|
+
minuteEstimatedTime: data.signal?.minuteEstimatedTime,
|
|
21644
|
+
}, { ...searchOptions, signalId: data.signal?.id });
|
|
21645
|
+
}
|
|
21646
|
+
else if (data.action === "waiting") {
|
|
21647
|
+
await Report.writeData("live", {
|
|
21648
|
+
...baseEvent,
|
|
21649
|
+
signalId: data.signal?.id,
|
|
21650
|
+
position: data.signal?.position,
|
|
21651
|
+
note: data.signal?.note,
|
|
21652
|
+
priceOpen: data.signal?.priceOpen,
|
|
21653
|
+
priceTakeProfit: data.signal?.priceTakeProfit,
|
|
21654
|
+
priceStopLoss: data.signal?.priceStopLoss,
|
|
21655
|
+
originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
|
|
21656
|
+
originalPriceStopLoss: data.signal?.originalPriceStopLoss,
|
|
21657
|
+
totalExecuted: data.signal?.totalExecuted,
|
|
21658
|
+
scheduledAt: data.signal?.scheduledAt,
|
|
21659
|
+
minuteEstimatedTime: data.signal?.minuteEstimatedTime,
|
|
21660
|
+
percentTp: data.percentTp,
|
|
21661
|
+
percentSl: data.percentSl,
|
|
21662
|
+
pnl: data.pnl.pnlPercentage,
|
|
21663
|
+
pnlPriceOpen: data.pnl.priceOpen,
|
|
21664
|
+
pnlPriceClose: data.pnl.priceClose,
|
|
21665
|
+
}, { ...searchOptions, signalId: data.signal?.id });
|
|
21666
|
+
}
|
|
21359
21667
|
else if (data.action === "opened") {
|
|
21360
21668
|
await Report.writeData("live", {
|
|
21361
21669
|
...baseEvent,
|
|
@@ -21422,6 +21730,24 @@ class LiveReportService {
|
|
|
21422
21730
|
closeTime: data.closeTimestamp,
|
|
21423
21731
|
}, { ...searchOptions, signalId: data.signal?.id });
|
|
21424
21732
|
}
|
|
21733
|
+
else if (data.action === "cancelled") {
|
|
21734
|
+
await Report.writeData("live", {
|
|
21735
|
+
...baseEvent,
|
|
21736
|
+
signalId: data.signal?.id,
|
|
21737
|
+
position: data.signal?.position,
|
|
21738
|
+
note: data.signal?.note,
|
|
21739
|
+
priceOpen: data.signal?.priceOpen,
|
|
21740
|
+
priceTakeProfit: data.signal?.priceTakeProfit,
|
|
21741
|
+
priceStopLoss: data.signal?.priceStopLoss,
|
|
21742
|
+
originalPriceTakeProfit: data.signal?.originalPriceTakeProfit,
|
|
21743
|
+
originalPriceStopLoss: data.signal?.originalPriceStopLoss,
|
|
21744
|
+
totalExecuted: data.signal?.totalExecuted,
|
|
21745
|
+
scheduledAt: data.signal?.scheduledAt,
|
|
21746
|
+
minuteEstimatedTime: data.signal?.minuteEstimatedTime,
|
|
21747
|
+
cancelReason: data.reason,
|
|
21748
|
+
closeTime: data.closeTimestamp,
|
|
21749
|
+
}, { ...searchOptions, signalId: data.signal?.id });
|
|
21750
|
+
}
|
|
21425
21751
|
};
|
|
21426
21752
|
/**
|
|
21427
21753
|
* Subscribes to live signal emitter to receive tick events.
|
|
@@ -22611,23 +22937,23 @@ const backtest = {
|
|
|
22611
22937
|
init();
|
|
22612
22938
|
var backtest$1 = backtest;
|
|
22613
22939
|
|
|
22614
|
-
const GET_TIMEFRAME_METHOD_NAME = "get.
|
|
22940
|
+
const GET_TIMEFRAME_METHOD_NAME = "get.getBacktestTimeframe";
|
|
22615
22941
|
/**
|
|
22616
22942
|
* Retrieves current backtest timeframe for given symbol.
|
|
22617
22943
|
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
22618
22944
|
* @returns Promise resolving to array of Date objects representing tick timestamps
|
|
22619
22945
|
* @throws Error if called outside of backtest execution context
|
|
22620
22946
|
*/
|
|
22621
|
-
async function
|
|
22947
|
+
async function getBacktestTimeframe(symbol) {
|
|
22622
22948
|
backtest$1.loggerService.info(GET_TIMEFRAME_METHOD_NAME, { symbol });
|
|
22623
22949
|
if (!ExecutionContextService.hasContext()) {
|
|
22624
|
-
throw new Error("
|
|
22950
|
+
throw new Error("getBacktestTimeframe requires an execution context");
|
|
22625
22951
|
}
|
|
22626
22952
|
if (!MethodContextService.hasContext()) {
|
|
22627
|
-
throw new Error("
|
|
22953
|
+
throw new Error("getBacktestTimeframe requires a method context");
|
|
22628
22954
|
}
|
|
22629
22955
|
if (!backtest$1.executionContextService.context.backtest) {
|
|
22630
|
-
throw new Error("
|
|
22956
|
+
throw new Error("getBacktestTimeframe can only be used during backtest execution");
|
|
22631
22957
|
}
|
|
22632
22958
|
return await backtest$1.frameCoreService.getTimeframe(symbol, backtest$1.methodContextService.context.frameName);
|
|
22633
22959
|
}
|
|
@@ -22827,14 +23153,14 @@ async function validate(args = {}) {
|
|
|
22827
23153
|
return await validateInternal(args);
|
|
22828
23154
|
}
|
|
22829
23155
|
|
|
22830
|
-
const GET_STRATEGY_METHOD_NAME = "get.
|
|
22831
|
-
const GET_EXCHANGE_METHOD_NAME = "get.
|
|
22832
|
-
const GET_FRAME_METHOD_NAME = "get.
|
|
22833
|
-
const GET_WALKER_METHOD_NAME = "get.
|
|
22834
|
-
const GET_SIZING_METHOD_NAME = "get.
|
|
22835
|
-
const GET_RISK_METHOD_NAME = "get.
|
|
22836
|
-
const GET_OPTIMIZER_METHOD_NAME = "get.
|
|
22837
|
-
const GET_ACTION_METHOD_NAME = "get.
|
|
23156
|
+
const GET_STRATEGY_METHOD_NAME = "get.getStrategySchema";
|
|
23157
|
+
const GET_EXCHANGE_METHOD_NAME = "get.getExchangeSchema";
|
|
23158
|
+
const GET_FRAME_METHOD_NAME = "get.getFrameSchema";
|
|
23159
|
+
const GET_WALKER_METHOD_NAME = "get.getWalkerSchema";
|
|
23160
|
+
const GET_SIZING_METHOD_NAME = "get.getSizingSchema";
|
|
23161
|
+
const GET_RISK_METHOD_NAME = "get.getRiskSchema";
|
|
23162
|
+
const GET_OPTIMIZER_METHOD_NAME = "get.getOptimizerSchema";
|
|
23163
|
+
const GET_ACTION_METHOD_NAME = "get.getActionSchema";
|
|
22838
23164
|
/**
|
|
22839
23165
|
* Retrieves a registered strategy schema by name.
|
|
22840
23166
|
*
|
|
@@ -22849,7 +23175,7 @@ const GET_ACTION_METHOD_NAME = "get.getAction";
|
|
|
22849
23175
|
* console.log(strategy.getSignal); // async function
|
|
22850
23176
|
* ```
|
|
22851
23177
|
*/
|
|
22852
|
-
function
|
|
23178
|
+
function getStrategySchema(strategyName) {
|
|
22853
23179
|
backtest$1.loggerService.log(GET_STRATEGY_METHOD_NAME, {
|
|
22854
23180
|
strategyName,
|
|
22855
23181
|
});
|
|
@@ -22870,7 +23196,7 @@ function getStrategy(strategyName) {
|
|
|
22870
23196
|
* console.log(exchange.formatPrice); // async function
|
|
22871
23197
|
* ```
|
|
22872
23198
|
*/
|
|
22873
|
-
function
|
|
23199
|
+
function getExchangeSchema(exchangeName) {
|
|
22874
23200
|
backtest$1.loggerService.log(GET_EXCHANGE_METHOD_NAME, {
|
|
22875
23201
|
exchangeName,
|
|
22876
23202
|
});
|
|
@@ -22892,7 +23218,7 @@ function getExchange(exchangeName) {
|
|
|
22892
23218
|
* console.log(frame.endDate); // Date object
|
|
22893
23219
|
* ```
|
|
22894
23220
|
*/
|
|
22895
|
-
function
|
|
23221
|
+
function getFrameSchema(frameName) {
|
|
22896
23222
|
backtest$1.loggerService.log(GET_FRAME_METHOD_NAME, {
|
|
22897
23223
|
frameName,
|
|
22898
23224
|
});
|
|
@@ -22915,7 +23241,7 @@ function getFrame(frameName) {
|
|
|
22915
23241
|
* console.log(walker.metric); // "sharpeRatio"
|
|
22916
23242
|
* ```
|
|
22917
23243
|
*/
|
|
22918
|
-
function
|
|
23244
|
+
function getWalkerSchema(walkerName) {
|
|
22919
23245
|
backtest$1.loggerService.log(GET_WALKER_METHOD_NAME, {
|
|
22920
23246
|
walkerName,
|
|
22921
23247
|
});
|
|
@@ -22937,7 +23263,7 @@ function getWalker(walkerName) {
|
|
|
22937
23263
|
* console.log(sizing.maxPositionPercentage); // 10
|
|
22938
23264
|
* ```
|
|
22939
23265
|
*/
|
|
22940
|
-
function
|
|
23266
|
+
function getSizingSchema(sizingName) {
|
|
22941
23267
|
backtest$1.loggerService.log(GET_SIZING_METHOD_NAME, {
|
|
22942
23268
|
sizingName,
|
|
22943
23269
|
});
|
|
@@ -22958,7 +23284,7 @@ function getSizing(sizingName) {
|
|
|
22958
23284
|
* console.log(risk.validations); // Array of validation functions
|
|
22959
23285
|
* ```
|
|
22960
23286
|
*/
|
|
22961
|
-
function
|
|
23287
|
+
function getRiskSchema(riskName) {
|
|
22962
23288
|
backtest$1.loggerService.log(GET_RISK_METHOD_NAME, {
|
|
22963
23289
|
riskName,
|
|
22964
23290
|
});
|
|
@@ -22981,7 +23307,7 @@ function getRisk(riskName) {
|
|
|
22981
23307
|
* console.log(optimizer.getPrompt); // async function
|
|
22982
23308
|
* ```
|
|
22983
23309
|
*/
|
|
22984
|
-
function
|
|
23310
|
+
function getOptimizerSchema(optimizerName) {
|
|
22985
23311
|
backtest$1.loggerService.log(GET_OPTIMIZER_METHOD_NAME, {
|
|
22986
23312
|
optimizerName,
|
|
22987
23313
|
});
|
|
@@ -23002,7 +23328,7 @@ function getOptimizer(optimizerName) {
|
|
|
23002
23328
|
* console.log(action.callbacks); // Optional lifecycle callbacks
|
|
23003
23329
|
* ```
|
|
23004
23330
|
*/
|
|
23005
|
-
function
|
|
23331
|
+
function getActionSchema(actionName) {
|
|
23006
23332
|
backtest$1.loggerService.log(GET_ACTION_METHOD_NAME, {
|
|
23007
23333
|
actionName,
|
|
23008
23334
|
});
|
|
@@ -23016,6 +23342,8 @@ const FORMAT_PRICE_METHOD_NAME = "exchange.formatPrice";
|
|
|
23016
23342
|
const FORMAT_QUANTITY_METHOD_NAME = "exchange.formatQuantity";
|
|
23017
23343
|
const GET_DATE_METHOD_NAME = "exchange.getDate";
|
|
23018
23344
|
const GET_MODE_METHOD_NAME = "exchange.getMode";
|
|
23345
|
+
const GET_SYMBOL_METHOD_NAME = "exchange.getSymbol";
|
|
23346
|
+
const GET_CONTEXT_METHOD_NAME = "exchange.getContext";
|
|
23019
23347
|
const HAS_TRADE_CONTEXT_METHOD_NAME = "exchange.hasTradeContext";
|
|
23020
23348
|
const GET_ORDER_BOOK_METHOD_NAME = "exchange.getOrderBook";
|
|
23021
23349
|
/**
|
|
@@ -23198,6 +23526,48 @@ async function getMode() {
|
|
|
23198
23526
|
const { backtest: bt } = backtest$1.executionContextService.context;
|
|
23199
23527
|
return bt ? "backtest" : "live";
|
|
23200
23528
|
}
|
|
23529
|
+
/**
|
|
23530
|
+
* Gets the current trading symbol from execution context.
|
|
23531
|
+
*
|
|
23532
|
+
* @returns Promise resolving to the current trading symbol (e.g., "BTCUSDT")
|
|
23533
|
+
* @throws Error if execution context is not active
|
|
23534
|
+
*
|
|
23535
|
+
* @example
|
|
23536
|
+
* ```typescript
|
|
23537
|
+
* const symbol = await getSymbol();
|
|
23538
|
+
* console.log(symbol); // "BTCUSDT"
|
|
23539
|
+
* ```
|
|
23540
|
+
*/
|
|
23541
|
+
async function getSymbol() {
|
|
23542
|
+
backtest$1.loggerService.info(GET_SYMBOL_METHOD_NAME);
|
|
23543
|
+
if (!ExecutionContextService.hasContext()) {
|
|
23544
|
+
throw new Error("getSymbol requires an execution context");
|
|
23545
|
+
}
|
|
23546
|
+
const { symbol } = backtest$1.executionContextService.context;
|
|
23547
|
+
return symbol;
|
|
23548
|
+
}
|
|
23549
|
+
/**
|
|
23550
|
+
* Gets the current method context.
|
|
23551
|
+
*
|
|
23552
|
+
* Returns the context object from the method context service, which contains
|
|
23553
|
+
* information about the current method execution environment.
|
|
23554
|
+
*
|
|
23555
|
+
* @returns Promise resolving to the current method context object
|
|
23556
|
+
* @throws Error if method context is not active
|
|
23557
|
+
*
|
|
23558
|
+
* @example
|
|
23559
|
+
* ```typescript
|
|
23560
|
+
* const context = await getContext();
|
|
23561
|
+
* console.log(context); // { ...method context data... }
|
|
23562
|
+
* ```
|
|
23563
|
+
*/
|
|
23564
|
+
async function getContext() {
|
|
23565
|
+
backtest$1.loggerService.info(GET_CONTEXT_METHOD_NAME);
|
|
23566
|
+
if (!MethodContextService.hasContext()) {
|
|
23567
|
+
throw new Error("getContext requires a method context");
|
|
23568
|
+
}
|
|
23569
|
+
return backtest$1.methodContextService.context;
|
|
23570
|
+
}
|
|
23201
23571
|
/**
|
|
23202
23572
|
* Fetches order book for a trading pair from the registered exchange.
|
|
23203
23573
|
*
|
|
@@ -23234,52 +23604,12 @@ async function getOrderBook(symbol, depth) {
|
|
|
23234
23604
|
return await backtest$1.exchangeConnectionService.getOrderBook(symbol, depth);
|
|
23235
23605
|
}
|
|
23236
23606
|
|
|
23237
|
-
const
|
|
23238
|
-
const
|
|
23239
|
-
const
|
|
23240
|
-
const
|
|
23241
|
-
const
|
|
23242
|
-
const
|
|
23243
|
-
const BREAKEVEN_METHOD_NAME = "strategy.breakeven";
|
|
23244
|
-
/**
|
|
23245
|
-
* Stops the strategy from generating new signals.
|
|
23246
|
-
*
|
|
23247
|
-
* Sets internal flag to prevent strategy from opening new signals.
|
|
23248
|
-
* Current active signal (if any) will complete normally.
|
|
23249
|
-
* Backtest/Live mode will stop at the next safe point (idle state or after signal closes).
|
|
23250
|
-
*
|
|
23251
|
-
* Automatically detects backtest/live mode from execution context.
|
|
23252
|
-
*
|
|
23253
|
-
* @param symbol - Trading pair symbol
|
|
23254
|
-
* @param strategyName - Strategy name to stop
|
|
23255
|
-
* @returns Promise that resolves when stop flag is set
|
|
23256
|
-
*
|
|
23257
|
-
* @example
|
|
23258
|
-
* ```typescript
|
|
23259
|
-
* import { stop } from "backtest-kit";
|
|
23260
|
-
*
|
|
23261
|
-
* // Stop strategy after some condition
|
|
23262
|
-
* await stop("BTCUSDT", "my-strategy");
|
|
23263
|
-
* ```
|
|
23264
|
-
*/
|
|
23265
|
-
async function stop(symbol) {
|
|
23266
|
-
backtest$1.loggerService.info(STOP_METHOD_NAME, {
|
|
23267
|
-
symbol,
|
|
23268
|
-
});
|
|
23269
|
-
if (!ExecutionContextService.hasContext()) {
|
|
23270
|
-
throw new Error("stop requires an execution context");
|
|
23271
|
-
}
|
|
23272
|
-
if (!MethodContextService.hasContext()) {
|
|
23273
|
-
throw new Error("stop requires a method context");
|
|
23274
|
-
}
|
|
23275
|
-
const { backtest: isBacktest } = backtest$1.executionContextService.context;
|
|
23276
|
-
const { exchangeName, frameName, strategyName } = backtest$1.methodContextService.context;
|
|
23277
|
-
await backtest$1.strategyCoreService.stop(isBacktest, symbol, {
|
|
23278
|
-
exchangeName,
|
|
23279
|
-
frameName,
|
|
23280
|
-
strategyName,
|
|
23281
|
-
});
|
|
23282
|
-
}
|
|
23607
|
+
const CANCEL_METHOD_NAME = "strategy.commitCancel";
|
|
23608
|
+
const PARTIAL_PROFIT_METHOD_NAME = "strategy.commitPartialProfit";
|
|
23609
|
+
const PARTIAL_LOSS_METHOD_NAME = "strategy.commitPartialLoss";
|
|
23610
|
+
const TRAILING_STOP_METHOD_NAME = "strategy.commitTrailingStop";
|
|
23611
|
+
const TRAILING_PROFIT_METHOD_NAME = "strategy.commitTrailingTake";
|
|
23612
|
+
const BREAKEVEN_METHOD_NAME = "strategy.commitBreakeven";
|
|
23283
23613
|
/**
|
|
23284
23614
|
* Cancels the scheduled signal without stopping the strategy.
|
|
23285
23615
|
*
|
|
@@ -23302,7 +23632,7 @@ async function stop(symbol) {
|
|
|
23302
23632
|
* await cancel("BTCUSDT", "my-strategy", "manual-cancel-001");
|
|
23303
23633
|
* ```
|
|
23304
23634
|
*/
|
|
23305
|
-
async function
|
|
23635
|
+
async function commitCancel(symbol, cancelId) {
|
|
23306
23636
|
backtest$1.loggerService.info(CANCEL_METHOD_NAME, {
|
|
23307
23637
|
symbol,
|
|
23308
23638
|
cancelId,
|
|
@@ -23344,7 +23674,7 @@ async function cancel(symbol, cancelId) {
|
|
|
23344
23674
|
* }
|
|
23345
23675
|
* ```
|
|
23346
23676
|
*/
|
|
23347
|
-
async function
|
|
23677
|
+
async function commitPartialProfit(symbol, percentToClose) {
|
|
23348
23678
|
backtest$1.loggerService.info(PARTIAL_PROFIT_METHOD_NAME, {
|
|
23349
23679
|
symbol,
|
|
23350
23680
|
percentToClose,
|
|
@@ -23387,7 +23717,7 @@ async function partialProfit(symbol, percentToClose) {
|
|
|
23387
23717
|
* }
|
|
23388
23718
|
* ```
|
|
23389
23719
|
*/
|
|
23390
|
-
async function
|
|
23720
|
+
async function commitPartialLoss(symbol, percentToClose) {
|
|
23391
23721
|
backtest$1.loggerService.info(PARTIAL_LOSS_METHOD_NAME, {
|
|
23392
23722
|
symbol,
|
|
23393
23723
|
percentToClose,
|
|
@@ -23446,7 +23776,7 @@ async function partialLoss(symbol, percentToClose) {
|
|
|
23446
23776
|
* // success3 = true (ACCEPTED: newDistance = 10% - 7% = 3%, newSL = 97 > 95, better protection)
|
|
23447
23777
|
* ```
|
|
23448
23778
|
*/
|
|
23449
|
-
async function
|
|
23779
|
+
async function commitTrailingStop(symbol, percentShift, currentPrice) {
|
|
23450
23780
|
backtest$1.loggerService.info(TRAILING_STOP_METHOD_NAME, {
|
|
23451
23781
|
symbol,
|
|
23452
23782
|
percentShift,
|
|
@@ -23505,7 +23835,7 @@ async function trailingStop(symbol, percentShift, currentPrice) {
|
|
|
23505
23835
|
* // success3 = true (ACCEPTED: newDistance = 10% - 5% = 5%, newTP = 105 < 107, more conservative)
|
|
23506
23836
|
* ```
|
|
23507
23837
|
*/
|
|
23508
|
-
async function
|
|
23838
|
+
async function commitTrailingTake(symbol, percentShift, currentPrice) {
|
|
23509
23839
|
backtest$1.loggerService.info(TRAILING_PROFIT_METHOD_NAME, {
|
|
23510
23840
|
symbol,
|
|
23511
23841
|
percentShift,
|
|
@@ -23546,7 +23876,7 @@ async function trailingTake(symbol, percentShift, currentPrice) {
|
|
|
23546
23876
|
* }
|
|
23547
23877
|
* ```
|
|
23548
23878
|
*/
|
|
23549
|
-
async function
|
|
23879
|
+
async function commitBreakeven(symbol) {
|
|
23550
23880
|
backtest$1.loggerService.info(BREAKEVEN_METHOD_NAME, {
|
|
23551
23881
|
symbol,
|
|
23552
23882
|
});
|
|
@@ -23562,6 +23892,47 @@ async function breakeven(symbol) {
|
|
|
23562
23892
|
return await backtest$1.strategyCoreService.breakeven(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
|
|
23563
23893
|
}
|
|
23564
23894
|
|
|
23895
|
+
const STOP_METHOD_NAME = "control.stop";
|
|
23896
|
+
/**
|
|
23897
|
+
* Stops the strategy from generating new signals.
|
|
23898
|
+
*
|
|
23899
|
+
* Sets internal flag to prevent strategy from opening new signals.
|
|
23900
|
+
* Current active signal (if any) will complete normally.
|
|
23901
|
+
* Backtest/Live mode will stop at the next safe point (idle state or after signal closes).
|
|
23902
|
+
*
|
|
23903
|
+
* Automatically detects backtest/live mode from execution context.
|
|
23904
|
+
*
|
|
23905
|
+
* @param symbol - Trading pair symbol
|
|
23906
|
+
* @param strategyName - Strategy name to stop
|
|
23907
|
+
* @returns Promise that resolves when stop flag is set
|
|
23908
|
+
*
|
|
23909
|
+
* @example
|
|
23910
|
+
* ```typescript
|
|
23911
|
+
* import { stop } from "backtest-kit";
|
|
23912
|
+
*
|
|
23913
|
+
* // Stop strategy after some condition
|
|
23914
|
+
* await stop("BTCUSDT", "my-strategy");
|
|
23915
|
+
* ```
|
|
23916
|
+
*/
|
|
23917
|
+
async function stop(symbol) {
|
|
23918
|
+
backtest$1.loggerService.info(STOP_METHOD_NAME, {
|
|
23919
|
+
symbol,
|
|
23920
|
+
});
|
|
23921
|
+
if (!ExecutionContextService.hasContext()) {
|
|
23922
|
+
throw new Error("stop requires an execution context");
|
|
23923
|
+
}
|
|
23924
|
+
if (!MethodContextService.hasContext()) {
|
|
23925
|
+
throw new Error("stop requires a method context");
|
|
23926
|
+
}
|
|
23927
|
+
const { backtest: isBacktest } = backtest$1.executionContextService.context;
|
|
23928
|
+
const { exchangeName, frameName, strategyName } = backtest$1.methodContextService.context;
|
|
23929
|
+
await backtest$1.strategyCoreService.stop(isBacktest, symbol, {
|
|
23930
|
+
exchangeName,
|
|
23931
|
+
frameName,
|
|
23932
|
+
strategyName,
|
|
23933
|
+
});
|
|
23934
|
+
}
|
|
23935
|
+
|
|
23565
23936
|
/**
|
|
23566
23937
|
* Sets custom logger implementation for the framework.
|
|
23567
23938
|
*
|
|
@@ -23712,14 +24083,14 @@ function getDefaultColumns() {
|
|
|
23712
24083
|
return DEFAULT_COLUMNS;
|
|
23713
24084
|
}
|
|
23714
24085
|
|
|
23715
|
-
const ADD_STRATEGY_METHOD_NAME = "add.
|
|
23716
|
-
const ADD_EXCHANGE_METHOD_NAME = "add.
|
|
23717
|
-
const ADD_FRAME_METHOD_NAME = "add.
|
|
23718
|
-
const ADD_WALKER_METHOD_NAME = "add.
|
|
23719
|
-
const ADD_SIZING_METHOD_NAME = "add.
|
|
23720
|
-
const ADD_RISK_METHOD_NAME = "add.
|
|
23721
|
-
const ADD_OPTIMIZER_METHOD_NAME = "add.
|
|
23722
|
-
const ADD_ACTION_METHOD_NAME = "add.
|
|
24086
|
+
const ADD_STRATEGY_METHOD_NAME = "add.addStrategySchema";
|
|
24087
|
+
const ADD_EXCHANGE_METHOD_NAME = "add.addExchangeSchema";
|
|
24088
|
+
const ADD_FRAME_METHOD_NAME = "add.addFrameSchema";
|
|
24089
|
+
const ADD_WALKER_METHOD_NAME = "add.addWalkerSchema";
|
|
24090
|
+
const ADD_SIZING_METHOD_NAME = "add.addSizingSchema";
|
|
24091
|
+
const ADD_RISK_METHOD_NAME = "add.addRiskSchema";
|
|
24092
|
+
const ADD_OPTIMIZER_METHOD_NAME = "add.addOptimizerSchema";
|
|
24093
|
+
const ADD_ACTION_METHOD_NAME = "add.addActionSchema";
|
|
23723
24094
|
/**
|
|
23724
24095
|
* Registers a trading strategy in the framework.
|
|
23725
24096
|
*
|
|
@@ -23754,7 +24125,7 @@ const ADD_ACTION_METHOD_NAME = "add.addAction";
|
|
|
23754
24125
|
* });
|
|
23755
24126
|
* ```
|
|
23756
24127
|
*/
|
|
23757
|
-
function
|
|
24128
|
+
function addStrategySchema(strategySchema) {
|
|
23758
24129
|
backtest$1.loggerService.info(ADD_STRATEGY_METHOD_NAME, {
|
|
23759
24130
|
strategySchema,
|
|
23760
24131
|
});
|
|
@@ -23796,7 +24167,7 @@ function addStrategy(strategySchema) {
|
|
|
23796
24167
|
* });
|
|
23797
24168
|
* ```
|
|
23798
24169
|
*/
|
|
23799
|
-
function
|
|
24170
|
+
function addExchangeSchema(exchangeSchema) {
|
|
23800
24171
|
backtest$1.loggerService.info(ADD_EXCHANGE_METHOD_NAME, {
|
|
23801
24172
|
exchangeSchema,
|
|
23802
24173
|
});
|
|
@@ -23833,7 +24204,7 @@ function addExchange(exchangeSchema) {
|
|
|
23833
24204
|
* });
|
|
23834
24205
|
* ```
|
|
23835
24206
|
*/
|
|
23836
|
-
function
|
|
24207
|
+
function addFrameSchema(frameSchema) {
|
|
23837
24208
|
backtest$1.loggerService.info(ADD_FRAME_METHOD_NAME, {
|
|
23838
24209
|
frameSchema,
|
|
23839
24210
|
});
|
|
@@ -23877,7 +24248,7 @@ function addFrame(frameSchema) {
|
|
|
23877
24248
|
* });
|
|
23878
24249
|
* ```
|
|
23879
24250
|
*/
|
|
23880
|
-
function
|
|
24251
|
+
function addWalkerSchema(walkerSchema) {
|
|
23881
24252
|
backtest$1.loggerService.info(ADD_WALKER_METHOD_NAME, {
|
|
23882
24253
|
walkerSchema,
|
|
23883
24254
|
});
|
|
@@ -23936,7 +24307,7 @@ function addWalker(walkerSchema) {
|
|
|
23936
24307
|
* });
|
|
23937
24308
|
* ```
|
|
23938
24309
|
*/
|
|
23939
|
-
function
|
|
24310
|
+
function addSizingSchema(sizingSchema) {
|
|
23940
24311
|
backtest$1.loggerService.info(ADD_SIZING_METHOD_NAME, {
|
|
23941
24312
|
sizingSchema,
|
|
23942
24313
|
});
|
|
@@ -24004,7 +24375,7 @@ function addSizing(sizingSchema) {
|
|
|
24004
24375
|
* });
|
|
24005
24376
|
* ```
|
|
24006
24377
|
*/
|
|
24007
|
-
function
|
|
24378
|
+
function addRiskSchema(riskSchema) {
|
|
24008
24379
|
backtest$1.loggerService.info(ADD_RISK_METHOD_NAME, {
|
|
24009
24380
|
riskSchema,
|
|
24010
24381
|
});
|
|
@@ -24098,7 +24469,7 @@ function addRisk(riskSchema) {
|
|
|
24098
24469
|
* });
|
|
24099
24470
|
* ```
|
|
24100
24471
|
*/
|
|
24101
|
-
function
|
|
24472
|
+
function addOptimizerSchema(optimizerSchema) {
|
|
24102
24473
|
backtest$1.loggerService.info(ADD_OPTIMIZER_METHOD_NAME, {
|
|
24103
24474
|
optimizerSchema,
|
|
24104
24475
|
});
|
|
@@ -24173,7 +24544,7 @@ function addOptimizer(optimizerSchema) {
|
|
|
24173
24544
|
* });
|
|
24174
24545
|
* ```
|
|
24175
24546
|
*/
|
|
24176
|
-
function
|
|
24547
|
+
function addActionSchema(actionSchema) {
|
|
24177
24548
|
backtest$1.loggerService.info(ADD_ACTION_METHOD_NAME, {
|
|
24178
24549
|
actionSchema,
|
|
24179
24550
|
});
|
|
@@ -24181,14 +24552,14 @@ function addAction(actionSchema) {
|
|
|
24181
24552
|
backtest$1.actionSchemaService.register(actionSchema.actionName, actionSchema);
|
|
24182
24553
|
}
|
|
24183
24554
|
|
|
24184
|
-
const METHOD_NAME_OVERRIDE_STRATEGY = "function.override.
|
|
24185
|
-
const METHOD_NAME_OVERRIDE_EXCHANGE = "function.override.
|
|
24186
|
-
const METHOD_NAME_OVERRIDE_FRAME = "function.override.
|
|
24187
|
-
const METHOD_NAME_OVERRIDE_WALKER = "function.override.
|
|
24188
|
-
const METHOD_NAME_OVERRIDE_SIZING = "function.override.
|
|
24189
|
-
const METHOD_NAME_OVERRIDE_RISK = "function.override.
|
|
24190
|
-
const METHOD_NAME_OVERRIDE_OPTIMIZER = "function.override.
|
|
24191
|
-
const METHOD_NAME_OVERRIDE_ACTION = "function.override.
|
|
24555
|
+
const METHOD_NAME_OVERRIDE_STRATEGY = "function.override.overrideStrategySchema";
|
|
24556
|
+
const METHOD_NAME_OVERRIDE_EXCHANGE = "function.override.overrideExchangeSchema";
|
|
24557
|
+
const METHOD_NAME_OVERRIDE_FRAME = "function.override.overrideFrameSchema";
|
|
24558
|
+
const METHOD_NAME_OVERRIDE_WALKER = "function.override.overrideWalkerSchema";
|
|
24559
|
+
const METHOD_NAME_OVERRIDE_SIZING = "function.override.overrideSizingSchema";
|
|
24560
|
+
const METHOD_NAME_OVERRIDE_RISK = "function.override.overrideRiskSchema";
|
|
24561
|
+
const METHOD_NAME_OVERRIDE_OPTIMIZER = "function.override.overrideOptimizerSchema";
|
|
24562
|
+
const METHOD_NAME_OVERRIDE_ACTION = "function.override.overrideActionSchema";
|
|
24192
24563
|
/**
|
|
24193
24564
|
* Overrides an existing trading strategy in the framework.
|
|
24194
24565
|
*
|
|
@@ -24209,7 +24580,7 @@ const METHOD_NAME_OVERRIDE_ACTION = "function.override.overrideAction";
|
|
|
24209
24580
|
* });
|
|
24210
24581
|
* ```
|
|
24211
24582
|
*/
|
|
24212
|
-
async function
|
|
24583
|
+
async function overrideStrategySchema(strategySchema) {
|
|
24213
24584
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_STRATEGY, {
|
|
24214
24585
|
strategySchema,
|
|
24215
24586
|
});
|
|
@@ -24237,7 +24608,7 @@ async function overrideStrategy(strategySchema) {
|
|
|
24237
24608
|
* });
|
|
24238
24609
|
* ```
|
|
24239
24610
|
*/
|
|
24240
|
-
async function
|
|
24611
|
+
async function overrideExchangeSchema(exchangeSchema) {
|
|
24241
24612
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_EXCHANGE, {
|
|
24242
24613
|
exchangeSchema,
|
|
24243
24614
|
});
|
|
@@ -24265,7 +24636,7 @@ async function overrideExchange(exchangeSchema) {
|
|
|
24265
24636
|
* });
|
|
24266
24637
|
* ```
|
|
24267
24638
|
*/
|
|
24268
|
-
async function
|
|
24639
|
+
async function overrideFrameSchema(frameSchema) {
|
|
24269
24640
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_FRAME, {
|
|
24270
24641
|
frameSchema,
|
|
24271
24642
|
});
|
|
@@ -24294,7 +24665,7 @@ async function overrideFrame(frameSchema) {
|
|
|
24294
24665
|
* });
|
|
24295
24666
|
* ```
|
|
24296
24667
|
*/
|
|
24297
|
-
async function
|
|
24668
|
+
async function overrideWalkerSchema(walkerSchema) {
|
|
24298
24669
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_WALKER, {
|
|
24299
24670
|
walkerSchema,
|
|
24300
24671
|
});
|
|
@@ -24326,7 +24697,7 @@ async function overrideWalker(walkerSchema) {
|
|
|
24326
24697
|
* });
|
|
24327
24698
|
* ```
|
|
24328
24699
|
*/
|
|
24329
|
-
async function
|
|
24700
|
+
async function overrideSizingSchema(sizingSchema) {
|
|
24330
24701
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_SIZING, {
|
|
24331
24702
|
sizingSchema,
|
|
24332
24703
|
});
|
|
@@ -24353,7 +24724,7 @@ async function overrideSizing(sizingSchema) {
|
|
|
24353
24724
|
* });
|
|
24354
24725
|
* ```
|
|
24355
24726
|
*/
|
|
24356
|
-
async function
|
|
24727
|
+
async function overrideRiskSchema(riskSchema) {
|
|
24357
24728
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_RISK, {
|
|
24358
24729
|
riskSchema,
|
|
24359
24730
|
});
|
|
@@ -24387,7 +24758,7 @@ async function overrideRisk(riskSchema) {
|
|
|
24387
24758
|
* });
|
|
24388
24759
|
* ```
|
|
24389
24760
|
*/
|
|
24390
|
-
async function
|
|
24761
|
+
async function overrideOptimizerSchema(optimizerSchema) {
|
|
24391
24762
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_OPTIMIZER, {
|
|
24392
24763
|
optimizerSchema,
|
|
24393
24764
|
});
|
|
@@ -24454,7 +24825,7 @@ async function overrideOptimizer(optimizerSchema) {
|
|
|
24454
24825
|
* });
|
|
24455
24826
|
* ```
|
|
24456
24827
|
*/
|
|
24457
|
-
async function
|
|
24828
|
+
async function overrideActionSchema(actionSchema) {
|
|
24458
24829
|
backtest$1.loggerService.log(METHOD_NAME_OVERRIDE_ACTION, {
|
|
24459
24830
|
actionSchema,
|
|
24460
24831
|
});
|
|
@@ -24462,13 +24833,13 @@ async function overrideAction(actionSchema) {
|
|
|
24462
24833
|
return backtest$1.actionSchemaService.override(actionSchema.actionName, actionSchema);
|
|
24463
24834
|
}
|
|
24464
24835
|
|
|
24465
|
-
const LIST_EXCHANGES_METHOD_NAME = "list.
|
|
24466
|
-
const LIST_STRATEGIES_METHOD_NAME = "list.
|
|
24467
|
-
const LIST_FRAMES_METHOD_NAME = "list.
|
|
24468
|
-
const LIST_WALKERS_METHOD_NAME = "list.
|
|
24469
|
-
const LIST_SIZINGS_METHOD_NAME = "list.
|
|
24470
|
-
const LIST_RISKS_METHOD_NAME = "list.
|
|
24471
|
-
const LIST_OPTIMIZERS_METHOD_NAME = "list.
|
|
24836
|
+
const LIST_EXCHANGES_METHOD_NAME = "list.listExchangeSchema";
|
|
24837
|
+
const LIST_STRATEGIES_METHOD_NAME = "list.listStrategySchema";
|
|
24838
|
+
const LIST_FRAMES_METHOD_NAME = "list.listFrameSchema";
|
|
24839
|
+
const LIST_WALKERS_METHOD_NAME = "list.listWalkerSchema";
|
|
24840
|
+
const LIST_SIZINGS_METHOD_NAME = "list.listSizingSchema";
|
|
24841
|
+
const LIST_RISKS_METHOD_NAME = "list.listRiskSchema";
|
|
24842
|
+
const LIST_OPTIMIZERS_METHOD_NAME = "list.listOptimizerSchema";
|
|
24472
24843
|
/**
|
|
24473
24844
|
* Returns a list of all registered exchange schemas.
|
|
24474
24845
|
*
|
|
@@ -24494,7 +24865,7 @@ const LIST_OPTIMIZERS_METHOD_NAME = "list.listOptimizers";
|
|
|
24494
24865
|
* // [{ exchangeName: "binance", note: "Binance cryptocurrency exchange", ... }]
|
|
24495
24866
|
* ```
|
|
24496
24867
|
*/
|
|
24497
|
-
async function
|
|
24868
|
+
async function listExchangeSchema() {
|
|
24498
24869
|
backtest$1.loggerService.log(LIST_EXCHANGES_METHOD_NAME);
|
|
24499
24870
|
return await backtest$1.exchangeValidationService.list();
|
|
24500
24871
|
}
|
|
@@ -24528,7 +24899,7 @@ async function listExchanges() {
|
|
|
24528
24899
|
* // [{ strategyName: "my-strategy", note: "Simple moving average...", ... }]
|
|
24529
24900
|
* ```
|
|
24530
24901
|
*/
|
|
24531
|
-
async function
|
|
24902
|
+
async function listStrategySchema() {
|
|
24532
24903
|
backtest$1.loggerService.log(LIST_STRATEGIES_METHOD_NAME);
|
|
24533
24904
|
return await backtest$1.strategyValidationService.list();
|
|
24534
24905
|
}
|
|
@@ -24557,7 +24928,7 @@ async function listStrategies() {
|
|
|
24557
24928
|
* // [{ frameName: "1d-backtest", note: "One day backtest...", ... }]
|
|
24558
24929
|
* ```
|
|
24559
24930
|
*/
|
|
24560
|
-
async function
|
|
24931
|
+
async function listFrameSchema() {
|
|
24561
24932
|
backtest$1.loggerService.log(LIST_FRAMES_METHOD_NAME);
|
|
24562
24933
|
return await backtest$1.frameValidationService.list();
|
|
24563
24934
|
}
|
|
@@ -24587,7 +24958,7 @@ async function listFrames() {
|
|
|
24587
24958
|
* // [{ walkerName: "llm-prompt-optimizer", note: "Compare LLM...", ... }]
|
|
24588
24959
|
* ```
|
|
24589
24960
|
*/
|
|
24590
|
-
async function
|
|
24961
|
+
async function listWalkerSchema() {
|
|
24591
24962
|
backtest$1.loggerService.log(LIST_WALKERS_METHOD_NAME);
|
|
24592
24963
|
return await backtest$1.walkerValidationService.list();
|
|
24593
24964
|
}
|
|
@@ -24626,7 +24997,7 @@ async function listWalkers() {
|
|
|
24626
24997
|
* // ]
|
|
24627
24998
|
* ```
|
|
24628
24999
|
*/
|
|
24629
|
-
async function
|
|
25000
|
+
async function listSizingSchema() {
|
|
24630
25001
|
backtest$1.loggerService.log(LIST_SIZINGS_METHOD_NAME);
|
|
24631
25002
|
return await backtest$1.sizingValidationService.list();
|
|
24632
25003
|
}
|
|
@@ -24662,7 +25033,7 @@ async function listSizings() {
|
|
|
24662
25033
|
* // ]
|
|
24663
25034
|
* ```
|
|
24664
25035
|
*/
|
|
24665
|
-
async function
|
|
25036
|
+
async function listRiskSchema() {
|
|
24666
25037
|
backtest$1.loggerService.log(LIST_RISKS_METHOD_NAME);
|
|
24667
25038
|
return await backtest$1.riskValidationService.list();
|
|
24668
25039
|
}
|
|
@@ -24702,7 +25073,7 @@ async function listRisks() {
|
|
|
24702
25073
|
* // [{ optimizerName: "llm-strategy-generator", note: "Generates...", ... }]
|
|
24703
25074
|
* ```
|
|
24704
25075
|
*/
|
|
24705
|
-
async function
|
|
25076
|
+
async function listOptimizerSchema() {
|
|
24706
25077
|
backtest$1.loggerService.log(LIST_OPTIMIZERS_METHOD_NAME);
|
|
24707
25078
|
return await backtest$1.optimizerValidationService.list();
|
|
24708
25079
|
}
|
|
@@ -24737,8 +25108,10 @@ const LISTEN_BREAKEVEN_METHOD_NAME = "event.listenBreakeven";
|
|
|
24737
25108
|
const LISTEN_BREAKEVEN_ONCE_METHOD_NAME = "event.listenBreakevenOnce";
|
|
24738
25109
|
const LISTEN_RISK_METHOD_NAME = "event.listenRisk";
|
|
24739
25110
|
const LISTEN_RISK_ONCE_METHOD_NAME = "event.listenRiskOnce";
|
|
24740
|
-
const
|
|
24741
|
-
const
|
|
25111
|
+
const LISTEN_SCHEDULE_PING_METHOD_NAME = "event.listenSchedulePing";
|
|
25112
|
+
const LISTEN_SCHEDULE_PING_ONCE_METHOD_NAME = "event.listenSchedulePingOnce";
|
|
25113
|
+
const LISTEN_ACTIVE_PING_METHOD_NAME = "event.listenActivePing";
|
|
25114
|
+
const LISTEN_ACTIVE_PING_ONCE_METHOD_NAME = "event.listenActivePingOnce";
|
|
24742
25115
|
/**
|
|
24743
25116
|
* Subscribes to all signal events with queued async processing.
|
|
24744
25117
|
*
|
|
@@ -25437,7 +25810,7 @@ function listenValidation(fn) {
|
|
|
25437
25810
|
* unsubscribe();
|
|
25438
25811
|
* ```
|
|
25439
25812
|
*/
|
|
25440
|
-
function
|
|
25813
|
+
function listenPartialProfitAvailable(fn) {
|
|
25441
25814
|
backtest$1.loggerService.log(LISTEN_PARTIAL_PROFIT_METHOD_NAME);
|
|
25442
25815
|
return partialProfitSubject.subscribe(queued(async (event) => fn(event)));
|
|
25443
25816
|
}
|
|
@@ -25471,7 +25844,7 @@ function listenPartialProfit(fn) {
|
|
|
25471
25844
|
* cancel();
|
|
25472
25845
|
* ```
|
|
25473
25846
|
*/
|
|
25474
|
-
function
|
|
25847
|
+
function listenPartialProfitAvailableOnce(filterFn, fn) {
|
|
25475
25848
|
backtest$1.loggerService.log(LISTEN_PARTIAL_PROFIT_ONCE_METHOD_NAME);
|
|
25476
25849
|
return partialProfitSubject.filter(filterFn).once(fn);
|
|
25477
25850
|
}
|
|
@@ -25499,7 +25872,7 @@ function listenPartialProfitOnce(filterFn, fn) {
|
|
|
25499
25872
|
* unsubscribe();
|
|
25500
25873
|
* ```
|
|
25501
25874
|
*/
|
|
25502
|
-
function
|
|
25875
|
+
function listenPartialLossAvailable(fn) {
|
|
25503
25876
|
backtest$1.loggerService.log(LISTEN_PARTIAL_LOSS_METHOD_NAME);
|
|
25504
25877
|
return partialLossSubject.subscribe(queued(async (event) => fn(event)));
|
|
25505
25878
|
}
|
|
@@ -25533,7 +25906,7 @@ function listenPartialLoss(fn) {
|
|
|
25533
25906
|
* cancel();
|
|
25534
25907
|
* ```
|
|
25535
25908
|
*/
|
|
25536
|
-
function
|
|
25909
|
+
function listenPartialLossAvailableOnce(filterFn, fn) {
|
|
25537
25910
|
backtest$1.loggerService.log(LISTEN_PARTIAL_LOSS_ONCE_METHOD_NAME);
|
|
25538
25911
|
return partialLossSubject.filter(filterFn).once(fn);
|
|
25539
25912
|
}
|
|
@@ -25563,7 +25936,7 @@ function listenPartialLossOnce(filterFn, fn) {
|
|
|
25563
25936
|
* unsubscribe();
|
|
25564
25937
|
* ```
|
|
25565
25938
|
*/
|
|
25566
|
-
function
|
|
25939
|
+
function listenBreakevenAvailable(fn) {
|
|
25567
25940
|
backtest$1.loggerService.log(LISTEN_BREAKEVEN_METHOD_NAME);
|
|
25568
25941
|
return breakevenSubject.subscribe(queued(async (event) => fn(event)));
|
|
25569
25942
|
}
|
|
@@ -25597,7 +25970,7 @@ function listenBreakeven(fn) {
|
|
|
25597
25970
|
* cancel();
|
|
25598
25971
|
* ```
|
|
25599
25972
|
*/
|
|
25600
|
-
function
|
|
25973
|
+
function listenBreakevenAvailableOnce(filterFn, fn) {
|
|
25601
25974
|
backtest$1.loggerService.log(LISTEN_BREAKEVEN_ONCE_METHOD_NAME);
|
|
25602
25975
|
return breakevenSubject.filter(filterFn).once(fn);
|
|
25603
25976
|
}
|
|
@@ -25693,9 +26066,9 @@ function listenRiskOnce(filterFn, fn) {
|
|
|
25693
26066
|
* unsubscribe();
|
|
25694
26067
|
* ```
|
|
25695
26068
|
*/
|
|
25696
|
-
function
|
|
25697
|
-
backtest$1.loggerService.log(
|
|
25698
|
-
return
|
|
26069
|
+
function listenSchedulePing(fn) {
|
|
26070
|
+
backtest$1.loggerService.log(LISTEN_SCHEDULE_PING_METHOD_NAME);
|
|
26071
|
+
return schedulePingSubject.subscribe(queued(async (event) => fn(event)));
|
|
25699
26072
|
}
|
|
25700
26073
|
/**
|
|
25701
26074
|
* Subscribes to filtered ping events with one-time execution.
|
|
@@ -25727,9 +26100,74 @@ function listenPing(fn) {
|
|
|
25727
26100
|
* cancel();
|
|
25728
26101
|
* ```
|
|
25729
26102
|
*/
|
|
25730
|
-
function
|
|
25731
|
-
backtest$1.loggerService.log(
|
|
25732
|
-
return
|
|
26103
|
+
function listenSchedulePingOnce(filterFn, fn) {
|
|
26104
|
+
backtest$1.loggerService.log(LISTEN_SCHEDULE_PING_ONCE_METHOD_NAME);
|
|
26105
|
+
return schedulePingSubject.filter(filterFn).once(fn);
|
|
26106
|
+
}
|
|
26107
|
+
/**
|
|
26108
|
+
* Subscribes to active ping events with queued async processing.
|
|
26109
|
+
*
|
|
26110
|
+
* Listens for active pending signal monitoring events emitted every minute.
|
|
26111
|
+
* Useful for tracking active signal lifecycle and implementing dynamic management logic.
|
|
26112
|
+
*
|
|
26113
|
+
* Events are processed sequentially in order received, even if callback is async.
|
|
26114
|
+
* Uses queued wrapper to prevent concurrent execution of the callback.
|
|
26115
|
+
*
|
|
26116
|
+
* @param fn - Callback function to handle active ping events
|
|
26117
|
+
* @returns Unsubscribe function to stop listening
|
|
26118
|
+
*
|
|
26119
|
+
* @example
|
|
26120
|
+
* ```typescript
|
|
26121
|
+
* import { listenActivePing } from "./function/event";
|
|
26122
|
+
*
|
|
26123
|
+
* const unsubscribe = listenActivePing((event) => {
|
|
26124
|
+
* console.log(`[${event.backtest ? "Backtest" : "Live"}] Active Ping`);
|
|
26125
|
+
* console.log(`Symbol: ${event.symbol}, Strategy: ${event.strategyName}`);
|
|
26126
|
+
* console.log(`Signal ID: ${event.data.id}, Position: ${event.data.position}`);
|
|
26127
|
+
* console.log(`Timestamp: ${new Date(event.timestamp).toISOString()}`);
|
|
26128
|
+
* });
|
|
26129
|
+
*
|
|
26130
|
+
* // Later: stop listening
|
|
26131
|
+
* unsubscribe();
|
|
26132
|
+
* ```
|
|
26133
|
+
*/
|
|
26134
|
+
function listenActivePing(fn) {
|
|
26135
|
+
backtest$1.loggerService.log(LISTEN_ACTIVE_PING_METHOD_NAME);
|
|
26136
|
+
return activePingSubject.subscribe(queued(async (event) => fn(event)));
|
|
26137
|
+
}
|
|
26138
|
+
/**
|
|
26139
|
+
* Subscribes to filtered active ping events with one-time execution.
|
|
26140
|
+
*
|
|
26141
|
+
* Listens for events matching the filter predicate, then executes callback once
|
|
26142
|
+
* and automatically unsubscribes. Useful for waiting for specific active ping conditions.
|
|
26143
|
+
*
|
|
26144
|
+
* @param filterFn - Predicate to filter which events trigger the callback
|
|
26145
|
+
* @param fn - Callback function to handle the filtered event (called only once)
|
|
26146
|
+
* @returns Unsubscribe function to cancel the listener before it fires
|
|
26147
|
+
*
|
|
26148
|
+
* @example
|
|
26149
|
+
* ```typescript
|
|
26150
|
+
* import { listenActivePingOnce } from "./function/event";
|
|
26151
|
+
*
|
|
26152
|
+
* // Wait for first active ping on BTCUSDT
|
|
26153
|
+
* listenActivePingOnce(
|
|
26154
|
+
* (event) => event.symbol === "BTCUSDT",
|
|
26155
|
+
* (event) => console.log("First BTCUSDT active ping received")
|
|
26156
|
+
* );
|
|
26157
|
+
*
|
|
26158
|
+
* // Wait for active ping in backtest mode
|
|
26159
|
+
* const cancel = listenActivePingOnce(
|
|
26160
|
+
* (event) => event.backtest === true,
|
|
26161
|
+
* (event) => console.log("Backtest active ping received at", new Date(event.timestamp))
|
|
26162
|
+
* );
|
|
26163
|
+
*
|
|
26164
|
+
* // Cancel if needed before event fires
|
|
26165
|
+
* cancel();
|
|
26166
|
+
* ```
|
|
26167
|
+
*/
|
|
26168
|
+
function listenActivePingOnce(filterFn, fn) {
|
|
26169
|
+
backtest$1.loggerService.log(LISTEN_ACTIVE_PING_ONCE_METHOD_NAME);
|
|
26170
|
+
return activePingSubject.filter(filterFn).once(fn);
|
|
25733
26171
|
}
|
|
25734
26172
|
|
|
25735
26173
|
const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
@@ -25799,7 +26237,7 @@ const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
|
25799
26237
|
* // ./dump/strategy/{uuid}/06_llm_output.md (final signal)
|
|
25800
26238
|
* ```
|
|
25801
26239
|
*/
|
|
25802
|
-
async function
|
|
26240
|
+
async function dumpSignalData(signalId, history, signal, outputDir = "./dump/strategy") {
|
|
25803
26241
|
backtest$1.loggerService.info(DUMP_SIGNAL_METHOD_NAME, {
|
|
25804
26242
|
signalId,
|
|
25805
26243
|
history,
|
|
@@ -25819,11 +26257,12 @@ const BACKTEST_METHOD_NAME_GET_STATUS = "BacktestUtils.getStatus";
|
|
|
25819
26257
|
const BACKTEST_METHOD_NAME_GET_PENDING_SIGNAL = "BacktestUtils.getPendingSignal";
|
|
25820
26258
|
const BACKTEST_METHOD_NAME_GET_SCHEDULED_SIGNAL = "BacktestUtils.getScheduledSignal";
|
|
25821
26259
|
const BACKTEST_METHOD_NAME_GET_BREAKEVEN = "BacktestUtils.getBreakeven";
|
|
25822
|
-
const
|
|
25823
|
-
const
|
|
25824
|
-
const
|
|
25825
|
-
const
|
|
25826
|
-
const
|
|
26260
|
+
const BACKTEST_METHOD_NAME_BREAKEVEN = "Backtest.commitBreakeven";
|
|
26261
|
+
const BACKTEST_METHOD_NAME_CANCEL = "BacktestUtils.commitCancel";
|
|
26262
|
+
const BACKTEST_METHOD_NAME_PARTIAL_PROFIT = "BacktestUtils.commitPartialProfit";
|
|
26263
|
+
const BACKTEST_METHOD_NAME_PARTIAL_LOSS = "BacktestUtils.commitPartialLoss";
|
|
26264
|
+
const BACKTEST_METHOD_NAME_TRAILING_STOP = "BacktestUtils.commitTrailingStop";
|
|
26265
|
+
const BACKTEST_METHOD_NAME_TRAILING_PROFIT = "BacktestUtils.commitTrailingTake";
|
|
25827
26266
|
const BACKTEST_METHOD_NAME_GET_DATA = "BacktestUtils.getData";
|
|
25828
26267
|
/**
|
|
25829
26268
|
* Internal task function that runs backtest and handles completion.
|
|
@@ -26347,14 +26786,14 @@ class BacktestUtils {
|
|
|
26347
26786
|
* @example
|
|
26348
26787
|
* ```typescript
|
|
26349
26788
|
* // Cancel scheduled signal with custom ID
|
|
26350
|
-
* await Backtest.
|
|
26789
|
+
* await Backtest.commitCancel("BTCUSDT", "my-strategy", {
|
|
26351
26790
|
* exchangeName: "binance",
|
|
26352
26791
|
* frameName: "frame1",
|
|
26353
26792
|
* strategyName: "my-strategy"
|
|
26354
26793
|
* }, "manual-cancel-001");
|
|
26355
26794
|
* ```
|
|
26356
26795
|
*/
|
|
26357
|
-
this.
|
|
26796
|
+
this.commitCancel = async (symbol, context, cancelId) => {
|
|
26358
26797
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_CANCEL, {
|
|
26359
26798
|
symbol,
|
|
26360
26799
|
context,
|
|
@@ -26392,7 +26831,7 @@ class BacktestUtils {
|
|
|
26392
26831
|
* @example
|
|
26393
26832
|
* ```typescript
|
|
26394
26833
|
* // Close 30% of LONG position at profit
|
|
26395
|
-
* const success = await Backtest.
|
|
26834
|
+
* const success = await Backtest.commitPartialProfit("BTCUSDT", 30, 45000, {
|
|
26396
26835
|
* exchangeName: "binance",
|
|
26397
26836
|
* frameName: "frame1",
|
|
26398
26837
|
* strategyName: "my-strategy"
|
|
@@ -26402,7 +26841,7 @@ class BacktestUtils {
|
|
|
26402
26841
|
* }
|
|
26403
26842
|
* ```
|
|
26404
26843
|
*/
|
|
26405
|
-
this.
|
|
26844
|
+
this.commitPartialProfit = async (symbol, percentToClose, currentPrice, context) => {
|
|
26406
26845
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_PARTIAL_PROFIT, {
|
|
26407
26846
|
symbol,
|
|
26408
26847
|
percentToClose,
|
|
@@ -26441,7 +26880,7 @@ class BacktestUtils {
|
|
|
26441
26880
|
* @example
|
|
26442
26881
|
* ```typescript
|
|
26443
26882
|
* // Close 40% of LONG position at loss
|
|
26444
|
-
* const success = await Backtest.
|
|
26883
|
+
* const success = await Backtest.commitPartialLoss("BTCUSDT", 40, 38000, {
|
|
26445
26884
|
* exchangeName: "binance",
|
|
26446
26885
|
* frameName: "frame1",
|
|
26447
26886
|
* strategyName: "my-strategy"
|
|
@@ -26451,7 +26890,7 @@ class BacktestUtils {
|
|
|
26451
26890
|
* }
|
|
26452
26891
|
* ```
|
|
26453
26892
|
*/
|
|
26454
|
-
this.
|
|
26893
|
+
this.commitPartialLoss = async (symbol, percentToClose, currentPrice, context) => {
|
|
26455
26894
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_PARTIAL_LOSS, {
|
|
26456
26895
|
symbol,
|
|
26457
26896
|
percentToClose,
|
|
@@ -26499,7 +26938,7 @@ class BacktestUtils {
|
|
|
26499
26938
|
* // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
|
|
26500
26939
|
*
|
|
26501
26940
|
* // First call: tighten by 5%
|
|
26502
|
-
* await Backtest.
|
|
26941
|
+
* await Backtest.commitTrailingStop("BTCUSDT", -5, 102, {
|
|
26503
26942
|
* exchangeName: "binance",
|
|
26504
26943
|
* frameName: "frame1",
|
|
26505
26944
|
* strategyName: "my-strategy"
|
|
@@ -26507,15 +26946,15 @@ class BacktestUtils {
|
|
|
26507
26946
|
* // newDistance = 10% - 5% = 5%, newSL = 95
|
|
26508
26947
|
*
|
|
26509
26948
|
* // Second call: try weaker protection (smaller percentShift)
|
|
26510
|
-
* await Backtest.
|
|
26949
|
+
* await Backtest.commitTrailingStop("BTCUSDT", -3, 102, context);
|
|
26511
26950
|
* // SKIPPED: newSL=97 < 95 (worse protection, larger % absorbs smaller)
|
|
26512
26951
|
*
|
|
26513
26952
|
* // Third call: stronger protection (larger percentShift)
|
|
26514
|
-
* await Backtest.
|
|
26953
|
+
* await Backtest.commitTrailingStop("BTCUSDT", -7, 102, context);
|
|
26515
26954
|
* // ACCEPTED: newDistance = 10% - 7% = 3%, newSL = 97 > 95 (better protection)
|
|
26516
26955
|
* ```
|
|
26517
26956
|
*/
|
|
26518
|
-
this.
|
|
26957
|
+
this.commitTrailingStop = async (symbol, percentShift, currentPrice, context) => {
|
|
26519
26958
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_TRAILING_STOP, {
|
|
26520
26959
|
symbol,
|
|
26521
26960
|
percentShift,
|
|
@@ -26563,7 +27002,7 @@ class BacktestUtils {
|
|
|
26563
27002
|
* // LONG: entry=100, originalTP=110, distance=10%, currentPrice=102
|
|
26564
27003
|
*
|
|
26565
27004
|
* // First call: bring TP closer by 3%
|
|
26566
|
-
* await Backtest.
|
|
27005
|
+
* await Backtest.commitTrailingTake("BTCUSDT", -3, 102, {
|
|
26567
27006
|
* exchangeName: "binance",
|
|
26568
27007
|
* frameName: "frame1",
|
|
26569
27008
|
* strategyName: "my-strategy"
|
|
@@ -26571,15 +27010,15 @@ class BacktestUtils {
|
|
|
26571
27010
|
* // newDistance = 10% - 3% = 7%, newTP = 107
|
|
26572
27011
|
*
|
|
26573
27012
|
* // Second call: try to move TP further (less conservative)
|
|
26574
|
-
* await Backtest.
|
|
27013
|
+
* await Backtest.commitTrailingTake("BTCUSDT", 2, 102, context);
|
|
26575
27014
|
* // SKIPPED: newTP=112 > 107 (less conservative, larger % absorbs smaller)
|
|
26576
27015
|
*
|
|
26577
27016
|
* // Third call: even more conservative
|
|
26578
|
-
* await Backtest.
|
|
27017
|
+
* await Backtest.commitTrailingTake("BTCUSDT", -5, 102, context);
|
|
26579
27018
|
* // ACCEPTED: newDistance = 10% - 5% = 5%, newTP = 105 < 107 (more conservative)
|
|
26580
27019
|
* ```
|
|
26581
27020
|
*/
|
|
26582
|
-
this.
|
|
27021
|
+
this.commitTrailingTake = async (symbol, percentShift, currentPrice, context) => {
|
|
26583
27022
|
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_TRAILING_PROFIT, {
|
|
26584
27023
|
symbol,
|
|
26585
27024
|
percentShift,
|
|
@@ -26612,7 +27051,7 @@ class BacktestUtils {
|
|
|
26612
27051
|
*
|
|
26613
27052
|
* @example
|
|
26614
27053
|
* ```typescript
|
|
26615
|
-
* const moved = await Backtest.
|
|
27054
|
+
* const moved = await Backtest.commitBreakeven(
|
|
26616
27055
|
* "BTCUSDT",
|
|
26617
27056
|
* 112,
|
|
26618
27057
|
* { strategyName: "my-strategy", exchangeName: "binance", frameName: "1h" }
|
|
@@ -26620,22 +27059,22 @@ class BacktestUtils {
|
|
|
26620
27059
|
* console.log(moved); // true (SL moved to entry price)
|
|
26621
27060
|
* ```
|
|
26622
27061
|
*/
|
|
26623
|
-
this.
|
|
26624
|
-
backtest$1.loggerService.info(
|
|
27062
|
+
this.commitBreakeven = async (symbol, currentPrice, context) => {
|
|
27063
|
+
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_BREAKEVEN, {
|
|
26625
27064
|
symbol,
|
|
26626
27065
|
currentPrice,
|
|
26627
27066
|
context,
|
|
26628
27067
|
});
|
|
26629
|
-
backtest$1.strategyValidationService.validate(context.strategyName,
|
|
26630
|
-
backtest$1.exchangeValidationService.validate(context.exchangeName,
|
|
27068
|
+
backtest$1.strategyValidationService.validate(context.strategyName, BACKTEST_METHOD_NAME_BREAKEVEN);
|
|
27069
|
+
backtest$1.exchangeValidationService.validate(context.exchangeName, BACKTEST_METHOD_NAME_BREAKEVEN);
|
|
26631
27070
|
{
|
|
26632
27071
|
const { riskName, riskList, actions } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
26633
27072
|
riskName &&
|
|
26634
|
-
backtest$1.riskValidationService.validate(riskName,
|
|
27073
|
+
backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_BREAKEVEN);
|
|
26635
27074
|
riskList &&
|
|
26636
|
-
riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName,
|
|
27075
|
+
riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, BACKTEST_METHOD_NAME_BREAKEVEN));
|
|
26637
27076
|
actions &&
|
|
26638
|
-
actions.forEach((actionName) => backtest$1.actionValidationService.validate(actionName,
|
|
27077
|
+
actions.forEach((actionName) => backtest$1.actionValidationService.validate(actionName, BACKTEST_METHOD_NAME_BREAKEVEN));
|
|
26639
27078
|
}
|
|
26640
27079
|
return await backtest$1.strategyCoreService.breakeven(true, symbol, currentPrice, context);
|
|
26641
27080
|
};
|
|
@@ -26807,11 +27246,12 @@ const LIVE_METHOD_NAME_GET_STATUS = "LiveUtils.getStatus";
|
|
|
26807
27246
|
const LIVE_METHOD_NAME_GET_PENDING_SIGNAL = "LiveUtils.getPendingSignal";
|
|
26808
27247
|
const LIVE_METHOD_NAME_GET_SCHEDULED_SIGNAL = "LiveUtils.getScheduledSignal";
|
|
26809
27248
|
const LIVE_METHOD_NAME_GET_BREAKEVEN = "LiveUtils.getBreakeven";
|
|
26810
|
-
const
|
|
26811
|
-
const
|
|
26812
|
-
const
|
|
26813
|
-
const
|
|
26814
|
-
const
|
|
27249
|
+
const LIVE_METHOD_NAME_BREAKEVEN = "Live.commitBreakeven";
|
|
27250
|
+
const LIVE_METHOD_NAME_CANCEL = "LiveUtils.commitCancel";
|
|
27251
|
+
const LIVE_METHOD_NAME_PARTIAL_PROFIT = "LiveUtils.commitPartialProfit";
|
|
27252
|
+
const LIVE_METHOD_NAME_PARTIAL_LOSS = "LiveUtils.commitPartialLoss";
|
|
27253
|
+
const LIVE_METHOD_NAME_TRAILING_STOP = "LiveUtils.commitTrailingStop";
|
|
27254
|
+
const LIVE_METHOD_NAME_TRAILING_PROFIT = "LiveUtils.commitTrailingTake";
|
|
26815
27255
|
/**
|
|
26816
27256
|
* Internal task function that runs live trading and handles completion.
|
|
26817
27257
|
* Consumes live trading results and updates instance state flags.
|
|
@@ -27301,14 +27741,14 @@ class LiveUtils {
|
|
|
27301
27741
|
* @example
|
|
27302
27742
|
* ```typescript
|
|
27303
27743
|
* // Cancel scheduled signal in live trading with custom ID
|
|
27304
|
-
* await Live.
|
|
27744
|
+
* await Live.commitCancel("BTCUSDT", "my-strategy", {
|
|
27305
27745
|
* exchangeName: "binance",
|
|
27306
27746
|
* frameName: "",
|
|
27307
27747
|
* strategyName: "my-strategy"
|
|
27308
27748
|
* }, "manual-cancel-001");
|
|
27309
27749
|
* ```
|
|
27310
27750
|
*/
|
|
27311
|
-
this.
|
|
27751
|
+
this.commitCancel = async (symbol, context, cancelId) => {
|
|
27312
27752
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_CANCEL, {
|
|
27313
27753
|
symbol,
|
|
27314
27754
|
context,
|
|
@@ -27347,7 +27787,7 @@ class LiveUtils {
|
|
|
27347
27787
|
* @example
|
|
27348
27788
|
* ```typescript
|
|
27349
27789
|
* // Close 30% of LONG position at profit
|
|
27350
|
-
* const success = await Live.
|
|
27790
|
+
* const success = await Live.commitPartialProfit("BTCUSDT", 30, 45000, {
|
|
27351
27791
|
* exchangeName: "binance",
|
|
27352
27792
|
* strategyName: "my-strategy"
|
|
27353
27793
|
* });
|
|
@@ -27356,7 +27796,7 @@ class LiveUtils {
|
|
|
27356
27796
|
* }
|
|
27357
27797
|
* ```
|
|
27358
27798
|
*/
|
|
27359
|
-
this.
|
|
27799
|
+
this.commitPartialProfit = async (symbol, percentToClose, currentPrice, context) => {
|
|
27360
27800
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_PARTIAL_PROFIT, {
|
|
27361
27801
|
symbol,
|
|
27362
27802
|
percentToClose,
|
|
@@ -27396,7 +27836,7 @@ class LiveUtils {
|
|
|
27396
27836
|
* @example
|
|
27397
27837
|
* ```typescript
|
|
27398
27838
|
* // Close 40% of LONG position at loss
|
|
27399
|
-
* const success = await Live.
|
|
27839
|
+
* const success = await Live.commitPartialLoss("BTCUSDT", 40, 38000, {
|
|
27400
27840
|
* exchangeName: "binance",
|
|
27401
27841
|
* strategyName: "my-strategy"
|
|
27402
27842
|
* });
|
|
@@ -27405,7 +27845,7 @@ class LiveUtils {
|
|
|
27405
27845
|
* }
|
|
27406
27846
|
* ```
|
|
27407
27847
|
*/
|
|
27408
|
-
this.
|
|
27848
|
+
this.commitPartialLoss = async (symbol, percentToClose, currentPrice, context) => {
|
|
27409
27849
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_PARTIAL_LOSS, {
|
|
27410
27850
|
symbol,
|
|
27411
27851
|
percentToClose,
|
|
@@ -27454,22 +27894,22 @@ class LiveUtils {
|
|
|
27454
27894
|
* // LONG: entry=100, originalSL=90, distance=10%, currentPrice=102
|
|
27455
27895
|
*
|
|
27456
27896
|
* // First call: tighten by 5%
|
|
27457
|
-
* const success1 = await Live.
|
|
27897
|
+
* const success1 = await Live.commitTrailingStop("BTCUSDT", -5, 102, {
|
|
27458
27898
|
* exchangeName: "binance",
|
|
27459
27899
|
* strategyName: "my-strategy"
|
|
27460
27900
|
* });
|
|
27461
27901
|
* // success1 = true, newDistance = 10% - 5% = 5%, newSL = 95
|
|
27462
27902
|
*
|
|
27463
27903
|
* // Second call: try weaker protection (smaller percentShift)
|
|
27464
|
-
* const success2 = await Live.
|
|
27904
|
+
* const success2 = await Live.commitTrailingStop("BTCUSDT", -3, 102, context);
|
|
27465
27905
|
* // success2 = false (SKIPPED: newSL=97 < 95, worse protection, larger % absorbs smaller)
|
|
27466
27906
|
*
|
|
27467
27907
|
* // Third call: stronger protection (larger percentShift)
|
|
27468
|
-
* const success3 = await Live.
|
|
27908
|
+
* const success3 = await Live.commitTrailingStop("BTCUSDT", -7, 102, context);
|
|
27469
27909
|
* // success3 = true (ACCEPTED: newDistance = 10% - 7% = 3%, newSL = 97 > 95, better protection)
|
|
27470
27910
|
* ```
|
|
27471
27911
|
*/
|
|
27472
|
-
this.
|
|
27912
|
+
this.commitTrailingStop = async (symbol, percentShift, currentPrice, context) => {
|
|
27473
27913
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_TRAILING_STOP, {
|
|
27474
27914
|
symbol,
|
|
27475
27915
|
percentShift,
|
|
@@ -27518,22 +27958,22 @@ class LiveUtils {
|
|
|
27518
27958
|
* // LONG: entry=100, originalTP=110, distance=10%, currentPrice=102
|
|
27519
27959
|
*
|
|
27520
27960
|
* // First call: bring TP closer by 3%
|
|
27521
|
-
* const success1 = await Live.
|
|
27961
|
+
* const success1 = await Live.commitTrailingTake("BTCUSDT", -3, 102, {
|
|
27522
27962
|
* exchangeName: "binance",
|
|
27523
27963
|
* strategyName: "my-strategy"
|
|
27524
27964
|
* });
|
|
27525
27965
|
* // success1 = true, newDistance = 10% - 3% = 7%, newTP = 107
|
|
27526
27966
|
*
|
|
27527
27967
|
* // Second call: try to move TP further (less conservative)
|
|
27528
|
-
* const success2 = await Live.
|
|
27968
|
+
* const success2 = await Live.commitTrailingTake("BTCUSDT", 2, 102, context);
|
|
27529
27969
|
* // success2 = false (SKIPPED: newTP=112 > 107, less conservative, larger % absorbs smaller)
|
|
27530
27970
|
*
|
|
27531
27971
|
* // Third call: even more conservative
|
|
27532
|
-
* const success3 = await Live.
|
|
27972
|
+
* const success3 = await Live.commitTrailingTake("BTCUSDT", -5, 102, context);
|
|
27533
27973
|
* // success3 = true (ACCEPTED: newDistance = 10% - 5% = 5%, newTP = 105 < 107, more conservative)
|
|
27534
27974
|
* ```
|
|
27535
27975
|
*/
|
|
27536
|
-
this.
|
|
27976
|
+
this.commitTrailingTake = async (symbol, percentShift, currentPrice, context) => {
|
|
27537
27977
|
backtest$1.loggerService.info(LIVE_METHOD_NAME_TRAILING_PROFIT, {
|
|
27538
27978
|
symbol,
|
|
27539
27979
|
percentShift,
|
|
@@ -27567,7 +28007,7 @@ class LiveUtils {
|
|
|
27567
28007
|
*
|
|
27568
28008
|
* @example
|
|
27569
28009
|
* ```typescript
|
|
27570
|
-
* const moved = await Live.
|
|
28010
|
+
* const moved = await Live.commitBreakeven(
|
|
27571
28011
|
* "BTCUSDT",
|
|
27572
28012
|
* 112,
|
|
27573
28013
|
* { strategyName: "my-strategy", exchangeName: "binance" }
|
|
@@ -27575,19 +28015,19 @@ class LiveUtils {
|
|
|
27575
28015
|
* console.log(moved); // true (SL moved to entry price)
|
|
27576
28016
|
* ```
|
|
27577
28017
|
*/
|
|
27578
|
-
this.
|
|
27579
|
-
backtest$1.loggerService.info(
|
|
28018
|
+
this.commitBreakeven = async (symbol, currentPrice, context) => {
|
|
28019
|
+
backtest$1.loggerService.info(LIVE_METHOD_NAME_BREAKEVEN, {
|
|
27580
28020
|
symbol,
|
|
27581
28021
|
currentPrice,
|
|
27582
28022
|
context,
|
|
27583
28023
|
});
|
|
27584
|
-
backtest$1.strategyValidationService.validate(context.strategyName,
|
|
27585
|
-
backtest$1.exchangeValidationService.validate(context.exchangeName,
|
|
28024
|
+
backtest$1.strategyValidationService.validate(context.strategyName, LIVE_METHOD_NAME_BREAKEVEN);
|
|
28025
|
+
backtest$1.exchangeValidationService.validate(context.exchangeName, LIVE_METHOD_NAME_BREAKEVEN);
|
|
27586
28026
|
{
|
|
27587
28027
|
const { riskName, riskList, actions } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
27588
|
-
riskName && backtest$1.riskValidationService.validate(riskName,
|
|
27589
|
-
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName,
|
|
27590
|
-
actions && actions.forEach((actionName) => backtest$1.actionValidationService.validate(actionName,
|
|
28028
|
+
riskName && backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_BREAKEVEN);
|
|
28029
|
+
riskList && riskList.forEach((riskName) => backtest$1.riskValidationService.validate(riskName, LIVE_METHOD_NAME_BREAKEVEN));
|
|
28030
|
+
actions && actions.forEach((actionName) => backtest$1.actionValidationService.validate(actionName, LIVE_METHOD_NAME_BREAKEVEN));
|
|
27591
28031
|
}
|
|
27592
28032
|
return await backtest$1.strategyCoreService.breakeven(false, symbol, currentPrice, {
|
|
27593
28033
|
strategyName: context.strategyName,
|
|
@@ -29415,10 +29855,15 @@ class ExchangeInstance {
|
|
|
29415
29855
|
const whenTimestamp = when.getTime();
|
|
29416
29856
|
const sinceTimestamp = since.getTime();
|
|
29417
29857
|
const filteredData = allData.filter((candle) => candle.timestamp >= sinceTimestamp && candle.timestamp <= whenTimestamp);
|
|
29418
|
-
|
|
29419
|
-
|
|
29858
|
+
// Apply distinct by timestamp to remove duplicates
|
|
29859
|
+
const uniqueData = Array.from(new Map(filteredData.map((candle) => [candle.timestamp, candle])).values());
|
|
29860
|
+
if (filteredData.length !== uniqueData.length) {
|
|
29861
|
+
backtest$1.loggerService.warn(`ExchangeInstance Removed ${filteredData.length - uniqueData.length} duplicate candles by timestamp`);
|
|
29420
29862
|
}
|
|
29421
|
-
|
|
29863
|
+
if (uniqueData.length < limit) {
|
|
29864
|
+
backtest$1.loggerService.warn(`ExchangeInstance Expected ${limit} candles, got ${uniqueData.length}`);
|
|
29865
|
+
}
|
|
29866
|
+
return uniqueData;
|
|
29422
29867
|
};
|
|
29423
29868
|
/**
|
|
29424
29869
|
* Calculates VWAP (Volume Weighted Average Price) from last N 1m candles.
|
|
@@ -30597,10 +31042,11 @@ const METHOD_NAME_INIT = "ActionBase.init";
|
|
|
30597
31042
|
const METHOD_NAME_EVENT = "ActionBase.event";
|
|
30598
31043
|
const METHOD_NAME_SIGNAL_LIVE = "ActionBase.signalLive";
|
|
30599
31044
|
const METHOD_NAME_SIGNAL_BACKTEST = "ActionBase.signalBacktest";
|
|
30600
|
-
const
|
|
30601
|
-
const
|
|
30602
|
-
const
|
|
30603
|
-
const
|
|
31045
|
+
const METHOD_NAME_BREAKEVEN_AVAILABLE = "ActionBase.breakevenAvailable";
|
|
31046
|
+
const METHOD_NAME_PARTIAL_PROFIT_AVAILABLE = "ActionBase.partialProfitAvailable";
|
|
31047
|
+
const METHOD_NAME_PARTIAL_LOSS_AVAILABLE = "ActionBase.partialLossAvailable";
|
|
31048
|
+
const METHOD_NAME_PING_SCHEDULED = "ActionBase.pingScheduled";
|
|
31049
|
+
const METHOD_NAME_PING_ACTIVE = "ActionBase.pingActive";
|
|
30604
31050
|
const METHOD_NAME_RISK_REJECTION = "ActionBase.riskRejection";
|
|
30605
31051
|
const METHOD_NAME_DISPOSE = "ActionBase.dispose";
|
|
30606
31052
|
const DEFAULT_SOURCE = "default";
|
|
@@ -30631,10 +31077,11 @@ const DEFAULT_SOURCE = "default";
|
|
|
30631
31077
|
* - signal() - Called on every tick/candle (all modes)
|
|
30632
31078
|
* - signalLive() - Called only in live mode
|
|
30633
31079
|
* - signalBacktest() - Called only in backtest mode
|
|
30634
|
-
* -
|
|
30635
|
-
* -
|
|
30636
|
-
* -
|
|
30637
|
-
* -
|
|
31080
|
+
* - breakevenAvailable() - Called when SL moved to entry
|
|
31081
|
+
* - partialProfitAvailable() - Called on profit milestones (10%, 20%, etc.)
|
|
31082
|
+
* - partialLossAvailable() - Called on loss milestones (-10%, -20%, etc.)
|
|
31083
|
+
* - pingScheduled() - Called every minute during scheduled signal monitoring
|
|
31084
|
+
* - pingActive() - Called every minute during active pending signal monitoring
|
|
30638
31085
|
* - riskRejection() - Called when signal rejected by risk management
|
|
30639
31086
|
*
|
|
30640
31087
|
* @example
|
|
@@ -30675,7 +31122,7 @@ const DEFAULT_SOURCE = "default";
|
|
|
30675
31122
|
* }
|
|
30676
31123
|
*
|
|
30677
31124
|
* // Register the action
|
|
30678
|
-
*
|
|
31125
|
+
* addActionSchema({
|
|
30679
31126
|
* actionName: "telegram-notifier",
|
|
30680
31127
|
* handler: TelegramNotifier
|
|
30681
31128
|
* });
|
|
@@ -30717,11 +31164,13 @@ class ActionBase {
|
|
|
30717
31164
|
* @param strategyName - Strategy identifier this action is attached to
|
|
30718
31165
|
* @param frameName - Timeframe identifier this action is attached to
|
|
30719
31166
|
* @param actionName - Action identifier
|
|
31167
|
+
* @param backtest - If running in backtest
|
|
30720
31168
|
*/
|
|
30721
|
-
constructor(strategyName, frameName, actionName) {
|
|
31169
|
+
constructor(strategyName, frameName, actionName, backtest) {
|
|
30722
31170
|
this.strategyName = strategyName;
|
|
30723
31171
|
this.frameName = frameName;
|
|
30724
31172
|
this.actionName = actionName;
|
|
31173
|
+
this.backtest = backtest;
|
|
30725
31174
|
}
|
|
30726
31175
|
/**
|
|
30727
31176
|
* Initializes the action handler.
|
|
@@ -30845,7 +31294,7 @@ class ActionBase {
|
|
|
30845
31294
|
* Called once per signal when price moves far enough to cover fees and slippage.
|
|
30846
31295
|
* Breakeven threshold: (CC_PERCENT_SLIPPAGE + CC_PERCENT_FEE) * 2 + CC_BREAKEVEN_THRESHOLD
|
|
30847
31296
|
*
|
|
30848
|
-
* Triggered by: ActionCoreService.
|
|
31297
|
+
* Triggered by: ActionCoreService.breakevenAvailable() via BreakevenConnectionService
|
|
30849
31298
|
* Source: breakevenSubject.next() in CREATE_COMMIT_BREAKEVEN_FN callback
|
|
30850
31299
|
* Frequency: Once per signal when threshold reached
|
|
30851
31300
|
*
|
|
@@ -30855,7 +31304,7 @@ class ActionBase {
|
|
|
30855
31304
|
*
|
|
30856
31305
|
* @example
|
|
30857
31306
|
* ```typescript
|
|
30858
|
-
* async
|
|
31307
|
+
* async breakevenAvailable(event: BreakevenContract) {
|
|
30859
31308
|
* await this.telegram.send(
|
|
30860
31309
|
* `[${event.strategyName}] Breakeven reached! ` +
|
|
30861
31310
|
* `Signal: ${event.data.side} @ ${event.currentPrice}`
|
|
@@ -30863,8 +31312,8 @@ class ActionBase {
|
|
|
30863
31312
|
* }
|
|
30864
31313
|
* ```
|
|
30865
31314
|
*/
|
|
30866
|
-
|
|
30867
|
-
backtest$1.loggerService.info(
|
|
31315
|
+
breakevenAvailable(event, source = DEFAULT_SOURCE) {
|
|
31316
|
+
backtest$1.loggerService.info(METHOD_NAME_BREAKEVEN_AVAILABLE, {
|
|
30868
31317
|
event,
|
|
30869
31318
|
source,
|
|
30870
31319
|
});
|
|
@@ -30875,7 +31324,7 @@ class ActionBase {
|
|
|
30875
31324
|
* Called once per profit level per signal (deduplicated).
|
|
30876
31325
|
* Use to track profit milestones and adjust position management.
|
|
30877
31326
|
*
|
|
30878
|
-
* Triggered by: ActionCoreService.
|
|
31327
|
+
* Triggered by: ActionCoreService.partialProfitAvailable() via PartialConnectionService
|
|
30879
31328
|
* Source: partialProfitSubject.next() in CREATE_COMMIT_PROFIT_FN callback
|
|
30880
31329
|
* Frequency: Once per profit level per signal
|
|
30881
31330
|
*
|
|
@@ -30885,7 +31334,7 @@ class ActionBase {
|
|
|
30885
31334
|
*
|
|
30886
31335
|
* @example
|
|
30887
31336
|
* ```typescript
|
|
30888
|
-
* async
|
|
31337
|
+
* async partialProfitAvailable(event: PartialProfitContract) {
|
|
30889
31338
|
* await this.telegram.send(
|
|
30890
31339
|
* `[${event.strategyName}] Profit ${event.level}% reached! ` +
|
|
30891
31340
|
* `Current price: ${event.currentPrice}`
|
|
@@ -30894,8 +31343,8 @@ class ActionBase {
|
|
|
30894
31343
|
* }
|
|
30895
31344
|
* ```
|
|
30896
31345
|
*/
|
|
30897
|
-
|
|
30898
|
-
backtest$1.loggerService.info(
|
|
31346
|
+
partialProfitAvailable(event, source = DEFAULT_SOURCE) {
|
|
31347
|
+
backtest$1.loggerService.info(METHOD_NAME_PARTIAL_PROFIT_AVAILABLE, {
|
|
30899
31348
|
event,
|
|
30900
31349
|
source,
|
|
30901
31350
|
});
|
|
@@ -30906,7 +31355,7 @@ class ActionBase {
|
|
|
30906
31355
|
* Called once per loss level per signal (deduplicated).
|
|
30907
31356
|
* Use to track loss milestones and implement risk management actions.
|
|
30908
31357
|
*
|
|
30909
|
-
* Triggered by: ActionCoreService.
|
|
31358
|
+
* Triggered by: ActionCoreService.partialLossAvailable() via PartialConnectionService
|
|
30910
31359
|
* Source: partialLossSubject.next() in CREATE_COMMIT_LOSS_FN callback
|
|
30911
31360
|
* Frequency: Once per loss level per signal
|
|
30912
31361
|
*
|
|
@@ -30916,7 +31365,7 @@ class ActionBase {
|
|
|
30916
31365
|
*
|
|
30917
31366
|
* @example
|
|
30918
31367
|
* ```typescript
|
|
30919
|
-
* async
|
|
31368
|
+
* async partialLossAvailable(event: PartialLossContract) {
|
|
30920
31369
|
* await this.telegram.send(
|
|
30921
31370
|
* `[${event.strategyName}] Loss ${event.level}% reached! ` +
|
|
30922
31371
|
* `Current price: ${event.currentPrice}`
|
|
@@ -30925,37 +31374,66 @@ class ActionBase {
|
|
|
30925
31374
|
* }
|
|
30926
31375
|
* ```
|
|
30927
31376
|
*/
|
|
30928
|
-
|
|
30929
|
-
backtest$1.loggerService.info(
|
|
31377
|
+
partialLossAvailable(event, source = DEFAULT_SOURCE) {
|
|
31378
|
+
backtest$1.loggerService.info(METHOD_NAME_PARTIAL_LOSS_AVAILABLE, {
|
|
30930
31379
|
event,
|
|
30931
31380
|
source,
|
|
30932
31381
|
});
|
|
30933
31382
|
}
|
|
30934
31383
|
/**
|
|
30935
|
-
* Handles ping events during scheduled signal monitoring.
|
|
31384
|
+
* Handles scheduled ping events during scheduled signal monitoring.
|
|
30936
31385
|
*
|
|
30937
31386
|
* Called every minute while a scheduled signal is waiting for activation.
|
|
30938
31387
|
* Use to monitor pending signals and track wait time.
|
|
30939
31388
|
*
|
|
30940
|
-
* Triggered by: ActionCoreService.
|
|
30941
|
-
* Source:
|
|
31389
|
+
* Triggered by: ActionCoreService.pingScheduled() via StrategyConnectionService
|
|
31390
|
+
* Source: schedulePingSubject.next() in CREATE_COMMIT_SCHEDULE_PING_FN callback
|
|
30942
31391
|
* Frequency: Every minute while scheduled signal is waiting
|
|
30943
31392
|
*
|
|
30944
|
-
* Default implementation: Logs ping event.
|
|
31393
|
+
* Default implementation: Logs scheduled ping event.
|
|
30945
31394
|
*
|
|
30946
31395
|
* @param event - Scheduled signal monitoring data with symbol, strategy info, signal data, timestamp
|
|
30947
31396
|
*
|
|
30948
31397
|
* @example
|
|
30949
31398
|
* ```typescript
|
|
30950
|
-
*
|
|
31399
|
+
* pingScheduled(event: SchedulePingContract) {
|
|
30951
31400
|
* const waitTime = Date.now() - event.data.timestampScheduled;
|
|
30952
31401
|
* const waitMinutes = Math.floor(waitTime / 60000);
|
|
30953
31402
|
* console.log(`Scheduled signal waiting ${waitMinutes} minutes`);
|
|
30954
31403
|
* }
|
|
30955
31404
|
* ```
|
|
30956
31405
|
*/
|
|
30957
|
-
|
|
30958
|
-
backtest$1.loggerService.info(
|
|
31406
|
+
pingScheduled(event, source = DEFAULT_SOURCE) {
|
|
31407
|
+
backtest$1.loggerService.info(METHOD_NAME_PING_SCHEDULED, {
|
|
31408
|
+
event,
|
|
31409
|
+
source,
|
|
31410
|
+
});
|
|
31411
|
+
}
|
|
31412
|
+
/**
|
|
31413
|
+
* Handles active ping events during active pending signal monitoring.
|
|
31414
|
+
*
|
|
31415
|
+
* Called every minute while a pending signal is active (position open).
|
|
31416
|
+
* Use to monitor active positions and track lifecycle.
|
|
31417
|
+
*
|
|
31418
|
+
* Triggered by: ActionCoreService.pingActive() via StrategyConnectionService
|
|
31419
|
+
* Source: activePingSubject.next() in CREATE_COMMIT_ACTIVE_PING_FN callback
|
|
31420
|
+
* Frequency: Every minute while pending signal is active
|
|
31421
|
+
*
|
|
31422
|
+
* Default implementation: Logs active ping event.
|
|
31423
|
+
*
|
|
31424
|
+
* @param event - Active pending signal monitoring data with symbol, strategy info, signal data, timestamp
|
|
31425
|
+
*
|
|
31426
|
+
* @example
|
|
31427
|
+
* ```typescript
|
|
31428
|
+
* pingActive(event: ActivePingContract) {
|
|
31429
|
+
* const holdTime = Date.now() - event.data.pendingAt;
|
|
31430
|
+
* const holdMinutes = Math.floor(holdTime / 60000);
|
|
31431
|
+
* console.log(`Active signal holding ${holdMinutes} minutes`);
|
|
31432
|
+
* }
|
|
31433
|
+
* ```
|
|
31434
|
+
*/
|
|
31435
|
+
pingActive(event, source = DEFAULT_SOURCE) {
|
|
31436
|
+
backtest$1.loggerService.info(METHOD_NAME_PING_ACTIVE, {
|
|
30959
31437
|
event,
|
|
30960
31438
|
source,
|
|
30961
31439
|
});
|
|
@@ -31074,4 +31552,4 @@ const set = (object, path, value) => {
|
|
|
31074
31552
|
}
|
|
31075
31553
|
};
|
|
31076
31554
|
|
|
31077
|
-
export { ActionBase, Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, Optimizer, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Walker,
|
|
31555
|
+
export { ActionBase, Backtest, Breakeven, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, Markdown, MarkdownFileBase, MarkdownFolderBase, MethodContextService, Notification, Optimizer, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Report, ReportBase, Risk, Schedule, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addOptimizerSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, commitBreakeven, commitCancel, commitPartialLoss, commitPartialProfit, commitTrailingStop, commitTrailingTake, dumpSignalData, emitters, formatPrice, formatQuantity, get, getActionSchema, getAveragePrice, getBacktestTimeframe, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getExchangeSchema, getFrameSchema, getMode, getOptimizerSchema, getOrderBook, getRiskSchema, getSizingSchema, getStrategySchema, getSymbol, getWalkerSchema, hasTradeContext, backtest as lib, listExchangeSchema, listFrameSchema, listOptimizerSchema, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideOptimizerSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, roundTicks, set, setColumns, setConfig, setLogger, stop, validate };
|