backtest-kit 1.4.8 → 1.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.cjs +173 -32
- package/build/index.mjs +173 -33
- package/package.json +1 -1
- package/types.d.ts +52 -11
package/build/index.cjs
CHANGED
|
@@ -399,10 +399,12 @@ const GET_CANDLES_FN = async (dto, since, self) => {
|
|
|
399
399
|
return result;
|
|
400
400
|
}
|
|
401
401
|
catch (err) {
|
|
402
|
-
|
|
402
|
+
const message = `ClientExchange GET_CANDLES_FN: attempt ${i + 1} failed for symbol=${dto.symbol}, interval=${dto.interval}, since=${since.toISOString()}, limit=${dto.limit}}`;
|
|
403
|
+
self.params.logger.warn(message, {
|
|
403
404
|
error: functoolsKit.errorData(err),
|
|
404
405
|
message: functoolsKit.getErrorMessage(err),
|
|
405
406
|
});
|
|
407
|
+
console.warn(message);
|
|
406
408
|
lastError = err;
|
|
407
409
|
await functoolsKit.sleep(GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_DELAY_MS);
|
|
408
410
|
}
|
|
@@ -1602,6 +1604,12 @@ const signalBacktestEmitter = new functoolsKit.Subject();
|
|
|
1602
1604
|
* Emits errors caught in background tasks (Live.background, Backtest.background).
|
|
1603
1605
|
*/
|
|
1604
1606
|
const errorEmitter = new functoolsKit.Subject();
|
|
1607
|
+
/**
|
|
1608
|
+
* Exit emitter for critical errors that require process termination.
|
|
1609
|
+
* Emits errors that should terminate the current execution (Backtest, Live, Walker).
|
|
1610
|
+
* Unlike errorEmitter (for recoverable errors), exitEmitter signals fatal errors.
|
|
1611
|
+
*/
|
|
1612
|
+
const exitEmitter = new functoolsKit.Subject();
|
|
1605
1613
|
/**
|
|
1606
1614
|
* Done emitter for live background execution completion.
|
|
1607
1615
|
* Emits when live background tasks complete (Live.background).
|
|
@@ -1674,6 +1682,7 @@ var emitters = /*#__PURE__*/Object.freeze({
|
|
|
1674
1682
|
doneLiveSubject: doneLiveSubject,
|
|
1675
1683
|
doneWalkerSubject: doneWalkerSubject,
|
|
1676
1684
|
errorEmitter: errorEmitter,
|
|
1685
|
+
exitEmitter: exitEmitter,
|
|
1677
1686
|
partialLossSubject: partialLossSubject,
|
|
1678
1687
|
partialProfitSubject: partialProfitSubject,
|
|
1679
1688
|
performanceEmitter: performanceEmitter,
|
|
@@ -3119,13 +3128,12 @@ class StrategyConnectionService {
|
|
|
3119
3128
|
* @param strategyName - Name of strategy to stop
|
|
3120
3129
|
* @returns Promise that resolves when stop flag is set
|
|
3121
3130
|
*/
|
|
3122
|
-
this.stop = async (
|
|
3131
|
+
this.stop = async (ctx) => {
|
|
3123
3132
|
this.loggerService.log("strategyConnectionService stop", {
|
|
3124
|
-
|
|
3125
|
-
strategyName,
|
|
3133
|
+
ctx
|
|
3126
3134
|
});
|
|
3127
|
-
const strategy = this.getStrategy(symbol, strategyName);
|
|
3128
|
-
await strategy.stop(symbol, strategyName);
|
|
3135
|
+
const strategy = this.getStrategy(ctx.symbol, ctx.strategyName);
|
|
3136
|
+
await strategy.stop(ctx.symbol, ctx.strategyName);
|
|
3129
3137
|
};
|
|
3130
3138
|
/**
|
|
3131
3139
|
* Clears the memoized ClientStrategy instance from cache.
|
|
@@ -4056,13 +4064,12 @@ class StrategyGlobalService {
|
|
|
4056
4064
|
* @param strategyName - Name of strategy to stop
|
|
4057
4065
|
* @returns Promise that resolves when stop flag is set
|
|
4058
4066
|
*/
|
|
4059
|
-
this.stop = async (
|
|
4067
|
+
this.stop = async (ctx) => {
|
|
4060
4068
|
this.loggerService.log("strategyGlobalService stop", {
|
|
4061
|
-
|
|
4062
|
-
strategyName,
|
|
4069
|
+
ctx,
|
|
4063
4070
|
});
|
|
4064
|
-
await this.validate(symbol, strategyName);
|
|
4065
|
-
return await this.strategyConnectionService.stop(
|
|
4071
|
+
await this.validate(ctx.symbol, ctx.strategyName);
|
|
4072
|
+
return await this.strategyConnectionService.stop(ctx);
|
|
4066
4073
|
};
|
|
4067
4074
|
/**
|
|
4068
4075
|
* Clears the memoized ClientStrategy instance from cache.
|
|
@@ -4754,7 +4761,21 @@ class BacktestLogicPrivateService {
|
|
|
4754
4761
|
progress: totalFrames > 0 ? i / totalFrames : 0,
|
|
4755
4762
|
});
|
|
4756
4763
|
}
|
|
4757
|
-
|
|
4764
|
+
let result;
|
|
4765
|
+
try {
|
|
4766
|
+
result = await this.strategyGlobalService.tick(symbol, when, true);
|
|
4767
|
+
}
|
|
4768
|
+
catch (error) {
|
|
4769
|
+
console.warn(`backtestLogicPrivateService tick failed, skipping timeframe when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
4770
|
+
this.loggerService.warn("backtestLogicPrivateService tick failed, skipping timeframe", {
|
|
4771
|
+
symbol,
|
|
4772
|
+
when: when.toISOString(),
|
|
4773
|
+
error: functoolsKit.errorData(error), message: functoolsKit.getErrorMessage(error),
|
|
4774
|
+
});
|
|
4775
|
+
await errorEmitter.next(error);
|
|
4776
|
+
i++;
|
|
4777
|
+
continue;
|
|
4778
|
+
}
|
|
4758
4779
|
// Если scheduled signal создан - обрабатываем через backtest()
|
|
4759
4780
|
if (result.action === "scheduled") {
|
|
4760
4781
|
const signalStartTime = performance.now();
|
|
@@ -4770,7 +4791,22 @@ class BacktestLogicPrivateService {
|
|
|
4770
4791
|
// + minuteEstimatedTime для работы сигнала ПОСЛЕ активации
|
|
4771
4792
|
// +1 потому что when включается как первая свеча (timestamp начинается с when, а не after when)
|
|
4772
4793
|
const candlesNeeded = GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES + signal.minuteEstimatedTime + 1;
|
|
4773
|
-
|
|
4794
|
+
let candles;
|
|
4795
|
+
try {
|
|
4796
|
+
candles = await this.exchangeGlobalService.getNextCandles(symbol, "1m", candlesNeeded, when, true);
|
|
4797
|
+
}
|
|
4798
|
+
catch (error) {
|
|
4799
|
+
console.warn(`backtestLogicPrivateService getNextCandles failed for scheduled signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
4800
|
+
this.loggerService.warn("backtestLogicPrivateService getNextCandles failed for scheduled signal", {
|
|
4801
|
+
symbol,
|
|
4802
|
+
signalId: signal.id,
|
|
4803
|
+
candlesNeeded,
|
|
4804
|
+
error: functoolsKit.errorData(error), message: functoolsKit.getErrorMessage(error),
|
|
4805
|
+
});
|
|
4806
|
+
await errorEmitter.next(error);
|
|
4807
|
+
i++;
|
|
4808
|
+
continue;
|
|
4809
|
+
}
|
|
4774
4810
|
if (!candles.length) {
|
|
4775
4811
|
i++;
|
|
4776
4812
|
continue;
|
|
@@ -4783,7 +4819,21 @@ class BacktestLogicPrivateService {
|
|
|
4783
4819
|
});
|
|
4784
4820
|
// backtest() сам обработает scheduled signal: найдет активацию/отмену
|
|
4785
4821
|
// и если активируется - продолжит с TP/SL мониторингом
|
|
4786
|
-
|
|
4822
|
+
let backtestResult;
|
|
4823
|
+
try {
|
|
4824
|
+
backtestResult = await this.strategyGlobalService.backtest(symbol, candles, when, true);
|
|
4825
|
+
}
|
|
4826
|
+
catch (error) {
|
|
4827
|
+
console.warn(`backtestLogicPrivateService backtest failed for scheduled signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
4828
|
+
this.loggerService.warn("backtestLogicPrivateService backtest failed for scheduled signal", {
|
|
4829
|
+
symbol,
|
|
4830
|
+
signalId: signal.id,
|
|
4831
|
+
error: functoolsKit.errorData(error), message: functoolsKit.getErrorMessage(error),
|
|
4832
|
+
});
|
|
4833
|
+
await errorEmitter.next(error);
|
|
4834
|
+
i++;
|
|
4835
|
+
continue;
|
|
4836
|
+
}
|
|
4787
4837
|
this.loggerService.info("backtestLogicPrivateService scheduled signal closed", {
|
|
4788
4838
|
symbol,
|
|
4789
4839
|
signalId: backtestResult.signal.id,
|
|
@@ -4824,9 +4874,24 @@ class BacktestLogicPrivateService {
|
|
|
4824
4874
|
minuteEstimatedTime: signal.minuteEstimatedTime,
|
|
4825
4875
|
});
|
|
4826
4876
|
// Получаем свечи для бектеста
|
|
4827
|
-
|
|
4877
|
+
let candles;
|
|
4878
|
+
try {
|
|
4879
|
+
candles = await this.exchangeGlobalService.getNextCandles(symbol, "1m", signal.minuteEstimatedTime, when, true);
|
|
4880
|
+
}
|
|
4881
|
+
catch (error) {
|
|
4882
|
+
console.warn(`backtestLogicPrivateService getNextCandles failed for opened signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
4883
|
+
this.loggerService.warn("backtestLogicPrivateService getNextCandles failed for opened signal", {
|
|
4884
|
+
symbol,
|
|
4885
|
+
signalId: signal.id,
|
|
4886
|
+
error: functoolsKit.errorData(error), message: functoolsKit.getErrorMessage(error),
|
|
4887
|
+
});
|
|
4888
|
+
await errorEmitter.next(error);
|
|
4889
|
+
i++;
|
|
4890
|
+
continue;
|
|
4891
|
+
}
|
|
4828
4892
|
if (!candles.length) {
|
|
4829
|
-
|
|
4893
|
+
i++;
|
|
4894
|
+
continue;
|
|
4830
4895
|
}
|
|
4831
4896
|
this.loggerService.info("backtestLogicPrivateService candles fetched", {
|
|
4832
4897
|
symbol,
|
|
@@ -4834,7 +4899,21 @@ class BacktestLogicPrivateService {
|
|
|
4834
4899
|
candlesCount: candles.length,
|
|
4835
4900
|
});
|
|
4836
4901
|
// Вызываем backtest - всегда возвращает closed
|
|
4837
|
-
|
|
4902
|
+
let backtestResult;
|
|
4903
|
+
try {
|
|
4904
|
+
backtestResult = await this.strategyGlobalService.backtest(symbol, candles, when, true);
|
|
4905
|
+
}
|
|
4906
|
+
catch (error) {
|
|
4907
|
+
console.warn(`backtestLogicPrivateService backtest failed for opened signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
4908
|
+
this.loggerService.warn("backtestLogicPrivateService backtest failed for opened signal", {
|
|
4909
|
+
symbol,
|
|
4910
|
+
signalId: signal.id,
|
|
4911
|
+
error: functoolsKit.errorData(error), message: functoolsKit.getErrorMessage(error),
|
|
4912
|
+
});
|
|
4913
|
+
await errorEmitter.next(error);
|
|
4914
|
+
i++;
|
|
4915
|
+
continue;
|
|
4916
|
+
}
|
|
4838
4917
|
this.loggerService.info("backtestLogicPrivateService signal closed", {
|
|
4839
4918
|
symbol,
|
|
4840
4919
|
signalId: backtestResult.signal.id,
|
|
@@ -4958,7 +5037,21 @@ class LiveLogicPrivateService {
|
|
|
4958
5037
|
while (true) {
|
|
4959
5038
|
const tickStartTime = performance.now();
|
|
4960
5039
|
const when = new Date();
|
|
4961
|
-
|
|
5040
|
+
let result;
|
|
5041
|
+
try {
|
|
5042
|
+
result = await this.strategyGlobalService.tick(symbol, when, false);
|
|
5043
|
+
}
|
|
5044
|
+
catch (error) {
|
|
5045
|
+
console.warn(`backtestLogicPrivateService tick failed when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
5046
|
+
this.loggerService.warn("liveLogicPrivateService tick failed, retrying after sleep", {
|
|
5047
|
+
symbol,
|
|
5048
|
+
when: when.toISOString(),
|
|
5049
|
+
error: functoolsKit.errorData(error), message: functoolsKit.getErrorMessage(error),
|
|
5050
|
+
});
|
|
5051
|
+
await errorEmitter.next(error);
|
|
5052
|
+
await functoolsKit.sleep(TICK_TTL);
|
|
5053
|
+
continue;
|
|
5054
|
+
}
|
|
4962
5055
|
this.loggerService.info("liveLogicPrivateService tick result", {
|
|
4963
5056
|
symbol,
|
|
4964
5057
|
action: result.action,
|
|
@@ -4989,7 +5082,7 @@ class LiveLogicPrivateService {
|
|
|
4989
5082
|
await functoolsKit.sleep(TICK_TTL);
|
|
4990
5083
|
continue;
|
|
4991
5084
|
}
|
|
4992
|
-
// Yield opened, closed
|
|
5085
|
+
// Yield opened, closed results
|
|
4993
5086
|
yield result;
|
|
4994
5087
|
await functoolsKit.sleep(TICK_TTL);
|
|
4995
5088
|
}
|
|
@@ -5080,10 +5173,27 @@ class WalkerLogicPrivateService {
|
|
|
5080
5173
|
frameName: context.frameName,
|
|
5081
5174
|
});
|
|
5082
5175
|
pendingStrategy = strategyName;
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5176
|
+
let result;
|
|
5177
|
+
try {
|
|
5178
|
+
result = await Promise.race([
|
|
5179
|
+
await functoolsKit.resolveDocuments(iterator),
|
|
5180
|
+
listenStop,
|
|
5181
|
+
]);
|
|
5182
|
+
}
|
|
5183
|
+
catch (error) {
|
|
5184
|
+
console.warn(`walkerLogicPrivateService backtest failed symbol=${symbol} strategyName=${strategyName} exchangeName=${context.exchangeName}`);
|
|
5185
|
+
this.loggerService.warn("walkerLogicPrivateService backtest failed for strategy, skipping", {
|
|
5186
|
+
strategyName,
|
|
5187
|
+
symbol,
|
|
5188
|
+
error: functoolsKit.errorData(error), message: functoolsKit.getErrorMessage(error),
|
|
5189
|
+
});
|
|
5190
|
+
await errorEmitter.next(error);
|
|
5191
|
+
// Call onStrategyError callback if provided
|
|
5192
|
+
if (walkerSchema.callbacks?.onStrategyError) {
|
|
5193
|
+
walkerSchema.callbacks.onStrategyError(strategyName, symbol, error);
|
|
5194
|
+
}
|
|
5195
|
+
continue;
|
|
5196
|
+
}
|
|
5087
5197
|
if (result === CANCEL_SYMBOL) {
|
|
5088
5198
|
this.loggerService.info("walkerLogicPrivateService received stop signal, cancelling walker", {
|
|
5089
5199
|
context,
|
|
@@ -11441,6 +11551,7 @@ const LISTEN_SIGNAL_LIVE_ONCE_METHOD_NAME = "event.listenSignalLiveOnce";
|
|
|
11441
11551
|
const LISTEN_SIGNAL_BACKTEST_METHOD_NAME = "event.listenSignalBacktest";
|
|
11442
11552
|
const LISTEN_SIGNAL_BACKTEST_ONCE_METHOD_NAME = "event.listenSignalBacktestOnce";
|
|
11443
11553
|
const LISTEN_ERROR_METHOD_NAME = "event.listenError";
|
|
11554
|
+
const LISTEN_EXIT_METHOD_NAME = "event.listenExit";
|
|
11444
11555
|
const LISTEN_DONE_LIVE_METHOD_NAME = "event.listenDoneLive";
|
|
11445
11556
|
const LISTEN_DONE_LIVE_ONCE_METHOD_NAME = "event.listenDoneLiveOnce";
|
|
11446
11557
|
const LISTEN_DONE_BACKTEST_METHOD_NAME = "event.listenDoneBacktest";
|
|
@@ -11623,9 +11734,10 @@ function listenSignalBacktestOnce(filterFn, fn) {
|
|
|
11623
11734
|
return signalBacktestEmitter.filter(filterFn).once(fn);
|
|
11624
11735
|
}
|
|
11625
11736
|
/**
|
|
11626
|
-
* Subscribes to
|
|
11737
|
+
* Subscribes to recoverable execution errors with queued async processing.
|
|
11627
11738
|
*
|
|
11628
|
-
* Listens to errors
|
|
11739
|
+
* Listens to recoverable errors during strategy execution (e.g., failed API calls).
|
|
11740
|
+
* These errors are caught and handled gracefully - execution continues.
|
|
11629
11741
|
* Events are processed sequentially in order received, even if callback is async.
|
|
11630
11742
|
* Uses queued wrapper to prevent concurrent execution of the callback.
|
|
11631
11743
|
*
|
|
@@ -11637,7 +11749,7 @@ function listenSignalBacktestOnce(filterFn, fn) {
|
|
|
11637
11749
|
* import { listenError } from "./function/event";
|
|
11638
11750
|
*
|
|
11639
11751
|
* const unsubscribe = listenError((error) => {
|
|
11640
|
-
* console.error("
|
|
11752
|
+
* console.error("Recoverable error (execution continues):", error.message);
|
|
11641
11753
|
* // Log to monitoring service, send alerts, etc.
|
|
11642
11754
|
* });
|
|
11643
11755
|
*
|
|
@@ -11649,6 +11761,34 @@ function listenError(fn) {
|
|
|
11649
11761
|
backtest$1.loggerService.log(LISTEN_ERROR_METHOD_NAME);
|
|
11650
11762
|
return errorEmitter.subscribe(functoolsKit.queued(async (error) => fn(error)));
|
|
11651
11763
|
}
|
|
11764
|
+
/**
|
|
11765
|
+
* Subscribes to fatal execution errors with queued async processing.
|
|
11766
|
+
*
|
|
11767
|
+
* Listens to critical errors that terminate execution (Live.background, Backtest.background, Walker.background).
|
|
11768
|
+
* Unlike listenError (recoverable errors), these errors stop the current process.
|
|
11769
|
+
* Events are processed sequentially in order received, even if callback is async.
|
|
11770
|
+
* Uses queued wrapper to prevent concurrent execution of the callback.
|
|
11771
|
+
*
|
|
11772
|
+
* @param fn - Callback function to handle fatal error events
|
|
11773
|
+
* @returns Unsubscribe function to stop listening
|
|
11774
|
+
*
|
|
11775
|
+
* @example
|
|
11776
|
+
* ```typescript
|
|
11777
|
+
* import { listenExit } from "./function/event";
|
|
11778
|
+
*
|
|
11779
|
+
* const unsubscribe = listenExit((error) => {
|
|
11780
|
+
* console.error("Fatal error (execution terminated):", error.message);
|
|
11781
|
+
* // Log to monitoring, send alerts, restart process, etc.
|
|
11782
|
+
* });
|
|
11783
|
+
*
|
|
11784
|
+
* // Later: stop listening
|
|
11785
|
+
* unsubscribe();
|
|
11786
|
+
* ```
|
|
11787
|
+
*/
|
|
11788
|
+
function listenExit(fn) {
|
|
11789
|
+
backtest$1.loggerService.log(LISTEN_EXIT_METHOD_NAME);
|
|
11790
|
+
return exitEmitter.subscribe(functoolsKit.queued(async (error) => fn(error)));
|
|
11791
|
+
}
|
|
11652
11792
|
/**
|
|
11653
11793
|
* Subscribes to live background execution completion events with queued async processing.
|
|
11654
11794
|
*
|
|
@@ -12462,9 +12602,9 @@ class BacktestUtils {
|
|
|
12462
12602
|
}
|
|
12463
12603
|
isDone = true;
|
|
12464
12604
|
};
|
|
12465
|
-
task().catch((error) =>
|
|
12605
|
+
task().catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
|
|
12466
12606
|
return () => {
|
|
12467
|
-
backtest$1.strategyGlobalService.stop(symbol, context.strategyName);
|
|
12607
|
+
backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName });
|
|
12468
12608
|
backtest$1.strategyGlobalService
|
|
12469
12609
|
.getPendingSignal(symbol, context.strategyName)
|
|
12470
12610
|
.then(async (pendingSignal) => {
|
|
@@ -12675,9 +12815,9 @@ class LiveUtils {
|
|
|
12675
12815
|
}
|
|
12676
12816
|
isDone = true;
|
|
12677
12817
|
};
|
|
12678
|
-
task().catch((error) =>
|
|
12818
|
+
task().catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
|
|
12679
12819
|
return () => {
|
|
12680
|
-
backtest$1.strategyGlobalService.stop(symbol, context.strategyName);
|
|
12820
|
+
backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName });
|
|
12681
12821
|
backtest$1.strategyGlobalService
|
|
12682
12822
|
.getPendingSignal(symbol, context.strategyName)
|
|
12683
12823
|
.then(async (pendingSignal) => {
|
|
@@ -13106,10 +13246,10 @@ class WalkerUtils {
|
|
|
13106
13246
|
}
|
|
13107
13247
|
isDone = true;
|
|
13108
13248
|
};
|
|
13109
|
-
task().catch((error) =>
|
|
13249
|
+
task().catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
|
|
13110
13250
|
return () => {
|
|
13111
13251
|
for (const strategyName of walkerSchema.strategies) {
|
|
13112
|
-
backtest$1.strategyGlobalService.stop(symbol, strategyName);
|
|
13252
|
+
backtest$1.strategyGlobalService.stop({ symbol, strategyName });
|
|
13113
13253
|
walkerStopSubject.next({ symbol, strategyName });
|
|
13114
13254
|
}
|
|
13115
13255
|
if (!isDone) {
|
|
@@ -13859,6 +13999,7 @@ exports.listenDoneLiveOnce = listenDoneLiveOnce;
|
|
|
13859
13999
|
exports.listenDoneWalker = listenDoneWalker;
|
|
13860
14000
|
exports.listenDoneWalkerOnce = listenDoneWalkerOnce;
|
|
13861
14001
|
exports.listenError = listenError;
|
|
14002
|
+
exports.listenExit = listenExit;
|
|
13862
14003
|
exports.listenOptimizerProgress = listenOptimizerProgress;
|
|
13863
14004
|
exports.listenPartialLoss = listenPartialLoss;
|
|
13864
14005
|
exports.listenPartialLossOnce = listenPartialLossOnce;
|
package/build/index.mjs
CHANGED
|
@@ -397,10 +397,12 @@ const GET_CANDLES_FN = async (dto, since, self) => {
|
|
|
397
397
|
return result;
|
|
398
398
|
}
|
|
399
399
|
catch (err) {
|
|
400
|
-
|
|
400
|
+
const message = `ClientExchange GET_CANDLES_FN: attempt ${i + 1} failed for symbol=${dto.symbol}, interval=${dto.interval}, since=${since.toISOString()}, limit=${dto.limit}}`;
|
|
401
|
+
self.params.logger.warn(message, {
|
|
401
402
|
error: errorData(err),
|
|
402
403
|
message: getErrorMessage(err),
|
|
403
404
|
});
|
|
405
|
+
console.warn(message);
|
|
404
406
|
lastError = err;
|
|
405
407
|
await sleep(GLOBAL_CONFIG.CC_GET_CANDLES_RETRY_DELAY_MS);
|
|
406
408
|
}
|
|
@@ -1600,6 +1602,12 @@ const signalBacktestEmitter = new Subject();
|
|
|
1600
1602
|
* Emits errors caught in background tasks (Live.background, Backtest.background).
|
|
1601
1603
|
*/
|
|
1602
1604
|
const errorEmitter = new Subject();
|
|
1605
|
+
/**
|
|
1606
|
+
* Exit emitter for critical errors that require process termination.
|
|
1607
|
+
* Emits errors that should terminate the current execution (Backtest, Live, Walker).
|
|
1608
|
+
* Unlike errorEmitter (for recoverable errors), exitEmitter signals fatal errors.
|
|
1609
|
+
*/
|
|
1610
|
+
const exitEmitter = new Subject();
|
|
1603
1611
|
/**
|
|
1604
1612
|
* Done emitter for live background execution completion.
|
|
1605
1613
|
* Emits when live background tasks complete (Live.background).
|
|
@@ -1672,6 +1680,7 @@ var emitters = /*#__PURE__*/Object.freeze({
|
|
|
1672
1680
|
doneLiveSubject: doneLiveSubject,
|
|
1673
1681
|
doneWalkerSubject: doneWalkerSubject,
|
|
1674
1682
|
errorEmitter: errorEmitter,
|
|
1683
|
+
exitEmitter: exitEmitter,
|
|
1675
1684
|
partialLossSubject: partialLossSubject,
|
|
1676
1685
|
partialProfitSubject: partialProfitSubject,
|
|
1677
1686
|
performanceEmitter: performanceEmitter,
|
|
@@ -3117,13 +3126,12 @@ class StrategyConnectionService {
|
|
|
3117
3126
|
* @param strategyName - Name of strategy to stop
|
|
3118
3127
|
* @returns Promise that resolves when stop flag is set
|
|
3119
3128
|
*/
|
|
3120
|
-
this.stop = async (
|
|
3129
|
+
this.stop = async (ctx) => {
|
|
3121
3130
|
this.loggerService.log("strategyConnectionService stop", {
|
|
3122
|
-
|
|
3123
|
-
strategyName,
|
|
3131
|
+
ctx
|
|
3124
3132
|
});
|
|
3125
|
-
const strategy = this.getStrategy(symbol, strategyName);
|
|
3126
|
-
await strategy.stop(symbol, strategyName);
|
|
3133
|
+
const strategy = this.getStrategy(ctx.symbol, ctx.strategyName);
|
|
3134
|
+
await strategy.stop(ctx.symbol, ctx.strategyName);
|
|
3127
3135
|
};
|
|
3128
3136
|
/**
|
|
3129
3137
|
* Clears the memoized ClientStrategy instance from cache.
|
|
@@ -4054,13 +4062,12 @@ class StrategyGlobalService {
|
|
|
4054
4062
|
* @param strategyName - Name of strategy to stop
|
|
4055
4063
|
* @returns Promise that resolves when stop flag is set
|
|
4056
4064
|
*/
|
|
4057
|
-
this.stop = async (
|
|
4065
|
+
this.stop = async (ctx) => {
|
|
4058
4066
|
this.loggerService.log("strategyGlobalService stop", {
|
|
4059
|
-
|
|
4060
|
-
strategyName,
|
|
4067
|
+
ctx,
|
|
4061
4068
|
});
|
|
4062
|
-
await this.validate(symbol, strategyName);
|
|
4063
|
-
return await this.strategyConnectionService.stop(
|
|
4069
|
+
await this.validate(ctx.symbol, ctx.strategyName);
|
|
4070
|
+
return await this.strategyConnectionService.stop(ctx);
|
|
4064
4071
|
};
|
|
4065
4072
|
/**
|
|
4066
4073
|
* Clears the memoized ClientStrategy instance from cache.
|
|
@@ -4752,7 +4759,21 @@ class BacktestLogicPrivateService {
|
|
|
4752
4759
|
progress: totalFrames > 0 ? i / totalFrames : 0,
|
|
4753
4760
|
});
|
|
4754
4761
|
}
|
|
4755
|
-
|
|
4762
|
+
let result;
|
|
4763
|
+
try {
|
|
4764
|
+
result = await this.strategyGlobalService.tick(symbol, when, true);
|
|
4765
|
+
}
|
|
4766
|
+
catch (error) {
|
|
4767
|
+
console.warn(`backtestLogicPrivateService tick failed, skipping timeframe when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
4768
|
+
this.loggerService.warn("backtestLogicPrivateService tick failed, skipping timeframe", {
|
|
4769
|
+
symbol,
|
|
4770
|
+
when: when.toISOString(),
|
|
4771
|
+
error: errorData(error), message: getErrorMessage(error),
|
|
4772
|
+
});
|
|
4773
|
+
await errorEmitter.next(error);
|
|
4774
|
+
i++;
|
|
4775
|
+
continue;
|
|
4776
|
+
}
|
|
4756
4777
|
// Если scheduled signal создан - обрабатываем через backtest()
|
|
4757
4778
|
if (result.action === "scheduled") {
|
|
4758
4779
|
const signalStartTime = performance.now();
|
|
@@ -4768,7 +4789,22 @@ class BacktestLogicPrivateService {
|
|
|
4768
4789
|
// + minuteEstimatedTime для работы сигнала ПОСЛЕ активации
|
|
4769
4790
|
// +1 потому что when включается как первая свеча (timestamp начинается с when, а не after when)
|
|
4770
4791
|
const candlesNeeded = GLOBAL_CONFIG.CC_SCHEDULE_AWAIT_MINUTES + signal.minuteEstimatedTime + 1;
|
|
4771
|
-
|
|
4792
|
+
let candles;
|
|
4793
|
+
try {
|
|
4794
|
+
candles = await this.exchangeGlobalService.getNextCandles(symbol, "1m", candlesNeeded, when, true);
|
|
4795
|
+
}
|
|
4796
|
+
catch (error) {
|
|
4797
|
+
console.warn(`backtestLogicPrivateService getNextCandles failed for scheduled signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
4798
|
+
this.loggerService.warn("backtestLogicPrivateService getNextCandles failed for scheduled signal", {
|
|
4799
|
+
symbol,
|
|
4800
|
+
signalId: signal.id,
|
|
4801
|
+
candlesNeeded,
|
|
4802
|
+
error: errorData(error), message: getErrorMessage(error),
|
|
4803
|
+
});
|
|
4804
|
+
await errorEmitter.next(error);
|
|
4805
|
+
i++;
|
|
4806
|
+
continue;
|
|
4807
|
+
}
|
|
4772
4808
|
if (!candles.length) {
|
|
4773
4809
|
i++;
|
|
4774
4810
|
continue;
|
|
@@ -4781,7 +4817,21 @@ class BacktestLogicPrivateService {
|
|
|
4781
4817
|
});
|
|
4782
4818
|
// backtest() сам обработает scheduled signal: найдет активацию/отмену
|
|
4783
4819
|
// и если активируется - продолжит с TP/SL мониторингом
|
|
4784
|
-
|
|
4820
|
+
let backtestResult;
|
|
4821
|
+
try {
|
|
4822
|
+
backtestResult = await this.strategyGlobalService.backtest(symbol, candles, when, true);
|
|
4823
|
+
}
|
|
4824
|
+
catch (error) {
|
|
4825
|
+
console.warn(`backtestLogicPrivateService backtest failed for scheduled signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
4826
|
+
this.loggerService.warn("backtestLogicPrivateService backtest failed for scheduled signal", {
|
|
4827
|
+
symbol,
|
|
4828
|
+
signalId: signal.id,
|
|
4829
|
+
error: errorData(error), message: getErrorMessage(error),
|
|
4830
|
+
});
|
|
4831
|
+
await errorEmitter.next(error);
|
|
4832
|
+
i++;
|
|
4833
|
+
continue;
|
|
4834
|
+
}
|
|
4785
4835
|
this.loggerService.info("backtestLogicPrivateService scheduled signal closed", {
|
|
4786
4836
|
symbol,
|
|
4787
4837
|
signalId: backtestResult.signal.id,
|
|
@@ -4822,9 +4872,24 @@ class BacktestLogicPrivateService {
|
|
|
4822
4872
|
minuteEstimatedTime: signal.minuteEstimatedTime,
|
|
4823
4873
|
});
|
|
4824
4874
|
// Получаем свечи для бектеста
|
|
4825
|
-
|
|
4875
|
+
let candles;
|
|
4876
|
+
try {
|
|
4877
|
+
candles = await this.exchangeGlobalService.getNextCandles(symbol, "1m", signal.minuteEstimatedTime, when, true);
|
|
4878
|
+
}
|
|
4879
|
+
catch (error) {
|
|
4880
|
+
console.warn(`backtestLogicPrivateService getNextCandles failed for opened signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
4881
|
+
this.loggerService.warn("backtestLogicPrivateService getNextCandles failed for opened signal", {
|
|
4882
|
+
symbol,
|
|
4883
|
+
signalId: signal.id,
|
|
4884
|
+
error: errorData(error), message: getErrorMessage(error),
|
|
4885
|
+
});
|
|
4886
|
+
await errorEmitter.next(error);
|
|
4887
|
+
i++;
|
|
4888
|
+
continue;
|
|
4889
|
+
}
|
|
4826
4890
|
if (!candles.length) {
|
|
4827
|
-
|
|
4891
|
+
i++;
|
|
4892
|
+
continue;
|
|
4828
4893
|
}
|
|
4829
4894
|
this.loggerService.info("backtestLogicPrivateService candles fetched", {
|
|
4830
4895
|
symbol,
|
|
@@ -4832,7 +4897,21 @@ class BacktestLogicPrivateService {
|
|
|
4832
4897
|
candlesCount: candles.length,
|
|
4833
4898
|
});
|
|
4834
4899
|
// Вызываем backtest - всегда возвращает closed
|
|
4835
|
-
|
|
4900
|
+
let backtestResult;
|
|
4901
|
+
try {
|
|
4902
|
+
backtestResult = await this.strategyGlobalService.backtest(symbol, candles, when, true);
|
|
4903
|
+
}
|
|
4904
|
+
catch (error) {
|
|
4905
|
+
console.warn(`backtestLogicPrivateService backtest failed for opened signal when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
4906
|
+
this.loggerService.warn("backtestLogicPrivateService backtest failed for opened signal", {
|
|
4907
|
+
symbol,
|
|
4908
|
+
signalId: signal.id,
|
|
4909
|
+
error: errorData(error), message: getErrorMessage(error),
|
|
4910
|
+
});
|
|
4911
|
+
await errorEmitter.next(error);
|
|
4912
|
+
i++;
|
|
4913
|
+
continue;
|
|
4914
|
+
}
|
|
4836
4915
|
this.loggerService.info("backtestLogicPrivateService signal closed", {
|
|
4837
4916
|
symbol,
|
|
4838
4917
|
signalId: backtestResult.signal.id,
|
|
@@ -4956,7 +5035,21 @@ class LiveLogicPrivateService {
|
|
|
4956
5035
|
while (true) {
|
|
4957
5036
|
const tickStartTime = performance.now();
|
|
4958
5037
|
const when = new Date();
|
|
4959
|
-
|
|
5038
|
+
let result;
|
|
5039
|
+
try {
|
|
5040
|
+
result = await this.strategyGlobalService.tick(symbol, when, false);
|
|
5041
|
+
}
|
|
5042
|
+
catch (error) {
|
|
5043
|
+
console.warn(`backtestLogicPrivateService tick failed when=${when.toISOString()} symbol=${symbol} strategyName=${this.methodContextService.context.strategyName} exchangeName=${this.methodContextService.context.exchangeName}`);
|
|
5044
|
+
this.loggerService.warn("liveLogicPrivateService tick failed, retrying after sleep", {
|
|
5045
|
+
symbol,
|
|
5046
|
+
when: when.toISOString(),
|
|
5047
|
+
error: errorData(error), message: getErrorMessage(error),
|
|
5048
|
+
});
|
|
5049
|
+
await errorEmitter.next(error);
|
|
5050
|
+
await sleep(TICK_TTL);
|
|
5051
|
+
continue;
|
|
5052
|
+
}
|
|
4960
5053
|
this.loggerService.info("liveLogicPrivateService tick result", {
|
|
4961
5054
|
symbol,
|
|
4962
5055
|
action: result.action,
|
|
@@ -4987,7 +5080,7 @@ class LiveLogicPrivateService {
|
|
|
4987
5080
|
await sleep(TICK_TTL);
|
|
4988
5081
|
continue;
|
|
4989
5082
|
}
|
|
4990
|
-
// Yield opened, closed
|
|
5083
|
+
// Yield opened, closed results
|
|
4991
5084
|
yield result;
|
|
4992
5085
|
await sleep(TICK_TTL);
|
|
4993
5086
|
}
|
|
@@ -5078,10 +5171,27 @@ class WalkerLogicPrivateService {
|
|
|
5078
5171
|
frameName: context.frameName,
|
|
5079
5172
|
});
|
|
5080
5173
|
pendingStrategy = strategyName;
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5174
|
+
let result;
|
|
5175
|
+
try {
|
|
5176
|
+
result = await Promise.race([
|
|
5177
|
+
await resolveDocuments(iterator),
|
|
5178
|
+
listenStop,
|
|
5179
|
+
]);
|
|
5180
|
+
}
|
|
5181
|
+
catch (error) {
|
|
5182
|
+
console.warn(`walkerLogicPrivateService backtest failed symbol=${symbol} strategyName=${strategyName} exchangeName=${context.exchangeName}`);
|
|
5183
|
+
this.loggerService.warn("walkerLogicPrivateService backtest failed for strategy, skipping", {
|
|
5184
|
+
strategyName,
|
|
5185
|
+
symbol,
|
|
5186
|
+
error: errorData(error), message: getErrorMessage(error),
|
|
5187
|
+
});
|
|
5188
|
+
await errorEmitter.next(error);
|
|
5189
|
+
// Call onStrategyError callback if provided
|
|
5190
|
+
if (walkerSchema.callbacks?.onStrategyError) {
|
|
5191
|
+
walkerSchema.callbacks.onStrategyError(strategyName, symbol, error);
|
|
5192
|
+
}
|
|
5193
|
+
continue;
|
|
5194
|
+
}
|
|
5085
5195
|
if (result === CANCEL_SYMBOL) {
|
|
5086
5196
|
this.loggerService.info("walkerLogicPrivateService received stop signal, cancelling walker", {
|
|
5087
5197
|
context,
|
|
@@ -11439,6 +11549,7 @@ const LISTEN_SIGNAL_LIVE_ONCE_METHOD_NAME = "event.listenSignalLiveOnce";
|
|
|
11439
11549
|
const LISTEN_SIGNAL_BACKTEST_METHOD_NAME = "event.listenSignalBacktest";
|
|
11440
11550
|
const LISTEN_SIGNAL_BACKTEST_ONCE_METHOD_NAME = "event.listenSignalBacktestOnce";
|
|
11441
11551
|
const LISTEN_ERROR_METHOD_NAME = "event.listenError";
|
|
11552
|
+
const LISTEN_EXIT_METHOD_NAME = "event.listenExit";
|
|
11442
11553
|
const LISTEN_DONE_LIVE_METHOD_NAME = "event.listenDoneLive";
|
|
11443
11554
|
const LISTEN_DONE_LIVE_ONCE_METHOD_NAME = "event.listenDoneLiveOnce";
|
|
11444
11555
|
const LISTEN_DONE_BACKTEST_METHOD_NAME = "event.listenDoneBacktest";
|
|
@@ -11621,9 +11732,10 @@ function listenSignalBacktestOnce(filterFn, fn) {
|
|
|
11621
11732
|
return signalBacktestEmitter.filter(filterFn).once(fn);
|
|
11622
11733
|
}
|
|
11623
11734
|
/**
|
|
11624
|
-
* Subscribes to
|
|
11735
|
+
* Subscribes to recoverable execution errors with queued async processing.
|
|
11625
11736
|
*
|
|
11626
|
-
* Listens to errors
|
|
11737
|
+
* Listens to recoverable errors during strategy execution (e.g., failed API calls).
|
|
11738
|
+
* These errors are caught and handled gracefully - execution continues.
|
|
11627
11739
|
* Events are processed sequentially in order received, even if callback is async.
|
|
11628
11740
|
* Uses queued wrapper to prevent concurrent execution of the callback.
|
|
11629
11741
|
*
|
|
@@ -11635,7 +11747,7 @@ function listenSignalBacktestOnce(filterFn, fn) {
|
|
|
11635
11747
|
* import { listenError } from "./function/event";
|
|
11636
11748
|
*
|
|
11637
11749
|
* const unsubscribe = listenError((error) => {
|
|
11638
|
-
* console.error("
|
|
11750
|
+
* console.error("Recoverable error (execution continues):", error.message);
|
|
11639
11751
|
* // Log to monitoring service, send alerts, etc.
|
|
11640
11752
|
* });
|
|
11641
11753
|
*
|
|
@@ -11647,6 +11759,34 @@ function listenError(fn) {
|
|
|
11647
11759
|
backtest$1.loggerService.log(LISTEN_ERROR_METHOD_NAME);
|
|
11648
11760
|
return errorEmitter.subscribe(queued(async (error) => fn(error)));
|
|
11649
11761
|
}
|
|
11762
|
+
/**
|
|
11763
|
+
* Subscribes to fatal execution errors with queued async processing.
|
|
11764
|
+
*
|
|
11765
|
+
* Listens to critical errors that terminate execution (Live.background, Backtest.background, Walker.background).
|
|
11766
|
+
* Unlike listenError (recoverable errors), these errors stop the current process.
|
|
11767
|
+
* Events are processed sequentially in order received, even if callback is async.
|
|
11768
|
+
* Uses queued wrapper to prevent concurrent execution of the callback.
|
|
11769
|
+
*
|
|
11770
|
+
* @param fn - Callback function to handle fatal error events
|
|
11771
|
+
* @returns Unsubscribe function to stop listening
|
|
11772
|
+
*
|
|
11773
|
+
* @example
|
|
11774
|
+
* ```typescript
|
|
11775
|
+
* import { listenExit } from "./function/event";
|
|
11776
|
+
*
|
|
11777
|
+
* const unsubscribe = listenExit((error) => {
|
|
11778
|
+
* console.error("Fatal error (execution terminated):", error.message);
|
|
11779
|
+
* // Log to monitoring, send alerts, restart process, etc.
|
|
11780
|
+
* });
|
|
11781
|
+
*
|
|
11782
|
+
* // Later: stop listening
|
|
11783
|
+
* unsubscribe();
|
|
11784
|
+
* ```
|
|
11785
|
+
*/
|
|
11786
|
+
function listenExit(fn) {
|
|
11787
|
+
backtest$1.loggerService.log(LISTEN_EXIT_METHOD_NAME);
|
|
11788
|
+
return exitEmitter.subscribe(queued(async (error) => fn(error)));
|
|
11789
|
+
}
|
|
11650
11790
|
/**
|
|
11651
11791
|
* Subscribes to live background execution completion events with queued async processing.
|
|
11652
11792
|
*
|
|
@@ -12460,9 +12600,9 @@ class BacktestUtils {
|
|
|
12460
12600
|
}
|
|
12461
12601
|
isDone = true;
|
|
12462
12602
|
};
|
|
12463
|
-
task().catch((error) =>
|
|
12603
|
+
task().catch((error) => exitEmitter.next(new Error(getErrorMessage(error))));
|
|
12464
12604
|
return () => {
|
|
12465
|
-
backtest$1.strategyGlobalService.stop(symbol, context.strategyName);
|
|
12605
|
+
backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName });
|
|
12466
12606
|
backtest$1.strategyGlobalService
|
|
12467
12607
|
.getPendingSignal(symbol, context.strategyName)
|
|
12468
12608
|
.then(async (pendingSignal) => {
|
|
@@ -12673,9 +12813,9 @@ class LiveUtils {
|
|
|
12673
12813
|
}
|
|
12674
12814
|
isDone = true;
|
|
12675
12815
|
};
|
|
12676
|
-
task().catch((error) =>
|
|
12816
|
+
task().catch((error) => exitEmitter.next(new Error(getErrorMessage(error))));
|
|
12677
12817
|
return () => {
|
|
12678
|
-
backtest$1.strategyGlobalService.stop(symbol, context.strategyName);
|
|
12818
|
+
backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName });
|
|
12679
12819
|
backtest$1.strategyGlobalService
|
|
12680
12820
|
.getPendingSignal(symbol, context.strategyName)
|
|
12681
12821
|
.then(async (pendingSignal) => {
|
|
@@ -13104,10 +13244,10 @@ class WalkerUtils {
|
|
|
13104
13244
|
}
|
|
13105
13245
|
isDone = true;
|
|
13106
13246
|
};
|
|
13107
|
-
task().catch((error) =>
|
|
13247
|
+
task().catch((error) => exitEmitter.next(new Error(getErrorMessage(error))));
|
|
13108
13248
|
return () => {
|
|
13109
13249
|
for (const strategyName of walkerSchema.strategies) {
|
|
13110
|
-
backtest$1.strategyGlobalService.stop(symbol, strategyName);
|
|
13250
|
+
backtest$1.strategyGlobalService.stop({ symbol, strategyName });
|
|
13111
13251
|
walkerStopSubject.next({ symbol, strategyName });
|
|
13112
13252
|
}
|
|
13113
13253
|
if (!isDone) {
|
|
@@ -13810,4 +13950,4 @@ class ConstantUtils {
|
|
|
13810
13950
|
*/
|
|
13811
13951
|
const Constant = new ConstantUtils();
|
|
13812
13952
|
|
|
13813
|
-
export { Backtest, Constant, ExecutionContextService, Heat, Live, MethodContextService, Optimizer, Partial, Performance, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setConfig, setLogger };
|
|
13953
|
+
export { Backtest, Constant, ExecutionContextService, Heat, Live, MethodContextService, Optimizer, Partial, Performance, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setConfig, setLogger };
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -1201,6 +1201,8 @@ interface IWalkerCallbacks {
|
|
|
1201
1201
|
onStrategyStart: (strategyName: StrategyName, symbol: string) => void;
|
|
1202
1202
|
/** Called when a strategy backtest completes */
|
|
1203
1203
|
onStrategyComplete: (strategyName: StrategyName, symbol: string, stats: BacktestStatistics, metric: number | null) => void;
|
|
1204
|
+
/** Called when a strategy backtest fails with an error */
|
|
1205
|
+
onStrategyError: (strategyName: StrategyName, symbol: string, error: Error | unknown) => void;
|
|
1204
1206
|
/** Called when all strategies have been tested */
|
|
1205
1207
|
onComplete: (results: IWalkerResults) => void;
|
|
1206
1208
|
}
|
|
@@ -2938,9 +2940,10 @@ declare function listenSignalBacktest(fn: (event: IStrategyTickResult) => void):
|
|
|
2938
2940
|
*/
|
|
2939
2941
|
declare function listenSignalBacktestOnce(filterFn: (event: IStrategyTickResult) => boolean, fn: (event: IStrategyTickResult) => void): () => void;
|
|
2940
2942
|
/**
|
|
2941
|
-
* Subscribes to
|
|
2943
|
+
* Subscribes to recoverable execution errors with queued async processing.
|
|
2942
2944
|
*
|
|
2943
|
-
* Listens to errors
|
|
2945
|
+
* Listens to recoverable errors during strategy execution (e.g., failed API calls).
|
|
2946
|
+
* These errors are caught and handled gracefully - execution continues.
|
|
2944
2947
|
* Events are processed sequentially in order received, even if callback is async.
|
|
2945
2948
|
* Uses queued wrapper to prevent concurrent execution of the callback.
|
|
2946
2949
|
*
|
|
@@ -2952,7 +2955,7 @@ declare function listenSignalBacktestOnce(filterFn: (event: IStrategyTickResult)
|
|
|
2952
2955
|
* import { listenError } from "./function/event";
|
|
2953
2956
|
*
|
|
2954
2957
|
* const unsubscribe = listenError((error) => {
|
|
2955
|
-
* console.error("
|
|
2958
|
+
* console.error("Recoverable error (execution continues):", error.message);
|
|
2956
2959
|
* // Log to monitoring service, send alerts, etc.
|
|
2957
2960
|
* });
|
|
2958
2961
|
*
|
|
@@ -2961,6 +2964,31 @@ declare function listenSignalBacktestOnce(filterFn: (event: IStrategyTickResult)
|
|
|
2961
2964
|
* ```
|
|
2962
2965
|
*/
|
|
2963
2966
|
declare function listenError(fn: (error: Error) => void): () => void;
|
|
2967
|
+
/**
|
|
2968
|
+
* Subscribes to fatal execution errors with queued async processing.
|
|
2969
|
+
*
|
|
2970
|
+
* Listens to critical errors that terminate execution (Live.background, Backtest.background, Walker.background).
|
|
2971
|
+
* Unlike listenError (recoverable errors), these errors stop the current process.
|
|
2972
|
+
* Events are processed sequentially in order received, even if callback is async.
|
|
2973
|
+
* Uses queued wrapper to prevent concurrent execution of the callback.
|
|
2974
|
+
*
|
|
2975
|
+
* @param fn - Callback function to handle fatal error events
|
|
2976
|
+
* @returns Unsubscribe function to stop listening
|
|
2977
|
+
*
|
|
2978
|
+
* @example
|
|
2979
|
+
* ```typescript
|
|
2980
|
+
* import { listenExit } from "./function/event";
|
|
2981
|
+
*
|
|
2982
|
+
* const unsubscribe = listenExit((error) => {
|
|
2983
|
+
* console.error("Fatal error (execution terminated):", error.message);
|
|
2984
|
+
* // Log to monitoring, send alerts, restart process, etc.
|
|
2985
|
+
* });
|
|
2986
|
+
*
|
|
2987
|
+
* // Later: stop listening
|
|
2988
|
+
* unsubscribe();
|
|
2989
|
+
* ```
|
|
2990
|
+
*/
|
|
2991
|
+
declare function listenExit(fn: (error: Error) => void): () => void;
|
|
2964
2992
|
/**
|
|
2965
2993
|
* Subscribes to live background execution completion events with queued async processing.
|
|
2966
2994
|
*
|
|
@@ -5170,7 +5198,7 @@ declare class LiveUtils {
|
|
|
5170
5198
|
run: (symbol: string, context: {
|
|
5171
5199
|
strategyName: string;
|
|
5172
5200
|
exchangeName: string;
|
|
5173
|
-
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed
|
|
5201
|
+
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
5174
5202
|
/**
|
|
5175
5203
|
* Runs live trading in background without yielding results.
|
|
5176
5204
|
*
|
|
@@ -6091,6 +6119,12 @@ declare const signalBacktestEmitter: Subject<IStrategyTickResult>;
|
|
|
6091
6119
|
* Emits errors caught in background tasks (Live.background, Backtest.background).
|
|
6092
6120
|
*/
|
|
6093
6121
|
declare const errorEmitter: Subject<Error>;
|
|
6122
|
+
/**
|
|
6123
|
+
* Exit emitter for critical errors that require process termination.
|
|
6124
|
+
* Emits errors that should terminate the current execution (Backtest, Live, Walker).
|
|
6125
|
+
* Unlike errorEmitter (for recoverable errors), exitEmitter signals fatal errors.
|
|
6126
|
+
*/
|
|
6127
|
+
declare const exitEmitter: Subject<Error>;
|
|
6094
6128
|
/**
|
|
6095
6129
|
* Done emitter for live background execution completion.
|
|
6096
6130
|
* Emits when live background tasks complete (Live.background).
|
|
@@ -6164,6 +6198,7 @@ declare const emitters_doneBacktestSubject: typeof doneBacktestSubject;
|
|
|
6164
6198
|
declare const emitters_doneLiveSubject: typeof doneLiveSubject;
|
|
6165
6199
|
declare const emitters_doneWalkerSubject: typeof doneWalkerSubject;
|
|
6166
6200
|
declare const emitters_errorEmitter: typeof errorEmitter;
|
|
6201
|
+
declare const emitters_exitEmitter: typeof exitEmitter;
|
|
6167
6202
|
declare const emitters_partialLossSubject: typeof partialLossSubject;
|
|
6168
6203
|
declare const emitters_partialProfitSubject: typeof partialProfitSubject;
|
|
6169
6204
|
declare const emitters_performanceEmitter: typeof performanceEmitter;
|
|
@@ -6178,7 +6213,7 @@ declare const emitters_walkerCompleteSubject: typeof walkerCompleteSubject;
|
|
|
6178
6213
|
declare const emitters_walkerEmitter: typeof walkerEmitter;
|
|
6179
6214
|
declare const emitters_walkerStopSubject: typeof walkerStopSubject;
|
|
6180
6215
|
declare namespace emitters {
|
|
6181
|
-
export { emitters_doneBacktestSubject as doneBacktestSubject, emitters_doneLiveSubject as doneLiveSubject, emitters_doneWalkerSubject as doneWalkerSubject, emitters_errorEmitter as errorEmitter, emitters_partialLossSubject as partialLossSubject, emitters_partialProfitSubject as partialProfitSubject, emitters_performanceEmitter as performanceEmitter, emitters_progressBacktestEmitter as progressBacktestEmitter, emitters_progressOptimizerEmitter as progressOptimizerEmitter, emitters_progressWalkerEmitter as progressWalkerEmitter, emitters_signalBacktestEmitter as signalBacktestEmitter, emitters_signalEmitter as signalEmitter, emitters_signalLiveEmitter as signalLiveEmitter, emitters_validationSubject as validationSubject, emitters_walkerCompleteSubject as walkerCompleteSubject, emitters_walkerEmitter as walkerEmitter, emitters_walkerStopSubject as walkerStopSubject };
|
|
6216
|
+
export { emitters_doneBacktestSubject as doneBacktestSubject, emitters_doneLiveSubject as doneLiveSubject, emitters_doneWalkerSubject as doneWalkerSubject, emitters_errorEmitter as errorEmitter, emitters_exitEmitter as exitEmitter, emitters_partialLossSubject as partialLossSubject, emitters_partialProfitSubject as partialProfitSubject, emitters_performanceEmitter as performanceEmitter, emitters_progressBacktestEmitter as progressBacktestEmitter, emitters_progressOptimizerEmitter as progressOptimizerEmitter, emitters_progressWalkerEmitter as progressWalkerEmitter, emitters_signalBacktestEmitter as signalBacktestEmitter, emitters_signalEmitter as signalEmitter, emitters_signalLiveEmitter as signalLiveEmitter, emitters_validationSubject as validationSubject, emitters_walkerCompleteSubject as walkerCompleteSubject, emitters_walkerEmitter as walkerEmitter, emitters_walkerStopSubject as walkerStopSubject };
|
|
6182
6217
|
}
|
|
6183
6218
|
|
|
6184
6219
|
/**
|
|
@@ -6485,7 +6520,10 @@ declare class StrategyConnectionService {
|
|
|
6485
6520
|
* @param strategyName - Name of strategy to stop
|
|
6486
6521
|
* @returns Promise that resolves when stop flag is set
|
|
6487
6522
|
*/
|
|
6488
|
-
stop: (
|
|
6523
|
+
stop: (ctx: {
|
|
6524
|
+
symbol: string;
|
|
6525
|
+
strategyName: StrategyName;
|
|
6526
|
+
}) => Promise<void>;
|
|
6489
6527
|
/**
|
|
6490
6528
|
* Clears the memoized ClientStrategy instance from cache.
|
|
6491
6529
|
*
|
|
@@ -6953,7 +6991,10 @@ declare class StrategyGlobalService {
|
|
|
6953
6991
|
* @param strategyName - Name of strategy to stop
|
|
6954
6992
|
* @returns Promise that resolves when stop flag is set
|
|
6955
6993
|
*/
|
|
6956
|
-
stop: (
|
|
6994
|
+
stop: (ctx: {
|
|
6995
|
+
symbol: string;
|
|
6996
|
+
strategyName: StrategyName;
|
|
6997
|
+
}) => Promise<void>;
|
|
6957
6998
|
/**
|
|
6958
6999
|
* Clears the memoized ClientStrategy instance from cache.
|
|
6959
7000
|
*
|
|
@@ -7461,7 +7502,7 @@ declare class LiveLogicPrivateService {
|
|
|
7461
7502
|
* }
|
|
7462
7503
|
* ```
|
|
7463
7504
|
*/
|
|
7464
|
-
run(symbol: string): AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed
|
|
7505
|
+
run(symbol: string): AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
7465
7506
|
}
|
|
7466
7507
|
|
|
7467
7508
|
/**
|
|
@@ -7606,7 +7647,7 @@ declare class LiveLogicPublicService {
|
|
|
7606
7647
|
run: (symbol: string, context: {
|
|
7607
7648
|
strategyName: string;
|
|
7608
7649
|
exchangeName: string;
|
|
7609
|
-
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed
|
|
7650
|
+
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
7610
7651
|
}
|
|
7611
7652
|
|
|
7612
7653
|
/**
|
|
@@ -7674,7 +7715,7 @@ declare class LiveCommandService {
|
|
|
7674
7715
|
run: (symbol: string, context: {
|
|
7675
7716
|
strategyName: string;
|
|
7676
7717
|
exchangeName: string;
|
|
7677
|
-
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed
|
|
7718
|
+
}) => AsyncGenerator<IStrategyTickResultOpened | IStrategyTickResultClosed, void, unknown>;
|
|
7678
7719
|
}
|
|
7679
7720
|
|
|
7680
7721
|
/**
|
|
@@ -8659,4 +8700,4 @@ declare const backtest: {
|
|
|
8659
8700
|
loggerService: LoggerService;
|
|
8660
8701
|
};
|
|
8661
8702
|
|
|
8662
|
-
export { Backtest, type BacktestStatistics, type CandleInterval, Constant, type DoneContract, type EntityId, ExecutionContextService, type FrameInterval, type GlobalConfig, Heat, type ICandleData, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type IHeatmapStatistics, type IOptimizerCallbacks, type IOptimizerData, type IOptimizerFetchArgs, type IOptimizerFilterArgs, type IOptimizerRange, type IOptimizerSchema, type IOptimizerSource, type IOptimizerStrategy, type IOptimizerTemplate, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type IScheduledSignalRow, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStrategyPnL, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultCancelled, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IStrategyTickResultScheduled, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, Live, type LiveStatistics, type MessageModel, type MessageRole, MethodContextService, Optimizer, Partial$1 as Partial, type PartialData, type PartialLossContract, type PartialProfitContract, type PartialStatistics, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatistics, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, type ProgressBacktestContract, type ProgressOptimizerContract, type ProgressWalkerContract, type RiskData, Schedule, type ScheduleData, type ScheduleStatistics, type SignalData, type SignalInterval, type TPersistBase, type TPersistBaseCtor, Walker, type WalkerContract, type WalkerMetric, type WalkerStatistics, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setConfig, setLogger };
|
|
8703
|
+
export { Backtest, type BacktestStatistics, type CandleInterval, Constant, type DoneContract, type EntityId, ExecutionContextService, type FrameInterval, type GlobalConfig, Heat, type ICandleData, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type IHeatmapStatistics, type IOptimizerCallbacks, type IOptimizerData, type IOptimizerFetchArgs, type IOptimizerFilterArgs, type IOptimizerRange, type IOptimizerSchema, type IOptimizerSource, type IOptimizerStrategy, type IOptimizerTemplate, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type IScheduledSignalRow, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStrategyPnL, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultCancelled, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IStrategyTickResultScheduled, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, Live, type LiveStatistics, type MessageModel, type MessageRole, MethodContextService, Optimizer, Partial$1 as Partial, type PartialData, type PartialLossContract, type PartialProfitContract, type PartialStatistics, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatistics, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, type ProgressBacktestContract, type ProgressOptimizerContract, type ProgressWalkerContract, type RiskData, Schedule, type ScheduleData, type ScheduleStatistics, type SignalData, type SignalInterval, type TPersistBase, type TPersistBaseCtor, Walker, type WalkerContract, type WalkerMetric, type WalkerStatistics, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setConfig, setLogger };
|