backtest-kit 1.1.7 → 1.1.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/types.d.ts CHANGED
@@ -819,6 +819,51 @@ interface ProgressContract {
819
819
  progress: number;
820
820
  }
821
821
 
822
+ /**
823
+ * Performance metric types tracked by the system.
824
+ *
825
+ * Backtest metrics:
826
+ * - backtest_total: Total backtest duration from start to finish
827
+ * - backtest_timeframe: Duration to process a single timeframe iteration
828
+ * - backtest_signal: Duration to process a signal (tick + getNextCandles + backtest)
829
+ *
830
+ * Live metrics:
831
+ * - live_tick: Duration of a single live tick iteration
832
+ */
833
+ type PerformanceMetricType = "backtest_total" | "backtest_timeframe" | "backtest_signal" | "live_tick";
834
+ /**
835
+ * Contract for performance tracking events.
836
+ *
837
+ * Emitted during execution to track performance metrics for various operations.
838
+ * Useful for profiling and identifying bottlenecks.
839
+ *
840
+ * @example
841
+ * ```typescript
842
+ * import { listenPerformance } from "backtest-kit";
843
+ *
844
+ * listenPerformance((event) => {
845
+ * console.log(`${event.metricType}: ${event.duration.toFixed(2)}ms`);
846
+ * console.log(`${event.strategyName} @ ${event.exchangeName}`);
847
+ * });
848
+ * ```
849
+ */
850
+ interface PerformanceContract {
851
+ /** Timestamp when the metric was recorded (milliseconds since epoch) */
852
+ timestamp: number;
853
+ /** Type of operation being measured */
854
+ metricType: PerformanceMetricType;
855
+ /** Duration of the operation in milliseconds */
856
+ duration: number;
857
+ /** Strategy name associated with this metric */
858
+ strategyName: string;
859
+ /** Exchange name associated with this metric */
860
+ exchangeName: string;
861
+ /** Trading symbol associated with this metric */
862
+ symbol: string;
863
+ /** Whether this metric is from backtest mode (true) or live mode (false) */
864
+ backtest: boolean;
865
+ }
866
+
822
867
  /**
823
868
  * Subscribes to all signal events with queued async processing.
824
869
  *
@@ -1078,6 +1123,39 @@ declare function listenDoneOnce(filterFn: (event: DoneContract) => boolean, fn:
1078
1123
  * ```
1079
1124
  */
1080
1125
  declare function listenProgress(fn: (event: ProgressContract) => void): () => void;
1126
+ /**
1127
+ * Subscribes to performance metric events with queued async processing.
1128
+ *
1129
+ * Emits during strategy execution to track timing metrics for operations.
1130
+ * Useful for profiling and identifying performance bottlenecks.
1131
+ * Events are processed sequentially in order received, even if callback is async.
1132
+ * Uses queued wrapper to prevent concurrent execution of the callback.
1133
+ *
1134
+ * @param fn - Callback function to handle performance events
1135
+ * @returns Unsubscribe function to stop listening to events
1136
+ *
1137
+ * @example
1138
+ * ```typescript
1139
+ * import { listenPerformance, Backtest } from "backtest-kit";
1140
+ *
1141
+ * const unsubscribe = listenPerformance((event) => {
1142
+ * console.log(`${event.metricType}: ${event.duration.toFixed(2)}ms`);
1143
+ * if (event.duration > 100) {
1144
+ * console.warn("Slow operation detected:", event.metricType);
1145
+ * }
1146
+ * });
1147
+ *
1148
+ * Backtest.background("BTCUSDT", {
1149
+ * strategyName: "my-strategy",
1150
+ * exchangeName: "binance",
1151
+ * frameName: "1d-backtest"
1152
+ * });
1153
+ *
1154
+ * // Later: stop listening
1155
+ * unsubscribe();
1156
+ * ```
1157
+ */
1158
+ declare function listenPerformance(fn: (event: PerformanceContract) => void): () => void;
1081
1159
 
