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