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/README.md +7 -7
- package/build/index.cjs +2398 -348
- package/build/index.mjs +2380 -349
- package/package.json +1 -1
- package/types.d.ts +1207 -107
package/build/index.cjs
CHANGED
|
@@ -1017,6 +1017,22 @@ const PERSIST_MEMORY_UTILS_METHOD_NAME_LIST_DATA = "PersistMemoryUtils.listMemor
|
|
|
1017
1017
|
const PERSIST_MEMORY_UTILS_METHOD_NAME_HAS_DATA = "PersistMemoryUtils.hasMemoryData";
|
|
1018
1018
|
const PERSIST_MEMORY_UTILS_METHOD_NAME_CLEAR = "PersistMemoryUtils.clear";
|
|
1019
1019
|
const PERSIST_MEMORY_UTILS_METHOD_NAME_DISPOSE = "PersistMemoryUtils.dispose";
|
|
1020
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_USE_PERSIST_STATE_ADAPTER = "PersistStateUtils.usePersistStateAdapter";
|
|
1021
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_READ_DATA = "PersistStateUtils.readStateData";
|
|
1022
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_WRITE_DATA = "PersistStateUtils.writeStateData";
|
|
1023
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_CLEAR = "PersistStateUtils.clear";
|
|
1024
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_DISPOSE = "PersistStateUtils.dispose";
|
|
1025
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_WAIT_FOR_INIT = "PersistStateUtils.waitForInit";
|
|
1026
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_USE_DUMMY = "PersistStateUtils.useDummy";
|
|
1027
|
+
const PERSIST_STATE_UTILS_METHOD_NAME_USE_JSON = "PersistStateUtils.useJson";
|
|
1028
|
+
const PERSIST_SESSION_UTILS_METHOD_NAME_USE_PERSIST_SESSION_ADAPTER = "PersistSessionUtils.usePersistSessionAdapter";
|
|
1029
|
+
const PERSIST_SESSION_UTILS_METHOD_NAME_READ_DATA = "PersistSessionUtils.readSessionData";
|
|
1030
|
+
const PERSIST_SESSION_UTILS_METHOD_NAME_WRITE_DATA = "PersistSessionUtils.writeSessionData";
|
|
1031
|
+
const PERSIST_SESSION_UTILS_METHOD_NAME_CLEAR = "PersistSessionUtils.clear";
|
|
1032
|
+
const PERSIST_SESSION_UTILS_METHOD_NAME_DISPOSE = "PersistSessionUtils.dispose";
|
|
1033
|
+
const PERSIST_SESSION_UTILS_METHOD_NAME_WAIT_FOR_INIT = "PersistSessionUtils.waitForInit";
|
|
1034
|
+
const PERSIST_SESSION_UTILS_METHOD_NAME_USE_DUMMY = "PersistSessionUtils.useDummy";
|
|
1035
|
+
const PERSIST_SESSION_UTILS_METHOD_NAME_USE_JSON = "PersistSessionUtils.useJson";
|
|
1020
1036
|
const PERSIST_RECENT_UTILS_METHOD_NAME_USE_PERSIST_RECENT_ADAPTER = "PersistRecentUtils.usePersistRecentAdapter";
|
|
1021
1037
|
const PERSIST_RECENT_UTILS_METHOD_NAME_READ_DATA = "PersistRecentUtils.readRecentData";
|
|
1022
1038
|
const PERSIST_RECENT_UTILS_METHOD_NAME_WRITE_DATA = "PersistRecentUtils.writeRecentData";
|
|
@@ -2978,6 +2994,271 @@ class PersistRecentUtils {
|
|
|
2978
2994
|
* Used by RecentPersistBacktestUtils/RecentPersistLiveUtils for recent signal persistence.
|
|
2979
2995
|
*/
|
|
2980
2996
|
const PersistRecentAdapter = new PersistRecentUtils();
|
|
2997
|
+
/**
|
|
2998
|
+
* Utility class for managing state persistence.
|
|
2999
|
+
*
|
|
3000
|
+
* Features:
|
|
3001
|
+
* - Memoized storage instances per (signalId, bucketName) pair
|
|
3002
|
+
* - Custom adapter support
|
|
3003
|
+
* - Atomic read/write operations
|
|
3004
|
+
*
|
|
3005
|
+
* Storage layout: ./dump/state/<signalId>/<bucketName>.json
|
|
3006
|
+
*
|
|
3007
|
+
* Used by StatePersistInstance for crash-safe state persistence.
|
|
3008
|
+
*/
|
|
3009
|
+
class PersistStateUtils {
|
|
3010
|
+
constructor() {
|
|
3011
|
+
this.PersistStateFactory = PersistBase;
|
|
3012
|
+
this.getStateStorage = functoolsKit.memoize(([signalId, bucketName]) => `${signalId}:${bucketName}`, (signalId, bucketName) => Reflect.construct(this.PersistStateFactory, [
|
|
3013
|
+
bucketName,
|
|
3014
|
+
`./dump/state/${signalId}/`,
|
|
3015
|
+
]));
|
|
3016
|
+
/**
|
|
3017
|
+
* Initializes the storage for a given (signalId, bucketName) pair.
|
|
3018
|
+
*
|
|
3019
|
+
* @param signalId - Signal identifier
|
|
3020
|
+
* @param bucketName - Bucket name
|
|
3021
|
+
* @param initial - Whether this is the first initialization
|
|
3022
|
+
*/
|
|
3023
|
+
this.waitForInit = async (signalId, bucketName, initial) => {
|
|
3024
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_WAIT_FOR_INIT, {
|
|
3025
|
+
signalId,
|
|
3026
|
+
bucketName,
|
|
3027
|
+
initial,
|
|
3028
|
+
});
|
|
3029
|
+
const key = `${signalId}:${bucketName}`;
|
|
3030
|
+
const isInitial = initial && !this.getStateStorage.has(key);
|
|
3031
|
+
const stateStorage = this.getStateStorage(signalId, bucketName);
|
|
3032
|
+
await stateStorage.waitForInit(isInitial);
|
|
3033
|
+
};
|
|
3034
|
+
/**
|
|
3035
|
+
* Reads a state entry from persistence storage.
|
|
3036
|
+
*
|
|
3037
|
+
* @param signalId - Signal identifier
|
|
3038
|
+
* @param bucketName - Bucket name
|
|
3039
|
+
* @returns Promise resolving to entry data or null if not found
|
|
3040
|
+
*/
|
|
3041
|
+
this.readStateData = async (signalId, bucketName) => {
|
|
3042
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_READ_DATA, {
|
|
3043
|
+
signalId,
|
|
3044
|
+
bucketName,
|
|
3045
|
+
});
|
|
3046
|
+
const key = `${signalId}:${bucketName}`;
|
|
3047
|
+
const isInitial = !this.getStateStorage.has(key);
|
|
3048
|
+
const stateStorage = this.getStateStorage(signalId, bucketName);
|
|
3049
|
+
await stateStorage.waitForInit(isInitial);
|
|
3050
|
+
if (await stateStorage.hasValue(bucketName)) {
|
|
3051
|
+
return await stateStorage.readValue(bucketName);
|
|
3052
|
+
}
|
|
3053
|
+
return null;
|
|
3054
|
+
};
|
|
3055
|
+
/**
|
|
3056
|
+
* Writes a state entry to disk with atomic file writes.
|
|
3057
|
+
*
|
|
3058
|
+
* @param data - Entry data to persist
|
|
3059
|
+
* @param signalId - Signal identifier
|
|
3060
|
+
* @param bucketName - Bucket name
|
|
3061
|
+
*/
|
|
3062
|
+
this.writeStateData = async (data, signalId, bucketName) => {
|
|
3063
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_WRITE_DATA, {
|
|
3064
|
+
signalId,
|
|
3065
|
+
bucketName,
|
|
3066
|
+
});
|
|
3067
|
+
const key = `${signalId}:${bucketName}`;
|
|
3068
|
+
const isInitial = !this.getStateStorage.has(key);
|
|
3069
|
+
const stateStorage = this.getStateStorage(signalId, bucketName);
|
|
3070
|
+
await stateStorage.waitForInit(isInitial);
|
|
3071
|
+
await stateStorage.writeValue(bucketName, data);
|
|
3072
|
+
};
|
|
3073
|
+
/**
|
|
3074
|
+
* Switches to a dummy persist adapter that discards all writes.
|
|
3075
|
+
* All future persistence writes will be no-ops.
|
|
3076
|
+
*/
|
|
3077
|
+
this.useDummy = () => {
|
|
3078
|
+
LOGGER_SERVICE$7.log(PERSIST_STATE_UTILS_METHOD_NAME_USE_DUMMY);
|
|
3079
|
+
this.usePersistStateAdapter(PersistDummy);
|
|
3080
|
+
};
|
|
3081
|
+
/**
|
|
3082
|
+
* Switches to the default JSON persist adapter.
|
|
3083
|
+
* All future persistence writes will use JSON storage.
|
|
3084
|
+
*/
|
|
3085
|
+
this.useJson = () => {
|
|
3086
|
+
LOGGER_SERVICE$7.log(PERSIST_STATE_UTILS_METHOD_NAME_USE_JSON);
|
|
3087
|
+
this.usePersistStateAdapter(PersistBase);
|
|
3088
|
+
};
|
|
3089
|
+
/**
|
|
3090
|
+
* Clears the memoized storage cache.
|
|
3091
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
3092
|
+
* so new storage instances are created with the updated base path.
|
|
3093
|
+
*/
|
|
3094
|
+
this.clear = () => {
|
|
3095
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_CLEAR);
|
|
3096
|
+
this.getStateStorage.clear();
|
|
3097
|
+
};
|
|
3098
|
+
/**
|
|
3099
|
+
* Disposes of the state adapter and releases any resources.
|
|
3100
|
+
* Call this when a signal is removed to clean up its associated storage.
|
|
3101
|
+
*
|
|
3102
|
+
* @param signalId - Signal identifier
|
|
3103
|
+
* @param bucketName - Bucket name
|
|
3104
|
+
*/
|
|
3105
|
+
this.dispose = (signalId, bucketName) => {
|
|
3106
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_DISPOSE);
|
|
3107
|
+
const key = `${signalId}:${bucketName}`;
|
|
3108
|
+
this.getStateStorage.clear(key);
|
|
3109
|
+
};
|
|
3110
|
+
}
|
|
3111
|
+
/**
|
|
3112
|
+
* Registers a custom persistence adapter.
|
|
3113
|
+
*
|
|
3114
|
+
* @param Ctor - Custom PersistBase constructor
|
|
3115
|
+
*/
|
|
3116
|
+
usePersistStateAdapter(Ctor) {
|
|
3117
|
+
LOGGER_SERVICE$7.info(PERSIST_STATE_UTILS_METHOD_NAME_USE_PERSIST_STATE_ADAPTER);
|
|
3118
|
+
this.PersistStateFactory = Ctor;
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
/**
|
|
3122
|
+
* Global singleton instance of PersistStateUtils.
|
|
3123
|
+
* Used by StatePersistInstance for crash-safe state persistence.
|
|
3124
|
+
*/
|
|
3125
|
+
const PersistStateAdapter = new PersistStateUtils();
|
|
3126
|
+
/**
|
|
3127
|
+
* Utility class for managing session persistence.
|
|
3128
|
+
*
|
|
3129
|
+
* Features:
|
|
3130
|
+
* - Memoized storage instances per (strategyName, exchangeName, frameName) key
|
|
3131
|
+
* - Custom adapter support
|
|
3132
|
+
* - Atomic read/write operations
|
|
3133
|
+
*
|
|
3134
|
+
* Storage layout: ./dump/session/<strategyName>/<exchangeName>/<frameName>.json
|
|
3135
|
+
*
|
|
3136
|
+
* Used by SessionPersistInstance for crash-safe session persistence.
|
|
3137
|
+
*/
|
|
3138
|
+
class PersistSessionUtils {
|
|
3139
|
+
constructor() {
|
|
3140
|
+
this.PersistSessionFactory = PersistBase;
|
|
3141
|
+
this.getSessionStorage = functoolsKit.memoize(([strategyName, exchangeName, frameName]) => `${strategyName}:${exchangeName}:${frameName}`, (strategyName, exchangeName, frameName) => Reflect.construct(this.PersistSessionFactory, [
|
|
3142
|
+
frameName,
|
|
3143
|
+
`./dump/session/${strategyName}/${exchangeName}/`,
|
|
3144
|
+
]));
|
|
3145
|
+
/**
|
|
3146
|
+
* Initializes the storage for a given (strategyName, exchangeName, frameName) triple.
|
|
3147
|
+
*
|
|
3148
|
+
* @param strategyName - Strategy identifier
|
|
3149
|
+
* @param exchangeName - Exchange identifier
|
|
3150
|
+
* @param frameName - Frame identifier
|
|
3151
|
+
* @param initial - Whether this is the first initialization
|
|
3152
|
+
*/
|
|
3153
|
+
this.waitForInit = async (strategyName, exchangeName, frameName, initial) => {
|
|
3154
|
+
LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_WAIT_FOR_INIT, {
|
|
3155
|
+
strategyName,
|
|
3156
|
+
exchangeName,
|
|
3157
|
+
frameName,
|
|
3158
|
+
initial,
|
|
3159
|
+
});
|
|
3160
|
+
const key = `${strategyName}:${exchangeName}:${frameName}`;
|
|
3161
|
+
const isInitial = initial && !this.getSessionStorage.has(key);
|
|
3162
|
+
const sessionStorage = this.getSessionStorage(strategyName, exchangeName, frameName);
|
|
3163
|
+
await sessionStorage.waitForInit(isInitial);
|
|
3164
|
+
};
|
|
3165
|
+
/**
|
|
3166
|
+
* Reads a session entry from persistence storage.
|
|
3167
|
+
*
|
|
3168
|
+
* @param strategyName - Strategy identifier
|
|
3169
|
+
* @param exchangeName - Exchange identifier
|
|
3170
|
+
* @param frameName - Frame identifier
|
|
3171
|
+
* @returns Promise resolving to entry data or null if not found
|
|
3172
|
+
*/
|
|
3173
|
+
this.readSessionData = async (strategyName, exchangeName, frameName) => {
|
|
3174
|
+
LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_READ_DATA, {
|
|
3175
|
+
strategyName,
|
|
3176
|
+
exchangeName,
|
|
3177
|
+
frameName,
|
|
3178
|
+
});
|
|
3179
|
+
const key = `${strategyName}:${exchangeName}:${frameName}`;
|
|
3180
|
+
const isInitial = !this.getSessionStorage.has(key);
|
|
3181
|
+
const sessionStorage = this.getSessionStorage(strategyName, exchangeName, frameName);
|
|
3182
|
+
await sessionStorage.waitForInit(isInitial);
|
|
3183
|
+
if (await sessionStorage.hasValue(frameName)) {
|
|
3184
|
+
return await sessionStorage.readValue(frameName);
|
|
3185
|
+
}
|
|
3186
|
+
return null;
|
|
3187
|
+
};
|
|
3188
|
+
/**
|
|
3189
|
+
* Writes a session entry to disk with atomic file writes.
|
|
3190
|
+
*
|
|
3191
|
+
* @param data - Entry data to persist
|
|
3192
|
+
* @param strategyName - Strategy identifier
|
|
3193
|
+
* @param exchangeName - Exchange identifier
|
|
3194
|
+
* @param frameName - Frame identifier
|
|
3195
|
+
*/
|
|
3196
|
+
this.writeSessionData = async (data, strategyName, exchangeName, frameName) => {
|
|
3197
|
+
LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_WRITE_DATA, {
|
|
3198
|
+
strategyName,
|
|
3199
|
+
exchangeName,
|
|
3200
|
+
frameName,
|
|
3201
|
+
});
|
|
3202
|
+
const key = `${strategyName}:${exchangeName}:${frameName}`;
|
|
3203
|
+
const isInitial = !this.getSessionStorage.has(key);
|
|
3204
|
+
const sessionStorage = this.getSessionStorage(strategyName, exchangeName, frameName);
|
|
3205
|
+
await sessionStorage.waitForInit(isInitial);
|
|
3206
|
+
await sessionStorage.writeValue(frameName, data);
|
|
3207
|
+
};
|
|
3208
|
+
/**
|
|
3209
|
+
* Switches to a dummy persist adapter that discards all writes.
|
|
3210
|
+
* All future persistence writes will be no-ops.
|
|
3211
|
+
*/
|
|
3212
|
+
this.useDummy = () => {
|
|
3213
|
+
LOGGER_SERVICE$7.log(PERSIST_SESSION_UTILS_METHOD_NAME_USE_DUMMY);
|
|
3214
|
+
this.usePersistSessionAdapter(PersistDummy);
|
|
3215
|
+
};
|
|
3216
|
+
/**
|
|
3217
|
+
* Switches to the default JSON persist adapter.
|
|
3218
|
+
* All future persistence writes will use JSON storage.
|
|
3219
|
+
*/
|
|
3220
|
+
this.useJson = () => {
|
|
3221
|
+
LOGGER_SERVICE$7.log(PERSIST_SESSION_UTILS_METHOD_NAME_USE_JSON);
|
|
3222
|
+
this.usePersistSessionAdapter(PersistBase);
|
|
3223
|
+
};
|
|
3224
|
+
/**
|
|
3225
|
+
* Clears the memoized storage cache.
|
|
3226
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
3227
|
+
* so new storage instances are created with the updated base path.
|
|
3228
|
+
*/
|
|
3229
|
+
this.clear = () => {
|
|
3230
|
+
LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_CLEAR);
|
|
3231
|
+
this.getSessionStorage.clear();
|
|
3232
|
+
};
|
|
3233
|
+
/**
|
|
3234
|
+
* Disposes of the session adapter and releases any resources.
|
|
3235
|
+
* Call this when a session is removed to clean up its associated storage.
|
|
3236
|
+
*
|
|
3237
|
+
* @param strategyName - Strategy identifier
|
|
3238
|
+
* @param exchangeName - Exchange identifier
|
|
3239
|
+
* @param frameName - Frame identifier
|
|
3240
|
+
*/
|
|
3241
|
+
this.dispose = (strategyName, exchangeName, frameName) => {
|
|
3242
|
+
LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_DISPOSE);
|
|
3243
|
+
const key = `${strategyName}:${exchangeName}:${frameName}`;
|
|
3244
|
+
this.getSessionStorage.clear(key);
|
|
3245
|
+
};
|
|
3246
|
+
}
|
|
3247
|
+
/**
|
|
3248
|
+
* Registers a custom persistence adapter.
|
|
3249
|
+
*
|
|
3250
|
+
* @param Ctor - Custom PersistBase constructor
|
|
3251
|
+
*/
|
|
3252
|
+
usePersistSessionAdapter(Ctor) {
|
|
3253
|
+
LOGGER_SERVICE$7.info(PERSIST_SESSION_UTILS_METHOD_NAME_USE_PERSIST_SESSION_ADAPTER);
|
|
3254
|
+
this.PersistSessionFactory = Ctor;
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
/**
|
|
3258
|
+
* Global singleton instance of PersistSessionUtils.
|
|
3259
|
+
* Used by SessionPersistInstance for crash-safe session persistence.
|
|
3260
|
+
*/
|
|
3261
|
+
const PersistSessionAdapter = new PersistSessionUtils();
|
|
2981
3262
|
|
|
2982
3263
|
var _a$2, _b$2;
|
|
2983
3264
|
const BUSY_DELAY = 100;
|
|
@@ -10443,7 +10724,7 @@ const GET_RISK_FN = (dto, backtest, exchangeName, frameName, self) => {
|
|
|
10443
10724
|
* @param backtest - Whether running in backtest mode
|
|
10444
10725
|
* @returns Unique string key for memoization
|
|
10445
10726
|
*/
|
|
10446
|
-
const CREATE_KEY_FN$
|
|
10727
|
+
const CREATE_KEY_FN$x = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
10447
10728
|
const parts = [symbol, strategyName, exchangeName];
|
|
10448
10729
|
if (frameName)
|
|
10449
10730
|
parts.push(frameName);
|
|
@@ -10743,7 +11024,7 @@ class StrategyConnectionService {
|
|
|
10743
11024
|
* @param backtest - Whether running in backtest mode
|
|
10744
11025
|
* @returns Configured ClientStrategy instance
|
|
10745
11026
|
*/
|
|
10746
|
-
this.getStrategy = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
11027
|
+
this.getStrategy = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$x(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
10747
11028
|
const { riskName = "", riskList = [], getSignal, interval = STRATEGY_DEFAULT_INTERVAL, callbacks, } = this.strategySchemaService.get(strategyName);
|
|
10748
11029
|
return new ClientStrategy({
|
|
10749
11030
|
symbol,
|
|
@@ -11705,7 +11986,7 @@ class StrategyConnectionService {
|
|
|
11705
11986
|
}
|
|
11706
11987
|
return;
|
|
11707
11988
|
}
|
|
11708
|
-
const key = CREATE_KEY_FN$
|
|
11989
|
+
const key = CREATE_KEY_FN$x(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
11709
11990
|
if (!this.getStrategy.has(key)) {
|
|
11710
11991
|
return;
|
|
11711
11992
|
}
|
|
@@ -12879,7 +13160,7 @@ class ClientRisk {
|
|
|
12879
13160
|
* @param backtest - Whether running in backtest mode
|
|
12880
13161
|
* @returns Unique string key for memoization
|
|
12881
13162
|
*/
|
|
12882
|
-
const CREATE_KEY_FN$
|
|
13163
|
+
const CREATE_KEY_FN$w = (riskName, exchangeName, frameName, backtest) => {
|
|
12883
13164
|
const parts = [riskName, exchangeName];
|
|
12884
13165
|
if (frameName)
|
|
12885
13166
|
parts.push(frameName);
|
|
@@ -12979,7 +13260,7 @@ class RiskConnectionService {
|
|
|
12979
13260
|
* @param backtest - True if backtest mode, false if live mode
|
|
12980
13261
|
* @returns Configured ClientRisk instance
|
|
12981
13262
|
*/
|
|
12982
|
-
this.getRisk = functoolsKit.memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
13263
|
+
this.getRisk = functoolsKit.memoize(([riskName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$w(riskName, exchangeName, frameName, backtest), (riskName, exchangeName, frameName, backtest) => {
|
|
12983
13264
|
const schema = this.riskSchemaService.get(riskName);
|
|
12984
13265
|
return new ClientRisk({
|
|
12985
13266
|
...schema,
|
|
@@ -13048,7 +13329,7 @@ class RiskConnectionService {
|
|
|
13048
13329
|
payload,
|
|
13049
13330
|
});
|
|
13050
13331
|
if (payload) {
|
|
13051
|
-
const key = CREATE_KEY_FN$
|
|
13332
|
+
const key = CREATE_KEY_FN$w(payload.riskName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
13052
13333
|
this.getRisk.clear(key);
|
|
13053
13334
|
}
|
|
13054
13335
|
else {
|
|
@@ -14167,7 +14448,7 @@ class ClientAction {
|
|
|
14167
14448
|
* @param backtest - Whether running in backtest mode
|
|
14168
14449
|
* @returns Unique string key for memoization
|
|
14169
14450
|
*/
|
|
14170
|
-
const CREATE_KEY_FN$
|
|
14451
|
+
const CREATE_KEY_FN$v = (actionName, strategyName, exchangeName, frameName, backtest) => {
|
|
14171
14452
|
const parts = [actionName, strategyName, exchangeName];
|
|
14172
14453
|
if (frameName)
|
|
14173
14454
|
parts.push(frameName);
|
|
@@ -14219,7 +14500,7 @@ class ActionConnectionService {
|
|
|
14219
14500
|
* @param backtest - True if backtest mode, false if live mode
|
|
14220
14501
|
* @returns Configured ClientAction instance
|
|
14221
14502
|
*/
|
|
14222
|
-
this.getAction = functoolsKit.memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
14503
|
+
this.getAction = functoolsKit.memoize(([actionName, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$v(actionName, strategyName, exchangeName, frameName, backtest), (actionName, strategyName, exchangeName, frameName, backtest) => {
|
|
14223
14504
|
const schema = this.actionSchemaService.get(actionName);
|
|
14224
14505
|
return new ClientAction({
|
|
14225
14506
|
...schema,
|
|
@@ -14445,7 +14726,7 @@ class ActionConnectionService {
|
|
|
14445
14726
|
await Promise.all(actions.map(async (action) => await action.dispose()));
|
|
14446
14727
|
return;
|
|
14447
14728
|
}
|
|
14448
|
-
const key = CREATE_KEY_FN$
|
|
14729
|
+
const key = CREATE_KEY_FN$v(payload.actionName, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
14449
14730
|
if (!this.getAction.has(key)) {
|
|
14450
14731
|
return;
|
|
14451
14732
|
}
|
|
@@ -14463,7 +14744,7 @@ const METHOD_NAME_VALIDATE$3 = "exchangeCoreService validate";
|
|
|
14463
14744
|
* @param exchangeName - Exchange name
|
|
14464
14745
|
* @returns Unique string key for memoization
|
|
14465
14746
|
*/
|
|
14466
|
-
const CREATE_KEY_FN$
|
|
14747
|
+
const CREATE_KEY_FN$u = (exchangeName) => {
|
|
14467
14748
|
return exchangeName;
|
|
14468
14749
|
};
|
|
14469
14750
|
/**
|
|
@@ -14487,7 +14768,7 @@ class ExchangeCoreService {
|
|
|
14487
14768
|
* @param exchangeName - Name of the exchange to validate
|
|
14488
14769
|
* @returns Promise that resolves when validation is complete
|
|
14489
14770
|
*/
|
|
14490
|
-
this.validate = functoolsKit.memoize(([exchangeName]) => CREATE_KEY_FN$
|
|
14771
|
+
this.validate = functoolsKit.memoize(([exchangeName]) => CREATE_KEY_FN$u(exchangeName), async (exchangeName) => {
|
|
14491
14772
|
this.loggerService.log(METHOD_NAME_VALIDATE$3, {
|
|
14492
14773
|
exchangeName,
|
|
14493
14774
|
});
|
|
@@ -14739,7 +15020,7 @@ const METHOD_NAME_VALIDATE$2 = "strategyCoreService validate";
|
|
|
14739
15020
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
14740
15021
|
* @returns Unique string key for memoization
|
|
14741
15022
|
*/
|
|
14742
|
-
const CREATE_KEY_FN$
|
|
15023
|
+
const CREATE_KEY_FN$t = (context) => {
|
|
14743
15024
|
const parts = [context.strategyName, context.exchangeName];
|
|
14744
15025
|
if (context.frameName)
|
|
14745
15026
|
parts.push(context.frameName);
|
|
@@ -14771,7 +15052,7 @@ class StrategyCoreService {
|
|
|
14771
15052
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
14772
15053
|
* @returns Promise that resolves when validation is complete
|
|
14773
15054
|
*/
|
|
14774
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
15055
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$t(context), async (context) => {
|
|
14775
15056
|
this.loggerService.log(METHOD_NAME_VALIDATE$2, {
|
|
14776
15057
|
context,
|
|
14777
15058
|
});
|
|
@@ -16141,7 +16422,7 @@ class SizingGlobalService {
|
|
|
16141
16422
|
* @param context - Context with riskName, exchangeName, frameName
|
|
16142
16423
|
* @returns Unique string key for memoization
|
|
16143
16424
|
*/
|
|
16144
|
-
const CREATE_KEY_FN$
|
|
16425
|
+
const CREATE_KEY_FN$s = (context) => {
|
|
16145
16426
|
const parts = [context.riskName, context.exchangeName];
|
|
16146
16427
|
if (context.frameName)
|
|
16147
16428
|
parts.push(context.frameName);
|
|
@@ -16167,7 +16448,7 @@ class RiskGlobalService {
|
|
|
16167
16448
|
* @param payload - Payload with riskName, exchangeName and frameName
|
|
16168
16449
|
* @returns Promise that resolves when validation is complete
|
|
16169
16450
|
*/
|
|
16170
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
16451
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$s(context), async (context) => {
|
|
16171
16452
|
this.loggerService.log("riskGlobalService validate", {
|
|
16172
16453
|
context,
|
|
16173
16454
|
});
|
|
@@ -16245,7 +16526,7 @@ const METHOD_NAME_VALIDATE$1 = "actionCoreService validate";
|
|
|
16245
16526
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
16246
16527
|
* @returns Unique string key for memoization
|
|
16247
16528
|
*/
|
|
16248
|
-
const CREATE_KEY_FN$
|
|
16529
|
+
const CREATE_KEY_FN$r = (context) => {
|
|
16249
16530
|
const parts = [context.strategyName, context.exchangeName];
|
|
16250
16531
|
if (context.frameName)
|
|
16251
16532
|
parts.push(context.frameName);
|
|
@@ -16289,7 +16570,7 @@ class ActionCoreService {
|
|
|
16289
16570
|
* @param context - Strategy execution context with strategyName, exchangeName and frameName
|
|
16290
16571
|
* @returns Promise that resolves when all validations complete
|
|
16291
16572
|
*/
|
|
16292
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
16573
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$r(context), async (context) => {
|
|
16293
16574
|
this.loggerService.log(METHOD_NAME_VALIDATE$1, {
|
|
16294
16575
|
context,
|
|
16295
16576
|
});
|
|
@@ -21359,7 +21640,7 @@ const ReportWriter = new ReportWriterAdapter();
|
|
|
21359
21640
|
* @param backtest - Whether running in backtest mode
|
|
21360
21641
|
* @returns Unique string key for memoization
|
|
21361
21642
|
*/
|
|
21362
|
-
const CREATE_KEY_FN$
|
|
21643
|
+
const CREATE_KEY_FN$q = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
21363
21644
|
const parts = [symbol, strategyName, exchangeName];
|
|
21364
21645
|
if (frameName)
|
|
21365
21646
|
parts.push(frameName);
|
|
@@ -21605,7 +21886,7 @@ class BacktestMarkdownService {
|
|
|
21605
21886
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
21606
21887
|
* Each combination gets its own isolated storage instance.
|
|
21607
21888
|
*/
|
|
21608
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
21889
|
+
this.getStorage = functoolsKit.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));
|
|
21609
21890
|
/**
|
|
21610
21891
|
* Processes tick events and accumulates closed signals.
|
|
21611
21892
|
* Should be called from IStrategyCallbacks.onTick.
|
|
@@ -21762,7 +22043,7 @@ class BacktestMarkdownService {
|
|
|
21762
22043
|
payload,
|
|
21763
22044
|
});
|
|
21764
22045
|
if (payload) {
|
|
21765
|
-
const key = CREATE_KEY_FN$
|
|
22046
|
+
const key = CREATE_KEY_FN$q(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
21766
22047
|
this.getStorage.clear(key);
|
|
21767
22048
|
}
|
|
21768
22049
|
else {
|
|
@@ -21824,7 +22105,7 @@ class BacktestMarkdownService {
|
|
|
21824
22105
|
* @param backtest - Whether running in backtest mode
|
|
21825
22106
|
* @returns Unique string key for memoization
|
|
21826
22107
|
*/
|
|
21827
|
-
const CREATE_KEY_FN$
|
|
22108
|
+
const CREATE_KEY_FN$p = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
21828
22109
|
const parts = [symbol, strategyName, exchangeName];
|
|
21829
22110
|
if (frameName)
|
|
21830
22111
|
parts.push(frameName);
|
|
@@ -22319,7 +22600,7 @@ class LiveMarkdownService {
|
|
|
22319
22600
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
22320
22601
|
* Each combination gets its own isolated storage instance.
|
|
22321
22602
|
*/
|
|
22322
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
22603
|
+
this.getStorage = functoolsKit.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));
|
|
22323
22604
|
/**
|
|
22324
22605
|
* Subscribes to live signal emitter to receive tick events.
|
|
22325
22606
|
* Protected against multiple subscriptions.
|
|
@@ -22537,7 +22818,7 @@ class LiveMarkdownService {
|
|
|
22537
22818
|
payload,
|
|
22538
22819
|
});
|
|
22539
22820
|
if (payload) {
|
|
22540
|
-
const key = CREATE_KEY_FN$
|
|
22821
|
+
const key = CREATE_KEY_FN$p(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
22541
22822
|
this.getStorage.clear(key);
|
|
22542
22823
|
}
|
|
22543
22824
|
else {
|
|
@@ -22557,7 +22838,7 @@ class LiveMarkdownService {
|
|
|
22557
22838
|
* @param backtest - Whether running in backtest mode
|
|
22558
22839
|
* @returns Unique string key for memoization
|
|
22559
22840
|
*/
|
|
22560
|
-
const CREATE_KEY_FN$
|
|
22841
|
+
const CREATE_KEY_FN$o = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
22561
22842
|
const parts = [symbol, strategyName, exchangeName];
|
|
22562
22843
|
if (frameName)
|
|
22563
22844
|
parts.push(frameName);
|
|
@@ -22846,7 +23127,7 @@ class ScheduleMarkdownService {
|
|
|
22846
23127
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
22847
23128
|
* Each combination gets its own isolated storage instance.
|
|
22848
23129
|
*/
|
|
22849
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
23130
|
+
this.getStorage = functoolsKit.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));
|
|
22850
23131
|
/**
|
|
22851
23132
|
* Subscribes to signal emitter to receive scheduled signal events.
|
|
22852
23133
|
* Protected against multiple subscriptions.
|
|
@@ -23049,7 +23330,7 @@ class ScheduleMarkdownService {
|
|
|
23049
23330
|
payload,
|
|
23050
23331
|
});
|
|
23051
23332
|
if (payload) {
|
|
23052
|
-
const key = CREATE_KEY_FN$
|
|
23333
|
+
const key = CREATE_KEY_FN$o(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
23053
23334
|
this.getStorage.clear(key);
|
|
23054
23335
|
}
|
|
23055
23336
|
else {
|
|
@@ -23069,7 +23350,7 @@ class ScheduleMarkdownService {
|
|
|
23069
23350
|
* @param backtest - Whether running in backtest mode
|
|
23070
23351
|
* @returns Unique string key for memoization
|
|
23071
23352
|
*/
|
|
23072
|
-
const CREATE_KEY_FN$
|
|
23353
|
+
const CREATE_KEY_FN$n = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
23073
23354
|
const parts = [symbol, strategyName, exchangeName];
|
|
23074
23355
|
if (frameName)
|
|
23075
23356
|
parts.push(frameName);
|
|
@@ -23314,7 +23595,7 @@ class PerformanceMarkdownService {
|
|
|
23314
23595
|
* Memoized function to get or create PerformanceStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
23315
23596
|
* Each combination gets its own isolated storage instance.
|
|
23316
23597
|
*/
|
|
23317
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
23598
|
+
this.getStorage = functoolsKit.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));
|
|
23318
23599
|
/**
|
|
23319
23600
|
* Subscribes to performance emitter to receive performance events.
|
|
23320
23601
|
* Protected against multiple subscriptions.
|
|
@@ -23481,7 +23762,7 @@ class PerformanceMarkdownService {
|
|
|
23481
23762
|
payload,
|
|
23482
23763
|
});
|
|
23483
23764
|
if (payload) {
|
|
23484
|
-
const key = CREATE_KEY_FN$
|
|
23765
|
+
const key = CREATE_KEY_FN$n(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
23485
23766
|
this.getStorage.clear(key);
|
|
23486
23767
|
}
|
|
23487
23768
|
else {
|
|
@@ -23960,7 +24241,7 @@ class WalkerMarkdownService {
|
|
|
23960
24241
|
* @param backtest - Whether running in backtest mode
|
|
23961
24242
|
* @returns Unique string key for memoization
|
|
23962
24243
|
*/
|
|
23963
|
-
const CREATE_KEY_FN$
|
|
24244
|
+
const CREATE_KEY_FN$m = (exchangeName, frameName, backtest) => {
|
|
23964
24245
|
const parts = [exchangeName];
|
|
23965
24246
|
if (frameName)
|
|
23966
24247
|
parts.push(frameName);
|
|
@@ -24407,7 +24688,7 @@ class HeatMarkdownService {
|
|
|
24407
24688
|
* Memoized function to get or create HeatmapStorage for exchange, frame and backtest mode.
|
|
24408
24689
|
* Each exchangeName + frameName + backtest mode combination gets its own isolated heatmap storage instance.
|
|
24409
24690
|
*/
|
|
24410
|
-
this.getStorage = functoolsKit.memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
24691
|
+
this.getStorage = functoolsKit.memoize(([exchangeName, frameName, backtest]) => CREATE_KEY_FN$m(exchangeName, frameName, backtest), (exchangeName, frameName, backtest) => new HeatmapStorage(exchangeName, frameName, backtest));
|
|
24411
24692
|
/**
|
|
24412
24693
|
* Subscribes to signal emitter to receive tick events.
|
|
24413
24694
|
* Protected against multiple subscriptions.
|
|
@@ -24625,7 +24906,7 @@ class HeatMarkdownService {
|
|
|
24625
24906
|
payload,
|
|
24626
24907
|
});
|
|
24627
24908
|
if (payload) {
|
|
24628
|
-
const key = CREATE_KEY_FN$
|
|
24909
|
+
const key = CREATE_KEY_FN$m(payload.exchangeName, payload.frameName, payload.backtest);
|
|
24629
24910
|
this.getStorage.clear(key);
|
|
24630
24911
|
}
|
|
24631
24912
|
else {
|
|
@@ -25656,7 +25937,7 @@ class ClientPartial {
|
|
|
25656
25937
|
* @param backtest - Whether running in backtest mode
|
|
25657
25938
|
* @returns Unique string key for memoization
|
|
25658
25939
|
*/
|
|
25659
|
-
const CREATE_KEY_FN$
|
|
25940
|
+
const CREATE_KEY_FN$l = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
25660
25941
|
/**
|
|
25661
25942
|
* Creates a callback function for emitting profit events to partialProfitSubject.
|
|
25662
25943
|
*
|
|
@@ -25778,7 +26059,7 @@ class PartialConnectionService {
|
|
|
25778
26059
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
25779
26060
|
* Value: ClientPartial instance with logger and event emitters
|
|
25780
26061
|
*/
|
|
25781
|
-
this.getPartial = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
26062
|
+
this.getPartial = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$l(signalId, backtest), (signalId, backtest) => {
|
|
25782
26063
|
return new ClientPartial({
|
|
25783
26064
|
signalId,
|
|
25784
26065
|
logger: this.loggerService,
|
|
@@ -25868,7 +26149,7 @@ class PartialConnectionService {
|
|
|
25868
26149
|
const partial = this.getPartial(data.id, backtest);
|
|
25869
26150
|
await partial.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
|
|
25870
26151
|
await partial.clear(symbol, data, priceClose, backtest);
|
|
25871
|
-
const key = CREATE_KEY_FN$
|
|
26152
|
+
const key = CREATE_KEY_FN$l(data.id, backtest);
|
|
25872
26153
|
this.getPartial.clear(key);
|
|
25873
26154
|
};
|
|
25874
26155
|
}
|
|
@@ -25884,7 +26165,7 @@ class PartialConnectionService {
|
|
|
25884
26165
|
* @param backtest - Whether running in backtest mode
|
|
25885
26166
|
* @returns Unique string key for memoization
|
|
25886
26167
|
*/
|
|
25887
|
-
const CREATE_KEY_FN$
|
|
26168
|
+
const CREATE_KEY_FN$k = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
25888
26169
|
const parts = [symbol, strategyName, exchangeName];
|
|
25889
26170
|
if (frameName)
|
|
25890
26171
|
parts.push(frameName);
|
|
@@ -26107,7 +26388,7 @@ class PartialMarkdownService {
|
|
|
26107
26388
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
26108
26389
|
* Each combination gets its own isolated storage instance.
|
|
26109
26390
|
*/
|
|
26110
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
26391
|
+
this.getStorage = functoolsKit.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));
|
|
26111
26392
|
/**
|
|
26112
26393
|
* Subscribes to partial profit/loss signal emitters to receive events.
|
|
26113
26394
|
* Protected against multiple subscriptions.
|
|
@@ -26317,7 +26598,7 @@ class PartialMarkdownService {
|
|
|
26317
26598
|
payload,
|
|
26318
26599
|
});
|
|
26319
26600
|
if (payload) {
|
|
26320
|
-
const key = CREATE_KEY_FN$
|
|
26601
|
+
const key = CREATE_KEY_FN$k(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
26321
26602
|
this.getStorage.clear(key);
|
|
26322
26603
|
}
|
|
26323
26604
|
else {
|
|
@@ -26333,7 +26614,7 @@ class PartialMarkdownService {
|
|
|
26333
26614
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
26334
26615
|
* @returns Unique string key for memoization
|
|
26335
26616
|
*/
|
|
26336
|
-
const CREATE_KEY_FN$
|
|
26617
|
+
const CREATE_KEY_FN$j = (context) => {
|
|
26337
26618
|
const parts = [context.strategyName, context.exchangeName];
|
|
26338
26619
|
if (context.frameName)
|
|
26339
26620
|
parts.push(context.frameName);
|
|
@@ -26407,7 +26688,7 @@ class PartialGlobalService {
|
|
|
26407
26688
|
* @param context - Context with strategyName, exchangeName and frameName
|
|
26408
26689
|
* @param methodName - Name of the calling method for error tracking
|
|
26409
26690
|
*/
|
|
26410
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
26691
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$j(context), (context, methodName) => {
|
|
26411
26692
|
this.loggerService.log("partialGlobalService validate", {
|
|
26412
26693
|
context,
|
|
26413
26694
|
methodName,
|
|
@@ -26862,7 +27143,7 @@ class ClientBreakeven {
|
|
|
26862
27143
|
* @param backtest - Whether running in backtest mode
|
|
26863
27144
|
* @returns Unique string key for memoization
|
|
26864
27145
|
*/
|
|
26865
|
-
const CREATE_KEY_FN$
|
|
27146
|
+
const CREATE_KEY_FN$i = (signalId, backtest) => `${signalId}:${backtest ? "backtest" : "live"}`;
|
|
26866
27147
|
/**
|
|
26867
27148
|
* Creates a callback function for emitting breakeven events to breakevenSubject.
|
|
26868
27149
|
*
|
|
@@ -26948,7 +27229,7 @@ class BreakevenConnectionService {
|
|
|
26948
27229
|
* Key format: "signalId:backtest" or "signalId:live"
|
|
26949
27230
|
* Value: ClientBreakeven instance with logger and event emitter
|
|
26950
27231
|
*/
|
|
26951
|
-
this.getBreakeven = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$
|
|
27232
|
+
this.getBreakeven = functoolsKit.memoize(([signalId, backtest]) => CREATE_KEY_FN$i(signalId, backtest), (signalId, backtest) => {
|
|
26952
27233
|
return new ClientBreakeven({
|
|
26953
27234
|
signalId,
|
|
26954
27235
|
logger: this.loggerService,
|
|
@@ -27009,7 +27290,7 @@ class BreakevenConnectionService {
|
|
|
27009
27290
|
const breakeven = this.getBreakeven(data.id, backtest);
|
|
27010
27291
|
await breakeven.waitForInit(symbol, data.strategyName, data.exchangeName, backtest);
|
|
27011
27292
|
await breakeven.clear(symbol, data, priceClose, backtest);
|
|
27012
|
-
const key = CREATE_KEY_FN$
|
|
27293
|
+
const key = CREATE_KEY_FN$i(data.id, backtest);
|
|
27013
27294
|
this.getBreakeven.clear(key);
|
|
27014
27295
|
};
|
|
27015
27296
|
}
|
|
@@ -27025,7 +27306,7 @@ class BreakevenConnectionService {
|
|
|
27025
27306
|
* @param backtest - Whether running in backtest mode
|
|
27026
27307
|
* @returns Unique string key for memoization
|
|
27027
27308
|
*/
|
|
27028
|
-
const CREATE_KEY_FN$
|
|
27309
|
+
const CREATE_KEY_FN$h = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
27029
27310
|
const parts = [symbol, strategyName, exchangeName];
|
|
27030
27311
|
if (frameName)
|
|
27031
27312
|
parts.push(frameName);
|
|
@@ -27200,7 +27481,7 @@ class BreakevenMarkdownService {
|
|
|
27200
27481
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
27201
27482
|
* Each combination gets its own isolated storage instance.
|
|
27202
27483
|
*/
|
|
27203
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
27484
|
+
this.getStorage = functoolsKit.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));
|
|
27204
27485
|
/**
|
|
27205
27486
|
* Subscribes to breakeven signal emitter to receive events.
|
|
27206
27487
|
* Protected against multiple subscriptions.
|
|
@@ -27389,7 +27670,7 @@ class BreakevenMarkdownService {
|
|
|
27389
27670
|
payload,
|
|
27390
27671
|
});
|
|
27391
27672
|
if (payload) {
|
|
27392
|
-
const key = CREATE_KEY_FN$
|
|
27673
|
+
const key = CREATE_KEY_FN$h(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
27393
27674
|
this.getStorage.clear(key);
|
|
27394
27675
|
}
|
|
27395
27676
|
else {
|
|
@@ -27405,7 +27686,7 @@ class BreakevenMarkdownService {
|
|
|
27405
27686
|
* @param context - Context with strategyName, exchangeName, frameName
|
|
27406
27687
|
* @returns Unique string key for memoization
|
|
27407
27688
|
*/
|
|
27408
|
-
const CREATE_KEY_FN$
|
|
27689
|
+
const CREATE_KEY_FN$g = (context) => {
|
|
27409
27690
|
const parts = [context.strategyName, context.exchangeName];
|
|
27410
27691
|
if (context.frameName)
|
|
27411
27692
|
parts.push(context.frameName);
|
|
@@ -27479,7 +27760,7 @@ class BreakevenGlobalService {
|
|
|
27479
27760
|
* @param context - Context with strategyName, exchangeName and frameName
|
|
27480
27761
|
* @param methodName - Name of the calling method for error tracking
|
|
27481
27762
|
*/
|
|
27482
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
27763
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$g(context), (context, methodName) => {
|
|
27483
27764
|
this.loggerService.log("breakevenGlobalService validate", {
|
|
27484
27765
|
context,
|
|
27485
27766
|
methodName,
|
|
@@ -27700,7 +27981,7 @@ class ConfigValidationService {
|
|
|
27700
27981
|
* @param backtest - Whether running in backtest mode
|
|
27701
27982
|
* @returns Unique string key for memoization
|
|
27702
27983
|
*/
|
|
27703
|
-
const CREATE_KEY_FN$
|
|
27984
|
+
const CREATE_KEY_FN$f = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
27704
27985
|
const parts = [symbol, strategyName, exchangeName];
|
|
27705
27986
|
if (frameName)
|
|
27706
27987
|
parts.push(frameName);
|
|
@@ -27867,7 +28148,7 @@ class RiskMarkdownService {
|
|
|
27867
28148
|
* Memoized function to get or create ReportStorage for a symbol-strategy-exchange-frame-backtest combination.
|
|
27868
28149
|
* Each combination gets its own isolated storage instance.
|
|
27869
28150
|
*/
|
|
27870
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
28151
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$f(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$4(symbol, strategyName, exchangeName, frameName));
|
|
27871
28152
|
/**
|
|
27872
28153
|
* Subscribes to risk rejection emitter to receive rejection events.
|
|
27873
28154
|
* Protected against multiple subscriptions.
|
|
@@ -28056,7 +28337,7 @@ class RiskMarkdownService {
|
|
|
28056
28337
|
payload,
|
|
28057
28338
|
});
|
|
28058
28339
|
if (payload) {
|
|
28059
|
-
const key = CREATE_KEY_FN$
|
|
28340
|
+
const key = CREATE_KEY_FN$f(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
28060
28341
|
this.getStorage.clear(key);
|
|
28061
28342
|
}
|
|
28062
28343
|
else {
|
|
@@ -30636,7 +30917,7 @@ class HighestProfitReportService {
|
|
|
30636
30917
|
* @returns Colon-separated key string for memoization
|
|
30637
30918
|
* @internal
|
|
30638
30919
|
*/
|
|
30639
|
-
const CREATE_KEY_FN$
|
|
30920
|
+
const CREATE_KEY_FN$e = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
30640
30921
|
const parts = [symbol, strategyName, exchangeName];
|
|
30641
30922
|
if (frameName)
|
|
30642
30923
|
parts.push(frameName);
|
|
@@ -30878,7 +31159,7 @@ class StrategyMarkdownService {
|
|
|
30878
31159
|
*
|
|
30879
31160
|
* @internal
|
|
30880
31161
|
*/
|
|
30881
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
31162
|
+
this.getStorage = functoolsKit.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));
|
|
30882
31163
|
/**
|
|
30883
31164
|
* Records a cancel-scheduled event when a scheduled signal is cancelled.
|
|
30884
31165
|
*
|
|
@@ -31452,7 +31733,7 @@ class StrategyMarkdownService {
|
|
|
31452
31733
|
this.clear = async (payload) => {
|
|
31453
31734
|
this.loggerService.log("strategyMarkdownService clear", { payload });
|
|
31454
31735
|
if (payload) {
|
|
31455
|
-
const key = CREATE_KEY_FN$
|
|
31736
|
+
const key = CREATE_KEY_FN$e(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
31456
31737
|
this.getStorage.clear(key);
|
|
31457
31738
|
}
|
|
31458
31739
|
else {
|
|
@@ -31560,7 +31841,7 @@ class StrategyMarkdownService {
|
|
|
31560
31841
|
* Creates a unique key for memoizing ReportStorage instances.
|
|
31561
31842
|
* Key format: "symbol:strategyName:exchangeName[:frameName]:backtest|live"
|
|
31562
31843
|
*/
|
|
31563
|
-
const CREATE_KEY_FN$
|
|
31844
|
+
const CREATE_KEY_FN$d = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
31564
31845
|
const parts = [symbol, strategyName, exchangeName];
|
|
31565
31846
|
if (frameName)
|
|
31566
31847
|
parts.push(frameName);
|
|
@@ -31753,7 +32034,7 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
31753
32034
|
class SyncMarkdownService {
|
|
31754
32035
|
constructor() {
|
|
31755
32036
|
this.loggerService = inject(TYPES.loggerService);
|
|
31756
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32037
|
+
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$d(symbol, strategyName, exchangeName, frameName, backtest), (symbol, strategyName, exchangeName, frameName, backtest) => new ReportStorage$2(symbol, strategyName, exchangeName, frameName, backtest));
|
|
31757
32038
|
/**
|
|
31758
32039
|
* Subscribes to `syncSubject` to start receiving `SignalSyncContract` events.
|
|
31759
32040
|
* Protected against multiple subscriptions via `singleshot` — subsequent calls
|
|
@@ -31951,7 +32232,7 @@ class SyncMarkdownService {
|
|
|
31951
32232
|
this.clear = async (payload) => {
|
|
31952
32233
|
this.loggerService.log("syncMarkdownService clear", { payload });
|
|
31953
32234
|
if (payload) {
|
|
31954
|
-
const key = CREATE_KEY_FN$
|
|
32235
|
+
const key = CREATE_KEY_FN$d(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
31955
32236
|
this.getStorage.clear(key);
|
|
31956
32237
|
}
|
|
31957
32238
|
else {
|
|
@@ -31964,7 +32245,7 @@ class SyncMarkdownService {
|
|
|
31964
32245
|
/**
|
|
31965
32246
|
* Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
|
|
31966
32247
|
*/
|
|
31967
|
-
const CREATE_KEY_FN$
|
|
32248
|
+
const CREATE_KEY_FN$c = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
31968
32249
|
const parts = [symbol, strategyName, exchangeName];
|
|
31969
32250
|
if (frameName)
|
|
31970
32251
|
parts.push(frameName);
|
|
@@ -32142,7 +32423,7 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
32142
32423
|
class HighestProfitMarkdownService {
|
|
32143
32424
|
constructor() {
|
|
32144
32425
|
this.loggerService = inject(TYPES.loggerService);
|
|
32145
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32426
|
+
this.getStorage = functoolsKit.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));
|
|
32146
32427
|
/**
|
|
32147
32428
|
* Subscribes to `highestProfitSubject` to start receiving `HighestProfitContract`
|
|
32148
32429
|
* events. Protected against multiple subscriptions via `singleshot` — subsequent
|
|
@@ -32308,7 +32589,7 @@ class HighestProfitMarkdownService {
|
|
|
32308
32589
|
this.clear = async (payload) => {
|
|
32309
32590
|
this.loggerService.log("highestProfitMarkdownService clear", { payload });
|
|
32310
32591
|
if (payload) {
|
|
32311
|
-
const key = CREATE_KEY_FN$
|
|
32592
|
+
const key = CREATE_KEY_FN$c(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32312
32593
|
this.getStorage.clear(key);
|
|
32313
32594
|
}
|
|
32314
32595
|
else {
|
|
@@ -32330,7 +32611,7 @@ const LISTEN_TIMEOUT$1 = 120000;
|
|
|
32330
32611
|
* @param backtest - Whether running in backtest mode
|
|
32331
32612
|
* @returns Unique string key for memoization
|
|
32332
32613
|
*/
|
|
32333
|
-
const CREATE_KEY_FN$
|
|
32614
|
+
const CREATE_KEY_FN$b = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
32334
32615
|
const parts = [symbol, strategyName, exchangeName];
|
|
32335
32616
|
if (frameName)
|
|
32336
32617
|
parts.push(frameName);
|
|
@@ -32373,7 +32654,7 @@ class PriceMetaService {
|
|
|
32373
32654
|
* Each subject holds the latest currentPrice emitted by the strategy iterator for that key.
|
|
32374
32655
|
* Instances are cached until clear() is called.
|
|
32375
32656
|
*/
|
|
32376
|
-
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32657
|
+
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$b(symbol, strategyName, exchangeName, frameName, backtest), () => new functoolsKit.BehaviorSubject());
|
|
32377
32658
|
/**
|
|
32378
32659
|
* Returns the current market price for the given symbol and context.
|
|
32379
32660
|
*
|
|
@@ -32402,10 +32683,10 @@ class PriceMetaService {
|
|
|
32402
32683
|
if (source.data) {
|
|
32403
32684
|
return source.data;
|
|
32404
32685
|
}
|
|
32405
|
-
console.warn(`PriceMetaService: No currentPrice available for ${CREATE_KEY_FN$
|
|
32686
|
+
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...`);
|
|
32406
32687
|
const currentPrice = await functoolsKit.waitForNext(source, (data) => !!data, LISTEN_TIMEOUT$1);
|
|
32407
32688
|
if (typeof currentPrice === "symbol") {
|
|
32408
|
-
throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$
|
|
32689
|
+
throw new Error(`PriceMetaService: Timeout while waiting for currentPrice for ${CREATE_KEY_FN$b(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
|
|
32409
32690
|
}
|
|
32410
32691
|
return currentPrice;
|
|
32411
32692
|
};
|
|
@@ -32447,7 +32728,7 @@ class PriceMetaService {
|
|
|
32447
32728
|
this.getSource.clear();
|
|
32448
32729
|
return;
|
|
32449
32730
|
}
|
|
32450
|
-
const key = CREATE_KEY_FN$
|
|
32731
|
+
const key = CREATE_KEY_FN$b(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32451
32732
|
this.getSource.clear(key);
|
|
32452
32733
|
};
|
|
32453
32734
|
}
|
|
@@ -32465,7 +32746,7 @@ const LISTEN_TIMEOUT = 120000;
|
|
|
32465
32746
|
* @param backtest - Whether running in backtest mode
|
|
32466
32747
|
* @returns Unique string key for memoization
|
|
32467
32748
|
*/
|
|
32468
|
-
const CREATE_KEY_FN$
|
|
32749
|
+
const CREATE_KEY_FN$a = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
32469
32750
|
const parts = [symbol, strategyName, exchangeName];
|
|
32470
32751
|
if (frameName)
|
|
32471
32752
|
parts.push(frameName);
|
|
@@ -32508,7 +32789,7 @@ class TimeMetaService {
|
|
|
32508
32789
|
* Each subject holds the latest createdAt timestamp emitted by the strategy iterator for that key.
|
|
32509
32790
|
* Instances are cached until clear() is called.
|
|
32510
32791
|
*/
|
|
32511
|
-
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
32792
|
+
this.getSource = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$a(symbol, strategyName, exchangeName, frameName, backtest), () => new functoolsKit.BehaviorSubject());
|
|
32512
32793
|
/**
|
|
32513
32794
|
* Returns the current candle timestamp (in milliseconds) for the given symbol and context.
|
|
32514
32795
|
*
|
|
@@ -32536,10 +32817,10 @@ class TimeMetaService {
|
|
|
32536
32817
|
if (source.data) {
|
|
32537
32818
|
return source.data;
|
|
32538
32819
|
}
|
|
32539
|
-
console.warn(`TimeMetaService: No timestamp available for ${CREATE_KEY_FN$
|
|
32820
|
+
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...`);
|
|
32540
32821
|
const timestamp = await functoolsKit.waitForNext(source, (data) => !!data, LISTEN_TIMEOUT);
|
|
32541
32822
|
if (typeof timestamp === "symbol") {
|
|
32542
|
-
throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$
|
|
32823
|
+
throw new Error(`TimeMetaService: Timeout while waiting for timestamp for ${CREATE_KEY_FN$a(symbol, context.strategyName, context.exchangeName, context.frameName, backtest)}`);
|
|
32543
32824
|
}
|
|
32544
32825
|
return timestamp;
|
|
32545
32826
|
};
|
|
@@ -32581,7 +32862,7 @@ class TimeMetaService {
|
|
|
32581
32862
|
this.getSource.clear();
|
|
32582
32863
|
return;
|
|
32583
32864
|
}
|
|
32584
|
-
const key = CREATE_KEY_FN$
|
|
32865
|
+
const key = CREATE_KEY_FN$a(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32585
32866
|
this.getSource.clear(key);
|
|
32586
32867
|
};
|
|
32587
32868
|
}
|
|
@@ -32687,7 +32968,7 @@ class MaxDrawdownReportService {
|
|
|
32687
32968
|
/**
|
|
32688
32969
|
* Creates a unique memoization key for a symbol-strategy-exchange-frame-backtest combination.
|
|
32689
32970
|
*/
|
|
32690
|
-
const CREATE_KEY_FN$
|
|
32971
|
+
const CREATE_KEY_FN$9 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
32691
32972
|
const parts = [symbol, strategyName, exchangeName];
|
|
32692
32973
|
if (frameName)
|
|
32693
32974
|
parts.push(frameName);
|
|
@@ -32813,7 +33094,7 @@ class ReportStorage {
|
|
|
32813
33094
|
class MaxDrawdownMarkdownService {
|
|
32814
33095
|
constructor() {
|
|
32815
33096
|
this.loggerService = inject(TYPES.loggerService);
|
|
32816
|
-
this.getStorage = functoolsKit.memoize(([symbol, strategyName, exchangeName, frameName, backtest]) => CREATE_KEY_FN$
|
|
33097
|
+
this.getStorage = functoolsKit.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));
|
|
32817
33098
|
/**
|
|
32818
33099
|
* Subscribes to `maxDrawdownSubject` to start receiving `MaxDrawdownContract`
|
|
32819
33100
|
* events. Protected against multiple subscriptions via `singleshot`.
|
|
@@ -32892,7 +33173,7 @@ class MaxDrawdownMarkdownService {
|
|
|
32892
33173
|
this.clear = async (payload) => {
|
|
32893
33174
|
this.loggerService.log("maxDrawdownMarkdownService clear", { payload });
|
|
32894
33175
|
if (payload) {
|
|
32895
|
-
const key = CREATE_KEY_FN$
|
|
33176
|
+
const key = CREATE_KEY_FN$9(payload.symbol, payload.strategyName, payload.exchangeName, payload.frameName, payload.backtest);
|
|
32896
33177
|
this.getStorage.clear(key);
|
|
32897
33178
|
}
|
|
32898
33179
|
else {
|
|
@@ -32910,7 +33191,7 @@ const METHOD_NAME_VALIDATE = "notificationHelperService.validate";
|
|
|
32910
33191
|
* @param context - Execution context with strategyName, exchangeName, frameName
|
|
32911
33192
|
* @returns Unique string key for memoization
|
|
32912
33193
|
*/
|
|
32913
|
-
const CREATE_KEY_FN$
|
|
33194
|
+
const CREATE_KEY_FN$8 = (context) => {
|
|
32914
33195
|
const parts = [context.strategyName, context.exchangeName];
|
|
32915
33196
|
if (context.frameName)
|
|
32916
33197
|
parts.push(context.frameName);
|
|
@@ -32943,7 +33224,7 @@ class NotificationHelperService {
|
|
|
32943
33224
|
* @param context - Routing context: strategyName, exchangeName, frameName
|
|
32944
33225
|
* @throws {Error} If any registered schema fails validation
|
|
32945
33226
|
*/
|
|
32946
|
-
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$
|
|
33227
|
+
this.validate = functoolsKit.memoize(([context]) => CREATE_KEY_FN$8(context), async (context) => {
|
|
32947
33228
|
this.loggerService.log(METHOD_NAME_VALIDATE, {
|
|
32948
33229
|
context,
|
|
32949
33230
|
});
|
|
@@ -46487,7 +46768,7 @@ const RECENT_ADAPTER_METHOD_NAME_GET_MINUTES_SINCE_LATEST_SIGNAL = "RecentAdapte
|
|
|
46487
46768
|
* @param backtest - Flag indicating if the context is backtest or live
|
|
46488
46769
|
* @returns Composite key string
|
|
46489
46770
|
*/
|
|
46490
|
-
const CREATE_KEY_FN$
|
|
46771
|
+
const CREATE_KEY_FN$7 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
46491
46772
|
const parts = [symbol, strategyName, exchangeName];
|
|
46492
46773
|
if (frameName)
|
|
46493
46774
|
parts.push(frameName);
|
|
@@ -46577,7 +46858,7 @@ class RecentMemoryBacktestUtils {
|
|
|
46577
46858
|
backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
46578
46859
|
signalId: event.data.id,
|
|
46579
46860
|
});
|
|
46580
|
-
const key = CREATE_KEY_FN$
|
|
46861
|
+
const key = CREATE_KEY_FN$7(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
|
|
46581
46862
|
this._signals.set(key, event.data);
|
|
46582
46863
|
};
|
|
46583
46864
|
/**
|
|
@@ -46590,7 +46871,7 @@ class RecentMemoryBacktestUtils {
|
|
|
46590
46871
|
* @returns The latest signal or null if not found
|
|
46591
46872
|
*/
|
|
46592
46873
|
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
46593
|
-
const key = CREATE_KEY_FN$
|
|
46874
|
+
const key = CREATE_KEY_FN$7(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
46594
46875
|
backtest.loggerService.info(RECENT_MEMORY_BACKTEST_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
46595
46876
|
return this._signals.get(key) ?? null;
|
|
46596
46877
|
};
|
|
@@ -46696,7 +46977,7 @@ class RecentMemoryLiveUtils {
|
|
|
46696
46977
|
backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_HANDLE_ACTIVE_PING, {
|
|
46697
46978
|
signalId: event.data.id,
|
|
46698
46979
|
});
|
|
46699
|
-
const key = CREATE_KEY_FN$
|
|
46980
|
+
const key = CREATE_KEY_FN$7(event.symbol, event.strategyName, event.exchangeName, event.data.frameName, event.backtest);
|
|
46700
46981
|
this._signals.set(key, event.data);
|
|
46701
46982
|
};
|
|
46702
46983
|
/**
|
|
@@ -46709,7 +46990,7 @@ class RecentMemoryLiveUtils {
|
|
|
46709
46990
|
* @returns The latest signal or null if not found
|
|
46710
46991
|
*/
|
|
46711
46992
|
this.getLatestSignal = async (symbol, strategyName, exchangeName, frameName, backtest$1) => {
|
|
46712
|
-
const key = CREATE_KEY_FN$
|
|
46993
|
+
const key = CREATE_KEY_FN$7(symbol, strategyName, exchangeName, frameName, backtest$1);
|
|
46713
46994
|
backtest.loggerService.info(RECENT_MEMORY_LIVE_METHOD_NAME_GET_LATEST_SIGNAL, { key });
|
|
46714
46995
|
return this._signals.get(key) ?? null;
|
|
46715
46996
|
};
|
|
@@ -47060,6 +47341,554 @@ const RecentLive = new RecentLiveAdapter();
|
|
|
47060
47341
|
*/
|
|
47061
47342
|
const RecentBacktest = new RecentBacktestAdapter();
|
|
47062
47343
|
|
|
47344
|
+
const CREATE_KEY_FN$6 = (signalId, bucketName) => `${signalId}_${bucketName}`;
|
|
47345
|
+
const STATE_LOCAL_INSTANCE_METHOD_NAME_GET = "StateLocalInstance.getState";
|
|
47346
|
+
const STATE_LOCAL_INSTANCE_METHOD_NAME_SET = "StateLocalInstance.setState";
|
|
47347
|
+
const STATE_PERSIST_INSTANCE_METHOD_NAME_WAIT_FOR_INIT = "StatePersistInstance.waitForInit";
|
|
47348
|
+
const STATE_PERSIST_INSTANCE_METHOD_NAME_GET = "StatePersistInstance.getState";
|
|
47349
|
+
const STATE_PERSIST_INSTANCE_METHOD_NAME_SET = "StatePersistInstance.setState";
|
|
47350
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE = "StateBacktestAdapter.dispose";
|
|
47351
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_GET = "StateBacktestAdapter.getState";
|
|
47352
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_SET = "StateBacktestAdapter.setState";
|
|
47353
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL = "StateBacktestAdapter.useLocal";
|
|
47354
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST = "StateBacktestAdapter.usePersist";
|
|
47355
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY = "StateBacktestAdapter.useDummy";
|
|
47356
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER = "StateBacktestAdapter.useStateAdapter";
|
|
47357
|
+
const STATE_BACKTEST_ADAPTER_METHOD_NAME_CLEAR = "StateBacktestAdapter.clear";
|
|
47358
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_DISPOSE = "StateLiveAdapter.dispose";
|
|
47359
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_GET = "StateLiveAdapter.getState";
|
|
47360
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_SET = "StateLiveAdapter.setState";
|
|
47361
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL = "StateLiveAdapter.useLocal";
|
|
47362
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST = "StateLiveAdapter.usePersist";
|
|
47363
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY = "StateLiveAdapter.useDummy";
|
|
47364
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER = "StateLiveAdapter.useStateAdapter";
|
|
47365
|
+
const STATE_LIVE_ADAPTER_METHOD_NAME_CLEAR = "StateLiveAdapter.clear";
|
|
47366
|
+
const STATE_ADAPTER_METHOD_NAME_ENABLE = "StateAdapter.enable";
|
|
47367
|
+
const STATE_ADAPTER_METHOD_NAME_DISABLE = "StateAdapter.disable";
|
|
47368
|
+
const STATE_ADAPTER_METHOD_NAME_GET = "StateAdapter.getState";
|
|
47369
|
+
const STATE_ADAPTER_METHOD_NAME_SET = "StateAdapter.setState";
|
|
47370
|
+
/**
|
|
47371
|
+
* In-process state instance backed by a plain object reference.
|
|
47372
|
+
* All data lives in process memory only - no disk persistence.
|
|
47373
|
+
*
|
|
47374
|
+
* Features:
|
|
47375
|
+
* - Mutable in-memory state with functional dispatch support
|
|
47376
|
+
* - Scoped per (signalId, bucketName) pair
|
|
47377
|
+
*
|
|
47378
|
+
* Use for backtesting and unit tests where persistence between runs is not needed.
|
|
47379
|
+
* Tracks per-trade metrics such as peakPercent and minutesOpen to implement
|
|
47380
|
+
* the capitulation rule: exit when peak < threshold after N minutes open.
|
|
47381
|
+
*/
|
|
47382
|
+
class StateLocalInstance {
|
|
47383
|
+
constructor(initialValue, signalId, bucketName) {
|
|
47384
|
+
this.initialValue = initialValue;
|
|
47385
|
+
this.signalId = signalId;
|
|
47386
|
+
this.bucketName = bucketName;
|
|
47387
|
+
/**
|
|
47388
|
+
* Initializes _value from initialValue - local state needs no async setup.
|
|
47389
|
+
* @returns Promise that resolves immediately
|
|
47390
|
+
*/
|
|
47391
|
+
this.waitForInit = functoolsKit.singleshot(async (_initial) => {
|
|
47392
|
+
this._value = this.initialValue;
|
|
47393
|
+
});
|
|
47394
|
+
/**
|
|
47395
|
+
* Update the in-memory state value.
|
|
47396
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47397
|
+
* @returns Updated state value
|
|
47398
|
+
*/
|
|
47399
|
+
this.setState = functoolsKit.queued(async (dispatch) => {
|
|
47400
|
+
backtest.loggerService.debug(STATE_LOCAL_INSTANCE_METHOD_NAME_SET, {
|
|
47401
|
+
signalId: this.signalId,
|
|
47402
|
+
bucketName: this.bucketName,
|
|
47403
|
+
});
|
|
47404
|
+
if (typeof dispatch === "function") {
|
|
47405
|
+
this._value = await dispatch(this._value);
|
|
47406
|
+
}
|
|
47407
|
+
else {
|
|
47408
|
+
this._value = dispatch;
|
|
47409
|
+
}
|
|
47410
|
+
return this._value;
|
|
47411
|
+
});
|
|
47412
|
+
}
|
|
47413
|
+
/**
|
|
47414
|
+
* Read the current in-memory state value.
|
|
47415
|
+
* @returns Current state value
|
|
47416
|
+
*/
|
|
47417
|
+
async getState() {
|
|
47418
|
+
backtest.loggerService.debug(STATE_LOCAL_INSTANCE_METHOD_NAME_GET, {
|
|
47419
|
+
signalId: this.signalId,
|
|
47420
|
+
bucketName: this.bucketName,
|
|
47421
|
+
});
|
|
47422
|
+
return this._value;
|
|
47423
|
+
}
|
|
47424
|
+
/** Releases resources held by this instance. */
|
|
47425
|
+
async dispose() {
|
|
47426
|
+
backtest.loggerService.debug(STATE_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
47427
|
+
signalId: this.signalId,
|
|
47428
|
+
bucketName: this.bucketName,
|
|
47429
|
+
});
|
|
47430
|
+
}
|
|
47431
|
+
}
|
|
47432
|
+
/**
|
|
47433
|
+
* No-op state instance that discards all writes.
|
|
47434
|
+
* Used for disabling state in tests or dry-run scenarios.
|
|
47435
|
+
*
|
|
47436
|
+
* Useful when replaying historical candles without needing to accumulate
|
|
47437
|
+
* peakPercent/minutesOpen — the capitulation rule is simply never triggered.
|
|
47438
|
+
*/
|
|
47439
|
+
class StateDummyInstance {
|
|
47440
|
+
constructor(initialValue, signalId, bucketName) {
|
|
47441
|
+
this.initialValue = initialValue;
|
|
47442
|
+
this.signalId = signalId;
|
|
47443
|
+
this.bucketName = bucketName;
|
|
47444
|
+
/**
|
|
47445
|
+
* No-op initialization.
|
|
47446
|
+
* @returns Promise that resolves immediately
|
|
47447
|
+
*/
|
|
47448
|
+
this.waitForInit = functoolsKit.singleshot(async (_initial) => {
|
|
47449
|
+
});
|
|
47450
|
+
}
|
|
47451
|
+
/**
|
|
47452
|
+
* No-op read - always returns initialValue.
|
|
47453
|
+
* @returns initialValue
|
|
47454
|
+
*/
|
|
47455
|
+
async getState() {
|
|
47456
|
+
return this.initialValue;
|
|
47457
|
+
}
|
|
47458
|
+
/**
|
|
47459
|
+
* No-op write - discards the value and returns initialValue.
|
|
47460
|
+
* @returns initialValue
|
|
47461
|
+
*/
|
|
47462
|
+
async setState(_dispatch) {
|
|
47463
|
+
return this.initialValue;
|
|
47464
|
+
}
|
|
47465
|
+
/** No-op. */
|
|
47466
|
+
async dispose() {
|
|
47467
|
+
}
|
|
47468
|
+
}
|
|
47469
|
+
/**
|
|
47470
|
+
* File-system backed state instance.
|
|
47471
|
+
* Data is persisted atomically to disk via PersistStateAdapter.
|
|
47472
|
+
* State is restored from disk on waitForInit.
|
|
47473
|
+
*
|
|
47474
|
+
* Features:
|
|
47475
|
+
* - Crash-safe atomic file writes
|
|
47476
|
+
* - Functional dispatch support
|
|
47477
|
+
* - Scoped per (signalId, bucketName) pair
|
|
47478
|
+
*
|
|
47479
|
+
* Use in live trading to survive process restarts mid-trade.
|
|
47480
|
+
* Preserves peakPercent and minutesOpen so the capitulation rule
|
|
47481
|
+
* (exit if peak < threshold after N minutes) continues correctly after a crash.
|
|
47482
|
+
*/
|
|
47483
|
+
class StatePersistInstance {
|
|
47484
|
+
constructor(initialValue, signalId, bucketName) {
|
|
47485
|
+
this.initialValue = initialValue;
|
|
47486
|
+
this.signalId = signalId;
|
|
47487
|
+
this.bucketName = bucketName;
|
|
47488
|
+
/**
|
|
47489
|
+
* Initialize persistence storage and restore state from disk.
|
|
47490
|
+
* @param initial - Whether this is the first initialization
|
|
47491
|
+
*/
|
|
47492
|
+
this.waitForInit = functoolsKit.singleshot(async (initial) => {
|
|
47493
|
+
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_WAIT_FOR_INIT, {
|
|
47494
|
+
signalId: this.signalId,
|
|
47495
|
+
bucketName: this.bucketName,
|
|
47496
|
+
initial,
|
|
47497
|
+
});
|
|
47498
|
+
await PersistStateAdapter.waitForInit(this.signalId, this.bucketName, initial);
|
|
47499
|
+
const data = await PersistStateAdapter.readStateData(this.signalId, this.bucketName);
|
|
47500
|
+
if (data) {
|
|
47501
|
+
this._value = data.data;
|
|
47502
|
+
return;
|
|
47503
|
+
}
|
|
47504
|
+
this._value = this.initialValue;
|
|
47505
|
+
});
|
|
47506
|
+
/**
|
|
47507
|
+
* Update state and persist to disk atomically.
|
|
47508
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47509
|
+
* @returns Updated state value
|
|
47510
|
+
*/
|
|
47511
|
+
this.setState = functoolsKit.queued(async (dispatch) => {
|
|
47512
|
+
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_SET, {
|
|
47513
|
+
signalId: this.signalId,
|
|
47514
|
+
bucketName: this.bucketName,
|
|
47515
|
+
});
|
|
47516
|
+
if (typeof dispatch === "function") {
|
|
47517
|
+
this._value = await dispatch(this._value);
|
|
47518
|
+
}
|
|
47519
|
+
else {
|
|
47520
|
+
this._value = dispatch;
|
|
47521
|
+
}
|
|
47522
|
+
const id = CREATE_KEY_FN$6(this.signalId, this.bucketName);
|
|
47523
|
+
await PersistStateAdapter.writeStateData({ id, data: this._value }, this.signalId, this.bucketName);
|
|
47524
|
+
return this._value;
|
|
47525
|
+
});
|
|
47526
|
+
}
|
|
47527
|
+
/**
|
|
47528
|
+
* Read the current persisted state value.
|
|
47529
|
+
* @returns Current state value
|
|
47530
|
+
*/
|
|
47531
|
+
async getState() {
|
|
47532
|
+
backtest.loggerService.debug(STATE_PERSIST_INSTANCE_METHOD_NAME_GET, {
|
|
47533
|
+
signalId: this.signalId,
|
|
47534
|
+
bucketName: this.bucketName,
|
|
47535
|
+
});
|
|
47536
|
+
return this._value;
|
|
47537
|
+
}
|
|
47538
|
+
/** Releases resources held by this instance. */
|
|
47539
|
+
async dispose() {
|
|
47540
|
+
backtest.loggerService.debug(STATE_LIVE_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
47541
|
+
signalId: this.signalId,
|
|
47542
|
+
bucketName: this.bucketName,
|
|
47543
|
+
});
|
|
47544
|
+
await PersistStateAdapter.dispose(this.signalId, this.bucketName);
|
|
47545
|
+
}
|
|
47546
|
+
}
|
|
47547
|
+
/**
|
|
47548
|
+
* Backtest state adapter with pluggable storage backend.
|
|
47549
|
+
*
|
|
47550
|
+
* Features:
|
|
47551
|
+
* - Adapter pattern for swappable state instance implementations
|
|
47552
|
+
* - Default backend: StateLocalInstance (in-memory, no disk persistence)
|
|
47553
|
+
* - Alternative backends: StatePersistInstance, StateDummyInstance
|
|
47554
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useStateAdapter()
|
|
47555
|
+
* - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from StateAdapter
|
|
47556
|
+
*
|
|
47557
|
+
* Primary use case — LLM-driven capitulation rule:
|
|
47558
|
+
* Profitable trades endure -0.5–2.5% drawdown and still reach peak 2–3%+.
|
|
47559
|
+
* SL trades never go positive (Feb25) or show peak < 0.15% (Feb08, Feb13).
|
|
47560
|
+
* Rule: if position open >= N minutes and peakPercent < threshold (e.g. 0.3%),
|
|
47561
|
+
* the LLM thesis was not confirmed by market — exit immediately.
|
|
47562
|
+
* State tracks `{ peakPercent, minutesOpen }` per signal across onActivePing ticks.
|
|
47563
|
+
*/
|
|
47564
|
+
class StateBacktestAdapter {
|
|
47565
|
+
constructor() {
|
|
47566
|
+
this.StateFactory = StateLocalInstance;
|
|
47567
|
+
this.getInstance = functoolsKit.memoize(([signalId, bucketName]) => CREATE_KEY_FN$6(signalId, bucketName), (signalId, bucketName, initialValue) => Reflect.construct(this.StateFactory, [initialValue, signalId, bucketName]));
|
|
47568
|
+
/**
|
|
47569
|
+
* Disposes all memoized instances for the given signalId.
|
|
47570
|
+
* Called by StateAdapter when a signal is cancelled or closed.
|
|
47571
|
+
* @param signalId - Signal identifier to dispose
|
|
47572
|
+
*/
|
|
47573
|
+
this.disposeSignal = (signalId) => {
|
|
47574
|
+
const prefix = CREATE_KEY_FN$6(signalId, "");
|
|
47575
|
+
for (const key of this.getInstance.keys()) {
|
|
47576
|
+
if (key.startsWith(prefix)) {
|
|
47577
|
+
const instance = this.getInstance.get(key);
|
|
47578
|
+
instance && instance.dispose();
|
|
47579
|
+
this.getInstance.clear(key);
|
|
47580
|
+
}
|
|
47581
|
+
}
|
|
47582
|
+
};
|
|
47583
|
+
/**
|
|
47584
|
+
* Read the current state value for a signal.
|
|
47585
|
+
* @param dto.signalId - Signal identifier
|
|
47586
|
+
* @param dto.bucketName - Bucket name
|
|
47587
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47588
|
+
* @returns Current state value
|
|
47589
|
+
*/
|
|
47590
|
+
this.getState = async (dto) => {
|
|
47591
|
+
backtest.loggerService.debug(STATE_BACKTEST_ADAPTER_METHOD_NAME_GET, {
|
|
47592
|
+
signalId: dto.signalId,
|
|
47593
|
+
bucketName: dto.bucketName,
|
|
47594
|
+
});
|
|
47595
|
+
const key = CREATE_KEY_FN$6(dto.signalId, dto.bucketName);
|
|
47596
|
+
const isInitial = !this.getInstance.has(key);
|
|
47597
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
47598
|
+
await instance.waitForInit(isInitial);
|
|
47599
|
+
return await instance.getState();
|
|
47600
|
+
};
|
|
47601
|
+
/**
|
|
47602
|
+
* Update the state value for a signal.
|
|
47603
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47604
|
+
* @param dto.signalId - Signal identifier
|
|
47605
|
+
* @param dto.bucketName - Bucket name
|
|
47606
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47607
|
+
* @returns Updated state value
|
|
47608
|
+
*/
|
|
47609
|
+
this.setState = async (dispatch, dto) => {
|
|
47610
|
+
backtest.loggerService.debug(STATE_BACKTEST_ADAPTER_METHOD_NAME_SET, {
|
|
47611
|
+
signalId: dto.signalId,
|
|
47612
|
+
bucketName: dto.bucketName,
|
|
47613
|
+
});
|
|
47614
|
+
const key = CREATE_KEY_FN$6(dto.signalId, dto.bucketName);
|
|
47615
|
+
const isInitial = !this.getInstance.has(key);
|
|
47616
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
47617
|
+
await instance.waitForInit(isInitial);
|
|
47618
|
+
return await instance.setState(dispatch);
|
|
47619
|
+
};
|
|
47620
|
+
/**
|
|
47621
|
+
* Switches to in-memory adapter (default).
|
|
47622
|
+
* All data lives in process memory only.
|
|
47623
|
+
*/
|
|
47624
|
+
this.useLocal = () => {
|
|
47625
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
47626
|
+
this.StateFactory = StateLocalInstance;
|
|
47627
|
+
};
|
|
47628
|
+
/**
|
|
47629
|
+
* Switches to file-system backed adapter.
|
|
47630
|
+
* Data is persisted to disk via PersistStateAdapter.
|
|
47631
|
+
*/
|
|
47632
|
+
this.usePersist = () => {
|
|
47633
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
47634
|
+
this.StateFactory = StatePersistInstance;
|
|
47635
|
+
};
|
|
47636
|
+
/**
|
|
47637
|
+
* Switches to dummy adapter that discards all writes.
|
|
47638
|
+
*/
|
|
47639
|
+
this.useDummy = () => {
|
|
47640
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
47641
|
+
this.StateFactory = StateDummyInstance;
|
|
47642
|
+
};
|
|
47643
|
+
/**
|
|
47644
|
+
* Switches to a custom state adapter implementation.
|
|
47645
|
+
* @param Ctor - Constructor for the custom state instance
|
|
47646
|
+
*/
|
|
47647
|
+
this.useStateAdapter = (Ctor) => {
|
|
47648
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER);
|
|
47649
|
+
this.StateFactory = Ctor;
|
|
47650
|
+
};
|
|
47651
|
+
/**
|
|
47652
|
+
* Clears the memoized instance cache.
|
|
47653
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
47654
|
+
* so new instances are created with the updated base path.
|
|
47655
|
+
*/
|
|
47656
|
+
this.clear = () => {
|
|
47657
|
+
backtest.loggerService.info(STATE_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
|
|
47658
|
+
this.getInstance.clear();
|
|
47659
|
+
};
|
|
47660
|
+
}
|
|
47661
|
+
}
|
|
47662
|
+
/**
|
|
47663
|
+
* Live trading state adapter with pluggable storage backend.
|
|
47664
|
+
*
|
|
47665
|
+
* Features:
|
|
47666
|
+
* - Adapter pattern for swappable state instance implementations
|
|
47667
|
+
* - Default backend: StatePersistInstance (file-system backed, survives restarts)
|
|
47668
|
+
* - Alternative backends: StateLocalInstance, StateDummyInstance
|
|
47669
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useStateAdapter()
|
|
47670
|
+
* - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from StateAdapter
|
|
47671
|
+
*
|
|
47672
|
+
* Primary use case — LLM-driven capitulation rule:
|
|
47673
|
+
* Profitable trades endure -0.5–2.5% drawdown and still reach peak 2–3%+.
|
|
47674
|
+
* SL trades never go positive (Feb25) or show peak < 0.15% (Feb08, Feb13).
|
|
47675
|
+
* Rule: if position open >= N minutes and peakPercent < threshold (e.g. 0.3%),
|
|
47676
|
+
* the LLM thesis was not confirmed by market — exit immediately.
|
|
47677
|
+
* State persists `{ peakPercent, minutesOpen }` per signal across process restarts.
|
|
47678
|
+
*/
|
|
47679
|
+
class StateLiveAdapter {
|
|
47680
|
+
constructor() {
|
|
47681
|
+
this.StateFactory = StatePersistInstance;
|
|
47682
|
+
this.getInstance = functoolsKit.memoize(([signalId, bucketName]) => CREATE_KEY_FN$6(signalId, bucketName), (signalId, bucketName, initialValue) => Reflect.construct(this.StateFactory, [initialValue, signalId, bucketName]));
|
|
47683
|
+
/**
|
|
47684
|
+
* Disposes all memoized instances for the given signalId.
|
|
47685
|
+
* Called by StateAdapter when a signal is cancelled or closed.
|
|
47686
|
+
* @param signalId - Signal identifier to dispose
|
|
47687
|
+
*/
|
|
47688
|
+
this.disposeSignal = (signalId) => {
|
|
47689
|
+
const prefix = CREATE_KEY_FN$6(signalId, "");
|
|
47690
|
+
for (const key of this.getInstance.keys()) {
|
|
47691
|
+
if (key.startsWith(prefix)) {
|
|
47692
|
+
const instance = this.getInstance.get(key);
|
|
47693
|
+
instance && instance.dispose();
|
|
47694
|
+
this.getInstance.clear(key);
|
|
47695
|
+
}
|
|
47696
|
+
}
|
|
47697
|
+
};
|
|
47698
|
+
/**
|
|
47699
|
+
* Read the current state value for a signal.
|
|
47700
|
+
* @param dto.signalId - Signal identifier
|
|
47701
|
+
* @param dto.bucketName - Bucket name
|
|
47702
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47703
|
+
* @returns Current state value
|
|
47704
|
+
*/
|
|
47705
|
+
this.getState = async (dto) => {
|
|
47706
|
+
backtest.loggerService.debug(STATE_LIVE_ADAPTER_METHOD_NAME_GET, {
|
|
47707
|
+
signalId: dto.signalId,
|
|
47708
|
+
bucketName: dto.bucketName,
|
|
47709
|
+
});
|
|
47710
|
+
const key = CREATE_KEY_FN$6(dto.signalId, dto.bucketName);
|
|
47711
|
+
const isInitial = !this.getInstance.has(key);
|
|
47712
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
47713
|
+
await instance.waitForInit(isInitial);
|
|
47714
|
+
return await instance.getState();
|
|
47715
|
+
};
|
|
47716
|
+
/**
|
|
47717
|
+
* Update the state value for a signal.
|
|
47718
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47719
|
+
* @param dto.signalId - Signal identifier
|
|
47720
|
+
* @param dto.bucketName - Bucket name
|
|
47721
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47722
|
+
* @returns Updated state value
|
|
47723
|
+
*/
|
|
47724
|
+
this.setState = async (dispatch, dto) => {
|
|
47725
|
+
backtest.loggerService.debug(STATE_LIVE_ADAPTER_METHOD_NAME_SET, {
|
|
47726
|
+
signalId: dto.signalId,
|
|
47727
|
+
bucketName: dto.bucketName,
|
|
47728
|
+
});
|
|
47729
|
+
const key = CREATE_KEY_FN$6(dto.signalId, dto.bucketName);
|
|
47730
|
+
const isInitial = !this.getInstance.has(key);
|
|
47731
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName, dto.initialValue);
|
|
47732
|
+
await instance.waitForInit(isInitial);
|
|
47733
|
+
return await instance.setState(dispatch);
|
|
47734
|
+
};
|
|
47735
|
+
/**
|
|
47736
|
+
* Switches to in-memory adapter.
|
|
47737
|
+
* All data lives in process memory only.
|
|
47738
|
+
*/
|
|
47739
|
+
this.useLocal = () => {
|
|
47740
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
47741
|
+
this.StateFactory = StateLocalInstance;
|
|
47742
|
+
};
|
|
47743
|
+
/**
|
|
47744
|
+
* Switches to file-system backed adapter (default).
|
|
47745
|
+
* Data is persisted to disk via PersistStateAdapter.
|
|
47746
|
+
*/
|
|
47747
|
+
this.usePersist = () => {
|
|
47748
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
47749
|
+
this.StateFactory = StatePersistInstance;
|
|
47750
|
+
};
|
|
47751
|
+
/**
|
|
47752
|
+
* Switches to dummy adapter that discards all writes.
|
|
47753
|
+
*/
|
|
47754
|
+
this.useDummy = () => {
|
|
47755
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
47756
|
+
this.StateFactory = StateDummyInstance;
|
|
47757
|
+
};
|
|
47758
|
+
/**
|
|
47759
|
+
* Switches to a custom state adapter implementation.
|
|
47760
|
+
* @param Ctor - Constructor for the custom state instance
|
|
47761
|
+
*/
|
|
47762
|
+
this.useStateAdapter = (Ctor) => {
|
|
47763
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_USE_STATE_ADAPTER);
|
|
47764
|
+
this.StateFactory = Ctor;
|
|
47765
|
+
};
|
|
47766
|
+
/**
|
|
47767
|
+
* Clears the memoized instance cache.
|
|
47768
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
47769
|
+
* so new instances are created with the updated base path.
|
|
47770
|
+
*/
|
|
47771
|
+
this.clear = () => {
|
|
47772
|
+
backtest.loggerService.info(STATE_LIVE_ADAPTER_METHOD_NAME_CLEAR);
|
|
47773
|
+
this.getInstance.clear();
|
|
47774
|
+
};
|
|
47775
|
+
}
|
|
47776
|
+
}
|
|
47777
|
+
/**
|
|
47778
|
+
* Main state adapter that manages both backtest and live state storage.
|
|
47779
|
+
*
|
|
47780
|
+
* Features:
|
|
47781
|
+
* - Subscribes to signal lifecycle events (cancelled/closed) to dispose stale instances
|
|
47782
|
+
* - Routes all operations to StateBacktest or StateLive based on dto.backtest
|
|
47783
|
+
* - Singleshot enable pattern prevents duplicate subscriptions
|
|
47784
|
+
* - Cleanup function for proper unsubscription
|
|
47785
|
+
*/
|
|
47786
|
+
class StateAdapter {
|
|
47787
|
+
constructor() {
|
|
47788
|
+
/**
|
|
47789
|
+
* Enables state storage by subscribing to signal lifecycle events.
|
|
47790
|
+
* Clears memoized instances in StateBacktest and StateLive when a signal
|
|
47791
|
+
* is cancelled or closed, preventing stale instances from accumulating.
|
|
47792
|
+
* Uses singleshot to ensure one-time subscription.
|
|
47793
|
+
*
|
|
47794
|
+
* @returns Cleanup function that unsubscribes from all emitters
|
|
47795
|
+
*/
|
|
47796
|
+
this.enable = functoolsKit.singleshot(() => {
|
|
47797
|
+
backtest.loggerService.info(STATE_ADAPTER_METHOD_NAME_ENABLE);
|
|
47798
|
+
const unCancel = signalEmitter
|
|
47799
|
+
.filter(({ action }) => action === "cancelled")
|
|
47800
|
+
.connect(({ signal }) => {
|
|
47801
|
+
StateBacktest.disposeSignal(signal.id);
|
|
47802
|
+
StateLive.disposeSignal(signal.id);
|
|
47803
|
+
});
|
|
47804
|
+
const unClose = signalEmitter
|
|
47805
|
+
.filter(({ action }) => action === "closed")
|
|
47806
|
+
.connect(({ signal }) => {
|
|
47807
|
+
StateBacktest.disposeSignal(signal.id);
|
|
47808
|
+
StateLive.disposeSignal(signal.id);
|
|
47809
|
+
});
|
|
47810
|
+
return functoolsKit.compose(() => unCancel(), () => unClose(), () => this.enable.clear());
|
|
47811
|
+
});
|
|
47812
|
+
/**
|
|
47813
|
+
* Disables state storage by unsubscribing from signal lifecycle events.
|
|
47814
|
+
* Safe to call multiple times.
|
|
47815
|
+
*/
|
|
47816
|
+
this.disable = () => {
|
|
47817
|
+
backtest.loggerService.info(STATE_ADAPTER_METHOD_NAME_DISABLE);
|
|
47818
|
+
if (this.enable.hasValue()) {
|
|
47819
|
+
const lastSubscription = this.enable();
|
|
47820
|
+
lastSubscription();
|
|
47821
|
+
}
|
|
47822
|
+
};
|
|
47823
|
+
/**
|
|
47824
|
+
* Read the current state value for a signal.
|
|
47825
|
+
* Routes to StateBacktest or StateLive based on dto.backtest.
|
|
47826
|
+
* @param dto.signalId - Signal identifier
|
|
47827
|
+
* @param dto.bucketName - Bucket name
|
|
47828
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47829
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47830
|
+
* @returns Current state value
|
|
47831
|
+
* @throws Error if adapter is not enabled
|
|
47832
|
+
*/
|
|
47833
|
+
this.getState = async (dto) => {
|
|
47834
|
+
if (!this.enable.hasValue()) {
|
|
47835
|
+
throw new Error("StateAdapter is not enabled. Call enable() first.");
|
|
47836
|
+
}
|
|
47837
|
+
backtest.loggerService.debug(STATE_ADAPTER_METHOD_NAME_GET, {
|
|
47838
|
+
signalId: dto.signalId,
|
|
47839
|
+
bucketName: dto.bucketName,
|
|
47840
|
+
backtest: dto.backtest,
|
|
47841
|
+
});
|
|
47842
|
+
if (dto.backtest) {
|
|
47843
|
+
return await StateBacktest.getState(dto);
|
|
47844
|
+
}
|
|
47845
|
+
return await StateLive.getState(dto);
|
|
47846
|
+
};
|
|
47847
|
+
/**
|
|
47848
|
+
* Update the state value for a signal.
|
|
47849
|
+
* Routes to StateBacktest or StateLive based on dto.backtest.
|
|
47850
|
+
* @param dispatch - New value or updater function receiving current value
|
|
47851
|
+
* @param dto.signalId - Signal identifier
|
|
47852
|
+
* @param dto.bucketName - Bucket name
|
|
47853
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47854
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47855
|
+
* @returns Updated state value
|
|
47856
|
+
* @throws Error if adapter is not enabled
|
|
47857
|
+
*/
|
|
47858
|
+
this.setState = async (dispatch, dto) => {
|
|
47859
|
+
if (!this.enable.hasValue()) {
|
|
47860
|
+
throw new Error("StateAdapter is not enabled. Call enable() first.");
|
|
47861
|
+
}
|
|
47862
|
+
backtest.loggerService.debug(STATE_ADAPTER_METHOD_NAME_SET, {
|
|
47863
|
+
signalId: dto.signalId,
|
|
47864
|
+
bucketName: dto.bucketName,
|
|
47865
|
+
backtest: dto.backtest,
|
|
47866
|
+
});
|
|
47867
|
+
if (dto.backtest) {
|
|
47868
|
+
return await StateBacktest.setState(dispatch, dto);
|
|
47869
|
+
}
|
|
47870
|
+
return await StateLive.setState(dispatch, dto);
|
|
47871
|
+
};
|
|
47872
|
+
}
|
|
47873
|
+
}
|
|
47874
|
+
/**
|
|
47875
|
+
* Global singleton instance of StateAdapter.
|
|
47876
|
+
* Provides unified state management for backtest and live trading.
|
|
47877
|
+
*/
|
|
47878
|
+
const State = new StateAdapter();
|
|
47879
|
+
/**
|
|
47880
|
+
* Global singleton instance of StateLiveAdapter.
|
|
47881
|
+
* Provides live trading state storage with pluggable backends.
|
|
47882
|
+
*/
|
|
47883
|
+
const StateLive = new StateLiveAdapter();
|
|
47884
|
+
/**
|
|
47885
|
+
* Global singleton instance of StateBacktestAdapter.
|
|
47886
|
+
* Provides backtest state storage with pluggable backends.
|
|
47887
|
+
*/
|
|
47888
|
+
const StateBacktest = new StateBacktestAdapter();
|
|
47889
|
+
|
|
47890
|
+
const GET_SIGNAL_STATE_METHOD_NAME = "signal.getSignalState";
|
|
47891
|
+
const SET_SIGNAL_STATE_METHOD_NAME = "signal.setSignalState";
|
|
47063
47892
|
const GET_LATEST_SIGNAL_METHOD_NAME = "signal.getLatestSignal";
|
|
47064
47893
|
const GET_MINUTES_SINCE_LATEST_SIGNAL_CREATED_METHOD_NAME = "signal.getMinutesSinceLatestSignalCreated";
|
|
47065
47894
|
/**
|
|
@@ -47135,6 +47964,777 @@ async function getMinutesSinceLatestSignalCreated(symbol) {
|
|
|
47135
47964
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47136
47965
|
return await Recent.getMinutesSinceLatestSignalCreated(symbol, { exchangeName, frameName, strategyName });
|
|
47137
47966
|
}
|
|
47967
|
+
/**
|
|
47968
|
+
* Reads the state value scoped to the current active signal.
|
|
47969
|
+
*
|
|
47970
|
+
* Resolves the active pending signal automatically from execution context.
|
|
47971
|
+
* If no pending signal exists, logs a warning and returns the initialValue.
|
|
47972
|
+
*
|
|
47973
|
+
* Automatically detects backtest/live mode from execution context.
|
|
47974
|
+
*
|
|
47975
|
+
* Intended for LLM-driven capitulation strategies that accumulate per-trade
|
|
47976
|
+
* metrics (e.g. peakPercent, minutesOpen) across onActivePing ticks.
|
|
47977
|
+
* Profitable trades endure -0.5–2.5% drawdown and reach peak 2–3%+.
|
|
47978
|
+
* SL trades show peak < 0.15% (Feb08, Feb13) or never go positive (Feb25).
|
|
47979
|
+
* Rule: if minutesOpen >= N and peakPercent < threshold (e.g. 0.3%) — exit.
|
|
47980
|
+
*
|
|
47981
|
+
* @param dto.bucketName - State bucket name
|
|
47982
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
47983
|
+
* @returns Promise resolving to current state value, or initialValue if no signal
|
|
47984
|
+
*
|
|
47985
|
+
* @deprecated Better use `createSignalState().getState` with codestyle native syntax
|
|
47986
|
+
*
|
|
47987
|
+
* @example
|
|
47988
|
+
* ```typescript
|
|
47989
|
+
* import { getSignalState } from "backtest-kit";
|
|
47990
|
+
*
|
|
47991
|
+
* const { peakPercent, minutesOpen } = await getSignalState({
|
|
47992
|
+
* bucketName: "trade",
|
|
47993
|
+
* initialValue: { peakPercent: 0, minutesOpen: 0 },
|
|
47994
|
+
* });
|
|
47995
|
+
* if (minutesOpen >= 15 && peakPercent < 0.3) {
|
|
47996
|
+
* await commitMarketClose(symbol); // capitulate — LLM thesis not confirmed
|
|
47997
|
+
* }
|
|
47998
|
+
* ```
|
|
47999
|
+
*/
|
|
48000
|
+
async function getSignalState(dto) {
|
|
48001
|
+
const { bucketName, initialValue } = dto;
|
|
48002
|
+
backtest.loggerService.info(GET_SIGNAL_STATE_METHOD_NAME, { bucketName });
|
|
48003
|
+
if (!ExecutionContextService.hasContext()) {
|
|
48004
|
+
throw new Error("getSignalState requires an execution context");
|
|
48005
|
+
}
|
|
48006
|
+
if (!MethodContextService.hasContext()) {
|
|
48007
|
+
throw new Error("getSignalState requires a method context");
|
|
48008
|
+
}
|
|
48009
|
+
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48010
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48011
|
+
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48012
|
+
let signal;
|
|
48013
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
48014
|
+
return await State.getState({
|
|
48015
|
+
signalId: signal.id,
|
|
48016
|
+
bucketName,
|
|
48017
|
+
initialValue,
|
|
48018
|
+
backtest: isBacktest,
|
|
48019
|
+
});
|
|
48020
|
+
}
|
|
48021
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
48022
|
+
return await State.getState({
|
|
48023
|
+
signalId: signal.id,
|
|
48024
|
+
bucketName,
|
|
48025
|
+
initialValue,
|
|
48026
|
+
backtest: isBacktest,
|
|
48027
|
+
});
|
|
48028
|
+
}
|
|
48029
|
+
throw new Error(`getSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
48030
|
+
}
|
|
48031
|
+
/**
|
|
48032
|
+
* Updates the state value scoped to the current active signal.
|
|
48033
|
+
*
|
|
48034
|
+
* Resolves the active pending signal automatically from execution context.
|
|
48035
|
+
* If no pending signal exists, logs a warning and returns without writing.
|
|
48036
|
+
*
|
|
48037
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48038
|
+
*
|
|
48039
|
+
* Intended for LLM-driven capitulation strategies that accumulate per-trade
|
|
48040
|
+
* metrics (e.g. peakPercent, minutesOpen) across onActivePing ticks.
|
|
48041
|
+
* Profitable trades endure -0.5–2.5% drawdown and reach peak 2–3%+.
|
|
48042
|
+
* SL trades show peak < 0.15% (Feb08, Feb13) or never go positive (Feb25).
|
|
48043
|
+
* Rule: if minutesOpen >= N and peakPercent < threshold (e.g. 0.3%) — exit.
|
|
48044
|
+
*
|
|
48045
|
+
* @param dto.bucketName - State bucket name
|
|
48046
|
+
* @param dto.initialValue - Default value when no persisted state exists
|
|
48047
|
+
* @param dto.dispatch - New value or updater function receiving current value
|
|
48048
|
+
* @returns Promise resolving to updated state value, or initialValue if no signal
|
|
48049
|
+
*
|
|
48050
|
+
* @deprecated Better use `createSignalState().setState` with codestyle native syntax
|
|
48051
|
+
*
|
|
48052
|
+
* @example
|
|
48053
|
+
* ```typescript
|
|
48054
|
+
* import { setSignalState } from "backtest-kit";
|
|
48055
|
+
*
|
|
48056
|
+
* await setSignalState(
|
|
48057
|
+
* dispatch: (s) => ({
|
|
48058
|
+
* peakPercent: Math.max(s.peakPercent, currentUnrealisedPercent),
|
|
48059
|
+
* minutesOpen: s.minutesOpen + 1,
|
|
48060
|
+
* }),
|
|
48061
|
+
* {
|
|
48062
|
+
* bucketName: "trade",
|
|
48063
|
+
* initialValue: { peakPercent: 0, minutesOpen: 0 },
|
|
48064
|
+
* }
|
|
48065
|
+
* );
|
|
48066
|
+
* ```
|
|
48067
|
+
*/
|
|
48068
|
+
async function setSignalState(dispatch, dto) {
|
|
48069
|
+
const { bucketName, initialValue } = dto;
|
|
48070
|
+
backtest.loggerService.info(SET_SIGNAL_STATE_METHOD_NAME, { bucketName });
|
|
48071
|
+
if (!ExecutionContextService.hasContext()) {
|
|
48072
|
+
throw new Error("setSignalState requires an execution context");
|
|
48073
|
+
}
|
|
48074
|
+
if (!MethodContextService.hasContext()) {
|
|
48075
|
+
throw new Error("setSignalState requires a method context");
|
|
48076
|
+
}
|
|
48077
|
+
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48078
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48079
|
+
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48080
|
+
let signal;
|
|
48081
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
48082
|
+
return await State.setState(dispatch, {
|
|
48083
|
+
signalId: signal.id,
|
|
48084
|
+
bucketName,
|
|
48085
|
+
initialValue,
|
|
48086
|
+
backtest: isBacktest,
|
|
48087
|
+
});
|
|
48088
|
+
}
|
|
48089
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
48090
|
+
return await State.setState(dispatch, {
|
|
48091
|
+
signalId: signal.id,
|
|
48092
|
+
bucketName,
|
|
48093
|
+
initialValue,
|
|
48094
|
+
backtest: isBacktest,
|
|
48095
|
+
});
|
|
48096
|
+
}
|
|
48097
|
+
throw new Error(`setSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
48098
|
+
}
|
|
48099
|
+
|
|
48100
|
+
const CREATE_KEY_FN$5 = (symbol, strategyName, exchangeName, frameName, backtest) => {
|
|
48101
|
+
const parts = [symbol, strategyName, exchangeName];
|
|
48102
|
+
if (frameName)
|
|
48103
|
+
parts.push(frameName);
|
|
48104
|
+
parts.push(backtest ? "backtest" : "live");
|
|
48105
|
+
return parts.join(":");
|
|
48106
|
+
};
|
|
48107
|
+
const SESSION_LOCAL_INSTANCE_METHOD_NAME_GET = "SessionLocalInstance.getData";
|
|
48108
|
+
const SESSION_LOCAL_INSTANCE_METHOD_NAME_SET = "SessionLocalInstance.setData";
|
|
48109
|
+
const SESSION_PERSIST_INSTANCE_METHOD_NAME_WAIT_FOR_INIT = "SessionPersistInstance.waitForInit";
|
|
48110
|
+
const SESSION_PERSIST_INSTANCE_METHOD_NAME_GET = "SessionPersistInstance.getData";
|
|
48111
|
+
const SESSION_PERSIST_INSTANCE_METHOD_NAME_SET = "SessionPersistInstance.setData";
|
|
48112
|
+
const SESSION_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE = "SessionBacktestAdapter.dispose";
|
|
48113
|
+
const SESSION_BACKTEST_ADAPTER_METHOD_NAME_GET = "SessionBacktestAdapter.getData";
|
|
48114
|
+
const SESSION_BACKTEST_ADAPTER_METHOD_NAME_SET = "SessionBacktestAdapter.setData";
|
|
48115
|
+
const SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL = "SessionBacktestAdapter.useLocal";
|
|
48116
|
+
const SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST = "SessionBacktestAdapter.usePersist";
|
|
48117
|
+
const SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY = "SessionBacktestAdapter.useDummy";
|
|
48118
|
+
const SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_SESSION_ADAPTER = "SessionBacktestAdapter.useSessionAdapter";
|
|
48119
|
+
const SESSION_BACKTEST_ADAPTER_METHOD_NAME_CLEAR = "SessionBacktestAdapter.clear";
|
|
48120
|
+
const SESSION_LIVE_ADAPTER_METHOD_NAME_DISPOSE = "SessionLiveAdapter.dispose";
|
|
48121
|
+
const SESSION_LIVE_ADAPTER_METHOD_NAME_GET = "SessionLiveAdapter.getData";
|
|
48122
|
+
const SESSION_LIVE_ADAPTER_METHOD_NAME_SET = "SessionLiveAdapter.setData";
|
|
48123
|
+
const SESSION_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL = "SessionLiveAdapter.useLocal";
|
|
48124
|
+
const SESSION_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST = "SessionLiveAdapter.usePersist";
|
|
48125
|
+
const SESSION_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY = "SessionLiveAdapter.useDummy";
|
|
48126
|
+
const SESSION_LIVE_ADAPTER_METHOD_NAME_USE_SESSION_ADAPTER = "SessionLiveAdapter.useSessionAdapter";
|
|
48127
|
+
const SESSION_LIVE_ADAPTER_METHOD_NAME_CLEAR = "SessionLiveAdapter.clear";
|
|
48128
|
+
const SESSION_ADAPTER_METHOD_NAME_GET = "SessionAdapter.getData";
|
|
48129
|
+
const SESSION_ADAPTER_METHOD_NAME_SET = "SessionAdapter.setData";
|
|
48130
|
+
/**
|
|
48131
|
+
* In-process session instance backed by a plain object reference.
|
|
48132
|
+
* All data lives in process memory only — no disk persistence.
|
|
48133
|
+
*
|
|
48134
|
+
* Features:
|
|
48135
|
+
* - Mutable in-memory session data
|
|
48136
|
+
* - Scoped per (symbol, strategyName, exchangeName, frameName) tuple
|
|
48137
|
+
*
|
|
48138
|
+
* Use for backtesting and unit tests where persistence between runs is not needed.
|
|
48139
|
+
*/
|
|
48140
|
+
class SessionLocalInstance {
|
|
48141
|
+
constructor(symbol, strategyName, exchangeName, frameName, backtest$1) {
|
|
48142
|
+
this.symbol = symbol;
|
|
48143
|
+
this.strategyName = strategyName;
|
|
48144
|
+
this.exchangeName = exchangeName;
|
|
48145
|
+
this.frameName = frameName;
|
|
48146
|
+
this.backtest = backtest$1;
|
|
48147
|
+
this._data = null;
|
|
48148
|
+
/**
|
|
48149
|
+
* Initializes _data to null — local session needs no async setup.
|
|
48150
|
+
* @returns Promise that resolves immediately
|
|
48151
|
+
*/
|
|
48152
|
+
this.waitForInit = functoolsKit.singleshot(async (_initial) => {
|
|
48153
|
+
this._data = null;
|
|
48154
|
+
});
|
|
48155
|
+
/**
|
|
48156
|
+
* Read the current in-memory session value.
|
|
48157
|
+
* @returns Current session value, or null if not set
|
|
48158
|
+
*/
|
|
48159
|
+
this.getData = async () => {
|
|
48160
|
+
backtest.loggerService.debug(SESSION_LOCAL_INSTANCE_METHOD_NAME_GET, {
|
|
48161
|
+
strategyName: this.strategyName,
|
|
48162
|
+
exchangeName: this.exchangeName,
|
|
48163
|
+
frameName: this.frameName,
|
|
48164
|
+
});
|
|
48165
|
+
return this._data;
|
|
48166
|
+
};
|
|
48167
|
+
/**
|
|
48168
|
+
* Update the in-memory session value.
|
|
48169
|
+
* @param value - New value or null to clear
|
|
48170
|
+
*/
|
|
48171
|
+
this.setData = async (value) => {
|
|
48172
|
+
backtest.loggerService.debug(SESSION_LOCAL_INSTANCE_METHOD_NAME_SET, {
|
|
48173
|
+
strategyName: this.strategyName,
|
|
48174
|
+
exchangeName: this.exchangeName,
|
|
48175
|
+
frameName: this.frameName,
|
|
48176
|
+
});
|
|
48177
|
+
this._data = value;
|
|
48178
|
+
};
|
|
48179
|
+
}
|
|
48180
|
+
/** Releases resources held by this instance. */
|
|
48181
|
+
async dispose() {
|
|
48182
|
+
backtest.loggerService.debug(SESSION_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
48183
|
+
strategyName: this.strategyName,
|
|
48184
|
+
exchangeName: this.exchangeName,
|
|
48185
|
+
frameName: this.frameName,
|
|
48186
|
+
});
|
|
48187
|
+
}
|
|
48188
|
+
}
|
|
48189
|
+
/**
|
|
48190
|
+
* No-op session instance that discards all writes.
|
|
48191
|
+
* Used for disabling session storage in tests or dry-run scenarios.
|
|
48192
|
+
*
|
|
48193
|
+
* Useful when replaying historical candles without needing to accumulate
|
|
48194
|
+
* cross-candle session state — getData always returns null.
|
|
48195
|
+
*/
|
|
48196
|
+
class SessionDummyInstance {
|
|
48197
|
+
constructor(symbol, strategyName, exchangeName, frameName, backtest) {
|
|
48198
|
+
this.symbol = symbol;
|
|
48199
|
+
this.strategyName = strategyName;
|
|
48200
|
+
this.exchangeName = exchangeName;
|
|
48201
|
+
this.frameName = frameName;
|
|
48202
|
+
this.backtest = backtest;
|
|
48203
|
+
/**
|
|
48204
|
+
* No-op initialization.
|
|
48205
|
+
* @returns Promise that resolves immediately
|
|
48206
|
+
*/
|
|
48207
|
+
this.waitForInit = functoolsKit.singleshot(async (_initial) => {
|
|
48208
|
+
});
|
|
48209
|
+
/**
|
|
48210
|
+
* No-op read — always returns null.
|
|
48211
|
+
* @returns null
|
|
48212
|
+
*/
|
|
48213
|
+
this.getData = async () => {
|
|
48214
|
+
return null;
|
|
48215
|
+
};
|
|
48216
|
+
/**
|
|
48217
|
+
* No-op write — discards the value.
|
|
48218
|
+
*/
|
|
48219
|
+
this.setData = async (_value) => {
|
|
48220
|
+
};
|
|
48221
|
+
}
|
|
48222
|
+
/** No-op. */
|
|
48223
|
+
async dispose() {
|
|
48224
|
+
}
|
|
48225
|
+
}
|
|
48226
|
+
/**
|
|
48227
|
+
* File-system backed session instance.
|
|
48228
|
+
* Data is persisted atomically to disk via PersistSessionAdapter.
|
|
48229
|
+
* Session is restored from disk on waitForInit.
|
|
48230
|
+
*
|
|
48231
|
+
* Features:
|
|
48232
|
+
* - Crash-safe atomic file writes
|
|
48233
|
+
* - Scoped per (symbol, strategyName, exchangeName, frameName) tuple
|
|
48234
|
+
*
|
|
48235
|
+
* Use in live trading to survive process restarts mid-session.
|
|
48236
|
+
*/
|
|
48237
|
+
class SessionPersistInstance {
|
|
48238
|
+
constructor(symbol, strategyName, exchangeName, frameName, backtest$1) {
|
|
48239
|
+
this.symbol = symbol;
|
|
48240
|
+
this.strategyName = strategyName;
|
|
48241
|
+
this.exchangeName = exchangeName;
|
|
48242
|
+
this.frameName = frameName;
|
|
48243
|
+
this.backtest = backtest$1;
|
|
48244
|
+
this._data = null;
|
|
48245
|
+
/**
|
|
48246
|
+
* Initialize persistence storage and restore session from disk.
|
|
48247
|
+
* @param initial - Whether this is the first initialization
|
|
48248
|
+
*/
|
|
48249
|
+
this.waitForInit = functoolsKit.singleshot(async (initial) => {
|
|
48250
|
+
backtest.loggerService.debug(SESSION_PERSIST_INSTANCE_METHOD_NAME_WAIT_FOR_INIT, {
|
|
48251
|
+
strategyName: this.strategyName,
|
|
48252
|
+
exchangeName: this.exchangeName,
|
|
48253
|
+
frameName: this.frameName,
|
|
48254
|
+
initial,
|
|
48255
|
+
});
|
|
48256
|
+
await PersistSessionAdapter.waitForInit(this.strategyName, this.exchangeName, this.frameName, initial);
|
|
48257
|
+
const data = await PersistSessionAdapter.readSessionData(this.strategyName, this.exchangeName, this.frameName);
|
|
48258
|
+
if (data) {
|
|
48259
|
+
this._data = data.data;
|
|
48260
|
+
return;
|
|
48261
|
+
}
|
|
48262
|
+
this._data = null;
|
|
48263
|
+
});
|
|
48264
|
+
/**
|
|
48265
|
+
* Read the current persisted session value.
|
|
48266
|
+
* @returns Current session value, or null if not set
|
|
48267
|
+
*/
|
|
48268
|
+
this.getData = async () => {
|
|
48269
|
+
backtest.loggerService.debug(SESSION_PERSIST_INSTANCE_METHOD_NAME_GET, {
|
|
48270
|
+
strategyName: this.strategyName,
|
|
48271
|
+
exchangeName: this.exchangeName,
|
|
48272
|
+
frameName: this.frameName,
|
|
48273
|
+
});
|
|
48274
|
+
return this._data;
|
|
48275
|
+
};
|
|
48276
|
+
/**
|
|
48277
|
+
* Update session value and persist to disk atomically.
|
|
48278
|
+
* @param value - New value or null to clear
|
|
48279
|
+
*/
|
|
48280
|
+
this.setData = async (value) => {
|
|
48281
|
+
backtest.loggerService.debug(SESSION_PERSIST_INSTANCE_METHOD_NAME_SET, {
|
|
48282
|
+
strategyName: this.strategyName,
|
|
48283
|
+
exchangeName: this.exchangeName,
|
|
48284
|
+
frameName: this.frameName,
|
|
48285
|
+
});
|
|
48286
|
+
this._data = value;
|
|
48287
|
+
const id = CREATE_KEY_FN$5(this.symbol, this.strategyName, this.exchangeName, this.frameName, this.backtest);
|
|
48288
|
+
await PersistSessionAdapter.writeSessionData({ id, data: value }, this.strategyName, this.exchangeName, this.frameName);
|
|
48289
|
+
};
|
|
48290
|
+
}
|
|
48291
|
+
/** Releases resources held by this instance. */
|
|
48292
|
+
async dispose() {
|
|
48293
|
+
backtest.loggerService.debug(SESSION_LIVE_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
48294
|
+
strategyName: this.strategyName,
|
|
48295
|
+
exchangeName: this.exchangeName,
|
|
48296
|
+
frameName: this.frameName,
|
|
48297
|
+
});
|
|
48298
|
+
await PersistSessionAdapter.dispose(this.strategyName, this.exchangeName, this.frameName);
|
|
48299
|
+
}
|
|
48300
|
+
}
|
|
48301
|
+
/**
|
|
48302
|
+
* Backtest session adapter with pluggable storage backend.
|
|
48303
|
+
*
|
|
48304
|
+
* Features:
|
|
48305
|
+
* - Adapter pattern for swappable session instance implementations
|
|
48306
|
+
* - Default backend: SessionLocalInstance (in-memory, no disk persistence)
|
|
48307
|
+
* - Alternative backends: SessionPersistInstance, SessionDummyInstance
|
|
48308
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useSessionAdapter()
|
|
48309
|
+
* - Memoized instances per (symbol, strategyName, exchangeName, frameName) tuple
|
|
48310
|
+
*/
|
|
48311
|
+
class SessionBacktestAdapter {
|
|
48312
|
+
constructor() {
|
|
48313
|
+
this.SessionFactory = SessionLocalInstance;
|
|
48314
|
+
this.getInstance = functoolsKit.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]));
|
|
48315
|
+
/**
|
|
48316
|
+
* Read the current session value for a backtest run.
|
|
48317
|
+
* @param symbol - Trading pair symbol
|
|
48318
|
+
* @param context.strategyName - Strategy identifier
|
|
48319
|
+
* @param context.exchangeName - Exchange identifier
|
|
48320
|
+
* @param context.frameName - Frame identifier
|
|
48321
|
+
* @returns Current session value, or null if not set
|
|
48322
|
+
*/
|
|
48323
|
+
this.getData = async (symbol, context) => {
|
|
48324
|
+
backtest.loggerService.debug(SESSION_BACKTEST_ADAPTER_METHOD_NAME_GET, {
|
|
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.getData();
|
|
48334
|
+
};
|
|
48335
|
+
/**
|
|
48336
|
+
* Update the session value for a backtest run.
|
|
48337
|
+
* @param symbol - Trading pair symbol
|
|
48338
|
+
* @param value - New value or null to clear
|
|
48339
|
+
* @param context.strategyName - Strategy identifier
|
|
48340
|
+
* @param context.exchangeName - Exchange identifier
|
|
48341
|
+
* @param context.frameName - Frame identifier
|
|
48342
|
+
*/
|
|
48343
|
+
this.setData = async (symbol, value, context) => {
|
|
48344
|
+
backtest.loggerService.debug(SESSION_BACKTEST_ADAPTER_METHOD_NAME_SET, {
|
|
48345
|
+
strategyName: context.strategyName,
|
|
48346
|
+
exchangeName: context.exchangeName,
|
|
48347
|
+
frameName: context.frameName,
|
|
48348
|
+
});
|
|
48349
|
+
const key = CREATE_KEY_FN$5(symbol, context.strategyName, context.exchangeName, context.frameName, true);
|
|
48350
|
+
const isInitial = !this.getInstance.has(key);
|
|
48351
|
+
const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, true);
|
|
48352
|
+
await instance.waitForInit(isInitial);
|
|
48353
|
+
return await instance.setData(value);
|
|
48354
|
+
};
|
|
48355
|
+
/**
|
|
48356
|
+
* Switches to in-memory adapter (default).
|
|
48357
|
+
* All data lives in process memory only.
|
|
48358
|
+
*/
|
|
48359
|
+
this.useLocal = () => {
|
|
48360
|
+
backtest.loggerService.info(SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
48361
|
+
this.SessionFactory = SessionLocalInstance;
|
|
48362
|
+
};
|
|
48363
|
+
/**
|
|
48364
|
+
* Switches to file-system backed adapter.
|
|
48365
|
+
* Data is persisted to disk via PersistSessionAdapter.
|
|
48366
|
+
*/
|
|
48367
|
+
this.usePersist = () => {
|
|
48368
|
+
backtest.loggerService.info(SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
48369
|
+
this.SessionFactory = SessionPersistInstance;
|
|
48370
|
+
};
|
|
48371
|
+
/**
|
|
48372
|
+
* Switches to dummy adapter that discards all writes.
|
|
48373
|
+
*/
|
|
48374
|
+
this.useDummy = () => {
|
|
48375
|
+
backtest.loggerService.info(SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
48376
|
+
this.SessionFactory = SessionDummyInstance;
|
|
48377
|
+
};
|
|
48378
|
+
/**
|
|
48379
|
+
* Switches to a custom session adapter implementation.
|
|
48380
|
+
* @param Ctor - Constructor for the custom session instance
|
|
48381
|
+
*/
|
|
48382
|
+
this.useSessionAdapter = (Ctor) => {
|
|
48383
|
+
backtest.loggerService.info(SESSION_BACKTEST_ADAPTER_METHOD_NAME_USE_SESSION_ADAPTER);
|
|
48384
|
+
this.SessionFactory = Ctor;
|
|
48385
|
+
};
|
|
48386
|
+
/**
|
|
48387
|
+
* Clears the memoized instance cache.
|
|
48388
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
48389
|
+
* so new instances are created with the updated base path.
|
|
48390
|
+
*/
|
|
48391
|
+
this.clear = () => {
|
|
48392
|
+
backtest.loggerService.info(SESSION_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
|
|
48393
|
+
this.getInstance.clear();
|
|
48394
|
+
};
|
|
48395
|
+
}
|
|
48396
|
+
}
|
|
48397
|
+
/**
|
|
48398
|
+
* Live trading session adapter with pluggable storage backend.
|
|
48399
|
+
*
|
|
48400
|
+
* Features:
|
|
48401
|
+
* - Adapter pattern for swappable session instance implementations
|
|
48402
|
+
* - Default backend: SessionPersistInstance (file-system backed, survives restarts)
|
|
48403
|
+
* - Alternative backends: SessionLocalInstance, SessionDummyInstance
|
|
48404
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useSessionAdapter()
|
|
48405
|
+
* - Memoized instances per (symbol, strategyName, exchangeName, frameName) tuple
|
|
48406
|
+
*/
|
|
48407
|
+
class SessionLiveAdapter {
|
|
48408
|
+
constructor() {
|
|
48409
|
+
this.SessionFactory = SessionPersistInstance;
|
|
48410
|
+
this.getInstance = functoolsKit.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]));
|
|
48411
|
+
/**
|
|
48412
|
+
* Read the current session value for a live run.
|
|
48413
|
+
* @param symbol - Trading pair symbol
|
|
48414
|
+
* @param context.strategyName - Strategy identifier
|
|
48415
|
+
* @param context.exchangeName - Exchange identifier
|
|
48416
|
+
* @param context.frameName - Frame identifier
|
|
48417
|
+
* @returns Current session value, or null if not set
|
|
48418
|
+
*/
|
|
48419
|
+
this.getData = async (symbol, context) => {
|
|
48420
|
+
backtest.loggerService.debug(SESSION_LIVE_ADAPTER_METHOD_NAME_GET, {
|
|
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.getData();
|
|
48430
|
+
};
|
|
48431
|
+
/**
|
|
48432
|
+
* Update the session value for a live run.
|
|
48433
|
+
* @param symbol - Trading pair symbol
|
|
48434
|
+
* @param value - New value or null to clear
|
|
48435
|
+
* @param context.strategyName - Strategy identifier
|
|
48436
|
+
* @param context.exchangeName - Exchange identifier
|
|
48437
|
+
* @param context.frameName - Frame identifier
|
|
48438
|
+
*/
|
|
48439
|
+
this.setData = async (symbol, value, context) => {
|
|
48440
|
+
backtest.loggerService.debug(SESSION_LIVE_ADAPTER_METHOD_NAME_SET, {
|
|
48441
|
+
strategyName: context.strategyName,
|
|
48442
|
+
exchangeName: context.exchangeName,
|
|
48443
|
+
frameName: context.frameName,
|
|
48444
|
+
});
|
|
48445
|
+
const key = CREATE_KEY_FN$5(symbol, context.strategyName, context.exchangeName, context.frameName, false);
|
|
48446
|
+
const isInitial = !this.getInstance.has(key);
|
|
48447
|
+
const instance = this.getInstance(symbol, context.strategyName, context.exchangeName, context.frameName, false);
|
|
48448
|
+
await instance.waitForInit(isInitial);
|
|
48449
|
+
return await instance.setData(value);
|
|
48450
|
+
};
|
|
48451
|
+
/**
|
|
48452
|
+
* Switches to in-memory adapter.
|
|
48453
|
+
* All data lives in process memory only.
|
|
48454
|
+
*/
|
|
48455
|
+
this.useLocal = () => {
|
|
48456
|
+
backtest.loggerService.info(SESSION_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
48457
|
+
this.SessionFactory = SessionLocalInstance;
|
|
48458
|
+
};
|
|
48459
|
+
/**
|
|
48460
|
+
* Switches to file-system backed adapter (default).
|
|
48461
|
+
* Data is persisted to disk via PersistSessionAdapter.
|
|
48462
|
+
*/
|
|
48463
|
+
this.usePersist = () => {
|
|
48464
|
+
backtest.loggerService.info(SESSION_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
48465
|
+
this.SessionFactory = SessionPersistInstance;
|
|
48466
|
+
};
|
|
48467
|
+
/**
|
|
48468
|
+
* Switches to dummy adapter that discards all writes.
|
|
48469
|
+
*/
|
|
48470
|
+
this.useDummy = () => {
|
|
48471
|
+
backtest.loggerService.info(SESSION_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
48472
|
+
this.SessionFactory = SessionDummyInstance;
|
|
48473
|
+
};
|
|
48474
|
+
/**
|
|
48475
|
+
* Switches to a custom session adapter implementation.
|
|
48476
|
+
* @param Ctor - Constructor for the custom session instance
|
|
48477
|
+
*/
|
|
48478
|
+
this.useSessionAdapter = (Ctor) => {
|
|
48479
|
+
backtest.loggerService.info(SESSION_LIVE_ADAPTER_METHOD_NAME_USE_SESSION_ADAPTER);
|
|
48480
|
+
this.SessionFactory = Ctor;
|
|
48481
|
+
};
|
|
48482
|
+
/**
|
|
48483
|
+
* Clears the memoized instance cache.
|
|
48484
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
48485
|
+
* so new instances are created with the updated base path.
|
|
48486
|
+
*/
|
|
48487
|
+
this.clear = () => {
|
|
48488
|
+
backtest.loggerService.info(SESSION_LIVE_ADAPTER_METHOD_NAME_CLEAR);
|
|
48489
|
+
this.getInstance.clear();
|
|
48490
|
+
};
|
|
48491
|
+
}
|
|
48492
|
+
}
|
|
48493
|
+
/**
|
|
48494
|
+
* Main session adapter that manages both backtest and live session storage.
|
|
48495
|
+
*
|
|
48496
|
+
* Features:
|
|
48497
|
+
* - Routes all operations to SessionBacktest or SessionLive based on the backtest flag
|
|
48498
|
+
*/
|
|
48499
|
+
class SessionAdapter {
|
|
48500
|
+
constructor() {
|
|
48501
|
+
/**
|
|
48502
|
+
* Read the current session value for a signal.
|
|
48503
|
+
* Routes to SessionBacktest or SessionLive based on backtest.
|
|
48504
|
+
* @param symbol - Trading pair symbol
|
|
48505
|
+
* @param context.strategyName - Strategy identifier
|
|
48506
|
+
* @param context.exchangeName - Exchange identifier
|
|
48507
|
+
* @param context.frameName - Frame identifier
|
|
48508
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
48509
|
+
* @returns Current session value, or null if not set
|
|
48510
|
+
*/
|
|
48511
|
+
this.getData = async (symbol, context, backtest$1) => {
|
|
48512
|
+
backtest.loggerService.debug(SESSION_ADAPTER_METHOD_NAME_GET, {
|
|
48513
|
+
strategyName: context.strategyName,
|
|
48514
|
+
exchangeName: context.exchangeName,
|
|
48515
|
+
frameName: context.frameName,
|
|
48516
|
+
backtest: backtest$1,
|
|
48517
|
+
});
|
|
48518
|
+
if (backtest$1) {
|
|
48519
|
+
return await SessionBacktest.getData(symbol, context);
|
|
48520
|
+
}
|
|
48521
|
+
return await SessionLive.getData(symbol, context);
|
|
48522
|
+
};
|
|
48523
|
+
/**
|
|
48524
|
+
* Update the session value for a signal.
|
|
48525
|
+
* Routes to SessionBacktest or SessionLive based on backtest.
|
|
48526
|
+
* @param symbol - Trading pair symbol
|
|
48527
|
+
* @param value - New value or null to clear
|
|
48528
|
+
* @param context.strategyName - Strategy identifier
|
|
48529
|
+
* @param context.exchangeName - Exchange identifier
|
|
48530
|
+
* @param context.frameName - Frame identifier
|
|
48531
|
+
* @param backtest - Flag indicating if the context is backtest or live
|
|
48532
|
+
*/
|
|
48533
|
+
this.setData = async (symbol, value, context, backtest$1) => {
|
|
48534
|
+
backtest.loggerService.debug(SESSION_ADAPTER_METHOD_NAME_SET, {
|
|
48535
|
+
strategyName: context.strategyName,
|
|
48536
|
+
exchangeName: context.exchangeName,
|
|
48537
|
+
frameName: context.frameName,
|
|
48538
|
+
backtest: backtest$1,
|
|
48539
|
+
});
|
|
48540
|
+
if (backtest$1) {
|
|
48541
|
+
return await SessionBacktest.setData(symbol, value, context);
|
|
48542
|
+
}
|
|
48543
|
+
return await SessionLive.setData(symbol, value, context);
|
|
48544
|
+
};
|
|
48545
|
+
}
|
|
48546
|
+
}
|
|
48547
|
+
/**
|
|
48548
|
+
* Global singleton instance of SessionAdapter.
|
|
48549
|
+
* Provides unified session management for backtest and live trading.
|
|
48550
|
+
*/
|
|
48551
|
+
const Session = new SessionAdapter();
|
|
48552
|
+
/**
|
|
48553
|
+
* Global singleton instance of SessionLiveAdapter.
|
|
48554
|
+
* Provides live trading session storage with pluggable backends.
|
|
48555
|
+
*/
|
|
48556
|
+
const SessionLive = new SessionLiveAdapter();
|
|
48557
|
+
/**
|
|
48558
|
+
* Global singleton instance of SessionBacktestAdapter.
|
|
48559
|
+
* Provides backtest session storage with pluggable backends.
|
|
48560
|
+
*/
|
|
48561
|
+
const SessionBacktest = new SessionBacktestAdapter();
|
|
48562
|
+
|
|
48563
|
+
const GET_SESSION_METHOD_NAME = "session.getSession";
|
|
48564
|
+
const SET_SESSION_METHOD_NAME = "session.setSession";
|
|
48565
|
+
/**
|
|
48566
|
+
* Reads the session value scoped to the current (symbol, strategy, exchange, frame) context.
|
|
48567
|
+
*
|
|
48568
|
+
* Session data persists across candles within a single run and can survive process
|
|
48569
|
+
* restarts in live mode — useful for caching LLM inference results, intermediate
|
|
48570
|
+
* indicator state, or any cross-candle accumulator that is not tied to a specific signal.
|
|
48571
|
+
*
|
|
48572
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48573
|
+
*
|
|
48574
|
+
* @param symbol - Trading pair symbol
|
|
48575
|
+
* @returns Promise resolving to current session value, or null if not set
|
|
48576
|
+
*
|
|
48577
|
+
* @example
|
|
48578
|
+
* ```typescript
|
|
48579
|
+
* import { getSession } from "backtest-kit";
|
|
48580
|
+
*
|
|
48581
|
+
* const session = await getSession<{ lastLlmSignal: string }>("BTCUSDT");
|
|
48582
|
+
* if (session?.lastLlmSignal === "buy") {
|
|
48583
|
+
* // reuse cached LLM result instead of calling the model again
|
|
48584
|
+
* }
|
|
48585
|
+
* ```
|
|
48586
|
+
*/
|
|
48587
|
+
async function getSessionData(symbol) {
|
|
48588
|
+
backtest.loggerService.info(GET_SESSION_METHOD_NAME, { symbol });
|
|
48589
|
+
if (!ExecutionContextService.hasContext()) {
|
|
48590
|
+
throw new Error("getSession requires an execution context");
|
|
48591
|
+
}
|
|
48592
|
+
if (!MethodContextService.hasContext()) {
|
|
48593
|
+
throw new Error("getSession requires a method context");
|
|
48594
|
+
}
|
|
48595
|
+
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
48596
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48597
|
+
return await Session.getData(symbol, { exchangeName, frameName, strategyName }, isBacktest);
|
|
48598
|
+
}
|
|
48599
|
+
/**
|
|
48600
|
+
* Writes a session value scoped to the current (symbol, strategy, exchange, frame) context.
|
|
48601
|
+
*
|
|
48602
|
+
* Session data persists across candles within a single run and can survive process
|
|
48603
|
+
* restarts in live mode — useful for caching LLM inference results, intermediate
|
|
48604
|
+
* indicator state, or any cross-candle accumulator that is not tied to a specific signal.
|
|
48605
|
+
*
|
|
48606
|
+
* Pass null to clear the session.
|
|
48607
|
+
*
|
|
48608
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48609
|
+
*
|
|
48610
|
+
* @param symbol - Trading pair symbol
|
|
48611
|
+
* @param value - New value or null to clear
|
|
48612
|
+
* @returns Promise that resolves when the session has been written
|
|
48613
|
+
*
|
|
48614
|
+
* @example
|
|
48615
|
+
* ```typescript
|
|
48616
|
+
* import { setSession } from "backtest-kit";
|
|
48617
|
+
*
|
|
48618
|
+
* await setSession("BTCUSDT", { lastLlmSignal: "buy" });
|
|
48619
|
+
* ```
|
|
48620
|
+
*/
|
|
48621
|
+
async function setSessionData(symbol, value) {
|
|
48622
|
+
backtest.loggerService.info(SET_SESSION_METHOD_NAME, { symbol });
|
|
48623
|
+
if (!ExecutionContextService.hasContext()) {
|
|
48624
|
+
throw new Error("setSession requires an execution context");
|
|
48625
|
+
}
|
|
48626
|
+
if (!MethodContextService.hasContext()) {
|
|
48627
|
+
throw new Error("setSession requires a method context");
|
|
48628
|
+
}
|
|
48629
|
+
const { backtest: isBacktest } = backtest.executionContextService.context;
|
|
48630
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48631
|
+
await Session.setData(symbol, value, { exchangeName, frameName, strategyName }, isBacktest);
|
|
48632
|
+
}
|
|
48633
|
+
|
|
48634
|
+
const CREATE_SIGNAL_STATE_METHOD_NAME = "state.createSignalState";
|
|
48635
|
+
const CREATE_SET_STATE_FN = (params) => async (dispatch) => {
|
|
48636
|
+
if (!ExecutionContextService.hasContext()) {
|
|
48637
|
+
throw new Error("createSignalState requires an execution context");
|
|
48638
|
+
}
|
|
48639
|
+
if (!MethodContextService.hasContext()) {
|
|
48640
|
+
throw new Error("createSignalState requires a method context");
|
|
48641
|
+
}
|
|
48642
|
+
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48643
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48644
|
+
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48645
|
+
let signal;
|
|
48646
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
48647
|
+
return await State.setState(dispatch, {
|
|
48648
|
+
backtest: isBacktest,
|
|
48649
|
+
bucketName: params.bucketName,
|
|
48650
|
+
initialValue: params.initialValue,
|
|
48651
|
+
signalId: signal.id,
|
|
48652
|
+
});
|
|
48653
|
+
}
|
|
48654
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
48655
|
+
return await State.setState(dispatch, {
|
|
48656
|
+
backtest: isBacktest,
|
|
48657
|
+
bucketName: params.bucketName,
|
|
48658
|
+
initialValue: params.initialValue,
|
|
48659
|
+
signalId: signal.id,
|
|
48660
|
+
});
|
|
48661
|
+
}
|
|
48662
|
+
throw new Error(`createSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${params.bucketName}`);
|
|
48663
|
+
};
|
|
48664
|
+
const CREATE_GET_STATE_FN = (params) => async () => {
|
|
48665
|
+
if (!ExecutionContextService.hasContext()) {
|
|
48666
|
+
throw new Error("createSignalState requires an execution context");
|
|
48667
|
+
}
|
|
48668
|
+
if (!MethodContextService.hasContext()) {
|
|
48669
|
+
throw new Error("createSignalState requires a method context");
|
|
48670
|
+
}
|
|
48671
|
+
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48672
|
+
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48673
|
+
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48674
|
+
let signal;
|
|
48675
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
48676
|
+
return await State.getState({
|
|
48677
|
+
backtest: isBacktest,
|
|
48678
|
+
bucketName: params.bucketName,
|
|
48679
|
+
initialValue: params.initialValue,
|
|
48680
|
+
signalId: signal.id,
|
|
48681
|
+
});
|
|
48682
|
+
}
|
|
48683
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
48684
|
+
return await State.getState({
|
|
48685
|
+
backtest: isBacktest,
|
|
48686
|
+
bucketName: params.bucketName,
|
|
48687
|
+
initialValue: params.initialValue,
|
|
48688
|
+
signalId: signal.id,
|
|
48689
|
+
});
|
|
48690
|
+
}
|
|
48691
|
+
throw new Error(`createSignalState requires a pending or scheduled signal for symbol=${symbol} bucketName=${params.bucketName}`);
|
|
48692
|
+
};
|
|
48693
|
+
/**
|
|
48694
|
+
* Creates a bound [getState, setState] tuple scoped to a bucket and initial value.
|
|
48695
|
+
*
|
|
48696
|
+
* Both returned functions resolve the active pending or scheduled signal and the
|
|
48697
|
+
* backtest/live flag automatically from execution context — no signalId argument required.
|
|
48698
|
+
*
|
|
48699
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48700
|
+
*
|
|
48701
|
+
* Intended for LLM-driven capitulation strategies that accumulate per-trade
|
|
48702
|
+
* metrics (e.g. peakPercent, minutesOpen) across onActivePing ticks.
|
|
48703
|
+
* Profitable trades endure -0.5–2.5% drawdown and reach peak 2–3%+.
|
|
48704
|
+
* SL trades show peak < 0.15% (Feb08, Feb13) or never go positive (Feb25).
|
|
48705
|
+
* Rule: if minutesOpen >= N and peakPercent < threshold (e.g. 0.3%) — exit.
|
|
48706
|
+
*
|
|
48707
|
+
* @param params.bucketName - Logical namespace for grouping state buckets within a signal
|
|
48708
|
+
* @param params.initialValue - Default value when no persisted state exists
|
|
48709
|
+
* @returns Tuple [getState, setState] bound to the bucket and initial value
|
|
48710
|
+
*
|
|
48711
|
+
* @example
|
|
48712
|
+
* ```typescript
|
|
48713
|
+
* import { createSignalState } from "backtest-kit";
|
|
48714
|
+
*
|
|
48715
|
+
* const [getTradeState, setTradeState] = createSignalState({
|
|
48716
|
+
* bucketName: "trade",
|
|
48717
|
+
* initialValue: { peakPercent: 0, minutesOpen: 0 },
|
|
48718
|
+
* });
|
|
48719
|
+
*
|
|
48720
|
+
* // in onActivePing:
|
|
48721
|
+
* await setTradeState((s) => ({
|
|
48722
|
+
* peakPercent: Math.max(s.peakPercent, currentUnrealisedPercent),
|
|
48723
|
+
* minutesOpen: s.minutesOpen + 1,
|
|
48724
|
+
* }));
|
|
48725
|
+
* const { peakPercent, minutesOpen } = await getTradeState();
|
|
48726
|
+
* if (minutesOpen >= 15 && peakPercent < 0.3) await commitMarketClose(symbol);
|
|
48727
|
+
* ```
|
|
48728
|
+
*/
|
|
48729
|
+
function createSignalState(params) {
|
|
48730
|
+
backtest.loggerService.info(CREATE_SIGNAL_STATE_METHOD_NAME, {
|
|
48731
|
+
bucketName: params.bucketName,
|
|
48732
|
+
});
|
|
48733
|
+
return [
|
|
48734
|
+
CREATE_GET_STATE_FN(params),
|
|
48735
|
+
CREATE_SET_STATE_FN(params),
|
|
48736
|
+
];
|
|
48737
|
+
}
|
|
47138
48738
|
|
|
47139
48739
|
const DEFAULT_BM25_K1 = 1.5;
|
|
47140
48740
|
const DEFAULT_BM25_B = 0.75;
|
|
@@ -47221,7 +48821,7 @@ const createSearchIndex = () => {
|
|
|
47221
48821
|
return { upsert, remove, list, search, read };
|
|
47222
48822
|
};
|
|
47223
48823
|
|
|
47224
|
-
const CREATE_KEY_FN$4 = (signalId, bucketName) => `${signalId}
|
|
48824
|
+
const CREATE_KEY_FN$4 = (signalId, bucketName) => `${signalId}_${bucketName}`;
|
|
47225
48825
|
const LIST_MEMORY_FN = ({ id, content }) => ({
|
|
47226
48826
|
memoryId: id,
|
|
47227
48827
|
content: content,
|
|
@@ -47242,18 +48842,35 @@ const MEMORY_PERSIST_INSTANCE_METHOD_NAME_READ = "MemoryPersistInstance.readMemo
|
|
|
47242
48842
|
const MEMORY_PERSIST_INSTANCE_METHOD_NAME_SEARCH = "MemoryPersistInstance.searchMemory";
|
|
47243
48843
|
const MEMORY_PERSIST_INSTANCE_METHOD_NAME_LIST = "MemoryPersistInstance.listMemory";
|
|
47244
48844
|
const MEMORY_PERSIST_INSTANCE_METHOD_NAME_REMOVE = "MemoryPersistInstance.removeMemory";
|
|
48845
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE = "MemoryBacktestAdapter.dispose";
|
|
48846
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_WRITE = "MemoryBacktestAdapter.writeMemory";
|
|
48847
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_SEARCH = "MemoryBacktestAdapter.searchMemory";
|
|
48848
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_LIST = "MemoryBacktestAdapter.listMemory";
|
|
48849
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_REMOVE = "MemoryBacktestAdapter.removeMemory";
|
|
48850
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_READ = "MemoryBacktestAdapter.readMemory";
|
|
48851
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL = "MemoryBacktestAdapter.useLocal";
|
|
48852
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST = "MemoryBacktestAdapter.usePersist";
|
|
48853
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY = "MemoryBacktestAdapter.useDummy";
|
|
48854
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER = "MemoryBacktestAdapter.useMemoryAdapter";
|
|
48855
|
+
const MEMORY_BACKTEST_ADAPTER_METHOD_NAME_CLEAR = "MemoryBacktestAdapter.clear";
|
|
48856
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_DISPOSE = "MemoryLiveAdapter.dispose";
|
|
48857
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_WRITE = "MemoryLiveAdapter.writeMemory";
|
|
48858
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_SEARCH = "MemoryLiveAdapter.searchMemory";
|
|
48859
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_LIST = "MemoryLiveAdapter.listMemory";
|
|
48860
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_REMOVE = "MemoryLiveAdapter.removeMemory";
|
|
48861
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_READ = "MemoryLiveAdapter.readMemory";
|
|
48862
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL = "MemoryLiveAdapter.useLocal";
|
|
48863
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST = "MemoryLiveAdapter.usePersist";
|
|
48864
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY = "MemoryLiveAdapter.useDummy";
|
|
48865
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER = "MemoryLiveAdapter.useMemoryAdapter";
|
|
48866
|
+
const MEMORY_LIVE_ADAPTER_METHOD_NAME_CLEAR = "MemoryLiveAdapter.clear";
|
|
47245
48867
|
const MEMORY_ADAPTER_METHOD_NAME_ENABLE = "MemoryAdapter.enable";
|
|
47246
48868
|
const MEMORY_ADAPTER_METHOD_NAME_DISABLE = "MemoryAdapter.disable";
|
|
47247
|
-
const MEMORY_ADAPTER_METHOD_NAME_DISPOSE = "MemoryAdapter.dispose";
|
|
47248
48869
|
const MEMORY_ADAPTER_METHOD_NAME_WRITE = "MemoryAdapter.writeMemory";
|
|
47249
48870
|
const MEMORY_ADAPTER_METHOD_NAME_SEARCH = "MemoryAdapter.searchMemory";
|
|
47250
48871
|
const MEMORY_ADAPTER_METHOD_NAME_LIST = "MemoryAdapter.listMemory";
|
|
47251
48872
|
const MEMORY_ADAPTER_METHOD_NAME_REMOVE = "MemoryAdapter.removeMemory";
|
|
47252
48873
|
const MEMORY_ADAPTER_METHOD_NAME_READ = "MemoryAdapter.readMemory";
|
|
47253
|
-
const MEMORY_ADAPTER_METHOD_NAME_USE_LOCAL = "MemoryAdapter.useLocal";
|
|
47254
|
-
const MEMORY_ADAPTER_METHOD_NAME_USE_PERSIST = "MemoryAdapter.usePersist";
|
|
47255
|
-
const MEMORY_ADAPTER_METHOD_NAME_USE_DUMMY = "MemoryAdapter.useDummy";
|
|
47256
|
-
const MEMORY_ADAPTER_METHOD_NAME_CLEAR = "MemoryAdapter.clear";
|
|
47257
48874
|
/**
|
|
47258
48875
|
* In-memory BM25 search index backed instance.
|
|
47259
48876
|
* All data lives in the process memory only - no disk persistence.
|
|
@@ -47278,7 +48895,7 @@ class MemoryLocalInstance {
|
|
|
47278
48895
|
* Write a value into the BM25 index.
|
|
47279
48896
|
* @param memoryId - Unique entry identifier
|
|
47280
48897
|
* @param value - Value to store and index
|
|
47281
|
-
* @param
|
|
48898
|
+
* @param description - BM25 index string
|
|
47282
48899
|
*/
|
|
47283
48900
|
async writeMemory(memoryId, value, description) {
|
|
47284
48901
|
backtest.loggerService.debug(MEMORY_LOCAL_INSTANCE_METHOD_NAME_WRITE, {
|
|
@@ -47349,7 +48966,7 @@ class MemoryLocalInstance {
|
|
|
47349
48966
|
}
|
|
47350
48967
|
/** Releases resources held by this instance. */
|
|
47351
48968
|
dispose() {
|
|
47352
|
-
backtest.loggerService.debug(
|
|
48969
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
47353
48970
|
signalId: this.signalId,
|
|
47354
48971
|
bucketName: this.bucketName,
|
|
47355
48972
|
});
|
|
@@ -47398,7 +49015,7 @@ class MemoryPersistInstance {
|
|
|
47398
49015
|
* Write a value to disk and update the BM25 index.
|
|
47399
49016
|
* @param memoryId - Unique entry identifier
|
|
47400
49017
|
* @param value - Value to persist and index
|
|
47401
|
-
* @param index -
|
|
49018
|
+
* @param index - BM25 index string; defaults to JSON.stringify(value)
|
|
47402
49019
|
*/
|
|
47403
49020
|
async writeMemory(memoryId, value, index = JSON.stringify(value)) {
|
|
47404
49021
|
backtest.loggerService.debug(MEMORY_PERSIST_INSTANCE_METHOD_NAME_WRITE, {
|
|
@@ -47472,7 +49089,7 @@ class MemoryPersistInstance {
|
|
|
47472
49089
|
}
|
|
47473
49090
|
/** Releases resources held by this instance. */
|
|
47474
49091
|
dispose() {
|
|
47475
|
-
backtest.loggerService.debug(
|
|
49092
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_DISPOSE, {
|
|
47476
49093
|
signalId: this.signalId,
|
|
47477
49094
|
bucketName: this.bucketName,
|
|
47478
49095
|
});
|
|
@@ -47532,48 +49149,377 @@ class MemoryDummyInstance {
|
|
|
47532
49149
|
}
|
|
47533
49150
|
}
|
|
47534
49151
|
/**
|
|
47535
|
-
*
|
|
47536
|
-
* Manages lazy initialization and instance lifecycle.
|
|
49152
|
+
* Backtest memory adapter with pluggable storage backend.
|
|
47537
49153
|
*
|
|
47538
49154
|
* Features:
|
|
47539
|
-
* -
|
|
47540
|
-
* -
|
|
47541
|
-
* -
|
|
49155
|
+
* - Adapter pattern for swappable memory instance implementations
|
|
49156
|
+
* - Default backend: MemoryLocalInstance (in-memory BM25, no disk persistence)
|
|
49157
|
+
* - Alternative backends: MemoryPersistInstance, MemoryDummyInstance
|
|
49158
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useMemoryAdapter()
|
|
49159
|
+
* - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from MemoryAdapter
|
|
49160
|
+
*
|
|
49161
|
+
* Use this adapter for backtest memory storage.
|
|
47542
49162
|
*/
|
|
47543
|
-
class
|
|
49163
|
+
class MemoryBacktestAdapter {
|
|
49164
|
+
constructor() {
|
|
49165
|
+
this.MemoryFactory = MemoryLocalInstance;
|
|
49166
|
+
this.getInstance = functoolsKit.memoize(([signalId, bucketName]) => CREATE_KEY_FN$4(signalId, bucketName), (signalId, bucketName) => Reflect.construct(this.MemoryFactory, [signalId, bucketName]));
|
|
49167
|
+
/**
|
|
49168
|
+
* Disposes all memoized instances for the given signalId.
|
|
49169
|
+
* Called by MemoryAdapter when a signal is cancelled or closed.
|
|
49170
|
+
* @param signalId - Signal identifier to dispose
|
|
49171
|
+
*/
|
|
49172
|
+
this.disposeSignal = (signalId) => {
|
|
49173
|
+
const prefix = CREATE_KEY_FN$4(signalId, "");
|
|
49174
|
+
for (const key of this.getInstance.keys()) {
|
|
49175
|
+
if (key.startsWith(prefix)) {
|
|
49176
|
+
const instance = this.getInstance.get(key);
|
|
49177
|
+
instance && instance.dispose();
|
|
49178
|
+
this.getInstance.clear(key);
|
|
49179
|
+
}
|
|
49180
|
+
}
|
|
49181
|
+
};
|
|
49182
|
+
/**
|
|
49183
|
+
* Write a value to memory.
|
|
49184
|
+
* @param dto.memoryId - Unique entry identifier
|
|
49185
|
+
* @param dto.value - Value to store
|
|
49186
|
+
* @param dto.signalId - Signal identifier
|
|
49187
|
+
* @param dto.bucketName - Bucket name
|
|
49188
|
+
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
49189
|
+
*/
|
|
49190
|
+
this.writeMemory = async (dto) => {
|
|
49191
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_WRITE, {
|
|
49192
|
+
signalId: dto.signalId,
|
|
49193
|
+
bucketName: dto.bucketName,
|
|
49194
|
+
memoryId: dto.memoryId,
|
|
49195
|
+
});
|
|
49196
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
49197
|
+
const isInitial = !this.getInstance.has(key);
|
|
49198
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
49199
|
+
await instance.waitForInit(isInitial);
|
|
49200
|
+
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
49201
|
+
};
|
|
49202
|
+
/**
|
|
49203
|
+
* Search memory using BM25 full-text scoring.
|
|
49204
|
+
* @param dto.query - Search query string
|
|
49205
|
+
* @param dto.signalId - Signal identifier
|
|
49206
|
+
* @param dto.bucketName - Bucket name
|
|
49207
|
+
* @returns Matching entries sorted by relevance score
|
|
49208
|
+
*/
|
|
49209
|
+
this.searchMemory = async (dto) => {
|
|
49210
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_SEARCH, {
|
|
49211
|
+
signalId: dto.signalId,
|
|
49212
|
+
bucketName: dto.bucketName,
|
|
49213
|
+
query: dto.query,
|
|
49214
|
+
});
|
|
49215
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
49216
|
+
const isInitial = !this.getInstance.has(key);
|
|
49217
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
49218
|
+
await instance.waitForInit(isInitial);
|
|
49219
|
+
return await instance.searchMemory(dto.query, dto.settings);
|
|
49220
|
+
};
|
|
49221
|
+
/**
|
|
49222
|
+
* List all entries in memory.
|
|
49223
|
+
* @param dto.signalId - Signal identifier
|
|
49224
|
+
* @param dto.bucketName - Bucket name
|
|
49225
|
+
* @returns Array of all stored entries
|
|
49226
|
+
*/
|
|
49227
|
+
this.listMemory = async (dto) => {
|
|
49228
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_LIST, {
|
|
49229
|
+
signalId: dto.signalId,
|
|
49230
|
+
bucketName: dto.bucketName,
|
|
49231
|
+
});
|
|
49232
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
49233
|
+
const isInitial = !this.getInstance.has(key);
|
|
49234
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
49235
|
+
await instance.waitForInit(isInitial);
|
|
49236
|
+
return await instance.listMemory();
|
|
49237
|
+
};
|
|
49238
|
+
/**
|
|
49239
|
+
* Remove an entry from memory.
|
|
49240
|
+
* @param dto.memoryId - Unique entry identifier
|
|
49241
|
+
* @param dto.signalId - Signal identifier
|
|
49242
|
+
* @param dto.bucketName - Bucket name
|
|
49243
|
+
*/
|
|
49244
|
+
this.removeMemory = async (dto) => {
|
|
49245
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_REMOVE, {
|
|
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.removeMemory(dto.memoryId);
|
|
49255
|
+
};
|
|
49256
|
+
/**
|
|
49257
|
+
* Read a single entry from memory.
|
|
49258
|
+
* @param dto.memoryId - Unique entry identifier
|
|
49259
|
+
* @param dto.signalId - Signal identifier
|
|
49260
|
+
* @param dto.bucketName - Bucket name
|
|
49261
|
+
* @returns Entry value
|
|
49262
|
+
* @throws Error if entry not found
|
|
49263
|
+
*/
|
|
49264
|
+
this.readMemory = async (dto) => {
|
|
49265
|
+
backtest.loggerService.debug(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_READ, {
|
|
49266
|
+
signalId: dto.signalId,
|
|
49267
|
+
bucketName: dto.bucketName,
|
|
49268
|
+
memoryId: dto.memoryId,
|
|
49269
|
+
});
|
|
49270
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
49271
|
+
const isInitial = !this.getInstance.has(key);
|
|
49272
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
49273
|
+
await instance.waitForInit(isInitial);
|
|
49274
|
+
return await instance.readMemory(dto.memoryId);
|
|
49275
|
+
};
|
|
49276
|
+
/**
|
|
49277
|
+
* Switches to in-memory BM25 adapter (default).
|
|
49278
|
+
* All data lives in process memory only.
|
|
49279
|
+
*/
|
|
49280
|
+
this.useLocal = () => {
|
|
49281
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
49282
|
+
this.MemoryFactory = MemoryLocalInstance;
|
|
49283
|
+
};
|
|
49284
|
+
/**
|
|
49285
|
+
* Switches to file-system backed adapter.
|
|
49286
|
+
* Data is persisted to ./dump/memory/<signalId>/<bucketName>/.
|
|
49287
|
+
*/
|
|
49288
|
+
this.usePersist = () => {
|
|
49289
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
49290
|
+
this.MemoryFactory = MemoryPersistInstance;
|
|
49291
|
+
};
|
|
49292
|
+
/**
|
|
49293
|
+
* Switches to dummy adapter that discards all writes.
|
|
49294
|
+
*/
|
|
49295
|
+
this.useDummy = () => {
|
|
49296
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
49297
|
+
this.MemoryFactory = MemoryDummyInstance;
|
|
49298
|
+
};
|
|
49299
|
+
/**
|
|
49300
|
+
* Switches to a custom memory adapter implementation.
|
|
49301
|
+
* @param Ctor - Constructor for the custom memory instance
|
|
49302
|
+
*/
|
|
49303
|
+
this.useMemoryAdapter = (Ctor) => {
|
|
49304
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_USE_ADAPTER);
|
|
49305
|
+
this.MemoryFactory = Ctor;
|
|
49306
|
+
};
|
|
49307
|
+
/**
|
|
49308
|
+
* Clears the memoized instance cache.
|
|
49309
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
49310
|
+
* so new instances are created with the updated base path.
|
|
49311
|
+
*/
|
|
49312
|
+
this.clear = () => {
|
|
49313
|
+
backtest.loggerService.info(MEMORY_BACKTEST_ADAPTER_METHOD_NAME_CLEAR);
|
|
49314
|
+
this.getInstance.clear();
|
|
49315
|
+
};
|
|
49316
|
+
}
|
|
49317
|
+
}
|
|
49318
|
+
/**
|
|
49319
|
+
* Live trading memory adapter with pluggable storage backend.
|
|
49320
|
+
*
|
|
49321
|
+
* Features:
|
|
49322
|
+
* - Adapter pattern for swappable memory instance implementations
|
|
49323
|
+
* - Default backend: MemoryPersistInstance (file-system backed, survives restarts)
|
|
49324
|
+
* - Alternative backends: MemoryLocalInstance, MemoryDummyInstance
|
|
49325
|
+
* - Convenience methods: useLocal(), usePersist(), useDummy(), useMemoryAdapter()
|
|
49326
|
+
* - Memoized instances per (signalId, bucketName) pair; cleared via disposeSignal() from MemoryAdapter
|
|
49327
|
+
*
|
|
49328
|
+
* Use this adapter for live trading memory storage.
|
|
49329
|
+
*/
|
|
49330
|
+
class MemoryLiveAdapter {
|
|
47544
49331
|
constructor() {
|
|
47545
49332
|
this.MemoryFactory = MemoryPersistInstance;
|
|
47546
49333
|
this.getInstance = functoolsKit.memoize(([signalId, bucketName]) => CREATE_KEY_FN$4(signalId, bucketName), (signalId, bucketName) => Reflect.construct(this.MemoryFactory, [signalId, bucketName]));
|
|
47547
49334
|
/**
|
|
47548
|
-
*
|
|
47549
|
-
*
|
|
47550
|
-
*
|
|
47551
|
-
|
|
47552
|
-
|
|
49335
|
+
* Disposes all memoized instances for the given signalId.
|
|
49336
|
+
* Called by MemoryAdapter when a signal is cancelled or closed.
|
|
49337
|
+
* @param signalId - Signal identifier to dispose
|
|
49338
|
+
*/
|
|
49339
|
+
this.disposeSignal = (signalId) => {
|
|
49340
|
+
const prefix = CREATE_KEY_FN$4(signalId, "");
|
|
49341
|
+
for (const key of this.getInstance.keys()) {
|
|
49342
|
+
if (key.startsWith(prefix)) {
|
|
49343
|
+
const instance = this.getInstance.get(key);
|
|
49344
|
+
instance && instance.dispose();
|
|
49345
|
+
this.getInstance.clear(key);
|
|
49346
|
+
}
|
|
49347
|
+
}
|
|
49348
|
+
};
|
|
49349
|
+
/**
|
|
49350
|
+
* Write a value to memory.
|
|
49351
|
+
* @param dto.memoryId - Unique entry identifier
|
|
49352
|
+
* @param dto.value - Value to store
|
|
49353
|
+
* @param dto.signalId - Signal identifier
|
|
49354
|
+
* @param dto.bucketName - Bucket name
|
|
49355
|
+
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
49356
|
+
*/
|
|
49357
|
+
this.writeMemory = async (dto) => {
|
|
49358
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_WRITE, {
|
|
49359
|
+
signalId: dto.signalId,
|
|
49360
|
+
bucketName: dto.bucketName,
|
|
49361
|
+
memoryId: dto.memoryId,
|
|
49362
|
+
});
|
|
49363
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
49364
|
+
const isInitial = !this.getInstance.has(key);
|
|
49365
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
49366
|
+
await instance.waitForInit(isInitial);
|
|
49367
|
+
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
49368
|
+
};
|
|
49369
|
+
/**
|
|
49370
|
+
* Search memory using BM25 full-text scoring.
|
|
49371
|
+
* @param dto.query - Search query string
|
|
49372
|
+
* @param dto.signalId - Signal identifier
|
|
49373
|
+
* @param dto.bucketName - Bucket name
|
|
49374
|
+
* @returns Matching entries sorted by relevance score
|
|
49375
|
+
*/
|
|
49376
|
+
this.searchMemory = async (dto) => {
|
|
49377
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_SEARCH, {
|
|
49378
|
+
signalId: dto.signalId,
|
|
49379
|
+
bucketName: dto.bucketName,
|
|
49380
|
+
query: dto.query,
|
|
49381
|
+
});
|
|
49382
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
49383
|
+
const isInitial = !this.getInstance.has(key);
|
|
49384
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
49385
|
+
await instance.waitForInit(isInitial);
|
|
49386
|
+
return await instance.searchMemory(dto.query, dto.settings);
|
|
49387
|
+
};
|
|
49388
|
+
/**
|
|
49389
|
+
* List all entries in memory.
|
|
49390
|
+
* @param dto.signalId - Signal identifier
|
|
49391
|
+
* @param dto.bucketName - Bucket name
|
|
49392
|
+
* @returns Array of all stored entries
|
|
49393
|
+
*/
|
|
49394
|
+
this.listMemory = async (dto) => {
|
|
49395
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_LIST, {
|
|
49396
|
+
signalId: dto.signalId,
|
|
49397
|
+
bucketName: dto.bucketName,
|
|
49398
|
+
});
|
|
49399
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
49400
|
+
const isInitial = !this.getInstance.has(key);
|
|
49401
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
49402
|
+
await instance.waitForInit(isInitial);
|
|
49403
|
+
return await instance.listMemory();
|
|
49404
|
+
};
|
|
49405
|
+
/**
|
|
49406
|
+
* Remove an entry from memory.
|
|
49407
|
+
* @param dto.memoryId - Unique entry identifier
|
|
49408
|
+
* @param dto.signalId - Signal identifier
|
|
49409
|
+
* @param dto.bucketName - Bucket name
|
|
49410
|
+
*/
|
|
49411
|
+
this.removeMemory = async (dto) => {
|
|
49412
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_REMOVE, {
|
|
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.removeMemory(dto.memoryId);
|
|
49422
|
+
};
|
|
49423
|
+
/**
|
|
49424
|
+
* Read a single entry from memory.
|
|
49425
|
+
* @param dto.memoryId - Unique entry identifier
|
|
49426
|
+
* @param dto.signalId - Signal identifier
|
|
49427
|
+
* @param dto.bucketName - Bucket name
|
|
49428
|
+
* @returns Entry value
|
|
49429
|
+
* @throws Error if entry not found
|
|
49430
|
+
*/
|
|
49431
|
+
this.readMemory = async (dto) => {
|
|
49432
|
+
backtest.loggerService.debug(MEMORY_LIVE_ADAPTER_METHOD_NAME_READ, {
|
|
49433
|
+
signalId: dto.signalId,
|
|
49434
|
+
bucketName: dto.bucketName,
|
|
49435
|
+
memoryId: dto.memoryId,
|
|
49436
|
+
});
|
|
49437
|
+
const key = CREATE_KEY_FN$4(dto.signalId, dto.bucketName);
|
|
49438
|
+
const isInitial = !this.getInstance.has(key);
|
|
49439
|
+
const instance = this.getInstance(dto.signalId, dto.bucketName);
|
|
49440
|
+
await instance.waitForInit(isInitial);
|
|
49441
|
+
return await instance.readMemory(dto.memoryId);
|
|
49442
|
+
};
|
|
49443
|
+
/**
|
|
49444
|
+
* Switches to in-memory BM25 adapter.
|
|
49445
|
+
* All data lives in process memory only.
|
|
49446
|
+
*/
|
|
49447
|
+
this.useLocal = () => {
|
|
49448
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
49449
|
+
this.MemoryFactory = MemoryLocalInstance;
|
|
49450
|
+
};
|
|
49451
|
+
/**
|
|
49452
|
+
* Switches to file-system backed adapter (default).
|
|
49453
|
+
* Data is persisted to ./dump/memory/<signalId>/<bucketName>/.
|
|
49454
|
+
*/
|
|
49455
|
+
this.usePersist = () => {
|
|
49456
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
49457
|
+
this.MemoryFactory = MemoryPersistInstance;
|
|
49458
|
+
};
|
|
49459
|
+
/**
|
|
49460
|
+
* Switches to dummy adapter that discards all writes.
|
|
49461
|
+
*/
|
|
49462
|
+
this.useDummy = () => {
|
|
49463
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
49464
|
+
this.MemoryFactory = MemoryDummyInstance;
|
|
49465
|
+
};
|
|
49466
|
+
/**
|
|
49467
|
+
* Switches to a custom memory adapter implementation.
|
|
49468
|
+
* @param Ctor - Constructor for the custom memory instance
|
|
49469
|
+
*/
|
|
49470
|
+
this.useMemoryAdapter = (Ctor) => {
|
|
49471
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_USE_ADAPTER);
|
|
49472
|
+
this.MemoryFactory = Ctor;
|
|
49473
|
+
};
|
|
49474
|
+
/**
|
|
49475
|
+
* Clears the memoized instance cache.
|
|
49476
|
+
* Call this when process.cwd() changes between strategy iterations
|
|
49477
|
+
* so new instances are created with the updated base path.
|
|
49478
|
+
*/
|
|
49479
|
+
this.clear = () => {
|
|
49480
|
+
backtest.loggerService.info(MEMORY_LIVE_ADAPTER_METHOD_NAME_CLEAR);
|
|
49481
|
+
this.getInstance.clear();
|
|
49482
|
+
};
|
|
49483
|
+
}
|
|
49484
|
+
}
|
|
49485
|
+
/**
|
|
49486
|
+
* Main memory adapter that manages both backtest and live memory storage.
|
|
49487
|
+
*
|
|
49488
|
+
* Features:
|
|
49489
|
+
* - Subscribes to signal lifecycle events (cancelled/closed) to dispose stale instances
|
|
49490
|
+
* - Routes all operations to MemoryBacktest or MemoryLive based on dto.backtest
|
|
49491
|
+
* - Singleshot enable pattern prevents duplicate subscriptions
|
|
49492
|
+
* - Cleanup function for proper unsubscription
|
|
49493
|
+
*/
|
|
49494
|
+
class MemoryAdapter {
|
|
49495
|
+
constructor() {
|
|
49496
|
+
/**
|
|
49497
|
+
* Enables memory storage by subscribing to signal lifecycle events.
|
|
49498
|
+
* Clears memoized instances in MemoryBacktest and MemoryLive when a signal
|
|
49499
|
+
* is cancelled or closed, preventing stale instances from accumulating.
|
|
49500
|
+
* Uses singleshot to ensure one-time subscription.
|
|
49501
|
+
*
|
|
49502
|
+
* @returns Cleanup function that unsubscribes from all emitters
|
|
47553
49503
|
*/
|
|
47554
49504
|
this.enable = functoolsKit.singleshot(() => {
|
|
47555
49505
|
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_ENABLE);
|
|
47556
|
-
const handleDispose = (signalId) => {
|
|
47557
|
-
const prefix = CREATE_KEY_FN$4(signalId, "");
|
|
47558
|
-
for (const key of this.getInstance.keys()) {
|
|
47559
|
-
if (key.startsWith(prefix)) {
|
|
47560
|
-
const instance = this.getInstance.get(key);
|
|
47561
|
-
instance && instance.dispose();
|
|
47562
|
-
this.getInstance.clear(key);
|
|
47563
|
-
}
|
|
47564
|
-
}
|
|
47565
|
-
};
|
|
47566
49506
|
const unCancel = signalEmitter
|
|
47567
49507
|
.filter(({ action }) => action === "cancelled")
|
|
47568
|
-
.connect(({ signal }) =>
|
|
49508
|
+
.connect(({ signal }) => {
|
|
49509
|
+
MemoryBacktest.disposeSignal(signal.id);
|
|
49510
|
+
MemoryLive.disposeSignal(signal.id);
|
|
49511
|
+
});
|
|
47569
49512
|
const unClose = signalEmitter
|
|
47570
49513
|
.filter(({ action }) => action === "closed")
|
|
47571
|
-
.connect(({ signal }) =>
|
|
47572
|
-
|
|
49514
|
+
.connect(({ signal }) => {
|
|
49515
|
+
MemoryBacktest.disposeSignal(signal.id);
|
|
49516
|
+
MemoryLive.disposeSignal(signal.id);
|
|
49517
|
+
});
|
|
49518
|
+
return functoolsKit.compose(() => unCancel(), () => unClose(), () => this.enable.clear());
|
|
47573
49519
|
});
|
|
47574
49520
|
/**
|
|
47575
|
-
*
|
|
47576
|
-
*
|
|
49521
|
+
* Disables memory storage by unsubscribing from signal lifecycle events.
|
|
49522
|
+
* Safe to call multiple times.
|
|
47577
49523
|
*/
|
|
47578
49524
|
this.disable = () => {
|
|
47579
49525
|
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_DISABLE);
|
|
@@ -47584,11 +49530,13 @@ class MemoryAdapter {
|
|
|
47584
49530
|
};
|
|
47585
49531
|
/**
|
|
47586
49532
|
* Write a value to memory.
|
|
49533
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47587
49534
|
* @param dto.memoryId - Unique entry identifier
|
|
47588
49535
|
* @param dto.value - Value to store
|
|
47589
49536
|
* @param dto.signalId - Signal identifier
|
|
47590
49537
|
* @param dto.bucketName - Bucket name
|
|
47591
|
-
* @param dto.description -
|
|
49538
|
+
* @param dto.description - BM25 index string; defaults to JSON.stringify(value)
|
|
49539
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47592
49540
|
*/
|
|
47593
49541
|
this.writeMemory = async (dto) => {
|
|
47594
49542
|
if (!this.enable.hasValue()) {
|
|
@@ -47598,18 +49546,20 @@ class MemoryAdapter {
|
|
|
47598
49546
|
signalId: dto.signalId,
|
|
47599
49547
|
bucketName: dto.bucketName,
|
|
47600
49548
|
memoryId: dto.memoryId,
|
|
49549
|
+
backtest: dto.backtest,
|
|
47601
49550
|
});
|
|
47602
|
-
|
|
47603
|
-
|
|
47604
|
-
|
|
47605
|
-
await
|
|
47606
|
-
return await instance.writeMemory(dto.memoryId, dto.value, dto.description);
|
|
49551
|
+
if (dto.backtest) {
|
|
49552
|
+
return await MemoryBacktest.writeMemory(dto);
|
|
49553
|
+
}
|
|
49554
|
+
return await MemoryLive.writeMemory(dto);
|
|
47607
49555
|
};
|
|
47608
49556
|
/**
|
|
47609
49557
|
* Search memory using BM25 full-text scoring.
|
|
49558
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47610
49559
|
* @param dto.query - Search query string
|
|
47611
49560
|
* @param dto.signalId - Signal identifier
|
|
47612
49561
|
* @param dto.bucketName - Bucket name
|
|
49562
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47613
49563
|
* @returns Matching entries sorted by relevance score
|
|
47614
49564
|
*/
|
|
47615
49565
|
this.searchMemory = async (dto) => {
|
|
@@ -47620,17 +49570,19 @@ class MemoryAdapter {
|
|
|
47620
49570
|
signalId: dto.signalId,
|
|
47621
49571
|
bucketName: dto.bucketName,
|
|
47622
49572
|
query: dto.query,
|
|
49573
|
+
backtest: dto.backtest,
|
|
47623
49574
|
});
|
|
47624
|
-
|
|
47625
|
-
|
|
47626
|
-
|
|
47627
|
-
await
|
|
47628
|
-
return await instance.searchMemory(dto.query, dto.settings);
|
|
49575
|
+
if (dto.backtest) {
|
|
49576
|
+
return await MemoryBacktest.searchMemory(dto);
|
|
49577
|
+
}
|
|
49578
|
+
return await MemoryLive.searchMemory(dto);
|
|
47629
49579
|
};
|
|
47630
49580
|
/**
|
|
47631
49581
|
* List all entries in memory.
|
|
49582
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47632
49583
|
* @param dto.signalId - Signal identifier
|
|
47633
49584
|
* @param dto.bucketName - Bucket name
|
|
49585
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47634
49586
|
* @returns Array of all stored entries
|
|
47635
49587
|
*/
|
|
47636
49588
|
this.listMemory = async (dto) => {
|
|
@@ -47640,18 +49592,20 @@ class MemoryAdapter {
|
|
|
47640
49592
|
backtest.loggerService.debug(MEMORY_ADAPTER_METHOD_NAME_LIST, {
|
|
47641
49593
|
signalId: dto.signalId,
|
|
47642
49594
|
bucketName: dto.bucketName,
|
|
49595
|
+
backtest: dto.backtest,
|
|
47643
49596
|
});
|
|
47644
|
-
|
|
47645
|
-
|
|
47646
|
-
|
|
47647
|
-
await
|
|
47648
|
-
return await instance.listMemory();
|
|
49597
|
+
if (dto.backtest) {
|
|
49598
|
+
return await MemoryBacktest.listMemory(dto);
|
|
49599
|
+
}
|
|
49600
|
+
return await MemoryLive.listMemory(dto);
|
|
47649
49601
|
};
|
|
47650
49602
|
/**
|
|
47651
49603
|
* Remove an entry from memory.
|
|
49604
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47652
49605
|
* @param dto.memoryId - Unique entry identifier
|
|
47653
49606
|
* @param dto.signalId - Signal identifier
|
|
47654
49607
|
* @param dto.bucketName - Bucket name
|
|
49608
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47655
49609
|
*/
|
|
47656
49610
|
this.removeMemory = async (dto) => {
|
|
47657
49611
|
if (!this.enable.hasValue()) {
|
|
@@ -47661,18 +49615,20 @@ class MemoryAdapter {
|
|
|
47661
49615
|
signalId: dto.signalId,
|
|
47662
49616
|
bucketName: dto.bucketName,
|
|
47663
49617
|
memoryId: dto.memoryId,
|
|
49618
|
+
backtest: dto.backtest,
|
|
47664
49619
|
});
|
|
47665
|
-
|
|
47666
|
-
|
|
47667
|
-
|
|
47668
|
-
await
|
|
47669
|
-
return await instance.removeMemory(dto.memoryId);
|
|
49620
|
+
if (dto.backtest) {
|
|
49621
|
+
return await MemoryBacktest.removeMemory(dto);
|
|
49622
|
+
}
|
|
49623
|
+
return await MemoryLive.removeMemory(dto);
|
|
47670
49624
|
};
|
|
47671
49625
|
/**
|
|
47672
49626
|
* Read a single entry from memory.
|
|
49627
|
+
* Routes to MemoryBacktest or MemoryLive based on dto.backtest.
|
|
47673
49628
|
* @param dto.memoryId - Unique entry identifier
|
|
47674
49629
|
* @param dto.signalId - Signal identifier
|
|
47675
49630
|
* @param dto.bucketName - Bucket name
|
|
49631
|
+
* @param dto.backtest - Flag indicating if the context is backtest or live
|
|
47676
49632
|
* @returns Entry value
|
|
47677
49633
|
* @throws Error if entry not found
|
|
47678
49634
|
*/
|
|
@@ -47684,56 +49640,30 @@ class MemoryAdapter {
|
|
|
47684
49640
|
signalId: dto.signalId,
|
|
47685
49641
|
bucketName: dto.bucketName,
|
|
47686
49642
|
memoryId: dto.memoryId,
|
|
49643
|
+
backtest: dto.backtest,
|
|
47687
49644
|
});
|
|
47688
|
-
|
|
47689
|
-
|
|
47690
|
-
|
|
47691
|
-
await
|
|
47692
|
-
return await instance.readMemory(dto.memoryId);
|
|
47693
|
-
};
|
|
47694
|
-
/**
|
|
47695
|
-
* Switches to in-memory BM25 adapter (default).
|
|
47696
|
-
* All data lives in process memory only.
|
|
47697
|
-
*/
|
|
47698
|
-
this.useLocal = () => {
|
|
47699
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_USE_LOCAL);
|
|
47700
|
-
this.MemoryFactory = MemoryLocalInstance;
|
|
47701
|
-
};
|
|
47702
|
-
/**
|
|
47703
|
-
* Switches to file-system backed adapter.
|
|
47704
|
-
* Data is persisted to ./dump/memory/<signalId>/<bucketName>/.
|
|
47705
|
-
*/
|
|
47706
|
-
this.usePersist = () => {
|
|
47707
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_USE_PERSIST);
|
|
47708
|
-
this.MemoryFactory = MemoryPersistInstance;
|
|
47709
|
-
};
|
|
47710
|
-
/**
|
|
47711
|
-
* Switches to dummy adapter that discards all writes.
|
|
47712
|
-
*/
|
|
47713
|
-
this.useDummy = () => {
|
|
47714
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_USE_DUMMY);
|
|
47715
|
-
this.MemoryFactory = MemoryDummyInstance;
|
|
47716
|
-
};
|
|
47717
|
-
/**
|
|
47718
|
-
* Clears the memoized instance cache.
|
|
47719
|
-
* Call this when process.cwd() changes between strategy iterations
|
|
47720
|
-
* so new instances are created with the updated base path.
|
|
47721
|
-
*/
|
|
47722
|
-
this.clear = () => {
|
|
47723
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_CLEAR);
|
|
47724
|
-
this.getInstance.clear();
|
|
47725
|
-
};
|
|
47726
|
-
/**
|
|
47727
|
-
* Releases resources held by this adapter.
|
|
47728
|
-
* Delegates to disable() to unsubscribe from signal lifecycle events.
|
|
47729
|
-
*/
|
|
47730
|
-
this.dispose = () => {
|
|
47731
|
-
backtest.loggerService.info(MEMORY_ADAPTER_METHOD_NAME_DISPOSE);
|
|
47732
|
-
this.disable();
|
|
49645
|
+
if (dto.backtest) {
|
|
49646
|
+
return await MemoryBacktest.readMemory(dto);
|
|
49647
|
+
}
|
|
49648
|
+
return await MemoryLive.readMemory(dto);
|
|
47733
49649
|
};
|
|
47734
49650
|
}
|
|
47735
49651
|
}
|
|
49652
|
+
/**
|
|
49653
|
+
* Global singleton instance of MemoryAdapter.
|
|
49654
|
+
* Provides unified memory management for backtest and live trading.
|
|
49655
|
+
*/
|
|
47736
49656
|
const Memory = new MemoryAdapter();
|
|
49657
|
+
/**
|
|
49658
|
+
* Global singleton instance of MemoryLiveAdapter.
|
|
49659
|
+
* Provides live trading memory storage with pluggable backends.
|
|
49660
|
+
*/
|
|
49661
|
+
const MemoryLive = new MemoryLiveAdapter();
|
|
49662
|
+
/**
|
|
49663
|
+
* Global singleton instance of MemoryBacktestAdapter.
|
|
49664
|
+
* Provides backtest memory storage with pluggable backends.
|
|
49665
|
+
*/
|
|
49666
|
+
const MemoryBacktest = new MemoryBacktestAdapter();
|
|
47737
49667
|
|
|
47738
49668
|
const WRITE_MEMORY_METHOD_NAME = "memory.writeMemory";
|
|
47739
49669
|
const READ_MEMORY_METHOD_NAME = "memory.readMemory";
|
|
@@ -47743,23 +49673,20 @@ const REMOVE_MEMORY_METHOD_NAME = "memory.removeMemory";
|
|
|
47743
49673
|
/**
|
|
47744
49674
|
* Writes a value to memory scoped to the current signal.
|
|
47745
49675
|
*
|
|
47746
|
-
*
|
|
47747
|
-
* If no pending signal exists, logs a warning and returns without writing.
|
|
47748
|
-
*
|
|
49676
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47749
49677
|
* Automatically detects backtest/live mode from execution context.
|
|
47750
49678
|
*
|
|
47751
49679
|
* @param dto.bucketName - Memory bucket name
|
|
47752
49680
|
* @param dto.memoryId - Unique memory entry identifier
|
|
47753
49681
|
* @param dto.value - Value to store
|
|
49682
|
+
* @param dto.description - BM25 index string for contextual search
|
|
47754
49683
|
* @returns Promise that resolves when write is complete
|
|
47755
49684
|
*
|
|
47756
|
-
* @deprecated Better use Memory.writeMemory with manual signalId argument
|
|
47757
|
-
*
|
|
47758
49685
|
* @example
|
|
47759
49686
|
* ```typescript
|
|
47760
49687
|
* import { writeMemory } from "backtest-kit";
|
|
47761
49688
|
*
|
|
47762
|
-
* await writeMemory({ bucketName: "my-strategy", memoryId: "context", value: { trend: "up", confidence: 0.9 } });
|
|
49689
|
+
* await writeMemory({ bucketName: "my-strategy", memoryId: "context", value: { trend: "up", confidence: 0.9 }, description: "Signal context at entry" });
|
|
47763
49690
|
* ```
|
|
47764
49691
|
*/
|
|
47765
49692
|
async function writeMemory(dto) {
|
|
@@ -47777,33 +49704,41 @@ async function writeMemory(dto) {
|
|
|
47777
49704
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47778
49705
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47779
49706
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47780
|
-
|
|
47781
|
-
if (
|
|
47782
|
-
|
|
49707
|
+
let signal;
|
|
49708
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49709
|
+
await Memory.writeMemory({
|
|
49710
|
+
memoryId,
|
|
49711
|
+
value,
|
|
49712
|
+
signalId: signal.id,
|
|
49713
|
+
bucketName,
|
|
49714
|
+
description,
|
|
49715
|
+
backtest: isBacktest,
|
|
49716
|
+
});
|
|
47783
49717
|
return;
|
|
47784
49718
|
}
|
|
47785
|
-
await
|
|
47786
|
-
|
|
47787
|
-
|
|
47788
|
-
|
|
47789
|
-
|
|
47790
|
-
|
|
47791
|
-
|
|
49719
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49720
|
+
await Memory.writeMemory({
|
|
49721
|
+
memoryId,
|
|
49722
|
+
value,
|
|
49723
|
+
signalId: signal.id,
|
|
49724
|
+
bucketName,
|
|
49725
|
+
description,
|
|
49726
|
+
backtest: isBacktest,
|
|
49727
|
+
});
|
|
49728
|
+
return;
|
|
49729
|
+
}
|
|
49730
|
+
throw new Error(`writeMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
|
|
47792
49731
|
}
|
|
47793
49732
|
/**
|
|
47794
49733
|
* Reads a value from memory scoped to the current signal.
|
|
47795
49734
|
*
|
|
47796
|
-
*
|
|
47797
|
-
* If no pending signal exists, logs a warning and returns null.
|
|
47798
|
-
*
|
|
49735
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47799
49736
|
* Automatically detects backtest/live mode from execution context.
|
|
47800
49737
|
*
|
|
47801
49738
|
* @param dto.bucketName - Memory bucket name
|
|
47802
49739
|
* @param dto.memoryId - Unique memory entry identifier
|
|
47803
|
-
* @returns Promise resolving to stored value
|
|
47804
|
-
* @throws Error if
|
|
47805
|
-
*
|
|
47806
|
-
* @deprecated Better use Memory.readMemory with manual signalId argument
|
|
49740
|
+
* @returns Promise resolving to stored value
|
|
49741
|
+
* @throws Error if no pending or scheduled signal exists, or if entry not found
|
|
47807
49742
|
*
|
|
47808
49743
|
* @example
|
|
47809
49744
|
* ```typescript
|
|
@@ -47827,30 +49762,35 @@ async function readMemory(dto) {
|
|
|
47827
49762
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47828
49763
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47829
49764
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47830
|
-
|
|
47831
|
-
if (
|
|
47832
|
-
|
|
47833
|
-
|
|
49765
|
+
let signal;
|
|
49766
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49767
|
+
return await Memory.readMemory({
|
|
49768
|
+
memoryId,
|
|
49769
|
+
signalId: signal.id,
|
|
49770
|
+
bucketName,
|
|
49771
|
+
backtest: isBacktest,
|
|
49772
|
+
});
|
|
47834
49773
|
}
|
|
47835
|
-
|
|
47836
|
-
|
|
47837
|
-
|
|
47838
|
-
|
|
47839
|
-
|
|
49774
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49775
|
+
return await Memory.readMemory({
|
|
49776
|
+
memoryId,
|
|
49777
|
+
signalId: signal.id,
|
|
49778
|
+
bucketName,
|
|
49779
|
+
backtest: isBacktest,
|
|
49780
|
+
});
|
|
49781
|
+
}
|
|
49782
|
+
throw new Error(`readMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
|
|
47840
49783
|
}
|
|
47841
49784
|
/**
|
|
47842
49785
|
* Searches memory entries for the current signal using BM25 full-text scoring.
|
|
47843
49786
|
*
|
|
47844
|
-
*
|
|
47845
|
-
* If no pending signal exists, logs a warning and returns an empty array.
|
|
47846
|
-
*
|
|
49787
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47847
49788
|
* Automatically detects backtest/live mode from execution context.
|
|
47848
49789
|
*
|
|
47849
49790
|
* @param dto.bucketName - Memory bucket name
|
|
47850
49791
|
* @param dto.query - Search query string
|
|
47851
|
-
* @returns Promise resolving to matching entries sorted by relevance
|
|
47852
|
-
*
|
|
47853
|
-
* @deprecated Better use Memory.searchMemory with manual signalId argument
|
|
49792
|
+
* @returns Promise resolving to matching entries sorted by relevance
|
|
49793
|
+
* @throws Error if no pending or scheduled signal exists
|
|
47854
49794
|
*
|
|
47855
49795
|
* @example
|
|
47856
49796
|
* ```typescript
|
|
@@ -47874,29 +49814,34 @@ async function searchMemory(dto) {
|
|
|
47874
49814
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47875
49815
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47876
49816
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47877
|
-
|
|
47878
|
-
if (
|
|
47879
|
-
|
|
47880
|
-
|
|
49817
|
+
let signal;
|
|
49818
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49819
|
+
return await Memory.searchMemory({
|
|
49820
|
+
query,
|
|
49821
|
+
signalId: signal.id,
|
|
49822
|
+
bucketName,
|
|
49823
|
+
backtest: isBacktest,
|
|
49824
|
+
});
|
|
47881
49825
|
}
|
|
47882
|
-
|
|
47883
|
-
|
|
47884
|
-
|
|
47885
|
-
|
|
47886
|
-
|
|
49826
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49827
|
+
return await Memory.searchMemory({
|
|
49828
|
+
query,
|
|
49829
|
+
signalId: signal.id,
|
|
49830
|
+
bucketName,
|
|
49831
|
+
backtest: isBacktest,
|
|
49832
|
+
});
|
|
49833
|
+
}
|
|
49834
|
+
throw new Error(`searchMemory requires a pending or scheduled signal for symbol=${symbol} query=${query}`);
|
|
47887
49835
|
}
|
|
47888
49836
|
/**
|
|
47889
49837
|
* Lists all memory entries for the current signal.
|
|
47890
49838
|
*
|
|
47891
|
-
*
|
|
47892
|
-
* If no pending signal exists, logs a warning and returns an empty array.
|
|
47893
|
-
*
|
|
49839
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47894
49840
|
* Automatically detects backtest/live mode from execution context.
|
|
47895
49841
|
*
|
|
47896
49842
|
* @param dto.bucketName - Memory bucket name
|
|
47897
|
-
* @returns Promise resolving to all stored entries
|
|
47898
|
-
*
|
|
47899
|
-
* @deprecated Better use Memory.listMemory with manual signalId argument
|
|
49843
|
+
* @returns Promise resolving to all stored entries
|
|
49844
|
+
* @throws Error if no pending or scheduled signal exists
|
|
47900
49845
|
*
|
|
47901
49846
|
* @example
|
|
47902
49847
|
* ```typescript
|
|
@@ -47919,29 +49864,33 @@ async function listMemory(dto) {
|
|
|
47919
49864
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47920
49865
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47921
49866
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47922
|
-
|
|
47923
|
-
if (
|
|
47924
|
-
|
|
47925
|
-
|
|
49867
|
+
let signal;
|
|
49868
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49869
|
+
return await Memory.listMemory({
|
|
49870
|
+
signalId: signal.id,
|
|
49871
|
+
bucketName,
|
|
49872
|
+
backtest: isBacktest,
|
|
49873
|
+
});
|
|
47926
49874
|
}
|
|
47927
|
-
|
|
47928
|
-
|
|
47929
|
-
|
|
47930
|
-
|
|
49875
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49876
|
+
return await Memory.listMemory({
|
|
49877
|
+
signalId: signal.id,
|
|
49878
|
+
bucketName,
|
|
49879
|
+
backtest: isBacktest,
|
|
49880
|
+
});
|
|
49881
|
+
}
|
|
49882
|
+
throw new Error(`listMemory requires a pending or scheduled signal for symbol=${symbol} bucketName=${bucketName}`);
|
|
47931
49883
|
}
|
|
47932
49884
|
/**
|
|
47933
49885
|
* Removes a memory entry for the current signal.
|
|
47934
49886
|
*
|
|
47935
|
-
*
|
|
47936
|
-
* If no pending signal exists, logs a warning and returns without removing.
|
|
47937
|
-
*
|
|
49887
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
47938
49888
|
* Automatically detects backtest/live mode from execution context.
|
|
47939
49889
|
*
|
|
47940
49890
|
* @param dto.bucketName - Memory bucket name
|
|
47941
49891
|
* @param dto.memoryId - Unique memory entry identifier
|
|
47942
49892
|
* @returns Promise that resolves when removal is complete
|
|
47943
|
-
*
|
|
47944
|
-
* @deprecated Better use Memory.removeMemory with manual signalId argument
|
|
49893
|
+
* @throws Error if no pending or scheduled signal exists
|
|
47945
49894
|
*
|
|
47946
49895
|
* @example
|
|
47947
49896
|
* ```typescript
|
|
@@ -47965,16 +49914,26 @@ async function removeMemory(dto) {
|
|
|
47965
49914
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
47966
49915
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
47967
49916
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
47968
|
-
|
|
47969
|
-
if (
|
|
47970
|
-
|
|
49917
|
+
let signal;
|
|
49918
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49919
|
+
await Memory.removeMemory({
|
|
49920
|
+
memoryId,
|
|
49921
|
+
signalId: signal.id,
|
|
49922
|
+
bucketName,
|
|
49923
|
+
backtest: isBacktest,
|
|
49924
|
+
});
|
|
47971
49925
|
return;
|
|
47972
49926
|
}
|
|
47973
|
-
await
|
|
47974
|
-
|
|
47975
|
-
|
|
47976
|
-
|
|
47977
|
-
|
|
49927
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
49928
|
+
await Memory.removeMemory({
|
|
49929
|
+
memoryId,
|
|
49930
|
+
signalId: signal.id,
|
|
49931
|
+
bucketName,
|
|
49932
|
+
backtest: isBacktest,
|
|
49933
|
+
});
|
|
49934
|
+
return;
|
|
49935
|
+
}
|
|
49936
|
+
throw new Error(`removeMemory requires a pending or scheduled signal for symbol=${symbol} memoryId=${memoryId}`);
|
|
47978
49937
|
}
|
|
47979
49938
|
|
|
47980
49939
|
const CREATE_KEY_FN$3 = (signalId, bucketName) => `${signalId}-${bucketName}`;
|
|
@@ -48069,11 +50028,12 @@ const RENDER_TABLE_FN = (rows) => {
|
|
|
48069
50028
|
* Scoped to (signalId, bucketName) via constructor.
|
|
48070
50029
|
*/
|
|
48071
50030
|
class DumpBothInstance {
|
|
48072
|
-
constructor(signalId, bucketName) {
|
|
50031
|
+
constructor(signalId, bucketName, backtest) {
|
|
48073
50032
|
this.signalId = signalId;
|
|
48074
50033
|
this.bucketName = bucketName;
|
|
48075
|
-
this.
|
|
48076
|
-
this.
|
|
50034
|
+
this.backtest = backtest;
|
|
50035
|
+
this._memory = new DumpMemoryInstance(signalId, bucketName, backtest);
|
|
50036
|
+
this._markdown = new DumpMarkdownInstance(signalId, bucketName, backtest);
|
|
48077
50037
|
}
|
|
48078
50038
|
/** Releases resources held by both backends. */
|
|
48079
50039
|
dispose() {
|
|
@@ -48205,9 +50165,10 @@ class DumpBothInstance {
|
|
|
48205
50165
|
* Useful for downstream LLM retrieval via Memory.searchMemory.
|
|
48206
50166
|
*/
|
|
48207
50167
|
class DumpMemoryInstance {
|
|
48208
|
-
constructor(signalId, bucketName) {
|
|
50168
|
+
constructor(signalId, bucketName, backtest) {
|
|
48209
50169
|
this.signalId = signalId;
|
|
48210
50170
|
this.bucketName = bucketName;
|
|
50171
|
+
this.backtest = backtest;
|
|
48211
50172
|
}
|
|
48212
50173
|
/**
|
|
48213
50174
|
* Stores the full agent message history in Memory as a `{ messages }` object.
|
|
@@ -48233,6 +50194,7 @@ class DumpMemoryInstance {
|
|
|
48233
50194
|
signalId: this.signalId,
|
|
48234
50195
|
value: { messages },
|
|
48235
50196
|
description,
|
|
50197
|
+
backtest: this.backtest,
|
|
48236
50198
|
});
|
|
48237
50199
|
}
|
|
48238
50200
|
/**
|
|
@@ -48254,6 +50216,7 @@ class DumpMemoryInstance {
|
|
|
48254
50216
|
signalId: this.signalId,
|
|
48255
50217
|
value: record,
|
|
48256
50218
|
description,
|
|
50219
|
+
backtest: this.backtest,
|
|
48257
50220
|
});
|
|
48258
50221
|
}
|
|
48259
50222
|
/**
|
|
@@ -48276,6 +50239,7 @@ class DumpMemoryInstance {
|
|
|
48276
50239
|
signalId: this.signalId,
|
|
48277
50240
|
value: { rows },
|
|
48278
50241
|
description,
|
|
50242
|
+
backtest: this.backtest,
|
|
48279
50243
|
});
|
|
48280
50244
|
}
|
|
48281
50245
|
/**
|
|
@@ -48297,6 +50261,7 @@ class DumpMemoryInstance {
|
|
|
48297
50261
|
signalId: this.signalId,
|
|
48298
50262
|
value: { content },
|
|
48299
50263
|
description,
|
|
50264
|
+
backtest: this.backtest,
|
|
48300
50265
|
});
|
|
48301
50266
|
}
|
|
48302
50267
|
/**
|
|
@@ -48318,6 +50283,7 @@ class DumpMemoryInstance {
|
|
|
48318
50283
|
signalId: this.signalId,
|
|
48319
50284
|
value: { content },
|
|
48320
50285
|
description,
|
|
50286
|
+
backtest: this.backtest,
|
|
48321
50287
|
});
|
|
48322
50288
|
}
|
|
48323
50289
|
/**
|
|
@@ -48340,6 +50306,7 @@ class DumpMemoryInstance {
|
|
|
48340
50306
|
signalId: this.signalId,
|
|
48341
50307
|
value: json,
|
|
48342
50308
|
description,
|
|
50309
|
+
backtest: this.backtest,
|
|
48343
50310
|
});
|
|
48344
50311
|
}
|
|
48345
50312
|
/** Releases resources held by this instance. */
|
|
@@ -48361,9 +50328,10 @@ class DumpMemoryInstance {
|
|
|
48361
50328
|
* If the file already exists, the call is skipped (idempotent).
|
|
48362
50329
|
*/
|
|
48363
50330
|
class DumpMarkdownInstance {
|
|
48364
|
-
constructor(signalId, bucketName) {
|
|
50331
|
+
constructor(signalId, bucketName, backtest) {
|
|
48365
50332
|
this.signalId = signalId;
|
|
48366
50333
|
this.bucketName = bucketName;
|
|
50334
|
+
this.backtest = backtest;
|
|
48367
50335
|
}
|
|
48368
50336
|
getFilePath(dumpId) {
|
|
48369
50337
|
return path.join("./dump/agent", this.signalId, this.bucketName, `${dumpId}.md`);
|
|
@@ -48552,9 +50520,10 @@ class DumpMarkdownInstance {
|
|
|
48552
50520
|
* Used for disabling dumps in tests or dry-run scenarios.
|
|
48553
50521
|
*/
|
|
48554
50522
|
class DumpDummyInstance {
|
|
48555
|
-
constructor(signalId, bucketName) {
|
|
50523
|
+
constructor(signalId, bucketName, backtest) {
|
|
48556
50524
|
this.signalId = signalId;
|
|
48557
50525
|
this.bucketName = bucketName;
|
|
50526
|
+
this.backtest = backtest;
|
|
48558
50527
|
}
|
|
48559
50528
|
/** No-op. */
|
|
48560
50529
|
async dumpAgentAnswer() {
|
|
@@ -48597,7 +50566,7 @@ class DumpDummyInstance {
|
|
|
48597
50566
|
class DumpAdapter {
|
|
48598
50567
|
constructor() {
|
|
48599
50568
|
this.DumpFactory = DumpMarkdownInstance;
|
|
48600
|
-
this.getInstance = functoolsKit.memoize(([signalId, bucketName]) => CREATE_KEY_FN$3(signalId, bucketName), (signalId, bucketName) => Reflect.construct(this.DumpFactory, [signalId, bucketName]));
|
|
50569
|
+
this.getInstance = functoolsKit.memoize(([signalId, bucketName]) => CREATE_KEY_FN$3(signalId, bucketName), (signalId, bucketName, backtest) => Reflect.construct(this.DumpFactory, [signalId, bucketName, backtest]));
|
|
48601
50570
|
/**
|
|
48602
50571
|
* Activates the adapter by subscribing to signal lifecycle events.
|
|
48603
50572
|
* Clears memoized instances for a signalId when it is cancelled or closed,
|
|
@@ -48648,7 +50617,7 @@ class DumpAdapter {
|
|
|
48648
50617
|
bucketName: context.bucketName,
|
|
48649
50618
|
dumpId: context.dumpId,
|
|
48650
50619
|
});
|
|
48651
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
50620
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48652
50621
|
return await instance.dumpAgentAnswer(messages, context.dumpId, context.description);
|
|
48653
50622
|
};
|
|
48654
50623
|
/**
|
|
@@ -48663,7 +50632,7 @@ class DumpAdapter {
|
|
|
48663
50632
|
bucketName: context.bucketName,
|
|
48664
50633
|
dumpId: context.dumpId,
|
|
48665
50634
|
});
|
|
48666
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
50635
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48667
50636
|
return await instance.dumpRecord(record, context.dumpId, context.description);
|
|
48668
50637
|
};
|
|
48669
50638
|
/**
|
|
@@ -48678,7 +50647,7 @@ class DumpAdapter {
|
|
|
48678
50647
|
bucketName: context.bucketName,
|
|
48679
50648
|
dumpId: context.dumpId,
|
|
48680
50649
|
});
|
|
48681
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
50650
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48682
50651
|
return await instance.dumpTable(rows, context.dumpId, context.description);
|
|
48683
50652
|
};
|
|
48684
50653
|
/**
|
|
@@ -48693,7 +50662,7 @@ class DumpAdapter {
|
|
|
48693
50662
|
bucketName: context.bucketName,
|
|
48694
50663
|
dumpId: context.dumpId,
|
|
48695
50664
|
});
|
|
48696
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
50665
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48697
50666
|
return await instance.dumpText(content, context.dumpId, context.description);
|
|
48698
50667
|
};
|
|
48699
50668
|
/**
|
|
@@ -48708,7 +50677,7 @@ class DumpAdapter {
|
|
|
48708
50677
|
bucketName: context.bucketName,
|
|
48709
50678
|
dumpId: context.dumpId,
|
|
48710
50679
|
});
|
|
48711
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
50680
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48712
50681
|
return await instance.dumpError(content, context.dumpId, context.description);
|
|
48713
50682
|
};
|
|
48714
50683
|
/**
|
|
@@ -48724,7 +50693,7 @@ class DumpAdapter {
|
|
|
48724
50693
|
bucketName: context.bucketName,
|
|
48725
50694
|
dumpId: context.dumpId,
|
|
48726
50695
|
});
|
|
48727
|
-
const instance = this.getInstance(context.signalId, context.bucketName);
|
|
50696
|
+
const instance = this.getInstance(context.signalId, context.bucketName, context.backtest);
|
|
48728
50697
|
return await instance.dumpJson(json, context.dumpId, context.description);
|
|
48729
50698
|
};
|
|
48730
50699
|
/**
|
|
@@ -48790,16 +50759,15 @@ const DUMP_JSON_METHOD_NAME = "dump.dumpJson";
|
|
|
48790
50759
|
/**
|
|
48791
50760
|
* Dumps the full agent message history scoped to the current signal.
|
|
48792
50761
|
*
|
|
48793
|
-
*
|
|
48794
|
-
*
|
|
50762
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50763
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48795
50764
|
*
|
|
48796
50765
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
48797
50766
|
* @param dto.dumpId - Unique identifier for this agent invocation
|
|
48798
50767
|
* @param dto.messages - Full chat history (system, user, assistant, tool)
|
|
48799
50768
|
* @param dto.description - Human-readable label describing the agent invocation context; included in the BM25 index for Memory search
|
|
48800
50769
|
* @returns Promise that resolves when the dump is complete
|
|
48801
|
-
*
|
|
48802
|
-
* @deprecated Better use Dump.dumpAgentAnswer with manual signalId argument
|
|
50770
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48803
50771
|
*
|
|
48804
50772
|
* @example
|
|
48805
50773
|
* ```typescript
|
|
@@ -48824,31 +50792,41 @@ async function dumpAgentAnswer(dto) {
|
|
|
48824
50792
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48825
50793
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48826
50794
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48827
|
-
|
|
48828
|
-
if (
|
|
48829
|
-
|
|
50795
|
+
let signal;
|
|
50796
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50797
|
+
await Dump.dumpAgentAnswer(messages, {
|
|
50798
|
+
dumpId,
|
|
50799
|
+
bucketName,
|
|
50800
|
+
signalId: signal.id,
|
|
50801
|
+
description,
|
|
50802
|
+
backtest: isBacktest,
|
|
50803
|
+
});
|
|
48830
50804
|
return;
|
|
48831
50805
|
}
|
|
48832
|
-
await
|
|
48833
|
-
|
|
48834
|
-
|
|
48835
|
-
|
|
48836
|
-
|
|
48837
|
-
|
|
50806
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50807
|
+
await Dump.dumpAgentAnswer(messages, {
|
|
50808
|
+
dumpId,
|
|
50809
|
+
bucketName,
|
|
50810
|
+
signalId: signal.id,
|
|
50811
|
+
description,
|
|
50812
|
+
backtest: isBacktest,
|
|
50813
|
+
});
|
|
50814
|
+
return;
|
|
50815
|
+
}
|
|
50816
|
+
throw new Error(`dumpAgentAnswer requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
48838
50817
|
}
|
|
48839
50818
|
/**
|
|
48840
50819
|
* Dumps a flat key-value record scoped to the current signal.
|
|
48841
50820
|
*
|
|
48842
|
-
*
|
|
48843
|
-
*
|
|
50821
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50822
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48844
50823
|
*
|
|
48845
50824
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
48846
50825
|
* @param dto.dumpId - Unique identifier for this dump entry
|
|
48847
50826
|
* @param dto.record - Arbitrary flat object to persist
|
|
48848
50827
|
* @param dto.description - Human-readable label describing the record contents; included in the BM25 index for Memory search
|
|
48849
50828
|
* @returns Promise that resolves when the dump is complete
|
|
48850
|
-
*
|
|
48851
|
-
* @deprecated Better use Dump.dumpRecord with manual signalId argument
|
|
50829
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48852
50830
|
*
|
|
48853
50831
|
* @example
|
|
48854
50832
|
* ```typescript
|
|
@@ -48872,23 +50850,34 @@ async function dumpRecord(dto) {
|
|
|
48872
50850
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48873
50851
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48874
50852
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48875
|
-
|
|
48876
|
-
if (
|
|
48877
|
-
|
|
50853
|
+
let signal;
|
|
50854
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50855
|
+
await Dump.dumpRecord(record, {
|
|
50856
|
+
dumpId,
|
|
50857
|
+
bucketName,
|
|
50858
|
+
signalId: signal.id,
|
|
50859
|
+
description,
|
|
50860
|
+
backtest: isBacktest,
|
|
50861
|
+
});
|
|
48878
50862
|
return;
|
|
48879
50863
|
}
|
|
48880
|
-
await
|
|
48881
|
-
|
|
48882
|
-
|
|
48883
|
-
|
|
48884
|
-
|
|
48885
|
-
|
|
50864
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50865
|
+
await Dump.dumpRecord(record, {
|
|
50866
|
+
dumpId,
|
|
50867
|
+
bucketName,
|
|
50868
|
+
signalId: signal.id,
|
|
50869
|
+
description,
|
|
50870
|
+
backtest: isBacktest,
|
|
50871
|
+
});
|
|
50872
|
+
return;
|
|
50873
|
+
}
|
|
50874
|
+
throw new Error(`dumpRecord requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
48886
50875
|
}
|
|
48887
50876
|
/**
|
|
48888
50877
|
* Dumps an array of objects as a table scoped to the current signal.
|
|
48889
50878
|
*
|
|
48890
|
-
*
|
|
48891
|
-
*
|
|
50879
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50880
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48892
50881
|
*
|
|
48893
50882
|
* Column headers are derived from the union of all keys across all rows.
|
|
48894
50883
|
*
|
|
@@ -48897,8 +50886,7 @@ async function dumpRecord(dto) {
|
|
|
48897
50886
|
* @param dto.rows - Array of arbitrary objects to render as a table
|
|
48898
50887
|
* @param dto.description - Human-readable label describing the table contents; included in the BM25 index for Memory search
|
|
48899
50888
|
* @returns Promise that resolves when the dump is complete
|
|
48900
|
-
*
|
|
48901
|
-
* @deprecated Better use Dump.dumpTable with manual signalId argument
|
|
50889
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48902
50890
|
*
|
|
48903
50891
|
* @example
|
|
48904
50892
|
* ```typescript
|
|
@@ -48923,31 +50911,41 @@ async function dumpTable(dto) {
|
|
|
48923
50911
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48924
50912
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48925
50913
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48926
|
-
|
|
48927
|
-
if (
|
|
48928
|
-
|
|
50914
|
+
let signal;
|
|
50915
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50916
|
+
await Dump.dumpTable(rows, {
|
|
50917
|
+
dumpId,
|
|
50918
|
+
bucketName,
|
|
50919
|
+
signalId: signal.id,
|
|
50920
|
+
description,
|
|
50921
|
+
backtest: isBacktest,
|
|
50922
|
+
});
|
|
48929
50923
|
return;
|
|
48930
50924
|
}
|
|
48931
|
-
await
|
|
48932
|
-
|
|
48933
|
-
|
|
48934
|
-
|
|
48935
|
-
|
|
48936
|
-
|
|
50925
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50926
|
+
await Dump.dumpTable(rows, {
|
|
50927
|
+
dumpId,
|
|
50928
|
+
bucketName,
|
|
50929
|
+
signalId: signal.id,
|
|
50930
|
+
description,
|
|
50931
|
+
backtest: isBacktest,
|
|
50932
|
+
});
|
|
50933
|
+
return;
|
|
50934
|
+
}
|
|
50935
|
+
throw new Error(`dumpTable requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
48937
50936
|
}
|
|
48938
50937
|
/**
|
|
48939
50938
|
* Dumps raw text content scoped to the current signal.
|
|
48940
50939
|
*
|
|
48941
|
-
*
|
|
48942
|
-
*
|
|
50940
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50941
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48943
50942
|
*
|
|
48944
50943
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
48945
50944
|
* @param dto.dumpId - Unique identifier for this dump entry
|
|
48946
50945
|
* @param dto.content - Arbitrary text content to persist
|
|
48947
50946
|
* @param dto.description - Human-readable label describing the content; included in the BM25 index for Memory search
|
|
48948
50947
|
* @returns Promise that resolves when the dump is complete
|
|
48949
|
-
*
|
|
48950
|
-
* @deprecated Better use Dump.dumpText with manual signalId argument
|
|
50948
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48951
50949
|
*
|
|
48952
50950
|
* @example
|
|
48953
50951
|
* ```typescript
|
|
@@ -48971,31 +50969,41 @@ async function dumpText(dto) {
|
|
|
48971
50969
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
48972
50970
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
48973
50971
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
48974
|
-
|
|
48975
|
-
if (
|
|
48976
|
-
|
|
50972
|
+
let signal;
|
|
50973
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50974
|
+
await Dump.dumpText(content, {
|
|
50975
|
+
dumpId,
|
|
50976
|
+
bucketName,
|
|
50977
|
+
signalId: signal.id,
|
|
50978
|
+
description,
|
|
50979
|
+
backtest: isBacktest,
|
|
50980
|
+
});
|
|
48977
50981
|
return;
|
|
48978
50982
|
}
|
|
48979
|
-
await
|
|
48980
|
-
|
|
48981
|
-
|
|
48982
|
-
|
|
48983
|
-
|
|
48984
|
-
|
|
50983
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
50984
|
+
await Dump.dumpText(content, {
|
|
50985
|
+
dumpId,
|
|
50986
|
+
bucketName,
|
|
50987
|
+
signalId: signal.id,
|
|
50988
|
+
description,
|
|
50989
|
+
backtest: isBacktest,
|
|
50990
|
+
});
|
|
50991
|
+
return;
|
|
50992
|
+
}
|
|
50993
|
+
throw new Error(`dumpText requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
48985
50994
|
}
|
|
48986
50995
|
/**
|
|
48987
50996
|
* Dumps an error description scoped to the current signal.
|
|
48988
50997
|
*
|
|
48989
|
-
*
|
|
48990
|
-
*
|
|
50998
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
50999
|
+
* Automatically detects backtest/live mode from execution context.
|
|
48991
51000
|
*
|
|
48992
51001
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
48993
51002
|
* @param dto.dumpId - Unique identifier for this dump entry
|
|
48994
51003
|
* @param dto.content - Error message or description to persist
|
|
48995
51004
|
* @param dto.description - Human-readable label describing the error context; included in the BM25 index for Memory search
|
|
48996
51005
|
* @returns Promise that resolves when the dump is complete
|
|
48997
|
-
*
|
|
48998
|
-
* @deprecated Better use Dump.dumpError with manual signalId argument
|
|
51006
|
+
* @throws Error if no pending or scheduled signal exists
|
|
48999
51007
|
*
|
|
49000
51008
|
* @example
|
|
49001
51009
|
* ```typescript
|
|
@@ -49019,29 +51027,41 @@ async function dumpError(dto) {
|
|
|
49019
51027
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
49020
51028
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49021
51029
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
49022
|
-
|
|
49023
|
-
if (
|
|
49024
|
-
|
|
51030
|
+
let signal;
|
|
51031
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
51032
|
+
await Dump.dumpError(content, {
|
|
51033
|
+
dumpId,
|
|
51034
|
+
bucketName,
|
|
51035
|
+
signalId: signal.id,
|
|
51036
|
+
description,
|
|
51037
|
+
backtest: isBacktest,
|
|
51038
|
+
});
|
|
49025
51039
|
return;
|
|
49026
51040
|
}
|
|
49027
|
-
await
|
|
49028
|
-
|
|
49029
|
-
|
|
49030
|
-
|
|
49031
|
-
|
|
49032
|
-
|
|
51041
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
51042
|
+
await Dump.dumpError(content, {
|
|
51043
|
+
dumpId,
|
|
51044
|
+
bucketName,
|
|
51045
|
+
signalId: signal.id,
|
|
51046
|
+
description,
|
|
51047
|
+
backtest: isBacktest,
|
|
51048
|
+
});
|
|
51049
|
+
return;
|
|
51050
|
+
}
|
|
51051
|
+
throw new Error(`dumpError requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
49033
51052
|
}
|
|
49034
51053
|
/**
|
|
49035
51054
|
* Dumps an arbitrary nested object as a fenced JSON block scoped to the current signal.
|
|
49036
51055
|
*
|
|
49037
|
-
*
|
|
49038
|
-
*
|
|
51056
|
+
* Resolves the active pending or scheduled signal automatically from execution context.
|
|
51057
|
+
* Automatically detects backtest/live mode from execution context.
|
|
49039
51058
|
*
|
|
49040
51059
|
* @param dto.bucketName - Bucket name grouping dumps by strategy or agent name
|
|
49041
51060
|
* @param dto.dumpId - Unique identifier for this dump entry
|
|
49042
51061
|
* @param dto.json - Arbitrary nested object to serialize with JSON.stringify
|
|
49043
51062
|
* @param dto.description - Human-readable label describing the object contents; included in the BM25 index for Memory search
|
|
49044
51063
|
* @returns Promise that resolves when the dump is complete
|
|
51064
|
+
* @throws Error if no pending or scheduled signal exists
|
|
49045
51065
|
*
|
|
49046
51066
|
* @deprecated Prefer dumpRecord — flat key-value structure maps naturally to markdown tables and SQL storage
|
|
49047
51067
|
*
|
|
@@ -49067,17 +51087,28 @@ async function dumpJson(dto) {
|
|
|
49067
51087
|
const { backtest: isBacktest, symbol } = backtest.executionContextService.context;
|
|
49068
51088
|
const { exchangeName, frameName, strategyName } = backtest.methodContextService.context;
|
|
49069
51089
|
const currentPrice = await backtest.exchangeConnectionService.getAveragePrice(symbol);
|
|
49070
|
-
|
|
49071
|
-
if (
|
|
49072
|
-
|
|
51090
|
+
let signal;
|
|
51091
|
+
if (signal = await backtest.strategyCoreService.getPendingSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
51092
|
+
await Dump.dumpJson(json, {
|
|
51093
|
+
dumpId,
|
|
51094
|
+
bucketName,
|
|
51095
|
+
signalId: signal.id,
|
|
51096
|
+
description,
|
|
51097
|
+
backtest: isBacktest,
|
|
51098
|
+
});
|
|
49073
51099
|
return;
|
|
49074
51100
|
}
|
|
49075
|
-
await
|
|
49076
|
-
|
|
49077
|
-
|
|
49078
|
-
|
|
49079
|
-
|
|
49080
|
-
|
|
51101
|
+
if (signal = await backtest.strategyCoreService.getScheduledSignal(isBacktest, symbol, currentPrice, { exchangeName, frameName, strategyName })) {
|
|
51102
|
+
await Dump.dumpJson(json, {
|
|
51103
|
+
dumpId,
|
|
51104
|
+
bucketName,
|
|
51105
|
+
signalId: signal.id,
|
|
51106
|
+
description,
|
|
51107
|
+
backtest: isBacktest,
|
|
51108
|
+
});
|
|
51109
|
+
return;
|
|
51110
|
+
}
|
|
51111
|
+
throw new Error(`dumpJson requires a pending or scheduled signal for symbol=${symbol} dumpId=${dumpId}`);
|
|
49081
51112
|
}
|
|
49082
51113
|
|
|
49083
51114
|
/**
|
|
@@ -50458,7 +52489,7 @@ class LogAdapter {
|
|
|
50458
52489
|
*/
|
|
50459
52490
|
const Log = new LogAdapter();
|
|
50460
52491
|
|
|
50461
|
-
const METHOD_NAME_CREATE_SNAPSHOT = "
|
|
52492
|
+
const METHOD_NAME_CREATE_SNAPSHOT = "SystemUtils.createSnapshot";
|
|
50462
52493
|
/** List of all global subjects whose listeners should be snapshotted for session isolation */
|
|
50463
52494
|
const SUBJECT_ISOLATION_LIST = [
|
|
50464
52495
|
activePingSubject,
|
|
@@ -50505,7 +52536,7 @@ const CREATE_SUBJECT_SNAPSHOT_FN = (subject) => {
|
|
|
50505
52536
|
* Allows temporarily detaching all subject subscriptions so that one session
|
|
50506
52537
|
* does not interfere with another, then restoring them afterwards.
|
|
50507
52538
|
*/
|
|
50508
|
-
class
|
|
52539
|
+
class SystemUtils {
|
|
50509
52540
|
constructor() {
|
|
50510
52541
|
/**
|
|
50511
52542
|
* Snapshots the current listener state of every global subject by replacing
|
|
@@ -50519,7 +52550,7 @@ class SessionUtils {
|
|
|
50519
52550
|
};
|
|
50520
52551
|
}
|
|
50521
52552
|
}
|
|
50522
|
-
const
|
|
52553
|
+
const System = new SystemUtils();
|
|
50523
52554
|
|
|
50524
52555
|
const SCHEDULE_METHOD_NAME_GET_DATA = "ScheduleUtils.getData";
|
|
50525
52556
|
const SCHEDULE_METHOD_NAME_GET_REPORT = "ScheduleUtils.getReport";
|
|
@@ -59565,6 +61596,10 @@ exports.MarkdownFolderBase = MarkdownFolderBase;
|
|
|
59565
61596
|
exports.MarkdownWriter = MarkdownWriter;
|
|
59566
61597
|
exports.MaxDrawdown = MaxDrawdown;
|
|
59567
61598
|
exports.Memory = Memory;
|
|
61599
|
+
exports.MemoryBacktest = MemoryBacktest;
|
|
61600
|
+
exports.MemoryBacktestAdapter = MemoryBacktestAdapter;
|
|
61601
|
+
exports.MemoryLive = MemoryLive;
|
|
61602
|
+
exports.MemoryLiveAdapter = MemoryLiveAdapter;
|
|
59568
61603
|
exports.MethodContextService = MethodContextService;
|
|
59569
61604
|
exports.Notification = Notification;
|
|
59570
61605
|
exports.NotificationBacktest = NotificationBacktest;
|
|
@@ -59583,7 +61618,9 @@ exports.PersistPartialAdapter = PersistPartialAdapter;
|
|
|
59583
61618
|
exports.PersistRecentAdapter = PersistRecentAdapter;
|
|
59584
61619
|
exports.PersistRiskAdapter = PersistRiskAdapter;
|
|
59585
61620
|
exports.PersistScheduleAdapter = PersistScheduleAdapter;
|
|
61621
|
+
exports.PersistSessionAdapter = PersistSessionAdapter;
|
|
59586
61622
|
exports.PersistSignalAdapter = PersistSignalAdapter;
|
|
61623
|
+
exports.PersistStateAdapter = PersistStateAdapter;
|
|
59587
61624
|
exports.PersistStorageAdapter = PersistStorageAdapter;
|
|
59588
61625
|
exports.Position = Position;
|
|
59589
61626
|
exports.PositionSize = PositionSize;
|
|
@@ -59597,11 +61634,19 @@ exports.ReportWriter = ReportWriter;
|
|
|
59597
61634
|
exports.Risk = Risk;
|
|
59598
61635
|
exports.Schedule = Schedule;
|
|
59599
61636
|
exports.Session = Session;
|
|
61637
|
+
exports.SessionBacktest = SessionBacktest;
|
|
61638
|
+
exports.SessionLive = SessionLive;
|
|
61639
|
+
exports.State = State;
|
|
61640
|
+
exports.StateBacktest = StateBacktest;
|
|
61641
|
+
exports.StateBacktestAdapter = StateBacktestAdapter;
|
|
61642
|
+
exports.StateLive = StateLive;
|
|
61643
|
+
exports.StateLiveAdapter = StateLiveAdapter;
|
|
59600
61644
|
exports.Storage = Storage;
|
|
59601
61645
|
exports.StorageBacktest = StorageBacktest;
|
|
59602
61646
|
exports.StorageLive = StorageLive;
|
|
59603
61647
|
exports.Strategy = Strategy;
|
|
59604
61648
|
exports.Sync = Sync;
|
|
61649
|
+
exports.System = System;
|
|
59605
61650
|
exports.Walker = Walker;
|
|
59606
61651
|
exports.addActionSchema = addActionSchema;
|
|
59607
61652
|
exports.addExchangeSchema = addExchangeSchema;
|
|
@@ -59626,6 +61671,7 @@ exports.commitTrailingStop = commitTrailingStop;
|
|
|
59626
61671
|
exports.commitTrailingStopCost = commitTrailingStopCost;
|
|
59627
61672
|
exports.commitTrailingTake = commitTrailingTake;
|
|
59628
61673
|
exports.commitTrailingTakeCost = commitTrailingTakeCost;
|
|
61674
|
+
exports.createSignalState = createSignalState;
|
|
59629
61675
|
exports.dumpAgentAnswer = dumpAgentAnswer;
|
|
59630
61676
|
exports.dumpError = dumpError;
|
|
59631
61677
|
exports.dumpJson = dumpJson;
|
|
@@ -59692,6 +61738,8 @@ exports.getPositionWaitingMinutes = getPositionWaitingMinutes;
|
|
|
59692
61738
|
exports.getRawCandles = getRawCandles;
|
|
59693
61739
|
exports.getRiskSchema = getRiskSchema;
|
|
59694
61740
|
exports.getScheduledSignal = getScheduledSignal;
|
|
61741
|
+
exports.getSessionData = getSessionData;
|
|
61742
|
+
exports.getSignalState = getSignalState;
|
|
59695
61743
|
exports.getSizingSchema = getSizingSchema;
|
|
59696
61744
|
exports.getStrategySchema = getStrategySchema;
|
|
59697
61745
|
exports.getSymbol = getSymbol;
|
|
@@ -59777,6 +61825,8 @@ exports.set = set;
|
|
|
59777
61825
|
exports.setColumns = setColumns;
|
|
59778
61826
|
exports.setConfig = setConfig;
|
|
59779
61827
|
exports.setLogger = setLogger;
|
|
61828
|
+
exports.setSessionData = setSessionData;
|
|
61829
|
+
exports.setSignalState = setSignalState;
|
|
59780
61830
|
exports.shutdown = shutdown;
|
|
59781
61831
|
exports.slPercentShiftToPrice = slPercentShiftToPrice;
|
|
59782
61832
|
exports.slPriceToPercentShift = slPriceToPercentShift;
|