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/README.md CHANGED
@@ -19,10 +19,11 @@
19
19
  - โšก **Memory Optimized** - Prototype methods + memoization + streaming
20
20
  - ๐Ÿ”Œ **Flexible Architecture** - Plug your own exchanges and strategies
21
21
  - ๐Ÿ“ **Markdown Reports** - Auto-generated trading reports with statistics (win rate, avg PNL, Sharpe Ratio, Standard Deviation, Certainty Ratio, Expected Yearly Returns, Risk-Adjusted Returns)
22
+ - ๐Ÿ“Š **Performance Profiling** - Built-in performance tracking with aggregated statistics (avg, min, max, stdDev, P95, P99) for bottleneck analysis
22
23
  - ๐Ÿ›‘ **Graceful Shutdown** - Live.background() waits for open positions to close before stopping
23
24
  - ๐Ÿ’‰ **Strategy Dependency Injection** - addStrategy() enables DI pattern for trading strategies
24
25
  - ๐Ÿ” **Schema Reflection API** - listExchanges(), listStrategies(), listFrames() for runtime introspection
25
- - ๐Ÿงช **Comprehensive Test Coverage** - 50+ unit tests covering validation, PNL, callbacks, reports, and event system
26
+ - ๐Ÿงช **Comprehensive Test Coverage** - 61 unit tests covering validation, PNL, callbacks, reports, performance tracking, and event system
26
27
  - ๐Ÿ’พ **Zero Data Download** - Unlike Freqtrade, no need to download gigabytes of historical data - plug any data source (CCXT, database, API)
27
28
  - ๐Ÿ”’ **Safe Math & Robustness** - All metrics protected against NaN/Infinity with unsafe numeric checks, returns N/A for invalid calculations
28
29
 
