backtest-kit 1.4.5 → 1.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.mjs CHANGED
@@ -1214,24 +1214,25 @@ const PersistBase = makeExtendable(class {
1214
1214
  class PersistSignalUtils {
1215
1215
  constructor() {
1216
1216
  this.PersistSignalFactory = PersistBase;
1217
- this.getSignalStorage = memoize(([strategyName]) => `${strategyName}`, (strategyName) => Reflect.construct(this.PersistSignalFactory, [
1218
- strategyName,
1217
+ this.getSignalStorage = memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, (symbol, strategyName) => Reflect.construct(this.PersistSignalFactory, [
1218
+ `${symbol}_${strategyName}`,
1219
1219
  `./dump/data/signal/`,
1220
1220
  ]));
1221
1221
  /**
1222
- * Reads persisted signal data for a strategy and symbol.
1222
+ * Reads persisted signal data for a symbol and strategy.
1223
1223
  *
1224
1224
  * Called by ClientStrategy.waitForInit() to restore state.
1225
1225
  * Returns null if no signal exists.
1226
1226
  *
1227
- * @param strategyName - Strategy identifier
1228
1227
  * @param symbol - Trading pair symbol
1228
+ * @param strategyName - Strategy identifier
1229
1229
  * @returns Promise resolving to signal or null
1230
1230
  */
1231
- this.readSignalData = async (strategyName, symbol) => {
1231
+ this.readSignalData = async (symbol, strategyName) => {
1232
1232
  backtest$1.loggerService.info(PERSIST_SIGNAL_UTILS_METHOD_NAME_READ_DATA);
1233
- const isInitial = !this.getSignalStorage.has(strategyName);
1234
- const stateStorage = this.getSignalStorage(strategyName);
1233
+ const key = `${symbol}:${strategyName}`;
1234
+ const isInitial = !this.getSignalStorage.has(key);
1235
+ const stateStorage = this.getSignalStorage(symbol, strategyName);
1235
1236
  await stateStorage.waitForInit(isInitial);
1236
1237
  if (await stateStorage.hasValue(symbol)) {
1237
1238
  return await stateStorage.readValue(symbol);
@@ -1245,14 +1246,15 @@ class PersistSignalUtils {
1245
1246
  * Uses atomic writes to prevent corruption on crashes.
1246
1247
  *
1247
1248
  * @param signalRow - Signal data (null to clear)
1248
- * @param strategyName - Strategy identifier
1249
1249
  * @param symbol - Trading pair symbol
1250
+ * @param strategyName - Strategy identifier
1250
1251
  * @returns Promise that resolves when write is complete
1251
1252
  */
1252
- this.writeSignalData = async (signalRow, strategyName, symbol) => {
1253
+ this.writeSignalData = async (signalRow, symbol, strategyName) => {
1253
1254
  backtest$1.loggerService.info(PERSIST_SIGNAL_UTILS_METHOD_NAME_WRITE_DATA);
1254
- const isInitial = !this.getSignalStorage.has(strategyName);
1255
- const stateStorage = this.getSignalStorage(strategyName);
1255
+ const key = `${symbol}:${strategyName}`;
1256
+ const isInitial = !this.getSignalStorage.has(key);
1257
+ const stateStorage = this.getSignalStorage(symbol, strategyName);
1256
1258
  await stateStorage.waitForInit(isInitial);
1257
1259
  await stateStorage.writeValue(symbol, signalRow);
1258
1260
  };
@@ -1403,24 +1405,25 @@ const PersistRiskAdapter = new PersistRiskUtils();
1403
1405
  class PersistScheduleUtils {
1404
1406
  constructor() {
1405
1407
  this.PersistScheduleFactory = PersistBase;
1406
- this.getScheduleStorage = memoize(([strategyName]) => `${strategyName}`, (strategyName) => Reflect.construct(this.PersistScheduleFactory, [
1407
- strategyName,
1408
+ this.getScheduleStorage = memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, (symbol, strategyName) => Reflect.construct(this.PersistScheduleFactory, [
1409
+ `${symbol}_${strategyName}`,
1408
1410
  `./dump/data/schedule/`,
1409
1411
  ]));
1410
1412
  /**
1411
- * Reads persisted scheduled signal data for a strategy and symbol.
1413
+ * Reads persisted scheduled signal data for a symbol and strategy.
1412
1414
  *
1413
1415
  * Called by ClientStrategy.waitForInit() to restore scheduled signal state.
1414
1416
  * Returns null if no scheduled signal exists.
1415
1417
  *
1416
- * @param strategyName - Strategy identifier
1417
1418
  * @param symbol - Trading pair symbol
1419
+ * @param strategyName - Strategy identifier
1418
1420
  * @returns Promise resolving to scheduled signal or null
1419
1421
  */
1420
- this.readScheduleData = async (strategyName, symbol) => {
1422
+ this.readScheduleData = async (symbol, strategyName) => {
1421
1423
  backtest$1.loggerService.info(PERSIST_SCHEDULE_UTILS_METHOD_NAME_READ_DATA);
1422
- const isInitial = !this.getScheduleStorage.has(strategyName);
1423
- const stateStorage = this.getScheduleStorage(strategyName);
1424
+ const key = `${symbol}:${strategyName}`;
1425
+ const isInitial = !this.getScheduleStorage.has(key);
1426
+ const stateStorage = this.getScheduleStorage(symbol, strategyName);
1424
1427
  await stateStorage.waitForInit(isInitial);
1425
1428
  if (await stateStorage.hasValue(symbol)) {
1426
1429
  return await stateStorage.readValue(symbol);
@@ -1434,14 +1437,15 @@ class PersistScheduleUtils {
1434
1437
  * Uses atomic writes to prevent corruption on crashes.
1435
1438
  *
1436
1439
  * @param scheduledSignalRow - Scheduled signal data (null to clear)
1437
- * @param strategyName - Strategy identifier
1438
1440
  * @param symbol - Trading pair symbol
1441
+ * @param strategyName - Strategy identifier
1439
1442
  * @returns Promise that resolves when write is complete
1440
1443
  */
1441
- this.writeScheduleData = async (scheduledSignalRow, strategyName, symbol) => {
1444
+ this.writeScheduleData = async (scheduledSignalRow, symbol, strategyName) => {
1442
1445
  backtest$1.loggerService.info(PERSIST_SCHEDULE_UTILS_METHOD_NAME_WRITE_DATA);
1443
- const isInitial = !this.getScheduleStorage.has(strategyName);
1444
- const stateStorage = this.getScheduleStorage(strategyName);
1446
+ const key = `${symbol}:${strategyName}`;
1447
+ const isInitial = !this.getScheduleStorage.has(key);
1448
+ const stateStorage = this.getScheduleStorage(symbol, strategyName);
1445
1449
  await stateStorage.waitForInit(isInitial);
1446
1450
  await stateStorage.writeValue(symbol, scheduledSignalRow);
1447
1451
  };
@@ -1967,7 +1971,7 @@ const WAIT_FOR_INIT_FN$2 = async (self) => {
1967
1971
  return;
1968
1972
  }
1969
1973
  // Restore pending signal
1970
- const pendingSignal = await PersistSignalAdapter.readSignalData(self.params.strategyName, self.params.execution.context.symbol);
1974
+ const pendingSignal = await PersistSignalAdapter.readSignalData(self.params.execution.context.symbol, self.params.strategyName);
1971
1975
  if (pendingSignal) {
1972
1976
  if (pendingSignal.exchangeName !== self.params.method.context.exchangeName) {
1973
1977
  return;
@@ -1983,7 +1987,7 @@ const WAIT_FOR_INIT_FN$2 = async (self) => {
1983
1987
  }
1984
1988
  }
1985
1989
  // Restore scheduled signal
1986
- const scheduledSignal = await PersistScheduleAdapter.readScheduleData(self.params.strategyName, self.params.execution.context.symbol);
1990
+ const scheduledSignal = await PersistScheduleAdapter.readScheduleData(self.params.execution.context.symbol, self.params.strategyName);
1987
1991
  if (scheduledSignal) {
1988
1992
  if (scheduledSignal.exchangeName !== self.params.method.context.exchangeName) {
1989
1993
  return;
@@ -2677,7 +2681,7 @@ class ClientStrategy {
2677
2681
  if (this.params.execution.context.backtest) {
2678
2682
  return;
2679
2683
  }
2680
- await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.strategyName, this.params.execution.context.symbol);
2684
+ await PersistSignalAdapter.writeSignalData(this._pendingSignal, this.params.execution.context.symbol, this.params.strategyName);
2681
2685
  }
2682
2686
  /**
2683
2687
  * Updates scheduled signal and persists to disk in live mode.
@@ -2696,14 +2700,18 @@ class ClientStrategy {
2696
2700
  if (this.params.execution.context.backtest) {
2697
2701
  return;
2698
2702
  }
2699
- await PersistScheduleAdapter.writeScheduleData(this._scheduledSignal, this.params.strategyName, this.params.execution.context.symbol);
2703
+ await PersistScheduleAdapter.writeScheduleData(this._scheduledSignal, this.params.execution.context.symbol, this.params.strategyName);
2700
2704
  }
2701
2705
  /**
2702
2706
  * Retrieves the current pending signal.
2703
2707
  * If no signal is pending, returns null.
2704
2708
  * @returns Promise resolving to the pending signal or null.
2705
2709
  */
2706
- async getPendingSignal() {
2710
+ async getPendingSignal(symbol, strategyName) {
2711
+ this.params.logger.debug("ClientStrategy getPendingSignal", {
2712
+ symbol,
2713
+ strategyName,
2714
+ });
2707
2715
  return this._pendingSignal;
2708
2716
  }
2709
2717
  /**
@@ -2736,8 +2744,11 @@ class ClientStrategy {
2736
2744
  * }
2737
2745
  * ```
2738
2746
  */
2739
- async tick() {
2740
- this.params.logger.debug("ClientStrategy tick");
2747
+ async tick(symbol, strategyName) {
2748
+ this.params.logger.debug("ClientStrategy tick", {
2749
+ symbol,
2750
+ strategyName,
2751
+ });
2741
2752
  // Получаем текущее время в начале tick для консистентности
2742
2753
  const currentTime = this.params.execution.context.when.getTime();
2743
2754
  // Early return if strategy was stopped
@@ -2822,9 +2833,11 @@ class ClientStrategy {
2822
2833
  * console.log(result.closeReason); // "take_profit" | "stop_loss" | "time_expired" | "cancelled"
2823
2834
  * ```
2824
2835
  */
2825
- async backtest(candles) {
2836
+ async backtest(symbol, strategyName, candles) {
2826
2837
  this.params.logger.debug("ClientStrategy backtest", {
2827
- symbol: this.params.execution.context.symbol,
2838
+ symbol,
2839
+ strategyName,
2840
+ contextSymbol: this.params.execution.context.symbol,
2828
2841
  candlesCount: candles.length,
2829
2842
  hasScheduled: !!this._scheduledSignal,
2830
2843
  hasPending: !!this._pendingSignal,
@@ -2940,8 +2953,10 @@ class ClientStrategy {
2940
2953
  * // Existing signal will continue until natural close
2941
2954
  * ```
2942
2955
  */
2943
- async stop() {
2956
+ async stop(symbol, strategyName) {
2944
2957
  this.params.logger.debug("ClientStrategy stop", {
2958
+ symbol,
2959
+ strategyName,
2945
2960
  hasPendingSignal: this._pendingSignal !== null,
2946
2961
  hasScheduledSignal: this._scheduledSignal !== null,
2947
2962
  });
@@ -2962,21 +2977,20 @@ const NOOP_RISK = {
2962
2977
  * Connection service routing strategy operations to correct ClientStrategy instance.
2963
2978
  *
2964
2979
  * Routes all IStrategy method calls to the appropriate strategy implementation
2965
- * based on methodContextService.context.strategyName. Uses memoization to cache
2980
+ * based on symbol-strategy pairs. Uses memoization to cache
2966
2981
  * ClientStrategy instances for performance.
2967
2982
  *
2968
2983
  * Key features:
2969
- * - Automatic strategy routing via method context
2970
- * - Memoized ClientStrategy instances by strategyName
2971
- * - Implements IStrategy interface
2984
+ * - Automatic strategy routing via symbol-strategy pairs
2985
+ * - Memoized ClientStrategy instances by symbol:strategyName
2972
2986
  * - Ensures initialization with waitForInit() before operations
2973
2987
  * - Handles both tick() (live) and backtest() operations
2974
2988
  *
2975
2989
  * @example
2976
2990
  * ```typescript
2977
2991
  * // Used internally by framework
2978
- * const result = await strategyConnectionService.tick();
2979
- * // Automatically routes to correct strategy based on methodContext
2992
+ * const result = await strategyConnectionService.tick(symbol, strategyName);
2993
+ * // Routes to correct strategy instance for symbol-strategy pair
2980
2994
  * ```
2981
2995
  */
2982
2996
  class StrategyConnectionService {
@@ -2989,15 +3003,16 @@ class StrategyConnectionService {
2989
3003
  this.methodContextService = inject(TYPES.methodContextService);
2990
3004
  this.partialConnectionService = inject(TYPES.partialConnectionService);
2991
3005
  /**
2992
- * Retrieves memoized ClientStrategy instance for given strategy name.
3006
+ * Retrieves memoized ClientStrategy instance for given symbol-strategy pair.
2993
3007
  *
2994
3008
  * Creates ClientStrategy on first call, returns cached instance on subsequent calls.
2995
- * Cache key is strategyName string.
3009
+ * Cache key is symbol:strategyName string.
2996
3010
  *
3011
+ * @param symbol - Trading pair symbol
2997
3012
  * @param strategyName - Name of registered strategy schema
2998
3013
  * @returns Configured ClientStrategy instance
2999
3014
  */
3000
- this.getStrategy = memoize(([strategyName]) => `${strategyName}`, (strategyName) => {
3015
+ this.getStrategy = memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, (symbol, strategyName) => {
3001
3016
  const { riskName, getSignal, interval, callbacks } = this.strategySchemaService.get(strategyName);
3002
3017
  return new ClientStrategy({
3003
3018
  interval,
@@ -3018,14 +3033,18 @@ class StrategyConnectionService {
3018
3033
  * If no active signal exists, returns null.
3019
3034
  * Used internally for monitoring TP/SL and time expiration.
3020
3035
  *
3036
+ * @param symbol - Trading pair symbol
3021
3037
  * @param strategyName - Name of strategy to get pending signal for
3022
3038
  *
3023
3039
  * @returns Promise resolving to pending signal or null
3024
3040
  */
3025
- this.getPendingSignal = async (strategyName) => {
3026
- this.loggerService.log("strategyConnectionService getPendingSignal");
3027
- const strategy = await this.getStrategy(strategyName);
3028
- return await strategy.getPendingSignal();
3041
+ this.getPendingSignal = async (symbol, strategyName) => {
3042
+ this.loggerService.log("strategyConnectionService getPendingSignal", {
3043
+ symbol,
3044
+ strategyName,
3045
+ });
3046
+ const strategy = this.getStrategy(symbol, strategyName);
3047
+ return await strategy.getPendingSignal(symbol, strategyName);
3029
3048
  };
3030
3049
  /**
3031
3050
  * Executes live trading tick for current strategy.
@@ -3033,13 +3052,18 @@ class StrategyConnectionService {
3033
3052
  * Waits for strategy initialization before processing tick.
3034
3053
  * Evaluates current market conditions and returns signal state.
3035
3054
  *
3055
+ * @param symbol - Trading pair symbol
3056
+ * @param strategyName - Name of strategy to tick
3036
3057
  * @returns Promise resolving to tick result (idle, opened, active, closed)
3037
3058
  */
3038
- this.tick = async () => {
3039
- this.loggerService.log("strategyConnectionService tick");
3040
- const strategy = await this.getStrategy(this.methodContextService.context.strategyName);
3059
+ this.tick = async (symbol, strategyName) => {
3060
+ this.loggerService.log("strategyConnectionService tick", {
3061
+ symbol,
3062
+ strategyName,
3063
+ });
3064
+ const strategy = this.getStrategy(symbol, strategyName);
3041
3065
  await strategy.waitForInit();
3042
- const tick = await strategy.tick();
3066
+ const tick = await strategy.tick(symbol, strategyName);
3043
3067
  {
3044
3068
  if (this.executionContextService.context.backtest) {
3045
3069
  await signalBacktestEmitter.next(tick);
@@ -3057,16 +3081,20 @@ class StrategyConnectionService {
3057
3081
  * Waits for strategy initialization before processing candles.
3058
3082
  * Evaluates strategy signals against historical data.
3059
3083
  *
3084
+ * @param symbol - Trading pair symbol
3085
+ * @param strategyName - Name of strategy to backtest
3060
3086
  * @param candles - Array of historical candle data to backtest
3061
3087
  * @returns Promise resolving to backtest result (signal or idle)
3062
3088
  */
3063
- this.backtest = async (candles) => {
3089
+ this.backtest = async (symbol, strategyName, candles) => {
3064
3090
  this.loggerService.log("strategyConnectionService backtest", {
3091
+ symbol,
3092
+ strategyName,
3065
3093
  candleCount: candles.length,
3066
3094
  });
3067
- const strategy = await this.getStrategy(this.methodContextService.context.strategyName);
3095
+ const strategy = this.getStrategy(symbol, strategyName);
3068
3096
  await strategy.waitForInit();
3069
- const tick = await strategy.backtest(candles);
3097
+ const tick = await strategy.backtest(symbol, strategyName, candles);
3070
3098
  {
3071
3099
  if (this.executionContextService.context.backtest) {
3072
3100
  await signalBacktestEmitter.next(tick);
@@ -3081,15 +3109,17 @@ class StrategyConnectionService {
3081
3109
  * Delegates to ClientStrategy.stop() which sets internal flag to prevent
3082
3110
  * getSignal from being called on subsequent ticks.
3083
3111
  *
3112
+ * @param symbol - Trading pair symbol
3084
3113
  * @param strategyName - Name of strategy to stop
3085
3114
  * @returns Promise that resolves when stop flag is set
3086
3115
  */
3087
- this.stop = async (strategyName) => {
3116
+ this.stop = async (symbol, strategyName) => {
3088
3117
  this.loggerService.log("strategyConnectionService stop", {
3118
+ symbol,
3089
3119
  strategyName,
3090
3120
  });
3091
- const strategy = this.getStrategy(strategyName);
3092
- await strategy.stop();
3121
+ const strategy = this.getStrategy(symbol, strategyName);
3122
+ await strategy.stop(symbol, strategyName);
3093
3123
  };
3094
3124
  /**
3095
3125
  * Clears the memoized ClientStrategy instance from cache.
@@ -3097,13 +3127,19 @@ class StrategyConnectionService {
3097
3127
  * Forces re-initialization of strategy on next getStrategy call.
3098
3128
  * Useful for resetting strategy state or releasing resources.
3099
3129
  *
3100
- * @param strategyName - Name of strategy to clear from cache
3130
+ * @param ctx - Optional context with symbol and strategyName (clears all if not provided)
3101
3131
  */
3102
- this.clear = async (strategyName) => {
3132
+ this.clear = async (ctx) => {
3103
3133
  this.loggerService.log("strategyConnectionService clear", {
3104
- strategyName,
3134
+ ctx,
3105
3135
  });
3106
- this.getStrategy.clear(strategyName);
3136
+ if (ctx) {
3137
+ const key = `${ctx.symbol}:${ctx.strategyName}`;
3138
+ this.getStrategy.clear(key);
3139
+ }
3140
+ else {
3141
+ this.getStrategy.clear();
3142
+ }
3107
3143
  };
3108
3144
  }
3109
3145
  }
@@ -3914,13 +3950,15 @@ class StrategyGlobalService {
3914
3950
  /**
3915
3951
  * Validates strategy and associated risk configuration.
3916
3952
  *
3917
- * Memoized to avoid redundant validations for the same strategy.
3953
+ * Memoized to avoid redundant validations for the same symbol-strategy pair.
3918
3954
  * Logs validation activity.
3955
+ * @param symbol - Trading pair symbol
3919
3956
  * @param strategyName - Name of the strategy to validate
3920
3957
  * @returns Promise that resolves when validation is complete
3921
3958
  */
3922
- this.validate = memoize(([strategyName]) => `${strategyName}`, async (strategyName) => {
3959
+ this.validate = memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, async (symbol, strategyName) => {
3923
3960
  this.loggerService.log(METHOD_NAME_VALIDATE, {
3961
+ symbol,
3924
3962
  strategyName,
3925
3963
  });
3926
3964
  const strategySchema = this.strategySchemaService.get(strategyName);
@@ -3935,16 +3973,16 @@ class StrategyGlobalService {
3935
3973
  * Used internally for monitoring TP/SL and time expiration.
3936
3974
  *
3937
3975
  * @param symbol - Trading pair symbol
3938
- * @param when - Timestamp for tick evaluation
3939
- * @param backtest - Whether running in backtest mode
3976
+ * @param strategyName - Name of the strategy
3940
3977
  * @returns Promise resolving to pending signal or null
3941
3978
  */
3942
- this.getPendingSignal = async (strategyName) => {
3979
+ this.getPendingSignal = async (symbol, strategyName) => {
3943
3980
  this.loggerService.log("strategyGlobalService getPendingSignal", {
3981
+ symbol,
3944
3982
  strategyName,
3945
3983
  });
3946
- await this.validate(this.methodContextService.context.strategyName);
3947
- return await this.strategyConnectionService.getPendingSignal(strategyName);
3984
+ await this.validate(symbol, strategyName);
3985
+ return await this.strategyConnectionService.getPendingSignal(symbol, strategyName);
3948
3986
  };
3949
3987
  /**
3950
3988
  * Checks signal status at a specific timestamp.
@@ -3963,9 +4001,10 @@ class StrategyGlobalService {
3963
4001
  when,
3964
4002
  backtest,
3965
4003
  });
3966
- await this.validate(this.methodContextService.context.strategyName);
4004
+ const strategyName = this.methodContextService.context.strategyName;
4005
+ await this.validate(symbol, strategyName);
3967
4006
  return await ExecutionContextService.runInContext(async () => {
3968
- return await this.strategyConnectionService.tick();
4007
+ return await this.strategyConnectionService.tick(symbol, strategyName);
3969
4008
  }, {
3970
4009
  symbol,
3971
4010
  when,
@@ -3991,9 +4030,10 @@ class StrategyGlobalService {
3991
4030
  when,
3992
4031
  backtest,
3993
4032
  });
3994
- await this.validate(this.methodContextService.context.strategyName);
4033
+ const strategyName = this.methodContextService.context.strategyName;
4034
+ await this.validate(symbol, strategyName);
3995
4035
  return await ExecutionContextService.runInContext(async () => {
3996
- return await this.strategyConnectionService.backtest(candles);
4036
+ return await this.strategyConnectionService.backtest(symbol, strategyName, candles);
3997
4037
  }, {
3998
4038
  symbol,
3999
4039
  when,
@@ -4006,15 +4046,17 @@ class StrategyGlobalService {
4006
4046
  * Delegates to StrategyConnectionService.stop() to set internal flag.
4007
4047
  * Does not require execution context.
4008
4048
  *
4049
+ * @param symbol - Trading pair symbol
4009
4050
  * @param strategyName - Name of strategy to stop
4010
4051
  * @returns Promise that resolves when stop flag is set
4011
4052
  */
4012
- this.stop = async (strategyName) => {
4053
+ this.stop = async (symbol, strategyName) => {
4013
4054
  this.loggerService.log("strategyGlobalService stop", {
4055
+ symbol,
4014
4056
  strategyName,
4015
4057
  });
4016
- await this.validate(strategyName);
4017
- return await this.strategyConnectionService.stop(strategyName);
4058
+ await this.validate(symbol, strategyName);
4059
+ return await this.strategyConnectionService.stop(symbol, strategyName);
4018
4060
  };
4019
4061
  /**
4020
4062
  * Clears the memoized ClientStrategy instance from cache.
@@ -4022,16 +4064,16 @@ class StrategyGlobalService {
4022
4064
  * Delegates to StrategyConnectionService.clear() to remove strategy from cache.
4023
4065
  * Forces re-initialization of strategy on next operation.
4024
4066
  *
4025
- * @param strategyName - Name of strategy to clear from cache
4067
+ * @param ctx - Optional context with symbol and strategyName (clears all if not provided)
4026
4068
  */
4027
- this.clear = async (strategyName) => {
4069
+ this.clear = async (ctx) => {
4028
4070
  this.loggerService.log("strategyGlobalService clear", {
4029
- strategyName,
4071
+ ctx,
4030
4072
  });
4031
- if (strategyName) {
4032
- await this.validate(strategyName);
4073
+ if (ctx) {
4074
+ await this.validate(ctx.symbol, ctx.strategyName);
4033
4075
  }
4034
- return await this.strategyConnectionService.clear(strategyName);
4076
+ return await this.strategyConnectionService.clear(ctx);
4035
4077
  };
4036
4078
  }
4037
4079
  }
@@ -5006,8 +5048,14 @@ class WalkerLogicPrivateService {
5006
5048
  let strategiesTested = 0;
5007
5049
  let bestMetric = null;
5008
5050
  let bestStrategy = null;
5051
+ let pendingStrategy;
5009
5052
  const listenStop = walkerStopSubject
5010
- .filter((walkerName) => walkerName === context.walkerName)
5053
+ .filter((data) => {
5054
+ let isOk = true;
5055
+ isOk = isOk && data.symbol === symbol;
5056
+ isOk = isOk && data.strategyName === pendingStrategy;
5057
+ return isOk;
5058
+ })
5011
5059
  .map(() => CANCEL_SYMBOL)
5012
5060
  .toPromise();
5013
5061
  // Run backtest for each strategy
@@ -5025,6 +5073,7 @@ class WalkerLogicPrivateService {
5025
5073
  exchangeName: context.exchangeName,
5026
5074
  frameName: context.frameName,
5027
5075
  });
5076
+ pendingStrategy = strategyName;
5028
5077
  const result = await Promise.race([
5029
5078
  await resolveDocuments(iterator),
5030
5079
  listenStop,
@@ -5040,7 +5089,7 @@ class WalkerLogicPrivateService {
5040
5089
  symbol,
5041
5090
  });
5042
5091
  // Get statistics from BacktestMarkdownService
5043
- const stats = await this.backtestMarkdownService.getData(strategyName);
5092
+ const stats = await this.backtestMarkdownService.getData(symbol, strategyName);
5044
5093
  // Extract metric value
5045
5094
  const value = stats[metric];
5046
5095
  const metricValue = value !== null &&
@@ -5099,7 +5148,7 @@ class WalkerLogicPrivateService {
5099
5148
  bestStrategy,
5100
5149
  bestMetric,
5101
5150
  bestStats: bestStrategy !== null
5102
- ? await this.backtestMarkdownService.getData(bestStrategy)
5151
+ ? await this.backtestMarkdownService.getData(symbol, bestStrategy)
5103
5152
  : null,
5104
5153
  };
5105
5154
  // Call onComplete callback if provided with final best results
@@ -5655,10 +5704,10 @@ class BacktestMarkdownService {
5655
5704
  /** Logger service for debug output */
5656
5705
  this.loggerService = inject(TYPES.loggerService);
5657
5706
  /**
5658
- * Memoized function to get or create ReportStorage for a strategy.
5659
- * Each strategy gets its own isolated storage instance.
5707
+ * Memoized function to get or create ReportStorage for a symbol-strategy pair.
5708
+ * Each symbol-strategy combination gets its own isolated storage instance.
5660
5709
  */
5661
- this.getStorage = memoize(([strategyName]) => `${strategyName}`, () => new ReportStorage$4());
5710
+ this.getStorage = memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, () => new ReportStorage$4());
5662
5711
  /**
5663
5712
  * Processes tick events and accumulates closed signals.
5664
5713
  * Should be called from IStrategyCallbacks.onTick.
@@ -5685,56 +5734,61 @@ class BacktestMarkdownService {
5685
5734
  if (data.action !== "closed") {
5686
5735
  return;
5687
5736
  }
5688
- const storage = this.getStorage(data.strategyName);
5737
+ const storage = this.getStorage(data.symbol, data.strategyName);
5689
5738
  storage.addSignal(data);
5690
5739
  };
5691
5740
  /**
5692
- * Gets statistical data from all closed signals for a strategy.
5741
+ * Gets statistical data from all closed signals for a symbol-strategy pair.
5693
5742
  * Delegates to ReportStorage.getData().
5694
5743
  *
5744
+ * @param symbol - Trading pair symbol
5695
5745
  * @param strategyName - Strategy name to get data for
5696
5746
  * @returns Statistical data object with all metrics
5697
5747
  *
5698
5748
  * @example
5699
5749
  * ```typescript
5700
5750
  * const service = new BacktestMarkdownService();
5701
- * const stats = await service.getData("my-strategy");
5751
+ * const stats = await service.getData("BTCUSDT", "my-strategy");
5702
5752
  * console.log(stats.sharpeRatio, stats.winRate);
5703
5753
  * ```
5704
5754
  */
5705
- this.getData = async (strategyName) => {
5755
+ this.getData = async (symbol, strategyName) => {
5706
5756
  this.loggerService.log("backtestMarkdownService getData", {
5757
+ symbol,
5707
5758
  strategyName,
5708
5759
  });
5709
- const storage = this.getStorage(strategyName);
5760
+ const storage = this.getStorage(symbol, strategyName);
5710
5761
  return storage.getData();
5711
5762
  };
5712
5763
  /**
5713
- * Generates markdown report with all closed signals for a strategy.
5764
+ * Generates markdown report with all closed signals for a symbol-strategy pair.
5714
5765
  * Delegates to ReportStorage.generateReport().
5715
5766
  *
5767
+ * @param symbol - Trading pair symbol
5716
5768
  * @param strategyName - Strategy name to generate report for
5717
5769
  * @returns Markdown formatted report string with table of all closed signals
5718
5770
  *
5719
5771
  * @example
5720
5772
  * ```typescript
5721
5773
  * const service = new BacktestMarkdownService();
5722
- * const markdown = await service.getReport("my-strategy");
5774
+ * const markdown = await service.getReport("BTCUSDT", "my-strategy");
5723
5775
  * console.log(markdown);
5724
5776
  * ```
5725
5777
  */
5726
- this.getReport = async (strategyName) => {
5778
+ this.getReport = async (symbol, strategyName) => {
5727
5779
  this.loggerService.log("backtestMarkdownService getReport", {
5780
+ symbol,
5728
5781
  strategyName,
5729
5782
  });
5730
- const storage = this.getStorage(strategyName);
5783
+ const storage = this.getStorage(symbol, strategyName);
5731
5784
  return storage.getReport(strategyName);
5732
5785
  };
5733
5786
  /**
5734
- * Saves strategy report to disk.
5787
+ * Saves symbol-strategy report to disk.
5735
5788
  * Creates directory if it doesn't exist.
5736
5789
  * Delegates to ReportStorage.dump().
5737
5790
  *
5791
+ * @param symbol - Trading pair symbol
5738
5792
  * @param strategyName - Strategy name to save report for
5739
5793
  * @param path - Directory path to save report (default: "./dump/backtest")
5740
5794
  *
@@ -5743,43 +5797,50 @@ class BacktestMarkdownService {
5743
5797
  * const service = new BacktestMarkdownService();
5744
5798
  *
5745
5799
  * // Save to default path: ./dump/backtest/my-strategy.md
5746
- * await service.dump("my-strategy");
5800
+ * await service.dump("BTCUSDT", "my-strategy");
5747
5801
  *
5748
5802
  * // Save to custom path: ./custom/path/my-strategy.md
5749
- * await service.dump("my-strategy", "./custom/path");
5803
+ * await service.dump("BTCUSDT", "my-strategy", "./custom/path");
5750
5804
  * ```
5751
5805
  */
5752
- this.dump = async (strategyName, path = "./dump/backtest") => {
5806
+ this.dump = async (symbol, strategyName, path = "./dump/backtest") => {
5753
5807
  this.loggerService.log("backtestMarkdownService dump", {
5808
+ symbol,
5754
5809
  strategyName,
5755
5810
  path,
5756
5811
  });
5757
- const storage = this.getStorage(strategyName);
5812
+ const storage = this.getStorage(symbol, strategyName);
5758
5813
  await storage.dump(strategyName, path);
5759
5814
  };
5760
5815
  /**
5761
5816
  * Clears accumulated signal data from storage.
5762
- * If strategyName is provided, clears only that strategy's data.
5763
- * If strategyName is omitted, clears all strategies' data.
5817
+ * If ctx is provided, clears only that specific symbol-strategy pair's data.
5818
+ * If nothing is provided, clears all data.
5764
5819
  *
5765
- * @param strategyName - Optional strategy name to clear specific strategy data
5820
+ * @param ctx - Optional context with symbol and strategyName
5766
5821
  *
5767
5822
  * @example
5768
5823
  * ```typescript
5769
5824
  * const service = new BacktestMarkdownService();
5770
5825
  *
5771
- * // Clear specific strategy data
5772
- * await service.clear("my-strategy");
5826
+ * // Clear specific symbol-strategy pair
5827
+ * await service.clear({ symbol: "BTCUSDT", strategyName: "my-strategy" });
5773
5828
  *
5774
- * // Clear all strategies' data
5829
+ * // Clear all data
5775
5830
  * await service.clear();
5776
5831
  * ```
5777
5832
  */
5778
- this.clear = async (strategyName) => {
5833
+ this.clear = async (ctx) => {
5779
5834
  this.loggerService.log("backtestMarkdownService clear", {
5780
- strategyName,
5835
+ ctx,
5781
5836
  });
5782
- this.getStorage.clear(strategyName);
5837
+ if (ctx) {
5838
+ const key = `${ctx.symbol}:${ctx.strategyName}`;
5839
+ this.getStorage.clear(key);
5840
+ }
5841
+ else {
5842
+ this.getStorage.clear();
5843
+ }
5783
5844
  };
5784
5845
  /**
5785
5846
  * Initializes the service by subscribing to backtest signal events.
@@ -6178,10 +6239,10 @@ class LiveMarkdownService {
6178
6239
  /** Logger service for debug output */
6179
6240
  this.loggerService = inject(TYPES.loggerService);
6180
6241
  /**
6181
- * Memoized function to get or create ReportStorage for a strategy.
6182
- * Each strategy gets its own isolated storage instance.
6242
+ * Memoized function to get or create ReportStorage for a symbol-strategy pair.
6243
+ * Each symbol-strategy combination gets its own isolated storage instance.
6183
6244
  */
6184
- this.getStorage = memoize(([strategyName]) => `${strategyName}`, () => new ReportStorage$3());
6245
+ this.getStorage = memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, () => new ReportStorage$3());
6185
6246
  /**
6186
6247
  * Processes tick events and accumulates all event types.
6187
6248
  * Should be called from IStrategyCallbacks.onTick.
@@ -6207,7 +6268,7 @@ class LiveMarkdownService {
6207
6268
  this.loggerService.log("liveMarkdownService tick", {
6208
6269
  data,
6209
6270
  });
6210
- const storage = this.getStorage(data.strategyName);
6271
+ const storage = this.getStorage(data.symbol, data.strategyName);
6211
6272
  if (data.action === "idle") {
6212
6273
  storage.addIdleEvent(data.currentPrice);
6213
6274
  }
@@ -6222,52 +6283,57 @@ class LiveMarkdownService {
6222
6283
  }
6223
6284
  };
6224
6285
  /**
6225
- * Gets statistical data from all live trading events for a strategy.
6286
+ * Gets statistical data from all live trading events for a symbol-strategy pair.
6226
6287
  * Delegates to ReportStorage.getData().
6227
6288
  *
6289
+ * @param symbol - Trading pair symbol
6228
6290
  * @param strategyName - Strategy name to get data for
6229
6291
  * @returns Statistical data object with all metrics
6230
6292
  *
6231
6293
  * @example
6232
6294
  * ```typescript
6233
6295
  * const service = new LiveMarkdownService();
6234
- * const stats = await service.getData("my-strategy");
6296
+ * const stats = await service.getData("BTCUSDT", "my-strategy");
6235
6297
  * console.log(stats.sharpeRatio, stats.winRate);
6236
6298
  * ```
6237
6299
  */
6238
- this.getData = async (strategyName) => {
6300
+ this.getData = async (symbol, strategyName) => {
6239
6301
  this.loggerService.log("liveMarkdownService getData", {
6302
+ symbol,
6240
6303
  strategyName,
6241
6304
  });
6242
- const storage = this.getStorage(strategyName);
6305
+ const storage = this.getStorage(symbol, strategyName);
6243
6306
  return storage.getData();
6244
6307
  };
6245
6308
  /**
6246
- * Generates markdown report with all events for a strategy.
6309
+ * Generates markdown report with all events for a symbol-strategy pair.
6247
6310
  * Delegates to ReportStorage.getReport().
6248
6311
  *
6312
+ * @param symbol - Trading pair symbol
6249
6313
  * @param strategyName - Strategy name to generate report for
6250
6314
  * @returns Markdown formatted report string with table of all events
6251
6315
  *
6252
6316
  * @example
6253
6317
  * ```typescript
6254
6318
  * const service = new LiveMarkdownService();
6255
- * const markdown = await service.getReport("my-strategy");
6319
+ * const markdown = await service.getReport("BTCUSDT", "my-strategy");
6256
6320
  * console.log(markdown);
6257
6321
  * ```
6258
6322
  */
6259
- this.getReport = async (strategyName) => {
6323
+ this.getReport = async (symbol, strategyName) => {
6260
6324
  this.loggerService.log("liveMarkdownService getReport", {
6325
+ symbol,
6261
6326
  strategyName,
6262
6327
  });
6263
- const storage = this.getStorage(strategyName);
6328
+ const storage = this.getStorage(symbol, strategyName);
6264
6329
  return storage.getReport(strategyName);
6265
6330
  };
6266
6331
  /**
6267
- * Saves strategy report to disk.
6332
+ * Saves symbol-strategy report to disk.
6268
6333
  * Creates directory if it doesn't exist.
6269
6334
  * Delegates to ReportStorage.dump().
6270
6335
  *
6336
+ * @param symbol - Trading pair symbol
6271
6337
  * @param strategyName - Strategy name to save report for
6272
6338
  * @param path - Directory path to save report (default: "./dump/live")
6273
6339
  *
@@ -6276,43 +6342,50 @@ class LiveMarkdownService {
6276
6342
  * const service = new LiveMarkdownService();
6277
6343
  *
6278
6344
  * // Save to default path: ./dump/live/my-strategy.md
6279
- * await service.dump("my-strategy");
6345
+ * await service.dump("BTCUSDT", "my-strategy");
6280
6346
  *
6281
6347
  * // Save to custom path: ./custom/path/my-strategy.md
6282
- * await service.dump("my-strategy", "./custom/path");
6348
+ * await service.dump("BTCUSDT", "my-strategy", "./custom/path");
6283
6349
  * ```
6284
6350
  */
6285
- this.dump = async (strategyName, path = "./dump/live") => {
6351
+ this.dump = async (symbol, strategyName, path = "./dump/live") => {
6286
6352
  this.loggerService.log("liveMarkdownService dump", {
6353
+ symbol,
6287
6354
  strategyName,
6288
6355
  path,
6289
6356
  });
6290
- const storage = this.getStorage(strategyName);
6357
+ const storage = this.getStorage(symbol, strategyName);
6291
6358
  await storage.dump(strategyName, path);
6292
6359
  };
6293
6360
  /**
6294
6361
  * Clears accumulated event data from storage.
6295
- * If strategyName is provided, clears only that strategy's data.
6296
- * If strategyName is omitted, clears all strategies' data.
6362
+ * If ctx is provided, clears only that specific symbol-strategy pair's data.
6363
+ * If nothing is provided, clears all data.
6297
6364
  *
6298
- * @param strategyName - Optional strategy name to clear specific strategy data
6365
+ * @param ctx - Optional context with symbol and strategyName
6299
6366
  *
6300
6367
  * @example
6301
6368
  * ```typescript
6302
6369
  * const service = new LiveMarkdownService();
6303
6370
  *
6304
- * // Clear specific strategy data
6305
- * await service.clear("my-strategy");
6371
+ * // Clear specific symbol-strategy pair
6372
+ * await service.clear({ symbol: "BTCUSDT", strategyName: "my-strategy" });
6306
6373
  *
6307
- * // Clear all strategies' data
6374
+ * // Clear all data
6308
6375
  * await service.clear();
6309
6376
  * ```
6310
6377
  */
6311
- this.clear = async (strategyName) => {
6378
+ this.clear = async (ctx) => {
6312
6379
  this.loggerService.log("liveMarkdownService clear", {
6313
- strategyName,
6380
+ ctx,
6314
6381
  });
6315
- this.getStorage.clear(strategyName);
6382
+ if (ctx) {
6383
+ const key = `${ctx.symbol}:${ctx.strategyName}`;
6384
+ this.getStorage.clear(key);
6385
+ }
6386
+ else {
6387
+ this.getStorage.clear();
6388
+ }
6316
6389
  };
6317
6390
  /**
6318
6391
  * Initializes the service by subscribing to live signal events.
@@ -6561,10 +6634,10 @@ class ScheduleMarkdownService {
6561
6634
  /** Logger service for debug output */
6562
6635
  this.loggerService = inject(TYPES.loggerService);
6563
6636
  /**
6564
- * Memoized function to get or create ReportStorage for a strategy.
6565
- * Each strategy gets its own isolated storage instance.
6637
+ * Memoized function to get or create ReportStorage for a symbol-strategy pair.
6638
+ * Each symbol-strategy combination gets its own isolated storage instance.
6566
6639
  */
6567
- this.getStorage = memoize(([strategyName]) => `${strategyName}`, () => new ReportStorage$2());
6640
+ this.getStorage = memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, () => new ReportStorage$2());
6568
6641
  /**
6569
6642
  * Processes tick events and accumulates scheduled/cancelled events.
6570
6643
  * Should be called from signalLiveEmitter subscription.
@@ -6583,7 +6656,7 @@ class ScheduleMarkdownService {
6583
6656
  this.loggerService.log("scheduleMarkdownService tick", {
6584
6657
  data,
6585
6658
  });
6586
- const storage = this.getStorage(data.strategyName);
6659
+ const storage = this.getStorage(data.symbol, data.strategyName);
6587
6660
  if (data.action === "scheduled") {
6588
6661
  storage.addScheduledEvent(data);
6589
6662
  }
@@ -6592,52 +6665,57 @@ class ScheduleMarkdownService {
6592
6665
  }
6593
6666
  };
6594
6667
  /**
6595
- * Gets statistical data from all scheduled signal events for a strategy.
6668
+ * Gets statistical data from all scheduled signal events for a symbol-strategy pair.
6596
6669
  * Delegates to ReportStorage.getData().
6597
6670
  *
6671
+ * @param symbol - Trading pair symbol
6598
6672
  * @param strategyName - Strategy name to get data for
6599
6673
  * @returns Statistical data object with all metrics
6600
6674
  *
6601
6675
  * @example
6602
6676
  * ```typescript
6603
6677
  * const service = new ScheduleMarkdownService();
6604
- * const stats = await service.getData("my-strategy");
6678
+ * const stats = await service.getData("BTCUSDT", "my-strategy");
6605
6679
  * console.log(stats.cancellationRate, stats.avgWaitTime);
6606
6680
  * ```
6607
6681
  */
6608
- this.getData = async (strategyName) => {
6682
+ this.getData = async (symbol, strategyName) => {
6609
6683
  this.loggerService.log("scheduleMarkdownService getData", {
6684
+ symbol,
6610
6685
  strategyName,
6611
6686
  });
6612
- const storage = this.getStorage(strategyName);
6687
+ const storage = this.getStorage(symbol, strategyName);
6613
6688
  return storage.getData();
6614
6689
  };
6615
6690
  /**
6616
- * Generates markdown report with all scheduled events for a strategy.
6691
+ * Generates markdown report with all scheduled events for a symbol-strategy pair.
6617
6692
  * Delegates to ReportStorage.getReport().
6618
6693
  *
6694
+ * @param symbol - Trading pair symbol
6619
6695
  * @param strategyName - Strategy name to generate report for
6620
6696
  * @returns Markdown formatted report string with table of all events
6621
6697
  *
6622
6698
  * @example
6623
6699
  * ```typescript
6624
6700
  * const service = new ScheduleMarkdownService();
6625
- * const markdown = await service.getReport("my-strategy");
6701
+ * const markdown = await service.getReport("BTCUSDT", "my-strategy");
6626
6702
  * console.log(markdown);
6627
6703
  * ```
6628
6704
  */
6629
- this.getReport = async (strategyName) => {
6705
+ this.getReport = async (symbol, strategyName) => {
6630
6706
  this.loggerService.log("scheduleMarkdownService getReport", {
6707
+ symbol,
6631
6708
  strategyName,
6632
6709
  });
6633
- const storage = this.getStorage(strategyName);
6710
+ const storage = this.getStorage(symbol, strategyName);
6634
6711
  return storage.getReport(strategyName);
6635
6712
  };
6636
6713
  /**
6637
- * Saves strategy report to disk.
6714
+ * Saves symbol-strategy report to disk.
6638
6715
  * Creates directory if it doesn't exist.
6639
6716
  * Delegates to ReportStorage.dump().
6640
6717
  *
6718
+ * @param symbol - Trading pair symbol
6641
6719
  * @param strategyName - Strategy name to save report for
6642
6720
  * @param path - Directory path to save report (default: "./dump/schedule")
6643
6721
  *
@@ -6646,43 +6724,50 @@ class ScheduleMarkdownService {
6646
6724
  * const service = new ScheduleMarkdownService();
6647
6725
  *
6648
6726
  * // Save to default path: ./dump/schedule/my-strategy.md
6649
- * await service.dump("my-strategy");
6727
+ * await service.dump("BTCUSDT", "my-strategy");
6650
6728
  *
6651
6729
  * // Save to custom path: ./custom/path/my-strategy.md
6652
- * await service.dump("my-strategy", "./custom/path");
6730
+ * await service.dump("BTCUSDT", "my-strategy", "./custom/path");
6653
6731
  * ```
6654
6732
  */
6655
- this.dump = async (strategyName, path = "./dump/schedule") => {
6733
+ this.dump = async (symbol, strategyName, path = "./dump/schedule") => {
6656
6734
  this.loggerService.log("scheduleMarkdownService dump", {
6735
+ symbol,
6657
6736
  strategyName,
6658
6737
  path,
6659
6738
  });
6660
- const storage = this.getStorage(strategyName);
6739
+ const storage = this.getStorage(symbol, strategyName);
6661
6740
  await storage.dump(strategyName, path);
6662
6741
  };
6663
6742
  /**
6664
6743
  * Clears accumulated event data from storage.
6665
- * If strategyName is provided, clears only that strategy's data.
6666
- * If strategyName is omitted, clears all strategies' data.
6744
+ * If ctx is provided, clears only that specific symbol-strategy pair's data.
6745
+ * If nothing is provided, clears all data.
6667
6746
  *
6668
- * @param strategyName - Optional strategy name to clear specific strategy data
6747
+ * @param ctx - Optional context with symbol and strategyName
6669
6748
  *
6670
6749
  * @example
6671
6750
  * ```typescript
6672
6751
  * const service = new ScheduleMarkdownService();
6673
6752
  *
6674
- * // Clear specific strategy data
6675
- * await service.clear("my-strategy");
6753
+ * // Clear specific symbol-strategy pair
6754
+ * await service.clear({ symbol: "BTCUSDT", strategyName: "my-strategy" });
6676
6755
  *
6677
- * // Clear all strategies' data
6756
+ * // Clear all data
6678
6757
  * await service.clear();
6679
6758
  * ```
6680
6759
  */
6681
- this.clear = async (strategyName) => {
6760
+ this.clear = async (ctx) => {
6682
6761
  this.loggerService.log("scheduleMarkdownService clear", {
6683
- strategyName,
6762
+ ctx,
6684
6763
  });
6685
- this.getStorage.clear(strategyName);
6764
+ if (ctx) {
6765
+ const key = `${ctx.symbol}:${ctx.strategyName}`;
6766
+ this.getStorage.clear(key);
6767
+ }
6768
+ else {
6769
+ this.getStorage.clear();
6770
+ }
6686
6771
  };
6687
6772
  /**
6688
6773
  * Initializes the service by subscribing to live signal events.
@@ -6916,10 +7001,10 @@ class PerformanceMarkdownService {
6916
7001
  /** Logger service for debug output */
6917
7002
  this.loggerService = inject(TYPES.loggerService);
6918
7003
  /**
6919
- * Memoized function to get or create PerformanceStorage for a strategy.
6920
- * Each strategy gets its own isolated storage instance.
7004
+ * Memoized function to get or create PerformanceStorage for a symbol-strategy pair.
7005
+ * Each symbol-strategy combination gets its own isolated storage instance.
6921
7006
  */
6922
- this.getStorage = memoize(([strategyName]) => `${strategyName}`, () => new PerformanceStorage());
7007
+ this.getStorage = memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, () => new PerformanceStorage());
6923
7008
  /**
6924
7009
  * Processes performance events and accumulates metrics.
6925
7010
  * Should be called from performance tracking code.
@@ -6930,83 +7015,97 @@ class PerformanceMarkdownService {
6930
7015
  this.loggerService.log("performanceMarkdownService track", {
6931
7016
  event,
6932
7017
  });
7018
+ const symbol = event.symbol || "global";
6933
7019
  const strategyName = event.strategyName || "global";
6934
- const storage = this.getStorage(strategyName);
7020
+ const storage = this.getStorage(symbol, strategyName);
6935
7021
  storage.addEvent(event);
6936
7022
  };
6937
7023
  /**
6938
- * Gets aggregated performance statistics for a strategy.
7024
+ * Gets aggregated performance statistics for a symbol-strategy pair.
6939
7025
  *
7026
+ * @param symbol - Trading pair symbol
6940
7027
  * @param strategyName - Strategy name to get data for
6941
7028
  * @returns Performance statistics with aggregated metrics
6942
7029
  *
6943
7030
  * @example
6944
7031
  * ```typescript
6945
- * const stats = await performanceService.getData("my-strategy");
7032
+ * const stats = await performanceService.getData("BTCUSDT", "my-strategy");
6946
7033
  * console.log("Total time:", stats.totalDuration);
6947
7034
  * console.log("Slowest operation:", Object.values(stats.metricStats)
6948
7035
  * .sort((a, b) => b.avgDuration - a.avgDuration)[0]);
6949
7036
  * ```
6950
7037
  */
6951
- this.getData = async (strategyName) => {
7038
+ this.getData = async (symbol, strategyName) => {
6952
7039
  this.loggerService.log("performanceMarkdownService getData", {
7040
+ symbol,
6953
7041
  strategyName,
6954
7042
  });
6955
- const storage = this.getStorage(strategyName);
7043
+ const storage = this.getStorage(symbol, strategyName);
6956
7044
  return storage.getData(strategyName);
6957
7045
  };
6958
7046
  /**
6959
7047
  * Generates markdown report with performance analysis.
6960
7048
  *
7049
+ * @param symbol - Trading pair symbol
6961
7050
  * @param strategyName - Strategy name to generate report for
6962
7051
  * @returns Markdown formatted report string
6963
7052
  *
6964
7053
  * @example
6965
7054
  * ```typescript
6966
- * const markdown = await performanceService.getReport("my-strategy");
7055
+ * const markdown = await performanceService.getReport("BTCUSDT", "my-strategy");
6967
7056
  * console.log(markdown);
6968
7057
  * ```
6969
7058
  */
6970
- this.getReport = async (strategyName) => {
7059
+ this.getReport = async (symbol, strategyName) => {
6971
7060
  this.loggerService.log("performanceMarkdownService getReport", {
7061
+ symbol,
6972
7062
  strategyName,
6973
7063
  });
6974
- const storage = this.getStorage(strategyName);
7064
+ const storage = this.getStorage(symbol, strategyName);
6975
7065
  return storage.getReport(strategyName);
6976
7066
  };
6977
7067
  /**
6978
7068
  * Saves performance report to disk.
6979
7069
  *
7070
+ * @param symbol - Trading pair symbol
6980
7071
  * @param strategyName - Strategy name to save report for
6981
7072
  * @param path - Directory path to save report
6982
7073
  *
6983
7074
  * @example
6984
7075
  * ```typescript
6985
7076
  * // Save to default path: ./dump/performance/my-strategy.md
6986
- * await performanceService.dump("my-strategy");
7077
+ * await performanceService.dump("BTCUSDT", "my-strategy");
6987
7078
  *
6988
7079
  * // Save to custom path
6989
- * await performanceService.dump("my-strategy", "./custom/path");
7080
+ * await performanceService.dump("BTCUSDT", "my-strategy", "./custom/path");
6990
7081
  * ```
6991
7082
  */
6992
- this.dump = async (strategyName, path = "./dump/performance") => {
7083
+ this.dump = async (symbol, strategyName, path = "./dump/performance") => {
6993
7084
  this.loggerService.log("performanceMarkdownService dump", {
7085
+ symbol,
6994
7086
  strategyName,
6995
7087
  path,
6996
7088
  });
6997
- const storage = this.getStorage(strategyName);
7089
+ const storage = this.getStorage(symbol, strategyName);
6998
7090
  await storage.dump(strategyName, path);
6999
7091
  };
7000
7092
  /**
7001
7093
  * Clears accumulated performance data from storage.
7002
7094
  *
7003
- * @param strategyName - Optional strategy name to clear specific strategy data
7095
+ * @param symbol - Optional trading pair symbol
7096
+ * @param strategyName - Optional strategy name
7004
7097
  */
7005
- this.clear = async (strategyName) => {
7098
+ this.clear = async (ctx) => {
7006
7099
  this.loggerService.log("performanceMarkdownService clear", {
7007
- strategyName,
7100
+ ctx,
7008
7101
  });
7009
- this.getStorage.clear(strategyName);
7102
+ if (ctx) {
7103
+ const key = `${ctx.symbol}:${ctx.strategyName}`;
7104
+ this.getStorage.clear(key);
7105
+ }
7106
+ {
7107
+ this.getStorage.clear();
7108
+ }
7010
7109
  };
7011
7110
  /**
7012
7111
  * Initializes the service by subscribing to performance events.
@@ -8700,7 +8799,7 @@ class OptimizerTemplateService {
8700
8799
  };
8701
8800
  /**
8702
8801
  * Generates text() helper for LLM text generation.
8703
- * Uses Ollama gpt-oss:20b model for market analysis.
8802
+ * Uses Ollama deepseek-v3.1:671b model for market analysis.
8704
8803
  *
8705
8804
  * @param symbol - Trading pair symbol (used in prompt)
8706
8805
  * @returns Generated async text() function
@@ -8725,7 +8824,7 @@ class OptimizerTemplateService {
8725
8824
  ` });`,
8726
8825
  ``,
8727
8826
  ` const response = await ollama.chat({`,
8728
- ` model: "gpt-oss:20b",`,
8827
+ ` model: "deepseek-v3.1:671b",`,
8729
8828
  ` messages: [`,
8730
8829
  ` {`,
8731
8830
  ` role: "system",`,
@@ -8790,7 +8889,7 @@ class OptimizerTemplateService {
8790
8889
  ` });`,
8791
8890
  ``,
8792
8891
  ` const response = await ollama.chat({`,
8793
- ` model: "gpt-oss:20b",`,
8892
+ ` model: "deepseek-v3.1:671b",`,
8794
8893
  ` messages: [`,
8795
8894
  ` {`,
8796
8895
  ` role: "system",`,
@@ -9284,7 +9383,7 @@ const GET_STRATEGY_CODE_FN = async (symbol, self) => {
9284
9383
  const walkerName = `${prefix}_walker`;
9285
9384
  const testFrameName = `${prefix}_test_frame`;
9286
9385
  const strategies = strategyData.map((_, i) => `${prefix}_strategy-${i + 1}`);
9287
- sections.push(await self.params.template.getWalkerTemplate(walkerName, `${prefix}_${exchangeName}`, testFrameName, strategies));
9386
+ sections.push(await self.params.template.getWalkerTemplate(walkerName, `${exchangeName}`, testFrameName, strategies));
9288
9387
  sections.push("");
9289
9388
  }
9290
9389
  // 9. Launcher template
@@ -12301,11 +12400,11 @@ class BacktestUtils {
12301
12400
  context,
12302
12401
  });
12303
12402
  {
12304
- backtest$1.backtestMarkdownService.clear(context.strategyName);
12305
- backtest$1.scheduleMarkdownService.clear(context.strategyName);
12403
+ backtest$1.backtestMarkdownService.clear({ symbol, strategyName: context.strategyName });
12404
+ backtest$1.scheduleMarkdownService.clear({ symbol, strategyName: context.strategyName });
12306
12405
  }
12307
12406
  {
12308
- backtest$1.strategyGlobalService.clear(context.strategyName);
12407
+ backtest$1.strategyGlobalService.clear({ symbol, strategyName: context.strategyName });
12309
12408
  }
12310
12409
  {
12311
12410
  const { riskName } = backtest$1.strategySchemaService.get(context.strategyName);
@@ -12359,9 +12458,9 @@ class BacktestUtils {
12359
12458
  };
12360
12459
  task().catch((error) => errorEmitter.next(new Error(getErrorMessage(error))));
12361
12460
  return () => {
12362
- backtest$1.strategyGlobalService.stop(context.strategyName);
12461
+ backtest$1.strategyGlobalService.stop(symbol, context.strategyName);
12363
12462
  backtest$1.strategyGlobalService
12364
- .getPendingSignal(context.strategyName)
12463
+ .getPendingSignal(symbol, context.strategyName)
12365
12464
  .then(async (pendingSignal) => {
12366
12465
  if (pendingSignal) {
12367
12466
  return;
@@ -12380,40 +12479,44 @@ class BacktestUtils {
12380
12479
  };
12381
12480
  };
12382
12481
  /**
12383
- * Gets statistical data from all closed signals for a strategy.
12482
+ * Gets statistical data from all closed signals for a symbol-strategy pair.
12384
12483
  *
12484
+ * @param symbol - Trading pair symbol
12385
12485
  * @param strategyName - Strategy name to get data for
12386
12486
  * @returns Promise resolving to statistical data object
12387
12487
  *
12388
12488
  * @example
12389
12489
  * ```typescript
12390
- * const stats = await Backtest.getData("my-strategy");
12490
+ * const stats = await Backtest.getData("BTCUSDT", "my-strategy");
12391
12491
  * console.log(stats.sharpeRatio, stats.winRate);
12392
12492
  * ```
12393
12493
  */
12394
- this.getData = async (strategyName) => {
12494
+ this.getData = async (symbol, strategyName) => {
12395
12495
  backtest$1.loggerService.info("BacktestUtils.getData", {
12496
+ symbol,
12396
12497
  strategyName,
12397
12498
  });
12398
- return await backtest$1.backtestMarkdownService.getData(strategyName);
12499
+ return await backtest$1.backtestMarkdownService.getData(symbol, strategyName);
12399
12500
  };
12400
12501
  /**
12401
- * Generates markdown report with all closed signals for a strategy.
12502
+ * Generates markdown report with all closed signals for a symbol-strategy pair.
12402
12503
  *
12504
+ * @param symbol - Trading pair symbol
12403
12505
  * @param strategyName - Strategy name to generate report for
12404
12506
  * @returns Promise resolving to markdown formatted report string
12405
12507
  *
12406
12508
  * @example
12407
12509
  * ```typescript
12408
- * const markdown = await Backtest.getReport("my-strategy");
12510
+ * const markdown = await Backtest.getReport("BTCUSDT", "my-strategy");
12409
12511
  * console.log(markdown);
12410
12512
  * ```
12411
12513
  */
12412
- this.getReport = async (strategyName) => {
12514
+ this.getReport = async (symbol, strategyName) => {
12413
12515
  backtest$1.loggerService.info(BACKTEST_METHOD_NAME_GET_REPORT, {
12516
+ symbol,
12414
12517
  strategyName,
12415
12518
  });
12416
- return await backtest$1.backtestMarkdownService.getReport(strategyName);
12519
+ return await backtest$1.backtestMarkdownService.getReport(symbol, strategyName);
12417
12520
  };
12418
12521
  /**
12419
12522
  * Saves strategy report to disk.
@@ -12510,11 +12613,11 @@ class LiveUtils {
12510
12613
  context,
12511
12614
  });
12512
12615
  {
12513
- backtest$1.liveMarkdownService.clear(context.strategyName);
12514
- backtest$1.scheduleMarkdownService.clear(context.strategyName);
12616
+ backtest$1.liveMarkdownService.clear({ symbol, strategyName: context.strategyName });
12617
+ backtest$1.scheduleMarkdownService.clear({ symbol, strategyName: context.strategyName });
12515
12618
  }
12516
12619
  {
12517
- backtest$1.strategyGlobalService.clear(context.strategyName);
12620
+ backtest$1.strategyGlobalService.clear({ symbol, strategyName: context.strategyName });
12518
12621
  }
12519
12622
  {
12520
12623
  const { riskName } = backtest$1.strategySchemaService.get(context.strategyName);
@@ -12568,9 +12671,9 @@ class LiveUtils {
12568
12671
  };
12569
12672
  task().catch((error) => errorEmitter.next(new Error(getErrorMessage(error))));
12570
12673
  return () => {
12571
- backtest$1.strategyGlobalService.stop(context.strategyName);
12674
+ backtest$1.strategyGlobalService.stop(symbol, context.strategyName);
12572
12675
  backtest$1.strategyGlobalService
12573
- .getPendingSignal(context.strategyName)
12676
+ .getPendingSignal(symbol, context.strategyName)
12574
12677
  .then(async (pendingSignal) => {
12575
12678
  if (pendingSignal) {
12576
12679
  return;
@@ -12589,40 +12692,44 @@ class LiveUtils {
12589
12692
  };
12590
12693
  };
12591
12694
  /**
12592
- * Gets statistical data from all live trading events for a strategy.
12695
+ * Gets statistical data from all live trading events for a symbol-strategy pair.
12593
12696
  *
12697
+ * @param symbol - Trading pair symbol
12594
12698
  * @param strategyName - Strategy name to get data for
12595
12699
  * @returns Promise resolving to statistical data object
12596
12700
  *
12597
12701
  * @example
12598
12702
  * ```typescript
12599
- * const stats = await Live.getData("my-strategy");
12703
+ * const stats = await Live.getData("BTCUSDT", "my-strategy");
12600
12704
  * console.log(stats.sharpeRatio, stats.winRate);
12601
12705
  * ```
12602
12706
  */
12603
- this.getData = async (strategyName) => {
12707
+ this.getData = async (symbol, strategyName) => {
12604
12708
  backtest$1.loggerService.info("LiveUtils.getData", {
12709
+ symbol,
12605
12710
  strategyName,
12606
12711
  });
12607
- return await backtest$1.liveMarkdownService.getData(strategyName);
12712
+ return await backtest$1.liveMarkdownService.getData(symbol, strategyName);
12608
12713
  };
12609
12714
  /**
12610
- * Generates markdown report with all events for a strategy.
12715
+ * Generates markdown report with all events for a symbol-strategy pair.
12611
12716
  *
12717
+ * @param symbol - Trading pair symbol
12612
12718
  * @param strategyName - Strategy name to generate report for
12613
12719
  * @returns Promise resolving to markdown formatted report string
12614
12720
  *
12615
12721
  * @example
12616
12722
  * ```typescript
12617
- * const markdown = await Live.getReport("my-strategy");
12723
+ * const markdown = await Live.getReport("BTCUSDT", "my-strategy");
12618
12724
  * console.log(markdown);
12619
12725
  * ```
12620
12726
  */
12621
- this.getReport = async (strategyName) => {
12727
+ this.getReport = async (symbol, strategyName) => {
12622
12728
  backtest$1.loggerService.info(LIVE_METHOD_NAME_GET_REPORT, {
12729
+ symbol,
12623
12730
  strategyName,
12624
12731
  });
12625
- return await backtest$1.liveMarkdownService.getReport(strategyName);
12732
+ return await backtest$1.liveMarkdownService.getReport(symbol, strategyName);
12626
12733
  };
12627
12734
  /**
12628
12735
  * Saves strategy report to disk.
@@ -12696,40 +12803,44 @@ const SCHEDULE_METHOD_NAME_DUMP = "ScheduleUtils.dump";
12696
12803
  class ScheduleUtils {
12697
12804
  constructor() {
12698
12805
  /**
12699
- * Gets statistical data from all scheduled signal events for a strategy.
12806
+ * Gets statistical data from all scheduled signal events for a symbol-strategy pair.
12700
12807
  *
12808
+ * @param symbol - Trading pair symbol
12701
12809
  * @param strategyName - Strategy name to get data for
12702
12810
  * @returns Promise resolving to statistical data object
12703
12811
  *
12704
12812
  * @example
12705
12813
  * ```typescript
12706
- * const stats = await Schedule.getData("my-strategy");
12814
+ * const stats = await Schedule.getData("BTCUSDT", "my-strategy");
12707
12815
  * console.log(stats.cancellationRate, stats.avgWaitTime);
12708
12816
  * ```
12709
12817
  */
12710
- this.getData = async (strategyName) => {
12818
+ this.getData = async (symbol, strategyName) => {
12711
12819
  backtest$1.loggerService.info(SCHEDULE_METHOD_NAME_GET_DATA, {
12820
+ symbol,
12712
12821
  strategyName,
12713
12822
  });
12714
- return await backtest$1.scheduleMarkdownService.getData(strategyName);
12823
+ return await backtest$1.scheduleMarkdownService.getData(symbol, strategyName);
12715
12824
  };
12716
12825
  /**
12717
- * Generates markdown report with all scheduled events for a strategy.
12826
+ * Generates markdown report with all scheduled events for a symbol-strategy pair.
12718
12827
  *
12828
+ * @param symbol - Trading pair symbol
12719
12829
  * @param strategyName - Strategy name to generate report for
12720
12830
  * @returns Promise resolving to markdown formatted report string
12721
12831
  *
12722
12832
  * @example
12723
12833
  * ```typescript
12724
- * const markdown = await Schedule.getReport("my-strategy");
12834
+ * const markdown = await Schedule.getReport("BTCUSDT", "my-strategy");
12725
12835
  * console.log(markdown);
12726
12836
  * ```
12727
12837
  */
12728
- this.getReport = async (strategyName) => {
12838
+ this.getReport = async (symbol, strategyName) => {
12729
12839
  backtest$1.loggerService.info(SCHEDULE_METHOD_NAME_GET_REPORT, {
12840
+ symbol,
12730
12841
  strategyName,
12731
12842
  });
12732
- return await backtest$1.scheduleMarkdownService.getReport(strategyName);
12843
+ return await backtest$1.scheduleMarkdownService.getReport(symbol, strategyName);
12733
12844
  };
12734
12845
  /**
12735
12846
  * Saves strategy report to disk.
@@ -12801,19 +12912,20 @@ const Schedule = new ScheduleUtils();
12801
12912
  */
12802
12913
  class Performance {
12803
12914
  /**
12804
- * Gets aggregated performance statistics for a strategy.
12915
+ * Gets aggregated performance statistics for a symbol-strategy pair.
12805
12916
  *
12806
12917
  * Returns detailed metrics grouped by operation type:
12807
12918
  * - Count, total duration, average, min, max
12808
12919
  * - Standard deviation for volatility
12809
12920
  * - Percentiles (median, P95, P99) for outlier detection
12810
12921
  *
12922
+ * @param symbol - Trading pair symbol
12811
12923
  * @param strategyName - Strategy name to analyze
12812
12924
  * @returns Performance statistics with aggregated metrics
12813
12925
  *
12814
12926
  * @example
12815
12927
  * ```typescript
12816
- * const stats = await Performance.getData("my-strategy");
12928
+ * const stats = await Performance.getData("BTCUSDT", "my-strategy");
12817
12929
  *
12818
12930
  * // Find slowest operation type
12819
12931
  * const slowest = Object.values(stats.metricStats)
@@ -12828,8 +12940,8 @@ class Performance {
12828
12940
  * }
12829
12941
  * ```
12830
12942
  */
12831
- static async getData(strategyName) {
12832
- return backtest$1.performanceMarkdownService.getData(strategyName);
12943
+ static async getData(symbol, strategyName) {
12944
+ return backtest$1.performanceMarkdownService.getData(symbol, strategyName);
12833
12945
  }
12834
12946
  /**
12835
12947
  * Generates markdown report with performance analysis.
@@ -12839,12 +12951,13 @@ class Performance {
12839
12951
  * - Detailed metrics table with statistics
12840
12952
  * - Percentile analysis for bottleneck detection
12841
12953
  *
12954
+ * @param symbol - Trading pair symbol
12842
12955
  * @param strategyName - Strategy name to generate report for
12843
12956
  * @returns Markdown formatted report string
12844
12957
  *
12845
12958
  * @example
12846
12959
  * ```typescript
12847
- * const markdown = await Performance.getReport("my-strategy");
12960
+ * const markdown = await Performance.getReport("BTCUSDT", "my-strategy");
12848
12961
  * console.log(markdown);
12849
12962
  *
12850
12963
  * // Or save to file
@@ -12852,8 +12965,8 @@ class Performance {
12852
12965
  * await fs.writeFile("performance-report.md", markdown);
12853
12966
  * ```
12854
12967
  */
12855
- static async getReport(strategyName) {
12856
- return backtest$1.performanceMarkdownService.getReport(strategyName);
12968
+ static async getReport(symbol, strategyName) {
12969
+ return backtest$1.performanceMarkdownService.getReport(symbol, strategyName);
12857
12970
  }
12858
12971
  /**
12859
12972
  * Saves performance report to disk.
@@ -12876,23 +12989,6 @@ class Performance {
12876
12989
  static async dump(strategyName, path = "./dump/performance") {
12877
12990
  return backtest$1.performanceMarkdownService.dump(strategyName, path);
12878
12991
  }
12879
- /**
12880
- * Clears accumulated performance metrics from memory.
12881
- *
12882
- * @param strategyName - Optional strategy name to clear specific strategy's metrics
12883
- *
12884
- * @example
12885
- * ```typescript
12886
- * // Clear specific strategy metrics
12887
- * await Performance.clear("my-strategy");
12888
- *
12889
- * // Clear all metrics for all strategies
12890
- * await Performance.clear();
12891
- * ```
12892
- */
12893
- static async clear(strategyName) {
12894
- return backtest$1.performanceMarkdownService.clear(strategyName);
12895
- }
12896
12992
  }
12897
12993
 
12898
12994
  const WALKER_METHOD_NAME_RUN = "WalkerUtils.run";
@@ -12944,11 +13040,11 @@ class WalkerUtils {
12944
13040
  // Clear backtest data for all strategies
12945
13041
  for (const strategyName of walkerSchema.strategies) {
12946
13042
  {
12947
- backtest$1.backtestMarkdownService.clear(strategyName);
12948
- backtest$1.scheduleMarkdownService.clear(strategyName);
13043
+ backtest$1.backtestMarkdownService.clear({ symbol, strategyName });
13044
+ backtest$1.scheduleMarkdownService.clear({ symbol, strategyName });
12949
13045
  }
12950
13046
  {
12951
- backtest$1.strategyGlobalService.clear(strategyName);
13047
+ backtest$1.strategyGlobalService.clear({ symbol, strategyName });
12952
13048
  }
12953
13049
  {
12954
13050
  const { riskName } = backtest$1.strategySchemaService.get(strategyName);
@@ -13007,9 +13103,9 @@ class WalkerUtils {
13007
13103
  task().catch((error) => errorEmitter.next(new Error(getErrorMessage(error))));
13008
13104
  return () => {
13009
13105
  for (const strategyName of walkerSchema.strategies) {
13010
- backtest$1.strategyGlobalService.stop(strategyName);
13106
+ backtest$1.strategyGlobalService.stop(symbol, strategyName);
13107
+ walkerStopSubject.next({ symbol, strategyName });
13011
13108
  }
13012
- walkerStopSubject.next(context.walkerName);
13013
13109
  if (!isDone) {
13014
13110
  doneWalkerSubject.next({
13015
13111
  exchangeName: walkerSchema.exchangeName,