backtest-kit 1.5.2 → 1.5.4
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 +177 -7
- package/build/index.cjs +1155 -268
- package/build/index.mjs +1156 -269
- package/package.json +2 -2
- package/types.d.ts +186 -6
package/build/index.cjs
CHANGED
|
@@ -1675,6 +1675,8 @@ const walkerCompleteSubject = new functoolsKit.Subject();
|
|
|
1675
1675
|
/**
|
|
1676
1676
|
* Walker stop emitter for walker cancellation events.
|
|
1677
1677
|
* Emits when a walker comparison is stopped/cancelled.
|
|
1678
|
+
*
|
|
1679
|
+
* Includes walkerName to support multiple walkers running on the same symbol.
|
|
1678
1680
|
*/
|
|
1679
1681
|
const walkerStopSubject = new functoolsKit.Subject();
|
|
1680
1682
|
/**
|
|
@@ -2909,6 +2911,23 @@ class ClientStrategy {
|
|
|
2909
2911
|
});
|
|
2910
2912
|
return this._pendingSignal;
|
|
2911
2913
|
}
|
|
2914
|
+
/**
|
|
2915
|
+
* Returns the stopped state of the strategy.
|
|
2916
|
+
*
|
|
2917
|
+
* Indicates whether the strategy has been explicitly stopped and should
|
|
2918
|
+
* not continue processing new ticks or signals.
|
|
2919
|
+
*
|
|
2920
|
+
* @param symbol - Trading pair symbol
|
|
2921
|
+
* @param strategyName - Name of the strategy
|
|
2922
|
+
* @returns Promise resolving to true if strategy is stopped, false otherwise
|
|
2923
|
+
*/
|
|
2924
|
+
async getStopped(symbol, strategyName) {
|
|
2925
|
+
this.params.logger.debug("ClientStrategy getStopped", {
|
|
2926
|
+
symbol,
|
|
2927
|
+
strategyName,
|
|
2928
|
+
});
|
|
2929
|
+
return this._isStopped;
|
|
2930
|
+
}
|
|
2912
2931
|
/**
|
|
2913
2932
|
* Performs a single tick of strategy execution.
|
|
2914
2933
|
*
|
|
@@ -3151,18 +3170,24 @@ class ClientStrategy {
|
|
|
3151
3170
|
* // Existing signal will continue until natural close
|
|
3152
3171
|
* ```
|
|
3153
3172
|
*/
|
|
3154
|
-
async stop(symbol, strategyName) {
|
|
3173
|
+
async stop(symbol, strategyName, backtest) {
|
|
3155
3174
|
this.params.logger.debug("ClientStrategy stop", {
|
|
3156
3175
|
symbol,
|
|
3157
3176
|
strategyName,
|
|
3158
3177
|
hasPendingSignal: this._pendingSignal !== null,
|
|
3159
3178
|
hasScheduledSignal: this._scheduledSignal !== null,
|
|
3179
|
+
backtest,
|
|
3160
3180
|
});
|
|
3161
3181
|
this._isStopped = true;
|
|
3162
3182
|
// Clear scheduled signal if exists
|
|
3163
|
-
if (this._scheduledSignal) {
|
|
3164
|
-
|
|
3183
|
+
if (!this._scheduledSignal) {
|
|
3184
|
+
return;
|
|
3185
|
+
}
|
|
3186
|
+
this._scheduledSignal = null;
|
|
3187
|
+
if (backtest) {
|
|
3188
|
+
return;
|
|
3165
3189
|
}
|
|
3190
|
+
await PersistScheduleAdapter.writeScheduleData(this._scheduledSignal, this.params.execution.context.symbol, this.params.strategyName);
|
|
3166
3191
|
}
|
|
3167
3192
|
}
|
|
3168
3193
|
|
|
@@ -3245,6 +3270,24 @@ class StrategyConnectionService {
|
|
|
3245
3270
|
const strategy = this.getStrategy(symbol, strategyName);
|
|
3246
3271
|
return await strategy.getPendingSignal(symbol, strategyName);
|
|
3247
3272
|
};
|
|
3273
|
+
/**
|
|
3274
|
+
* Retrieves the stopped state of the strategy.
|
|
3275
|
+
*
|
|
3276
|
+
* Delegates to the underlying strategy instance to check if it has been
|
|
3277
|
+
* marked as stopped and should cease operation.
|
|
3278
|
+
*
|
|
3279
|
+
* @param symbol - Trading pair symbol
|
|
3280
|
+
* @param strategyName - Name of the strategy
|
|
3281
|
+
* @returns Promise resolving to true if strategy is stopped, false otherwise
|
|
3282
|
+
*/
|
|
3283
|
+
this.getStopped = async (symbol, strategyName) => {
|
|
3284
|
+
this.loggerService.log("strategyConnectionService getStopped", {
|
|
3285
|
+
symbol,
|
|
3286
|
+
strategyName,
|
|
3287
|
+
});
|
|
3288
|
+
const strategy = this.getStrategy(symbol, strategyName);
|
|
3289
|
+
return await strategy.getStopped(symbol, strategyName);
|
|
3290
|
+
};
|
|
3248
3291
|
/**
|
|
3249
3292
|
* Executes live trading tick for current strategy.
|
|
3250
3293
|
*
|
|
@@ -3312,12 +3355,12 @@ class StrategyConnectionService {
|
|
|
3312
3355
|
* @param strategyName - Name of strategy to stop
|
|
3313
3356
|
* @returns Promise that resolves when stop flag is set
|
|
3314
3357
|
*/
|
|
3315
|
-
this.stop = async (ctx) => {
|
|
3358
|
+
this.stop = async (ctx, backtest) => {
|
|
3316
3359
|
this.loggerService.log("strategyConnectionService stop", {
|
|
3317
3360
|
ctx
|
|
3318
3361
|
});
|
|
3319
3362
|
const strategy = this.getStrategy(ctx.symbol, ctx.strategyName);
|
|
3320
|
-
await strategy.stop(ctx.symbol, ctx.strategyName);
|
|
3363
|
+
await strategy.stop(ctx.symbol, ctx.strategyName, backtest);
|
|
3321
3364
|
};
|
|
3322
3365
|
/**
|
|
3323
3366
|
* Clears the memoized ClientStrategy instance from cache.
|
|
@@ -4182,6 +4225,24 @@ class StrategyGlobalService {
|
|
|
4182
4225
|
await this.validate(symbol, strategyName);
|
|
4183
4226
|
return await this.strategyConnectionService.getPendingSignal(symbol, strategyName);
|
|
4184
4227
|
};
|
|
4228
|
+
/**
|
|
4229
|
+
* Checks if the strategy has been stopped.
|
|
4230
|
+
*
|
|
4231
|
+
* Validates strategy existence and delegates to connection service
|
|
4232
|
+
* to retrieve the stopped state from the strategy instance.
|
|
4233
|
+
*
|
|
4234
|
+
* @param symbol - Trading pair symbol
|
|
4235
|
+
* @param strategyName - Name of the strategy
|
|
4236
|
+
* @returns Promise resolving to true if strategy is stopped, false otherwise
|
|
4237
|
+
*/
|
|
4238
|
+
this.getStopped = async (symbol, strategyName) => {
|
|
4239
|
+
this.loggerService.log("strategyGlobalService getStopped", {
|
|
4240
|
+
symbol,
|
|
4241
|
+
strategyName,
|
|
4242
|
+
});
|
|
4243
|
+
await this.validate(symbol, strategyName);
|
|
4244
|
+
return await this.strategyConnectionService.getStopped(symbol, strategyName);
|
|
4245
|
+
};
|
|
4185
4246
|
/**
|
|
4186
4247
|
* Checks signal status at a specific timestamp.
|
|
4187
4248
|
*
|
|
@@ -4248,12 +4309,13 @@ class StrategyGlobalService {
|
|
|
4248
4309
|
* @param strategyName - Name of strategy to stop
|
|
4249
4310
|
* @returns Promise that resolves when stop flag is set
|
|
4250
4311
|
*/
|
|
4251
|
-
this.stop = async (ctx) => {
|
|
4312
|
+
this.stop = async (ctx, backtest) => {
|
|
4252
4313
|
this.loggerService.log("strategyGlobalService stop", {
|
|
4253
4314
|
ctx,
|
|
4315
|
+
backtest,
|
|
4254
4316
|
});
|
|
4255
4317
|
await this.validate(ctx.symbol, ctx.strategyName);
|
|
4256
|
-
return await this.strategyConnectionService.stop(ctx);
|
|
4318
|
+
return await this.strategyConnectionService.stop(ctx, backtest);
|
|
4257
4319
|
};
|
|
4258
4320
|
/**
|
|
4259
4321
|
* Clears the memoized ClientStrategy instance from cache.
|
|
@@ -4945,6 +5007,16 @@ class BacktestLogicPrivateService {
|
|
|
4945
5007
|
progress: totalFrames > 0 ? i / totalFrames : 0,
|
|
4946
5008
|
});
|
|
4947
5009
|
}
|
|
5010
|
+
// Check if strategy should stop before processing next frame
|
|
5011
|
+
if (await this.strategyGlobalService.getStopped(symbol, this.methodContextService.context.strategyName)) {
|
|
5012
|
+
this.loggerService.info("backtestLogicPrivateService stopped by user request (before tick)", {
|
|
5013
|
+
symbol,
|
|
5014
|
+
when: when.toISOString(),
|
|
5015
|
+
processedFrames: i,
|
|
5016
|
+
totalFrames,
|
|
5017
|
+
});
|
|
5018
|
+
break;
|
|
5019
|
+
}
|
|
4948
5020
|
let result;
|
|
4949
5021
|
try {
|
|
4950
5022
|
result = await this.strategyGlobalService.tick(symbol, when, true);
|
|
@@ -4960,6 +5032,16 @@ class BacktestLogicPrivateService {
|
|
|
4960
5032
|
i++;
|
|
4961
5033
|
continue;
|
|
4962
5034
|
}
|
|
5035
|
+
// Check if strategy should stop when idle (no active signal)
|
|
5036
|
+
if (await functoolsKit.and(Promise.resolve(result.action === "idle"), this.strategyGlobalService.getStopped(symbol, this.methodContextService.context.strategyName))) {
|
|
5037
|
+
this.loggerService.info("backtestLogicPrivateService stopped by user request (idle state)", {
|
|
5038
|
+
symbol,
|
|
5039
|
+
when: when.toISOString(),
|
|
5040
|
+
processedFrames: i,
|
|
5041
|
+
totalFrames,
|
|
5042
|
+
});
|
|
5043
|
+
break;
|
|
5044
|
+
}
|
|
4963
5045
|
// Если scheduled signal создан - обрабатываем через backtest()
|
|
4964
5046
|
if (result.action === "scheduled") {
|
|
4965
5047
|
const signalStartTime = performance.now();
|
|
@@ -5052,6 +5134,16 @@ class BacktestLogicPrivateService {
|
|
|
5052
5134
|
i++;
|
|
5053
5135
|
}
|
|
5054
5136
|
yield backtestResult;
|
|
5137
|
+
// Check if strategy should stop after signal is closed
|
|
5138
|
+
if (await this.strategyGlobalService.getStopped(symbol, this.methodContextService.context.strategyName)) {
|
|
5139
|
+
this.loggerService.info("backtestLogicPrivateService stopped by user request (after scheduled signal closed)", {
|
|
5140
|
+
symbol,
|
|
5141
|
+
signalId: backtestResult.signal.id,
|
|
5142
|
+
processedFrames: i,
|
|
5143
|
+
totalFrames,
|
|
5144
|
+
});
|
|
5145
|
+
break;
|
|
5146
|
+
}
|
|
5055
5147
|
}
|
|
5056
5148
|
// Если обычный сигнал открыт, вызываем backtest
|
|
5057
5149
|
if (result.action === "opened") {
|
|
@@ -5135,6 +5227,16 @@ class BacktestLogicPrivateService {
|
|
|
5135
5227
|
i++;
|
|
5136
5228
|
}
|
|
5137
5229
|
yield backtestResult;
|
|
5230
|
+
// Check if strategy should stop after signal is closed
|
|
5231
|
+
if (await this.strategyGlobalService.getStopped(symbol, this.methodContextService.context.strategyName)) {
|
|
5232
|
+
this.loggerService.info("backtestLogicPrivateService stopped by user request (after signal closed)", {
|
|
5233
|
+
symbol,
|
|
5234
|
+
signalId: backtestResult.signal.id,
|
|
5235
|
+
processedFrames: i,
|
|
5236
|
+
totalFrames,
|
|
5237
|
+
});
|
|
5238
|
+
break;
|
|
5239
|
+
}
|
|
5138
5240
|
}
|
|
5139
5241
|
// Track timeframe processing duration
|
|
5140
5242
|
const timeframeEndTime = performance.now();
|
|
@@ -5242,7 +5344,8 @@ class LiveLogicPrivateService {
|
|
|
5242
5344
|
this.loggerService.warn("liveLogicPrivateService tick failed, retrying after sleep", {
|
|
5243
5345
|
symbol,
|
|
5244
5346
|
when: when.toISOString(),
|
|
5245
|
-
error: functoolsKit.errorData(error),
|
|
5347
|
+
error: functoolsKit.errorData(error),
|
|
5348
|
+
message: functoolsKit.getErrorMessage(error),
|
|
5246
5349
|
});
|
|
5247
5350
|
await errorEmitter.next(error);
|
|
5248
5351
|
await functoolsKit.sleep(TICK_TTL);
|
|
@@ -5266,11 +5369,19 @@ class LiveLogicPrivateService {
|
|
|
5266
5369
|
backtest: false,
|
|
5267
5370
|
});
|
|
5268
5371
|
previousEventTimestamp = currentTimestamp;
|
|
5269
|
-
if (
|
|
5372
|
+
// Check if strategy should stop when idle (no active signal)
|
|
5373
|
+
if (result.action === "idle") {
|
|
5374
|
+
if (await functoolsKit.and(Promise.resolve(true), this.strategyGlobalService.getStopped(symbol, this.methodContextService.context.strategyName))) {
|
|
5375
|
+
this.loggerService.info("liveLogicPrivateService stopped by user request (idle state)", {
|
|
5376
|
+
symbol,
|
|
5377
|
+
when: when.toISOString(),
|
|
5378
|
+
});
|
|
5379
|
+
break;
|
|
5380
|
+
}
|
|
5270
5381
|
await functoolsKit.sleep(TICK_TTL);
|
|
5271
5382
|
continue;
|
|
5272
5383
|
}
|
|
5273
|
-
if (result.action === "
|
|
5384
|
+
if (result.action === "active") {
|
|
5274
5385
|
await functoolsKit.sleep(TICK_TTL);
|
|
5275
5386
|
continue;
|
|
5276
5387
|
}
|
|
@@ -5280,12 +5391,21 @@ class LiveLogicPrivateService {
|
|
|
5280
5391
|
}
|
|
5281
5392
|
// Yield opened, closed results
|
|
5282
5393
|
yield result;
|
|
5394
|
+
// Check if strategy should stop after signal is closed
|
|
5395
|
+
if (result.action === "closed") {
|
|
5396
|
+
if (await this.strategyGlobalService.getStopped(symbol, this.methodContextService.context.strategyName)) {
|
|
5397
|
+
this.loggerService.info("liveLogicPrivateService stopped by user request (after signal closed)", {
|
|
5398
|
+
symbol,
|
|
5399
|
+
signalId: result.signal.id,
|
|
5400
|
+
});
|
|
5401
|
+
break;
|
|
5402
|
+
}
|
|
5403
|
+
}
|
|
5283
5404
|
await functoolsKit.sleep(TICK_TTL);
|
|
5284
5405
|
}
|
|
5285
5406
|
}
|
|
5286
5407
|
}
|
|
5287
5408
|
|
|
5288
|
-
const CANCEL_SYMBOL = Symbol("CANCEL_SYMBOL");
|
|
5289
5409
|
/**
|
|
5290
5410
|
* Private service for walker orchestration (strategy comparison).
|
|
5291
5411
|
*
|
|
@@ -5343,112 +5463,121 @@ class WalkerLogicPrivateService {
|
|
|
5343
5463
|
let strategiesTested = 0;
|
|
5344
5464
|
let bestMetric = null;
|
|
5345
5465
|
let bestStrategy = null;
|
|
5346
|
-
|
|
5347
|
-
const
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
.
|
|
5355
|
-
.
|
|
5356
|
-
// Run backtest for each strategy
|
|
5357
|
-
for (const strategyName of strategies) {
|
|
5358
|
-
// Call onStrategyStart callback if provided
|
|
5359
|
-
if (walkerSchema.callbacks?.onStrategyStart) {
|
|
5360
|
-
walkerSchema.callbacks.onStrategyStart(strategyName, symbol);
|
|
5361
|
-
}
|
|
5362
|
-
this.loggerService.info("walkerLogicPrivateService testing strategy", {
|
|
5363
|
-
strategyName,
|
|
5466
|
+
// Track stopped strategies in Set for efficient lookup
|
|
5467
|
+
const stoppedStrategies = new Set();
|
|
5468
|
+
// Subscribe to stop signals and collect them in Set
|
|
5469
|
+
// Filter by both symbol AND walkerName to support multiple walkers on same symbol
|
|
5470
|
+
// connect() returns destructor function
|
|
5471
|
+
const unsubscribe = walkerStopSubject
|
|
5472
|
+
.filter((data) => data.symbol === symbol && data.walkerName === context.walkerName)
|
|
5473
|
+
.connect((data) => {
|
|
5474
|
+
stoppedStrategies.add(data.strategyName);
|
|
5475
|
+
this.loggerService.info("walkerLogicPrivateService received stop signal for strategy", {
|
|
5364
5476
|
symbol,
|
|
5477
|
+
walkerName: context.walkerName,
|
|
5478
|
+
strategyName: data.strategyName,
|
|
5479
|
+
stoppedCount: stoppedStrategies.size,
|
|
5365
5480
|
});
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5481
|
+
});
|
|
5482
|
+
try {
|
|
5483
|
+
// Run backtest for each strategy
|
|
5484
|
+
for (const strategyName of strategies) {
|
|
5485
|
+
// Check if this strategy should be stopped before starting
|
|
5486
|
+
if (stoppedStrategies.has(strategyName)) {
|
|
5487
|
+
this.loggerService.info("walkerLogicPrivateService skipping stopped strategy", {
|
|
5488
|
+
symbol,
|
|
5489
|
+
strategyName,
|
|
5490
|
+
});
|
|
5491
|
+
break;
|
|
5492
|
+
}
|
|
5493
|
+
// Call onStrategyStart callback if provided
|
|
5494
|
+
if (walkerSchema.callbacks?.onStrategyStart) {
|
|
5495
|
+
walkerSchema.callbacks.onStrategyStart(strategyName, symbol);
|
|
5496
|
+
}
|
|
5497
|
+
this.loggerService.info("walkerLogicPrivateService testing strategy", {
|
|
5382
5498
|
strategyName,
|
|
5383
5499
|
symbol,
|
|
5384
|
-
error: functoolsKit.errorData(error), message: functoolsKit.getErrorMessage(error),
|
|
5385
5500
|
});
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5501
|
+
const iterator = this.backtestLogicPublicService.run(symbol, {
|
|
5502
|
+
strategyName,
|
|
5503
|
+
exchangeName: context.exchangeName,
|
|
5504
|
+
frameName: context.frameName,
|
|
5505
|
+
});
|
|
5506
|
+
try {
|
|
5507
|
+
await functoolsKit.resolveDocuments(iterator);
|
|
5390
5508
|
}
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5509
|
+
catch (error) {
|
|
5510
|
+
console.warn(`walkerLogicPrivateService backtest failed symbol=${symbol} strategyName=${strategyName} exchangeName=${context.exchangeName}`);
|
|
5511
|
+
this.loggerService.warn("walkerLogicPrivateService backtest failed for strategy, skipping", {
|
|
5512
|
+
strategyName,
|
|
5513
|
+
symbol,
|
|
5514
|
+
error: functoolsKit.errorData(error), message: functoolsKit.getErrorMessage(error),
|
|
5515
|
+
});
|
|
5516
|
+
await errorEmitter.next(error);
|
|
5517
|
+
// Call onStrategyError callback if provided
|
|
5518
|
+
if (walkerSchema.callbacks?.onStrategyError) {
|
|
5519
|
+
walkerSchema.callbacks.onStrategyError(strategyName, symbol, error);
|
|
5520
|
+
}
|
|
5521
|
+
continue;
|
|
5522
|
+
}
|
|
5523
|
+
this.loggerService.info("walkerLogicPrivateService backtest complete", {
|
|
5524
|
+
strategyName,
|
|
5525
|
+
symbol,
|
|
5396
5526
|
});
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
// Call onStrategyComplete callback if provided
|
|
5447
|
-
if (walkerSchema.callbacks?.onStrategyComplete) {
|
|
5448
|
-
walkerSchema.callbacks.onStrategyComplete(strategyName, symbol, stats, metricValue);
|
|
5527
|
+
// Get statistics from BacktestMarkdownService
|
|
5528
|
+
const stats = await this.backtestMarkdownService.getData(symbol, strategyName);
|
|
5529
|
+
// Extract metric value
|
|
5530
|
+
const value = stats[metric];
|
|
5531
|
+
const metricValue = value !== null &&
|
|
5532
|
+
value !== undefined &&
|
|
5533
|
+
typeof value === "number" &&
|
|
5534
|
+
!isNaN(value) &&
|
|
5535
|
+
isFinite(value)
|
|
5536
|
+
? value
|
|
5537
|
+
: null;
|
|
5538
|
+
// Update best strategy if needed
|
|
5539
|
+
const isBetter = bestMetric === null ||
|
|
5540
|
+
(metricValue !== null && metricValue > bestMetric);
|
|
5541
|
+
if (isBetter && metricValue !== null) {
|
|
5542
|
+
bestMetric = metricValue;
|
|
5543
|
+
bestStrategy = strategyName;
|
|
5544
|
+
}
|
|
5545
|
+
strategiesTested++;
|
|
5546
|
+
const walkerContract = {
|
|
5547
|
+
walkerName: context.walkerName,
|
|
5548
|
+
exchangeName: context.exchangeName,
|
|
5549
|
+
frameName: context.frameName,
|
|
5550
|
+
symbol,
|
|
5551
|
+
strategyName,
|
|
5552
|
+
stats,
|
|
5553
|
+
metricValue,
|
|
5554
|
+
metric,
|
|
5555
|
+
bestMetric,
|
|
5556
|
+
bestStrategy,
|
|
5557
|
+
strategiesTested,
|
|
5558
|
+
totalStrategies: strategies.length,
|
|
5559
|
+
};
|
|
5560
|
+
// Emit progress event
|
|
5561
|
+
await progressWalkerEmitter.next({
|
|
5562
|
+
walkerName: context.walkerName,
|
|
5563
|
+
exchangeName: context.exchangeName,
|
|
5564
|
+
frameName: context.frameName,
|
|
5565
|
+
symbol,
|
|
5566
|
+
totalStrategies: strategies.length,
|
|
5567
|
+
processedStrategies: strategiesTested,
|
|
5568
|
+
progress: strategies.length > 0 ? strategiesTested / strategies.length : 0,
|
|
5569
|
+
});
|
|
5570
|
+
// Call onStrategyComplete callback if provided
|
|
5571
|
+
if (walkerSchema.callbacks?.onStrategyComplete) {
|
|
5572
|
+
await walkerSchema.callbacks.onStrategyComplete(strategyName, symbol, stats, metricValue);
|
|
5573
|
+
}
|
|
5574
|
+
await walkerEmitter.next(walkerContract);
|
|
5575
|
+
yield walkerContract;
|
|
5449
5576
|
}
|
|
5450
|
-
|
|
5451
|
-
|
|
5577
|
+
}
|
|
5578
|
+
finally {
|
|
5579
|
+
// Unsubscribe from stop signals by calling destructor
|
|
5580
|
+
unsubscribe();
|
|
5452
5581
|
}
|
|
5453
5582
|
const finalResults = {
|
|
5454
5583
|
walkerName: context.walkerName,
|
|
@@ -13270,19 +13399,53 @@ async function dumpSignal(signalId, history, signal, outputDir = "./dump/strateg
|
|
|
13270
13399
|
|
|
13271
13400
|
const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
|
|
13272
13401
|
const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
|
|
13402
|
+
const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
|
|
13273
13403
|
const BACKTEST_METHOD_NAME_GET_REPORT = "BacktestUtils.getReport";
|
|
13274
13404
|
const BACKTEST_METHOD_NAME_DUMP = "BacktestUtils.dump";
|
|
13405
|
+
const BACKTEST_METHOD_NAME_TASK = "BacktestUtils.task";
|
|
13406
|
+
const BACKTEST_METHOD_NAME_GET_STATUS = "BacktestUtils.getStatus";
|
|
13275
13407
|
/**
|
|
13276
|
-
*
|
|
13408
|
+
* Internal task function that runs backtest and handles completion.
|
|
13409
|
+
* Consumes backtest results and updates instance state flags.
|
|
13277
13410
|
*
|
|
13278
|
-
*
|
|
13279
|
-
*
|
|
13411
|
+
* @param symbol - Trading pair symbol
|
|
13412
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
13413
|
+
* @param self - BacktestInstance reference for state management
|
|
13414
|
+
* @returns Promise that resolves when backtest completes
|
|
13415
|
+
*
|
|
13416
|
+
* @internal
|
|
13417
|
+
*/
|
|
13418
|
+
const INSTANCE_TASK_FN$2 = async (symbol, context, self) => {
|
|
13419
|
+
{
|
|
13420
|
+
self._isStopped = false;
|
|
13421
|
+
self._isDone = false;
|
|
13422
|
+
}
|
|
13423
|
+
for await (const _ of self.run(symbol, context)) {
|
|
13424
|
+
if (self._isStopped) {
|
|
13425
|
+
break;
|
|
13426
|
+
}
|
|
13427
|
+
}
|
|
13428
|
+
if (!self._isDone) {
|
|
13429
|
+
await doneBacktestSubject.next({
|
|
13430
|
+
exchangeName: context.exchangeName,
|
|
13431
|
+
strategyName: context.strategyName,
|
|
13432
|
+
backtest: true,
|
|
13433
|
+
symbol,
|
|
13434
|
+
});
|
|
13435
|
+
}
|
|
13436
|
+
self._isDone = true;
|
|
13437
|
+
};
|
|
13438
|
+
/**
|
|
13439
|
+
* Instance class for backtest operations on a specific symbol-strategy pair.
|
|
13440
|
+
*
|
|
13441
|
+
* Provides isolated backtest execution and reporting for a single symbol-strategy combination.
|
|
13442
|
+
* Each instance maintains its own state and context.
|
|
13280
13443
|
*
|
|
13281
13444
|
* @example
|
|
13282
13445
|
* ```typescript
|
|
13283
|
-
*
|
|
13446
|
+
* const instance = new BacktestInstance("BTCUSDT", "my-strategy");
|
|
13284
13447
|
*
|
|
13285
|
-
* for await (const result of
|
|
13448
|
+
* for await (const result of instance.run("BTCUSDT", {
|
|
13286
13449
|
* strategyName: "my-strategy",
|
|
13287
13450
|
* exchangeName: "my-exchange",
|
|
13288
13451
|
* frameName: "1d-backtest"
|
|
@@ -13291,8 +13454,57 @@ const BACKTEST_METHOD_NAME_DUMP = "BacktestUtils.dump";
|
|
|
13291
13454
|
* }
|
|
13292
13455
|
* ```
|
|
13293
13456
|
*/
|
|
13294
|
-
class
|
|
13295
|
-
|
|
13457
|
+
class BacktestInstance {
|
|
13458
|
+
/**
|
|
13459
|
+
* Creates a new BacktestInstance for a specific symbol-strategy pair.
|
|
13460
|
+
*
|
|
13461
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
13462
|
+
* @param strategyName - Strategy name for this backtest instance
|
|
13463
|
+
*/
|
|
13464
|
+
constructor(symbol, strategyName) {
|
|
13465
|
+
this.symbol = symbol;
|
|
13466
|
+
this.strategyName = strategyName;
|
|
13467
|
+
/** Internal flag indicating if backtest was stopped manually */
|
|
13468
|
+
this._isStopped = false;
|
|
13469
|
+
/** Internal flag indicating if backtest task completed */
|
|
13470
|
+
this._isDone = false;
|
|
13471
|
+
/**
|
|
13472
|
+
* Internal singlerun task that executes the backtest.
|
|
13473
|
+
* Ensures only one backtest run per instance using singlerun wrapper.
|
|
13474
|
+
*
|
|
13475
|
+
* @param symbol - Trading pair symbol
|
|
13476
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
13477
|
+
* @returns Promise that resolves when backtest completes
|
|
13478
|
+
*
|
|
13479
|
+
* @internal
|
|
13480
|
+
*/
|
|
13481
|
+
this.task = functoolsKit.singlerun(async (symbol, context) => {
|
|
13482
|
+
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_TASK, {
|
|
13483
|
+
symbol,
|
|
13484
|
+
context,
|
|
13485
|
+
});
|
|
13486
|
+
return await INSTANCE_TASK_FN$2(symbol, context, this);
|
|
13487
|
+
});
|
|
13488
|
+
/**
|
|
13489
|
+
* Gets the current status of this backtest instance.
|
|
13490
|
+
*
|
|
13491
|
+
* @returns Promise resolving to status object with symbol, strategyName, and task status
|
|
13492
|
+
*
|
|
13493
|
+
* @example
|
|
13494
|
+
* ```typescript
|
|
13495
|
+
* const instance = new BacktestInstance("BTCUSDT", "my-strategy");
|
|
13496
|
+
* const status = await instance.getStatus();
|
|
13497
|
+
* console.log(status.status); // "idle", "running", or "done"
|
|
13498
|
+
* ```
|
|
13499
|
+
*/
|
|
13500
|
+
this.getStatus = async () => {
|
|
13501
|
+
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_GET_STATUS);
|
|
13502
|
+
return {
|
|
13503
|
+
symbol: this.symbol,
|
|
13504
|
+
strategyName: this.strategyName,
|
|
13505
|
+
status: this.task.getStatus(),
|
|
13506
|
+
};
|
|
13507
|
+
};
|
|
13296
13508
|
/**
|
|
13297
13509
|
* Runs backtest for a symbol with context propagation.
|
|
13298
13510
|
*
|
|
@@ -13330,13 +13542,12 @@ class BacktestUtils {
|
|
|
13330
13542
|
*
|
|
13331
13543
|
* @example
|
|
13332
13544
|
* ```typescript
|
|
13333
|
-
*
|
|
13334
|
-
*
|
|
13545
|
+
* const instance = new BacktestInstance();
|
|
13546
|
+
* const cancel = instance.background("BTCUSDT", {
|
|
13335
13547
|
* strategyName: "my-strategy",
|
|
13336
13548
|
* exchangeName: "my-exchange",
|
|
13337
13549
|
* frameName: "1d-backtest"
|
|
13338
13550
|
* });
|
|
13339
|
-
* console.log("Backtest completed");
|
|
13340
13551
|
* ```
|
|
13341
13552
|
*/
|
|
13342
13553
|
this.background = (symbol, context) => {
|
|
@@ -13344,34 +13555,16 @@ class BacktestUtils {
|
|
|
13344
13555
|
symbol,
|
|
13345
13556
|
context,
|
|
13346
13557
|
});
|
|
13347
|
-
|
|
13348
|
-
let isDone = false;
|
|
13349
|
-
const task = async () => {
|
|
13350
|
-
for await (const _ of this.run(symbol, context)) {
|
|
13351
|
-
if (isStopped) {
|
|
13352
|
-
break;
|
|
13353
|
-
}
|
|
13354
|
-
}
|
|
13355
|
-
if (!isDone) {
|
|
13356
|
-
await doneBacktestSubject.next({
|
|
13357
|
-
exchangeName: context.exchangeName,
|
|
13358
|
-
strategyName: context.strategyName,
|
|
13359
|
-
backtest: true,
|
|
13360
|
-
symbol,
|
|
13361
|
-
});
|
|
13362
|
-
}
|
|
13363
|
-
isDone = true;
|
|
13364
|
-
};
|
|
13365
|
-
task().catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
|
|
13558
|
+
this.task(symbol, context).catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
|
|
13366
13559
|
return () => {
|
|
13367
|
-
backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName });
|
|
13560
|
+
backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName }, true);
|
|
13368
13561
|
backtest$1.strategyGlobalService
|
|
13369
13562
|
.getPendingSignal(symbol, context.strategyName)
|
|
13370
13563
|
.then(async (pendingSignal) => {
|
|
13371
13564
|
if (pendingSignal) {
|
|
13372
13565
|
return;
|
|
13373
13566
|
}
|
|
13374
|
-
if (!
|
|
13567
|
+
if (!this._isDone) {
|
|
13375
13568
|
await doneBacktestSubject.next({
|
|
13376
13569
|
exchangeName: context.exchangeName,
|
|
13377
13570
|
strategyName: context.strategyName,
|
|
@@ -13379,11 +13572,35 @@ class BacktestUtils {
|
|
|
13379
13572
|
symbol,
|
|
13380
13573
|
});
|
|
13381
13574
|
}
|
|
13382
|
-
|
|
13575
|
+
this._isDone = true;
|
|
13383
13576
|
});
|
|
13384
|
-
|
|
13577
|
+
this._isStopped = true;
|
|
13385
13578
|
};
|
|
13386
13579
|
};
|
|
13580
|
+
/**
|
|
13581
|
+
* Stops the strategy from generating new signals.
|
|
13582
|
+
*
|
|
13583
|
+
* Sets internal flag to prevent strategy from opening new signals.
|
|
13584
|
+
* Current active signal (if any) will complete normally.
|
|
13585
|
+
* Backtest will stop at the next safe point (idle state or after signal closes).
|
|
13586
|
+
*
|
|
13587
|
+
* @param symbol - Trading pair symbol
|
|
13588
|
+
* @param strategyName - Strategy name to stop
|
|
13589
|
+
* @returns Promise that resolves when stop flag is set
|
|
13590
|
+
*
|
|
13591
|
+
* @example
|
|
13592
|
+
* ```typescript
|
|
13593
|
+
* const instance = new BacktestInstance();
|
|
13594
|
+
* await instance.stop("BTCUSDT", "my-strategy");
|
|
13595
|
+
* ```
|
|
13596
|
+
*/
|
|
13597
|
+
this.stop = async (symbol, strategyName) => {
|
|
13598
|
+
backtest$1.loggerService.info(BACKTEST_METHOD_NAME_STOP, {
|
|
13599
|
+
symbol,
|
|
13600
|
+
strategyName,
|
|
13601
|
+
});
|
|
13602
|
+
await backtest$1.strategyGlobalService.stop({ symbol, strategyName }, true);
|
|
13603
|
+
};
|
|
13387
13604
|
/**
|
|
13388
13605
|
* Gets statistical data from all closed signals for a symbol-strategy pair.
|
|
13389
13606
|
*
|
|
@@ -13393,7 +13610,8 @@ class BacktestUtils {
|
|
|
13393
13610
|
*
|
|
13394
13611
|
* @example
|
|
13395
13612
|
* ```typescript
|
|
13396
|
-
* const
|
|
13613
|
+
* const instance = new BacktestInstance();
|
|
13614
|
+
* const stats = await instance.getData("BTCUSDT", "my-strategy");
|
|
13397
13615
|
* console.log(stats.sharpeRatio, stats.winRate);
|
|
13398
13616
|
* ```
|
|
13399
13617
|
*/
|
|
@@ -13413,7 +13631,8 @@ class BacktestUtils {
|
|
|
13413
13631
|
*
|
|
13414
13632
|
* @example
|
|
13415
13633
|
* ```typescript
|
|
13416
|
-
* const
|
|
13634
|
+
* const instance = new BacktestInstance();
|
|
13635
|
+
* const markdown = await instance.getReport("BTCUSDT", "my-strategy");
|
|
13417
13636
|
* console.log(markdown);
|
|
13418
13637
|
* ```
|
|
13419
13638
|
*/
|
|
@@ -13433,11 +13652,12 @@ class BacktestUtils {
|
|
|
13433
13652
|
*
|
|
13434
13653
|
* @example
|
|
13435
13654
|
* ```typescript
|
|
13655
|
+
* const instance = new BacktestInstance();
|
|
13436
13656
|
* // Save to default path: ./dump/backtest/my-strategy.md
|
|
13437
|
-
* await
|
|
13657
|
+
* await instance.dump("BTCUSDT", "my-strategy");
|
|
13438
13658
|
*
|
|
13439
13659
|
* // Save to custom path: ./custom/path/my-strategy.md
|
|
13440
|
-
* await
|
|
13660
|
+
* await instance.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
13441
13661
|
* ```
|
|
13442
13662
|
*/
|
|
13443
13663
|
this.dump = async (symbol, strategyName, path) => {
|
|
@@ -13451,7 +13671,10 @@ class BacktestUtils {
|
|
|
13451
13671
|
}
|
|
13452
13672
|
}
|
|
13453
13673
|
/**
|
|
13454
|
-
*
|
|
13674
|
+
* Utility class for backtest operations.
|
|
13675
|
+
*
|
|
13676
|
+
* Provides simplified access to backtestCommandService.run() with logging.
|
|
13677
|
+
* Exported as singleton instance for convenient usage.
|
|
13455
13678
|
*
|
|
13456
13679
|
* @example
|
|
13457
13680
|
* ```typescript
|
|
@@ -13462,49 +13685,276 @@ class BacktestUtils {
|
|
|
13462
13685
|
* exchangeName: "my-exchange",
|
|
13463
13686
|
* frameName: "1d-backtest"
|
|
13464
13687
|
* })) {
|
|
13465
|
-
*
|
|
13466
|
-
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
13467
|
-
* }
|
|
13688
|
+
* console.log("Closed signal PNL:", result.pnl.pnlPercentage);
|
|
13468
13689
|
* }
|
|
13469
13690
|
* ```
|
|
13470
13691
|
*/
|
|
13471
|
-
|
|
13472
|
-
|
|
13473
|
-
const LIVE_METHOD_NAME_RUN = "LiveUtils.run";
|
|
13474
|
-
const LIVE_METHOD_NAME_BACKGROUND = "LiveUtils.background";
|
|
13475
|
-
const LIVE_METHOD_NAME_GET_REPORT = "LiveUtils.getReport";
|
|
13476
|
-
const LIVE_METHOD_NAME_DUMP = "LiveUtils.dump";
|
|
13477
|
-
/**
|
|
13478
|
-
* Utility class for live trading operations.
|
|
13479
|
-
*
|
|
13480
|
-
* Provides simplified access to liveCommandService.run() with logging.
|
|
13481
|
-
* Exported as singleton instance for convenient usage.
|
|
13482
|
-
*
|
|
13483
|
-
* Features:
|
|
13484
|
-
* - Infinite async generator (never completes)
|
|
13485
|
-
* - Crash recovery via persisted state
|
|
13486
|
-
* - Real-time progression with Date.now()
|
|
13487
|
-
*
|
|
13488
|
-
* @example
|
|
13489
|
-
* ```typescript
|
|
13490
|
-
* import { Live } from "./classes/Live";
|
|
13491
|
-
*
|
|
13492
|
-
* // Infinite loop - use Ctrl+C to stop
|
|
13493
|
-
* for await (const result of Live.run("BTCUSDT", {
|
|
13494
|
-
* strategyName: "my-strategy",
|
|
13495
|
-
* exchangeName: "my-exchange",
|
|
13496
|
-
* frameName: ""
|
|
13497
|
-
* })) {
|
|
13498
|
-
* if (result.action === "opened") {
|
|
13499
|
-
* console.log("Signal opened:", result.signal);
|
|
13500
|
-
* } else if (result.action === "closed") {
|
|
13501
|
-
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
13502
|
-
* }
|
|
13503
|
-
* }
|
|
13504
|
-
* ```
|
|
13505
|
-
*/
|
|
13506
|
-
class LiveUtils {
|
|
13692
|
+
class BacktestUtils {
|
|
13507
13693
|
constructor() {
|
|
13694
|
+
/**
|
|
13695
|
+
* Memoized function to get or create BacktestInstance for a symbol-strategy pair.
|
|
13696
|
+
* Each symbol-strategy combination gets its own isolated instance.
|
|
13697
|
+
*/
|
|
13698
|
+
this._getInstance = functoolsKit.memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, (symbol, strategyName) => new BacktestInstance(symbol, strategyName));
|
|
13699
|
+
/**
|
|
13700
|
+
* Runs backtest for a symbol with context propagation.
|
|
13701
|
+
*
|
|
13702
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
13703
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
13704
|
+
* @returns Async generator yielding closed signals with PNL
|
|
13705
|
+
*/
|
|
13706
|
+
this.run = (symbol, context) => {
|
|
13707
|
+
const instance = this._getInstance(symbol, context.strategyName);
|
|
13708
|
+
return instance.run(symbol, context);
|
|
13709
|
+
};
|
|
13710
|
+
/**
|
|
13711
|
+
* Runs backtest in background without yielding results.
|
|
13712
|
+
*
|
|
13713
|
+
* Consumes all backtest results internally without exposing them.
|
|
13714
|
+
* Useful for running backtests for side effects only (callbacks, logging).
|
|
13715
|
+
*
|
|
13716
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
13717
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
13718
|
+
* @returns Cancellation closure
|
|
13719
|
+
*
|
|
13720
|
+
* @example
|
|
13721
|
+
* ```typescript
|
|
13722
|
+
* // Run backtest silently, only callbacks will fire
|
|
13723
|
+
* await Backtest.background("BTCUSDT", {
|
|
13724
|
+
* strategyName: "my-strategy",
|
|
13725
|
+
* exchangeName: "my-exchange",
|
|
13726
|
+
* frameName: "1d-backtest"
|
|
13727
|
+
* });
|
|
13728
|
+
* console.log("Backtest completed");
|
|
13729
|
+
* ```
|
|
13730
|
+
*/
|
|
13731
|
+
this.background = (symbol, context) => {
|
|
13732
|
+
const instance = this._getInstance(symbol, context.strategyName);
|
|
13733
|
+
return instance.background(symbol, context);
|
|
13734
|
+
};
|
|
13735
|
+
/**
|
|
13736
|
+
* Stops the strategy from generating new signals.
|
|
13737
|
+
*
|
|
13738
|
+
* Sets internal flag to prevent strategy from opening new signals.
|
|
13739
|
+
* Current active signal (if any) will complete normally.
|
|
13740
|
+
* Backtest will stop at the next safe point (idle state or after signal closes).
|
|
13741
|
+
*
|
|
13742
|
+
* @param symbol - Trading pair symbol
|
|
13743
|
+
* @param strategyName - Strategy name to stop
|
|
13744
|
+
* @returns Promise that resolves when stop flag is set
|
|
13745
|
+
*
|
|
13746
|
+
* @example
|
|
13747
|
+
* ```typescript
|
|
13748
|
+
* // Stop strategy after some condition
|
|
13749
|
+
* await Backtest.stop("BTCUSDT", "my-strategy");
|
|
13750
|
+
* ```
|
|
13751
|
+
*/
|
|
13752
|
+
this.stop = async (symbol, strategyName) => {
|
|
13753
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
13754
|
+
return await instance.stop(symbol, strategyName);
|
|
13755
|
+
};
|
|
13756
|
+
/**
|
|
13757
|
+
* Gets statistical data from all closed signals for a symbol-strategy pair.
|
|
13758
|
+
*
|
|
13759
|
+
* @param symbol - Trading pair symbol
|
|
13760
|
+
* @param strategyName - Strategy name to get data for
|
|
13761
|
+
* @returns Promise resolving to statistical data object
|
|
13762
|
+
*
|
|
13763
|
+
* @example
|
|
13764
|
+
* ```typescript
|
|
13765
|
+
* const stats = await Backtest.getData("BTCUSDT", "my-strategy");
|
|
13766
|
+
* console.log(stats.sharpeRatio, stats.winRate);
|
|
13767
|
+
* ```
|
|
13768
|
+
*/
|
|
13769
|
+
this.getData = async (symbol, strategyName) => {
|
|
13770
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
13771
|
+
return await instance.getData(symbol, strategyName);
|
|
13772
|
+
};
|
|
13773
|
+
/**
|
|
13774
|
+
* Generates markdown report with all closed signals for a symbol-strategy pair.
|
|
13775
|
+
*
|
|
13776
|
+
* @param symbol - Trading pair symbol
|
|
13777
|
+
* @param strategyName - Strategy name to generate report for
|
|
13778
|
+
* @returns Promise resolving to markdown formatted report string
|
|
13779
|
+
*
|
|
13780
|
+
* @example
|
|
13781
|
+
* ```typescript
|
|
13782
|
+
* const markdown = await Backtest.getReport("BTCUSDT", "my-strategy");
|
|
13783
|
+
* console.log(markdown);
|
|
13784
|
+
* ```
|
|
13785
|
+
*/
|
|
13786
|
+
this.getReport = async (symbol, strategyName) => {
|
|
13787
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
13788
|
+
return await instance.getReport(symbol, strategyName);
|
|
13789
|
+
};
|
|
13790
|
+
/**
|
|
13791
|
+
* Saves strategy report to disk.
|
|
13792
|
+
*
|
|
13793
|
+
* @param symbol - Trading pair symbol
|
|
13794
|
+
* @param strategyName - Strategy name to save report for
|
|
13795
|
+
* @param path - Optional directory path to save report (default: "./dump/backtest")
|
|
13796
|
+
*
|
|
13797
|
+
* @example
|
|
13798
|
+
* ```typescript
|
|
13799
|
+
* // Save to default path: ./dump/backtest/my-strategy.md
|
|
13800
|
+
* await Backtest.dump("BTCUSDT", "my-strategy");
|
|
13801
|
+
*
|
|
13802
|
+
* // Save to custom path: ./custom/path/my-strategy.md
|
|
13803
|
+
* await Backtest.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
13804
|
+
* ```
|
|
13805
|
+
*/
|
|
13806
|
+
this.dump = async (symbol, strategyName, path) => {
|
|
13807
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
13808
|
+
return await instance.dump(symbol, strategyName, path);
|
|
13809
|
+
};
|
|
13810
|
+
/**
|
|
13811
|
+
* Lists all active backtest instances with their current status.
|
|
13812
|
+
*
|
|
13813
|
+
* @returns Promise resolving to array of status objects for all instances
|
|
13814
|
+
*
|
|
13815
|
+
* @example
|
|
13816
|
+
* ```typescript
|
|
13817
|
+
* const statusList = await Backtest.list();
|
|
13818
|
+
* statusList.forEach(status => {
|
|
13819
|
+
* console.log(`${status.symbol} - ${status.strategyName}: ${status.status}`);
|
|
13820
|
+
* });
|
|
13821
|
+
* ```
|
|
13822
|
+
*/
|
|
13823
|
+
this.list = async () => {
|
|
13824
|
+
const instanceList = this._getInstance.values();
|
|
13825
|
+
return await Promise.all(instanceList.map((instance) => instance.getStatus()));
|
|
13826
|
+
};
|
|
13827
|
+
}
|
|
13828
|
+
}
|
|
13829
|
+
/**
|
|
13830
|
+
* Singleton instance of BacktestUtils for convenient backtest operations.
|
|
13831
|
+
*
|
|
13832
|
+
* @example
|
|
13833
|
+
* ```typescript
|
|
13834
|
+
* import { Backtest } from "./classes/Backtest";
|
|
13835
|
+
*
|
|
13836
|
+
* for await (const result of Backtest.run("BTCUSDT", {
|
|
13837
|
+
* strategyName: "my-strategy",
|
|
13838
|
+
* exchangeName: "my-exchange",
|
|
13839
|
+
* frameName: "1d-backtest"
|
|
13840
|
+
* })) {
|
|
13841
|
+
* if (result.action === "closed") {
|
|
13842
|
+
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
13843
|
+
* }
|
|
13844
|
+
* }
|
|
13845
|
+
* ```
|
|
13846
|
+
*/
|
|
13847
|
+
const Backtest = new BacktestUtils();
|
|
13848
|
+
|
|
13849
|
+
const LIVE_METHOD_NAME_RUN = "LiveUtils.run";
|
|
13850
|
+
const LIVE_METHOD_NAME_BACKGROUND = "LiveUtils.background";
|
|
13851
|
+
const LIVE_METHOD_NAME_STOP = "LiveUtils.stop";
|
|
13852
|
+
const LIVE_METHOD_NAME_GET_REPORT = "LiveUtils.getReport";
|
|
13853
|
+
const LIVE_METHOD_NAME_DUMP = "LiveUtils.dump";
|
|
13854
|
+
const LIVE_METHOD_NAME_TASK = "LiveUtils.task";
|
|
13855
|
+
const LIVE_METHOD_NAME_GET_STATUS = "LiveUtils.getStatus";
|
|
13856
|
+
/**
|
|
13857
|
+
* Internal task function that runs live trading and handles completion.
|
|
13858
|
+
* Consumes live trading results and updates instance state flags.
|
|
13859
|
+
*
|
|
13860
|
+
* @param symbol - Trading pair symbol
|
|
13861
|
+
* @param context - Execution context with strategy and exchange names
|
|
13862
|
+
* @param self - LiveInstance reference for state management
|
|
13863
|
+
* @returns Promise that resolves when live trading completes
|
|
13864
|
+
*
|
|
13865
|
+
* @internal
|
|
13866
|
+
*/
|
|
13867
|
+
const INSTANCE_TASK_FN$1 = async (symbol, context, self) => {
|
|
13868
|
+
{
|
|
13869
|
+
self._isStopped = false;
|
|
13870
|
+
self._isDone = false;
|
|
13871
|
+
}
|
|
13872
|
+
for await (const signal of self.run(symbol, context)) {
|
|
13873
|
+
if (signal?.action === "closed" && self._isStopped) {
|
|
13874
|
+
break;
|
|
13875
|
+
}
|
|
13876
|
+
}
|
|
13877
|
+
if (!self._isDone) {
|
|
13878
|
+
await doneLiveSubject.next({
|
|
13879
|
+
exchangeName: context.exchangeName,
|
|
13880
|
+
strategyName: context.strategyName,
|
|
13881
|
+
backtest: false,
|
|
13882
|
+
symbol,
|
|
13883
|
+
});
|
|
13884
|
+
}
|
|
13885
|
+
self._isDone = true;
|
|
13886
|
+
};
|
|
13887
|
+
/**
|
|
13888
|
+
* Instance class for live trading operations on a specific symbol-strategy pair.
|
|
13889
|
+
*
|
|
13890
|
+
* Provides isolated live trading execution and reporting for a single symbol-strategy combination.
|
|
13891
|
+
* Each instance maintains its own state and context.
|
|
13892
|
+
*
|
|
13893
|
+
* @example
|
|
13894
|
+
* ```typescript
|
|
13895
|
+
* const instance = new LiveInstance("BTCUSDT", "my-strategy");
|
|
13896
|
+
*
|
|
13897
|
+
* for await (const result of instance.run("BTCUSDT", {
|
|
13898
|
+
* strategyName: "my-strategy",
|
|
13899
|
+
* exchangeName: "my-exchange"
|
|
13900
|
+
* })) {
|
|
13901
|
+
* if (result.action === "closed") {
|
|
13902
|
+
* console.log("Signal closed, PNL:", result.pnl.pnlPercentage);
|
|
13903
|
+
* }
|
|
13904
|
+
* }
|
|
13905
|
+
* ```
|
|
13906
|
+
*/
|
|
13907
|
+
class LiveInstance {
|
|
13908
|
+
/**
|
|
13909
|
+
* Creates a new LiveInstance for a specific symbol-strategy pair.
|
|
13910
|
+
*
|
|
13911
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
13912
|
+
* @param strategyName - Strategy name for this live trading instance
|
|
13913
|
+
*/
|
|
13914
|
+
constructor(symbol, strategyName) {
|
|
13915
|
+
this.symbol = symbol;
|
|
13916
|
+
this.strategyName = strategyName;
|
|
13917
|
+
/** Internal flag indicating if live trading was stopped manually */
|
|
13918
|
+
this._isStopped = false;
|
|
13919
|
+
/** Internal flag indicating if live trading task completed */
|
|
13920
|
+
this._isDone = false;
|
|
13921
|
+
/**
|
|
13922
|
+
* Internal singlerun task that executes the live trading.
|
|
13923
|
+
* Ensures only one live trading run per instance using singlerun wrapper.
|
|
13924
|
+
*
|
|
13925
|
+
* @param symbol - Trading pair symbol
|
|
13926
|
+
* @param context - Execution context with strategy and exchange names
|
|
13927
|
+
* @returns Promise that resolves when live trading completes
|
|
13928
|
+
*
|
|
13929
|
+
* @internal
|
|
13930
|
+
*/
|
|
13931
|
+
this.task = functoolsKit.singlerun(async (symbol, context) => {
|
|
13932
|
+
backtest$1.loggerService.info(LIVE_METHOD_NAME_TASK, {
|
|
13933
|
+
symbol,
|
|
13934
|
+
context,
|
|
13935
|
+
});
|
|
13936
|
+
return await INSTANCE_TASK_FN$1(symbol, context, this);
|
|
13937
|
+
});
|
|
13938
|
+
/**
|
|
13939
|
+
* Gets the current status of this live trading instance.
|
|
13940
|
+
*
|
|
13941
|
+
* @returns Promise resolving to status object with symbol, strategyName, and task status
|
|
13942
|
+
*
|
|
13943
|
+
* @example
|
|
13944
|
+
* ```typescript
|
|
13945
|
+
* const instance = new LiveInstance("BTCUSDT", "my-strategy");
|
|
13946
|
+
* const status = await instance.getStatus();
|
|
13947
|
+
* console.log(status.status); // "idle", "running", or "done"
|
|
13948
|
+
* ```
|
|
13949
|
+
*/
|
|
13950
|
+
this.getStatus = async () => {
|
|
13951
|
+
backtest$1.loggerService.info(LIVE_METHOD_NAME_GET_STATUS);
|
|
13952
|
+
return {
|
|
13953
|
+
symbol: this.symbol,
|
|
13954
|
+
strategyName: this.strategyName,
|
|
13955
|
+
status: this.task.getStatus(),
|
|
13956
|
+
};
|
|
13957
|
+
};
|
|
13508
13958
|
/**
|
|
13509
13959
|
* Runs live trading for a symbol with context propagation.
|
|
13510
13960
|
*
|
|
@@ -13546,9 +13996,8 @@ class LiveUtils {
|
|
|
13546
13996
|
*
|
|
13547
13997
|
* @example
|
|
13548
13998
|
* ```typescript
|
|
13549
|
-
*
|
|
13550
|
-
*
|
|
13551
|
-
* await Live.background("BTCUSDT", {
|
|
13999
|
+
* const instance = new LiveInstance();
|
|
14000
|
+
* const cancel = instance.background("BTCUSDT", {
|
|
13552
14001
|
* strategyName: "my-strategy",
|
|
13553
14002
|
* exchangeName: "my-exchange"
|
|
13554
14003
|
* });
|
|
@@ -13559,34 +14008,16 @@ class LiveUtils {
|
|
|
13559
14008
|
symbol,
|
|
13560
14009
|
context,
|
|
13561
14010
|
});
|
|
13562
|
-
|
|
13563
|
-
let isDone = false;
|
|
13564
|
-
const task = async () => {
|
|
13565
|
-
for await (const signal of this.run(symbol, context)) {
|
|
13566
|
-
if (signal?.action === "closed" && isStopped) {
|
|
13567
|
-
break;
|
|
13568
|
-
}
|
|
13569
|
-
}
|
|
13570
|
-
if (!isDone) {
|
|
13571
|
-
await doneLiveSubject.next({
|
|
13572
|
-
exchangeName: context.exchangeName,
|
|
13573
|
-
strategyName: context.strategyName,
|
|
13574
|
-
backtest: false,
|
|
13575
|
-
symbol,
|
|
13576
|
-
});
|
|
13577
|
-
}
|
|
13578
|
-
isDone = true;
|
|
13579
|
-
};
|
|
13580
|
-
task().catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
|
|
14011
|
+
this.task(symbol, context).catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
|
|
13581
14012
|
return () => {
|
|
13582
|
-
backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName });
|
|
14013
|
+
backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName }, false);
|
|
13583
14014
|
backtest$1.strategyGlobalService
|
|
13584
14015
|
.getPendingSignal(symbol, context.strategyName)
|
|
13585
14016
|
.then(async (pendingSignal) => {
|
|
13586
14017
|
if (pendingSignal) {
|
|
13587
14018
|
return;
|
|
13588
14019
|
}
|
|
13589
|
-
if (!
|
|
14020
|
+
if (!this._isDone) {
|
|
13590
14021
|
await doneLiveSubject.next({
|
|
13591
14022
|
exchangeName: context.exchangeName,
|
|
13592
14023
|
strategyName: context.strategyName,
|
|
@@ -13594,11 +14025,200 @@ class LiveUtils {
|
|
|
13594
14025
|
symbol,
|
|
13595
14026
|
});
|
|
13596
14027
|
}
|
|
13597
|
-
|
|
14028
|
+
this._isDone = true;
|
|
13598
14029
|
});
|
|
13599
|
-
|
|
14030
|
+
this._isStopped = true;
|
|
13600
14031
|
};
|
|
13601
14032
|
};
|
|
14033
|
+
/**
|
|
14034
|
+
* Stops the strategy from generating new signals.
|
|
14035
|
+
*
|
|
14036
|
+
* Sets internal flag to prevent strategy from opening new signals.
|
|
14037
|
+
* Current active signal (if any) will complete normally.
|
|
14038
|
+
* Live trading will stop at the next safe point (idle/closed state).
|
|
14039
|
+
*
|
|
14040
|
+
* @param symbol - Trading pair symbol
|
|
14041
|
+
* @param strategyName - Strategy name to stop
|
|
14042
|
+
* @returns Promise that resolves when stop flag is set
|
|
14043
|
+
*
|
|
14044
|
+
* @example
|
|
14045
|
+
* ```typescript
|
|
14046
|
+
* const instance = new LiveInstance();
|
|
14047
|
+
* await instance.stop("BTCUSDT", "my-strategy");
|
|
14048
|
+
* ```
|
|
14049
|
+
*/
|
|
14050
|
+
this.stop = async (symbol, strategyName) => {
|
|
14051
|
+
backtest$1.loggerService.info(LIVE_METHOD_NAME_STOP, {
|
|
14052
|
+
symbol,
|
|
14053
|
+
strategyName,
|
|
14054
|
+
});
|
|
14055
|
+
await backtest$1.strategyGlobalService.stop({ symbol, strategyName }, false);
|
|
14056
|
+
};
|
|
14057
|
+
/**
|
|
14058
|
+
* Gets statistical data from all live trading events for a symbol-strategy pair.
|
|
14059
|
+
*
|
|
14060
|
+
* @param symbol - Trading pair symbol
|
|
14061
|
+
* @param strategyName - Strategy name to get data for
|
|
14062
|
+
* @returns Promise resolving to statistical data object
|
|
14063
|
+
*
|
|
14064
|
+
* @example
|
|
14065
|
+
* ```typescript
|
|
14066
|
+
* const instance = new LiveInstance();
|
|
14067
|
+
* const stats = await instance.getData("BTCUSDT", "my-strategy");
|
|
14068
|
+
* console.log(stats.sharpeRatio, stats.winRate);
|
|
14069
|
+
* ```
|
|
14070
|
+
*/
|
|
14071
|
+
this.getData = async (symbol, strategyName) => {
|
|
14072
|
+
backtest$1.loggerService.info("LiveUtils.getData", {
|
|
14073
|
+
symbol,
|
|
14074
|
+
strategyName,
|
|
14075
|
+
});
|
|
14076
|
+
return await backtest$1.liveMarkdownService.getData(symbol, strategyName);
|
|
14077
|
+
};
|
|
14078
|
+
/**
|
|
14079
|
+
* Generates markdown report with all events for a symbol-strategy pair.
|
|
14080
|
+
*
|
|
14081
|
+
* @param symbol - Trading pair symbol
|
|
14082
|
+
* @param strategyName - Strategy name to generate report for
|
|
14083
|
+
* @returns Promise resolving to markdown formatted report string
|
|
14084
|
+
*
|
|
14085
|
+
* @example
|
|
14086
|
+
* ```typescript
|
|
14087
|
+
* const instance = new LiveInstance();
|
|
14088
|
+
* const markdown = await instance.getReport("BTCUSDT", "my-strategy");
|
|
14089
|
+
* console.log(markdown);
|
|
14090
|
+
* ```
|
|
14091
|
+
*/
|
|
14092
|
+
this.getReport = async (symbol, strategyName) => {
|
|
14093
|
+
backtest$1.loggerService.info(LIVE_METHOD_NAME_GET_REPORT, {
|
|
14094
|
+
symbol,
|
|
14095
|
+
strategyName,
|
|
14096
|
+
});
|
|
14097
|
+
return await backtest$1.liveMarkdownService.getReport(symbol, strategyName);
|
|
14098
|
+
};
|
|
14099
|
+
/**
|
|
14100
|
+
* Saves strategy report to disk.
|
|
14101
|
+
*
|
|
14102
|
+
* @param symbol - Trading pair symbol
|
|
14103
|
+
* @param strategyName - Strategy name to save report for
|
|
14104
|
+
* @param path - Optional directory path to save report (default: "./dump/live")
|
|
14105
|
+
*
|
|
14106
|
+
* @example
|
|
14107
|
+
* ```typescript
|
|
14108
|
+
* const instance = new LiveInstance();
|
|
14109
|
+
* // Save to default path: ./dump/live/my-strategy.md
|
|
14110
|
+
* await instance.dump("BTCUSDT", "my-strategy");
|
|
14111
|
+
*
|
|
14112
|
+
* // Save to custom path: ./custom/path/my-strategy.md
|
|
14113
|
+
* await instance.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
14114
|
+
* ```
|
|
14115
|
+
*/
|
|
14116
|
+
this.dump = async (symbol, strategyName, path) => {
|
|
14117
|
+
backtest$1.loggerService.info(LIVE_METHOD_NAME_DUMP, {
|
|
14118
|
+
symbol,
|
|
14119
|
+
strategyName,
|
|
14120
|
+
path,
|
|
14121
|
+
});
|
|
14122
|
+
await backtest$1.liveMarkdownService.dump(symbol, strategyName, path);
|
|
14123
|
+
};
|
|
14124
|
+
}
|
|
14125
|
+
}
|
|
14126
|
+
/**
|
|
14127
|
+
* Utility class for live trading operations.
|
|
14128
|
+
*
|
|
14129
|
+
* Provides simplified access to liveCommandService.run() with logging.
|
|
14130
|
+
* Exported as singleton instance for convenient usage.
|
|
14131
|
+
*
|
|
14132
|
+
* Features:
|
|
14133
|
+
* - Infinite async generator (never completes)
|
|
14134
|
+
* - Crash recovery via persisted state
|
|
14135
|
+
* - Real-time progression with Date.now()
|
|
14136
|
+
*
|
|
14137
|
+
* @example
|
|
14138
|
+
* ```typescript
|
|
14139
|
+
* import { Live } from "./classes/Live";
|
|
14140
|
+
*
|
|
14141
|
+
* // Infinite loop - use Ctrl+C to stop
|
|
14142
|
+
* for await (const result of Live.run("BTCUSDT", {
|
|
14143
|
+
* strategyName: "my-strategy",
|
|
14144
|
+
* exchangeName: "my-exchange",
|
|
14145
|
+
* frameName: ""
|
|
14146
|
+
* })) {
|
|
14147
|
+
* if (result.action === "opened") {
|
|
14148
|
+
* console.log("Signal opened:", result.signal);
|
|
14149
|
+
* } else if (result.action === "closed") {
|
|
14150
|
+
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
14151
|
+
* }
|
|
14152
|
+
* }
|
|
14153
|
+
* ```
|
|
14154
|
+
*/
|
|
14155
|
+
class LiveUtils {
|
|
14156
|
+
constructor() {
|
|
14157
|
+
/**
|
|
14158
|
+
* Memoized function to get or create LiveInstance for a symbol-strategy pair.
|
|
14159
|
+
* Each symbol-strategy combination gets its own isolated instance.
|
|
14160
|
+
*/
|
|
14161
|
+
this._getInstance = functoolsKit.memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, (symbol, strategyName) => new LiveInstance(symbol, strategyName));
|
|
14162
|
+
/**
|
|
14163
|
+
* Runs live trading for a symbol with context propagation.
|
|
14164
|
+
*
|
|
14165
|
+
* Infinite async generator with crash recovery support.
|
|
14166
|
+
* Process can crash and restart - state will be recovered from disk.
|
|
14167
|
+
*
|
|
14168
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
14169
|
+
* @param context - Execution context with strategy and exchange names
|
|
14170
|
+
* @returns Infinite async generator yielding opened and closed signals
|
|
14171
|
+
*/
|
|
14172
|
+
this.run = (symbol, context) => {
|
|
14173
|
+
const instance = this._getInstance(symbol, context.strategyName);
|
|
14174
|
+
return instance.run(symbol, context);
|
|
14175
|
+
};
|
|
14176
|
+
/**
|
|
14177
|
+
* Runs live trading in background without yielding results.
|
|
14178
|
+
*
|
|
14179
|
+
* Consumes all live trading results internally without exposing them.
|
|
14180
|
+
* Infinite loop - will run until process is stopped or crashes.
|
|
14181
|
+
* Useful for running live trading for side effects only (callbacks, persistence).
|
|
14182
|
+
*
|
|
14183
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
14184
|
+
* @param context - Execution context with strategy and exchange names
|
|
14185
|
+
* @returns Cancellation closure
|
|
14186
|
+
*
|
|
14187
|
+
* @example
|
|
14188
|
+
* ```typescript
|
|
14189
|
+
* // Run live trading silently in background, only callbacks will fire
|
|
14190
|
+
* // This will run forever until Ctrl+C
|
|
14191
|
+
* await Live.background("BTCUSDT", {
|
|
14192
|
+
* strategyName: "my-strategy",
|
|
14193
|
+
* exchangeName: "my-exchange"
|
|
14194
|
+
* });
|
|
14195
|
+
* ```
|
|
14196
|
+
*/
|
|
14197
|
+
this.background = (symbol, context) => {
|
|
14198
|
+
const instance = this._getInstance(symbol, context.strategyName);
|
|
14199
|
+
return instance.background(symbol, context);
|
|
14200
|
+
};
|
|
14201
|
+
/**
|
|
14202
|
+
* Stops the strategy from generating new signals.
|
|
14203
|
+
*
|
|
14204
|
+
* Sets internal flag to prevent strategy from opening new signals.
|
|
14205
|
+
* Current active signal (if any) will complete normally.
|
|
14206
|
+
* Live trading will stop at the next safe point (idle/closed state).
|
|
14207
|
+
*
|
|
14208
|
+
* @param symbol - Trading pair symbol
|
|
14209
|
+
* @param strategyName - Strategy name to stop
|
|
14210
|
+
* @returns Promise that resolves when stop flag is set
|
|
14211
|
+
*
|
|
14212
|
+
* @example
|
|
14213
|
+
* ```typescript
|
|
14214
|
+
* // Stop live trading gracefully
|
|
14215
|
+
* await Live.stop("BTCUSDT", "my-strategy");
|
|
14216
|
+
* ```
|
|
14217
|
+
*/
|
|
14218
|
+
this.stop = async (symbol, strategyName) => {
|
|
14219
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
14220
|
+
return await instance.stop(symbol, strategyName);
|
|
14221
|
+
};
|
|
13602
14222
|
/**
|
|
13603
14223
|
* Gets statistical data from all live trading events for a symbol-strategy pair.
|
|
13604
14224
|
*
|
|
@@ -13613,11 +14233,8 @@ class LiveUtils {
|
|
|
13613
14233
|
* ```
|
|
13614
14234
|
*/
|
|
13615
14235
|
this.getData = async (symbol, strategyName) => {
|
|
13616
|
-
|
|
13617
|
-
|
|
13618
|
-
strategyName,
|
|
13619
|
-
});
|
|
13620
|
-
return await backtest$1.liveMarkdownService.getData(symbol, strategyName);
|
|
14236
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
14237
|
+
return await instance.getData(symbol, strategyName);
|
|
13621
14238
|
};
|
|
13622
14239
|
/**
|
|
13623
14240
|
* Generates markdown report with all events for a symbol-strategy pair.
|
|
@@ -13633,11 +14250,8 @@ class LiveUtils {
|
|
|
13633
14250
|
* ```
|
|
13634
14251
|
*/
|
|
13635
14252
|
this.getReport = async (symbol, strategyName) => {
|
|
13636
|
-
|
|
13637
|
-
|
|
13638
|
-
strategyName,
|
|
13639
|
-
});
|
|
13640
|
-
return await backtest$1.liveMarkdownService.getReport(symbol, strategyName);
|
|
14253
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
14254
|
+
return await instance.getReport(symbol, strategyName);
|
|
13641
14255
|
};
|
|
13642
14256
|
/**
|
|
13643
14257
|
* Saves strategy report to disk.
|
|
@@ -13656,12 +14270,25 @@ class LiveUtils {
|
|
|
13656
14270
|
* ```
|
|
13657
14271
|
*/
|
|
13658
14272
|
this.dump = async (symbol, strategyName, path) => {
|
|
13659
|
-
|
|
13660
|
-
|
|
13661
|
-
|
|
13662
|
-
|
|
13663
|
-
|
|
13664
|
-
|
|
14273
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
14274
|
+
return await instance.dump(symbol, strategyName, path);
|
|
14275
|
+
};
|
|
14276
|
+
/**
|
|
14277
|
+
* Lists all active live trading instances with their current status.
|
|
14278
|
+
*
|
|
14279
|
+
* @returns Promise resolving to array of status objects for all instances
|
|
14280
|
+
*
|
|
14281
|
+
* @example
|
|
14282
|
+
* ```typescript
|
|
14283
|
+
* const statusList = await Live.list();
|
|
14284
|
+
* statusList.forEach(status => {
|
|
14285
|
+
* console.log(`${status.symbol} - ${status.strategyName}: ${status.status}`);
|
|
14286
|
+
* });
|
|
14287
|
+
* ```
|
|
14288
|
+
*/
|
|
14289
|
+
this.list = async () => {
|
|
14290
|
+
const instanceList = this._getInstance.values();
|
|
14291
|
+
return await Promise.all(instanceList.map((instance) => instance.getStatus()));
|
|
13665
14292
|
};
|
|
13666
14293
|
}
|
|
13667
14294
|
}
|
|
@@ -13906,30 +14533,112 @@ class Performance {
|
|
|
13906
14533
|
|
|
13907
14534
|
const WALKER_METHOD_NAME_RUN = "WalkerUtils.run";
|
|
13908
14535
|
const WALKER_METHOD_NAME_BACKGROUND = "WalkerUtils.background";
|
|
14536
|
+
const WALKER_METHOD_NAME_STOP = "WalkerUtils.stop";
|
|
13909
14537
|
const WALKER_METHOD_NAME_GET_DATA = "WalkerUtils.getData";
|
|
13910
14538
|
const WALKER_METHOD_NAME_GET_REPORT = "WalkerUtils.getReport";
|
|
13911
14539
|
const WALKER_METHOD_NAME_DUMP = "WalkerUtils.dump";
|
|
14540
|
+
const WALKER_METHOD_NAME_TASK = "WalkerUtils.task";
|
|
14541
|
+
const WALKER_METHOD_NAME_GET_STATUS = "WalkerUtils.getStatus";
|
|
13912
14542
|
/**
|
|
13913
|
-
*
|
|
14543
|
+
* Internal task function that runs walker and handles completion.
|
|
14544
|
+
* Consumes walker results and updates instance state flags.
|
|
13914
14545
|
*
|
|
13915
|
-
*
|
|
13916
|
-
*
|
|
13917
|
-
*
|
|
14546
|
+
* @param symbol - Trading pair symbol
|
|
14547
|
+
* @param context - Execution context with walker name
|
|
14548
|
+
* @param self - WalkerInstance reference for state management
|
|
14549
|
+
* @returns Promise that resolves when walker completes
|
|
14550
|
+
*
|
|
14551
|
+
* @internal
|
|
14552
|
+
*/
|
|
14553
|
+
const INSTANCE_TASK_FN = async (symbol, context, self) => {
|
|
14554
|
+
{
|
|
14555
|
+
self._isStopped = false;
|
|
14556
|
+
self._isDone = false;
|
|
14557
|
+
}
|
|
14558
|
+
for await (const _ of self.run(symbol, context)) {
|
|
14559
|
+
if (self._isStopped) {
|
|
14560
|
+
break;
|
|
14561
|
+
}
|
|
14562
|
+
}
|
|
14563
|
+
if (!self._isDone) {
|
|
14564
|
+
const walkerSchema = backtest$1.walkerSchemaService.get(context.walkerName);
|
|
14565
|
+
await doneWalkerSubject.next({
|
|
14566
|
+
exchangeName: walkerSchema.exchangeName,
|
|
14567
|
+
strategyName: context.walkerName,
|
|
14568
|
+
backtest: true,
|
|
14569
|
+
symbol,
|
|
14570
|
+
});
|
|
14571
|
+
}
|
|
14572
|
+
self._isDone = true;
|
|
14573
|
+
};
|
|
14574
|
+
/**
|
|
14575
|
+
* Instance class for walker operations on a specific symbol-walker pair.
|
|
14576
|
+
*
|
|
14577
|
+
* Provides isolated walker execution and reporting for a single symbol-walker combination.
|
|
14578
|
+
* Each instance maintains its own state and context.
|
|
13918
14579
|
*
|
|
13919
14580
|
* @example
|
|
13920
14581
|
* ```typescript
|
|
13921
|
-
*
|
|
14582
|
+
* const instance = new WalkerInstance("BTCUSDT", "my-walker");
|
|
13922
14583
|
*
|
|
13923
|
-
* for await (const result of
|
|
14584
|
+
* for await (const result of instance.run("BTCUSDT", {
|
|
13924
14585
|
* walkerName: "my-walker"
|
|
13925
14586
|
* })) {
|
|
13926
14587
|
* console.log("Progress:", result.strategiesTested, "/", result.totalStrategies);
|
|
13927
|
-
* console.log("Best strategy:", result.bestStrategy, result.bestMetric);
|
|
13928
14588
|
* }
|
|
13929
14589
|
* ```
|
|
13930
14590
|
*/
|
|
13931
|
-
class
|
|
13932
|
-
|
|
14591
|
+
class WalkerInstance {
|
|
14592
|
+
/**
|
|
14593
|
+
* Creates a new WalkerInstance for a specific symbol-walker pair.
|
|
14594
|
+
*
|
|
14595
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
14596
|
+
* @param walkerName - Walker name for this walker instance
|
|
14597
|
+
*/
|
|
14598
|
+
constructor(symbol, walkerName) {
|
|
14599
|
+
this.symbol = symbol;
|
|
14600
|
+
this.walkerName = walkerName;
|
|
14601
|
+
/** Internal flag indicating if walker was stopped manually */
|
|
14602
|
+
this._isStopped = false;
|
|
14603
|
+
/** Internal flag indicating if walker task completed */
|
|
14604
|
+
this._isDone = false;
|
|
14605
|
+
/**
|
|
14606
|
+
* Internal singlerun task that executes the walker.
|
|
14607
|
+
* Ensures only one walker run per instance using singlerun wrapper.
|
|
14608
|
+
*
|
|
14609
|
+
* @param symbol - Trading pair symbol
|
|
14610
|
+
* @param context - Execution context with walker name
|
|
14611
|
+
* @returns Promise that resolves when walker completes
|
|
14612
|
+
*
|
|
14613
|
+
* @internal
|
|
14614
|
+
*/
|
|
14615
|
+
this.task = functoolsKit.singlerun(async (symbol, context) => {
|
|
14616
|
+
backtest$1.loggerService.info(WALKER_METHOD_NAME_TASK, {
|
|
14617
|
+
symbol,
|
|
14618
|
+
context,
|
|
14619
|
+
});
|
|
14620
|
+
return await INSTANCE_TASK_FN(symbol, context, this);
|
|
14621
|
+
});
|
|
14622
|
+
/**
|
|
14623
|
+
* Gets the current status of this walker instance.
|
|
14624
|
+
*
|
|
14625
|
+
* @returns Promise resolving to status object with symbol, walkerName, and task status
|
|
14626
|
+
*
|
|
14627
|
+
* @example
|
|
14628
|
+
* ```typescript
|
|
14629
|
+
* const instance = new WalkerInstance("BTCUSDT", "my-walker");
|
|
14630
|
+
* const status = await instance.getStatus();
|
|
14631
|
+
* console.log(status.status); // "idle", "running", or "done"
|
|
14632
|
+
* ```
|
|
14633
|
+
*/
|
|
14634
|
+
this.getStatus = async () => {
|
|
14635
|
+
backtest$1.loggerService.info(WALKER_METHOD_NAME_GET_STATUS);
|
|
14636
|
+
return {
|
|
14637
|
+
symbol: this.symbol,
|
|
14638
|
+
walkerName: this.walkerName,
|
|
14639
|
+
status: this.task.getStatus(),
|
|
14640
|
+
};
|
|
14641
|
+
};
|
|
13933
14642
|
/**
|
|
13934
14643
|
* Runs walker comparison for a symbol with context propagation.
|
|
13935
14644
|
*
|
|
@@ -13982,11 +14691,10 @@ class WalkerUtils {
|
|
|
13982
14691
|
*
|
|
13983
14692
|
* @example
|
|
13984
14693
|
* ```typescript
|
|
13985
|
-
*
|
|
13986
|
-
*
|
|
14694
|
+
* const instance = new WalkerInstance();
|
|
14695
|
+
* const cancel = instance.background("BTCUSDT", {
|
|
13987
14696
|
* walkerName: "my-walker"
|
|
13988
14697
|
* });
|
|
13989
|
-
* console.log("Walker comparison completed");
|
|
13990
14698
|
* ```
|
|
13991
14699
|
*/
|
|
13992
14700
|
this.background = (symbol, context) => {
|
|
@@ -13995,31 +14703,13 @@ class WalkerUtils {
|
|
|
13995
14703
|
context,
|
|
13996
14704
|
});
|
|
13997
14705
|
const walkerSchema = backtest$1.walkerSchemaService.get(context.walkerName);
|
|
13998
|
-
|
|
13999
|
-
let isDone = false;
|
|
14000
|
-
const task = async () => {
|
|
14001
|
-
for await (const _ of this.run(symbol, context)) {
|
|
14002
|
-
if (isStopped) {
|
|
14003
|
-
break;
|
|
14004
|
-
}
|
|
14005
|
-
}
|
|
14006
|
-
if (!isDone) {
|
|
14007
|
-
await doneWalkerSubject.next({
|
|
14008
|
-
exchangeName: walkerSchema.exchangeName,
|
|
14009
|
-
strategyName: context.walkerName,
|
|
14010
|
-
backtest: true,
|
|
14011
|
-
symbol,
|
|
14012
|
-
});
|
|
14013
|
-
}
|
|
14014
|
-
isDone = true;
|
|
14015
|
-
};
|
|
14016
|
-
task().catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
|
|
14706
|
+
this.task(symbol, context).catch((error) => exitEmitter.next(new Error(functoolsKit.getErrorMessage(error))));
|
|
14017
14707
|
return () => {
|
|
14018
14708
|
for (const strategyName of walkerSchema.strategies) {
|
|
14019
|
-
backtest$1.strategyGlobalService.stop({ symbol, strategyName });
|
|
14020
|
-
walkerStopSubject.next({ symbol, strategyName });
|
|
14709
|
+
backtest$1.strategyGlobalService.stop({ symbol, strategyName }, true);
|
|
14710
|
+
walkerStopSubject.next({ symbol, strategyName, walkerName: context.walkerName });
|
|
14021
14711
|
}
|
|
14022
|
-
if (!
|
|
14712
|
+
if (!this._isDone) {
|
|
14023
14713
|
doneWalkerSubject.next({
|
|
14024
14714
|
exchangeName: walkerSchema.exchangeName,
|
|
14025
14715
|
strategyName: context.walkerName,
|
|
@@ -14027,10 +14717,44 @@ class WalkerUtils {
|
|
|
14027
14717
|
symbol,
|
|
14028
14718
|
});
|
|
14029
14719
|
}
|
|
14030
|
-
|
|
14031
|
-
|
|
14720
|
+
this._isDone = true;
|
|
14721
|
+
this._isStopped = true;
|
|
14032
14722
|
};
|
|
14033
14723
|
};
|
|
14724
|
+
/**
|
|
14725
|
+
* Stops all strategies in the walker from generating new signals.
|
|
14726
|
+
*
|
|
14727
|
+
* Iterates through all strategies defined in walker schema and:
|
|
14728
|
+
* 1. Sends stop signal via walkerStopSubject (interrupts current running strategy)
|
|
14729
|
+
* 2. Sets internal stop flag for each strategy (prevents new signals)
|
|
14730
|
+
*
|
|
14731
|
+
* Current active signals (if any) will complete normally.
|
|
14732
|
+
* Walker will stop at the next safe point.
|
|
14733
|
+
*
|
|
14734
|
+
* Supports multiple walkers running on the same symbol simultaneously.
|
|
14735
|
+
* Stop signal is filtered by walkerName to prevent interference.
|
|
14736
|
+
*
|
|
14737
|
+
* @param symbol - Trading pair symbol
|
|
14738
|
+
* @param walkerName - Walker name to stop
|
|
14739
|
+
* @returns Promise that resolves when all stop flags are set
|
|
14740
|
+
*
|
|
14741
|
+
* @example
|
|
14742
|
+
* ```typescript
|
|
14743
|
+
* const instance = new WalkerInstance();
|
|
14744
|
+
* await instance.stop("BTCUSDT", "my-walker");
|
|
14745
|
+
* ```
|
|
14746
|
+
*/
|
|
14747
|
+
this.stop = async (symbol, walkerName) => {
|
|
14748
|
+
backtest$1.loggerService.info(WALKER_METHOD_NAME_STOP, {
|
|
14749
|
+
symbol,
|
|
14750
|
+
walkerName,
|
|
14751
|
+
});
|
|
14752
|
+
const walkerSchema = backtest$1.walkerSchemaService.get(walkerName);
|
|
14753
|
+
for (const strategyName of walkerSchema.strategies) {
|
|
14754
|
+
await walkerStopSubject.next({ symbol, strategyName, walkerName });
|
|
14755
|
+
await backtest$1.strategyGlobalService.stop({ symbol, strategyName }, true);
|
|
14756
|
+
}
|
|
14757
|
+
};
|
|
14034
14758
|
/**
|
|
14035
14759
|
* Gets walker results data from all strategy comparisons.
|
|
14036
14760
|
*
|
|
@@ -14040,7 +14764,8 @@ class WalkerUtils {
|
|
|
14040
14764
|
*
|
|
14041
14765
|
* @example
|
|
14042
14766
|
* ```typescript
|
|
14043
|
-
* const
|
|
14767
|
+
* const instance = new WalkerInstance();
|
|
14768
|
+
* const results = await instance.getData("BTCUSDT", "my-walker");
|
|
14044
14769
|
* console.log(results.bestStrategy, results.bestMetric);
|
|
14045
14770
|
* ```
|
|
14046
14771
|
*/
|
|
@@ -14064,7 +14789,8 @@ class WalkerUtils {
|
|
|
14064
14789
|
*
|
|
14065
14790
|
* @example
|
|
14066
14791
|
* ```typescript
|
|
14067
|
-
* const
|
|
14792
|
+
* const instance = new WalkerInstance();
|
|
14793
|
+
* const markdown = await instance.getReport("BTCUSDT", "my-walker");
|
|
14068
14794
|
* console.log(markdown);
|
|
14069
14795
|
* ```
|
|
14070
14796
|
*/
|
|
@@ -14088,11 +14814,12 @@ class WalkerUtils {
|
|
|
14088
14814
|
*
|
|
14089
14815
|
* @example
|
|
14090
14816
|
* ```typescript
|
|
14817
|
+
* const instance = new WalkerInstance();
|
|
14091
14818
|
* // Save to default path: ./dump/walker/my-walker.md
|
|
14092
|
-
* await
|
|
14819
|
+
* await instance.dump("BTCUSDT", "my-walker");
|
|
14093
14820
|
*
|
|
14094
14821
|
* // Save to custom path: ./custom/path/my-walker.md
|
|
14095
|
-
* await
|
|
14822
|
+
* await instance.dump("BTCUSDT", "my-walker", "./custom/path");
|
|
14096
14823
|
* ```
|
|
14097
14824
|
*/
|
|
14098
14825
|
this.dump = async (symbol, walkerName, path) => {
|
|
@@ -14109,6 +14836,166 @@ class WalkerUtils {
|
|
|
14109
14836
|
};
|
|
14110
14837
|
}
|
|
14111
14838
|
}
|
|
14839
|
+
/**
|
|
14840
|
+
* Utility class for walker operations.
|
|
14841
|
+
*
|
|
14842
|
+
* Provides simplified access to walkerCommandService.run() with logging.
|
|
14843
|
+
* Automatically pulls exchangeName and frameName from walker schema.
|
|
14844
|
+
* Exported as singleton instance for convenient usage.
|
|
14845
|
+
*
|
|
14846
|
+
* @example
|
|
14847
|
+
* ```typescript
|
|
14848
|
+
* import { Walker } from "./classes/Walker";
|
|
14849
|
+
*
|
|
14850
|
+
* for await (const result of Walker.run("BTCUSDT", {
|
|
14851
|
+
* walkerName: "my-walker"
|
|
14852
|
+
* })) {
|
|
14853
|
+
* console.log("Progress:", result.strategiesTested, "/", result.totalStrategies);
|
|
14854
|
+
* console.log("Best strategy:", result.bestStrategy, result.bestMetric);
|
|
14855
|
+
* }
|
|
14856
|
+
* ```
|
|
14857
|
+
*/
|
|
14858
|
+
class WalkerUtils {
|
|
14859
|
+
constructor() {
|
|
14860
|
+
/**
|
|
14861
|
+
* Memoized function to get or create WalkerInstance for a symbol-walker pair.
|
|
14862
|
+
* Each symbol-walker combination gets its own isolated instance.
|
|
14863
|
+
*/
|
|
14864
|
+
this._getInstance = functoolsKit.memoize(([symbol, walkerName]) => `${symbol}:${walkerName}`, (symbol, walkerName) => new WalkerInstance(symbol, walkerName));
|
|
14865
|
+
/**
|
|
14866
|
+
* Runs walker comparison for a symbol with context propagation.
|
|
14867
|
+
*
|
|
14868
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
14869
|
+
* @param context - Execution context with walker name
|
|
14870
|
+
* @returns Async generator yielding progress updates after each strategy
|
|
14871
|
+
*/
|
|
14872
|
+
this.run = (symbol, context) => {
|
|
14873
|
+
const instance = this._getInstance(symbol, context.walkerName);
|
|
14874
|
+
return instance.run(symbol, context);
|
|
14875
|
+
};
|
|
14876
|
+
/**
|
|
14877
|
+
* Runs walker comparison in background without yielding results.
|
|
14878
|
+
*
|
|
14879
|
+
* Consumes all walker progress updates internally without exposing them.
|
|
14880
|
+
* Useful for running walker comparison for side effects only (callbacks, logging).
|
|
14881
|
+
*
|
|
14882
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
14883
|
+
* @param context - Execution context with walker name
|
|
14884
|
+
* @returns Cancellation closure
|
|
14885
|
+
*
|
|
14886
|
+
* @example
|
|
14887
|
+
* ```typescript
|
|
14888
|
+
* // Run walker silently, only callbacks will fire
|
|
14889
|
+
* await Walker.background("BTCUSDT", {
|
|
14890
|
+
* walkerName: "my-walker"
|
|
14891
|
+
* });
|
|
14892
|
+
* console.log("Walker comparison completed");
|
|
14893
|
+
* ```
|
|
14894
|
+
*/
|
|
14895
|
+
this.background = (symbol, context) => {
|
|
14896
|
+
const instance = this._getInstance(symbol, context.walkerName);
|
|
14897
|
+
return instance.background(symbol, context);
|
|
14898
|
+
};
|
|
14899
|
+
/**
|
|
14900
|
+
* Stops all strategies in the walker from generating new signals.
|
|
14901
|
+
*
|
|
14902
|
+
* Iterates through all strategies defined in walker schema and:
|
|
14903
|
+
* 1. Sends stop signal via walkerStopSubject (interrupts current running strategy)
|
|
14904
|
+
* 2. Sets internal stop flag for each strategy (prevents new signals)
|
|
14905
|
+
*
|
|
14906
|
+
* Current active signals (if any) will complete normally.
|
|
14907
|
+
* Walker will stop at the next safe point.
|
|
14908
|
+
*
|
|
14909
|
+
* Supports multiple walkers running on the same symbol simultaneously.
|
|
14910
|
+
* Stop signal is filtered by walkerName to prevent interference.
|
|
14911
|
+
*
|
|
14912
|
+
* @param symbol - Trading pair symbol
|
|
14913
|
+
* @param walkerName - Walker name to stop
|
|
14914
|
+
* @returns Promise that resolves when all stop flags are set
|
|
14915
|
+
*
|
|
14916
|
+
* @example
|
|
14917
|
+
* ```typescript
|
|
14918
|
+
* // Stop walker and all its strategies
|
|
14919
|
+
* await Walker.stop("BTCUSDT", "my-walker");
|
|
14920
|
+
* ```
|
|
14921
|
+
*/
|
|
14922
|
+
this.stop = async (symbol, walkerName) => {
|
|
14923
|
+
const instance = this._getInstance(symbol, walkerName);
|
|
14924
|
+
return await instance.stop(symbol, walkerName);
|
|
14925
|
+
};
|
|
14926
|
+
/**
|
|
14927
|
+
* Gets walker results data from all strategy comparisons.
|
|
14928
|
+
*
|
|
14929
|
+
* @param symbol - Trading symbol
|
|
14930
|
+
* @param walkerName - Walker name to get data for
|
|
14931
|
+
* @returns Promise resolving to walker results data object
|
|
14932
|
+
*
|
|
14933
|
+
* @example
|
|
14934
|
+
* ```typescript
|
|
14935
|
+
* const results = await Walker.getData("BTCUSDT", "my-walker");
|
|
14936
|
+
* console.log(results.bestStrategy, results.bestMetric);
|
|
14937
|
+
* ```
|
|
14938
|
+
*/
|
|
14939
|
+
this.getData = async (symbol, walkerName) => {
|
|
14940
|
+
const instance = this._getInstance(symbol, walkerName);
|
|
14941
|
+
return await instance.getData(symbol, walkerName);
|
|
14942
|
+
};
|
|
14943
|
+
/**
|
|
14944
|
+
* Generates markdown report with all strategy comparisons for a walker.
|
|
14945
|
+
*
|
|
14946
|
+
* @param symbol - Trading symbol
|
|
14947
|
+
* @param walkerName - Walker name to generate report for
|
|
14948
|
+
* @returns Promise resolving to markdown formatted report string
|
|
14949
|
+
*
|
|
14950
|
+
* @example
|
|
14951
|
+
* ```typescript
|
|
14952
|
+
* const markdown = await Walker.getReport("BTCUSDT", "my-walker");
|
|
14953
|
+
* console.log(markdown);
|
|
14954
|
+
* ```
|
|
14955
|
+
*/
|
|
14956
|
+
this.getReport = async (symbol, walkerName) => {
|
|
14957
|
+
const instance = this._getInstance(symbol, walkerName);
|
|
14958
|
+
return await instance.getReport(symbol, walkerName);
|
|
14959
|
+
};
|
|
14960
|
+
/**
|
|
14961
|
+
* Saves walker report to disk.
|
|
14962
|
+
*
|
|
14963
|
+
* @param symbol - Trading symbol
|
|
14964
|
+
* @param walkerName - Walker name to save report for
|
|
14965
|
+
* @param path - Optional directory path to save report (default: "./dump/walker")
|
|
14966
|
+
*
|
|
14967
|
+
* @example
|
|
14968
|
+
* ```typescript
|
|
14969
|
+
* // Save to default path: ./dump/walker/my-walker.md
|
|
14970
|
+
* await Walker.dump("BTCUSDT", "my-walker");
|
|
14971
|
+
*
|
|
14972
|
+
* // Save to custom path: ./custom/path/my-walker.md
|
|
14973
|
+
* await Walker.dump("BTCUSDT", "my-walker", "./custom/path");
|
|
14974
|
+
* ```
|
|
14975
|
+
*/
|
|
14976
|
+
this.dump = async (symbol, walkerName, path) => {
|
|
14977
|
+
const instance = this._getInstance(symbol, walkerName);
|
|
14978
|
+
return await instance.dump(symbol, walkerName, path);
|
|
14979
|
+
};
|
|
14980
|
+
/**
|
|
14981
|
+
* Lists all active walker instances with their current status.
|
|
14982
|
+
*
|
|
14983
|
+
* @returns Promise resolving to array of status objects for all instances
|
|
14984
|
+
*
|
|
14985
|
+
* @example
|
|
14986
|
+
* ```typescript
|
|
14987
|
+
* const statusList = await Walker.list();
|
|
14988
|
+
* statusList.forEach(status => {
|
|
14989
|
+
* console.log(`${status.symbol} - ${status.walkerName}: ${status.status}`);
|
|
14990
|
+
* });
|
|
14991
|
+
* ```
|
|
14992
|
+
*/
|
|
14993
|
+
this.list = async () => {
|
|
14994
|
+
const instanceList = this._getInstance.values();
|
|
14995
|
+
return await Promise.all(instanceList.map((instance) => instance.getStatus()));
|
|
14996
|
+
};
|
|
14997
|
+
}
|
|
14998
|
+
}
|
|
14112
14999
|
/**
|
|
14113
15000
|
* Singleton instance of WalkerUtils for convenient walker operations.
|
|
14114
15001
|
*
|