backtest-kit 3.0.6 → 3.0.8
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 +113 -15
- package/build/index.mjs +113 -15
- package/package.json +1 -1
- package/types.d.ts +39 -5
package/build/index.cjs
CHANGED
|
@@ -36250,6 +36250,7 @@ const Exchange = new ExchangeUtils();
|
|
|
36250
36250
|
const CACHE_METHOD_NAME_FLUSH = "CacheUtils.flush";
|
|
36251
36251
|
const CACHE_METHOD_NAME_CLEAR = "CacheInstance.clear";
|
|
36252
36252
|
const CACHE_METHOD_NAME_RUN = "CacheInstance.run";
|
|
36253
|
+
const CACHE_METHOD_NAME_GC = "CacheInstance.gc";
|
|
36253
36254
|
const CACHE_METHOD_NAME_FN = "CacheUtils.fn";
|
|
36254
36255
|
const MS_PER_MINUTE = 60000;
|
|
36255
36256
|
const INTERVAL_MINUTES = {
|
|
@@ -36307,6 +36308,11 @@ const CREATE_KEY_FN = (strategyName, exchangeName, frameName, backtest) => {
|
|
|
36307
36308
|
parts.push(backtest ? "backtest" : "live");
|
|
36308
36309
|
return parts.join(":");
|
|
36309
36310
|
};
|
|
36311
|
+
/**
|
|
36312
|
+
* A unique symbol representing a value that should never occur.
|
|
36313
|
+
* Used as default key when no key function is provided.
|
|
36314
|
+
*/
|
|
36315
|
+
const NEVER_VALUE = Symbol("never");
|
|
36310
36316
|
/**
|
|
36311
36317
|
* Instance class for caching function results with timeframe-based invalidation.
|
|
36312
36318
|
*
|
|
@@ -36315,6 +36321,7 @@ const CREATE_KEY_FN = (strategyName, exchangeName, frameName, backtest) => {
|
|
|
36315
36321
|
* Cache is invalidated when the current time moves to a different interval.
|
|
36316
36322
|
*
|
|
36317
36323
|
* @template T - Function type to cache
|
|
36324
|
+
* @template K - Key type for argument-based caching
|
|
36318
36325
|
*
|
|
36319
36326
|
* @example
|
|
36320
36327
|
* ```typescript
|
|
@@ -36331,11 +36338,13 @@ class CacheInstance {
|
|
|
36331
36338
|
*
|
|
36332
36339
|
* @param fn - Function to cache
|
|
36333
36340
|
* @param interval - Candle interval for cache invalidation (e.g., "1m", "1h")
|
|
36341
|
+
* @param key - Optional key generator function for argument-based caching
|
|
36334
36342
|
*/
|
|
36335
|
-
constructor(fn, interval) {
|
|
36343
|
+
constructor(fn, interval, key = () => NEVER_VALUE) {
|
|
36336
36344
|
this.fn = fn;
|
|
36337
36345
|
this.interval = interval;
|
|
36338
|
-
|
|
36346
|
+
this.key = key;
|
|
36347
|
+
/** Cache map storing results per strategy/exchange/mode/argKey combination */
|
|
36339
36348
|
this._cacheMap = new Map();
|
|
36340
36349
|
/**
|
|
36341
36350
|
* Execute function with caching based on timeframe intervals.
|
|
@@ -36382,7 +36391,9 @@ class CacheInstance {
|
|
|
36382
36391
|
throw new Error(`CacheInstance unknown cache ttl interval=${this.interval}`);
|
|
36383
36392
|
}
|
|
36384
36393
|
}
|
|
36385
|
-
const
|
|
36394
|
+
const contextKey = CREATE_KEY_FN(bt.methodContextService.context.strategyName, bt.methodContextService.context.exchangeName, bt.methodContextService.context.frameName, bt.executionContextService.context.backtest);
|
|
36395
|
+
const argKey = String(this.key(args));
|
|
36396
|
+
const key = `${contextKey}:${argKey}`;
|
|
36386
36397
|
const currentWhen = bt.executionContextService.context.when;
|
|
36387
36398
|
const cached = this._cacheMap.get(key);
|
|
36388
36399
|
if (cached) {
|
|
@@ -36400,13 +36411,13 @@ class CacheInstance {
|
|
|
36400
36411
|
return newCache;
|
|
36401
36412
|
};
|
|
36402
36413
|
/**
|
|
36403
|
-
* Clear cached
|
|
36414
|
+
* Clear cached values for current execution context.
|
|
36404
36415
|
*
|
|
36405
|
-
* Removes
|
|
36416
|
+
* Removes all cached entries for the current strategy/exchange/mode combination
|
|
36406
36417
|
* from this instance's cache map. The next `run()` call will recompute the value.
|
|
36407
36418
|
*
|
|
36408
36419
|
* Requires active execution context (strategy, exchange, backtest mode) and method context
|
|
36409
|
-
* to determine which cache
|
|
36420
|
+
* to determine which cache entries to clear.
|
|
36410
36421
|
*
|
|
36411
36422
|
* @example
|
|
36412
36423
|
* ```typescript
|
|
@@ -36414,14 +36425,51 @@ class CacheInstance {
|
|
|
36414
36425
|
* const result1 = instance.run("BTCUSDT", 14); // Computed
|
|
36415
36426
|
* const result2 = instance.run("BTCUSDT", 14); // Cached
|
|
36416
36427
|
*
|
|
36417
|
-
* instance.clear(); // Clear cache for current context
|
|
36428
|
+
* instance.clear(); // Clear all cache entries for current context
|
|
36418
36429
|
*
|
|
36419
36430
|
* const result3 = instance.run("BTCUSDT", 14); // Recomputed
|
|
36420
36431
|
* ```
|
|
36421
36432
|
*/
|
|
36422
36433
|
this.clear = () => {
|
|
36423
|
-
const
|
|
36424
|
-
|
|
36434
|
+
const contextKey = CREATE_KEY_FN(bt.methodContextService.context.strategyName, bt.methodContextService.context.exchangeName, bt.methodContextService.context.frameName, bt.executionContextService.context.backtest);
|
|
36435
|
+
const prefix = `${contextKey}:`;
|
|
36436
|
+
for (const key of this._cacheMap.keys()) {
|
|
36437
|
+
if (key.startsWith(prefix)) {
|
|
36438
|
+
this._cacheMap.delete(key);
|
|
36439
|
+
}
|
|
36440
|
+
}
|
|
36441
|
+
};
|
|
36442
|
+
/**
|
|
36443
|
+
* Garbage collect expired cache entries.
|
|
36444
|
+
*
|
|
36445
|
+
* Removes all cached entries whose interval has expired (not aligned with current time).
|
|
36446
|
+
* Call this periodically to free memory from stale cache entries.
|
|
36447
|
+
*
|
|
36448
|
+
* Requires active execution context to get current time.
|
|
36449
|
+
*
|
|
36450
|
+
* @returns Number of entries removed
|
|
36451
|
+
*
|
|
36452
|
+
* @example
|
|
36453
|
+
* ```typescript
|
|
36454
|
+
* const instance = new CacheInstance(calculateIndicator, "1h");
|
|
36455
|
+
* instance.run("BTCUSDT", 14); // Cached at 10:00
|
|
36456
|
+
* instance.run("ETHUSDT", 14); // Cached at 10:00
|
|
36457
|
+
* // Time passes to 11:00
|
|
36458
|
+
* const removed = instance.gc(); // Returns 2, removes both expired entries
|
|
36459
|
+
* ```
|
|
36460
|
+
*/
|
|
36461
|
+
this.gc = () => {
|
|
36462
|
+
const currentWhen = bt.executionContextService.context.when;
|
|
36463
|
+
const currentAligned = align(currentWhen.getTime(), this.interval);
|
|
36464
|
+
let removed = 0;
|
|
36465
|
+
for (const [key, cached] of this._cacheMap.entries()) {
|
|
36466
|
+
const cachedAligned = align(cached.when.getTime(), this.interval);
|
|
36467
|
+
if (currentAligned !== cachedAligned) {
|
|
36468
|
+
this._cacheMap.delete(key);
|
|
36469
|
+
removed++;
|
|
36470
|
+
}
|
|
36471
|
+
}
|
|
36472
|
+
return removed;
|
|
36425
36473
|
};
|
|
36426
36474
|
}
|
|
36427
36475
|
}
|
|
@@ -36446,7 +36494,7 @@ class CacheUtils {
|
|
|
36446
36494
|
* Memoized function to get or create CacheInstance for a function.
|
|
36447
36495
|
* Each function gets its own isolated cache instance.
|
|
36448
36496
|
*/
|
|
36449
|
-
this._getInstance = functoolsKit.memoize(([run]) => run, (run, interval) => new CacheInstance(run, interval));
|
|
36497
|
+
this._getInstance = functoolsKit.memoize(([run]) => run, (run, interval, key) => new CacheInstance(run, interval, key));
|
|
36450
36498
|
/**
|
|
36451
36499
|
* Wrap a function with caching based on timeframe intervals.
|
|
36452
36500
|
*
|
|
@@ -36454,8 +36502,10 @@ class CacheUtils {
|
|
|
36454
36502
|
* and invalidates based on the specified candle interval.
|
|
36455
36503
|
*
|
|
36456
36504
|
* @template T - Function type to cache
|
|
36505
|
+
* @template K - Key type for argument-based caching
|
|
36457
36506
|
* @param run - Function to wrap with caching
|
|
36458
|
-
* @param interval - Candle interval for cache invalidation (e.g., "1m", "1h")
|
|
36507
|
+
* @param context.interval - Candle interval for cache invalidation (e.g., "1m", "1h")
|
|
36508
|
+
* @param context.key - Optional key generator function for argument-based caching
|
|
36459
36509
|
* @returns Wrapped function with automatic caching
|
|
36460
36510
|
*
|
|
36461
36511
|
* @example
|
|
@@ -36465,9 +36515,17 @@ class CacheUtils {
|
|
|
36465
36515
|
* return result;
|
|
36466
36516
|
* };
|
|
36467
36517
|
*
|
|
36468
|
-
*
|
|
36469
|
-
* const
|
|
36470
|
-
*
|
|
36518
|
+
* // Without key - single cache entry per context
|
|
36519
|
+
* const cachedCalculate = Cache.fn(calculateIndicator, { interval: "15m" });
|
|
36520
|
+
*
|
|
36521
|
+
* // With key - separate cache entries per symbol
|
|
36522
|
+
* const cachedCalculate = Cache.fn(calculateIndicator, {
|
|
36523
|
+
* interval: "15m",
|
|
36524
|
+
* key: ([symbol]) => symbol,
|
|
36525
|
+
* });
|
|
36526
|
+
* const result1 = cachedCalculate("BTCUSDT", 14); // Computed
|
|
36527
|
+
* const result2 = cachedCalculate("ETHUSDT", 14); // Computed (different key)
|
|
36528
|
+
* const result3 = cachedCalculate("BTCUSDT", 14); // Cached (same key, same interval)
|
|
36471
36529
|
* ```
|
|
36472
36530
|
*/
|
|
36473
36531
|
this.fn = (run, context) => {
|
|
@@ -36475,7 +36533,7 @@ class CacheUtils {
|
|
|
36475
36533
|
context,
|
|
36476
36534
|
});
|
|
36477
36535
|
const wrappedFn = (...args) => {
|
|
36478
|
-
const instance = this._getInstance(run, context.interval);
|
|
36536
|
+
const instance = this._getInstance(run, context.interval, context.key);
|
|
36479
36537
|
return instance.run(...args).value;
|
|
36480
36538
|
};
|
|
36481
36539
|
return wrappedFn;
|
|
@@ -36548,8 +36606,48 @@ class CacheUtils {
|
|
|
36548
36606
|
bt.loggerService.info(CACHE_METHOD_NAME_CLEAR, {
|
|
36549
36607
|
run,
|
|
36550
36608
|
});
|
|
36609
|
+
if (!MethodContextService.hasContext()) {
|
|
36610
|
+
console.warn(`${CACHE_METHOD_NAME_CLEAR} called without method context, skipping clear`);
|
|
36611
|
+
return;
|
|
36612
|
+
}
|
|
36613
|
+
if (!ExecutionContextService.hasContext()) {
|
|
36614
|
+
console.warn(`${CACHE_METHOD_NAME_CLEAR} called without execution context, skipping clear`);
|
|
36615
|
+
return;
|
|
36616
|
+
}
|
|
36551
36617
|
this._getInstance.get(run).clear();
|
|
36552
36618
|
};
|
|
36619
|
+
/**
|
|
36620
|
+
* Garbage collect expired cache entries for a specific function.
|
|
36621
|
+
*
|
|
36622
|
+
* Removes all cached entries whose interval has expired (not aligned with current time).
|
|
36623
|
+
* Call this periodically to free memory from stale cache entries.
|
|
36624
|
+
*
|
|
36625
|
+
* Requires active execution context to get current time.
|
|
36626
|
+
*
|
|
36627
|
+
* @template T - Function type
|
|
36628
|
+
* @param run - Function whose expired cache entries should be removed
|
|
36629
|
+
* @returns Number of entries removed
|
|
36630
|
+
*
|
|
36631
|
+
* @example
|
|
36632
|
+
* ```typescript
|
|
36633
|
+
* const cachedFn = Cache.fn(calculateIndicator, { interval: "1h" });
|
|
36634
|
+
*
|
|
36635
|
+
* cachedFn("BTCUSDT", 14); // Cached at 10:00
|
|
36636
|
+
* cachedFn("ETHUSDT", 14); // Cached at 10:00
|
|
36637
|
+
* // Time passes to 11:00
|
|
36638
|
+
* const removed = Cache.gc(calculateIndicator); // Returns 2
|
|
36639
|
+
* ```
|
|
36640
|
+
*/
|
|
36641
|
+
this.gc = (run) => {
|
|
36642
|
+
bt.loggerService.info(CACHE_METHOD_NAME_GC, {
|
|
36643
|
+
run,
|
|
36644
|
+
});
|
|
36645
|
+
if (!ExecutionContextService.hasContext()) {
|
|
36646
|
+
console.warn(`${CACHE_METHOD_NAME_GC} called without execution context, skipping garbage collection`);
|
|
36647
|
+
return;
|
|
36648
|
+
}
|
|
36649
|
+
return this._getInstance.get(run).gc();
|
|
36650
|
+
};
|
|
36553
36651
|
}
|
|
36554
36652
|
}
|
|
36555
36653
|
/**
|
package/build/index.mjs
CHANGED
|
@@ -36230,6 +36230,7 @@ const Exchange = new ExchangeUtils();
|
|
|
36230
36230
|
const CACHE_METHOD_NAME_FLUSH = "CacheUtils.flush";
|
|
36231
36231
|
const CACHE_METHOD_NAME_CLEAR = "CacheInstance.clear";
|
|
36232
36232
|
const CACHE_METHOD_NAME_RUN = "CacheInstance.run";
|
|
36233
|
+
const CACHE_METHOD_NAME_GC = "CacheInstance.gc";
|
|
36233
36234
|
const CACHE_METHOD_NAME_FN = "CacheUtils.fn";
|
|
36234
36235
|
const MS_PER_MINUTE = 60000;
|
|
36235
36236
|
const INTERVAL_MINUTES = {
|
|
@@ -36287,6 +36288,11 @@ const CREATE_KEY_FN = (strategyName, exchangeName, frameName, backtest) => {
|
|
|
36287
36288
|
parts.push(backtest ? "backtest" : "live");
|
|
36288
36289
|
return parts.join(":");
|
|
36289
36290
|
};
|
|
36291
|
+
/**
|
|
36292
|
+
* A unique symbol representing a value that should never occur.
|
|
36293
|
+
* Used as default key when no key function is provided.
|
|
36294
|
+
*/
|
|
36295
|
+
const NEVER_VALUE = Symbol("never");
|
|
36290
36296
|
/**
|
|
36291
36297
|
* Instance class for caching function results with timeframe-based invalidation.
|
|
36292
36298
|
*
|
|
@@ -36295,6 +36301,7 @@ const CREATE_KEY_FN = (strategyName, exchangeName, frameName, backtest) => {
|
|
|
36295
36301
|
* Cache is invalidated when the current time moves to a different interval.
|
|
36296
36302
|
*
|
|
36297
36303
|
* @template T - Function type to cache
|
|
36304
|
+
* @template K - Key type for argument-based caching
|
|
36298
36305
|
*
|
|
36299
36306
|
* @example
|
|
36300
36307
|
* ```typescript
|
|
@@ -36311,11 +36318,13 @@ class CacheInstance {
|
|
|
36311
36318
|
*
|
|
36312
36319
|
* @param fn - Function to cache
|
|
36313
36320
|
* @param interval - Candle interval for cache invalidation (e.g., "1m", "1h")
|
|
36321
|
+
* @param key - Optional key generator function for argument-based caching
|
|
36314
36322
|
*/
|
|
36315
|
-
constructor(fn, interval) {
|
|
36323
|
+
constructor(fn, interval, key = () => NEVER_VALUE) {
|
|
36316
36324
|
this.fn = fn;
|
|
36317
36325
|
this.interval = interval;
|
|
36318
|
-
|
|
36326
|
+
this.key = key;
|
|
36327
|
+
/** Cache map storing results per strategy/exchange/mode/argKey combination */
|
|
36319
36328
|
this._cacheMap = new Map();
|
|
36320
36329
|
/**
|
|
36321
36330
|
* Execute function with caching based on timeframe intervals.
|
|
@@ -36362,7 +36371,9 @@ class CacheInstance {
|
|
|
36362
36371
|
throw new Error(`CacheInstance unknown cache ttl interval=${this.interval}`);
|
|
36363
36372
|
}
|
|
36364
36373
|
}
|
|
36365
|
-
const
|
|
36374
|
+
const contextKey = CREATE_KEY_FN(bt.methodContextService.context.strategyName, bt.methodContextService.context.exchangeName, bt.methodContextService.context.frameName, bt.executionContextService.context.backtest);
|
|
36375
|
+
const argKey = String(this.key(args));
|
|
36376
|
+
const key = `${contextKey}:${argKey}`;
|
|
36366
36377
|
const currentWhen = bt.executionContextService.context.when;
|
|
36367
36378
|
const cached = this._cacheMap.get(key);
|
|
36368
36379
|
if (cached) {
|
|
@@ -36380,13 +36391,13 @@ class CacheInstance {
|
|
|
36380
36391
|
return newCache;
|
|
36381
36392
|
};
|
|
36382
36393
|
/**
|
|
36383
|
-
* Clear cached
|
|
36394
|
+
* Clear cached values for current execution context.
|
|
36384
36395
|
*
|
|
36385
|
-
* Removes
|
|
36396
|
+
* Removes all cached entries for the current strategy/exchange/mode combination
|
|
36386
36397
|
* from this instance's cache map. The next `run()` call will recompute the value.
|
|
36387
36398
|
*
|
|
36388
36399
|
* Requires active execution context (strategy, exchange, backtest mode) and method context
|
|
36389
|
-
* to determine which cache
|
|
36400
|
+
* to determine which cache entries to clear.
|
|
36390
36401
|
*
|
|
36391
36402
|
* @example
|
|
36392
36403
|
* ```typescript
|
|
@@ -36394,14 +36405,51 @@ class CacheInstance {
|
|
|
36394
36405
|
* const result1 = instance.run("BTCUSDT", 14); // Computed
|
|
36395
36406
|
* const result2 = instance.run("BTCUSDT", 14); // Cached
|
|
36396
36407
|
*
|
|
36397
|
-
* instance.clear(); // Clear cache for current context
|
|
36408
|
+
* instance.clear(); // Clear all cache entries for current context
|
|
36398
36409
|
*
|
|
36399
36410
|
* const result3 = instance.run("BTCUSDT", 14); // Recomputed
|
|
36400
36411
|
* ```
|
|
36401
36412
|
*/
|
|
36402
36413
|
this.clear = () => {
|
|
36403
|
-
const
|
|
36404
|
-
|
|
36414
|
+
const contextKey = CREATE_KEY_FN(bt.methodContextService.context.strategyName, bt.methodContextService.context.exchangeName, bt.methodContextService.context.frameName, bt.executionContextService.context.backtest);
|
|
36415
|
+
const prefix = `${contextKey}:`;
|
|
36416
|
+
for (const key of this._cacheMap.keys()) {
|
|
36417
|
+
if (key.startsWith(prefix)) {
|
|
36418
|
+
this._cacheMap.delete(key);
|
|
36419
|
+
}
|
|
36420
|
+
}
|
|
36421
|
+
};
|
|
36422
|
+
/**
|
|
36423
|
+
* Garbage collect expired cache entries.
|
|
36424
|
+
*
|
|
36425
|
+
* Removes all cached entries whose interval has expired (not aligned with current time).
|
|
36426
|
+
* Call this periodically to free memory from stale cache entries.
|
|
36427
|
+
*
|
|
36428
|
+
* Requires active execution context to get current time.
|
|
36429
|
+
*
|
|
36430
|
+
* @returns Number of entries removed
|
|
36431
|
+
*
|
|
36432
|
+
* @example
|
|
36433
|
+
* ```typescript
|
|
36434
|
+
* const instance = new CacheInstance(calculateIndicator, "1h");
|
|
36435
|
+
* instance.run("BTCUSDT", 14); // Cached at 10:00
|
|
36436
|
+
* instance.run("ETHUSDT", 14); // Cached at 10:00
|
|
36437
|
+
* // Time passes to 11:00
|
|
36438
|
+
* const removed = instance.gc(); // Returns 2, removes both expired entries
|
|
36439
|
+
* ```
|
|
36440
|
+
*/
|
|
36441
|
+
this.gc = () => {
|
|
36442
|
+
const currentWhen = bt.executionContextService.context.when;
|
|
36443
|
+
const currentAligned = align(currentWhen.getTime(), this.interval);
|
|
36444
|
+
let removed = 0;
|
|
36445
|
+
for (const [key, cached] of this._cacheMap.entries()) {
|
|
36446
|
+
const cachedAligned = align(cached.when.getTime(), this.interval);
|
|
36447
|
+
if (currentAligned !== cachedAligned) {
|
|
36448
|
+
this._cacheMap.delete(key);
|
|
36449
|
+
removed++;
|
|
36450
|
+
}
|
|
36451
|
+
}
|
|
36452
|
+
return removed;
|
|
36405
36453
|
};
|
|
36406
36454
|
}
|
|
36407
36455
|
}
|
|
@@ -36426,7 +36474,7 @@ class CacheUtils {
|
|
|
36426
36474
|
* Memoized function to get or create CacheInstance for a function.
|
|
36427
36475
|
* Each function gets its own isolated cache instance.
|
|
36428
36476
|
*/
|
|
36429
|
-
this._getInstance = memoize(([run]) => run, (run, interval) => new CacheInstance(run, interval));
|
|
36477
|
+
this._getInstance = memoize(([run]) => run, (run, interval, key) => new CacheInstance(run, interval, key));
|
|
36430
36478
|
/**
|
|
36431
36479
|
* Wrap a function with caching based on timeframe intervals.
|
|
36432
36480
|
*
|
|
@@ -36434,8 +36482,10 @@ class CacheUtils {
|
|
|
36434
36482
|
* and invalidates based on the specified candle interval.
|
|
36435
36483
|
*
|
|
36436
36484
|
* @template T - Function type to cache
|
|
36485
|
+
* @template K - Key type for argument-based caching
|
|
36437
36486
|
* @param run - Function to wrap with caching
|
|
36438
|
-
* @param interval - Candle interval for cache invalidation (e.g., "1m", "1h")
|
|
36487
|
+
* @param context.interval - Candle interval for cache invalidation (e.g., "1m", "1h")
|
|
36488
|
+
* @param context.key - Optional key generator function for argument-based caching
|
|
36439
36489
|
* @returns Wrapped function with automatic caching
|
|
36440
36490
|
*
|
|
36441
36491
|
* @example
|
|
@@ -36445,9 +36495,17 @@ class CacheUtils {
|
|
|
36445
36495
|
* return result;
|
|
36446
36496
|
* };
|
|
36447
36497
|
*
|
|
36448
|
-
*
|
|
36449
|
-
* const
|
|
36450
|
-
*
|
|
36498
|
+
* // Without key - single cache entry per context
|
|
36499
|
+
* const cachedCalculate = Cache.fn(calculateIndicator, { interval: "15m" });
|
|
36500
|
+
*
|
|
36501
|
+
* // With key - separate cache entries per symbol
|
|
36502
|
+
* const cachedCalculate = Cache.fn(calculateIndicator, {
|
|
36503
|
+
* interval: "15m",
|
|
36504
|
+
* key: ([symbol]) => symbol,
|
|
36505
|
+
* });
|
|
36506
|
+
* const result1 = cachedCalculate("BTCUSDT", 14); // Computed
|
|
36507
|
+
* const result2 = cachedCalculate("ETHUSDT", 14); // Computed (different key)
|
|
36508
|
+
* const result3 = cachedCalculate("BTCUSDT", 14); // Cached (same key, same interval)
|
|
36451
36509
|
* ```
|
|
36452
36510
|
*/
|
|
36453
36511
|
this.fn = (run, context) => {
|
|
@@ -36455,7 +36513,7 @@ class CacheUtils {
|
|
|
36455
36513
|
context,
|
|
36456
36514
|
});
|
|
36457
36515
|
const wrappedFn = (...args) => {
|
|
36458
|
-
const instance = this._getInstance(run, context.interval);
|
|
36516
|
+
const instance = this._getInstance(run, context.interval, context.key);
|
|
36459
36517
|
return instance.run(...args).value;
|
|
36460
36518
|
};
|
|
36461
36519
|
return wrappedFn;
|
|
@@ -36528,8 +36586,48 @@ class CacheUtils {
|
|
|
36528
36586
|
bt.loggerService.info(CACHE_METHOD_NAME_CLEAR, {
|
|
36529
36587
|
run,
|
|
36530
36588
|
});
|
|
36589
|
+
if (!MethodContextService.hasContext()) {
|
|
36590
|
+
console.warn(`${CACHE_METHOD_NAME_CLEAR} called without method context, skipping clear`);
|
|
36591
|
+
return;
|
|
36592
|
+
}
|
|
36593
|
+
if (!ExecutionContextService.hasContext()) {
|
|
36594
|
+
console.warn(`${CACHE_METHOD_NAME_CLEAR} called without execution context, skipping clear`);
|
|
36595
|
+
return;
|
|
36596
|
+
}
|
|
36531
36597
|
this._getInstance.get(run).clear();
|
|
36532
36598
|
};
|
|
36599
|
+
/**
|
|
36600
|
+
* Garbage collect expired cache entries for a specific function.
|
|
36601
|
+
*
|
|
36602
|
+
* Removes all cached entries whose interval has expired (not aligned with current time).
|
|
36603
|
+
* Call this periodically to free memory from stale cache entries.
|
|
36604
|
+
*
|
|
36605
|
+
* Requires active execution context to get current time.
|
|
36606
|
+
*
|
|
36607
|
+
* @template T - Function type
|
|
36608
|
+
* @param run - Function whose expired cache entries should be removed
|
|
36609
|
+
* @returns Number of entries removed
|
|
36610
|
+
*
|
|
36611
|
+
* @example
|
|
36612
|
+
* ```typescript
|
|
36613
|
+
* const cachedFn = Cache.fn(calculateIndicator, { interval: "1h" });
|
|
36614
|
+
*
|
|
36615
|
+
* cachedFn("BTCUSDT", 14); // Cached at 10:00
|
|
36616
|
+
* cachedFn("ETHUSDT", 14); // Cached at 10:00
|
|
36617
|
+
* // Time passes to 11:00
|
|
36618
|
+
* const removed = Cache.gc(calculateIndicator); // Returns 2
|
|
36619
|
+
* ```
|
|
36620
|
+
*/
|
|
36621
|
+
this.gc = (run) => {
|
|
36622
|
+
bt.loggerService.info(CACHE_METHOD_NAME_GC, {
|
|
36623
|
+
run,
|
|
36624
|
+
});
|
|
36625
|
+
if (!ExecutionContextService.hasContext()) {
|
|
36626
|
+
console.warn(`${CACHE_METHOD_NAME_GC} called without execution context, skipping garbage collection`);
|
|
36627
|
+
return;
|
|
36628
|
+
}
|
|
36629
|
+
return this._getInstance.get(run).gc();
|
|
36630
|
+
};
|
|
36533
36631
|
}
|
|
36534
36632
|
}
|
|
36535
36633
|
/**
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -13901,8 +13901,10 @@ declare class CacheUtils {
|
|
|
13901
13901
|
* and invalidates based on the specified candle interval.
|
|
13902
13902
|
*
|
|
13903
13903
|
* @template T - Function type to cache
|
|
13904
|
+
* @template K - Key type for argument-based caching
|
|
13904
13905
|
* @param run - Function to wrap with caching
|
|
13905
|
-
* @param interval - Candle interval for cache invalidation (e.g., "1m", "1h")
|
|
13906
|
+
* @param context.interval - Candle interval for cache invalidation (e.g., "1m", "1h")
|
|
13907
|
+
* @param context.key - Optional key generator function for argument-based caching
|
|
13906
13908
|
* @returns Wrapped function with automatic caching
|
|
13907
13909
|
*
|
|
13908
13910
|
* @example
|
|
@@ -13912,13 +13914,22 @@ declare class CacheUtils {
|
|
|
13912
13914
|
* return result;
|
|
13913
13915
|
* };
|
|
13914
13916
|
*
|
|
13915
|
-
*
|
|
13916
|
-
* const
|
|
13917
|
-
*
|
|
13917
|
+
* // Without key - single cache entry per context
|
|
13918
|
+
* const cachedCalculate = Cache.fn(calculateIndicator, { interval: "15m" });
|
|
13919
|
+
*
|
|
13920
|
+
* // With key - separate cache entries per symbol
|
|
13921
|
+
* const cachedCalculate = Cache.fn(calculateIndicator, {
|
|
13922
|
+
* interval: "15m",
|
|
13923
|
+
* key: ([symbol]) => symbol,
|
|
13924
|
+
* });
|
|
13925
|
+
* const result1 = cachedCalculate("BTCUSDT", 14); // Computed
|
|
13926
|
+
* const result2 = cachedCalculate("ETHUSDT", 14); // Computed (different key)
|
|
13927
|
+
* const result3 = cachedCalculate("BTCUSDT", 14); // Cached (same key, same interval)
|
|
13918
13928
|
* ```
|
|
13919
13929
|
*/
|
|
13920
|
-
fn: <T extends Function>(run: T, context: {
|
|
13930
|
+
fn: <T extends Function, K = symbol>(run: T, context: {
|
|
13921
13931
|
interval: CandleInterval;
|
|
13932
|
+
key?: (args: Parameters<T>) => K;
|
|
13922
13933
|
}) => T;
|
|
13923
13934
|
/**
|
|
13924
13935
|
* Flush (remove) cached CacheInstance for a specific function or all functions.
|
|
@@ -13980,6 +13991,29 @@ declare class CacheUtils {
|
|
|
13980
13991
|
* ```
|
|
13981
13992
|
*/
|
|
13982
13993
|
clear: <T extends Function>(run: T) => void;
|
|
13994
|
+
/**
|
|
13995
|
+
* Garbage collect expired cache entries for a specific function.
|
|
13996
|
+
*
|
|
13997
|
+
* Removes all cached entries whose interval has expired (not aligned with current time).
|
|
13998
|
+
* Call this periodically to free memory from stale cache entries.
|
|
13999
|
+
*
|
|
14000
|
+
* Requires active execution context to get current time.
|
|
14001
|
+
*
|
|
14002
|
+
* @template T - Function type
|
|
14003
|
+
* @param run - Function whose expired cache entries should be removed
|
|
14004
|
+
* @returns Number of entries removed
|
|
14005
|
+
*
|
|
14006
|
+
* @example
|
|
14007
|
+
* ```typescript
|
|
14008
|
+
* const cachedFn = Cache.fn(calculateIndicator, { interval: "1h" });
|
|
14009
|
+
*
|
|
14010
|
+
* cachedFn("BTCUSDT", 14); // Cached at 10:00
|
|
14011
|
+
* cachedFn("ETHUSDT", 14); // Cached at 10:00
|
|
14012
|
+
* // Time passes to 11:00
|
|
14013
|
+
* const removed = Cache.gc(calculateIndicator); // Returns 2
|
|
14014
|
+
* ```
|
|
14015
|
+
*/
|
|
14016
|
+
gc: <T extends Function>(run: T) => number;
|
|
13983
14017
|
}
|
|
13984
14018
|
/**
|
|
13985
14019
|
* Singleton instance of CacheUtils for convenient function caching.
|