1082
1160
  /**
1083
1161
  * Fetches historical candle data from the registered exchange.
@@ -1590,6 +1668,145 @@ declare class LiveMarkdownService {
1590
1668
  protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
1591
1669
  }
1592
1670
 
1671
+ /**
1672
+ * Aggregated statistics for a specific metric type.
1673
+ */
1674
+ interface MetricStats {
1675
+ /** Type of metric */
1676
+ metricType: PerformanceMetricType;
1677
+ /** Number of recorded samples */
1678
+ count: number;
1679
+ /** Total duration across all samples (ms) */
1680
+ totalDuration: number;
1681
+ /** Average duration (ms) */
1682
+ avgDuration: number;
1683
+ /** Minimum duration (ms) */
1684
+ minDuration: number;
1685
+ /** Maximum duration (ms) */
1686
+ maxDuration: number;
1687
+ /** Standard deviation of duration (ms) */
1688
+ stdDev: number;
1689
+ /** Median duration (ms) */
1690
+ median: number;
1691
+ /** 95th percentile duration (ms) */
1692
+ p95: number;
1693
+ /** 99th percentile duration (ms) */
1694
+ p99: number;
1695
+ }
1696
+ /**
1697
+ * Performance statistics aggregated by strategy.
1698
+ */
1699
+ interface PerformanceStatistics {
1700
+ /** Strategy name */
1701
+ strategyName: string;
1702
+ /** Total number of performance events recorded */
1703
+ totalEvents: number;
1704
+ /** Total execution time across all metrics (ms) */
1705
+ totalDuration: number;
1706
+ /** Statistics grouped by metric type */
1707
+ metricStats: Record<string, MetricStats>;
1708
+ /** All raw performance events */
1709
+ events: PerformanceContract[];
1710
+ }
1711
+ /**
1712
+ * Service for collecting and analyzing performance metrics.
1713
+ *
1714
+ * Features:
1715
+ * - Listens to performance events via performanceEmitter
1716
+ * - Accumulates metrics per strategy
1717
+ * - Calculates aggregated statistics (avg, min, max, percentiles)
1718
+ * - Generates markdown reports with bottleneck analysis
1719
+ * - Saves reports to disk in logs/performance/{strategyName}.md
1720
+ *
1721
+ * @example
1722
+ * ```typescript
1723
+ * import { listenPerformance } from "backtest-kit";
1724
+ *
1725
+ * // Subscribe to performance events
1726
+ * listenPerformance((event) => {
1727
+ * console.log(`${event.metricType}: ${event.duration.toFixed(2)}ms`);
1728
+ * });
1729
+ *
1730
+ * // After execution, generate report
1731
+ * const stats = await Performance.getData("my-strategy");
1732
+ * console.log("Bottlenecks:", stats.metricStats);
1733
+ *
1734
+ * // Save report to disk
1735
+ * await Performance.dump("my-strategy");
1736
+ * ```
1737
+ */
1738
+ declare class PerformanceMarkdownService {
1739
+ /** Logger service for debug output */
1740
+ private readonly loggerService;
1741
+ /**
1742
+ * Memoized function to get or create PerformanceStorage for a strategy.
1743
+ * Each strategy gets its own isolated storage instance.
1744
+ */
1745
+ private getStorage;
1746
+ /**
1747
+ * Processes performance events and accumulates metrics.
1748
+ * Should be called from performance tracking code.
1749
+ *
1750
+ * @param event - Performance event with timing data
1751
+ */
1752
+ private track;
1753
+ /**
1754
+ * Gets aggregated performance statistics for a strategy.
1755
+ *
1756
+ * @param strategyName - Strategy name to get data for
1757
+ * @returns Performance statistics with aggregated metrics
1758
+ *
1759
+ * @example
1760
+ * ```typescript
1761
+ * const stats = await performanceService.getData("my-strategy");
1762
+ * console.log("Total time:", stats.totalDuration);
1763
+ * console.log("Slowest operation:", Object.values(stats.metricStats)
1764
+ * .sort((a, b) => b.avgDuration - a.avgDuration)[0]);
1765
+ * ```
1766
+ */
1767
+ getData: (strategyName: string) => Promise<PerformanceStatistics>;
1768
+ /**
1769
+ * Generates markdown report with performance analysis.
1770
+ *
1771
+ * @param strategyName - Strategy name to generate report for
1772
+ * @returns Markdown formatted report string
1773
+ *
1774
+ * @example
1775
+ * ```typescript
1776
+ * const markdown = await performanceService.getReport("my-strategy");
1777
+ * console.log(markdown);
1778
+ * ```
1779
+ */
1780
+ getReport: (strategyName: string) => Promise<string>;
1781
+ /**
1782
+ * Saves performance report to disk.
1783
+ *
1784
+ * @param strategyName - Strategy name to save report for
1785
+ * @param path - Directory path to save report
1786
+ *
1787
+ * @example
1788
+ * ```typescript
1789
+ * // Save to default path: ./logs/performance/my-strategy.md
1790
+ * await performanceService.dump("my-strategy");
1791
+ *
1792
+ * // Save to custom path
1793
+ * await performanceService.dump("my-strategy", "./custom/path");
1794
+ * ```
1795
+ */
1796
+ dump: (strategyName: string, path?: string) => Promise<void>;
1797
+ /**
1798
+ * Clears accumulated performance data from storage.
1799
+ *
1800
+ * @param strategyName - Optional strategy name to clear specific strategy data
1801
+ */
1802
+ clear: (strategyName?: string) => Promise<void>;
1803
+ /**
1804
+ * Initializes the service by subscribing to performance events.
1805
+ * Uses singleshot to ensure initialization happens only once.
1806
+ */
1807
+ protected init: (() => Promise<void>) & functools_kit.ISingleshotClearable;
1808
+ }
1809
+
1593
1810
  declare const BASE_WAIT_FOR_INIT_SYMBOL: unique symbol;
