backtest-kit 7.4.0 → 7.5.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/README.md +7 -7
- package/build/index.cjs +1702 -345
- package/build/index.mjs +1690 -346
- package/package.json +1 -1
- package/types.d.ts +810 -79
package/build/index.mjs
CHANGED
|
@@ -997,6 +997,13 @@ const PERSIST_MEMORY_UTILS_METHOD_NAME_LIST_DATA = "PersistMemoryUtils.listMemor
|
|
|
997
997
|
const PERSIST_MEMORY_UTILS_METHOD_NAME_HAS_DATA = "PersistMemoryUtils.hasMemoryData";
|
|
998
998
|
const PERSIST_MEMORY_UTILS_METHOD_NAME_CLEAR = "PersistMemoryUtils.clear";
|
|
999
999
|
const PERSIST_MEMORY_UTILS_METHOD_NAME_DISPOSE = "PersistMemoryUtils.dispose";
|
|
1000
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_USE_PERSIST_STATE_ADAPTER = "PersistStateUtils.usePersistStateAdapter";
|
|
1001
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_READ_DATA = "PersistStateUtils.readStateData";
|
|
1002
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_WRITE_DATA = "PersistStateUtils.writeStateData";
|
|
1003
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_CLEAR = "PersistStateUtils.clear";
|
|
1004
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_DISPOSE = "PersistStateUtils.dispose";
|
|
1005
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_WAIT_FOR_INIT = "PersistStateUtils.waitForInit";
|
|
1006
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_USE_DUMMY = "PersistStateUtils.useDummy";
|
|
1000
1007
|
const PERSIST_RECENT_UTILS_METHOD_NAME_USE_PERSIST_RECENT_ADAPTER = "PersistRecentUtils.usePersistRecentAdapter";
|
|
1001
1008
|
const PERSIST_RECENT_UTILS_METHOD_NAME_READ_DATA = "PersistRecentUtils.readRecentData";
|
|
1002
1009
|
const PERSIST_RECENT_UTILS_METHOD_NAME_WRITE_DATA = "PersistRecentUtils.writeRecentData";
|
|
@@ -2958,6 +2965,127 @@ class PersistRecentUtils {
|
|
|
2958
2965
|
* Used by RecentPersistBacktestUtils/RecentPersistLiveUtils for recent signal persistence.
|
|
2959
2966
|
*/
|
|
2960
2967
|
const PersistRecentAdapter = new PersistRecentUtils();
|
|
2968
|
+
/**
|
|
2969
|
+
* Utility class for managing state persistence.
|
|
2970
|
+
*
|
|
2971
|
+
* Features:
|
|
2972
|
+
* - Memoized storage instances per (signalId, bucketName) pair
|
|
2973
|
+
* - Custom adapter support
|
|
2974
|
+
* - Atomic read/write operations
|
|
2975
|
+
*
|
|
2976
|
+
* Storage layout: ./dump/state/<signalId>/<bucketName>.json
|
|
2977
|
+
*
|
|
2978
|
+
* Used by StatePersistInstance for crash-safe state persistence.
|
|
2979
|
+
*/
|
|
2980
|
+
class PersistStateUtils {
|
|
2981
|
+
constructor() {
|
|
2982
|
+
this.PersistStateFactory = PersistBase;
|
|
2983
|
+
this.getStateStorage = memoize(([signalId, bucketName]) => `${signalId}:${bucketName}`, (signalId, bucketName) => Reflect.construct(this.PersistStateFactory, [
|
|
2984
|
+
bucketName,
|
|
2985
|
+
`./dump/state/${signalId}/`,
|
|
2986
|
+
]));
|
|
2987
|
+
/**
|
|
2988
|
+
* Initializes the storage for a given (signalId, bucketName) pair.
|
|
2989
|
+
*
|
|
2990
|
+
* @param signalId - Signal identifier
|
|
2991
|
+
* @param bucketName - Bucket name
|
|
2992
|
+
* @param initial - Whether this is the first initialization
|
|
2993
|
+
*/
|
|
2994
|
+
this.waitForInit = async (signalId, bucketName, initial) => {
|
|
2995
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_WAIT_FOR_INIT, {
|
|
2996
|
+
signalId,
|
|
2997
|
+
bucketName,
|
|
2998
|
+
initial,
|
|
2999
|
+
});
|
|
3000
|
+
const key = `${signalId}:${bucketName}`;
|
|
3001
|
+
const isInitial = initial && !this.getStateStorage.has(key);
|
|
3002
|
+
const stateStorage = this.getStateStorage(signalId, bucketName);
|
|
3003
|
+
await stateStorage.waitForInit(isInitial);
|
|
3004
|
+
};
|
|
3005
|
+
/**
|
|
3006
|
+
* Reads a state entry from persistence storage.
|
|
3007
|
+
*
|
|
3008
|
+
* @param signalId - Signal identifier
|
|
3009
|
+
* @param bucketName - Bucket name
|
|
3010
|
+
* @returns Promise resolving to entry data or null if not found
|
|
3011
|
+
*/
|
|
3012
|
+
this.readStateData = async (signalId, bucketName) => {
|
|
3013
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_READ_DATA, {
|
|
3014
|
+
signalId,
|
|
3015
|
+
bucketName,
|
|
3016
|
+
});
|
|
3017
|
+
const key = `${signalId}:${bucketName}`;
|
|
3018
|
+
const isInitial = !this.getStateStorage.has(key);
|
|
3019
|
+
const stateStorage = this.getStateStorage(signalId, bucketName);
|
|
3020
|
+
await stateStorage.waitForInit(isInitial);
|
|
3021
|
+
if (await stateStorage.hasValue(bucketName)) {
|
|
3022
|
+
return await stateStorage.readValue(bucketName);
|
|
3023
|
+
}
|
|
3024
|
+
return null;
|
|
3025
|
+
};
|
|
3026
|
+
/**
|
|
3027
|
+
* Writes a state entry to disk with atomic file writes.
|
|
3028
|
+
*
|
|
3029
|
+
* @param data - Entry data to persist
|
|
3030
|
+
* @param signalId - Signal identifier
|
|
3031
|
+
* @param bucketName - Bucket name
|
|
3032
|
+
*/
|
|
3033
|
+
this.writeStateData = async (data, signalId, bucketName) => {
|
|
3034
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_WRITE_DATA, {
|
|
3035
|
+
signalId,
|
|
3036
|
+
bucketName,
|
|
3037
|
+
});
|
|
3038
|
+
const key = `${signalId}:${bucketName}`;
|
|
3039
|
+
const isInitial = !this.getStateStorage.has(key);
|
|
3040
|
+
const stateStorage = this.getStateStorage(signalId, bucketName);
|
|
3041
|
+
await stateStorage.waitForInit(isInitial);
|
|
3042
|
+
await stateStorage.writeValue(bucketName, data);
|
|
3043
|
+
};
|
|
3044
|
+
/**
|
|
3045
|
+
* Switches to a dummy persist adapter that discards all writes.
|
|
3046
|
+
* All future persistence writes will be no-ops.
|
|
3047
|
+
*/
|
|
3048
|
+
this.useDummy = () => {
|
|
3049
|
+
LOGGER_SERVICE$7.log(PERSIST_STATE_UTILS_METHOD_NAME_USE_DUMMY);
|
|
3050
|
+
this.usePersistStateAdapter(PersistDummy);
|
|
3051
|
+
};
|
|
3052
|
+
/**
|
|
3053
|
+
* Clears the memoized storage cache.
|
|
3054
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
3055
|
+
* so new storage instances are created with the updated base path.
|
|
3056
|
+
*/
|
|
3057
|
+
this.clear = () => {
|
|
3058
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_CLEAR);
|
|
3059
|
+
this.getStateStorage.clear();
|
|
3060
|
+
};
|
|
3061
|
+
/**
|
|
3062
|
+
* Disposes of the state adapter and releases any resources.
|
|
3063
|
+
* Call this when a signal is removed to clean up its associated storage.
|
|
3064
|
+
*
|
|
3065
|
+
* @param signalId - Signal identifier
|
|
3066
|
+
* @param bucketName - Bucket name
|
|
3067
|
+
*/
|
|
3068
|
+
this.dispose = (signalId, bucketName) => {
|
|
3069
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_DISPOSE);
|
|
3070
|
+
const key = `${signalId}:${bucketName}`;
|
|
3071
|
+
this.getStateStorage.clear(key);
|
|
3072
|
+
};
|
|
3073
|
+
}
|
|
3074
|
+
/**
|
|
3075
|
+
* Registers a custom persistence adapter.
|
|
3076
|
+
*
|
|
3077
|
+
* @param Ctor - Custom PersistBase constructor
|
|
3078
|
+
*/
|
|
3079
|
+
usePersistStateAdapter(Ctor) {
|
|
3080
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_USE_PERSIST_STATE_ADAPTER);
|
|
3081
|
+
this.PersistStateFactory = Ctor;
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
/**
|
|
3085
|
+
* Global singleton instance of PersistStateUtils.
|
|
3086
|
+
* Used by StatePersistInstance for crash-safe state persistence.
|
|
3087
|
+
*/
|
|
3088
|
+
const PersistStateAdapter = new PersistStateUtils();
|
|
2961
3089
|
|
|
2962
3090
|
var _a$2, _b$2;
|
|
2963
3091
|
const BUSY_DELAY = 100;
|
|
@@ -10423,7 +10551,7 @@ const GET_RISK_FN = (dto, backtest, exchangeName, frameName, self) => {
|
|
|
10423
10551
|
* @param backtest - Whether running in backtest mode
|
|
10424
10552
|
* @returns Unique string key for memoization
|
|
10425
10553
|
*/
|
|
10426
|
-
const CREATE_KEY_FN$
|
|
10554
|
+
const CREATE_KEY_FN$w = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
10427
10555
|
const parts = [symbol, strategyName, exchangeName];
|
|
10428
10556
|
if (frameName)
|
|
10429
10557
|
parts.push(frameName);
|
|
@@ -10723,7 +10851,7 @@ class StrategyConnectionService {
|
|
|
10723
10851
|
* @param backtest - Whether running in backtest mode
|
|
10724
10852
|
* @returns Configured ClientStrategy instance
|
|
10725
10853
|
*/
|
|
10726
|
-
this.getStrategy = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
10854
|
+
this.getStrategy = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$w(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
10727
10855
|
const { riskName = "", riskList = [], getSignal, interval = STRATEGY_DEFAULT_INTERVAL, callbacks, } = this.strategySchemaService.get(strategyName);
|
|
10728
10856
|
return new ClientStrategy({
|
|
10729
10857
|
symbol,
|
|
@@ -11685,7 +11813,7 @@ class StrategyConnectionService {
|
|
|
11685
11813
|
}
|
|
11686
11814
|
return;
|
|
11687
11815
|
}
|
|
11688
|
-
const key = CREATE_KEY_FN$
|
|
11816
|
+
const key = CREATE_KEY_FN$w(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
11689
11817
|
if (!this.getStrategy.has(key)) {
|
|
11690
11818
|
return;
|
|
11691
11819
|
}
|
|
@@ -12859,7 +12987,7 @@ class ClientRisk {
|
|
|
12859
12987
|
* @param backtest - Whether running in backtest mode
|
|
12860
12988
|
* @returns Unique string key for memoization
|
|
12861
12989
|
*/
|
|
12862
|
-
const CREATE_KEY_FN$
|
|
12990
|
+
const CREATE_KEY_FN$v = (riskName, exchangeName, frameName, backtest) => {
|
|
12863
12991
|
const parts = [riskName, exchangeName];
|
|
12864
12992
|
if (frameName)
|
|
12865
12993
|
parts.push(frameName);
|
|
@@ -12959,7 +13087,7 @@ class RiskConnectionService {
|
|
|
12959
13087
|
* @param backtest - True if backtest mode, false if live mode
|
|
12960
13088
|
* @returns Configured ClientRisk instance
|
|
12961
13089
|
*/
|
|
12962
|
-
this.getRisk = memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
13090
|
+
this.getRisk = memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$v(riskName, exchangeName, frameName, backtest), (riskName, exchangeName, frameName, backtest) => {
|
|
12963
13091
|
const schema = this.riskSchemaService.get(riskName);
|
|
12964
13092
|
return new ClientRisk({
|
|
12965
13093
|
...schema,
|
|
@@ -13028,7 +13156,7 @@ class RiskConnectionService {
|
|
|
13028
13156
|
payload,
|
|
13029
13157
|
});
|
|
13030
13158
|
if (payload) {
|
|
13031
|
-
const key = CREATE_KEY_FN$
|
|
13159
|
+
const key = CREATE_KEY_FN$v(payload.riskName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
13032
13160
|
this.getRisk.clear(key);
|
|
13033
13161
|
}
|
|
13034
13162
|
else {
|
|
@@ -14147,7 +14275,7 @@ class ClientAction {
|
|
|
14147
14275
|
* @param backtest - Whether running in backtest mode
|
|
14148
14276
|
* @returns Unique string key for memoization
|
|
14149
14277
|
*/
|
|
14150
|
-
const CREATE_KEY_FN$
|
|
14278
|
+
const CREATE_KEY_FN$u = (actionName, strategyName, exchangeName, frameName, backtest) => {
|
|
14151
14279
|
const parts = [actionName, strategyName, exchangeName];
|
|
14152
14280
|
if (frameName)
|
|
14153
14281
|
parts.push(frameName);
|
|
@@ -14199,7 +14327,7 @@ class ActionConnectionService {
|
|
|
14199
14327
|
* @param backtest - True if backtest mode, false if live mode
|
|
14200
14328
|
* @returns Configured ClientAction instance
|
|
14201
14329
|
*/
|
|
14202
|
-
this.getAction = memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
14330
|
+
this.getAction = memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$u(actionName, strategyName, exchangeName, frameName, backtest), (actionName, strategyName, exchangeName, frameName, backtest) => {
|
|
14203
14331
|
const schema = this.actionSchemaService.get(actionName);
|
|
14204
14332
|
return new ClientAction({
|
|
14205
14333
|
...schema,
|
|
@@ -14425,7 +14553,7 @@ class ActionConnectionService {
|
|
|
14425
14553
|
await Promise.all(actions.map(async (action) => await action.dispose()));
|
|
14426
14554
|
return;
|
|
14427
14555
|
}
|
|
14428
|
-
const key = CREATE_KEY_FN$
|
|
14556
|
+
const key = CREATE_KEY_FN$u(payload.actionName, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
14429
14557
|
if (!this.getAction.has(key)) {
|
|
14430
14558
|
return;
|
|
14431
14559
|
}
|
|
@@ -14443,7 +14571,7 @@ const METHOD_NAME_VALIDATE$3 = "exchangeCoreService validate";
|
|
|
14443
14571
|
* @param exchangeName - Exchange name
|
|
14444
14572
|
* @returns Unique string key for memoization
|
|
14445
14573
|
*/
|
|
14446
|
-
const CREATE_KEY_FN$
|
|
14574
|
+
const CREATE_KEY_FN$t = (exchangeName) => {
|
|
14447
14575
|
return exchangeName;
|
|
14448
14576
|
};
|
|
14449
14577
|
/**
|
|
@@ -14467,7 +14595,7 @@ class ExchangeCoreService {
|
|
|
14467
14595
|
* @param exchangeName - Name of the exchange to validate
|
|
14468
14596
|
* @returns Promise that resolves when validation is complete
|
|
14469
14597
|
*/
|
|
14470
|
-
this.validate = memoize(([exchangeName]) => CREATE_KEY_FN$
|
|
14598
|
+
this.validate = memoize(([exchangeName]) => CREATE_KEY_FN$t(exchangeName), async (exchangeName) => {
|
|
14471
14599
|
this.loggerService.log(METHOD_NAME_VALIDATE$3, {
|
|
14472
14600
|
exchangeName,
|
|
14473
14601
|
});
|
|
@@ -14719,7 +14847,7 @@ const METHOD_NAME_VALIDATE$2 = "strategyCoreService validate";
|
|
|
14719
14847
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
14720
14848
|
* @returns Unique string key for memoization
|
|
14721
14849
|
*/
|
|
14722
|
-
const CREATE_KEY_FN$
|
|
14850
|
+
const CREATE_KEY_FN$s = (context) => {
|
|
14723
14851
|
const parts = [context.strategyName, context.exchangeName];
|
|
14724
14852
|
if (context.frameName)
|
|
14725
14853
|
parts.push(context.frameName);
|
|
@@ -14751,7 +14879,7 @@ class StrategyCoreService {
|
|
|
14751
14879
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
14752
14880
|
* @returns Promise that resolves when validation is complete
|
|
14753
14881
|
*/
|
|
14754
|
-
this.validate = memoize(([context]) => CREATE_KEY_FN$
|
|
14882
|
+
this.validate = memoize(([context]) => CREATE_KEY_FN$s(context), async (context) => {
|
|
14755
14883
|
this.loggerService.log(METHOD_NAME_VALIDATE$2, {
|
|
14756
14884
|
context,
|
|
14757
14885
|
});
|
|
@@ -16121,7 +16249,7 @@ class SizingGlobalService {
|
|
|
16121
16249
|
* @param context - Context with riskName, exchangeName, frameName
|
|
16122
16250
|
* @returns Unique string key for memoization
|
|
16123
16251
|
*/
|
|
16124
|
-
const CREATE_KEY_FN$
|
|
16252
|
+
const CREATE_KEY_FN$r = (context) => {
|
|
16125
16253
|
const parts = [context.riskName, context.exchangeName];
|
|
16126
16254
|
if (context.frameName)
|
|
16127
16255
|
parts.push(context.frameName);
|
|
@@ -16147,7 +16275,7 @@ class RiskGlobalService {
|
|
|
16147
16275
|
* @param payload - Payload with riskName, exchangeName and frameName
|
|
16148
16276
|
* @returns Promise that resolves when validation is complete
|
|
16149
16277
|
*/
|
|
16150
|
-
this.validate = memoize(([context]) => CREATE_KEY_FN$
|
|
16278
|
+
this.validate = memoize(([context]) => CREATE_KEY_FN$r(context), async (context) => {
|
|
16151
16279
|
this.loggerService.log("riskGlobalService validate", {
|
|
16152
16280
|
context,
|
|
16153
16281
|
});
|
|
@@ -16225,7 +16353,7 @@ const METHOD_NAME_VALIDATE$1 = "actionCoreService validate";
|
|
|
16225
16353
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
16226
16354
|
* @returns Unique string key for memoization
|
|
16227
16355
|
*/
|
|
16228
|
-
const CREATE_KEY_FN$
|
|
16356
|
+
const CREATE_KEY_FN$q = (context) => {
|
|
16229
16357
|
const parts = [context.strategyName, context.exchangeName];
|
|
16230
16358
|
if (context.frameName)
|
|
16231
16359
|
parts.push(context.frameName);
|
|
@@ -16269,7 +16397,7 @@ class ActionCoreService {
|
|
|
16269
16397
|
* @param context - Strategy execution context with strategyName, exchangeName and frameName
|
|
16270
16398
|
* @returns Promise that resolves when all validations complete
|
|
16271
16399
|
*/
|
|
16272
|
-
this.validate = memoize(([context]) => CREATE_KEY_FN$
|
|
16400
|
+
this.validate = memoize(([context]) => CREATE_KEY_FN$q(context), async (context) => {
|
|
16273
16401
|
this.loggerService.log(METHOD_NAME_VALIDATE$1, {
|
|
16274
16402
|
context,
|
|
16275
16403
|
});
|
|
@@ -21339,7 +21467,7 @@ const ReportWriter = new ReportWriterAdapter();
|
|
|
21339
21467
|
* @param backtest - Whether running in backtest mode
|
|
21340
21468
|
* @returns Unique string key for memoization
|
|
21341
21469
|
*/
|
|
21342
|
-
const CREATE_KEY_FN$
|
|
21470
|
+
const CREATE_KEY_FN$p = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
21343
21471
|
const parts = [symbol, strategyName, exchangeName];
|
|
21344
21472
|
if (frameName)
|
|
21345
21473
|
parts.push(frameName);
|
|
@@ -21585,7 +21713,7 @@ class BacktestMarkdownService {
|
|
|
21585
21713
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
21586
21714
|
* Each combination gets its own isolated storage instance.
|
|
21587
21715
|
*/
|
|
21588
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
21716
|
+
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$p(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$a(symbol, strategyName, exchangeName, frameName));
|
|
21589
21717
|
/**
|
|
21590
21718
|
* Processes tick events and accumulates closed signals.
|
|
21591
21719
|
* Should be called from IStrategyCallbacks.onTick.
|
|
@@ -21742,7 +21870,7 @@ class BacktestMarkdownService {
|
|
|
21742
21870
|
payload,
|
|
21743
21871
|
});
|
|
21744
21872
|
if (payload) {
|
|
21745
|
-
const key = CREATE_KEY_FN$
|
|
21873
|
+
const key = CREATE_KEY_FN$p(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
21746
21874
|
this.getStorage.clear(key);
|
|
21747
21875
|
}
|
|
21748
21876
|
else {
|
|
@@ -21804,7 +21932,7 @@ class BacktestMarkdownService {
|
|
|
21804
21932
|
* @param backtest - Whether running in backtest mode
|
|
21805
21933
|
* @returns Unique string key for memoization
|
|
21806
21934
|
*/
|
|
21807
|
-
const CREATE_KEY_FN$
|
|
21935
|
+
const CREATE_KEY_FN$o = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
21808
21936
|
const parts = [symbol, strategyName, exchangeName];
|
|
21809
21937
|
if (frameName)
|
|
21810
21938
|
parts.push(frameName);
|
|
@@ -22299,7 +22427,7 @@ class LiveMarkdownService {
|
|
|
22299
22427
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
22300
22428
|
* Each combination gets its own isolated storage instance.
|
|
22301
22429
|
*/
|
|
22302
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
22430
|
+
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$o(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$9(symbol, strategyName, exchangeName, frameName));
|
|
22303
22431
|
/**
|
|
22304
22432
|
* Subscribes to live signal emitter to receive tick events.
|
|
22305
22433
|
* Protected against multiple subscriptions.
|
|
@@ -22517,7 +22645,7 @@ class LiveMarkdownService {
|
|
|
22517
22645
|
payload,
|
|
22518
22646
|
});
|
|
22519
22647
|
if (payload) {
|
|
22520
|
-
const key = CREATE_KEY_FN$
|
|
22648
|
+
const key = CREATE_KEY_FN$o(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
22521
22649
|
this.getStorage.clear(key);
|
|
22522
22650
|
}
|
|
22523
22651
|
else {
|
|
@@ -22537,7 +22665,7 @@ class LiveMarkdownService {
|
|
|
22537
22665
|
* @param backtest - Whether running in backtest mode
|
|
22538
22666
|
* @returns Unique string key for memoization
|
|
22539
22667
|
*/
|
|
22540
|
-
const CREATE_KEY_FN$
|
|
22668
|
+
const CREATE_KEY_FN$n = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
22541
22669
|
const parts = [symbol, strategyName, exchangeName];
|
|
22542
22670
|
if (frameName)
|
|
22543
22671
|
parts.push(frameName);
|
|
@@ -22826,7 +22954,7 @@ class ScheduleMarkdownService {
|
|
|
22826
22954
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
22827
22955
|
* Each combination gets its own isolated storage instance.
|
|
22828
22956
|
*/
|
|
22829
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
22957
|
+
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$n(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$8(symbol, strategyName, exchangeName, frameName));
|
|
22830
22958
|
/**
|
|
22831
22959
|
* Subscribes to signal emitter to receive scheduled signal events.
|
|
22832
22960
|
* Protected against multiple subscriptions.
|
|
@@ -23029,7 +23157,7 @@ class ScheduleMarkdownService {
|
|
|
23029
23157
|
payload,
|
|
23030
23158
|
});
|
|
23031
23159
|
if (payload) {
|
|
23032
|
-
const key = CREATE_KEY_FN$
|
|
23160
|
+
const key = CREATE_KEY_FN$n(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
23033
23161
|
this.getStorage.clear(key);
|
|
23034
23162
|
}
|
|
23035
23163
|
else {
|
|
@@ -23049,7 +23177,7 @@ class ScheduleMarkdownService {
|
|
|
23049
23177
|
* @param backtest - Whether running in backtest mode
|
|
23050
23178
|
* @returns Unique string key for memoization
|
|
23051
23179
|
*/
|
|
23052
|
-
const CREATE_KEY_FN$
|
|
23180
|
+
const CREATE_KEY_FN$m = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
23053
23181
|
const parts = [symbol, strategyName, exchangeName];
|
|
23054
23182
|
if (frameName)
|
|
23055
23183
|
parts.push(frameName);
|
|
@@ -23294,7 +23422,7 @@ class PerformanceMarkdownService {
|
|
|
23294
23422
|
* Memoized function to get or create PerformanceStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
23295
23423
|
* Each combination gets its own isolated storage instance.
|
|
23296
23424
|
*/
|
|
23297
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
23425
|
+
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$m(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new PerformanceStorage(symbol, strategyName, exchangeName, frameName));
|
|
23298
23426
|
/**
|
|
23299
23427
|
* Subscribes to performance emitter to receive performance events.
|
|
23300
23428
|
* Protected against multiple subscriptions.
|
|
@@ -23461,7 +23589,7 @@ class PerformanceMarkdownService {
|
|
|
23461
23589
|
payload,
|
|
23462
23590
|
});
|
|
23463
23591
|
if (payload) {
|
|
23464
|
-
const key = CREATE_KEY_FN$
|
|
23592
|
+
const key = CREATE_KEY_FN$m(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
23465
23593
|
this.getStorage.clear(key);
|
|
23466
23594
|
}
|
|
23467
23595
|
else {
|
|
@@ -23940,7 +24068,7 @@ class WalkerMarkdownService {
|
|
|
23940
24068
|
* @param backtest - Whether running in backtest mode
|
|
23941
24069
|
* @returns Unique string key for memoization
|
|
23942
24070
|
*/
|
|
23943
|
-
const CREATE_KEY_FN$
|
|
24071
|
+
const CREATE_KEY_FN$l = (exchangeName, frameName, backtest) => {
|
|
23944
24072
|
const parts = [exchangeName];
|
|
23945
24073
|
if (frameName)
|
|
23946
24074
|
parts.push(frameName);
|
|
@@ -24387,7 +24515,7 @@ class HeatMarkdownService {
|
|
|
24387
24515
|
* Memoized function to get or create HeatmapStorage for exchange, frame and backtest mode.
|
|
24388
24516
|
* Each exchangeName + frameName + backtest mode combination gets its own isolated heatmap storage instance.
|
|
24389
24517
|
*/
|
|
24390
|
-
this.getStorage = memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
24518
|
+
this.getStorage = memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$l(exchangeName, frameName, backtest), (exchangeName, frameName, backtest) => new HeatmapStorage(exchangeName, frameName, backtest));
|
|
24391
24519
|
/**
|
|
24392
24520
|
* Subscribes to signal emitter to receive tick events.
|
|
24393
24521
|
* Protected against multiple subscriptions.
|
|
@@ -24605,7 +24733,7 @@ class HeatMarkdownService {
|
|
|
24605
24733
|
payload,
|
|
24606
24734
|
});
|
|
24607
24735
|
if (payload) {
|
|
24608
|
-
const key = CREATE_KEY_FN$
|
|
24736
|
+
const key = CREATE_KEY_FN$l(payload.exchangeName, payload.frameName, payload.backtest);
|
|
24609
24737
|
this.getStorage.clear(key);
|
|
24610
24738
|
}
|
|
24611
24739
|
else {
|
|
@@ -25636,7 +25764,7 @@ class ClientPartial {
|
|
|
25636
25764
|
* @param backtest - Whether running in backtest mode
|
|
25637
25765
|
* @returns Unique string key for memoization
|
|
25638
25766
|
*/
|
|
25639
|
-
const CREATE_KEY_FN$
|
|
25767
|
+
const CREATE_KEY_FN$k = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
25640
25768
|
/**
|
|
25641
25769
|
* Creates a callback function for emitting profit events to partialProfitSubject.
|
|
25642
25770
|
*
|
|
@@ -25758,7 +25886,7 @@ class PartialConnectionService {
|
|
|
25758
25886
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
25759
25887
|
* Value: ClientPartial instance with logger and event emitters
|
|
25760
25888
|
*/
|
|
25761
|
-
this.getPartial = memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
25889
|
+
this.getPartial = memoize(([signalId, backtest]) => CREATE_KEY_FN$k(signalId, backtest), (signalId, backtest) => {
|
|
25762
25890
|
return new ClientPartial({
|
|
25763
25891
|
signalId,
|
|
25764
25892
|
logger: this.loggerService,
|
|
@@ -25848,7 +25976,7 @@ class PartialConnectionService {
|
|
|
25848
25976
|
const partial = this.getPartial(data.id, backtest);
|
|
25849
25977
|
await partial.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
|
|
25850
25978
|
await partial.clear(symbol, data, priceClose, backtest);
|
|
25851
|
-
const key = CREATE_KEY_FN$
|
|
25979
|
+
const key = CREATE_KEY_FN$k(data.id, backtest);
|
|
25852
25980
|
this.getPartial.clear(key);
|
|
25853
25981
|
};
|
|
25854
25982
|
}
|
|
@@ -25864,7 +25992,7 @@ class PartialConnectionService {
|
|
|
25864
25992
|
* @param backtest - Whether running in backtest mode
|
|
25865
25993
|
* @returns Unique string key for memoization
|
|
25866
25994
|
*/
|
|
25867
|
-
const CREATE_KEY_FN$
|
|
25995
|
+
const CREATE_KEY_FN$j = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
25868
25996
|
const parts = [symbol, strategyName, exchangeName];
|
|
25869
25997
|
if (frameName)
|
|
25870
25998
|
parts.push(frameName);
|
|
@@ -26087,7 +26215,7 @@ class PartialMarkdownService {
|
|
|
26087
26215
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
26088
26216
|
* Each combination gets its own isolated storage instance.
|
|
26089
26217
|
*/
|
|
26090
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
26218
|
+
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$j(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$6(symbol, strategyName, exchangeName, frameName));
|
|
26091
26219
|
/**
|
|
26092
26220
|
* Subscribes to partial profit/loss signal emitters to receive events.
|
|
26093
26221
|
* Protected against multiple subscriptions.
|
|
@@ -26297,7 +26425,7 @@ class PartialMarkdownService {
|
|
|
26297
26425
|
payload,
|
|
26298
26426
|
});
|
|
26299
26427
|
if (payload) {
|
|
26300
|
-
const key = CREATE_KEY_FN$
|
|
26428
|
+
const key = CREATE_KEY_FN$j(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
26301
26429
|
this.getStorage.clear(key);
|
|
26302
26430
|
}
|
|
26303
26431
|
else {
|
|
@@ -26313,7 +26441,7 @@ class PartialMarkdownService {
|
|
|
26313
26441
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
26314
26442
|
* @returns Unique string key for memoization
|
|
26315
26443
|
*/
|
|
26316
|
-
const CREATE_KEY_FN$
|
|
26444
|
+
const CREATE_KEY_FN$i = (context) => {
|
|
26317
26445
|
const parts = [context.strategyName, context.exchangeName];
|
|
26318
26446
|
if (context.frameName)
|
|
26319
26447
|
parts.push(context.frameName);
|
|
@@ -26387,7 +26515,7 @@ class PartialGlobalService {
|
|
|
26387
26515
|
* @param context - Context with strategyName, exchangeName and frameName
|
|
26388
26516
|
* @param methodName - Name of the calling method for error tracking
|
|
26389
26517
|
*/
|
|
26390
|
-
this.validate = memoize(([context]) => CREATE_KEY_FN$
|
|
26518
|
+
this.validate = memoize(([context]) => CREATE_KEY_FN$i(context), (context, methodName) => {
|
|
26391
26519
|
this.loggerService.log("partialGlobalService validate", {
|
|
26392
26520
|
context,
|
|
26393
26521
|
methodName,
|
|
@@ -26842,7 +26970,7 @@ class ClientBreakeven {
|
|
|
26842
26970
|
* @param backtest - Whether running in backtest mode
|
|
26843
26971
|
* @returns Unique string key for memoization
|
|
26844
26972
|
*/
|
|
26845
|
-
const CREATE_KEY_FN$
|
|
26973
|
+
const CREATE_KEY_FN$h = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
26846
26974
|
/**
|
|
26847
26975
|
* Creates a callback function for emitting breakeven events to breakevenSubject.
|
|
26848
26976
|
*
|
|
@@ -26928,7 +27056,7 @@ class BreakevenConnectionService {
|
|
|
26928
27056
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
26929
27057
|
* Value: ClientBreakeven instance with logger and event emitter
|
|
26930
27058
|
*/
|
|
26931
|
-
this.getBreakeven = memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
27059
|
+
this.getBreakeven = memoize(([signalId, backtest]) => CREATE_KEY_FN$h(signalId, backtest), (signalId, backtest) => {
|
|
26932
27060
|
return new ClientBreakeven({
|
|
26933
27061
|
signalId,
|
|
26934
27062
|
logger: this.loggerService,
|
|
@@ -26989,7 +27117,7 @@ class BreakevenConnectionService {
|
|
|
26989
27117
|
const breakeven = this.getBreakeven(data.id, backtest);
|
|
26990
27118
|
await breakeven.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
|
|
26991
27119
|
await breakeven.clear(symbol, data, priceClose, backtest);
|
|
26992
|
-
const key = CREATE_KEY_FN$
|
|
27120
|
+
const key = CREATE_KEY_FN$h(data.id, backtest);
|
|
26993
27121
|
this.getBreakeven.clear(key);
|
|
26994
27122
|
};
|
|
26995
27123
|
}
|
|
@@ -27005,7 +27133,7 @@ class BreakevenConnectionService {
|
|
|
27005
27133
|
* @param backtest - Whether running in backtest mode
|
|
27006
27134
|
* @returns Unique string key for memoization
|
|
27007
27135
|
*/
|
|
27008
|
-
const CREATE_KEY_FN$
|
|
27136
|
+
const CREATE_KEY_FN$g = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
27009
27137
|
const parts = [symbol, strategyName, exchangeName];
|
|
27010
27138
|
if (frameName)
|
|
27011
27139
|
parts.push(frameName);
|
|
@@ -27180,7 +27308,7 @@ class BreakevenMarkdownService {
|
|
|
27180
27308
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
27181
27309
|
* Each combination gets its own isolated storage instance.
|
|
27182
27310
|
*/
|
|
27183
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
27311
|
+
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$g(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$5(symbol, strategyName, exchangeName, frameName));
|
|
27184
27312
|
/**
|
|
27185
27313
|
* Subscribes to breakeven signal emitter to receive events.
|
|
27186
27314
|
* Protected against multiple subscriptions.
|
|
@@ -27369,7 +27497,7 @@ class BreakevenMarkdownService {
|
|
|
27369
27497
|
payload,
|
|
27370
27498
|
});
|
|
27371
27499
|
if (payload) {
|
|
27372
|
-
const key = CREATE_KEY_FN$
|
|
27500
|
+
const key = CREATE_KEY_FN$g(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
27373
27501
|
this.getStorage.clear(key);
|
|
27374
27502
|
}
|
|
27375
27503
|
else {
|
|
@@ -27385,7 +27513,7 @@ class BreakevenMarkdownService {
|
|
|
27385
27513
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
27386
27514
|
* @returns Unique string key for memoization
|
|
27387
27515
|
*/
|
|
27388
|
-
const CREATE_KEY_FN$
|
|
27516
|
+
const CREATE_KEY_FN$f = (context) => {
|
|
27389
27517
|
const parts = [context.strategyName, context.exchangeName];
|
|
27390
27518
|
if (context.frameName)
|
|
27391
27519
|
parts.push(context.frameName);
|
|
@@ -27459,7 +27587,7 @@ class BreakevenGlobalService {
|
|
|
27459
27587
|
* @param context - Context with strategyName, exchangeName and frameName
|
|
27460
27588
|
* @param methodName - Name of the calling method for error tracking
|
|
27461
27589
|
*/
|
|
27462
|
-
this.validate = memoize(([context]) => CREATE_KEY_FN$
|
|
27590
|
+
this.validate = memoize(([context]) => CREATE_KEY_FN$f(context), (context, methodName) => {
|
|
27463
27591
|
this.loggerService.log("breakevenGlobalService validate", {
|
|
27464
27592
|
context,
|
|
27465
27593
|
methodName,
|
|
@@ -27680,7 +27808,7 @@ class ConfigValidationService {
|
|
|
27680
27808
|
* @param backtest - Whether running in backtest mode
|
|
27681
27809
|
* @returns Unique string key for memoization
|
|
27682
27810
|
*/
|
|
27683
|
-
const CREATE_KEY_FN$
|
|
27811
|
+
const CREATE_KEY_FN$e = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
27684
27812
|
const parts = [symbol, strategyName, exchangeName];
|
|
27685
27813
|
if (frameName)
|
|
27686
27814
|
parts.push(frameName);
|
|
@@ -27847,7 +27975,7 @@ class RiskMarkdownService {
|
|
|
27847
27975
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
27848
27976
|
* Each combination gets its own isolated storage instance.
|
|
27849
27977
|
*/
|
|
27850
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
27978
|
+
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$e(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$4(symbol, strategyName, exchangeName, frameName));
|
|
27851
27979
|
/**
|
|
27852
27980
|
* Subscribes to risk rejection emitter to receive rejection events.
|
|
27853
27981
|
* Protected against multiple subscriptions.
|
|
@@ -28036,7 +28164,7 @@ class RiskMarkdownService {
|
|
|
28036
28164
|
payload,
|
|
28037
28165
|
});
|
|
28038
28166
|
if (payload) {
|
|
28039
|
-
const key = CREATE_KEY_FN$
|
|
28167
|
+
const key = CREATE_KEY_FN$e(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
28040
28168
|
this.getStorage.clear(key);
|
|
28041
28169
|
}
|
|
28042
28170
|
else {
|
|
@@ -30616,7 +30744,7 @@ class HighestProfitReportService {
|
|
|
30616
30744
|
* @returns Colon-separated key string for memoization
|
|
30617
30745
|
* @internal
|
|
30618
30746
|
*/
|
|
30619
|
-
const CREATE_KEY_FN$
|
|
30747
|
+
const CREATE_KEY_FN$d = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
30620
30748
|
const parts = [symbol, strategyName, exchangeName];
|
|
30621
30749
|
if (frameName)
|
|
30622
30750
|
parts.push(frameName);
|
|
@@ -30858,7 +30986,7 @@ class StrategyMarkdownService {
|
|
|
30858
30986
|
*
|
|
30859
30987
|
* @internal
|
|
30860
30988
|
*/
|
|
30861
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
30989
|
+
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$d(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$3(symbol, strategyName, exchangeName, frameName));
|
|
30862
30990
|
/**
|
|
30863
30991
|
* Records a cancel-scheduled event when a scheduled signal is cancelled.
|
|
30864
30992
|
*
|
|
@@ -31432,7 +31560,7 @@ class StrategyMarkdownService {
|
|
|
31432
31560
|
this.clear = async (payload) => {
|
|
31433
31561
|
this.loggerService.log("strategyMarkdownService clear", { payload });
|
|
31434
31562
|
if (payload) {
|
|
31435
|
-
const key = CREATE_KEY_FN$
|
|
31563
|
+
const key = CREATE_KEY_FN$d(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
31436
31564
|
this.getStorage.clear(key);
|
|
31437
31565
|
}
|
|
31438
31566
|
else {
|
|
@@ -31540,7 +31668,7 @@ class StrategyMarkdownService {
|
|
|
31540
31668
|
* Creates a unique key for memoizing ReportStorage instances.
|
|
31541
31669
|
* Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
|
|
31542
31670
|
*/
|
|
31543
|
-
const CREATE_KEY_FN$
|
|
31671
|
+
const CREATE_KEY_FN$c = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
31544
31672
|
const parts = [symbol, strategyName, exchangeName];
|
|
31545
31673
|
if (frameName)
|
|
31546
31674
|
parts.push(frameName);
|
|
@@ -31733,7 +31861,7 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
31733
31861
|
class SyncMarkdownService {
|
|
31734
31862
|
constructor() {
|
|
31735
31863
|
this.loggerService = inject(TYPES.loggerService);
|
|
31736
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
31864
|
+
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$c(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$2(symbol, strategyName, exchangeName, frameName, backtest));
|
|
31737
31865
|
/**
|
|
31738
31866
|
* Subscribes to `syncSubject` to start receiving `SignalSyncContract` events.
|
|
31739
31867
|
* Protected against multiple subscriptions via `singleshot` — subsequent calls
|
|
@@ -31931,7 +32059,7 @@ class SyncMarkdownService {
|
|
|
31931
32059
|
this.clear = async (payload) => {
|
|
31932
32060
|
this.loggerService.log("syncMarkdownService clear", { payload });
|
|
31933
32061
|
if (payload) {
|
|
31934
|
-
const key = CREATE_KEY_FN$
|
|
32062
|
+
const key = CREATE_KEY_FN$c(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
31935
32063
|
this.getStorage.clear(key);
|
|
31936
32064
|
}
|
|
31937
32065
|
else {
|
|
@@ -31944,7 +32072,7 @@ class SyncMarkdownService {
|
|
|
31944
32072
|
/**
|
|
31945
32073
|
* Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
|
|
31946
32074
|
*/
|
|
31947
|
-
const CREATE_KEY_FN$
|
|
32075
|
+
const CREATE_KEY_FN$b = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
31948
32076
|
const parts = [symbol, strategyName, exchangeName];
|
|
31949
32077
|
if (frameName)
|
|
31950
32078
|
parts.push(frameName);
|
|
@@ -32122,7 +32250,7 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
32122
32250
|
class HighestProfitMarkdownService {
|
|
32123
32251
|
constructor() {
|
|
32124
32252
|
this.loggerService = inject(TYPES.loggerService);
|
|
32125
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32253
|
+
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$b(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$1(symbol, strategyName, exchangeName, frameName));
|
|
32126
32254
|
/**
|
|
32127
32255
|
* Subscribes to `highestProfitSubject` to start receiving `HighestProfitContract`
|
|
32128
32256
|
* events. Protected against multiple subscriptions via `singleshot` — subsequent
|
|
@@ -32288,7 +32416,7 @@ class HighestProfitMarkdownService {
|
|
|
32288
32416
|
this.clear = async (payload) => {
|
|
32289
32417
|
this.loggerService.log("highestProfitMarkdownService clear", { payload });
|
|
32290
32418
|
if (payload) {
|
|
32291
|
-
const key = CREATE_KEY_FN$
|
|
32419
|
+
const key = CREATE_KEY_FN$b(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32292
32420
|
this.getStorage.clear(key);
|
|
32293
32421
|
}
|
|
32294
32422
|
else {
|
|
@@ -32310,7 +32438,7 @@ const LISTEN_TIMEOUT$1 = 120000;
|
|
|
32310
32438
|
* @param backtest - Whether running in backtest mode
|
|
32311
32439
|
* @returns Unique string key for memoization
|
|
32312
32440
|
*/
|
|
32313
|
-
const CREATE_KEY_FN$
|
|
32441
|
+
const CREATE_KEY_FN$a = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
32314
32442
|
const parts = [symbol, strategyName, exchangeName];
|
|
32315
32443
|
if (frameName)
|
|
32316
32444
|
parts.push(frameName);
|
|
@@ -32353,7 +32481,7 @@ class PriceMetaService {
|
|
|
32353
32481
|
* Each subject holds the latest currentPrice emitted by the strategy iterator for that key.
|
|
32354
32482
|
* Instances are cached until clear() is called.
|
|
32355
32483
|
*/
|
|
32356
|
-
this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32484
|
+
this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$a(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
|
|
32357
32485
|
/**
|
|
32358
32486
|
* Returns the current market price for the given symbol and context.
|
|
32359
32487
|
*
|
|
@@ -32382,10 +32510,10 @@ class PriceMetaService {
|
|
|
32382
32510
|
if (source.data) {
|
|
32383
32511
|
return source.data;
|
|
32384
32512
|
}
|
|
32385
|
-
console.warn(`PriceMetaService: No currentPrice available for ${CREATE_KEY_FN$
|
|
32513
|
+
console.warn(`PriceMetaService: No currentPrice available for ${CREATE_KEY_FN$a(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
|
|
32386
32514
|
const currentPrice = await waitForNext(source, (data) => !!data, LISTEN_TIMEOUT$1);
|
|
32387
32515
|
if (typeof currentPrice === "symbol") {
|
|
32388
|
-
throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$
|
|
32516
|
+
throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$a(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
|
|
32389
32517
|
}
|
|
32390
32518
|
return currentPrice;
|
|
32391
32519
|
};
|
|
@@ -32427,7 +32555,7 @@ class PriceMetaService {
|
|
|
32427
32555
|
this.getSource.clear();
|
|
32428
32556
|
return;
|
|
32429
32557
|
}
|
|
32430
|
-
const key = CREATE_KEY_FN$
|
|
32558
|
+
const key = CREATE_KEY_FN$a(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32431
32559
|
this.getSource.clear(key);
|
|
32432
32560
|
};
|
|
32433
32561
|
}
|
|
@@ -32445,7 +32573,7 @@ const LISTEN_TIMEOUT = 120000;
|
|
|
32445
32573
|
* @param backtest - Whether running in backtest mode
|
|
32446
32574
|
* @returns Unique string key for memoization
|
|
32447
32575
|
*/
|
|
32448
|
-
const CREATE_KEY_FN$
|
|
32576
|
+
const CREATE_KEY_FN$9 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
32449
32577
|
const parts = [symbol, strategyName, exchangeName];
|
|
32450
32578
|
if (frameName)
|
|
32451
32579
|
parts.push(frameName);
|
|
@@ -32488,7 +32616,7 @@ class TimeMetaService {
|
|
|
32488
32616
|
* Each subject holds the latest createdAt timestamp emitted by the strategy iterator for that key.
|
|
32489
32617
|
* Instances are cached until clear() is called.
|
|
32490
32618
|
*/
|
|
32491
|
-
this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32619
|
+
this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$9(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
|
|
32492
32620
|
/**
|
|
32493
32621
|
* Returns the current candle timestamp (in milliseconds) for the given symbol and context.
|
|
32494
32622
|
*
|
|
@@ -32516,10 +32644,10 @@ class TimeMetaService {
|
|
|
32516
32644
|
if (source.data) {
|
|
32517
32645
|
return source.data;
|
|
32518
32646
|
}
|
|
32519
|
-
console.warn(`TimeMetaService: No timestamp available for ${CREATE_KEY_FN$
|
|
32647
|
+
console.warn(`TimeMetaService: No timestamp available for ${CREATE_KEY_FN$9(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
|
|
32520
32648
|
const timestamp = await waitForNext(source, (data) => !!data, LISTEN_TIMEOUT);
|
|
32521
32649
|
if (typeof timestamp === "symbol") {
|
|
32522
|
-
throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$
|
|
32650
|
+
throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$9(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
|
|
32523
32651
|
}
|
|
32524
32652
|
return timestamp;
|
|
32525
32653
|
};
|
|
@@ -32561,7 +32689,7 @@ class TimeMetaService {
|
|
|
32561
32689
|
this.getSource.clear();
|
|
32562
32690
|
return;
|
|
32563
32691
|
}
|
|
32564
|
-
const key = CREATE_KEY_FN$
|
|
32692
|
+
const key = CREATE_KEY_FN$9(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32565
32693
|
this.getSource.clear(key);
|
|
32566
32694
|
};
|
|
32567
32695
|
}
|
|
@@ -32667,7 +32795,7 @@ class MaxDrawdownReportService {
|
|
|
32667
32795
|
/**
|
|
32668
32796
|
* Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
|
|
32669
32797
|
*/
|
|
32670
|
-
const CREATE_KEY_FN$
|
|
32798
|
+
const CREATE_KEY_FN$8 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
32671
32799
|
const parts = [symbol, strategyName, exchangeName];
|
|
32672
32800
|
if (frameName)
|
|
32673
32801
|
parts.push(frameName);
|
|
@@ -32793,7 +32921,7 @@ class ReportStorage {
|
|
|
32793
32921
|
class MaxDrawdownMarkdownService {
|
|
32794
32922
|
constructor() {
|
|
32795
32923
|
this.loggerService = inject(TYPES.loggerService);
|
|
32796
|
-
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32924
|
+
this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$8(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage(symbol, strategyName, exchangeName, frameName));
|
|
32797
32925
|
/**
|
|
32798
32926
|
* Subscribes to `maxDrawdownSubject` to start receiving `MaxDrawdownContract`
|
|
32799
32927
|
* events. Protected against multiple subscriptions via `singleshot`.
|
|
@@ -32872,7 +33000,7 @@ class MaxDrawdownMarkdownService {
|
|
|
32872
33000
|
this.clear = async (payload) => {
|
|
32873
33001
|
this.loggerService.log("maxDrawdownMarkdownService clear", { payload });
|
|
32874
33002
|
if (payload) {
|
|
32875
|
-
const key = CREATE_KEY_FN$
|
|
33003
|
+
const key = CREATE_KEY_FN$8(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32876
33004
|
this.getStorage.clear(key);
|
|
32877
33005
|
}
|
|
32878
33006
|
else {
|
|
@@ -32890,7 +33018,7 @@ const METHOD_NAME_VALIDATE = "notificationHelperService.validate";
|
|
|
32890
33018
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
32891
33019
|
* @returns Unique string key for memoization
|
|
32892
33020
|
*/
|
|
32893
|
-
const CREATE_KEY_FN$
|
|
33021
|
+
const CREATE_KEY_FN$7 = (context) => {
|
|
32894
33022
|
const parts = [context.strategyName, context.exchangeName];
|
|
32895
33023
|
if (context.frameName)
|
|
32896
33024
|
parts.push(context.frameName);
|
|
@@ -32923,7 +33051,7 @@ class NotificationHelperService {
|
|
|
32923
33051
|
* @param context - Routing context: strategyName, exchangeName, frameName
|
|
32924
33052
|
* @throws {Error} If any registered schema fails validation
|
|
32925
33053
|
*/
|
|
32926
|
-
this.validate = memoize(([context]) => CREATE_KEY_FN$
|
|
33054
|
+
this.validate = memoize(([context]) => CREATE_KEY_FN$7(context), async (context) => {
|
|
32927
33055
|
this.loggerService.log(METHOD_NAME_VALIDATE, {
|
|
32928
33056
|
context,
|
|
32929
33057
|
});
|
|
@@ -46467,7 +46595,7 @@ const RECENT_ADAPTER_METHOD_NAME_GET_MINUTES_SINCE_LATEST_SIGNAL = "RecentAdapte
|
|
|
46467
46595
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
46468
46596
|
* @returns Composite key string
|
|
46469
46597
|
*/
|
|
46470
|
-
const CREATE_KEY_FN$
|
|
46598
|
+
const CREATE_KEY_FN$6 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
46471
46599
|
const parts = [symbol, strategyName, exchangeName];
|
|
46472
46600
|
if (frameName)
|
|
46473
46601
|
parts.push(frameName);
|
|
@@ -46557,7 +46685,7 @@ class RecentMemoryBacktestUtils {
|
|
|
46557
46685
|
backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
46558
46686
|
signalId: event.data.id,
|
|
46559
46687
|
});
|
|
46560
|
-
const key = CREATE_KEY_FN$
|
|
46688
|
+
const key = CREATE_KEY_FN$6(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
|
|
46561
46689
|
this._signals.set(key, event.data);
|
|
46562
46690
|
};
|
|
46563
46691
|
/**
|
|
@@ -46570,7 +46698,7 @@ class RecentMemoryBacktestUtils {
|
|
|
46570
46698
|
* @returns The latest signal or null if not found
|
|
46571
46699
|
*/
|
|
46572
46700
|
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
46573
|
-
const key = CREATE_KEY_FN$
|
|
46701
|
+
const key = CREATE_KEY_FN$6(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
46574
46702
|
backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
46575
46703
|
return this._signals.get(key) ?? null;
|
|
46576
46704
|
};
|
|
@@ -46676,7 +46804,7 @@ class RecentMemoryLiveUtils {
|
|
|
46676
46804
|
backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
46677
46805
|
signalId: event.data.id,
|
|
46678
46806
|
});
|
|
46679
|
-
const key = CREATE_KEY_FN$
|
|
46807
|
+
const key = CREATE_KEY_FN$6(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
|
|
46680
46808
|
this._signals.set(key, event.data);
|
|
46681
46809
|
};
|
|
46682
46810
|
/**
|
|
@@ -46689,7 +46817,7 @@ class RecentMemoryLiveUtils {
|
|
|
46689
46817
|
* @returns The latest signal or null if not found
|
|
46690
46818
|
*/
|
|
46691
46819
|
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
46692
|
-
const key = CREATE_KEY_FN$
|
|
46820
|
+
const key = CREATE_KEY_FN$6(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
46693
46821
|
backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
46694
46822
|
return this._signals.get(key) ?? null;
|
|
46695
46823
|
};
|
|
@@ -47040,6 +47168,554 @@ const RecentLive = new RecentLiveAdapter();
|
|
|
47040
47168
|
*/
|
|
47041
47169
|
const RecentBacktest = new RecentBacktestAdapter();
|
|
47042
47170
|
|
|
47171
|
+
const CREATE_KEY_FN$5 = (signalId, bucketName) => `${signalId}_${bucketName}`;
|
|
47172
|
+
const STATE_LOCAL_INSTANCE_METHOD_NAME_GET = "StateLocalInstance.getState";
|
|
47173
|
+
const STATE_LOCAL_INSTANCE_METHOD_NAME_SET = "StateLocalInstance.setState";
|
|
47174
|
+
const STATE_PERSIST_INSTANCE_METHOD_NAME_WAIT_FOR_INIT = "StatePersistInstance.waitForInit";
|
|
47175
|
+
const STATE_PERSIST_INSTANCE_METHOD_NAME_GET = "StatePersistInstance.getState";
|
|
47176
|
+
const STATE_PERSIST_INSTANCE_METHOD_NAME_SET = "StatePersistInstance.setState";
|
|
47177
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE = "StateBacktestAdapter.dispose";
|
|
47178
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_GET = "StateBacktestAdapter.getState";
|
|
47179
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_SET = "StateBacktestAdapter.setState";
|
|
47180
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL = "StateBacktestAdapter.useLocal";
|
|
47181
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST = "StateBacktestAdapter.usePersist";
|
|
47182
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY = "StateBacktestAdapter.useDummy";
|
|
47183
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER = "StateBacktestAdapter.useStateAdapter";
|
|
47184
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_CLEAR = "StateBacktestAdapter.clear";
|
|
47185
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_DISPOSE = "StateLiveAdapter.dispose";
|
|
47186
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_GET = "StateLiveAdapter.getState";
|
|
47187
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_SET = "StateLiveAdapter.setState";
|
|
47188
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL = "StateLiveAdapter.useLocal";
|
|
47189
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST = "StateLiveAdapter.usePersist";
|
|
47190
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY = "StateLiveAdapter.useDummy";
|
|
47191
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER = "StateLiveAdapter.useStateAdapter";
|
|
47192
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_CLEAR = "StateLiveAdapter.clear";
|
|
47193
|
+
const STATE_ADAPTER_METHOD_NAME_ENABLE = "StateAdapter.enable";
|
|
47194
|
+
const STATE_ADAPTER_METHOD_NAME_DISABLE = "StateAdapter.disable";
|
|
47195
|
+
const STATE_ADAPTER_METHOD_NAME_GET = "StateAdapter.getState";
|
|
47196
|
+
const STATE_ADAPTER_METHOD_NAME_SET = "StateAdapter.setState";
|
|
47197
|
+
/**
|
|
47198
|
+
* In-process state instance backed by a plain object reference.
|
|
47199
|
+
* All data lives in process memory only - no disk persistence.
|
|
47200
|
+
*
|
|
47201
|
+
* Features:
|
|
47202
|
+
* - Mutable in-memory state with functional dispatch support
|
|
47203
|
+
* - Scoped per (signalId, bucketName) pair
|
|
47204
|
+
*
|
|
47205
|
+
* Use for backtesting and unit tests where persistence between runs is not needed.
|
|
47206
|
+
* Tracks per-trade metrics such as peakPercent and minutesOpen to implement
|
|
47207
|
+
* the capitulation rule: exit when peak < threshold after N minutes open.
|
|
47208
|
+
*/
|
|
47209
|
+
class StateLocalInstance {
|
|
47210
|
+
constructor(initialValue, signalId, bucketName) {
|
|
47211
|
+
this.initialValue = initialValue;
|
|
47212
|
+
this.signalId = signalId;
|
|
47213
|
+
this.bucketName = bucketName;
|
|
47214
|
+
/**
|
|
47215
|
+
* Initializes _value from initialValue - local state needs no async setup.
|
|
47216
|
+
* @returns Promise that resolves immediately
|
|
47217
|
+
*/
|
|
47218
|
+
this.waitForInit = singleshot(async (_initial) => {
|
|
47219
|
+
this._value = this.initialValue;
|
|
47220
|
+
});
|
|
47221
|
+
/**
|
|
47222
|
+
* Update the in-memory state value.
|
|
47223
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47224
|
+
* @returns Updated state value
|
|
47225
|
+
*/
|
|
47226
|
+
this.setState = queued(async (dispatch) => {
|
|
47227
|
+
backtest.loggerService.debug(STATE_LOCAL_INSTANCE_METHOD_NAME_SET, {
|
|
47228
|
+
signalId: this.signalId,
|
|
47229
|
+
bucketName: this.bucketName,
|
|
47230
|
+
});
|
|
47231
|
+
if (typeof dispatch === "function") {
|
|
47232
|
+
this._value = await dispatch(this._value);
|
|
47233
|
+
}
|
|
47234
|
+
else {
|
|
47235
|
+
this._value = dispatch;
|
|
47236
|
+
}
|
|
47237
|
+
return this._value;
|
|
47238
|
+
});
|
|
47239
|
+
}
|
|
47240
|
+
/**
|
|
47241
|
+
* Read the current in-memory state value.
|
|
47242
|
+
* @returns Current state value
|
|
47243
|
+
*/
|
|
47244
|
+
async getState() {
|
|
47245
|
+
backtest.loggerService.debug(STATE_LOCAL_INSTANCE_METHOD_NAME_GET, {
|
|
47246
|
+
signalId: this.signalId,
|
|
47247
|
+
bucketName: this.bucketName,
|
|
47248
|
+
});
|
|
47249
|
+
return this._value;
|
|
47250
|
+
}
|
|
47251
|
+
/** Releases resources held by this instance. */
|
|
47252
|
+
async dispose() {
|
|
47253
|
+
backtest.loggerService.debug(STATE_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
47254
|
+
signalId: this.signalId,
|
|
47255
|
+
bucketName: this.bucketName,
|
|
47256
|
+
});
|
|
47257
|
+
}
|
|
47258
|
+
}
|
|
47259
|
+
/**
|
|
47260
|
+
* No-op state instance that discards all writes.
|
|
47261
|
+
* Used for disabling state in tests or dry-run scenarios.
|
|
47262
|
+
*
|
|
47263
|
+
* Useful when replaying historical candles without needing to accumulate
|
|
47264
|
+
* peakPercent/minutesOpen — the capitulation rule is simply never triggered.
|
|
47265
|
+
*/
|
|
47266
|
+
class StateDummyInstance {
|
|
47267
|
+
constructor(initialValue, signalId, bucketName) {
|
|
47268
|
+
this.initialValue = initialValue;
|
|
47269
|
+
this.signalId = signalId;
|
|
47270
|
+
this.bucketName = bucketName;
|
|
47271
|
+
/**
|
|
47272
|
+
* No-op initialization.
|
|
47273
|
+
* @returns Promise that resolves immediately
|
|
47274
|
+
*/
|
|
47275
|
+
this.waitForInit = singleshot(async (_initial) => {
|
|
47276
|
+
});
|
|
47277
|
+
}
|
|
47278
|
+
/**
|
|
47279
|
+
* No-op read - always returns initialValue.
|
|
47280
|
+
* @returns initialValue
|
|
47281
|
+
*/
|
|
47282
|
+
async getState() {
|
|
47283
|
+
return this.initialValue;
|
|
47284
|
+
}
|
|
47285
|
+
/**
|
|
47286
|
+
* No-op write - discards the value and returns initialValue.
|
|
47287
|
+
* @returns initialValue
|
|
47288
|
+
*/
|
|
47289
|
+
async setState(_dispatch) {
|
|
47290
|
+
return this.initialValue;
|
|
47291
|
+
}
|
|
47292
|
+
/** No-op. */
|
|
47293
|
+
async dispose() {
|
|
47294
|
+
}
|
|
47295
|
+
}
|
|
47296
|
+
/**
|
|
47297
|
+
* File-system backed state instance.
|
|
47298
|
+
* Data is persisted atomically to disk via PersistStateAdapter.
|
|
47299
|
+
* State is restored from disk on waitForInit.
|
|
47300
|
+
*
|
|
47301
|
+
* Features:
|
|
47302
|
+
* - Crash-safe atomic file writes
|
|
47303
|
+
* - Functional dispatch support
|
|
47304
|
+
* - Scoped per (signalId, bucketName) pair
|
|
47305
|
+
*
|
|
47306
|
+
* Use in live trading to survive process restarts mid-trade.
|
|
47307
|
+
* Preserves peakPercent and minutesOpen so the capitulation rule
|
|
47308
|
+
* (exit if peak < threshold after N minutes) continues correctly after a crash.
|
|
47309
|
+
*/
|
|
47310
|
+
class StatePersistInstance {
|
|
47311
|
+
constructor(initialValue, signalId, bucketName) {
|
|
47312
|
+
this.initialValue = initialValue;
|
|
47313
|
+
this.signalId = signalId;
|
|
47314
|
+
this.bucketName = bucketName;
|
|
47315
|
+
/**
|
|
47316
|
+
* Initialize persistence storage and restore state from disk.
|
|
47317
|
+
* @param initial - Whether this is the first initialization
|
|
47318
|
+
*/
|
|
47319
|
+
this.waitForInit = singleshot(async (initial) => {
|
|
47320
|
+
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_WAIT_FOR_INIT, {
|
|
47321
|
+
signalId: this.signalId,
|
|
47322
|
+
bucketName: this.bucketName,
|
|
47323
|
+
initial,
|
|
47324
|
+
});
|
|
47325
|
+
await PersistStateAdapter.waitForInit(this.signalId, this.bucketName, initial);
|
|
47326
|
+
const data = await PersistStateAdapter.readStateData(this.signalId, this.bucketName);
|
|
47327
|
+
if (data) {
|
|
47328
|
+
this._value = data.data;
|
|
47329
|
+
return;
|
|
47330
|
+
}
|
|
47331
|
+
this._value = this.initialValue;
|
|
47332
|
+
});
|
|
47333
|
+
/**
|
|
47334
|
+
* Update state and persist to disk atomically.
|
|
47335
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47336
|
+
* @returns Updated state value
|
|
47337
|
+
*/
|
|
47338
|
+
this.setState = queued(async (dispatch) => {
|
|
47339
|
+
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_SET, {
|
|
47340
|
+
signalId: this.signalId,
|
|
47341
|
+
bucketName: this.bucketName,
|
|
47342
|
+
});
|
|
47343
|
+
if (typeof dispatch === "function") {
|
|
47344
|
+
this._value = await dispatch(this._value);
|
|
47345
|
+
}
|
|
47346
|
+
else {
|
|
47347
|
+
this._value = dispatch;
|
|
47348
|
+
}
|
|
47349
|
+
const id = CREATE_KEY_FN$5(this.signalId, this.bucketName);
|
|
47350
|
+
await PersistStateAdapter.writeStateData({ id, data: this._value }, this.signalId, this.bucketName);
|
|
47351
|
+
return this._value;
|
|
47352
|
+
});
|
|
47353
|
+
}
|
|
47354
|
+
/**
|
|
47355
|
+
* Read the current persisted state value.
|
|
47356
|
+
* @returns Current state value
|
|
47357
|
+
*/
|
|
47358
|
+
async getState() {
|
|
47359
|
+
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_GET, {
|
|
47360
|
+
signalId: this.signalId,
|
|
47361
|
+
bucketName: this.bucketName,
|
|
47362
|
+
});
|
|
47363
|
+
return this._value;
|
|
47364
|
+
}
|
|
47365
|
+
/** Releases resources held by this instance. */
|
|
47366
|
+
async dispose() {
|
|
47367
|
+
backtest.loggerService.debug(STATE_LIVE_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
47368
|
+
signalId: this.signalId,
|
|
47369
|
+
bucketName: this.bucketName,
|
|
47370
|
+
});
|
|
47371
|
+
await PersistStateAdapter.dispose(this.signalId, this.bucketName);
|
|
47372
|
+
}
|
|
47373
|
+
}
|
|
47374
|
+
/**
|
|
47375
|
+
* Backtest state adapter with pluggable storage backend.
|
|
47376
|
+
*
|
|
47377
|
+
* Features:
|
|
47378
|
+
* - Adapter pattern for swappable state instance implementations
|
|
47379
|
+
* - Default backend: StateLocalInstance (in-memory, no disk persistence)
|
|
47380
|
+
* - Alternative backends: StatePersistInstance, StateDummyInstance
|
|
47381
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useStateAdapter()
|
|
47382
|
+
* - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from StateAdapter
|
|
47383
|
+
*
|
|
47384
|
+
* Primary use case — LLM-driven capitulation rule:
|
|
47385
|
+
* Profitable trades endure -0.5–2.5% drawdown and still reach peak 2–3%+.
|
|
47386
|
+
* SL trades never go positive (Feb25) or show peak < 0.15% (Feb08, Feb13).
|
|
47387
|
+
* Rule: if position open >= N minutes and peakPercent < threshold (e.g. 0.3%),
|
|
47388
|
+
* the LLM thesis was not confirmed by market — exit immediately.
|
|
47389
|
+
* State tracks `{ peakPercent, minutesOpen }` per signal across onActivePing ticks.
|
|
47390
|
+
*/
|
|
47391
|
+
class StateBacktestAdapter {
|
|
47392
|
+
constructor() {
|
|
47393
|
+
this.StateFactory = StateLocalInstance;
|
|
47394
|
+
this.getInstance = memoize(([signalId, bucketName]) => CREATE_KEY_FN$5(signalId, bucketName), (signalId, bucketName, initialValue) => Reflect.construct(this.StateFactory, [initialValue, signalId, bucketName]));
|
|
47395
|
+
/**
|
|
47396
|
+
* Disposes all memoized instances for the given signalId.
|
|
47397
|
+
* Called by StateAdapter when a signal is cancelled or closed.
|
|
47398
|
+
* @param signalId - Signal identifier to dispose
|
|
47399
|
+
*/
|
|
47400
|
+
this.disposeSignal = (signalId) => {
|
|
47401
|
+
const prefix = CREATE_KEY_FN$5(signalId, "");
|
|
47402
|
+
for (const key of this.getInstance.keys()) {
|
|
47403
|
+
if (key.startsWith(prefix)) {
|
|
47404
|
+
const instance = this.getInstance.get(key);
|
|
47405
|
+
instance && instance.dispose();
|
|
47406
|
+
this.getInstance.clear(key);
|
|
47407
|
+
}
|
|
47408
|
+
}
|
|
47409
|
+
};
|
|
47410
|
+
/**
|
|
47411
|
+
* Read the current state value for a signal.
|
|
47412
|
+
* @param dto.signalId - Signal identifier
|
|
47413
|
+
* @param dto.bucketName - Bucket name
|
|
47414
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47415
|
+
* @returns Current state value
|
|
47416
|
+
*/
|
|
47417
|
+
this.getState = async (dto) => {
|
|
47418
|
+
backtest.loggerService.debug(STATE_BACKTEST_ADAPTER_METHOD_NAME_GET, {
|
|
47419
|
+
signalId: dto.signalId,
|
|
47420
|
+
bucketName: dto.bucketName,
|
|
47421
|
+
});
|
|
47422
|
+
const key = CREATE_KEY_FN$5(dto.signalId, dto.bucketName);
|
|
47423
|
+
const isInitial = !this.getInstance.has(key);
|
|
47424
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
47425
|
+
await instance.waitForInit(isInitial);
|
|
47426
|
+
return await instance.getState();
|
|
47427
|
+
};
|
|
47428
|
+
/**
|
|
47429
|
+
* Update the state value for a signal.
|
|
47430
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47431
|
+
* @param dto.signalId - Signal identifier
|
|
47432
|
+
* @param dto.bucketName - Bucket name
|
|
47433
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47434
|
+
* @returns Updated state value
|
|
47435
|
+
*/
|
|
47436
|
+
this.setState = async (dispatch, dto) => {
|
|
47437
|
+
backtest.loggerService.debug(STATE_BACKTEST_ADAPTER_METHOD_NAME_SET, {
|
|
47438
|
+
signalId: dto.signalId,
|
|
47439
|
+
bucketName: dto.bucketName,
|
|
47440
|
+
});
|
|
47441
|
+
const key = CREATE_KEY_FN$5(dto.signalId, dto.bucketName);
|
|
47442
|
+
const isInitial = !this.getInstance.has(key);
|
|
47443
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
47444
|
+
await instance.waitForInit(isInitial);
|
|
47445
|
+
return await instance.setState(dispatch);
|
|
47446
|
+
};
|
|
47447
|
+
/**
|
|
47448
|
+
* Switches to in-memory adapter (default).
|
|
47449
|
+
* All data lives in process memory only.
|
|
47450
|
+
*/
|
|
47451
|
+
this.useLocal = () => {
|
|
47452
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
47453
|
+
this.StateFactory = StateLocalInstance;
|
|
47454
|
+
};
|
|
47455
|
+
/**
|
|
47456
|
+
* Switches to file-system backed adapter.
|
|
47457
|
+
* Data is persisted to disk via PersistStateAdapter.
|
|
47458
|
+
*/
|
|
47459
|
+
this.usePersist = () => {
|
|
47460
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
47461
|
+
this.StateFactory = StatePersistInstance;
|
|
47462
|
+
};
|
|
47463
|
+
/**
|
|
47464
|
+
* Switches to dummy adapter that discards all writes.
|
|
47465
|
+
*/
|
|
47466
|
+
this.useDummy = () => {
|
|
47467
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
47468
|
+
this.StateFactory = StateDummyInstance;
|
|
47469
|
+
};
|
|
47470
|
+
/**
|
|
47471
|
+
* Switches to a custom state adapter implementation.
|
|
47472
|
+
* @param Ctor - Constructor for the custom state instance
|
|
47473
|
+
*/
|
|
47474
|
+
this.useStateAdapter = (Ctor) => {
|
|
47475
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER);
|
|
47476
|
+
this.StateFactory = Ctor;
|
|
47477
|
+
};
|
|
47478
|
+
/**
|
|
47479
|
+
* Clears the memoized instance cache.
|
|
47480
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
47481
|
+
* so new instances are created with the updated base path.
|
|
47482
|
+
*/
|
|
47483
|
+
this.clear = () => {
|
|
47484
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
|
|
47485
|
+
this.getInstance.clear();
|
|
47486
|
+
};
|
|
47487
|
+
}
|
|
47488
|
+
}
|
|
47489
|
+
/**
|
|
47490
|
+
* Live trading state adapter with pluggable storage backend.
|
|
47491
|
+
*
|
|
47492
|
+
* Features:
|
|
47493
|
+
* - Adapter pattern for swappable state instance implementations
|
|
47494
|
+
* - Default backend: StatePersistInstance (file-system backed, survives restarts)
|
|
47495
|
+
* - Alternative backends: StateLocalInstance, StateDummyInstance
|
|
47496
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useStateAdapter()
|
|
47497
|
+
* - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from StateAdapter
|
|
47498
|
+
*
|
|
47499
|
+
* Primary use case — LLM-driven capitulation rule:
|
|
47500
|
+
* Profitable trades endure -0.5–2.5% drawdown and still reach peak 2–3%+.
|
|
47501
|
+
* SL trades never go positive (Feb25) or show peak < 0.15% (Feb08, Feb13).
|
|
47502
|
+
* Rule: if position open >= N minutes and peakPercent < threshold (e.g. 0.3%),
|
|
47503
|
+
* the LLM thesis was not confirmed by market — exit immediately.
|
|
47504
|
+
* State persists `{ peakPercent, minutesOpen }` per signal across process restarts.
|
|
47505
|
+
*/
|
|
47506
|
+
class StateLiveAdapter {
|
|
47507
|
+
constructor() {
|
|
47508
|
+
this.StateFactory = StatePersistInstance;
|
|
47509
|
+
this.getInstance = memoize(([signalId, bucketName]) => CREATE_KEY_FN$5(signalId, bucketName), (signalId, bucketName, initialValue) => Reflect.construct(this.StateFactory, [initialValue, signalId, bucketName]));
|
|
47510
|
+
/**
|
|
47511
|
+
* Disposes all memoized instances for the given signalId.
|
|
47512
|
+
* Called by StateAdapter when a signal is cancelled or closed.
|
|
47513
|
+
* @param signalId - Signal identifier to dispose
|
|
47514
|
+
*/
|
|
47515
|
+
this.disposeSignal = (signalId) => {
|
|
47516
|
+
const prefix = CREATE_KEY_FN$5(signalId, "");
|
|
47517
|
+
for (const key of this.getInstance.keys()) {
|
|
47518
|
+
if (key.startsWith(prefix)) {
|
|
47519
|
+
const instance = this.getInstance.get(key);
|
|
47520
|
+
instance && instance.dispose();
|
|
47521
|
+
this.getInstance.clear(key);
|
|
47522
|
+
}
|
|
47523
|
+
}
|
|
47524
|
+
};
|
|
47525
|
+
/**
|
|
47526
|
+
* Read the current state value for a signal.
|
|
47527
|
+
* @param dto.signalId - Signal identifier
|
|
47528
|
+
* @param dto.bucketName - Bucket name
|
|
47529
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47530
|
+
* @returns Current state value
|
|
47531
|
+
*/
|
|
47532
|
+
this.getState = async (dto) => {
|
|
47533
|
+
backtest.loggerService.debug(STATE_LIVE_ADAPTER_METHOD_NAME_GET, {
|
|
47534
|
+
signalId: dto.signalId,
|
|
47535
|
+
bucketName: dto.bucketName,
|
|
47536
|
+
});
|
|
47537
|
+
const key = CREATE_KEY_FN$5(dto.signalId, dto.bucketName);
|
|
47538
|
+
const isInitial = !this.getInstance.has(key);
|
|
47539
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
47540
|
+
await instance.waitForInit(isInitial);
|
|
47541
|
+
return await instance.getState();
|
|
47542
|
+
};
|
|
47543
|
+
/**
|
|
47544
|
+
* Update the state value for a signal.
|
|
47545
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47546
|
+
* @param dto.signalId - Signal identifier
|
|
47547
|
+
* @param dto.bucketName - Bucket name
|
|
47548
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47549
|
+
* @returns Updated state value
|
|
47550
|
+
*/
|
|
47551
|
+
this.setState = async (dispatch, dto) => {
|
|
47552
|
+
backtest.loggerService.debug(STATE_LIVE_ADAPTER_METHOD_NAME_SET, {
|
|
47553
|
+
signalId: dto.signalId,
|
|
47554
|
+
bucketName: dto.bucketName,
|
|
47555
|
+
});
|
|
47556
|
+
const key = CREATE_KEY_FN$5(dto.signalId, dto.bucketName);
|
|
47557
|
+
const isInitial = !this.getInstance.has(key);
|
|
47558
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
47559
|
+
await instance.waitForInit(isInitial);
|
|
47560
|
+
return await instance.setState(dispatch);
|
|
47561
|
+
};
|
|
47562
|
+
/**
|
|
47563
|
+
* Switches to in-memory adapter.
|
|
47564
|
+
* All data lives in process memory only.
|
|
47565
|
+
*/
|
|
47566
|
+
this.useLocal = () => {
|
|
47567
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
47568
|
+
this.StateFactory = StateLocalInstance;
|
|
47569
|
+
};
|
|
47570
|
+
/**
|
|
47571
|
+
* Switches to file-system backed adapter (default).
|
|
47572
|
+
* Data is persisted to disk via PersistStateAdapter.
|
|
47573
|
+
*/
|
|
47574
|
+
this.usePersist = () => {
|
|
47575
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
47576
|
+
this.StateFactory = StatePersistInstance;
|
|
47577
|
+
};
|
|
47578
|
+
/**
|
|
47579
|
+
* Switches to dummy adapter that discards all writes.
|
|
47580
|
+
*/
|
|
47581
|
+
this.useDummy = () => {
|
|
47582
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
47583
|
+
this.StateFactory = StateDummyInstance;
|
|
47584
|
+
};
|
|
47585
|
+
/**
|
|
47586
|
+
* Switches to a custom state adapter implementation.
|
|
47587
|
+
* @param Ctor - Constructor for the custom state instance
|
|
47588
|
+
*/
|
|
47589
|
+
this.useStateAdapter = (Ctor) => {
|
|
47590
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER);
|
|
47591
|
+
this.StateFactory = Ctor;
|
|
47592
|
+
};
|
|
47593
|
+
/**
|
|
47594
|
+
* Clears the memoized instance cache.
|
|
47595
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
47596
|
+
* so new instances are created with the updated base path.
|
|
47597
|
+
*/
|
|
47598
|
+
this.clear = () => {
|
|
47599
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_CLEAR);
|
|
47600
|
+
this.getInstance.clear();
|
|
47601
|
+
};
|
|
47602
|
+
}
|
|
47603
|
+
}
|
|
47604
|
+
/**
|
|
47605
|
+
* Main state adapter that manages both backtest and live state storage.
|
|
47606
|
+
*
|
|
47607
|
+
* Features:
|
|
47608
|
+
* - Subscribes to signal lifecycle events (cancelled/closed) to dispose stale instances
|
|
47609
|
+
* - Routes all operations to StateBacktest or StateLive based on dto.backtest
|
|
47610
|
+
* - Singleshot enable pattern prevents duplicate subscriptions
|
|
47611
|
+
* - Cleanup function for proper unsubscription
|
|
47612
|
+
*/
|
|
47613
|
+
class StateAdapter {
|
|
47614
|
+
constructor() {
|
|
47615
|
+
/**
|
|
47616
|
+
* Enables state storage by subscribing to signal lifecycle events.
|
|
47617
|
+
* Clears memoized instances in StateBacktest and StateLive when a signal
|
|
47618
|
+
* is cancelled or closed, preventing stale instances from accumulating.
|
|
47619
|
+
* Uses singleshot to ensure one-time subscription.
|
|
47620
|
+
*
|
|
47621
|
+
* @returns Cleanup function that unsubscribes from all emitters
|
|
47622
|
+
*/
|
|
47623
|
+
this.enable = singleshot(() => {
|
|
47624
|
+
backtest.loggerService.info(STATE_ADAPTER_METHOD_NAME_ENABLE);
|
|
47625
|
+
const unCancel = signalEmitter
|
|
47626
|
+
.filter(({ action }) => action === "cancelled")
|
|
47627
|
+
.connect(({ signal }) => {
|
|
47628
|
+
StateBacktest.disposeSignal(signal.id);
|
|
47629
|
+
StateLive.disposeSignal(signal.id);
|
|
47630
|
+
});
|
|
47631
|
+
const unClose = signalEmitter
|
|
47632
|
+
.filter(({ action }) => action === "closed")
|
|
47633
|
+
.connect(({ signal }) => {
|
|
47634
|
+
StateBacktest.disposeSignal(signal.id);
|
|
47635
|
+
StateLive.disposeSignal(signal.id);
|
|
47636
|
+
});
|
|
47637
|
+
return compose(() => unCancel(), () => unClose(), () => this.enable.clear());
|
|
47638
|
+
});
|
|
47639
|
+
/**
|
|
47640
|
+
* Disables state storage by unsubscribing from signal lifecycle events.
|
|
47641
|
+
* Safe to call multiple times.
|
|
47642
|
+
*/
|
|
47643
|
+
this.disable = () => {
|
|
47644
|
+
backtest.loggerService.info(STATE_ADAPTER_METHOD_NAME_DISABLE);
|
|
47645
|
+
if (this.enable.hasValue()) {
|
|
47646
|
+
const lastSubscription = this.enable();
|
|
47647
|
+
lastSubscription();
|
|
47648
|
+
}
|
|
47649
|
+
};
|
|
47650
|
+
/**
|
|
47651
|
+
* Read the current state value for a signal.
|
|
47652
|
+
* Routes to StateBacktest or StateLive based on dto.backtest.
|
|
47653
|
+
* @param dto.signalId - Signal identifier
|
|
47654
|
+
* @param dto.bucketName - Bucket name
|
|
47655
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47656
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47657
|
+
* @returns Current state value
|
|
47658
|
+
* @throws Error if adapter is not enabled
|
|
47659
|
+
*/
|
|
47660
|
+
this.getState = async (dto) => {
|
|
47661
|
+
if (!this.enable.hasValue()) {
|
|
47662
|
+
throw new Error("StateAdapter is not enabled. Call enable() first.");
|
|
47663
|
+
}
|
|
47664
|
+
backtest.loggerService.debug(STATE_ADAPTER_METHOD_NAME_GET, {
|
|
47665
|
+
signalId: dto.signalId,
|
|
47666
|
+
bucketName: dto.bucketName,
|
|
47667
|
+
backtest: dto.backtest,
|
|
47668
|
+
});
|
|
47669
|
+
if (dto.backtest) {
|
|
47670
|
+
return await StateBacktest.getState(dto);
|
|
47671
|
+
}
|
|
47672
|
+
return await StateLive.getState(dto);
|
|
47673
|
+
};
|
|
47674
|
+
/**
|
|
47675
|
+
* Update the state value for a signal.
|
|
47676
|
+
* Routes to StateBacktest or StateLive based on dto.backtest.
|
|
47677
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47678
|
+
* @param dto.signalId - Signal identifier
|
|
47679
|
+
* @param dto.bucketName - Bucket name
|
|
47680
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47681
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47682
|
+
* @returns Updated state value
|
|
47683
|
+
* @throws Error if adapter is not enabled
|
|
47684
|
+
*/
|
|
47685
|
+
this.setState = async (dispatch, dto) => {
|
|
47686
|
+
if (!this.enable.hasValue()) {
|
|
47687
|
+
throw new Error("StateAdapter is not enabled. Call enable() first.");
|
|
47688
|
+
}
|
|
47689
|
+
backtest.loggerService.debug(STATE_ADAPTER_METHOD_NAME_SET, {
|
|
47690
|
+
signalId: dto.signalId,
|
|
47691
|
+
bucketName: dto.bucketName,
|
|
47692
|
+
backtest: dto.backtest,
|
|
47693
|
+
});
|
|
47694
|
+
if (dto.backtest) {
|
|
47695
|
+
return await StateBacktest.setState(dispatch, dto);
|
|
47696
|
+
}
|
|
47697
|
+
return await StateLive.setState(dispatch, dto);
|
|
47698
|
+
};
|
|
47699
|
+
}
|
|
47700
|
+
}
|
|
47701
|
+
/**
|
|
47702
|
+
* Global singleton instance of StateAdapter.
|
|
47703
|
+
* Provides unified state management for backtest and live trading.
|
|
47704
|
+
*/
|
|
47705
|
+
const State = new StateAdapter();
|
|
47706
|
+
/**
|
|
47707
|
+
* Global singleton instance of StateLiveAdapter.
|
|
47708
|
+
* Provides live trading state storage with pluggable backends.
|
|
47709
|
+
*/
|
|
47710
|
+
const StateLive = new StateLiveAdapter();
|
|
47711
|
+
/**
|
|
47712
|
+
* Global singleton instance of StateBacktestAdapter.
|
|
47713
|
+
* Provides backtest state storage with pluggable backends.
|
|
47714
|
+
*/
|
|
47715
|
+
const StateBacktest = new StateBacktestAdapter();
|
|
47716
|
+
|
|
47717
|
+
const GET_SIGNAL_STATE_METHOD_NAME = "signal.getSignalState";
|
|
47718
|
+
const SET_SIGNAL_STATE_METHOD_NAME = "signal.setSignalState";
|
|
47043
47719
|
const GET_LATEST_SIGNAL_METHOD_NAME = "signal.getLatestSignal";
|
|
47044
47720
|
const GET_MINUTES_SINCE_LATEST_SIGNAL_CREATED_METHOD_NAME = "signal.getMinutesSinceLatestSignalCreated";
|
|
47045
47721
|
/**
|
|
@@ -47115,6 +47791,243 @@ async function getMinutesSinceLatestSignalCreated(symbol) {
|
|
|
47115
47791
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47116
47792
|
return await Recent.getMinutesSinceLatestSignalCreated(symbol, { exchangeName, frameName, strategyName });
|
|
47117
47793
|
}
|
|
47794
|
+
/**
|
|
47795
|
+
* Reads the state value scoped to the current active signal.
|
|
47796
|
+
*
|
|
47797
|
+
* Resolves the active pending signal automatically from execution context.
|
|
47798
|
+
* If no pending signal exists, logs a warning and returns the initialValue.
|
|
47799
|
+
*
|
|
47800
|
+
* Automatically detects backtest/live mode from execution context.
|
|
47801
|
+
*
|
|
47802
|
+
* Intended for LLM-driven capitulation strategies that accumulate per-trade
|
|
47803
|
+
* metrics (e.g. peakPercent, minutesOpen) across onActivePing ticks.
|
|
47804
|
+
* Profitable trades endure -0.5–2.5% drawdown and reach peak 2–3%+.
|
|
47805
|
+
* SL trades show peak < 0.15% (Feb08, Feb13) or never go positive (Feb25).
|
|
47806
|
+
* Rule: if minutesOpen >= N and peakPercent < threshold (e.g. 0.3%) — exit.
|
|
47807
|
+
*
|
|
47808
|
+
* @param dto.bucketName - State bucket name
|
|
47809
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47810
|
+
* @returns Promise resolving to current state value, or initialValue if no signal
|
|
47811
|
+
*
|
|
47812
|
+
* @deprecated Better use `createSignalState().getState` with codestyle native syntax
|
|
47813
|
+
*
|
|
47814
|
+
* @example
|
|
47815
|
+
* ```typescript
|
|
47816
|
+
* import { getSignalState } from "backtest-kit";
|
|
47817
|
+
*
|
|
47818
|
+
* const { peakPercent, minutesOpen } = await getSignalState({
|
|
47819
|
+
* bucketName: "trade",
|
|
47820
|
+
* initialValue: { peakPercent: 0, minutesOpen: 0 },
|
|
47821
|
+
* });
|
|
47822
|
+
* if (minutesOpen >= 15 && peakPercent < 0.3) {
|
|
47823
|
+
* await commitMarketClose(symbol); // capitulate — LLM thesis not confirmed
|
|
47824
|
+
* }
|
|
47825
|
+
* ```
|
|
47826
|
+
*/
|
|
47827
|
+
async function getSignalState(dto) {
|
|
47828
|
+
const { bucketName, initialValue } = dto;
|
|
47829
|
+
backtest.loggerService.info(GET_SIGNAL_STATE_METHOD_NAME, { bucketName });
|
|
47830
|
+
if (!ExecutionContextService.hasContext()) {
|
|
47831
|
+
throw new Error("getSignalState requires an execution context");
|
|
47832
|
+
}
|
|
47833
|
+
if (!MethodContextService.hasContext()) {
|
|
47834
|
+
throw new Error("getSignalState requires a method context");
|
|
47835
|
+
}
|
|
47836
|
+
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47837
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47838
|
+
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47839
|
+
let signal;
|
|
47840
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47841
|
+
return await State.getState({
|
|
47842
|
+
signalId: signal.id,
|
|
47843
|
+
bucketName,
|
|
47844
|
+
initialValue,
|
|
47845
|
+
backtest: isBacktest,
|
|
47846
|
+
});
|
|
47847
|
+
}
|
|
47848
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47849
|
+
return await State.getState({
|
|
47850
|
+
signalId: signal.id,
|
|
47851
|
+
bucketName,
|
|
47852
|
+
initialValue,
|
|
47853
|
+
backtest: isBacktest,
|
|
47854
|
+
});
|
|
47855
|
+
}
|
|
47856
|
+
throw new Error(`getSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
47857
|
+
}
|
|
47858
|
+
/**
|
|
47859
|
+
* Updates the state value scoped to the current active signal.
|
|
47860
|
+
*
|
|
47861
|
+
* Resolves the active pending signal automatically from execution context.
|
|
47862
|
+
* If no pending signal exists, logs a warning and returns without writing.
|
|
47863
|
+
*
|
|
47864
|
+
* Automatically detects backtest/live mode from execution context.
|
|
47865
|
+
*
|
|
47866
|
+
* Intended for LLM-driven capitulation strategies that accumulate per-trade
|
|
47867
|
+
* metrics (e.g. peakPercent, minutesOpen) across onActivePing ticks.
|
|
47868
|
+
* Profitable trades endure -0.5–2.5% drawdown and reach peak 2–3%+.
|
|
47869
|
+
* SL trades show peak < 0.15% (Feb08, Feb13) or never go positive (Feb25).
|
|
47870
|
+
* Rule: if minutesOpen >= N and peakPercent < threshold (e.g. 0.3%) — exit.
|
|
47871
|
+
*
|
|
47872
|
+
* @param dto.bucketName - State bucket name
|
|
47873
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47874
|
+
* @param dto.dispatch - New value or updater function receiving current value
|
|
47875
|
+
* @returns Promise resolving to updated state value, or initialValue if no signal
|
|
47876
|
+
*
|
|
47877
|
+
* @deprecated Better use `createSignalState().setState` with codestyle native syntax
|
|
47878
|
+
*
|
|
47879
|
+
* @example
|
|
47880
|
+
* ```typescript
|
|
47881
|
+
* import { setSignalState } from "backtest-kit";
|
|
47882
|
+
*
|
|
47883
|
+
* await setSignalState(
|
|
47884
|
+
* dispatch: (s) => ({
|
|
47885
|
+
* peakPercent: Math.max(s.peakPercent, currentUnrealisedPercent),
|
|
47886
|
+
* minutesOpen: s.minutesOpen + 1,
|
|
47887
|
+
* }),
|
|
47888
|
+
* {
|
|
47889
|
+
* bucketName: "trade",
|
|
47890
|
+
* initialValue: { peakPercent: 0, minutesOpen: 0 },
|
|
47891
|
+
* }
|
|
47892
|
+
* );
|
|
47893
|
+
* ```
|
|
47894
|
+
*/
|
|
47895
|
+
async function setSignalState(dispatch, dto) {
|
|
47896
|
+
const { bucketName, initialValue } = dto;
|
|
47897
|
+
backtest.loggerService.info(SET_SIGNAL_STATE_METHOD_NAME, { bucketName });
|
|
47898
|
+
if (!ExecutionContextService.hasContext()) {
|
|
47899
|
+
throw new Error("setSignalState requires an execution context");
|
|
47900
|
+
}
|
|
47901
|
+
if (!MethodContextService.hasContext()) {
|
|
47902
|
+
throw new Error("setSignalState requires a method context");
|
|
47903
|
+
}
|
|
47904
|
+
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47905
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47906
|
+
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47907
|
+
let signal;
|
|
47908
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47909
|
+
return await State.setState(dispatch, {
|
|
47910
|
+
signalId: signal.id,
|
|
47911
|
+
bucketName,
|
|
47912
|
+
initialValue,
|
|
47913
|
+
backtest: isBacktest,
|
|
47914
|
+
});
|
|
47915
|
+
}
|
|
47916
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47917
|
+
return await State.setState(dispatch, {
|
|
47918
|
+
signalId: signal.id,
|
|
47919
|
+
bucketName,
|
|
47920
|
+
initialValue,
|
|
47921
|
+
backtest: isBacktest,
|
|
47922
|
+
});
|
|
47923
|
+
}
|
|
47924
|
+
throw new Error(`setSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
47925
|
+
}
|
|
47926
|
+
|
|
47927
|
+
const CREATE_SIGNAL_STATE_METHOD_NAME = "state.createSignalState";
|
|
47928
|
+
const CREATE_SET_STATE_FN = (params) => async (dispatch) => {
|
|
47929
|
+
if (!ExecutionContextService.hasContext()) {
|
|
47930
|
+
throw new Error("createSignalState requires an execution context");
|
|
47931
|
+
}
|
|
47932
|
+
if (!MethodContextService.hasContext()) {
|
|
47933
|
+
throw new Error("createSignalState requires a method context");
|
|
47934
|
+
}
|
|
47935
|
+
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47936
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47937
|
+
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47938
|
+
let signal;
|
|
47939
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47940
|
+
return await State.setState(dispatch, {
|
|
47941
|
+
backtest: isBacktest,
|
|
47942
|
+
bucketName: params.bucketName,
|
|
47943
|
+
initialValue: params.initialValue,
|
|
47944
|
+
signalId: signal.id,
|
|
47945
|
+
});
|
|
47946
|
+
}
|
|
47947
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47948
|
+
return await State.setState(dispatch, {
|
|
47949
|
+
backtest: isBacktest,
|
|
47950
|
+
bucketName: params.bucketName,
|
|
47951
|
+
initialValue: params.initialValue,
|
|
47952
|
+
signalId: signal.id,
|
|
47953
|
+
});
|
|
47954
|
+
}
|
|
47955
|
+
throw new Error(`createSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${params.bucketName}`);
|
|
47956
|
+
};
|
|
47957
|
+
const CREATE_GET_STATE_FN = (params) => async () => {
|
|
47958
|
+
if (!ExecutionContextService.hasContext()) {
|
|
47959
|
+
throw new Error("createSignalState requires an execution context");
|
|
47960
|
+
}
|
|
47961
|
+
if (!MethodContextService.hasContext()) {
|
|
47962
|
+
throw new Error("createSignalState requires a method context");
|
|
47963
|
+
}
|
|
47964
|
+
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47965
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47966
|
+
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47967
|
+
let signal;
|
|
47968
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47969
|
+
return await State.getState({
|
|
47970
|
+
backtest: isBacktest,
|
|
47971
|
+
bucketName: params.bucketName,
|
|
47972
|
+
initialValue: params.initialValue,
|
|
47973
|
+
signalId: signal.id,
|
|
47974
|
+
});
|
|
47975
|
+
}
|
|
47976
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47977
|
+
return await State.getState({
|
|
47978
|
+
backtest: isBacktest,
|
|
47979
|
+
bucketName: params.bucketName,
|
|
47980
|
+
initialValue: params.initialValue,
|
|
47981
|
+
signalId: signal.id,
|
|
47982
|
+
});
|
|
47983
|
+
}
|
|
47984
|
+
throw new Error(`createSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${params.bucketName}`);
|
|
47985
|
+
};
|
|
47986
|
+
/**
|
|
47987
|
+
* Creates a bound [getState, setState] tuple scoped to a bucket and initial value.
|
|
47988
|
+
*
|
|
47989
|
+
* Both returned functions resolve the active pending or scheduled signal and the
|
|
47990
|
+
* backtest/live flag automatically from execution context — no signalId argument required.
|
|
47991
|
+
*
|
|
47992
|
+
* Automatically detects backtest/live mode from execution context.
|
|
47993
|
+
*
|
|
47994
|
+
* Intended for LLM-driven capitulation strategies that accumulate per-trade
|
|
47995
|
+
* metrics (e.g. peakPercent, minutesOpen) across onActivePing ticks.
|
|
47996
|
+
* Profitable trades endure -0.5–2.5% drawdown and reach peak 2–3%+.
|
|
47997
|
+
* SL trades show peak < 0.15% (Feb08, Feb13) or never go positive (Feb25).
|
|
47998
|
+
* Rule: if minutesOpen >= N and peakPercent < threshold (e.g. 0.3%) — exit.
|
|
47999
|
+
*
|
|
48000
|
+
* @param params.bucketName - Logical namespace for grouping state buckets within a signal
|
|
48001
|
+
* @param params.initialValue - Default value when no persisted state exists
|
|
48002
|
+
* @returns Tuple [getState, setState] bound to the bucket and initial value
|
|
48003
|
+
*
|
|
48004
|
+
* @example
|
|
48005
|
+
* ```typescript
|
|
48006
|
+
* import { createSignalState } from "backtest-kit";
|
|
48007
|
+
*
|
|
48008
|
+
* const [getTradeState, setTradeState] = createSignalState({
|
|
48009
|
+
* bucketName: "trade",
|
|
48010
|
+
* initialValue: { peakPercent: 0, minutesOpen: 0 },
|
|
48011
|
+
* });
|
|
48012
|
+
*
|
|
48013
|
+
* // in onActivePing:
|
|
48014
|
+
* await setTradeState((s) => ({
|
|
48015
|
+
* peakPercent: Math.max(s.peakPercent, currentUnrealisedPercent),
|
|
48016
|
+
* minutesOpen: s.minutesOpen + 1,
|
|
48017
|
+
* }));
|
|
48018
|
+
* const { peakPercent, minutesOpen } = await getTradeState();
|
|
48019
|
+
* if (minutesOpen >= 15 && peakPercent < 0.3) await commitMarketClose(symbol);
|
|
48020
|
+
* ```
|
|
48021
|
+
*/
|
|
48022
|
+
function createSignalState(params) {
|
|
48023
|
+
backtest.loggerService.info(CREATE_SIGNAL_STATE_METHOD_NAME, {
|
|
48024
|
+
bucketName: params.bucketName,
|
|
48025
|
+
});
|
|
48026
|
+
return [
|
|
48027
|
+
CREATE_GET_STATE_FN(params),
|
|
48028
|
+
CREATE_SET_STATE_FN(params),
|
|
48029
|
+
];
|
|
48030
|
+
}
|
|
47118
48031
|
|
|
47119
48032
|
const DEFAULT_BM25_K1 = 1.5;
|
|
47120
48033
|
const DEFAULT_BM25_B = 0.75;
|
|
@@ -47201,7 +48114,7 @@ const createSearchIndex = () => {
|
|
|
47201
48114
|
return { upsert, remove, list, search, read };
|
|
47202
48115
|
};
|
|
47203
48116
|
|
|
47204
|
-
const CREATE_KEY_FN$4 = (signalId, bucketName) => `${signalId}
|
|
48117
|
+
const CREATE_KEY_FN$4 = (signalId, bucketName) => `${signalId}_${bucketName}`;
|
|
47205
48118
|
const LIST_MEMORY_FN = ({ id, content }) => ({
|
|
47206
48119
|
memoryId: id,
|
|
47207
48120
|
content: content,
|
|
@@ -47222,18 +48135,35 @@ const MEMORY_PERSIST_INSTANCE_METHOD_NAME_READ = "MemoryPersistInstance.readMemo
|
|
|
47222
48135
|
const MEMORY_PERSIST_INSTANCE_METHOD_NAME_SEARCH = "MemoryPersistInstance.searchMemory";
|
|
47223
48136
|
const MEMORY_PERSIST_INSTANCE_METHOD_NAME_LIST = "MemoryPersistInstance.listMemory";
|
|
47224
48137
|
const MEMORY_PERSIST_INSTANCE_METHOD_NAME_REMOVE = "MemoryPersistInstance.removeMemory";
|
|
48138
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE = "MemoryBacktestAdapter.dispose";
|
|
48139
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_WRITE = "MemoryBacktestAdapter.writeMemory";
|
|
48140
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_SEARCH = "MemoryBacktestAdapter.searchMemory";
|
|
48141
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_LIST = "MemoryBacktestAdapter.listMemory";
|
|
48142
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_REMOVE = "MemoryBacktestAdapter.removeMemory";
|
|
48143
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_READ = "MemoryBacktestAdapter.readMemory";
|
|
48144
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL = "MemoryBacktestAdapter.useLocal";
|
|
48145
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST = "MemoryBacktestAdapter.usePersist";
|
|
48146
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY = "MemoryBacktestAdapter.useDummy";
|
|
48147
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER = "MemoryBacktestAdapter.useMemoryAdapter";
|
|
48148
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_CLEAR = "MemoryBacktestAdapter.clear";
|
|
48149
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_DISPOSE = "MemoryLiveAdapter.dispose";
|
|
48150
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_WRITE = "MemoryLiveAdapter.writeMemory";
|
|
48151
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_SEARCH = "MemoryLiveAdapter.searchMemory";
|
|
48152
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_LIST = "MemoryLiveAdapter.listMemory";
|
|
48153
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_REMOVE = "MemoryLiveAdapter.removeMemory";
|
|
48154
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_READ = "MemoryLiveAdapter.readMemory";
|
|
48155
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL = "MemoryLiveAdapter.useLocal";
|
|
48156
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST = "MemoryLiveAdapter.usePersist";
|
|
48157
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY = "MemoryLiveAdapter.useDummy";
|
|
48158
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER = "MemoryLiveAdapter.useMemoryAdapter";
|
|
48159
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_CLEAR = "MemoryLiveAdapter.clear";
|
|
47225
48160
|
const MEMORY_ADAPTER_METHOD_NAME_ENABLE = "MemoryAdapter.enable";
|
|
47226
48161
|
const MEMORY_ADAPTER_METHOD_NAME_DISABLE = "MemoryAdapter.disable";
|
|
47227
|
-
const MEMORY_ADAPTER_METHOD_NAME_DISPOSE = "MemoryAdapter.dispose";
|
|
47228
48162
|
const MEMORY_ADAPTER_METHOD_NAME_WRITE = "MemoryAdapter.writeMemory";
|
|
47229
48163
|
const MEMORY_ADAPTER_METHOD_NAME_SEARCH = "MemoryAdapter.searchMemory";
|
|
47230
48164
|
const MEMORY_ADAPTER_METHOD_NAME_LIST = "MemoryAdapter.listMemory";
|
|
47231
48165
|
const MEMORY_ADAPTER_METHOD_NAME_REMOVE = "MemoryAdapter.removeMemory";
|
|
47232
48166
|
const MEMORY_ADAPTER_METHOD_NAME_READ = "MemoryAdapter.readMemory";
|
|
47233
|
-
const MEMORY_ADAPTER_METHOD_NAME_USE_LOCAL = "MemoryAdapter.useLocal";
|
|
47234
|
-
const MEMORY_ADAPTER_METHOD_NAME_USE_PERSIST = "MemoryAdapter.usePersist";
|
|
47235
|
-
const MEMORY_ADAPTER_METHOD_NAME_USE_DUMMY = "MemoryAdapter.useDummy";
|
|
47236
|
-
const MEMORY_ADAPTER_METHOD_NAME_CLEAR = "MemoryAdapter.clear";
|
|
47237
48167
|
/**
|
|
47238
48168
|
* In-memory BM25 search index backed instance.
|
|
47239
48169
|
* All data lives in the process memory only - no disk persistence.
|
|
@@ -47258,7 +48188,7 @@ class MemoryLocalInstance {
|
|
|
47258
48188
|
* Write a value into the BM25 index.
|
|
47259
48189
|
* @param memoryId - Unique entry identifier
|
|
47260
48190
|
* @param value - Value to store and index
|
|
47261
|
-
* @param
|
|
48191
|
+
* @param description - BM25 index string
|
|
47262
48192
|
*/
|
|
47263
48193
|
async writeMemory(memoryId, value, description) {
|
|
47264
48194
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_WRITE, {
|
|
@@ -47329,7 +48259,7 @@ class MemoryLocalInstance {
|
|
|
47329
48259
|
}
|
|
47330
48260
|
/** Releases resources held by this instance. */
|
|
47331
48261
|
dispose() {
|
|
47332
|
-
backtest.loggerService.debug(
|
|
48262
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
47333
48263
|
signalId: this.signalId,
|
|
47334
48264
|
bucketName: this.bucketName,
|
|
47335
48265
|
});
|
|
@@ -47378,7 +48308,7 @@ class MemoryPersistInstance {
|
|
|
47378
48308
|
* Write a value to disk and update the BM25 index.
|
|
47379
48309
|
* @param memoryId - Unique entry identifier
|
|
47380
48310
|
* @param value - Value to persist and index
|
|
47381
|
-
* @param index -
|
|
48311
|
+
* @param index - BM25 index string; defaults to JSON.stringify(value)
|
|
47382
48312
|
*/
|
|
47383
48313
|
async writeMemory(memoryId, value, index = JSON.stringify(value)) {
|
|
47384
48314
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_WRITE, {
|
|
@@ -47452,7 +48382,7 @@ class MemoryPersistInstance {
|
|
|
47452
48382
|
}
|
|
47453
48383
|
/** Releases resources held by this instance. */
|
|
47454
48384
|
dispose() {
|
|
47455
|
-
backtest.loggerService.debug(
|
|
48385
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
47456
48386
|
signalId: this.signalId,
|
|
47457
48387
|
bucketName: this.bucketName,
|
|
47458
48388
|
});
|
|
@@ -47512,48 +48442,377 @@ class MemoryDummyInstance {
|
|
|
47512
48442
|
}
|
|
47513
48443
|
}
|
|
47514
48444
|
/**
|
|
47515
|
-
*
|
|
47516
|
-
* Manages lazy initialization and instance lifecycle.
|
|
48445
|
+
* Backtest memory adapter with pluggable storage backend.
|
|
47517
48446
|
*
|
|
47518
48447
|
* Features:
|
|
47519
|
-
* -
|
|
47520
|
-
* -
|
|
47521
|
-
* -
|
|
48448
|
+
* - Adapter pattern for swappable memory instance implementations
|
|
48449
|
+
* - Default backend: MemoryLocalInstance (in-memory BM25, no disk persistence)
|
|
48450
|
+
* - Alternative backends: MemoryPersistInstance, MemoryDummyInstance
|
|
48451
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useMemoryAdapter()
|
|
48452
|
+
* - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from MemoryAdapter
|
|
48453
|
+
*
|
|
48454
|
+
* Use this adapter for backtest memory storage.
|
|
47522
48455
|
*/
|
|
47523
|
-
class
|
|
48456
|
+
class MemoryBacktestAdapter {
|
|
48457
|
+
constructor() {
|
|
48458
|
+
this.MemoryFactory = MemoryLocalInstance;
|
|
48459
|
+
this.getInstance = memoize(([signalId, bucketName]) => CREATE_KEY_FN$4(signalId, bucketName), (signalId, bucketName) => Reflect.construct(this.MemoryFactory, [signalId, bucketName]));
|
|
48460
|
+
/**
|
|
48461
|
+
* Disposes all memoized instances for the given signalId.
|
|
48462
|
+
* Called by MemoryAdapter when a signal is cancelled or closed.
|
|
48463
|
+
* @param signalId - Signal identifier to dispose
|
|
48464
|
+
*/
|
|
48465
|
+
this.disposeSignal = (signalId) => {
|
|
48466
|
+
const prefix = CREATE_KEY_FN$4(signalId, "");
|
|
48467
|
+
for (const key of this.getInstance.keys()) {
|
|
48468
|
+
if (key.startsWith(prefix)) {
|
|
48469
|
+
const instance = this.getInstance.get(key);
|
|
48470
|
+
instance && instance.dispose();
|
|
48471
|
+
this.getInstance.clear(key);
|
|
48472
|
+
}
|
|
48473
|
+
}
|
|
48474
|
+
};
|
|
48475
|
+
/**
|
|
48476
|
+
* Write a value to memory.
|
|
48477
|
+
* @param dto.memoryId - Unique entry identifier
|
|
48478
|
+
* @param dto.value - Value to store
|
|
48479
|
+
* @param dto.signalId - Signal identifier
|
|
48480
|
+
* @param dto.bucketName - Bucket name
|
|
48481
|
+
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
48482
|
+
*/
|
|
48483
|
+
this.writeMemory = async (dto) => {
|
|
48484
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_WRITE, {
|
|
48485
|
+
signalId: dto.signalId,
|
|
48486
|
+
bucketName: dto.bucketName,
|
|
48487
|
+
memoryId: dto.memoryId,
|
|
48488
|
+
});
|
|
48489
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48490
|
+
const isInitial = !this.getInstance.has(key);
|
|
48491
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48492
|
+
await instance.waitForInit(isInitial);
|
|
48493
|
+
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
48494
|
+
};
|
|
48495
|
+
/**
|
|
48496
|
+
* Search memory using BM25 full-text scoring.
|
|
48497
|
+
* @param dto.query - Search query string
|
|
48498
|
+
* @param dto.signalId - Signal identifier
|
|
48499
|
+
* @param dto.bucketName - Bucket name
|
|
48500
|
+
* @returns Matching entries sorted by relevance score
|
|
48501
|
+
*/
|
|
48502
|
+
this.searchMemory = async (dto) => {
|
|
48503
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_SEARCH, {
|
|
48504
|
+
signalId: dto.signalId,
|
|
48505
|
+
bucketName: dto.bucketName,
|
|
48506
|
+
query: dto.query,
|
|
48507
|
+
});
|
|
48508
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48509
|
+
const isInitial = !this.getInstance.has(key);
|
|
48510
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48511
|
+
await instance.waitForInit(isInitial);
|
|
48512
|
+
return await instance.searchMemory(dto.query, dto.settings);
|
|
48513
|
+
};
|
|
48514
|
+
/**
|
|
48515
|
+
* List all entries in memory.
|
|
48516
|
+
* @param dto.signalId - Signal identifier
|
|
48517
|
+
* @param dto.bucketName - Bucket name
|
|
48518
|
+
* @returns Array of all stored entries
|
|
48519
|
+
*/
|
|
48520
|
+
this.listMemory = async (dto) => {
|
|
48521
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_LIST, {
|
|
48522
|
+
signalId: dto.signalId,
|
|
48523
|
+
bucketName: dto.bucketName,
|
|
48524
|
+
});
|
|
48525
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48526
|
+
const isInitial = !this.getInstance.has(key);
|
|
48527
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48528
|
+
await instance.waitForInit(isInitial);
|
|
48529
|
+
return await instance.listMemory();
|
|
48530
|
+
};
|
|
48531
|
+
/**
|
|
48532
|
+
* Remove an entry from memory.
|
|
48533
|
+
* @param dto.memoryId - Unique entry identifier
|
|
48534
|
+
* @param dto.signalId - Signal identifier
|
|
48535
|
+
* @param dto.bucketName - Bucket name
|
|
48536
|
+
*/
|
|
48537
|
+
this.removeMemory = async (dto) => {
|
|
48538
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_REMOVE, {
|
|
48539
|
+
signalId: dto.signalId,
|
|
48540
|
+
bucketName: dto.bucketName,
|
|
48541
|
+
memoryId: dto.memoryId,
|
|
48542
|
+
});
|
|
48543
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48544
|
+
const isInitial = !this.getInstance.has(key);
|
|
48545
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48546
|
+
await instance.waitForInit(isInitial);
|
|
48547
|
+
return await instance.removeMemory(dto.memoryId);
|
|
48548
|
+
};
|
|
48549
|
+
/**
|
|
48550
|
+
* Read a single entry from memory.
|
|
48551
|
+
* @param dto.memoryId - Unique entry identifier
|
|
48552
|
+
* @param dto.signalId - Signal identifier
|
|
48553
|
+
* @param dto.bucketName - Bucket name
|
|
48554
|
+
* @returns Entry value
|
|
48555
|
+
* @throws Error if entry not found
|
|
48556
|
+
*/
|
|
48557
|
+
this.readMemory = async (dto) => {
|
|
48558
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_READ, {
|
|
48559
|
+
signalId: dto.signalId,
|
|
48560
|
+
bucketName: dto.bucketName,
|
|
48561
|
+
memoryId: dto.memoryId,
|
|
48562
|
+
});
|
|
48563
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48564
|
+
const isInitial = !this.getInstance.has(key);
|
|
48565
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48566
|
+
await instance.waitForInit(isInitial);
|
|
48567
|
+
return await instance.readMemory(dto.memoryId);
|
|
48568
|
+
};
|
|
48569
|
+
/**
|
|
48570
|
+
* Switches to in-memory BM25 adapter (default).
|
|
48571
|
+
* All data lives in process memory only.
|
|
48572
|
+
*/
|
|
48573
|
+
this.useLocal = () => {
|
|
48574
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
48575
|
+
this.MemoryFactory = MemoryLocalInstance;
|
|
48576
|
+
};
|
|
48577
|
+
/**
|
|
48578
|
+
* Switches to file-system backed adapter.
|
|
48579
|
+
* Data is persisted to ./dump/memory/<signalId>/<bucketName>/.
|
|
48580
|
+
*/
|
|
48581
|
+
this.usePersist = () => {
|
|
48582
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
48583
|
+
this.MemoryFactory = MemoryPersistInstance;
|
|
48584
|
+
};
|
|
48585
|
+
/**
|
|
48586
|
+
* Switches to dummy adapter that discards all writes.
|
|
48587
|
+
*/
|
|
48588
|
+
this.useDummy = () => {
|
|
48589
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
48590
|
+
this.MemoryFactory = MemoryDummyInstance;
|
|
48591
|
+
};
|
|
48592
|
+
/**
|
|
48593
|
+
* Switches to a custom memory adapter implementation.
|
|
48594
|
+
* @param Ctor - Constructor for the custom memory instance
|
|
48595
|
+
*/
|
|
48596
|
+
this.useMemoryAdapter = (Ctor) => {
|
|
48597
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER);
|
|
48598
|
+
this.MemoryFactory = Ctor;
|
|
48599
|
+
};
|
|
48600
|
+
/**
|
|
48601
|
+
* Clears the memoized instance cache.
|
|
48602
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
48603
|
+
* so new instances are created with the updated base path.
|
|
48604
|
+
*/
|
|
48605
|
+
this.clear = () => {
|
|
48606
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
|
|
48607
|
+
this.getInstance.clear();
|
|
48608
|
+
};
|
|
48609
|
+
}
|
|
48610
|
+
}
|
|
48611
|
+
/**
|
|
48612
|
+
* Live trading memory adapter with pluggable storage backend.
|
|
48613
|
+
*
|
|
48614
|
+
* Features:
|
|
48615
|
+
* - Adapter pattern for swappable memory instance implementations
|
|
48616
|
+
* - Default backend: MemoryPersistInstance (file-system backed, survives restarts)
|
|
48617
|
+
* - Alternative backends: MemoryLocalInstance, MemoryDummyInstance
|
|
48618
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useMemoryAdapter()
|
|
48619
|
+
* - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from MemoryAdapter
|
|
48620
|
+
*
|
|
48621
|
+
* Use this adapter for live trading memory storage.
|
|
48622
|
+
*/
|
|
48623
|
+
class MemoryLiveAdapter {
|
|
47524
48624
|
constructor() {
|
|
47525
48625
|
this.MemoryFactory = MemoryPersistInstance;
|
|
47526
48626
|
this.getInstance = memoize(([signalId, bucketName]) => CREATE_KEY_FN$4(signalId, bucketName), (signalId, bucketName) => Reflect.construct(this.MemoryFactory, [signalId, bucketName]));
|
|
47527
48627
|
/**
|
|
47528
|
-
*
|
|
47529
|
-
*
|
|
47530
|
-
*
|
|
47531
|
-
|
|
47532
|
-
|
|
48628
|
+
* Disposes all memoized instances for the given signalId.
|
|
48629
|
+
* Called by MemoryAdapter when a signal is cancelled or closed.
|
|
48630
|
+
* @param signalId - Signal identifier to dispose
|
|
48631
|
+
*/
|
|
48632
|
+
this.disposeSignal = (signalId) => {
|
|
48633
|
+
const prefix = CREATE_KEY_FN$4(signalId, "");
|
|
48634
|
+
for (const key of this.getInstance.keys()) {
|
|
48635
|
+
if (key.startsWith(prefix)) {
|
|
48636
|
+
const instance = this.getInstance.get(key);
|
|
48637
|
+
instance && instance.dispose();
|
|
48638
|
+
this.getInstance.clear(key);
|
|
48639
|
+
}
|
|
48640
|
+
}
|
|
48641
|
+
};
|
|
48642
|
+
/**
|
|
48643
|
+
* Write a value to memory.
|
|
48644
|
+
* @param dto.memoryId - Unique entry identifier
|
|
48645
|
+
* @param dto.value - Value to store
|
|
48646
|
+
* @param dto.signalId - Signal identifier
|
|
48647
|
+
* @param dto.bucketName - Bucket name
|
|
48648
|
+
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
48649
|
+
*/
|
|
48650
|
+
this.writeMemory = async (dto) => {
|
|
48651
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_WRITE, {
|
|
48652
|
+
signalId: dto.signalId,
|
|
48653
|
+
bucketName: dto.bucketName,
|
|
48654
|
+
memoryId: dto.memoryId,
|
|
48655
|
+
});
|
|
48656
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48657
|
+
const isInitial = !this.getInstance.has(key);
|
|
48658
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48659
|
+
await instance.waitForInit(isInitial);
|
|
48660
|
+
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
48661
|
+
};
|
|
48662
|
+
/**
|
|
48663
|
+
* Search memory using BM25 full-text scoring.
|
|
48664
|
+
* @param dto.query - Search query string
|
|
48665
|
+
* @param dto.signalId - Signal identifier
|
|
48666
|
+
* @param dto.bucketName - Bucket name
|
|
48667
|
+
* @returns Matching entries sorted by relevance score
|
|
48668
|
+
*/
|
|
48669
|
+
this.searchMemory = async (dto) => {
|
|
48670
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_SEARCH, {
|
|
48671
|
+
signalId: dto.signalId,
|
|
48672
|
+
bucketName: dto.bucketName,
|
|
48673
|
+
query: dto.query,
|
|
48674
|
+
});
|
|
48675
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48676
|
+
const isInitial = !this.getInstance.has(key);
|
|
48677
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48678
|
+
await instance.waitForInit(isInitial);
|
|
48679
|
+
return await instance.searchMemory(dto.query, dto.settings);
|
|
48680
|
+
};
|
|
48681
|
+
/**
|
|
48682
|
+
* List all entries in memory.
|
|
48683
|
+
* @param dto.signalId - Signal identifier
|
|
48684
|
+
* @param dto.bucketName - Bucket name
|
|
48685
|
+
* @returns Array of all stored entries
|
|
48686
|
+
*/
|
|
48687
|
+
this.listMemory = async (dto) => {
|
|
48688
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_LIST, {
|
|
48689
|
+
signalId: dto.signalId,
|
|
48690
|
+
bucketName: dto.bucketName,
|
|
48691
|
+
});
|
|
48692
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48693
|
+
const isInitial = !this.getInstance.has(key);
|
|
48694
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48695
|
+
await instance.waitForInit(isInitial);
|
|
48696
|
+
return await instance.listMemory();
|
|
48697
|
+
};
|
|
48698
|
+
/**
|
|
48699
|
+
* Remove an entry from memory.
|
|
48700
|
+
* @param dto.memoryId - Unique entry identifier
|
|
48701
|
+
* @param dto.signalId - Signal identifier
|
|
48702
|
+
* @param dto.bucketName - Bucket name
|
|
48703
|
+
*/
|
|
48704
|
+
this.removeMemory = async (dto) => {
|
|
48705
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_REMOVE, {
|
|
48706
|
+
signalId: dto.signalId,
|
|
48707
|
+
bucketName: dto.bucketName,
|
|
48708
|
+
memoryId: dto.memoryId,
|
|
48709
|
+
});
|
|
48710
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48711
|
+
const isInitial = !this.getInstance.has(key);
|
|
48712
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48713
|
+
await instance.waitForInit(isInitial);
|
|
48714
|
+
return await instance.removeMemory(dto.memoryId);
|
|
48715
|
+
};
|
|
48716
|
+
/**
|
|
48717
|
+
* Read a single entry from memory.
|
|
48718
|
+
* @param dto.memoryId - Unique entry identifier
|
|
48719
|
+
* @param dto.signalId - Signal identifier
|
|
48720
|
+
* @param dto.bucketName - Bucket name
|
|
48721
|
+
* @returns Entry value
|
|
48722
|
+
* @throws Error if entry not found
|
|
48723
|
+
*/
|
|
48724
|
+
this.readMemory = async (dto) => {
|
|
48725
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_READ, {
|
|
48726
|
+
signalId: dto.signalId,
|
|
48727
|
+
bucketName: dto.bucketName,
|
|
48728
|
+
memoryId: dto.memoryId,
|
|
48729
|
+
});
|
|
48730
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48731
|
+
const isInitial = !this.getInstance.has(key);
|
|
48732
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48733
|
+
await instance.waitForInit(isInitial);
|
|
48734
|
+
return await instance.readMemory(dto.memoryId);
|
|
48735
|
+
};
|
|
48736
|
+
/**
|
|
48737
|
+
* Switches to in-memory BM25 adapter.
|
|
48738
|
+
* All data lives in process memory only.
|
|
48739
|
+
*/
|
|
48740
|
+
this.useLocal = () => {
|
|
48741
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
48742
|
+
this.MemoryFactory = MemoryLocalInstance;
|
|
48743
|
+
};
|
|
48744
|
+
/**
|
|
48745
|
+
* Switches to file-system backed adapter (default).
|
|
48746
|
+
* Data is persisted to ./dump/memory/<signalId>/<bucketName>/.
|
|
48747
|
+
*/
|
|
48748
|
+
this.usePersist = () => {
|
|
48749
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
48750
|
+
this.MemoryFactory = MemoryPersistInstance;
|
|
48751
|
+
};
|
|
48752
|
+
/**
|
|
48753
|
+
* Switches to dummy adapter that discards all writes.
|
|
48754
|
+
*/
|
|
48755
|
+
this.useDummy = () => {
|
|
48756
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
48757
|
+
this.MemoryFactory = MemoryDummyInstance;
|
|
48758
|
+
};
|
|
48759
|
+
/**
|
|
48760
|
+
* Switches to a custom memory adapter implementation.
|
|
48761
|
+
* @param Ctor - Constructor for the custom memory instance
|
|
48762
|
+
*/
|
|
48763
|
+
this.useMemoryAdapter = (Ctor) => {
|
|
48764
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER);
|
|
48765
|
+
this.MemoryFactory = Ctor;
|
|
48766
|
+
};
|
|
48767
|
+
/**
|
|
48768
|
+
* Clears the memoized instance cache.
|
|
48769
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
48770
|
+
* so new instances are created with the updated base path.
|
|
48771
|
+
*/
|
|
48772
|
+
this.clear = () => {
|
|
48773
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_CLEAR);
|
|
48774
|
+
this.getInstance.clear();
|
|
48775
|
+
};
|
|
48776
|
+
}
|
|
48777
|
+
}
|
|
48778
|
+
/**
|
|
48779
|
+
* Main memory adapter that manages both backtest and live memory storage.
|
|
48780
|
+
*
|
|
48781
|
+
* Features:
|
|
48782
|
+
* - Subscribes to signal lifecycle events (cancelled/closed) to dispose stale instances
|
|
48783
|
+
* - Routes all operations to MemoryBacktest or MemoryLive based on dto.backtest
|
|
48784
|
+
* - Singleshot enable pattern prevents duplicate subscriptions
|
|
48785
|
+
* - Cleanup function for proper unsubscription
|
|
48786
|
+
*/
|
|
48787
|
+
class MemoryAdapter {
|
|
48788
|
+
constructor() {
|
|
48789
|
+
/**
|
|
48790
|
+
* Enables memory storage by subscribing to signal lifecycle events.
|
|
48791
|
+
* Clears memoized instances in MemoryBacktest and MemoryLive when a signal
|
|
48792
|
+
* is cancelled or closed, preventing stale instances from accumulating.
|
|
48793
|
+
* Uses singleshot to ensure one-time subscription.
|
|
48794
|
+
*
|
|
48795
|
+
* @returns Cleanup function that unsubscribes from all emitters
|
|
47533
48796
|
*/
|
|
47534
48797
|
this.enable = singleshot(() => {
|
|
47535
48798
|
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_ENABLE);
|
|
47536
|
-
const handleDispose = (signalId) => {
|
|
47537
|
-
const prefix = CREATE_KEY_FN$4(signalId, "");
|
|
47538
|
-
for (const key of this.getInstance.keys()) {
|
|
47539
|
-
if (key.startsWith(prefix)) {
|
|
47540
|
-
const instance = this.getInstance.get(key);
|
|
47541
|
-
instance && instance.dispose();
|
|
47542
|
-
this.getInstance.clear(key);
|
|
47543
|
-
}
|
|
47544
|
-
}
|
|
47545
|
-
};
|
|
47546
48799
|
const unCancel = signalEmitter
|
|
47547
48800
|
.filter(({ action }) => action === "cancelled")
|
|
47548
|
-
.connect(({ signal }) =>
|
|
48801
|
+
.connect(({ signal }) => {
|
|
48802
|
+
MemoryBacktest.disposeSignal(signal.id);
|
|
48803
|
+
MemoryLive.disposeSignal(signal.id);
|
|
48804
|
+
});
|
|
47549
48805
|
const unClose = signalEmitter
|
|
47550
48806
|
.filter(({ action }) => action === "closed")
|
|
47551
|
-
.connect(({ signal }) =>
|
|
47552
|
-
|
|
48807
|
+
.connect(({ signal }) => {
|
|
48808
|
+
MemoryBacktest.disposeSignal(signal.id);
|
|
48809
|
+
MemoryLive.disposeSignal(signal.id);
|
|
48810
|
+
});
|
|
48811
|
+
return compose(() => unCancel(), () => unClose(), () => this.enable.clear());
|
|
47553
48812
|
});
|
|
47554
48813
|
/**
|
|
47555
|
-
*
|
|
47556
|
-
*
|
|
48814
|
+
* Disables memory storage by unsubscribing from signal lifecycle events.
|
|
48815
|
+
* Safe to call multiple times.
|
|
47557
48816
|
*/
|
|
47558
48817
|
this.disable = () => {
|
|
47559
48818
|
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_DISABLE);
|
|
@@ -47564,11 +48823,13 @@ class MemoryAdapter {
|
|
|
47564
48823
|
};
|
|
47565
48824
|
/**
|
|
47566
48825
|
* Write a value to memory.
|
|
48826
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47567
48827
|
* @param dto.memoryId - Unique entry identifier
|
|
47568
48828
|
* @param dto.value - Value to store
|
|
47569
48829
|
* @param dto.signalId - Signal identifier
|
|
47570
48830
|
* @param dto.bucketName - Bucket name
|
|
47571
|
-
* @param dto.description -
|
|
48831
|
+
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
48832
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47572
48833
|
*/
|
|
47573
48834
|
this.writeMemory = async (dto) => {
|
|
47574
48835
|
if (!this.enable.hasValue()) {
|
|
@@ -47578,18 +48839,20 @@ class MemoryAdapter {
|
|
|
47578
48839
|
signalId: dto.signalId,
|
|
47579
48840
|
bucketName: dto.bucketName,
|
|
47580
48841
|
memoryId: dto.memoryId,
|
|
48842
|
+
backtest: dto.backtest,
|
|
47581
48843
|
});
|
|
47582
|
-
|
|
47583
|
-
|
|
47584
|
-
|
|
47585
|
-
await
|
|
47586
|
-
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
48844
|
+
if (dto.backtest) {
|
|
48845
|
+
return await MemoryBacktest.writeMemory(dto);
|
|
48846
|
+
}
|
|
48847
|
+
return await MemoryLive.writeMemory(dto);
|
|
47587
48848
|
};
|
|
47588
48849
|
/**
|
|
47589
48850
|
* Search memory using BM25 full-text scoring.
|
|
48851
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47590
48852
|
* @param dto.query - Search query string
|
|
47591
48853
|
* @param dto.signalId - Signal identifier
|
|
47592
48854
|
* @param dto.bucketName - Bucket name
|
|
48855
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47593
48856
|
* @returns Matching entries sorted by relevance score
|
|
47594
48857
|
*/
|
|
47595
48858
|
this.searchMemory = async (dto) => {
|
|
@@ -47600,17 +48863,19 @@ class MemoryAdapter {
|
|
|
47600
48863
|
signalId: dto.signalId,
|
|
47601
48864
|
bucketName: dto.bucketName,
|
|
47602
48865
|
query: dto.query,
|
|
48866
|
+
backtest: dto.backtest,
|
|
47603
48867
|
});
|
|
47604
|
-
|
|
47605
|
-
|
|
47606
|
-
|
|
47607
|
-
await
|
|
47608
|
-
return await instance.searchMemory(dto.query, dto.settings);
|
|
48868
|
+
if (dto.backtest) {
|
|
48869
|
+
return await MemoryBacktest.searchMemory(dto);
|
|
48870
|
+
}
|
|
48871
|
+
return await MemoryLive.searchMemory(dto);
|
|
47609
48872
|
};
|
|
47610
48873
|
/**
|
|
47611
48874
|
* List all entries in memory.
|
|
48875
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47612
48876
|
* @param dto.signalId - Signal identifier
|
|
47613
48877
|
* @param dto.bucketName - Bucket name
|
|
48878
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47614
48879
|
* @returns Array of all stored entries
|
|
47615
48880
|
*/
|
|
47616
48881
|
this.listMemory = async (dto) => {
|
|
@@ -47620,18 +48885,20 @@ class MemoryAdapter {
|
|
|
47620
48885
|
backtest.loggerService.debug(MEMORY_ADAPTER_METHOD_NAME_LIST, {
|
|
47621
48886
|
signalId: dto.signalId,
|
|
47622
48887
|
bucketName: dto.bucketName,
|
|
48888
|
+
backtest: dto.backtest,
|
|
47623
48889
|
});
|
|
47624
|
-
|
|
47625
|
-
|
|
47626
|
-
|
|
47627
|
-
await
|
|
47628
|
-
return await instance.listMemory();
|
|
48890
|
+
if (dto.backtest) {
|
|
48891
|
+
return await MemoryBacktest.listMemory(dto);
|
|
48892
|
+
}
|
|
48893
|
+
return await MemoryLive.listMemory(dto);
|
|
47629
48894
|
};
|
|
47630
48895
|
/**
|
|
47631
48896
|
* Remove an entry from memory.
|
|
48897
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47632
48898
|
* @param dto.memoryId - Unique entry identifier
|
|
47633
48899
|
* @param dto.signalId - Signal identifier
|
|
47634
48900
|
* @param dto.bucketName - Bucket name
|
|
48901
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47635
48902
|
*/
|
|
47636
48903
|
this.removeMemory = async (dto) => {
|
|
47637
48904
|
if (!this.enable.hasValue()) {
|
|
@@ -47641,18 +48908,20 @@ class MemoryAdapter {
|
|
|
47641
48908
|
signalId: dto.signalId,
|
|
47642
48909
|
bucketName: dto.bucketName,
|
|
47643
48910
|
memoryId: dto.memoryId,
|
|
48911
|
+
backtest: dto.backtest,
|
|
47644
48912
|
});
|
|
47645
|
-
|
|
47646
|
-
|
|
47647
|
-
|
|
47648
|
-
await
|
|
47649
|
-
return await instance.removeMemory(dto.memoryId);
|
|
48913
|
+
if (dto.backtest) {
|
|
48914
|
+
return await MemoryBacktest.removeMemory(dto);
|
|
48915
|
+
}
|
|
48916
|
+
return await MemoryLive.removeMemory(dto);
|
|
47650
48917
|
};
|
|
47651
48918
|
/**
|
|
47652
48919
|
* Read a single entry from memory.
|
|
48920
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47653
48921
|
* @param dto.memoryId - Unique entry identifier
|
|
47654
48922
|
* @param dto.signalId - Signal identifier
|
|
47655
48923
|
* @param dto.bucketName - Bucket name
|
|
48924
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47656
48925
|
* @returns Entry value
|
|
47657
48926
|
* @throws Error if entry not found
|
|
47658
48927
|
*/
|
|
@@ -47664,56 +48933,30 @@ class MemoryAdapter {
|
|
|
47664
48933
|
signalId: dto.signalId,
|
|
47665
48934
|
bucketName: dto.bucketName,
|
|
47666
48935
|
memoryId: dto.memoryId,
|
|
48936
|
+
backtest: dto.backtest,
|
|
47667
48937
|
});
|
|
47668
|
-
|
|
47669
|
-
|
|
47670
|
-
|
|
47671
|
-
await
|
|
47672
|
-
return await instance.readMemory(dto.memoryId);
|
|
47673
|
-
};
|
|
47674
|
-
/**
|
|
47675
|
-
* Switches to in-memory BM25 adapter (default).
|
|
47676
|
-
* All data lives in process memory only.
|
|
47677
|
-
*/
|
|
47678
|
-
this.useLocal = () => {
|
|
47679
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
47680
|
-
this.MemoryFactory = MemoryLocalInstance;
|
|
47681
|
-
};
|
|
47682
|
-
/**
|
|
47683
|
-
* Switches to file-system backed adapter.
|
|
47684
|
-
* Data is persisted to ./dump/memory/<signalId>/<bucketName>/.
|
|
47685
|
-
*/
|
|
47686
|
-
this.usePersist = () => {
|
|
47687
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
47688
|
-
this.MemoryFactory = MemoryPersistInstance;
|
|
47689
|
-
};
|
|
47690
|
-
/**
|
|
47691
|
-
* Switches to dummy adapter that discards all writes.
|
|
47692
|
-
*/
|
|
47693
|
-
this.useDummy = () => {
|
|
47694
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
47695
|
-
this.MemoryFactory = MemoryDummyInstance;
|
|
47696
|
-
};
|
|
47697
|
-
/**
|
|
47698
|
-
* Clears the memoized instance cache.
|
|
47699
|
-
* Call this when process.cwd() changes between strategy iterations
|
|
47700
|
-
* so new instances are created with the updated base path.
|
|
47701
|
-
*/
|
|
47702
|
-
this.clear = () => {
|
|
47703
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_CLEAR);
|
|
47704
|
-
this.getInstance.clear();
|
|
47705
|
-
};
|
|
47706
|
-
/**
|
|
47707
|
-
* Releases resources held by this adapter.
|
|
47708
|
-
* Delegates to disable() to unsubscribe from signal lifecycle events.
|
|
47709
|
-
*/
|
|
47710
|
-
this.dispose = () => {
|
|
47711
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_DISPOSE);
|
|
47712
|
-
this.disable();
|
|
48938
|
+
if (dto.backtest) {
|
|
48939
|
+
return await MemoryBacktest.readMemory(dto);
|
|
48940
|
+
}
|
|
48941
|
+
return await MemoryLive.readMemory(dto);
|
|
47713
48942
|
};
|
|
47714
48943
|
}
|
|
47715
48944
|
}
|
|
48945
|
+
/**
|
|
48946
|
+
* Global singleton instance of MemoryAdapter.
|
|
48947
|
+
* Provides unified memory management for backtest and live trading.
|
|
48948
|
+
*/
|
|
47716
48949
|
const Memory = new MemoryAdapter();
|
|
48950
|
+
/**
|
|
48951
|
+
* Global singleton instance of MemoryLiveAdapter.
|
|
48952
|
+
* Provides live trading memory storage with pluggable backends.
|
|
48953
|
+
*/
|
|
48954
|
+
const MemoryLive = new MemoryLiveAdapter();
|
|
48955
|
+
/**
|
|
48956
|
+
* Global singleton instance of MemoryBacktestAdapter.
|
|
48957
|
+
* Provides backtest memory storage with pluggable backends.
|
|
48958
|
+
*/
|
|
48959
|
+
const MemoryBacktest = new MemoryBacktestAdapter();
|
|
47717
48960
|
|
|
47718
48961
|
const WRITE_MEMORY_METHOD_NAME = "memory.writeMemory";
|
|
47719
48962
|
const READ_MEMORY_METHOD_NAME = "memory.readMemory";
|
|
@@ -47723,23 +48966,20 @@ const REMOVE_MEMORY_METHOD_NAME = "memory.removeMemory";
|
|
|
47723
48966
|
/**
|
|
47724
48967
|
* Writes a value to memory scoped to the current signal.
|
|
47725
48968
|
*
|
|
47726
|
-
*
|
|
47727
|
-
* If no pending signal exists, logs a warning and returns without writing.
|
|
47728
|
-
*
|
|
48969
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47729
48970
|
* Automatically detects backtest/live mode from execution context.
|
|
47730
48971
|
*
|
|
47731
48972
|
* @param dto.bucketName - Memory bucket name
|
|
47732
48973
|
* @param dto.memoryId - Unique memory entry identifier
|
|
47733
48974
|
* @param dto.value - Value to store
|
|
48975
|
+
* @param dto.description - BM25 index string for contextual search
|
|
47734
48976
|
* @returns Promise that resolves when write is complete
|
|
47735
48977
|
*
|
|
47736
|
-
* @deprecated Better use Memory.writeMemory with manual signalId argument
|
|
47737
|
-
*
|
|
47738
48978
|
* @example
|
|
47739
48979
|
* ```typescript
|
|
47740
48980
|
* import { writeMemory } from "backtest-kit";
|
|
47741
48981
|
*
|
|
47742
|
-
* await writeMemory({ bucketName: "my-strategy", memoryId: "context", value: { trend: "up", confidence: 0.9 } });
|
|
48982
|
+
* await writeMemory({ bucketName: "my-strategy", memoryId: "context", value: { trend: "up", confidence: 0.9 }, description: "Signal context at entry" });
|
|
47743
48983
|
* ```
|
|
47744
48984
|
*/
|
|
47745
48985
|
async function writeMemory(dto) {
|
|
@@ -47757,33 +48997,41 @@ async function writeMemory(dto) {
|
|
|
47757
48997
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47758
48998
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47759
48999
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47760
|
-
|
|
47761
|
-
if (
|
|
47762
|
-
|
|
49000
|
+
let signal;
|
|
49001
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49002
|
+
await Memory.writeMemory({
|
|
49003
|
+
memoryId,
|
|
49004
|
+
value,
|
|
49005
|
+
signalId: signal.id,
|
|
49006
|
+
bucketName,
|
|
49007
|
+
description,
|
|
49008
|
+
backtest: isBacktest,
|
|
49009
|
+
});
|
|
47763
49010
|
return;
|
|
47764
49011
|
}
|
|
47765
|
-
await
|
|
47766
|
-
|
|
47767
|
-
|
|
47768
|
-
|
|
47769
|
-
|
|
47770
|
-
|
|
47771
|
-
|
|
49012
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49013
|
+
await Memory.writeMemory({
|
|
49014
|
+
memoryId,
|
|
49015
|
+
value,
|
|
49016
|
+
signalId: signal.id,
|
|
49017
|
+
bucketName,
|
|
49018
|
+
description,
|
|
49019
|
+
backtest: isBacktest,
|
|
49020
|
+
});
|
|
49021
|
+
return;
|
|
49022
|
+
}
|
|
49023
|
+
throw new Error(`writeMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
|
|
47772
49024
|
}
|
|
47773
49025
|
/**
|
|
47774
49026
|
* Reads a value from memory scoped to the current signal.
|
|
47775
49027
|
*
|
|
47776
|
-
*
|
|
47777
|
-
* If no pending signal exists, logs a warning and returns null.
|
|
47778
|
-
*
|
|
49028
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47779
49029
|
* Automatically detects backtest/live mode from execution context.
|
|
47780
49030
|
*
|
|
47781
49031
|
* @param dto.bucketName - Memory bucket name
|
|
47782
49032
|
* @param dto.memoryId - Unique memory entry identifier
|
|
47783
|
-
* @returns Promise resolving to stored value
|
|
47784
|
-
* @throws Error if
|
|
47785
|
-
*
|
|
47786
|
-
* @deprecated Better use Memory.readMemory with manual signalId argument
|
|
49033
|
+
* @returns Promise resolving to stored value
|
|
49034
|
+
* @throws Error if no pending or scheduled signal exists, or if entry not found
|
|
47787
49035
|
*
|
|
47788
49036
|
* @example
|
|
47789
49037
|
* ```typescript
|
|
@@ -47807,30 +49055,35 @@ async function readMemory(dto) {
|
|
|
47807
49055
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47808
49056
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47809
49057
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47810
|
-
|
|
47811
|
-
if (
|
|
47812
|
-
|
|
47813
|
-
|
|
49058
|
+
let signal;
|
|
49059
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49060
|
+
return await Memory.readMemory({
|
|
49061
|
+
memoryId,
|
|
49062
|
+
signalId: signal.id,
|
|
49063
|
+
bucketName,
|
|
49064
|
+
backtest: isBacktest,
|
|
49065
|
+
});
|
|
47814
49066
|
}
|
|
47815
|
-
|
|
47816
|
-
|
|
47817
|
-
|
|
47818
|
-
|
|
47819
|
-
|
|
49067
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49068
|
+
return await Memory.readMemory({
|
|
49069
|
+
memoryId,
|
|
49070
|
+
signalId: signal.id,
|
|
49071
|
+
bucketName,
|
|
49072
|
+
backtest: isBacktest,
|
|
49073
|
+
});
|
|
49074
|
+
}
|
|
49075
|
+
throw new Error(`readMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
|
|
47820
49076
|
}
|
|
47821
49077
|
/**
|
|
47822
49078
|
* Searches memory entries for the current signal using BM25 full-text scoring.
|
|
47823
49079
|
*
|
|
47824
|
-
*
|
|
47825
|
-
* If no pending signal exists, logs a warning and returns an empty array.
|
|
47826
|
-
*
|
|
49080
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47827
49081
|
* Automatically detects backtest/live mode from execution context.
|
|
47828
49082
|
*
|
|
47829
49083
|
* @param dto.bucketName - Memory bucket name
|
|
47830
49084
|
* @param dto.query - Search query string
|
|
47831
|
-
* @returns Promise resolving to matching entries sorted by relevance
|
|
47832
|
-
*
|
|
47833
|
-
* @deprecated Better use Memory.searchMemory with manual signalId argument
|
|
49085
|
+
* @returns Promise resolving to matching entries sorted by relevance
|
|
49086
|
+
* @throws Error if no pending or scheduled signal exists
|
|
47834
49087
|
*
|
|
47835
49088
|
* @example
|
|
47836
49089
|
* ```typescript
|
|
@@ -47854,29 +49107,34 @@ async function searchMemory(dto) {
|
|
|
47854
49107
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47855
49108
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47856
49109
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47857
|
-
|
|
47858
|
-
if (
|
|
47859
|
-
|
|
47860
|
-
|
|
49110
|
+
let signal;
|
|
49111
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49112
|
+
return await Memory.searchMemory({
|
|
49113
|
+
query,
|
|
49114
|
+
signalId: signal.id,
|
|
49115
|
+
bucketName,
|
|
49116
|
+
backtest: isBacktest,
|
|
49117
|
+
});
|
|
47861
49118
|
}
|
|
47862
|
-
|
|
47863
|
-
|
|
47864
|
-
|
|
47865
|
-
|
|
47866
|
-
|
|
49119
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49120
|
+
return await Memory.searchMemory({
|
|
49121
|
+
query,
|
|
49122
|
+
signalId: signal.id,
|
|
49123
|
+
bucketName,
|
|
49124
|
+
backtest: isBacktest,
|
|
49125
|
+
});
|
|
49126
|
+
}
|
|
49127
|
+
throw new Error(`searchMemory requires a pending or scheduled signal for symbol=${symbol} query=${query}`);
|
|
47867
49128
|
}
|
|
47868
49129
|
/**
|
|
47869
49130
|
* Lists all memory entries for the current signal.
|
|
47870
49131
|
*
|
|
47871
|
-
*
|
|
47872
|
-
* If no pending signal exists, logs a warning and returns an empty array.
|
|
47873
|
-
*
|
|
49132
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47874
49133
|
* Automatically detects backtest/live mode from execution context.
|
|
47875
49134
|
*
|
|
47876
49135
|
* @param dto.bucketName - Memory bucket name
|
|
47877
|
-
* @returns Promise resolving to all stored entries
|
|
47878
|
-
*
|
|
47879
|
-
* @deprecated Better use Memory.listMemory with manual signalId argument
|
|
49136
|
+
* @returns Promise resolving to all stored entries
|
|
49137
|
+
* @throws Error if no pending or scheduled signal exists
|
|
47880
49138
|
*
|
|
47881
49139
|
* @example
|
|
47882
49140
|
* ```typescript
|
|
@@ -47899,29 +49157,33 @@ async function listMemory(dto) {
|
|
|
47899
49157
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47900
49158
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47901
49159
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47902
|
-
|
|
47903
|
-
if (
|
|
47904
|
-
|
|
47905
|
-
|
|
49160
|
+
let signal;
|
|
49161
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49162
|
+
return await Memory.listMemory({
|
|
49163
|
+
signalId: signal.id,
|
|
49164
|
+
bucketName,
|
|
49165
|
+
backtest: isBacktest,
|
|
49166
|
+
});
|
|
47906
49167
|
}
|
|
47907
|
-
|
|
47908
|
-
|
|
47909
|
-
|
|
47910
|
-
|
|
49168
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49169
|
+
return await Memory.listMemory({
|
|
49170
|
+
signalId: signal.id,
|
|
49171
|
+
bucketName,
|
|
49172
|
+
backtest: isBacktest,
|
|
49173
|
+
});
|
|
49174
|
+
}
|
|
49175
|
+
throw new Error(`listMemory requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
47911
49176
|
}
|
|
47912
49177
|
/**
|
|
47913
49178
|
* Removes a memory entry for the current signal.
|
|
47914
49179
|
*
|
|
47915
|
-
*
|
|
47916
|
-
* If no pending signal exists, logs a warning and returns without removing.
|
|
47917
|
-
*
|
|
49180
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47918
49181
|
* Automatically detects backtest/live mode from execution context.
|
|
47919
49182
|
*
|
|
47920
49183
|
* @param dto.bucketName - Memory bucket name
|
|
47921
49184
|
* @param dto.memoryId - Unique memory entry identifier
|
|
47922
49185
|
* @returns Promise that resolves when removal is complete
|
|
47923
|
-
*
|
|
47924
|
-
* @deprecated Better use Memory.removeMemory with manual signalId argument
|
|
49186
|
+
* @throws Error if no pending or scheduled signal exists
|
|
47925
49187
|
*
|
|
47926
49188
|
* @example
|
|
47927
49189
|
* ```typescript
|
|
@@ -47945,16 +49207,26 @@ async function removeMemory(dto) {
|
|
|
47945
49207
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47946
49208
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47947
49209
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47948
|
-
|
|
47949
|
-
if (
|
|
47950
|
-
|
|
49210
|
+
let signal;
|
|
49211
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49212
|
+
await Memory.removeMemory({
|
|
49213
|
+
memoryId,
|
|
49214
|
+
signalId: signal.id,
|
|
49215
|
+
bucketName,
|
|
49216
|
+
backtest: isBacktest,
|
|
49217
|
+
});
|
|
47951
49218
|
return;
|
|
47952
49219
|
}
|
|
47953
|
-
await
|
|
47954
|
-
|
|
47955
|
-
|
|
47956
|
-
|
|
47957
|
-
|
|
49220
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49221
|
+
await Memory.removeMemory({
|
|
49222
|
+
memoryId,
|
|
49223
|
+
signalId: signal.id,
|
|
49224
|
+
bucketName,
|
|
49225
|
+
backtest: isBacktest,
|
|
49226
|
+
});
|
|
49227
|
+
return;
|
|
49228
|
+
}
|
|
49229
|
+
throw new Error(`removeMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
|
|
47958
49230
|
}
|
|
47959
49231
|
|
|
47960
49232
|
const CREATE_KEY_FN$3 = (signalId, bucketName) => `${signalId}-${bucketName}`;
|
|
@@ -48049,11 +49321,12 @@ const RENDER_TABLE_FN = (rows) => {
|
|
|
48049
49321
|
* Scoped to (signalId, bucketName) via constructor.
|
|
48050
49322
|
*/
|
|
48051
49323
|
class DumpBothInstance {
|
|
48052
|
-
constructor(signalId, bucketName) {
|
|
49324
|
+
constructor(signalId, bucketName, backtest) {
|
|
48053
49325
|
this.signalId = signalId;
|
|
48054
49326
|
this.bucketName = bucketName;
|
|
48055
|
-
this.
|
|
48056
|
-
this.
|
|
49327
|
+
this.backtest = backtest;
|
|
49328
|
+
this._memory = new DumpMemoryInstance(signalId, bucketName, backtest);
|
|
49329
|
+
this._markdown = new DumpMarkdownInstance(signalId, bucketName, backtest);
|
|
48057
49330
|
}
|
|
48058
49331
|
/** Releases resources held by both backends. */
|
|
48059
49332
|
dispose() {
|
|
@@ -48185,9 +49458,10 @@ class DumpBothInstance {
|
|
|
48185
49458
|
* Useful for downstream LLM retrieval via Memory.searchMemory.
|
|
48186
49459
|
*/
|
|
48187
49460
|
class DumpMemoryInstance {
|
|
48188
|
-
constructor(signalId, bucketName) {
|
|
49461
|
+
constructor(signalId, bucketName, backtest) {
|
|
48189
49462
|
this.signalId = signalId;
|
|
48190
49463
|
this.bucketName = bucketName;
|
|
49464
|
+
this.backtest = backtest;
|
|
48191
49465
|
}
|
|
48192
49466
|
/**
|
|
48193
49467
|
* Stores the full agent message history in Memory as a `{ messages }` object.
|
|
@@ -48213,6 +49487,7 @@ class DumpMemoryInstance {
|
|
|
48213
49487
|
signalId: this.signalId,
|
|
48214
49488
|
value: { messages },
|
|
48215
49489
|
description,
|
|
49490
|
+
backtest: this.backtest,
|
|
48216
49491
|
});
|
|
48217
49492
|
}
|
|
48218
49493
|
/**
|
|
@@ -48234,6 +49509,7 @@ class DumpMemoryInstance {
|
|
|
48234
49509
|
signalId: this.signalId,
|
|
48235
49510
|
value: record,
|
|
48236
49511
|
description,
|
|
49512
|
+
backtest: this.backtest,
|
|
48237
49513
|
});
|
|
48238
49514
|
}
|
|
48239
49515
|
/**
|
|
@@ -48256,6 +49532,7 @@ class DumpMemoryInstance {
|
|
|
48256
49532
|
signalId: this.signalId,
|
|
48257
49533
|
value: { rows },
|
|
48258
49534
|
description,
|
|
49535
|
+
backtest: this.backtest,
|
|
48259
49536
|
});
|
|
48260
49537
|
}
|
|
48261
49538
|
/**
|
|
@@ -48277,6 +49554,7 @@ class DumpMemoryInstance {
|
|
|
48277
49554
|
signalId: this.signalId,
|
|
48278
49555
|
value: { content },
|
|
48279
49556
|
description,
|
|
49557
|
+
backtest: this.backtest,
|
|
48280
49558
|
});
|
|
48281
49559
|
}
|
|
48282
49560
|
/**
|
|
@@ -48298,6 +49576,7 @@ class DumpMemoryInstance {
|
|
|
48298
49576
|
signalId: this.signalId,
|
|
48299
49577
|
value: { content },
|
|
48300
49578
|
description,
|
|
49579
|
+
backtest: this.backtest,
|
|
48301
49580
|
});
|
|
48302
49581
|
}
|
|
48303
49582
|
/**
|
|
@@ -48320,6 +49599,7 @@ class DumpMemoryInstance {
|
|
|
48320
49599
|
signalId: this.signalId,
|
|
48321
49600
|
value: json,
|
|
48322
49601
|
description,
|
|
49602
|
+
backtest: this.backtest,
|
|
48323
49603
|
});
|
|
48324
49604
|
}
|
|
48325
49605
|
/** Releases resources held by this instance. */
|
|
@@ -48341,9 +49621,10 @@ class DumpMemoryInstance {
|
|
|
48341
49621
|
* If the file already exists, the call is skipped (idempotent).
|
|
48342
49622
|
*/
|
|
48343
49623
|
class DumpMarkdownInstance {
|
|
48344
|
-
constructor(signalId, bucketName) {
|
|
49624
|
+
constructor(signalId, bucketName, backtest) {
|
|
48345
49625
|
this.signalId = signalId;
|
|
48346
49626
|
this.bucketName = bucketName;
|
|
49627
|
+
this.backtest = backtest;
|
|
48347
49628
|
}
|
|
48348
49629
|
getFilePath(dumpId) {
|
|
48349
49630
|
return join("./dump/agent", this.signalId, this.bucketName, `${dumpId}.md`);
|
|
@@ -48532,9 +49813,10 @@ class DumpMarkdownInstance {
|
|
|
48532
49813
|
* Used for disabling dumps in tests or dry-run scenarios.
|
|
48533
49814
|
*/
|
|
48534
49815
|
class DumpDummyInstance {
|
|
48535
|
-
constructor(signalId, bucketName) {
|
|
49816
|
+
constructor(signalId, bucketName, backtest) {
|
|
48536
49817
|
this.signalId = signalId;
|
|
48537
49818
|
this.bucketName = bucketName;
|
|
49819
|
+
this.backtest = backtest;
|
|
48538
49820
|
}
|
|
48539
49821
|
/** No-op. */
|
|
48540
49822
|
async dumpAgentAnswer() {
|
|
@@ -48577,7 +49859,7 @@ class DumpDummyInstance {
|
|
|
48577
49859
|
class DumpAdapter {
|
|
48578
49860
|
constructor() {
|
|
48579
49861
|
this.DumpFactory = DumpMarkdownInstance;
|
|
48580
|
-
this.getInstance = memoize(([signalId, bucketName]) => CREATE_KEY_FN$3(signalId, bucketName), (signalId, bucketName) => Reflect.construct(this.DumpFactory, [signalId, bucketName]));
|
|
49862
|
+
this.getInstance = memoize(([signalId, bucketName]) => CREATE_KEY_FN$3(signalId, bucketName), (signalId, bucketName, backtest) => Reflect.construct(this.DumpFactory, [signalId, bucketName, backtest]));
|
|
48581
49863
|
/**
|
|
48582
49864
|
* Activates the adapter by subscribing to signal lifecycle events.
|
|
48583
49865
|
* Clears memoized instances for a signalId when it is cancelled or closed,
|
|
@@ -48628,7 +49910,7 @@ class DumpAdapter {
|
|
|
48628
49910
|
bucketName: context.bucketName,
|
|
48629
49911
|
dumpId: context.dumpId,
|
|
48630
49912
|
});
|
|
48631
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
49913
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48632
49914
|
return await instance.dumpAgentAnswer(messages, context.dumpId, context.description);
|
|
48633
49915
|
};
|
|
48634
49916
|
/**
|
|
@@ -48643,7 +49925,7 @@ class DumpAdapter {
|
|
|
48643
49925
|
bucketName: context.bucketName,
|
|
48644
49926
|
dumpId: context.dumpId,
|
|
48645
49927
|
});
|
|
48646
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
49928
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48647
49929
|
return await instance.dumpRecord(record, context.dumpId, context.description);
|
|
48648
49930
|
};
|
|
48649
49931
|
/**
|
|
@@ -48658,7 +49940,7 @@ class DumpAdapter {
|
|
|
48658
49940
|
bucketName: context.bucketName,
|
|
48659
49941
|
dumpId: context.dumpId,
|
|
48660
49942
|
});
|
|
48661
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
49943
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48662
49944
|
return await instance.dumpTable(rows, context.dumpId, context.description);
|
|
48663
49945
|
};
|
|
48664
49946
|
/**
|
|
@@ -48673,7 +49955,7 @@ class DumpAdapter {
|
|
|
48673
49955
|
bucketName: context.bucketName,
|
|
48674
49956
|
dumpId: context.dumpId,
|
|
48675
49957
|
});
|
|
48676
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
49958
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48677
49959
|
return await instance.dumpText(content, context.dumpId, context.description);
|
|
48678
49960
|
};
|
|
48679
49961
|
/**
|
|
@@ -48688,7 +49970,7 @@ class DumpAdapter {
|
|
|
48688
49970
|
bucketName: context.bucketName,
|
|
48689
49971
|
dumpId: context.dumpId,
|
|
48690
49972
|
});
|
|
48691
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
49973
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48692
49974
|
return await instance.dumpError(content, context.dumpId, context.description);
|
|
48693
49975
|
};
|
|
48694
49976
|
/**
|
|
@@ -48704,7 +49986,7 @@ class DumpAdapter {
|
|
|
48704
49986
|
bucketName: context.bucketName,
|
|
48705
49987
|
dumpId: context.dumpId,
|
|
48706
49988
|
});
|
|
48707
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
49989
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48708
49990
|
return await instance.dumpJson(json, context.dumpId, context.description);
|
|
48709
49991
|
};
|
|
48710
49992
|
/**
|
|
@@ -48770,16 +50052,15 @@ const DUMP_JSON_METHOD_NAME = "dump.dumpJson";
|
|
|
48770
50052
|
/**
|
|
48771
50053
|
* Dumps the full agent message history scoped to the current signal.
|
|
48772
50054
|
*
|
|
48773
|
-
*
|
|
48774
|
-
*
|
|
50055
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50056
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48775
50057
|
*
|
|
48776
50058
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
48777
50059
|
* @param dto.dumpId - Unique identifier for this agent invocation
|
|
48778
50060
|
* @param dto.messages - Full chat history (system, user, assistant, tool)
|
|
48779
50061
|
* @param dto.description - Human-readable label describing the agent invocation context; included in the BM25 index for Memory search
|
|
48780
50062
|
* @returns Promise that resolves when the dump is complete
|
|
48781
|
-
*
|
|
48782
|
-
* @deprecated Better use Dump.dumpAgentAnswer with manual signalId argument
|
|
50063
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48783
50064
|
*
|
|
48784
50065
|
* @example
|
|
48785
50066
|
* ```typescript
|
|
@@ -48804,31 +50085,41 @@ async function dumpAgentAnswer(dto) {
|
|
|
48804
50085
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48805
50086
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48806
50087
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48807
|
-
|
|
48808
|
-
if (
|
|
48809
|
-
|
|
50088
|
+
let signal;
|
|
50089
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50090
|
+
await Dump.dumpAgentAnswer(messages, {
|
|
50091
|
+
dumpId,
|
|
50092
|
+
bucketName,
|
|
50093
|
+
signalId: signal.id,
|
|
50094
|
+
description,
|
|
50095
|
+
backtest: isBacktest,
|
|
50096
|
+
});
|
|
48810
50097
|
return;
|
|
48811
50098
|
}
|
|
48812
|
-
await
|
|
48813
|
-
|
|
48814
|
-
|
|
48815
|
-
|
|
48816
|
-
|
|
48817
|
-
|
|
50099
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50100
|
+
await Dump.dumpAgentAnswer(messages, {
|
|
50101
|
+
dumpId,
|
|
50102
|
+
bucketName,
|
|
50103
|
+
signalId: signal.id,
|
|
50104
|
+
description,
|
|
50105
|
+
backtest: isBacktest,
|
|
50106
|
+
});
|
|
50107
|
+
return;
|
|
50108
|
+
}
|
|
50109
|
+
throw new Error(`dumpAgentAnswer requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
48818
50110
|
}
|
|
48819
50111
|
/**
|
|
48820
50112
|
* Dumps a flat key-value record scoped to the current signal.
|
|
48821
50113
|
*
|
|
48822
|
-
*
|
|
48823
|
-
*
|
|
50114
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50115
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48824
50116
|
*
|
|
48825
50117
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
48826
50118
|
* @param dto.dumpId - Unique identifier for this dump entry
|
|
48827
50119
|
* @param dto.record - Arbitrary flat object to persist
|
|
48828
50120
|
* @param dto.description - Human-readable label describing the record contents; included in the BM25 index for Memory search
|
|
48829
50121
|
* @returns Promise that resolves when the dump is complete
|
|
48830
|
-
*
|
|
48831
|
-
* @deprecated Better use Dump.dumpRecord with manual signalId argument
|
|
50122
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48832
50123
|
*
|
|
48833
50124
|
* @example
|
|
48834
50125
|
* ```typescript
|
|
@@ -48852,23 +50143,34 @@ async function dumpRecord(dto) {
|
|
|
48852
50143
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48853
50144
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48854
50145
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48855
|
-
|
|
48856
|
-
if (
|
|
48857
|
-
|
|
50146
|
+
let signal;
|
|
50147
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50148
|
+
await Dump.dumpRecord(record, {
|
|
50149
|
+
dumpId,
|
|
50150
|
+
bucketName,
|
|
50151
|
+
signalId: signal.id,
|
|
50152
|
+
description,
|
|
50153
|
+
backtest: isBacktest,
|
|
50154
|
+
});
|
|
48858
50155
|
return;
|
|
48859
50156
|
}
|
|
48860
|
-
await
|
|
48861
|
-
|
|
48862
|
-
|
|
48863
|
-
|
|
48864
|
-
|
|
48865
|
-
|
|
50157
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50158
|
+
await Dump.dumpRecord(record, {
|
|
50159
|
+
dumpId,
|
|
50160
|
+
bucketName,
|
|
50161
|
+
signalId: signal.id,
|
|
50162
|
+
description,
|
|
50163
|
+
backtest: isBacktest,
|
|
50164
|
+
});
|
|
50165
|
+
return;
|
|
50166
|
+
}
|
|
50167
|
+
throw new Error(`dumpRecord requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
48866
50168
|
}
|
|
48867
50169
|
/**
|
|
48868
50170
|
* Dumps an array of objects as a table scoped to the current signal.
|
|
48869
50171
|
*
|
|
48870
|
-
*
|
|
48871
|
-
*
|
|
50172
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50173
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48872
50174
|
*
|
|
48873
50175
|
* Column headers are derived from the union of all keys across all rows.
|
|
48874
50176
|
*
|
|
@@ -48877,8 +50179,7 @@ async function dumpRecord(dto) {
|
|
|
48877
50179
|
* @param dto.rows - Array of arbitrary objects to render as a table
|
|
48878
50180
|
* @param dto.description - Human-readable label describing the table contents; included in the BM25 index for Memory search
|
|
48879
50181
|
* @returns Promise that resolves when the dump is complete
|
|
48880
|
-
*
|
|
48881
|
-
* @deprecated Better use Dump.dumpTable with manual signalId argument
|
|
50182
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48882
50183
|
*
|
|
48883
50184
|
* @example
|
|
48884
50185
|
* ```typescript
|
|
@@ -48903,31 +50204,41 @@ async function dumpTable(dto) {
|
|
|
48903
50204
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48904
50205
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48905
50206
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48906
|
-
|
|
48907
|
-
if (
|
|
48908
|
-
|
|
50207
|
+
let signal;
|
|
50208
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50209
|
+
await Dump.dumpTable(rows, {
|
|
50210
|
+
dumpId,
|
|
50211
|
+
bucketName,
|
|
50212
|
+
signalId: signal.id,
|
|
50213
|
+
description,
|
|
50214
|
+
backtest: isBacktest,
|
|
50215
|
+
});
|
|
48909
50216
|
return;
|
|
48910
50217
|
}
|
|
48911
|
-
await
|
|
48912
|
-
|
|
48913
|
-
|
|
48914
|
-
|
|
48915
|
-
|
|
48916
|
-
|
|
50218
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50219
|
+
await Dump.dumpTable(rows, {
|
|
50220
|
+
dumpId,
|
|
50221
|
+
bucketName,
|
|
50222
|
+
signalId: signal.id,
|
|
50223
|
+
description,
|
|
50224
|
+
backtest: isBacktest,
|
|
50225
|
+
});
|
|
50226
|
+
return;
|
|
50227
|
+
}
|
|
50228
|
+
throw new Error(`dumpTable requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
48917
50229
|
}
|
|
48918
50230
|
/**
|
|
48919
50231
|
* Dumps raw text content scoped to the current signal.
|
|
48920
50232
|
*
|
|
48921
|
-
*
|
|
48922
|
-
*
|
|
50233
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50234
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48923
50235
|
*
|
|
48924
50236
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
48925
50237
|
* @param dto.dumpId - Unique identifier for this dump entry
|
|
48926
50238
|
* @param dto.content - Arbitrary text content to persist
|
|
48927
50239
|
* @param dto.description - Human-readable label describing the content; included in the BM25 index for Memory search
|
|
48928
50240
|
* @returns Promise that resolves when the dump is complete
|
|
48929
|
-
*
|
|
48930
|
-
* @deprecated Better use Dump.dumpText with manual signalId argument
|
|
50241
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48931
50242
|
*
|
|
48932
50243
|
* @example
|
|
48933
50244
|
* ```typescript
|
|
@@ -48951,31 +50262,41 @@ async function dumpText(dto) {
|
|
|
48951
50262
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48952
50263
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48953
50264
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48954
|
-
|
|
48955
|
-
if (
|
|
48956
|
-
|
|
50265
|
+
let signal;
|
|
50266
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50267
|
+
await Dump.dumpText(content, {
|
|
50268
|
+
dumpId,
|
|
50269
|
+
bucketName,
|
|
50270
|
+
signalId: signal.id,
|
|
50271
|
+
description,
|
|
50272
|
+
backtest: isBacktest,
|
|
50273
|
+
});
|
|
48957
50274
|
return;
|
|
48958
50275
|
}
|
|
48959
|
-
await
|
|
48960
|
-
|
|
48961
|
-
|
|
48962
|
-
|
|
48963
|
-
|
|
48964
|
-
|
|
50276
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50277
|
+
await Dump.dumpText(content, {
|
|
50278
|
+
dumpId,
|
|
50279
|
+
bucketName,
|
|
50280
|
+
signalId: signal.id,
|
|
50281
|
+
description,
|
|
50282
|
+
backtest: isBacktest,
|
|
50283
|
+
});
|
|
50284
|
+
return;
|
|
50285
|
+
}
|
|
50286
|
+
throw new Error(`dumpText requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
48965
50287
|
}
|
|
48966
50288
|
/**
|
|
48967
50289
|
* Dumps an error description scoped to the current signal.
|
|
48968
50290
|
*
|
|
48969
|
-
*
|
|
48970
|
-
*
|
|
50291
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50292
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48971
50293
|
*
|
|
48972
50294
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
48973
50295
|
* @param dto.dumpId - Unique identifier for this dump entry
|
|
48974
50296
|
* @param dto.content - Error message or description to persist
|
|
48975
50297
|
* @param dto.description - Human-readable label describing the error context; included in the BM25 index for Memory search
|
|
48976
50298
|
* @returns Promise that resolves when the dump is complete
|
|
48977
|
-
*
|
|
48978
|
-
* @deprecated Better use Dump.dumpError with manual signalId argument
|
|
50299
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48979
50300
|
*
|
|
48980
50301
|
* @example
|
|
48981
50302
|
* ```typescript
|
|
@@ -48999,29 +50320,41 @@ async function dumpError(dto) {
|
|
|
48999
50320
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
49000
50321
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49001
50322
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
49002
|
-
|
|
49003
|
-
if (
|
|
49004
|
-
|
|
50323
|
+
let signal;
|
|
50324
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50325
|
+
await Dump.dumpError(content, {
|
|
50326
|
+
dumpId,
|
|
50327
|
+
bucketName,
|
|
50328
|
+
signalId: signal.id,
|
|
50329
|
+
description,
|
|
50330
|
+
backtest: isBacktest,
|
|
50331
|
+
});
|
|
49005
50332
|
return;
|
|
49006
50333
|
}
|
|
49007
|
-
await
|
|
49008
|
-
|
|
49009
|
-
|
|
49010
|
-
|
|
49011
|
-
|
|
49012
|
-
|
|
50334
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50335
|
+
await Dump.dumpError(content, {
|
|
50336
|
+
dumpId,
|
|
50337
|
+
bucketName,
|
|
50338
|
+
signalId: signal.id,
|
|
50339
|
+
description,
|
|
50340
|
+
backtest: isBacktest,
|
|
50341
|
+
});
|
|
50342
|
+
return;
|
|
50343
|
+
}
|
|
50344
|
+
throw new Error(`dumpError requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
49013
50345
|
}
|
|
49014
50346
|
/**
|
|
49015
50347
|
* Dumps an arbitrary nested object as a fenced JSON block scoped to the current signal.
|
|
49016
50348
|
*
|
|
49017
|
-
*
|
|
49018
|
-
*
|
|
50349
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50350
|
+
* Automatically detects backtest/live mode from execution context.
|
|
49019
50351
|
*
|
|
49020
50352
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
49021
50353
|
* @param dto.dumpId - Unique identifier for this dump entry
|
|
49022
50354
|
* @param dto.json - Arbitrary nested object to serialize with JSON.stringify
|
|
49023
50355
|
* @param dto.description - Human-readable label describing the object contents; included in the BM25 index for Memory search
|
|
49024
50356
|
* @returns Promise that resolves when the dump is complete
|
|
50357
|
+
* @throws Error if no pending or scheduled signal exists
|
|
49025
50358
|
*
|
|
49026
50359
|
* @deprecated Prefer dumpRecord — flat key-value structure maps naturally to markdown tables and SQL storage
|
|
49027
50360
|
*
|
|
@@ -49047,17 +50380,28 @@ async function dumpJson(dto) {
|
|
|
49047
50380
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
49048
50381
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49049
50382
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
49050
|
-
|
|
49051
|
-
if (
|
|
49052
|
-
|
|
50383
|
+
let signal;
|
|
50384
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50385
|
+
await Dump.dumpJson(json, {
|
|
50386
|
+
dumpId,
|
|
50387
|
+
bucketName,
|
|
50388
|
+
signalId: signal.id,
|
|
50389
|
+
description,
|
|
50390
|
+
backtest: isBacktest,
|
|
50391
|
+
});
|
|
49053
50392
|
return;
|
|
49054
50393
|
}
|
|
49055
|
-
await
|
|
49056
|
-
|
|
49057
|
-
|
|
49058
|
-
|
|
49059
|
-
|
|
49060
|
-
|
|
50394
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50395
|
+
await Dump.dumpJson(json, {
|
|
50396
|
+
dumpId,
|
|
50397
|
+
bucketName,
|
|
50398
|
+
signalId: signal.id,
|
|
50399
|
+
description,
|
|
50400
|
+
backtest: isBacktest,
|
|
50401
|
+
});
|
|
50402
|
+
return;
|
|
50403
|
+
}
|
|
50404
|
+
throw new Error(`dumpJson requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
49061
50405
|
}
|
|
49062
50406
|
|
|
49063
50407
|
/**
|
|
@@ -59524,4 +60868,4 @@ const validateSignal = (signal, currentPrice) => {
|
|
|
59524
60868
|
return !errors.length;
|
|
59525
60869
|
};
|
|
59526
60870
|
|
|
59527
|
-
export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistIntervalAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistMemoryAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRecentAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, Storage, StorageBacktest, StorageLive, Strategy, Sync, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitSignalNotify, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMinutesSinceLatestSignalCreated, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionActiveMinutes, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getPositionWaitingMinutes, getRawCandles, getRiskSchema, getScheduledSignal, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenIdlePing, listenIdlePingOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalNotify, listenSignalNotifyOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, warmCandles, writeMemory };
|
|
60871
|
+
export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MemoryBacktest, MemoryBacktestAdapter, MemoryLive, MemoryLiveAdapter, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistIntervalAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistMemoryAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRecentAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStateAdapter, PersistStorageAdapter, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, State, StateBacktest, StateBacktestAdapter, StateLive, StateLiveAdapter, Storage, StorageBacktest, StorageLive, Strategy, Sync, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitSignalNotify, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, createSignalState, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMinutesSinceLatestSignalCreated, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionActiveMinutes, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getPositionWaitingMinutes, getRawCandles, getRiskSchema, getScheduledSignal, getSignalState, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenIdlePing, listenIdlePingOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalNotify, listenSignalNotifyOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, setSignalState, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, warmCandles, writeMemory };
|