backtest-kit 1.6.4 → 1.6.6
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 +487 -42
- package/build/index.mjs +487 -43
- package/package.json +1 -1
- package/types.d.ts +330 -1
package/build/index.cjs
CHANGED
|
@@ -3734,11 +3734,22 @@ class RiskUtils {
|
|
|
3734
3734
|
*/
|
|
3735
3735
|
const Risk = new RiskUtils();
|
|
3736
3736
|
|
|
3737
|
+
/**
|
|
3738
|
+
* No-operation IRisk implementation.
|
|
3739
|
+
* Always allows signals and performs no actions.
|
|
3740
|
+
*/
|
|
3737
3741
|
const NOOP_RISK = {
|
|
3738
3742
|
checkSignal: () => Promise.resolve(true),
|
|
3739
3743
|
addSignal: () => Promise.resolve(),
|
|
3740
3744
|
removeSignal: () => Promise.resolve(),
|
|
3741
3745
|
};
|
|
3746
|
+
/**
|
|
3747
|
+
* Determines the appropriate IRisk instance based on provided riskName and riskList.
|
|
3748
|
+
* @param dto - Object containing riskName and riskList
|
|
3749
|
+
* @param backtest - Whether running in backtest mode
|
|
3750
|
+
* @param self - Reference to StrategyConnectionService instance
|
|
3751
|
+
* @returns Configured IRisk instance (single or merged)
|
|
3752
|
+
*/
|
|
3742
3753
|
const GET_RISK_FN = (dto, backtest, self) => {
|
|
3743
3754
|
const hasRiskName = !!dto.riskName;
|
|
3744
3755
|
const hasRiskList = !!dto.riskList?.length;
|
|
@@ -3768,7 +3779,7 @@ const GET_RISK_FN = (dto, backtest, self) => {
|
|
|
3768
3779
|
* @param backtest - Whether running in backtest mode
|
|
3769
3780
|
* @returns Unique string key for memoization
|
|
3770
3781
|
*/
|
|
3771
|
-
const CREATE_KEY_FN$
|
|
3782
|
+
const CREATE_KEY_FN$b = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
3772
3783
|
/**
|
|
3773
3784
|
* Callback function for emitting ping events to pingSubject.
|
|
3774
3785
|
*
|
|
@@ -3829,7 +3840,7 @@ class StrategyConnectionService {
|
|
|
3829
3840
|
* @param strategyName - Name of registered strategy schema
|
|
3830
3841
|
* @returns Configured ClientStrategy instance
|
|
3831
3842
|
*/
|
|
3832
|
-
this.getStrategy = functoolsKit.memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$
|
|
3843
|
+
this.getStrategy = functoolsKit.memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$b(symbol, strategyName, backtest), (symbol, strategyName, backtest) => {
|
|
3833
3844
|
const { riskName = "", riskList = [], getSignal, interval, callbacks, } = this.strategySchemaService.get(strategyName);
|
|
3834
3845
|
return new ClientStrategy({
|
|
3835
3846
|
symbol,
|
|
@@ -3996,7 +4007,7 @@ class StrategyConnectionService {
|
|
|
3996
4007
|
ctx,
|
|
3997
4008
|
});
|
|
3998
4009
|
if (ctx) {
|
|
3999
|
-
const key = CREATE_KEY_FN$
|
|
4010
|
+
const key = CREATE_KEY_FN$b(ctx.symbol, ctx.strategyName, backtest);
|
|
4000
4011
|
this.getStrategy.clear(key);
|
|
4001
4012
|
}
|
|
4002
4013
|
else {
|
|
@@ -4606,7 +4617,7 @@ class ClientRisk {
|
|
|
4606
4617
|
* @param backtest - Whether running in backtest mode
|
|
4607
4618
|
* @returns Unique string key for memoization
|
|
4608
4619
|
*/
|
|
4609
|
-
const CREATE_KEY_FN$
|
|
4620
|
+
const CREATE_KEY_FN$a = (riskName, backtest) => `${riskName}:${backtest ? "backtest" : "live"}`;
|
|
4610
4621
|
/**
|
|
4611
4622
|
* Callback function for emitting risk rejection events to riskSubject.
|
|
4612
4623
|
*
|
|
@@ -4678,7 +4689,7 @@ class RiskConnectionService {
|
|
|
4678
4689
|
* @param backtest - True if backtest mode, false if live mode
|
|
4679
4690
|
* @returns Configured ClientRisk instance
|
|
4680
4691
|
*/
|
|
4681
|
-
this.getRisk = functoolsKit.memoize(([riskName, backtest]) => CREATE_KEY_FN$
|
|
4692
|
+
this.getRisk = functoolsKit.memoize(([riskName, backtest]) => CREATE_KEY_FN$a(riskName, backtest), (riskName, backtest) => {
|
|
4682
4693
|
const schema = this.riskSchemaService.get(riskName);
|
|
4683
4694
|
return new ClientRisk({
|
|
4684
4695
|
...schema,
|
|
@@ -4745,7 +4756,7 @@ class RiskConnectionService {
|
|
|
4745
4756
|
backtest,
|
|
4746
4757
|
});
|
|
4747
4758
|
if (ctx) {
|
|
4748
|
-
const key = CREATE_KEY_FN$
|
|
4759
|
+
const key = CREATE_KEY_FN$a(ctx.riskName, backtest);
|
|
4749
4760
|
this.getRisk.clear(key);
|
|
4750
4761
|
}
|
|
4751
4762
|
else {
|
|
@@ -4978,9 +4989,6 @@ class StrategyCoreService {
|
|
|
4978
4989
|
symbol,
|
|
4979
4990
|
strategyName,
|
|
4980
4991
|
});
|
|
4981
|
-
if (!MethodContextService.hasContext()) {
|
|
4982
|
-
throw new Error("strategyCoreService getPendingSignal requires a method context");
|
|
4983
|
-
}
|
|
4984
4992
|
await this.validate(symbol, strategyName);
|
|
4985
4993
|
return await this.strategyConnectionService.getPendingSignal(backtest, symbol, strategyName);
|
|
4986
4994
|
};
|
|
@@ -4998,9 +5006,6 @@ class StrategyCoreService {
|
|
|
4998
5006
|
symbol,
|
|
4999
5007
|
strategyName,
|
|
5000
5008
|
});
|
|
5001
|
-
if (!MethodContextService.hasContext()) {
|
|
5002
|
-
throw new Error("strategyCoreService getScheduledSignal requires a method context");
|
|
5003
|
-
}
|
|
5004
5009
|
await this.validate(symbol, strategyName);
|
|
5005
5010
|
return await this.strategyConnectionService.getScheduledSignal(backtest, symbol, strategyName);
|
|
5006
5011
|
};
|
|
@@ -5020,9 +5025,6 @@ class StrategyCoreService {
|
|
|
5020
5025
|
strategyName,
|
|
5021
5026
|
backtest,
|
|
5022
5027
|
});
|
|
5023
|
-
if (!MethodContextService.hasContext()) {
|
|
5024
|
-
throw new Error("strategyCoreService getStopped requires a method context");
|
|
5025
|
-
}
|
|
5026
5028
|
await this.validate(symbol, strategyName);
|
|
5027
5029
|
return await this.strategyConnectionService.getStopped(backtest, symbol, strategyName);
|
|
5028
5030
|
};
|
|
@@ -7793,7 +7795,7 @@ const DEFAULT_COLUMNS = Object.freeze({ ...COLUMN_CONFIG });
|
|
|
7793
7795
|
* @param backtest - Whether running in backtest mode
|
|
7794
7796
|
* @returns Unique string key for memoization
|
|
7795
7797
|
*/
|
|
7796
|
-
const CREATE_KEY_FN$
|
|
7798
|
+
const CREATE_KEY_FN$9 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
7797
7799
|
/**
|
|
7798
7800
|
* Checks if a value is unsafe for display (not a number, NaN, or Infinity).
|
|
7799
7801
|
*
|
|
@@ -8002,7 +8004,7 @@ class BacktestMarkdownService {
|
|
|
8002
8004
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
8003
8005
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
8004
8006
|
*/
|
|
8005
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$
|
|
8007
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$9(symbol, strategyName, backtest), () => new ReportStorage$5());
|
|
8006
8008
|
/**
|
|
8007
8009
|
* Processes tick events and accumulates closed signals.
|
|
8008
8010
|
* Should be called from IStrategyCallbacks.onTick.
|
|
@@ -8140,7 +8142,7 @@ class BacktestMarkdownService {
|
|
|
8140
8142
|
ctx,
|
|
8141
8143
|
});
|
|
8142
8144
|
if (ctx) {
|
|
8143
|
-
const key = CREATE_KEY_FN$
|
|
8145
|
+
const key = CREATE_KEY_FN$9(ctx.symbol, ctx.strategyName, backtest);
|
|
8144
8146
|
this.getStorage.clear(key);
|
|
8145
8147
|
}
|
|
8146
8148
|
else {
|
|
@@ -8173,7 +8175,7 @@ class BacktestMarkdownService {
|
|
|
8173
8175
|
* @param backtest - Whether running in backtest mode
|
|
8174
8176
|
* @returns Unique string key for memoization
|
|
8175
8177
|
*/
|
|
8176
|
-
const CREATE_KEY_FN$
|
|
8178
|
+
const CREATE_KEY_FN$8 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
8177
8179
|
/**
|
|
8178
8180
|
* Checks if a value is unsafe for display (not a number, NaN, or Infinity).
|
|
8179
8181
|
*
|
|
@@ -8503,7 +8505,7 @@ class LiveMarkdownService {
|
|
|
8503
8505
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
8504
8506
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
8505
8507
|
*/
|
|
8506
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$
|
|
8508
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$8(symbol, strategyName, backtest), () => new ReportStorage$4());
|
|
8507
8509
|
/**
|
|
8508
8510
|
* Processes tick events and accumulates all event types.
|
|
8509
8511
|
* Should be called from IStrategyCallbacks.onTick.
|
|
@@ -8651,7 +8653,7 @@ class LiveMarkdownService {
|
|
|
8651
8653
|
ctx,
|
|
8652
8654
|
});
|
|
8653
8655
|
if (ctx) {
|
|
8654
|
-
const key = CREATE_KEY_FN$
|
|
8656
|
+
const key = CREATE_KEY_FN$8(ctx.symbol, ctx.strategyName, backtest);
|
|
8655
8657
|
this.getStorage.clear(key);
|
|
8656
8658
|
}
|
|
8657
8659
|
else {
|
|
@@ -8684,7 +8686,7 @@ class LiveMarkdownService {
|
|
|
8684
8686
|
* @param backtest - Whether running in backtest mode
|
|
8685
8687
|
* @returns Unique string key for memoization
|
|
8686
8688
|
*/
|
|
8687
|
-
const CREATE_KEY_FN$
|
|
8689
|
+
const CREATE_KEY_FN$7 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
8688
8690
|
/** Maximum number of events to store in schedule reports */
|
|
8689
8691
|
const MAX_EVENTS$4 = 250;
|
|
8690
8692
|
/**
|
|
@@ -8920,7 +8922,7 @@ class ScheduleMarkdownService {
|
|
|
8920
8922
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
8921
8923
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
8922
8924
|
*/
|
|
8923
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$
|
|
8925
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$7(symbol, strategyName, backtest), () => new ReportStorage$3());
|
|
8924
8926
|
/**
|
|
8925
8927
|
* Processes tick events and accumulates scheduled/opened/cancelled events.
|
|
8926
8928
|
* Should be called from signalEmitter subscription.
|
|
@@ -9062,7 +9064,7 @@ class ScheduleMarkdownService {
|
|
|
9062
9064
|
ctx,
|
|
9063
9065
|
});
|
|
9064
9066
|
if (ctx) {
|
|
9065
|
-
const key = CREATE_KEY_FN$
|
|
9067
|
+
const key = CREATE_KEY_FN$7(ctx.symbol, ctx.strategyName, backtest);
|
|
9066
9068
|
this.getStorage.clear(key);
|
|
9067
9069
|
}
|
|
9068
9070
|
else {
|
|
@@ -9095,7 +9097,7 @@ class ScheduleMarkdownService {
|
|
|
9095
9097
|
* @param backtest - Whether running in backtest mode
|
|
9096
9098
|
* @returns Unique string key for memoization
|
|
9097
9099
|
*/
|
|
9098
|
-
const CREATE_KEY_FN$
|
|
9100
|
+
const CREATE_KEY_FN$6 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
9099
9101
|
/**
|
|
9100
9102
|
* Calculates percentile value from sorted array.
|
|
9101
9103
|
*/
|
|
@@ -9313,7 +9315,7 @@ class PerformanceMarkdownService {
|
|
|
9313
9315
|
* Memoized function to get or create PerformanceStorage for a symbol-strategy-backtest triple.
|
|
9314
9316
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
9315
9317
|
*/
|
|
9316
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$
|
|
9318
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$6(symbol, strategyName, backtest), () => new PerformanceStorage());
|
|
9317
9319
|
/**
|
|
9318
9320
|
* Processes performance events and accumulates metrics.
|
|
9319
9321
|
* Should be called from performance tracking code.
|
|
@@ -9418,7 +9420,7 @@ class PerformanceMarkdownService {
|
|
|
9418
9420
|
ctx,
|
|
9419
9421
|
});
|
|
9420
9422
|
if (ctx) {
|
|
9421
|
-
const key = CREATE_KEY_FN$
|
|
9423
|
+
const key = CREATE_KEY_FN$6(ctx.symbol, ctx.strategyName, backtest);
|
|
9422
9424
|
this.getStorage.clear(key);
|
|
9423
9425
|
}
|
|
9424
9426
|
else {
|
|
@@ -9852,7 +9854,7 @@ class WalkerMarkdownService {
|
|
|
9852
9854
|
* @param backtest - Whether running in backtest mode
|
|
9853
9855
|
* @returns Unique string key for memoization
|
|
9854
9856
|
*/
|
|
9855
|
-
const CREATE_KEY_FN$
|
|
9857
|
+
const CREATE_KEY_FN$5 = (strategyName, backtest) => `${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
9856
9858
|
const HEATMAP_METHOD_NAME_GET_DATA = "HeatMarkdownService.getData";
|
|
9857
9859
|
const HEATMAP_METHOD_NAME_GET_REPORT = "HeatMarkdownService.getReport";
|
|
9858
9860
|
const HEATMAP_METHOD_NAME_DUMP = "HeatMarkdownService.dump";
|
|
@@ -10196,7 +10198,7 @@ class HeatMarkdownService {
|
|
|
10196
10198
|
* Memoized function to get or create HeatmapStorage for a strategy and backtest mode.
|
|
10197
10199
|
* Each strategy + backtest mode combination gets its own isolated heatmap storage instance.
|
|
10198
10200
|
*/
|
|
10199
|
-
this.getStorage = functoolsKit.memoize(([strategyName, backtest]) => CREATE_KEY_FN$
|
|
10201
|
+
this.getStorage = functoolsKit.memoize(([strategyName, backtest]) => CREATE_KEY_FN$5(strategyName, backtest), () => new HeatmapStorage());
|
|
10200
10202
|
/**
|
|
10201
10203
|
* Processes tick events and accumulates closed signals.
|
|
10202
10204
|
* Should be called from signal emitter subscription.
|
|
@@ -10332,7 +10334,7 @@ class HeatMarkdownService {
|
|
|
10332
10334
|
ctx,
|
|
10333
10335
|
});
|
|
10334
10336
|
if (ctx) {
|
|
10335
|
-
const key = CREATE_KEY_FN$
|
|
10337
|
+
const key = CREATE_KEY_FN$5(ctx.strategyName, backtest);
|
|
10336
10338
|
this.getStorage.clear(key);
|
|
10337
10339
|
}
|
|
10338
10340
|
else {
|
|
@@ -12550,7 +12552,7 @@ class ClientPartial {
|
|
|
12550
12552
|
* @param backtest - Whether running in backtest mode
|
|
12551
12553
|
* @returns Unique string key for memoization
|
|
12552
12554
|
*/
|
|
12553
|
-
const CREATE_KEY_FN$
|
|
12555
|
+
const CREATE_KEY_FN$4 = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
12554
12556
|
/**
|
|
12555
12557
|
* Callback function for emitting profit events to partialProfitSubject.
|
|
12556
12558
|
*
|
|
@@ -12648,7 +12650,7 @@ class PartialConnectionService {
|
|
|
12648
12650
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
12649
12651
|
* Value: ClientPartial instance with logger and event emitters
|
|
12650
12652
|
*/
|
|
12651
|
-
this.getPartial = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
12653
|
+
this.getPartial = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$4(signalId, backtest), (signalId, backtest) => {
|
|
12652
12654
|
return new ClientPartial({
|
|
12653
12655
|
signalId,
|
|
12654
12656
|
logger: this.loggerService,
|
|
@@ -12738,7 +12740,7 @@ class PartialConnectionService {
|
|
|
12738
12740
|
const partial = this.getPartial(data.id, backtest);
|
|
12739
12741
|
await partial.waitForInit(symbol, data.strategyName);
|
|
12740
12742
|
await partial.clear(symbol, data, priceClose, backtest);
|
|
12741
|
-
const key = CREATE_KEY_FN$
|
|
12743
|
+
const key = CREATE_KEY_FN$4(data.id, backtest);
|
|
12742
12744
|
this.getPartial.clear(key);
|
|
12743
12745
|
};
|
|
12744
12746
|
}
|
|
@@ -12750,7 +12752,7 @@ class PartialConnectionService {
|
|
|
12750
12752
|
* @param param0 - Tuple of symbol, strategyName and backtest boolean
|
|
12751
12753
|
* @returns Unique string key for memoization
|
|
12752
12754
|
*/
|
|
12753
|
-
const CREATE_KEY_FN$
|
|
12755
|
+
const CREATE_KEY_FN$3 = ([symbol, strategyName, backtest]) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
12754
12756
|
/** Maximum number of events to store in partial reports */
|
|
12755
12757
|
const MAX_EVENTS$1 = 250;
|
|
12756
12758
|
/**
|
|
@@ -12925,7 +12927,7 @@ class PartialMarkdownService {
|
|
|
12925
12927
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
12926
12928
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
12927
12929
|
*/
|
|
12928
|
-
this.getStorage = functoolsKit.memoize(CREATE_KEY_FN$
|
|
12930
|
+
this.getStorage = functoolsKit.memoize(CREATE_KEY_FN$3, () => new ReportStorage$1());
|
|
12929
12931
|
/**
|
|
12930
12932
|
* Processes profit events and accumulates them.
|
|
12931
12933
|
* Should be called from partialProfitSubject subscription.
|
|
@@ -13072,7 +13074,7 @@ class PartialMarkdownService {
|
|
|
13072
13074
|
ctx,
|
|
13073
13075
|
});
|
|
13074
13076
|
if (ctx) {
|
|
13075
|
-
const key = CREATE_KEY_FN$
|
|
13077
|
+
const key = CREATE_KEY_FN$3([ctx.symbol, ctx.strategyName, backtest]);
|
|
13076
13078
|
this.getStorage.clear(key);
|
|
13077
13079
|
}
|
|
13078
13080
|
else {
|
|
@@ -13515,7 +13517,7 @@ class ConfigValidationService {
|
|
|
13515
13517
|
* @param backtest - Whether running in backtest mode
|
|
13516
13518
|
* @returns Unique string key for memoization
|
|
13517
13519
|
*/
|
|
13518
|
-
const CREATE_KEY_FN = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
13520
|
+
const CREATE_KEY_FN$2 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
13519
13521
|
/** Maximum number of events to store in risk reports */
|
|
13520
13522
|
const MAX_EVENTS = 250;
|
|
13521
13523
|
/**
|
|
@@ -13660,7 +13662,7 @@ class RiskMarkdownService {
|
|
|
13660
13662
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
13661
13663
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
13662
13664
|
*/
|
|
13663
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN(symbol, strategyName, backtest), () => new ReportStorage());
|
|
13665
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, backtest]) => CREATE_KEY_FN$2(symbol, strategyName, backtest), () => new ReportStorage());
|
|
13664
13666
|
/**
|
|
13665
13667
|
* Processes risk rejection events and accumulates them.
|
|
13666
13668
|
* Should be called from riskSubject subscription.
|
|
@@ -13788,7 +13790,7 @@ class RiskMarkdownService {
|
|
|
13788
13790
|
ctx,
|
|
13789
13791
|
});
|
|
13790
13792
|
if (ctx) {
|
|
13791
|
-
const key = CREATE_KEY_FN(ctx.symbol, ctx.strategyName, backtest);
|
|
13793
|
+
const key = CREATE_KEY_FN$2(ctx.symbol, ctx.strategyName, backtest);
|
|
13792
13794
|
this.getStorage.clear(key);
|
|
13793
13795
|
}
|
|
13794
13796
|
else {
|
|
@@ -19500,7 +19502,15 @@ const INTERVAL_MINUTES = {
|
|
|
19500
19502
|
"6h": 360,
|
|
19501
19503
|
"8h": 480,
|
|
19502
19504
|
};
|
|
19503
|
-
|
|
19505
|
+
/**
|
|
19506
|
+
* Create a cache key string from strategy name, exchange name, and backtest mode.
|
|
19507
|
+
*
|
|
19508
|
+
* @param strategyName - Name of the strategy
|
|
19509
|
+
* @param exchangeName - Name of the exchange
|
|
19510
|
+
* @param backtest - Whether running in backtest mode
|
|
19511
|
+
* @returns Cache key string
|
|
19512
|
+
*/
|
|
19513
|
+
const CREATE_KEY_FN$1 = (strategyName, exchangeName, backtest) => `${strategyName}:${exchangeName}:${backtest ? "backtest" : "live"}`;
|
|
19504
19514
|
/**
|
|
19505
19515
|
* Instance class for caching function results with timeframe-based invalidation.
|
|
19506
19516
|
*
|
|
@@ -19576,7 +19586,7 @@ class CacheInstance {
|
|
|
19576
19586
|
throw new Error(`CacheInstance unknown cache ttl interval=${this.interval}`);
|
|
19577
19587
|
}
|
|
19578
19588
|
}
|
|
19579
|
-
const key =
|
|
19589
|
+
const key = CREATE_KEY_FN$1(backtest$1.methodContextService.context.strategyName, backtest$1.methodContextService.context.exchangeName, backtest$1.executionContextService.context.backtest);
|
|
19580
19590
|
const currentWhen = backtest$1.executionContextService.context.when;
|
|
19581
19591
|
const cached = this._cacheMap.get(key);
|
|
19582
19592
|
if (cached) {
|
|
@@ -19614,7 +19624,7 @@ class CacheInstance {
|
|
|
19614
19624
|
* ```
|
|
19615
19625
|
*/
|
|
19616
19626
|
this.clear = () => {
|
|
19617
|
-
const key =
|
|
19627
|
+
const key = CREATE_KEY_FN$1(backtest$1.methodContextService.context.strategyName, backtest$1.methodContextService.context.exchangeName, backtest$1.executionContextService.context.backtest);
|
|
19618
19628
|
this._cacheMap.delete(key);
|
|
19619
19629
|
};
|
|
19620
19630
|
}
|
|
@@ -19762,6 +19772,440 @@ class CacheUtils {
|
|
|
19762
19772
|
*/
|
|
19763
19773
|
const Cache = new CacheUtils();
|
|
19764
19774
|
|
|
19775
|
+
/** Maximum number of notifications to store in history */
|
|
19776
|
+
const MAX_NOTIFICATIONS = 250;
|
|
19777
|
+
/** Function to create unique notification IDs */
|
|
19778
|
+
const CREATE_KEY_FN = () => functoolsKit.randomString();
|
|
19779
|
+
/**
|
|
19780
|
+
* Instance class for notification history management.
|
|
19781
|
+
*
|
|
19782
|
+
* Contains all business logic for notification collection from emitters/subjects.
|
|
19783
|
+
* Stores notifications in chronological order with automatic limit management.
|
|
19784
|
+
*
|
|
19785
|
+
* @example
|
|
19786
|
+
* ```typescript
|
|
19787
|
+
* const instance = new NotificationInstance();
|
|
19788
|
+
* await instance.waitForInit();
|
|
19789
|
+
*
|
|
19790
|
+
* // Get all notifications
|
|
19791
|
+
* const all = instance.getData();
|
|
19792
|
+
*
|
|
19793
|
+
* // Process notifications with type discrimination
|
|
19794
|
+
* all.forEach(notification => {
|
|
19795
|
+
* switch (notification.type) {
|
|
19796
|
+
* case "signal.closed":
|
|
19797
|
+
* console.log(`Closed: ${notification.pnlPercentage}%`);
|
|
19798
|
+
* break;
|
|
19799
|
+
* case "partial.loss":
|
|
19800
|
+
* if (notification.level >= 30) {
|
|
19801
|
+
* alert("High loss!");
|
|
19802
|
+
* }
|
|
19803
|
+
* break;
|
|
19804
|
+
* case "risk.rejection":
|
|
19805
|
+
* console.warn(notification.rejectionNote);
|
|
19806
|
+
* break;
|
|
19807
|
+
* }
|
|
19808
|
+
* });
|
|
19809
|
+
*
|
|
19810
|
+
* // Clear history
|
|
19811
|
+
* instance.clear();
|
|
19812
|
+
* ```
|
|
19813
|
+
*/
|
|
19814
|
+
class NotificationInstance {
|
|
19815
|
+
constructor() {
|
|
19816
|
+
/** Internal notification history storage (newest first) */
|
|
19817
|
+
this._notifications = [];
|
|
19818
|
+
/**
|
|
19819
|
+
* Processes signal events and creates appropriate notifications.
|
|
19820
|
+
*/
|
|
19821
|
+
this._handleSignal = async (data) => {
|
|
19822
|
+
if (data.action === "opened") {
|
|
19823
|
+
this._addNotification({
|
|
19824
|
+
type: "signal.opened",
|
|
19825
|
+
id: CREATE_KEY_FN(),
|
|
19826
|
+
timestamp: data.signal.pendingAt,
|
|
19827
|
+
backtest: data.backtest,
|
|
19828
|
+
symbol: data.symbol,
|
|
19829
|
+
strategyName: data.strategyName,
|
|
19830
|
+
exchangeName: data.exchangeName,
|
|
19831
|
+
signalId: data.signal.id,
|
|
19832
|
+
position: data.signal.position,
|
|
19833
|
+
priceOpen: data.signal.priceOpen,
|
|
19834
|
+
priceTakeProfit: data.signal.priceTakeProfit,
|
|
19835
|
+
priceStopLoss: data.signal.priceStopLoss,
|
|
19836
|
+
note: data.signal.note,
|
|
19837
|
+
});
|
|
19838
|
+
}
|
|
19839
|
+
else if (data.action === "closed") {
|
|
19840
|
+
const durationMs = data.closeTimestamp - data.signal.pendingAt;
|
|
19841
|
+
const durationMin = Math.round(durationMs / 60000);
|
|
19842
|
+
this._addNotification({
|
|
19843
|
+
type: "signal.closed",
|
|
19844
|
+
id: CREATE_KEY_FN(),
|
|
19845
|
+
timestamp: data.closeTimestamp,
|
|
19846
|
+
backtest: data.backtest,
|
|
19847
|
+
symbol: data.symbol,
|
|
19848
|
+
strategyName: data.strategyName,
|
|
19849
|
+
exchangeName: data.exchangeName,
|
|
19850
|
+
signalId: data.signal.id,
|
|
19851
|
+
position: data.signal.position,
|
|
19852
|
+
priceOpen: data.signal.priceOpen,
|
|
19853
|
+
priceClose: data.currentPrice,
|
|
19854
|
+
pnlPercentage: data.pnl.pnlPercentage,
|
|
19855
|
+
closeReason: data.closeReason,
|
|
19856
|
+
duration: durationMin,
|
|
19857
|
+
note: data.signal.note,
|
|
19858
|
+
});
|
|
19859
|
+
}
|
|
19860
|
+
else if (data.action === "scheduled") {
|
|
19861
|
+
this._addNotification({
|
|
19862
|
+
type: "signal.scheduled",
|
|
19863
|
+
id: CREATE_KEY_FN(),
|
|
19864
|
+
timestamp: data.signal.scheduledAt,
|
|
19865
|
+
backtest: data.backtest,
|
|
19866
|
+
symbol: data.symbol,
|
|
19867
|
+
strategyName: data.strategyName,
|
|
19868
|
+
exchangeName: data.exchangeName,
|
|
19869
|
+
signalId: data.signal.id,
|
|
19870
|
+
position: data.signal.position,
|
|
19871
|
+
priceOpen: data.signal.priceOpen,
|
|
19872
|
+
scheduledAt: data.signal.scheduledAt,
|
|
19873
|
+
currentPrice: data.currentPrice,
|
|
19874
|
+
});
|
|
19875
|
+
}
|
|
19876
|
+
else if (data.action === "cancelled") {
|
|
19877
|
+
const durationMs = data.closeTimestamp - data.signal.scheduledAt;
|
|
19878
|
+
const durationMin = Math.round(durationMs / 60000);
|
|
19879
|
+
this._addNotification({
|
|
19880
|
+
type: "signal.cancelled",
|
|
19881
|
+
id: CREATE_KEY_FN(),
|
|
19882
|
+
timestamp: data.closeTimestamp,
|
|
19883
|
+
backtest: data.backtest,
|
|
19884
|
+
symbol: data.symbol,
|
|
19885
|
+
strategyName: data.strategyName,
|
|
19886
|
+
exchangeName: data.exchangeName,
|
|
19887
|
+
signalId: data.signal.id,
|
|
19888
|
+
position: data.signal.position,
|
|
19889
|
+
cancelReason: data.reason,
|
|
19890
|
+
cancelId: data.cancelId,
|
|
19891
|
+
duration: durationMin,
|
|
19892
|
+
});
|
|
19893
|
+
}
|
|
19894
|
+
};
|
|
19895
|
+
/**
|
|
19896
|
+
* Processes partial profit events.
|
|
19897
|
+
*/
|
|
19898
|
+
this._handlePartialProfit = async (data) => {
|
|
19899
|
+
this._addNotification({
|
|
19900
|
+
type: "partial.profit",
|
|
19901
|
+
id: CREATE_KEY_FN(),
|
|
19902
|
+
timestamp: data.timestamp,
|
|
19903
|
+
backtest: data.backtest,
|
|
19904
|
+
symbol: data.symbol,
|
|
19905
|
+
strategyName: data.strategyName,
|
|
19906
|
+
exchangeName: data.exchangeName,
|
|
19907
|
+
signalId: data.data.id,
|
|
19908
|
+
level: data.level,
|
|
19909
|
+
currentPrice: data.currentPrice,
|
|
19910
|
+
priceOpen: data.data.priceOpen,
|
|
19911
|
+
position: data.data.position,
|
|
19912
|
+
});
|
|
19913
|
+
};
|
|
19914
|
+
/**
|
|
19915
|
+
* Processes partial loss events.
|
|
19916
|
+
*/
|
|
19917
|
+
this._handlePartialLoss = async (data) => {
|
|
19918
|
+
this._addNotification({
|
|
19919
|
+
type: "partial.loss",
|
|
19920
|
+
id: CREATE_KEY_FN(),
|
|
19921
|
+
timestamp: data.timestamp,
|
|
19922
|
+
backtest: data.backtest,
|
|
19923
|
+
symbol: data.symbol,
|
|
19924
|
+
strategyName: data.strategyName,
|
|
19925
|
+
exchangeName: data.exchangeName,
|
|
19926
|
+
signalId: data.data.id,
|
|
19927
|
+
level: data.level,
|
|
19928
|
+
currentPrice: data.currentPrice,
|
|
19929
|
+
priceOpen: data.data.priceOpen,
|
|
19930
|
+
position: data.data.position,
|
|
19931
|
+
});
|
|
19932
|
+
};
|
|
19933
|
+
/**
|
|
19934
|
+
* Processes risk rejection events.
|
|
19935
|
+
*/
|
|
19936
|
+
this._handleRisk = async (data) => {
|
|
19937
|
+
this._addNotification({
|
|
19938
|
+
type: "risk.rejection",
|
|
19939
|
+
id: CREATE_KEY_FN(),
|
|
19940
|
+
timestamp: data.timestamp,
|
|
19941
|
+
backtest: data.backtest,
|
|
19942
|
+
symbol: data.symbol,
|
|
19943
|
+
strategyName: data.strategyName,
|
|
19944
|
+
exchangeName: data.exchangeName,
|
|
19945
|
+
rejectionNote: data.rejectionNote,
|
|
19946
|
+
rejectionId: data.rejectionId,
|
|
19947
|
+
activePositionCount: data.activePositionCount,
|
|
19948
|
+
currentPrice: data.currentPrice,
|
|
19949
|
+
pendingSignal: data.pendingSignal,
|
|
19950
|
+
});
|
|
19951
|
+
};
|
|
19952
|
+
/**
|
|
19953
|
+
* Processes done events (live/backtest).
|
|
19954
|
+
*/
|
|
19955
|
+
this._handleDoneLive = async (data) => {
|
|
19956
|
+
this._addNotification({
|
|
19957
|
+
type: "live.done",
|
|
19958
|
+
id: CREATE_KEY_FN(),
|
|
19959
|
+
timestamp: Date.now(),
|
|
19960
|
+
backtest: false,
|
|
19961
|
+
symbol: data.symbol,
|
|
19962
|
+
strategyName: data.strategyName,
|
|
19963
|
+
exchangeName: data.exchangeName,
|
|
19964
|
+
});
|
|
19965
|
+
};
|
|
19966
|
+
/**
|
|
19967
|
+
* Processes done events (backtest).
|
|
19968
|
+
*/
|
|
19969
|
+
this._handleDoneBacktest = async (data) => {
|
|
19970
|
+
this._addNotification({
|
|
19971
|
+
type: "backtest.done",
|
|
19972
|
+
id: CREATE_KEY_FN(),
|
|
19973
|
+
timestamp: Date.now(),
|
|
19974
|
+
backtest: true,
|
|
19975
|
+
symbol: data.symbol,
|
|
19976
|
+
strategyName: data.strategyName,
|
|
19977
|
+
exchangeName: data.exchangeName,
|
|
19978
|
+
});
|
|
19979
|
+
};
|
|
19980
|
+
/**
|
|
19981
|
+
* Processes error events.
|
|
19982
|
+
*/
|
|
19983
|
+
this._handleError = async (error) => {
|
|
19984
|
+
this._addNotification({
|
|
19985
|
+
type: "error.info",
|
|
19986
|
+
id: CREATE_KEY_FN(),
|
|
19987
|
+
timestamp: Date.now(),
|
|
19988
|
+
error: functoolsKit.errorData(error),
|
|
19989
|
+
message: functoolsKit.getErrorMessage(error),
|
|
19990
|
+
backtest: false,
|
|
19991
|
+
});
|
|
19992
|
+
};
|
|
19993
|
+
/**
|
|
19994
|
+
* Processes critical error events.
|
|
19995
|
+
*/
|
|
19996
|
+
this._handleCriticalError = async (error) => {
|
|
19997
|
+
this._addNotification({
|
|
19998
|
+
type: "error.critical",
|
|
19999
|
+
id: CREATE_KEY_FN(),
|
|
20000
|
+
timestamp: Date.now(),
|
|
20001
|
+
error: functoolsKit.errorData(error),
|
|
20002
|
+
message: functoolsKit.getErrorMessage(error),
|
|
20003
|
+
backtest: false,
|
|
20004
|
+
});
|
|
20005
|
+
};
|
|
20006
|
+
/**
|
|
20007
|
+
* Processes validation error events.
|
|
20008
|
+
*/
|
|
20009
|
+
this._handleValidationError = async (error) => {
|
|
20010
|
+
this._addNotification({
|
|
20011
|
+
type: "error.validation",
|
|
20012
|
+
id: CREATE_KEY_FN(),
|
|
20013
|
+
timestamp: Date.now(),
|
|
20014
|
+
error: functoolsKit.errorData(error),
|
|
20015
|
+
message: functoolsKit.getErrorMessage(error),
|
|
20016
|
+
backtest: false,
|
|
20017
|
+
});
|
|
20018
|
+
};
|
|
20019
|
+
/**
|
|
20020
|
+
* Processes progress events.
|
|
20021
|
+
*/
|
|
20022
|
+
this._handleProgressBacktest = async (data) => {
|
|
20023
|
+
this._addNotification({
|
|
20024
|
+
type: "progress.backtest",
|
|
20025
|
+
id: CREATE_KEY_FN(),
|
|
20026
|
+
timestamp: Date.now(),
|
|
20027
|
+
backtest: true,
|
|
20028
|
+
exchangeName: data.exchangeName,
|
|
20029
|
+
strategyName: data.strategyName,
|
|
20030
|
+
symbol: data.symbol,
|
|
20031
|
+
totalFrames: data.totalFrames,
|
|
20032
|
+
processedFrames: data.processedFrames,
|
|
20033
|
+
progress: data.progress,
|
|
20034
|
+
});
|
|
20035
|
+
};
|
|
20036
|
+
/**
|
|
20037
|
+
* Initializes notification system by subscribing to all emitters.
|
|
20038
|
+
* Uses singleshot to ensure initialization happens only once.
|
|
20039
|
+
* Automatically called on first use.
|
|
20040
|
+
*/
|
|
20041
|
+
this.waitForInit = functoolsKit.singleshot(async () => {
|
|
20042
|
+
// Signal events
|
|
20043
|
+
signalEmitter.subscribe(this._handleSignal);
|
|
20044
|
+
// Partial profit/loss events
|
|
20045
|
+
partialProfitSubject.subscribe(this._handlePartialProfit);
|
|
20046
|
+
partialLossSubject.subscribe(this._handlePartialLoss);
|
|
20047
|
+
// Risk events
|
|
20048
|
+
riskSubject.subscribe(this._handleRisk);
|
|
20049
|
+
// Done events
|
|
20050
|
+
doneLiveSubject.subscribe(this._handleDoneLive);
|
|
20051
|
+
doneBacktestSubject.subscribe(this._handleDoneBacktest);
|
|
20052
|
+
// Error events
|
|
20053
|
+
errorEmitter.subscribe(this._handleError);
|
|
20054
|
+
exitEmitter.subscribe(this._handleCriticalError);
|
|
20055
|
+
validationSubject.subscribe(this._handleValidationError);
|
|
20056
|
+
// Progress events
|
|
20057
|
+
progressBacktestEmitter.subscribe(this._handleProgressBacktest);
|
|
20058
|
+
});
|
|
20059
|
+
}
|
|
20060
|
+
/**
|
|
20061
|
+
* Adds notification to history with automatic limit management.
|
|
20062
|
+
*/
|
|
20063
|
+
_addNotification(notification) {
|
|
20064
|
+
this._notifications.unshift(notification);
|
|
20065
|
+
// Trim history if exceeded MAX_NOTIFICATIONS
|
|
20066
|
+
if (this._notifications.length > MAX_NOTIFICATIONS) {
|
|
20067
|
+
this._notifications.pop();
|
|
20068
|
+
}
|
|
20069
|
+
}
|
|
20070
|
+
/**
|
|
20071
|
+
* Returns all notifications in chronological order (newest first).
|
|
20072
|
+
*
|
|
20073
|
+
* @returns Array of strongly-typed notification objects
|
|
20074
|
+
*
|
|
20075
|
+
* @example
|
|
20076
|
+
* ```typescript
|
|
20077
|
+
* const notifications = instance.getData();
|
|
20078
|
+
*
|
|
20079
|
+
* notifications.forEach(notification => {
|
|
20080
|
+
* switch (notification.type) {
|
|
20081
|
+
* case "signal.closed":
|
|
20082
|
+
* console.log(`${notification.symbol}: ${notification.pnlPercentage}%`);
|
|
20083
|
+
* break;
|
|
20084
|
+
* case "partial.loss":
|
|
20085
|
+
* if (notification.level >= 30) {
|
|
20086
|
+
* console.warn(`High loss: ${notification.symbol}`);
|
|
20087
|
+
* }
|
|
20088
|
+
* break;
|
|
20089
|
+
* }
|
|
20090
|
+
* });
|
|
20091
|
+
* ```
|
|
20092
|
+
*/
|
|
20093
|
+
getData() {
|
|
20094
|
+
return [...this._notifications];
|
|
20095
|
+
}
|
|
20096
|
+
/**
|
|
20097
|
+
* Clears all notification history.
|
|
20098
|
+
*
|
|
20099
|
+
* @example
|
|
20100
|
+
* ```typescript
|
|
20101
|
+
* instance.clear();
|
|
20102
|
+
* ```
|
|
20103
|
+
*/
|
|
20104
|
+
clear() {
|
|
20105
|
+
this._notifications = [];
|
|
20106
|
+
}
|
|
20107
|
+
}
|
|
20108
|
+
/**
|
|
20109
|
+
* Public facade for notification operations.
|
|
20110
|
+
*
|
|
20111
|
+
* Automatically calls waitForInit on each userspace method call.
|
|
20112
|
+
* Provides simplified access to notification instance methods.
|
|
20113
|
+
*
|
|
20114
|
+
* @example
|
|
20115
|
+
* ```typescript
|
|
20116
|
+
* import { Notification } from "./classes/Notification";
|
|
20117
|
+
*
|
|
20118
|
+
* // Get all notifications
|
|
20119
|
+
* const all = await Notification.getData();
|
|
20120
|
+
*
|
|
20121
|
+
* // Process notifications with type discrimination
|
|
20122
|
+
* all.forEach(notification => {
|
|
20123
|
+
* switch (notification.type) {
|
|
20124
|
+
* case "signal.closed":
|
|
20125
|
+
* console.log(`Closed: ${notification.pnlPercentage}%`);
|
|
20126
|
+
* break;
|
|
20127
|
+
* case "partial.loss":
|
|
20128
|
+
* if (notification.level >= 30) {
|
|
20129
|
+
* alert("High loss!");
|
|
20130
|
+
* }
|
|
20131
|
+
* break;
|
|
20132
|
+
* case "risk.rejection":
|
|
20133
|
+
* console.warn(notification.rejectionNote);
|
|
20134
|
+
* break;
|
|
20135
|
+
* }
|
|
20136
|
+
* });
|
|
20137
|
+
*
|
|
20138
|
+
* // Clear history
|
|
20139
|
+
* await Notification.clear();
|
|
20140
|
+
* ```
|
|
20141
|
+
*/
|
|
20142
|
+
class NotificationUtils {
|
|
20143
|
+
constructor() {
|
|
20144
|
+
/** Internal instance containing business logic */
|
|
20145
|
+
this._instance = new NotificationInstance();
|
|
20146
|
+
}
|
|
20147
|
+
/**
|
|
20148
|
+
* Returns all notifications in chronological order (newest first).
|
|
20149
|
+
*
|
|
20150
|
+
* @returns Array of strongly-typed notification objects
|
|
20151
|
+
*
|
|
20152
|
+
* @example
|
|
20153
|
+
* ```typescript
|
|
20154
|
+
* const notifications = await Notification.getData();
|
|
20155
|
+
*
|
|
20156
|
+
* notifications.forEach(notification => {
|
|
20157
|
+
* switch (notification.type) {
|
|
20158
|
+
* case "signal.closed":
|
|
20159
|
+
* console.log(`${notification.symbol}: ${notification.pnlPercentage}%`);
|
|
20160
|
+
* break;
|
|
20161
|
+
* case "partial.loss":
|
|
20162
|
+
* if (notification.level >= 30) {
|
|
20163
|
+
* console.warn(`High loss: ${notification.symbol}`);
|
|
20164
|
+
* }
|
|
20165
|
+
* break;
|
|
20166
|
+
* }
|
|
20167
|
+
* });
|
|
20168
|
+
* ```
|
|
20169
|
+
*/
|
|
20170
|
+
async getData() {
|
|
20171
|
+
await this._instance.waitForInit();
|
|
20172
|
+
return this._instance.getData();
|
|
20173
|
+
}
|
|
20174
|
+
/**
|
|
20175
|
+
* Clears all notification history.
|
|
20176
|
+
*
|
|
20177
|
+
* @example
|
|
20178
|
+
* ```typescript
|
|
20179
|
+
* await Notification.clear();
|
|
20180
|
+
* ```
|
|
20181
|
+
*/
|
|
20182
|
+
async clear() {
|
|
20183
|
+
await this._instance.waitForInit();
|
|
20184
|
+
this._instance.clear();
|
|
20185
|
+
}
|
|
20186
|
+
}
|
|
20187
|
+
/**
|
|
20188
|
+
* Singleton instance of NotificationUtils for convenient notification access.
|
|
20189
|
+
*
|
|
20190
|
+
* @example
|
|
20191
|
+
* ```typescript
|
|
20192
|
+
* import { Notification } from "./classes/Notification";
|
|
20193
|
+
*
|
|
20194
|
+
* // Get all notifications
|
|
20195
|
+
* const all = await Notification.getData();
|
|
20196
|
+
*
|
|
20197
|
+
* // Filter by type using type discrimination
|
|
20198
|
+
* const closedSignals = all.filter(n => n.type === "signal.closed");
|
|
20199
|
+
* const highLosses = all.filter(n =>
|
|
20200
|
+
* n.type === "partial.loss" && n.level >= 30
|
|
20201
|
+
* );
|
|
20202
|
+
*
|
|
20203
|
+
* // Clear history
|
|
20204
|
+
* await Notification.clear();
|
|
20205
|
+
* ```
|
|
20206
|
+
*/
|
|
20207
|
+
const Notification = new NotificationUtils();
|
|
20208
|
+
|
|
19765
20209
|
exports.Backtest = Backtest;
|
|
19766
20210
|
exports.Cache = Cache;
|
|
19767
20211
|
exports.Constant = Constant;
|
|
@@ -19770,6 +20214,7 @@ exports.ExecutionContextService = ExecutionContextService;
|
|
|
19770
20214
|
exports.Heat = Heat;
|
|
19771
20215
|
exports.Live = Live;
|
|
19772
20216
|
exports.MethodContextService = MethodContextService;
|
|
20217
|
+
exports.Notification = Notification;
|
|
19773
20218
|
exports.Optimizer = Optimizer;
|
|
19774
20219
|
exports.Partial = Partial;
|
|
19775
20220
|
exports.Performance = Performance;
|