@@ -710,6 +711,8 @@ Live.background("BTCUSDT", {
710
711
  - `listenSignalBacktestOnce(filter, callback)` - Subscribe to backtest signals once
711
712
  - `listenSignalLive(callback)` - Subscribe to live signals only
712
713
  - `listenSignalLiveOnce(filter, callback)` - Subscribe to live signals once
714
+ - `listenPerformance(callback)` - Subscribe to performance metrics (backtest + live)
715
+ - `listenProgress(callback)` - Subscribe to backtest progress events
713
716
  - `listenError(callback)` - Subscribe to background execution errors
714
717
  - `listenDone(callback)` - Subscribe to background completion events
715
718
  - `listenDoneOnce(filter, callback)` - Subscribe to background completion once
@@ -873,6 +876,31 @@ Live.getReport(strategyName: string): Promise<string>
873
876
  Live.dump(strategyName: string, path?: string): Promise<void>
874
877
  ```
875
878
 
879
+ #### Performance Profiling API
880
+
881
+ ```typescript
882
+ import { Performance, PerformanceStatistics, listenPerformance } from "backtest-kit";
883
+
884
+ // Get raw performance statistics (Controller)
885
+ Performance.getData(strategyName: string): Promise<PerformanceStatistics>
886
+
887
+ // Generate markdown report with bottleneck analysis (View)
888
+ Performance.getReport(strategyName: string): Promise<string>
889
+
890
+ // Save performance report to disk (default: ./logs/performance)
891
+ Performance.dump(strategyName: string, path?: string): Promise<void>
892
+
893
+ // Clear accumulated performance data
894
+ Performance.clear(strategyName?: string): Promise<void>
895
+
896
+ // Listen to real-time performance events
897
+ listenPerformance((event) => {
898
+ console.log(`${event.metricType}: ${event.duration.toFixed(2)}ms`);
899
+ console.log(`Strategy: ${event.strategyName} @ ${event.exchangeName}`);
900
+ console.log(`Symbol: ${event.symbol}, Backtest: ${event.backtest}`);
901
+ });
902
+ ```
903
+
876
904
  ## Type Definitions
877
905
 
878
906
  ### Statistics Types
@@ -910,6 +938,44 @@ interface LiveStatistics {
910
938
  certaintyRatio: number | null; // avgWin / |avgLoss| (higher is better)
911
939
  expectedYearlyReturns: number | null; // Estimated yearly trades (higher is better)
912
940
  }
941
+
942
+ // Performance statistics (exported from "backtest-kit")
943
+ interface PerformanceStatistics {
944
+ strategyName: string; // Strategy name
945
+ totalEvents: number; // Total number of performance events
946
+ totalDuration: number; // Total execution time (ms)
947
+ metricStats: Record<string, { // Statistics by metric type
948
+ metricType: PerformanceMetricType; // backtest_total | backtest_timeframe | backtest_signal | live_tick
949
+ count: number; // Number of samples
950
+ totalDuration: number; // Total duration (ms)
951
+ avgDuration: number; // Average duration (ms)
952
+ minDuration: number; // Minimum duration (ms)
953
+ maxDuration: number; // Maximum duration (ms)
954
+ stdDev: number; // Standard deviation (ms)
955
+ median: number; // Median duration (ms)
956
+ p95: number; // 95th percentile (ms)
957
+ p99: number; // 99th percentile (ms)
958
+ }>;
959
+ events: PerformanceContract[]; // All raw performance events
960
+ }
961
+
962
+ // Performance event (exported from "backtest-kit")
963
+ interface PerformanceContract {
964
+ timestamp: number; // When metric was recorded (epoch ms)
965
+ metricType: PerformanceMetricType; // Type of operation measured
966
+ duration: number; // Operation duration (ms)
967
+ strategyName: string; // Strategy name
968
+ exchangeName: string; // Exchange name
969
+ symbol: string; // Trading symbol
970
+ backtest: boolean; // true = backtest, false = live
971
+ }
972
+
973
+ // Performance metric types (exported from "backtest-kit")
974
+ type PerformanceMetricType =
975
+ | "backtest_total" // Total backtest duration
976
+ | "backtest_timeframe" // Single timeframe processing
977
+ | "backtest_signal" // Signal processing (tick + getNextCandles + backtest)
978
+ | "live_tick"; // Single live tick duration
913
979
  ```
914
980
 
915
981
  ### Signal Data
@@ -1046,6 +1112,72 @@ Backtest.background("BTCUSDT", {
1046
1112
  });
1047
1113
  ```
1048
1114
 
1115
+ ### Performance Profiling
1116
+
1117
+ ```typescript
1118
+ import { Performance, listenPerformance, Backtest } from "backtest-kit";
1119
+
1120
+ // Listen to real-time performance metrics
1121
+ listenPerformance((event) => {
1122
+ console.log(`[${event.metricType}] ${event.duration.toFixed(2)}ms`);
1123
+ console.log(` Strategy: ${event.strategyName}`);
1124
+ console.log(` Symbol: ${event.symbol}, Backtest: ${event.backtest}`);
1125
+ });
1126
+
1127
+ // Run backtest
1128
+ await Backtest.background("BTCUSDT", {
1129
+ strategyName: "my-strategy",
1130
+ exchangeName: "binance",
1131
+ frameName: "1d-backtest"
1132
+ });
1133
+
1134
+ // Get aggregated performance statistics
1135
+ const perfStats = await Performance.getData("my-strategy");
1136
+ console.log("Performance Statistics:");
1137
+ console.log(` Total events: ${perfStats.totalEvents}`);
1138
+ console.log(` Total duration: ${perfStats.totalDuration.toFixed(2)}ms`);
1139
+ console.log(` Metrics tracked: ${Object.keys(perfStats.metricStats).join(", ")}`);
1140
+
1141
+ // Analyze bottlenecks
1142
+ for (const [type, stats] of Object.entries(perfStats.metricStats)) {
1143
+ console.log(`\n${type}:`);
1144
+ console.log(` Count: ${stats.count}`);
1145
+ console.log(` Average: ${stats.avgDuration.toFixed(2)}ms`);
1146
+ console.log(` Min/Max: ${stats.minDuration.toFixed(2)}ms / ${stats.maxDuration.toFixed(2)}ms`);
1147
+ console.log(` P95/P99: ${stats.p95.toFixed(2)}ms / ${stats.p99.toFixed(2)}ms`);
1148
+ console.log(` Std Dev: ${stats.stdDev.toFixed(2)}ms`);
1149
+ }
1150
+
1151
+ // Generate and save performance report
1152
+ const markdown = await Performance.getReport("my-strategy");
1153
+ await Performance.dump("my-strategy"); // Saves to ./logs/performance/my-strategy.md
1154
+ ```
1155
+
1156
+ **Performance Report Example:**
1157
+ ```markdown
1158
+ # Performance Report: my-strategy
1159
+
1160
+ **Total events:** 1440
1161
+ **Total execution time:** 12345.67ms
1162
+ **Number of metric types:** 3
1163
+
1164
+ ## Time Distribution
1165
+
1166
+ - **backtest_timeframe**: 65.4% (8074.32ms total)
1167
+ - **backtest_signal**: 28.3% (3493.85ms total)
1168
+ - **backtest_total**: 6.3% (777.50ms total)
1169
+
1170
+ ## Detailed Metrics
1171
+
1172
+ | Metric Type | Count | Total (ms) | Avg (ms) | Min (ms) | Max (ms) | Std Dev (ms) | Median (ms) | P95 (ms) | P99 (ms) |
1173
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
1174
+ | backtest_timeframe | 1440 | 8074.32 | 5.61 | 2.10 | 12.45 | 1.85 | 5.20 | 8.90 | 10.50 |
1175
+ | backtest_signal | 45 | 3493.85 | 77.64 | 45.20 | 125.80 | 18.32 | 75.10 | 110.20 | 120.15 |
1176
+ | backtest_total | 1 | 777.50 | 777.50 | 777.50 | 777.50 | 0.00 | 777.50 | 777.50 | 777.50 |
1177
+
1178
+ **Note:** All durations are in milliseconds. P95/P99 represent 95th and 99th percentile response times.
1179
+ ```
1180
+
1049
1181
  ### Early Termination
1050
1182
 
1051
1183
  **Using async generator with break:**