backtest-kit 7.4.0 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.mjs CHANGED
@@ -997,6 +997,22 @@ const PERSIST_MEMORY_UTILS_METHOD_NAME_LIST_DATA = "PersistMemoryUtils.listMemor
997
997
  const PERSIST_MEMORY_UTILS_METHOD_NAME_HAS_DATA = "PersistMemoryUtils.hasMemoryData";
998
998
  const PERSIST_MEMORY_UTILS_METHOD_NAME_CLEAR = "PersistMemoryUtils.clear";
999
999
  const PERSIST_MEMORY_UTILS_METHOD_NAME_DISPOSE = "PersistMemoryUtils.dispose";
1000
+ const PERSIST_STATE_UTILS_METHOD_NAME_USE_PERSIST_STATE_ADAPTER = "PersistStateUtils.usePersistStateAdapter";
1001
+ const PERSIST_STATE_UTILS_METHOD_NAME_READ_DATA = "PersistStateUtils.readStateData";
1002
+ const PERSIST_STATE_UTILS_METHOD_NAME_WRITE_DATA = "PersistStateUtils.writeStateData";
1003
+ const PERSIST_STATE_UTILS_METHOD_NAME_CLEAR = "PersistStateUtils.clear";
1004
+ const PERSIST_STATE_UTILS_METHOD_NAME_DISPOSE = "PersistStateUtils.dispose";
1005
+ const PERSIST_STATE_UTILS_METHOD_NAME_WAIT_FOR_INIT = "PersistStateUtils.waitForInit";
1006
+ const PERSIST_STATE_UTILS_METHOD_NAME_USE_DUMMY = "PersistStateUtils.useDummy";
1007
+ const PERSIST_STATE_UTILS_METHOD_NAME_USE_JSON = "PersistStateUtils.useJson";
1008
+ const PERSIST_SESSION_UTILS_METHOD_NAME_USE_PERSIST_SESSION_ADAPTER = "PersistSessionUtils.usePersistSessionAdapter";
1009
+ const PERSIST_SESSION_UTILS_METHOD_NAME_READ_DATA = "PersistSessionUtils.readSessionData";
1010
+ const PERSIST_SESSION_UTILS_METHOD_NAME_WRITE_DATA = "PersistSessionUtils.writeSessionData";
1011
+ const PERSIST_SESSION_UTILS_METHOD_NAME_CLEAR = "PersistSessionUtils.clear";
1012
+ const PERSIST_SESSION_UTILS_METHOD_NAME_DISPOSE = "PersistSessionUtils.dispose";
1013
+ const PERSIST_SESSION_UTILS_METHOD_NAME_WAIT_FOR_INIT = "PersistSessionUtils.waitForInit";
1014
+ const PERSIST_SESSION_UTILS_METHOD_NAME_USE_DUMMY = "PersistSessionUtils.useDummy";
1015
+ const PERSIST_SESSION_UTILS_METHOD_NAME_USE_JSON = "PersistSessionUtils.useJson";
1000
1016
  const PERSIST_RECENT_UTILS_METHOD_NAME_USE_PERSIST_RECENT_ADAPTER = "PersistRecentUtils.usePersistRecentAdapter";
1001
1017
  const PERSIST_RECENT_UTILS_METHOD_NAME_READ_DATA = "PersistRecentUtils.readRecentData";
1002
1018
  const PERSIST_RECENT_UTILS_METHOD_NAME_WRITE_DATA = "PersistRecentUtils.writeRecentData";
@@ -2958,6 +2974,271 @@ class PersistRecentUtils {
2958
2974
  * Used by RecentPersistBacktestUtils/RecentPersistLiveUtils for recent signal persistence.
2959
2975
  */
2960
2976
  const PersistRecentAdapter = new PersistRecentUtils();
2977
+ /**
2978
+ * Utility class for managing state persistence.
2979
+ *
2980
+ * Features:
2981
+ * - Memoized storage instances per (signalId, bucketName) pair
2982
+ * - Custom adapter support
2983
+ * - Atomic read/write operations
2984
+ *
2985
+ * Storage layout: ./dump/state/<signalId>/<bucketName>.json
2986
+ *
2987
+ * Used by StatePersistInstance for crash-safe state persistence.
2988
+ */
2989
+ class PersistStateUtils {
2990
+ constructor() {
2991
+ this.PersistStateFactory = PersistBase;
2992
+ this.getStateStorage = memoize(([signalId, bucketName]) => `${signalId}:${bucketName}`, (signalId, bucketName) => Reflect.construct(this.PersistStateFactory, [
2993
+ bucketName,
2994
+ `./dump/state/${signalId}/`,
2995
+ ]));
2996
+ /**
2997
+ * Initializes the storage for a given (signalId, bucketName) pair.
2998
+ *
2999
+ * @param signalId - Signal identifier
3000
+ * @param bucketName - Bucket name
3001
+ * @param initial - Whether this is the first initialization
3002
+ */
3003
+ this.waitForInit = async (signalId, bucketName, initial) => {
3004
+ LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_WAIT_FOR_INIT, {
3005
+ signalId,
3006
+ bucketName,
3007
+ initial,
3008
+ });
3009
+ const key = `${signalId}:${bucketName}`;
3010
+ const isInitial = initial && !this.getStateStorage.has(key);
3011
+ const stateStorage = this.getStateStorage(signalId, bucketName);
3012
+ await stateStorage.waitForInit(isInitial);
3013
+ };
3014
+ /**
3015
+ * Reads a state entry from persistence storage.
3016
+ *
3017
+ * @param signalId - Signal identifier
3018
+ * @param bucketName - Bucket name
3019
+ * @returns Promise resolving to entry data or null if not found
3020
+ */
3021
+ this.readStateData = async (signalId, bucketName) => {
3022
+ LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_READ_DATA, {
3023
+ signalId,
3024
+ bucketName,
3025
+ });
3026
+ const key = `${signalId}:${bucketName}`;
3027
+ const isInitial = !this.getStateStorage.has(key);
3028
+ const stateStorage = this.getStateStorage(signalId, bucketName);
3029
+ await stateStorage.waitForInit(isInitial);
3030
+ if (await stateStorage.hasValue(bucketName)) {
3031
+ return await stateStorage.readValue(bucketName);
3032
+ }
3033
+ return null;
3034
+ };
3035
+ /**
3036
+ * Writes a state entry to disk with atomic file writes.
3037
+ *
3038
+ * @param data - Entry data to persist
3039
+ * @param signalId - Signal identifier
3040
+ * @param bucketName - Bucket name
3041
+ */
3042
+ this.writeStateData = async (data, signalId, bucketName) => {
3043
+ LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_WRITE_DATA, {
3044
+ signalId,
3045
+ bucketName,
3046
+ });
3047
+ const key = `${signalId}:${bucketName}`;
3048
+ const isInitial = !this.getStateStorage.has(key);
3049
+ const stateStorage = this.getStateStorage(signalId, bucketName);
3050
+ await stateStorage.waitForInit(isInitial);
3051
+ await stateStorage.writeValue(bucketName, data);
3052
+ };
3053
+ /**
3054
+ * Switches to a dummy persist adapter that discards all writes.
3055
+ * All future persistence writes will be no-ops.
3056
+ */
3057
+ this.useDummy = () => {
3058
+ LOGGER_SERVICE$7.log(PERSIST_STATE_UTILS_METHOD_NAME_USE_DUMMY);
3059
+ this.usePersistStateAdapter(PersistDummy);
3060
+ };
3061
+ /**
3062
+ * Switches to the default JSON persist adapter.
3063
+ * All future persistence writes will use JSON storage.
3064
+ */
3065
+ this.useJson = () => {
3066
+ LOGGER_SERVICE$7.log(PERSIST_STATE_UTILS_METHOD_NAME_USE_JSON);
3067
+ this.usePersistStateAdapter(PersistBase);
3068
+ };
3069
+ /**
3070
+ * Clears the memoized storage cache.
3071
+ * Call this when process.cwd() changes between strategy iterations
3072
+ * so new storage instances are created with the updated base path.
3073
+ */
3074
+ this.clear = () => {
3075
+ LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_CLEAR);
3076
+ this.getStateStorage.clear();
3077
+ };
3078
+ /**
3079
+ * Disposes of the state adapter and releases any resources.
3080
+ * Call this when a signal is removed to clean up its associated storage.
3081
+ *
3082
+ * @param signalId - Signal identifier
3083
+ * @param bucketName - Bucket name
3084
+ */
3085
+ this.dispose = (signalId, bucketName) => {
3086
+ LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_DISPOSE);
3087
+ const key = `${signalId}:${bucketName}`;
3088
+ this.getStateStorage.clear(key);
3089
+ };
3090
+ }
3091
+ /**
3092
+ * Registers a custom persistence adapter.
3093
+ *
3094
+ * @param Ctor - Custom PersistBase constructor
3095
+ */
3096
+ usePersistStateAdapter(Ctor) {
3097
+ LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_USE_PERSIST_STATE_ADAPTER);
3098
+ this.PersistStateFactory = Ctor;
3099
+ }
3100
+ }
3101
+ /**
3102
+ * Global singleton instance of PersistStateUtils.
3103
+ * Used by StatePersistInstance for crash-safe state persistence.
3104
+ */
3105
+ const PersistStateAdapter = new PersistStateUtils();
3106
+ /**
3107
+ * Utility class for managing session persistence.
3108
+ *
3109
+ * Features:
3110
+ * - Memoized storage instances per (strategyName, exchangeName, frameName) key
3111
+ * - Custom adapter support
3112
+ * - Atomic read/write operations
3113
+ *
3114
+ * Storage layout: ./dump/session/<strategyName>/<exchangeName>/<frameName>.json
3115
+ *
3116
+ * Used by SessionPersistInstance for crash-safe session persistence.
3117
+ */
3118
+ class PersistSessionUtils {
3119
+ constructor() {
3120
+ this.PersistSessionFactory = PersistBase;
3121
+ this.getSessionStorage = memoize(([strategyName, exchangeName, frameName]) => `${strategyName}:${exchangeName}:${frameName}`, (strategyName, exchangeName, frameName) => Reflect.construct(this.PersistSessionFactory, [
3122
+ frameName,
3123
+ `./dump/session/${strategyName}/${exchangeName}/`,
3124
+ ]));
3125
+ /**
3126
+ * Initializes the storage for a given (strategyName, exchangeName, frameName) triple.
3127
+ *
3128
+ * @param strategyName - Strategy identifier
3129
+ * @param exchangeName - Exchange identifier
3130
+ * @param frameName - Frame identifier
3131
+ * @param initial - Whether this is the first initialization
3132
+ */
3133
+ this.waitForInit = async (strategyName, exchangeName, frameName, initial) => {
3134
+ LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_WAIT_FOR_INIT, {
3135
+ strategyName,
3136
+ exchangeName,
3137
+ frameName,
3138
+ initial,
3139
+ });
3140
+ const key = `${strategyName}:${exchangeName}:${frameName}`;
3141
+ const isInitial = initial && !this.getSessionStorage.has(key);
3142
+ const sessionStorage = this.getSessionStorage(strategyName, exchangeName, frameName);
3143
+ await sessionStorage.waitForInit(isInitial);
3144
+ };
3145
+ /**
3146
+ * Reads a session entry from persistence storage.
3147
+ *
3148
+ * @param strategyName - Strategy identifier
3149
+ * @param exchangeName - Exchange identifier
3150
+ * @param frameName - Frame identifier
3151
+ * @returns Promise resolving to entry data or null if not found
3152
+ */
3153
+ this.readSessionData = async (strategyName, exchangeName, frameName) => {
3154
+ LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_READ_DATA, {
3155
+ strategyName,
3156
+ exchangeName,
3157
+ frameName,
3158
+ });
3159
+ const key = `${strategyName}:${exchangeName}:${frameName}`;
3160
+ const isInitial = !this.getSessionStorage.has(key);
3161
+ const sessionStorage = this.getSessionStorage(strategyName, exchangeName, frameName);
3162
+ await sessionStorage.waitForInit(isInitial);
3163
+ if (await sessionStorage.hasValue(frameName)) {
3164
+ return await sessionStorage.readValue(frameName);
3165
+ }
3166
+ return null;
3167
+ };
3168
+ /**
3169
+ * Writes a session entry to disk with atomic file writes.
3170
+ *
3171
+ * @param data - Entry data to persist
3172
+ * @param strategyName - Strategy identifier
3173
+ * @param exchangeName - Exchange identifier
3174
+ * @param frameName - Frame identifier
3175
+ */
3176
+ this.writeSessionData = async (data, strategyName, exchangeName, frameName) => {
3177
+ LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_WRITE_DATA, {
3178
+ strategyName,
3179
+ exchangeName,
3180
+ frameName,
3181
+ });
3182
+ const key = `${strategyName}:${exchangeName}:${frameName}`;
3183
+ const isInitial = !this.getSessionStorage.has(key);
3184
+ const sessionStorage = this.getSessionStorage(strategyName, exchangeName, frameName);
3185
+ await sessionStorage.waitForInit(isInitial);
3186
+ await sessionStorage.writeValue(frameName, data);
3187
+ };
3188
+ /**
3189
+ * Switches to a dummy persist adapter that discards all writes.
3190
+ * All future persistence writes will be no-ops.
3191
+ */
3192
+ this.useDummy = () => {
3193
+ LOGGER_SERVICE$7.log(PERSIST_SESSION_UTILS_METHOD_NAME_USE_DUMMY);
3194
+ this.usePersistSessionAdapter(PersistDummy);
3195
+ };
3196
+ /**
3197
+ * Switches to the default JSON persist adapter.
3198
+ * All future persistence writes will use JSON storage.
3199
+ */
3200
+ this.useJson = () => {
3201
+ LOGGER_SERVICE$7.log(PERSIST_SESSION_UTILS_METHOD_NAME_USE_JSON);
3202
+ this.usePersistSessionAdapter(PersistBase);
3203
+ };
3204
+ /**
3205
+ * Clears the memoized storage cache.
3206
+ * Call this when process.cwd() changes between strategy iterations
3207
+ * so new storage instances are created with the updated base path.
3208
+ */
3209
+ this.clear = () => {
3210
+ LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_CLEAR);
3211
+ this.getSessionStorage.clear();
3212
+ };
3213
+ /**
3214
+ * Disposes of the session adapter and releases any resources.
3215
+ * Call this when a session is removed to clean up its associated storage.
3216
+ *
3217
+ * @param strategyName - Strategy identifier
3218
+ * @param exchangeName - Exchange identifier
3219
+ * @param frameName - Frame identifier
3220
+ */
3221
+ this.dispose = (strategyName, exchangeName, frameName) => {
3222
+ LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_DISPOSE);
3223
+ const key = `${strategyName}:${exchangeName}:${frameName}`;
3224
+ this.getSessionStorage.clear(key);
3225
+ };
3226
+ }
3227
+ /**
3228
+ * Registers a custom persistence adapter.
3229
+ *
3230
+ * @param Ctor - Custom PersistBase constructor
3231
+ */
3232
+ usePersistSessionAdapter(Ctor) {
3233
+ LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_USE_PERSIST_SESSION_ADAPTER);
3234
+ this.PersistSessionFactory = Ctor;
3235
+ }
3236
+ }
3237
+ /**
3238
+ * Global singleton instance of PersistSessionUtils.
3239
+ * Used by SessionPersistInstance for crash-safe session persistence.
3240
+ */
3241
+ const PersistSessionAdapter = new PersistSessionUtils();
2961
3242
 
2962
3243
  var _a$2, _b$2;
2963
3244
  const BUSY_DELAY = 100;
