backtest-kit 1.5.33 → 1.5.35

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.cjs CHANGED
@@ -1438,7 +1438,7 @@ class LoggerService {
1438
1438
  }
1439
1439
  }
1440
1440
 
1441
- const INTERVAL_MINUTES$3 = {
1441
+ const INTERVAL_MINUTES$4 = {
1442
1442
  "1m": 1,
1443
1443
  "3m": 3,
1444
1444
  "5m": 5,
@@ -1583,7 +1583,7 @@ class ClientExchange {
1583
1583
  interval,
1584
1584
  limit,
1585
1585
  });
1586
- const step = INTERVAL_MINUTES$3[interval];
1586
+ const step = INTERVAL_MINUTES$4[interval];
1587
1587
  const adjust = step * limit - step;
1588
1588
  if (!adjust) {
1589
1589
  throw new Error(`ClientExchange unknown time adjust for interval=${interval}`);
@@ -1621,7 +1621,7 @@ class ClientExchange {
1621
1621
  const since = new Date(this.params.execution.context.when.getTime());
1622
1622
  const now = Date.now();
1623
1623
  // Вычисляем конечное время запроса
1624
- const step = INTERVAL_MINUTES$3[interval];
1624
+ const step = INTERVAL_MINUTES$4[interval];
1625
1625
  const endTime = since.getTime() + limit * step * 60 * 1000;
1626
1626
  // Проверяем что запрошенный период не заходит за Date.now()
1627
1627
  if (endTime > now) {
@@ -2845,7 +2845,7 @@ var emitters = /*#__PURE__*/Object.freeze({
2845
2845
  walkerStopSubject: walkerStopSubject
2846
2846
  });
2847
2847
 
2848
- const INTERVAL_MINUTES$2 = {
2848
+ const INTERVAL_MINUTES$3 = {
2849
2849
  "1m": 1,
2850
2850
  "3m": 3,
2851
2851
  "5m": 5,
@@ -3063,7 +3063,7 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
3063
3063
  }
3064
3064
  const currentTime = self.params.execution.context.when.getTime();
3065
3065
  {
3066
- const intervalMinutes = INTERVAL_MINUTES$2[self.params.interval];
3066
+ const intervalMinutes = INTERVAL_MINUTES$3[self.params.interval];
3067
3067
  const intervalMs = intervalMinutes * 60 * 1000;
3068
3068
  // Проверяем что прошел нужный интервал с последнего getSignal
3069
3069
  if (self._lastSignalTimestamp !== null &&
@@ -4811,7 +4811,7 @@ class StrategyConnectionService {
4811
4811
  * Maps FrameInterval to minutes for timestamp calculation.
4812
4812
  * Used to generate timeframe arrays with proper spacing.
4813
4813
  */
4814
- const INTERVAL_MINUTES$1 = {
4814
+ const INTERVAL_MINUTES$2 = {
4815
4815
  "1m": 1,
4816
4816
  "3m": 3,
4817
4817
  "5m": 5,
@@ -4840,7 +4840,7 @@ const GET_TIMEFRAME_FN = async (symbol, self) => {
4840
4840
  symbol,
4841
4841
  });
4842
4842
  const { interval, startDate, endDate } = self.params;
4843
- const intervalMinutes = INTERVAL_MINUTES$1[interval];
4843
+ const intervalMinutes = INTERVAL_MINUTES$2[interval];
4844
4844
  if (!intervalMinutes) {
4845
4845
  throw new Error(`ClientFrame unknown interval: ${interval}`);
4846
4846
  }
@@ -18142,7 +18142,7 @@ const EXCHANGE_METHOD_NAME_GET_CANDLES = "ExchangeUtils.getCandles";
18142
18142
  const EXCHANGE_METHOD_NAME_GET_AVERAGE_PRICE = "ExchangeUtils.getAveragePrice";
18143
18143
  const EXCHANGE_METHOD_NAME_FORMAT_QUANTITY = "ExchangeUtils.formatQuantity";
18144
18144
  const EXCHANGE_METHOD_NAME_FORMAT_PRICE = "ExchangeUtils.formatPrice";
18145
- const INTERVAL_MINUTES = {
18145
+ const INTERVAL_MINUTES$1 = {
18146
18146
  "1m": 1,
18147
18147
  "3m": 3,
18148
18148
  "5m": 5,
@@ -18203,7 +18203,7 @@ class ExchangeInstance {
18203
18203
  interval,
18204
18204
  limit,
18205
18205
  });
18206
- const step = INTERVAL_MINUTES[interval];
18206
+ const step = INTERVAL_MINUTES$1[interval];
18207
18207
  const adjust = step * limit - step;
18208
18208
  if (!adjust) {
18209
18209
  throw new Error(`ExchangeInstance unknown time adjust for interval=${interval}`);
@@ -18426,7 +18426,219 @@ class ExchangeUtils {
18426
18426
  */
18427
18427
  const Exchange = new ExchangeUtils();
18428
18428
 
18429
+ const CACHE_METHOD_NAME_CLEAR = "CacheUtils.clear";
18430
+ const CACHE_METHOD_NAME_RUN = "CacheInstance.run";
18431
+ const CACHE_METHOD_NAME_FN = "CacheUtils.fn";
18432
+ const INTERVAL_MINUTES = {
18433
+ "1m": 1,
18434
+ "3m": 3,
18435
+ "5m": 5,
18436
+ "15m": 15,
18437
+ "30m": 30,
18438
+ "1h": 60,
18439
+ "2h": 120,
18440
+ "4h": 240,
18441
+ "6h": 360,
18442
+ "8h": 480,
18443
+ };
18444
+ const createKey = (strategyName, exchangeName, backtest) => `${strategyName}:${exchangeName}:${backtest ? "backtest" : "live"}`;
18445
+ /**
18446
+ * Instance class for caching function results with timeframe-based invalidation.
18447
+ *
18448
+ * Provides automatic cache invalidation based on candle intervals.
18449
+ * Each instance maintains its own cache map keyed by strategy, exchange, and mode.
18450
+ * Cache is invalidated when the current time moves to a different interval.
18451
+ *
18452
+ * @template T - Function type to cache
18453
+ *
18454
+ * @example
18455
+ * ```typescript
18456
+ * const instance = new CacheInstance(myExpensiveFunction, "1h");
18457
+ * const result = instance.run(arg1, arg2); // Computed
18458
+ * const result2 = instance.run(arg1, arg2); // Cached (within same hour)
18459
+ * // After 1 hour passes
18460
+ * const result3 = instance.run(arg1, arg2); // Recomputed
18461
+ * ```
18462
+ */
18463
+ class CacheInstance {
18464
+ /**
18465
+ * Creates a new CacheInstance for a specific function and interval.
18466
+ *
18467
+ * @param fn - Function to cache
18468
+ * @param interval - Candle interval for cache invalidation (e.g., "1m", "1h")
18469
+ */
18470
+ constructor(fn, interval) {
18471
+ this.fn = fn;
18472
+ this.interval = interval;
18473
+ /** Cache map storing results per strategy/exchange/mode combination */
18474
+ this._cacheMap = new Map();
18475
+ /**
18476
+ * Execute function with caching based on timeframe intervals.
18477
+ *
18478
+ * This method implements intelligent time-based caching:
18479
+ * 1. Generates cache key from strategy name, exchange name, and execution mode (backtest/live)
18480
+ * 2. Checks if cached value exists and is still valid for current interval
18481
+ * 3. Returns cached value if time elapsed is less than interval duration
18482
+ * 4. Recomputes and caches new value when moving to next interval boundary
18483
+ *
18484
+ * Cache invalidation example with 15m interval:
18485
+ * - 10:00 AM: First call → computes and caches result
18486
+ * - 10:05 AM: Same interval → returns cached result
18487
+ * - 10:15 AM: New interval → recomputes and caches new result
18488
+ *
18489
+ * Requires active execution context (strategy, exchange, backtest mode) and method context.
18490
+ * Each unique combination of these contexts maintains separate cache entries.
18491
+ *
18492
+ * @param args - Arguments to pass to the cached function
18493
+ * @returns Cached result object containing:
18494
+ * - `value`: The computed or cached function result
18495
+ * - `when`: Timestamp when this value was cached
18496
+ * @throws Error if interval is unknown or required context is missing
18497
+ *
18498
+ * @example
18499
+ * ```typescript
18500
+ * const instance = new CacheInstance(calculateIndicator, "15m");
18501
+ * const result = instance.run("BTCUSDT", 100);
18502
+ * console.log(result.value); // Calculated value
18503
+ * console.log(result.when); // Cache timestamp
18504
+ * ```
18505
+ */
18506
+ this.run = (...args) => {
18507
+ backtest$1.loggerService.debug(CACHE_METHOD_NAME_RUN, { args });
18508
+ const step = INTERVAL_MINUTES[this.interval];
18509
+ if (!step) {
18510
+ throw new Error(`CacheInstance unknown cache ttl interval=${this.interval}`);
18511
+ }
18512
+ if (!MethodContextService.hasContext()) {
18513
+ throw new Error("CacheInstance run requires method context");
18514
+ }
18515
+ if (!ExecutionContextService.hasContext()) {
18516
+ throw new Error("CacheInstance run requires execution context");
18517
+ }
18518
+ const key = createKey(backtest$1.methodContextService.context.strategyName, backtest$1.methodContextService.context.exchangeName, backtest$1.executionContextService.context.backtest);
18519
+ const currentWhen = backtest$1.executionContextService.context.when;
18520
+ const cached = this._cacheMap.get(key);
18521
+ if (cached) {
18522
+ const stepMs = step * 60 * 1000;
18523
+ const elapsed = currentWhen.getTime() - cached.when.getTime();
18524
+ if (elapsed < stepMs) {
18525
+ return cached;
18526
+ }
18527
+ }
18528
+ const newCache = {
18529
+ when: currentWhen,
18530
+ value: this.fn(...args),
18531
+ };
18532
+ this._cacheMap.set(key, newCache);
18533
+ return newCache;
18534
+ };
18535
+ }
18536
+ }
18537
+ /**
18538
+ * Utility class for function caching with timeframe-based invalidation.
18539
+ *
18540
+ * Provides simplified API for wrapping functions with automatic caching.
18541
+ * Exported as singleton instance for convenient usage.
18542
+ *
18543
+ * @example
18544
+ * ```typescript
18545
+ * import { Cache } from "./classes/Cache";
18546
+ *
18547
+ * const cachedFn = Cache.fn(expensiveCalculation, "1h");
18548
+ * const result = cachedFn(arg1, arg2); // Computed on first call
18549
+ * const result2 = cachedFn(arg1, arg2); // Cached (within same hour)
18550
+ * ```
18551
+ */
18552
+ class CacheUtils {
18553
+ constructor() {
18554
+ /**
18555
+ * Memoized function to get or create CacheInstance for a function.
18556
+ * Each function gets its own isolated cache instance.
18557
+ */
18558
+ this._getInstance = functoolsKit.memoize(([run]) => run, (run, interval) => new CacheInstance(run, interval));
18559
+ /**
18560
+ * Wrap a function with caching based on timeframe intervals.
18561
+ *
18562
+ * Returns a wrapped version of the function that automatically caches results
18563
+ * and invalidates based on the specified candle interval.
18564
+ *
18565
+ * @template T - Function type to cache
18566
+ * @param run - Function to wrap with caching
18567
+ * @param interval - Candle interval for cache invalidation (e.g., "1m", "1h")
18568
+ * @returns Wrapped function with automatic caching
18569
+ *
18570
+ * @example
18571
+ * ```typescript
18572
+ * const calculateIndicator = (symbol: string, period: number) => {
18573
+ * // Expensive calculation
18574
+ * return result;
18575
+ * };
18576
+ *
18577
+ * const cachedCalculate = Cache.fn(calculateIndicator, "15m");
18578
+ * const result = cachedCalculate("BTCUSDT", 14); // Computed
18579
+ * const result2 = cachedCalculate("BTCUSDT", 14); // Cached (same 15m interval)
18580
+ * ```
18581
+ */
18582
+ this.fn = (run, context) => {
18583
+ backtest$1.loggerService.debug(CACHE_METHOD_NAME_FN, {
18584
+ context,
18585
+ });
18586
+ return (...args) => {
18587
+ const instance = this._getInstance(run, context.interval);
18588
+ return instance.run(...args).value;
18589
+ };
18590
+ };
18591
+ /**
18592
+ * Clear cached instances for specific function or all cached functions.
18593
+ *
18594
+ * This method delegates to the memoized `_getInstance` function's clear method,
18595
+ * which removes cached CacheInstance objects. When a CacheInstance is removed,
18596
+ * all cached function results for that instance are also discarded.
18597
+ *
18598
+ * Use cases:
18599
+ * - Clear cache for a specific function when its implementation changes
18600
+ * - Free memory by removing unused cached instances
18601
+ * - Reset all caches when switching contexts (e.g., between different backtests)
18602
+ *
18603
+ * @param run - Optional function to clear cache for. If omitted, clears all cached instances.
18604
+ *
18605
+ * @example
18606
+ * ```typescript
18607
+ * const cachedCalc = Cache.fn(calculateIndicator, { interval: "1h" });
18608
+ *
18609
+ * // Clear cache for specific function
18610
+ * Cache.clear(calculateIndicator);
18611
+ *
18612
+ * // Clear all cached instances
18613
+ * Cache.clear();
18614
+ * ```
18615
+ */
18616
+ this.clear = (run) => {
18617
+ backtest$1.loggerService.debug(CACHE_METHOD_NAME_CLEAR, {
18618
+ run,
18619
+ });
18620
+ this._getInstance.clear(run);
18621
+ };
18622
+ }
18623
+ }
18624
+ /**
18625
+ * Singleton instance of CacheUtils for convenient function caching.
18626
+ *
18627
+ * @example
18628
+ * ```typescript
18629
+ * import { Cache } from "./classes/Cache";
18630
+ *
18631
+ * // Wrap expensive function with 1-hour cache
18632
+ * const cachedFn = Cache.fn(myExpensiveFunction, "1h");
18633
+ * const result = cachedFn(arg1, arg2);
18634
+ *
18635
+ * // Cache is automatically invalidated when moving to next hour interval
18636
+ * ```
18637
+ */
18638
+ const Cache = new CacheUtils();
18639
+
18429
18640
  exports.Backtest = Backtest;
18641
+ exports.Cache = Cache;
18430
18642
  exports.Constant = Constant;
18431
18643
  exports.Exchange = Exchange;
18432
18644
  exports.ExecutionContextService = ExecutionContextService;
package/build/index.mjs CHANGED
@@ -1436,7 +1436,7 @@ class LoggerService {
1436
1436
  }
1437
1437
  }
1438
1438
 
1439
- const INTERVAL_MINUTES$3 = {
1439
+ const INTERVAL_MINUTES$4 = {
1440
1440
  "1m": 1,
1441
1441
  "3m": 3,
1442
1442
  "5m": 5,
@@ -1581,7 +1581,7 @@ class ClientExchange {
1581
1581
  interval,
1582
1582
  limit,
1583
1583
  });
1584
- const step = INTERVAL_MINUTES$3[interval];
1584
+ const step = INTERVAL_MINUTES$4[interval];
1585
1585
  const adjust = step * limit - step;
1586
1586
  if (!adjust) {
1587
1587
  throw new Error(`ClientExchange unknown time adjust for interval=${interval}`);
@@ -1619,7 +1619,7 @@ class ClientExchange {
1619
1619
  const since = new Date(this.params.execution.context.when.getTime());
1620
1620
  const now = Date.now();
1621
1621
  // Вычисляем конечное время запроса
1622
- const step = INTERVAL_MINUTES$3[interval];
1622
+ const step = INTERVAL_MINUTES$4[interval];
1623
1623
  const endTime = since.getTime() + limit * step * 60 * 1000;
1624
1624
  // Проверяем что запрошенный период не заходит за Date.now()
1625
1625
  if (endTime > now) {
@@ -2843,7 +2843,7 @@ var emitters = /*#__PURE__*/Object.freeze({
2843
2843
  walkerStopSubject: walkerStopSubject
2844
2844
  });
2845
2845
 
2846
- const INTERVAL_MINUTES$2 = {
2846
+ const INTERVAL_MINUTES$3 = {
2847
2847
  "1m": 1,
2848
2848
  "3m": 3,
2849
2849
  "5m": 5,
@@ -3061,7 +3061,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
3061
3061
  }
3062
3062
  const currentTime = self.params.execution.context.when.getTime();
3063
3063
  {
3064
- const intervalMinutes = INTERVAL_MINUTES$2[self.params.interval];
3064
+ const intervalMinutes = INTERVAL_MINUTES$3[self.params.interval];
3065
3065
  const intervalMs = intervalMinutes * 60 * 1000;
3066
3066
  // Проверяем что прошел нужный интервал с последнего getSignal
3067
3067
  if (self._lastSignalTimestamp !== null &&
@@ -4809,7 +4809,7 @@ class StrategyConnectionService {
4809
4809
  * Maps FrameInterval to minutes for timestamp calculation.
4810
4810
  * Used to generate timeframe arrays with proper spacing.
4811
4811
  */
4812
- const INTERVAL_MINUTES$1 = {
4812
+ const INTERVAL_MINUTES$2 = {
4813
4813
  "1m": 1,
4814
4814
  "3m": 3,
4815
4815
  "5m": 5,
@@ -4838,7 +4838,7 @@ const GET_TIMEFRAME_FN = async (symbol, self) => {
4838
4838
  symbol,
4839
4839
  });
4840
4840
  const { interval, startDate, endDate } = self.params;
4841
- const intervalMinutes = INTERVAL_MINUTES$1[interval];
4841
+ const intervalMinutes = INTERVAL_MINUTES$2[interval];
4842
4842
  if (!intervalMinutes) {
4843
4843
  throw new Error(`ClientFrame unknown interval: ${interval}`);
4844
4844
  }
@@ -18140,7 +18140,7 @@ const EXCHANGE_METHOD_NAME_GET_CANDLES = "ExchangeUtils.getCandles";
18140
18140
  const EXCHANGE_METHOD_NAME_GET_AVERAGE_PRICE = "ExchangeUtils.getAveragePrice";
18141
18141
  const EXCHANGE_METHOD_NAME_FORMAT_QUANTITY = "ExchangeUtils.formatQuantity";
18142
18142
  const EXCHANGE_METHOD_NAME_FORMAT_PRICE = "ExchangeUtils.formatPrice";
18143
- const INTERVAL_MINUTES = {
18143
+ const INTERVAL_MINUTES$1 = {
18144
18144
  "1m": 1,
18145
18145
  "3m": 3,
18146
18146
  "5m": 5,
@@ -18201,7 +18201,7 @@ class ExchangeInstance {
18201
18201
  interval,
18202
18202
  limit,
18203
18203
  });
18204
- const step = INTERVAL_MINUTES[interval];
18204
+ const step = INTERVAL_MINUTES$1[interval];
18205
18205
  const adjust = step * limit - step;
18206
18206
  if (!adjust) {
18207
18207
  throw new Error(`ExchangeInstance unknown time adjust for interval=${interval}`);
@@ -18424,4 +18424,215 @@ class ExchangeUtils {
18424
18424
  */
18425
18425
  const Exchange = new ExchangeUtils();
18426
18426
 
18427
- export { Backtest, Constant, Exchange, ExecutionContextService, Heat, Live, MethodContextService, Optimizer, Partial, Performance, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Risk, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getColumns, getConfig, getDate, getDefaultColumns, getDefaultConfig, getMode, hasTradeContext, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setColumns, setConfig, setLogger };
18427
+ const CACHE_METHOD_NAME_CLEAR = "CacheUtils.clear";
18428
+ const CACHE_METHOD_NAME_RUN = "CacheInstance.run";
18429
+ const CACHE_METHOD_NAME_FN = "CacheUtils.fn";
18430
+ const INTERVAL_MINUTES = {
18431
+ "1m": 1,
18432
+ "3m": 3,
18433
+ "5m": 5,
18434
+ "15m": 15,
18435
+ "30m": 30,
18436
+ "1h": 60,
18437
+ "2h": 120,
18438
+ "4h": 240,
18439
+ "6h": 360,
18440
+ "8h": 480,
18441
+ };
18442
+ const createKey = (strategyName, exchangeName, backtest) => `${strategyName}:${exchangeName}:${backtest ? "backtest" : "live"}`;
18443
+ /**
18444
+ * Instance class for caching function results with timeframe-based invalidation.
18445
+ *
18446
+ * Provides automatic cache invalidation based on candle intervals.
18447
+ * Each instance maintains its own cache map keyed by strategy, exchange, and mode.
18448
+ * Cache is invalidated when the current time moves to a different interval.
18449
+ *
18450
+ * @template T - Function type to cache
18451
+ *
18452
+ * @example
18453
+ * ```typescript
18454
+ * const instance = new CacheInstance(myExpensiveFunction, "1h");
18455
+ * const result = instance.run(arg1, arg2); // Computed
18456
+ * const result2 = instance.run(arg1, arg2); // Cached (within same hour)
18457
+ * // After 1 hour passes
18458
+ * const result3 = instance.run(arg1, arg2); // Recomputed
18459
+ * ```
18460
+ */
18461
+ class CacheInstance {
18462
+ /**
18463
+ * Creates a new CacheInstance for a specific function and interval.
18464
+ *
18465
+ * @param fn - Function to cache
18466
+ * @param interval - Candle interval for cache invalidation (e.g., "1m", "1h")
18467
+ */
18468
+ constructor(fn, interval) {
18469
+ this.fn = fn;
18470
+ this.interval = interval;
18471
+ /** Cache map storing results per strategy/exchange/mode combination */
18472
+ this._cacheMap = new Map();
18473
+ /**
18474
+ * Execute function with caching based on timeframe intervals.
18475
+ *
18476
+ * This method implements intelligent time-based caching:
18477
+ * 1. Generates cache key from strategy name, exchange name, and execution mode (backtest/live)
18478
+ * 2. Checks if cached value exists and is still valid for current interval
18479
+ * 3. Returns cached value if time elapsed is less than interval duration
18480
+ * 4. Recomputes and caches new value when moving to next interval boundary
18481
+ *
18482
+ * Cache invalidation example with 15m interval:
18483
+ * - 10:00 AM: First call → computes and caches result
18484
+ * - 10:05 AM: Same interval → returns cached result
18485
+ * - 10:15 AM: New interval → recomputes and caches new result
18486
+ *
18487
+ * Requires active execution context (strategy, exchange, backtest mode) and method context.
18488
+ * Each unique combination of these contexts maintains separate cache entries.
18489
+ *
18490
+ * @param args - Arguments to pass to the cached function
18491
+ * @returns Cached result object containing:
18492
+ * - `value`: The computed or cached function result
18493
+ * - `when`: Timestamp when this value was cached
18494
+ * @throws Error if interval is unknown or required context is missing
18495
+ *
18496
+ * @example
18497
+ * ```typescript
18498
+ * const instance = new CacheInstance(calculateIndicator, "15m");
18499
+ * const result = instance.run("BTCUSDT", 100);
18500
+ * console.log(result.value); // Calculated value
18501
+ * console.log(result.when); // Cache timestamp
18502
+ * ```
18503
+ */
18504
+ this.run = (...args) => {
18505
+ backtest$1.loggerService.debug(CACHE_METHOD_NAME_RUN, { args });
18506
+ const step = INTERVAL_MINUTES[this.interval];
18507
+ if (!step) {
18508
+ throw new Error(`CacheInstance unknown cache ttl interval=${this.interval}`);
18509
+ }
18510
+ if (!MethodContextService.hasContext()) {
18511
+ throw new Error("CacheInstance run requires method context");
18512
+ }
18513
+ if (!ExecutionContextService.hasContext()) {
18514
+ throw new Error("CacheInstance run requires execution context");
18515
+ }
18516
+ const key = createKey(backtest$1.methodContextService.context.strategyName, backtest$1.methodContextService.context.exchangeName, backtest$1.executionContextService.context.backtest);
18517
+ const currentWhen = backtest$1.executionContextService.context.when;
18518
+ const cached = this._cacheMap.get(key);
18519
+ if (cached) {
18520
+ const stepMs = step * 60 * 1000;
18521
+ const elapsed = currentWhen.getTime() - cached.when.getTime();
18522
+ if (elapsed < stepMs) {
18523
+ return cached;
18524
+ }
18525
+ }
18526
+ const newCache = {
18527
+ when: currentWhen,
18528
+ value: this.fn(...args),
18529
+ };
18530
+ this._cacheMap.set(key, newCache);
18531
+ return newCache;
18532
+ };
18533
+ }
18534
+ }
18535
+ /**
18536
+ * Utility class for function caching with timeframe-based invalidation.
18537
+ *
18538
+ * Provides simplified API for wrapping functions with automatic caching.
18539
+ * Exported as singleton instance for convenient usage.
18540
+ *
18541
+ * @example
18542
+ * ```typescript
18543
+ * import { Cache } from "./classes/Cache";
18544
+ *
18545
+ * const cachedFn = Cache.fn(expensiveCalculation, "1h");
18546
+ * const result = cachedFn(arg1, arg2); // Computed on first call
18547
+ * const result2 = cachedFn(arg1, arg2); // Cached (within same hour)
18548
+ * ```
18549
+ */
18550
+ class CacheUtils {
18551
+ constructor() {
18552
+ /**
18553
+ * Memoized function to get or create CacheInstance for a function.
18554
+ * Each function gets its own isolated cache instance.
18555
+ */
18556
+ this._getInstance = memoize(([run]) => run, (run, interval) => new CacheInstance(run, interval));
18557
+ /**
18558
+ * Wrap a function with caching based on timeframe intervals.
18559
+ *
18560
+ * Returns a wrapped version of the function that automatically caches results
18561
+ * and invalidates based on the specified candle interval.
18562
+ *
18563
+ * @template T - Function type to cache
18564
+ * @param run - Function to wrap with caching
18565
+ * @param interval - Candle interval for cache invalidation (e.g., "1m", "1h")
18566
+ * @returns Wrapped function with automatic caching
18567
+ *
18568
+ * @example
18569
+ * ```typescript
18570
+ * const calculateIndicator = (symbol: string, period: number) => {
18571
+ * // Expensive calculation
18572
+ * return result;
18573
+ * };
18574
+ *
18575
+ * const cachedCalculate = Cache.fn(calculateIndicator, "15m");
18576
+ * const result = cachedCalculate("BTCUSDT", 14); // Computed
18577
+ * const result2 = cachedCalculate("BTCUSDT", 14); // Cached (same 15m interval)
18578
+ * ```
18579
+ */
18580
+ this.fn = (run, context) => {
18581
+ backtest$1.loggerService.debug(CACHE_METHOD_NAME_FN, {
18582
+ context,
18583
+ });
18584
+ return (...args) => {
18585
+ const instance = this._getInstance(run, context.interval);
18586
+ return instance.run(...args).value;
18587
+ };
18588
+ };
18589
+ /**
18590
+ * Clear cached instances for specific function or all cached functions.
18591
+ *
18592
+ * This method delegates to the memoized `_getInstance` function's clear method,
18593
+ * which removes cached CacheInstance objects. When a CacheInstance is removed,
18594
+ * all cached function results for that instance are also discarded.
18595
+ *
18596
+ * Use cases:
18597
+ * - Clear cache for a specific function when its implementation changes
18598
+ * - Free memory by removing unused cached instances
18599
+ * - Reset all caches when switching contexts (e.g., between different backtests)
18600
+ *
18601
+ * @param run - Optional function to clear cache for. If omitted, clears all cached instances.
18602
+ *
18603
+ * @example
18604
+ * ```typescript
18605
+ * const cachedCalc = Cache.fn(calculateIndicator, { interval: "1h" });
18606
+ *
18607
+ * // Clear cache for specific function
18608
+ * Cache.clear(calculateIndicator);
18609
+ *
18610
+ * // Clear all cached instances
18611
+ * Cache.clear();
18612
+ * ```
18613
+ */
18614
+ this.clear = (run) => {
18615
+ backtest$1.loggerService.debug(CACHE_METHOD_NAME_CLEAR, {
18616
+ run,
18617
+ });
18618
+ this._getInstance.clear(run);
18619
+ };
18620
+ }
18621
+ }
18622
+ /**
18623
+ * Singleton instance of CacheUtils for convenient function caching.
18624
+ *
18625
+ * @example
18626
+ * ```typescript
18627
+ * import { Cache } from "./classes/Cache";
18628
+ *
18629
+ * // Wrap expensive function with 1-hour cache
18630
+ * const cachedFn = Cache.fn(myExpensiveFunction, "1h");
18631
+ * const result = cachedFn(arg1, arg2);
18632
+ *
18633
+ * // Cache is automatically invalidated when moving to next hour interval
18634
+ * ```
18635
+ */
18636
+ const Cache = new CacheUtils();
18637
+
18638
+ export { Backtest, Cache, Constant, Exchange, ExecutionContextService, Heat, Live, MethodContextService, Optimizer, Partial, Performance, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, Risk, Schedule, Walker, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getColumns, getConfig, getDate, getDefaultColumns, getDefaultConfig, getMode, hasTradeContext, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setColumns, setConfig, setLogger };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backtest-kit",
3
- "version": "1.5.33",
3
+ "version": "1.5.35",
4
4
  "description": "A TypeScript library for trading system backtest",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
package/types.d.ts CHANGED
@@ -7814,6 +7814,101 @@ declare class ExchangeUtils {
7814
7814
  */
7815
7815
  declare const Exchange: ExchangeUtils;
7816
7816
 
7817
+ /**
7818
+ * Generic function type that accepts any arguments and returns any value.
7819
+ * Used as a constraint for cached functions.
7820
+ */
7821
+ type Function = (...args: any[]) => any;
7822
+ /**
7823
+ * Utility class for function caching with timeframe-based invalidation.
7824
+ *
7825
+ * Provides simplified API for wrapping functions with automatic caching.
7826
+ * Exported as singleton instance for convenient usage.
7827
+ *
7828
+ * @example
7829
+ * ```typescript
7830
+ * import { Cache } from "./classes/Cache";
7831
+ *
7832
+ * const cachedFn = Cache.fn(expensiveCalculation, "1h");
7833
+ * const result = cachedFn(arg1, arg2); // Computed on first call
7834
+ * const result2 = cachedFn(arg1, arg2); // Cached (within same hour)
7835
+ * ```
7836
+ */
7837
+ declare class CacheUtils {
7838
+ /**
7839
+ * Memoized function to get or create CacheInstance for a function.
7840
+ * Each function gets its own isolated cache instance.
7841
+ */
7842
+ private _getInstance;
7843
+ /**
7844
+ * Wrap a function with caching based on timeframe intervals.
7845
+ *
7846
+ * Returns a wrapped version of the function that automatically caches results
7847
+ * and invalidates based on the specified candle interval.
7848
+ *
7849
+ * @template T - Function type to cache
7850
+ * @param run - Function to wrap with caching
7851
+ * @param interval - Candle interval for cache invalidation (e.g., "1m", "1h")
7852
+ * @returns Wrapped function with automatic caching
7853
+ *
7854
+ * @example
7855
+ * ```typescript
7856
+ * const calculateIndicator = (symbol: string, period: number) => {
7857
+ * // Expensive calculation
7858
+ * return result;
7859
+ * };
7860
+ *
7861
+ * const cachedCalculate = Cache.fn(calculateIndicator, "15m");
7862
+ * const result = cachedCalculate("BTCUSDT", 14); // Computed
7863
+ * const result2 = cachedCalculate("BTCUSDT", 14); // Cached (same 15m interval)
7864
+ * ```
7865
+ */
7866
+ fn: <T extends Function>(run: T, context: {
7867
+ interval: CandleInterval;
7868
+ }) => Function;
7869
+ /**
7870
+ * Clear cached instances for specific function or all cached functions.
7871
+ *
7872
+ * This method delegates to the memoized `_getInstance` function's clear method,
7873
+ * which removes cached CacheInstance objects. When a CacheInstance is removed,
7874
+ * all cached function results for that instance are also discarded.
7875
+ *
7876
+ * Use cases:
7877
+ * - Clear cache for a specific function when its implementation changes
7878
+ * - Free memory by removing unused cached instances
7879
+ * - Reset all caches when switching contexts (e.g., between different backtests)
7880
+ *
7881
+ * @param run - Optional function to clear cache for. If omitted, clears all cached instances.
7882
+ *
7883
+ * @example
7884
+ * ```typescript
7885
+ * const cachedCalc = Cache.fn(calculateIndicator, { interval: "1h" });
7886
+ *
7887
+ * // Clear cache for specific function
7888
+ * Cache.clear(calculateIndicator);
7889
+ *
7890
+ * // Clear all cached instances
7891
+ * Cache.clear();
7892
+ * ```
7893
+ */
7894
+ clear: <T extends Function>(run?: T) => void;
7895
+ }
7896
+ /**
7897
+ * Singleton instance of CacheUtils for convenient function caching.
7898
+ *
7899
+ * @example
7900
+ * ```typescript
7901
+ * import { Cache } from "./classes/Cache";
7902
+ *
7903
+ * // Wrap expensive function with 1-hour cache
7904
+ * const cachedFn = Cache.fn(myExpensiveFunction, "1h");
7905
+ * const result = cachedFn(arg1, arg2);
7906
+ *
7907
+ * // Cache is automatically invalidated when moving to next hour interval
7908
+ * ```
7909
+ */
7910
+ declare const Cache: CacheUtils;
7911
+
7817
7912
  /**
7818
7913
  * Contract for walker stop signal events.
7819
7914
  *
@@ -10649,4 +10744,4 @@ declare const backtest: {
10649
10744
  loggerService: LoggerService;
10650
10745
  };
10651
10746
 
10652
- export { Backtest, type BacktestStatisticsModel, type CandleInterval, type ColumnConfig, type ColumnModel, Constant, type DoneContract, type EntityId, Exchange, ExecutionContextService, type FrameInterval, type GlobalConfig, Heat, type HeatmapStatisticsModel, type ICandleData, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type IOptimizerCallbacks, type IOptimizerData, type IOptimizerFetchArgs, type IOptimizerFilterArgs, type IOptimizerRange, type IOptimizerSchema, type IOptimizerSource, type IOptimizerStrategy, type IOptimizerTemplate, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type IScheduledSignalRow, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStrategyPnL, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultCancelled, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IStrategyTickResultScheduled, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, Live, type LiveStatisticsModel, type MessageModel, type MessageRole, MethodContextService, Optimizer, Partial$1 as Partial, type PartialData, type PartialLossContract, type PartialProfitContract, type PartialStatisticsModel, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatisticsModel, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, type ProgressBacktestContract, type ProgressOptimizerContract, type ProgressWalkerContract, Risk, type RiskContract, type RiskData, type RiskStatisticsModel, Schedule, type ScheduleData, type ScheduleStatisticsModel, type SignalData, type SignalInterval, type TPersistBase, type TPersistBaseCtor, Walker, type WalkerCompleteContract, type WalkerContract, type WalkerMetric, type WalkerStatisticsModel, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getColumns, getConfig, getDate, getDefaultColumns, getDefaultConfig, getMode, hasTradeContext, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setColumns, setConfig, setLogger };
10747
+ export { Backtest, type BacktestStatisticsModel, Cache, type CandleInterval, type ColumnConfig, type ColumnModel, Constant, type DoneContract, type EntityId, Exchange, ExecutionContextService, type FrameInterval, type GlobalConfig, Heat, type HeatmapStatisticsModel, type ICandleData, type IExchangeSchema, type IFrameSchema, type IHeatmapRow, type IOptimizerCallbacks, type IOptimizerData, type IOptimizerFetchArgs, type IOptimizerFilterArgs, type IOptimizerRange, type IOptimizerSchema, type IOptimizerSource, type IOptimizerStrategy, type IOptimizerTemplate, type IPersistBase, type IPositionSizeATRParams, type IPositionSizeFixedPercentageParams, type IPositionSizeKellyParams, type IRiskActivePosition, type IRiskCheckArgs, type IRiskSchema, type IRiskValidation, type IRiskValidationFn, type IRiskValidationPayload, type IScheduledSignalRow, type ISignalDto, type ISignalRow, type ISizingCalculateParams, type ISizingCalculateParamsATR, type ISizingCalculateParamsFixedPercentage, type ISizingCalculateParamsKelly, type ISizingSchema, type ISizingSchemaATR, type ISizingSchemaFixedPercentage, type ISizingSchemaKelly, type IStrategyPnL, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultCancelled, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, type IStrategyTickResultScheduled, type IWalkerResults, type IWalkerSchema, type IWalkerStrategyResult, Live, type LiveStatisticsModel, type MessageModel, type MessageRole, MethodContextService, Optimizer, Partial$1 as Partial, type PartialData, type PartialLossContract, type PartialProfitContract, type PartialStatisticsModel, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatisticsModel, PersistBase, PersistPartialAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistSignalAdapter, PositionSize, type ProgressBacktestContract, type ProgressOptimizerContract, type ProgressWalkerContract, Risk, type RiskContract, type RiskData, type RiskStatisticsModel, Schedule, type ScheduleData, type ScheduleStatisticsModel, type SignalData, type SignalInterval, type TPersistBase, type TPersistBaseCtor, Walker, type WalkerCompleteContract, type WalkerContract, type WalkerMetric, type WalkerStatisticsModel, addExchange, addFrame, addOptimizer, addRisk, addSizing, addStrategy, addWalker, dumpSignal, emitters, formatPrice, formatQuantity, getAveragePrice, getCandles, getColumns, getConfig, getDate, getDefaultColumns, getDefaultConfig, getMode, hasTradeContext, backtest as lib, listExchanges, listFrames, listOptimizers, listRisks, listSizings, listStrategies, listWalkers, listenBacktestProgress, listenDoneBacktest, listenDoneBacktestOnce, listenDoneLive, listenDoneLiveOnce, listenDoneWalker, listenDoneWalkerOnce, listenError, listenExit, listenOptimizerProgress, listenPartialLoss, listenPartialLossOnce, listenPartialProfit, listenPartialProfitOnce, listenPerformance, listenRisk, listenRiskOnce, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, listenValidation, listenWalker, listenWalkerComplete, listenWalkerOnce, listenWalkerProgress, setColumns, setConfig, setLogger };