backtest-kit 6.12.0 → 6.14.0
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 +1412 -117
- package/build/index.mjs +1404 -118
- package/package.json +2 -2
- package/types.d.ts +657 -9
package/build/index.cjs
CHANGED
|
@@ -147,6 +147,9 @@ const reportServices$1 = {
|
|
|
147
147
|
highestProfitReportService: Symbol('highestProfitReportService'),
|
|
148
148
|
maxDrawdownReportService: Symbol('maxDrawdownReportService'),
|
|
149
149
|
};
|
|
150
|
+
const helperServices$1 = {
|
|
151
|
+
notificationHelperService: Symbol('notificationHelperService'),
|
|
152
|
+
};
|
|
150
153
|
const validationServices$1 = {
|
|
151
154
|
exchangeValidationService: Symbol('exchangeValidationService'),
|
|
152
155
|
strategyValidationService: Symbol('strategyValidationService'),
|
|
@@ -172,6 +175,7 @@ const TYPES = {
|
|
|
172
175
|
...markdownServices$1,
|
|
173
176
|
...reportServices$1,
|
|
174
177
|
...validationServices$1,
|
|
178
|
+
...helperServices$1,
|
|
175
179
|
};
|
|
176
180
|
|
|
177
181
|
/**
|
|
@@ -764,6 +768,11 @@ const highestProfitSubject = new functoolsKit.Subject();
|
|
|
764
768
|
* Allows users to track drawdown levels and implement custom risk management logic based on drawdown thresholds.
|
|
765
769
|
*/
|
|
766
770
|
const maxDrawdownSubject = new functoolsKit.Subject();
|
|
771
|
+
/**
|
|
772
|
+
* Signal info emitter for user-defined informational notes on open positions.
|
|
773
|
+
* Emits when a strategy calls commitSignalInfo() to broadcast a custom annotation.
|
|
774
|
+
*/
|
|
775
|
+
const signalNotifySubject = new functoolsKit.Subject();
|
|
767
776
|
|
|
768
777
|
var emitters = /*#__PURE__*/Object.freeze({
|
|
769
778
|
__proto__: null,
|
|
@@ -788,6 +797,7 @@ var emitters = /*#__PURE__*/Object.freeze({
|
|
|
788
797
|
signalBacktestEmitter: signalBacktestEmitter,
|
|
789
798
|
signalEmitter: signalEmitter,
|
|
790
799
|
signalLiveEmitter: signalLiveEmitter,
|
|
800
|
+
signalNotifySubject: signalNotifySubject,
|
|
791
801
|
strategyCommitSubject: strategyCommitSubject,
|
|
792
802
|
syncSubject: syncSubject,
|
|
793
803
|
validationSubject: validationSubject,
|
|
@@ -1002,6 +1012,12 @@ const PERSIST_MEMORY_UTILS_METHOD_NAME_LIST_DATA = "PersistMemoryUtils.listMemor
|
|
|
1002
1012
|
const PERSIST_MEMORY_UTILS_METHOD_NAME_HAS_DATA = "PersistMemoryUtils.hasMemoryData";
|
|
1003
1013
|
const PERSIST_MEMORY_UTILS_METHOD_NAME_CLEAR = "PersistMemoryUtils.clear";
|
|
1004
1014
|
const PERSIST_MEMORY_UTILS_METHOD_NAME_DISPOSE = "PersistMemoryUtils.dispose";
|
|
1015
|
+
const PERSIST_RECENT_UTILS_METHOD_NAME_USE_PERSIST_RECENT_ADAPTER = "PersistRecentUtils.usePersistRecentAdapter";
|
|
1016
|
+
const PERSIST_RECENT_UTILS_METHOD_NAME_READ_DATA = "PersistRecentUtils.readRecentData";
|
|
1017
|
+
const PERSIST_RECENT_UTILS_METHOD_NAME_WRITE_DATA = "PersistRecentUtils.writeRecentData";
|
|
1018
|
+
const PERSIST_RECENT_UTILS_METHOD_NAME_USE_JSON = "PersistRecentUtils.useJson";
|
|
1019
|
+
const PERSIST_RECENT_UTILS_METHOD_NAME_USE_DUMMY = "PersistRecentUtils.useDummy";
|
|
1020
|
+
const PERSIST_RECENT_UTILS_METHOD_NAME_CLEAR = "PersistRecentUtils.clear";
|
|
1005
1021
|
const BASE_WAIT_FOR_INIT_FN_METHOD_NAME = "PersistBase.waitForInitFn";
|
|
1006
1022
|
const BASE_UNLINK_RETRY_COUNT = 5;
|
|
1007
1023
|
const BASE_UNLINK_RETRY_DELAY = 1000;
|
|
@@ -2848,6 +2864,115 @@ class PersistMemoryUtils {
|
|
|
2848
2864
|
* ```
|
|
2849
2865
|
*/
|
|
2850
2866
|
const PersistMemoryAdapter = new PersistMemoryUtils();
|
|
2867
|
+
/**
|
|
2868
|
+
* Utility class for managing recent signal persistence.
|
|
2869
|
+
*
|
|
2870
|
+
* Features:
|
|
2871
|
+
* - Memoized storage instances per (symbol, strategyName, exchangeName, frameName) context
|
|
2872
|
+
* - Custom adapter support
|
|
2873
|
+
* - Atomic read/write operations
|
|
2874
|
+
* - Crash-safe recent signal state management
|
|
2875
|
+
*
|
|
2876
|
+
* Used by RecentPersistBacktestUtils/RecentPersistLiveUtils for recent signal persistence.
|
|
2877
|
+
*/
|
|
2878
|
+
class PersistRecentUtils {
|
|
2879
|
+
constructor() {
|
|
2880
|
+
this.PersistRecentFactory = PersistBase;
|
|
2881
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => this.createKeyParts(symbol, strategyName, exchangeName, frameName, backtest).join(":"), (symbol, strategyName, exchangeName, frameName, backtest) => Reflect.construct(this.PersistRecentFactory, [
|
|
2882
|
+
this.createKeyParts(symbol, strategyName, exchangeName, frameName, backtest).join("_"),
|
|
2883
|
+
`./dump/data/recent/`,
|
|
2884
|
+
]));
|
|
2885
|
+
/**
|
|
2886
|
+
* Reads the latest persisted recent signal for a given context.
|
|
2887
|
+
*
|
|
2888
|
+
* Returns null if no recent signal exists.
|
|
2889
|
+
*
|
|
2890
|
+
* @param symbol - Trading pair symbol
|
|
2891
|
+
* @param strategyName - Strategy identifier
|
|
2892
|
+
* @param exchangeName - Exchange identifier
|
|
2893
|
+
* @param frameName - Frame identifier
|
|
2894
|
+
* @returns Promise resolving to recent signal or null
|
|
2895
|
+
*/
|
|
2896
|
+
this.readRecentData = async (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
2897
|
+
LOGGER_SERVICE$7.info(PERSIST_RECENT_UTILS_METHOD_NAME_READ_DATA);
|
|
2898
|
+
const key = this.createKeyParts(symbol, strategyName, exchangeName, frameName, backtest).join(":");
|
|
2899
|
+
const isInitial = !this.getStorage.has(key);
|
|
2900
|
+
const stateStorage = this.getStorage(symbol, strategyName, exchangeName, frameName, backtest);
|
|
2901
|
+
await stateStorage.waitForInit(isInitial);
|
|
2902
|
+
if (await stateStorage.hasValue(symbol)) {
|
|
2903
|
+
return await stateStorage.readValue(symbol);
|
|
2904
|
+
}
|
|
2905
|
+
return null;
|
|
2906
|
+
};
|
|
2907
|
+
/**
|
|
2908
|
+
* Writes the latest recent signal to disk with atomic file writes.
|
|
2909
|
+
*
|
|
2910
|
+
* Uses symbol as the entity ID within the per-context storage instance.
|
|
2911
|
+
* Uses atomic writes to prevent corruption on crashes.
|
|
2912
|
+
*
|
|
2913
|
+
* @param signalRow - Recent signal data to persist
|
|
2914
|
+
* @param symbol - Trading pair symbol
|
|
2915
|
+
* @param strategyName - Strategy identifier
|
|
2916
|
+
* @param exchangeName - Exchange identifier
|
|
2917
|
+
* @param frameName - Frame identifier
|
|
2918
|
+
* @returns Promise that resolves when write is complete
|
|
2919
|
+
*/
|
|
2920
|
+
this.writeRecentData = async (signalRow, symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
2921
|
+
LOGGER_SERVICE$7.info(PERSIST_RECENT_UTILS_METHOD_NAME_WRITE_DATA);
|
|
2922
|
+
const key = this.createKeyParts(symbol, strategyName, exchangeName, frameName, backtest).join(":");
|
|
2923
|
+
const isInitial = !this.getStorage.has(key);
|
|
2924
|
+
const stateStorage = this.getStorage(symbol, strategyName, exchangeName, frameName, backtest);
|
|
2925
|
+
await stateStorage.waitForInit(isInitial);
|
|
2926
|
+
await stateStorage.writeValue(symbol, signalRow);
|
|
2927
|
+
};
|
|
2928
|
+
}
|
|
2929
|
+
createKeyParts(symbol, strategyName, exchangeName, frameName, backtest) {
|
|
2930
|
+
const parts = [symbol, strategyName, exchangeName];
|
|
2931
|
+
if (frameName)
|
|
2932
|
+
parts.push(frameName);
|
|
2933
|
+
parts.push(backtest ? "backtest" : "live");
|
|
2934
|
+
return parts;
|
|
2935
|
+
}
|
|
2936
|
+
/**
|
|
2937
|
+
* Registers a custom persistence adapter.
|
|
2938
|
+
*
|
|
2939
|
+
* @param Ctor - Custom PersistBase constructor
|
|
2940
|
+
*/
|
|
2941
|
+
usePersistRecentAdapter(Ctor) {
|
|
2942
|
+
LOGGER_SERVICE$7.info(PERSIST_RECENT_UTILS_METHOD_NAME_USE_PERSIST_RECENT_ADAPTER);
|
|
2943
|
+
this.PersistRecentFactory = Ctor;
|
|
2944
|
+
}
|
|
2945
|
+
/**
|
|
2946
|
+
* Clears the memoized storage cache.
|
|
2947
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
2948
|
+
* so new storage instances are created with the updated base path.
|
|
2949
|
+
*/
|
|
2950
|
+
clear() {
|
|
2951
|
+
LOGGER_SERVICE$7.log(PERSIST_RECENT_UTILS_METHOD_NAME_CLEAR);
|
|
2952
|
+
this.getStorage.clear();
|
|
2953
|
+
}
|
|
2954
|
+
/**
|
|
2955
|
+
* Switches to the default JSON persist adapter.
|
|
2956
|
+
* All future persistence writes will use JSON storage.
|
|
2957
|
+
*/
|
|
2958
|
+
useJson() {
|
|
2959
|
+
LOGGER_SERVICE$7.log(PERSIST_RECENT_UTILS_METHOD_NAME_USE_JSON);
|
|
2960
|
+
this.usePersistRecentAdapter(PersistBase);
|
|
2961
|
+
}
|
|
2962
|
+
/**
|
|
2963
|
+
* Switches to a dummy persist adapter that discards all writes.
|
|
2964
|
+
* All future persistence writes will be no-ops.
|
|
2965
|
+
*/
|
|
2966
|
+
useDummy() {
|
|
2967
|
+
LOGGER_SERVICE$7.log(PERSIST_RECENT_UTILS_METHOD_NAME_USE_DUMMY);
|
|
2968
|
+
this.usePersistRecentAdapter(PersistDummy);
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
/**
|
|
2972
|
+
* Global singleton instance of PersistRecentUtils.
|
|
2973
|
+
* Used by RecentPersistBacktestUtils/RecentPersistLiveUtils for recent signal persistence.
|
|
2974
|
+
*/
|
|
2975
|
+
const PersistRecentAdapter = new PersistRecentUtils();
|
|
2851
2976
|
|
|
2852
2977
|
var _a$2, _b$2;
|
|
2853
2978
|
const BUSY_DELAY = 100;
|
|
@@ -10175,7 +10300,7 @@ const GET_RISK_FN = (dto, backtest, exchangeName, frameName, self) => {
|
|
|
10175
10300
|
* @param backtest - Whether running in backtest mode
|
|
10176
10301
|
* @returns Unique string key for memoization
|
|
10177
10302
|
*/
|
|
10178
|
-
const CREATE_KEY_FN$
|
|
10303
|
+
const CREATE_KEY_FN$v = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
10179
10304
|
const parts = [symbol, strategyName, exchangeName];
|
|
10180
10305
|
if (frameName)
|
|
10181
10306
|
parts.push(frameName);
|
|
@@ -10442,7 +10567,7 @@ class StrategyConnectionService {
|
|
|
10442
10567
|
* @param backtest - Whether running in backtest mode
|
|
10443
10568
|
* @returns Configured ClientStrategy instance
|
|
10444
10569
|
*/
|
|
10445
|
-
this.getStrategy = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
10570
|
+
this.getStrategy = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$v(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
10446
10571
|
const { riskName = "", riskList = [], getSignal, interval = STRATEGY_DEFAULT_INTERVAL, callbacks, } = this.strategySchemaService.get(strategyName);
|
|
10447
10572
|
return new ClientStrategy({
|
|
10448
10573
|
symbol,
|
|
@@ -11363,7 +11488,7 @@ class StrategyConnectionService {
|
|
|
11363
11488
|
}
|
|
11364
11489
|
return;
|
|
11365
11490
|
}
|
|
11366
|
-
const key = CREATE_KEY_FN$
|
|
11491
|
+
const key = CREATE_KEY_FN$v(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
11367
11492
|
if (!this.getStrategy.has(key)) {
|
|
11368
11493
|
return;
|
|
11369
11494
|
}
|
|
@@ -12532,7 +12657,7 @@ class ClientRisk {
|
|
|
12532
12657
|
* @param backtest - Whether running in backtest mode
|
|
12533
12658
|
* @returns Unique string key for memoization
|
|
12534
12659
|
*/
|
|
12535
|
-
const CREATE_KEY_FN$
|
|
12660
|
+
const CREATE_KEY_FN$u = (riskName, exchangeName, frameName, backtest) => {
|
|
12536
12661
|
const parts = [riskName, exchangeName];
|
|
12537
12662
|
if (frameName)
|
|
12538
12663
|
parts.push(frameName);
|
|
@@ -12632,7 +12757,7 @@ class RiskConnectionService {
|
|
|
12632
12757
|
* @param backtest - True if backtest mode, false if live mode
|
|
12633
12758
|
* @returns Configured ClientRisk instance
|
|
12634
12759
|
*/
|
|
12635
|
-
this.getRisk = functoolsKit.memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
12760
|
+
this.getRisk = functoolsKit.memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$u(riskName, exchangeName, frameName, backtest), (riskName, exchangeName, frameName, backtest) => {
|
|
12636
12761
|
const schema = this.riskSchemaService.get(riskName);
|
|
12637
12762
|
return new ClientRisk({
|
|
12638
12763
|
...schema,
|
|
@@ -12701,7 +12826,7 @@ class RiskConnectionService {
|
|
|
12701
12826
|
payload,
|
|
12702
12827
|
});
|
|
12703
12828
|
if (payload) {
|
|
12704
|
-
const key = CREATE_KEY_FN$
|
|
12829
|
+
const key = CREATE_KEY_FN$u(payload.riskName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
12705
12830
|
this.getRisk.clear(key);
|
|
12706
12831
|
}
|
|
12707
12832
|
else {
|
|
@@ -13745,7 +13870,7 @@ class ClientAction {
|
|
|
13745
13870
|
* @param backtest - Whether running in backtest mode
|
|
13746
13871
|
* @returns Unique string key for memoization
|
|
13747
13872
|
*/
|
|
13748
|
-
const CREATE_KEY_FN$
|
|
13873
|
+
const CREATE_KEY_FN$t = (actionName, strategyName, exchangeName, frameName, backtest) => {
|
|
13749
13874
|
const parts = [actionName, strategyName, exchangeName];
|
|
13750
13875
|
if (frameName)
|
|
13751
13876
|
parts.push(frameName);
|
|
@@ -13797,7 +13922,7 @@ class ActionConnectionService {
|
|
|
13797
13922
|
* @param backtest - True if backtest mode, false if live mode
|
|
13798
13923
|
* @returns Configured ClientAction instance
|
|
13799
13924
|
*/
|
|
13800
|
-
this.getAction = functoolsKit.memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
13925
|
+
this.getAction = functoolsKit.memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$t(actionName, strategyName, exchangeName, frameName, backtest), (actionName, strategyName, exchangeName, frameName, backtest) => {
|
|
13801
13926
|
const schema = this.actionSchemaService.get(actionName);
|
|
13802
13927
|
return new ClientAction({
|
|
13803
13928
|
...schema,
|
|
@@ -14008,7 +14133,7 @@ class ActionConnectionService {
|
|
|
14008
14133
|
await Promise.all(actions.map(async (action) => await action.dispose()));
|
|
14009
14134
|
return;
|
|
14010
14135
|
}
|
|
14011
|
-
const key = CREATE_KEY_FN$
|
|
14136
|
+
const key = CREATE_KEY_FN$t(payload.actionName, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
14012
14137
|
if (!this.getAction.has(key)) {
|
|
14013
14138
|
return;
|
|
14014
14139
|
}
|
|
@@ -14019,14 +14144,14 @@ class ActionConnectionService {
|
|
|
14019
14144
|
}
|
|
14020
14145
|
}
|
|
14021
14146
|
|
|
14022
|
-
const METHOD_NAME_VALIDATE$
|
|
14147
|
+
const METHOD_NAME_VALIDATE$3 = "exchangeCoreService validate";
|
|
14023
14148
|
/**
|
|
14024
14149
|
* Creates a unique key for memoizing validate calls.
|
|
14025
14150
|
* Key format: "exchangeName"
|
|
14026
14151
|
* @param exchangeName - Exchange name
|
|
14027
14152
|
* @returns Unique string key for memoization
|
|
14028
14153
|
*/
|
|
14029
|
-
const CREATE_KEY_FN$
|
|
14154
|
+
const CREATE_KEY_FN$s = (exchangeName) => {
|
|
14030
14155
|
return exchangeName;
|
|
14031
14156
|
};
|
|
14032
14157
|
/**
|
|
@@ -14050,11 +14175,11 @@ class ExchangeCoreService {
|
|
|
14050
14175
|
* @param exchangeName - Name of the exchange to validate
|
|
14051
14176
|
* @returns Promise that resolves when validation is complete
|
|
14052
14177
|
*/
|
|
14053
|
-
this.validate = functoolsKit.memoize(([exchangeName]) => CREATE_KEY_FN$
|
|
14054
|
-
this.loggerService.log(METHOD_NAME_VALIDATE$
|
|
14178
|
+
this.validate = functoolsKit.memoize(([exchangeName]) => CREATE_KEY_FN$s(exchangeName), async (exchangeName) => {
|
|
14179
|
+
this.loggerService.log(METHOD_NAME_VALIDATE$3, {
|
|
14055
14180
|
exchangeName,
|
|
14056
14181
|
});
|
|
14057
|
-
this.exchangeValidationService.validate(exchangeName, METHOD_NAME_VALIDATE$
|
|
14182
|
+
this.exchangeValidationService.validate(exchangeName, METHOD_NAME_VALIDATE$3);
|
|
14058
14183
|
});
|
|
14059
14184
|
/**
|
|
14060
14185
|
* Fetches historical candles with execution context.
|
|
@@ -14295,14 +14420,14 @@ class ExchangeCoreService {
|
|
|
14295
14420
|
}
|
|
14296
14421
|
}
|
|
14297
14422
|
|
|
14298
|
-
const METHOD_NAME_VALIDATE$
|
|
14423
|
+
const METHOD_NAME_VALIDATE$2 = "strategyCoreService validate";
|
|
14299
14424
|
/**
|
|
14300
14425
|
* Creates a unique key for memoizing validate calls.
|
|
14301
14426
|
* Key format: "strategyName:exchangeName:frameName"
|
|
14302
14427
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
14303
14428
|
* @returns Unique string key for memoization
|
|
14304
14429
|
*/
|
|
14305
|
-
const CREATE_KEY_FN$
|
|
14430
|
+
const CREATE_KEY_FN$r = (context) => {
|
|
14306
14431
|
const parts = [context.strategyName, context.exchangeName];
|
|
14307
14432
|
if (context.frameName)
|
|
14308
14433
|
parts.push(context.frameName);
|
|
@@ -14334,16 +14459,16 @@ class StrategyCoreService {
|
|
|
14334
14459
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
14335
14460
|
* @returns Promise that resolves when validation is complete
|
|
14336
14461
|
*/
|
|
14337
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
14338
|
-
this.loggerService.log(METHOD_NAME_VALIDATE$
|
|
14462
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$r(context), async (context) => {
|
|
14463
|
+
this.loggerService.log(METHOD_NAME_VALIDATE$2, {
|
|
14339
14464
|
context,
|
|
14340
14465
|
});
|
|
14341
14466
|
const { riskName, riskList } = this.strategySchemaService.get(context.strategyName);
|
|
14342
|
-
this.strategyValidationService.validate(context.strategyName, METHOD_NAME_VALIDATE$
|
|
14343
|
-
this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_VALIDATE$
|
|
14344
|
-
context.frameName && this.frameValidationService.validate(context.frameName, METHOD_NAME_VALIDATE$
|
|
14345
|
-
riskName && this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$
|
|
14346
|
-
riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$
|
|
14467
|
+
this.strategyValidationService.validate(context.strategyName, METHOD_NAME_VALIDATE$2);
|
|
14468
|
+
this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_VALIDATE$2);
|
|
14469
|
+
context.frameName && this.frameValidationService.validate(context.frameName, METHOD_NAME_VALIDATE$2);
|
|
14470
|
+
riskName && this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$2);
|
|
14471
|
+
riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$2));
|
|
14347
14472
|
});
|
|
14348
14473
|
/**
|
|
14349
14474
|
* Retrieves the currently active pending signal for the symbol.
|
|
@@ -15672,7 +15797,7 @@ class SizingGlobalService {
|
|
|
15672
15797
|
* @param context - Context with riskName, exchangeName, frameName
|
|
15673
15798
|
* @returns Unique string key for memoization
|
|
15674
15799
|
*/
|
|
15675
|
-
const CREATE_KEY_FN$
|
|
15800
|
+
const CREATE_KEY_FN$q = (context) => {
|
|
15676
15801
|
const parts = [context.riskName, context.exchangeName];
|
|
15677
15802
|
if (context.frameName)
|
|
15678
15803
|
parts.push(context.frameName);
|
|
@@ -15698,7 +15823,7 @@ class RiskGlobalService {
|
|
|
15698
15823
|
* @param payload - Payload with riskName, exchangeName and frameName
|
|
15699
15824
|
* @returns Promise that resolves when validation is complete
|
|
15700
15825
|
*/
|
|
15701
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
15826
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$q(context), async (context) => {
|
|
15702
15827
|
this.loggerService.log("riskGlobalService validate", {
|
|
15703
15828
|
context,
|
|
15704
15829
|
});
|
|
@@ -15769,14 +15894,14 @@ class RiskGlobalService {
|
|
|
15769
15894
|
}
|
|
15770
15895
|
}
|
|
15771
15896
|
|
|
15772
|
-
const METHOD_NAME_VALIDATE = "actionCoreService validate";
|
|
15897
|
+
const METHOD_NAME_VALIDATE$1 = "actionCoreService validate";
|
|
15773
15898
|
/**
|
|
15774
15899
|
* Creates a unique key for memoizing validate calls.
|
|
15775
15900
|
* Key format: "strategyName:exchangeName:frameName"
|
|
15776
15901
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
15777
15902
|
* @returns Unique string key for memoization
|
|
15778
15903
|
*/
|
|
15779
|
-
const CREATE_KEY_FN$
|
|
15904
|
+
const CREATE_KEY_FN$p = (context) => {
|
|
15780
15905
|
const parts = [context.strategyName, context.exchangeName];
|
|
15781
15906
|
if (context.frameName)
|
|
15782
15907
|
parts.push(context.frameName);
|
|
@@ -15820,17 +15945,17 @@ class ActionCoreService {
|
|
|
15820
15945
|
* @param context - Strategy execution context with strategyName, exchangeName and frameName
|
|
15821
15946
|
* @returns Promise that resolves when all validations complete
|
|
15822
15947
|
*/
|
|
15823
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
15824
|
-
this.loggerService.log(METHOD_NAME_VALIDATE, {
|
|
15948
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$p(context), async (context) => {
|
|
15949
|
+
this.loggerService.log(METHOD_NAME_VALIDATE$1, {
|
|
15825
15950
|
context,
|
|
15826
15951
|
});
|
|
15827
15952
|
const { riskName, riskList, actions } = this.strategySchemaService.get(context.strategyName);
|
|
15828
|
-
this.strategyValidationService.validate(context.strategyName, METHOD_NAME_VALIDATE);
|
|
15829
|
-
this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_VALIDATE);
|
|
15830
|
-
context.frameName && this.frameValidationService.validate(context.frameName, METHOD_NAME_VALIDATE);
|
|
15831
|
-
riskName && this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE);
|
|
15832
|
-
riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE));
|
|
15833
|
-
actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, METHOD_NAME_VALIDATE));
|
|
15953
|
+
this.strategyValidationService.validate(context.strategyName, METHOD_NAME_VALIDATE$1);
|
|
15954
|
+
this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_VALIDATE$1);
|
|
15955
|
+
context.frameName && this.frameValidationService.validate(context.frameName, METHOD_NAME_VALIDATE$1);
|
|
15956
|
+
riskName && this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$1);
|
|
15957
|
+
riskList && riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE$1));
|
|
15958
|
+
actions && actions.forEach((actionName) => this.actionValidationService.validate(actionName, METHOD_NAME_VALIDATE$1));
|
|
15834
15959
|
});
|
|
15835
15960
|
/**
|
|
15836
15961
|
* Initializes all ClientAction instances for the strategy.
|
|
@@ -20863,7 +20988,7 @@ const ReportWriter = new ReportWriterAdapter();
|
|
|
20863
20988
|
* @param backtest - Whether running in backtest mode
|
|
20864
20989
|
* @returns Unique string key for memoization
|
|
20865
20990
|
*/
|
|
20866
|
-
const CREATE_KEY_FN$
|
|
20991
|
+
const CREATE_KEY_FN$o = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
20867
20992
|
const parts = [symbol, strategyName, exchangeName];
|
|
20868
20993
|
if (frameName)
|
|
20869
20994
|
parts.push(frameName);
|
|
@@ -21109,7 +21234,7 @@ class BacktestMarkdownService {
|
|
|
21109
21234
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
21110
21235
|
* Each combination gets its own isolated storage instance.
|
|
21111
21236
|
*/
|
|
21112
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
21237
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$o(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$a(symbol, strategyName, exchangeName, frameName));
|
|
21113
21238
|
/**
|
|
21114
21239
|
* Processes tick events and accumulates closed signals.
|
|
21115
21240
|
* Should be called from IStrategyCallbacks.onTick.
|
|
@@ -21266,7 +21391,7 @@ class BacktestMarkdownService {
|
|
|
21266
21391
|
payload,
|
|
21267
21392
|
});
|
|
21268
21393
|
if (payload) {
|
|
21269
|
-
const key = CREATE_KEY_FN$
|
|
21394
|
+
const key = CREATE_KEY_FN$o(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
21270
21395
|
this.getStorage.clear(key);
|
|
21271
21396
|
}
|
|
21272
21397
|
else {
|
|
@@ -21328,7 +21453,7 @@ class BacktestMarkdownService {
|
|
|
21328
21453
|
* @param backtest - Whether running in backtest mode
|
|
21329
21454
|
* @returns Unique string key for memoization
|
|
21330
21455
|
*/
|
|
21331
|
-
const CREATE_KEY_FN$
|
|
21456
|
+
const CREATE_KEY_FN$n = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
21332
21457
|
const parts = [symbol, strategyName, exchangeName];
|
|
21333
21458
|
if (frameName)
|
|
21334
21459
|
parts.push(frameName);
|
|
@@ -21823,7 +21948,7 @@ class LiveMarkdownService {
|
|
|
21823
21948
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
21824
21949
|
* Each combination gets its own isolated storage instance.
|
|
21825
21950
|
*/
|
|
21826
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
21951
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$n(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$9(symbol, strategyName, exchangeName, frameName));
|
|
21827
21952
|
/**
|
|
21828
21953
|
* Subscribes to live signal emitter to receive tick events.
|
|
21829
21954
|
* Protected against multiple subscriptions.
|
|
@@ -22041,7 +22166,7 @@ class LiveMarkdownService {
|
|
|
22041
22166
|
payload,
|
|
22042
22167
|
});
|
|
22043
22168
|
if (payload) {
|
|
22044
|
-
const key = CREATE_KEY_FN$
|
|
22169
|
+
const key = CREATE_KEY_FN$n(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
22045
22170
|
this.getStorage.clear(key);
|
|
22046
22171
|
}
|
|
22047
22172
|
else {
|
|
@@ -22061,7 +22186,7 @@ class LiveMarkdownService {
|
|
|
22061
22186
|
* @param backtest - Whether running in backtest mode
|
|
22062
22187
|
* @returns Unique string key for memoization
|
|
22063
22188
|
*/
|
|
22064
|
-
const CREATE_KEY_FN$
|
|
22189
|
+
const CREATE_KEY_FN$m = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
22065
22190
|
const parts = [symbol, strategyName, exchangeName];
|
|
22066
22191
|
if (frameName)
|
|
22067
22192
|
parts.push(frameName);
|
|
@@ -22350,7 +22475,7 @@ class ScheduleMarkdownService {
|
|
|
22350
22475
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
22351
22476
|
* Each combination gets its own isolated storage instance.
|
|
22352
22477
|
*/
|
|
22353
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
22478
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$m(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$8(symbol, strategyName, exchangeName, frameName));
|
|
22354
22479
|
/**
|
|
22355
22480
|
* Subscribes to signal emitter to receive scheduled signal events.
|
|
22356
22481
|
* Protected against multiple subscriptions.
|
|
@@ -22553,7 +22678,7 @@ class ScheduleMarkdownService {
|
|
|
22553
22678
|
payload,
|
|
22554
22679
|
});
|
|
22555
22680
|
if (payload) {
|
|
22556
|
-
const key = CREATE_KEY_FN$
|
|
22681
|
+
const key = CREATE_KEY_FN$m(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
22557
22682
|
this.getStorage.clear(key);
|
|
22558
22683
|
}
|
|
22559
22684
|
else {
|
|
@@ -22573,7 +22698,7 @@ class ScheduleMarkdownService {
|
|
|
22573
22698
|
* @param backtest - Whether running in backtest mode
|
|
22574
22699
|
* @returns Unique string key for memoization
|
|
22575
22700
|
*/
|
|
22576
|
-
const CREATE_KEY_FN$
|
|
22701
|
+
const CREATE_KEY_FN$l = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
22577
22702
|
const parts = [symbol, strategyName, exchangeName];
|
|
22578
22703
|
if (frameName)
|
|
22579
22704
|
parts.push(frameName);
|
|
@@ -22818,7 +22943,7 @@ class PerformanceMarkdownService {
|
|
|
22818
22943
|
* Memoized function to get or create PerformanceStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
22819
22944
|
* Each combination gets its own isolated storage instance.
|
|
22820
22945
|
*/
|
|
22821
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
22946
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$l(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new PerformanceStorage(symbol, strategyName, exchangeName, frameName));
|
|
22822
22947
|
/**
|
|
22823
22948
|
* Subscribes to performance emitter to receive performance events.
|
|
22824
22949
|
* Protected against multiple subscriptions.
|
|
@@ -22985,7 +23110,7 @@ class PerformanceMarkdownService {
|
|
|
22985
23110
|
payload,
|
|
22986
23111
|
});
|
|
22987
23112
|
if (payload) {
|
|
22988
|
-
const key = CREATE_KEY_FN$
|
|
23113
|
+
const key = CREATE_KEY_FN$l(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
22989
23114
|
this.getStorage.clear(key);
|
|
22990
23115
|
}
|
|
22991
23116
|
else {
|
|
@@ -23464,7 +23589,7 @@ class WalkerMarkdownService {
|
|
|
23464
23589
|
* @param backtest - Whether running in backtest mode
|
|
23465
23590
|
* @returns Unique string key for memoization
|
|
23466
23591
|
*/
|
|
23467
|
-
const CREATE_KEY_FN$
|
|
23592
|
+
const CREATE_KEY_FN$k = (exchangeName, frameName, backtest) => {
|
|
23468
23593
|
const parts = [exchangeName];
|
|
23469
23594
|
if (frameName)
|
|
23470
23595
|
parts.push(frameName);
|
|
@@ -23911,7 +24036,7 @@ class HeatMarkdownService {
|
|
|
23911
24036
|
* Memoized function to get or create HeatmapStorage for exchange, frame and backtest mode.
|
|
23912
24037
|
* Each exchangeName + frameName + backtest mode combination gets its own isolated heatmap storage instance.
|
|
23913
24038
|
*/
|
|
23914
|
-
this.getStorage = functoolsKit.memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
24039
|
+
this.getStorage = functoolsKit.memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$k(exchangeName, frameName, backtest), (exchangeName, frameName, backtest) => new HeatmapStorage(exchangeName, frameName, backtest));
|
|
23915
24040
|
/**
|
|
23916
24041
|
* Subscribes to signal emitter to receive tick events.
|
|
23917
24042
|
* Protected against multiple subscriptions.
|
|
@@ -24129,7 +24254,7 @@ class HeatMarkdownService {
|
|
|
24129
24254
|
payload,
|
|
24130
24255
|
});
|
|
24131
24256
|
if (payload) {
|
|
24132
|
-
const key = CREATE_KEY_FN$
|
|
24257
|
+
const key = CREATE_KEY_FN$k(payload.exchangeName, payload.frameName, payload.backtest);
|
|
24133
24258
|
this.getStorage.clear(key);
|
|
24134
24259
|
}
|
|
24135
24260
|
else {
|
|
@@ -25160,7 +25285,7 @@ class ClientPartial {
|
|
|
25160
25285
|
* @param backtest - Whether running in backtest mode
|
|
25161
25286
|
* @returns Unique string key for memoization
|
|
25162
25287
|
*/
|
|
25163
|
-
const CREATE_KEY_FN$
|
|
25288
|
+
const CREATE_KEY_FN$j = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
25164
25289
|
/**
|
|
25165
25290
|
* Creates a callback function for emitting profit events to partialProfitSubject.
|
|
25166
25291
|
*
|
|
@@ -25282,7 +25407,7 @@ class PartialConnectionService {
|
|
|
25282
25407
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
25283
25408
|
* Value: ClientPartial instance with logger and event emitters
|
|
25284
25409
|
*/
|
|
25285
|
-
this.getPartial = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
25410
|
+
this.getPartial = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$j(signalId, backtest), (signalId, backtest) => {
|
|
25286
25411
|
return new ClientPartial({
|
|
25287
25412
|
signalId,
|
|
25288
25413
|
logger: this.loggerService,
|
|
@@ -25372,7 +25497,7 @@ class PartialConnectionService {
|
|
|
25372
25497
|
const partial = this.getPartial(data.id, backtest);
|
|
25373
25498
|
await partial.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
|
|
25374
25499
|
await partial.clear(symbol, data, priceClose, backtest);
|
|
25375
|
-
const key = CREATE_KEY_FN$
|
|
25500
|
+
const key = CREATE_KEY_FN$j(data.id, backtest);
|
|
25376
25501
|
this.getPartial.clear(key);
|
|
25377
25502
|
};
|
|
25378
25503
|
}
|
|
@@ -25388,7 +25513,7 @@ class PartialConnectionService {
|
|
|
25388
25513
|
* @param backtest - Whether running in backtest mode
|
|
25389
25514
|
* @returns Unique string key for memoization
|
|
25390
25515
|
*/
|
|
25391
|
-
const CREATE_KEY_FN$
|
|
25516
|
+
const CREATE_KEY_FN$i = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
25392
25517
|
const parts = [symbol, strategyName, exchangeName];
|
|
25393
25518
|
if (frameName)
|
|
25394
25519
|
parts.push(frameName);
|
|
@@ -25611,7 +25736,7 @@ class PartialMarkdownService {
|
|
|
25611
25736
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
25612
25737
|
* Each combination gets its own isolated storage instance.
|
|
25613
25738
|
*/
|
|
25614
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
25739
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$i(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$6(symbol, strategyName, exchangeName, frameName));
|
|
25615
25740
|
/**
|
|
25616
25741
|
* Subscribes to partial profit/loss signal emitters to receive events.
|
|
25617
25742
|
* Protected against multiple subscriptions.
|
|
@@ -25821,7 +25946,7 @@ class PartialMarkdownService {
|
|
|
25821
25946
|
payload,
|
|
25822
25947
|
});
|
|
25823
25948
|
if (payload) {
|
|
25824
|
-
const key = CREATE_KEY_FN$
|
|
25949
|
+
const key = CREATE_KEY_FN$i(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
25825
25950
|
this.getStorage.clear(key);
|
|
25826
25951
|
}
|
|
25827
25952
|
else {
|
|
@@ -25837,7 +25962,7 @@ class PartialMarkdownService {
|
|
|
25837
25962
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
25838
25963
|
* @returns Unique string key for memoization
|
|
25839
25964
|
*/
|
|
25840
|
-
const CREATE_KEY_FN$
|
|
25965
|
+
const CREATE_KEY_FN$h = (context) => {
|
|
25841
25966
|
const parts = [context.strategyName, context.exchangeName];
|
|
25842
25967
|
if (context.frameName)
|
|
25843
25968
|
parts.push(context.frameName);
|
|
@@ -25911,7 +26036,7 @@ class PartialGlobalService {
|
|
|
25911
26036
|
* @param context - Context with strategyName, exchangeName and frameName
|
|
25912
26037
|
* @param methodName - Name of the calling method for error tracking
|
|
25913
26038
|
*/
|
|
25914
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
26039
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$h(context), (context, methodName) => {
|
|
25915
26040
|
this.loggerService.log("partialGlobalService validate", {
|
|
25916
26041
|
context,
|
|
25917
26042
|
methodName,
|
|
@@ -26366,7 +26491,7 @@ class ClientBreakeven {
|
|
|
26366
26491
|
* @param backtest - Whether running in backtest mode
|
|
26367
26492
|
* @returns Unique string key for memoization
|
|
26368
26493
|
*/
|
|
26369
|
-
const CREATE_KEY_FN$
|
|
26494
|
+
const CREATE_KEY_FN$g = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
26370
26495
|
/**
|
|
26371
26496
|
* Creates a callback function for emitting breakeven events to breakevenSubject.
|
|
26372
26497
|
*
|
|
@@ -26452,7 +26577,7 @@ class BreakevenConnectionService {
|
|
|
26452
26577
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
26453
26578
|
* Value: ClientBreakeven instance with logger and event emitter
|
|
26454
26579
|
*/
|
|
26455
|
-
this.getBreakeven = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
26580
|
+
this.getBreakeven = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$g(signalId, backtest), (signalId, backtest) => {
|
|
26456
26581
|
return new ClientBreakeven({
|
|
26457
26582
|
signalId,
|
|
26458
26583
|
logger: this.loggerService,
|
|
@@ -26513,7 +26638,7 @@ class BreakevenConnectionService {
|
|
|
26513
26638
|
const breakeven = this.getBreakeven(data.id, backtest);
|
|
26514
26639
|
await breakeven.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
|
|
26515
26640
|
await breakeven.clear(symbol, data, priceClose, backtest);
|
|
26516
|
-
const key = CREATE_KEY_FN$
|
|
26641
|
+
const key = CREATE_KEY_FN$g(data.id, backtest);
|
|
26517
26642
|
this.getBreakeven.clear(key);
|
|
26518
26643
|
};
|
|
26519
26644
|
}
|
|
@@ -26529,7 +26654,7 @@ class BreakevenConnectionService {
|
|
|
26529
26654
|
* @param backtest - Whether running in backtest mode
|
|
26530
26655
|
* @returns Unique string key for memoization
|
|
26531
26656
|
*/
|
|
26532
|
-
const CREATE_KEY_FN$
|
|
26657
|
+
const CREATE_KEY_FN$f = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
26533
26658
|
const parts = [symbol, strategyName, exchangeName];
|
|
26534
26659
|
if (frameName)
|
|
26535
26660
|
parts.push(frameName);
|
|
@@ -26704,7 +26829,7 @@ class BreakevenMarkdownService {
|
|
|
26704
26829
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
26705
26830
|
* Each combination gets its own isolated storage instance.
|
|
26706
26831
|
*/
|
|
26707
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
26832
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$f(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$5(symbol, strategyName, exchangeName, frameName));
|
|
26708
26833
|
/**
|
|
26709
26834
|
* Subscribes to breakeven signal emitter to receive events.
|
|
26710
26835
|
* Protected against multiple subscriptions.
|
|
@@ -26893,7 +27018,7 @@ class BreakevenMarkdownService {
|
|
|
26893
27018
|
payload,
|
|
26894
27019
|
});
|
|
26895
27020
|
if (payload) {
|
|
26896
|
-
const key = CREATE_KEY_FN$
|
|
27021
|
+
const key = CREATE_KEY_FN$f(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
26897
27022
|
this.getStorage.clear(key);
|
|
26898
27023
|
}
|
|
26899
27024
|
else {
|
|
@@ -26909,7 +27034,7 @@ class BreakevenMarkdownService {
|
|
|
26909
27034
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
26910
27035
|
* @returns Unique string key for memoization
|
|
26911
27036
|
*/
|
|
26912
|
-
const CREATE_KEY_FN$
|
|
27037
|
+
const CREATE_KEY_FN$e = (context) => {
|
|
26913
27038
|
const parts = [context.strategyName, context.exchangeName];
|
|
26914
27039
|
if (context.frameName)
|
|
26915
27040
|
parts.push(context.frameName);
|
|
@@ -26983,7 +27108,7 @@ class BreakevenGlobalService {
|
|
|
26983
27108
|
* @param context - Context with strategyName, exchangeName and frameName
|
|
26984
27109
|
* @param methodName - Name of the calling method for error tracking
|
|
26985
27110
|
*/
|
|
26986
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
27111
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$e(context), (context, methodName) => {
|
|
26987
27112
|
this.loggerService.log("breakevenGlobalService validate", {
|
|
26988
27113
|
context,
|
|
26989
27114
|
methodName,
|
|
@@ -27204,7 +27329,7 @@ class ConfigValidationService {
|
|
|
27204
27329
|
* @param backtest - Whether running in backtest mode
|
|
27205
27330
|
* @returns Unique string key for memoization
|
|
27206
27331
|
*/
|
|
27207
|
-
const CREATE_KEY_FN$
|
|
27332
|
+
const CREATE_KEY_FN$d = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
27208
27333
|
const parts = [symbol, strategyName, exchangeName];
|
|
27209
27334
|
if (frameName)
|
|
27210
27335
|
parts.push(frameName);
|
|
@@ -27371,7 +27496,7 @@ class RiskMarkdownService {
|
|
|
27371
27496
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
27372
27497
|
* Each combination gets its own isolated storage instance.
|
|
27373
27498
|
*/
|
|
27374
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
27499
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$d(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$4(symbol, strategyName, exchangeName, frameName));
|
|
27375
27500
|
/**
|
|
27376
27501
|
* Subscribes to risk rejection emitter to receive rejection events.
|
|
27377
27502
|
* Protected against multiple subscriptions.
|
|
@@ -27560,7 +27685,7 @@ class RiskMarkdownService {
|
|
|
27560
27685
|
payload,
|
|
27561
27686
|
});
|
|
27562
27687
|
if (payload) {
|
|
27563
|
-
const key = CREATE_KEY_FN$
|
|
27688
|
+
const key = CREATE_KEY_FN$d(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
27564
27689
|
this.getStorage.clear(key);
|
|
27565
27690
|
}
|
|
27566
27691
|
else {
|
|
@@ -29942,7 +30067,7 @@ class HighestProfitReportService {
|
|
|
29942
30067
|
* @returns Colon-separated key string for memoization
|
|
29943
30068
|
* @internal
|
|
29944
30069
|
*/
|
|
29945
|
-
const CREATE_KEY_FN$
|
|
30070
|
+
const CREATE_KEY_FN$c = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
29946
30071
|
const parts = [symbol, strategyName, exchangeName];
|
|
29947
30072
|
if (frameName)
|
|
29948
30073
|
parts.push(frameName);
|
|
@@ -30184,7 +30309,7 @@ class StrategyMarkdownService {
|
|
|
30184
30309
|
*
|
|
30185
30310
|
* @internal
|
|
30186
30311
|
*/
|
|
30187
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
30312
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$c(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$3(symbol, strategyName, exchangeName, frameName));
|
|
30188
30313
|
/**
|
|
30189
30314
|
* Records a cancel-scheduled event when a scheduled signal is cancelled.
|
|
30190
30315
|
*
|
|
@@ -30758,7 +30883,7 @@ class StrategyMarkdownService {
|
|
|
30758
30883
|
this.clear = async (payload) => {
|
|
30759
30884
|
this.loggerService.log("strategyMarkdownService clear", { payload });
|
|
30760
30885
|
if (payload) {
|
|
30761
|
-
const key = CREATE_KEY_FN$
|
|
30886
|
+
const key = CREATE_KEY_FN$c(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
30762
30887
|
this.getStorage.clear(key);
|
|
30763
30888
|
}
|
|
30764
30889
|
else {
|
|
@@ -30866,7 +30991,7 @@ class StrategyMarkdownService {
|
|
|
30866
30991
|
* Creates a unique key for memoizing ReportStorage instances.
|
|
30867
30992
|
* Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
|
|
30868
30993
|
*/
|
|
30869
|
-
const CREATE_KEY_FN$
|
|
30994
|
+
const CREATE_KEY_FN$b = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
30870
30995
|
const parts = [symbol, strategyName, exchangeName];
|
|
30871
30996
|
if (frameName)
|
|
30872
30997
|
parts.push(frameName);
|
|
@@ -31059,7 +31184,7 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
31059
31184
|
class SyncMarkdownService {
|
|
31060
31185
|
constructor() {
|
|
31061
31186
|
this.loggerService = inject(TYPES.loggerService);
|
|
31062
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
31187
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$b(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$2(symbol, strategyName, exchangeName, frameName, backtest));
|
|
31063
31188
|
/**
|
|
31064
31189
|
* Subscribes to `syncSubject` to start receiving `SignalSyncContract` events.
|
|
31065
31190
|
* Protected against multiple subscriptions via `singleshot` — subsequent calls
|
|
@@ -31255,7 +31380,7 @@ class SyncMarkdownService {
|
|
|
31255
31380
|
this.clear = async (payload) => {
|
|
31256
31381
|
this.loggerService.log("syncMarkdownService clear", { payload });
|
|
31257
31382
|
if (payload) {
|
|
31258
|
-
const key = CREATE_KEY_FN$
|
|
31383
|
+
const key = CREATE_KEY_FN$b(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
31259
31384
|
this.getStorage.clear(key);
|
|
31260
31385
|
}
|
|
31261
31386
|
else {
|
|
@@ -31268,7 +31393,7 @@ class SyncMarkdownService {
|
|
|
31268
31393
|
/**
|
|
31269
31394
|
* Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
|
|
31270
31395
|
*/
|
|
31271
|
-
const CREATE_KEY_FN$
|
|
31396
|
+
const CREATE_KEY_FN$a = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
31272
31397
|
const parts = [symbol, strategyName, exchangeName];
|
|
31273
31398
|
if (frameName)
|
|
31274
31399
|
parts.push(frameName);
|
|
@@ -31444,7 +31569,7 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
31444
31569
|
class HighestProfitMarkdownService {
|
|
31445
31570
|
constructor() {
|
|
31446
31571
|
this.loggerService = inject(TYPES.loggerService);
|
|
31447
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
31572
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$a(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$1(symbol, strategyName, exchangeName, frameName));
|
|
31448
31573
|
/**
|
|
31449
31574
|
* Subscribes to `highestProfitSubject` to start receiving `HighestProfitContract`
|
|
31450
31575
|
* events. Protected against multiple subscriptions via `singleshot` — subsequent
|
|
@@ -31610,7 +31735,7 @@ class HighestProfitMarkdownService {
|
|
|
31610
31735
|
this.clear = async (payload) => {
|
|
31611
31736
|
this.loggerService.log("highestProfitMarkdownService clear", { payload });
|
|
31612
31737
|
if (payload) {
|
|
31613
|
-
const key = CREATE_KEY_FN$
|
|
31738
|
+
const key = CREATE_KEY_FN$a(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
31614
31739
|
this.getStorage.clear(key);
|
|
31615
31740
|
}
|
|
31616
31741
|
else {
|
|
@@ -31632,7 +31757,7 @@ const LISTEN_TIMEOUT$1 = 120000;
|
|
|
31632
31757
|
* @param backtest - Whether running in backtest mode
|
|
31633
31758
|
* @returns Unique string key for memoization
|
|
31634
31759
|
*/
|
|
31635
|
-
const CREATE_KEY_FN$
|
|
31760
|
+
const CREATE_KEY_FN$9 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
31636
31761
|
const parts = [symbol, strategyName, exchangeName];
|
|
31637
31762
|
if (frameName)
|
|
31638
31763
|
parts.push(frameName);
|
|
@@ -31675,7 +31800,7 @@ class PriceMetaService {
|
|
|
31675
31800
|
* Each subject holds the latest currentPrice emitted by the strategy iterator for that key.
|
|
31676
31801
|
* Instances are cached until clear() is called.
|
|
31677
31802
|
*/
|
|
31678
|
-
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
31803
|
+
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$9(symbol, strategyName, exchangeName, frameName, backtest), () => new functoolsKit.BehaviorSubject());
|
|
31679
31804
|
/**
|
|
31680
31805
|
* Returns the current market price for the given symbol and context.
|
|
31681
31806
|
*
|
|
@@ -31704,10 +31829,10 @@ class PriceMetaService {
|
|
|
31704
31829
|
if (source.data) {
|
|
31705
31830
|
return source.data;
|
|
31706
31831
|
}
|
|
31707
|
-
console.warn(`PriceMetaService: No currentPrice available for ${CREATE_KEY_FN$
|
|
31832
|
+
console.warn(`PriceMetaService: No currentPrice available for ${CREATE_KEY_FN$9(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
|
|
31708
31833
|
const currentPrice = await functoolsKit.waitForNext(source, (data) => !!data, LISTEN_TIMEOUT$1);
|
|
31709
31834
|
if (typeof currentPrice === "symbol") {
|
|
31710
|
-
throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$
|
|
31835
|
+
throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$9(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
|
|
31711
31836
|
}
|
|
31712
31837
|
return currentPrice;
|
|
31713
31838
|
};
|
|
@@ -31749,7 +31874,7 @@ class PriceMetaService {
|
|
|
31749
31874
|
this.getSource.clear();
|
|
31750
31875
|
return;
|
|
31751
31876
|
}
|
|
31752
|
-
const key = CREATE_KEY_FN$
|
|
31877
|
+
const key = CREATE_KEY_FN$9(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
31753
31878
|
this.getSource.clear(key);
|
|
31754
31879
|
};
|
|
31755
31880
|
}
|
|
@@ -31767,7 +31892,7 @@ const LISTEN_TIMEOUT = 120000;
|
|
|
31767
31892
|
* @param backtest - Whether running in backtest mode
|
|
31768
31893
|
* @returns Unique string key for memoization
|
|
31769
31894
|
*/
|
|
31770
|
-
const CREATE_KEY_FN$
|
|
31895
|
+
const CREATE_KEY_FN$8 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
31771
31896
|
const parts = [symbol, strategyName, exchangeName];
|
|
31772
31897
|
if (frameName)
|
|
31773
31898
|
parts.push(frameName);
|
|
@@ -31810,7 +31935,7 @@ class TimeMetaService {
|
|
|
31810
31935
|
* Each subject holds the latest createdAt timestamp emitted by the strategy iterator for that key.
|
|
31811
31936
|
* Instances are cached until clear() is called.
|
|
31812
31937
|
*/
|
|
31813
|
-
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
31938
|
+
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$8(symbol, strategyName, exchangeName, frameName, backtest), () => new functoolsKit.BehaviorSubject());
|
|
31814
31939
|
/**
|
|
31815
31940
|
* Returns the current candle timestamp (in milliseconds) for the given symbol and context.
|
|
31816
31941
|
*
|
|
@@ -31838,10 +31963,10 @@ class TimeMetaService {
|
|
|
31838
31963
|
if (source.data) {
|
|
31839
31964
|
return source.data;
|
|
31840
31965
|
}
|
|
31841
|
-
console.warn(`TimeMetaService: No timestamp available for ${CREATE_KEY_FN$
|
|
31966
|
+
console.warn(`TimeMetaService: No timestamp available for ${CREATE_KEY_FN$8(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
|
|
31842
31967
|
const timestamp = await functoolsKit.waitForNext(source, (data) => !!data, LISTEN_TIMEOUT);
|
|
31843
31968
|
if (typeof timestamp === "symbol") {
|
|
31844
|
-
throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$
|
|
31969
|
+
throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$8(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
|
|
31845
31970
|
}
|
|
31846
31971
|
return timestamp;
|
|
31847
31972
|
};
|
|
@@ -31883,7 +32008,7 @@ class TimeMetaService {
|
|
|
31883
32008
|
this.getSource.clear();
|
|
31884
32009
|
return;
|
|
31885
32010
|
}
|
|
31886
|
-
const key = CREATE_KEY_FN$
|
|
32011
|
+
const key = CREATE_KEY_FN$8(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
31887
32012
|
this.getSource.clear(key);
|
|
31888
32013
|
};
|
|
31889
32014
|
}
|
|
@@ -31979,7 +32104,7 @@ class MaxDrawdownReportService {
|
|
|
31979
32104
|
/**
|
|
31980
32105
|
* Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
|
|
31981
32106
|
*/
|
|
31982
|
-
const CREATE_KEY_FN$
|
|
32107
|
+
const CREATE_KEY_FN$7 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
31983
32108
|
const parts = [symbol, strategyName, exchangeName];
|
|
31984
32109
|
if (frameName)
|
|
31985
32110
|
parts.push(frameName);
|
|
@@ -32103,7 +32228,7 @@ class ReportStorage {
|
|
|
32103
32228
|
class MaxDrawdownMarkdownService {
|
|
32104
32229
|
constructor() {
|
|
32105
32230
|
this.loggerService = inject(TYPES.loggerService);
|
|
32106
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32231
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$7(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage(symbol, strategyName, exchangeName, frameName));
|
|
32107
32232
|
/**
|
|
32108
32233
|
* Subscribes to `maxDrawdownSubject` to start receiving `MaxDrawdownContract`
|
|
32109
32234
|
* events. Protected against multiple subscriptions via `singleshot`.
|
|
@@ -32182,7 +32307,7 @@ class MaxDrawdownMarkdownService {
|
|
|
32182
32307
|
this.clear = async (payload) => {
|
|
32183
32308
|
this.loggerService.log("maxDrawdownMarkdownService clear", { payload });
|
|
32184
32309
|
if (payload) {
|
|
32185
|
-
const key = CREATE_KEY_FN$
|
|
32310
|
+
const key = CREATE_KEY_FN$7(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32186
32311
|
this.getStorage.clear(key);
|
|
32187
32312
|
}
|
|
32188
32313
|
else {
|
|
@@ -32192,6 +32317,125 @@ class MaxDrawdownMarkdownService {
|
|
|
32192
32317
|
}
|
|
32193
32318
|
}
|
|
32194
32319
|
|
|
32320
|
+
const METHOD_NAME_COMMIT_SIGNAL_NOTIFY = "notificationHelperService.commitSignalNotify";
|
|
32321
|
+
const METHOD_NAME_VALIDATE = "notificationHelperService.validate";
|
|
32322
|
+
/**
|
|
32323
|
+
* Creates a unique key for memoizing validate calls.
|
|
32324
|
+
* Key format: "strategyName:exchangeName:frameName"
|
|
32325
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
32326
|
+
* @returns Unique string key for memoization
|
|
32327
|
+
*/
|
|
32328
|
+
const CREATE_KEY_FN$6 = (context) => {
|
|
32329
|
+
const parts = [context.strategyName, context.exchangeName];
|
|
32330
|
+
if (context.frameName)
|
|
32331
|
+
parts.push(context.frameName);
|
|
32332
|
+
return parts.join(":");
|
|
32333
|
+
};
|
|
32334
|
+
/**
|
|
32335
|
+
* Helper service for emitting signal info notifications.
|
|
32336
|
+
*
|
|
32337
|
+
* Handles validation (memoized per context) and emission of `signal.info` events
|
|
32338
|
+
* via `signalNotifySubject`. Used internally by the framework action pipeline —
|
|
32339
|
+
* end users interact with this via `commitSignalNotify()` in `onActivePing` callbacks.
|
|
32340
|
+
*/
|
|
32341
|
+
class NotificationHelperService {
|
|
32342
|
+
constructor() {
|
|
32343
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
32344
|
+
this.strategySchemaService = inject(TYPES.strategySchemaService);
|
|
32345
|
+
this.riskValidationService = inject(TYPES.riskValidationService);
|
|
32346
|
+
this.strategyValidationService = inject(TYPES.strategyValidationService);
|
|
32347
|
+
this.exchangeValidationService = inject(TYPES.exchangeValidationService);
|
|
32348
|
+
this.frameValidationService = inject(TYPES.frameValidationService);
|
|
32349
|
+
this.actionValidationService = inject(TYPES.actionValidationService);
|
|
32350
|
+
this.strategyCoreService = inject(TYPES.strategyCoreService);
|
|
32351
|
+
this.timeMetaService = inject(TYPES.timeMetaService);
|
|
32352
|
+
/**
|
|
32353
|
+
* Validates strategy, exchange, frame, risk, and action schemas for the given context.
|
|
32354
|
+
*
|
|
32355
|
+
* Memoized per unique `"strategyName:exchangeName[:frameName]"` key — subsequent calls
|
|
32356
|
+
* with the same context are no-ops, so validation runs at most once per context.
|
|
32357
|
+
*
|
|
32358
|
+
* @param context - Routing context: strategyName, exchangeName, frameName
|
|
32359
|
+
* @throws {Error} If any registered schema fails validation
|
|
32360
|
+
*/
|
|
32361
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$6(context), async (context) => {
|
|
32362
|
+
this.loggerService.log(METHOD_NAME_VALIDATE, {
|
|
32363
|
+
context,
|
|
32364
|
+
});
|
|
32365
|
+
this.strategyValidationService.validate(context.strategyName, METHOD_NAME_VALIDATE);
|
|
32366
|
+
this.exchangeValidationService.validate(context.exchangeName, METHOD_NAME_VALIDATE);
|
|
32367
|
+
const { riskName, riskList, actions } = this.strategySchemaService.get(context.strategyName);
|
|
32368
|
+
context.frameName &&
|
|
32369
|
+
this.frameValidationService.validate(context.frameName, METHOD_NAME_VALIDATE);
|
|
32370
|
+
riskName &&
|
|
32371
|
+
this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE);
|
|
32372
|
+
riskList &&
|
|
32373
|
+
riskList.forEach((riskName) => this.riskValidationService.validate(riskName, METHOD_NAME_VALIDATE));
|
|
32374
|
+
actions &&
|
|
32375
|
+
actions.forEach((actionName) => this.actionValidationService.validate(actionName, METHOD_NAME_VALIDATE));
|
|
32376
|
+
});
|
|
32377
|
+
/**
|
|
32378
|
+
* Emits a `signal.info` notification for the currently active pending signal.
|
|
32379
|
+
*
|
|
32380
|
+
* Validates all schemas (via memoized `validate`), resolves the pending signal
|
|
32381
|
+
* for the given symbol, then emits a `SignalInfoContract` via `signalNotifySubject`,
|
|
32382
|
+
* which is routed to all registered `listenSignalNotify` callbacks and persisted
|
|
32383
|
+
* by `NotificationAdapter`.
|
|
32384
|
+
*
|
|
32385
|
+
* @param payload - Optional notification fields (notificationId, notificationNote)
|
|
32386
|
+
* @param symbol - Trading pair symbol (e.g. "BTCUSDT")
|
|
32387
|
+
* @param currentPrice - Market price at the time of the call
|
|
32388
|
+
* @param context - Routing context: strategyName, exchangeName, frameName
|
|
32389
|
+
* @param backtest - true when called during a backtest run
|
|
32390
|
+
*
|
|
32391
|
+
* @throws {Error} If no active pending signal is found for the given symbol
|
|
32392
|
+
*
|
|
32393
|
+
* @example
|
|
32394
|
+
* ```typescript
|
|
32395
|
+
* // Inside onActivePing callback:
|
|
32396
|
+
* await commitSignalNotify("BTCUSDT", {
|
|
32397
|
+
* notificationNote: "RSI crossed 70, consider closing",
|
|
32398
|
+
* notificationId: "msg-123",
|
|
32399
|
+
* });
|
|
32400
|
+
* ```
|
|
32401
|
+
*/
|
|
32402
|
+
this.commitSignalNotify = async (payload, symbol, currentPrice, context, backtest) => {
|
|
32403
|
+
this.loggerService.info(METHOD_NAME_COMMIT_SIGNAL_NOTIFY, {
|
|
32404
|
+
symbol,
|
|
32405
|
+
context,
|
|
32406
|
+
backtest,
|
|
32407
|
+
currentPrice,
|
|
32408
|
+
});
|
|
32409
|
+
this.validate(context);
|
|
32410
|
+
const pendingSignal = await this.strategyCoreService.getPendingSignal(backtest, symbol, currentPrice, {
|
|
32411
|
+
strategyName: context.strategyName,
|
|
32412
|
+
exchangeName: context.exchangeName,
|
|
32413
|
+
frameName: "",
|
|
32414
|
+
});
|
|
32415
|
+
if (!pendingSignal) {
|
|
32416
|
+
throw new Error(`SignalUtils notify No pending signal found symbol=${symbol} `);
|
|
32417
|
+
}
|
|
32418
|
+
const timestamp = await this.timeMetaService.getTimestamp(symbol, {
|
|
32419
|
+
strategyName: context.strategyName,
|
|
32420
|
+
exchangeName: context.exchangeName,
|
|
32421
|
+
frameName: context.frameName,
|
|
32422
|
+
}, backtest);
|
|
32423
|
+
await signalNotifySubject.next({
|
|
32424
|
+
backtest,
|
|
32425
|
+
symbol,
|
|
32426
|
+
currentPrice,
|
|
32427
|
+
data: pendingSignal,
|
|
32428
|
+
exchangeName: context.exchangeName,
|
|
32429
|
+
strategyName: context.strategyName,
|
|
32430
|
+
frameName: context.frameName,
|
|
32431
|
+
note: payload.notificationNote || pendingSignal.note,
|
|
32432
|
+
notificationId: payload.notificationId,
|
|
32433
|
+
timestamp,
|
|
32434
|
+
});
|
|
32435
|
+
};
|
|
32436
|
+
}
|
|
32437
|
+
}
|
|
32438
|
+
|
|
32195
32439
|
{
|
|
32196
32440
|
provide(TYPES.loggerService, () => new LoggerService());
|
|
32197
32441
|
}
|
|
@@ -32280,6 +32524,9 @@ class MaxDrawdownMarkdownService {
|
|
|
32280
32524
|
provide(TYPES.highestProfitReportService, () => new HighestProfitReportService());
|
|
32281
32525
|
provide(TYPES.maxDrawdownReportService, () => new MaxDrawdownReportService());
|
|
32282
32526
|
}
|
|
32527
|
+
{
|
|
32528
|
+
provide(TYPES.notificationHelperService, () => new NotificationHelperService());
|
|
32529
|
+
}
|
|
32283
32530
|
{
|
|
32284
32531
|
provide(TYPES.exchangeValidationService, () => new ExchangeValidationService());
|
|
32285
32532
|
provide(TYPES.strategyValidationService, () => new StrategyValidationService());
|
|
@@ -32380,6 +32627,9 @@ const reportServices = {
|
|
|
32380
32627
|
highestProfitReportService: inject(TYPES.highestProfitReportService),
|
|
32381
32628
|
maxDrawdownReportService: inject(TYPES.maxDrawdownReportService),
|
|
32382
32629
|
};
|
|
32630
|
+
const helperServices = {
|
|
32631
|
+
notificationHelperService: inject(TYPES.notificationHelperService),
|
|
32632
|
+
};
|
|
32383
32633
|
const validationServices = {
|
|
32384
32634
|
exchangeValidationService: inject(TYPES.exchangeValidationService),
|
|
32385
32635
|
strategyValidationService: inject(TYPES.strategyValidationService),
|
|
@@ -32405,6 +32655,7 @@ const backtest = {
|
|
|
32405
32655
|
...markdownServices,
|
|
32406
32656
|
...reportServices,
|
|
32407
32657
|
...validationServices,
|
|
32658
|
+
...helperServices,
|
|
32408
32659
|
};
|
|
32409
32660
|
init();
|
|
32410
32661
|
|
|
@@ -35321,6 +35572,7 @@ const GET_POSITION_ENTRY_OVERLAP_METHOD_NAME = "strategy.getPositionEntryOverlap
|
|
|
35321
35572
|
const GET_POSITION_PARTIAL_OVERLAP_METHOD_NAME = "strategy.getPositionPartialOverlap";
|
|
35322
35573
|
const HAS_NO_PENDING_SIGNAL_METHOD_NAME = "strategy.hasNoPendingSignal";
|
|
35323
35574
|
const HAS_NO_SCHEDULED_SIGNAL_METHOD_NAME = "strategy.hasNoScheduledSignal";
|
|
35575
|
+
const COMMIT_SIGNAL_NOTIFY_METHOD_NAME = "strategy.commitSignalNotify";
|
|
35324
35576
|
/**
|
|
35325
35577
|
* Cancels the scheduled signal without stopping the strategy.
|
|
35326
35578
|
*
|
|
@@ -37285,6 +37537,50 @@ async function hasNoScheduledSignal(symbol) {
|
|
|
37285
37537
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
37286
37538
|
return await functoolsKit.not(backtest.strategyCoreService.hasScheduledSignal(isBacktest, symbol, { exchangeName, frameName, strategyName }));
|
|
37287
37539
|
}
|
|
37540
|
+
/**
|
|
37541
|
+
* Emits a `signal.info` notification for the currently active pending signal.
|
|
37542
|
+
*
|
|
37543
|
+
* Broadcasts a user-defined informational note without affecting position state.
|
|
37544
|
+
* Useful for annotating strategy decisions, triggering external alerts, or logging
|
|
37545
|
+
* mid-position events (e.g. RSI crossing a threshold, volume spike detected).
|
|
37546
|
+
*
|
|
37547
|
+
* Automatically reads backtest/live mode from execution context.
|
|
37548
|
+
* Automatically reads strategyName, exchangeName, frameName from method context.
|
|
37549
|
+
* Automatically fetches current price via getAveragePrice.
|
|
37550
|
+
*
|
|
37551
|
+
* @param symbol - Trading pair symbol (e.g. "BTCUSDT")
|
|
37552
|
+
* @param payload - Optional notification fields
|
|
37553
|
+
* @param payload.notificationNote - Human-readable note. Falls back to signal.note if omitted.
|
|
37554
|
+
* @param payload.notificationId - Optional correlation ID for external systems (e.g. Telegram message ID).
|
|
37555
|
+
*
|
|
37556
|
+
* @throws {Error} If called outside an execution context
|
|
37557
|
+
* @throws {Error} If called outside a method context
|
|
37558
|
+
* @throws {Error} If no active pending signal exists for the given symbol
|
|
37559
|
+
*
|
|
37560
|
+
* @example
|
|
37561
|
+
* ```typescript
|
|
37562
|
+
* import { commitSignalNotify } from "backtest-kit";
|
|
37563
|
+
*
|
|
37564
|
+
* // Inside onActivePing callback:
|
|
37565
|
+
* await commitSignalNotify("BTCUSDT", {
|
|
37566
|
+
* notificationNote: "RSI crossed 70, consider closing",
|
|
37567
|
+
* notificationId: "msg-123",
|
|
37568
|
+
* });
|
|
37569
|
+
* ```
|
|
37570
|
+
*/
|
|
37571
|
+
async function commitSignalNotify(symbol, payload = {}) {
|
|
37572
|
+
backtest.loggerService.info(COMMIT_SIGNAL_NOTIFY_METHOD_NAME, { symbol, payload });
|
|
37573
|
+
if (!ExecutionContextService.hasContext()) {
|
|
37574
|
+
throw new Error("commitSignalNotify requires an execution context");
|
|
37575
|
+
}
|
|
37576
|
+
if (!MethodContextService.hasContext()) {
|
|
37577
|
+
throw new Error("commitSignalNotify requires a method context");
|
|
37578
|
+
}
|
|
37579
|
+
const currentPrice = await getAveragePrice(symbol);
|
|
37580
|
+
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
37581
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
37582
|
+
await backtest.notificationHelperService.commitSignalNotify(payload, symbol, currentPrice, { strategyName, exchangeName, frameName }, isBacktest);
|
|
37583
|
+
}
|
|
37288
37584
|
|
|
37289
37585
|
const STOP_STRATEGY_METHOD_NAME = "control.stopStrategy";
|
|
37290
37586
|
/**
|
|
@@ -37368,6 +37664,8 @@ const LISTEN_HIGHEST_PROFIT_METHOD_NAME = "event.listenHighestProfit";
|
|
|
37368
37664
|
const LISTEN_HIGHEST_PROFIT_ONCE_METHOD_NAME = "event.listenHighestProfitOnce";
|
|
37369
37665
|
const LISTEN_MAX_DRAWDOWN_METHOD_NAME = "event.listenMaxDrawdown";
|
|
37370
37666
|
const LISTEN_MAX_DRAWDOWN_ONCE_METHOD_NAME = "event.listenMaxDrawdownOnce";
|
|
37667
|
+
const LISTEN_SIGNAL_NOTIFY_METHOD_NAME = "event.listenSignalNotify";
|
|
37668
|
+
const LISTEN_SIGNAL_NOTIFY_ONCE_METHOD_NAME = "event.listenSignalNotifyOnce";
|
|
37371
37669
|
/**
|
|
37372
37670
|
* Subscribes to all signal events with queued async processing.
|
|
37373
37671
|
*
|
|
@@ -38759,6 +39057,46 @@ function listenMaxDrawdownOnce(filterFn, fn) {
|
|
|
38759
39057
|
};
|
|
38760
39058
|
return disposeFn = listenMaxDrawdown(wrappedFn);
|
|
38761
39059
|
}
|
|
39060
|
+
/**
|
|
39061
|
+
* Subscribes to signal info events with queued async processing.
|
|
39062
|
+
* Emits when a strategy calls commitSignalInfo() to broadcast a user-defined note for an open position.
|
|
39063
|
+
* Events are processed sequentially in order received, even if callback is async.
|
|
39064
|
+
* Uses queued wrapper to prevent concurrent execution of the callback.
|
|
39065
|
+
* @param fn - Callback function to handle signal info events
|
|
39066
|
+
* @return Unsubscribe function to stop listening to events
|
|
39067
|
+
*/
|
|
39068
|
+
function listenSignalNotify(fn) {
|
|
39069
|
+
backtest.loggerService.log(LISTEN_SIGNAL_NOTIFY_METHOD_NAME);
|
|
39070
|
+
const wrappedFn = async (event) => {
|
|
39071
|
+
if (await backtest.strategyCoreService.hasPendingSignal(event.backtest, event.symbol, {
|
|
39072
|
+
strategyName: event.strategyName,
|
|
39073
|
+
exchangeName: event.exchangeName,
|
|
39074
|
+
frameName: event.frameName,
|
|
39075
|
+
})) {
|
|
39076
|
+
await fn(event);
|
|
39077
|
+
}
|
|
39078
|
+
};
|
|
39079
|
+
return signalNotifySubject.subscribe(functoolsKit.queued(wrappedFn));
|
|
39080
|
+
}
|
|
39081
|
+
/**
|
|
39082
|
+
* Subscribes to filtered signal info events with one-time execution.
|
|
39083
|
+
* Listens for events matching the filter predicate, then executes callback once
|
|
39084
|
+
* and automatically unsubscribes.
|
|
39085
|
+
* @param filterFn - Predicate to filter which events trigger the callback
|
|
39086
|
+
* @param fn - Callback function to handle the filtered event (called only once)
|
|
39087
|
+
* @return Unsubscribe function to cancel the listener before it fires
|
|
39088
|
+
*/
|
|
39089
|
+
function listenSignalNotifyOnce(filterFn, fn) {
|
|
39090
|
+
backtest.loggerService.log(LISTEN_SIGNAL_NOTIFY_ONCE_METHOD_NAME);
|
|
39091
|
+
let disposeFn;
|
|
39092
|
+
const wrappedFn = async (event) => {
|
|
39093
|
+
if (filterFn(event)) {
|
|
39094
|
+
await fn(event);
|
|
39095
|
+
disposeFn && disposeFn();
|
|
39096
|
+
}
|
|
39097
|
+
};
|
|
39098
|
+
return disposeFn = listenSignalNotify(wrappedFn);
|
|
39099
|
+
}
|
|
38762
39100
|
|
|
38763
39101
|
const BACKTEST_METHOD_NAME_RUN = "BacktestUtils.run";
|
|
38764
39102
|
const BACKTEST_METHOD_NAME_BACKGROUND = "BacktestUtils.background";
|
|
@@ -38815,6 +39153,7 @@ const BACKTEST_METHOD_NAME_TRAILING_STOP_COST = "BacktestUtils.commitTrailingSto
|
|
|
38815
39153
|
const BACKTEST_METHOD_NAME_TRAILING_PROFIT_COST = "BacktestUtils.commitTrailingTakeCost";
|
|
38816
39154
|
const BACKTEST_METHOD_NAME_ACTIVATE_SCHEDULED = "Backtest.commitActivateScheduled";
|
|
38817
39155
|
const BACKTEST_METHOD_NAME_AVERAGE_BUY = "Backtest.commitAverageBuy";
|
|
39156
|
+
const BACKTEST_METHOD_NAME_SIGNAL_NOTIFY = "Backtest.commitSignalNotify";
|
|
38818
39157
|
const BACKTEST_METHOD_NAME_GET_DATA = "BacktestUtils.getData";
|
|
38819
39158
|
const BACKTEST_METHOD_NAME_HAS_NO_PENDING_SIGNAL = "BacktestUtils.hasNoPendingSignal";
|
|
38820
39159
|
const BACKTEST_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL = "BacktestUtils.hasNoScheduledSignal";
|
|
@@ -41170,6 +41509,33 @@ class BacktestUtils {
|
|
|
41170
41509
|
});
|
|
41171
41510
|
return await backtest.strategyCoreService.averageBuy(true, symbol, currentPrice, context, cost);
|
|
41172
41511
|
};
|
|
41512
|
+
/**
|
|
41513
|
+
* Emits a `signal.info` notification for the currently active pending signal.
|
|
41514
|
+
*
|
|
41515
|
+
* @param symbol - Trading pair symbol
|
|
41516
|
+
* @param currentPrice - Market price at the time of the call
|
|
41517
|
+
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
41518
|
+
* @param payload - Optional notification fields (notificationNote, notificationId)
|
|
41519
|
+
*
|
|
41520
|
+
* @throws {Error} If no active pending signal exists for the given symbol
|
|
41521
|
+
*
|
|
41522
|
+
* @example
|
|
41523
|
+
* ```typescript
|
|
41524
|
+
* await Backtest.commitSignalNotify("BTCUSDT", 42000, {
|
|
41525
|
+
* strategyName: "my-strategy",
|
|
41526
|
+
* exchangeName: "binance",
|
|
41527
|
+
* frameName: "1h"
|
|
41528
|
+
* }, { notificationNote: "RSI crossed 70" });
|
|
41529
|
+
* ```
|
|
41530
|
+
*/
|
|
41531
|
+
this.commitSignalNotify = async (symbol, currentPrice, context, payload = {}) => {
|
|
41532
|
+
backtest.loggerService.info(BACKTEST_METHOD_NAME_SIGNAL_NOTIFY, {
|
|
41533
|
+
symbol,
|
|
41534
|
+
currentPrice,
|
|
41535
|
+
context,
|
|
41536
|
+
});
|
|
41537
|
+
await backtest.notificationHelperService.commitSignalNotify(payload, symbol, currentPrice, context, true);
|
|
41538
|
+
};
|
|
41173
41539
|
/**
|
|
41174
41540
|
* Gets statistical data from all closed signals for a symbol-strategy pair.
|
|
41175
41541
|
*
|
|
@@ -41383,6 +41749,7 @@ const LIVE_METHOD_NAME_TRAILING_STOP_COST = "LiveUtils.commitTrailingStopCost";
|
|
|
41383
41749
|
const LIVE_METHOD_NAME_TRAILING_PROFIT_COST = "LiveUtils.commitTrailingTakeCost";
|
|
41384
41750
|
const LIVE_METHOD_NAME_ACTIVATE_SCHEDULED = "Live.commitActivateScheduled";
|
|
41385
41751
|
const LIVE_METHOD_NAME_AVERAGE_BUY = "Live.commitAverageBuy";
|
|
41752
|
+
const LIVE_METHOD_NAME_SIGNAL_NOTIFY = "Live.commitSignalNotify";
|
|
41386
41753
|
const LIVE_METHOD_NAME_HAS_NO_PENDING_SIGNAL = "LiveUtils.hasNoPendingSignal";
|
|
41387
41754
|
const LIVE_METHOD_NAME_HAS_NO_SCHEDULED_SIGNAL = "LiveUtils.hasNoScheduledSignal";
|
|
41388
41755
|
/**
|
|
@@ -44079,6 +44446,36 @@ class LiveUtils {
|
|
|
44079
44446
|
frameName: "",
|
|
44080
44447
|
}, cost);
|
|
44081
44448
|
};
|
|
44449
|
+
/**
|
|
44450
|
+
* Emits a `signal.info` notification for the currently active pending signal.
|
|
44451
|
+
*
|
|
44452
|
+
* @param symbol - Trading pair symbol
|
|
44453
|
+
* @param currentPrice - Market price at the time of the call
|
|
44454
|
+
* @param context - Execution context with strategyName and exchangeName
|
|
44455
|
+
* @param payload - Optional notification fields (notificationNote, notificationId)
|
|
44456
|
+
*
|
|
44457
|
+
* @throws {Error} If no active pending signal exists for the given symbol
|
|
44458
|
+
*
|
|
44459
|
+
* @example
|
|
44460
|
+
* ```typescript
|
|
44461
|
+
* await Live.commitSignalNotify("BTCUSDT", 42000, {
|
|
44462
|
+
* strategyName: "my-strategy",
|
|
44463
|
+
* exchangeName: "binance",
|
|
44464
|
+
* }, { notificationNote: "RSI crossed 70" });
|
|
44465
|
+
* ```
|
|
44466
|
+
*/
|
|
44467
|
+
this.commitSignalNotify = async (symbol, currentPrice, context, payload = {}) => {
|
|
44468
|
+
backtest.loggerService.info(LIVE_METHOD_NAME_SIGNAL_NOTIFY, {
|
|
44469
|
+
symbol,
|
|
44470
|
+
currentPrice,
|
|
44471
|
+
context,
|
|
44472
|
+
});
|
|
44473
|
+
await backtest.notificationHelperService.commitSignalNotify(payload, symbol, currentPrice, {
|
|
44474
|
+
strategyName: context.strategyName,
|
|
44475
|
+
exchangeName: context.exchangeName,
|
|
44476
|
+
frameName: "",
|
|
44477
|
+
}, false);
|
|
44478
|
+
};
|
|
44082
44479
|
/**
|
|
44083
44480
|
* Gets statistical data from all live trading events for a symbol-strategy pair.
|
|
44084
44481
|
*
|
|
@@ -45248,6 +45645,503 @@ async function listRiskSchema() {
|
|
|
45248
45645
|
return await backtest.riskValidationService.list();
|
|
45249
45646
|
}
|
|
45250
45647
|
|
|
45648
|
+
const RECENT_PERSIST_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentPersistBacktestUtils.handleActivePing";
|
|
45649
|
+
const RECENT_PERSIST_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL = "RecentPersistBacktestUtils.getLatestSignal";
|
|
45650
|
+
const RECENT_PERSIST_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentPersistLiveUtils.handleActivePing";
|
|
45651
|
+
const RECENT_PERSIST_LIVE_METHOD_NAME_GET_LATEST_SIGNAL = "RecentPersistLiveUtils.getLatestSignal";
|
|
45652
|
+
const RECENT_MEMORY_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentMemoryBacktestUtils.handleActivePing";
|
|
45653
|
+
const RECENT_MEMORY_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL = "RecentMemoryBacktestUtils.getLatestSignal";
|
|
45654
|
+
const RECENT_MEMORY_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentMemoryLiveUtils.handleActivePing";
|
|
45655
|
+
const RECENT_MEMORY_LIVE_METHOD_NAME_GET_LATEST_SIGNAL = "RecentMemoryLiveUtils.getLatestSignal";
|
|
45656
|
+
const RECENT_BACKTEST_ADAPTER_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentBacktestAdapter.handleActivePing";
|
|
45657
|
+
const RECENT_BACKTEST_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL = "RecentBacktestAdapter.getLatestSignal";
|
|
45658
|
+
const RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER = "RecentBacktestAdapter.useRecentAdapter";
|
|
45659
|
+
const RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST = "RecentBacktestAdapter.usePersist";
|
|
45660
|
+
const RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_MEMORY = "RecentBacktestAdapter.useMemory";
|
|
45661
|
+
const RECENT_BACKTEST_ADAPTER_METHOD_NAME_CLEAR = "RecentBacktestAdapter.clear";
|
|
45662
|
+
const RECENT_LIVE_ADAPTER_METHOD_NAME_HANDLE_ACTIVE_PING = "RecentLiveAdapter.handleActivePing";
|
|
45663
|
+
const RECENT_LIVE_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL = "RecentLiveAdapter.getLatestSignal";
|
|
45664
|
+
const RECENT_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER = "RecentLiveAdapter.useRecentAdapter";
|
|
45665
|
+
const RECENT_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST = "RecentLiveAdapter.usePersist";
|
|
45666
|
+
const RECENT_LIVE_ADAPTER_METHOD_NAME_USE_MEMORY = "RecentLiveAdapter.useMemory";
|
|
45667
|
+
const RECENT_LIVE_ADAPTER_METHOD_NAME_CLEAR = "RecentLiveAdapter.clear";
|
|
45668
|
+
const RECENT_ADAPTER_METHOD_NAME_ENABLE = "RecentAdapter.enable";
|
|
45669
|
+
const RECENT_ADAPTER_METHOD_NAME_DISABLE = "RecentAdapter.disable";
|
|
45670
|
+
const RECENT_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL = "RecentAdapter.getLatestSignal";
|
|
45671
|
+
/**
|
|
45672
|
+
* Builds a composite storage key from context parts.
|
|
45673
|
+
* Includes backtest flag as the last segment to prevent live/backtest collisions.
|
|
45674
|
+
* @param symbol - Trading pair symbol
|
|
45675
|
+
* @param strategyName - Strategy identifier
|
|
45676
|
+
* @param exchangeName - Exchange identifier
|
|
45677
|
+
* @param frameName - Frame identifier
|
|
45678
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
45679
|
+
* @returns Composite key string
|
|
45680
|
+
*/
|
|
45681
|
+
const CREATE_KEY_FN$5 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
45682
|
+
const parts = [symbol, strategyName, exchangeName];
|
|
45683
|
+
if (frameName)
|
|
45684
|
+
parts.push(frameName);
|
|
45685
|
+
parts.push(backtest ? "backtest" : "live");
|
|
45686
|
+
return parts.join(":");
|
|
45687
|
+
};
|
|
45688
|
+
/**
|
|
45689
|
+
* Persistent storage adapter for backtest recent signals.
|
|
45690
|
+
*
|
|
45691
|
+
* Features:
|
|
45692
|
+
* - Persists the latest active signal per context to disk using PersistRecentAdapter
|
|
45693
|
+
* - Handles active ping events only
|
|
45694
|
+
*
|
|
45695
|
+
* Use this adapter for backtest recent signal persistence across sessions.
|
|
45696
|
+
*/
|
|
45697
|
+
class RecentPersistBacktestUtils {
|
|
45698
|
+
constructor() {
|
|
45699
|
+
/**
|
|
45700
|
+
* Handles active ping event.
|
|
45701
|
+
* Persists the latest signal to disk via PersistRecentAdapter.
|
|
45702
|
+
* @param event - Active ping contract with signal data and backtest flag
|
|
45703
|
+
*/
|
|
45704
|
+
this.handleActivePing = async (event) => {
|
|
45705
|
+
backtest.loggerService.info(RECENT_PERSIST_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
45706
|
+
signalId: event.data.id,
|
|
45707
|
+
});
|
|
45708
|
+
await PersistRecentAdapter.writeRecentData(event.data, event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
|
|
45709
|
+
};
|
|
45710
|
+
/**
|
|
45711
|
+
* Retrieves the latest persisted signal for the given context.
|
|
45712
|
+
* @param symbol - Trading pair symbol
|
|
45713
|
+
* @param strategyName - Strategy identifier
|
|
45714
|
+
* @param exchangeName - Exchange identifier
|
|
45715
|
+
* @param frameName - Frame identifier
|
|
45716
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
45717
|
+
* @returns The latest signal or null if not found
|
|
45718
|
+
*/
|
|
45719
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
45720
|
+
backtest.loggerService.info(RECENT_PERSIST_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
45721
|
+
symbol,
|
|
45722
|
+
strategyName,
|
|
45723
|
+
exchangeName,
|
|
45724
|
+
frameName,
|
|
45725
|
+
backtest: backtest$1,
|
|
45726
|
+
});
|
|
45727
|
+
return await PersistRecentAdapter.readRecentData(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
45728
|
+
};
|
|
45729
|
+
}
|
|
45730
|
+
}
|
|
45731
|
+
/**
|
|
45732
|
+
* In-memory storage adapter for backtest recent signals.
|
|
45733
|
+
*
|
|
45734
|
+
* Features:
|
|
45735
|
+
* - Stores the latest active signal per context key in memory only
|
|
45736
|
+
* - Fast read/write operations
|
|
45737
|
+
* - Data is lost when application restarts
|
|
45738
|
+
*
|
|
45739
|
+
* Use this adapter for testing or when persistence is not required.
|
|
45740
|
+
*/
|
|
45741
|
+
class RecentMemoryBacktestUtils {
|
|
45742
|
+
constructor() {
|
|
45743
|
+
/** Map of composite context keys to the latest signal */
|
|
45744
|
+
this._signals = new Map();
|
|
45745
|
+
/**
|
|
45746
|
+
* Handles active ping event.
|
|
45747
|
+
* Stores the latest signal in memory under the composite context key.
|
|
45748
|
+
* @param event - Active ping contract with signal data and backtest flag
|
|
45749
|
+
*/
|
|
45750
|
+
this.handleActivePing = async (event) => {
|
|
45751
|
+
backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
45752
|
+
signalId: event.data.id,
|
|
45753
|
+
});
|
|
45754
|
+
const key = CREATE_KEY_FN$5(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
|
|
45755
|
+
this._signals.set(key, event.data);
|
|
45756
|
+
};
|
|
45757
|
+
/**
|
|
45758
|
+
* Retrieves the latest in-memory signal for the given context.
|
|
45759
|
+
* @param symbol - Trading pair symbol
|
|
45760
|
+
* @param strategyName - Strategy identifier
|
|
45761
|
+
* @param exchangeName - Exchange identifier
|
|
45762
|
+
* @param frameName - Frame identifier
|
|
45763
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
45764
|
+
* @returns The latest signal or null if not found
|
|
45765
|
+
*/
|
|
45766
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
45767
|
+
const key = CREATE_KEY_FN$5(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
45768
|
+
backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
45769
|
+
return this._signals.get(key) ?? null;
|
|
45770
|
+
};
|
|
45771
|
+
}
|
|
45772
|
+
}
|
|
45773
|
+
/**
|
|
45774
|
+
* Persistent storage adapter for live recent signals.
|
|
45775
|
+
*
|
|
45776
|
+
* Features:
|
|
45777
|
+
* - Persists the latest active signal per context to disk using PersistRecentAdapter
|
|
45778
|
+
* - Handles active ping events only
|
|
45779
|
+
*
|
|
45780
|
+
* Use this adapter (default) for live recent signal persistence across sessions.
|
|
45781
|
+
*/
|
|
45782
|
+
class RecentPersistLiveUtils {
|
|
45783
|
+
constructor() {
|
|
45784
|
+
/**
|
|
45785
|
+
* Handles active ping event.
|
|
45786
|
+
* Persists the latest signal to disk via PersistRecentAdapter.
|
|
45787
|
+
* @param event - Active ping contract with signal data and backtest flag
|
|
45788
|
+
*/
|
|
45789
|
+
this.handleActivePing = async (event) => {
|
|
45790
|
+
backtest.loggerService.info(RECENT_PERSIST_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
45791
|
+
signalId: event.data.id,
|
|
45792
|
+
});
|
|
45793
|
+
await PersistRecentAdapter.writeRecentData(event.data, event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
|
|
45794
|
+
};
|
|
45795
|
+
/**
|
|
45796
|
+
* Retrieves the latest persisted signal for the given context.
|
|
45797
|
+
* @param symbol - Trading pair symbol
|
|
45798
|
+
* @param strategyName - Strategy identifier
|
|
45799
|
+
* @param exchangeName - Exchange identifier
|
|
45800
|
+
* @param frameName - Frame identifier
|
|
45801
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
45802
|
+
* @returns The latest signal or null if not found
|
|
45803
|
+
*/
|
|
45804
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
45805
|
+
backtest.loggerService.info(RECENT_PERSIST_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
45806
|
+
symbol,
|
|
45807
|
+
strategyName,
|
|
45808
|
+
exchangeName,
|
|
45809
|
+
frameName,
|
|
45810
|
+
backtest: backtest$1,
|
|
45811
|
+
});
|
|
45812
|
+
return await PersistRecentAdapter.readRecentData(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
45813
|
+
};
|
|
45814
|
+
}
|
|
45815
|
+
}
|
|
45816
|
+
/**
|
|
45817
|
+
* In-memory storage adapter for live recent signals.
|
|
45818
|
+
*
|
|
45819
|
+
* Features:
|
|
45820
|
+
* - Stores the latest active signal per context key in memory only
|
|
45821
|
+
* - Fast read/write operations
|
|
45822
|
+
* - Data is lost when application restarts
|
|
45823
|
+
*
|
|
45824
|
+
* Use this adapter for testing or when persistence is not required.
|
|
45825
|
+
*/
|
|
45826
|
+
class RecentMemoryLiveUtils {
|
|
45827
|
+
constructor() {
|
|
45828
|
+
/** Map of composite context keys to the latest signal */
|
|
45829
|
+
this._signals = new Map();
|
|
45830
|
+
/**
|
|
45831
|
+
* Handles active ping event.
|
|
45832
|
+
* Stores the latest signal in memory under the composite context key.
|
|
45833
|
+
* @param event - Active ping contract with signal data and backtest flag
|
|
45834
|
+
*/
|
|
45835
|
+
this.handleActivePing = async (event) => {
|
|
45836
|
+
backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
45837
|
+
signalId: event.data.id,
|
|
45838
|
+
});
|
|
45839
|
+
const key = CREATE_KEY_FN$5(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
|
|
45840
|
+
this._signals.set(key, event.data);
|
|
45841
|
+
};
|
|
45842
|
+
/**
|
|
45843
|
+
* Retrieves the latest in-memory signal for the given context.
|
|
45844
|
+
* @param symbol - Trading pair symbol
|
|
45845
|
+
* @param strategyName - Strategy identifier
|
|
45846
|
+
* @param exchangeName - Exchange identifier
|
|
45847
|
+
* @param frameName - Frame identifier
|
|
45848
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
45849
|
+
* @returns The latest signal or null if not found
|
|
45850
|
+
*/
|
|
45851
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
45852
|
+
const key = CREATE_KEY_FN$5(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
45853
|
+
backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
45854
|
+
return this._signals.get(key) ?? null;
|
|
45855
|
+
};
|
|
45856
|
+
}
|
|
45857
|
+
}
|
|
45858
|
+
/**
|
|
45859
|
+
* Backtest recent signal adapter with pluggable storage backend.
|
|
45860
|
+
*
|
|
45861
|
+
* Features:
|
|
45862
|
+
* - Adapter pattern for swappable storage implementations
|
|
45863
|
+
* - Default adapter: RecentMemoryBacktestUtils (in-memory storage)
|
|
45864
|
+
* - Alternative adapter: RecentPersistBacktestUtils
|
|
45865
|
+
* - Convenience methods: usePersist(), useMemory()
|
|
45866
|
+
*/
|
|
45867
|
+
class RecentBacktestAdapter {
|
|
45868
|
+
constructor() {
|
|
45869
|
+
/** Internal storage utils instance */
|
|
45870
|
+
this._recentBacktestUtils = new RecentMemoryBacktestUtils();
|
|
45871
|
+
/**
|
|
45872
|
+
* Handles active ping event.
|
|
45873
|
+
* Proxies call to the underlying storage adapter.
|
|
45874
|
+
* @param event - Active ping contract with signal data
|
|
45875
|
+
*/
|
|
45876
|
+
this.handleActivePing = async (event) => {
|
|
45877
|
+
backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
45878
|
+
signalId: event.data.id,
|
|
45879
|
+
});
|
|
45880
|
+
return await this._recentBacktestUtils.handleActivePing(event);
|
|
45881
|
+
};
|
|
45882
|
+
/**
|
|
45883
|
+
* Retrieves the latest signal for the given context.
|
|
45884
|
+
* Proxies call to the underlying storage adapter.
|
|
45885
|
+
* @param symbol - Trading pair symbol
|
|
45886
|
+
* @param strategyName - Strategy identifier
|
|
45887
|
+
* @param exchangeName - Exchange identifier
|
|
45888
|
+
* @param frameName - Frame identifier
|
|
45889
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
45890
|
+
* @returns The latest signal or null if not found
|
|
45891
|
+
*/
|
|
45892
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
45893
|
+
backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
45894
|
+
symbol,
|
|
45895
|
+
strategyName,
|
|
45896
|
+
exchangeName,
|
|
45897
|
+
frameName,
|
|
45898
|
+
backtest: backtest$1,
|
|
45899
|
+
});
|
|
45900
|
+
return await this._recentBacktestUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
45901
|
+
};
|
|
45902
|
+
/**
|
|
45903
|
+
* Sets the storage adapter constructor.
|
|
45904
|
+
* All future storage operations will use this adapter.
|
|
45905
|
+
* @param Ctor - Constructor for recent adapter
|
|
45906
|
+
*/
|
|
45907
|
+
this.useRecentAdapter = (Ctor) => {
|
|
45908
|
+
backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER);
|
|
45909
|
+
this._recentBacktestUtils = Reflect.construct(Ctor, []);
|
|
45910
|
+
};
|
|
45911
|
+
/**
|
|
45912
|
+
* Switches to persistent storage adapter.
|
|
45913
|
+
* Signals will be persisted to disk.
|
|
45914
|
+
*/
|
|
45915
|
+
this.usePersist = () => {
|
|
45916
|
+
backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
45917
|
+
this._recentBacktestUtils = new RecentPersistBacktestUtils();
|
|
45918
|
+
};
|
|
45919
|
+
/**
|
|
45920
|
+
* Switches to in-memory storage adapter (default).
|
|
45921
|
+
* Signals will be stored in memory only.
|
|
45922
|
+
*/
|
|
45923
|
+
this.useMemory = () => {
|
|
45924
|
+
backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_USE_MEMORY);
|
|
45925
|
+
this._recentBacktestUtils = new RecentMemoryBacktestUtils();
|
|
45926
|
+
};
|
|
45927
|
+
/**
|
|
45928
|
+
* Clears the cached utils instance by resetting to the default in-memory adapter.
|
|
45929
|
+
*/
|
|
45930
|
+
this.clear = () => {
|
|
45931
|
+
backtest.loggerService.info(RECENT_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
|
|
45932
|
+
this._recentBacktestUtils = new RecentMemoryBacktestUtils();
|
|
45933
|
+
};
|
|
45934
|
+
}
|
|
45935
|
+
}
|
|
45936
|
+
/**
|
|
45937
|
+
* Live recent signal adapter with pluggable storage backend.
|
|
45938
|
+
*
|
|
45939
|
+
* Features:
|
|
45940
|
+
* - Adapter pattern for swappable storage implementations
|
|
45941
|
+
* - Default adapter: RecentPersistLiveUtils (persistent storage)
|
|
45942
|
+
* - Alternative adapter: RecentMemoryLiveUtils
|
|
45943
|
+
* - Convenience methods: usePersist(), useMemory()
|
|
45944
|
+
*/
|
|
45945
|
+
class RecentLiveAdapter {
|
|
45946
|
+
constructor() {
|
|
45947
|
+
/** Internal storage utils instance */
|
|
45948
|
+
this._recentLiveUtils = new RecentPersistLiveUtils();
|
|
45949
|
+
/**
|
|
45950
|
+
* Handles active ping event.
|
|
45951
|
+
* Proxies call to the underlying storage adapter.
|
|
45952
|
+
* @param event - Active ping contract with signal data
|
|
45953
|
+
*/
|
|
45954
|
+
this.handleActivePing = async (event) => {
|
|
45955
|
+
backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
45956
|
+
signalId: event.data.id,
|
|
45957
|
+
});
|
|
45958
|
+
return await this._recentLiveUtils.handleActivePing(event);
|
|
45959
|
+
};
|
|
45960
|
+
/**
|
|
45961
|
+
* Retrieves the latest signal for the given context.
|
|
45962
|
+
* Proxies call to the underlying storage adapter.
|
|
45963
|
+
* @param symbol - Trading pair symbol
|
|
45964
|
+
* @param strategyName - Strategy identifier
|
|
45965
|
+
* @param exchangeName - Exchange identifier
|
|
45966
|
+
* @param frameName - Frame identifier
|
|
45967
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
45968
|
+
* @returns The latest signal or null if not found
|
|
45969
|
+
*/
|
|
45970
|
+
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
45971
|
+
backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
45972
|
+
symbol,
|
|
45973
|
+
strategyName,
|
|
45974
|
+
exchangeName,
|
|
45975
|
+
frameName,
|
|
45976
|
+
backtest: backtest$1,
|
|
45977
|
+
});
|
|
45978
|
+
return await this._recentLiveUtils.getLatestSignal(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
45979
|
+
};
|
|
45980
|
+
/**
|
|
45981
|
+
* Sets the storage adapter constructor.
|
|
45982
|
+
* All future storage operations will use this adapter.
|
|
45983
|
+
* @param Ctor - Constructor for recent adapter
|
|
45984
|
+
*/
|
|
45985
|
+
this.useRecentAdapter = (Ctor) => {
|
|
45986
|
+
backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER);
|
|
45987
|
+
this._recentLiveUtils = Reflect.construct(Ctor, []);
|
|
45988
|
+
};
|
|
45989
|
+
/**
|
|
45990
|
+
* Switches to persistent storage adapter (default).
|
|
45991
|
+
* Signals will be persisted to disk.
|
|
45992
|
+
*/
|
|
45993
|
+
this.usePersist = () => {
|
|
45994
|
+
backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
45995
|
+
this._recentLiveUtils = new RecentPersistLiveUtils();
|
|
45996
|
+
};
|
|
45997
|
+
/**
|
|
45998
|
+
* Switches to in-memory storage adapter.
|
|
45999
|
+
* Signals will be stored in memory only.
|
|
46000
|
+
*/
|
|
46001
|
+
this.useMemory = () => {
|
|
46002
|
+
backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_USE_MEMORY);
|
|
46003
|
+
this._recentLiveUtils = new RecentMemoryLiveUtils();
|
|
46004
|
+
};
|
|
46005
|
+
/**
|
|
46006
|
+
* Clears the cached utils instance by resetting to the default persistent adapter.
|
|
46007
|
+
*/
|
|
46008
|
+
this.clear = () => {
|
|
46009
|
+
backtest.loggerService.info(RECENT_LIVE_ADAPTER_METHOD_NAME_CLEAR);
|
|
46010
|
+
this._recentLiveUtils = new RecentPersistLiveUtils();
|
|
46011
|
+
};
|
|
46012
|
+
}
|
|
46013
|
+
}
|
|
46014
|
+
/**
|
|
46015
|
+
* Main recent signal adapter that manages both backtest and live recent signal storage.
|
|
46016
|
+
*
|
|
46017
|
+
* Features:
|
|
46018
|
+
* - Subscribes to activePingSubject for automatic storage updates
|
|
46019
|
+
* - Provides unified access to the latest signal for any context
|
|
46020
|
+
* - Singleshot enable pattern prevents duplicate subscriptions
|
|
46021
|
+
* - Cleanup function for proper unsubscription
|
|
46022
|
+
*/
|
|
46023
|
+
class RecentAdapter {
|
|
46024
|
+
constructor() {
|
|
46025
|
+
/**
|
|
46026
|
+
* Enables recent signal storage by subscribing to activePingSubject.
|
|
46027
|
+
* Uses singleshot to ensure one-time subscription.
|
|
46028
|
+
*
|
|
46029
|
+
* @returns Cleanup function that unsubscribes from all emitters
|
|
46030
|
+
*/
|
|
46031
|
+
this.enable = functoolsKit.singleshot(() => {
|
|
46032
|
+
backtest.loggerService.info(RECENT_ADAPTER_METHOD_NAME_ENABLE);
|
|
46033
|
+
let unBacktest;
|
|
46034
|
+
let unLive;
|
|
46035
|
+
{
|
|
46036
|
+
const unBacktestPingActive = activePingSubject
|
|
46037
|
+
.filter(({ backtest }) => backtest)
|
|
46038
|
+
.connect((event) => RecentBacktest.handleActivePing(event));
|
|
46039
|
+
unBacktest = functoolsKit.compose(() => unBacktestPingActive());
|
|
46040
|
+
}
|
|
46041
|
+
{
|
|
46042
|
+
const unLivePingActive = activePingSubject
|
|
46043
|
+
.filter(({ backtest }) => !backtest)
|
|
46044
|
+
.connect((event) => RecentLive.handleActivePing(event));
|
|
46045
|
+
unLive = functoolsKit.compose(() => unLivePingActive());
|
|
46046
|
+
}
|
|
46047
|
+
const unEnable = () => this.enable.clear();
|
|
46048
|
+
return functoolsKit.compose(() => unBacktest(), () => unLive(), () => unEnable());
|
|
46049
|
+
});
|
|
46050
|
+
/**
|
|
46051
|
+
* Disables recent signal storage by unsubscribing from all emitters.
|
|
46052
|
+
* Safe to call multiple times.
|
|
46053
|
+
*/
|
|
46054
|
+
this.disable = () => {
|
|
46055
|
+
backtest.loggerService.info(RECENT_ADAPTER_METHOD_NAME_DISABLE);
|
|
46056
|
+
if (this.enable.hasValue()) {
|
|
46057
|
+
const lastSubscription = this.enable();
|
|
46058
|
+
lastSubscription();
|
|
46059
|
+
}
|
|
46060
|
+
};
|
|
46061
|
+
/**
|
|
46062
|
+
* Retrieves the latest active signal for the given symbol and context.
|
|
46063
|
+
* Searches backtest storage first, then live storage.
|
|
46064
|
+
*
|
|
46065
|
+
* @param symbol - Trading pair symbol
|
|
46066
|
+
* @param context - Execution context with strategyName, exchangeName, and frameName
|
|
46067
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
46068
|
+
* @returns The latest signal or null if not found
|
|
46069
|
+
* @throws Error if RecentAdapter is not enabled
|
|
46070
|
+
*/
|
|
46071
|
+
this.getLatestSignal = async (symbol, context) => {
|
|
46072
|
+
backtest.loggerService.info(RECENT_ADAPTER_METHOD_NAME_GET_LATEST_SIGNAL, {
|
|
46073
|
+
symbol,
|
|
46074
|
+
context,
|
|
46075
|
+
});
|
|
46076
|
+
if (!this.enable.hasValue()) {
|
|
46077
|
+
throw new Error("RecentAdapter is not enabled. Call enable() first.");
|
|
46078
|
+
}
|
|
46079
|
+
let result = null;
|
|
46080
|
+
if (result = await RecentBacktest.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, true)) {
|
|
46081
|
+
return result;
|
|
46082
|
+
}
|
|
46083
|
+
if (result = await RecentLive.getLatestSignal(symbol, context.strategyName, context.exchangeName, context.frameName, false)) {
|
|
46084
|
+
return result;
|
|
46085
|
+
}
|
|
46086
|
+
return null;
|
|
46087
|
+
};
|
|
46088
|
+
}
|
|
46089
|
+
}
|
|
46090
|
+
/**
|
|
46091
|
+
* Global singleton instance of RecentAdapter.
|
|
46092
|
+
* Provides unified recent signal management for backtest and live trading.
|
|
46093
|
+
*/
|
|
46094
|
+
const Recent = new RecentAdapter();
|
|
46095
|
+
/**
|
|
46096
|
+
* Global singleton instance of RecentLiveAdapter.
|
|
46097
|
+
* Provides live trading recent signal storage with pluggable backends.
|
|
46098
|
+
*/
|
|
46099
|
+
const RecentLive = new RecentLiveAdapter();
|
|
46100
|
+
/**
|
|
46101
|
+
* Global singleton instance of RecentBacktestAdapter.
|
|
46102
|
+
* Provides backtest recent signal storage with pluggable backends.
|
|
46103
|
+
*/
|
|
46104
|
+
const RecentBacktest = new RecentBacktestAdapter();
|
|
46105
|
+
|
|
46106
|
+
const GET_LATEST_SIGNAL_METHOD_NAME = "signal.getLatestSignal";
|
|
46107
|
+
/**
|
|
46108
|
+
* Returns the latest signal (pending or closed) for the current strategy context.
|
|
46109
|
+
*
|
|
46110
|
+
* Does not distinguish between active and closed signals — returns whichever
|
|
46111
|
+
* was recorded last. Useful for cooldown logic: e.g. skip opening a new position
|
|
46112
|
+
* for 4 hours after a stop-loss by checking the timestamp of the latest signal
|
|
46113
|
+
* regardless of its outcome.
|
|
46114
|
+
*
|
|
46115
|
+
* Searches backtest storage first, then live storage.
|
|
46116
|
+
* Returns null if no signal exists at all.
|
|
46117
|
+
*
|
|
46118
|
+
* Automatically detects backtest/live mode from execution context.
|
|
46119
|
+
*
|
|
46120
|
+
* @param symbol - Trading pair symbol
|
|
46121
|
+
* @returns Promise resolving to the latest signal or null
|
|
46122
|
+
*
|
|
46123
|
+
* @example
|
|
46124
|
+
* ```typescript
|
|
46125
|
+
* import { getLatestSignal } from "backtest-kit";
|
|
46126
|
+
*
|
|
46127
|
+
* const latest = await getLatestSignal("BTCUSDT");
|
|
46128
|
+
* if (latest && Date.now() - latest.closedAt < 4 * 60 * 60 * 1000) {
|
|
46129
|
+
* return; // cooldown after SL — skip new signal for 4 hours
|
|
46130
|
+
* }
|
|
46131
|
+
* ```
|
|
46132
|
+
*/
|
|
46133
|
+
async function getLatestSignal(symbol) {
|
|
46134
|
+
backtest.loggerService.info(GET_LATEST_SIGNAL_METHOD_NAME, { symbol });
|
|
46135
|
+
if (!ExecutionContextService.hasContext()) {
|
|
46136
|
+
throw new Error("getLatestSignal requires an execution context");
|
|
46137
|
+
}
|
|
46138
|
+
if (!MethodContextService.hasContext()) {
|
|
46139
|
+
throw new Error("getLatestSignal requires a method context");
|
|
46140
|
+
}
|
|
46141
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
46142
|
+
return await Recent.getLatestSignal(symbol, { exchangeName, frameName, strategyName });
|
|
46143
|
+
}
|
|
46144
|
+
|
|
45251
46145
|
const DEFAULT_BM25_K1 = 1.5;
|
|
45252
46146
|
const DEFAULT_BM25_B = 0.75;
|
|
45253
46147
|
const DEFAULT_BM25_SCORE = 0.5;
|
|
@@ -47776,6 +48670,87 @@ class MarkdownUtils {
|
|
|
47776
48670
|
backtest.maxDrawdownMarkdownService.unsubscribe();
|
|
47777
48671
|
}
|
|
47778
48672
|
};
|
|
48673
|
+
/**
|
|
48674
|
+
* Clears markdown report data selectively.
|
|
48675
|
+
*
|
|
48676
|
+
* Clears accumulated data for specified markdown services without unsubscribing.
|
|
48677
|
+
* Use this method to reset report data for specific services while keeping them active.
|
|
48678
|
+
*
|
|
48679
|
+
* Each cleared service will:
|
|
48680
|
+
* - Clear accumulated data for all reports
|
|
48681
|
+
* - Start fresh with new data for future events
|
|
48682
|
+
* - Not affect event subscriptions or report generation status
|
|
48683
|
+
*
|
|
48684
|
+
* @param config - Service configuration object specifying which services to clear. Defaults to clearing all services.
|
|
48685
|
+
* @param config.backtest - Clear backtest result report data
|
|
48686
|
+
* @param config.breakeven - Clear breakeven event tracking data
|
|
48687
|
+
* @param config.partial - Clear partial profit/loss event tracking data
|
|
48688
|
+
* @param config.heat - Clear portfolio heatmap analysis data
|
|
48689
|
+
* @param config.walker - Clear walker strategy comparison report data
|
|
48690
|
+
* @param config.performance - Clear performance bottleneck analysis data
|
|
48691
|
+
* @param config.risk - Clear risk rejection tracking data
|
|
48692
|
+
* @param config.schedule - Clear scheduled signal tracking data
|
|
48693
|
+
* @param config.live - Clear live trading event report data
|
|
48694
|
+
* @param config.strategy - Clear strategy report data
|
|
48695
|
+
* @param config.sync - Clear sync report data
|
|
48696
|
+
* @param config.highest_profit - Clear highest profit report data
|
|
48697
|
+
* @param config.max_drawdown - Clear max drawdown report data
|
|
48698
|
+
*/
|
|
48699
|
+
this.clear = ({ backtest: bt = false, breakeven = false, heat = false, live = false, partial = false, performance = false, risk = false, strategy = false, schedule = false, walker = false, sync = false, highest_profit = false, max_drawdown = false, } = WILDCARD_TARGET) => {
|
|
48700
|
+
LOGGER_SERVICE$1.debug(MARKDOWN_METHOD_NAME_CLEAR, {
|
|
48701
|
+
backtest: bt,
|
|
48702
|
+
breakeven,
|
|
48703
|
+
heat,
|
|
48704
|
+
live,
|
|
48705
|
+
partial,
|
|
48706
|
+
performance,
|
|
48707
|
+
risk,
|
|
48708
|
+
strategy,
|
|
48709
|
+
schedule,
|
|
48710
|
+
walker,
|
|
48711
|
+
sync,
|
|
48712
|
+
highest_profit,
|
|
48713
|
+
});
|
|
48714
|
+
if (bt) {
|
|
48715
|
+
backtest.backtestMarkdownService.clear();
|
|
48716
|
+
}
|
|
48717
|
+
if (breakeven) {
|
|
48718
|
+
backtest.breakevenMarkdownService.clear();
|
|
48719
|
+
}
|
|
48720
|
+
if (heat) {
|
|
48721
|
+
backtest.heatMarkdownService.clear();
|
|
48722
|
+
}
|
|
48723
|
+
if (live) {
|
|
48724
|
+
backtest.liveMarkdownService.clear();
|
|
48725
|
+
}
|
|
48726
|
+
if (partial) {
|
|
48727
|
+
backtest.partialMarkdownService.clear();
|
|
48728
|
+
}
|
|
48729
|
+
if (performance) {
|
|
48730
|
+
backtest.performanceMarkdownService.clear();
|
|
48731
|
+
}
|
|
48732
|
+
if (risk) {
|
|
48733
|
+
backtest.riskMarkdownService.clear();
|
|
48734
|
+
}
|
|
48735
|
+
if (strategy) {
|
|
48736
|
+
backtest.strategyMarkdownService.clear();
|
|
48737
|
+
}
|
|
48738
|
+
if (schedule) {
|
|
48739
|
+
backtest.scheduleMarkdownService.clear();
|
|
48740
|
+
}
|
|
48741
|
+
if (walker) {
|
|
48742
|
+
backtest.walkerMarkdownService.clear();
|
|
48743
|
+
}
|
|
48744
|
+
if (sync) {
|
|
48745
|
+
backtest.syncMarkdownService.clear();
|
|
48746
|
+
}
|
|
48747
|
+
if (highest_profit) {
|
|
48748
|
+
backtest.highestProfitMarkdownService.clear();
|
|
48749
|
+
}
|
|
48750
|
+
if (max_drawdown) {
|
|
48751
|
+
backtest.maxDrawdownMarkdownService.clear();
|
|
48752
|
+
}
|
|
48753
|
+
};
|
|
47779
48754
|
}
|
|
47780
48755
|
}
|
|
47781
48756
|
/**
|
|
@@ -47818,15 +48793,6 @@ class MarkdownAdapter extends MarkdownUtils {
|
|
|
47818
48793
|
LOGGER_SERVICE$1.debug(MARKDOWN_METHOD_NAME_USE_JSONL);
|
|
47819
48794
|
MarkdownWriter.useJsonl();
|
|
47820
48795
|
}
|
|
47821
|
-
/**
|
|
47822
|
-
* Clears the memoized storage cache.
|
|
47823
|
-
* Call this when process.cwd() changes between strategy iterations
|
|
47824
|
-
* so new storage instances are created with the updated base path.
|
|
47825
|
-
*/
|
|
47826
|
-
clear() {
|
|
47827
|
-
LOGGER_SERVICE$1.log(MARKDOWN_METHOD_NAME_CLEAR);
|
|
47828
|
-
MarkdownWriter.clear();
|
|
47829
|
-
}
|
|
47830
48796
|
/**
|
|
47831
48797
|
* Switches to a dummy markdown adapter that discards all writes.
|
|
47832
48798
|
* All future markdown writes will be no-ops.
|
|
@@ -48498,6 +49464,68 @@ class LogAdapter {
|
|
|
48498
49464
|
*/
|
|
48499
49465
|
const Log = new LogAdapter();
|
|
48500
49466
|
|
|
49467
|
+
const METHOD_NAME_CREATE_SNAPSHOT = "SessionUtils.createSnapshot";
|
|
49468
|
+
/** List of all global subjects whose listeners should be snapshotted for session isolation */
|
|
49469
|
+
const SUBJECT_ISOLATION_LIST = [
|
|
49470
|
+
activePingSubject,
|
|
49471
|
+
backtestScheduleOpenSubject,
|
|
49472
|
+
breakevenSubject,
|
|
49473
|
+
doneBacktestSubject,
|
|
49474
|
+
doneLiveSubject,
|
|
49475
|
+
errorEmitter,
|
|
49476
|
+
exitEmitter,
|
|
49477
|
+
highestProfitSubject,
|
|
49478
|
+
maxDrawdownSubject,
|
|
49479
|
+
partialLossSubject,
|
|
49480
|
+
partialProfitSubject,
|
|
49481
|
+
performanceEmitter,
|
|
49482
|
+
progressBacktestEmitter,
|
|
49483
|
+
riskSubject,
|
|
49484
|
+
schedulePingSubject,
|
|
49485
|
+
shutdownEmitter,
|
|
49486
|
+
signalBacktestEmitter,
|
|
49487
|
+
signalEmitter,
|
|
49488
|
+
signalLiveEmitter,
|
|
49489
|
+
strategyCommitSubject,
|
|
49490
|
+
syncSubject,
|
|
49491
|
+
validationSubject,
|
|
49492
|
+
signalNotifySubject,
|
|
49493
|
+
];
|
|
49494
|
+
/**
|
|
49495
|
+
* Creates a snapshot function for a given subject by clearing its internal
|
|
49496
|
+
* events map and returning a restore function that can put the original listeners back.
|
|
49497
|
+
* @param subject The subject to snapshot
|
|
49498
|
+
* @returns A function that restores the subject's original listeners when called
|
|
49499
|
+
*/
|
|
49500
|
+
const CREATE_SUBJECT_SNAPSHOT_FN = (subject) => {
|
|
49501
|
+
const emitter = subject["_emitter"];
|
|
49502
|
+
const events = emitter["_events"];
|
|
49503
|
+
emitter["_events"] = {};
|
|
49504
|
+
return () => {
|
|
49505
|
+
emitter["_events"] = events;
|
|
49506
|
+
};
|
|
49507
|
+
};
|
|
49508
|
+
/**
|
|
49509
|
+
* Manages isolation of global event-bus state between backtest sessions.
|
|
49510
|
+
* Allows temporarily detaching all subject subscriptions so that one session
|
|
49511
|
+
* does not interfere with another, then restoring them afterwards.
|
|
49512
|
+
*/
|
|
49513
|
+
class SessionUtils {
|
|
49514
|
+
constructor() {
|
|
49515
|
+
/**
|
|
49516
|
+
* Snapshots the current listener state of every global subject by replacing
|
|
49517
|
+
* their internal `_events` map with an empty object.
|
|
49518
|
+
* @returns A restore function that, when called, puts all original listeners back.
|
|
49519
|
+
*/
|
|
49520
|
+
this.createSnapshot = () => {
|
|
49521
|
+
backtest.loggerService.log(METHOD_NAME_CREATE_SNAPSHOT);
|
|
49522
|
+
const snapshotList = SUBJECT_ISOLATION_LIST.map(CREATE_SUBJECT_SNAPSHOT_FN);
|
|
49523
|
+
return functoolsKit.compose(...snapshotList);
|
|
49524
|
+
};
|
|
49525
|
+
}
|
|
49526
|
+
}
|
|
49527
|
+
const Session = new SessionUtils();
|
|
49528
|
+
|
|
48501
49529
|
const SCHEDULE_METHOD_NAME_GET_DATA = "ScheduleUtils.getData";
|
|
48502
49530
|
const SCHEDULE_METHOD_NAME_GET_REPORT = "ScheduleUtils.getReport";
|
|
48503
49531
|
const SCHEDULE_METHOD_NAME_DUMP = "ScheduleUtils.dump";
|
|
@@ -53307,7 +54335,44 @@ const CREATE_VALIDATION_ERROR_NOTIFICATION_FN = (error) => ({
|
|
|
53307
54335
|
message: functoolsKit.getErrorMessage(error),
|
|
53308
54336
|
backtest: false,
|
|
53309
54337
|
});
|
|
54338
|
+
/**
|
|
54339
|
+
* Creates a notification model for signal info events.
|
|
54340
|
+
* @param data - The signal info contract data
|
|
54341
|
+
* @returns NotificationModel for signal info event
|
|
54342
|
+
*/
|
|
54343
|
+
const CREATE_SIGNAL_INFO_NOTIFICATION_FN = (data) => ({
|
|
54344
|
+
type: "signal.info",
|
|
54345
|
+
id: CREATE_KEY_FN$2(),
|
|
54346
|
+
timestamp: data.timestamp,
|
|
54347
|
+
backtest: data.backtest,
|
|
54348
|
+
symbol: data.symbol,
|
|
54349
|
+
strategyName: data.strategyName,
|
|
54350
|
+
exchangeName: data.exchangeName,
|
|
54351
|
+
signalId: data.data.id,
|
|
54352
|
+
currentPrice: data.currentPrice,
|
|
54353
|
+
position: data.data.position,
|
|
54354
|
+
priceOpen: data.data.priceOpen,
|
|
54355
|
+
priceTakeProfit: data.data.priceTakeProfit,
|
|
54356
|
+
priceStopLoss: data.data.priceStopLoss,
|
|
54357
|
+
originalPriceTakeProfit: data.data.originalPriceTakeProfit,
|
|
54358
|
+
originalPriceStopLoss: data.data.originalPriceStopLoss,
|
|
54359
|
+
originalPriceOpen: data.data.originalPriceOpen,
|
|
54360
|
+
totalEntries: data.data.totalEntries,
|
|
54361
|
+
totalPartials: data.data.totalPartials,
|
|
54362
|
+
pnl: data.data.pnl,
|
|
54363
|
+
pnlPercentage: data.data.pnl.pnlPercentage,
|
|
54364
|
+
pnlPriceOpen: data.data.pnl.priceOpen,
|
|
54365
|
+
pnlPriceClose: data.data.pnl.priceClose,
|
|
54366
|
+
pnlCost: data.data.pnl.pnlCost,
|
|
54367
|
+
pnlEntries: data.data.pnl.pnlEntries,
|
|
54368
|
+
note: data.note,
|
|
54369
|
+
notificationId: data.notificationId,
|
|
54370
|
+
scheduledAt: data.data.scheduledAt,
|
|
54371
|
+
pendingAt: data.data.pendingAt,
|
|
54372
|
+
createdAt: data.timestamp,
|
|
54373
|
+
});
|
|
53310
54374
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_SIGNAL = "NotificationMemoryBacktestUtils.handleSignal";
|
|
54375
|
+
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_SIGNAL_NOTIFY = "NotificationMemoryBacktestUtils.handleSignalNotify";
|
|
53311
54376
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_PARTIAL_PROFIT = "NotificationMemoryBacktestUtils.handlePartialProfit";
|
|
53312
54377
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_PARTIAL_LOSS = "NotificationMemoryBacktestUtils.handlePartialLoss";
|
|
53313
54378
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_BREAKEVEN = "NotificationMemoryBacktestUtils.handleBreakeven";
|
|
@@ -53320,6 +54385,7 @@ const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_VALIDATION_ERROR = "Notifi
|
|
|
53320
54385
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_GET_DATA = "NotificationMemoryBacktestUtils.getData";
|
|
53321
54386
|
const NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_DISPOSE = "NotificationMemoryBacktestUtils.dispose";
|
|
53322
54387
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_SIGNAL = "NotificationMemoryLiveUtils.handleSignal";
|
|
54388
|
+
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_SIGNAL_NOTIFY = "NotificationMemoryLiveUtils.handleSignalNotify";
|
|
53323
54389
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_PARTIAL_PROFIT = "NotificationMemoryLiveUtils.handlePartialProfit";
|
|
53324
54390
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_PARTIAL_LOSS = "NotificationMemoryLiveUtils.handlePartialLoss";
|
|
53325
54391
|
const NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_BREAKEVEN = "NotificationMemoryLiveUtils.handleBreakeven";
|
|
@@ -53348,6 +54414,7 @@ const NOTIFICATION_LIVE_ADAPTER_METHOD_NAME_CLEAR = "NotificationLiveAdapter.cle
|
|
|
53348
54414
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_WAIT_FOR_INIT = "NotificationPersistBacktestUtils.waitForInit";
|
|
53349
54415
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_UPDATE_NOTIFICATIONS = "NotificationPersistBacktestUtils._updateNotifications";
|
|
53350
54416
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_SIGNAL = "NotificationPersistBacktestUtils.handleSignal";
|
|
54417
|
+
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_SIGNAL_NOTIFY = "NotificationPersistBacktestUtils.handleSignalNotify";
|
|
53351
54418
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_PARTIAL_PROFIT = "NotificationPersistBacktestUtils.handlePartialProfit";
|
|
53352
54419
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_PARTIAL_LOSS = "NotificationPersistBacktestUtils.handlePartialLoss";
|
|
53353
54420
|
const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_BREAKEVEN = "NotificationPersistBacktestUtils.handleBreakeven";
|
|
@@ -53362,6 +54429,7 @@ const NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_DISPOSE = "NotificationPersistBa
|
|
|
53362
54429
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_WAIT_FOR_INIT = "NotificationPersistLiveUtils.waitForInit";
|
|
53363
54430
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_UPDATE_NOTIFICATIONS = "NotificationPersistLiveUtils._updateNotifications";
|
|
53364
54431
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_SIGNAL = "NotificationPersistLiveUtils.handleSignal";
|
|
54432
|
+
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_SIGNAL_NOTIFY = "NotificationPersistLiveUtils.handleSignalNotify";
|
|
53365
54433
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_PARTIAL_PROFIT = "NotificationPersistLiveUtils.handlePartialProfit";
|
|
53366
54434
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_PARTIAL_LOSS = "NotificationPersistLiveUtils.handlePartialLoss";
|
|
53367
54435
|
const NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_BREAKEVEN = "NotificationPersistLiveUtils.handleBreakeven";
|
|
@@ -53404,6 +54472,12 @@ class NotificationMemoryBacktestUtils {
|
|
|
53404
54472
|
this._addNotification(notification);
|
|
53405
54473
|
}
|
|
53406
54474
|
};
|
|
54475
|
+
this.handleSignalNotify = async (data) => {
|
|
54476
|
+
backtest.loggerService.info(NOTIFICATION_MEMORY_BACKTEST_METHOD_NAME_HANDLE_SIGNAL_NOTIFY, {
|
|
54477
|
+
signalId: data.data.id,
|
|
54478
|
+
});
|
|
54479
|
+
this._addNotification(CREATE_SIGNAL_INFO_NOTIFICATION_FN(data));
|
|
54480
|
+
};
|
|
53407
54481
|
/**
|
|
53408
54482
|
* Handles partial profit availability event.
|
|
53409
54483
|
* @param data - The partial profit contract data
|
|
@@ -53548,6 +54622,8 @@ class NotificationDummyBacktestUtils {
|
|
|
53548
54622
|
*/
|
|
53549
54623
|
this.handleSignal = async () => {
|
|
53550
54624
|
};
|
|
54625
|
+
this.handleSignalNotify = async () => {
|
|
54626
|
+
};
|
|
53551
54627
|
/**
|
|
53552
54628
|
* No-op handler for partial profit event.
|
|
53553
54629
|
*/
|
|
@@ -53655,6 +54731,14 @@ class NotificationPersistBacktestUtils {
|
|
|
53655
54731
|
await this._updateNotifications();
|
|
53656
54732
|
}
|
|
53657
54733
|
};
|
|
54734
|
+
this.handleSignalNotify = async (data) => {
|
|
54735
|
+
backtest.loggerService.info(NOTIFICATION_PERSIST_BACKTEST_METHOD_NAME_HANDLE_SIGNAL_NOTIFY, {
|
|
54736
|
+
signalId: data.data.id,
|
|
54737
|
+
});
|
|
54738
|
+
await this.waitForInit();
|
|
54739
|
+
this._addNotification(CREATE_SIGNAL_INFO_NOTIFICATION_FN(data));
|
|
54740
|
+
await this._updateNotifications();
|
|
54741
|
+
};
|
|
53658
54742
|
/**
|
|
53659
54743
|
* Handles partial profit availability event.
|
|
53660
54744
|
* @param data - The partial profit contract data
|
|
@@ -53856,6 +54940,12 @@ class NotificationMemoryLiveUtils {
|
|
|
53856
54940
|
this._addNotification(notification);
|
|
53857
54941
|
}
|
|
53858
54942
|
};
|
|
54943
|
+
this.handleSignalNotify = async (data) => {
|
|
54944
|
+
backtest.loggerService.info(NOTIFICATION_MEMORY_LIVE_METHOD_NAME_HANDLE_SIGNAL_NOTIFY, {
|
|
54945
|
+
signalId: data.data.id,
|
|
54946
|
+
});
|
|
54947
|
+
this._addNotification(CREATE_SIGNAL_INFO_NOTIFICATION_FN(data));
|
|
54948
|
+
};
|
|
53859
54949
|
/**
|
|
53860
54950
|
* Handles partial profit availability event.
|
|
53861
54951
|
* @param data - The partial profit contract data
|
|
@@ -54000,6 +55090,8 @@ class NotificationDummyLiveUtils {
|
|
|
54000
55090
|
*/
|
|
54001
55091
|
this.handleSignal = async () => {
|
|
54002
55092
|
};
|
|
55093
|
+
this.handleSignalNotify = async () => {
|
|
55094
|
+
};
|
|
54003
55095
|
/**
|
|
54004
55096
|
* No-op handler for partial profit event.
|
|
54005
55097
|
*/
|
|
@@ -54108,6 +55200,14 @@ class NotificationPersistLiveUtils {
|
|
|
54108
55200
|
await this._updateNotifications();
|
|
54109
55201
|
}
|
|
54110
55202
|
};
|
|
55203
|
+
this.handleSignalNotify = async (data) => {
|
|
55204
|
+
backtest.loggerService.info(NOTIFICATION_PERSIST_LIVE_METHOD_NAME_HANDLE_SIGNAL_NOTIFY, {
|
|
55205
|
+
signalId: data.data.id,
|
|
55206
|
+
});
|
|
55207
|
+
await this.waitForInit();
|
|
55208
|
+
this._addNotification(CREATE_SIGNAL_INFO_NOTIFICATION_FN(data));
|
|
55209
|
+
await this._updateNotifications();
|
|
55210
|
+
};
|
|
54111
55211
|
/**
|
|
54112
55212
|
* Handles partial profit availability event.
|
|
54113
55213
|
* @param data - The partial profit contract data
|
|
@@ -54301,6 +55401,9 @@ class NotificationBacktestAdapter {
|
|
|
54301
55401
|
this.handleSignal = async (data) => {
|
|
54302
55402
|
return await this._notificationBacktestUtils.handleSignal(data);
|
|
54303
55403
|
};
|
|
55404
|
+
this.handleSignalNotify = async (data) => {
|
|
55405
|
+
return await this._notificationBacktestUtils.handleSignalNotify(data);
|
|
55406
|
+
};
|
|
54304
55407
|
/**
|
|
54305
55408
|
* Handles partial profit availability event.
|
|
54306
55409
|
* Proxies call to the underlying notification adapter.
|
|
@@ -54456,6 +55559,9 @@ class NotificationLiveAdapter {
|
|
|
54456
55559
|
this.handleSignal = async (data) => {
|
|
54457
55560
|
return await this._notificationLiveUtils.handleSignal(data);
|
|
54458
55561
|
};
|
|
55562
|
+
this.handleSignalNotify = async (data) => {
|
|
55563
|
+
return await this._notificationLiveUtils.handleSignalNotify(data);
|
|
55564
|
+
};
|
|
54459
55565
|
/**
|
|
54460
55566
|
* Handles partial profit availability event.
|
|
54461
55567
|
* Proxies call to the underlying notification adapter.
|
|
@@ -54634,7 +55740,10 @@ class NotificationAdapter {
|
|
|
54634
55740
|
const unBacktestError = errorEmitter.subscribe((error) => NotificationBacktest.handleError(error));
|
|
54635
55741
|
const unBacktestExit = exitEmitter.subscribe((error) => NotificationBacktest.handleCriticalError(error));
|
|
54636
55742
|
const unBacktestValidation = validationSubject.subscribe((error) => NotificationBacktest.handleValidationError(error));
|
|
54637
|
-
|
|
55743
|
+
const unBacktestSignalNotify = signalNotifySubject
|
|
55744
|
+
.filter(({ backtest }) => backtest)
|
|
55745
|
+
.connect((data) => NotificationBacktest.handleSignalNotify(data));
|
|
55746
|
+
unBacktest = functoolsKit.compose(() => unBacktestSignal(), () => unBacktestPartialProfit(), () => unBacktestPartialLoss(), () => unBacktestBreakeven(), () => unBacktestStrategyCommit(), () => unBacktestSync(), () => unBacktestRisk(), () => unBacktestError(), () => unBacktestExit(), () => unBacktestValidation(), () => unBacktestSignalNotify());
|
|
54638
55747
|
}
|
|
54639
55748
|
{
|
|
54640
55749
|
const unLiveSignal = signalLiveEmitter.subscribe((data) => NotificationLive.handleSignal(data));
|
|
@@ -54659,7 +55768,10 @@ class NotificationAdapter {
|
|
|
54659
55768
|
const unLiveError = errorEmitter.subscribe((error) => NotificationLive.handleError(error));
|
|
54660
55769
|
const unLiveExit = exitEmitter.subscribe((error) => NotificationLive.handleCriticalError(error));
|
|
54661
55770
|
const unLiveValidation = validationSubject.subscribe((error) => NotificationLive.handleValidationError(error));
|
|
54662
|
-
|
|
55771
|
+
const unLiveSignalNotify = signalNotifySubject
|
|
55772
|
+
.filter(({ backtest }) => !backtest)
|
|
55773
|
+
.connect((data) => NotificationLive.handleSignalNotify(data));
|
|
55774
|
+
unLive = functoolsKit.compose(() => unLiveSignal(), () => unLivePartialProfit(), () => unLivePartialLoss(), () => unLiveBreakeven(), () => unLiveStrategyCommit(), () => unLiveSync(), () => unLiveRisk(), () => unLiveError(), () => unLiveExit(), () => unLiveValidation(), () => unLiveSignalNotify());
|
|
54663
55775
|
}
|
|
54664
55776
|
return () => {
|
|
54665
55777
|
unLive();
|
|
@@ -54735,12 +55847,15 @@ const CACHE_METHOD_NAME_RUN = "CacheFnInstance.run";
|
|
|
54735
55847
|
const CACHE_METHOD_NAME_FN = "CacheUtils.fn";
|
|
54736
55848
|
const CACHE_METHOD_NAME_FN_CLEAR = "CacheUtils.fn.clear";
|
|
54737
55849
|
const CACHE_METHOD_NAME_FN_GC = "CacheUtils.fn.gc";
|
|
55850
|
+
const CACHE_METHOD_NAME_FN_HAS_VALUE = "CacheUtils.fn.hasValue";
|
|
54738
55851
|
const CACHE_METHOD_NAME_FILE = "CacheUtils.file";
|
|
54739
55852
|
const CACHE_METHOD_NAME_FILE_CLEAR = "CacheUtils.file.clear";
|
|
55853
|
+
const CACHE_METHOD_NAME_FILE_HAS_VALUE = "CacheUtils.file.hasValue";
|
|
54740
55854
|
const CACHE_METHOD_NAME_DISPOSE = "CacheUtils.dispose";
|
|
54741
55855
|
const CACHE_METHOD_NAME_CLEAR = "CacheUtils.clear";
|
|
54742
55856
|
const CACHE_METHOD_NAME_RESET_COUNTER = "CacheUtils.resetCounter";
|
|
54743
55857
|
const CACHE_FILE_INSTANCE_METHOD_NAME_RUN = "CacheFileInstance.run";
|
|
55858
|
+
const CACHE_FILE_INSTANCE_METHOD_NAME_HAS_VALUE = "CacheFileInstance.hasValue";
|
|
54744
55859
|
const MS_PER_MINUTE$1 = 60000;
|
|
54745
55860
|
const INTERVAL_MINUTES$1 = {
|
|
54746
55861
|
"1m": 1,
|
|
@@ -54894,11 +56009,17 @@ class CacheFnInstance {
|
|
|
54894
56009
|
return cached;
|
|
54895
56010
|
}
|
|
54896
56011
|
}
|
|
56012
|
+
const value = this.fn(...args);
|
|
54897
56013
|
const newCache = {
|
|
54898
56014
|
when: currentWhen,
|
|
54899
|
-
value
|
|
56015
|
+
value,
|
|
54900
56016
|
};
|
|
54901
56017
|
this._cacheMap.set(key, newCache);
|
|
56018
|
+
if (value && value instanceof Promise) {
|
|
56019
|
+
value.catch(() => {
|
|
56020
|
+
this._cacheMap.delete(key);
|
|
56021
|
+
});
|
|
56022
|
+
}
|
|
54902
56023
|
return newCache;
|
|
54903
56024
|
};
|
|
54904
56025
|
/**
|
|
@@ -54930,6 +56051,36 @@ class CacheFnInstance {
|
|
|
54930
56051
|
}
|
|
54931
56052
|
}
|
|
54932
56053
|
};
|
|
56054
|
+
/**
|
|
56055
|
+
* Check whether a valid (non-expired) cache entry exists for the current context and arguments.
|
|
56056
|
+
*
|
|
56057
|
+
* Returns `true` if a cached value exists and its interval is still current.
|
|
56058
|
+
* Returns `false` if there is no entry or the cached entry has expired.
|
|
56059
|
+
*
|
|
56060
|
+
* Requires active execution context and method context.
|
|
56061
|
+
*
|
|
56062
|
+
* @param args - Arguments to look up in the cache
|
|
56063
|
+
* @returns `true` if a fresh cached value exists, `false` otherwise
|
|
56064
|
+
*/
|
|
56065
|
+
this.hasValue = (...args) => {
|
|
56066
|
+
if (!MethodContextService.hasContext()) {
|
|
56067
|
+
throw new Error("CacheFnInstance hasValue requires method context");
|
|
56068
|
+
}
|
|
56069
|
+
if (!ExecutionContextService.hasContext()) {
|
|
56070
|
+
throw new Error("CacheFnInstance hasValue requires execution context");
|
|
56071
|
+
}
|
|
56072
|
+
const contextKey = CREATE_KEY_FN$1(backtest.methodContextService.context.strategyName, backtest.methodContextService.context.exchangeName, backtest.methodContextService.context.frameName, backtest.executionContextService.context.backtest);
|
|
56073
|
+
const argKey = String(this.key(args));
|
|
56074
|
+
const key = `${contextKey}:${argKey}`;
|
|
56075
|
+
const cached = this._cacheMap.get(key);
|
|
56076
|
+
if (!cached) {
|
|
56077
|
+
return false;
|
|
56078
|
+
}
|
|
56079
|
+
const currentWhen = backtest.executionContextService.context.when;
|
|
56080
|
+
const currentAligned = align$1(currentWhen.getTime(), this.interval);
|
|
56081
|
+
const cachedAligned = align$1(cached.when.getTime(), this.interval);
|
|
56082
|
+
return currentAligned === cachedAligned;
|
|
56083
|
+
};
|
|
54933
56084
|
/**
|
|
54934
56085
|
* Garbage collect expired cache entries.
|
|
54935
56086
|
*
|
|
@@ -55054,6 +56205,33 @@ class CacheFileInstance {
|
|
|
55054
56205
|
await PersistMeasureAdapter.writeMeasureData({ id: entityKey, data: result, removed: false }, bucket, entityKey);
|
|
55055
56206
|
return result;
|
|
55056
56207
|
};
|
|
56208
|
+
/**
|
|
56209
|
+
* Check whether a cached value exists on disk for the given arguments and current interval.
|
|
56210
|
+
*
|
|
56211
|
+
* Returns `true` if a persisted record exists for the current aligned timestamp.
|
|
56212
|
+
* Returns `false` if no record is found.
|
|
56213
|
+
*
|
|
56214
|
+
* Requires active execution context and method context.
|
|
56215
|
+
*
|
|
56216
|
+
* @param args - Arguments forwarded to the key generator
|
|
56217
|
+
* @returns `true` if a cached record exists, `false` otherwise
|
|
56218
|
+
*/
|
|
56219
|
+
this.hasValue = async (...args) => {
|
|
56220
|
+
backtest.loggerService.debug(CACHE_FILE_INSTANCE_METHOD_NAME_HAS_VALUE, { args });
|
|
56221
|
+
if (!MethodContextService.hasContext()) {
|
|
56222
|
+
throw new Error("CacheFileInstance hasValue requires method context");
|
|
56223
|
+
}
|
|
56224
|
+
if (!ExecutionContextService.hasContext()) {
|
|
56225
|
+
throw new Error("CacheFileInstance hasValue requires execution context");
|
|
56226
|
+
}
|
|
56227
|
+
const [symbol, ...rest] = args;
|
|
56228
|
+
const { when } = backtest.executionContextService.context;
|
|
56229
|
+
const alignedTs = align$1(when.getTime(), this.interval);
|
|
56230
|
+
const bucket = `${this.name}_${this.interval}_${this.index}`;
|
|
56231
|
+
const entityKey = this.key([symbol, alignedTs, ...rest]);
|
|
56232
|
+
const cached = await PersistMeasureAdapter.readMeasureData(bucket, entityKey);
|
|
56233
|
+
return cached !== null;
|
|
56234
|
+
};
|
|
55057
56235
|
/**
|
|
55058
56236
|
* Soft-delete all persisted records for this instance's bucket.
|
|
55059
56237
|
* After this call the next `run()` will recompute and re-cache the value.
|
|
@@ -55140,23 +56318,30 @@ class CacheUtils {
|
|
|
55140
56318
|
wrappedFn.clear = () => {
|
|
55141
56319
|
backtest.loggerService.info(CACHE_METHOD_NAME_FN_CLEAR);
|
|
55142
56320
|
if (!MethodContextService.hasContext()) {
|
|
55143
|
-
|
|
55144
|
-
return;
|
|
56321
|
+
throw new Error(`${CACHE_METHOD_NAME_FN_CLEAR} requires method context`);
|
|
55145
56322
|
}
|
|
55146
56323
|
if (!ExecutionContextService.hasContext()) {
|
|
55147
|
-
|
|
55148
|
-
return;
|
|
56324
|
+
throw new Error(`${CACHE_METHOD_NAME_FN_CLEAR} requires execution context`);
|
|
55149
56325
|
}
|
|
55150
56326
|
this._getFnInstance.get(run)?.clear();
|
|
55151
56327
|
};
|
|
55152
56328
|
wrappedFn.gc = () => {
|
|
55153
56329
|
backtest.loggerService.info(CACHE_METHOD_NAME_FN_GC);
|
|
55154
56330
|
if (!ExecutionContextService.hasContext()) {
|
|
55155
|
-
|
|
55156
|
-
return;
|
|
56331
|
+
throw new Error(`${CACHE_METHOD_NAME_FN_GC} requires execution context`);
|
|
55157
56332
|
}
|
|
55158
56333
|
return this._getFnInstance.get(run)?.gc();
|
|
55159
56334
|
};
|
|
56335
|
+
wrappedFn.hasValue = (...args) => {
|
|
56336
|
+
backtest.loggerService.info(CACHE_METHOD_NAME_FN_HAS_VALUE);
|
|
56337
|
+
if (!MethodContextService.hasContext()) {
|
|
56338
|
+
throw new Error(`${CACHE_METHOD_NAME_FN_HAS_VALUE} requires method context`);
|
|
56339
|
+
}
|
|
56340
|
+
if (!ExecutionContextService.hasContext()) {
|
|
56341
|
+
throw new Error(`${CACHE_METHOD_NAME_FN_HAS_VALUE} requires execution context`);
|
|
56342
|
+
}
|
|
56343
|
+
return this._getFnInstance.get(run)?.hasValue(...args) ?? false;
|
|
56344
|
+
};
|
|
55160
56345
|
return wrappedFn;
|
|
55161
56346
|
};
|
|
55162
56347
|
/**
|
|
@@ -55208,8 +56393,25 @@ class CacheUtils {
|
|
|
55208
56393
|
};
|
|
55209
56394
|
wrappedFn.clear = async () => {
|
|
55210
56395
|
backtest.loggerService.info(CACHE_METHOD_NAME_FILE_CLEAR);
|
|
56396
|
+
if (!MethodContextService.hasContext()) {
|
|
56397
|
+
throw new Error(`${CACHE_METHOD_NAME_FILE_CLEAR} requires method context`);
|
|
56398
|
+
}
|
|
56399
|
+
if (!ExecutionContextService.hasContext()) {
|
|
56400
|
+
throw new Error(`${CACHE_METHOD_NAME_FILE_CLEAR} requires execution context`);
|
|
56401
|
+
}
|
|
55211
56402
|
await this._getFileInstance.get(run)?.clear();
|
|
55212
56403
|
};
|
|
56404
|
+
wrappedFn.hasValue = async (...args) => {
|
|
56405
|
+
backtest.loggerService.info(CACHE_METHOD_NAME_FILE_HAS_VALUE);
|
|
56406
|
+
if (!MethodContextService.hasContext()) {
|
|
56407
|
+
throw new Error(`${CACHE_METHOD_NAME_FILE_HAS_VALUE} requires method context`);
|
|
56408
|
+
}
|
|
56409
|
+
if (!ExecutionContextService.hasContext()) {
|
|
56410
|
+
throw new Error(`${CACHE_METHOD_NAME_FILE_HAS_VALUE} requires execution context`);
|
|
56411
|
+
}
|
|
56412
|
+
const instance = this._getFileInstance(run, context.interval, context.name, context.key);
|
|
56413
|
+
return await instance.hasValue(...args);
|
|
56414
|
+
};
|
|
55213
56415
|
return wrappedFn;
|
|
55214
56416
|
};
|
|
55215
56417
|
/**
|
|
@@ -55278,11 +56480,14 @@ const Cache = new CacheUtils();
|
|
|
55278
56480
|
|
|
55279
56481
|
const INTERVAL_METHOD_NAME_RUN = "IntervalFnInstance.run";
|
|
55280
56482
|
const INTERVAL_FILE_INSTANCE_METHOD_NAME_RUN = "IntervalFileInstance.run";
|
|
56483
|
+
const INTERVAL_FILE_INSTANCE_METHOD_NAME_HAS_VALUE = "IntervalFileInstance.hasValue";
|
|
55281
56484
|
const INTERVAL_METHOD_NAME_FN = "IntervalUtils.fn";
|
|
55282
56485
|
const INTERVAL_METHOD_NAME_FN_CLEAR = "IntervalUtils.fn.clear";
|
|
55283
56486
|
const INTERVAL_METHOD_NAME_FN_GC = "IntervalUtils.fn.gc";
|
|
56487
|
+
const INTERVAL_METHOD_NAME_FN_HAS_VALUE = "IntervalUtils.fn.hasValue";
|
|
55284
56488
|
const INTERVAL_METHOD_NAME_FILE = "IntervalUtils.file";
|
|
55285
56489
|
const INTERVAL_METHOD_NAME_FILE_CLEAR = "IntervalUtils.file.clear";
|
|
56490
|
+
const INTERVAL_METHOD_NAME_FILE_HAS_VALUE = "IntervalUtils.file.hasValue";
|
|
55286
56491
|
const INTERVAL_METHOD_NAME_DISPOSE = "IntervalUtils.dispose";
|
|
55287
56492
|
const INTERVAL_METHOD_NAME_CLEAR = "IntervalUtils.clear";
|
|
55288
56493
|
const INTERVAL_METHOD_NAME_RESET_COUNTER = "IntervalUtils.resetCounter";
|
|
@@ -55395,7 +56600,7 @@ class IntervalFnInstance {
|
|
|
55395
56600
|
* within the same interval or when `fn` itself returned `null`
|
|
55396
56601
|
* @throws Error if method context, execution context, or interval is missing
|
|
55397
56602
|
*/
|
|
55398
|
-
this.run =
|
|
56603
|
+
this.run = (...args) => {
|
|
55399
56604
|
backtest.loggerService.debug(INTERVAL_METHOD_NAME_RUN, { args });
|
|
55400
56605
|
const step = INTERVAL_MINUTES[this.interval];
|
|
55401
56606
|
{
|
|
@@ -55417,10 +56622,15 @@ class IntervalFnInstance {
|
|
|
55417
56622
|
if (this._stateMap.get(stateKey) === currentAligned) {
|
|
55418
56623
|
return null;
|
|
55419
56624
|
}
|
|
55420
|
-
const result =
|
|
56625
|
+
const result = this.fn.apply(null, args);
|
|
55421
56626
|
if (result !== null) {
|
|
55422
56627
|
this._stateMap.set(stateKey, currentAligned);
|
|
55423
56628
|
}
|
|
56629
|
+
if (result && result instanceof Promise) {
|
|
56630
|
+
result.catch(() => {
|
|
56631
|
+
this._stateMap.delete(stateKey);
|
|
56632
|
+
});
|
|
56633
|
+
}
|
|
55424
56634
|
return result;
|
|
55425
56635
|
};
|
|
55426
56636
|
/**
|
|
@@ -55440,6 +56650,31 @@ class IntervalFnInstance {
|
|
|
55440
56650
|
}
|
|
55441
56651
|
}
|
|
55442
56652
|
};
|
|
56653
|
+
/**
|
|
56654
|
+
* Check whether the function has already fired for the current interval and context.
|
|
56655
|
+
*
|
|
56656
|
+
* Returns `true` if the function fired (non-null result) within the current interval boundary.
|
|
56657
|
+
* Returns `false` if there is no recorded firing for this interval.
|
|
56658
|
+
*
|
|
56659
|
+
* Requires active method context and execution context.
|
|
56660
|
+
*
|
|
56661
|
+
* @param args - Arguments to look up in the state map
|
|
56662
|
+
* @returns `true` if the function has already fired this interval, `false` otherwise
|
|
56663
|
+
*/
|
|
56664
|
+
this.hasValue = (...args) => {
|
|
56665
|
+
if (!MethodContextService.hasContext()) {
|
|
56666
|
+
throw new Error("IntervalFnInstance hasValue requires method context");
|
|
56667
|
+
}
|
|
56668
|
+
if (!ExecutionContextService.hasContext()) {
|
|
56669
|
+
throw new Error("IntervalFnInstance hasValue requires execution context");
|
|
56670
|
+
}
|
|
56671
|
+
const contextKey = CREATE_KEY_FN(backtest.methodContextService.context.strategyName, backtest.methodContextService.context.exchangeName, backtest.methodContextService.context.frameName, backtest.executionContextService.context.backtest);
|
|
56672
|
+
const currentWhen = backtest.executionContextService.context.when;
|
|
56673
|
+
const currentAligned = align(currentWhen.getTime(), this.interval);
|
|
56674
|
+
const argKey = this.key(args);
|
|
56675
|
+
const stateKey = `${contextKey}:${argKey}`;
|
|
56676
|
+
return this._stateMap.get(stateKey) === currentAligned;
|
|
56677
|
+
};
|
|
55443
56678
|
/**
|
|
55444
56679
|
* Garbage collect expired state entries.
|
|
55445
56680
|
*
|
|
@@ -55561,6 +56796,33 @@ class IntervalFileInstance {
|
|
|
55561
56796
|
}
|
|
55562
56797
|
return result;
|
|
55563
56798
|
};
|
|
56799
|
+
/**
|
|
56800
|
+
* Check whether the function has already fired for the current interval on disk.
|
|
56801
|
+
*
|
|
56802
|
+
* Returns `true` if a persisted record exists for the current aligned timestamp.
|
|
56803
|
+
* Returns `false` if no record is found.
|
|
56804
|
+
*
|
|
56805
|
+
* Requires active execution context and method context.
|
|
56806
|
+
*
|
|
56807
|
+
* @param args - Arguments forwarded to the key generator
|
|
56808
|
+
* @returns `true` if a fired record exists, `false` otherwise
|
|
56809
|
+
*/
|
|
56810
|
+
this.hasValue = async (...args) => {
|
|
56811
|
+
backtest.loggerService.debug(INTERVAL_FILE_INSTANCE_METHOD_NAME_HAS_VALUE, { args });
|
|
56812
|
+
if (!MethodContextService.hasContext()) {
|
|
56813
|
+
throw new Error("IntervalFileInstance hasValue requires method context");
|
|
56814
|
+
}
|
|
56815
|
+
if (!ExecutionContextService.hasContext()) {
|
|
56816
|
+
throw new Error("IntervalFileInstance hasValue requires execution context");
|
|
56817
|
+
}
|
|
56818
|
+
const [symbol, ...rest] = args;
|
|
56819
|
+
const { when } = backtest.executionContextService.context;
|
|
56820
|
+
const alignedMs = align(when.getTime(), this.interval);
|
|
56821
|
+
const bucket = `${this.name}_${this.interval}_${this.index}`;
|
|
56822
|
+
const entityKey = this.key([symbol, alignedMs, ...rest]);
|
|
56823
|
+
const cached = await PersistIntervalAdapter.readIntervalData(bucket, entityKey);
|
|
56824
|
+
return cached !== null;
|
|
56825
|
+
};
|
|
55564
56826
|
/**
|
|
55565
56827
|
* Soft-delete all persisted records for this instance's bucket.
|
|
55566
56828
|
* After this call the function will fire again on the next `run()`.
|
|
@@ -55641,23 +56903,30 @@ class IntervalUtils {
|
|
|
55641
56903
|
wrappedFn.clear = () => {
|
|
55642
56904
|
backtest.loggerService.info(INTERVAL_METHOD_NAME_FN_CLEAR);
|
|
55643
56905
|
if (!MethodContextService.hasContext()) {
|
|
55644
|
-
|
|
55645
|
-
return;
|
|
56906
|
+
throw new Error(`${INTERVAL_METHOD_NAME_FN_CLEAR} requires method context`);
|
|
55646
56907
|
}
|
|
55647
56908
|
if (!ExecutionContextService.hasContext()) {
|
|
55648
|
-
|
|
55649
|
-
return;
|
|
56909
|
+
throw new Error(`${INTERVAL_METHOD_NAME_FN_CLEAR} requires execution context`);
|
|
55650
56910
|
}
|
|
55651
56911
|
this._getInstance.get(run)?.clear();
|
|
55652
56912
|
};
|
|
55653
56913
|
wrappedFn.gc = () => {
|
|
55654
56914
|
backtest.loggerService.info(INTERVAL_METHOD_NAME_FN_GC);
|
|
55655
56915
|
if (!ExecutionContextService.hasContext()) {
|
|
55656
|
-
|
|
55657
|
-
return;
|
|
56916
|
+
throw new Error(`${INTERVAL_METHOD_NAME_FN_GC} requires execution context`);
|
|
55658
56917
|
}
|
|
55659
56918
|
return this._getInstance.get(run)?.gc();
|
|
55660
56919
|
};
|
|
56920
|
+
wrappedFn.hasValue = (...args) => {
|
|
56921
|
+
backtest.loggerService.info(INTERVAL_METHOD_NAME_FN_HAS_VALUE);
|
|
56922
|
+
if (!MethodContextService.hasContext()) {
|
|
56923
|
+
throw new Error(`${INTERVAL_METHOD_NAME_FN_HAS_VALUE} requires method context`);
|
|
56924
|
+
}
|
|
56925
|
+
if (!ExecutionContextService.hasContext()) {
|
|
56926
|
+
throw new Error(`${INTERVAL_METHOD_NAME_FN_HAS_VALUE} requires execution context`);
|
|
56927
|
+
}
|
|
56928
|
+
return this._getInstance.get(run)?.hasValue(...args) ?? false;
|
|
56929
|
+
};
|
|
55661
56930
|
return wrappedFn;
|
|
55662
56931
|
};
|
|
55663
56932
|
/**
|
|
@@ -55700,8 +56969,25 @@ class IntervalUtils {
|
|
|
55700
56969
|
};
|
|
55701
56970
|
wrappedFn.clear = async () => {
|
|
55702
56971
|
backtest.loggerService.info(INTERVAL_METHOD_NAME_FILE_CLEAR);
|
|
56972
|
+
if (!MethodContextService.hasContext()) {
|
|
56973
|
+
throw new Error(`${INTERVAL_METHOD_NAME_FILE_CLEAR} requires method context`);
|
|
56974
|
+
}
|
|
56975
|
+
if (!ExecutionContextService.hasContext()) {
|
|
56976
|
+
throw new Error(`${INTERVAL_METHOD_NAME_FILE_CLEAR} requires execution context`);
|
|
56977
|
+
}
|
|
55703
56978
|
await this._getFileInstance.get(run)?.clear();
|
|
55704
56979
|
};
|
|
56980
|
+
wrappedFn.hasValue = async (...args) => {
|
|
56981
|
+
backtest.loggerService.info(INTERVAL_METHOD_NAME_FILE_HAS_VALUE);
|
|
56982
|
+
if (!MethodContextService.hasContext()) {
|
|
56983
|
+
throw new Error(`${INTERVAL_METHOD_NAME_FILE_HAS_VALUE} requires method context`);
|
|
56984
|
+
}
|
|
56985
|
+
if (!ExecutionContextService.hasContext()) {
|
|
56986
|
+
throw new Error(`${INTERVAL_METHOD_NAME_FILE_HAS_VALUE} requires execution context`);
|
|
56987
|
+
}
|
|
56988
|
+
const instance = this._getFileInstance(run, context.interval, context.name, context.key);
|
|
56989
|
+
return await instance.hasValue(...args);
|
|
56990
|
+
};
|
|
55705
56991
|
return wrappedFn;
|
|
55706
56992
|
};
|
|
55707
56993
|
/**
|
|
@@ -56911,18 +58197,23 @@ exports.PersistMeasureAdapter = PersistMeasureAdapter;
|
|
|
56911
58197
|
exports.PersistMemoryAdapter = PersistMemoryAdapter;
|
|
56912
58198
|
exports.PersistNotificationAdapter = PersistNotificationAdapter;
|
|
56913
58199
|
exports.PersistPartialAdapter = PersistPartialAdapter;
|
|
58200
|
+
exports.PersistRecentAdapter = PersistRecentAdapter;
|
|
56914
58201
|
exports.PersistRiskAdapter = PersistRiskAdapter;
|
|
56915
58202
|
exports.PersistScheduleAdapter = PersistScheduleAdapter;
|
|
56916
58203
|
exports.PersistSignalAdapter = PersistSignalAdapter;
|
|
56917
58204
|
exports.PersistStorageAdapter = PersistStorageAdapter;
|
|
56918
58205
|
exports.Position = Position;
|
|
56919
58206
|
exports.PositionSize = PositionSize;
|
|
58207
|
+
exports.Recent = Recent;
|
|
58208
|
+
exports.RecentBacktest = RecentBacktest;
|
|
58209
|
+
exports.RecentLive = RecentLive;
|
|
56920
58210
|
exports.Reflect = Reflect$1;
|
|
56921
58211
|
exports.Report = Report;
|
|
56922
58212
|
exports.ReportBase = ReportBase;
|
|
56923
58213
|
exports.ReportWriter = ReportWriter;
|
|
56924
58214
|
exports.Risk = Risk;
|
|
56925
58215
|
exports.Schedule = Schedule;
|
|
58216
|
+
exports.Session = Session;
|
|
56926
58217
|
exports.Storage = Storage;
|
|
56927
58218
|
exports.StorageBacktest = StorageBacktest;
|
|
56928
58219
|
exports.StorageLive = StorageLive;
|
|
@@ -56947,6 +58238,7 @@ exports.commitPartialLoss = commitPartialLoss;
|
|
|
56947
58238
|
exports.commitPartialLossCost = commitPartialLossCost;
|
|
56948
58239
|
exports.commitPartialProfit = commitPartialProfit;
|
|
56949
58240
|
exports.commitPartialProfitCost = commitPartialProfitCost;
|
|
58241
|
+
exports.commitSignalNotify = commitSignalNotify;
|
|
56950
58242
|
exports.commitTrailingStop = commitTrailingStop;
|
|
56951
58243
|
exports.commitTrailingStopCost = commitTrailingStopCost;
|
|
56952
58244
|
exports.commitTrailingTake = commitTrailingTake;
|
|
@@ -56976,6 +58268,7 @@ exports.getDefaultConfig = getDefaultConfig;
|
|
|
56976
58268
|
exports.getEffectivePriceOpen = getEffectivePriceOpen;
|
|
56977
58269
|
exports.getExchangeSchema = getExchangeSchema;
|
|
56978
58270
|
exports.getFrameSchema = getFrameSchema;
|
|
58271
|
+
exports.getLatestSignal = getLatestSignal;
|
|
56979
58272
|
exports.getMaxDrawdownDistancePnlCost = getMaxDrawdownDistancePnlCost;
|
|
56980
58273
|
exports.getMaxDrawdownDistancePnlPercentage = getMaxDrawdownDistancePnlPercentage;
|
|
56981
58274
|
exports.getMode = getMode;
|
|
@@ -57064,6 +58357,8 @@ exports.listenSignalBacktest = listenSignalBacktest;
|
|
|
57064
58357
|
exports.listenSignalBacktestOnce = listenSignalBacktestOnce;
|
|
57065
58358
|
exports.listenSignalLive = listenSignalLive;
|
|
57066
58359
|
exports.listenSignalLiveOnce = listenSignalLiveOnce;
|
|
58360
|
+
exports.listenSignalNotify = listenSignalNotify;
|
|
58361
|
+
exports.listenSignalNotifyOnce = listenSignalNotifyOnce;
|
|
57067
58362
|
exports.listenSignalOnce = listenSignalOnce;
|
|
57068
58363
|
exports.listenStrategyCommit = listenStrategyCommit;
|
|
57069
58364
|
exports.listenStrategyCommitOnce = listenStrategyCommitOnce;
|