@@ -10423,7 +10704,7 @@ const GET_RISK_FN = (dto, backtest, exchangeName, frameName, self) => {
10423
10704
  * @param backtest - Whether running in backtest mode
10424
10705
  * @returns Unique string key for memoization
10425
10706
  */
10426
- const CREATE_KEY_FN$v = (symbol, strategyName, exchangeName, frameName, backtest) => {
10707
+ const CREATE_KEY_FN$x = (symbol, strategyName, exchangeName, frameName, backtest) => {
10427
10708
  const parts = [symbol, strategyName, exchangeName];
10428
10709
  if (frameName)
10429
10710
  parts.push(frameName);
@@ -10723,7 +11004,7 @@ class StrategyConnectionService {
10723
11004
  * @param backtest - Whether running in backtest mode
10724
11005
  * @returns Configured ClientStrategy instance
10725
11006
  */
10726
- this.getStrategy = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$v(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
11007
+ this.getStrategy = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$x(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
10727
11008
  const { riskName = "", riskList = [], getSignal, interval = STRATEGY_DEFAULT_INTERVAL, callbacks, } = this.strategySchemaService.get(strategyName);
10728
11009
  return new ClientStrategy({
10729
11010
  symbol,
@@ -11685,7 +11966,7 @@ class StrategyConnectionService {
11685
11966
  }
11686
11967
  return;
11687
11968
  }
11688
- const key = CREATE_KEY_FN$v(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
11969
+ const key = CREATE_KEY_FN$x(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
11689
11970
  if (!this.getStrategy.has(key)) {
11690
11971
  return;
11691
11972
  }
@@ -12859,7 +13140,7 @@ class ClientRisk {
12859
13140
  * @param backtest - Whether running in backtest mode
12860
13141
  * @returns Unique string key for memoization
12861
13142
  */
12862
- const CREATE_KEY_FN$u = (riskName, exchangeName, frameName, backtest) => {
13143
+ const CREATE_KEY_FN$w = (riskName, exchangeName, frameName, backtest) => {
12863
13144
  const parts = [riskName, exchangeName];
12864
13145
  if (frameName)
12865
13146
  parts.push(frameName);
@@ -12959,7 +13240,7 @@ class RiskConnectionService {
12959
13240
  * @param backtest - True if backtest mode, false if live mode
12960
13241
  * @returns Configured ClientRisk instance
12961
13242
  */
12962
- this.getRisk = memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$u(riskName, exchangeName, frameName, backtest), (riskName, exchangeName, frameName, backtest) => {
13243
+ this.getRisk = memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$w(riskName, exchangeName, frameName, backtest), (riskName, exchangeName, frameName, backtest) => {
12963
13244
  const schema = this.riskSchemaService.get(riskName);
12964
13245
  return new ClientRisk({
12965
13246
  ...schema,
@@ -13028,7 +13309,7 @@ class RiskConnectionService {
13028
13309
  payload,
13029
13310
  });
13030
13311
  if (payload) {
13031
- const key = CREATE_KEY_FN$u(payload.riskName, payload.exchangeName, payload.frameName, payload.backtest);
13312
+ const key = CREATE_KEY_FN$w(payload.riskName, payload.exchangeName, payload.frameName, payload.backtest);
13032
13313
  this.getRisk.clear(key);
13033
13314
  }
13034
13315
  else {
@@ -14147,7 +14428,7 @@ class ClientAction {
14147
14428
  * @param backtest - Whether running in backtest mode
14148
14429
  * @returns Unique string key for memoization
14149
14430
  */
14150
- const CREATE_KEY_FN$t = (actionName, strategyName, exchangeName, frameName, backtest) => {
14431
+ const CREATE_KEY_FN$v = (actionName, strategyName, exchangeName, frameName, backtest) => {
14151
14432
  const parts = [actionName, strategyName, exchangeName];
14152
14433
  if (frameName)
14153
14434
  parts.push(frameName);
@@ -14199,7 +14480,7 @@ class ActionConnectionService {
14199
14480
  * @param backtest - True if backtest mode, false if live mode
14200
14481
  * @returns Configured ClientAction instance
14201
14482
  */
14202
- this.getAction = memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$t(actionName, strategyName, exchangeName, frameName, backtest), (actionName, strategyName, exchangeName, frameName, backtest) => {
14483
+ this.getAction = memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$v(actionName, strategyName, exchangeName, frameName, backtest), (actionName, strategyName, exchangeName, frameName, backtest) => {
14203
14484
  const schema = this.actionSchemaService.get(actionName);
14204
14485
  return new ClientAction({
14205
14486
  ...schema,
@@ -14425,7 +14706,7 @@ class ActionConnectionService {
14425
14706
  await Promise.all(actions.map(async (action) => await action.dispose()));
14426
14707
  return;
14427
14708
  }
14428
- const key = CREATE_KEY_FN$t(payload.actionName, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
14709
+ const key = CREATE_KEY_FN$v(payload.actionName, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
14429
14710
  if (!this.getAction.has(key)) {
14430
14711
  return;
14431
14712
  }
@@ -14443,7 +14724,7 @@ const METHOD_NAME_VALIDATE$3 = "exchangeCoreService validate";
14443
14724
  * @param exchangeName - Exchange name
14444
14725
  * @returns Unique string key for memoization
14445
14726
  */
14446
- const CREATE_KEY_FN$s = (exchangeName) => {
14727
+ const CREATE_KEY_FN$u = (exchangeName) => {
14447
14728
  return exchangeName;
14448
14729
  };
14449
14730
  /**
@@ -14467,7 +14748,7 @@ class ExchangeCoreService {
14467
14748
  * @param exchangeName - Name of the exchange to validate
14468
14749
  * @returns Promise that resolves when validation is complete
14469
14750
  */
14470
- this.validate = memoize(([exchangeName]) => CREATE_KEY_FN$s(exchangeName), async (exchangeName) => {
14751
+ this.validate = memoize(([exchangeName]) => CREATE_KEY_FN$u(exchangeName), async (exchangeName) => {
14471
14752
  this.loggerService.log(METHOD_NAME_VALIDATE$3, {
14472
14753
  exchangeName,
14473
14754
  });
@@ -14719,7 +15000,7 @@ const METHOD_NAME_VALIDATE$2 = "strategyCoreService validate";
14719
15000
  * @param context - Execution context with strategyName, exchangeName, frameName
14720
15001
  * @returns Unique string key for memoization
14721
15002
  */
14722
- const CREATE_KEY_FN$r = (context) => {
15003
+ const CREATE_KEY_FN$t = (context) => {
14723
15004
  const parts = [context.strategyName, context.exchangeName];
14724
15005
  if (context.frameName)
14725
15006
  parts.push(context.frameName);
@@ -14751,7 +15032,7 @@ class StrategyCoreService {
14751
15032
  * @param context - Execution context with strategyName, exchangeName, frameName
14752
15033
  * @returns Promise that resolves when validation is complete
14753
15034
  */
14754
- this.validate = memoize(([context]) => CREATE_KEY_FN$r(context), async (context) => {
15035
+ this.validate = memoize(([context]) => CREATE_KEY_FN$t(context), async (context) => {
14755
15036
  this.loggerService.log(METHOD_NAME_VALIDATE$2, {
14756
15037
  context,
14757
15038
  });
@@ -16121,7 +16402,7 @@ class SizingGlobalService {
16121
16402
  * @param context - Context with riskName, exchangeName, frameName
16122
16403
  * @returns Unique string key for memoization
16123
16404
  */
16124
- const CREATE_KEY_FN$q = (context) => {
16405
+ const CREATE_KEY_FN$s = (context) => {
16125
16406
  const parts = [context.riskName, context.exchangeName];
16126
16407
  if (context.frameName)
16127
16408
  parts.push(context.frameName);
@@ -16147,7 +16428,7 @@ class RiskGlobalService {
16147
16428
  * @param payload - Payload with riskName, exchangeName and frameName
16148
16429
  * @returns Promise that resolves when validation is complete
16149
16430
  */
16150
- this.validate = memoize(([context]) => CREATE_KEY_FN$q(context), async (context) => {
16431
+ this.validate = memoize(([context]) => CREATE_KEY_FN$s(context), async (context) => {
16151
16432
  this.loggerService.log("riskGlobalService validate", {
16152
16433
  context,
16153
16434
  });
@@ -16225,7 +16506,7 @@ const METHOD_NAME_VALIDATE$1 = "actionCoreService validate";
16225
16506
  * @param context - Execution context with strategyName, exchangeName, frameName
16226
16507
  * @returns Unique string key for memoization
16227
16508
  */
16228
- const CREATE_KEY_FN$p = (context) => {
16509
+ const CREATE_KEY_FN$r = (context) => {
16229
16510
  const parts = [context.strategyName, context.exchangeName];
16230
16511
  if (context.frameName)
16231
16512
  parts.push(context.frameName);
@@ -16269,7 +16550,7 @@ class ActionCoreService {
16269
16550
  * @param context - Strategy execution context with strategyName, exchangeName and frameName
16270
16551
  * @returns Promise that resolves when all validations complete
16271
16552
  */
16272
- this.validate = memoize(([context]) => CREATE_KEY_FN$p(context), async (context) => {
16553
+ this.validate = memoize(([context]) => CREATE_KEY_FN$r(context), async (context) => {
16273
16554
  this.loggerService.log(METHOD_NAME_VALIDATE$1, {
16274
16555
  context,
16275
16556
  });
@@ -21339,7 +21620,7 @@ const ReportWriter = new ReportWriterAdapter();
21339
21620
  * @param backtest - Whether running in backtest mode
21340
21621
  * @returns Unique string key for memoization
21341
21622
  */
21342
- const CREATE_KEY_FN$o = (symbol, strategyName, exchangeName, frameName, backtest) => {
21623
+ const CREATE_KEY_FN$q = (symbol, strategyName, exchangeName, frameName, backtest) => {
21343
21624
  const parts = [symbol, strategyName, exchangeName];
21344
21625
  if (frameName)
21345
21626
  parts.push(frameName);
@@ -21585,7 +21866,7 @@ class BacktestMarkdownService {
21585
21866
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
21586
21867
  * Each combination gets its own isolated storage instance.
21587
21868
  */
21588
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$o(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$a(symbol, strategyName, exchangeName, frameName));
21869
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$q(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$a(symbol, strategyName, exchangeName, frameName));
21589
21870
  /**
21590
21871
  * Processes tick events and accumulates closed signals.
21591
21872
  * Should be called from IStrategyCallbacks.onTick.
@@ -21742,7 +22023,7 @@ class BacktestMarkdownService {
21742
22023
  payload,
21743
22024
  });
21744
22025
  if (payload) {
21745
- const key = CREATE_KEY_FN$o(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
22026
+ const key = CREATE_KEY_FN$q(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
21746
22027
  this.getStorage.clear(key);
21747
22028
  }
21748
22029
  else {
@@ -21804,7 +22085,7 @@ class BacktestMarkdownService {
21804
22085
  * @param backtest - Whether running in backtest mode
21805
22086
  * @returns Unique string key for memoization
21806
22087
  */
21807
- const CREATE_KEY_FN$n = (symbol, strategyName, exchangeName, frameName, backtest) => {
22088
+ const CREATE_KEY_FN$p = (symbol, strategyName, exchangeName, frameName, backtest) => {
21808
22089
  const parts = [symbol, strategyName, exchangeName];
21809
22090
  if (frameName)
21810
22091
  parts.push(frameName);
@@ -22299,7 +22580,7 @@ class LiveMarkdownService {
22299
22580
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
22300
22581
  * Each combination gets its own isolated storage instance.
22301
22582
  */
22302
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$n(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$9(symbol, strategyName, exchangeName, frameName));
22583
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$p(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$9(symbol, strategyName, exchangeName, frameName));
22303
22584
  /**
22304
22585
  * Subscribes to live signal emitter to receive tick events.
22305
22586
  * Protected against multiple subscriptions.
@@ -22517,7 +22798,7 @@ class LiveMarkdownService {
22517
22798
  payload,
22518
22799
  });
22519
22800
  if (payload) {
22520
- const key = CREATE_KEY_FN$n(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
22801
+ const key = CREATE_KEY_FN$p(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
22521
22802
  this.getStorage.clear(key);
22522
22803
  }
22523
22804
  else {
@@ -22537,7 +22818,7 @@ class LiveMarkdownService {
22537
22818
  * @param backtest - Whether running in backtest mode
22538
22819
  * @returns Unique string key for memoization
22539
22820
  */
22540
- const CREATE_KEY_FN$m = (symbol, strategyName, exchangeName, frameName, backtest) => {
22821
+ const CREATE_KEY_FN$o = (symbol, strategyName, exchangeName, frameName, backtest) => {
22541
22822
  const parts = [symbol, strategyName, exchangeName];
22542
22823
  if (frameName)
22543
22824
  parts.push(frameName);
@@ -22826,7 +23107,7 @@ class ScheduleMarkdownService {
22826
23107
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
22827
23108
  * Each combination gets its own isolated storage instance.
22828
23109
  */
22829
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$m(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$8(symbol, strategyName, exchangeName, frameName));
23110
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$o(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$8(symbol, strategyName, exchangeName, frameName));
22830
23111
  /**
22831
23112
  * Subscribes to signal emitter to receive scheduled signal events.
22832
23113
  * Protected against multiple subscriptions.
@@ -23029,7 +23310,7 @@ class ScheduleMarkdownService {
23029
23310
  payload,
23030
23311
  });
23031
23312
  if (payload) {
23032
- const key = CREATE_KEY_FN$m(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
23313
+ const key = CREATE_KEY_FN$o(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
23033
23314
  this.getStorage.clear(key);
23034
23315
  }
23035
23316
  else {
@@ -23049,7 +23330,7 @@ class ScheduleMarkdownService {
23049
23330
  * @param backtest - Whether running in backtest mode
23050
23331
  * @returns Unique string key for memoization
23051
23332
  */
23052
- const CREATE_KEY_FN$l = (symbol, strategyName, exchangeName, frameName, backtest) => {
23333
+ const CREATE_KEY_FN$n = (symbol, strategyName, exchangeName, frameName, backtest) => {
23053
23334
  const parts = [symbol, strategyName, exchangeName];
23054
23335
  if (frameName)
23055
23336
  parts.push(frameName);
@@ -23294,7 +23575,7 @@ class PerformanceMarkdownService {
23294
23575
  * Memoized function to get or create PerformanceStorage for a symbol-strategy-exchange-frame-backtest combination.
23295
23576
  * Each combination gets its own isolated storage instance.
23296
23577
  */
23297
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$l(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new PerformanceStorage(symbol, strategyName, exchangeName, frameName));
23578
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$n(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new PerformanceStorage(symbol, strategyName, exchangeName, frameName));
23298
23579
  /**
23299
23580
  * Subscribes to performance emitter to receive performance events.
23300
23581
  * Protected against multiple subscriptions.
@@ -23461,7 +23742,7 @@ class PerformanceMarkdownService {
23461
23742
  payload,
23462
23743
  });
23463
23744
  if (payload) {
23464
- const key = CREATE_KEY_FN$l(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
23745
+ const key = CREATE_KEY_FN$n(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
23465
23746
  this.getStorage.clear(key);
23466
23747
  }
23467
23748
  else {
@@ -23940,7 +24221,7 @@ class WalkerMarkdownService {
23940
24221
  * @param backtest - Whether running in backtest mode
23941
24222
  * @returns Unique string key for memoization
23942
24223
  */
23943
- const CREATE_KEY_FN$k = (exchangeName, frameName, backtest) => {
24224
+ const CREATE_KEY_FN$m = (exchangeName, frameName, backtest) => {
23944
24225
  const parts = [exchangeName];
23945
24226
  if (frameName)
23946
24227
  parts.push(frameName);
@@ -24387,7 +24668,7 @@ class HeatMarkdownService {
24387
24668
  * Memoized function to get or create HeatmapStorage for exchange, frame and backtest mode.
24388
24669
  * Each exchangeName + frameName + backtest mode combination gets its own isolated heatmap storage instance.
24389
24670
  */
24390
- this.getStorage = memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$k(exchangeName, frameName, backtest), (exchangeName, frameName, backtest) => new HeatmapStorage(exchangeName, frameName, backtest));
24671
+ this.getStorage = memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$m(exchangeName, frameName, backtest), (exchangeName, frameName, backtest) => new HeatmapStorage(exchangeName, frameName, backtest));
24391
24672
  /**
24392
24673
  * Subscribes to signal emitter to receive tick events.
24393
24674
  * Protected against multiple subscriptions.
@@ -24605,7 +24886,7 @@ class HeatMarkdownService {
24605
24886
  payload,
24606
24887
  });
24607
24888
  if (payload) {
24608
- const key = CREATE_KEY_FN$k(payload.exchangeName, payload.frameName, payload.backtest);
24889
+ const key = CREATE_KEY_FN$m(payload.exchangeName, payload.frameName, payload.backtest);
24609
24890
  this.getStorage.clear(key);
24610
24891
  }
24611
24892
  else {
@@ -25636,7 +25917,7 @@ class ClientPartial {
25636
25917
  * @param backtest - Whether running in backtest mode
25637
25918
  * @returns Unique string key for memoization
25638
25919
  */
25639
- const CREATE_KEY_FN$j = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
25920
+ const CREATE_KEY_FN$l = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
25640
25921
  /**
25641
25922
  * Creates a callback function for emitting profit events to partialProfitSubject.
25642
25923
  *
@@ -25758,7 +26039,7 @@ class PartialConnectionService {
25758
26039
  * Key format: "signalId:backtest" or "signalId:live"
25759
26040
  * Value: ClientPartial instance with logger and event emitters
25760
26041
  */
25761
- this.getPartial = memoize(([signalId, backtest]) => CREATE_KEY_FN$j(signalId, backtest), (signalId, backtest) => {
26042
+ this.getPartial = memoize(([signalId, backtest]) => CREATE_KEY_FN$l(signalId, backtest), (signalId, backtest) => {
25762
26043
  return new ClientPartial({
25763
26044
  signalId,
25764
26045
  logger: this.loggerService,
@@ -25848,7 +26129,7 @@ class PartialConnectionService {
25848
26129
  const partial = this.getPartial(data.id, backtest);
25849
26130
  await partial.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
25850
26131
  await partial.clear(symbol, data, priceClose, backtest);
25851
- const key = CREATE_KEY_FN$j(data.id, backtest);
26132
+ const key = CREATE_KEY_FN$l(data.id, backtest);
25852
26133
  this.getPartial.clear(key);
25853
26134
  };
25854
26135
  }
@@ -25864,7 +26145,7 @@ class PartialConnectionService {
25864
26145
  * @param backtest - Whether running in backtest mode
25865
26146
  * @returns Unique string key for memoization
25866
26147
  */
25867
- const CREATE_KEY_FN$i = (symbol, strategyName, exchangeName, frameName, backtest) => {
26148
+ const CREATE_KEY_FN$k = (symbol, strategyName, exchangeName, frameName, backtest) => {
25868
26149
  const parts = [symbol, strategyName, exchangeName];
25869
26150
  if (frameName)
25870
26151
  parts.push(frameName);
@@ -26087,7 +26368,7 @@ class PartialMarkdownService {
26087
26368
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
26088
26369
  * Each combination gets its own isolated storage instance.
26089
26370
  */
26090
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$i(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$6(symbol, strategyName, exchangeName, frameName));
26371
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$k(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$6(symbol, strategyName, exchangeName, frameName));
26091
26372
  /**
26092
26373
  * Subscribes to partial profit/loss signal emitters to receive events.
26093
26374
  * Protected against multiple subscriptions.
@@ -26297,7 +26578,7 @@ class PartialMarkdownService {
26297
26578
  payload,
26298
26579
  });
26299
26580
  if (payload) {
26300
- const key = CREATE_KEY_FN$i(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
26581
+ const key = CREATE_KEY_FN$k(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
26301
26582
  this.getStorage.clear(key);
26302
26583
  }
26303
26584
  else {
@@ -26313,7 +26594,7 @@ class PartialMarkdownService {
26313
26594
  * @param context - Context with strategyName, exchangeName, frameName
26314
26595
  * @returns Unique string key for memoization
26315
26596
  */
26316
- const CREATE_KEY_FN$h = (context) => {
26597
+ const CREATE_KEY_FN$j = (context) => {
26317
26598
  const parts = [context.strategyName, context.exchangeName];
26318
26599
  if (context.frameName)
26319
26600
  parts.push(context.frameName);
@@ -26387,7 +26668,7 @@ class PartialGlobalService {
26387
26668
  * @param context - Context with strategyName, exchangeName and frameName
26388
26669
  * @param methodName - Name of the calling method for error tracking
26389
26670
  */
26390
- this.validate = memoize(([context]) => CREATE_KEY_FN$h(context), (context, methodName) => {
26671
+ this.validate = memoize(([context]) => CREATE_KEY_FN$j(context), (context, methodName) => {
26391
26672
  this.loggerService.log("partialGlobalService validate", {
26392
26673
  context,
26393
26674
  methodName,
@@ -26842,7 +27123,7 @@ class ClientBreakeven {
26842
27123
  * @param backtest - Whether running in backtest mode
26843
27124
  * @returns Unique string key for memoization
26844
27125
  */
26845
- const CREATE_KEY_FN$g = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
27126
+ const CREATE_KEY_FN$i = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
26846
27127
  /**
26847
27128
  * Creates a callback function for emitting breakeven events to breakevenSubject.
26848
27129
  *
@@ -26928,7 +27209,7 @@ class BreakevenConnectionService {
26928
27209
  * Key format: "signalId:backtest" or "signalId:live"
26929
27210
  * Value: ClientBreakeven instance with logger and event emitter
26930
27211
  */
26931
- this.getBreakeven = memoize(([signalId, backtest]) => CREATE_KEY_FN$g(signalId, backtest), (signalId, backtest) => {
27212
+ this.getBreakeven = memoize(([signalId, backtest]) => CREATE_KEY_FN$i(signalId, backtest), (signalId, backtest) => {
26932
27213
  return new ClientBreakeven({
26933
27214
  signalId,
26934
27215
  logger: this.loggerService,
@@ -26989,7 +27270,7 @@ class BreakevenConnectionService {
26989
27270
  const breakeven = this.getBreakeven(data.id, backtest);
26990
27271
  await breakeven.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
26991
27272
  await breakeven.clear(symbol, data, priceClose, backtest);
26992
- const key = CREATE_KEY_FN$g(data.id, backtest);
27273
+ const key = CREATE_KEY_FN$i(data.id, backtest);
26993
27274
  this.getBreakeven.clear(key);
26994
27275
  };
26995
27276
  }
@@ -27005,7 +27286,7 @@ class BreakevenConnectionService {
27005
27286
  * @param backtest - Whether running in backtest mode
27006
27287
  * @returns Unique string key for memoization
27007
27288
  */
27008
- const CREATE_KEY_FN$f = (symbol, strategyName, exchangeName, frameName, backtest) => {
27289
+ const CREATE_KEY_FN$h = (symbol, strategyName, exchangeName, frameName, backtest) => {
27009
27290
  const parts = [symbol, strategyName, exchangeName];
27010
27291
  if (frameName)
27011
27292
  parts.push(frameName);
@@ -27180,7 +27461,7 @@ class BreakevenMarkdownService {
27180
27461
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
27181
27462
  * Each combination gets its own isolated storage instance.
27182
27463
  */
27183
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$f(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$5(symbol, strategyName, exchangeName, frameName));
27464
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$h(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$5(symbol, strategyName, exchangeName, frameName));
27184
27465
  /**
27185
27466
  * Subscribes to breakeven signal emitter to receive events.
27186
27467
  * Protected against multiple subscriptions.
@@ -27369,7 +27650,7 @@ class BreakevenMarkdownService {
27369
27650
  payload,
27370
27651
  });
27371
27652
  if (payload) {
27372
- const key = CREATE_KEY_FN$f(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
27653
+ const key = CREATE_KEY_FN$h(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
27373
27654
  this.getStorage.clear(key);
27374
27655
  }
27375
27656
  else {
@@ -27385,7 +27666,7 @@ class BreakevenMarkdownService {
27385
27666
  * @param context - Context with strategyName, exchangeName, frameName
27386
27667
  * @returns Unique string key for memoization
27387
27668
  */
27388
- const CREATE_KEY_FN$e = (context) => {
27669
+ const CREATE_KEY_FN$g = (context) => {
27389
27670
  const parts = [context.strategyName, context.exchangeName];
27390
27671
  if (context.frameName)
27391
27672
  parts.push(context.frameName);
@@ -27459,7 +27740,7 @@ class BreakevenGlobalService {
27459
27740
  * @param context - Context with strategyName, exchangeName and frameName
27460
27741
  * @param methodName - Name of the calling method for error tracking
27461
27742
  */
27462
- this.validate = memoize(([context]) => CREATE_KEY_FN$e(context), (context, methodName) => {
27743
+ this.validate = memoize(([context]) => CREATE_KEY_FN$g(context), (context, methodName) => {
27463
27744
  this.loggerService.log("breakevenGlobalService validate", {
27464
27745
  context,
27465
27746
  methodName,
@@ -27680,7 +27961,7 @@ class ConfigValidationService {
27680
27961
  * @param backtest - Whether running in backtest mode
27681
27962
  * @returns Unique string key for memoization
27682
27963
  */
27683
- const CREATE_KEY_FN$d = (symbol, strategyName, exchangeName, frameName, backtest) => {
27964
+ const CREATE_KEY_FN$f = (symbol, strategyName, exchangeName, frameName, backtest) => {
27684
27965
  const parts = [symbol, strategyName, exchangeName];
27685
27966
  if (frameName)
27686
27967
  parts.push(frameName);
@@ -27847,7 +28128,7 @@ class RiskMarkdownService {
27847
28128
  * Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
27848
28129
  * Each combination gets its own isolated storage instance.
27849
28130
  */
27850
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$d(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$4(symbol, strategyName, exchangeName, frameName));
28131
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$f(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$4(symbol, strategyName, exchangeName, frameName));
27851
28132
  /**
27852
28133
  * Subscribes to risk rejection emitter to receive rejection events.
27853
28134
  * Protected against multiple subscriptions.
@@ -28036,7 +28317,7 @@ class RiskMarkdownService {
28036
28317
  payload,
28037
28318
  });
28038
28319
  if (payload) {
28039
- const key = CREATE_KEY_FN$d(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
28320
+ const key = CREATE_KEY_FN$f(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
28040
28321
  this.getStorage.clear(key);
28041
28322
  }
28042
28323
  else {
@@ -30616,7 +30897,7 @@ class HighestProfitReportService {
30616
30897
  * @returns Colon-separated key string for memoization
30617
30898
  * @internal
30618
30899
  */
30619
- const CREATE_KEY_FN$c = (symbol, strategyName, exchangeName, frameName, backtest) => {
30900
+ const CREATE_KEY_FN$e = (symbol, strategyName, exchangeName, frameName, backtest) => {
30620
30901
  const parts = [symbol, strategyName, exchangeName];
30621
30902
  if (frameName)
30622
30903
  parts.push(frameName);
@@ -30858,7 +31139,7 @@ class StrategyMarkdownService {
30858
31139
  *
30859
31140
  * @internal
30860
31141
  */
30861
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$c(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$3(symbol, strategyName, exchangeName, frameName));
31142
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$e(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$3(symbol, strategyName, exchangeName, frameName));
30862
31143
  /**
30863
31144
  * Records a cancel-scheduled event when a scheduled signal is cancelled.
30864
31145
  *
@@ -31432,7 +31713,7 @@ class StrategyMarkdownService {
31432
31713
  this.clear = async (payload) => {
31433
31714
  this.loggerService.log("strategyMarkdownService clear", { payload });
31434
31715
  if (payload) {
31435
- const key = CREATE_KEY_FN$c(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
31716
+ const key = CREATE_KEY_FN$e(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
31436
31717
  this.getStorage.clear(key);
31437
31718
  }
31438
31719
  else {
@@ -31540,7 +31821,7 @@ class StrategyMarkdownService {
31540
31821
  * Creates a unique key for memoizing ReportStorage instances.
31541
31822
  * Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
31542
31823
  */
31543
- const CREATE_KEY_FN$b = (symbol, strategyName, exchangeName, frameName, backtest) => {
31824
+ const CREATE_KEY_FN$d = (symbol, strategyName, exchangeName, frameName, backtest) => {
31544
31825
  const parts = [symbol, strategyName, exchangeName];
31545
31826
  if (frameName)
31546
31827
  parts.push(frameName);
@@ -31733,7 +32014,7 @@ let ReportStorage$2 = class ReportStorage {
31733
32014
  class SyncMarkdownService {
31734
32015
  constructor() {
31735
32016
  this.loggerService = inject(TYPES.loggerService);
31736
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$b(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$2(symbol, strategyName, exchangeName, frameName, backtest));
32017
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$d(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$2(symbol, strategyName, exchangeName, frameName, backtest));
31737
32018
  /**
31738
32019
  * Subscribes to `syncSubject` to start receiving `SignalSyncContract` events.
31739
32020
  * Protected against multiple subscriptions via `singleshot` — subsequent calls
@@ -31931,7 +32212,7 @@ class SyncMarkdownService {
31931
32212
  this.clear = async (payload) => {
31932
32213
  this.loggerService.log("syncMarkdownService clear", { payload });
31933
32214
  if (payload) {
31934
- const key = CREATE_KEY_FN$b(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
32215
+ const key = CREATE_KEY_FN$d(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
31935
32216
  this.getStorage.clear(key);
31936
32217
  }
31937
32218
  else {
@@ -31944,7 +32225,7 @@ class SyncMarkdownService {
31944
32225
  /**
31945
32226
  * Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
31946
32227
  */
31947
- const CREATE_KEY_FN$a = (symbol, strategyName, exchangeName, frameName, backtest) => {
32228
+ const CREATE_KEY_FN$c = (symbol, strategyName, exchangeName, frameName, backtest) => {
31948
32229
  const parts = [symbol, strategyName, exchangeName];
31949
32230
  if (frameName)
31950
32231
  parts.push(frameName);
@@ -32122,7 +32403,7 @@ let ReportStorage$1 = class ReportStorage {
32122
32403
  class HighestProfitMarkdownService {
32123
32404
  constructor() {
32124
32405
  this.loggerService = inject(TYPES.loggerService);
32125
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$a(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$1(symbol, strategyName, exchangeName, frameName));
32406
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$c(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage$1(symbol, strategyName, exchangeName, frameName));
32126
32407
  /**
32127
32408
  * Subscribes to `highestProfitSubject` to start receiving `HighestProfitContract`
32128
32409
  * events. Protected against multiple subscriptions via `singleshot` — subsequent
@@ -32288,7 +32569,7 @@ class HighestProfitMarkdownService {
32288
32569
  this.clear = async (payload) => {
32289
32570
  this.loggerService.log("highestProfitMarkdownService clear", { payload });
32290
32571
  if (payload) {
32291
- const key = CREATE_KEY_FN$a(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
32572
+ const key = CREATE_KEY_FN$c(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
32292
32573
  this.getStorage.clear(key);
32293
32574
  }
32294
32575
  else {
@@ -32310,7 +32591,7 @@ const LISTEN_TIMEOUT$1 = 120000;
32310
32591
  * @param backtest - Whether running in backtest mode
32311
32592
  * @returns Unique string key for memoization
32312
32593
  */
32313
- const CREATE_KEY_FN$9 = (symbol, strategyName, exchangeName, frameName, backtest) => {
32594
+ const CREATE_KEY_FN$b = (symbol, strategyName, exchangeName, frameName, backtest) => {
32314
32595
  const parts = [symbol, strategyName, exchangeName];
32315
32596
  if (frameName)
32316
32597
  parts.push(frameName);
@@ -32353,7 +32634,7 @@ class PriceMetaService {
32353
32634
  * Each subject holds the latest currentPrice emitted by the strategy iterator for that key.
32354
32635
  * Instances are cached until clear() is called.
32355
32636
  */
32356
- this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$9(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
32637
+ this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$b(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
32357
32638
  /**
32358
32639
  * Returns the current market price for the given symbol and context.
32359
32640
  *
@@ -32382,10 +32663,10 @@ class PriceMetaService {
32382
32663
  if (source.data) {
32383
32664
  return source.data;
32384
32665
  }
32385
- console.warn(`PriceMetaService: No currentPrice available for ${CREATE_KEY_FN$9(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
32666
+ console.warn(`PriceMetaService: No currentPrice available for ${CREATE_KEY_FN$b(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
32386
32667
  const currentPrice = await waitForNext(source, (data) => !!data, LISTEN_TIMEOUT$1);
32387
32668
  if (typeof currentPrice === "symbol") {
32388
- throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$9(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
32669
+ throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$b(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
32389
32670
  }
32390
32671
  return currentPrice;
32391
32672
  };
@@ -32427,7 +32708,7 @@ class PriceMetaService {
32427
32708
  this.getSource.clear();
32428
32709
  return;
32429
32710
  }
32430
- const key = CREATE_KEY_FN$9(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
32711
+ const key = CREATE_KEY_FN$b(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
32431
32712
  this.getSource.clear(key);
32432
32713
  };
32433
32714
  }
@@ -32445,7 +32726,7 @@ const LISTEN_TIMEOUT = 120000;
32445
32726
  * @param backtest - Whether running in backtest mode
32446
32727
  * @returns Unique string key for memoization
32447
32728
  */
32448
- const CREATE_KEY_FN$8 = (symbol, strategyName, exchangeName, frameName, backtest) => {
32729
+ const CREATE_KEY_FN$a = (symbol, strategyName, exchangeName, frameName, backtest) => {
32449
32730
  const parts = [symbol, strategyName, exchangeName];
32450
32731
  if (frameName)
32451
32732
  parts.push(frameName);
@@ -32488,7 +32769,7 @@ class TimeMetaService {
32488
32769
  * Each subject holds the latest createdAt timestamp emitted by the strategy iterator for that key.
32489
32770
  * Instances are cached until clear() is called.
32490
32771
  */
32491
- this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$8(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
32772
+ this.getSource = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$a(symbol, strategyName, exchangeName, frameName, backtest), () => new BehaviorSubject());
32492
32773
  /**
32493
32774
  * Returns the current candle timestamp (in milliseconds) for the given symbol and context.
32494
32775
  *
@@ -32516,10 +32797,10 @@ class TimeMetaService {
32516
32797
  if (source.data) {
32517
32798
  return source.data;
32518
32799
  }
32519
- console.warn(`TimeMetaService: No timestamp available for ${CREATE_KEY_FN$8(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
32800
+ console.warn(`TimeMetaService: No timestamp available for ${CREATE_KEY_FN$a(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}. Trying to fetch from strategy iterator as a fallback...`);
32520
32801
  const timestamp = await waitForNext(source, (data) => !!data, LISTEN_TIMEOUT);
32521
32802
  if (typeof timestamp === "symbol") {
32522
- throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$8(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
32803
+ throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$a(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
32523
32804
  }
32524
32805
  return timestamp;
32525
32806
  };
@@ -32561,7 +32842,7 @@ class TimeMetaService {
32561
32842
  this.getSource.clear();
32562
32843
  return;
32563
32844
  }
32564
- const key = CREATE_KEY_FN$8(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
32845
+ const key = CREATE_KEY_FN$a(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
32565
32846
  this.getSource.clear(key);
32566
32847
  };
32567
32848
  }
@@ -32667,7 +32948,7 @@ class MaxDrawdownReportService {
32667
32948
  /**
32668
32949
  * Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
32669
32950
  */
32670
- const CREATE_KEY_FN$7 = (symbol, strategyName, exchangeName, frameName, backtest) => {
32951
+ const CREATE_KEY_FN$9 = (symbol, strategyName, exchangeName, frameName, backtest) => {
32671
32952
  const parts = [symbol, strategyName, exchangeName];
32672
32953
  if (frameName)
32673
32954
  parts.push(frameName);
@@ -32793,7 +33074,7 @@ class ReportStorage {
32793
33074
  class MaxDrawdownMarkdownService {
32794
33075
  constructor() {
32795
33076
  this.loggerService = inject(TYPES.loggerService);
32796
- this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$7(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage(symbol, strategyName, exchangeName, frameName));
33077
+ this.getStorage = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$9(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName) => new ReportStorage(symbol, strategyName, exchangeName, frameName));
32797
33078
  /**
32798
33079
  * Subscribes to `maxDrawdownSubject` to start receiving `MaxDrawdownContract`
32799
33080
  * events. Protected against multiple subscriptions via `singleshot`.
@@ -32872,7 +33153,7 @@ class MaxDrawdownMarkdownService {
32872
33153
  this.clear = async (payload) => {
32873
33154
  this.loggerService.log("maxDrawdownMarkdownService clear", { payload });
32874
33155
  if (payload) {
32875
- const key = CREATE_KEY_FN$7(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
33156
+ const key = CREATE_KEY_FN$9(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
32876
33157
  this.getStorage.clear(key);
32877
33158
  }
32878
33159
  else {
@@ -32890,7 +33171,7 @@ const METHOD_NAME_VALIDATE = "notificationHelperService.validate";
32890
33171
  * @param context - Execution context with strategyName, exchangeName, frameName
32891
33172
  * @returns Unique string key for memoization
32892
33173
  */
32893
- const CREATE_KEY_FN$6 = (context) => {
33174
+ const CREATE_KEY_FN$8 = (context) => {
32894
33175
  const parts = [context.strategyName, context.exchangeName];
32895
33176
  if (context.frameName)
32896
33177
  parts.push(context.frameName);
@@ -32923,7 +33204,7 @@ class NotificationHelperService {
32923
33204
  * @param context - Routing context: strategyName, exchangeName, frameName
32924
33205
  * @throws {Error} If any registered schema fails validation
32925
33206
  */
32926
- this.validate = memoize(([context]) => CREATE_KEY_FN$6(context), async (context) => {
33207
+ this.validate = memoize(([context]) => CREATE_KEY_FN$8(context), async (context) => {
32927
33208
  this.loggerService.log(METHOD_NAME_VALIDATE, {
32928
33209
  context,
32929
33210
  });
@@ -46467,7 +46748,7 @@ const RECENT_ADAPTER_METHOD_NAME_GET_MINUTES_SINCE_LATEST_SIGNAL = "RecentAdapte
46467
46748
  * @param backtest - Flag indicating if the context is backtest or live
46468
46749
  * @returns Composite key string
46469
46750
  */
46470
- const CREATE_KEY_FN$5 = (symbol, strategyName, exchangeName, frameName, backtest) => {
46751
+ const CREATE_KEY_FN$7 = (symbol, strategyName, exchangeName, frameName, backtest) => {
46471
46752
  const parts = [symbol, strategyName, exchangeName];
46472
46753
  if (frameName)
46473
46754
  parts.push(frameName);
@@ -46557,7 +46838,7 @@ class RecentMemoryBacktestUtils {
46557
46838
  backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING, {
46558
46839
  signalId: event.data.id,
46559
46840
  });
46560
- const key = CREATE_KEY_FN$5(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
46841
+ const key = CREATE_KEY_FN$7(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
46561
46842
  this._signals.set(key, event.data);
46562
46843
  };
46563
46844
  /**
@@ -46570,7 +46851,7 @@ class RecentMemoryBacktestUtils {
46570
46851
  * @returns The latest signal or null if not found
46571
46852
  */
46572
46853
  this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
46573
- const key = CREATE_KEY_FN$5(symbol, strategyName, exchangeName, frameName, backtest$1);
46854
+ const key = CREATE_KEY_FN$7(symbol, strategyName, exchangeName, frameName, backtest$1);
46574
46855
  backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, { key });
46575
46856
  return this._signals.get(key) ?? null;
46576
46857
  };
@@ -46676,7 +46957,7 @@ class RecentMemoryLiveUtils {
46676
46957
  backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING, {
46677
46958
  signalId: event.data.id,
46678
46959
  });
46679
- const key = CREATE_KEY_FN$5(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
46960
+ const key = CREATE_KEY_FN$7(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
46680
46961
  this._signals.set(key, event.data);
46681
46962
  };
46682
46963
  /**
@@ -46689,7 +46970,7 @@ class RecentMemoryLiveUtils {
46689
46970
  * @returns The latest signal or null if not found
46690
46971
  */
46691
46972
  this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
46692
- const key = CREATE_KEY_FN$5(symbol, strategyName, exchangeName, frameName, backtest$1);
46973
+ const key = CREATE_KEY_FN$7(symbol, strategyName, exchangeName, frameName, backtest$1);
46693
46974
  backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, { key });
46694
46975
  return this._signals.get(key) ?? null;
46695
46976
  };
@@ -47040,6 +47321,554 @@ const RecentLive = new RecentLiveAdapter();
47040
47321
  */
47041
47322
  const RecentBacktest = new RecentBacktestAdapter();
47042
47323
 
47324
+ const CREATE_KEY_FN$6 = (signalId, bucketName) => `${signalId}_${bucketName}`;
47325
+ const STATE_LOCAL_INSTANCE_METHOD_NAME_GET = "StateLocalInstance.getState";
47326
+ const STATE_LOCAL_INSTANCE_METHOD_NAME_SET = "StateLocalInstance.setState";
47327
+ const STATE_PERSIST_INSTANCE_METHOD_NAME_WAIT_FOR_INIT = "StatePersistInstance.waitForInit";
47328
+ const STATE_PERSIST_INSTANCE_METHOD_NAME_GET = "StatePersistInstance.getState";
47329
+ const STATE_PERSIST_INSTANCE_METHOD_NAME_SET = "StatePersistInstance.setState";
47330
+ const STATE_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE = "StateBacktestAdapter.dispose";
47331
+ const STATE_BACKTEST_ADAPTER_METHOD_NAME_GET = "StateBacktestAdapter.getState";
47332
+ const STATE_BACKTEST_ADAPTER_METHOD_NAME_SET = "StateBacktestAdapter.setState";
47333
+ const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL = "StateBacktestAdapter.useLocal";
47334
+ const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST = "StateBacktestAdapter.usePersist";
47335
+ const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY = "StateBacktestAdapter.useDummy";
47336
+ const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER = "StateBacktestAdapter.useStateAdapter";
47337
+ const STATE_BACKTEST_ADAPTER_METHOD_NAME_CLEAR = "StateBacktestAdapter.clear";
47338
+ const STATE_LIVE_ADAPTER_METHOD_NAME_DISPOSE = "StateLiveAdapter.dispose";
47339
+ const STATE_LIVE_ADAPTER_METHOD_NAME_GET = "StateLiveAdapter.getState";
47340
+ const STATE_LIVE_ADAPTER_METHOD_NAME_SET = "StateLiveAdapter.setState";
47341
+ const STATE_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL = "StateLiveAdapter.useLocal";
47342
+ const STATE_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST = "StateLiveAdapter.usePersist";
47343
+ const STATE_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY = "StateLiveAdapter.useDummy";
47344
+ const STATE_LIVE_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER = "StateLiveAdapter.useStateAdapter";
47345
+ const STATE_LIVE_ADAPTER_METHOD_NAME_CLEAR = "StateLiveAdapter.clear";
47346
+ const STATE_ADAPTER_METHOD_NAME_ENABLE = "StateAdapter.enable";
47347
+ const STATE_ADAPTER_METHOD_NAME_DISABLE = "StateAdapter.disable";
47348
+ const STATE_ADAPTER_METHOD_NAME_GET = "StateAdapter.getState";
47349
+ const STATE_ADAPTER_METHOD_NAME_SET = "StateAdapter.setState";
47350
+ /**
47351
+ * In-process state instance backed by a plain object reference.
47352
+ * All data lives in process memory only - no disk persistence.
47353
+ *
47354
+ * Features:
47355
+ * - Mutable in-memory state with functional dispatch support
47356
+ * - Scoped per (signalId, bucketName) pair
47357
+ *
47358
+ * Use for backtesting and unit tests where persistence between runs is not needed.
47359
+ * Tracks per-trade metrics such as peakPercent and minutesOpen to implement
47360
+ * the capitulation rule: exit when peak < threshold after N minutes open.
47361
+ */
47362
+ class StateLocalInstance {
47363
+ constructor(initialValue, signalId, bucketName) {
47364
+ this.initialValue = initialValue;
47365
+ this.signalId = signalId;
47366
+ this.bucketName = bucketName;
47367
+ /**
47368
+ * Initializes _value from initialValue - local state needs no async setup.
47369
+ * @returns Promise that resolves immediately
47370
+ */
47371
+ this.waitForInit = singleshot(async (_initial) => {
47372
+ this._value = this.initialValue;
47373
+ });
47374
+ /**
47375
+ * Update the in-memory state value.
47376
+ * @param dispatch - New value or updater function receiving current value
47377
+ * @returns Updated state value
47378
+ */
47379
+ this.setState = queued(async (dispatch) => {
47380
+ backtest.loggerService.debug(STATE_LOCAL_INSTANCE_METHOD_NAME_SET, {
47381
+ signalId: this.signalId,
47382
+ bucketName: this.bucketName,
47383
+ });
47384
+ if (typeof dispatch === "function") {
47385
+ this._value = await dispatch(this._value);
47386
+ }
47387
+ else {
47388
+ this._value = dispatch;
47389
+ }
47390
+ return this._value;
47391
+ });
47392
+ }
47393
+ /**
47394
+ * Read the current in-memory state value.
47395
+ * @returns Current state value
47396
+ */
47397
+ async getState() {
47398
+ backtest.loggerService.debug(STATE_LOCAL_INSTANCE_METHOD_NAME_GET, {
47399
+ signalId: this.signalId,
47400
+ bucketName: this.bucketName,
47401
+ });
47402
+ return this._value;
47403
+ }
47404
+ /** Releases resources held by this instance. */
47405
+ async dispose() {
47406
+ backtest.loggerService.debug(STATE_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE, {
47407
+ signalId: this.signalId,
47408
+ bucketName: this.bucketName,
47409
+ });
47410
+ }
47411
+ }
47412
+ /**
47413
+ * No-op state instance that discards all writes.
47414
+ * Used for disabling state in tests or dry-run scenarios.
47415
+ *
47416
+ * Useful when replaying historical candles without needing to accumulate
47417
+ * peakPercent/minutesOpen — the capitulation rule is simply never triggered.
47418
+ */
47419
+ class StateDummyInstance {
47420
+ constructor(initialValue, signalId, bucketName) {
47421
+ this.initialValue = initialValue;
47422
+ this.signalId = signalId;
47423
+ this.bucketName = bucketName;
47424
+ /**
47425
+ * No-op initialization.
47426
+ * @returns Promise that resolves immediately
47427
+ */
47428
+ this.waitForInit = singleshot(async (_initial) => {
47429
+ });
47430
+ }
47431
+ /**
47432
+ * No-op read - always returns initialValue.
47433
+ * @returns initialValue
47434
+ */
47435
+ async getState() {
47436
+ return this.initialValue;
47437
+ }
47438
+ /**
47439
+ * No-op write - discards the value and returns initialValue.
47440
+ * @returns initialValue
47441
+ */
47442
+ async setState(_dispatch) {
47443
+ return this.initialValue;
47444
+ }
47445
+ /** No-op. */
47446
+ async dispose() {
47447
+ }
47448
+ }
47449
+ /**
47450
+ * File-system backed state instance.
47451
+ * Data is persisted atomically to disk via PersistStateAdapter.
47452
+ * State is restored from disk on waitForInit.
47453
+ *
47454
+ * Features:
47455
+ * - Crash-safe atomic file writes
47456
+ * - Functional dispatch support
47457
+ * - Scoped per (signalId, bucketName) pair
47458
+ *
47459
+ * Use in live trading to survive process restarts mid-trade.
47460
+ * Preserves peakPercent and minutesOpen so the capitulation rule
47461
+ * (exit if peak < threshold after N minutes) continues correctly after a crash.
47462
+ */
47463
+ class StatePersistInstance {
47464
+ constructor(initialValue, signalId, bucketName) {
47465
+ this.initialValue = initialValue;
47466
+ this.signalId = signalId;
47467
+ this.bucketName = bucketName;
47468
+ /**
47469
+ * Initialize persistence storage and restore state from disk.
47470
+ * @param initial - Whether this is the first initialization
47471
+ */
47472
+ this.waitForInit = singleshot(async (initial) => {
47473
+ backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_WAIT_FOR_INIT, {
47474
+ signalId: this.signalId,
47475
+ bucketName: this.bucketName,
47476
+ initial,
47477
+ });
47478
+ await PersistStateAdapter.waitForInit(this.signalId, this.bucketName, initial);
47479
+ const data = await PersistStateAdapter.readStateData(this.signalId, this.bucketName);
47480
+ if (data) {
47481
+ this._value = data.data;
47482
+ return;
47483
+ }
47484
+ this._value = this.initialValue;
47485
+ });
47486
+ /**
47487
+ * Update state and persist to disk atomically.
47488
+ * @param dispatch - New value or updater function receiving current value
47489
+ * @returns Updated state value
47490
+ */
47491
+ this.setState = queued(async (dispatch) => {
47492
+ backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_SET, {
47493
+ signalId: this.signalId,
47494
+ bucketName: this.bucketName,
47495
+ });
47496
+ if (typeof dispatch === "function") {
47497
+ this._value = await dispatch(this._value);
47498
+ }
47499
+ else {
47500
+ this._value = dispatch;
47501
+ }
47502
+ const id = CREATE_KEY_FN$6(this.signalId, this.bucketName);
47503
+ await PersistStateAdapter.writeStateData({ id, data: this._value }, this.signalId, this.bucketName);
47504
+ return this._value;
47505
+ });
47506
+ }
47507
+ /**
47508
+ * Read the current persisted state value.
47509
+ * @returns Current state value
47510
+ */
47511
+ async getState() {
47512
+ backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_GET, {
47513
+ signalId: this.signalId,
47514
+ bucketName: this.bucketName,
47515
+ });
47516
+ return this._value;
47517
+ }
47518
+ /** Releases resources held by this instance. */
47519
+ async dispose() {
47520
+ backtest.loggerService.debug(STATE_LIVE_ADAPTER_METHOD_NAME_DISPOSE, {
47521
+ signalId: this.signalId,
47522
+ bucketName: this.bucketName,
47523
+ });
47524
+ await PersistStateAdapter.dispose(this.signalId, this.bucketName);
47525
+ }
47526
+ }
47527
+ /**
47528
+ * Backtest state adapter with pluggable storage backend.
47529
+ *
47530
+ * Features:
47531
+ * - Adapter pattern for swappable state instance implementations
47532
+ * - Default backend: StateLocalInstance (in-memory, no disk persistence)
47533
+ * - Alternative backends: StatePersistInstance, StateDummyInstance
47534
+ * - Convenience methods: useLocal(), usePersist(), useDummy(), useStateAdapter()
47535
+ * - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from StateAdapter
47536
+ *
47537
+ * Primary use case — LLM-driven capitulation rule:
47538
+ * Profitable trades endure -0.5–2.5% drawdown and still reach peak 2–3%+.
47539
+ * SL trades never go positive (Feb25) or show peak < 0.15% (Feb08, Feb13).
47540
+ * Rule: if position open >= N minutes and peakPercent < threshold (e.g. 0.3%),
47541
+ * the LLM thesis was not confirmed by market — exit immediately.
47542
+ * State tracks `{ peakPercent, minutesOpen }` per signal across onActivePing ticks.
47543
+ */
47544
+ class StateBacktestAdapter {
47545
+ constructor() {
47546
+ this.StateFactory = StateLocalInstance;
47547
+ this.getInstance = memoize(([signalId, bucketName]) => CREATE_KEY_FN$6(signalId, bucketName), (signalId, bucketName, initialValue) => Reflect.construct(this.StateFactory, [initialValue, signalId, bucketName]));
47548
+ /**
47549
+ * Disposes all memoized instances for the given signalId.
47550
+ * Called by StateAdapter when a signal is cancelled or closed.
47551
+ * @param signalId - Signal identifier to dispose
47552
+ */
47553
+ this.disposeSignal = (signalId) => {
47554
+ const prefix = CREATE_KEY_FN$6(signalId, "");
47555
+ for (const key of this.getInstance.keys()) {
47556
+ if (key.startsWith(prefix)) {
47557
+ const instance = this.getInstance.get(key);
47558
+ instance && instance.dispose();
47559
+ this.getInstance.clear(key);
47560
+ }
47561
+ }
47562
+ };
47563
+ /**
47564
+ * Read the current state value for a signal.
47565
+ * @param dto.signalId - Signal identifier
47566
+ * @param dto.bucketName - Bucket name
47567
+ * @param dto.initialValue - Default value when no persisted state exists
47568
+ * @returns Current state value
47569
+ */
47570
+ this.getState = async (dto) => {
47571
+ backtest.loggerService.debug(STATE_BACKTEST_ADAPTER_METHOD_NAME_GET, {
47572
+ signalId: dto.signalId,
47573
+ bucketName: dto.bucketName,
47574
+ });
47575
+ const key = CREATE_KEY_FN$6(dto.signalId, dto.bucketName);
47576
+ const isInitial = !this.getInstance.has(key);
47577
+ const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
47578
+ await instance.waitForInit(isInitial);
47579
+ return await instance.getState();
47580
+ };
47581
+ /**
47582
+ * Update the state value for a signal.
47583
+ * @param dispatch - New value or updater function receiving current value
47584
+ * @param dto.signalId - Signal identifier
47585
+ * @param dto.bucketName - Bucket name
47586
+ * @param dto.initialValue - Default value when no persisted state exists
47587
+ * @returns Updated state value
47588
+ */
47589
+ this.setState = async (dispatch, dto) => {
47590
+ backtest.loggerService.debug(STATE_BACKTEST_ADAPTER_METHOD_NAME_SET, {
47591
+ signalId: dto.signalId,
47592
+ bucketName: dto.bucketName,
47593
+ });
47594
+ const key = CREATE_KEY_FN$6(dto.signalId, dto.bucketName);
47595
+ const isInitial = !this.getInstance.has(key);
47596
+ const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
47597
+ await instance.waitForInit(isInitial);
47598
+ return await instance.setState(dispatch);
47599
+ };
47600
+ /**
47601
+ * Switches to in-memory adapter (default).
47602
+ * All data lives in process memory only.
47603
+ */
47604
+ this.useLocal = () => {
47605
+ backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL);
47606
+ this.StateFactory = StateLocalInstance;
47607
+ };
47608
+ /**
47609
+ * Switches to file-system backed adapter.
47610
+ * Data is persisted to disk via PersistStateAdapter.
47611
+ */
47612
+ this.usePersist = () => {
47613
+ backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
47614
+ this.StateFactory = StatePersistInstance;
47615
+ };
47616
+ /**
47617
+ * Switches to dummy adapter that discards all writes.
47618
+ */
47619
+ this.useDummy = () => {
47620
+ backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY);
47621
+ this.StateFactory = StateDummyInstance;
47622
+ };
47623
+ /**
47624
+ * Switches to a custom state adapter implementation.
47625
+ * @param Ctor - Constructor for the custom state instance
47626
+ */
47627
+ this.useStateAdapter = (Ctor) => {
47628
+ backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER);
47629
+ this.StateFactory = Ctor;
47630
+ };
47631
+ /**
47632
+ * Clears the memoized instance cache.
47633
+ * Call this when process.cwd() changes between strategy iterations
47634
+ * so new instances are created with the updated base path.
47635
+ */
47636
+ this.clear = () => {
47637
+ backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
47638
+ this.getInstance.clear();
47639
+ };
47640
+ }
47641
+ }
47642
+ /**
47643
+ * Live trading state adapter with pluggable storage backend.
47644
+ *
47645
+ * Features:
47646
+ * - Adapter pattern for swappable state instance implementations
47647
+ * - Default backend: StatePersistInstance (file-system backed, survives restarts)
47648
+ * - Alternative backends: StateLocalInstance, StateDummyInstance
47649
+ * - Convenience methods: useLocal(), usePersist(), useDummy(), useStateAdapter()
47650
+ * - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from StateAdapter
47651
+ *
47652
+ * Primary use case — LLM-driven capitulation rule:
47653
+ * Profitable trades endure -0.5–2.5% drawdown and still reach peak 2–3%+.
47654
+ * SL trades never go positive (Feb25) or show peak < 0.15% (Feb08, Feb13).
47655
+ * Rule: if position open >= N minutes and peakPercent < threshold (e.g. 0.3%),
47656
+ * the LLM thesis was not confirmed by market — exit immediately.
47657
+ * State persists `{ peakPercent, minutesOpen }` per signal across process restarts.
47658
+ */
47659
+ class StateLiveAdapter {
47660
+ constructor() {
47661
+ this.StateFactory = StatePersistInstance;
47662
+ this.getInstance = memoize(([signalId, bucketName]) => CREATE_KEY_FN$6(signalId, bucketName), (signalId, bucketName, initialValue) => Reflect.construct(this.StateFactory, [initialValue, signalId, bucketName]));
47663
+ /**
47664
+ * Disposes all memoized instances for the given signalId.
47665
+ * Called by StateAdapter when a signal is cancelled or closed.
47666
+ * @param signalId - Signal identifier to dispose
47667
+ */
47668
+ this.disposeSignal = (signalId) => {
47669
+ const prefix = CREATE_KEY_FN$6(signalId, "");
47670
+ for (const key of this.getInstance.keys()) {
47671
+ if (key.startsWith(prefix)) {
47672
+ const instance = this.getInstance.get(key);
47673
+ instance && instance.dispose();
47674
+ this.getInstance.clear(key);
47675
+ }
47676
+ }
47677
+ };
47678
+ /**
47679
+ * Read the current state value for a signal.
47680
+ * @param dto.signalId - Signal identifier
47681
+ * @param dto.bucketName - Bucket name
47682
+ * @param dto.initialValue - Default value when no persisted state exists
47683
+ * @returns Current state value
47684
+ */
47685
+ this.getState = async (dto) => {
47686
+ backtest.loggerService.debug(STATE_LIVE_ADAPTER_METHOD_NAME_GET, {
47687
+ signalId: dto.signalId,
47688
+ bucketName: dto.bucketName,
47689
+ });
47690
+ const key = CREATE_KEY_FN$6(dto.signalId, dto.bucketName);
47691
+ const isInitial = !this.getInstance.has(key);
47692
+ const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
47693
+ await instance.waitForInit(isInitial);
47694
+ return await instance.getState();
47695
+ };
47696
+ /**
47697
+ * Update the state value for a signal.
47698
+ * @param dispatch - New value or updater function receiving current value
47699
+ * @param dto.signalId - Signal identifier
47700
+ * @param dto.bucketName - Bucket name
47701
+ * @param dto.initialValue - Default value when no persisted state exists
47702
+ * @returns Updated state value
47703
+ */
47704
+ this.setState = async (dispatch, dto) => {
47705
+ backtest.loggerService.debug(STATE_LIVE_ADAPTER_METHOD_NAME_SET, {
47706
+ signalId: dto.signalId,
47707
+ bucketName: dto.bucketName,
47708
+ });
47709
+ const key = CREATE_KEY_FN$6(dto.signalId, dto.bucketName);
47710
+ const isInitial = !this.getInstance.has(key);
47711
+ const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
47712
+ await instance.waitForInit(isInitial);
47713
+ return await instance.setState(dispatch);
47714
+ };
47715
+ /**
47716
+ * Switches to in-memory adapter.
47717
+ * All data lives in process memory only.
47718
+ */
47719
+ this.useLocal = () => {
47720
+ backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL);
47721
+ this.StateFactory = StateLocalInstance;
47722
+ };
47723
+ /**
47724
+ * Switches to file-system backed adapter (default).
47725
+ * Data is persisted to disk via PersistStateAdapter.
47726
+ */
47727
+ this.usePersist = () => {
47728
+ backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
47729
+ this.StateFactory = StatePersistInstance;
47730
+ };
47731
+ /**
47732
+ * Switches to dummy adapter that discards all writes.
47733
+ */
47734
+ this.useDummy = () => {
47735
+ backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY);
47736
+ this.StateFactory = StateDummyInstance;
47737
+ };
47738
+ /**
47739
+ * Switches to a custom state adapter implementation.
47740
+ * @param Ctor - Constructor for the custom state instance
47741
+ */
47742
+ this.useStateAdapter = (Ctor) => {
47743
+ backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER);
47744
+ this.StateFactory = Ctor;
47745
+ };
47746
+ /**
47747
+ * Clears the memoized instance cache.
47748
+ * Call this when process.cwd() changes between strategy iterations
47749
+ * so new instances are created with the updated base path.
47750
+ */
47751
+ this.clear = () => {
47752
+ backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_CLEAR);
47753
+ this.getInstance.clear();
47754
+ };
47755
+ }
47756
+ }
47757
+ /**
47758
+ * Main state adapter that manages both backtest and live state storage.
47759
+ *
47760
+ * Features:
47761
+ * - Subscribes to signal lifecycle events (cancelled/closed) to dispose stale instances
47762
+ * - Routes all operations to StateBacktest or StateLive based on dto.backtest
47763
+ * - Singleshot enable pattern prevents duplicate subscriptions
47764
+ * - Cleanup function for proper unsubscription
47765
+ */
47766
+ class StateAdapter {
47767
+ constructor() {
47768
+ /**
47769
+ * Enables state storage by subscribing to signal lifecycle events.
47770
+ * Clears memoized instances in StateBacktest and StateLive when a signal
47771
+ * is cancelled or closed, preventing stale instances from accumulating.
47772
+ * Uses singleshot to ensure one-time subscription.
47773
+ *
47774
+ * @returns Cleanup function that unsubscribes from all emitters
47775
+ */
47776
+ this.enable = singleshot(() => {
47777
+ backtest.loggerService.info(STATE_ADAPTER_METHOD_NAME_ENABLE);
47778
+ const unCancel = signalEmitter
47779
+ .filter(({ action }) => action === "cancelled")
47780
+ .connect(({ signal }) => {
47781
+ StateBacktest.disposeSignal(signal.id);
47782
+ StateLive.disposeSignal(signal.id);
47783
+ });
47784
+ const unClose = signalEmitter
47785
+ .filter(({ action }) => action === "closed")
47786
+ .connect(({ signal }) => {
47787
+ StateBacktest.disposeSignal(signal.id);
47788
+ StateLive.disposeSignal(signal.id);
47789
+ });
47790
+ return compose(() => unCancel(), () => unClose(), () => this.enable.clear());
47791
+ });
47792
+ /**
47793
+ * Disables state storage by unsubscribing from signal lifecycle events.
47794
+ * Safe to call multiple times.
47795
+ */
47796
+ this.disable = () => {
47797
+ backtest.loggerService.info(STATE_ADAPTER_METHOD_NAME_DISABLE);
47798
+ if (this.enable.hasValue()) {
47799
+ const lastSubscription = this.enable();
47800
+ lastSubscription();
47801
+ }
47802
+ };
47803
+ /**
47804
+ * Read the current state value for a signal.
47805
+ * Routes to StateBacktest or StateLive based on dto.backtest.
47806
+ * @param dto.signalId - Signal identifier
47807
+ * @param dto.bucketName - Bucket name
47808
+ * @param dto.initialValue - Default value when no persisted state exists
47809
+ * @param dto.backtest - Flag indicating if the context is backtest or live
47810
+ * @returns Current state value
47811
+ * @throws Error if adapter is not enabled
47812
+ */
47813
+ this.getState = async (dto) => {
47814
+ if (!this.enable.hasValue()) {
47815
+ throw new Error("StateAdapter is not enabled. Call enable() first.");
47816
+ }
47817
+ backtest.loggerService.debug(STATE_ADAPTER_METHOD_NAME_GET, {
47818
+ signalId: dto.signalId,
47819
+ bucketName: dto.bucketName,
47820
+ backtest: dto.backtest,
47821
+ });
47822
+ if (dto.backtest) {
47823
+ return await StateBacktest.getState(dto);
47824
+ }
47825
+ return await StateLive.getState(dto);
47826
+ };
47827
+ /**
47828
+ * Update the state value for a signal.
47829
+ * Routes to StateBacktest or StateLive based on dto.backtest.
47830
+ * @param dispatch - New value or updater function receiving current value
47831
+ * @param dto.signalId - Signal identifier
47832
+ * @param dto.bucketName - Bucket name
47833
+ * @param dto.initialValue - Default value when no persisted state exists
47834
+ * @param dto.backtest - Flag indicating if the context is backtest or live
47835
+ * @returns Updated state value
47836
+ * @throws Error if adapter is not enabled
47837
+ */
47838
+ this.setState = async (dispatch, dto) => {
47839
+ if (!this.enable.hasValue()) {
47840
+ throw new Error("StateAdapter is not enabled. Call enable() first.");
47841
+ }
47842
+ backtest.loggerService.debug(STATE_ADAPTER_METHOD_NAME_SET, {
47843
+ signalId: dto.signalId,
47844
+ bucketName: dto.bucketName,
47845
+ backtest: dto.backtest,
47846
+ });
47847
+ if (dto.backtest) {
47848
+ return await StateBacktest.setState(dispatch, dto);
47849
+ }
47850
+ return await StateLive.setState(dispatch, dto);
47851
+ };
47852
+ }
47853
+ }
47854
+ /**
47855
+ * Global singleton instance of StateAdapter.
47856
+ * Provides unified state management for backtest and live trading.
47857
+ */
47858
+ const State = new StateAdapter();
47859
+ /**
47860
+ * Global singleton instance of StateLiveAdapter.
47861
+ * Provides live trading state storage with pluggable backends.
47862
+ */
47863
+ const StateLive = new StateLiveAdapter();
47864
+ /**
47865
+ * Global singleton instance of StateBacktestAdapter.
47866
+ * Provides backtest state storage with pluggable backends.
47867
+ */
47868
+ const StateBacktest = new StateBacktestAdapter();
47869
+
47870
+ const GET_SIGNAL_STATE_METHOD_NAME = "signal.getSignalState";
47871
+ const SET_SIGNAL_STATE_METHOD_NAME = "signal.setSignalState";
47043
47872
  const GET_LATEST_SIGNAL_METHOD_NAME = "signal.getLatestSignal";
47044
47873
  const GET_MINUTES_SINCE_LATEST_SIGNAL_CREATED_METHOD_NAME = "signal.getMinutesSinceLatestSignalCreated";
47045
47874
  /**
@@ -47115,6 +47944,777 @@ async function getMinutesSinceLatestSignalCreated(symbol) {
47115
47944
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
47116
47945
  return await Recent.getMinutesSinceLatestSignalCreated(symbol, { exchangeName, frameName, strategyName });
47117
47946
  }
47947
+ /**
47948
+ * Reads the state value scoped to the current active signal.
47949
+ *
47950
+ * Resolves the active pending signal automatically from execution context.
47951
+ * If no pending signal exists, logs a warning and returns the initialValue.
47952
+ *
47953
+ * Automatically detects backtest/live mode from execution context.
47954
+ *
47955
+ * Intended for LLM-driven capitulation strategies that accumulate per-trade
47956
+ * metrics (e.g. peakPercent, minutesOpen) across onActivePing ticks.
47957
+ * Profitable trades endure -0.5–2.5% drawdown and reach peak 2–3%+.
47958
+ * SL trades show peak < 0.15% (Feb08, Feb13) or never go positive (Feb25).
47959
+ * Rule: if minutesOpen >= N and peakPercent < threshold (e.g. 0.3%) — exit.
47960
+ *
47961
+ * @param dto.bucketName - State bucket name
47962
+ * @param dto.initialValue - Default value when no persisted state exists
47963
+ * @returns Promise resolving to current state value, or initialValue if no signal
47964
+ *
47965
+ * @deprecated Better use `createSignalState().getState` with codestyle native syntax
47966
+ *
47967
+ * @example
47968
+ * ```typescript
47969
+ * import { getSignalState } from "backtest-kit";
47970
+ *
47971
+ * const { peakPercent, minutesOpen } = await getSignalState({
47972
+ * bucketName: "trade",
47973
+ * initialValue: { peakPercent: 0, minutesOpen: 0 },
47974
+ * });
47975
+ * if (minutesOpen >= 15 && peakPercent < 0.3) {
47976
+ * await commitMarketClose(symbol); // capitulate — LLM thesis not confirmed
47977
+ * }
47978
+ * ```
47979
+ */
47980
+ async function getSignalState(dto) {
47981
+ const { bucketName, initialValue } = dto;
47982
+ backtest.loggerService.info(GET_SIGNAL_STATE_METHOD_NAME, { bucketName });
47983
+ if (!ExecutionContextService.hasContext()) {
47984
+ throw new Error("getSignalState requires an execution context");
47985
+ }
47986
+ if (!MethodContextService.hasContext()) {
47987
+ throw new Error("getSignalState requires a method context");
47988
+ }
47989
+ const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
47990
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
47991
+ const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
47992
+ let signal;
47993
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
47994
+ return await State.getState({
47995
+ signalId: signal.id,
47996
+ bucketName,
47997
+ initialValue,
47998
+ backtest: isBacktest,
47999
+ });
48000
+ }
48001
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
48002
+ return await State.getState({
48003
+ signalId: signal.id,
48004
+ bucketName,
48005
+ initialValue,
48006
+ backtest: isBacktest,
48007
+ });
48008
+ }
48009
+ throw new Error(`getSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
48010
+ }
48011
+ /**
48012
+ * Updates the state value scoped to the current active signal.
48013
+ *
48014
+ * Resolves the active pending signal automatically from execution context.
48015
+ * If no pending signal exists, logs a warning and returns without writing.
48016
+ *
48017
+ * Automatically detects backtest/live mode from execution context.
48018
+ *
48019
+ * Intended for LLM-driven capitulation strategies that accumulate per-trade
48020
+ * metrics (e.g. peakPercent, minutesOpen) across onActivePing ticks.
48021
+ * Profitable trades endure -0.5–2.5% drawdown and reach peak 2–3%+.
48022
+ * SL trades show peak < 0.15% (Feb08, Feb13) or never go positive (Feb25).
48023
+ * Rule: if minutesOpen >= N and peakPercent < threshold (e.g. 0.3%) — exit.
48024
+ *
48025
+ * @param dto.bucketName - State bucket name
48026
+ * @param dto.initialValue - Default value when no persisted state exists
48027
+ * @param dto.dispatch - New value or updater function receiving current value
48028
+ * @returns Promise resolving to updated state value, or initialValue if no signal
48029
+ *
48030
+ * @deprecated Better use `createSignalState().setState` with codestyle native syntax
48031
+ *
48032
+ * @example
48033
+ * ```typescript
48034
+ * import { setSignalState } from "backtest-kit";
48035
+ *
48036
+ * await setSignalState(
48037
+ * dispatch: (s) => ({
48038
+ * peakPercent: Math.max(s.peakPercent, currentUnrealisedPercent),
48039
+ * minutesOpen: s.minutesOpen + 1,
48040
+ * }),
48041
+ * {
48042
+ * bucketName: "trade",
48043
+ * initialValue: { peakPercent: 0, minutesOpen: 0 },
48044
+ * }
48045
+ * );
48046
+ * ```
48047
+ */
48048
+ async function setSignalState(dispatch, dto) {
48049
+ const { bucketName, initialValue } = dto;
48050
+ backtest.loggerService.info(SET_SIGNAL_STATE_METHOD_NAME, { bucketName });
48051
+ if (!ExecutionContextService.hasContext()) {
48052
+ throw new Error("setSignalState requires an execution context");
48053
+ }
48054
+ if (!MethodContextService.hasContext()) {
48055
+ throw new Error("setSignalState requires a method context");
48056
+ }
48057
+ const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
48058
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
48059
+ const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
48060
+ let signal;
48061
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
48062
+ return await State.setState(dispatch, {
48063
+ signalId: signal.id,
48064
+ bucketName,
48065
+ initialValue,
48066
+ backtest: isBacktest,
48067
+ });
48068
+ }
48069
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
48070
+ return await State.setState(dispatch, {
48071
+ signalId: signal.id,
48072
+ bucketName,
48073
+ initialValue,
48074
+ backtest: isBacktest,
48075
+ });
48076
+ }
48077
+ throw new Error(`setSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
48078
+ }
48079
+
48080
+ const CREATE_KEY_FN$5 = (symbol, strategyName, exchangeName, frameName, backtest) => {
48081
+ const parts = [symbol, strategyName, exchangeName];
48082
+ if (frameName)
48083
+ parts.push(frameName);
48084
+ parts.push(backtest ? "backtest" : "live");
48085
+ return parts.join(":");
48086
+ };
48087
+ const SESSION_LOCAL_INSTANCE_METHOD_NAME_GET = "SessionLocalInstance.getData";
48088
+ const SESSION_LOCAL_INSTANCE_METHOD_NAME_SET = "SessionLocalInstance.setData";
48089
+ const SESSION_PERSIST_INSTANCE_METHOD_NAME_WAIT_FOR_INIT = "SessionPersistInstance.waitForInit";
48090
+ const SESSION_PERSIST_INSTANCE_METHOD_NAME_GET = "SessionPersistInstance.getData";
48091
+ const SESSION_PERSIST_INSTANCE_METHOD_NAME_SET = "SessionPersistInstance.setData";
48092
+ const SESSION_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE = "SessionBacktestAdapter.dispose";
48093
+ const SESSION_BACKTEST_ADAPTER_METHOD_NAME_GET = "SessionBacktestAdapter.getData";
48094
+ const SESSION_BACKTEST_ADAPTER_METHOD_NAME_SET = "SessionBacktestAdapter.setData";
48095
+ const SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL = "SessionBacktestAdapter.useLocal";
48096
+ const SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST = "SessionBacktestAdapter.usePersist";
48097
+ const SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY = "SessionBacktestAdapter.useDummy";
48098
+ const SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_SESSION_ADAPTER = "SessionBacktestAdapter.useSessionAdapter";
48099
+ const SESSION_BACKTEST_ADAPTER_METHOD_NAME_CLEAR = "SessionBacktestAdapter.clear";
48100
+ const SESSION_LIVE_ADAPTER_METHOD_NAME_DISPOSE = "SessionLiveAdapter.dispose";
48101
+ const SESSION_LIVE_ADAPTER_METHOD_NAME_GET = "SessionLiveAdapter.getData";
48102
+ const SESSION_LIVE_ADAPTER_METHOD_NAME_SET = "SessionLiveAdapter.setData";
48103
+ const SESSION_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL = "SessionLiveAdapter.useLocal";
48104
+ const SESSION_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST = "SessionLiveAdapter.usePersist";
48105
+ const SESSION_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY = "SessionLiveAdapter.useDummy";
48106
+ const SESSION_LIVE_ADAPTER_METHOD_NAME_USE_SESSION_ADAPTER = "SessionLiveAdapter.useSessionAdapter";
48107
+ const SESSION_LIVE_ADAPTER_METHOD_NAME_CLEAR = "SessionLiveAdapter.clear";
48108
+ const SESSION_ADAPTER_METHOD_NAME_GET = "SessionAdapter.getData";
48109
+ const SESSION_ADAPTER_METHOD_NAME_SET = "SessionAdapter.setData";
48110
+ /**
48111
+ * In-process session instance backed by a plain object reference.
48112
+ * All data lives in process memory only — no disk persistence.
48113
+ *
48114
+ * Features:
48115
+ * - Mutable in-memory session data
48116
+ * - Scoped per (symbol, strategyName, exchangeName, frameName) tuple
48117
+ *
48118
+ * Use for backtesting and unit tests where persistence between runs is not needed.
48119
+ */
48120
+ class SessionLocalInstance {
48121
+ constructor(symbol, strategyName, exchangeName, frameName, backtest$1) {
48122
+ this.symbol = symbol;
48123
+ this.strategyName = strategyName;
48124
+ this.exchangeName = exchangeName;
48125
+ this.frameName = frameName;
48126
+ this.backtest = backtest$1;
48127
+ this._data = null;
48128
+ /**
48129
+ * Initializes _data to null — local session needs no async setup.
48130
+ * @returns Promise that resolves immediately
48131
+ */
48132
+ this.waitForInit = singleshot(async (_initial) => {
48133
+ this._data = null;
48134
+ });
48135
+ /**
48136
+ * Read the current in-memory session value.
48137
+ * @returns Current session value, or null if not set
48138
+ */
48139
+ this.getData = async () => {
48140
+ backtest.loggerService.debug(SESSION_LOCAL_INSTANCE_METHOD_NAME_GET, {
48141
+ strategyName: this.strategyName,
48142
+ exchangeName: this.exchangeName,
48143
+ frameName: this.frameName,
48144
+ });
48145
+ return this._data;
48146
+ };
48147
+ /**
48148
+ * Update the in-memory session value.
48149
+ * @param value - New value or null to clear
48150
+ */
48151
+ this.setData = async (value) => {
48152
+ backtest.loggerService.debug(SESSION_LOCAL_INSTANCE_METHOD_NAME_SET, {
48153
+ strategyName: this.strategyName,
48154
+ exchangeName: this.exchangeName,
48155
+ frameName: this.frameName,
48156
+ });
48157
+ this._data = value;
48158
+ };
48159
+ }
48160
+ /** Releases resources held by this instance. */
48161
+ async dispose() {
48162
+ backtest.loggerService.debug(SESSION_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE, {
48163
+ strategyName: this.strategyName,
48164
+ exchangeName: this.exchangeName,
48165
+ frameName: this.frameName,
48166
+ });
48167
+ }
48168
+ }
48169
+ /**
48170
+ * No-op session instance that discards all writes.
48171
+ * Used for disabling session storage in tests or dry-run scenarios.
48172
+ *
48173
+ * Useful when replaying historical candles without needing to accumulate
48174
+ * cross-candle session state — getData always returns null.
48175
+ */
48176
+ class SessionDummyInstance {
48177
+ constructor(symbol, strategyName, exchangeName, frameName, backtest) {
48178
+ this.symbol = symbol;
48179
+ this.strategyName = strategyName;
48180
+ this.exchangeName = exchangeName;
48181
+ this.frameName = frameName;
48182
+ this.backtest = backtest;
48183
+ /**
48184
+ * No-op initialization.
48185
+ * @returns Promise that resolves immediately
48186
+ */
48187
+ this.waitForInit = singleshot(async (_initial) => {
48188
+ });
48189
+ /**
48190
+ * No-op read — always returns null.
48191
+ * @returns null
48192
+ */
48193
+ this.getData = async () => {
48194
+ return null;
48195
+ };
48196
+ /**
48197
+ * No-op write — discards the value.
48198
+ */
48199
+ this.setData = async (_value) => {
48200
+ };
48201
+ }
48202
+ /** No-op. */
48203
+ async dispose() {
48204
+ }
48205
+ }
48206
+ /**
48207
+ * File-system backed session instance.
48208
+ * Data is persisted atomically to disk via PersistSessionAdapter.
48209
+ * Session is restored from disk on waitForInit.
48210
+ *
48211
+ * Features:
48212
+ * - Crash-safe atomic file writes
48213
+ * - Scoped per (symbol, strategyName, exchangeName, frameName) tuple
48214
+ *
48215
+ * Use in live trading to survive process restarts mid-session.
48216
+ */
48217
+ class SessionPersistInstance {
48218
+ constructor(symbol, strategyName, exchangeName, frameName, backtest$1) {
48219
+ this.symbol = symbol;
48220
+ this.strategyName = strategyName;
48221
+ this.exchangeName = exchangeName;
48222
+ this.frameName = frameName;
48223
+ this.backtest = backtest$1;
48224
+ this._data = null;
48225
+ /**
48226
+ * Initialize persistence storage and restore session from disk.
48227
+ * @param initial - Whether this is the first initialization
48228
+ */
48229
+ this.waitForInit = singleshot(async (initial) => {
48230
+ backtest.loggerService.debug(SESSION_PERSIST_INSTANCE_METHOD_NAME_WAIT_FOR_INIT, {
48231
+ strategyName: this.strategyName,
48232
+ exchangeName: this.exchangeName,
48233
+ frameName: this.frameName,
48234
+ initial,
48235
+ });
48236
+ await PersistSessionAdapter.waitForInit(this.strategyName, this.exchangeName, this.frameName, initial);
48237
+ const data = await PersistSessionAdapter.readSessionData(this.strategyName, this.exchangeName, this.frameName);
48238
+ if (data) {
48239
+ this._data = data.data;
48240
+ return;
48241
+ }
48242
+ this._data = null;
48243
+ });
48244
+ /**
48245
+ * Read the current persisted session value.
48246
+ * @returns Current session value, or null if not set
48247
+ */
48248
+ this.getData = async () => {
48249
+ backtest.loggerService.debug(SESSION_PERSIST_INSTANCE_METHOD_NAME_GET, {
48250
+ strategyName: this.strategyName,
48251
+ exchangeName: this.exchangeName,
48252
+ frameName: this.frameName,
48253
+ });
48254
+ return this._data;
48255
+ };
48256
+ /**
48257
+ * Update session value and persist to disk atomically.
48258
+ * @param value - New value or null to clear
48259
+ */
48260
+ this.setData = async (value) => {
48261
+ backtest.loggerService.debug(SESSION_PERSIST_INSTANCE_METHOD_NAME_SET, {
48262
+ strategyName: this.strategyName,
48263
+ exchangeName: this.exchangeName,
48264
+ frameName: this.frameName,
48265
+ });
48266
+ this._data = value;
48267
+ const id = CREATE_KEY_FN$5(this.symbol, this.strategyName, this.exchangeName, this.frameName, this.backtest);
48268
+ await PersistSessionAdapter.writeSessionData({ id, data: value }, this.strategyName, this.exchangeName, this.frameName);
48269
+ };
48270
+ }
48271
+ /** Releases resources held by this instance. */
48272
+ async dispose() {
48273
+ backtest.loggerService.debug(SESSION_LIVE_ADAPTER_METHOD_NAME_DISPOSE, {
48274
+ strategyName: this.strategyName,
48275
+ exchangeName: this.exchangeName,
48276
+ frameName: this.frameName,
48277
+ });
48278
+ await PersistSessionAdapter.dispose(this.strategyName, this.exchangeName, this.frameName);
48279
+ }
48280
+ }
48281
+ /**
48282
+ * Backtest session adapter with pluggable storage backend.
48283
+ *
48284
+ * Features:
48285
+ * - Adapter pattern for swappable session instance implementations
48286
+ * - Default backend: SessionLocalInstance (in-memory, no disk persistence)
48287
+ * - Alternative backends: SessionPersistInstance, SessionDummyInstance
48288
+ * - Convenience methods: useLocal(), usePersist(), useDummy(), useSessionAdapter()
48289
+ * - Memoized instances per (symbol, strategyName, exchangeName, frameName) tuple
48290
+ */
48291
+ class SessionBacktestAdapter {
48292
+ constructor() {
48293
+ this.SessionFactory = SessionLocalInstance;
48294
+ this.getInstance = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$5(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => Reflect.construct(this.SessionFactory, [symbol, strategyName, exchangeName, frameName, backtest]));
48295
+ /**
48296
+ * Read the current session value for a backtest run.
48297
+ * @param symbol - Trading pair symbol
48298
+ * @param context.strategyName - Strategy identifier
48299
+ * @param context.exchangeName - Exchange identifier
48300
+ * @param context.frameName - Frame identifier
48301
+ * @returns Current session value, or null if not set
48302
+ */
48303
+ this.getData = async (symbol, context) => {
48304
+ backtest.loggerService.debug(SESSION_BACKTEST_ADAPTER_METHOD_NAME_GET, {
48305
+ strategyName: context.strategyName,
48306
+ exchangeName: context.exchangeName,
48307
+ frameName: context.frameName,
48308
+ });
48309
+ const key = CREATE_KEY_FN$5(symbol, context.strategyName, context.exchangeName, context.frameName, true);
48310
+ const isInitial = !this.getInstance.has(key);
48311
+ const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, true);
48312
+ await instance.waitForInit(isInitial);
48313
+ return await instance.getData();
48314
+ };
48315
+ /**
48316
+ * Update the session value for a backtest run.
48317
+ * @param symbol - Trading pair symbol
48318
+ * @param value - New value or null to clear
48319
+ * @param context.strategyName - Strategy identifier
48320
+ * @param context.exchangeName - Exchange identifier
48321
+ * @param context.frameName - Frame identifier
48322
+ */
48323
+ this.setData = async (symbol, value, context) => {
48324
+ backtest.loggerService.debug(SESSION_BACKTEST_ADAPTER_METHOD_NAME_SET, {
48325
+ strategyName: context.strategyName,
48326
+ exchangeName: context.exchangeName,
48327
+ frameName: context.frameName,
48328
+ });
48329
+ const key = CREATE_KEY_FN$5(symbol, context.strategyName, context.exchangeName, context.frameName, true);
48330
+ const isInitial = !this.getInstance.has(key);
48331
+ const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, true);
48332
+ await instance.waitForInit(isInitial);
48333
+ return await instance.setData(value);
48334
+ };
48335
+ /**
48336
+ * Switches to in-memory adapter (default).
48337
+ * All data lives in process memory only.
48338
+ */
48339
+ this.useLocal = () => {
48340
+ backtest.loggerService.info(SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL);
48341
+ this.SessionFactory = SessionLocalInstance;
48342
+ };
48343
+ /**
48344
+ * Switches to file-system backed adapter.
48345
+ * Data is persisted to disk via PersistSessionAdapter.
48346
+ */
48347
+ this.usePersist = () => {
48348
+ backtest.loggerService.info(SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
48349
+ this.SessionFactory = SessionPersistInstance;
48350
+ };
48351
+ /**
48352
+ * Switches to dummy adapter that discards all writes.
48353
+ */
48354
+ this.useDummy = () => {
48355
+ backtest.loggerService.info(SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY);
48356
+ this.SessionFactory = SessionDummyInstance;
48357
+ };
48358
+ /**
48359
+ * Switches to a custom session adapter implementation.
48360
+ * @param Ctor - Constructor for the custom session instance
48361
+ */
48362
+ this.useSessionAdapter = (Ctor) => {
48363
+ backtest.loggerService.info(SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_SESSION_ADAPTER);
48364
+ this.SessionFactory = Ctor;
48365
+ };
48366
+ /**
48367
+ * Clears the memoized instance cache.
48368
+ * Call this when process.cwd() changes between strategy iterations
48369
+ * so new instances are created with the updated base path.
48370
+ */
48371
+ this.clear = () => {
48372
+ backtest.loggerService.info(SESSION_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
48373
+ this.getInstance.clear();
48374
+ };
48375
+ }
48376
+ }
48377
+ /**
48378
+ * Live trading session adapter with pluggable storage backend.
48379
+ *
48380
+ * Features:
48381
+ * - Adapter pattern for swappable session instance implementations
48382
+ * - Default backend: SessionPersistInstance (file-system backed, survives restarts)
48383
+ * - Alternative backends: SessionLocalInstance, SessionDummyInstance
48384
+ * - Convenience methods: useLocal(), usePersist(), useDummy(), useSessionAdapter()
48385
+ * - Memoized instances per (symbol, strategyName, exchangeName, frameName) tuple
48386
+ */
48387
+ class SessionLiveAdapter {
48388
+ constructor() {
48389
+ this.SessionFactory = SessionPersistInstance;
48390
+ this.getInstance = memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$5(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => Reflect.construct(this.SessionFactory, [symbol, strategyName, exchangeName, frameName, backtest]));
48391
+ /**
48392
+ * Read the current session value for a live run.
48393
+ * @param symbol - Trading pair symbol
48394
+ * @param context.strategyName - Strategy identifier
48395
+ * @param context.exchangeName - Exchange identifier
48396
+ * @param context.frameName - Frame identifier
48397
+ * @returns Current session value, or null if not set
48398
+ */
48399
+ this.getData = async (symbol, context) => {
48400
+ backtest.loggerService.debug(SESSION_LIVE_ADAPTER_METHOD_NAME_GET, {
48401
+ strategyName: context.strategyName,
48402
+ exchangeName: context.exchangeName,
48403
+ frameName: context.frameName,
48404
+ });
48405
+ const key = CREATE_KEY_FN$5(symbol, context.strategyName, context.exchangeName, context.frameName, false);
48406
+ const isInitial = !this.getInstance.has(key);
48407
+ const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, false);
48408
+ await instance.waitForInit(isInitial);
48409
+ return await instance.getData();
48410
+ };
48411
+ /**
48412
+ * Update the session value for a live run.
48413
+ * @param symbol - Trading pair symbol
48414
+ * @param value - New value or null to clear
48415
+ * @param context.strategyName - Strategy identifier
48416
+ * @param context.exchangeName - Exchange identifier
48417
+ * @param context.frameName - Frame identifier
48418
+ */
48419
+ this.setData = async (symbol, value, context) => {
48420
+ backtest.loggerService.debug(SESSION_LIVE_ADAPTER_METHOD_NAME_SET, {
48421
+ strategyName: context.strategyName,
48422
+ exchangeName: context.exchangeName,
48423
+ frameName: context.frameName,
48424
+ });
48425
+ const key = CREATE_KEY_FN$5(symbol, context.strategyName, context.exchangeName, context.frameName, false);
48426
+ const isInitial = !this.getInstance.has(key);
48427
+ const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, false);
48428
+ await instance.waitForInit(isInitial);
48429
+ return await instance.setData(value);
48430
+ };
48431
+ /**
48432
+ * Switches to in-memory adapter.
48433
+ * All data lives in process memory only.
48434
+ */
48435
+ this.useLocal = () => {
48436
+ backtest.loggerService.info(SESSION_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL);
48437
+ this.SessionFactory = SessionLocalInstance;
48438
+ };
48439
+ /**
48440
+ * Switches to file-system backed adapter (default).
48441
+ * Data is persisted to disk via PersistSessionAdapter.
48442
+ */
48443
+ this.usePersist = () => {
48444
+ backtest.loggerService.info(SESSION_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
48445
+ this.SessionFactory = SessionPersistInstance;
48446
+ };
48447
+ /**
48448
+ * Switches to dummy adapter that discards all writes.
48449
+ */
48450
+ this.useDummy = () => {
48451
+ backtest.loggerService.info(SESSION_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY);
48452
+ this.SessionFactory = SessionDummyInstance;
48453
+ };
48454
+ /**
48455
+ * Switches to a custom session adapter implementation.
48456
+ * @param Ctor - Constructor for the custom session instance
48457
+ */
48458
+ this.useSessionAdapter = (Ctor) => {
48459
+ backtest.loggerService.info(SESSION_LIVE_ADAPTER_METHOD_NAME_USE_SESSION_ADAPTER);
48460
+ this.SessionFactory = Ctor;
48461
+ };
48462
+ /**
48463
+ * Clears the memoized instance cache.
48464
+ * Call this when process.cwd() changes between strategy iterations
48465
+ * so new instances are created with the updated base path.
48466
+ */
48467
+ this.clear = () => {
48468
+ backtest.loggerService.info(SESSION_LIVE_ADAPTER_METHOD_NAME_CLEAR);
48469
+ this.getInstance.clear();
48470
+ };
48471
+ }
48472
+ }
48473
+ /**
48474
+ * Main session adapter that manages both backtest and live session storage.
48475
+ *
48476
+ * Features:
48477
+ * - Routes all operations to SessionBacktest or SessionLive based on the backtest flag
48478
+ */
48479
+ class SessionAdapter {
48480
+ constructor() {
48481
+ /**
48482
+ * Read the current session value for a signal.
48483
+ * Routes to SessionBacktest or SessionLive based on backtest.
48484
+ * @param symbol - Trading pair symbol
48485
+ * @param context.strategyName - Strategy identifier
48486
+ * @param context.exchangeName - Exchange identifier
48487
+ * @param context.frameName - Frame identifier
48488
+ * @param backtest - Flag indicating if the context is backtest or live
48489
+ * @returns Current session value, or null if not set
48490
+ */
48491
+ this.getData = async (symbol, context, backtest$1) => {
48492
+ backtest.loggerService.debug(SESSION_ADAPTER_METHOD_NAME_GET, {
48493
+ strategyName: context.strategyName,
48494
+ exchangeName: context.exchangeName,
48495
+ frameName: context.frameName,
48496
+ backtest: backtest$1,
48497
+ });
48498
+ if (backtest$1) {
48499
+ return await SessionBacktest.getData(symbol, context);
48500
+ }
48501
+ return await SessionLive.getData(symbol, context);
48502
+ };
48503
+ /**
48504
+ * Update the session value for a signal.
48505
+ * Routes to SessionBacktest or SessionLive based on backtest.
48506
+ * @param symbol - Trading pair symbol
48507
+ * @param value - New value or null to clear
48508
+ * @param context.strategyName - Strategy identifier
48509
+ * @param context.exchangeName - Exchange identifier
48510
+ * @param context.frameName - Frame identifier
48511
+ * @param backtest - Flag indicating if the context is backtest or live
48512
+ */
48513
+ this.setData = async (symbol, value, context, backtest$1) => {
48514
+ backtest.loggerService.debug(SESSION_ADAPTER_METHOD_NAME_SET, {
48515
+ strategyName: context.strategyName,
48516
+ exchangeName: context.exchangeName,
48517
+ frameName: context.frameName,
48518
+ backtest: backtest$1,
48519
+ });
48520
+ if (backtest$1) {
48521
+ return await SessionBacktest.setData(symbol, value, context);
48522
+ }
48523
+ return await SessionLive.setData(symbol, value, context);
48524
+ };
48525
+ }
48526
+ }
48527
+ /**
48528
+ * Global singleton instance of SessionAdapter.
48529
+ * Provides unified session management for backtest and live trading.
48530
+ */
48531
+ const Session = new SessionAdapter();
48532
+ /**
48533
+ * Global singleton instance of SessionLiveAdapter.
48534
+ * Provides live trading session storage with pluggable backends.
48535
+ */
48536
+ const SessionLive = new SessionLiveAdapter();
48537
+ /**
48538
+ * Global singleton instance of SessionBacktestAdapter.
48539
+ * Provides backtest session storage with pluggable backends.
48540
+ */
48541
+ const SessionBacktest = new SessionBacktestAdapter();
48542
+
48543
+ const GET_SESSION_METHOD_NAME = "session.getSession";
48544
+ const SET_SESSION_METHOD_NAME = "session.setSession";
48545
+ /**
48546
+ * Reads the session value scoped to the current (symbol, strategy, exchange, frame) context.
48547
+ *
48548
+ * Session data persists across candles within a single run and can survive process
48549
+ * restarts in live mode — useful for caching LLM inference results, intermediate
48550
+ * indicator state, or any cross-candle accumulator that is not tied to a specific signal.
48551
+ *
48552
+ * Automatically detects backtest/live mode from execution context.
48553
+ *
48554
+ * @param symbol - Trading pair symbol
48555
+ * @returns Promise resolving to current session value, or null if not set
48556
+ *
48557
+ * @example
48558
+ * ```typescript
48559
+ * import { getSession } from "backtest-kit";
48560
+ *
48561
+ * const session = await getSession<{ lastLlmSignal: string }>("BTCUSDT");
48562
+ * if (session?.lastLlmSignal === "buy") {
48563
+ * // reuse cached LLM result instead of calling the model again
48564
+ * }
48565
+ * ```
48566
+ */
48567
+ async function getSessionData(symbol) {
48568
+ backtest.loggerService.info(GET_SESSION_METHOD_NAME, { symbol });
48569
+ if (!ExecutionContextService.hasContext()) {
48570
+ throw new Error("getSession requires an execution context");
48571
+ }
48572
+ if (!MethodContextService.hasContext()) {
48573
+ throw new Error("getSession requires a method context");
48574
+ }
48575
+ const { backtest: isBacktest } = backtest.executionContextService.context;
48576
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
48577
+ return await Session.getData(symbol, { exchangeName, frameName, strategyName }, isBacktest);
48578
+ }
48579
+ /**
48580
+ * Writes a session value scoped to the current (symbol, strategy, exchange, frame) context.
48581
+ *
48582
+ * Session data persists across candles within a single run and can survive process
48583
+ * restarts in live mode — useful for caching LLM inference results, intermediate
48584
+ * indicator state, or any cross-candle accumulator that is not tied to a specific signal.
48585
+ *
48586
+ * Pass null to clear the session.
48587
+ *
48588
+ * Automatically detects backtest/live mode from execution context.
48589
+ *
48590
+ * @param symbol - Trading pair symbol
48591
+ * @param value - New value or null to clear
48592
+ * @returns Promise that resolves when the session has been written
48593
+ *
48594
+ * @example
48595
+ * ```typescript
48596
+ * import { setSession } from "backtest-kit";
48597
+ *
48598
+ * await setSession("BTCUSDT", { lastLlmSignal: "buy" });
48599
+ * ```
48600
+ */
48601
+ async function setSessionData(symbol, value) {
48602
+ backtest.loggerService.info(SET_SESSION_METHOD_NAME, { symbol });
48603
+ if (!ExecutionContextService.hasContext()) {
48604
+ throw new Error("setSession requires an execution context");
48605
+ }
48606
+ if (!MethodContextService.hasContext()) {
48607
+ throw new Error("setSession requires a method context");
48608
+ }
48609
+ const { backtest: isBacktest } = backtest.executionContextService.context;
48610
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
48611
+ await Session.setData(symbol, value, { exchangeName, frameName, strategyName }, isBacktest);
48612
+ }
48613
+
48614
+ const CREATE_SIGNAL_STATE_METHOD_NAME = "state.createSignalState";
48615
+ const CREATE_SET_STATE_FN = (params) => async (dispatch) => {
48616
+ if (!ExecutionContextService.hasContext()) {
48617
+ throw new Error("createSignalState requires an execution context");
48618
+ }
48619
+ if (!MethodContextService.hasContext()) {
48620
+ throw new Error("createSignalState requires a method context");
48621
+ }
48622
+ const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
48623
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
48624
+ const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
48625
+ let signal;
48626
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
48627
+ return await State.setState(dispatch, {
48628
+ backtest: isBacktest,
48629
+ bucketName: params.bucketName,
48630
+ initialValue: params.initialValue,
48631
+ signalId: signal.id,
48632
+ });
48633
+ }
48634
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
48635
+ return await State.setState(dispatch, {
48636
+ backtest: isBacktest,
48637
+ bucketName: params.bucketName,
48638
+ initialValue: params.initialValue,
48639
+ signalId: signal.id,
48640
+ });
48641
+ }
48642
+ throw new Error(`createSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${params.bucketName}`);
48643
+ };
48644
+ const CREATE_GET_STATE_FN = (params) => async () => {
48645
+ if (!ExecutionContextService.hasContext()) {
48646
+ throw new Error("createSignalState requires an execution context");
48647
+ }
48648
+ if (!MethodContextService.hasContext()) {
48649
+ throw new Error("createSignalState requires a method context");
48650
+ }
48651
+ const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
48652
+ const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
48653
+ const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
48654
+ let signal;
48655
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
48656
+ return await State.getState({
48657
+ backtest: isBacktest,
48658
+ bucketName: params.bucketName,
48659
+ initialValue: params.initialValue,
48660
+ signalId: signal.id,
48661
+ });
48662
+ }
48663
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
48664
+ return await State.getState({
48665
+ backtest: isBacktest,
48666
+ bucketName: params.bucketName,
48667
+ initialValue: params.initialValue,
48668
+ signalId: signal.id,
48669
+ });
48670
+ }
48671
+ throw new Error(`createSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${params.bucketName}`);
48672
+ };
48673
+ /**
48674
+ * Creates a bound [getState, setState] tuple scoped to a bucket and initial value.
48675
+ *
48676
+ * Both returned functions resolve the active pending or scheduled signal and the
48677
+ * backtest/live flag automatically from execution context — no signalId argument required.
48678
+ *
48679
+ * Automatically detects backtest/live mode from execution context.
48680
+ *
48681
+ * Intended for LLM-driven capitulation strategies that accumulate per-trade
48682
+ * metrics (e.g. peakPercent, minutesOpen) across onActivePing ticks.
48683
+ * Profitable trades endure -0.5–2.5% drawdown and reach peak 2–3%+.
48684
+ * SL trades show peak < 0.15% (Feb08, Feb13) or never go positive (Feb25).
48685
+ * Rule: if minutesOpen >= N and peakPercent < threshold (e.g. 0.3%) — exit.
48686
+ *
48687
+ * @param params.bucketName - Logical namespace for grouping state buckets within a signal
48688
+ * @param params.initialValue - Default value when no persisted state exists
48689
+ * @returns Tuple [getState, setState] bound to the bucket and initial value
48690
+ *
48691
+ * @example
48692
+ * ```typescript
48693
+ * import { createSignalState } from "backtest-kit";
48694
+ *
48695
+ * const [getTradeState, setTradeState] = createSignalState({
48696
+ * bucketName: "trade",
48697
+ * initialValue: { peakPercent: 0, minutesOpen: 0 },
48698
+ * });
48699
+ *
48700
+ * // in onActivePing:
48701
+ * await setTradeState((s) => ({
48702
+ * peakPercent: Math.max(s.peakPercent, currentUnrealisedPercent),
48703
+ * minutesOpen: s.minutesOpen + 1,
48704
+ * }));
48705
+ * const { peakPercent, minutesOpen } = await getTradeState();
48706
+ * if (minutesOpen >= 15 && peakPercent < 0.3) await commitMarketClose(symbol);
48707
+ * ```
48708
+ */
48709
+ function createSignalState(params) {
48710
+ backtest.loggerService.info(CREATE_SIGNAL_STATE_METHOD_NAME, {
48711
+ bucketName: params.bucketName,
48712
+ });
48713
+ return [
48714
+ CREATE_GET_STATE_FN(params),
48715
+ CREATE_SET_STATE_FN(params),
48716
+ ];
48717
+ }
47118
48718
 
47119
48719
  const DEFAULT_BM25_K1 = 1.5;
47120
48720
  const DEFAULT_BM25_B = 0.75;
@@ -47201,7 +48801,7 @@ const createSearchIndex = () => {
47201
48801
  return { upsert, remove, list, search, read };
47202
48802
  };
47203
48803
 
47204
- const CREATE_KEY_FN$4 = (signalId, bucketName) => `${signalId}-${bucketName}`;
48804
+ const CREATE_KEY_FN$4 = (signalId, bucketName) => `${signalId}_${bucketName}`;
47205
48805
  const LIST_MEMORY_FN = ({ id, content }) => ({
47206
48806
  memoryId: id,
47207
48807
  content: content,
@@ -47222,18 +48822,35 @@ const MEMORY_PERSIST_INSTANCE_METHOD_NAME_READ = "MemoryPersistInstance.readMemo
47222
48822
  const MEMORY_PERSIST_INSTANCE_METHOD_NAME_SEARCH = "MemoryPersistInstance.searchMemory";
47223
48823
  const MEMORY_PERSIST_INSTANCE_METHOD_NAME_LIST = "MemoryPersistInstance.listMemory";
47224
48824
  const MEMORY_PERSIST_INSTANCE_METHOD_NAME_REMOVE = "MemoryPersistInstance.removeMemory";
48825
+ const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE = "MemoryBacktestAdapter.dispose";
48826
+ const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_WRITE = "MemoryBacktestAdapter.writeMemory";
48827
+ const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_SEARCH = "MemoryBacktestAdapter.searchMemory";
48828
+ const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_LIST = "MemoryBacktestAdapter.listMemory";
48829
+ const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_REMOVE = "MemoryBacktestAdapter.removeMemory";
48830
+ const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_READ = "MemoryBacktestAdapter.readMemory";
48831
+ const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL = "MemoryBacktestAdapter.useLocal";
48832
+ const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST = "MemoryBacktestAdapter.usePersist";
48833
+ const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY = "MemoryBacktestAdapter.useDummy";
48834
+ const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER = "MemoryBacktestAdapter.useMemoryAdapter";
48835
+ const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_CLEAR = "MemoryBacktestAdapter.clear";
48836
+ const MEMORY_LIVE_ADAPTER_METHOD_NAME_DISPOSE = "MemoryLiveAdapter.dispose";
48837
+ const MEMORY_LIVE_ADAPTER_METHOD_NAME_WRITE = "MemoryLiveAdapter.writeMemory";
48838
+ const MEMORY_LIVE_ADAPTER_METHOD_NAME_SEARCH = "MemoryLiveAdapter.searchMemory";
48839
+ const MEMORY_LIVE_ADAPTER_METHOD_NAME_LIST = "MemoryLiveAdapter.listMemory";
48840
+ const MEMORY_LIVE_ADAPTER_METHOD_NAME_REMOVE = "MemoryLiveAdapter.removeMemory";
48841
+ const MEMORY_LIVE_ADAPTER_METHOD_NAME_READ = "MemoryLiveAdapter.readMemory";
48842
+ const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL = "MemoryLiveAdapter.useLocal";
48843
+ const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST = "MemoryLiveAdapter.usePersist";
48844
+ const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY = "MemoryLiveAdapter.useDummy";
48845
+ const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER = "MemoryLiveAdapter.useMemoryAdapter";
48846
+ const MEMORY_LIVE_ADAPTER_METHOD_NAME_CLEAR = "MemoryLiveAdapter.clear";
47225
48847
  const MEMORY_ADAPTER_METHOD_NAME_ENABLE = "MemoryAdapter.enable";
47226
48848
  const MEMORY_ADAPTER_METHOD_NAME_DISABLE = "MemoryAdapter.disable";
47227
- const MEMORY_ADAPTER_METHOD_NAME_DISPOSE = "MemoryAdapter.dispose";
47228
48849
  const MEMORY_ADAPTER_METHOD_NAME_WRITE = "MemoryAdapter.writeMemory";
47229
48850
  const MEMORY_ADAPTER_METHOD_NAME_SEARCH = "MemoryAdapter.searchMemory";
47230
48851
  const MEMORY_ADAPTER_METHOD_NAME_LIST = "MemoryAdapter.listMemory";
47231
48852
  const MEMORY_ADAPTER_METHOD_NAME_REMOVE = "MemoryAdapter.removeMemory";
47232
48853
  const MEMORY_ADAPTER_METHOD_NAME_READ = "MemoryAdapter.readMemory";
47233
- const MEMORY_ADAPTER_METHOD_NAME_USE_LOCAL = "MemoryAdapter.useLocal";
47234
- const MEMORY_ADAPTER_METHOD_NAME_USE_PERSIST = "MemoryAdapter.usePersist";
47235
- const MEMORY_ADAPTER_METHOD_NAME_USE_DUMMY = "MemoryAdapter.useDummy";
47236
- const MEMORY_ADAPTER_METHOD_NAME_CLEAR = "MemoryAdapter.clear";
47237
48854
  /**
47238
48855
  * In-memory BM25 search index backed instance.
47239
48856
  * All data lives in the process memory only - no disk persistence.
@@ -47258,7 +48875,7 @@ class MemoryLocalInstance {
47258
48875
  * Write a value into the BM25 index.
47259
48876
  * @param memoryId - Unique entry identifier
47260
48877
  * @param value - Value to store and index
47261
- * @param index - Optional BM25 index string; defaults to JSON.stringify(value)
48878
+ * @param description - BM25 index string
47262
48879
  */
47263
48880
  async writeMemory(memoryId, value, description) {
47264
48881
  backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_WRITE, {
@@ -47329,7 +48946,7 @@ class MemoryLocalInstance {
47329
48946
  }
47330
48947
  /** Releases resources held by this instance. */
47331
48948
  dispose() {
47332
- backtest.loggerService.debug(MEMORY_ADAPTER_METHOD_NAME_DISPOSE, {
48949
+ backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE, {
47333
48950
  signalId: this.signalId,
47334
48951
  bucketName: this.bucketName,
47335
48952
  });
@@ -47378,7 +48995,7 @@ class MemoryPersistInstance {
47378
48995
  * Write a value to disk and update the BM25 index.
47379
48996
  * @param memoryId - Unique entry identifier
47380
48997
  * @param value - Value to persist and index
47381
- * @param index - Optional BM25 index string; defaults to JSON.stringify(value)
48998
+ * @param index - BM25 index string; defaults to JSON.stringify(value)
47382
48999
  */
47383
49000
  async writeMemory(memoryId, value, index = JSON.stringify(value)) {
47384
49001
  backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_WRITE, {
@@ -47452,7 +49069,7 @@ class MemoryPersistInstance {
47452
49069
  }
47453
49070
  /** Releases resources held by this instance. */
47454
49071
  dispose() {
47455
- backtest.loggerService.debug(MEMORY_ADAPTER_METHOD_NAME_DISPOSE, {
49072
+ backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_DISPOSE, {
47456
49073
  signalId: this.signalId,
47457
49074
  bucketName: this.bucketName,
47458
49075
  });
@@ -47512,48 +49129,377 @@ class MemoryDummyInstance {
47512
49129
  }
47513
49130
  }
47514
49131
  /**
47515
- * Facade for memory instances scoped per (signalId, bucketName).
47516
- * Manages lazy initialization and instance lifecycle.
49132
+ * Backtest memory adapter with pluggable storage backend.
47517
49133
  *
47518
49134
  * Features:
47519
- * - Memoized instances per (signalId, bucketName) pair
47520
- * - Swappable backend via useLocal(), usePersist(), useDummy()
47521
- * - Default backend: MemoryPersistInstance (in-memory BM25 + persist storage)
49135
+ * - Adapter pattern for swappable memory instance implementations
49136
+ * - Default backend: MemoryLocalInstance (in-memory BM25, no disk persistence)
49137
+ * - Alternative backends: MemoryPersistInstance, MemoryDummyInstance
49138
+ * - Convenience methods: useLocal(), usePersist(), useDummy(), useMemoryAdapter()
49139
+ * - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from MemoryAdapter
49140
+ *
49141
+ * Use this adapter for backtest memory storage.
47522
49142
  */
47523
- class MemoryAdapter {
49143
+ class MemoryBacktestAdapter {
49144
+ constructor() {
49145
+ this.MemoryFactory = MemoryLocalInstance;
49146
+ this.getInstance = memoize(([signalId, bucketName]) => CREATE_KEY_FN$4(signalId, bucketName), (signalId, bucketName) => Reflect.construct(this.MemoryFactory, [signalId, bucketName]));
49147
+ /**
49148
+ * Disposes all memoized instances for the given signalId.
49149
+ * Called by MemoryAdapter when a signal is cancelled or closed.
49150
+ * @param signalId - Signal identifier to dispose
49151
+ */
49152
+ this.disposeSignal = (signalId) => {
49153
+ const prefix = CREATE_KEY_FN$4(signalId, "");
49154
+ for (const key of this.getInstance.keys()) {
49155
+ if (key.startsWith(prefix)) {
49156
+ const instance = this.getInstance.get(key);
49157
+ instance && instance.dispose();
49158
+ this.getInstance.clear(key);
49159
+ }
49160
+ }
49161
+ };
49162
+ /**
49163
+ * Write a value to memory.
49164
+ * @param dto.memoryId - Unique entry identifier
49165
+ * @param dto.value - Value to store
49166
+ * @param dto.signalId - Signal identifier
49167
+ * @param dto.bucketName - Bucket name
49168
+ * @param dto.description - BM25 index string; defaults to JSON.stringify(value)
49169
+ */
49170
+ this.writeMemory = async (dto) => {
49171
+ backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_WRITE, {
49172
+ signalId: dto.signalId,
49173
+ bucketName: dto.bucketName,
49174
+ memoryId: dto.memoryId,
49175
+ });
49176
+ const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
49177
+ const isInitial = !this.getInstance.has(key);
49178
+ const instance = this.getInstance(dto.signalId, dto.bucketName);
49179
+ await instance.waitForInit(isInitial);
49180
+ return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
49181
+ };
49182
+ /**
49183
+ * Search memory using BM25 full-text scoring.
49184
+ * @param dto.query - Search query string
49185
+ * @param dto.signalId - Signal identifier
49186
+ * @param dto.bucketName - Bucket name
49187
+ * @returns Matching entries sorted by relevance score
49188
+ */
49189
+ this.searchMemory = async (dto) => {
49190
+ backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_SEARCH, {
49191
+ signalId: dto.signalId,
49192
+ bucketName: dto.bucketName,
49193
+ query: dto.query,
49194
+ });
49195
+ const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
49196
+ const isInitial = !this.getInstance.has(key);
49197
+ const instance = this.getInstance(dto.signalId, dto.bucketName);
49198
+ await instance.waitForInit(isInitial);
49199
+ return await instance.searchMemory(dto.query, dto.settings);
49200
+ };
49201
+ /**
49202
+ * List all entries in memory.
49203
+ * @param dto.signalId - Signal identifier
49204
+ * @param dto.bucketName - Bucket name
49205
+ * @returns Array of all stored entries
49206
+ */
49207
+ this.listMemory = async (dto) => {
49208
+ backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_LIST, {
49209
+ signalId: dto.signalId,
49210
+ bucketName: dto.bucketName,
49211
+ });
49212
+ const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
49213
+ const isInitial = !this.getInstance.has(key);
49214
+ const instance = this.getInstance(dto.signalId, dto.bucketName);
49215
+ await instance.waitForInit(isInitial);
49216
+ return await instance.listMemory();
49217
+ };
49218
+ /**
49219
+ * Remove an entry from memory.
49220
+ * @param dto.memoryId - Unique entry identifier
49221
+ * @param dto.signalId - Signal identifier
49222
+ * @param dto.bucketName - Bucket name
49223
+ */
49224
+ this.removeMemory = async (dto) => {
49225
+ backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_REMOVE, {
49226
+ signalId: dto.signalId,
49227
+ bucketName: dto.bucketName,
49228
+ memoryId: dto.memoryId,
49229
+ });
49230
+ const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
49231
+ const isInitial = !this.getInstance.has(key);
49232
+ const instance = this.getInstance(dto.signalId, dto.bucketName);
49233
+ await instance.waitForInit(isInitial);
49234
+ return await instance.removeMemory(dto.memoryId);
49235
+ };
49236
+ /**
49237
+ * Read a single entry from memory.
49238
+ * @param dto.memoryId - Unique entry identifier
49239
+ * @param dto.signalId - Signal identifier
49240
+ * @param dto.bucketName - Bucket name
49241
+ * @returns Entry value
49242
+ * @throws Error if entry not found
49243
+ */
49244
+ this.readMemory = async (dto) => {
49245
+ backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_READ, {
49246
+ signalId: dto.signalId,
49247
+ bucketName: dto.bucketName,
49248
+ memoryId: dto.memoryId,
49249
+ });
49250
+ const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
49251
+ const isInitial = !this.getInstance.has(key);
49252
+ const instance = this.getInstance(dto.signalId, dto.bucketName);
49253
+ await instance.waitForInit(isInitial);
49254
+ return await instance.readMemory(dto.memoryId);
49255
+ };
49256
+ /**
49257
+ * Switches to in-memory BM25 adapter (default).
49258
+ * All data lives in process memory only.
49259
+ */
49260
+ this.useLocal = () => {
49261
+ backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL);
49262
+ this.MemoryFactory = MemoryLocalInstance;
49263
+ };
49264
+ /**
49265
+ * Switches to file-system backed adapter.
49266
+ * Data is persisted to ./dump/memory/<signalId>/<bucketName>/.
49267
+ */
49268
+ this.usePersist = () => {
49269
+ backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
49270
+ this.MemoryFactory = MemoryPersistInstance;
49271
+ };
49272
+ /**
49273
+ * Switches to dummy adapter that discards all writes.
49274
+ */
49275
+ this.useDummy = () => {
49276
+ backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY);
49277
+ this.MemoryFactory = MemoryDummyInstance;
49278
+ };
49279
+ /**
49280
+ * Switches to a custom memory adapter implementation.
49281
+ * @param Ctor - Constructor for the custom memory instance
49282
+ */
49283
+ this.useMemoryAdapter = (Ctor) => {
49284
+ backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER);
49285
+ this.MemoryFactory = Ctor;
49286
+ };
49287
+ /**
49288
+ * Clears the memoized instance cache.
49289
+ * Call this when process.cwd() changes between strategy iterations
49290
+ * so new instances are created with the updated base path.
49291
+ */
49292
+ this.clear = () => {
49293
+ backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
49294
+ this.getInstance.clear();
49295
+ };
49296
+ }
49297
+ }
49298
+ /**
49299
+ * Live trading memory adapter with pluggable storage backend.
49300
+ *
49301
+ * Features:
49302
+ * - Adapter pattern for swappable memory instance implementations
49303
+ * - Default backend: MemoryPersistInstance (file-system backed, survives restarts)
49304
+ * - Alternative backends: MemoryLocalInstance, MemoryDummyInstance
49305
+ * - Convenience methods: useLocal(), usePersist(), useDummy(), useMemoryAdapter()
49306
+ * - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from MemoryAdapter
49307
+ *
49308
+ * Use this adapter for live trading memory storage.
49309
+ */
49310
+ class MemoryLiveAdapter {
47524
49311
  constructor() {
47525
49312
  this.MemoryFactory = MemoryPersistInstance;
47526
49313
  this.getInstance = memoize(([signalId, bucketName]) => CREATE_KEY_FN$4(signalId, bucketName), (signalId, bucketName) => Reflect.construct(this.MemoryFactory, [signalId, bucketName]));
47527
49314
  /**
47528
- * Activates the adapter by subscribing to signal lifecycle events.
47529
- * Clears memoized instances for a signalId when it is cancelled or closed,
47530
- * preventing stale instances from accumulating in memory.
47531
- * Idempotent — subsequent calls return the same subscription handle.
47532
- * Must be called before any memory method is used.
49315
+ * Disposes all memoized instances for the given signalId.
49316
+ * Called by MemoryAdapter when a signal is cancelled or closed.
49317
+ * @param signalId - Signal identifier to dispose
49318
+ */
49319
+ this.disposeSignal = (signalId) => {
49320
+ const prefix = CREATE_KEY_FN$4(signalId, "");
49321
+ for (const key of this.getInstance.keys()) {
49322
+ if (key.startsWith(prefix)) {
49323
+ const instance = this.getInstance.get(key);
49324
+ instance && instance.dispose();
49325
+ this.getInstance.clear(key);
49326
+ }
49327
+ }
49328
+ };
49329
+ /**
49330
+ * Write a value to memory.
49331
+ * @param dto.memoryId - Unique entry identifier
49332
+ * @param dto.value - Value to store
49333
+ * @param dto.signalId - Signal identifier
49334
+ * @param dto.bucketName - Bucket name
49335
+ * @param dto.description - BM25 index string; defaults to JSON.stringify(value)
49336
+ */
49337
+ this.writeMemory = async (dto) => {
49338
+ backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_WRITE, {
49339
+ signalId: dto.signalId,
49340
+ bucketName: dto.bucketName,
49341
+ memoryId: dto.memoryId,
49342
+ });
49343
+ const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
49344
+ const isInitial = !this.getInstance.has(key);
49345
+ const instance = this.getInstance(dto.signalId, dto.bucketName);
49346
+ await instance.waitForInit(isInitial);
49347
+ return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
49348
+ };
49349
+ /**
49350
+ * Search memory using BM25 full-text scoring.
49351
+ * @param dto.query - Search query string
49352
+ * @param dto.signalId - Signal identifier
49353
+ * @param dto.bucketName - Bucket name
49354
+ * @returns Matching entries sorted by relevance score
49355
+ */
49356
+ this.searchMemory = async (dto) => {
49357
+ backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_SEARCH, {
49358
+ signalId: dto.signalId,
49359
+ bucketName: dto.bucketName,
49360
+ query: dto.query,
49361
+ });
49362
+ const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
49363
+ const isInitial = !this.getInstance.has(key);
49364
+ const instance = this.getInstance(dto.signalId, dto.bucketName);
49365
+ await instance.waitForInit(isInitial);
49366
+ return await instance.searchMemory(dto.query, dto.settings);
49367
+ };
49368
+ /**
49369
+ * List all entries in memory.
49370
+ * @param dto.signalId - Signal identifier
49371
+ * @param dto.bucketName - Bucket name
49372
+ * @returns Array of all stored entries
49373
+ */
49374
+ this.listMemory = async (dto) => {
49375
+ backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_LIST, {
49376
+ signalId: dto.signalId,
49377
+ bucketName: dto.bucketName,
49378
+ });
49379
+ const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
49380
+ const isInitial = !this.getInstance.has(key);
49381
+ const instance = this.getInstance(dto.signalId, dto.bucketName);
49382
+ await instance.waitForInit(isInitial);
49383
+ return await instance.listMemory();
49384
+ };
49385
+ /**
49386
+ * Remove an entry from memory.
49387
+ * @param dto.memoryId - Unique entry identifier
49388
+ * @param dto.signalId - Signal identifier
49389
+ * @param dto.bucketName - Bucket name
49390
+ */
49391
+ this.removeMemory = async (dto) => {
49392
+ backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_REMOVE, {
49393
+ signalId: dto.signalId,
49394
+ bucketName: dto.bucketName,
49395
+ memoryId: dto.memoryId,
49396
+ });
49397
+ const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
49398
+ const isInitial = !this.getInstance.has(key);
49399
+ const instance = this.getInstance(dto.signalId, dto.bucketName);
49400
+ await instance.waitForInit(isInitial);
49401
+ return await instance.removeMemory(dto.memoryId);
49402
+ };
49403
+ /**
49404
+ * Read a single entry from memory.
49405
+ * @param dto.memoryId - Unique entry identifier
49406
+ * @param dto.signalId - Signal identifier
49407
+ * @param dto.bucketName - Bucket name
49408
+ * @returns Entry value
49409
+ * @throws Error if entry not found
49410
+ */
49411
+ this.readMemory = async (dto) => {
49412
+ backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_READ, {
49413
+ signalId: dto.signalId,
49414
+ bucketName: dto.bucketName,
49415
+ memoryId: dto.memoryId,
49416
+ });
49417
+ const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
49418
+ const isInitial = !this.getInstance.has(key);
49419
+ const instance = this.getInstance(dto.signalId, dto.bucketName);
49420
+ await instance.waitForInit(isInitial);
49421
+ return await instance.readMemory(dto.memoryId);
49422
+ };
49423
+ /**
49424
+ * Switches to in-memory BM25 adapter.
49425
+ * All data lives in process memory only.
49426
+ */
49427
+ this.useLocal = () => {
49428
+ backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL);
49429
+ this.MemoryFactory = MemoryLocalInstance;
49430
+ };
49431
+ /**
49432
+ * Switches to file-system backed adapter (default).
49433
+ * Data is persisted to ./dump/memory/<signalId>/<bucketName>/.
49434
+ */
49435
+ this.usePersist = () => {
49436
+ backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
49437
+ this.MemoryFactory = MemoryPersistInstance;
49438
+ };
49439
+ /**
49440
+ * Switches to dummy adapter that discards all writes.
49441
+ */
49442
+ this.useDummy = () => {
49443
+ backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY);
49444
+ this.MemoryFactory = MemoryDummyInstance;
49445
+ };
49446
+ /**
49447
+ * Switches to a custom memory adapter implementation.
49448
+ * @param Ctor - Constructor for the custom memory instance
49449
+ */
49450
+ this.useMemoryAdapter = (Ctor) => {
49451
+ backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER);
49452
+ this.MemoryFactory = Ctor;
49453
+ };
49454
+ /**
49455
+ * Clears the memoized instance cache.
49456
+ * Call this when process.cwd() changes between strategy iterations
49457
+ * so new instances are created with the updated base path.
49458
+ */
49459
+ this.clear = () => {
49460
+ backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_CLEAR);
49461
+ this.getInstance.clear();
49462
+ };
49463
+ }
49464
+ }
49465
+ /**
49466
+ * Main memory adapter that manages both backtest and live memory storage.
49467
+ *
49468
+ * Features:
49469
+ * - Subscribes to signal lifecycle events (cancelled/closed) to dispose stale instances
49470
+ * - Routes all operations to MemoryBacktest or MemoryLive based on dto.backtest
49471
+ * - Singleshot enable pattern prevents duplicate subscriptions
49472
+ * - Cleanup function for proper unsubscription
49473
+ */
49474
+ class MemoryAdapter {
49475
+ constructor() {
49476
+ /**
49477
+ * Enables memory storage by subscribing to signal lifecycle events.
49478
+ * Clears memoized instances in MemoryBacktest and MemoryLive when a signal
49479
+ * is cancelled or closed, preventing stale instances from accumulating.
49480
+ * Uses singleshot to ensure one-time subscription.
49481
+ *
49482
+ * @returns Cleanup function that unsubscribes from all emitters
47533
49483
  */
47534
49484
  this.enable = singleshot(() => {
47535
49485
  backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_ENABLE);
47536
- const handleDispose = (signalId) => {
47537
- const prefix = CREATE_KEY_FN$4(signalId, "");
47538
- for (const key of this.getInstance.keys()) {
47539
- if (key.startsWith(prefix)) {
47540
- const instance = this.getInstance.get(key);
47541
- instance && instance.dispose();
47542
- this.getInstance.clear(key);
47543
- }
47544
- }
47545
- };
47546
49486
  const unCancel = signalEmitter
47547
49487
  .filter(({ action }) => action === "cancelled")
47548
- .connect(({ signal }) => handleDispose(signal.id));
49488
+ .connect(({ signal }) => {
49489
+ MemoryBacktest.disposeSignal(signal.id);
49490
+ MemoryLive.disposeSignal(signal.id);
49491
+ });
47549
49492
  const unClose = signalEmitter
47550
49493
  .filter(({ action }) => action === "closed")
47551
- .connect(({ signal }) => handleDispose(signal.id));
47552
- return compose(() => unCancel(), () => unClose());
49494
+ .connect(({ signal }) => {
49495
+ MemoryBacktest.disposeSignal(signal.id);
49496
+ MemoryLive.disposeSignal(signal.id);
49497
+ });
49498
+ return compose(() => unCancel(), () => unClose(), () => this.enable.clear());
47553
49499
  });
47554
49500
  /**
47555
- * Deactivates the adapter by unsubscribing from signal lifecycle events.
47556
- * No-op if enable() was never called.
49501
+ * Disables memory storage by unsubscribing from signal lifecycle events.
49502
+ * Safe to call multiple times.
47557
49503
  */
47558
49504
  this.disable = () => {
47559
49505
  backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_DISABLE);
@@ -47564,11 +49510,13 @@ class MemoryAdapter {
47564
49510
  };
47565
49511
  /**
47566
49512
  * Write a value to memory.
49513
+ * Routes to MemoryBacktest or MemoryLive based on dto.backtest.
47567
49514
  * @param dto.memoryId - Unique entry identifier
47568
49515
  * @param dto.value - Value to store
47569
49516
  * @param dto.signalId - Signal identifier
47570
49517
  * @param dto.bucketName - Bucket name
47571
- * @param dto.description - Optional BM25 index string; defaults to JSON.stringify(value)
49518
+ * @param dto.description - BM25 index string; defaults to JSON.stringify(value)
49519
+ * @param dto.backtest - Flag indicating if the context is backtest or live
47572
49520
  */
47573
49521
  this.writeMemory = async (dto) => {
47574
49522
  if (!this.enable.hasValue()) {
@@ -47578,18 +49526,20 @@ class MemoryAdapter {
47578
49526
  signalId: dto.signalId,
47579
49527
  bucketName: dto.bucketName,
47580
49528
  memoryId: dto.memoryId,
49529
+ backtest: dto.backtest,
47581
49530
  });
47582
- const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
47583
- const isInitial = !this.getInstance.has(key);
47584
- const instance = this.getInstance(dto.signalId, dto.bucketName);
47585
- await instance.waitForInit(isInitial);
47586
- return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
49531
+ if (dto.backtest) {
49532
+ return await MemoryBacktest.writeMemory(dto);
49533
+ }
49534
+ return await MemoryLive.writeMemory(dto);
47587
49535
  };
47588
49536
  /**
47589
49537
  * Search memory using BM25 full-text scoring.
49538
+ * Routes to MemoryBacktest or MemoryLive based on dto.backtest.
47590
49539
  * @param dto.query - Search query string
47591
49540
  * @param dto.signalId - Signal identifier
47592
49541
  * @param dto.bucketName - Bucket name
49542
+ * @param dto.backtest - Flag indicating if the context is backtest or live
47593
49543
  * @returns Matching entries sorted by relevance score
47594
49544
  */
47595
49545
  this.searchMemory = async (dto) => {
@@ -47600,17 +49550,19 @@ class MemoryAdapter {
47600
49550
  signalId: dto.signalId,
47601
49551
  bucketName: dto.bucketName,
47602
49552
  query: dto.query,
49553
+ backtest: dto.backtest,
47603
49554
  });
47604
- const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
47605
- const isInitial = !this.getInstance.has(key);
47606
- const instance = this.getInstance(dto.signalId, dto.bucketName);
47607
- await instance.waitForInit(isInitial);
47608
- return await instance.searchMemory(dto.query, dto.settings);
49555
+ if (dto.backtest) {
49556
+ return await MemoryBacktest.searchMemory(dto);
49557
+ }
49558
+ return await MemoryLive.searchMemory(dto);
47609
49559
  };
47610
49560
  /**
47611
49561
  * List all entries in memory.
49562
+ * Routes to MemoryBacktest or MemoryLive based on dto.backtest.
47612
49563
  * @param dto.signalId - Signal identifier
47613
49564
  * @param dto.bucketName - Bucket name
49565
+ * @param dto.backtest - Flag indicating if the context is backtest or live
47614
49566
  * @returns Array of all stored entries
47615
49567
  */
47616
49568
  this.listMemory = async (dto) => {
@@ -47620,18 +49572,20 @@ class MemoryAdapter {
47620
49572
  backtest.loggerService.debug(MEMORY_ADAPTER_METHOD_NAME_LIST, {
47621
49573
  signalId: dto.signalId,
47622
49574
  bucketName: dto.bucketName,
49575
+ backtest: dto.backtest,
47623
49576
  });
47624
- const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
47625
- const isInitial = !this.getInstance.has(key);
47626
- const instance = this.getInstance(dto.signalId, dto.bucketName);
47627
- await instance.waitForInit(isInitial);
47628
- return await instance.listMemory();
49577
+ if (dto.backtest) {
49578
+ return await MemoryBacktest.listMemory(dto);
49579
+ }
49580
+ return await MemoryLive.listMemory(dto);
47629
49581
  };
47630
49582
  /**
47631
49583
  * Remove an entry from memory.
49584
+ * Routes to MemoryBacktest or MemoryLive based on dto.backtest.
47632
49585
  * @param dto.memoryId - Unique entry identifier
47633
49586
  * @param dto.signalId - Signal identifier
47634
49587
  * @param dto.bucketName - Bucket name
49588
+ * @param dto.backtest - Flag indicating if the context is backtest or live
47635
49589
  */
47636
49590
  this.removeMemory = async (dto) => {
47637
49591
  if (!this.enable.hasValue()) {
@@ -47641,18 +49595,20 @@ class MemoryAdapter {
47641
49595
  signalId: dto.signalId,
47642
49596
  bucketName: dto.bucketName,
47643
49597
  memoryId: dto.memoryId,
49598
+ backtest: dto.backtest,
47644
49599
  });
47645
- const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
47646
- const isInitial = !this.getInstance.has(key);
47647
- const instance = this.getInstance(dto.signalId, dto.bucketName);
47648
- await instance.waitForInit(isInitial);
47649
- return await instance.removeMemory(dto.memoryId);
49600
+ if (dto.backtest) {
49601
+ return await MemoryBacktest.removeMemory(dto);
49602
+ }
49603
+ return await MemoryLive.removeMemory(dto);
47650
49604
  };
47651
49605
  /**
47652
49606
  * Read a single entry from memory.
49607
+ * Routes to MemoryBacktest or MemoryLive based on dto.backtest.
47653
49608
  * @param dto.memoryId - Unique entry identifier
47654
49609
  * @param dto.signalId - Signal identifier
47655
49610
  * @param dto.bucketName - Bucket name
49611
+ * @param dto.backtest - Flag indicating if the context is backtest or live
47656
49612
  * @returns Entry value
47657
49613
  * @throws Error if entry not found
47658
49614
  */
@@ -47664,56 +49620,30 @@ class MemoryAdapter {
47664
49620
  signalId: dto.signalId,
47665
49621
  bucketName: dto.bucketName,
47666
49622
  memoryId: dto.memoryId,
49623
+ backtest: dto.backtest,
47667
49624
  });
47668
- const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
47669
- const isInitial = !this.getInstance.has(key);
47670
- const instance = this.getInstance(dto.signalId, dto.bucketName);
47671
- await instance.waitForInit(isInitial);
47672
- return await instance.readMemory(dto.memoryId);
47673
- };
47674
- /**
47675
- * Switches to in-memory BM25 adapter (default).
47676
- * All data lives in process memory only.
47677
- */
47678
- this.useLocal = () => {
47679
- backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_USE_LOCAL);
47680
- this.MemoryFactory = MemoryLocalInstance;
47681
- };
47682
- /**
47683
- * Switches to file-system backed adapter.
47684
- * Data is persisted to ./dump/memory/<signalId>/<bucketName>/.
47685
- */
47686
- this.usePersist = () => {
47687
- backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_USE_PERSIST);
47688
- this.MemoryFactory = MemoryPersistInstance;
47689
- };
47690
- /**
47691
- * Switches to dummy adapter that discards all writes.
47692
- */
47693
- this.useDummy = () => {
47694
- backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_USE_DUMMY);
47695
- this.MemoryFactory = MemoryDummyInstance;
47696
- };
47697
- /**
47698
- * Clears the memoized instance cache.
47699
- * Call this when process.cwd() changes between strategy iterations
47700
- * so new instances are created with the updated base path.
47701
- */
47702
- this.clear = () => {
47703
- backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_CLEAR);
47704
- this.getInstance.clear();
47705
- };
47706
- /**
47707
- * Releases resources held by this adapter.
47708
- * Delegates to disable() to unsubscribe from signal lifecycle events.
47709
- */
47710
- this.dispose = () => {
47711
- backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_DISPOSE);
47712
- this.disable();
49625
+ if (dto.backtest) {
49626
+ return await MemoryBacktest.readMemory(dto);
49627
+ }
49628
+ return await MemoryLive.readMemory(dto);
47713
49629
  };
47714
49630
  }
47715
49631
  }
49632
+ /**
49633
+ * Global singleton instance of MemoryAdapter.
49634
+ * Provides unified memory management for backtest and live trading.
49635
+ */
47716
49636
  const Memory = new MemoryAdapter();
49637
+ /**
49638
+ * Global singleton instance of MemoryLiveAdapter.
49639
+ * Provides live trading memory storage with pluggable backends.
49640
+ */
49641
+ const MemoryLive = new MemoryLiveAdapter();
49642
+ /**
49643
+ * Global singleton instance of MemoryBacktestAdapter.
49644
+ * Provides backtest memory storage with pluggable backends.
49645
+ */
49646
+ const MemoryBacktest = new MemoryBacktestAdapter();
47717
49647
 
47718
49648
  const WRITE_MEMORY_METHOD_NAME = "memory.writeMemory";
47719
49649
  const READ_MEMORY_METHOD_NAME = "memory.readMemory";
@@ -47723,23 +49653,20 @@ const REMOVE_MEMORY_METHOD_NAME = "memory.removeMemory";
47723
49653
  /**
47724
49654
  * Writes a value to memory scoped to the current signal.
47725
49655
  *
47726
- * Reads symbol from execution context and signalId from the active pending signal.
47727
- * If no pending signal exists, logs a warning and returns without writing.
47728
- *
49656
+ * Resolves the active pending or scheduled signal automatically from execution context.
47729
49657
  * Automatically detects backtest/live mode from execution context.
47730
49658
  *
47731
49659
  * @param dto.bucketName - Memory bucket name
47732
49660
  * @param dto.memoryId - Unique memory entry identifier
47733
49661
  * @param dto.value - Value to store
49662
+ * @param dto.description - BM25 index string for contextual search
47734
49663
  * @returns Promise that resolves when write is complete
47735
49664
  *
47736
- * @deprecated Better use Memory.writeMemory with manual signalId argument
47737
- *
47738
49665
  * @example
47739
49666
  * ```typescript
47740
49667
  * import { writeMemory } from "backtest-kit";
47741
49668
  *
47742
- * await writeMemory({ bucketName: "my-strategy", memoryId: "context", value: { trend: "up", confidence: 0.9 } });
49669
+ * await writeMemory({ bucketName: "my-strategy", memoryId: "context", value: { trend: "up", confidence: 0.9 }, description: "Signal context at entry" });
47743
49670
  * ```
47744
49671
  */
47745
49672
  async function writeMemory(dto) {
@@ -47757,33 +49684,41 @@ async function writeMemory(dto) {
47757
49684
  const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
47758
49685
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
47759
49686
  const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
47760
- const signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
47761
- if (!signal) {
47762
- console.warn(`backtest-kit writeMemory no pending signal for symbol=${symbol} memoryId=${memoryId}`);
49687
+ let signal;
49688
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
49689
+ await Memory.writeMemory({
49690
+ memoryId,
49691
+ value,
49692
+ signalId: signal.id,
49693
+ bucketName,
49694
+ description,
49695
+ backtest: isBacktest,
49696
+ });
47763
49697
  return;
47764
49698
  }
47765
- await Memory.writeMemory({
47766
- memoryId,
47767
- value,
47768
- signalId: signal.id,
47769
- bucketName,
47770
- description,
47771
- });
49699
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
49700
+ await Memory.writeMemory({
49701
+ memoryId,
49702
+ value,
49703
+ signalId: signal.id,
49704
+ bucketName,
49705
+ description,
49706
+ backtest: isBacktest,
49707
+ });
49708
+ return;
49709
+ }
49710
+ throw new Error(`writeMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
47772
49711
  }
47773
49712
  /**
47774
49713
  * Reads a value from memory scoped to the current signal.
47775
49714
  *
47776
- * Reads symbol from execution context and signalId from the active pending signal.
47777
- * If no pending signal exists, logs a warning and returns null.
47778
- *
49715
+ * Resolves the active pending or scheduled signal automatically from execution context.
47779
49716
  * Automatically detects backtest/live mode from execution context.
47780
49717
  *
47781
49718
  * @param dto.bucketName - Memory bucket name
47782
49719
  * @param dto.memoryId - Unique memory entry identifier
47783
- * @returns Promise resolving to stored value or null if no signal
47784
- * @throws Error if entry not found within an active signal
47785
- *
47786
- * @deprecated Better use Memory.readMemory with manual signalId argument
49720
+ * @returns Promise resolving to stored value
49721
+ * @throws Error if no pending or scheduled signal exists, or if entry not found
47787
49722
  *
47788
49723
  * @example
47789
49724
  * ```typescript
@@ -47807,30 +49742,35 @@ async function readMemory(dto) {
47807
49742
  const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
47808
49743
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
47809
49744
  const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
47810
- const signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
47811
- if (!signal) {
47812
- console.warn(`backtest-kit readMemory no pending signal for symbol=${symbol} memoryId=${memoryId}`);
47813
- return null;
49745
+ let signal;
49746
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
49747
+ return await Memory.readMemory({
49748
+ memoryId,
49749
+ signalId: signal.id,
49750
+ bucketName,
49751
+ backtest: isBacktest,
49752
+ });
47814
49753
  }
47815
- return await Memory.readMemory({
47816
- memoryId,
47817
- signalId: signal.id,
47818
- bucketName,
47819
- });
49754
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
49755
+ return await Memory.readMemory({
49756
+ memoryId,
49757
+ signalId: signal.id,
49758
+ bucketName,
49759
+ backtest: isBacktest,
49760
+ });
49761
+ }
49762
+ throw new Error(`readMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
47820
49763
  }
47821
49764
  /**
47822
49765
  * Searches memory entries for the current signal using BM25 full-text scoring.
47823
49766
  *
47824
- * Reads symbol from execution context and signalId from the active pending signal.
47825
- * If no pending signal exists, logs a warning and returns an empty array.
47826
- *
49767
+ * Resolves the active pending or scheduled signal automatically from execution context.
47827
49768
  * Automatically detects backtest/live mode from execution context.
47828
49769
  *
47829
49770
  * @param dto.bucketName - Memory bucket name
47830
49771
  * @param dto.query - Search query string
47831
- * @returns Promise resolving to matching entries sorted by relevance, or empty array if no signal
47832
- *
47833
- * @deprecated Better use Memory.searchMemory with manual signalId argument
49772
+ * @returns Promise resolving to matching entries sorted by relevance
49773
+ * @throws Error if no pending or scheduled signal exists
47834
49774
  *
47835
49775
  * @example
47836
49776
  * ```typescript
@@ -47854,29 +49794,34 @@ async function searchMemory(dto) {
47854
49794
  const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
47855
49795
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
47856
49796
  const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
47857
- const signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
47858
- if (!signal) {
47859
- console.warn(`backtest-kit searchMemory no pending signal for symbol=${symbol} query=${query}`);
47860
- return [];
49797
+ let signal;
49798
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
49799
+ return await Memory.searchMemory({
49800
+ query,
49801
+ signalId: signal.id,
49802
+ bucketName,
49803
+ backtest: isBacktest,
49804
+ });
47861
49805
  }
47862
- return await Memory.searchMemory({
47863
- query,
47864
- signalId: signal.id,
47865
- bucketName,
47866
- });
49806
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
49807
+ return await Memory.searchMemory({
49808
+ query,
49809
+ signalId: signal.id,
49810
+ bucketName,
49811
+ backtest: isBacktest,
49812
+ });
49813
+ }
49814
+ throw new Error(`searchMemory requires a pending or scheduled signal for symbol=${symbol} query=${query}`);
47867
49815
  }
47868
49816
  /**
47869
49817
  * Lists all memory entries for the current signal.
47870
49818
  *
47871
- * Reads symbol from execution context and signalId from the active pending signal.
47872
- * If no pending signal exists, logs a warning and returns an empty array.
47873
- *
49819
+ * Resolves the active pending or scheduled signal automatically from execution context.
47874
49820
  * Automatically detects backtest/live mode from execution context.
47875
49821
  *
47876
49822
  * @param dto.bucketName - Memory bucket name
47877
- * @returns Promise resolving to all stored entries, or empty array if no signal
47878
- *
47879
- * @deprecated Better use Memory.listMemory with manual signalId argument
49823
+ * @returns Promise resolving to all stored entries
49824
+ * @throws Error if no pending or scheduled signal exists
47880
49825
  *
47881
49826
  * @example
47882
49827
  * ```typescript
@@ -47899,29 +49844,33 @@ async function listMemory(dto) {
47899
49844
  const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
47900
49845
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
47901
49846
  const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
47902
- const signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
47903
- if (!signal) {
47904
- console.warn(`backtest-kit listMemory no pending signal for symbol=${symbol}`);
47905
- return [];
49847
+ let signal;
49848
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
49849
+ return await Memory.listMemory({
49850
+ signalId: signal.id,
49851
+ bucketName,
49852
+ backtest: isBacktest,
49853
+ });
47906
49854
  }
47907
- return await Memory.listMemory({
47908
- signalId: signal.id,
47909
- bucketName,
47910
- });
49855
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
49856
+ return await Memory.listMemory({
49857
+ signalId: signal.id,
49858
+ bucketName,
49859
+ backtest: isBacktest,
49860
+ });
49861
+ }
49862
+ throw new Error(`listMemory requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
47911
49863
  }
47912
49864
  /**
47913
49865
  * Removes a memory entry for the current signal.
47914
49866
  *
47915
- * Reads symbol from execution context and signalId from the active pending signal.
47916
- * If no pending signal exists, logs a warning and returns without removing.
47917
- *
49867
+ * Resolves the active pending or scheduled signal automatically from execution context.
47918
49868
  * Automatically detects backtest/live mode from execution context.
47919
49869
  *
47920
49870
  * @param dto.bucketName - Memory bucket name
47921
49871
  * @param dto.memoryId - Unique memory entry identifier
47922
49872
  * @returns Promise that resolves when removal is complete
47923
- *
47924
- * @deprecated Better use Memory.removeMemory with manual signalId argument
49873
+ * @throws Error if no pending or scheduled signal exists
47925
49874
  *
47926
49875
  * @example
47927
49876
  * ```typescript
@@ -47945,16 +49894,26 @@ async function removeMemory(dto) {
47945
49894
  const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
47946
49895
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
47947
49896
  const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
47948
- const signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
47949
- if (!signal) {
47950
- console.warn(`backtest-kit removeMemory no pending signal for symbol=${symbol} memoryId=${memoryId}`);
49897
+ let signal;
49898
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
49899
+ await Memory.removeMemory({
49900
+ memoryId,
49901
+ signalId: signal.id,
49902
+ bucketName,
49903
+ backtest: isBacktest,
49904
+ });
47951
49905
  return;
47952
49906
  }
47953
- await Memory.removeMemory({
47954
- memoryId,
47955
- signalId: signal.id,
47956
- bucketName,
47957
- });
49907
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
49908
+ await Memory.removeMemory({
49909
+ memoryId,
49910
+ signalId: signal.id,
49911
+ bucketName,
49912
+ backtest: isBacktest,
49913
+ });
49914
+ return;
49915
+ }
49916
+ throw new Error(`removeMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
47958
49917
  }
47959
49918
 
47960
49919
  const CREATE_KEY_FN$3 = (signalId, bucketName) => `${signalId}-${bucketName}`;
@@ -48049,11 +50008,12 @@ const RENDER_TABLE_FN = (rows) => {
48049
50008
  * Scoped to (signalId, bucketName) via constructor.
48050
50009
  */
48051
50010
  class DumpBothInstance {
48052
- constructor(signalId, bucketName) {
50011
+ constructor(signalId, bucketName, backtest) {
48053
50012
  this.signalId = signalId;
48054
50013
  this.bucketName = bucketName;
48055
- this._memory = new DumpMemoryInstance(signalId, bucketName);
48056
- this._markdown = new DumpMarkdownInstance(signalId, bucketName);
50014
+ this.backtest = backtest;
50015
+ this._memory = new DumpMemoryInstance(signalId, bucketName, backtest);
50016
+ this._markdown = new DumpMarkdownInstance(signalId, bucketName, backtest);
48057
50017
  }
48058
50018
  /** Releases resources held by both backends. */
48059
50019
  dispose() {
@@ -48185,9 +50145,10 @@ class DumpBothInstance {
48185
50145
  * Useful for downstream LLM retrieval via Memory.searchMemory.
48186
50146
  */
48187
50147
  class DumpMemoryInstance {
48188
- constructor(signalId, bucketName) {
50148
+ constructor(signalId, bucketName, backtest) {
48189
50149
  this.signalId = signalId;
48190
50150
  this.bucketName = bucketName;
50151
+ this.backtest = backtest;
48191
50152
  }
48192
50153
  /**
48193
50154
  * Stores the full agent message history in Memory as a `{ messages }` object.
@@ -48213,6 +50174,7 @@ class DumpMemoryInstance {
48213
50174
  signalId: this.signalId,
48214
50175
  value: { messages },
48215
50176
  description,
50177
+ backtest: this.backtest,
48216
50178
  });
48217
50179
  }
48218
50180
  /**
@@ -48234,6 +50196,7 @@ class DumpMemoryInstance {
48234
50196
  signalId: this.signalId,
48235
50197
  value: record,
48236
50198
  description,
50199
+ backtest: this.backtest,
48237
50200
  });
48238
50201
  }
48239
50202
  /**
@@ -48256,6 +50219,7 @@ class DumpMemoryInstance {
48256
50219
  signalId: this.signalId,
48257
50220
  value: { rows },
48258
50221
  description,
50222
+ backtest: this.backtest,
48259
50223
  });
48260
50224
  }
48261
50225
  /**
@@ -48277,6 +50241,7 @@ class DumpMemoryInstance {
48277
50241
  signalId: this.signalId,
48278
50242
  value: { content },
48279
50243
  description,
50244
+ backtest: this.backtest,
48280
50245
  });
48281
50246
  }
48282
50247
  /**
@@ -48298,6 +50263,7 @@ class DumpMemoryInstance {
48298
50263
  signalId: this.signalId,
48299
50264
  value: { content },
48300
50265
  description,
50266
+ backtest: this.backtest,
48301
50267
  });
48302
50268
  }
48303
50269
  /**
@@ -48320,6 +50286,7 @@ class DumpMemoryInstance {
48320
50286
  signalId: this.signalId,
48321
50287
  value: json,
48322
50288
  description,
50289
+ backtest: this.backtest,
48323
50290
  });
48324
50291
  }
48325
50292
  /** Releases resources held by this instance. */
@@ -48341,9 +50308,10 @@ class DumpMemoryInstance {
48341
50308
  * If the file already exists, the call is skipped (idempotent).
48342
50309
  */
48343
50310
  class DumpMarkdownInstance {
48344
- constructor(signalId, bucketName) {
50311
+ constructor(signalId, bucketName, backtest) {
48345
50312
  this.signalId = signalId;
48346
50313
  this.bucketName = bucketName;
50314
+ this.backtest = backtest;
48347
50315
  }
48348
50316
  getFilePath(dumpId) {
48349
50317
  return join("./dump/agent", this.signalId, this.bucketName, `${dumpId}.md`);
@@ -48532,9 +50500,10 @@ class DumpMarkdownInstance {
48532
50500
  * Used for disabling dumps in tests or dry-run scenarios.
48533
50501
  */
48534
50502
  class DumpDummyInstance {
48535
- constructor(signalId, bucketName) {
50503
+ constructor(signalId, bucketName, backtest) {
48536
50504
  this.signalId = signalId;
48537
50505
  this.bucketName = bucketName;
50506
+ this.backtest = backtest;
48538
50507
  }
48539
50508
  /** No-op. */
48540
50509
  async dumpAgentAnswer() {
@@ -48577,7 +50546,7 @@ class DumpDummyInstance {
48577
50546
  class DumpAdapter {
48578
50547
  constructor() {
48579
50548
  this.DumpFactory = DumpMarkdownInstance;
48580
- this.getInstance = memoize(([signalId, bucketName]) => CREATE_KEY_FN$3(signalId, bucketName), (signalId, bucketName) => Reflect.construct(this.DumpFactory, [signalId, bucketName]));
50549
+ this.getInstance = memoize(([signalId, bucketName]) => CREATE_KEY_FN$3(signalId, bucketName), (signalId, bucketName, backtest) => Reflect.construct(this.DumpFactory, [signalId, bucketName, backtest]));
48581
50550
  /**
48582
50551
  * Activates the adapter by subscribing to signal lifecycle events.
48583
50552
  * Clears memoized instances for a signalId when it is cancelled or closed,
@@ -48628,7 +50597,7 @@ class DumpAdapter {
48628
50597
  bucketName: context.bucketName,
48629
50598
  dumpId: context.dumpId,
48630
50599
  });
48631
- const instance = this.getInstance(context.signalId, context.bucketName);
50600
+ const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
48632
50601
  return await instance.dumpAgentAnswer(messages, context.dumpId, context.description);
48633
50602
  };
48634
50603
  /**
@@ -48643,7 +50612,7 @@ class DumpAdapter {
48643
50612
  bucketName: context.bucketName,
48644
50613
  dumpId: context.dumpId,
48645
50614
  });
48646
- const instance = this.getInstance(context.signalId, context.bucketName);
50615
+ const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
48647
50616
  return await instance.dumpRecord(record, context.dumpId, context.description);
48648
50617
  };
48649
50618
  /**
@@ -48658,7 +50627,7 @@ class DumpAdapter {
48658
50627
  bucketName: context.bucketName,
48659
50628
  dumpId: context.dumpId,
48660
50629
  });
48661
- const instance = this.getInstance(context.signalId, context.bucketName);
50630
+ const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
48662
50631
  return await instance.dumpTable(rows, context.dumpId, context.description);
48663
50632
  };
48664
50633
  /**
@@ -48673,7 +50642,7 @@ class DumpAdapter {
48673
50642
  bucketName: context.bucketName,
48674
50643
  dumpId: context.dumpId,
48675
50644
  });
48676
- const instance = this.getInstance(context.signalId, context.bucketName);
50645
+ const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
48677
50646
  return await instance.dumpText(content, context.dumpId, context.description);
48678
50647
  };
48679
50648
  /**
@@ -48688,7 +50657,7 @@ class DumpAdapter {
48688
50657
  bucketName: context.bucketName,
48689
50658
  dumpId: context.dumpId,
48690
50659
  });
48691
- const instance = this.getInstance(context.signalId, context.bucketName);
50660
+ const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
48692
50661
  return await instance.dumpError(content, context.dumpId, context.description);
48693
50662
  };
48694
50663
  /**
@@ -48704,7 +50673,7 @@ class DumpAdapter {
48704
50673
  bucketName: context.bucketName,
48705
50674
  dumpId: context.dumpId,
48706
50675
  });
48707
- const instance = this.getInstance(context.signalId, context.bucketName);
50676
+ const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
48708
50677
  return await instance.dumpJson(json, context.dumpId, context.description);
48709
50678
  };
48710
50679
  /**
@@ -48770,16 +50739,15 @@ const DUMP_JSON_METHOD_NAME = "dump.dumpJson";
48770
50739
  /**
48771
50740
  * Dumps the full agent message history scoped to the current signal.
48772
50741
  *
48773
- * Reads signalId from the active pending signal via execution and method context.
48774
- * If no pending signal exists, logs a warning and returns without writing.
50742
+ * Resolves the active pending or scheduled signal automatically from execution context.
50743
+ * Automatically detects backtest/live mode from execution context.
48775
50744
  *
48776
50745
  * @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
48777
50746
  * @param dto.dumpId - Unique identifier for this agent invocation
48778
50747
  * @param dto.messages - Full chat history (system, user, assistant, tool)
48779
50748
  * @param dto.description - Human-readable label describing the agent invocation context; included in the BM25 index for Memory search
48780
50749
  * @returns Promise that resolves when the dump is complete
48781
- *
48782
- * @deprecated Better use Dump.dumpAgentAnswer with manual signalId argument
50750
+ * @throws Error if no pending or scheduled signal exists
48783
50751
  *
48784
50752
  * @example
48785
50753
  * ```typescript
@@ -48804,31 +50772,41 @@ async function dumpAgentAnswer(dto) {
48804
50772
  const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
48805
50773
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
48806
50774
  const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
48807
- const signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
48808
- if (!signal) {
48809
- console.warn(`backtest-kit dumpAgentAnswer no pending signal for symbol=${symbol} dumpId=${dumpId}`);
50775
+ let signal;
50776
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
50777
+ await Dump.dumpAgentAnswer(messages, {
50778
+ dumpId,
50779
+ bucketName,
50780
+ signalId: signal.id,
50781
+ description,
50782
+ backtest: isBacktest,
50783
+ });
48810
50784
  return;
48811
50785
  }
48812
- await Dump.dumpAgentAnswer(messages, {
48813
- dumpId,
48814
- bucketName,
48815
- signalId: signal.id,
48816
- description,
48817
- });
50786
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
50787
+ await Dump.dumpAgentAnswer(messages, {
50788
+ dumpId,
50789
+ bucketName,
50790
+ signalId: signal.id,
50791
+ description,
50792
+ backtest: isBacktest,
50793
+ });
50794
+ return;
50795
+ }
50796
+ throw new Error(`dumpAgentAnswer requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
48818
50797
  }
48819
50798
  /**
48820
50799
  * Dumps a flat key-value record scoped to the current signal.
48821
50800
  *
48822
- * Reads signalId from the active pending signal via execution and method context.
48823
- * If no pending signal exists, logs a warning and returns without writing.
50801
+ * Resolves the active pending or scheduled signal automatically from execution context.
50802
+ * Automatically detects backtest/live mode from execution context.
48824
50803
  *
48825
50804
  * @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
48826
50805
  * @param dto.dumpId - Unique identifier for this dump entry
48827
50806
  * @param dto.record - Arbitrary flat object to persist
48828
50807
  * @param dto.description - Human-readable label describing the record contents; included in the BM25 index for Memory search
48829
50808
  * @returns Promise that resolves when the dump is complete
48830
- *
48831
- * @deprecated Better use Dump.dumpRecord with manual signalId argument
50809
+ * @throws Error if no pending or scheduled signal exists
48832
50810
  *
48833
50811
  * @example
48834
50812
  * ```typescript
@@ -48852,23 +50830,34 @@ async function dumpRecord(dto) {
48852
50830
  const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
48853
50831
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
48854
50832
  const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
48855
- const signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
48856
- if (!signal) {
48857
- console.warn(`backtest-kit dumpRecord no pending signal for symbol=${symbol} dumpId=${dumpId}`);
50833
+ let signal;
50834
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
50835
+ await Dump.dumpRecord(record, {
50836
+ dumpId,
50837
+ bucketName,
50838
+ signalId: signal.id,
50839
+ description,
50840
+ backtest: isBacktest,
50841
+ });
48858
50842
  return;
48859
50843
  }
48860
- await Dump.dumpRecord(record, {
48861
- dumpId,
48862
- bucketName,
48863
- signalId: signal.id,
48864
- description,
48865
- });
50844
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
50845
+ await Dump.dumpRecord(record, {
50846
+ dumpId,
50847
+ bucketName,
50848
+ signalId: signal.id,
50849
+ description,
50850
+ backtest: isBacktest,
50851
+ });
50852
+ return;
50853
+ }
50854
+ throw new Error(`dumpRecord requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
48866
50855
  }
48867
50856
  /**
48868
50857
  * Dumps an array of objects as a table scoped to the current signal.
48869
50858
  *
48870
- * Reads signalId from the active pending signal via execution and method context.
48871
- * If no pending signal exists, logs a warning and returns without writing.
50859
+ * Resolves the active pending or scheduled signal automatically from execution context.
50860
+ * Automatically detects backtest/live mode from execution context.
48872
50861
  *
48873
50862
  * Column headers are derived from the union of all keys across all rows.
48874
50863
  *
@@ -48877,8 +50866,7 @@ async function dumpRecord(dto) {
48877
50866
  * @param dto.rows - Array of arbitrary objects to render as a table
48878
50867
  * @param dto.description - Human-readable label describing the table contents; included in the BM25 index for Memory search
48879
50868
  * @returns Promise that resolves when the dump is complete
48880
- *
48881
- * @deprecated Better use Dump.dumpTable with manual signalId argument
50869
+ * @throws Error if no pending or scheduled signal exists
48882
50870
  *
48883
50871
  * @example
48884
50872
  * ```typescript
@@ -48903,31 +50891,41 @@ async function dumpTable(dto) {
48903
50891
  const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
48904
50892
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
48905
50893
  const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
48906
- const signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
48907
- if (!signal) {
48908
- console.warn(`backtest-kit dumpTable no pending signal for symbol=${symbol} dumpId=${dumpId}`);
50894
+ let signal;
50895
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
50896
+ await Dump.dumpTable(rows, {
50897
+ dumpId,
50898
+ bucketName,
50899
+ signalId: signal.id,
50900
+ description,
50901
+ backtest: isBacktest,
50902
+ });
48909
50903
  return;
48910
50904
  }
48911
- await Dump.dumpTable(rows, {
48912
- dumpId,
48913
- bucketName,
48914
- signalId: signal.id,
48915
- description,
48916
- });
50905
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
50906
+ await Dump.dumpTable(rows, {
50907
+ dumpId,
50908
+ bucketName,
50909
+ signalId: signal.id,
50910
+ description,
50911
+ backtest: isBacktest,
50912
+ });
50913
+ return;
50914
+ }
50915
+ throw new Error(`dumpTable requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
48917
50916
  }
48918
50917
  /**
48919
50918
  * Dumps raw text content scoped to the current signal.
48920
50919
  *
48921
- * Reads signalId from the active pending signal via execution and method context.
48922
- * If no pending signal exists, logs a warning and returns without writing.
50920
+ * Resolves the active pending or scheduled signal automatically from execution context.
50921
+ * Automatically detects backtest/live mode from execution context.
48923
50922
  *
48924
50923
  * @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
48925
50924
  * @param dto.dumpId - Unique identifier for this dump entry
48926
50925
  * @param dto.content - Arbitrary text content to persist
48927
50926
  * @param dto.description - Human-readable label describing the content; included in the BM25 index for Memory search
48928
50927
  * @returns Promise that resolves when the dump is complete
48929
- *
48930
- * @deprecated Better use Dump.dumpText with manual signalId argument
50928
+ * @throws Error if no pending or scheduled signal exists
48931
50929
  *
48932
50930
  * @example
48933
50931
  * ```typescript
@@ -48951,31 +50949,41 @@ async function dumpText(dto) {
48951
50949
  const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
48952
50950
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
48953
50951
  const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
48954
- const signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
48955
- if (!signal) {
48956
- console.warn(`backtest-kit dumpText no pending signal for symbol=${symbol} dumpId=${dumpId}`);
50952
+ let signal;
50953
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
50954
+ await Dump.dumpText(content, {
50955
+ dumpId,
50956
+ bucketName,
50957
+ signalId: signal.id,
50958
+ description,
50959
+ backtest: isBacktest,
50960
+ });
48957
50961
  return;
48958
50962
  }
48959
- await Dump.dumpText(content, {
48960
- dumpId,
48961
- bucketName,
48962
- signalId: signal.id,
48963
- description,
48964
- });
50963
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
50964
+ await Dump.dumpText(content, {
50965
+ dumpId,
50966
+ bucketName,
50967
+ signalId: signal.id,
50968
+ description,
50969
+ backtest: isBacktest,
50970
+ });
50971
+ return;
50972
+ }
50973
+ throw new Error(`dumpText requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
48965
50974
  }
48966
50975
  /**
48967
50976
  * Dumps an error description scoped to the current signal.
48968
50977
  *
48969
- * Reads signalId from the active pending signal via execution and method context.
48970
- * If no pending signal exists, logs a warning and returns without writing.
50978
+ * Resolves the active pending or scheduled signal automatically from execution context.
50979
+ * Automatically detects backtest/live mode from execution context.
48971
50980
  *
48972
50981
  * @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
48973
50982
  * @param dto.dumpId - Unique identifier for this dump entry
48974
50983
  * @param dto.content - Error message or description to persist
48975
50984
  * @param dto.description - Human-readable label describing the error context; included in the BM25 index for Memory search
48976
50985
  * @returns Promise that resolves when the dump is complete
48977
- *
48978
- * @deprecated Better use Dump.dumpError with manual signalId argument
50986
+ * @throws Error if no pending or scheduled signal exists
48979
50987
  *
48980
50988
  * @example
48981
50989
  * ```typescript
@@ -48999,29 +51007,41 @@ async function dumpError(dto) {
48999
51007
  const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
49000
51008
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
49001
51009
  const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
49002
- const signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
49003
- if (!signal) {
49004
- console.warn(`backtest-kit dumpError no pending signal for symbol=${symbol} dumpId=${dumpId}`);
51010
+ let signal;
51011
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
51012
+ await Dump.dumpError(content, {
51013
+ dumpId,
51014
+ bucketName,
51015
+ signalId: signal.id,
51016
+ description,
51017
+ backtest: isBacktest,
51018
+ });
49005
51019
  return;
49006
51020
  }
49007
- await Dump.dumpError(content, {
49008
- dumpId,
49009
- bucketName,
49010
- signalId: signal.id,
49011
- description,
49012
- });
51021
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
51022
+ await Dump.dumpError(content, {
51023
+ dumpId,
51024
+ bucketName,
51025
+ signalId: signal.id,
51026
+ description,
51027
+ backtest: isBacktest,
51028
+ });
51029
+ return;
51030
+ }
51031
+ throw new Error(`dumpError requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
49013
51032
  }
49014
51033
  /**
49015
51034
  * Dumps an arbitrary nested object as a fenced JSON block scoped to the current signal.
49016
51035
  *
49017
- * Reads signalId from the active pending signal via execution and method context.
49018
- * If no pending signal exists, logs a warning and returns without writing.
51036
+ * Resolves the active pending or scheduled signal automatically from execution context.
51037
+ * Automatically detects backtest/live mode from execution context.
49019
51038
  *
49020
51039
  * @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
49021
51040
  * @param dto.dumpId - Unique identifier for this dump entry
49022
51041
  * @param dto.json - Arbitrary nested object to serialize with JSON.stringify
49023
51042
  * @param dto.description - Human-readable label describing the object contents; included in the BM25 index for Memory search
49024
51043
  * @returns Promise that resolves when the dump is complete
51044
+ * @throws Error if no pending or scheduled signal exists
49025
51045
  *
49026
51046
  * @deprecated Prefer dumpRecord — flat key-value structure maps naturally to markdown tables and SQL storage
49027
51047
  *
@@ -49047,17 +51067,28 @@ async function dumpJson(dto) {
49047
51067
  const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
49048
51068
  const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
49049
51069
  const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
49050
- const signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName });
49051
- if (!signal) {
49052
- console.warn(`backtest-kit dumpJson no pending signal for symbol=${symbol} dumpId=${dumpId}`);
51070
+ let signal;
51071
+ if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
51072
+ await Dump.dumpJson(json, {
51073
+ dumpId,
51074
+ bucketName,
51075
+ signalId: signal.id,
51076
+ description,
51077
+ backtest: isBacktest,
51078
+ });
49053
51079
  return;
49054
51080
  }
49055
- await Dump.dumpJson(json, {
49056
- dumpId,
49057
- bucketName,
49058
- signalId: signal.id,
49059
- description,
49060
- });
51081
+ if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
51082
+ await Dump.dumpJson(json, {
51083
+ dumpId,
51084
+ bucketName,
51085
+ signalId: signal.id,
51086
+ description,
51087
+ backtest: isBacktest,
51088
+ });
51089
+ return;
51090
+ }
51091
+ throw new Error(`dumpJson requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
49061
51092
  }
49062
51093
 
49063
51094
  /**
@@ -50438,7 +52469,7 @@ class LogAdapter {
50438
52469
  */
50439
52470
  const Log = new LogAdapter();
50440
52471
 
50441
- const METHOD_NAME_CREATE_SNAPSHOT = "SessionUtils.createSnapshot";
52472
+ const METHOD_NAME_CREATE_SNAPSHOT = "SystemUtils.createSnapshot";
50442
52473
  /** List of all global subjects whose listeners should be snapshotted for session isolation */
50443
52474
  const SUBJECT_ISOLATION_LIST = [
50444
52475
  activePingSubject,
@@ -50485,7 +52516,7 @@ const CREATE_SUBJECT_SNAPSHOT_FN = (subject) => {
50485
52516
  * Allows temporarily detaching all subject subscriptions so that one session
50486
52517
  * does not interfere with another, then restoring them afterwards.
50487
52518
  */
50488
- class SessionUtils {
52519
+ class SystemUtils {
50489
52520
  constructor() {
50490
52521
  /**
50491
52522
  * Snapshots the current listener state of every global subject by replacing
@@ -50499,7 +52530,7 @@ class SessionUtils {
50499
52530
  };
50500
52531
  }
50501
52532
  }
50502
- const Session = new SessionUtils();
52533
+ const System = new SystemUtils();
50503
52534
 
50504
52535
  const SCHEDULE_METHOD_NAME_GET_DATA = "ScheduleUtils.getData";
50505
52536
  const SCHEDULE_METHOD_NAME_GET_REPORT = "ScheduleUtils.getReport";
@@ -59524,4 +61555,4 @@ const validateSignal = (signal, currentPrice) => {
59524
61555
  return !errors.length;
59525
61556
  };
59526
61557
 
59527
- export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistIntervalAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistMemoryAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRecentAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PersistStorageAdapter, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, Storage, StorageBacktest, StorageLive, Strategy, Sync, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitSignalNotify, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMinutesSinceLatestSignalCreated, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionActiveMinutes, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getPositionWaitingMinutes, getRawCandles, getRiskSchema, getScheduledSignal, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenIdlePing, listenIdlePingOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalNotify, listenSignalNotifyOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, warmCandles, writeMemory };
61558
+ export { ActionBase, Backtest, Breakeven, Broker, BrokerBase, Cache, Constant, Dump, Exchange, ExecutionContextService, Heat, HighestProfit, Interval, Live, Log, Markdown, MarkdownFileBase, MarkdownFolderBase, MarkdownWriter, MaxDrawdown, Memory, MemoryBacktest, MemoryBacktestAdapter, MemoryLive, MemoryLiveAdapter, MethodContextService, Notification, NotificationBacktest, NotificationLive, Partial, Performance, PersistBase, PersistBreakevenAdapter, PersistCandleAdapter, PersistIntervalAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistMemoryAdapter, PersistNotificationAdapter, PersistPartialAdapter, PersistRecentAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSessionAdapter, PersistSignalAdapter, PersistStateAdapter, PersistStorageAdapter, Position, PositionSize, Recent, RecentBacktest, RecentLive, Reflect$1 as Reflect, Report, ReportBase, ReportWriter, Risk, Schedule, Session, SessionBacktest, SessionLive, State, StateBacktest, StateBacktestAdapter, StateLive, StateLiveAdapter, Storage, StorageBacktest, StorageLive, Strategy, Sync, System, Walker, addActionSchema, addExchangeSchema, addFrameSchema, addRiskSchema, addSizingSchema, addStrategySchema, addWalkerSchema, alignToInterval, checkCandles, commitActivateScheduled, commitAverageBuy, commitBreakeven, commitCancelScheduled, commitClosePending, commitPartialLoss, commitPartialLossCost, commitPartialProfit, commitPartialProfitCost, commitSignalNotify, commitTrailingStop, commitTrailingStopCost, commitTrailingTake, commitTrailingTakeCost, createSignalState, dumpAgentAnswer, dumpError, dumpJson, dumpRecord, dumpTable, dumpText, emitters, formatPrice, formatQuantity, get, getActionSchema, getAggregatedTrades, getAveragePrice, getBacktestTimeframe, getBreakeven, getCandles, getColumns, getConfig, getContext, getDate, getDefaultColumns, getDefaultConfig, getEffectivePriceOpen, getExchangeSchema, getFrameSchema, getLatestSignal, getMaxDrawdownDistancePnlCost, getMaxDrawdownDistancePnlPercentage, getMinutesSinceLatestSignalCreated, getMode, getNextCandles, getOrderBook, getPendingSignal, getPositionActiveMinutes, getPositionCountdownMinutes, getPositionDrawdownMinutes, getPositionEffectivePrice, getPositionEntries, getPositionEntryOverlap, getPositionEstimateMinutes, getPositionHighestMaxDrawdownPnlCost, getPositionHighestMaxDrawdownPnlPercentage, getPositionHighestPnlCost, getPositionHighestPnlPercentage, getPositionHighestProfitBreakeven, getPositionHighestProfitDistancePnlCost, getPositionHighestProfitDistancePnlPercentage, getPositionHighestProfitMinutes, getPositionHighestProfitPrice, getPositionHighestProfitTimestamp, getPositionInvestedCost, getPositionInvestedCount, getPositionLevels, getPositionMaxDrawdownMinutes, getPositionMaxDrawdownPnlCost, getPositionMaxDrawdownPnlPercentage, getPositionMaxDrawdownPrice, getPositionMaxDrawdownTimestamp, getPositionPartialOverlap, getPositionPartials, getPositionPnlCost, getPositionPnlPercent, getPositionWaitingMinutes, getRawCandles, getRiskSchema, getScheduledSignal, getSessionData, getSignalState, getSizingSchema, getStrategySchema, getSymbol, getTimestamp, getTotalClosed, getTotalCostClosed, getTotalPercentClosed, getWalkerSchema, hasNoPendingSignal, hasNoScheduledSignal, hasTradeContext, investedCostToPercent, backtest as lib, listExchangeSchema, listFrameSchema, listMemory, listRiskSchema, listSizingSchema, listStrategySchema, listWalkerSchema, listenActivePing, listenActivePingOnce, listenBacktestProgress, listenBreakevenAvailable, listenBreakevenAvailableOnce, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenHighestProfit, listenHighestProfitOnce, listenIdlePing, listenIdlePingOnce, listenMaxDrawdown, listenMaxDrawdownOnce, listenPartialLossAvailable, listenPartialLossAvailableOnce, listenPartialProfitAvailable, listenPartialProfitAvailableOnce, listenPerformance, listenRisk, listenRiskOnce, listenSchedulePing, listenSchedulePingOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalNotify, listenSignalNotifyOnce, listenSignalOnce, listenStrategyCommit, listenStrategyCommitOnce, listenSync, listenSyncOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, overrideActionSchema, overrideExchangeSchema, overrideFrameSchema, overrideRiskSchema, overrideSizingSchema, overrideStrategySchema, overrideWalkerSchema, parseArgs, percentDiff, percentToCloseCost, percentValue, readMemory, removeMemory, roundTicks, runInMockContext, searchMemory, set, setColumns, setConfig, setLogger, setSessionData, setSignalState, shutdown, slPercentShiftToPrice, slPriceToPercentShift, stopStrategy, toProfitLossDto, tpPercentShiftToPrice, tpPriceToPercentShift, validate, validateCommonSignal, validatePendingSignal, validateScheduledSignal, validateSignal, waitForCandle, warmCandles, writeMemory };