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.cjs
CHANGED
|
@@ -3779,7 +3779,7 @@ const GET_RISK_FN = (dto, backtest, self) => {
|
|
|
3779
3779
|
* @param backtest - Whether running in backtest mode
|
|
3780
3780
|
* @returns Unique string key for memoization
|
|
3781
3781
|
*/
|
|
3782
|
-
const CREATE_KEY_FN$
|
|
3782
|
+
const CREATE_KEY_FN$b = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
3783
3783
|
/**
|
|
3784
3784
|
* Callback function for emitting ping events to pingSubject.
|
|
3785
3785
|
*
|
|
@@ -3840,7 +3840,7 @@ class StrategyConnectionService {
|
|
|
3840
3840
|
* @param strategyName - Name of registered strategy schema
|
|
3841
3841
|
* @returns Configured ClientStrategy instance
|
|
3842
3842
|
*/
|
|
3843
|
-
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) => {
|
|
3844
3844
|
const { riskName = "", riskList = [], getSignal, interval, callbacks, } = this.strategySchemaService.get(strategyName);
|
|
3845
3845
|
return new ClientStrategy({
|
|
3846
3846
|
symbol,
|
|
@@ -4007,7 +4007,7 @@ class StrategyConnectionService {
|
|
|
4007
4007
|
ctx,
|
|
4008
4008
|
});
|
|
4009
4009
|
if (ctx) {
|
|
4010
|
-
const key = CREATE_KEY_FN$
|
|
4010
|
+
const key = CREATE_KEY_FN$b(ctx.symbol, ctx.strategyName, backtest);
|
|
4011
4011
|
this.getStrategy.clear(key);
|
|
4012
4012
|
}
|
|
4013
4013
|
else {
|
|
@@ -4617,7 +4617,7 @@ class ClientRisk {
|
|
|
4617
4617
|
* @param backtest - Whether running in backtest mode
|
|
4618
4618
|
* @returns Unique string key for memoization
|
|
4619
4619
|
*/
|
|
4620
|
-
const CREATE_KEY_FN$
|
|
4620
|
+
const CREATE_KEY_FN$a = (riskName, backtest) => `${riskName}:${backtest ? "backtest" : "live"}`;
|
|
4621
4621
|
/**
|
|
4622
4622
|
* Callback function for emitting risk rejection events to riskSubject.
|
|
4623
4623
|
*
|
|
@@ -4689,7 +4689,7 @@ class RiskConnectionService {
|
|
|
4689
4689
|
* @param backtest - True if backtest mode, false if live mode
|
|
4690
4690
|
* @returns Configured ClientRisk instance
|
|
4691
4691
|
*/
|
|
4692
|
-
this.getRisk = functoolsKit.memoize(([riskName, backtest]) => CREATE_KEY_FN$
|
|
4692
|
+
this.getRisk = functoolsKit.memoize(([riskName, backtest]) => CREATE_KEY_FN$a(riskName, backtest), (riskName, backtest) => {
|
|
4693
4693
|
const schema = this.riskSchemaService.get(riskName);
|
|
4694
4694
|
return new ClientRisk({
|
|
4695
4695
|
...schema,
|
|
@@ -4756,7 +4756,7 @@ class RiskConnectionService {
|
|
|
4756
4756
|
backtest,
|
|
4757
4757
|
});
|
|
4758
4758
|
if (ctx) {
|
|
4759
|
-
const key = CREATE_KEY_FN$
|
|
4759
|
+
const key = CREATE_KEY_FN$a(ctx.riskName, backtest);
|
|
4760
4760
|
this.getRisk.clear(key);
|
|
4761
4761
|
}
|
|
4762
4762
|
else {
|
|
@@ -7795,7 +7795,7 @@ const DEFAULT_COLUMNS = Object.freeze({ ...COLUMN_CONFIG });
|
|
|
7795
7795
|
* @param backtest - Whether running in backtest mode
|
|
7796
7796
|
* @returns Unique string key for memoization
|
|
7797
7797
|
*/
|
|
7798
|
-
const CREATE_KEY_FN$
|
|
7798
|
+
const CREATE_KEY_FN$9 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
7799
7799
|
/**
|
|
7800
7800
|
* Checks if a value is unsafe for display (not a number, NaN, or Infinity).
|
|
7801
7801
|
*
|
|
@@ -8004,7 +8004,7 @@ class BacktestMarkdownService {
|
|
|
8004
8004
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
8005
8005
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
8006
8006
|
*/
|
|
8007
|
-
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());
|
|
8008
8008
|
/**
|
|
8009
8009
|
* Processes tick events and accumulates closed signals.
|
|
8010
8010
|
* Should be called from IStrategyCallbacks.onTick.
|
|
@@ -8142,7 +8142,7 @@ class BacktestMarkdownService {
|
|
|
8142
8142
|
ctx,
|
|
8143
8143
|
});
|
|
8144
8144
|
if (ctx) {
|
|
8145
|
-
const key = CREATE_KEY_FN$
|
|
8145
|
+
const key = CREATE_KEY_FN$9(ctx.symbol, ctx.strategyName, backtest);
|
|
8146
8146
|
this.getStorage.clear(key);
|
|
8147
8147
|
}
|
|
8148
8148
|
else {
|
|
@@ -8175,7 +8175,7 @@ class BacktestMarkdownService {
|
|
|
8175
8175
|
* @param backtest - Whether running in backtest mode
|
|
8176
8176
|
* @returns Unique string key for memoization
|
|
8177
8177
|
*/
|
|
8178
|
-
const CREATE_KEY_FN$
|
|
8178
|
+
const CREATE_KEY_FN$8 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
8179
8179
|
/**
|
|
8180
8180
|
* Checks if a value is unsafe for display (not a number, NaN, or Infinity).
|
|
8181
8181
|
*
|
|
@@ -8505,7 +8505,7 @@ class LiveMarkdownService {
|
|
|
8505
8505
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
8506
8506
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
8507
8507
|
*/
|
|
8508
|
-
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());
|
|
8509
8509
|
/**
|
|
8510
8510
|
* Processes tick events and accumulates all event types.
|
|
8511
8511
|
* Should be called from IStrategyCallbacks.onTick.
|
|
@@ -8653,7 +8653,7 @@ class LiveMarkdownService {
|
|
|
8653
8653
|
ctx,
|
|
8654
8654
|
});
|
|
8655
8655
|
if (ctx) {
|
|
8656
|
-
const key = CREATE_KEY_FN$
|
|
8656
|
+
const key = CREATE_KEY_FN$8(ctx.symbol, ctx.strategyName, backtest);
|
|
8657
8657
|
this.getStorage.clear(key);
|
|
8658
8658
|
}
|
|
8659
8659
|
else {
|
|
@@ -8686,7 +8686,7 @@ class LiveMarkdownService {
|
|
|
8686
8686
|
* @param backtest - Whether running in backtest mode
|
|
8687
8687
|
* @returns Unique string key for memoization
|
|
8688
8688
|
*/
|
|
8689
|
-
const CREATE_KEY_FN$
|
|
8689
|
+
const CREATE_KEY_FN$7 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
8690
8690
|
/** Maximum number of events to store in schedule reports */
|
|
8691
8691
|
const MAX_EVENTS$4 = 250;
|
|
8692
8692
|
/**
|
|
@@ -8922,7 +8922,7 @@ class ScheduleMarkdownService {
|
|
|
8922
8922
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
8923
8923
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
8924
8924
|
*/
|
|
8925
|
-
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());
|
|
8926
8926
|
/**
|
|
8927
8927
|
* Processes tick events and accumulates scheduled/opened/cancelled events.
|
|
8928
8928
|
* Should be called from signalEmitter subscription.
|
|
@@ -9064,7 +9064,7 @@ class ScheduleMarkdownService {
|
|
|
9064
9064
|
ctx,
|
|
9065
9065
|
});
|
|
9066
9066
|
if (ctx) {
|
|
9067
|
-
const key = CREATE_KEY_FN$
|
|
9067
|
+
const key = CREATE_KEY_FN$7(ctx.symbol, ctx.strategyName, backtest);
|
|
9068
9068
|
this.getStorage.clear(key);
|
|
9069
9069
|
}
|
|
9070
9070
|
else {
|
|
@@ -9097,7 +9097,7 @@ class ScheduleMarkdownService {
|
|
|
9097
9097
|
* @param backtest - Whether running in backtest mode
|
|
9098
9098
|
* @returns Unique string key for memoization
|
|
9099
9099
|
*/
|
|
9100
|
-
const CREATE_KEY_FN$
|
|
9100
|
+
const CREATE_KEY_FN$6 = (symbol, strategyName, backtest) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
9101
9101
|
/**
|
|
9102
9102
|
* Calculates percentile value from sorted array.
|
|
9103
9103
|
*/
|
|
@@ -9315,7 +9315,7 @@ class PerformanceMarkdownService {
|
|
|
9315
9315
|
* Memoized function to get or create PerformanceStorage for a symbol-strategy-backtest triple.
|
|
9316
9316
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
9317
9317
|
*/
|
|
9318
|
-
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());
|
|
9319
9319
|
/**
|
|
9320
9320
|
* Processes performance events and accumulates metrics.
|
|
9321
9321
|
* Should be called from performance tracking code.
|
|
@@ -9420,7 +9420,7 @@ class PerformanceMarkdownService {
|
|
|
9420
9420
|
ctx,
|
|
9421
9421
|
});
|
|
9422
9422
|
if (ctx) {
|
|
9423
|
-
const key = CREATE_KEY_FN$
|
|
9423
|
+
const key = CREATE_KEY_FN$6(ctx.symbol, ctx.strategyName, backtest);
|
|
9424
9424
|
this.getStorage.clear(key);
|
|
9425
9425
|
}
|
|
9426
9426
|
else {
|
|
@@ -9854,7 +9854,7 @@ class WalkerMarkdownService {
|
|
|
9854
9854
|
* @param backtest - Whether running in backtest mode
|
|
9855
9855
|
* @returns Unique string key for memoization
|
|
9856
9856
|
*/
|
|
9857
|
-
const CREATE_KEY_FN$
|
|
9857
|
+
const CREATE_KEY_FN$5 = (strategyName, backtest) => `${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
9858
9858
|
const HEATMAP_METHOD_NAME_GET_DATA = "HeatMarkdownService.getData";
|
|
9859
9859
|
const HEATMAP_METHOD_NAME_GET_REPORT = "HeatMarkdownService.getReport";
|
|
9860
9860
|
const HEATMAP_METHOD_NAME_DUMP = "HeatMarkdownService.dump";
|
|
@@ -10198,7 +10198,7 @@ class HeatMarkdownService {
|
|
|
10198
10198
|
* Memoized function to get or create HeatmapStorage for a strategy and backtest mode.
|
|
10199
10199
|
* Each strategy + backtest mode combination gets its own isolated heatmap storage instance.
|
|
10200
10200
|
*/
|
|
10201
|
-
this.getStorage = functoolsKit.memoize(([strategyName, backtest]) => CREATE_KEY_FN$
|
|
10201
|
+
this.getStorage = functoolsKit.memoize(([strategyName, backtest]) => CREATE_KEY_FN$5(strategyName, backtest), () => new HeatmapStorage());
|
|
10202
10202
|
/**
|
|
10203
10203
|
* Processes tick events and accumulates closed signals.
|
|
10204
10204
|
* Should be called from signal emitter subscription.
|
|
@@ -10334,7 +10334,7 @@ class HeatMarkdownService {
|
|
|
10334
10334
|
ctx,
|
|
10335
10335
|
});
|
|
10336
10336
|
if (ctx) {
|
|
10337
|
-
const key = CREATE_KEY_FN$
|
|
10337
|
+
const key = CREATE_KEY_FN$5(ctx.strategyName, backtest);
|
|
10338
10338
|
this.getStorage.clear(key);
|
|
10339
10339
|
}
|
|
10340
10340
|
else {
|
|
@@ -12552,7 +12552,7 @@ class ClientPartial {
|
|
|
12552
12552
|
* @param backtest - Whether running in backtest mode
|
|
12553
12553
|
* @returns Unique string key for memoization
|
|
12554
12554
|
*/
|
|
12555
|
-
const CREATE_KEY_FN$
|
|
12555
|
+
const CREATE_KEY_FN$4 = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
12556
12556
|
/**
|
|
12557
12557
|
* Callback function for emitting profit events to partialProfitSubject.
|
|
12558
12558
|
*
|
|
@@ -12650,7 +12650,7 @@ class PartialConnectionService {
|
|
|
12650
12650
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
12651
12651
|
* Value: ClientPartial instance with logger and event emitters
|
|
12652
12652
|
*/
|
|
12653
|
-
this.getPartial = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
12653
|
+
this.getPartial = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$4(signalId, backtest), (signalId, backtest) => {
|
|
12654
12654
|
return new ClientPartial({
|
|
12655
12655
|
signalId,
|
|
12656
12656
|
logger: this.loggerService,
|
|
@@ -12740,7 +12740,7 @@ class PartialConnectionService {
|
|
|
12740
12740
|
const partial = this.getPartial(data.id, backtest);
|
|
12741
12741
|
await partial.waitForInit(symbol, data.strategyName);
|
|
12742
12742
|
await partial.clear(symbol, data, priceClose, backtest);
|
|
12743
|
-
const key = CREATE_KEY_FN$
|
|
12743
|
+
const key = CREATE_KEY_FN$4(data.id, backtest);
|
|
12744
12744
|
this.getPartial.clear(key);
|
|
12745
12745
|
};
|
|
12746
12746
|
}
|
|
@@ -12752,7 +12752,7 @@ class PartialConnectionService {
|
|
|
12752
12752
|
* @param param0 - Tuple of symbol, strategyName and backtest boolean
|
|
12753
12753
|
* @returns Unique string key for memoization
|
|
12754
12754
|
*/
|
|
12755
|
-
const CREATE_KEY_FN$
|
|
12755
|
+
const CREATE_KEY_FN$3 = ([symbol, strategyName, backtest]) => `${symbol}:${strategyName}:${backtest ? "backtest" : "live"}`;
|
|
12756
12756
|
/** Maximum number of events to store in partial reports */
|
|
12757
12757
|
const MAX_EVENTS$1 = 250;
|
|
12758
12758
|
/**
|
|
@@ -12927,7 +12927,7 @@ class PartialMarkdownService {
|
|
|
12927
12927
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
12928
12928
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
12929
12929
|
*/
|
|
12930
|
-
this.getStorage = functoolsKit.memoize(CREATE_KEY_FN$
|
|
12930
|
+
this.getStorage = functoolsKit.memoize(CREATE_KEY_FN$3, () => new ReportStorage$1());
|
|
12931
12931
|
/**
|
|
12932
12932
|
* Processes profit events and accumulates them.
|
|
12933
12933
|
* Should be called from partialProfitSubject subscription.
|
|
@@ -13074,7 +13074,7 @@ class PartialMarkdownService {
|
|
|
13074
13074
|
ctx,
|
|
13075
13075
|
});
|
|
13076
13076
|
if (ctx) {
|
|
13077
|
-
const key = CREATE_KEY_FN$
|
|
13077
|
+
const key = CREATE_KEY_FN$3([ctx.symbol, ctx.strategyName, backtest]);
|
|
13078
13078
|
this.getStorage.clear(key);
|
|
13079
13079
|
}
|
|
13080
13080
|
else {
|
|
@@ -13517,7 +13517,7 @@ class ConfigValidationService {
|
|
|
13517
13517
|
* @param backtest - Whether running in backtest mode
|
|
13518
13518
|
* @returns Unique string key for memoization
|
|
13519
13519
|
*/
|
|
13520
|
-
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"}`;
|
|
13521
13521
|
/** Maximum number of events to store in risk reports */
|
|
13522
13522
|
const MAX_EVENTS = 250;
|
|
13523
13523
|
/**
|
|
@@ -13662,7 +13662,7 @@ class RiskMarkdownService {
|
|
|
13662
13662
|
* Memoized function to get or create ReportStorage for a symbol-strategy-backtest triple.
|
|
13663
13663
|
* Each symbol-strategy-backtest combination gets its own isolated storage instance.
|
|
13664
13664
|
*/
|
|
13665
|
-
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());
|
|
13666
13666
|
/**
|
|
13667
13667
|
* Processes risk rejection events and accumulates them.
|
|
13668
13668
|
* Should be called from riskSubject subscription.
|
|
@@ -13790,7 +13790,7 @@ class RiskMarkdownService {
|
|
|
13790
13790
|
ctx,
|
|
13791
13791
|
});
|
|
13792
13792
|
if (ctx) {
|
|
13793
|
-
const key = CREATE_KEY_FN(ctx.symbol, ctx.strategyName, backtest);
|
|
13793
|
+
const key = CREATE_KEY_FN$2(ctx.symbol, ctx.strategyName, backtest);
|
|
13794
13794
|
this.getStorage.clear(key);
|
|
13795
13795
|
}
|
|
13796
13796
|
else {
|
|
@@ -19502,7 +19502,15 @@ const INTERVAL_MINUTES = {
|
|
|
19502
19502
|
"6h": 360,
|
|
19503
19503
|
"8h": 480,
|
|
19504
19504
|
};
|
|
19505
|
-
|
|
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"}`;
|
|
19506
19514
|
/**
|
|
19507
19515
|
* Instance class for caching function results with timeframe-based invalidation.
|
|
19508
19516
|
*
|
|
@@ -19578,7 +19586,7 @@ class CacheInstance {
|
|
|
19578
19586
|
throw new Error(`CacheInstance unknown cache ttl interval=${this.interval}`);
|
|
19579
19587
|
}
|
|
19580
19588
|
}
|
|
19581
|
-
const key =
|
|
19589
|
+
const key = CREATE_KEY_FN$1(backtest$1.methodContextService.context.strategyName, backtest$1.methodContextService.context.exchangeName, backtest$1.executionContextService.context.backtest);
|
|
19582
19590
|
const currentWhen = backtest$1.executionContextService.context.when;
|
|
19583
19591
|
const cached = this._cacheMap.get(key);
|
|
19584
19592
|
if (cached) {
|
|
@@ -19616,7 +19624,7 @@ class CacheInstance {
|
|
|
19616
19624
|
* ```
|
|
19617
19625
|
*/
|
|
19618
19626
|
this.clear = () => {
|
|
19619
|
-
const key =
|
|
19627
|
+
const key = CREATE_KEY_FN$1(backtest$1.methodContextService.context.strategyName, backtest$1.methodContextService.context.exchangeName, backtest$1.executionContextService.context.backtest);
|
|
19620
19628
|
this._cacheMap.delete(key);
|
|
19621
19629
|
};
|
|
19622
19630
|
}
|
|
@@ -19764,6 +19772,446 @@ class CacheUtils {
|
|
|
19764
19772
|
*/
|
|
19765
19773
|
const Cache = new CacheUtils();
|
|
19766
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
|
+
// Add bootstrap notification to mark initialization
|
|
20043
|
+
this._addNotification({
|
|
20044
|
+
type: "bootstrap",
|
|
20045
|
+
id: CREATE_KEY_FN(),
|
|
20046
|
+
timestamp: Date.now(),
|
|
20047
|
+
});
|
|
20048
|
+
// Signal events
|
|
20049
|
+
signalEmitter.subscribe(this._handleSignal);
|
|
20050
|
+
// Partial profit/loss events
|
|
20051
|
+
partialProfitSubject.subscribe(this._handlePartialProfit);
|
|
20052
|
+
partialLossSubject.subscribe(this._handlePartialLoss);
|
|
20053
|
+
// Risk events
|
|
20054
|
+
riskSubject.subscribe(this._handleRisk);
|
|
20055
|
+
// Done events
|
|
20056
|
+
doneLiveSubject.subscribe(this._handleDoneLive);
|
|
20057
|
+
doneBacktestSubject.subscribe(this._handleDoneBacktest);
|
|
20058
|
+
// Error events
|
|
20059
|
+
errorEmitter.subscribe(this._handleError);
|
|
20060
|
+
exitEmitter.subscribe(this._handleCriticalError);
|
|
20061
|
+
validationSubject.subscribe(this._handleValidationError);
|
|
20062
|
+
// Progress events
|
|
20063
|
+
progressBacktestEmitter.subscribe(this._handleProgressBacktest);
|
|
20064
|
+
});
|
|
20065
|
+
}
|
|
20066
|
+
/**
|
|
20067
|
+
* Adds notification to history with automatic limit management.
|
|
20068
|
+
*/
|
|
20069
|
+
_addNotification(notification) {
|
|
20070
|
+
this._notifications.unshift(notification);
|
|
20071
|
+
// Trim history if exceeded MAX_NOTIFICATIONS
|
|
20072
|
+
if (this._notifications.length > MAX_NOTIFICATIONS) {
|
|
20073
|
+
this._notifications.pop();
|
|
20074
|
+
}
|
|
20075
|
+
}
|
|
20076
|
+
/**
|
|
20077
|
+
* Returns all notifications in chronological order (newest first).
|
|
20078
|
+
*
|
|
20079
|
+
* @returns Array of strongly-typed notification objects
|
|
20080
|
+
*
|
|
20081
|
+
* @example
|
|
20082
|
+
* ```typescript
|
|
20083
|
+
* const notifications = instance.getData();
|
|
20084
|
+
*
|
|
20085
|
+
* notifications.forEach(notification => {
|
|
20086
|
+
* switch (notification.type) {
|
|
20087
|
+
* case "signal.closed":
|
|
20088
|
+
* console.log(`${notification.symbol}: ${notification.pnlPercentage}%`);
|
|
20089
|
+
* break;
|
|
20090
|
+
* case "partial.loss":
|
|
20091
|
+
* if (notification.level >= 30) {
|
|
20092
|
+
* console.warn(`High loss: ${notification.symbol}`);
|
|
20093
|
+
* }
|
|
20094
|
+
* break;
|
|
20095
|
+
* }
|
|
20096
|
+
* });
|
|
20097
|
+
* ```
|
|
20098
|
+
*/
|
|
20099
|
+
getData() {
|
|
20100
|
+
return [...this._notifications];
|
|
20101
|
+
}
|
|
20102
|
+
/**
|
|
20103
|
+
* Clears all notification history.
|
|
20104
|
+
*
|
|
20105
|
+
* @example
|
|
20106
|
+
* ```typescript
|
|
20107
|
+
* instance.clear();
|
|
20108
|
+
* ```
|
|
20109
|
+
*/
|
|
20110
|
+
clear() {
|
|
20111
|
+
this._notifications = [];
|
|
20112
|
+
}
|
|
20113
|
+
}
|
|
20114
|
+
/**
|
|
20115
|
+
* Public facade for notification operations.
|
|
20116
|
+
*
|
|
20117
|
+
* Automatically calls waitForInit on each userspace method call.
|
|
20118
|
+
* Provides simplified access to notification instance methods.
|
|
20119
|
+
*
|
|
20120
|
+
* @example
|
|
20121
|
+
* ```typescript
|
|
20122
|
+
* import { Notification } from "./classes/Notification";
|
|
20123
|
+
*
|
|
20124
|
+
* // Get all notifications
|
|
20125
|
+
* const all = await Notification.getData();
|
|
20126
|
+
*
|
|
20127
|
+
* // Process notifications with type discrimination
|
|
20128
|
+
* all.forEach(notification => {
|
|
20129
|
+
* switch (notification.type) {
|
|
20130
|
+
* case "signal.closed":
|
|
20131
|
+
* console.log(`Closed: ${notification.pnlPercentage}%`);
|
|
20132
|
+
* break;
|
|
20133
|
+
* case "partial.loss":
|
|
20134
|
+
* if (notification.level >= 30) {
|
|
20135
|
+
* alert("High loss!");
|
|
20136
|
+
* }
|
|
20137
|
+
* break;
|
|
20138
|
+
* case "risk.rejection":
|
|
20139
|
+
* console.warn(notification.rejectionNote);
|
|
20140
|
+
* break;
|
|
20141
|
+
* }
|
|
20142
|
+
* });
|
|
20143
|
+
*
|
|
20144
|
+
* // Clear history
|
|
20145
|
+
* await Notification.clear();
|
|
20146
|
+
* ```
|
|
20147
|
+
*/
|
|
20148
|
+
class NotificationUtils {
|
|
20149
|
+
constructor() {
|
|
20150
|
+
/** Internal instance containing business logic */
|
|
20151
|
+
this._instance = new NotificationInstance();
|
|
20152
|
+
}
|
|
20153
|
+
/**
|
|
20154
|
+
* Returns all notifications in chronological order (newest first).
|
|
20155
|
+
*
|
|
20156
|
+
* @returns Array of strongly-typed notification objects
|
|
20157
|
+
*
|
|
20158
|
+
* @example
|
|
20159
|
+
* ```typescript
|
|
20160
|
+
* const notifications = await Notification.getData();
|
|
20161
|
+
*
|
|
20162
|
+
* notifications.forEach(notification => {
|
|
20163
|
+
* switch (notification.type) {
|
|
20164
|
+
* case "signal.closed":
|
|
20165
|
+
* console.log(`${notification.symbol}: ${notification.pnlPercentage}%`);
|
|
20166
|
+
* break;
|
|
20167
|
+
* case "partial.loss":
|
|
20168
|
+
* if (notification.level >= 30) {
|
|
20169
|
+
* console.warn(`High loss: ${notification.symbol}`);
|
|
20170
|
+
* }
|
|
20171
|
+
* break;
|
|
20172
|
+
* }
|
|
20173
|
+
* });
|
|
20174
|
+
* ```
|
|
20175
|
+
*/
|
|
20176
|
+
async getData() {
|
|
20177
|
+
await this._instance.waitForInit();
|
|
20178
|
+
return this._instance.getData();
|
|
20179
|
+
}
|
|
20180
|
+
/**
|
|
20181
|
+
* Clears all notification history.
|
|
20182
|
+
*
|
|
20183
|
+
* @example
|
|
20184
|
+
* ```typescript
|
|
20185
|
+
* await Notification.clear();
|
|
20186
|
+
* ```
|
|
20187
|
+
*/
|
|
20188
|
+
async clear() {
|
|
20189
|
+
await this._instance.waitForInit();
|
|
20190
|
+
this._instance.clear();
|
|
20191
|
+
}
|
|
20192
|
+
}
|
|
20193
|
+
/**
|
|
20194
|
+
* Singleton instance of NotificationUtils for convenient notification access.
|
|
20195
|
+
*
|
|
20196
|
+
* @example
|
|
20197
|
+
* ```typescript
|
|
20198
|
+
* import { Notification } from "./classes/Notification";
|
|
20199
|
+
*
|
|
20200
|
+
* // Get all notifications
|
|
20201
|
+
* const all = await Notification.getData();
|
|
20202
|
+
*
|
|
20203
|
+
* // Filter by type using type discrimination
|
|
20204
|
+
* const closedSignals = all.filter(n => n.type === "signal.closed");
|
|
20205
|
+
* const highLosses = all.filter(n =>
|
|
20206
|
+
* n.type === "partial.loss" && n.level >= 30
|
|
20207
|
+
* );
|
|
20208
|
+
*
|
|
20209
|
+
* // Clear history
|
|
20210
|
+
* await Notification.clear();
|
|
20211
|
+
* ```
|
|
20212
|
+
*/
|
|
20213
|
+
const Notification = new NotificationUtils();
|
|
20214
|
+
|
|
19767
20215
|
exports.Backtest = Backtest;
|
|
19768
20216
|
exports.Cache = Cache;
|
|
19769
20217
|
exports.Constant = Constant;
|
|
@@ -19772,6 +20220,7 @@ exports.ExecutionContextService = ExecutionContextService;
|
|
|
19772
20220
|
exports.Heat = Heat;
|
|
19773
20221
|
exports.Live = Live;
|
|
19774
20222
|
exports.MethodContextService = MethodContextService;
|
|
20223
|
+
exports.Notification = Notification;
|
|
19775
20224
|
exports.Optimizer = Optimizer;
|
|
19776
20225
|
exports.Partial = Partial;
|
|
19777
20226
|
exports.Performance = Performance;
|