1594
1811
  /**
1595
1812
  * Signal data stored in persistence layer.
@@ -2069,6 +2286,125 @@ declare class LiveUtils {
2069
2286
  */
2070
2287
  declare const Live: LiveUtils;
2071
2288
 
2289
+ /**
2290
+ * Performance class provides static methods for performance metrics analysis.
2291
+ *
2292
+ * Features:
2293
+ * - Get aggregated performance statistics by strategy
2294
+ * - Generate markdown reports with bottleneck analysis
2295
+ * - Save reports to disk
2296
+ * - Clear accumulated metrics
2297
+ *
2298
+ * @example
2299
+ * ```typescript
2300
+ * import { Performance, listenPerformance } from "backtest-kit";
2301
+ *
2302
+ * // Subscribe to performance events
2303
+ * listenPerformance((event) => {
2304
+ * console.log(`${event.metricType}: ${event.duration.toFixed(2)}ms`);
2305
+ * });
2306
+ *
2307
+ * // Run backtest...
2308
+ *
2309
+ * // Get aggregated statistics
2310
+ * const stats = await Performance.getData("my-strategy");
2311
+ * console.log("Total time:", stats.totalDuration);
2312
+ * console.log("Slowest operations:", Object.values(stats.metricStats)
2313
+ * .sort((a, b) => b.avgDuration - a.avgDuration)
2314
+ * .slice(0, 5));
2315
+ *
2316
+ * // Generate and save report
2317
+ * await Performance.dump("my-strategy");
2318
+ * ```
2319
+ */
2320
+ declare class Performance {
2321
+ /**
2322
+ * Gets aggregated performance statistics for a strategy.
2323
+ *
2324
+ * Returns detailed metrics grouped by operation type:
2325
+ * - Count, total duration, average, min, max
2326
+ * - Standard deviation for volatility
2327
+ * - Percentiles (median, P95, P99) for outlier detection
2328
+ *
2329
+ * @param strategyName - Strategy name to analyze
2330
+ * @returns Performance statistics with aggregated metrics
2331
+ *
2332
+ * @example
2333
+ * ```typescript
2334
+ * const stats = await Performance.getData("my-strategy");
2335
+ *
2336
+ * // Find slowest operation type
2337
+ * const slowest = Object.values(stats.metricStats)
2338
+ * .sort((a, b) => b.avgDuration - a.avgDuration)[0];
2339
+ * console.log(`Slowest: ${slowest.metricType} (${slowest.avgDuration.toFixed(2)}ms avg)`);
2340
+ *
2341
+ * // Check for outliers
2342
+ * for (const metric of Object.values(stats.metricStats)) {
2343
+ * if (metric.p99 > metric.avgDuration * 5) {
2344
+ * console.warn(`High variance in ${metric.metricType}: P99=${metric.p99}ms, Avg=${metric.avgDuration}ms`);
2345
+ * }
2346
+ * }
2347
+ * ```
2348
+ */
2349
+ static getData(strategyName: string): Promise<PerformanceStatistics>;
2350
+ /**
2351
+ * Generates markdown report with performance analysis.
2352
+ *
2353
+ * Report includes:
2354
+ * - Time distribution across operation types
2355
+ * - Detailed metrics table with statistics
2356
+ * - Percentile analysis for bottleneck detection
2357
+ *
2358
+ * @param strategyName - Strategy name to generate report for
2359
+ * @returns Markdown formatted report string
2360
+ *
2361
+ * @example
2362
+ * ```typescript
2363
+ * const markdown = await Performance.getReport("my-strategy");
2364
+ * console.log(markdown);
2365
+ *
2366
+ * // Or save to file
2367
+ * import fs from "fs/promises";
2368
+ * await fs.writeFile("performance-report.md", markdown);
2369
+ * ```
2370
+ */
2371
+ static getReport(strategyName: string): Promise<string>;
2372
+ /**
2373
+ * Saves performance report to disk.
2374
+ *
2375
+ * Creates directory if it doesn't exist.
2376
+ * Default path: ./logs/performance/{strategyName}.md
2377
+ *
2378
+ * @param strategyName - Strategy name to save report for
2379
+ * @param path - Optional custom directory path
2380
+ *
2381
+ * @example
2382
+ * ```typescript
2383
+ * // Save to default path: ./logs/performance/my-strategy.md
2384
+ * await Performance.dump("my-strategy");
2385
+ *
2386
+ * // Save to custom path: ./reports/perf/my-strategy.md
2387
+ * await Performance.dump("my-strategy", "./reports/perf");
2388
+ * ```
2389
+ */
2390
+ static dump(strategyName: string, path?: string): Promise<void>;
2391
+ /**
2392
+ * Clears accumulated performance metrics from memory.
2393
+ *
2394
+ * @param strategyName - Optional strategy name to clear specific strategy's metrics
2395
+ *
2396
+ * @example
2397
+ * ```typescript
2398
+ * // Clear specific strategy metrics
2399
+ * await Performance.clear("my-strategy");
2400
+ *
2401
+ * // Clear all metrics for all strategies
2402
+ * await Performance.clear();
2403
+ * ```
2404
+ */
2405
+ static clear(strategyName?: string): Promise<void>;
2406
+ }
2407
+
2072
2408
  /**
2073
2409
  * Logger service with automatic context injection.
2074
2410
  *
@@ -2778,6 +3114,7 @@ declare class BacktestLogicPrivateService {
2778
3114
  declare class LiveLogicPrivateService {
2779
3115
  private readonly loggerService;
2780
3116
  private readonly strategyGlobalService;
3117
+ private readonly methodContextService;
2781
3118
  /**
2782
3119
  * Runs live trading for a symbol, streaming results as async generator.
2783
3120
  *
@@ -3067,6 +3404,7 @@ declare const backtest: {
3067
3404
  frameValidationService: FrameValidationService;
3068
3405
  backtestMarkdownService: BacktestMarkdownService;
3069
3406
  liveMarkdownService: LiveMarkdownService;
3407
+ performanceMarkdownService: PerformanceMarkdownService;
3070
3408
  backtestLogicPublicService: BacktestLogicPublicService;
3071
3409
  liveLogicPublicService: LiveLogicPublicService;
3072
3410
  backtestLogicPrivateService: BacktestLogicPrivateService;
@@ -3091,4 +3429,4 @@ declare const backtest: {
3091
3429
  loggerService: LoggerService;
3092
3430
  };
3093
3431
 
3094
- export { Backtest, type BacktestStatistics, type CandleInterval, type DoneContract, type EntityId, ExecutionContextService, type FrameInterval, type ICandleData, type IExchangeSchema, type IFrameSchema, type IPersistBase, type ISignalDto, type ISignalRow, type IStrategyPnL, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, Live, type LiveStatistics, MethodContextService, PersistBase, PersistSignalAdaper, type ProgressContract, type SignalData, type SignalInterval, type TPersistBase, type TPersistBaseCtor, addExchange, addFrame, addStrategy, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listExchanges, listFrames, listStrategies, listenDone, listenDoneOnce, listenError, listenProgress, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, setLogger };
3432
+ export { Backtest, type BacktestStatistics, type CandleInterval, type DoneContract, type EntityId, ExecutionContextService, type FrameInterval, type ICandleData, type IExchangeSchema, type IFrameSchema, type IPersistBase, type ISignalDto, type ISignalRow, type IStrategyPnL, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, Live, type LiveStatistics, MethodContextService, Performance, type PerformanceContract, type PerformanceMetricType, type PerformanceStatistics, PersistBase, PersistSignalAdaper, type ProgressContract, type SignalData, type SignalInterval, type TPersistBase, type TPersistBaseCtor, addExchange, addFrame, addStrategy, formatPrice, formatQuantity, getAveragePrice, getCandles, getDate, getMode, backtest as lib, listExchanges, listFrames, listStrategies, listenDone, listenDoneOnce, listenError, listenPerformance, listenProgress, listenSignal, listenSignalBacktest, listenSignalBacktestOnce, listenSignalLive, listenSignalLiveOnce, listenSignalOnce, setLogger };