backtest-kit 7.3.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.cjs
CHANGED
|
@@ -1017,6 +1017,13 @@ const PERSIST_MEMORY_UTILS_METHOD_NAME_LIST_DATA = "PersistMemoryUtils.listMemor
|
|
|
1017
1017
|
const PERSIST_MEMORY_UTILS_METHOD_NAME_HAS_DATA = "PersistMemoryUtils.hasMemoryData";
|
|
1018
1018
|
const PERSIST_MEMORY_UTILS_METHOD_NAME_CLEAR = "PersistMemoryUtils.clear";
|
|
1019
1019
|
const PERSIST_MEMORY_UTILS_METHOD_NAME_DISPOSE = "PersistMemoryUtils.dispose";
|
|
1020
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_USE_PERSIST_STATE_ADAPTER = "PersistStateUtils.usePersistStateAdapter";
|
|
1021
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_READ_DATA = "PersistStateUtils.readStateData";
|
|
1022
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_WRITE_DATA = "PersistStateUtils.writeStateData";
|
|
1023
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_CLEAR = "PersistStateUtils.clear";
|
|
1024
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_DISPOSE = "PersistStateUtils.dispose";
|
|
1025
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_WAIT_FOR_INIT = "PersistStateUtils.waitForInit";
|
|
1026
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_USE_DUMMY = "PersistStateUtils.useDummy";
|
|
1020
1027
|
const PERSIST_RECENT_UTILS_METHOD_NAME_USE_PERSIST_RECENT_ADAPTER = "PersistRecentUtils.usePersistRecentAdapter";
|
|
1021
1028
|
const PERSIST_RECENT_UTILS_METHOD_NAME_READ_DATA = "PersistRecentUtils.readRecentData";
|
|
1022
1029
|
const PERSIST_RECENT_UTILS_METHOD_NAME_WRITE_DATA = "PersistRecentUtils.writeRecentData";
|
|
@@ -2978,6 +2985,127 @@ class PersistRecentUtils {
|
|
|
2978
2985
|
* Used by RecentPersistBacktestUtils/RecentPersistLiveUtils for recent signal persistence.
|
|
2979
2986
|
*/
|
|
2980
2987
|
const PersistRecentAdapter = new PersistRecentUtils();
|
|
2988
|
+
/**
|
|
2989
|
+
* Utility class for managing state persistence.
|
|
2990
|
+
*
|
|
2991
|
+
* Features:
|
|
2992
|
+
* - Memoized storage instances per (signalId, bucketName) pair
|
|
2993
|
+
* - Custom adapter support
|
|
2994
|
+
* - Atomic read/write operations
|
|
2995
|
+
*
|
|
2996
|
+
* Storage layout: ./dump/state/<signalId>/<bucketName>.json
|
|
2997
|
+
*
|
|
2998
|
+
* Used by StatePersistInstance for crash-safe state persistence.
|
|
2999
|
+
*/
|
|
3000
|
+
class PersistStateUtils {
|
|
3001
|
+
constructor() {
|
|
3002
|
+
this.PersistStateFactory = PersistBase;
|
|
3003
|
+
this.getStateStorage = functoolsKit.memoize(([signalId, bucketName]) => `${signalId}:${bucketName}`, (signalId, bucketName) => Reflect.construct(this.PersistStateFactory, [
|
|
3004
|
+
bucketName,
|
|
3005
|
+
`./dump/state/${signalId}/`,
|
|
3006
|
+
]));
|
|
3007
|
+
/**
|
|
3008
|
+
* Initializes the storage for a given (signalId, bucketName) pair.
|
|
3009
|
+
*
|
|
3010
|
+
* @param signalId - Signal identifier
|
|
3011
|
+
* @param bucketName - Bucket name
|
|
3012
|
+
* @param initial - Whether this is the first initialization
|
|
3013
|
+
*/
|
|
3014
|
+
this.waitForInit = async (signalId, bucketName, initial) => {
|
|
3015
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_WAIT_FOR_INIT, {
|
|
3016
|
+
signalId,
|
|
3017
|
+
bucketName,
|
|
3018
|
+
initial,
|
|
3019
|
+
});
|
|
3020
|
+
const key = `${signalId}:${bucketName}`;
|
|
3021
|
+
const isInitial = initial && !this.getStateStorage.has(key);
|
|
3022
|
+
const stateStorage = this.getStateStorage(signalId, bucketName);
|
|
3023
|
+
await stateStorage.waitForInit(isInitial);
|
|
3024
|
+
};
|
|
3025
|
+
/**
|
|
3026
|
+
* Reads a state entry from persistence storage.
|
|
3027
|
+
*
|
|
3028
|
+
* @param signalId - Signal identifier
|
|
3029
|
+
* @param bucketName - Bucket name
|
|
3030
|
+
* @returns Promise resolving to entry data or null if not found
|
|
3031
|
+
*/
|
|
3032
|
+
this.readStateData = async (signalId, bucketName) => {
|
|
3033
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_READ_DATA, {
|
|
3034
|
+
signalId,
|
|
3035
|
+
bucketName,
|
|
3036
|
+
});
|
|
3037
|
+
const key = `${signalId}:${bucketName}`;
|
|
3038
|
+
const isInitial = !this.getStateStorage.has(key);
|
|
3039
|
+
const stateStorage = this.getStateStorage(signalId, bucketName);
|
|
3040
|
+
await stateStorage.waitForInit(isInitial);
|
|
3041
|
+
if (await stateStorage.hasValue(bucketName)) {
|
|
3042
|
+
return await stateStorage.readValue(bucketName);
|
|
3043
|
+
}
|
|
3044
|
+
return null;
|
|
3045
|
+
};
|
|
3046
|
+
/**
|
|
3047
|
+
* Writes a state entry to disk with atomic file writes.
|
|
3048
|
+
*
|
|
3049
|
+
* @param data - Entry data to persist
|
|
3050
|
+
* @param signalId - Signal identifier
|
|
3051
|
+
* @param bucketName - Bucket name
|
|
3052
|
+
*/
|
|
3053
|
+
this.writeStateData = async (data, signalId, bucketName) => {
|
|
3054
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_WRITE_DATA, {
|
|
3055
|
+
signalId,
|
|
3056
|
+
bucketName,
|
|
3057
|
+
});
|
|
3058
|
+
const key = `${signalId}:${bucketName}`;
|
|
3059
|
+
const isInitial = !this.getStateStorage.has(key);
|
|
3060
|
+
const stateStorage = this.getStateStorage(signalId, bucketName);
|
|
3061
|
+
await stateStorage.waitForInit(isInitial);
|
|
3062
|
+
await stateStorage.writeValue(bucketName, data);
|
|
3063
|
+
};
|
|
3064
|
+
/**
|
|
3065
|
+
* Switches to a dummy persist adapter that discards all writes.
|
|
3066
|
+
* All future persistence writes will be no-ops.
|
|
3067
|
+
*/
|
|
3068
|
+
this.useDummy = () => {
|
|
3069
|
+
LOGGER_SERVICE$7.log(PERSIST_STATE_UTILS_METHOD_NAME_USE_DUMMY);
|
|
3070
|
+
this.usePersistStateAdapter(PersistDummy);
|
|
3071
|
+
};
|
|
3072
|
+
/**
|
|
3073
|
+
* Clears the memoized storage cache.
|
|
3074
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
3075
|
+
* so new storage instances are created with the updated base path.
|
|
3076
|
+
*/
|
|
3077
|
+
this.clear = () => {
|
|
3078
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_CLEAR);
|
|
3079
|
+
this.getStateStorage.clear();
|
|
3080
|
+
};
|
|
3081
|
+
/**
|
|
3082
|
+
* Disposes of the state adapter and releases any resources.
|
|
3083
|
+
* Call this when a signal is removed to clean up its associated storage.
|
|
3084
|
+
*
|
|
3085
|
+
* @param signalId - Signal identifier
|
|
3086
|
+
* @param bucketName - Bucket name
|
|
3087
|
+
*/
|
|
3088
|
+
this.dispose = (signalId, bucketName) => {
|
|
3089
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_DISPOSE);
|
|
3090
|
+
const key = `${signalId}:${bucketName}`;
|
|
3091
|
+
this.getStateStorage.clear(key);
|
|
3092
|
+
};
|
|
3093
|
+
}
|
|
3094
|
+
/**
|
|
3095
|
+
* Registers a custom persistence adapter.
|
|
3096
|
+
*
|
|
3097
|
+
* @param Ctor - Custom PersistBase constructor
|
|
3098
|
+
*/
|
|
3099
|
+
usePersistStateAdapter(Ctor) {
|
|
3100
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_USE_PERSIST_STATE_ADAPTER);
|
|
3101
|
+
this.PersistStateFactory = Ctor;
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
/**
|
|
3105
|
+
* Global singleton instance of PersistStateUtils.
|
|
3106
|
+
* Used by StatePersistInstance for crash-safe state persistence.
|
|
3107
|
+
*/
|
|
3108
|
+
const PersistStateAdapter = new PersistStateUtils();
|
|
2981
3109
|
|
|
2982
3110
|
var _a$2, _b$2;
|
|
2983
3111
|
const BUSY_DELAY = 100;
|
|
@@ -10443,7 +10571,7 @@ const GET_RISK_FN = (dto, backtest, exchangeName, frameName, self) => {
|
|
|
10443
10571
|
* @param backtest - Whether running in backtest mode
|
|
10444
10572
|
* @returns Unique string key for memoization
|
|
10445
10573
|
*/
|
|
10446
|
-
const CREATE_KEY_FN$
|
|
10574
|
+
const CREATE_KEY_FN$w = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
10447
10575
|
const parts = [symbol, strategyName, exchangeName];
|
|
10448
10576
|
if (frameName)
|
|
10449
10577
|
parts.push(frameName);
|
|
@@ -10743,7 +10871,7 @@ class StrategyConnectionService {
|
|
|
10743
10871
|
* @param backtest - Whether running in backtest mode
|
|
10744
10872
|
* @returns Configured ClientStrategy instance
|
|
10745
10873
|
*/
|
|
10746
|
-
this.getStrategy = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
10874
|
+
this.getStrategy = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$w(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
10747
10875
|
const { riskName = "", riskList = [], getSignal, interval = STRATEGY_DEFAULT_INTERVAL, callbacks, } = this.strategySchemaService.get(strategyName);
|
|
10748
10876
|
return new ClientStrategy({
|
|
10749
10877
|
symbol,
|
|
@@ -11705,7 +11833,7 @@ class StrategyConnectionService {
|
|
|
11705
11833
|
}
|
|
11706
11834
|
return;
|
|
11707
11835
|
}
|
|
11708
|
-
const key = CREATE_KEY_FN$
|
|
11836
|
+
const key = CREATE_KEY_FN$w(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
11709
11837
|
if (!this.getStrategy.has(key)) {
|
|
11710
11838
|
return;
|
|
11711
11839
|
}
|
|
@@ -12879,7 +13007,7 @@ class ClientRisk {
|
|
|
12879
13007
|
* @param backtest - Whether running in backtest mode
|
|
12880
13008
|
* @returns Unique string key for memoization
|
|
12881
13009
|
*/
|
|
12882
|
-
const CREATE_KEY_FN$
|
|
13010
|
+
const CREATE_KEY_FN$v = (riskName, exchangeName, frameName, backtest) => {
|
|
12883
13011
|
const parts = [riskName, exchangeName];
|
|
12884
13012
|
if (frameName)
|
|
12885
13013
|
parts.push(frameName);
|
|
@@ -12979,7 +13107,7 @@ class RiskConnectionService {
|
|
|
12979
13107
|
* @param backtest - True if backtest mode, false if live mode
|
|
12980
13108
|
* @returns Configured ClientRisk instance
|
|
12981
13109
|
*/
|
|
12982
|
-
this.getRisk = functoolsKit.memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
13110
|
+
this.getRisk = functoolsKit.memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$v(riskName, exchangeName, frameName, backtest), (riskName, exchangeName, frameName, backtest) => {
|
|
12983
13111
|
const schema = this.riskSchemaService.get(riskName);
|
|
12984
13112
|
return new ClientRisk({
|
|
12985
13113
|
...schema,
|
|
@@ -13048,7 +13176,7 @@ class RiskConnectionService {
|
|
|
13048
13176
|
payload,
|
|
13049
13177
|
});
|
|
13050
13178
|
if (payload) {
|
|
13051
|
-
const key = CREATE_KEY_FN$
|
|
13179
|
+
const key = CREATE_KEY_FN$v(payload.riskName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
13052
13180
|
this.getRisk.clear(key);
|
|
13053
13181
|
}
|
|
13054
13182
|
else {
|
|
@@ -14167,7 +14295,7 @@ class ClientAction {
|
|
|
14167
14295
|
* @param backtest - Whether running in backtest mode
|
|
14168
14296
|
* @returns Unique string key for memoization
|
|
14169
14297
|
*/
|
|
14170
|
-
const CREATE_KEY_FN$
|
|
14298
|
+
const CREATE_KEY_FN$u = (actionName, strategyName, exchangeName, frameName, backtest) => {
|
|
14171
14299
|
const parts = [actionName, strategyName, exchangeName];
|
|
14172
14300
|
if (frameName)
|
|
14173
14301
|
parts.push(frameName);
|
|
@@ -14219,7 +14347,7 @@ class ActionConnectionService {
|
|
|
14219
14347
|
* @param backtest - True if backtest mode, false if live mode
|
|
14220
14348
|
* @returns Configured ClientAction instance
|
|
14221
14349
|
*/
|
|
14222
|
-
this.getAction = functoolsKit.memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
14350
|
+
this.getAction = functoolsKit.memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$u(actionName, strategyName, exchangeName, frameName, backtest), (actionName, strategyName, exchangeName, frameName, backtest) => {
|
|
14223
14351
|
const schema = this.actionSchemaService.get(actionName);
|
|
14224
14352
|
return new ClientAction({
|
|
14225
14353
|
...schema,
|
|
@@ -14445,7 +14573,7 @@ class ActionConnectionService {
|
|
|
14445
14573
|
await Promise.all(actions.map(async (action) => await action.dispose()));
|
|
14446
14574
|
return;
|
|
14447
14575
|
}
|
|
14448
|
-
const key = CREATE_KEY_FN$
|
|
14576
|
+
const key = CREATE_KEY_FN$u(payload.actionName, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
14449
14577
|
if (!this.getAction.has(key)) {
|
|
14450
14578
|
return;
|
|
14451
14579
|
}
|
|
@@ -14463,7 +14591,7 @@ const METHOD_NAME_VALIDATE$3 = "exchangeCoreService validate";
|
|
|
14463
14591
|
* @param exchangeName - Exchange name
|
|
14464
14592
|
* @returns Unique string key for memoization
|
|
14465
14593
|
*/
|
|
14466
|
-
const CREATE_KEY_FN$
|
|
14594
|
+
const CREATE_KEY_FN$t = (exchangeName) => {
|
|
14467
14595
|
return exchangeName;
|
|
14468
14596
|
};
|
|
14469
14597
|
/**
|
|
@@ -14487,7 +14615,7 @@ class ExchangeCoreService {
|
|
|
14487
14615
|
* @param exchangeName - Name of the exchange to validate
|
|
14488
14616
|
* @returns Promise that resolves when validation is complete
|
|
14489
14617
|
*/
|
|
14490
|
-
this.validate = functoolsKit.memoize(([exchangeName]) => CREATE_KEY_FN$
|
|
14618
|
+
this.validate = functoolsKit.memoize(([exchangeName]) => CREATE_KEY_FN$t(exchangeName), async (exchangeName) => {
|
|
14491
14619
|
this.loggerService.log(METHOD_NAME_VALIDATE$3, {
|
|
14492
14620
|
exchangeName,
|
|
14493
14621
|
});
|
|
@@ -14739,7 +14867,7 @@ const METHOD_NAME_VALIDATE$2 = "strategyCoreService validate";
|
|
|
14739
14867
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
14740
14868
|
* @returns Unique string key for memoization
|
|
14741
14869
|
*/
|
|
14742
|
-
const CREATE_KEY_FN$
|
|
14870
|
+
const CREATE_KEY_FN$s = (context) => {
|
|
14743
14871
|
const parts = [context.strategyName, context.exchangeName];
|
|
14744
14872
|
if (context.frameName)
|
|
14745
14873
|
parts.push(context.frameName);
|
|
@@ -14771,7 +14899,7 @@ class StrategyCoreService {
|
|
|
14771
14899
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
14772
14900
|
* @returns Promise that resolves when validation is complete
|
|
14773
14901
|
*/
|
|
14774
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
14902
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$s(context), async (context) => {
|
|
14775
14903
|
this.loggerService.log(METHOD_NAME_VALIDATE$2, {
|
|
14776
14904
|
context,
|
|
14777
14905
|
});
|
|
@@ -16141,7 +16269,7 @@ class SizingGlobalService {
|
|
|
16141
16269
|
* @param context - Context with riskName, exchangeName, frameName
|
|
16142
16270
|
* @returns Unique string key for memoization
|
|
16143
16271
|
*/
|
|
16144
|
-
const CREATE_KEY_FN$
|
|
16272
|
+
const CREATE_KEY_FN$r = (context) => {
|
|
16145
16273
|
const parts = [context.riskName, context.exchangeName];
|
|
16146
16274
|
if (context.frameName)
|
|
16147
16275
|
parts.push(context.frameName);
|
|
@@ -16167,7 +16295,7 @@ class RiskGlobalService {
|
|
|
16167
16295
|
* @param payload - Payload with riskName, exchangeName and frameName
|
|
16168
16296
|
* @returns Promise that resolves when validation is complete
|
|
16169
16297
|
*/
|
|
16170
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
16298
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$r(context), async (context) => {
|
|
16171
16299
|
this.loggerService.log("riskGlobalService validate", {
|
|
16172
16300
|
context,
|
|
16173
16301
|
});
|
|
@@ -16245,7 +16373,7 @@ const METHOD_NAME_VALIDATE$1 = "actionCoreService validate";
|
|
|
16245
16373
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
16246
16374
|
* @returns Unique string key for memoization
|
|
16247
16375
|
*/
|
|
16248
|
-
const CREATE_KEY_FN$
|
|
16376
|
+
const CREATE_KEY_FN$q = (context) => {
|
|
16249
16377
|
const parts = [context.strategyName, context.exchangeName];
|
|
16250
16378
|
if (context.frameName)
|
|
16251
16379
|
parts.push(context.frameName);
|
|
@@ -16289,7 +16417,7 @@ class ActionCoreService {
|
|
|
16289
16417
|
* @param context - Strategy execution context with strategyName, exchangeName and frameName
|
|
16290
16418
|
* @returns Promise that resolves when all validations complete
|
|
16291
16419
|
*/
|
|
16292
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
16420
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$q(context), async (context) => {
|
|
16293
16421
|
this.loggerService.log(METHOD_NAME_VALIDATE$1, {
|
|
16294
16422
|
context,
|
|
16295
16423
|
});
|
|
@@ -21359,7 +21487,7 @@ const ReportWriter = new ReportWriterAdapter();
|
|
|
21359
21487
|
* @param backtest - Whether running in backtest mode
|
|
21360
21488
|
* @returns Unique string key for memoization
|
|
21361
21489
|
*/
|
|
21362
|
-
const CREATE_KEY_FN$
|
|
21490
|
+
const CREATE_KEY_FN$p = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
21363
21491
|
const parts = [symbol, strategyName, exchangeName];
|
|
21364
21492
|
if (frameName)
|
|
21365
21493
|
parts.push(frameName);
|
|
@@ -21605,7 +21733,7 @@ class BacktestMarkdownService {
|
|
|
21605
21733
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
21606
21734
|
* Each combination gets its own isolated storage instance.
|
|
21607
21735
|
*/
|
|
21608
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
21736
|
+
this.getStorage = functoolsKit.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));
|
|
21609
21737
|
/**
|
|
21610
21738
|
* Processes tick events and accumulates closed signals.
|
|
21611
21739
|
* Should be called from IStrategyCallbacks.onTick.
|
|
@@ -21762,7 +21890,7 @@ class BacktestMarkdownService {
|
|
|
21762
21890
|
payload,
|
|
21763
21891
|
});
|
|
21764
21892
|
if (payload) {
|
|
21765
|
-
const key = CREATE_KEY_FN$
|
|
21893
|
+
const key = CREATE_KEY_FN$p(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
21766
21894
|
this.getStorage.clear(key);
|
|
21767
21895
|
}
|
|
21768
21896
|
else {
|
|
@@ -21824,7 +21952,7 @@ class BacktestMarkdownService {
|
|
|
21824
21952
|
* @param backtest - Whether running in backtest mode
|
|
21825
21953
|
* @returns Unique string key for memoization
|
|
21826
21954
|
*/
|
|
21827
|
-
const CREATE_KEY_FN$
|
|
21955
|
+
const CREATE_KEY_FN$o = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
21828
21956
|
const parts = [symbol, strategyName, exchangeName];
|
|
21829
21957
|
if (frameName)
|
|
21830
21958
|
parts.push(frameName);
|
|
@@ -22319,7 +22447,7 @@ class LiveMarkdownService {
|
|
|
22319
22447
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
22320
22448
|
* Each combination gets its own isolated storage instance.
|
|
22321
22449
|
*/
|
|
22322
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
22450
|
+
this.getStorage = functoolsKit.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));
|
|
22323
22451
|
/**
|
|
22324
22452
|
* Subscribes to live signal emitter to receive tick events.
|
|
22325
22453
|
* Protected against multiple subscriptions.
|
|
@@ -22537,7 +22665,7 @@ class LiveMarkdownService {
|
|
|
22537
22665
|
payload,
|
|
22538
22666
|
});
|
|
22539
22667
|
if (payload) {
|
|
22540
|
-
const key = CREATE_KEY_FN$
|
|
22668
|
+
const key = CREATE_KEY_FN$o(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
22541
22669
|
this.getStorage.clear(key);
|
|
22542
22670
|
}
|
|
22543
22671
|
else {
|
|
@@ -22557,7 +22685,7 @@ class LiveMarkdownService {
|
|
|
22557
22685
|
* @param backtest - Whether running in backtest mode
|
|
22558
22686
|
* @returns Unique string key for memoization
|
|
22559
22687
|
*/
|
|
22560
|
-
const CREATE_KEY_FN$
|
|
22688
|
+
const CREATE_KEY_FN$n = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
22561
22689
|
const parts = [symbol, strategyName, exchangeName];
|
|
22562
22690
|
if (frameName)
|
|
22563
22691
|
parts.push(frameName);
|
|
@@ -22846,7 +22974,7 @@ class ScheduleMarkdownService {
|
|
|
22846
22974
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
22847
22975
|
* Each combination gets its own isolated storage instance.
|
|
22848
22976
|
*/
|
|
22849
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
22977
|
+
this.getStorage = functoolsKit.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));
|
|
22850
22978
|
/**
|
|
22851
22979
|
* Subscribes to signal emitter to receive scheduled signal events.
|
|
22852
22980
|
* Protected against multiple subscriptions.
|
|
@@ -23049,7 +23177,7 @@ class ScheduleMarkdownService {
|
|
|
23049
23177
|
payload,
|
|
23050
23178
|
});
|
|
23051
23179
|
if (payload) {
|
|
23052
|
-
const key = CREATE_KEY_FN$
|
|
23180
|
+
const key = CREATE_KEY_FN$n(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
23053
23181
|
this.getStorage.clear(key);
|
|
23054
23182
|
}
|
|
23055
23183
|
else {
|
|
@@ -23069,7 +23197,7 @@ class ScheduleMarkdownService {
|
|
|
23069
23197
|
* @param backtest - Whether running in backtest mode
|
|
23070
23198
|
* @returns Unique string key for memoization
|
|
23071
23199
|
*/
|
|
23072
|
-
const CREATE_KEY_FN$
|
|
23200
|
+
const CREATE_KEY_FN$m = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
23073
23201
|
const parts = [symbol, strategyName, exchangeName];
|
|
23074
23202
|
if (frameName)
|
|
23075
23203
|
parts.push(frameName);
|
|
@@ -23314,7 +23442,7 @@ class PerformanceMarkdownService {
|
|
|
23314
23442
|
* Memoized function to get or create PerformanceStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
23315
23443
|
* Each combination gets its own isolated storage instance.
|
|
23316
23444
|
*/
|
|
23317
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
23445
|
+
this.getStorage = functoolsKit.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));
|
|
23318
23446
|
/**
|
|
23319
23447
|
* Subscribes to performance emitter to receive performance events.
|
|
23320
23448
|
* Protected against multiple subscriptions.
|
|
@@ -23481,7 +23609,7 @@ class PerformanceMarkdownService {
|
|
|
23481
23609
|
payload,
|
|
23482
23610
|
});
|
|
23483
23611
|
if (payload) {
|
|
23484
|
-
const key = CREATE_KEY_FN$
|
|
23612
|
+
const key = CREATE_KEY_FN$m(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
23485
23613
|
this.getStorage.clear(key);
|
|
23486
23614
|
}
|
|
23487
23615
|
else {
|
|
@@ -23960,7 +24088,7 @@ class WalkerMarkdownService {
|
|
|
23960
24088
|
* @param backtest - Whether running in backtest mode
|
|
23961
24089
|
* @returns Unique string key for memoization
|
|
23962
24090
|
*/
|
|
23963
|
-
const CREATE_KEY_FN$
|
|
24091
|
+
const CREATE_KEY_FN$l = (exchangeName, frameName, backtest) => {
|
|
23964
24092
|
const parts = [exchangeName];
|
|
23965
24093
|
if (frameName)
|
|
23966
24094
|
parts.push(frameName);
|
|
@@ -24407,7 +24535,7 @@ class HeatMarkdownService {
|
|
|
24407
24535
|
* Memoized function to get or create HeatmapStorage for exchange, frame and backtest mode.
|
|
24408
24536
|
* Each exchangeName + frameName + backtest mode combination gets its own isolated heatmap storage instance.
|
|
24409
24537
|
*/
|
|
24410
|
-
this.getStorage = functoolsKit.memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
24538
|
+
this.getStorage = functoolsKit.memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$l(exchangeName, frameName, backtest), (exchangeName, frameName, backtest) => new HeatmapStorage(exchangeName, frameName, backtest));
|
|
24411
24539
|
/**
|
|
24412
24540
|
* Subscribes to signal emitter to receive tick events.
|
|
24413
24541
|
* Protected against multiple subscriptions.
|
|
@@ -24625,7 +24753,7 @@ class HeatMarkdownService {
|
|
|
24625
24753
|
payload,
|
|
24626
24754
|
});
|
|
24627
24755
|
if (payload) {
|
|
24628
|
-
const key = CREATE_KEY_FN$
|
|
24756
|
+
const key = CREATE_KEY_FN$l(payload.exchangeName, payload.frameName, payload.backtest);
|
|
24629
24757
|
this.getStorage.clear(key);
|
|
24630
24758
|
}
|
|
24631
24759
|
else {
|
|
@@ -25656,7 +25784,7 @@ class ClientPartial {
|
|
|
25656
25784
|
* @param backtest - Whether running in backtest mode
|
|
25657
25785
|
* @returns Unique string key for memoization
|
|
25658
25786
|
*/
|
|
25659
|
-
const CREATE_KEY_FN$
|
|
25787
|
+
const CREATE_KEY_FN$k = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
25660
25788
|
/**
|
|
25661
25789
|
* Creates a callback function for emitting profit events to partialProfitSubject.
|
|
25662
25790
|
*
|
|
@@ -25778,7 +25906,7 @@ class PartialConnectionService {
|
|
|
25778
25906
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
25779
25907
|
* Value: ClientPartial instance with logger and event emitters
|
|
25780
25908
|
*/
|
|
25781
|
-
this.getPartial = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
25909
|
+
this.getPartial = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$k(signalId, backtest), (signalId, backtest) => {
|
|
25782
25910
|
return new ClientPartial({
|
|
25783
25911
|
signalId,
|
|
25784
25912
|
logger: this.loggerService,
|
|
@@ -25868,7 +25996,7 @@ class PartialConnectionService {
|
|
|
25868
25996
|
const partial = this.getPartial(data.id, backtest);
|
|
25869
25997
|
await partial.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
|
|
25870
25998
|
await partial.clear(symbol, data, priceClose, backtest);
|
|
25871
|
-
const key = CREATE_KEY_FN$
|
|
25999
|
+
const key = CREATE_KEY_FN$k(data.id, backtest);
|
|
25872
26000
|
this.getPartial.clear(key);
|
|
25873
26001
|
};
|
|
25874
26002
|
}
|
|
@@ -25884,7 +26012,7 @@ class PartialConnectionService {
|
|
|
25884
26012
|
* @param backtest - Whether running in backtest mode
|
|
25885
26013
|
* @returns Unique string key for memoization
|
|
25886
26014
|
*/
|
|
25887
|
-
const CREATE_KEY_FN$
|
|
26015
|
+
const CREATE_KEY_FN$j = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
25888
26016
|
const parts = [symbol, strategyName, exchangeName];
|
|
25889
26017
|
if (frameName)
|
|
25890
26018
|
parts.push(frameName);
|
|
@@ -26107,7 +26235,7 @@ class PartialMarkdownService {
|
|
|
26107
26235
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
26108
26236
|
* Each combination gets its own isolated storage instance.
|
|
26109
26237
|
*/
|
|
26110
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
26238
|
+
this.getStorage = functoolsKit.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));
|
|
26111
26239
|
/**
|
|
26112
26240
|
* Subscribes to partial profit/loss signal emitters to receive events.
|
|
26113
26241
|
* Protected against multiple subscriptions.
|
|
@@ -26317,7 +26445,7 @@ class PartialMarkdownService {
|
|
|
26317
26445
|
payload,
|
|
26318
26446
|
});
|
|
26319
26447
|
if (payload) {
|
|
26320
|
-
const key = CREATE_KEY_FN$
|
|
26448
|
+
const key = CREATE_KEY_FN$j(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
26321
26449
|
this.getStorage.clear(key);
|
|
26322
26450
|
}
|
|
26323
26451
|
else {
|
|
@@ -26333,7 +26461,7 @@ class PartialMarkdownService {
|
|
|
26333
26461
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
26334
26462
|
* @returns Unique string key for memoization
|
|
26335
26463
|
*/
|
|
26336
|
-
const CREATE_KEY_FN$
|
|
26464
|
+
const CREATE_KEY_FN$i = (context) => {
|
|
26337
26465
|
const parts = [context.strategyName, context.exchangeName];
|
|
26338
26466
|
if (context.frameName)
|
|
26339
26467
|
parts.push(context.frameName);
|
|
@@ -26407,7 +26535,7 @@ class PartialGlobalService {
|
|
|
26407
26535
|
* @param context - Context with strategyName, exchangeName and frameName
|
|
26408
26536
|
* @param methodName - Name of the calling method for error tracking
|
|
26409
26537
|
*/
|
|
26410
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
26538
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$i(context), (context, methodName) => {
|
|
26411
26539
|
this.loggerService.log("partialGlobalService validate", {
|
|
26412
26540
|
context,
|
|
26413
26541
|
methodName,
|
|
@@ -26862,7 +26990,7 @@ class ClientBreakeven {
|
|
|
26862
26990
|
* @param backtest - Whether running in backtest mode
|
|
26863
26991
|
* @returns Unique string key for memoization
|
|
26864
26992
|
*/
|
|
26865
|
-
const CREATE_KEY_FN$
|
|
26993
|
+
const CREATE_KEY_FN$h = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
26866
26994
|
/**
|
|
26867
26995
|
* Creates a callback function for emitting breakeven events to breakevenSubject.
|
|
26868
26996
|
*
|
|
@@ -26948,7 +27076,7 @@ class BreakevenConnectionService {
|
|
|
26948
27076
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
26949
27077
|
* Value: ClientBreakeven instance with logger and event emitter
|
|
26950
27078
|
*/
|
|
26951
|
-
this.getBreakeven = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
27079
|
+
this.getBreakeven = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$h(signalId, backtest), (signalId, backtest) => {
|
|
26952
27080
|
return new ClientBreakeven({
|
|
26953
27081
|
signalId,
|
|
26954
27082
|
logger: this.loggerService,
|
|
@@ -27009,7 +27137,7 @@ class BreakevenConnectionService {
|
|
|
27009
27137
|
const breakeven = this.getBreakeven(data.id, backtest);
|
|
27010
27138
|
await breakeven.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
|
|
27011
27139
|
await breakeven.clear(symbol, data, priceClose, backtest);
|
|
27012
|
-
const key = CREATE_KEY_FN$
|
|
27140
|
+
const key = CREATE_KEY_FN$h(data.id, backtest);
|
|
27013
27141
|
this.getBreakeven.clear(key);
|
|
27014
27142
|
};
|
|
27015
27143
|
}
|
|
@@ -27025,7 +27153,7 @@ class BreakevenConnectionService {
|
|
|
27025
27153
|
* @param backtest - Whether running in backtest mode
|
|
27026
27154
|
* @returns Unique string key for memoization
|
|
27027
27155
|
*/
|
|
27028
|
-
const CREATE_KEY_FN$
|
|
27156
|
+
const CREATE_KEY_FN$g = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
27029
27157
|
const parts = [symbol, strategyName, exchangeName];
|
|
27030
27158
|
if (frameName)
|
|
27031
27159
|
parts.push(frameName);
|
|
@@ -27200,7 +27328,7 @@ class BreakevenMarkdownService {
|
|
|
27200
27328
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
27201
27329
|
* Each combination gets its own isolated storage instance.
|
|
27202
27330
|
*/
|
|
27203
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
27331
|
+
this.getStorage = functoolsKit.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));
|
|
27204
27332
|
/**
|
|
27205
27333
|
* Subscribes to breakeven signal emitter to receive events.
|
|
27206
27334
|
* Protected against multiple subscriptions.
|
|
@@ -27389,7 +27517,7 @@ class BreakevenMarkdownService {
|
|
|
27389
27517
|
payload,
|
|
27390
27518
|
});
|
|
27391
27519
|
if (payload) {
|
|
27392
|
-
const key = CREATE_KEY_FN$
|
|
27520
|
+
const key = CREATE_KEY_FN$g(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
27393
27521
|
this.getStorage.clear(key);
|
|
27394
27522
|
}
|
|
27395
27523
|
else {
|
|
@@ -27405,7 +27533,7 @@ class BreakevenMarkdownService {
|
|
|
27405
27533
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
27406
27534
|
* @returns Unique string key for memoization
|
|
27407
27535
|
*/
|
|
27408
|
-
const CREATE_KEY_FN$
|
|
27536
|
+
const CREATE_KEY_FN$f = (context) => {
|
|
27409
27537
|
const parts = [context.strategyName, context.exchangeName];
|
|
27410
27538
|
if (context.frameName)
|
|
27411
27539
|
parts.push(context.frameName);
|
|
@@ -27479,7 +27607,7 @@ class BreakevenGlobalService {
|
|
|
27479
27607
|
* @param context - Context with strategyName, exchangeName and frameName
|
|
27480
27608
|
* @param methodName - Name of the calling method for error tracking
|
|
27481
27609
|
*/
|
|
27482
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
27610
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$f(context), (context, methodName) => {
|
|
27483
27611
|
this.loggerService.log("breakevenGlobalService validate", {
|
|
27484
27612
|
context,
|
|
27485
27613
|
methodName,
|
|
@@ -27700,7 +27828,7 @@ class ConfigValidationService {
|
|
|
27700
27828
|
* @param backtest - Whether running in backtest mode
|
|
27701
27829
|
* @returns Unique string key for memoization
|
|
27702
27830
|
*/
|
|
27703
|
-
const CREATE_KEY_FN$
|
|
27831
|
+
const CREATE_KEY_FN$e = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
27704
27832
|
const parts = [symbol, strategyName, exchangeName];
|
|
27705
27833
|
if (frameName)
|
|
27706
27834
|
parts.push(frameName);
|
|
@@ -27867,7 +27995,7 @@ class RiskMarkdownService {
|
|
|
27867
27995
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
27868
27996
|
* Each combination gets its own isolated storage instance.
|
|
27869
27997
|
*/
|
|
27870
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
27998
|
+
this.getStorage = functoolsKit.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));
|
|
27871
27999
|
/**
|
|
27872
28000
|
* Subscribes to risk rejection emitter to receive rejection events.
|
|
27873
28001
|
* Protected against multiple subscriptions.
|
|
@@ -28056,7 +28184,7 @@ class RiskMarkdownService {
|
|
|
28056
28184
|
payload,
|
|
28057
28185
|
});
|
|
28058
28186
|
if (payload) {
|
|
28059
|
-
const key = CREATE_KEY_FN$
|
|
28187
|
+
const key = CREATE_KEY_FN$e(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
28060
28188
|
this.getStorage.clear(key);
|
|
28061
28189
|
}
|
|
28062
28190
|
else {
|
|
@@ -30636,7 +30764,7 @@ class HighestProfitReportService {
|
|
|
30636
30764
|
* @returns Colon-separated key string for memoization
|
|
30637
30765
|
* @internal
|
|
30638
30766
|
*/
|
|
30639
|
-
const CREATE_KEY_FN$
|
|
30767
|
+
const CREATE_KEY_FN$d = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
30640
30768
|
const parts = [symbol, strategyName, exchangeName];
|
|
30641
30769
|
if (frameName)
|
|
30642
30770
|
parts.push(frameName);
|
|
@@ -30878,7 +31006,7 @@ class StrategyMarkdownService {
|
|
|
30878
31006
|
*
|
|
30879
31007
|
* @internal
|
|
30880
31008
|
*/
|
|
30881
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
31009
|
+
this.getStorage = functoolsKit.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));
|
|
30882
31010
|
/**
|
|
30883
31011
|
* Records a cancel-scheduled event when a scheduled signal is cancelled.
|
|
30884
31012
|
*
|
|
@@ -31452,7 +31580,7 @@ class StrategyMarkdownService {
|
|
|
31452
31580
|
this.clear = async (payload) => {
|
|
31453
31581
|
this.loggerService.log("strategyMarkdownService clear", { payload });
|
|
31454
31582
|
if (payload) {
|
|
31455
|
-
const key = CREATE_KEY_FN$
|
|
31583
|
+
const key = CREATE_KEY_FN$d(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
31456
31584
|
this.getStorage.clear(key);
|
|
31457
31585
|
}
|
|
31458
31586
|
else {
|
|
@@ -31560,7 +31688,7 @@ class StrategyMarkdownService {
|
|
|
31560
31688
|
* Creates a unique key for memoizing ReportStorage instances.
|
|
31561
31689
|
* Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
|
|
31562
31690
|
*/
|
|
31563
|
-
const CREATE_KEY_FN$
|
|
31691
|
+
const CREATE_KEY_FN$c = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
31564
31692
|
const parts = [symbol, strategyName, exchangeName];
|
|
31565
31693
|
if (frameName)
|
|
31566
31694
|
parts.push(frameName);
|
|
@@ -31753,7 +31881,7 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
31753
31881
|
class SyncMarkdownService {
|
|
31754
31882
|
constructor() {
|
|
31755
31883
|
this.loggerService = inject(TYPES.loggerService);
|
|
31756
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
31884
|
+
this.getStorage = functoolsKit.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));
|
|
31757
31885
|
/**
|
|
31758
31886
|
* Subscribes to `syncSubject` to start receiving `SignalSyncContract` events.
|
|
31759
31887
|
* Protected against multiple subscriptions via `singleshot` — subsequent calls
|
|
@@ -31951,7 +32079,7 @@ class SyncMarkdownService {
|
|
|
31951
32079
|
this.clear = async (payload) => {
|
|
31952
32080
|
this.loggerService.log("syncMarkdownService clear", { payload });
|
|
31953
32081
|
if (payload) {
|
|
31954
|
-
const key = CREATE_KEY_FN$
|
|
32082
|
+
const key = CREATE_KEY_FN$c(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
31955
32083
|
this.getStorage.clear(key);
|
|
31956
32084
|
}
|
|
31957
32085
|
else {
|
|
@@ -31964,7 +32092,7 @@ class SyncMarkdownService {
|
|
|
31964
32092
|
/**
|
|
31965
32093
|
* Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
|
|
31966
32094
|
*/
|
|
31967
|
-
const CREATE_KEY_FN$
|
|
32095
|
+
const CREATE_KEY_FN$b = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
31968
32096
|
const parts = [symbol, strategyName, exchangeName];
|
|
31969
32097
|
if (frameName)
|
|
31970
32098
|
parts.push(frameName);
|
|
@@ -32142,7 +32270,7 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
32142
32270
|
class HighestProfitMarkdownService {
|
|
32143
32271
|
constructor() {
|
|
32144
32272
|
this.loggerService = inject(TYPES.loggerService);
|
|
32145
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32273
|
+
this.getStorage = functoolsKit.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));
|
|
32146
32274
|
/**
|
|
32147
32275
|
* Subscribes to `highestProfitSubject` to start receiving `HighestProfitContract`
|
|
32148
32276
|
* events. Protected against multiple subscriptions via `singleshot` — subsequent
|
|
@@ -32308,7 +32436,7 @@ class HighestProfitMarkdownService {
|
|
|
32308
32436
|
this.clear = async (payload) => {
|
|
32309
32437
|
this.loggerService.log("highestProfitMarkdownService clear", { payload });
|
|
32310
32438
|
if (payload) {
|
|
32311
|
-
const key = CREATE_KEY_FN$
|
|
32439
|
+
const key = CREATE_KEY_FN$b(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32312
32440
|
this.getStorage.clear(key);
|
|
32313
32441
|
}
|
|
32314
32442
|
else {
|
|
@@ -32330,7 +32458,7 @@ const LISTEN_TIMEOUT$1 = 120000;
|
|
|
32330
32458
|
* @param backtest - Whether running in backtest mode
|
|
32331
32459
|
* @returns Unique string key for memoization
|
|
32332
32460
|
*/
|
|
32333
|
-
const CREATE_KEY_FN$
|
|
32461
|
+
const CREATE_KEY_FN$a = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
32334
32462
|
const parts = [symbol, strategyName, exchangeName];
|
|
32335
32463
|
if (frameName)
|
|
32336
32464
|
parts.push(frameName);
|
|
@@ -32373,7 +32501,7 @@ class PriceMetaService {
|
|
|
32373
32501
|
* Each subject holds the latest currentPrice emitted by the strategy iterator for that key.
|
|
32374
32502
|
* Instances are cached until clear() is called.
|
|
32375
32503
|
*/
|
|
32376
|
-
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32504
|
+
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$a(symbol, strategyName, exchangeName, frameName, backtest), () => new functoolsKit.BehaviorSubject());
|
|
32377
32505
|
/**
|
|
32378
32506
|
* Returns the current market price for the given symbol and context.
|
|
32379
32507
|
*
|
|
@@ -32402,10 +32530,10 @@ class PriceMetaService {
|
|
|
32402
32530
|
if (source.data) {
|
|
32403
32531
|
return source.data;
|
|
32404
32532
|
}
|
|
32405
|
-
console.warn(`PriceMetaService: No currentPrice available for ${CREATE_KEY_FN$
|
|
32533
|
+
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...`);
|
|
32406
32534
|
const currentPrice = await functoolsKit.waitForNext(source, (data) => !!data, LISTEN_TIMEOUT$1);
|
|
32407
32535
|
if (typeof currentPrice === "symbol") {
|
|
32408
|
-
throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$
|
|
32536
|
+
throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$a(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
|
|
32409
32537
|
}
|
|
32410
32538
|
return currentPrice;
|
|
32411
32539
|
};
|
|
@@ -32447,7 +32575,7 @@ class PriceMetaService {
|
|
|
32447
32575
|
this.getSource.clear();
|
|
32448
32576
|
return;
|
|
32449
32577
|
}
|
|
32450
|
-
const key = CREATE_KEY_FN$
|
|
32578
|
+
const key = CREATE_KEY_FN$a(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32451
32579
|
this.getSource.clear(key);
|
|
32452
32580
|
};
|
|
32453
32581
|
}
|
|
@@ -32465,7 +32593,7 @@ const LISTEN_TIMEOUT = 120000;
|
|
|
32465
32593
|
* @param backtest - Whether running in backtest mode
|
|
32466
32594
|
* @returns Unique string key for memoization
|
|
32467
32595
|
*/
|
|
32468
|
-
const CREATE_KEY_FN$
|
|
32596
|
+
const CREATE_KEY_FN$9 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
32469
32597
|
const parts = [symbol, strategyName, exchangeName];
|
|
32470
32598
|
if (frameName)
|
|
32471
32599
|
parts.push(frameName);
|
|
@@ -32508,7 +32636,7 @@ class TimeMetaService {
|
|
|
32508
32636
|
* Each subject holds the latest createdAt timestamp emitted by the strategy iterator for that key.
|
|
32509
32637
|
* Instances are cached until clear() is called.
|
|
32510
32638
|
*/
|
|
32511
|
-
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32639
|
+
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$9(symbol, strategyName, exchangeName, frameName, backtest), () => new functoolsKit.BehaviorSubject());
|
|
32512
32640
|
/**
|
|
32513
32641
|
* Returns the current candle timestamp (in milliseconds) for the given symbol and context.
|
|
32514
32642
|
*
|
|
@@ -32536,10 +32664,10 @@ class TimeMetaService {
|
|
|
32536
32664
|
if (source.data) {
|
|
32537
32665
|
return source.data;
|
|
32538
32666
|
}
|
|
32539
|
-
console.warn(`TimeMetaService: No timestamp available for ${CREATE_KEY_FN$
|
|
32667
|
+
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...`);
|
|
32540
32668
|
const timestamp = await functoolsKit.waitForNext(source, (data) => !!data, LISTEN_TIMEOUT);
|
|
32541
32669
|
if (typeof timestamp === "symbol") {
|
|
32542
|
-
throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$
|
|
32670
|
+
throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$9(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
|
|
32543
32671
|
}
|
|
32544
32672
|
return timestamp;
|
|
32545
32673
|
};
|
|
@@ -32581,7 +32709,7 @@ class TimeMetaService {
|
|
|
32581
32709
|
this.getSource.clear();
|
|
32582
32710
|
return;
|
|
32583
32711
|
}
|
|
32584
|
-
const key = CREATE_KEY_FN$
|
|
32712
|
+
const key = CREATE_KEY_FN$9(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32585
32713
|
this.getSource.clear(key);
|
|
32586
32714
|
};
|
|
32587
32715
|
}
|
|
@@ -32687,7 +32815,7 @@ class MaxDrawdownReportService {
|
|
|
32687
32815
|
/**
|
|
32688
32816
|
* Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
|
|
32689
32817
|
*/
|
|
32690
|
-
const CREATE_KEY_FN$
|
|
32818
|
+
const CREATE_KEY_FN$8 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
32691
32819
|
const parts = [symbol, strategyName, exchangeName];
|
|
32692
32820
|
if (frameName)
|
|
32693
32821
|
parts.push(frameName);
|
|
@@ -32813,7 +32941,7 @@ class ReportStorage {
|
|
|
32813
32941
|
class MaxDrawdownMarkdownService {
|
|
32814
32942
|
constructor() {
|
|
32815
32943
|
this.loggerService = inject(TYPES.loggerService);
|
|
32816
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32944
|
+
this.getStorage = functoolsKit.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));
|
|
32817
32945
|
/**
|
|
32818
32946
|
* Subscribes to `maxDrawdownSubject` to start receiving `MaxDrawdownContract`
|
|
32819
32947
|
* events. Protected against multiple subscriptions via `singleshot`.
|
|
@@ -32892,7 +33020,7 @@ class MaxDrawdownMarkdownService {
|
|
|
32892
33020
|
this.clear = async (payload) => {
|
|
32893
33021
|
this.loggerService.log("maxDrawdownMarkdownService clear", { payload });
|
|
32894
33022
|
if (payload) {
|
|
32895
|
-
const key = CREATE_KEY_FN$
|
|
33023
|
+
const key = CREATE_KEY_FN$8(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32896
33024
|
this.getStorage.clear(key);
|
|
32897
33025
|
}
|
|
32898
33026
|
else {
|
|
@@ -32910,7 +33038,7 @@ const METHOD_NAME_VALIDATE = "notificationHelperService.validate";
|
|
|
32910
33038
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
32911
33039
|
* @returns Unique string key for memoization
|
|
32912
33040
|
*/
|
|
32913
|
-
const CREATE_KEY_FN$
|
|
33041
|
+
const CREATE_KEY_FN$7 = (context) => {
|
|
32914
33042
|
const parts = [context.strategyName, context.exchangeName];
|
|
32915
33043
|
if (context.frameName)
|
|
32916
33044
|
parts.push(context.frameName);
|
|
@@ -32943,7 +33071,7 @@ class NotificationHelperService {
|
|
|
32943
33071
|
* @param context - Routing context: strategyName, exchangeName, frameName
|
|
32944
33072
|
* @throws {Error} If any registered schema fails validation
|
|
32945
33073
|
*/
|
|
32946
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
33074
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$7(context), async (context) => {
|
|
32947
33075
|
this.loggerService.log(METHOD_NAME_VALIDATE, {
|
|
32948
33076
|
context,
|
|
32949
33077
|
});
|
|
@@ -46487,7 +46615,7 @@ const RECENT_ADAPTER_METHOD_NAME_GET_MINUTES_SINCE_LATEST_SIGNAL = "RecentAdapte
|
|
|
46487
46615
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
46488
46616
|
* @returns Composite key string
|
|
46489
46617
|
*/
|
|
46490
|
-
const CREATE_KEY_FN$
|
|
46618
|
+
const CREATE_KEY_FN$6 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
46491
46619
|
const parts = [symbol, strategyName, exchangeName];
|
|
46492
46620
|
if (frameName)
|
|
46493
46621
|
parts.push(frameName);
|
|
@@ -46577,7 +46705,7 @@ class RecentMemoryBacktestUtils {
|
|
|
46577
46705
|
backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
46578
46706
|
signalId: event.data.id,
|
|
46579
46707
|
});
|
|
46580
|
-
const key = CREATE_KEY_FN$
|
|
46708
|
+
const key = CREATE_KEY_FN$6(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
|
|
46581
46709
|
this._signals.set(key, event.data);
|
|
46582
46710
|
};
|
|
46583
46711
|
/**
|
|
@@ -46590,7 +46718,7 @@ class RecentMemoryBacktestUtils {
|
|
|
46590
46718
|
* @returns The latest signal or null if not found
|
|
46591
46719
|
*/
|
|
46592
46720
|
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
46593
|
-
const key = CREATE_KEY_FN$
|
|
46721
|
+
const key = CREATE_KEY_FN$6(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
46594
46722
|
backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
46595
46723
|
return this._signals.get(key) ?? null;
|
|
46596
46724
|
};
|
|
@@ -46696,7 +46824,7 @@ class RecentMemoryLiveUtils {
|
|
|
46696
46824
|
backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
46697
46825
|
signalId: event.data.id,
|
|
46698
46826
|
});
|
|
46699
|
-
const key = CREATE_KEY_FN$
|
|
46827
|
+
const key = CREATE_KEY_FN$6(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
|
|
46700
46828
|
this._signals.set(key, event.data);
|
|
46701
46829
|
};
|
|
46702
46830
|
/**
|
|
@@ -46709,7 +46837,7 @@ class RecentMemoryLiveUtils {
|
|
|
46709
46837
|
* @returns The latest signal or null if not found
|
|
46710
46838
|
*/
|
|
46711
46839
|
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
46712
|
-
const key = CREATE_KEY_FN$
|
|
46840
|
+
const key = CREATE_KEY_FN$6(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
46713
46841
|
backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
46714
46842
|
return this._signals.get(key) ?? null;
|
|
46715
46843
|
};
|
|
@@ -47060,6 +47188,554 @@ const RecentLive = new RecentLiveAdapter();
|
|
|
47060
47188
|
*/
|
|
47061
47189
|
const RecentBacktest = new RecentBacktestAdapter();
|
|
47062
47190
|
|
|
47191
|
+
const CREATE_KEY_FN$5 = (signalId, bucketName) => `${signalId}_${bucketName}`;
|
|
47192
|
+
const STATE_LOCAL_INSTANCE_METHOD_NAME_GET = "StateLocalInstance.getState";
|
|
47193
|
+
const STATE_LOCAL_INSTANCE_METHOD_NAME_SET = "StateLocalInstance.setState";
|
|
47194
|
+
const STATE_PERSIST_INSTANCE_METHOD_NAME_WAIT_FOR_INIT = "StatePersistInstance.waitForInit";
|
|
47195
|
+
const STATE_PERSIST_INSTANCE_METHOD_NAME_GET = "StatePersistInstance.getState";
|
|
47196
|
+
const STATE_PERSIST_INSTANCE_METHOD_NAME_SET = "StatePersistInstance.setState";
|
|
47197
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE = "StateBacktestAdapter.dispose";
|
|
47198
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_GET = "StateBacktestAdapter.getState";
|
|
47199
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_SET = "StateBacktestAdapter.setState";
|
|
47200
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL = "StateBacktestAdapter.useLocal";
|
|
47201
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST = "StateBacktestAdapter.usePersist";
|
|
47202
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY = "StateBacktestAdapter.useDummy";
|
|
47203
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER = "StateBacktestAdapter.useStateAdapter";
|
|
47204
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_CLEAR = "StateBacktestAdapter.clear";
|
|
47205
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_DISPOSE = "StateLiveAdapter.dispose";
|
|
47206
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_GET = "StateLiveAdapter.getState";
|
|
47207
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_SET = "StateLiveAdapter.setState";
|
|
47208
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL = "StateLiveAdapter.useLocal";
|
|
47209
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST = "StateLiveAdapter.usePersist";
|
|
47210
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY = "StateLiveAdapter.useDummy";
|
|
47211
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER = "StateLiveAdapter.useStateAdapter";
|
|
47212
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_CLEAR = "StateLiveAdapter.clear";
|
|
47213
|
+
const STATE_ADAPTER_METHOD_NAME_ENABLE = "StateAdapter.enable";
|
|
47214
|
+
const STATE_ADAPTER_METHOD_NAME_DISABLE = "StateAdapter.disable";
|
|
47215
|
+
const STATE_ADAPTER_METHOD_NAME_GET = "StateAdapter.getState";
|
|
47216
|
+
const STATE_ADAPTER_METHOD_NAME_SET = "StateAdapter.setState";
|
|
47217
|
+
/**
|
|
47218
|
+
* In-process state instance backed by a plain object reference.
|
|
47219
|
+
* All data lives in process memory only - no disk persistence.
|
|
47220
|
+
*
|
|
47221
|
+
* Features:
|
|
47222
|
+
* - Mutable in-memory state with functional dispatch support
|
|
47223
|
+
* - Scoped per (signalId, bucketName) pair
|
|
47224
|
+
*
|
|
47225
|
+
* Use for backtesting and unit tests where persistence between runs is not needed.
|
|
47226
|
+
* Tracks per-trade metrics such as peakPercent and minutesOpen to implement
|
|
47227
|
+
* the capitulation rule: exit when peak < threshold after N minutes open.
|
|
47228
|
+
*/
|
|
47229
|
+
class StateLocalInstance {
|
|
47230
|
+
constructor(initialValue, signalId, bucketName) {
|
|
47231
|
+
this.initialValue = initialValue;
|
|
47232
|
+
this.signalId = signalId;
|
|
47233
|
+
this.bucketName = bucketName;
|
|
47234
|
+
/**
|
|
47235
|
+
* Initializes _value from initialValue - local state needs no async setup.
|
|
47236
|
+
* @returns Promise that resolves immediately
|
|
47237
|
+
*/
|
|
47238
|
+
this.waitForInit = functoolsKit.singleshot(async (_initial) => {
|
|
47239
|
+
this._value = this.initialValue;
|
|
47240
|
+
});
|
|
47241
|
+
/**
|
|
47242
|
+
* Update the in-memory state value.
|
|
47243
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47244
|
+
* @returns Updated state value
|
|
47245
|
+
*/
|
|
47246
|
+
this.setState = functoolsKit.queued(async (dispatch) => {
|
|
47247
|
+
backtest.loggerService.debug(STATE_LOCAL_INSTANCE_METHOD_NAME_SET, {
|
|
47248
|
+
signalId: this.signalId,
|
|
47249
|
+
bucketName: this.bucketName,
|
|
47250
|
+
});
|
|
47251
|
+
if (typeof dispatch === "function") {
|
|
47252
|
+
this._value = await dispatch(this._value);
|
|
47253
|
+
}
|
|
47254
|
+
else {
|
|
47255
|
+
this._value = dispatch;
|
|
47256
|
+
}
|
|
47257
|
+
return this._value;
|
|
47258
|
+
});
|
|
47259
|
+
}
|
|
47260
|
+
/**
|
|
47261
|
+
* Read the current in-memory state value.
|
|
47262
|
+
* @returns Current state value
|
|
47263
|
+
*/
|
|
47264
|
+
async getState() {
|
|
47265
|
+
backtest.loggerService.debug(STATE_LOCAL_INSTANCE_METHOD_NAME_GET, {
|
|
47266
|
+
signalId: this.signalId,
|
|
47267
|
+
bucketName: this.bucketName,
|
|
47268
|
+
});
|
|
47269
|
+
return this._value;
|
|
47270
|
+
}
|
|
47271
|
+
/** Releases resources held by this instance. */
|
|
47272
|
+
async dispose() {
|
|
47273
|
+
backtest.loggerService.debug(STATE_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
47274
|
+
signalId: this.signalId,
|
|
47275
|
+
bucketName: this.bucketName,
|
|
47276
|
+
});
|
|
47277
|
+
}
|
|
47278
|
+
}
|
|
47279
|
+
/**
|
|
47280
|
+
* No-op state instance that discards all writes.
|
|
47281
|
+
* Used for disabling state in tests or dry-run scenarios.
|
|
47282
|
+
*
|
|
47283
|
+
* Useful when replaying historical candles without needing to accumulate
|
|
47284
|
+
* peakPercent/minutesOpen — the capitulation rule is simply never triggered.
|
|
47285
|
+
*/
|
|
47286
|
+
class StateDummyInstance {
|
|
47287
|
+
constructor(initialValue, signalId, bucketName) {
|
|
47288
|
+
this.initialValue = initialValue;
|
|
47289
|
+
this.signalId = signalId;
|
|
47290
|
+
this.bucketName = bucketName;
|
|
47291
|
+
/**
|
|
47292
|
+
* No-op initialization.
|
|
47293
|
+
* @returns Promise that resolves immediately
|
|
47294
|
+
*/
|
|
47295
|
+
this.waitForInit = functoolsKit.singleshot(async (_initial) => {
|
|
47296
|
+
});
|
|
47297
|
+
}
|
|
47298
|
+
/**
|
|
47299
|
+
* No-op read - always returns initialValue.
|
|
47300
|
+
* @returns initialValue
|
|
47301
|
+
*/
|
|
47302
|
+
async getState() {
|
|
47303
|
+
return this.initialValue;
|
|
47304
|
+
}
|
|
47305
|
+
/**
|
|
47306
|
+
* No-op write - discards the value and returns initialValue.
|
|
47307
|
+
* @returns initialValue
|
|
47308
|
+
*/
|
|
47309
|
+
async setState(_dispatch) {
|
|
47310
|
+
return this.initialValue;
|
|
47311
|
+
}
|
|
47312
|
+
/** No-op. */
|
|
47313
|
+
async dispose() {
|
|
47314
|
+
}
|
|
47315
|
+
}
|
|
47316
|
+
/**
|
|
47317
|
+
* File-system backed state instance.
|
|
47318
|
+
* Data is persisted atomically to disk via PersistStateAdapter.
|
|
47319
|
+
* State is restored from disk on waitForInit.
|
|
47320
|
+
*
|
|
47321
|
+
* Features:
|
|
47322
|
+
* - Crash-safe atomic file writes
|
|
47323
|
+
* - Functional dispatch support
|
|
47324
|
+
* - Scoped per (signalId, bucketName) pair
|
|
47325
|
+
*
|
|
47326
|
+
* Use in live trading to survive process restarts mid-trade.
|
|
47327
|
+
* Preserves peakPercent and minutesOpen so the capitulation rule
|
|
47328
|
+
* (exit if peak < threshold after N minutes) continues correctly after a crash.
|
|
47329
|
+
*/
|
|
47330
|
+
class StatePersistInstance {
|
|
47331
|
+
constructor(initialValue, signalId, bucketName) {
|
|
47332
|
+
this.initialValue = initialValue;
|
|
47333
|
+
this.signalId = signalId;
|
|
47334
|
+
this.bucketName = bucketName;
|
|
47335
|
+
/**
|
|
47336
|
+
* Initialize persistence storage and restore state from disk.
|
|
47337
|
+
* @param initial - Whether this is the first initialization
|
|
47338
|
+
*/
|
|
47339
|
+
this.waitForInit = functoolsKit.singleshot(async (initial) => {
|
|
47340
|
+
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_WAIT_FOR_INIT, {
|
|
47341
|
+
signalId: this.signalId,
|
|
47342
|
+
bucketName: this.bucketName,
|
|
47343
|
+
initial,
|
|
47344
|
+
});
|
|
47345
|
+
await PersistStateAdapter.waitForInit(this.signalId, this.bucketName, initial);
|
|
47346
|
+
const data = await PersistStateAdapter.readStateData(this.signalId, this.bucketName);
|
|
47347
|
+
if (data) {
|
|
47348
|
+
this._value = data.data;
|
|
47349
|
+
return;
|
|
47350
|
+
}
|
|
47351
|
+
this._value = this.initialValue;
|
|
47352
|
+
});
|
|
47353
|
+
/**
|
|
47354
|
+
* Update state and persist to disk atomically.
|
|
47355
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47356
|
+
* @returns Updated state value
|
|
47357
|
+
*/
|
|
47358
|
+
this.setState = functoolsKit.queued(async (dispatch) => {
|
|
47359
|
+
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_SET, {
|
|
47360
|
+
signalId: this.signalId,
|
|
47361
|
+
bucketName: this.bucketName,
|
|
47362
|
+
});
|
|
47363
|
+
if (typeof dispatch === "function") {
|
|
47364
|
+
this._value = await dispatch(this._value);
|
|
47365
|
+
}
|
|
47366
|
+
else {
|
|
47367
|
+
this._value = dispatch;
|
|
47368
|
+
}
|
|
47369
|
+
const id = CREATE_KEY_FN$5(this.signalId, this.bucketName);
|
|
47370
|
+
await PersistStateAdapter.writeStateData({ id, data: this._value }, this.signalId, this.bucketName);
|
|
47371
|
+
return this._value;
|
|
47372
|
+
});
|
|
47373
|
+
}
|
|
47374
|
+
/**
|
|
47375
|
+
* Read the current persisted state value.
|
|
47376
|
+
* @returns Current state value
|
|
47377
|
+
*/
|
|
47378
|
+
async getState() {
|
|
47379
|
+
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_GET, {
|
|
47380
|
+
signalId: this.signalId,
|
|
47381
|
+
bucketName: this.bucketName,
|
|
47382
|
+
});
|
|
47383
|
+
return this._value;
|
|
47384
|
+
}
|
|
47385
|
+
/** Releases resources held by this instance. */
|
|
47386
|
+
async dispose() {
|
|
47387
|
+
backtest.loggerService.debug(STATE_LIVE_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
47388
|
+
signalId: this.signalId,
|
|
47389
|
+
bucketName: this.bucketName,
|
|
47390
|
+
});
|
|
47391
|
+
await PersistStateAdapter.dispose(this.signalId, this.bucketName);
|
|
47392
|
+
}
|
|
47393
|
+
}
|
|
47394
|
+
/**
|
|
47395
|
+
* Backtest state adapter with pluggable storage backend.
|
|
47396
|
+
*
|
|
47397
|
+
* Features:
|
|
47398
|
+
* - Adapter pattern for swappable state instance implementations
|
|
47399
|
+
* - Default backend: StateLocalInstance (in-memory, no disk persistence)
|
|
47400
|
+
* - Alternative backends: StatePersistInstance, StateDummyInstance
|
|
47401
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useStateAdapter()
|
|
47402
|
+
* - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from StateAdapter
|
|
47403
|
+
*
|
|
47404
|
+
* Primary use case — LLM-driven capitulation rule:
|
|
47405
|
+
* Profitable trades endure -0.5–2.5% drawdown and still reach peak 2–3%+.
|
|
47406
|
+
* SL trades never go positive (Feb25) or show peak < 0.15% (Feb08, Feb13).
|
|
47407
|
+
* Rule: if position open >= N minutes and peakPercent < threshold (e.g. 0.3%),
|
|
47408
|
+
* the LLM thesis was not confirmed by market — exit immediately.
|
|
47409
|
+
* State tracks `{ peakPercent, minutesOpen }` per signal across onActivePing ticks.
|
|
47410
|
+
*/
|
|
47411
|
+
class StateBacktestAdapter {
|
|
47412
|
+
constructor() {
|
|
47413
|
+
this.StateFactory = StateLocalInstance;
|
|
47414
|
+
this.getInstance = functoolsKit.memoize(([signalId, bucketName]) => CREATE_KEY_FN$5(signalId, bucketName), (signalId, bucketName, initialValue) => Reflect.construct(this.StateFactory, [initialValue, signalId, bucketName]));
|
|
47415
|
+
/**
|
|
47416
|
+
* Disposes all memoized instances for the given signalId.
|
|
47417
|
+
* Called by StateAdapter when a signal is cancelled or closed.
|
|
47418
|
+
* @param signalId - Signal identifier to dispose
|
|
47419
|
+
*/
|
|
47420
|
+
this.disposeSignal = (signalId) => {
|
|
47421
|
+
const prefix = CREATE_KEY_FN$5(signalId, "");
|
|
47422
|
+
for (const key of this.getInstance.keys()) {
|
|
47423
|
+
if (key.startsWith(prefix)) {
|
|
47424
|
+
const instance = this.getInstance.get(key);
|
|
47425
|
+
instance && instance.dispose();
|
|
47426
|
+
this.getInstance.clear(key);
|
|
47427
|
+
}
|
|
47428
|
+
}
|
|
47429
|
+
};
|
|
47430
|
+
/**
|
|
47431
|
+
* Read the current state value for a signal.
|
|
47432
|
+
* @param dto.signalId - Signal identifier
|
|
47433
|
+
* @param dto.bucketName - Bucket name
|
|
47434
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47435
|
+
* @returns Current state value
|
|
47436
|
+
*/
|
|
47437
|
+
this.getState = async (dto) => {
|
|
47438
|
+
backtest.loggerService.debug(STATE_BACKTEST_ADAPTER_METHOD_NAME_GET, {
|
|
47439
|
+
signalId: dto.signalId,
|
|
47440
|
+
bucketName: dto.bucketName,
|
|
47441
|
+
});
|
|
47442
|
+
const key = CREATE_KEY_FN$5(dto.signalId, dto.bucketName);
|
|
47443
|
+
const isInitial = !this.getInstance.has(key);
|
|
47444
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
47445
|
+
await instance.waitForInit(isInitial);
|
|
47446
|
+
return await instance.getState();
|
|
47447
|
+
};
|
|
47448
|
+
/**
|
|
47449
|
+
* Update the state value for a signal.
|
|
47450
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47451
|
+
* @param dto.signalId - Signal identifier
|
|
47452
|
+
* @param dto.bucketName - Bucket name
|
|
47453
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47454
|
+
* @returns Updated state value
|
|
47455
|
+
*/
|
|
47456
|
+
this.setState = async (dispatch, dto) => {
|
|
47457
|
+
backtest.loggerService.debug(STATE_BACKTEST_ADAPTER_METHOD_NAME_SET, {
|
|
47458
|
+
signalId: dto.signalId,
|
|
47459
|
+
bucketName: dto.bucketName,
|
|
47460
|
+
});
|
|
47461
|
+
const key = CREATE_KEY_FN$5(dto.signalId, dto.bucketName);
|
|
47462
|
+
const isInitial = !this.getInstance.has(key);
|
|
47463
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
47464
|
+
await instance.waitForInit(isInitial);
|
|
47465
|
+
return await instance.setState(dispatch);
|
|
47466
|
+
};
|
|
47467
|
+
/**
|
|
47468
|
+
* Switches to in-memory adapter (default).
|
|
47469
|
+
* All data lives in process memory only.
|
|
47470
|
+
*/
|
|
47471
|
+
this.useLocal = () => {
|
|
47472
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
47473
|
+
this.StateFactory = StateLocalInstance;
|
|
47474
|
+
};
|
|
47475
|
+
/**
|
|
47476
|
+
* Switches to file-system backed adapter.
|
|
47477
|
+
* Data is persisted to disk via PersistStateAdapter.
|
|
47478
|
+
*/
|
|
47479
|
+
this.usePersist = () => {
|
|
47480
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
47481
|
+
this.StateFactory = StatePersistInstance;
|
|
47482
|
+
};
|
|
47483
|
+
/**
|
|
47484
|
+
* Switches to dummy adapter that discards all writes.
|
|
47485
|
+
*/
|
|
47486
|
+
this.useDummy = () => {
|
|
47487
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
47488
|
+
this.StateFactory = StateDummyInstance;
|
|
47489
|
+
};
|
|
47490
|
+
/**
|
|
47491
|
+
* Switches to a custom state adapter implementation.
|
|
47492
|
+
* @param Ctor - Constructor for the custom state instance
|
|
47493
|
+
*/
|
|
47494
|
+
this.useStateAdapter = (Ctor) => {
|
|
47495
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER);
|
|
47496
|
+
this.StateFactory = Ctor;
|
|
47497
|
+
};
|
|
47498
|
+
/**
|
|
47499
|
+
* Clears the memoized instance cache.
|
|
47500
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
47501
|
+
* so new instances are created with the updated base path.
|
|
47502
|
+
*/
|
|
47503
|
+
this.clear = () => {
|
|
47504
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
|
|
47505
|
+
this.getInstance.clear();
|
|
47506
|
+
};
|
|
47507
|
+
}
|
|
47508
|
+
}
|
|
47509
|
+
/**
|
|
47510
|
+
* Live trading state adapter with pluggable storage backend.
|
|
47511
|
+
*
|
|
47512
|
+
* Features:
|
|
47513
|
+
* - Adapter pattern for swappable state instance implementations
|
|
47514
|
+
* - Default backend: StatePersistInstance (file-system backed, survives restarts)
|
|
47515
|
+
* - Alternative backends: StateLocalInstance, StateDummyInstance
|
|
47516
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useStateAdapter()
|
|
47517
|
+
* - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from StateAdapter
|
|
47518
|
+
*
|
|
47519
|
+
* Primary use case — LLM-driven capitulation rule:
|
|
47520
|
+
* Profitable trades endure -0.5–2.5% drawdown and still reach peak 2–3%+.
|
|
47521
|
+
* SL trades never go positive (Feb25) or show peak < 0.15% (Feb08, Feb13).
|
|
47522
|
+
* Rule: if position open >= N minutes and peakPercent < threshold (e.g. 0.3%),
|
|
47523
|
+
* the LLM thesis was not confirmed by market — exit immediately.
|
|
47524
|
+
* State persists `{ peakPercent, minutesOpen }` per signal across process restarts.
|
|
47525
|
+
*/
|
|
47526
|
+
class StateLiveAdapter {
|
|
47527
|
+
constructor() {
|
|
47528
|
+
this.StateFactory = StatePersistInstance;
|
|
47529
|
+
this.getInstance = functoolsKit.memoize(([signalId, bucketName]) => CREATE_KEY_FN$5(signalId, bucketName), (signalId, bucketName, initialValue) => Reflect.construct(this.StateFactory, [initialValue, signalId, bucketName]));
|
|
47530
|
+
/**
|
|
47531
|
+
* Disposes all memoized instances for the given signalId.
|
|
47532
|
+
* Called by StateAdapter when a signal is cancelled or closed.
|
|
47533
|
+
* @param signalId - Signal identifier to dispose
|
|
47534
|
+
*/
|
|
47535
|
+
this.disposeSignal = (signalId) => {
|
|
47536
|
+
const prefix = CREATE_KEY_FN$5(signalId, "");
|
|
47537
|
+
for (const key of this.getInstance.keys()) {
|
|
47538
|
+
if (key.startsWith(prefix)) {
|
|
47539
|
+
const instance = this.getInstance.get(key);
|
|
47540
|
+
instance && instance.dispose();
|
|
47541
|
+
this.getInstance.clear(key);
|
|
47542
|
+
}
|
|
47543
|
+
}
|
|
47544
|
+
};
|
|
47545
|
+
/**
|
|
47546
|
+
* Read the current state value for a signal.
|
|
47547
|
+
* @param dto.signalId - Signal identifier
|
|
47548
|
+
* @param dto.bucketName - Bucket name
|
|
47549
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47550
|
+
* @returns Current state value
|
|
47551
|
+
*/
|
|
47552
|
+
this.getState = async (dto) => {
|
|
47553
|
+
backtest.loggerService.debug(STATE_LIVE_ADAPTER_METHOD_NAME_GET, {
|
|
47554
|
+
signalId: dto.signalId,
|
|
47555
|
+
bucketName: dto.bucketName,
|
|
47556
|
+
});
|
|
47557
|
+
const key = CREATE_KEY_FN$5(dto.signalId, dto.bucketName);
|
|
47558
|
+
const isInitial = !this.getInstance.has(key);
|
|
47559
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
47560
|
+
await instance.waitForInit(isInitial);
|
|
47561
|
+
return await instance.getState();
|
|
47562
|
+
};
|
|
47563
|
+
/**
|
|
47564
|
+
* Update the state value for a signal.
|
|
47565
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47566
|
+
* @param dto.signalId - Signal identifier
|
|
47567
|
+
* @param dto.bucketName - Bucket name
|
|
47568
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47569
|
+
* @returns Updated state value
|
|
47570
|
+
*/
|
|
47571
|
+
this.setState = async (dispatch, dto) => {
|
|
47572
|
+
backtest.loggerService.debug(STATE_LIVE_ADAPTER_METHOD_NAME_SET, {
|
|
47573
|
+
signalId: dto.signalId,
|
|
47574
|
+
bucketName: dto.bucketName,
|
|
47575
|
+
});
|
|
47576
|
+
const key = CREATE_KEY_FN$5(dto.signalId, dto.bucketName);
|
|
47577
|
+
const isInitial = !this.getInstance.has(key);
|
|
47578
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
47579
|
+
await instance.waitForInit(isInitial);
|
|
47580
|
+
return await instance.setState(dispatch);
|
|
47581
|
+
};
|
|
47582
|
+
/**
|
|
47583
|
+
* Switches to in-memory adapter.
|
|
47584
|
+
* All data lives in process memory only.
|
|
47585
|
+
*/
|
|
47586
|
+
this.useLocal = () => {
|
|
47587
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
47588
|
+
this.StateFactory = StateLocalInstance;
|
|
47589
|
+
};
|
|
47590
|
+
/**
|
|
47591
|
+
* Switches to file-system backed adapter (default).
|
|
47592
|
+
* Data is persisted to disk via PersistStateAdapter.
|
|
47593
|
+
*/
|
|
47594
|
+
this.usePersist = () => {
|
|
47595
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
47596
|
+
this.StateFactory = StatePersistInstance;
|
|
47597
|
+
};
|
|
47598
|
+
/**
|
|
47599
|
+
* Switches to dummy adapter that discards all writes.
|
|
47600
|
+
*/
|
|
47601
|
+
this.useDummy = () => {
|
|
47602
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
47603
|
+
this.StateFactory = StateDummyInstance;
|
|
47604
|
+
};
|
|
47605
|
+
/**
|
|
47606
|
+
* Switches to a custom state adapter implementation.
|
|
47607
|
+
* @param Ctor - Constructor for the custom state instance
|
|
47608
|
+
*/
|
|
47609
|
+
this.useStateAdapter = (Ctor) => {
|
|
47610
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER);
|
|
47611
|
+
this.StateFactory = Ctor;
|
|
47612
|
+
};
|
|
47613
|
+
/**
|
|
47614
|
+
* Clears the memoized instance cache.
|
|
47615
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
47616
|
+
* so new instances are created with the updated base path.
|
|
47617
|
+
*/
|
|
47618
|
+
this.clear = () => {
|
|
47619
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_CLEAR);
|
|
47620
|
+
this.getInstance.clear();
|
|
47621
|
+
};
|
|
47622
|
+
}
|
|
47623
|
+
}
|
|
47624
|
+
/**
|
|
47625
|
+
* Main state adapter that manages both backtest and live state storage.
|
|
47626
|
+
*
|
|
47627
|
+
* Features:
|
|
47628
|
+
* - Subscribes to signal lifecycle events (cancelled/closed) to dispose stale instances
|
|
47629
|
+
* - Routes all operations to StateBacktest or StateLive based on dto.backtest
|
|
47630
|
+
* - Singleshot enable pattern prevents duplicate subscriptions
|
|
47631
|
+
* - Cleanup function for proper unsubscription
|
|
47632
|
+
*/
|
|
47633
|
+
class StateAdapter {
|
|
47634
|
+
constructor() {
|
|
47635
|
+
/**
|
|
47636
|
+
* Enables state storage by subscribing to signal lifecycle events.
|
|
47637
|
+
* Clears memoized instances in StateBacktest and StateLive when a signal
|
|
47638
|
+
* is cancelled or closed, preventing stale instances from accumulating.
|
|
47639
|
+
* Uses singleshot to ensure one-time subscription.
|
|
47640
|
+
*
|
|
47641
|
+
* @returns Cleanup function that unsubscribes from all emitters
|
|
47642
|
+
*/
|
|
47643
|
+
this.enable = functoolsKit.singleshot(() => {
|
|
47644
|
+
backtest.loggerService.info(STATE_ADAPTER_METHOD_NAME_ENABLE);
|
|
47645
|
+
const unCancel = signalEmitter
|
|
47646
|
+
.filter(({ action }) => action === "cancelled")
|
|
47647
|
+
.connect(({ signal }) => {
|
|
47648
|
+
StateBacktest.disposeSignal(signal.id);
|
|
47649
|
+
StateLive.disposeSignal(signal.id);
|
|
47650
|
+
});
|
|
47651
|
+
const unClose = signalEmitter
|
|
47652
|
+
.filter(({ action }) => action === "closed")
|
|
47653
|
+
.connect(({ signal }) => {
|
|
47654
|
+
StateBacktest.disposeSignal(signal.id);
|
|
47655
|
+
StateLive.disposeSignal(signal.id);
|
|
47656
|
+
});
|
|
47657
|
+
return functoolsKit.compose(() => unCancel(), () => unClose(), () => this.enable.clear());
|
|
47658
|
+
});
|
|
47659
|
+
/**
|
|
47660
|
+
* Disables state storage by unsubscribing from signal lifecycle events.
|
|
47661
|
+
* Safe to call multiple times.
|
|
47662
|
+
*/
|
|
47663
|
+
this.disable = () => {
|
|
47664
|
+
backtest.loggerService.info(STATE_ADAPTER_METHOD_NAME_DISABLE);
|
|
47665
|
+
if (this.enable.hasValue()) {
|
|
47666
|
+
const lastSubscription = this.enable();
|
|
47667
|
+
lastSubscription();
|
|
47668
|
+
}
|
|
47669
|
+
};
|
|
47670
|
+
/**
|
|
47671
|
+
* Read the current state value for a signal.
|
|
47672
|
+
* Routes to StateBacktest or StateLive based on dto.backtest.
|
|
47673
|
+
* @param dto.signalId - Signal identifier
|
|
47674
|
+
* @param dto.bucketName - Bucket name
|
|
47675
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47676
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47677
|
+
* @returns Current state value
|
|
47678
|
+
* @throws Error if adapter is not enabled
|
|
47679
|
+
*/
|
|
47680
|
+
this.getState = async (dto) => {
|
|
47681
|
+
if (!this.enable.hasValue()) {
|
|
47682
|
+
throw new Error("StateAdapter is not enabled. Call enable() first.");
|
|
47683
|
+
}
|
|
47684
|
+
backtest.loggerService.debug(STATE_ADAPTER_METHOD_NAME_GET, {
|
|
47685
|
+
signalId: dto.signalId,
|
|
47686
|
+
bucketName: dto.bucketName,
|
|
47687
|
+
backtest: dto.backtest,
|
|
47688
|
+
});
|
|
47689
|
+
if (dto.backtest) {
|
|
47690
|
+
return await StateBacktest.getState(dto);
|
|
47691
|
+
}
|
|
47692
|
+
return await StateLive.getState(dto);
|
|
47693
|
+
};
|
|
47694
|
+
/**
|
|
47695
|
+
* Update the state value for a signal.
|
|
47696
|
+
* Routes to StateBacktest or StateLive based on dto.backtest.
|
|
47697
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47698
|
+
* @param dto.signalId - Signal identifier
|
|
47699
|
+
* @param dto.bucketName - Bucket name
|
|
47700
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47701
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47702
|
+
* @returns Updated state value
|
|
47703
|
+
* @throws Error if adapter is not enabled
|
|
47704
|
+
*/
|
|
47705
|
+
this.setState = async (dispatch, dto) => {
|
|
47706
|
+
if (!this.enable.hasValue()) {
|
|
47707
|
+
throw new Error("StateAdapter is not enabled. Call enable() first.");
|
|
47708
|
+
}
|
|
47709
|
+
backtest.loggerService.debug(STATE_ADAPTER_METHOD_NAME_SET, {
|
|
47710
|
+
signalId: dto.signalId,
|
|
47711
|
+
bucketName: dto.bucketName,
|
|
47712
|
+
backtest: dto.backtest,
|
|
47713
|
+
});
|
|
47714
|
+
if (dto.backtest) {
|
|
47715
|
+
return await StateBacktest.setState(dispatch, dto);
|
|
47716
|
+
}
|
|
47717
|
+
return await StateLive.setState(dispatch, dto);
|
|
47718
|
+
};
|
|
47719
|
+
}
|
|
47720
|
+
}
|
|
47721
|
+
/**
|
|
47722
|
+
* Global singleton instance of StateAdapter.
|
|
47723
|
+
* Provides unified state management for backtest and live trading.
|
|
47724
|
+
*/
|
|
47725
|
+
const State = new StateAdapter();
|
|
47726
|
+
/**
|
|
47727
|
+
* Global singleton instance of StateLiveAdapter.
|
|
47728
|
+
* Provides live trading state storage with pluggable backends.
|
|
47729
|
+
*/
|
|
47730
|
+
const StateLive = new StateLiveAdapter();
|
|
47731
|
+
/**
|
|
47732
|
+
* Global singleton instance of StateBacktestAdapter.
|
|
47733
|
+
* Provides backtest state storage with pluggable backends.
|
|
47734
|
+
*/
|
|
47735
|
+
const StateBacktest = new StateBacktestAdapter();
|
|
47736
|
+
|
|
47737
|
+
const GET_SIGNAL_STATE_METHOD_NAME = "signal.getSignalState";
|
|
47738
|
+
const SET_SIGNAL_STATE_METHOD_NAME = "signal.setSignalState";
|
|
47063
47739
|
const GET_LATEST_SIGNAL_METHOD_NAME = "signal.getLatestSignal";
|
|
47064
47740
|
const GET_MINUTES_SINCE_LATEST_SIGNAL_CREATED_METHOD_NAME = "signal.getMinutesSinceLatestSignalCreated";
|
|
47065
47741
|
/**
|
|
@@ -47135,6 +47811,243 @@ async function getMinutesSinceLatestSignalCreated(symbol) {
|
|
|
47135
47811
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47136
47812
|
return await Recent.getMinutesSinceLatestSignalCreated(symbol, { exchangeName, frameName, strategyName });
|
|
47137
47813
|
}
|
|
47814
|
+
/**
|
|
47815
|
+
* Reads the state value scoped to the current active signal.
|
|
47816
|
+
*
|
|
47817
|
+
* Resolves the active pending signal automatically from execution context.
|
|
47818
|
+
* If no pending signal exists, logs a warning and returns the initialValue.
|
|
47819
|
+
*
|
|
47820
|
+
* Automatically detects backtest/live mode from execution context.
|
|
47821
|
+
*
|
|
47822
|
+
* Intended for LLM-driven capitulation strategies that accumulate per-trade
|
|
47823
|
+
* metrics (e.g. peakPercent, minutesOpen) across onActivePing ticks.
|
|
47824
|
+
* Profitable trades endure -0.5–2.5% drawdown and reach peak 2–3%+.
|
|
47825
|
+
* SL trades show peak < 0.15% (Feb08, Feb13) or never go positive (Feb25).
|
|
47826
|
+
* Rule: if minutesOpen >= N and peakPercent < threshold (e.g. 0.3%) — exit.
|
|
47827
|
+
*
|
|
47828
|
+
* @param dto.bucketName - State bucket name
|
|
47829
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47830
|
+
* @returns Promise resolving to current state value, or initialValue if no signal
|
|
47831
|
+
*
|
|
47832
|
+
* @deprecated Better use `createSignalState().getState` with codestyle native syntax
|
|
47833
|
+
*
|
|
47834
|
+
* @example
|
|
47835
|
+
* ```typescript
|
|
47836
|
+
* import { getSignalState } from "backtest-kit";
|
|
47837
|
+
*
|
|
47838
|
+
* const { peakPercent, minutesOpen } = await getSignalState({
|
|
47839
|
+
* bucketName: "trade",
|
|
47840
|
+
* initialValue: { peakPercent: 0, minutesOpen: 0 },
|
|
47841
|
+
* });
|
|
47842
|
+
* if (minutesOpen >= 15 && peakPercent < 0.3) {
|
|
47843
|
+
* await commitMarketClose(symbol); // capitulate — LLM thesis not confirmed
|
|
47844
|
+
* }
|
|
47845
|
+
* ```
|
|
47846
|
+
*/
|
|
47847
|
+
async function getSignalState(dto) {
|
|
47848
|
+
const { bucketName, initialValue } = dto;
|
|
47849
|
+
backtest.loggerService.info(GET_SIGNAL_STATE_METHOD_NAME, { bucketName });
|
|
47850
|
+
if (!ExecutionContextService.hasContext()) {
|
|
47851
|
+
throw new Error("getSignalState requires an execution context");
|
|
47852
|
+
}
|
|
47853
|
+
if (!MethodContextService.hasContext()) {
|
|
47854
|
+
throw new Error("getSignalState requires a method context");
|
|
47855
|
+
}
|
|
47856
|
+
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47857
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47858
|
+
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47859
|
+
let signal;
|
|
47860
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47861
|
+
return await State.getState({
|
|
47862
|
+
signalId: signal.id,
|
|
47863
|
+
bucketName,
|
|
47864
|
+
initialValue,
|
|
47865
|
+
backtest: isBacktest,
|
|
47866
|
+
});
|
|
47867
|
+
}
|
|
47868
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47869
|
+
return await State.getState({
|
|
47870
|
+
signalId: signal.id,
|
|
47871
|
+
bucketName,
|
|
47872
|
+
initialValue,
|
|
47873
|
+
backtest: isBacktest,
|
|
47874
|
+
});
|
|
47875
|
+
}
|
|
47876
|
+
throw new Error(`getSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
47877
|
+
}
|
|
47878
|
+
/**
|
|
47879
|
+
* Updates the state value scoped to the current active signal.
|
|
47880
|
+
*
|
|
47881
|
+
* Resolves the active pending signal automatically from execution context.
|
|
47882
|
+
* If no pending signal exists, logs a warning and returns without writing.
|
|
47883
|
+
*
|
|
47884
|
+
* Automatically detects backtest/live mode from execution context.
|
|
47885
|
+
*
|
|
47886
|
+
* Intended for LLM-driven capitulation strategies that accumulate per-trade
|
|
47887
|
+
* metrics (e.g. peakPercent, minutesOpen) across onActivePing ticks.
|
|
47888
|
+
* Profitable trades endure -0.5–2.5% drawdown and reach peak 2–3%+.
|
|
47889
|
+
* SL trades show peak < 0.15% (Feb08, Feb13) or never go positive (Feb25).
|
|
47890
|
+
* Rule: if minutesOpen >= N and peakPercent < threshold (e.g. 0.3%) — exit.
|
|
47891
|
+
*
|
|
47892
|
+
* @param dto.bucketName - State bucket name
|
|
47893
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47894
|
+
* @param dto.dispatch - New value or updater function receiving current value
|
|
47895
|
+
* @returns Promise resolving to updated state value, or initialValue if no signal
|
|
47896
|
+
*
|
|
47897
|
+
* @deprecated Better use `createSignalState().setState` with codestyle native syntax
|
|
47898
|
+
*
|
|
47899
|
+
* @example
|
|
47900
|
+
* ```typescript
|
|
47901
|
+
* import { setSignalState } from "backtest-kit";
|
|
47902
|
+
*
|
|
47903
|
+
* await setSignalState(
|
|
47904
|
+
* dispatch: (s) => ({
|
|
47905
|
+
* peakPercent: Math.max(s.peakPercent, currentUnrealisedPercent),
|
|
47906
|
+
* minutesOpen: s.minutesOpen + 1,
|
|
47907
|
+
* }),
|
|
47908
|
+
* {
|
|
47909
|
+
* bucketName: "trade",
|
|
47910
|
+
* initialValue: { peakPercent: 0, minutesOpen: 0 },
|
|
47911
|
+
* }
|
|
47912
|
+
* );
|
|
47913
|
+
* ```
|
|
47914
|
+
*/
|
|
47915
|
+
async function setSignalState(dispatch, dto) {
|
|
47916
|
+
const { bucketName, initialValue } = dto;
|
|
47917
|
+
backtest.loggerService.info(SET_SIGNAL_STATE_METHOD_NAME, { bucketName });
|
|
47918
|
+
if (!ExecutionContextService.hasContext()) {
|
|
47919
|
+
throw new Error("setSignalState requires an execution context");
|
|
47920
|
+
}
|
|
47921
|
+
if (!MethodContextService.hasContext()) {
|
|
47922
|
+
throw new Error("setSignalState requires a method context");
|
|
47923
|
+
}
|
|
47924
|
+
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47925
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47926
|
+
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47927
|
+
let signal;
|
|
47928
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47929
|
+
return await State.setState(dispatch, {
|
|
47930
|
+
signalId: signal.id,
|
|
47931
|
+
bucketName,
|
|
47932
|
+
initialValue,
|
|
47933
|
+
backtest: isBacktest,
|
|
47934
|
+
});
|
|
47935
|
+
}
|
|
47936
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47937
|
+
return await State.setState(dispatch, {
|
|
47938
|
+
signalId: signal.id,
|
|
47939
|
+
bucketName,
|
|
47940
|
+
initialValue,
|
|
47941
|
+
backtest: isBacktest,
|
|
47942
|
+
});
|
|
47943
|
+
}
|
|
47944
|
+
throw new Error(`setSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
47945
|
+
}
|
|
47946
|
+
|
|
47947
|
+
const CREATE_SIGNAL_STATE_METHOD_NAME = "state.createSignalState";
|
|
47948
|
+
const CREATE_SET_STATE_FN = (params) => async (dispatch) => {
|
|
47949
|
+
if (!ExecutionContextService.hasContext()) {
|
|
47950
|
+
throw new Error("createSignalState requires an execution context");
|
|
47951
|
+
}
|
|
47952
|
+
if (!MethodContextService.hasContext()) {
|
|
47953
|
+
throw new Error("createSignalState requires a method context");
|
|
47954
|
+
}
|
|
47955
|
+
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47956
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47957
|
+
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47958
|
+
let signal;
|
|
47959
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47960
|
+
return await State.setState(dispatch, {
|
|
47961
|
+
backtest: isBacktest,
|
|
47962
|
+
bucketName: params.bucketName,
|
|
47963
|
+
initialValue: params.initialValue,
|
|
47964
|
+
signalId: signal.id,
|
|
47965
|
+
});
|
|
47966
|
+
}
|
|
47967
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47968
|
+
return await State.setState(dispatch, {
|
|
47969
|
+
backtest: isBacktest,
|
|
47970
|
+
bucketName: params.bucketName,
|
|
47971
|
+
initialValue: params.initialValue,
|
|
47972
|
+
signalId: signal.id,
|
|
47973
|
+
});
|
|
47974
|
+
}
|
|
47975
|
+
throw new Error(`createSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${params.bucketName}`);
|
|
47976
|
+
};
|
|
47977
|
+
const CREATE_GET_STATE_FN = (params) => async () => {
|
|
47978
|
+
if (!ExecutionContextService.hasContext()) {
|
|
47979
|
+
throw new Error("createSignalState requires an execution context");
|
|
47980
|
+
}
|
|
47981
|
+
if (!MethodContextService.hasContext()) {
|
|
47982
|
+
throw new Error("createSignalState requires a method context");
|
|
47983
|
+
}
|
|
47984
|
+
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47985
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47986
|
+
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47987
|
+
let signal;
|
|
47988
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47989
|
+
return await State.getState({
|
|
47990
|
+
backtest: isBacktest,
|
|
47991
|
+
bucketName: params.bucketName,
|
|
47992
|
+
initialValue: params.initialValue,
|
|
47993
|
+
signalId: signal.id,
|
|
47994
|
+
});
|
|
47995
|
+
}
|
|
47996
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
47997
|
+
return await State.getState({
|
|
47998
|
+
backtest: isBacktest,
|
|
47999
|
+
bucketName: params.bucketName,
|
|
48000
|
+
initialValue: params.initialValue,
|
|
48001
|
+
signalId: signal.id,
|
|
48002
|
+
});
|
|
48003
|
+
}
|
|
48004
|
+
throw new Error(`createSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${params.bucketName}`);
|
|
48005
|
+
};
|
|
48006
|
+
/**
|
|
48007
|
+
* Creates a bound [getState, setState] tuple scoped to a bucket and initial value.
|
|
48008
|
+
*
|
|
48009
|
+
* Both returned functions resolve the active pending or scheduled signal and the
|
|
48010
|
+
* backtest/live flag automatically from execution context — no signalId argument required.
|
|
48011
|
+
*
|
|
48012
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48013
|
+
*
|
|
48014
|
+
* Intended for LLM-driven capitulation strategies that accumulate per-trade
|
|
48015
|
+
* metrics (e.g. peakPercent, minutesOpen) across onActivePing ticks.
|
|
48016
|
+
* Profitable trades endure -0.5–2.5% drawdown and reach peak 2–3%+.
|
|
48017
|
+
* SL trades show peak < 0.15% (Feb08, Feb13) or never go positive (Feb25).
|
|
48018
|
+
* Rule: if minutesOpen >= N and peakPercent < threshold (e.g. 0.3%) — exit.
|
|
48019
|
+
*
|
|
48020
|
+
* @param params.bucketName - Logical namespace for grouping state buckets within a signal
|
|
48021
|
+
* @param params.initialValue - Default value when no persisted state exists
|
|
48022
|
+
* @returns Tuple [getState, setState] bound to the bucket and initial value
|
|
48023
|
+
*
|
|
48024
|
+
* @example
|
|
48025
|
+
* ```typescript
|
|
48026
|
+
* import { createSignalState } from "backtest-kit";
|
|
48027
|
+
*
|
|
48028
|
+
* const [getTradeState, setTradeState] = createSignalState({
|
|
48029
|
+
* bucketName: "trade",
|
|
48030
|
+
* initialValue: { peakPercent: 0, minutesOpen: 0 },
|
|
48031
|
+
* });
|
|
48032
|
+
*
|
|
48033
|
+
* // in onActivePing:
|
|
48034
|
+
* await setTradeState((s) => ({
|
|
48035
|
+
* peakPercent: Math.max(s.peakPercent, currentUnrealisedPercent),
|
|
48036
|
+
* minutesOpen: s.minutesOpen + 1,
|
|
48037
|
+
* }));
|
|
48038
|
+
* const { peakPercent, minutesOpen } = await getTradeState();
|
|
48039
|
+
* if (minutesOpen >= 15 && peakPercent < 0.3) await commitMarketClose(symbol);
|
|
48040
|
+
* ```
|
|
48041
|
+
*/
|
|
48042
|
+
function createSignalState(params) {
|
|
48043
|
+
backtest.loggerService.info(CREATE_SIGNAL_STATE_METHOD_NAME, {
|
|
48044
|
+
bucketName: params.bucketName,
|
|
48045
|
+
});
|
|
48046
|
+
return [
|
|
48047
|
+
CREATE_GET_STATE_FN(params),
|
|
48048
|
+
CREATE_SET_STATE_FN(params),
|
|
48049
|
+
];
|
|
48050
|
+
}
|
|
47138
48051
|
|
|
47139
48052
|
const DEFAULT_BM25_K1 = 1.5;
|
|
47140
48053
|
const DEFAULT_BM25_B = 0.75;
|
|
@@ -47221,7 +48134,7 @@ const createSearchIndex = () => {
|
|
|
47221
48134
|
return { upsert, remove, list, search, read };
|
|
47222
48135
|
};
|
|
47223
48136
|
|
|
47224
|
-
const CREATE_KEY_FN$4 = (signalId, bucketName) => `${signalId}
|
|
48137
|
+
const CREATE_KEY_FN$4 = (signalId, bucketName) => `${signalId}_${bucketName}`;
|
|
47225
48138
|
const LIST_MEMORY_FN = ({ id, content }) => ({
|
|
47226
48139
|
memoryId: id,
|
|
47227
48140
|
content: content,
|
|
@@ -47242,18 +48155,35 @@ const MEMORY_PERSIST_INSTANCE_METHOD_NAME_READ = "MemoryPersistInstance.readMemo
|
|
|
47242
48155
|
const MEMORY_PERSIST_INSTANCE_METHOD_NAME_SEARCH = "MemoryPersistInstance.searchMemory";
|
|
47243
48156
|
const MEMORY_PERSIST_INSTANCE_METHOD_NAME_LIST = "MemoryPersistInstance.listMemory";
|
|
47244
48157
|
const MEMORY_PERSIST_INSTANCE_METHOD_NAME_REMOVE = "MemoryPersistInstance.removeMemory";
|
|
48158
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE = "MemoryBacktestAdapter.dispose";
|
|
48159
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_WRITE = "MemoryBacktestAdapter.writeMemory";
|
|
48160
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_SEARCH = "MemoryBacktestAdapter.searchMemory";
|
|
48161
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_LIST = "MemoryBacktestAdapter.listMemory";
|
|
48162
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_REMOVE = "MemoryBacktestAdapter.removeMemory";
|
|
48163
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_READ = "MemoryBacktestAdapter.readMemory";
|
|
48164
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL = "MemoryBacktestAdapter.useLocal";
|
|
48165
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST = "MemoryBacktestAdapter.usePersist";
|
|
48166
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY = "MemoryBacktestAdapter.useDummy";
|
|
48167
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER = "MemoryBacktestAdapter.useMemoryAdapter";
|
|
48168
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_CLEAR = "MemoryBacktestAdapter.clear";
|
|
48169
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_DISPOSE = "MemoryLiveAdapter.dispose";
|
|
48170
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_WRITE = "MemoryLiveAdapter.writeMemory";
|
|
48171
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_SEARCH = "MemoryLiveAdapter.searchMemory";
|
|
48172
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_LIST = "MemoryLiveAdapter.listMemory";
|
|
48173
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_REMOVE = "MemoryLiveAdapter.removeMemory";
|
|
48174
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_READ = "MemoryLiveAdapter.readMemory";
|
|
48175
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL = "MemoryLiveAdapter.useLocal";
|
|
48176
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST = "MemoryLiveAdapter.usePersist";
|
|
48177
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY = "MemoryLiveAdapter.useDummy";
|
|
48178
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER = "MemoryLiveAdapter.useMemoryAdapter";
|
|
48179
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_CLEAR = "MemoryLiveAdapter.clear";
|
|
47245
48180
|
const MEMORY_ADAPTER_METHOD_NAME_ENABLE = "MemoryAdapter.enable";
|
|
47246
48181
|
const MEMORY_ADAPTER_METHOD_NAME_DISABLE = "MemoryAdapter.disable";
|
|
47247
|
-
const MEMORY_ADAPTER_METHOD_NAME_DISPOSE = "MemoryAdapter.dispose";
|
|
47248
48182
|
const MEMORY_ADAPTER_METHOD_NAME_WRITE = "MemoryAdapter.writeMemory";
|
|
47249
48183
|
const MEMORY_ADAPTER_METHOD_NAME_SEARCH = "MemoryAdapter.searchMemory";
|
|
47250
48184
|
const MEMORY_ADAPTER_METHOD_NAME_LIST = "MemoryAdapter.listMemory";
|
|
47251
48185
|
const MEMORY_ADAPTER_METHOD_NAME_REMOVE = "MemoryAdapter.removeMemory";
|
|
47252
48186
|
const MEMORY_ADAPTER_METHOD_NAME_READ = "MemoryAdapter.readMemory";
|
|
47253
|
-
const MEMORY_ADAPTER_METHOD_NAME_USE_LOCAL = "MemoryAdapter.useLocal";
|
|
47254
|
-
const MEMORY_ADAPTER_METHOD_NAME_USE_PERSIST = "MemoryAdapter.usePersist";
|
|
47255
|
-
const MEMORY_ADAPTER_METHOD_NAME_USE_DUMMY = "MemoryAdapter.useDummy";
|
|
47256
|
-
const MEMORY_ADAPTER_METHOD_NAME_CLEAR = "MemoryAdapter.clear";
|
|
47257
48187
|
/**
|
|
47258
48188
|
* In-memory BM25 search index backed instance.
|
|
47259
48189
|
* All data lives in the process memory only - no disk persistence.
|
|
@@ -47278,7 +48208,7 @@ class MemoryLocalInstance {
|
|
|
47278
48208
|
* Write a value into the BM25 index.
|
|
47279
48209
|
* @param memoryId - Unique entry identifier
|
|
47280
48210
|
* @param value - Value to store and index
|
|
47281
|
-
* @param
|
|
48211
|
+
* @param description - BM25 index string
|
|
47282
48212
|
*/
|
|
47283
48213
|
async writeMemory(memoryId, value, description) {
|
|
47284
48214
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_WRITE, {
|
|
@@ -47349,7 +48279,7 @@ class MemoryLocalInstance {
|
|
|
47349
48279
|
}
|
|
47350
48280
|
/** Releases resources held by this instance. */
|
|
47351
48281
|
dispose() {
|
|
47352
|
-
backtest.loggerService.debug(
|
|
48282
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
47353
48283
|
signalId: this.signalId,
|
|
47354
48284
|
bucketName: this.bucketName,
|
|
47355
48285
|
});
|
|
@@ -47398,7 +48328,7 @@ class MemoryPersistInstance {
|
|
|
47398
48328
|
* Write a value to disk and update the BM25 index.
|
|
47399
48329
|
* @param memoryId - Unique entry identifier
|
|
47400
48330
|
* @param value - Value to persist and index
|
|
47401
|
-
* @param index -
|
|
48331
|
+
* @param index - BM25 index string; defaults to JSON.stringify(value)
|
|
47402
48332
|
*/
|
|
47403
48333
|
async writeMemory(memoryId, value, index = JSON.stringify(value)) {
|
|
47404
48334
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_WRITE, {
|
|
@@ -47472,7 +48402,7 @@ class MemoryPersistInstance {
|
|
|
47472
48402
|
}
|
|
47473
48403
|
/** Releases resources held by this instance. */
|
|
47474
48404
|
dispose() {
|
|
47475
|
-
backtest.loggerService.debug(
|
|
48405
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
47476
48406
|
signalId: this.signalId,
|
|
47477
48407
|
bucketName: this.bucketName,
|
|
47478
48408
|
});
|
|
@@ -47532,48 +48462,377 @@ class MemoryDummyInstance {
|
|
|
47532
48462
|
}
|
|
47533
48463
|
}
|
|
47534
48464
|
/**
|
|
47535
|
-
*
|
|
47536
|
-
* Manages lazy initialization and instance lifecycle.
|
|
48465
|
+
* Backtest memory adapter with pluggable storage backend.
|
|
47537
48466
|
*
|
|
47538
48467
|
* Features:
|
|
47539
|
-
* -
|
|
47540
|
-
* -
|
|
47541
|
-
* -
|
|
48468
|
+
* - Adapter pattern for swappable memory instance implementations
|
|
48469
|
+
* - Default backend: MemoryLocalInstance (in-memory BM25, no disk persistence)
|
|
48470
|
+
* - Alternative backends: MemoryPersistInstance, MemoryDummyInstance
|
|
48471
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useMemoryAdapter()
|
|
48472
|
+
* - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from MemoryAdapter
|
|
48473
|
+
*
|
|
48474
|
+
* Use this adapter for backtest memory storage.
|
|
47542
48475
|
*/
|
|
47543
|
-
class
|
|
48476
|
+
class MemoryBacktestAdapter {
|
|
48477
|
+
constructor() {
|
|
48478
|
+
this.MemoryFactory = MemoryLocalInstance;
|
|
48479
|
+
this.getInstance = functoolsKit.memoize(([signalId, bucketName]) => CREATE_KEY_FN$4(signalId, bucketName), (signalId, bucketName) => Reflect.construct(this.MemoryFactory, [signalId, bucketName]));
|
|
48480
|
+
/**
|
|
48481
|
+
* Disposes all memoized instances for the given signalId.
|
|
48482
|
+
* Called by MemoryAdapter when a signal is cancelled or closed.
|
|
48483
|
+
* @param signalId - Signal identifier to dispose
|
|
48484
|
+
*/
|
|
48485
|
+
this.disposeSignal = (signalId) => {
|
|
48486
|
+
const prefix = CREATE_KEY_FN$4(signalId, "");
|
|
48487
|
+
for (const key of this.getInstance.keys()) {
|
|
48488
|
+
if (key.startsWith(prefix)) {
|
|
48489
|
+
const instance = this.getInstance.get(key);
|
|
48490
|
+
instance && instance.dispose();
|
|
48491
|
+
this.getInstance.clear(key);
|
|
48492
|
+
}
|
|
48493
|
+
}
|
|
48494
|
+
};
|
|
48495
|
+
/**
|
|
48496
|
+
* Write a value to memory.
|
|
48497
|
+
* @param dto.memoryId - Unique entry identifier
|
|
48498
|
+
* @param dto.value - Value to store
|
|
48499
|
+
* @param dto.signalId - Signal identifier
|
|
48500
|
+
* @param dto.bucketName - Bucket name
|
|
48501
|
+
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
48502
|
+
*/
|
|
48503
|
+
this.writeMemory = async (dto) => {
|
|
48504
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_WRITE, {
|
|
48505
|
+
signalId: dto.signalId,
|
|
48506
|
+
bucketName: dto.bucketName,
|
|
48507
|
+
memoryId: dto.memoryId,
|
|
48508
|
+
});
|
|
48509
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48510
|
+
const isInitial = !this.getInstance.has(key);
|
|
48511
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48512
|
+
await instance.waitForInit(isInitial);
|
|
48513
|
+
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
48514
|
+
};
|
|
48515
|
+
/**
|
|
48516
|
+
* Search memory using BM25 full-text scoring.
|
|
48517
|
+
* @param dto.query - Search query string
|
|
48518
|
+
* @param dto.signalId - Signal identifier
|
|
48519
|
+
* @param dto.bucketName - Bucket name
|
|
48520
|
+
* @returns Matching entries sorted by relevance score
|
|
48521
|
+
*/
|
|
48522
|
+
this.searchMemory = async (dto) => {
|
|
48523
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_SEARCH, {
|
|
48524
|
+
signalId: dto.signalId,
|
|
48525
|
+
bucketName: dto.bucketName,
|
|
48526
|
+
query: dto.query,
|
|
48527
|
+
});
|
|
48528
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48529
|
+
const isInitial = !this.getInstance.has(key);
|
|
48530
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48531
|
+
await instance.waitForInit(isInitial);
|
|
48532
|
+
return await instance.searchMemory(dto.query, dto.settings);
|
|
48533
|
+
};
|
|
48534
|
+
/**
|
|
48535
|
+
* List all entries in memory.
|
|
48536
|
+
* @param dto.signalId - Signal identifier
|
|
48537
|
+
* @param dto.bucketName - Bucket name
|
|
48538
|
+
* @returns Array of all stored entries
|
|
48539
|
+
*/
|
|
48540
|
+
this.listMemory = async (dto) => {
|
|
48541
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_LIST, {
|
|
48542
|
+
signalId: dto.signalId,
|
|
48543
|
+
bucketName: dto.bucketName,
|
|
48544
|
+
});
|
|
48545
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48546
|
+
const isInitial = !this.getInstance.has(key);
|
|
48547
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48548
|
+
await instance.waitForInit(isInitial);
|
|
48549
|
+
return await instance.listMemory();
|
|
48550
|
+
};
|
|
48551
|
+
/**
|
|
48552
|
+
* Remove an entry from memory.
|
|
48553
|
+
* @param dto.memoryId - Unique entry identifier
|
|
48554
|
+
* @param dto.signalId - Signal identifier
|
|
48555
|
+
* @param dto.bucketName - Bucket name
|
|
48556
|
+
*/
|
|
48557
|
+
this.removeMemory = async (dto) => {
|
|
48558
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_REMOVE, {
|
|
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.removeMemory(dto.memoryId);
|
|
48568
|
+
};
|
|
48569
|
+
/**
|
|
48570
|
+
* Read a single entry from memory.
|
|
48571
|
+
* @param dto.memoryId - Unique entry identifier
|
|
48572
|
+
* @param dto.signalId - Signal identifier
|
|
48573
|
+
* @param dto.bucketName - Bucket name
|
|
48574
|
+
* @returns Entry value
|
|
48575
|
+
* @throws Error if entry not found
|
|
48576
|
+
*/
|
|
48577
|
+
this.readMemory = async (dto) => {
|
|
48578
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_READ, {
|
|
48579
|
+
signalId: dto.signalId,
|
|
48580
|
+
bucketName: dto.bucketName,
|
|
48581
|
+
memoryId: dto.memoryId,
|
|
48582
|
+
});
|
|
48583
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48584
|
+
const isInitial = !this.getInstance.has(key);
|
|
48585
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48586
|
+
await instance.waitForInit(isInitial);
|
|
48587
|
+
return await instance.readMemory(dto.memoryId);
|
|
48588
|
+
};
|
|
48589
|
+
/**
|
|
48590
|
+
* Switches to in-memory BM25 adapter (default).
|
|
48591
|
+
* All data lives in process memory only.
|
|
48592
|
+
*/
|
|
48593
|
+
this.useLocal = () => {
|
|
48594
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
48595
|
+
this.MemoryFactory = MemoryLocalInstance;
|
|
48596
|
+
};
|
|
48597
|
+
/**
|
|
48598
|
+
* Switches to file-system backed adapter.
|
|
48599
|
+
* Data is persisted to ./dump/memory/<signalId>/<bucketName>/.
|
|
48600
|
+
*/
|
|
48601
|
+
this.usePersist = () => {
|
|
48602
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
48603
|
+
this.MemoryFactory = MemoryPersistInstance;
|
|
48604
|
+
};
|
|
48605
|
+
/**
|
|
48606
|
+
* Switches to dummy adapter that discards all writes.
|
|
48607
|
+
*/
|
|
48608
|
+
this.useDummy = () => {
|
|
48609
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
48610
|
+
this.MemoryFactory = MemoryDummyInstance;
|
|
48611
|
+
};
|
|
48612
|
+
/**
|
|
48613
|
+
* Switches to a custom memory adapter implementation.
|
|
48614
|
+
* @param Ctor - Constructor for the custom memory instance
|
|
48615
|
+
*/
|
|
48616
|
+
this.useMemoryAdapter = (Ctor) => {
|
|
48617
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER);
|
|
48618
|
+
this.MemoryFactory = Ctor;
|
|
48619
|
+
};
|
|
48620
|
+
/**
|
|
48621
|
+
* Clears the memoized instance cache.
|
|
48622
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
48623
|
+
* so new instances are created with the updated base path.
|
|
48624
|
+
*/
|
|
48625
|
+
this.clear = () => {
|
|
48626
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
|
|
48627
|
+
this.getInstance.clear();
|
|
48628
|
+
};
|
|
48629
|
+
}
|
|
48630
|
+
}
|
|
48631
|
+
/**
|
|
48632
|
+
* Live trading memory adapter with pluggable storage backend.
|
|
48633
|
+
*
|
|
48634
|
+
* Features:
|
|
48635
|
+
* - Adapter pattern for swappable memory instance implementations
|
|
48636
|
+
* - Default backend: MemoryPersistInstance (file-system backed, survives restarts)
|
|
48637
|
+
* - Alternative backends: MemoryLocalInstance, MemoryDummyInstance
|
|
48638
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useMemoryAdapter()
|
|
48639
|
+
* - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from MemoryAdapter
|
|
48640
|
+
*
|
|
48641
|
+
* Use this adapter for live trading memory storage.
|
|
48642
|
+
*/
|
|
48643
|
+
class MemoryLiveAdapter {
|
|
47544
48644
|
constructor() {
|
|
47545
48645
|
this.MemoryFactory = MemoryPersistInstance;
|
|
47546
48646
|
this.getInstance = functoolsKit.memoize(([signalId, bucketName]) => CREATE_KEY_FN$4(signalId, bucketName), (signalId, bucketName) => Reflect.construct(this.MemoryFactory, [signalId, bucketName]));
|
|
47547
48647
|
/**
|
|
47548
|
-
*
|
|
47549
|
-
*
|
|
47550
|
-
*
|
|
47551
|
-
|
|
47552
|
-
|
|
48648
|
+
* Disposes all memoized instances for the given signalId.
|
|
48649
|
+
* Called by MemoryAdapter when a signal is cancelled or closed.
|
|
48650
|
+
* @param signalId - Signal identifier to dispose
|
|
48651
|
+
*/
|
|
48652
|
+
this.disposeSignal = (signalId) => {
|
|
48653
|
+
const prefix = CREATE_KEY_FN$4(signalId, "");
|
|
48654
|
+
for (const key of this.getInstance.keys()) {
|
|
48655
|
+
if (key.startsWith(prefix)) {
|
|
48656
|
+
const instance = this.getInstance.get(key);
|
|
48657
|
+
instance && instance.dispose();
|
|
48658
|
+
this.getInstance.clear(key);
|
|
48659
|
+
}
|
|
48660
|
+
}
|
|
48661
|
+
};
|
|
48662
|
+
/**
|
|
48663
|
+
* Write a value to memory.
|
|
48664
|
+
* @param dto.memoryId - Unique entry identifier
|
|
48665
|
+
* @param dto.value - Value to store
|
|
48666
|
+
* @param dto.signalId - Signal identifier
|
|
48667
|
+
* @param dto.bucketName - Bucket name
|
|
48668
|
+
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
48669
|
+
*/
|
|
48670
|
+
this.writeMemory = async (dto) => {
|
|
48671
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_WRITE, {
|
|
48672
|
+
signalId: dto.signalId,
|
|
48673
|
+
bucketName: dto.bucketName,
|
|
48674
|
+
memoryId: dto.memoryId,
|
|
48675
|
+
});
|
|
48676
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48677
|
+
const isInitial = !this.getInstance.has(key);
|
|
48678
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48679
|
+
await instance.waitForInit(isInitial);
|
|
48680
|
+
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
48681
|
+
};
|
|
48682
|
+
/**
|
|
48683
|
+
* Search memory using BM25 full-text scoring.
|
|
48684
|
+
* @param dto.query - Search query string
|
|
48685
|
+
* @param dto.signalId - Signal identifier
|
|
48686
|
+
* @param dto.bucketName - Bucket name
|
|
48687
|
+
* @returns Matching entries sorted by relevance score
|
|
48688
|
+
*/
|
|
48689
|
+
this.searchMemory = async (dto) => {
|
|
48690
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_SEARCH, {
|
|
48691
|
+
signalId: dto.signalId,
|
|
48692
|
+
bucketName: dto.bucketName,
|
|
48693
|
+
query: dto.query,
|
|
48694
|
+
});
|
|
48695
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48696
|
+
const isInitial = !this.getInstance.has(key);
|
|
48697
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48698
|
+
await instance.waitForInit(isInitial);
|
|
48699
|
+
return await instance.searchMemory(dto.query, dto.settings);
|
|
48700
|
+
};
|
|
48701
|
+
/**
|
|
48702
|
+
* List all entries in memory.
|
|
48703
|
+
* @param dto.signalId - Signal identifier
|
|
48704
|
+
* @param dto.bucketName - Bucket name
|
|
48705
|
+
* @returns Array of all stored entries
|
|
48706
|
+
*/
|
|
48707
|
+
this.listMemory = async (dto) => {
|
|
48708
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_LIST, {
|
|
48709
|
+
signalId: dto.signalId,
|
|
48710
|
+
bucketName: dto.bucketName,
|
|
48711
|
+
});
|
|
48712
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48713
|
+
const isInitial = !this.getInstance.has(key);
|
|
48714
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48715
|
+
await instance.waitForInit(isInitial);
|
|
48716
|
+
return await instance.listMemory();
|
|
48717
|
+
};
|
|
48718
|
+
/**
|
|
48719
|
+
* Remove an entry from memory.
|
|
48720
|
+
* @param dto.memoryId - Unique entry identifier
|
|
48721
|
+
* @param dto.signalId - Signal identifier
|
|
48722
|
+
* @param dto.bucketName - Bucket name
|
|
48723
|
+
*/
|
|
48724
|
+
this.removeMemory = async (dto) => {
|
|
48725
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_REMOVE, {
|
|
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.removeMemory(dto.memoryId);
|
|
48735
|
+
};
|
|
48736
|
+
/**
|
|
48737
|
+
* Read a single entry from memory.
|
|
48738
|
+
* @param dto.memoryId - Unique entry identifier
|
|
48739
|
+
* @param dto.signalId - Signal identifier
|
|
48740
|
+
* @param dto.bucketName - Bucket name
|
|
48741
|
+
* @returns Entry value
|
|
48742
|
+
* @throws Error if entry not found
|
|
48743
|
+
*/
|
|
48744
|
+
this.readMemory = async (dto) => {
|
|
48745
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_READ, {
|
|
48746
|
+
signalId: dto.signalId,
|
|
48747
|
+
bucketName: dto.bucketName,
|
|
48748
|
+
memoryId: dto.memoryId,
|
|
48749
|
+
});
|
|
48750
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
48751
|
+
const isInitial = !this.getInstance.has(key);
|
|
48752
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
48753
|
+
await instance.waitForInit(isInitial);
|
|
48754
|
+
return await instance.readMemory(dto.memoryId);
|
|
48755
|
+
};
|
|
48756
|
+
/**
|
|
48757
|
+
* Switches to in-memory BM25 adapter.
|
|
48758
|
+
* All data lives in process memory only.
|
|
48759
|
+
*/
|
|
48760
|
+
this.useLocal = () => {
|
|
48761
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
48762
|
+
this.MemoryFactory = MemoryLocalInstance;
|
|
48763
|
+
};
|
|
48764
|
+
/**
|
|
48765
|
+
* Switches to file-system backed adapter (default).
|
|
48766
|
+
* Data is persisted to ./dump/memory/<signalId>/<bucketName>/.
|
|
48767
|
+
*/
|
|
48768
|
+
this.usePersist = () => {
|
|
48769
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
48770
|
+
this.MemoryFactory = MemoryPersistInstance;
|
|
48771
|
+
};
|
|
48772
|
+
/**
|
|
48773
|
+
* Switches to dummy adapter that discards all writes.
|
|
48774
|
+
*/
|
|
48775
|
+
this.useDummy = () => {
|
|
48776
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
48777
|
+
this.MemoryFactory = MemoryDummyInstance;
|
|
48778
|
+
};
|
|
48779
|
+
/**
|
|
48780
|
+
* Switches to a custom memory adapter implementation.
|
|
48781
|
+
* @param Ctor - Constructor for the custom memory instance
|
|
48782
|
+
*/
|
|
48783
|
+
this.useMemoryAdapter = (Ctor) => {
|
|
48784
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER);
|
|
48785
|
+
this.MemoryFactory = Ctor;
|
|
48786
|
+
};
|
|
48787
|
+
/**
|
|
48788
|
+
* Clears the memoized instance cache.
|
|
48789
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
48790
|
+
* so new instances are created with the updated base path.
|
|
48791
|
+
*/
|
|
48792
|
+
this.clear = () => {
|
|
48793
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_CLEAR);
|
|
48794
|
+
this.getInstance.clear();
|
|
48795
|
+
};
|
|
48796
|
+
}
|
|
48797
|
+
}
|
|
48798
|
+
/**
|
|
48799
|
+
* Main memory adapter that manages both backtest and live memory storage.
|
|
48800
|
+
*
|
|
48801
|
+
* Features:
|
|
48802
|
+
* - Subscribes to signal lifecycle events (cancelled/closed) to dispose stale instances
|
|
48803
|
+
* - Routes all operations to MemoryBacktest or MemoryLive based on dto.backtest
|
|
48804
|
+
* - Singleshot enable pattern prevents duplicate subscriptions
|
|
48805
|
+
* - Cleanup function for proper unsubscription
|
|
48806
|
+
*/
|
|
48807
|
+
class MemoryAdapter {
|
|
48808
|
+
constructor() {
|
|
48809
|
+
/**
|
|
48810
|
+
* Enables memory storage by subscribing to signal lifecycle events.
|
|
48811
|
+
* Clears memoized instances in MemoryBacktest and MemoryLive when a signal
|
|
48812
|
+
* is cancelled or closed, preventing stale instances from accumulating.
|
|
48813
|
+
* Uses singleshot to ensure one-time subscription.
|
|
48814
|
+
*
|
|
48815
|
+
* @returns Cleanup function that unsubscribes from all emitters
|
|
47553
48816
|
*/
|
|
47554
48817
|
this.enable = functoolsKit.singleshot(() => {
|
|
47555
48818
|
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_ENABLE);
|
|
47556
|
-
const handleDispose = (signalId) => {
|
|
47557
|
-
const prefix = CREATE_KEY_FN$4(signalId, "");
|
|
47558
|
-
for (const key of this.getInstance.keys()) {
|
|
47559
|
-
if (key.startsWith(prefix)) {
|
|
47560
|
-
const instance = this.getInstance.get(key);
|
|
47561
|
-
instance && instance.dispose();
|
|
47562
|
-
this.getInstance.clear(key);
|
|
47563
|
-
}
|
|
47564
|
-
}
|
|
47565
|
-
};
|
|
47566
48819
|
const unCancel = signalEmitter
|
|
47567
48820
|
.filter(({ action }) => action === "cancelled")
|
|
47568
|
-
.connect(({ signal }) =>
|
|
48821
|
+
.connect(({ signal }) => {
|
|
48822
|
+
MemoryBacktest.disposeSignal(signal.id);
|
|
48823
|
+
MemoryLive.disposeSignal(signal.id);
|
|
48824
|
+
});
|
|
47569
48825
|
const unClose = signalEmitter
|
|
47570
48826
|
.filter(({ action }) => action === "closed")
|
|
47571
|
-
.connect(({ signal }) =>
|
|
47572
|
-
|
|
48827
|
+
.connect(({ signal }) => {
|
|
48828
|
+
MemoryBacktest.disposeSignal(signal.id);
|
|
48829
|
+
MemoryLive.disposeSignal(signal.id);
|
|
48830
|
+
});
|
|
48831
|
+
return functoolsKit.compose(() => unCancel(), () => unClose(), () => this.enable.clear());
|
|
47573
48832
|
});
|
|
47574
48833
|
/**
|
|
47575
|
-
*
|
|
47576
|
-
*
|
|
48834
|
+
* Disables memory storage by unsubscribing from signal lifecycle events.
|
|
48835
|
+
* Safe to call multiple times.
|
|
47577
48836
|
*/
|
|
47578
48837
|
this.disable = () => {
|
|
47579
48838
|
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_DISABLE);
|
|
@@ -47584,11 +48843,13 @@ class MemoryAdapter {
|
|
|
47584
48843
|
};
|
|
47585
48844
|
/**
|
|
47586
48845
|
* Write a value to memory.
|
|
48846
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47587
48847
|
* @param dto.memoryId - Unique entry identifier
|
|
47588
48848
|
* @param dto.value - Value to store
|
|
47589
48849
|
* @param dto.signalId - Signal identifier
|
|
47590
48850
|
* @param dto.bucketName - Bucket name
|
|
47591
|
-
* @param dto.description -
|
|
48851
|
+
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
48852
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47592
48853
|
*/
|
|
47593
48854
|
this.writeMemory = async (dto) => {
|
|
47594
48855
|
if (!this.enable.hasValue()) {
|
|
@@ -47598,18 +48859,20 @@ class MemoryAdapter {
|
|
|
47598
48859
|
signalId: dto.signalId,
|
|
47599
48860
|
bucketName: dto.bucketName,
|
|
47600
48861
|
memoryId: dto.memoryId,
|
|
48862
|
+
backtest: dto.backtest,
|
|
47601
48863
|
});
|
|
47602
|
-
|
|
47603
|
-
|
|
47604
|
-
|
|
47605
|
-
await
|
|
47606
|
-
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
48864
|
+
if (dto.backtest) {
|
|
48865
|
+
return await MemoryBacktest.writeMemory(dto);
|
|
48866
|
+
}
|
|
48867
|
+
return await MemoryLive.writeMemory(dto);
|
|
47607
48868
|
};
|
|
47608
48869
|
/**
|
|
47609
48870
|
* Search memory using BM25 full-text scoring.
|
|
48871
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47610
48872
|
* @param dto.query - Search query string
|
|
47611
48873
|
* @param dto.signalId - Signal identifier
|
|
47612
48874
|
* @param dto.bucketName - Bucket name
|
|
48875
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47613
48876
|
* @returns Matching entries sorted by relevance score
|
|
47614
48877
|
*/
|
|
47615
48878
|
this.searchMemory = async (dto) => {
|
|
@@ -47620,17 +48883,19 @@ class MemoryAdapter {
|
|
|
47620
48883
|
signalId: dto.signalId,
|
|
47621
48884
|
bucketName: dto.bucketName,
|
|
47622
48885
|
query: dto.query,
|
|
48886
|
+
backtest: dto.backtest,
|
|
47623
48887
|
});
|
|
47624
|
-
|
|
47625
|
-
|
|
47626
|
-
|
|
47627
|
-
await
|
|
47628
|
-
return await instance.searchMemory(dto.query, dto.settings);
|
|
48888
|
+
if (dto.backtest) {
|
|
48889
|
+
return await MemoryBacktest.searchMemory(dto);
|
|
48890
|
+
}
|
|
48891
|
+
return await MemoryLive.searchMemory(dto);
|
|
47629
48892
|
};
|
|
47630
48893
|
/**
|
|
47631
48894
|
* List all entries in memory.
|
|
48895
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47632
48896
|
* @param dto.signalId - Signal identifier
|
|
47633
48897
|
* @param dto.bucketName - Bucket name
|
|
48898
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47634
48899
|
* @returns Array of all stored entries
|
|
47635
48900
|
*/
|
|
47636
48901
|
this.listMemory = async (dto) => {
|
|
@@ -47640,18 +48905,20 @@ class MemoryAdapter {
|
|
|
47640
48905
|
backtest.loggerService.debug(MEMORY_ADAPTER_METHOD_NAME_LIST, {
|
|
47641
48906
|
signalId: dto.signalId,
|
|
47642
48907
|
bucketName: dto.bucketName,
|
|
48908
|
+
backtest: dto.backtest,
|
|
47643
48909
|
});
|
|
47644
|
-
|
|
47645
|
-
|
|
47646
|
-
|
|
47647
|
-
await
|
|
47648
|
-
return await instance.listMemory();
|
|
48910
|
+
if (dto.backtest) {
|
|
48911
|
+
return await MemoryBacktest.listMemory(dto);
|
|
48912
|
+
}
|
|
48913
|
+
return await MemoryLive.listMemory(dto);
|
|
47649
48914
|
};
|
|
47650
48915
|
/**
|
|
47651
48916
|
* Remove an entry from memory.
|
|
48917
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47652
48918
|
* @param dto.memoryId - Unique entry identifier
|
|
47653
48919
|
* @param dto.signalId - Signal identifier
|
|
47654
48920
|
* @param dto.bucketName - Bucket name
|
|
48921
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47655
48922
|
*/
|
|
47656
48923
|
this.removeMemory = async (dto) => {
|
|
47657
48924
|
if (!this.enable.hasValue()) {
|
|
@@ -47661,18 +48928,20 @@ class MemoryAdapter {
|
|
|
47661
48928
|
signalId: dto.signalId,
|
|
47662
48929
|
bucketName: dto.bucketName,
|
|
47663
48930
|
memoryId: dto.memoryId,
|
|
48931
|
+
backtest: dto.backtest,
|
|
47664
48932
|
});
|
|
47665
|
-
|
|
47666
|
-
|
|
47667
|
-
|
|
47668
|
-
await
|
|
47669
|
-
return await instance.removeMemory(dto.memoryId);
|
|
48933
|
+
if (dto.backtest) {
|
|
48934
|
+
return await MemoryBacktest.removeMemory(dto);
|
|
48935
|
+
}
|
|
48936
|
+
return await MemoryLive.removeMemory(dto);
|
|
47670
48937
|
};
|
|
47671
48938
|
/**
|
|
47672
48939
|
* Read a single entry from memory.
|
|
48940
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47673
48941
|
* @param dto.memoryId - Unique entry identifier
|
|
47674
48942
|
* @param dto.signalId - Signal identifier
|
|
47675
48943
|
* @param dto.bucketName - Bucket name
|
|
48944
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47676
48945
|
* @returns Entry value
|
|
47677
48946
|
* @throws Error if entry not found
|
|
47678
48947
|
*/
|
|
@@ -47684,56 +48953,30 @@ class MemoryAdapter {
|
|
|
47684
48953
|
signalId: dto.signalId,
|
|
47685
48954
|
bucketName: dto.bucketName,
|
|
47686
48955
|
memoryId: dto.memoryId,
|
|
48956
|
+
backtest: dto.backtest,
|
|
47687
48957
|
});
|
|
47688
|
-
|
|
47689
|
-
|
|
47690
|
-
|
|
47691
|
-
await
|
|
47692
|
-
return await instance.readMemory(dto.memoryId);
|
|
47693
|
-
};
|
|
47694
|
-
/**
|
|
47695
|
-
* Switches to in-memory BM25 adapter (default).
|
|
47696
|
-
* All data lives in process memory only.
|
|
47697
|
-
*/
|
|
47698
|
-
this.useLocal = () => {
|
|
47699
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
47700
|
-
this.MemoryFactory = MemoryLocalInstance;
|
|
47701
|
-
};
|
|
47702
|
-
/**
|
|
47703
|
-
* Switches to file-system backed adapter.
|
|
47704
|
-
* Data is persisted to ./dump/memory/<signalId>/<bucketName>/.
|
|
47705
|
-
*/
|
|
47706
|
-
this.usePersist = () => {
|
|
47707
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
47708
|
-
this.MemoryFactory = MemoryPersistInstance;
|
|
47709
|
-
};
|
|
47710
|
-
/**
|
|
47711
|
-
* Switches to dummy adapter that discards all writes.
|
|
47712
|
-
*/
|
|
47713
|
-
this.useDummy = () => {
|
|
47714
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
47715
|
-
this.MemoryFactory = MemoryDummyInstance;
|
|
47716
|
-
};
|
|
47717
|
-
/**
|
|
47718
|
-
* Clears the memoized instance cache.
|
|
47719
|
-
* Call this when process.cwd() changes between strategy iterations
|
|
47720
|
-
* so new instances are created with the updated base path.
|
|
47721
|
-
*/
|
|
47722
|
-
this.clear = () => {
|
|
47723
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_CLEAR);
|
|
47724
|
-
this.getInstance.clear();
|
|
47725
|
-
};
|
|
47726
|
-
/**
|
|
47727
|
-
* Releases resources held by this adapter.
|
|
47728
|
-
* Delegates to disable() to unsubscribe from signal lifecycle events.
|
|
47729
|
-
*/
|
|
47730
|
-
this.dispose = () => {
|
|
47731
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_DISPOSE);
|
|
47732
|
-
this.disable();
|
|
48958
|
+
if (dto.backtest) {
|
|
48959
|
+
return await MemoryBacktest.readMemory(dto);
|
|
48960
|
+
}
|
|
48961
|
+
return await MemoryLive.readMemory(dto);
|
|
47733
48962
|
};
|
|
47734
48963
|
}
|
|
47735
48964
|
}
|
|
48965
|
+
/**
|
|
48966
|
+
* Global singleton instance of MemoryAdapter.
|
|
48967
|
+
* Provides unified memory management for backtest and live trading.
|
|
48968
|
+
*/
|
|
47736
48969
|
const Memory = new MemoryAdapter();
|
|
48970
|
+
/**
|
|
48971
|
+
* Global singleton instance of MemoryLiveAdapter.
|
|
48972
|
+
* Provides live trading memory storage with pluggable backends.
|
|
48973
|
+
*/
|
|
48974
|
+
const MemoryLive = new MemoryLiveAdapter();
|
|
48975
|
+
/**
|
|
48976
|
+
* Global singleton instance of MemoryBacktestAdapter.
|
|
48977
|
+
* Provides backtest memory storage with pluggable backends.
|
|
48978
|
+
*/
|
|
48979
|
+
const MemoryBacktest = new MemoryBacktestAdapter();
|
|
47737
48980
|
|
|
47738
48981
|
const WRITE_MEMORY_METHOD_NAME = "memory.writeMemory";
|
|
47739
48982
|
const READ_MEMORY_METHOD_NAME = "memory.readMemory";
|
|
@@ -47743,23 +48986,20 @@ const REMOVE_MEMORY_METHOD_NAME = "memory.removeMemory";
|
|
|
47743
48986
|
/**
|
|
47744
48987
|
* Writes a value to memory scoped to the current signal.
|
|
47745
48988
|
*
|
|
47746
|
-
*
|
|
47747
|
-
* If no pending signal exists, logs a warning and returns without writing.
|
|
47748
|
-
*
|
|
48989
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47749
48990
|
* Automatically detects backtest/live mode from execution context.
|
|
47750
48991
|
*
|
|
47751
48992
|
* @param dto.bucketName - Memory bucket name
|
|
47752
48993
|
* @param dto.memoryId - Unique memory entry identifier
|
|
47753
48994
|
* @param dto.value - Value to store
|
|
48995
|
+
* @param dto.description - BM25 index string for contextual search
|
|
47754
48996
|
* @returns Promise that resolves when write is complete
|
|
47755
48997
|
*
|
|
47756
|
-
* @deprecated Better use Memory.writeMemory with manual signalId argument
|
|
47757
|
-
*
|
|
47758
48998
|
* @example
|
|
47759
48999
|
* ```typescript
|
|
47760
49000
|
* import { writeMemory } from "backtest-kit";
|
|
47761
49001
|
*
|
|
47762
|
-
* await writeMemory({ bucketName: "my-strategy", memoryId: "context", value: { trend: "up", confidence: 0.9 } });
|
|
49002
|
+
* await writeMemory({ bucketName: "my-strategy", memoryId: "context", value: { trend: "up", confidence: 0.9 }, description: "Signal context at entry" });
|
|
47763
49003
|
* ```
|
|
47764
49004
|
*/
|
|
47765
49005
|
async function writeMemory(dto) {
|
|
@@ -47777,33 +49017,41 @@ async function writeMemory(dto) {
|
|
|
47777
49017
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47778
49018
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47779
49019
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47780
|
-
|
|
47781
|
-
if (
|
|
47782
|
-
|
|
49020
|
+
let signal;
|
|
49021
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49022
|
+
await Memory.writeMemory({
|
|
49023
|
+
memoryId,
|
|
49024
|
+
value,
|
|
49025
|
+
signalId: signal.id,
|
|
49026
|
+
bucketName,
|
|
49027
|
+
description,
|
|
49028
|
+
backtest: isBacktest,
|
|
49029
|
+
});
|
|
47783
49030
|
return;
|
|
47784
49031
|
}
|
|
47785
|
-
await
|
|
47786
|
-
|
|
47787
|
-
|
|
47788
|
-
|
|
47789
|
-
|
|
47790
|
-
|
|
47791
|
-
|
|
49032
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49033
|
+
await Memory.writeMemory({
|
|
49034
|
+
memoryId,
|
|
49035
|
+
value,
|
|
49036
|
+
signalId: signal.id,
|
|
49037
|
+
bucketName,
|
|
49038
|
+
description,
|
|
49039
|
+
backtest: isBacktest,
|
|
49040
|
+
});
|
|
49041
|
+
return;
|
|
49042
|
+
}
|
|
49043
|
+
throw new Error(`writeMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
|
|
47792
49044
|
}
|
|
47793
49045
|
/**
|
|
47794
49046
|
* Reads a value from memory scoped to the current signal.
|
|
47795
49047
|
*
|
|
47796
|
-
*
|
|
47797
|
-
* If no pending signal exists, logs a warning and returns null.
|
|
47798
|
-
*
|
|
49048
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47799
49049
|
* Automatically detects backtest/live mode from execution context.
|
|
47800
49050
|
*
|
|
47801
49051
|
* @param dto.bucketName - Memory bucket name
|
|
47802
49052
|
* @param dto.memoryId - Unique memory entry identifier
|
|
47803
|
-
* @returns Promise resolving to stored value
|
|
47804
|
-
* @throws Error if
|
|
47805
|
-
*
|
|
47806
|
-
* @deprecated Better use Memory.readMemory with manual signalId argument
|
|
49053
|
+
* @returns Promise resolving to stored value
|
|
49054
|
+
* @throws Error if no pending or scheduled signal exists, or if entry not found
|
|
47807
49055
|
*
|
|
47808
49056
|
* @example
|
|
47809
49057
|
* ```typescript
|
|
@@ -47827,30 +49075,35 @@ async function readMemory(dto) {
|
|
|
47827
49075
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47828
49076
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47829
49077
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47830
|
-
|
|
47831
|
-
if (
|
|
47832
|
-
|
|
47833
|
-
|
|
49078
|
+
let signal;
|
|
49079
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49080
|
+
return await Memory.readMemory({
|
|
49081
|
+
memoryId,
|
|
49082
|
+
signalId: signal.id,
|
|
49083
|
+
bucketName,
|
|
49084
|
+
backtest: isBacktest,
|
|
49085
|
+
});
|
|
47834
49086
|
}
|
|
47835
|
-
|
|
47836
|
-
|
|
47837
|
-
|
|
47838
|
-
|
|
47839
|
-
|
|
49087
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49088
|
+
return await Memory.readMemory({
|
|
49089
|
+
memoryId,
|
|
49090
|
+
signalId: signal.id,
|
|
49091
|
+
bucketName,
|
|
49092
|
+
backtest: isBacktest,
|
|
49093
|
+
});
|
|
49094
|
+
}
|
|
49095
|
+
throw new Error(`readMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
|
|
47840
49096
|
}
|
|
47841
49097
|
/**
|
|
47842
49098
|
* Searches memory entries for the current signal using BM25 full-text scoring.
|
|
47843
49099
|
*
|
|
47844
|
-
*
|
|
47845
|
-
* If no pending signal exists, logs a warning and returns an empty array.
|
|
47846
|
-
*
|
|
49100
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47847
49101
|
* Automatically detects backtest/live mode from execution context.
|
|
47848
49102
|
*
|
|
47849
49103
|
* @param dto.bucketName - Memory bucket name
|
|
47850
49104
|
* @param dto.query - Search query string
|
|
47851
|
-
* @returns Promise resolving to matching entries sorted by relevance
|
|
47852
|
-
*
|
|
47853
|
-
* @deprecated Better use Memory.searchMemory with manual signalId argument
|
|
49105
|
+
* @returns Promise resolving to matching entries sorted by relevance
|
|
49106
|
+
* @throws Error if no pending or scheduled signal exists
|
|
47854
49107
|
*
|
|
47855
49108
|
* @example
|
|
47856
49109
|
* ```typescript
|
|
@@ -47874,29 +49127,34 @@ async function searchMemory(dto) {
|
|
|
47874
49127
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47875
49128
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47876
49129
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47877
|
-
|
|
47878
|
-
if (
|
|
47879
|
-
|
|
47880
|
-
|
|
49130
|
+
let signal;
|
|
49131
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49132
|
+
return await Memory.searchMemory({
|
|
49133
|
+
query,
|
|
49134
|
+
signalId: signal.id,
|
|
49135
|
+
bucketName,
|
|
49136
|
+
backtest: isBacktest,
|
|
49137
|
+
});
|
|
47881
49138
|
}
|
|
47882
|
-
|
|
47883
|
-
|
|
47884
|
-
|
|
47885
|
-
|
|
47886
|
-
|
|
49139
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49140
|
+
return await Memory.searchMemory({
|
|
49141
|
+
query,
|
|
49142
|
+
signalId: signal.id,
|
|
49143
|
+
bucketName,
|
|
49144
|
+
backtest: isBacktest,
|
|
49145
|
+
});
|
|
49146
|
+
}
|
|
49147
|
+
throw new Error(`searchMemory requires a pending or scheduled signal for symbol=${symbol} query=${query}`);
|
|
47887
49148
|
}
|
|
47888
49149
|
/**
|
|
47889
49150
|
* Lists all memory entries for the current signal.
|
|
47890
49151
|
*
|
|
47891
|
-
*
|
|
47892
|
-
* If no pending signal exists, logs a warning and returns an empty array.
|
|
47893
|
-
*
|
|
49152
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47894
49153
|
* Automatically detects backtest/live mode from execution context.
|
|
47895
49154
|
*
|
|
47896
49155
|
* @param dto.bucketName - Memory bucket name
|
|
47897
|
-
* @returns Promise resolving to all stored entries
|
|
47898
|
-
*
|
|
47899
|
-
* @deprecated Better use Memory.listMemory with manual signalId argument
|
|
49156
|
+
* @returns Promise resolving to all stored entries
|
|
49157
|
+
* @throws Error if no pending or scheduled signal exists
|
|
47900
49158
|
*
|
|
47901
49159
|
* @example
|
|
47902
49160
|
* ```typescript
|
|
@@ -47919,29 +49177,33 @@ async function listMemory(dto) {
|
|
|
47919
49177
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47920
49178
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47921
49179
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47922
|
-
|
|
47923
|
-
if (
|
|
47924
|
-
|
|
47925
|
-
|
|
49180
|
+
let signal;
|
|
49181
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49182
|
+
return await Memory.listMemory({
|
|
49183
|
+
signalId: signal.id,
|
|
49184
|
+
bucketName,
|
|
49185
|
+
backtest: isBacktest,
|
|
49186
|
+
});
|
|
47926
49187
|
}
|
|
47927
|
-
|
|
47928
|
-
|
|
47929
|
-
|
|
47930
|
-
|
|
49188
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49189
|
+
return await Memory.listMemory({
|
|
49190
|
+
signalId: signal.id,
|
|
49191
|
+
bucketName,
|
|
49192
|
+
backtest: isBacktest,
|
|
49193
|
+
});
|
|
49194
|
+
}
|
|
49195
|
+
throw new Error(`listMemory requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
47931
49196
|
}
|
|
47932
49197
|
/**
|
|
47933
49198
|
* Removes a memory entry for the current signal.
|
|
47934
49199
|
*
|
|
47935
|
-
*
|
|
47936
|
-
* If no pending signal exists, logs a warning and returns without removing.
|
|
47937
|
-
*
|
|
49200
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47938
49201
|
* Automatically detects backtest/live mode from execution context.
|
|
47939
49202
|
*
|
|
47940
49203
|
* @param dto.bucketName - Memory bucket name
|
|
47941
49204
|
* @param dto.memoryId - Unique memory entry identifier
|
|
47942
49205
|
* @returns Promise that resolves when removal is complete
|
|
47943
|
-
*
|
|
47944
|
-
* @deprecated Better use Memory.removeMemory with manual signalId argument
|
|
49206
|
+
* @throws Error if no pending or scheduled signal exists
|
|
47945
49207
|
*
|
|
47946
49208
|
* @example
|
|
47947
49209
|
* ```typescript
|
|
@@ -47965,16 +49227,26 @@ async function removeMemory(dto) {
|
|
|
47965
49227
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47966
49228
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47967
49229
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47968
|
-
|
|
47969
|
-
if (
|
|
47970
|
-
|
|
49230
|
+
let signal;
|
|
49231
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49232
|
+
await Memory.removeMemory({
|
|
49233
|
+
memoryId,
|
|
49234
|
+
signalId: signal.id,
|
|
49235
|
+
bucketName,
|
|
49236
|
+
backtest: isBacktest,
|
|
49237
|
+
});
|
|
47971
49238
|
return;
|
|
47972
49239
|
}
|
|
47973
|
-
await
|
|
47974
|
-
|
|
47975
|
-
|
|
47976
|
-
|
|
47977
|
-
|
|
49240
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49241
|
+
await Memory.removeMemory({
|
|
49242
|
+
memoryId,
|
|
49243
|
+
signalId: signal.id,
|
|
49244
|
+
bucketName,
|
|
49245
|
+
backtest: isBacktest,
|
|
49246
|
+
});
|
|
49247
|
+
return;
|
|
49248
|
+
}
|
|
49249
|
+
throw new Error(`removeMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
|
|
47978
49250
|
}
|
|
47979
49251
|
|
|
47980
49252
|
const CREATE_KEY_FN$3 = (signalId, bucketName) => `${signalId}-${bucketName}`;
|
|
@@ -48069,11 +49341,12 @@ const RENDER_TABLE_FN = (rows) => {
|
|
|
48069
49341
|
* Scoped to (signalId, bucketName) via constructor.
|
|
48070
49342
|
*/
|
|
48071
49343
|
class DumpBothInstance {
|
|
48072
|
-
constructor(signalId, bucketName) {
|
|
49344
|
+
constructor(signalId, bucketName, backtest) {
|
|
48073
49345
|
this.signalId = signalId;
|
|
48074
49346
|
this.bucketName = bucketName;
|
|
48075
|
-
this.
|
|
48076
|
-
this.
|
|
49347
|
+
this.backtest = backtest;
|
|
49348
|
+
this._memory = new DumpMemoryInstance(signalId, bucketName, backtest);
|
|
49349
|
+
this._markdown = new DumpMarkdownInstance(signalId, bucketName, backtest);
|
|
48077
49350
|
}
|
|
48078
49351
|
/** Releases resources held by both backends. */
|
|
48079
49352
|
dispose() {
|
|
@@ -48205,9 +49478,10 @@ class DumpBothInstance {
|
|
|
48205
49478
|
* Useful for downstream LLM retrieval via Memory.searchMemory.
|
|
48206
49479
|
*/
|
|
48207
49480
|
class DumpMemoryInstance {
|
|
48208
|
-
constructor(signalId, bucketName) {
|
|
49481
|
+
constructor(signalId, bucketName, backtest) {
|
|
48209
49482
|
this.signalId = signalId;
|
|
48210
49483
|
this.bucketName = bucketName;
|
|
49484
|
+
this.backtest = backtest;
|
|
48211
49485
|
}
|
|
48212
49486
|
/**
|
|
48213
49487
|
* Stores the full agent message history in Memory as a `{ messages }` object.
|
|
@@ -48233,6 +49507,7 @@ class DumpMemoryInstance {
|
|
|
48233
49507
|
signalId: this.signalId,
|
|
48234
49508
|
value: { messages },
|
|
48235
49509
|
description,
|
|
49510
|
+
backtest: this.backtest,
|
|
48236
49511
|
});
|
|
48237
49512
|
}
|
|
48238
49513
|
/**
|
|
@@ -48254,6 +49529,7 @@ class DumpMemoryInstance {
|
|
|
48254
49529
|
signalId: this.signalId,
|
|
48255
49530
|
value: record,
|
|
48256
49531
|
description,
|
|
49532
|
+
backtest: this.backtest,
|
|
48257
49533
|
});
|
|
48258
49534
|
}
|
|
48259
49535
|
/**
|
|
@@ -48276,6 +49552,7 @@ class DumpMemoryInstance {
|
|
|
48276
49552
|
signalId: this.signalId,
|
|
48277
49553
|
value: { rows },
|
|
48278
49554
|
description,
|
|
49555
|
+
backtest: this.backtest,
|
|
48279
49556
|
});
|
|
48280
49557
|
}
|
|
48281
49558
|
/**
|
|
@@ -48297,6 +49574,7 @@ class DumpMemoryInstance {
|
|
|
48297
49574
|
signalId: this.signalId,
|
|
48298
49575
|
value: { content },
|
|
48299
49576
|
description,
|
|
49577
|
+
backtest: this.backtest,
|
|
48300
49578
|
});
|
|
48301
49579
|
}
|
|
48302
49580
|
/**
|
|
@@ -48318,6 +49596,7 @@ class DumpMemoryInstance {
|
|
|
48318
49596
|
signalId: this.signalId,
|
|
48319
49597
|
value: { content },
|
|
48320
49598
|
description,
|
|
49599
|
+
backtest: this.backtest,
|
|
48321
49600
|
});
|
|
48322
49601
|
}
|
|
48323
49602
|
/**
|
|
@@ -48340,6 +49619,7 @@ class DumpMemoryInstance {
|
|
|
48340
49619
|
signalId: this.signalId,
|
|
48341
49620
|
value: json,
|
|
48342
49621
|
description,
|
|
49622
|
+
backtest: this.backtest,
|
|
48343
49623
|
});
|
|
48344
49624
|
}
|
|
48345
49625
|
/** Releases resources held by this instance. */
|
|
@@ -48361,9 +49641,10 @@ class DumpMemoryInstance {
|
|
|
48361
49641
|
* If the file already exists, the call is skipped (idempotent).
|
|
48362
49642
|
*/
|
|
48363
49643
|
class DumpMarkdownInstance {
|
|
48364
|
-
constructor(signalId, bucketName) {
|
|
49644
|
+
constructor(signalId, bucketName, backtest) {
|
|
48365
49645
|
this.signalId = signalId;
|
|
48366
49646
|
this.bucketName = bucketName;
|
|
49647
|
+
this.backtest = backtest;
|
|
48367
49648
|
}
|
|
48368
49649
|
getFilePath(dumpId) {
|
|
48369
49650
|
return path.join("./dump/agent", this.signalId, this.bucketName, `${dumpId}.md`);
|
|
@@ -48552,9 +49833,10 @@ class DumpMarkdownInstance {
|
|
|
48552
49833
|
* Used for disabling dumps in tests or dry-run scenarios.
|
|
48553
49834
|
*/
|
|
48554
49835
|
class DumpDummyInstance {
|
|
48555
|
-
constructor(signalId, bucketName) {
|
|
49836
|
+
constructor(signalId, bucketName, backtest) {
|
|
48556
49837
|
this.signalId = signalId;
|
|
48557
49838
|
this.bucketName = bucketName;
|
|
49839
|
+
this.backtest = backtest;
|
|
48558
49840
|
}
|
|
48559
49841
|
/** No-op. */
|
|
48560
49842
|
async dumpAgentAnswer() {
|
|
@@ -48597,7 +49879,7 @@ class DumpDummyInstance {
|
|
|
48597
49879
|
class DumpAdapter {
|
|
48598
49880
|
constructor() {
|
|
48599
49881
|
this.DumpFactory = DumpMarkdownInstance;
|
|
48600
|
-
this.getInstance = functoolsKit.memoize(([signalId, bucketName]) => CREATE_KEY_FN$3(signalId, bucketName), (signalId, bucketName) => Reflect.construct(this.DumpFactory, [signalId, bucketName]));
|
|
49882
|
+
this.getInstance = functoolsKit.memoize(([signalId, bucketName]) => CREATE_KEY_FN$3(signalId, bucketName), (signalId, bucketName, backtest) => Reflect.construct(this.DumpFactory, [signalId, bucketName, backtest]));
|
|
48601
49883
|
/**
|
|
48602
49884
|
* Activates the adapter by subscribing to signal lifecycle events.
|
|
48603
49885
|
* Clears memoized instances for a signalId when it is cancelled or closed,
|
|
@@ -48648,7 +49930,7 @@ class DumpAdapter {
|
|
|
48648
49930
|
bucketName: context.bucketName,
|
|
48649
49931
|
dumpId: context.dumpId,
|
|
48650
49932
|
});
|
|
48651
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
49933
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48652
49934
|
return await instance.dumpAgentAnswer(messages, context.dumpId, context.description);
|
|
48653
49935
|
};
|
|
48654
49936
|
/**
|
|
@@ -48663,7 +49945,7 @@ class DumpAdapter {
|
|
|
48663
49945
|
bucketName: context.bucketName,
|
|
48664
49946
|
dumpId: context.dumpId,
|
|
48665
49947
|
});
|
|
48666
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
49948
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48667
49949
|
return await instance.dumpRecord(record, context.dumpId, context.description);
|
|
48668
49950
|
};
|
|
48669
49951
|
/**
|
|
@@ -48678,7 +49960,7 @@ class DumpAdapter {
|
|
|
48678
49960
|
bucketName: context.bucketName,
|
|
48679
49961
|
dumpId: context.dumpId,
|
|
48680
49962
|
});
|
|
48681
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
49963
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48682
49964
|
return await instance.dumpTable(rows, context.dumpId, context.description);
|
|
48683
49965
|
};
|
|
48684
49966
|
/**
|
|
@@ -48693,7 +49975,7 @@ class DumpAdapter {
|
|
|
48693
49975
|
bucketName: context.bucketName,
|
|
48694
49976
|
dumpId: context.dumpId,
|
|
48695
49977
|
});
|
|
48696
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
49978
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48697
49979
|
return await instance.dumpText(content, context.dumpId, context.description);
|
|
48698
49980
|
};
|
|
48699
49981
|
/**
|
|
@@ -48708,7 +49990,7 @@ class DumpAdapter {
|
|
|
48708
49990
|
bucketName: context.bucketName,
|
|
48709
49991
|
dumpId: context.dumpId,
|
|
48710
49992
|
});
|
|
48711
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
49993
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48712
49994
|
return await instance.dumpError(content, context.dumpId, context.description);
|
|
48713
49995
|
};
|
|
48714
49996
|
/**
|
|
@@ -48724,7 +50006,7 @@ class DumpAdapter {
|
|
|
48724
50006
|
bucketName: context.bucketName,
|
|
48725
50007
|
dumpId: context.dumpId,
|
|
48726
50008
|
});
|
|
48727
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
50009
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48728
50010
|
return await instance.dumpJson(json, context.dumpId, context.description);
|
|
48729
50011
|
};
|
|
48730
50012
|
/**
|
|
@@ -48790,16 +50072,15 @@ const DUMP_JSON_METHOD_NAME = "dump.dumpJson";
|
|
|
48790
50072
|
/**
|
|
48791
50073
|
* Dumps the full agent message history scoped to the current signal.
|
|
48792
50074
|
*
|
|
48793
|
-
*
|
|
48794
|
-
*
|
|
50075
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50076
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48795
50077
|
*
|
|
48796
50078
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
48797
50079
|
* @param dto.dumpId - Unique identifier for this agent invocation
|
|
48798
50080
|
* @param dto.messages - Full chat history (system, user, assistant, tool)
|
|
48799
50081
|
* @param dto.description - Human-readable label describing the agent invocation context; included in the BM25 index for Memory search
|
|
48800
50082
|
* @returns Promise that resolves when the dump is complete
|
|
48801
|
-
*
|
|
48802
|
-
* @deprecated Better use Dump.dumpAgentAnswer with manual signalId argument
|
|
50083
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48803
50084
|
*
|
|
48804
50085
|
* @example
|
|
48805
50086
|
* ```typescript
|
|
@@ -48824,31 +50105,41 @@ async function dumpAgentAnswer(dto) {
|
|
|
48824
50105
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48825
50106
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48826
50107
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48827
|
-
|
|
48828
|
-
if (
|
|
48829
|
-
|
|
50108
|
+
let signal;
|
|
50109
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50110
|
+
await Dump.dumpAgentAnswer(messages, {
|
|
50111
|
+
dumpId,
|
|
50112
|
+
bucketName,
|
|
50113
|
+
signalId: signal.id,
|
|
50114
|
+
description,
|
|
50115
|
+
backtest: isBacktest,
|
|
50116
|
+
});
|
|
48830
50117
|
return;
|
|
48831
50118
|
}
|
|
48832
|
-
await
|
|
48833
|
-
|
|
48834
|
-
|
|
48835
|
-
|
|
48836
|
-
|
|
48837
|
-
|
|
50119
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50120
|
+
await Dump.dumpAgentAnswer(messages, {
|
|
50121
|
+
dumpId,
|
|
50122
|
+
bucketName,
|
|
50123
|
+
signalId: signal.id,
|
|
50124
|
+
description,
|
|
50125
|
+
backtest: isBacktest,
|
|
50126
|
+
});
|
|
50127
|
+
return;
|
|
50128
|
+
}
|
|
50129
|
+
throw new Error(`dumpAgentAnswer requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
48838
50130
|
}
|
|
48839
50131
|
/**
|
|
48840
50132
|
* Dumps a flat key-value record scoped to the current signal.
|
|
48841
50133
|
*
|
|
48842
|
-
*
|
|
48843
|
-
*
|
|
50134
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50135
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48844
50136
|
*
|
|
48845
50137
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
48846
50138
|
* @param dto.dumpId - Unique identifier for this dump entry
|
|
48847
50139
|
* @param dto.record - Arbitrary flat object to persist
|
|
48848
50140
|
* @param dto.description - Human-readable label describing the record contents; included in the BM25 index for Memory search
|
|
48849
50141
|
* @returns Promise that resolves when the dump is complete
|
|
48850
|
-
*
|
|
48851
|
-
* @deprecated Better use Dump.dumpRecord with manual signalId argument
|
|
50142
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48852
50143
|
*
|
|
48853
50144
|
* @example
|
|
48854
50145
|
* ```typescript
|
|
@@ -48872,23 +50163,34 @@ async function dumpRecord(dto) {
|
|
|
48872
50163
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48873
50164
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48874
50165
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48875
|
-
|
|
48876
|
-
if (
|
|
48877
|
-
|
|
50166
|
+
let signal;
|
|
50167
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50168
|
+
await Dump.dumpRecord(record, {
|
|
50169
|
+
dumpId,
|
|
50170
|
+
bucketName,
|
|
50171
|
+
signalId: signal.id,
|
|
50172
|
+
description,
|
|
50173
|
+
backtest: isBacktest,
|
|
50174
|
+
});
|
|
48878
50175
|
return;
|
|
48879
50176
|
}
|
|
48880
|
-
await
|
|
48881
|
-
|
|
48882
|
-
|
|
48883
|
-
|
|
48884
|
-
|
|
48885
|
-
|
|
50177
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50178
|
+
await Dump.dumpRecord(record, {
|
|
50179
|
+
dumpId,
|
|
50180
|
+
bucketName,
|
|
50181
|
+
signalId: signal.id,
|
|
50182
|
+
description,
|
|
50183
|
+
backtest: isBacktest,
|
|
50184
|
+
});
|
|
50185
|
+
return;
|
|
50186
|
+
}
|
|
50187
|
+
throw new Error(`dumpRecord requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
48886
50188
|
}
|
|
48887
50189
|
/**
|
|
48888
50190
|
* Dumps an array of objects as a table scoped to the current signal.
|
|
48889
50191
|
*
|
|
48890
|
-
*
|
|
48891
|
-
*
|
|
50192
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50193
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48892
50194
|
*
|
|
48893
50195
|
* Column headers are derived from the union of all keys across all rows.
|
|
48894
50196
|
*
|
|
@@ -48897,8 +50199,7 @@ async function dumpRecord(dto) {
|
|
|
48897
50199
|
* @param dto.rows - Array of arbitrary objects to render as a table
|
|
48898
50200
|
* @param dto.description - Human-readable label describing the table contents; included in the BM25 index for Memory search
|
|
48899
50201
|
* @returns Promise that resolves when the dump is complete
|
|
48900
|
-
*
|
|
48901
|
-
* @deprecated Better use Dump.dumpTable with manual signalId argument
|
|
50202
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48902
50203
|
*
|
|
48903
50204
|
* @example
|
|
48904
50205
|
* ```typescript
|
|
@@ -48923,31 +50224,41 @@ async function dumpTable(dto) {
|
|
|
48923
50224
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48924
50225
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48925
50226
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48926
|
-
|
|
48927
|
-
if (
|
|
48928
|
-
|
|
50227
|
+
let signal;
|
|
50228
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50229
|
+
await Dump.dumpTable(rows, {
|
|
50230
|
+
dumpId,
|
|
50231
|
+
bucketName,
|
|
50232
|
+
signalId: signal.id,
|
|
50233
|
+
description,
|
|
50234
|
+
backtest: isBacktest,
|
|
50235
|
+
});
|
|
48929
50236
|
return;
|
|
48930
50237
|
}
|
|
48931
|
-
await
|
|
48932
|
-
|
|
48933
|
-
|
|
48934
|
-
|
|
48935
|
-
|
|
48936
|
-
|
|
50238
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50239
|
+
await Dump.dumpTable(rows, {
|
|
50240
|
+
dumpId,
|
|
50241
|
+
bucketName,
|
|
50242
|
+
signalId: signal.id,
|
|
50243
|
+
description,
|
|
50244
|
+
backtest: isBacktest,
|
|
50245
|
+
});
|
|
50246
|
+
return;
|
|
50247
|
+
}
|
|
50248
|
+
throw new Error(`dumpTable requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
48937
50249
|
}
|
|
48938
50250
|
/**
|
|
48939
50251
|
* Dumps raw text content scoped to the current signal.
|
|
48940
50252
|
*
|
|
48941
|
-
*
|
|
48942
|
-
*
|
|
50253
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50254
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48943
50255
|
*
|
|
48944
50256
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
48945
50257
|
* @param dto.dumpId - Unique identifier for this dump entry
|
|
48946
50258
|
* @param dto.content - Arbitrary text content to persist
|
|
48947
50259
|
* @param dto.description - Human-readable label describing the content; included in the BM25 index for Memory search
|
|
48948
50260
|
* @returns Promise that resolves when the dump is complete
|
|
48949
|
-
*
|
|
48950
|
-
* @deprecated Better use Dump.dumpText with manual signalId argument
|
|
50261
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48951
50262
|
*
|
|
48952
50263
|
* @example
|
|
48953
50264
|
* ```typescript
|
|
@@ -48971,31 +50282,41 @@ async function dumpText(dto) {
|
|
|
48971
50282
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48972
50283
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48973
50284
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48974
|
-
|
|
48975
|
-
if (
|
|
48976
|
-
|
|
50285
|
+
let signal;
|
|
50286
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50287
|
+
await Dump.dumpText(content, {
|
|
50288
|
+
dumpId,
|
|
50289
|
+
bucketName,
|
|
50290
|
+
signalId: signal.id,
|
|
50291
|
+
description,
|
|
50292
|
+
backtest: isBacktest,
|
|
50293
|
+
});
|
|
48977
50294
|
return;
|
|
48978
50295
|
}
|
|
48979
|
-
await
|
|
48980
|
-
|
|
48981
|
-
|
|
48982
|
-
|
|
48983
|
-
|
|
48984
|
-
|
|
50296
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50297
|
+
await Dump.dumpText(content, {
|
|
50298
|
+
dumpId,
|
|
50299
|
+
bucketName,
|
|
50300
|
+
signalId: signal.id,
|
|
50301
|
+
description,
|
|
50302
|
+
backtest: isBacktest,
|
|
50303
|
+
});
|
|
50304
|
+
return;
|
|
50305
|
+
}
|
|
50306
|
+
throw new Error(`dumpText requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
48985
50307
|
}
|
|
48986
50308
|
/**
|
|
48987
50309
|
* Dumps an error description scoped to the current signal.
|
|
48988
50310
|
*
|
|
48989
|
-
*
|
|
48990
|
-
*
|
|
50311
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50312
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48991
50313
|
*
|
|
48992
50314
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
48993
50315
|
* @param dto.dumpId - Unique identifier for this dump entry
|
|
48994
50316
|
* @param dto.content - Error message or description to persist
|
|
48995
50317
|
* @param dto.description - Human-readable label describing the error context; included in the BM25 index for Memory search
|
|
48996
50318
|
* @returns Promise that resolves when the dump is complete
|
|
48997
|
-
*
|
|
48998
|
-
* @deprecated Better use Dump.dumpError with manual signalId argument
|
|
50319
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48999
50320
|
*
|
|
49000
50321
|
* @example
|
|
49001
50322
|
* ```typescript
|
|
@@ -49019,29 +50340,41 @@ async function dumpError(dto) {
|
|
|
49019
50340
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
49020
50341
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49021
50342
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
49022
|
-
|
|
49023
|
-
if (
|
|
49024
|
-
|
|
50343
|
+
let signal;
|
|
50344
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50345
|
+
await Dump.dumpError(content, {
|
|
50346
|
+
dumpId,
|
|
50347
|
+
bucketName,
|
|
50348
|
+
signalId: signal.id,
|
|
50349
|
+
description,
|
|
50350
|
+
backtest: isBacktest,
|
|
50351
|
+
});
|
|
49025
50352
|
return;
|
|
49026
50353
|
}
|
|
49027
|
-
await
|
|
49028
|
-
|
|
49029
|
-
|
|
49030
|
-
|
|
49031
|
-
|
|
49032
|
-
|
|
50354
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50355
|
+
await Dump.dumpError(content, {
|
|
50356
|
+
dumpId,
|
|
50357
|
+
bucketName,
|
|
50358
|
+
signalId: signal.id,
|
|
50359
|
+
description,
|
|
50360
|
+
backtest: isBacktest,
|
|
50361
|
+
});
|
|
50362
|
+
return;
|
|
50363
|
+
}
|
|
50364
|
+
throw new Error(`dumpError requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
49033
50365
|
}
|
|
49034
50366
|
/**
|
|
49035
50367
|
* Dumps an arbitrary nested object as a fenced JSON block scoped to the current signal.
|
|
49036
50368
|
*
|
|
49037
|
-
*
|
|
49038
|
-
*
|
|
50369
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50370
|
+
* Automatically detects backtest/live mode from execution context.
|
|
49039
50371
|
*
|
|
49040
50372
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
49041
50373
|
* @param dto.dumpId - Unique identifier for this dump entry
|
|
49042
50374
|
* @param dto.json - Arbitrary nested object to serialize with JSON.stringify
|
|
49043
50375
|
* @param dto.description - Human-readable label describing the object contents; included in the BM25 index for Memory search
|
|
49044
50376
|
* @returns Promise that resolves when the dump is complete
|
|
50377
|
+
* @throws Error if no pending or scheduled signal exists
|
|
49045
50378
|
*
|
|
49046
50379
|
* @deprecated Prefer dumpRecord — flat key-value structure maps naturally to markdown tables and SQL storage
|
|
49047
50380
|
*
|
|
@@ -49067,17 +50400,28 @@ async function dumpJson(dto) {
|
|
|
49067
50400
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
49068
50401
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49069
50402
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
49070
|
-
|
|
49071
|
-
if (
|
|
49072
|
-
|
|
50403
|
+
let signal;
|
|
50404
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50405
|
+
await Dump.dumpJson(json, {
|
|
50406
|
+
dumpId,
|
|
50407
|
+
bucketName,
|
|
50408
|
+
signalId: signal.id,
|
|
50409
|
+
description,
|
|
50410
|
+
backtest: isBacktest,
|
|
50411
|
+
});
|
|
49073
50412
|
return;
|
|
49074
50413
|
}
|
|
49075
|
-
await
|
|
49076
|
-
|
|
49077
|
-
|
|
49078
|
-
|
|
49079
|
-
|
|
49080
|
-
|
|
50414
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50415
|
+
await Dump.dumpJson(json, {
|
|
50416
|
+
dumpId,
|
|
50417
|
+
bucketName,
|
|
50418
|
+
signalId: signal.id,
|
|
50419
|
+
description,
|
|
50420
|
+
backtest: isBacktest,
|
|
50421
|
+
});
|
|
50422
|
+
return;
|
|
50423
|
+
}
|
|
50424
|
+
throw new Error(`dumpJson requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
49081
50425
|
}
|
|
49082
50426
|
|
|
49083
50427
|
/**
|
|
@@ -59565,6 +60909,10 @@ exports.MarkdownFolderBase = MarkdownFolderBase;
|
|
|
59565
60909
|
exports.MarkdownWriter = MarkdownWriter;
|
|
59566
60910
|
exports.MaxDrawdown = MaxDrawdown;
|
|
59567
60911
|
exports.Memory = Memory;
|
|
60912
|
+
exports.MemoryBacktest = MemoryBacktest;
|
|
60913
|
+
exports.MemoryBacktestAdapter = MemoryBacktestAdapter;
|
|
60914
|
+
exports.MemoryLive = MemoryLive;
|
|
60915
|
+
exports.MemoryLiveAdapter = MemoryLiveAdapter;
|
|
59568
60916
|
exports.MethodContextService = MethodContextService;
|
|
59569
60917
|
exports.Notification = Notification;
|
|
59570
60918
|
exports.NotificationBacktest = NotificationBacktest;
|
|
@@ -59584,6 +60932,7 @@ exports.PersistRecentAdapter = PersistRecentAdapter;
|
|
|
59584
60932
|
exports.PersistRiskAdapter = PersistRiskAdapter;
|
|
59585
60933
|
exports.PersistScheduleAdapter = PersistScheduleAdapter;
|
|
59586
60934
|
exports.PersistSignalAdapter = PersistSignalAdapter;
|
|
60935
|
+
exports.PersistStateAdapter = PersistStateAdapter;
|
|
59587
60936
|
exports.PersistStorageAdapter = PersistStorageAdapter;
|
|
59588
60937
|
exports.Position = Position;
|
|
59589
60938
|
exports.PositionSize = PositionSize;
|
|
@@ -59597,6 +60946,11 @@ exports.ReportWriter = ReportWriter;
|
|
|
59597
60946
|
exports.Risk = Risk;
|
|
59598
60947
|
exports.Schedule = Schedule;
|
|
59599
60948
|
exports.Session = Session;
|
|
60949
|
+
exports.State = State;
|
|
60950
|
+
exports.StateBacktest = StateBacktest;
|
|
60951
|
+
exports.StateBacktestAdapter = StateBacktestAdapter;
|
|
60952
|
+
exports.StateLive = StateLive;
|
|
60953
|
+
exports.StateLiveAdapter = StateLiveAdapter;
|
|
59600
60954
|
exports.Storage = Storage;
|
|
59601
60955
|
exports.StorageBacktest = StorageBacktest;
|
|
59602
60956
|
exports.StorageLive = StorageLive;
|
|
@@ -59626,6 +60980,7 @@ exports.commitTrailingStop = commitTrailingStop;
|
|
|
59626
60980
|
exports.commitTrailingStopCost = commitTrailingStopCost;
|
|
59627
60981
|
exports.commitTrailingTake = commitTrailingTake;
|
|
59628
60982
|
exports.commitTrailingTakeCost = commitTrailingTakeCost;
|
|
60983
|
+
exports.createSignalState = createSignalState;
|
|
59629
60984
|
exports.dumpAgentAnswer = dumpAgentAnswer;
|
|
59630
60985
|
exports.dumpError = dumpError;
|
|
59631
60986
|
exports.dumpJson = dumpJson;
|
|
@@ -59692,6 +61047,7 @@ exports.getPositionWaitingMinutes = getPositionWaitingMinutes;
|
|
|
59692
61047
|
exports.getRawCandles = getRawCandles;
|
|
59693
61048
|
exports.getRiskSchema = getRiskSchema;
|
|
59694
61049
|
exports.getScheduledSignal = getScheduledSignal;
|
|
61050
|
+
exports.getSignalState = getSignalState;
|
|
59695
61051
|
exports.getSizingSchema = getSizingSchema;
|
|
59696
61052
|
exports.getStrategySchema = getStrategySchema;
|
|
59697
61053
|
exports.getSymbol = getSymbol;
|
|
@@ -59777,6 +61133,7 @@ exports.set = set;
|
|
|
59777
61133
|
exports.setColumns = setColumns;
|
|
59778
61134
|
exports.setConfig = setConfig;
|
|
59779
61135
|
exports.setLogger = setLogger;
|
|
61136
|
+
exports.setSignalState = setSignalState;
|
|
59780
61137
|
exports.shutdown = shutdown;
|
|
59781
61138
|
exports.slPercentShiftToPrice = slPercentShiftToPrice;
|
|
59782
61139
|
exports.slPriceToPercentShift = slPriceToPercentShift;
|