backtest-kit 1.5.3 → 1.5.5
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 +97 -8
- package/build/index.cjs +909 -234
- package/build/index.mjs +910 -235
- package/package.json +2 -2
- package/types.d.ts +69 -0
package/build/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createActivator } from 'di-kit';
|
|
2
2
|
import { scoped } from 'di-scoped';
|
|
3
|
-
import { errorData, getErrorMessage, sleep, memoize, makeExtendable, singleshot, not, trycatch, retry, Subject, randomString, ToolRegistry, isObject, and, resolveDocuments, str, iterateDocuments, distinctDocuments, queued } from 'functools-kit';
|
|
3
|
+
import { errorData, getErrorMessage, sleep, memoize, makeExtendable, singleshot, not, trycatch, retry, Subject, randomString, ToolRegistry, isObject, and, resolveDocuments, str, iterateDocuments, distinctDocuments, queued, singlerun } from 'functools-kit';
|
|
4
4
|
import fs, { mkdir, writeFile } from 'fs/promises';
|
|
5
5
|
import path, { join } from 'path';
|
|
6
6
|
import crypto from 'crypto';
|
|
@@ -3177,15 +3177,17 @@ class ClientStrategy {
|
|
|
3177
3177
|
backtest,
|
|
3178
3178
|
});
|
|
3179
3179
|
this._isStopped = true;
|
|
3180
|
-
|
|
3181
|
-
if (!this._scheduledSignal) {
|
|
3180
|
+
if (!this._pendingSignal) {
|
|
3182
3181
|
return;
|
|
3183
3182
|
}
|
|
3184
|
-
this.
|
|
3183
|
+
this._pendingSignal = null;
|
|
3184
|
+
if (this.params.callbacks?.onWrite) {
|
|
3185
|
+
this.params.callbacks.onWrite(symbol, this._pendingSignal, backtest);
|
|
3186
|
+
}
|
|
3185
3187
|
if (backtest) {
|
|
3186
3188
|
return;
|
|
3187
3189
|
}
|
|
3188
|
-
await PersistScheduleAdapter.writeScheduleData(this.
|
|
3190
|
+
await PersistScheduleAdapter.writeScheduleData(this._pendingSignal, symbol, strategyName);
|
|
3189
3191
|
}
|
|
3190
3192
|
}
|
|
3191
3193
|
|
|
@@ -13400,17 +13402,50 @@ const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
|
|
|
13400
13402
|
const BACKTEST_METHOD_NAME_STOP = "BacktestUtils.stop";
|
|
13401
13403
|
const BACKTEST_METHOD_NAME_GET_REPORT = "BacktestUtils.getReport";
|
|
13402
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";
|
|
13403
13407
|
/**
|
|
13404
|
-
*
|
|
13408
|
+
* Internal task function that runs backtest and handles completion.
|
|
13409
|
+
* Consumes backtest results and updates instance state flags.
|
|
13405
13410
|
*
|
|
13406
|
-
*
|
|
13407
|
-
*
|
|
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.
|
|
13408
13443
|
*
|
|
13409
13444
|
* @example
|
|
13410
13445
|
* ```typescript
|
|
13411
|
-
*
|
|
13446
|
+
* const instance = new BacktestInstance("BTCUSDT", "my-strategy");
|
|
13412
13447
|
*
|
|
13413
|
-
* for await (const result of
|
|
13448
|
+
* for await (const result of instance.run("BTCUSDT", {
|
|
13414
13449
|
* strategyName: "my-strategy",
|
|
13415
13450
|
* exchangeName: "my-exchange",
|
|
13416
13451
|
* frameName: "1d-backtest"
|
|
@@ -13419,8 +13454,57 @@ const BACKTEST_METHOD_NAME_DUMP = "BacktestUtils.dump";
|
|
|
13419
13454
|
* }
|
|
13420
13455
|
* ```
|
|
13421
13456
|
*/
|
|
13422
|
-
class
|
|
13423
|
-
|
|
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 = 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
|
+
};
|
|
13424
13508
|
/**
|
|
13425
13509
|
* Runs backtest for a symbol with context propagation.
|
|
13426
13510
|
*
|
|
@@ -13458,13 +13542,12 @@ class BacktestUtils {
|
|
|
13458
13542
|
*
|
|
13459
13543
|
* @example
|
|
13460
13544
|
* ```typescript
|
|
13461
|
-
*
|
|
13462
|
-
*
|
|
13545
|
+
* const instance = new BacktestInstance();
|
|
13546
|
+
* const cancel = instance.background("BTCUSDT", {
|
|
13463
13547
|
* strategyName: "my-strategy",
|
|
13464
13548
|
* exchangeName: "my-exchange",
|
|
13465
13549
|
* frameName: "1d-backtest"
|
|
13466
13550
|
* });
|
|
13467
|
-
* console.log("Backtest completed");
|
|
13468
13551
|
* ```
|
|
13469
13552
|
*/
|
|
13470
13553
|
this.background = (symbol, context) => {
|
|
@@ -13472,25 +13555,7 @@ class BacktestUtils {
|
|
|
13472
13555
|
symbol,
|
|
13473
13556
|
context,
|
|
13474
13557
|
});
|
|
13475
|
-
|
|
13476
|
-
let isDone = false;
|
|
13477
|
-
const task = async () => {
|
|
13478
|
-
for await (const _ of this.run(symbol, context)) {
|
|
13479
|
-
if (isStopped) {
|
|
13480
|
-
break;
|
|
13481
|
-
}
|
|
13482
|
-
}
|
|
13483
|
-
if (!isDone) {
|
|
13484
|
-
await doneBacktestSubject.next({
|
|
13485
|
-
exchangeName: context.exchangeName,
|
|
13486
|
-
strategyName: context.strategyName,
|
|
13487
|
-
backtest: true,
|
|
13488
|
-
symbol,
|
|
13489
|
-
});
|
|
13490
|
-
}
|
|
13491
|
-
isDone = true;
|
|
13492
|
-
};
|
|
13493
|
-
task().catch((error) => exitEmitter.next(new Error(getErrorMessage(error))));
|
|
13558
|
+
this.task(symbol, context).catch((error) => exitEmitter.next(new Error(getErrorMessage(error))));
|
|
13494
13559
|
return () => {
|
|
13495
13560
|
backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName }, true);
|
|
13496
13561
|
backtest$1.strategyGlobalService
|
|
@@ -13499,7 +13564,7 @@ class BacktestUtils {
|
|
|
13499
13564
|
if (pendingSignal) {
|
|
13500
13565
|
return;
|
|
13501
13566
|
}
|
|
13502
|
-
if (!
|
|
13567
|
+
if (!this._isDone) {
|
|
13503
13568
|
await doneBacktestSubject.next({
|
|
13504
13569
|
exchangeName: context.exchangeName,
|
|
13505
13570
|
strategyName: context.strategyName,
|
|
@@ -13507,9 +13572,9 @@ class BacktestUtils {
|
|
|
13507
13572
|
symbol,
|
|
13508
13573
|
});
|
|
13509
13574
|
}
|
|
13510
|
-
|
|
13575
|
+
this._isDone = true;
|
|
13511
13576
|
});
|
|
13512
|
-
|
|
13577
|
+
this._isStopped = true;
|
|
13513
13578
|
};
|
|
13514
13579
|
};
|
|
13515
13580
|
/**
|
|
@@ -13525,8 +13590,8 @@ class BacktestUtils {
|
|
|
13525
13590
|
*
|
|
13526
13591
|
* @example
|
|
13527
13592
|
* ```typescript
|
|
13528
|
-
*
|
|
13529
|
-
* await
|
|
13593
|
+
* const instance = new BacktestInstance();
|
|
13594
|
+
* await instance.stop("BTCUSDT", "my-strategy");
|
|
13530
13595
|
* ```
|
|
13531
13596
|
*/
|
|
13532
13597
|
this.stop = async (symbol, strategyName) => {
|
|
@@ -13545,7 +13610,8 @@ class BacktestUtils {
|
|
|
13545
13610
|
*
|
|
13546
13611
|
* @example
|
|
13547
13612
|
* ```typescript
|
|
13548
|
-
* const
|
|
13613
|
+
* const instance = new BacktestInstance();
|
|
13614
|
+
* const stats = await instance.getData("BTCUSDT", "my-strategy");
|
|
13549
13615
|
* console.log(stats.sharpeRatio, stats.winRate);
|
|
13550
13616
|
* ```
|
|
13551
13617
|
*/
|
|
@@ -13565,7 +13631,8 @@ class BacktestUtils {
|
|
|
13565
13631
|
*
|
|
13566
13632
|
* @example
|
|
13567
13633
|
* ```typescript
|
|
13568
|
-
* const
|
|
13634
|
+
* const instance = new BacktestInstance();
|
|
13635
|
+
* const markdown = await instance.getReport("BTCUSDT", "my-strategy");
|
|
13569
13636
|
* console.log(markdown);
|
|
13570
13637
|
* ```
|
|
13571
13638
|
*/
|
|
@@ -13585,11 +13652,12 @@ class BacktestUtils {
|
|
|
13585
13652
|
*
|
|
13586
13653
|
* @example
|
|
13587
13654
|
* ```typescript
|
|
13655
|
+
* const instance = new BacktestInstance();
|
|
13588
13656
|
* // Save to default path: ./dump/backtest/my-strategy.md
|
|
13589
|
-
* await
|
|
13657
|
+
* await instance.dump("BTCUSDT", "my-strategy");
|
|
13590
13658
|
*
|
|
13591
13659
|
* // Save to custom path: ./custom/path/my-strategy.md
|
|
13592
|
-
* await
|
|
13660
|
+
* await instance.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
13593
13661
|
* ```
|
|
13594
13662
|
*/
|
|
13595
13663
|
this.dump = async (symbol, strategyName, path) => {
|
|
@@ -13603,7 +13671,10 @@ class BacktestUtils {
|
|
|
13603
13671
|
}
|
|
13604
13672
|
}
|
|
13605
13673
|
/**
|
|
13606
|
-
*
|
|
13674
|
+
* Utility class for backtest operations.
|
|
13675
|
+
*
|
|
13676
|
+
* Provides simplified access to backtestCommandService.run() with logging.
|
|
13677
|
+
* Exported as singleton instance for convenient usage.
|
|
13607
13678
|
*
|
|
13608
13679
|
* @example
|
|
13609
13680
|
* ```typescript
|
|
@@ -13614,150 +13685,59 @@ class BacktestUtils {
|
|
|
13614
13685
|
* exchangeName: "my-exchange",
|
|
13615
13686
|
* frameName: "1d-backtest"
|
|
13616
13687
|
* })) {
|
|
13617
|
-
*
|
|
13618
|
-
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
13619
|
-
* }
|
|
13620
|
-
* }
|
|
13621
|
-
* ```
|
|
13622
|
-
*/
|
|
13623
|
-
const Backtest = new BacktestUtils();
|
|
13624
|
-
|
|
13625
|
-
const LIVE_METHOD_NAME_RUN = "LiveUtils.run";
|
|
13626
|
-
const LIVE_METHOD_NAME_BACKGROUND = "LiveUtils.background";
|
|
13627
|
-
const LIVE_METHOD_NAME_STOP = "LiveUtils.stop";
|
|
13628
|
-
const LIVE_METHOD_NAME_GET_REPORT = "LiveUtils.getReport";
|
|
13629
|
-
const LIVE_METHOD_NAME_DUMP = "LiveUtils.dump";
|
|
13630
|
-
/**
|
|
13631
|
-
* Utility class for live trading operations.
|
|
13632
|
-
*
|
|
13633
|
-
* Provides simplified access to liveCommandService.run() with logging.
|
|
13634
|
-
* Exported as singleton instance for convenient usage.
|
|
13635
|
-
*
|
|
13636
|
-
* Features:
|
|
13637
|
-
* - Infinite async generator (never completes)
|
|
13638
|
-
* - Crash recovery via persisted state
|
|
13639
|
-
* - Real-time progression with Date.now()
|
|
13640
|
-
*
|
|
13641
|
-
* @example
|
|
13642
|
-
* ```typescript
|
|
13643
|
-
* import { Live } from "./classes/Live";
|
|
13644
|
-
*
|
|
13645
|
-
* // Infinite loop - use Ctrl+C to stop
|
|
13646
|
-
* for await (const result of Live.run("BTCUSDT", {
|
|
13647
|
-
* strategyName: "my-strategy",
|
|
13648
|
-
* exchangeName: "my-exchange",
|
|
13649
|
-
* frameName: ""
|
|
13650
|
-
* })) {
|
|
13651
|
-
* if (result.action === "opened") {
|
|
13652
|
-
* console.log("Signal opened:", result.signal);
|
|
13653
|
-
* } else if (result.action === "closed") {
|
|
13654
|
-
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
13655
|
-
* }
|
|
13688
|
+
* console.log("Closed signal PNL:", result.pnl.pnlPercentage);
|
|
13656
13689
|
* }
|
|
13657
13690
|
* ```
|
|
13658
13691
|
*/
|
|
13659
|
-
class
|
|
13692
|
+
class BacktestUtils {
|
|
13660
13693
|
constructor() {
|
|
13661
13694
|
/**
|
|
13662
|
-
*
|
|
13663
|
-
*
|
|
13664
|
-
|
|
13665
|
-
|
|
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 = memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, (symbol, strategyName) => new BacktestInstance(symbol, strategyName));
|
|
13699
|
+
/**
|
|
13700
|
+
* Runs backtest for a symbol with context propagation.
|
|
13666
13701
|
*
|
|
13667
13702
|
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
13668
|
-
* @param context - Execution context with strategy and
|
|
13669
|
-
* @returns
|
|
13703
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
13704
|
+
* @returns Async generator yielding closed signals with PNL
|
|
13670
13705
|
*/
|
|
13671
13706
|
this.run = (symbol, context) => {
|
|
13672
|
-
|
|
13673
|
-
|
|
13674
|
-
context,
|
|
13675
|
-
});
|
|
13676
|
-
{
|
|
13677
|
-
backtest$1.liveMarkdownService.clear({ symbol, strategyName: context.strategyName });
|
|
13678
|
-
backtest$1.scheduleMarkdownService.clear({ symbol, strategyName: context.strategyName });
|
|
13679
|
-
}
|
|
13680
|
-
{
|
|
13681
|
-
backtest$1.strategyGlobalService.clear({ symbol, strategyName: context.strategyName });
|
|
13682
|
-
}
|
|
13683
|
-
{
|
|
13684
|
-
const { riskName } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
13685
|
-
riskName && backtest$1.riskGlobalService.clear(riskName);
|
|
13686
|
-
}
|
|
13687
|
-
return backtest$1.liveCommandService.run(symbol, context);
|
|
13707
|
+
const instance = this._getInstance(symbol, context.strategyName);
|
|
13708
|
+
return instance.run(symbol, context);
|
|
13688
13709
|
};
|
|
13689
13710
|
/**
|
|
13690
|
-
* Runs
|
|
13711
|
+
* Runs backtest in background without yielding results.
|
|
13691
13712
|
*
|
|
13692
|
-
* Consumes all
|
|
13693
|
-
*
|
|
13694
|
-
* Useful for running live trading for side effects only (callbacks, persistence).
|
|
13713
|
+
* Consumes all backtest results internally without exposing them.
|
|
13714
|
+
* Useful for running backtests for side effects only (callbacks, logging).
|
|
13695
13715
|
*
|
|
13696
13716
|
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
13697
|
-
* @param context - Execution context with strategy and
|
|
13717
|
+
* @param context - Execution context with strategy, exchange, and frame names
|
|
13698
13718
|
* @returns Cancellation closure
|
|
13699
13719
|
*
|
|
13700
13720
|
* @example
|
|
13701
13721
|
* ```typescript
|
|
13702
|
-
* // Run
|
|
13703
|
-
*
|
|
13704
|
-
* await Live.background("BTCUSDT", {
|
|
13722
|
+
* // Run backtest silently, only callbacks will fire
|
|
13723
|
+
* await Backtest.background("BTCUSDT", {
|
|
13705
13724
|
* strategyName: "my-strategy",
|
|
13706
|
-
* exchangeName: "my-exchange"
|
|
13725
|
+
* exchangeName: "my-exchange",
|
|
13726
|
+
* frameName: "1d-backtest"
|
|
13707
13727
|
* });
|
|
13728
|
+
* console.log("Backtest completed");
|
|
13708
13729
|
* ```
|
|
13709
13730
|
*/
|
|
13710
13731
|
this.background = (symbol, context) => {
|
|
13711
|
-
|
|
13712
|
-
|
|
13713
|
-
context,
|
|
13714
|
-
});
|
|
13715
|
-
let isStopped = false;
|
|
13716
|
-
let isDone = false;
|
|
13717
|
-
const task = async () => {
|
|
13718
|
-
for await (const signal of this.run(symbol, context)) {
|
|
13719
|
-
if (signal?.action === "closed" && isStopped) {
|
|
13720
|
-
break;
|
|
13721
|
-
}
|
|
13722
|
-
}
|
|
13723
|
-
if (!isDone) {
|
|
13724
|
-
await doneLiveSubject.next({
|
|
13725
|
-
exchangeName: context.exchangeName,
|
|
13726
|
-
strategyName: context.strategyName,
|
|
13727
|
-
backtest: false,
|
|
13728
|
-
symbol,
|
|
13729
|
-
});
|
|
13730
|
-
}
|
|
13731
|
-
isDone = true;
|
|
13732
|
-
};
|
|
13733
|
-
task().catch((error) => exitEmitter.next(new Error(getErrorMessage(error))));
|
|
13734
|
-
return () => {
|
|
13735
|
-
backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName }, false);
|
|
13736
|
-
backtest$1.strategyGlobalService
|
|
13737
|
-
.getPendingSignal(symbol, context.strategyName)
|
|
13738
|
-
.then(async (pendingSignal) => {
|
|
13739
|
-
if (pendingSignal) {
|
|
13740
|
-
return;
|
|
13741
|
-
}
|
|
13742
|
-
if (!isDone) {
|
|
13743
|
-
await doneLiveSubject.next({
|
|
13744
|
-
exchangeName: context.exchangeName,
|
|
13745
|
-
strategyName: context.strategyName,
|
|
13746
|
-
backtest: false,
|
|
13747
|
-
symbol,
|
|
13748
|
-
});
|
|
13749
|
-
}
|
|
13750
|
-
isDone = true;
|
|
13751
|
-
});
|
|
13752
|
-
isStopped = true;
|
|
13753
|
-
};
|
|
13732
|
+
const instance = this._getInstance(symbol, context.strategyName);
|
|
13733
|
+
return instance.background(symbol, context);
|
|
13754
13734
|
};
|
|
13755
13735
|
/**
|
|
13756
13736
|
* Stops the strategy from generating new signals.
|
|
13757
13737
|
*
|
|
13758
13738
|
* Sets internal flag to prevent strategy from opening new signals.
|
|
13759
13739
|
* Current active signal (if any) will complete normally.
|
|
13760
|
-
*
|
|
13740
|
+
* Backtest will stop at the next safe point (idle state or after signal closes).
|
|
13761
13741
|
*
|
|
13762
13742
|
* @param symbol - Trading pair symbol
|
|
13763
13743
|
* @param strategyName - Strategy name to stop
|
|
@@ -13765,19 +13745,16 @@ class LiveUtils {
|
|
|
13765
13745
|
*
|
|
13766
13746
|
* @example
|
|
13767
13747
|
* ```typescript
|
|
13768
|
-
* // Stop
|
|
13769
|
-
* await
|
|
13748
|
+
* // Stop strategy after some condition
|
|
13749
|
+
* await Backtest.stop("BTCUSDT", "my-strategy");
|
|
13770
13750
|
* ```
|
|
13771
13751
|
*/
|
|
13772
13752
|
this.stop = async (symbol, strategyName) => {
|
|
13773
|
-
|
|
13774
|
-
|
|
13775
|
-
strategyName,
|
|
13776
|
-
});
|
|
13777
|
-
await backtest$1.strategyGlobalService.stop({ symbol, strategyName }, false);
|
|
13753
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
13754
|
+
return await instance.stop(symbol, strategyName);
|
|
13778
13755
|
};
|
|
13779
13756
|
/**
|
|
13780
|
-
* Gets statistical data from all
|
|
13757
|
+
* Gets statistical data from all closed signals for a symbol-strategy pair.
|
|
13781
13758
|
*
|
|
13782
13759
|
* @param symbol - Trading pair symbol
|
|
13783
13760
|
* @param strategyName - Strategy name to get data for
|
|
@@ -13785,19 +13762,16 @@ class LiveUtils {
|
|
|
13785
13762
|
*
|
|
13786
13763
|
* @example
|
|
13787
13764
|
* ```typescript
|
|
13788
|
-
* const stats = await
|
|
13765
|
+
* const stats = await Backtest.getData("BTCUSDT", "my-strategy");
|
|
13789
13766
|
* console.log(stats.sharpeRatio, stats.winRate);
|
|
13790
13767
|
* ```
|
|
13791
13768
|
*/
|
|
13792
13769
|
this.getData = async (symbol, strategyName) => {
|
|
13793
|
-
|
|
13794
|
-
|
|
13795
|
-
strategyName,
|
|
13796
|
-
});
|
|
13797
|
-
return await backtest$1.liveMarkdownService.getData(symbol, strategyName);
|
|
13770
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
13771
|
+
return await instance.getData(symbol, strategyName);
|
|
13798
13772
|
};
|
|
13799
13773
|
/**
|
|
13800
|
-
* Generates markdown report with all
|
|
13774
|
+
* Generates markdown report with all closed signals for a symbol-strategy pair.
|
|
13801
13775
|
*
|
|
13802
13776
|
* @param symbol - Trading pair symbol
|
|
13803
13777
|
* @param strategyName - Strategy name to generate report for
|
|
@@ -13805,59 +13779,535 @@ class LiveUtils {
|
|
|
13805
13779
|
*
|
|
13806
13780
|
* @example
|
|
13807
13781
|
* ```typescript
|
|
13808
|
-
* const markdown = await
|
|
13782
|
+
* const markdown = await Backtest.getReport("BTCUSDT", "my-strategy");
|
|
13809
13783
|
* console.log(markdown);
|
|
13810
13784
|
* ```
|
|
13811
13785
|
*/
|
|
13812
13786
|
this.getReport = async (symbol, strategyName) => {
|
|
13813
|
-
|
|
13814
|
-
|
|
13815
|
-
strategyName,
|
|
13816
|
-
});
|
|
13817
|
-
return await backtest$1.liveMarkdownService.getReport(symbol, strategyName);
|
|
13787
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
13788
|
+
return await instance.getReport(symbol, strategyName);
|
|
13818
13789
|
};
|
|
13819
13790
|
/**
|
|
13820
13791
|
* Saves strategy report to disk.
|
|
13821
13792
|
*
|
|
13822
13793
|
* @param symbol - Trading pair symbol
|
|
13823
13794
|
* @param strategyName - Strategy name to save report for
|
|
13824
|
-
* @param path - Optional directory path to save report (default: "./dump/
|
|
13795
|
+
* @param path - Optional directory path to save report (default: "./dump/backtest")
|
|
13825
13796
|
*
|
|
13826
13797
|
* @example
|
|
13827
13798
|
* ```typescript
|
|
13828
|
-
* // Save to default path: ./dump/
|
|
13829
|
-
* await
|
|
13799
|
+
* // Save to default path: ./dump/backtest/my-strategy.md
|
|
13800
|
+
* await Backtest.dump("BTCUSDT", "my-strategy");
|
|
13830
13801
|
*
|
|
13831
13802
|
* // Save to custom path: ./custom/path/my-strategy.md
|
|
13832
|
-
* await
|
|
13803
|
+
* await Backtest.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
13833
13804
|
* ```
|
|
13834
13805
|
*/
|
|
13835
13806
|
this.dump = async (symbol, strategyName, path) => {
|
|
13836
|
-
|
|
13837
|
-
|
|
13838
|
-
|
|
13839
|
-
|
|
13840
|
-
|
|
13841
|
-
|
|
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()));
|
|
13842
13826
|
};
|
|
13843
13827
|
}
|
|
13844
13828
|
}
|
|
13845
13829
|
/**
|
|
13846
|
-
* Singleton instance of
|
|
13830
|
+
* Singleton instance of BacktestUtils for convenient backtest operations.
|
|
13847
13831
|
*
|
|
13848
13832
|
* @example
|
|
13849
13833
|
* ```typescript
|
|
13850
|
-
* import {
|
|
13834
|
+
* import { Backtest } from "./classes/Backtest";
|
|
13851
13835
|
*
|
|
13852
|
-
* for await (const result of
|
|
13836
|
+
* for await (const result of Backtest.run("BTCUSDT", {
|
|
13853
13837
|
* strategyName: "my-strategy",
|
|
13854
13838
|
* exchangeName: "my-exchange",
|
|
13839
|
+
* frameName: "1d-backtest"
|
|
13855
13840
|
* })) {
|
|
13856
|
-
*
|
|
13841
|
+
* if (result.action === "closed") {
|
|
13842
|
+
* console.log("PNL:", result.pnl.pnlPercentage);
|
|
13843
|
+
* }
|
|
13857
13844
|
* }
|
|
13858
13845
|
* ```
|
|
13859
13846
|
*/
|
|
13860
|
-
const
|
|
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 = 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
|
+
};
|
|
13958
|
+
/**
|
|
13959
|
+
* Runs live trading for a symbol with context propagation.
|
|
13960
|
+
*
|
|
13961
|
+
* Infinite async generator with crash recovery support.
|
|
13962
|
+
* Process can crash and restart - state will be recovered from disk.
|
|
13963
|
+
*
|
|
13964
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
13965
|
+
* @param context - Execution context with strategy and exchange names
|
|
13966
|
+
* @returns Infinite async generator yielding opened and closed signals
|
|
13967
|
+
*/
|
|
13968
|
+
this.run = (symbol, context) => {
|
|
13969
|
+
backtest$1.loggerService.info(LIVE_METHOD_NAME_RUN, {
|
|
13970
|
+
symbol,
|
|
13971
|
+
context,
|
|
13972
|
+
});
|
|
13973
|
+
{
|
|
13974
|
+
backtest$1.liveMarkdownService.clear({ symbol, strategyName: context.strategyName });
|
|
13975
|
+
backtest$1.scheduleMarkdownService.clear({ symbol, strategyName: context.strategyName });
|
|
13976
|
+
}
|
|
13977
|
+
{
|
|
13978
|
+
backtest$1.strategyGlobalService.clear({ symbol, strategyName: context.strategyName });
|
|
13979
|
+
}
|
|
13980
|
+
{
|
|
13981
|
+
const { riskName } = backtest$1.strategySchemaService.get(context.strategyName);
|
|
13982
|
+
riskName && backtest$1.riskGlobalService.clear(riskName);
|
|
13983
|
+
}
|
|
13984
|
+
return backtest$1.liveCommandService.run(symbol, context);
|
|
13985
|
+
};
|
|
13986
|
+
/**
|
|
13987
|
+
* Runs live trading in background without yielding results.
|
|
13988
|
+
*
|
|
13989
|
+
* Consumes all live trading results internally without exposing them.
|
|
13990
|
+
* Infinite loop - will run until process is stopped or crashes.
|
|
13991
|
+
* Useful for running live trading for side effects only (callbacks, persistence).
|
|
13992
|
+
*
|
|
13993
|
+
* @param symbol - Trading pair symbol (e.g., "BTCUSDT")
|
|
13994
|
+
* @param context - Execution context with strategy and exchange names
|
|
13995
|
+
* @returns Cancellation closure
|
|
13996
|
+
*
|
|
13997
|
+
* @example
|
|
13998
|
+
* ```typescript
|
|
13999
|
+
* const instance = new LiveInstance();
|
|
14000
|
+
* const cancel = instance.background("BTCUSDT", {
|
|
14001
|
+
* strategyName: "my-strategy",
|
|
14002
|
+
* exchangeName: "my-exchange"
|
|
14003
|
+
* });
|
|
14004
|
+
* ```
|
|
14005
|
+
*/
|
|
14006
|
+
this.background = (symbol, context) => {
|
|
14007
|
+
backtest$1.loggerService.info(LIVE_METHOD_NAME_BACKGROUND, {
|
|
14008
|
+
symbol,
|
|
14009
|
+
context,
|
|
14010
|
+
});
|
|
14011
|
+
this.task(symbol, context).catch((error) => exitEmitter.next(new Error(getErrorMessage(error))));
|
|
14012
|
+
return () => {
|
|
14013
|
+
backtest$1.strategyGlobalService.stop({ symbol, strategyName: context.strategyName }, false);
|
|
14014
|
+
backtest$1.strategyGlobalService
|
|
14015
|
+
.getPendingSignal(symbol, context.strategyName)
|
|
14016
|
+
.then(async (pendingSignal) => {
|
|
14017
|
+
if (pendingSignal) {
|
|
14018
|
+
return;
|
|
14019
|
+
}
|
|
14020
|
+
if (!this._isDone) {
|
|
14021
|
+
await doneLiveSubject.next({
|
|
14022
|
+
exchangeName: context.exchangeName,
|
|
14023
|
+
strategyName: context.strategyName,
|
|
14024
|
+
backtest: false,
|
|
14025
|
+
symbol,
|
|
14026
|
+
});
|
|
14027
|
+
}
|
|
14028
|
+
this._isDone = true;
|
|
14029
|
+
});
|
|
14030
|
+
this._isStopped = true;
|
|
14031
|
+
};
|
|
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 = 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
|
+
};
|
|
14222
|
+
/**
|
|
14223
|
+
* Gets statistical data from all live trading events for a symbol-strategy pair.
|
|
14224
|
+
*
|
|
14225
|
+
* @param symbol - Trading pair symbol
|
|
14226
|
+
* @param strategyName - Strategy name to get data for
|
|
14227
|
+
* @returns Promise resolving to statistical data object
|
|
14228
|
+
*
|
|
14229
|
+
* @example
|
|
14230
|
+
* ```typescript
|
|
14231
|
+
* const stats = await Live.getData("BTCUSDT", "my-strategy");
|
|
14232
|
+
* console.log(stats.sharpeRatio, stats.winRate);
|
|
14233
|
+
* ```
|
|
14234
|
+
*/
|
|
14235
|
+
this.getData = async (symbol, strategyName) => {
|
|
14236
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
14237
|
+
return await instance.getData(symbol, strategyName);
|
|
14238
|
+
};
|
|
14239
|
+
/**
|
|
14240
|
+
* Generates markdown report with all events for a symbol-strategy pair.
|
|
14241
|
+
*
|
|
14242
|
+
* @param symbol - Trading pair symbol
|
|
14243
|
+
* @param strategyName - Strategy name to generate report for
|
|
14244
|
+
* @returns Promise resolving to markdown formatted report string
|
|
14245
|
+
*
|
|
14246
|
+
* @example
|
|
14247
|
+
* ```typescript
|
|
14248
|
+
* const markdown = await Live.getReport("BTCUSDT", "my-strategy");
|
|
14249
|
+
* console.log(markdown);
|
|
14250
|
+
* ```
|
|
14251
|
+
*/
|
|
14252
|
+
this.getReport = async (symbol, strategyName) => {
|
|
14253
|
+
const instance = this._getInstance(symbol, strategyName);
|
|
14254
|
+
return await instance.getReport(symbol, strategyName);
|
|
14255
|
+
};
|
|
14256
|
+
/**
|
|
14257
|
+
* Saves strategy report to disk.
|
|
14258
|
+
*
|
|
14259
|
+
* @param symbol - Trading pair symbol
|
|
14260
|
+
* @param strategyName - Strategy name to save report for
|
|
14261
|
+
* @param path - Optional directory path to save report (default: "./dump/live")
|
|
14262
|
+
*
|
|
14263
|
+
* @example
|
|
14264
|
+
* ```typescript
|
|
14265
|
+
* // Save to default path: ./dump/live/my-strategy.md
|
|
14266
|
+
* await Live.dump("BTCUSDT", "my-strategy");
|
|
14267
|
+
*
|
|
14268
|
+
* // Save to custom path: ./custom/path/my-strategy.md
|
|
14269
|
+
* await Live.dump("BTCUSDT", "my-strategy", "./custom/path");
|
|
14270
|
+
* ```
|
|
14271
|
+
*/
|
|
14272
|
+
this.dump = async (symbol, strategyName, path) => {
|
|
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()));
|
|
14292
|
+
};
|
|
14293
|
+
}
|
|
14294
|
+
}
|
|
14295
|
+
/**
|
|
14296
|
+
* Singleton instance of LiveUtils for convenient live trading operations.
|
|
14297
|
+
*
|
|
14298
|
+
* @example
|
|
14299
|
+
* ```typescript
|
|
14300
|
+
* import { Live } from "./classes/Live";
|
|
14301
|
+
*
|
|
14302
|
+
* for await (const result of Live.run("BTCUSDT", {
|
|
14303
|
+
* strategyName: "my-strategy",
|
|
14304
|
+
* exchangeName: "my-exchange",
|
|
14305
|
+
* })) {
|
|
14306
|
+
* console.log("Result:", result.action);
|
|
14307
|
+
* }
|
|
14308
|
+
* ```
|
|
14309
|
+
*/
|
|
14310
|
+
const Live = new LiveUtils();
|
|
13861
14311
|
|
|
13862
14312
|
const SCHEDULE_METHOD_NAME_GET_DATA = "ScheduleUtils.getData";
|
|
13863
14313
|
const SCHEDULE_METHOD_NAME_GET_REPORT = "ScheduleUtils.getReport";
|
|
@@ -14087,27 +14537,108 @@ const WALKER_METHOD_NAME_STOP = "WalkerUtils.stop";
|
|
|
14087
14537
|
const WALKER_METHOD_NAME_GET_DATA = "WalkerUtils.getData";
|
|
14088
14538
|
const WALKER_METHOD_NAME_GET_REPORT = "WalkerUtils.getReport";
|
|
14089
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";
|
|
14090
14542
|
/**
|
|
14091
|
-
*
|
|
14543
|
+
* Internal task function that runs walker and handles completion.
|
|
14544
|
+
* Consumes walker results and updates instance state flags.
|
|
14092
14545
|
*
|
|
14093
|
-
*
|
|
14094
|
-
*
|
|
14095
|
-
*
|
|
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.
|
|
14096
14579
|
*
|
|
14097
14580
|
* @example
|
|
14098
14581
|
* ```typescript
|
|
14099
|
-
*
|
|
14582
|
+
* const instance = new WalkerInstance("BTCUSDT", "my-walker");
|
|
14100
14583
|
*
|
|
14101
|
-
* for await (const result of
|
|
14584
|
+
* for await (const result of instance.run("BTCUSDT", {
|
|
14102
14585
|
* walkerName: "my-walker"
|
|
14103
14586
|
* })) {
|
|
14104
14587
|
* console.log("Progress:", result.strategiesTested, "/", result.totalStrategies);
|
|
14105
|
-
* console.log("Best strategy:", result.bestStrategy, result.bestMetric);
|
|
14106
14588
|
* }
|
|
14107
14589
|
* ```
|
|
14108
14590
|
*/
|
|
14109
|
-
class
|
|
14110
|
-
|
|
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 = 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
|
+
};
|
|
14111
14642
|
/**
|
|
14112
14643
|
* Runs walker comparison for a symbol with context propagation.
|
|
14113
14644
|
*
|
|
@@ -14160,11 +14691,10 @@ class WalkerUtils {
|
|
|
14160
14691
|
*
|
|
14161
14692
|
* @example
|
|
14162
14693
|
* ```typescript
|
|
14163
|
-
*
|
|
14164
|
-
*
|
|
14694
|
+
* const instance = new WalkerInstance();
|
|
14695
|
+
* const cancel = instance.background("BTCUSDT", {
|
|
14165
14696
|
* walkerName: "my-walker"
|
|
14166
14697
|
* });
|
|
14167
|
-
* console.log("Walker comparison completed");
|
|
14168
14698
|
* ```
|
|
14169
14699
|
*/
|
|
14170
14700
|
this.background = (symbol, context) => {
|
|
@@ -14173,31 +14703,13 @@ class WalkerUtils {
|
|
|
14173
14703
|
context,
|
|
14174
14704
|
});
|
|
14175
14705
|
const walkerSchema = backtest$1.walkerSchemaService.get(context.walkerName);
|
|
14176
|
-
|
|
14177
|
-
let isDone = false;
|
|
14178
|
-
const task = async () => {
|
|
14179
|
-
for await (const _ of this.run(symbol, context)) {
|
|
14180
|
-
if (isStopped) {
|
|
14181
|
-
break;
|
|
14182
|
-
}
|
|
14183
|
-
}
|
|
14184
|
-
if (!isDone) {
|
|
14185
|
-
await doneWalkerSubject.next({
|
|
14186
|
-
exchangeName: walkerSchema.exchangeName,
|
|
14187
|
-
strategyName: context.walkerName,
|
|
14188
|
-
backtest: true,
|
|
14189
|
-
symbol,
|
|
14190
|
-
});
|
|
14191
|
-
}
|
|
14192
|
-
isDone = true;
|
|
14193
|
-
};
|
|
14194
|
-
task().catch((error) => exitEmitter.next(new Error(getErrorMessage(error))));
|
|
14706
|
+
this.task(symbol, context).catch((error) => exitEmitter.next(new Error(getErrorMessage(error))));
|
|
14195
14707
|
return () => {
|
|
14196
14708
|
for (const strategyName of walkerSchema.strategies) {
|
|
14197
14709
|
backtest$1.strategyGlobalService.stop({ symbol, strategyName }, true);
|
|
14198
14710
|
walkerStopSubject.next({ symbol, strategyName, walkerName: context.walkerName });
|
|
14199
14711
|
}
|
|
14200
|
-
if (!
|
|
14712
|
+
if (!this._isDone) {
|
|
14201
14713
|
doneWalkerSubject.next({
|
|
14202
14714
|
exchangeName: walkerSchema.exchangeName,
|
|
14203
14715
|
strategyName: context.walkerName,
|
|
@@ -14205,8 +14717,8 @@ class WalkerUtils {
|
|
|
14205
14717
|
symbol,
|
|
14206
14718
|
});
|
|
14207
14719
|
}
|
|
14208
|
-
|
|
14209
|
-
|
|
14720
|
+
this._isDone = true;
|
|
14721
|
+
this._isStopped = true;
|
|
14210
14722
|
};
|
|
14211
14723
|
};
|
|
14212
14724
|
/**
|
|
@@ -14228,8 +14740,8 @@ class WalkerUtils {
|
|
|
14228
14740
|
*
|
|
14229
14741
|
* @example
|
|
14230
14742
|
* ```typescript
|
|
14231
|
-
*
|
|
14232
|
-
* await
|
|
14743
|
+
* const instance = new WalkerInstance();
|
|
14744
|
+
* await instance.stop("BTCUSDT", "my-walker");
|
|
14233
14745
|
* ```
|
|
14234
14746
|
*/
|
|
14235
14747
|
this.stop = async (symbol, walkerName) => {
|
|
@@ -14252,7 +14764,8 @@ class WalkerUtils {
|
|
|
14252
14764
|
*
|
|
14253
14765
|
* @example
|
|
14254
14766
|
* ```typescript
|
|
14255
|
-
* const
|
|
14767
|
+
* const instance = new WalkerInstance();
|
|
14768
|
+
* const results = await instance.getData("BTCUSDT", "my-walker");
|
|
14256
14769
|
* console.log(results.bestStrategy, results.bestMetric);
|
|
14257
14770
|
* ```
|
|
14258
14771
|
*/
|
|
@@ -14276,7 +14789,8 @@ class WalkerUtils {
|
|
|
14276
14789
|
*
|
|
14277
14790
|
* @example
|
|
14278
14791
|
* ```typescript
|
|
14279
|
-
* const
|
|
14792
|
+
* const instance = new WalkerInstance();
|
|
14793
|
+
* const markdown = await instance.getReport("BTCUSDT", "my-walker");
|
|
14280
14794
|
* console.log(markdown);
|
|
14281
14795
|
* ```
|
|
14282
14796
|
*/
|
|
@@ -14300,11 +14814,12 @@ class WalkerUtils {
|
|
|
14300
14814
|
*
|
|
14301
14815
|
* @example
|
|
14302
14816
|
* ```typescript
|
|
14817
|
+
* const instance = new WalkerInstance();
|
|
14303
14818
|
* // Save to default path: ./dump/walker/my-walker.md
|
|
14304
|
-
* await
|
|
14819
|
+
* await instance.dump("BTCUSDT", "my-walker");
|
|
14305
14820
|
*
|
|
14306
14821
|
* // Save to custom path: ./custom/path/my-walker.md
|
|
14307
|
-
* await
|
|
14822
|
+
* await instance.dump("BTCUSDT", "my-walker", "./custom/path");
|
|
14308
14823
|
* ```
|
|
14309
14824
|
*/
|
|
14310
14825
|
this.dump = async (symbol, walkerName, path) => {
|
|
@@ -14321,6 +14836,166 @@ class WalkerUtils {
|
|
|
14321
14836
|
};
|
|
14322
14837
|
}
|
|
14323
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 = 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
|
+
}
|
|
14324
14999
|
/**
|
|
14325
15000
|
* Singleton instance of WalkerUtils for convenient walker operations.
|
|
14326
15001
|
*
|