backtest-kit 1.6.5 → 1.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.cjs +482 -33
- package/build/index.mjs +482 -34
- package/package.json +1 -1
- package/types.d.ts +340 -1
package/build/index.mjs
CHANGED
|
@@ -3777,7 +3777,7 @@ const GET_RISK_FN = (dto, backtest, self) => {
|
|
|
3777
3777
|
* @param backtest - Whether running in backtest mode
|
|
3778
3778
|
* @returns Unique string key for memoization
|
|
3779
3779
|
*/
|
|
3780
|
-
const CREATE_KEY_FN$
|
|
3780
|
+
const CREATE_KEY_FN$b = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
3781
3781
|
/**
|
|
3782
3782
|
* Callback function for emitting ping events to pingSubject.
|
|
3783
3783
|
*
|
|
@@ -3838,7 +3838,7 @@ class StrategyConnectionService {
|
|
|
3838
3838
|
* @param strategyName - Name of registered strategy schema
|
|
3839
3839
|
* @returns Configured ClientStrategy instance
|
|
3840
3840
|
*/
|
|
3841
|
-
this.getStrategy = memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$
|
|
3841
|
+
this.getStrategy = memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$b(symbol, strategyName, backtest), (symbol, strategyName, backtest) => {
|
|
3842
3842
|
const { riskName = "", riskList = [], getSignal, interval, callbacks, } = this.strategySchemaService.get(strategyName);
|
|
3843
3843
|
return new ClientStrategy({
|
|
3844
3844
|
symbol,
|
|
@@ -4005,7 +4005,7 @@ class StrategyConnectionService {
|
|
|
4005
4005
|
ctx,
|
|
4006
4006
|
});
|
|
4007
4007
|
if (ctx) {
|
|
4008
|
-
const key = CREATE_KEY_FN$
|
|
4008
|
+
const key = CREATE_KEY_FN$b(ctx.symbol, ctx.strategyName, backtest);
|
|
4009
4009
|
this.getStrategy.clear(key);
|
|
4010
4010
|
}
|
|
4011
4011
|
else {
|
|
@@ -4615,7 +4615,7 @@ class ClientRisk {
|
|
|
4615
4615
|
* @param backtest - Whether running in backtest mode
|
|
4616
4616
|
* @returns Unique string key for memoization
|
|
4617
4617
|
*/
|
|
4618
|
-
const CREATE_KEY_FN$
|
|
4618
|
+
const CREATE_KEY_FN$a = (riskName, backtest) => `${riskName}:${backtest ? "backtest" : "live"}`;
|
|
4619
4619
|
/**
|
|
4620
4620
|
* Callback function for emitting risk rejection events to riskSubject.
|
|
4621
4621
|
*
|
|
@@ -4687,7 +4687,7 @@ class RiskConnectionService {
|
|
|
4687
4687
|
* @param backtest - True if backtest mode, false if live mode
|
|
4688
4688
|
* @returns Configured ClientRisk instance
|
|
4689
4689
|
*/
|
|
4690
|
-
this.getRisk = memoize(([riskName, backtest]) => CREATE_KEY_FN$
|
|
4690
|
+
this.getRisk = memoize(([riskName, backtest]) => CREATE_KEY_FN$a(riskName, backtest), (riskName, backtest) => {
|
|
4691
4691
|
const schema = this.riskSchemaService.get(riskName);
|
|
4692
4692
|
return new ClientRisk({
|
|
4693
4693
|
...schema,
|
|
@@ -4754,7 +4754,7 @@ class RiskConnectionService {
|
|
|
4754
4754
|
backtest,
|
|
4755
4755
|
});
|
|
4756
4756
|
if (ctx) {
|
|
4757
|
-
const key = CREATE_KEY_FN$
|
|
4757
|
+
const key = CREATE_KEY_FN$a(ctx.riskName, backtest);
|
|
4758
4758
|
this.getRisk.clear(key);
|
|
4759
4759
|
}
|
|
4760
4760
|
else {
|
|
@@ -7793,7 +7793,7 @@ const DEFAULT_COLUMNS = Object.freeze({ ...COLUMN_CONFIG });
|
|
|
7793
7793
|
* @param backtest - Whether running in backtest mode
|
|
7794
7794
|
* @returns Unique string key for memoization
|
|
7795
7795
|
*/
|
|
7796
|
-
const CREATE_KEY_FN$
|
|
7796
|
+
const CREATE_KEY_FN$9 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
7797
7797
|
/**
|
|
7798
7798
|
* Checks if a value is unsafe for display (not a number, NaN, or Infinity).
|
|
7799
7799
|
*
|
|
@@ -8002,7 +8002,7 @@ class BacktestMarkdownService {
|
|
|
8002
8002
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
8003
8003
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
8004
8004
|
*/
|
|
8005
|
-
this.getStorage = memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$
|
|
8005
|
+
this.getStorage = memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$9(symbol, strategyName, backtest), () => new ReportStorage$5());
|
|
8006
8006
|
/**
|
|
8007
8007
|
* Processes tick events and accumulates closed signals.
|
|
8008
8008
|
* Should be called from IStrategyCallbacks.onTick.
|
|
@@ -8140,7 +8140,7 @@ class BacktestMarkdownService {
|
|
|
8140
8140
|
ctx,
|
|
8141
8141
|
});
|
|
8142
8142
|
if (ctx) {
|
|
8143
|
-
const key = CREATE_KEY_FN$
|
|
8143
|
+
const key = CREATE_KEY_FN$9(ctx.symbol, ctx.strategyName, backtest);
|
|
8144
8144
|
this.getStorage.clear(key);
|
|
8145
8145
|
}
|
|
8146
8146
|
else {
|
|
@@ -8173,7 +8173,7 @@ class BacktestMarkdownService {
|
|
|
8173
8173
|
* @param backtest - Whether running in backtest mode
|
|
8174
8174
|
* @returns Unique string key for memoization
|
|
8175
8175
|
*/
|
|
8176
|
-
const CREATE_KEY_FN$
|
|
8176
|
+
const CREATE_KEY_FN$8 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
8177
8177
|
/**
|
|
8178
8178
|
* Checks if a value is unsafe for display (not a number, NaN, or Infinity).
|
|
8179
8179
|
*
|
|
@@ -8503,7 +8503,7 @@ class LiveMarkdownService {
|
|
|
8503
8503
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
8504
8504
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
8505
8505
|
*/
|
|
8506
|
-
this.getStorage = memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$
|
|
8506
|
+
this.getStorage = memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$8(symbol, strategyName, backtest), () => new ReportStorage$4());
|
|
8507
8507
|
/**
|
|
8508
8508
|
* Processes tick events and accumulates all event types.
|
|
8509
8509
|
* Should be called from IStrategyCallbacks.onTick.
|
|
@@ -8651,7 +8651,7 @@ class LiveMarkdownService {
|
|
|
8651
8651
|
ctx,
|
|
8652
8652
|
});
|
|
8653
8653
|
if (ctx) {
|
|
8654
|
-
const key = CREATE_KEY_FN$
|
|
8654
|
+
const key = CREATE_KEY_FN$8(ctx.symbol, ctx.strategyName, backtest);
|
|
8655
8655
|
this.getStorage.clear(key);
|
|
8656
8656
|
}
|
|
8657
8657
|
else {
|
|
@@ -8684,7 +8684,7 @@ class LiveMarkdownService {
|
|
|
8684
8684
|
* @param backtest - Whether running in backtest mode
|
|
8685
8685
|
* @returns Unique string key for memoization
|
|
8686
8686
|
*/
|
|
8687
|
-
const CREATE_KEY_FN$
|
|
8687
|
+
const CREATE_KEY_FN$7 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
8688
8688
|
/** Maximum number of events to store in schedule reports */
|
|
8689
8689
|
const MAX_EVENTS$4 = 250;
|
|
8690
8690
|
/**
|
|
@@ -8920,7 +8920,7 @@ class ScheduleMarkdownService {
|
|
|
8920
8920
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
8921
8921
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
8922
8922
|
*/
|
|
8923
|
-
this.getStorage = memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$
|
|
8923
|
+
this.getStorage = memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$7(symbol, strategyName, backtest), () => new ReportStorage$3());
|
|
8924
8924
|
/**
|
|
8925
8925
|
* Processes tick events and accumulates scheduled/opened/cancelled events.
|
|
8926
8926
|
* Should be called from signalEmitter subscription.
|
|
@@ -9062,7 +9062,7 @@ class ScheduleMarkdownService {
|
|
|
9062
9062
|
ctx,
|
|
9063
9063
|
});
|
|
9064
9064
|
if (ctx) {
|
|
9065
|
-
const key = CREATE_KEY_FN$
|
|
9065
|
+
const key = CREATE_KEY_FN$7(ctx.symbol, ctx.strategyName, backtest);
|
|
9066
9066
|
this.getStorage.clear(key);
|
|
9067
9067
|
}
|
|
9068
9068
|
else {
|
|
@@ -9095,7 +9095,7 @@ class ScheduleMarkdownService {
|
|
|
9095
9095
|
* @param backtest - Whether running in backtest mode
|
|
9096
9096
|
* @returns Unique string key for memoization
|
|
9097
9097
|
*/
|
|
9098
|
-
const CREATE_KEY_FN$
|
|
9098
|
+
const CREATE_KEY_FN$6 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
9099
9099
|
/**
|
|
9100
9100
|
* Calculates percentile value from sorted array.
|
|
9101
9101
|
*/
|
|
@@ -9313,7 +9313,7 @@ class PerformanceMarkdownService {
|
|
|
9313
9313
|
* Memoized function to get or create PerformanceStorage for a symbol-strategy-backtest triple.
|
|
9314
9314
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
9315
9315
|
*/
|
|
9316
|
-
this.getStorage = memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$
|
|
9316
|
+
this.getStorage = memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$6(symbol, strategyName, backtest), () => new PerformanceStorage());
|
|
9317
9317
|
/**
|
|
9318
9318
|
* Processes performance events and accumulates metrics.
|
|
9319
9319
|
* Should be called from performance tracking code.
|
|
@@ -9418,7 +9418,7 @@ class PerformanceMarkdownService {
|
|
|
9418
9418
|
ctx,
|
|
9419
9419
|
});
|
|
9420
9420
|
if (ctx) {
|
|
9421
|
-
const key = CREATE_KEY_FN$
|
|
9421
|
+
const key = CREATE_KEY_FN$6(ctx.symbol, ctx.strategyName, backtest);
|
|
9422
9422
|
this.getStorage.clear(key);
|
|
9423
9423
|
}
|
|
9424
9424
|
else {
|
|
@@ -9852,7 +9852,7 @@ class WalkerMarkdownService {
|
|
|
9852
9852
|
* @param backtest - Whether running in backtest mode
|
|
9853
9853
|
* @returns Unique string key for memoization
|
|
9854
9854
|
*/
|
|
9855
|
-
const CREATE_KEY_FN$
|
|
9855
|
+
const CREATE_KEY_FN$5 = (strategyName, backtest) => `${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
9856
9856
|
const HEATMAP_METHOD_NAME_GET_DATA = "HeatMarkdownService.getData";
|
|
9857
9857
|
const HEATMAP_METHOD_NAME_GET_REPORT = "HeatMarkdownService.getReport";
|
|
9858
9858
|
const HEATMAP_METHOD_NAME_DUMP = "HeatMarkdownService.dump";
|
|
@@ -10196,7 +10196,7 @@ class HeatMarkdownService {
|
|
|
10196
10196
|
* Memoized function to get or create HeatmapStorage for a strategy and backtest mode.
|
|
10197
10197
|
* Each strategy + backtest mode combination gets its own isolated heatmap storage instance.
|
|
10198
10198
|
*/
|
|
10199
|
-
this.getStorage = memoize(([strategyName, backtest]) => CREATE_KEY_FN$
|
|
10199
|
+
this.getStorage = memoize(([strategyName, backtest]) => CREATE_KEY_FN$5(strategyName, backtest), () => new HeatmapStorage());
|
|
10200
10200
|
/**
|
|
10201
10201
|
* Processes tick events and accumulates closed signals.
|
|
10202
10202
|
* Should be called from signal emitter subscription.
|
|
@@ -10332,7 +10332,7 @@ class HeatMarkdownService {
|
|
|
10332
10332
|
ctx,
|
|
10333
10333
|
});
|
|
10334
10334
|
if (ctx) {
|
|
10335
|
-
const key = CREATE_KEY_FN$
|
|
10335
|
+
const key = CREATE_KEY_FN$5(ctx.strategyName, backtest);
|
|
10336
10336
|
this.getStorage.clear(key);
|
|
10337
10337
|
}
|
|
10338
10338
|
else {
|
|
@@ -12550,7 +12550,7 @@ class ClientPartial {
|
|
|
12550
12550
|
* @param backtest - Whether running in backtest mode
|
|
12551
12551
|
* @returns Unique string key for memoization
|
|
12552
12552
|
*/
|
|
12553
|
-
const CREATE_KEY_FN$
|
|
12553
|
+
const CREATE_KEY_FN$4 = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
12554
12554
|
/**
|
|
12555
12555
|
* Callback function for emitting profit events to partialProfitSubject.
|
|
12556
12556
|
*
|
|
@@ -12648,7 +12648,7 @@ class PartialConnectionService {
|
|
|
12648
12648
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
12649
12649
|
* Value: ClientPartial instance with logger and event emitters
|
|
12650
12650
|
*/
|
|
12651
|
-
this.getPartial = memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
12651
|
+
this.getPartial = memoize(([signalId, backtest]) => CREATE_KEY_FN$4(signalId, backtest), (signalId, backtest) => {
|
|
12652
12652
|
return new ClientPartial({
|
|
12653
12653
|
signalId,
|
|
12654
12654
|
logger: this.loggerService,
|
|
@@ -12738,7 +12738,7 @@ class PartialConnectionService {
|
|
|
12738
12738
|
const partial = this.getPartial(data.id, backtest);
|
|
12739
12739
|
await partial.waitForInit(symbol, data.strategyName);
|
|
12740
12740
|
await partial.clear(symbol, data, priceClose, backtest);
|
|
12741
|
-
const key = CREATE_KEY_FN$
|
|
12741
|
+
const key = CREATE_KEY_FN$4(data.id, backtest);
|
|
12742
12742
|
this.getPartial.clear(key);
|
|
12743
12743
|
};
|
|
12744
12744
|
}
|
|
@@ -12750,7 +12750,7 @@ class PartialConnectionService {
|
|
|
12750
12750
|
* @param param0 - Tuple of symbol, strategyName and backtest boolean
|
|
12751
12751
|
* @returns Unique string key for memoization
|
|
12752
12752
|
*/
|
|
12753
|
-
const CREATE_KEY_FN$
|
|
12753
|
+
const CREATE_KEY_FN$3 = ([symbol, strategyName, backtest]) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
12754
12754
|
/** Maximum number of events to store in partial reports */
|
|
12755
12755
|
const MAX_EVENTS$1 = 250;
|
|
12756
12756
|
/**
|
|
@@ -12925,7 +12925,7 @@ class PartialMarkdownService {
|
|
|
12925
12925
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
12926
12926
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
12927
12927
|
*/
|
|
12928
|
-
this.getStorage = memoize(CREATE_KEY_FN$
|
|
12928
|
+
this.getStorage = memoize(CREATE_KEY_FN$3, () => new ReportStorage$1());
|
|
12929
12929
|
/**
|
|
12930
12930
|
* Processes profit events and accumulates them.
|
|
12931
12931
|
* Should be called from partialProfitSubject subscription.
|
|
@@ -13072,7 +13072,7 @@ class PartialMarkdownService {
|
|
|
13072
13072
|
ctx,
|
|
13073
13073
|
});
|
|
13074
13074
|
if (ctx) {
|
|
13075
|
-
const key = CREATE_KEY_FN$
|
|
13075
|
+
const key = CREATE_KEY_FN$3([ctx.symbol, ctx.strategyName, backtest]);
|
|
13076
13076
|
this.getStorage.clear(key);
|
|
13077
13077
|
}
|
|
13078
13078
|
else {
|
|
@@ -13515,7 +13515,7 @@ class ConfigValidationService {
|
|
|
13515
13515
|
* @param backtest - Whether running in backtest mode
|
|
13516
13516
|
* @returns Unique string key for memoization
|
|
13517
13517
|
*/
|
|
13518
|
-
const CREATE_KEY_FN = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
13518
|
+
const CREATE_KEY_FN$2 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
13519
13519
|
/** Maximum number of events to store in risk reports */
|
|
13520
13520
|
const MAX_EVENTS = 250;
|
|
13521
13521
|
/**
|
|
@@ -13660,7 +13660,7 @@ class RiskMarkdownService {
|
|
|
13660
13660
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
13661
13661
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
13662
13662
|
*/
|
|
13663
|
-
this.getStorage = memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN(symbol, strategyName, backtest), () => new ReportStorage());
|
|
13663
|
+
this.getStorage = memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$2(symbol, strategyName, backtest), () => new ReportStorage());
|
|
13664
13664
|
/**
|
|
13665
13665
|
* Processes risk rejection events and accumulates them.
|
|
13666
13666
|
* Should be called from riskSubject subscription.
|
|
@@ -13788,7 +13788,7 @@ class RiskMarkdownService {
|
|
|
13788
13788
|
ctx,
|
|
13789
13789
|
});
|
|
13790
13790
|
if (ctx) {
|
|
13791
|
-
const key = CREATE_KEY_FN(ctx.symbol, ctx.strategyName, backtest);
|
|
13791
|
+
const key = CREATE_KEY_FN$2(ctx.symbol, ctx.strategyName, backtest);
|
|
13792
13792
|
this.getStorage.clear(key);
|
|
13793
13793
|
}
|
|
13794
13794
|
else {
|
|
@@ -19500,7 +19500,15 @@ const INTERVAL_MINUTES = {
|
|
|
19500
19500
|
"6h": 360,
|
|
19501
19501
|
"8h": 480,
|
|
19502
19502
|
};
|
|
19503
|
-
|
|
19503
|
+
/**
|
|
19504
|
+
* Create a cache key string from strategy name, exchange name, and backtest mode.
|
|
19505
|
+
*
|
|
19506
|
+
* @param strategyName - Name of the strategy
|
|
19507
|
+
* @param exchangeName - Name of the exchange
|
|
19508
|
+
* @param backtest - Whether running in backtest mode
|
|
19509
|
+
* @returns Cache key string
|
|
19510
|
+
*/
|
|
19511
|
+
const CREATE_KEY_FN$1 = (strategyName, exchangeName, backtest) => `${strategyName}:${exchangeName}:${backtest ? "backtest" : "live"}`;
|
|
19504
19512
|
/**
|
|
19505
19513
|
* Instance class for caching function results with timeframe-based invalidation.
|
|
19506
19514
|
*
|
|
@@ -19576,7 +19584,7 @@ class CacheInstance {
|
|
|
19576
19584
|
throw new Error(`CacheInstance unknown cache ttl interval=${this.interval}`);
|
|
19577
19585
|
}
|
|
19578
19586
|
}
|
|
19579
|
-
const key =
|
|
19587
|
+
const key = CREATE_KEY_FN$1(backtest$1.methodContextService.context.strategyName, backtest$1.methodContextService.context.exchangeName, backtest$1.executionContextService.context.backtest);
|
|
19580
19588
|
const currentWhen = backtest$1.executionContextService.context.when;
|
|
19581
19589
|
const cached = this._cacheMap.get(key);
|
|
19582
19590
|
if (cached) {
|
|
@@ -19614,7 +19622,7 @@ class CacheInstance {
|
|
|
19614
19622
|
* ```
|
|
19615
19623
|
*/
|
|
19616
19624
|
this.clear = () => {
|
|
19617
|
-
const key =
|
|
19625
|
+
const key = CREATE_KEY_FN$1(backtest$1.methodContextService.context.strategyName, backtest$1.methodContextService.context.exchangeName, backtest$1.executionContextService.context.backtest);
|
|
19618
19626
|
this._cacheMap.delete(key);
|
|
19619
19627
|
};
|
|
19620
19628
|
}
|
|
@@ -19762,4 +19770,444 @@ class CacheUtils {
|
|
|
19762
19770
|
*/
|
|
19763
19771
|
const Cache = new CacheUtils();
|
|
19764
19772
|
|
|
19765
|
-
|
|
19773
|
+
/** Maximum number of notifications to store in history */
|
|
19774
|
+
const MAX_NOTIFICATIONS = 250;
|
|
19775
|
+
/** Function to create unique notification IDs */
|
|
19776
|
+
const CREATE_KEY_FN = () => randomString();
|
|
19777
|
+
/**
|
|
19778
|
+
* Instance class for notification history management.
|
|
19779
|
+
*
|
|
19780
|
+
* Contains all business logic for notification collection from emitters/subjects.
|
|
19781
|
+
* Stores notifications in chronological order with automatic limit management.
|
|
19782
|
+
*
|
|
19783
|
+
* @example
|
|
19784
|
+
* ```typescript
|
|
19785
|
+
* const instance = new NotificationInstance();
|
|
19786
|
+
* await instance.waitForInit();
|
|
19787
|
+
*
|
|
19788
|
+
* // Get all notifications
|
|
19789
|
+
* const all = instance.getData();
|
|
19790
|
+
*
|
|
19791
|
+
* // Process notifications with type discrimination
|
|
19792
|
+
* all.forEach(notification => {
|
|
19793
|
+
* switch (notification.type) {
|
|
19794
|
+
* case "signal.closed":
|
|
19795
|
+
* console.log(`Closed: ${notification.pnlPercentage}%`);
|
|
19796
|
+
* break;
|
|
19797
|
+
* case "partial.loss":
|
|
19798
|
+
* if (notification.level >= 30) {
|
|
19799
|
+
* alert("High loss!");
|
|
19800
|
+
* }
|
|
19801
|
+
* break;
|
|
19802
|
+
* case "risk.rejection":
|
|
19803
|
+
* console.warn(notification.rejectionNote);
|
|
19804
|
+
* break;
|
|
19805
|
+
* }
|
|
19806
|
+
* });
|
|
19807
|
+
*
|
|
19808
|
+
* // Clear history
|
|
19809
|
+
* instance.clear();
|
|
19810
|
+
* ```
|
|
19811
|
+
*/
|
|
19812
|
+
class NotificationInstance {
|
|
19813
|
+
constructor() {
|
|
19814
|
+
/** Internal notification history storage (newest first) */
|
|
19815
|
+
this._notifications = [];
|
|
19816
|
+
/**
|
|
19817
|
+
* Processes signal events and creates appropriate notifications.
|
|
19818
|
+
*/
|
|
19819
|
+
this._handleSignal = async (data) => {
|
|
19820
|
+
if (data.action === "opened") {
|
|
19821
|
+
this._addNotification({
|
|
19822
|
+
type: "signal.opened",
|
|
19823
|
+
id: CREATE_KEY_FN(),
|
|
19824
|
+
timestamp: data.signal.pendingAt,
|
|
19825
|
+
backtest: data.backtest,
|
|
19826
|
+
symbol: data.symbol,
|
|
19827
|
+
strategyName: data.strategyName,
|
|
19828
|
+
exchangeName: data.exchangeName,
|
|
19829
|
+
signalId: data.signal.id,
|
|
19830
|
+
position: data.signal.position,
|
|
19831
|
+
priceOpen: data.signal.priceOpen,
|
|
19832
|
+
priceTakeProfit: data.signal.priceTakeProfit,
|
|
19833
|
+
priceStopLoss: data.signal.priceStopLoss,
|
|
19834
|
+
note: data.signal.note,
|
|
19835
|
+
});
|
|
19836
|
+
}
|
|
19837
|
+
else if (data.action === "closed") {
|
|
19838
|
+
const durationMs = data.closeTimestamp - data.signal.pendingAt;
|
|
19839
|
+
const durationMin = Math.round(durationMs / 60000);
|
|
19840
|
+
this._addNotification({
|
|
19841
|
+
type: "signal.closed",
|
|
19842
|
+
id: CREATE_KEY_FN(),
|
|
19843
|
+
timestamp: data.closeTimestamp,
|
|
19844
|
+
backtest: data.backtest,
|
|
19845
|
+
symbol: data.symbol,
|
|
19846
|
+
strategyName: data.strategyName,
|
|
19847
|
+
exchangeName: data.exchangeName,
|
|
19848
|
+
signalId: data.signal.id,
|
|
19849
|
+
position: data.signal.position,
|
|
19850
|
+
priceOpen: data.signal.priceOpen,
|
|
19851
|
+
priceClose: data.currentPrice,
|
|
19852
|
+
pnlPercentage: data.pnl.pnlPercentage,
|
|
19853
|
+
closeReason: data.closeReason,
|
|
19854
|
+
duration: durationMin,
|
|
19855
|
+
note: data.signal.note,
|
|
19856
|
+
});
|
|
19857
|
+
}
|
|
19858
|
+
else if (data.action === "scheduled") {
|
|
19859
|
+
this._addNotification({
|
|
19860
|
+
type: "signal.scheduled",
|
|
19861
|
+
id: CREATE_KEY_FN(),
|
|
19862
|
+
timestamp: data.signal.scheduledAt,
|
|
19863
|
+
backtest: data.backtest,
|
|
19864
|
+
symbol: data.symbol,
|
|
19865
|
+
strategyName: data.strategyName,
|
|
19866
|
+
exchangeName: data.exchangeName,
|
|
19867
|
+
signalId: data.signal.id,
|
|
19868
|
+
position: data.signal.position,
|
|
19869
|
+
priceOpen: data.signal.priceOpen,
|
|
19870
|
+
scheduledAt: data.signal.scheduledAt,
|
|
19871
|
+
currentPrice: data.currentPrice,
|
|
19872
|
+
});
|
|
19873
|
+
}
|
|
19874
|
+
else if (data.action === "cancelled") {
|
|
19875
|
+
const durationMs = data.closeTimestamp - data.signal.scheduledAt;
|
|
19876
|
+
const durationMin = Math.round(durationMs / 60000);
|
|
19877
|
+
this._addNotification({
|
|
19878
|
+
type: "signal.cancelled",
|
|
19879
|
+
id: CREATE_KEY_FN(),
|
|
19880
|
+
timestamp: data.closeTimestamp,
|
|
19881
|
+
backtest: data.backtest,
|
|
19882
|
+
symbol: data.symbol,
|
|
19883
|
+
strategyName: data.strategyName,
|
|
19884
|
+
exchangeName: data.exchangeName,
|
|
19885
|
+
signalId: data.signal.id,
|
|
19886
|
+
position: data.signal.position,
|
|
19887
|
+
cancelReason: data.reason,
|
|
19888
|
+
cancelId: data.cancelId,
|
|
19889
|
+
duration: durationMin,
|
|
19890
|
+
});
|
|
19891
|
+
}
|
|
19892
|
+
};
|
|
19893
|
+
/**
|
|
19894
|
+
* Processes partial profit events.
|
|
19895
|
+
*/
|
|
19896
|
+
this._handlePartialProfit = async (data) => {
|
|
19897
|
+
this._addNotification({
|
|
19898
|
+
type: "partial.profit",
|
|
19899
|
+
id: CREATE_KEY_FN(),
|
|
19900
|
+
timestamp: data.timestamp,
|
|
19901
|
+
backtest: data.backtest,
|
|
19902
|
+
symbol: data.symbol,
|
|
19903
|
+
strategyName: data.strategyName,
|
|
19904
|
+
exchangeName: data.exchangeName,
|
|
19905
|
+
signalId: data.data.id,
|
|
19906
|
+
level: data.level,
|
|
19907
|
+
currentPrice: data.currentPrice,
|
|
19908
|
+
priceOpen: data.data.priceOpen,
|
|
19909
|
+
position: data.data.position,
|
|
19910
|
+
});
|
|
19911
|
+
};
|
|
19912
|
+
/**
|
|
19913
|
+
* Processes partial loss events.
|
|
19914
|
+
*/
|
|
19915
|
+
this._handlePartialLoss = async (data) => {
|
|
19916
|
+
this._addNotification({
|
|
19917
|
+
type: "partial.loss",
|
|
19918
|
+
id: CREATE_KEY_FN(),
|
|
19919
|
+
timestamp: data.timestamp,
|
|
19920
|
+
backtest: data.backtest,
|
|
19921
|
+
symbol: data.symbol,
|
|
19922
|
+
strategyName: data.strategyName,
|
|
19923
|
+
exchangeName: data.exchangeName,
|
|
19924
|
+
signalId: data.data.id,
|
|
19925
|
+
level: data.level,
|
|
19926
|
+
currentPrice: data.currentPrice,
|
|
19927
|
+
priceOpen: data.data.priceOpen,
|
|
19928
|
+
position: data.data.position,
|
|
19929
|
+
});
|
|
19930
|
+
};
|
|
19931
|
+
/**
|
|
19932
|
+
* Processes risk rejection events.
|
|
19933
|
+
*/
|
|
19934
|
+
this._handleRisk = async (data) => {
|
|
19935
|
+
this._addNotification({
|
|
19936
|
+
type: "risk.rejection",
|
|
19937
|
+
id: CREATE_KEY_FN(),
|
|
19938
|
+
timestamp: data.timestamp,
|
|
19939
|
+
backtest: data.backtest,
|
|
19940
|
+
symbol: data.symbol,
|
|
19941
|
+
strategyName: data.strategyName,
|
|
19942
|
+
exchangeName: data.exchangeName,
|
|
19943
|
+
rejectionNote: data.rejectionNote,
|
|
19944
|
+
rejectionId: data.rejectionId,
|
|
19945
|
+
activePositionCount: data.activePositionCount,
|
|
19946
|
+
currentPrice: data.currentPrice,
|
|
19947
|
+
pendingSignal: data.pendingSignal,
|
|
19948
|
+
});
|
|
19949
|
+
};
|
|
19950
|
+
/**
|
|
19951
|
+
* Processes done events (live/backtest).
|
|
19952
|
+
*/
|
|
19953
|
+
this._handleDoneLive = async (data) => {
|
|
19954
|
+
this._addNotification({
|
|
19955
|
+
type: "live.done",
|
|
19956
|
+
id: CREATE_KEY_FN(),
|
|
19957
|
+
timestamp: Date.now(),
|
|
19958
|
+
backtest: false,
|
|
19959
|
+
symbol: data.symbol,
|
|
19960
|
+
strategyName: data.strategyName,
|
|
19961
|
+
exchangeName: data.exchangeName,
|
|
19962
|
+
});
|
|
19963
|
+
};
|
|
19964
|
+
/**
|
|
19965
|
+
* Processes done events (backtest).
|
|
19966
|
+
*/
|
|
19967
|
+
this._handleDoneBacktest = async (data) => {
|
|
19968
|
+
this._addNotification({
|
|
19969
|
+
type: "backtest.done",
|
|
19970
|
+
id: CREATE_KEY_FN(),
|
|
19971
|
+
timestamp: Date.now(),
|
|
19972
|
+
backtest: true,
|
|
19973
|
+
symbol: data.symbol,
|
|
19974
|
+
strategyName: data.strategyName,
|
|
19975
|
+
exchangeName: data.exchangeName,
|
|
19976
|
+
});
|
|
19977
|
+
};
|
|
19978
|
+
/**
|
|
19979
|
+
* Processes error events.
|
|
19980
|
+
*/
|
|
19981
|
+
this._handleError = async (error) => {
|
|
19982
|
+
this._addNotification({
|
|
19983
|
+
type: "error.info",
|
|
19984
|
+
id: CREATE_KEY_FN(),
|
|
19985
|
+
timestamp: Date.now(),
|
|
19986
|
+
error: errorData(error),
|
|
19987
|
+
message: getErrorMessage(error),
|
|
19988
|
+
backtest: false,
|
|
19989
|
+
});
|
|
19990
|
+
};
|
|
19991
|
+
/**
|
|
19992
|
+
* Processes critical error events.
|
|
19993
|
+
*/
|
|
19994
|
+
this._handleCriticalError = async (error) => {
|
|
19995
|
+
this._addNotification({
|
|
19996
|
+
type: "error.critical",
|
|
19997
|
+
id: CREATE_KEY_FN(),
|
|
19998
|
+
timestamp: Date.now(),
|
|
19999
|
+
error: errorData(error),
|
|
20000
|
+
message: getErrorMessage(error),
|
|
20001
|
+
backtest: false,
|
|
20002
|
+
});
|
|
20003
|
+
};
|
|
20004
|
+
/**
|
|
20005
|
+
* Processes validation error events.
|
|
20006
|
+
*/
|
|
20007
|
+
this._handleValidationError = async (error) => {
|
|
20008
|
+
this._addNotification({
|
|
20009
|
+
type: "error.validation",
|
|
20010
|
+
id: CREATE_KEY_FN(),
|
|
20011
|
+
timestamp: Date.now(),
|
|
20012
|
+
error: errorData(error),
|
|
20013
|
+
message: getErrorMessage(error),
|
|
20014
|
+
backtest: false,
|
|
20015
|
+
});
|
|
20016
|
+
};
|
|
20017
|
+
/**
|
|
20018
|
+
* Processes progress events.
|
|
20019
|
+
*/
|
|
20020
|
+
this._handleProgressBacktest = async (data) => {
|
|
20021
|
+
this._addNotification({
|
|
20022
|
+
type: "progress.backtest",
|
|
20023
|
+
id: CREATE_KEY_FN(),
|
|
20024
|
+
timestamp: Date.now(),
|
|
20025
|
+
backtest: true,
|
|
20026
|
+
exchangeName: data.exchangeName,
|
|
20027
|
+
strategyName: data.strategyName,
|
|
20028
|
+
symbol: data.symbol,
|
|
20029
|
+
totalFrames: data.totalFrames,
|
|
20030
|
+
processedFrames: data.processedFrames,
|
|
20031
|
+
progress: data.progress,
|
|
20032
|
+
});
|
|
20033
|
+
};
|
|
20034
|
+
/**
|
|
20035
|
+
* Initializes notification system by subscribing to all emitters.
|
|
20036
|
+
* Uses singleshot to ensure initialization happens only once.
|
|
20037
|
+
* Automatically called on first use.
|
|
20038
|
+
*/
|
|
20039
|
+
this.waitForInit = singleshot(async () => {
|
|
20040
|
+
// Add bootstrap notification to mark initialization
|
|
20041
|
+
this._addNotification({
|
|
20042
|
+
type: "bootstrap",
|
|
20043
|
+
id: CREATE_KEY_FN(),
|
|
20044
|
+
timestamp: Date.now(),
|
|
20045
|
+
});
|
|
20046
|
+
// Signal events
|
|
20047
|
+
signalEmitter.subscribe(this._handleSignal);
|
|
20048
|
+
// Partial profit/loss events
|
|
20049
|
+
partialProfitSubject.subscribe(this._handlePartialProfit);
|
|
20050
|
+
partialLossSubject.subscribe(this._handlePartialLoss);
|
|
20051
|
+
// Risk events
|
|
20052
|
+
riskSubject.subscribe(this._handleRisk);
|
|
20053
|
+
// Done events
|
|
20054
|
+
doneLiveSubject.subscribe(this._handleDoneLive);
|
|
20055
|
+
doneBacktestSubject.subscribe(this._handleDoneBacktest);
|
|
20056
|
+
// Error events
|
|
20057
|
+
errorEmitter.subscribe(this._handleError);
|
|
20058
|
+
exitEmitter.subscribe(this._handleCriticalError);
|
|
20059
|
+
validationSubject.subscribe(this._handleValidationError);
|
|
20060
|
+
// Progress events
|
|
20061
|
+
progressBacktestEmitter.subscribe(this._handleProgressBacktest);
|
|
20062
|
+
});
|
|
20063
|
+
}
|
|
20064
|
+
/**
|
|
20065
|
+
* Adds notification to history with automatic limit management.
|
|
20066
|
+
*/
|
|
20067
|
+
_addNotification(notification) {
|
|
20068
|
+
this._notifications.unshift(notification);
|
|
20069
|
+
// Trim history if exceeded MAX_NOTIFICATIONS
|
|
20070
|
+
if (this._notifications.length > MAX_NOTIFICATIONS) {
|
|
20071
|
+
this._notifications.pop();
|
|
20072
|
+
}
|
|
20073
|
+
}
|
|
20074
|
+
/**
|
|
20075
|
+
* Returns all notifications in chronological order (newest first).
|
|
20076
|
+
*
|
|
20077
|
+
* @returns Array of strongly-typed notification objects
|
|
20078
|
+
*
|
|
20079
|
+
* @example
|
|
20080
|
+
* ```typescript
|
|
20081
|
+
* const notifications = instance.getData();
|
|
20082
|
+
*
|
|
20083
|
+
* notifications.forEach(notification => {
|
|
20084
|
+
* switch (notification.type) {
|
|
20085
|
+
* case "signal.closed":
|
|
20086
|
+
* console.log(`${notification.symbol}: ${notification.pnlPercentage}%`);
|
|
20087
|
+
* break;
|
|
20088
|
+
* case "partial.loss":
|
|
20089
|
+
* if (notification.level >= 30) {
|
|
20090
|
+
* console.warn(`High loss: ${notification.symbol}`);
|
|
20091
|
+
* }
|
|
20092
|
+
* break;
|
|
20093
|
+
* }
|
|
20094
|
+
* });
|
|
20095
|
+
* ```
|
|
20096
|
+
*/
|
|
20097
|
+
getData() {
|
|
20098
|
+
return [...this._notifications];
|
|
20099
|
+
}
|
|
20100
|
+
/**
|
|
20101
|
+
* Clears all notification history.
|
|
20102
|
+
*
|
|
20103
|
+
* @example
|
|
20104
|
+
* ```typescript
|
|
20105
|
+
* instance.clear();
|
|
20106
|
+
* ```
|
|
20107
|
+
*/
|
|
20108
|
+
clear() {
|
|
20109
|
+
this._notifications = [];
|
|
20110
|
+
}
|
|
20111
|
+
}
|
|
20112
|
+
/**
|
|
20113
|
+
* Public facade for notification operations.
|
|
20114
|
+
*
|
|
20115
|
+
* Automatically calls waitForInit on each userspace method call.
|
|
20116
|
+
* Provides simplified access to notification instance methods.
|
|
20117
|
+
*
|
|
20118
|
+
* @example
|
|
20119
|
+
* ```typescript
|
|
20120
|
+
* import { Notification } from "./classes/Notification";
|
|
20121
|
+
*
|
|
20122
|
+
* // Get all notifications
|
|
20123
|
+
* const all = await Notification.getData();
|
|
20124
|
+
*
|
|
20125
|
+
* // Process notifications with type discrimination
|
|
20126
|
+
* all.forEach(notification => {
|
|
20127
|
+
* switch (notification.type) {
|
|
20128
|
+
* case "signal.closed":
|
|
20129
|
+
* console.log(`Closed: ${notification.pnlPercentage}%`);
|
|
20130
|
+
* break;
|
|
20131
|
+
* case "partial.loss":
|
|
20132
|
+
* if (notification.level >= 30) {
|
|
20133
|
+
* alert("High loss!");
|
|
20134
|
+
* }
|
|
20135
|
+
* break;
|
|
20136
|
+
* case "risk.rejection":
|
|
20137
|
+
* console.warn(notification.rejectionNote);
|
|
20138
|
+
* break;
|
|
20139
|
+
* }
|
|
20140
|
+
* });
|
|
20141
|
+
*
|
|
20142
|
+
* // Clear history
|
|
20143
|
+
* await Notification.clear();
|
|
20144
|
+
* ```
|
|
20145
|
+
*/
|
|
20146
|
+
class NotificationUtils {
|
|
20147
|
+
constructor() {
|
|
20148
|
+
/** Internal instance containing business logic */
|
|
20149
|
+
this._instance = new NotificationInstance();
|
|
20150
|
+
}
|
|
20151
|
+
/**
|
|
20152
|
+
* Returns all notifications in chronological order (newest first).
|
|
20153
|
+
*
|
|
20154
|
+
* @returns Array of strongly-typed notification objects
|
|
20155
|
+
*
|
|
20156
|
+
* @example
|
|
20157
|
+
* ```typescript
|
|
20158
|
+
* const notifications = await Notification.getData();
|
|
20159
|
+
*
|
|
20160
|
+
* notifications.forEach(notification => {
|
|
20161
|
+
* switch (notification.type) {
|
|
20162
|
+
* case "signal.closed":
|
|
20163
|
+
* console.log(`${notification.symbol}: ${notification.pnlPercentage}%`);
|
|
20164
|
+
* break;
|
|
20165
|
+
* case "partial.loss":
|
|
20166
|
+
* if (notification.level >= 30) {
|
|
20167
|
+
* console.warn(`High loss: ${notification.symbol}`);
|
|
20168
|
+
* }
|
|
20169
|
+
* break;
|
|
20170
|
+
* }
|
|
20171
|
+
* });
|
|
20172
|
+
* ```
|
|
20173
|
+
*/
|
|
20174
|
+
async getData() {
|
|
20175
|
+
await this._instance.waitForInit();
|
|
20176
|
+
return this._instance.getData();
|
|
20177
|
+
}
|
|
20178
|
+
/**
|
|
20179
|
+
* Clears all notification history.
|
|
20180
|
+
*
|
|
20181
|
+
* @example
|
|
20182
|
+
* ```typescript
|
|
20183
|
+
* await Notification.clear();
|
|
20184
|
+
* ```
|
|
20185
|
+
*/
|
|
20186
|
+
async clear() {
|
|
20187
|
+
await this._instance.waitForInit();
|
|
20188
|
+
this._instance.clear();
|
|
20189
|
+
}
|
|
20190
|
+
}
|
|
20191
|
+
/**
|
|
20192
|
+
* Singleton instance of NotificationUtils for convenient notification access.
|
|
20193
|
+
*
|
|
20194
|
+
* @example
|
|
20195
|
+
* ```typescript
|
|
20196
|
+
* import { Notification } from "./classes/Notification";
|
|
20197
|
+
*
|
|
20198
|
+
* // Get all notifications
|
|
20199
|
+
* const all = await Notification.getData();
|
|
20200
|
+
*
|
|
20201
|
+
* // Filter by type using type discrimination
|
|
20202
|
+
* const closedSignals = all.filter(n => n.type === "signal.closed");
|
|
20203
|
+
* const highLosses = all.filter(n =>
|
|
20204
|
+
* n.type === "partial.loss" && n.level >= 30
|
|
20205
|
+
* );
|
|
20206
|
+
*
|
|
20207
|
+
* // Clear history
|
|
20208
|
+
* await Notification.clear();
|
|
20209
|
+
* ```
|
|
20210
|
+
*/
|
|
20211
|
+
const Notification = new NotificationUtils();
|
|
20212
|
+
|
|
20213
|
+
export { Backtest, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, MethodContextService, Notification, Optimizer, Partial, Performance, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Risk, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, cancel, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getColumns, getConfig, getDate, getDefaultColumns, getDefaultConfig, getMode, hasTradeContext, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenPing, listenPingOnce, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setColumns, setConfig, setLogger, stop, validate };
|