backtest-kit 1.1.6 โ 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 +299 -30
- package/build/index.cjs +524 -10
- package/build/index.mjs +523 -11
- package/package.json +2 -2
- package/types.d.ts +346 -9
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Backtest Kit
|
|
1
|
+
# ๐งฟ Backtest Kit
|
|
2
2
|
|
|
3
3
|
> A production-ready TypeScript framework for backtesting and live trading strategies with crash-safe state persistence, signal validation, and memory-optimized architecture.
|
|
4
4
|
|
|
@@ -19,10 +19,13 @@
|
|
|
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** -
|
|
26
|
+
- ๐งช **Comprehensive Test Coverage** - 61 unit tests covering validation, PNL, callbacks, reports, performance tracking, and event system
|
|
27
|
+
- ๐พ **Zero Data Download** - Unlike Freqtrade, no need to download gigabytes of historical data - plug any data source (CCXT, database, API)
|
|
28
|
+
- ๐ **Safe Math & Robustness** - All metrics protected against NaN/Infinity with unsafe numeric checks, returns N/A for invalid calculations
|
|
26
29
|
|
|
27
30
|
## Installation
|
|
28
31
|
|
|
@@ -106,7 +109,7 @@ addStrategy({
|
|
|
106
109
|
interval: "5m", // Throttling: signals generated max once per 5 minutes
|
|
107
110
|
getSignal: async (symbol) => {
|
|
108
111
|
// Your signal generation logic
|
|
109
|
-
// Validation happens automatically (prices, TP/SL logic
|
|
112
|
+
// Validation happens automatically (prices, TP/SL logic)
|
|
110
113
|
return {
|
|
111
114
|
position: "long",
|
|
112
115
|
note: "BTC breakout",
|
|
@@ -114,14 +117,13 @@ addStrategy({
|
|
|
114
117
|
priceTakeProfit: 51000, // Must be > priceOpen for long
|
|
115
118
|
priceStopLoss: 49000, // Must be < priceOpen for long
|
|
116
119
|
minuteEstimatedTime: 60, // Signal duration in minutes
|
|
117
|
-
timestamp: Date.now(),
|
|
118
120
|
};
|
|
119
121
|
},
|
|
120
122
|
callbacks: {
|
|
121
|
-
onOpen: (
|
|
123
|
+
onOpen: (symbol, signal, currentPrice, backtest) => {
|
|
122
124
|
console.log(`[${backtest ? "BT" : "LIVE"}] Signal opened:`, signal.id);
|
|
123
125
|
},
|
|
124
|
-
onClose: (
|
|
126
|
+
onClose: (symbol, signal, priceClose, backtest) => {
|
|
125
127
|
console.log(`[${backtest ? "BT" : "LIVE"}] Signal closed:`, priceClose);
|
|
126
128
|
},
|
|
127
129
|
},
|
|
@@ -348,7 +350,6 @@ All signals are validated automatically before execution:
|
|
|
348
350
|
priceTakeProfit: 51000, // โ
51000 > 50000
|
|
349
351
|
priceStopLoss: 49000, // โ
49000 < 50000
|
|
350
352
|
minuteEstimatedTime: 60, // โ
positive
|
|
351
|
-
timestamp: Date.now(), // โ
positive
|
|
352
353
|
}
|
|
353
354
|
|
|
354
355
|
// โ Invalid long signal - throws error
|
|
@@ -465,7 +466,26 @@ const stopBacktest = Backtest.background("BTCUSDT", {
|
|
|
465
466
|
frameName: "1d-backtest"
|
|
466
467
|
});
|
|
467
468
|
|
|
468
|
-
//
|
|
469
|
+
// Get raw statistical data (Controller)
|
|
470
|
+
const stats = await Backtest.getData("my-strategy");
|
|
471
|
+
console.log(stats);
|
|
472
|
+
// Returns:
|
|
473
|
+
// {
|
|
474
|
+
// signalList: [...], // All closed signals
|
|
475
|
+
// totalSignals: 10,
|
|
476
|
+
// winCount: 7,
|
|
477
|
+
// lossCount: 3,
|
|
478
|
+
// winRate: 70.0, // Percentage (higher is better)
|
|
479
|
+
// avgPnl: 1.23, // Average PNL % (higher is better)
|
|
480
|
+
// totalPnl: 12.30, // Total PNL % (higher is better)
|
|
481
|
+
// stdDev: 2.45, // Standard deviation (lower is better)
|
|
482
|
+
// sharpeRatio: 0.50, // Risk-adjusted return (higher is better)
|
|
483
|
+
// annualizedSharpeRatio: 9.55, // Sharpe ร โ365 (higher is better)
|
|
484
|
+
// certaintyRatio: 1.75, // avgWin / |avgLoss| (higher is better)
|
|
485
|
+
// expectedYearlyReturns: 156 // Estimated yearly trades (higher is better)
|
|
486
|
+
// }
|
|
487
|
+
|
|
488
|
+
// Generate markdown report (View)
|
|
469
489
|
const markdown = await Backtest.getReport("my-strategy");
|
|
470
490
|
console.log(markdown);
|
|
471
491
|
|
|
@@ -476,29 +496,75 @@ await Backtest.dump("my-strategy");
|
|
|
476
496
|
await Backtest.dump("my-strategy", "./custom/path");
|
|
477
497
|
```
|
|
478
498
|
|
|
479
|
-
**
|
|
480
|
-
-
|
|
499
|
+
**getData() returns BacktestStatistics:**
|
|
500
|
+
- `signalList` - Array of all closed signals
|
|
501
|
+
- `totalSignals` - Total number of closed signals
|
|
502
|
+
- `winCount` / `lossCount` - Number of winning/losing trades
|
|
503
|
+
- `winRate` - Win percentage (higher is better)
|
|
504
|
+
- `avgPnl` - Average PNL percentage (higher is better)
|
|
505
|
+
- `totalPnl` - Total PNL percentage (higher is better)
|
|
506
|
+
- `stdDev` - Standard deviation / volatility (lower is better)
|
|
507
|
+
- `sharpeRatio` - Risk-adjusted return (higher is better)
|
|
508
|
+
- `annualizedSharpeRatio` - Sharpe Ratio ร โ365 (higher is better)
|
|
509
|
+
- `certaintyRatio` - avgWin / |avgLoss| (higher is better)
|
|
510
|
+
- `expectedYearlyReturns` - Estimated number of trades per year (higher is better)
|
|
511
|
+
|
|
512
|
+
**getReport() includes:**
|
|
513
|
+
- All metrics from getData() formatted as markdown
|
|
481
514
|
- All signal details (prices, TP/SL, PNL, duration, close reason)
|
|
482
515
|
- Timestamps for each signal
|
|
516
|
+
- "Higher is better" / "Lower is better" annotations
|
|
483
517
|
|
|
484
518
|
### Live Trading Reports
|
|
485
519
|
|
|
486
520
|
```typescript
|
|
487
521
|
import { Live } from "backtest-kit";
|
|
488
522
|
|
|
489
|
-
//
|
|
523
|
+
// Get raw statistical data (Controller)
|
|
524
|
+
const stats = await Live.getData("my-strategy");
|
|
525
|
+
console.log(stats);
|
|
526
|
+
// Returns:
|
|
527
|
+
// {
|
|
528
|
+
// eventList: [...], // All events (idle, opened, active, closed)
|
|
529
|
+
// totalEvents: 15,
|
|
530
|
+
// totalClosed: 5,
|
|
531
|
+
// winCount: 3,
|
|
532
|
+
// lossCount: 2,
|
|
533
|
+
// winRate: 60.0, // Percentage (higher is better)
|
|
534
|
+
// avgPnl: 1.23, // Average PNL % (higher is better)
|
|
535
|
+
// totalPnl: 6.15, // Total PNL % (higher is better)
|
|
536
|
+
// stdDev: 1.85, // Standard deviation (lower is better)
|
|
537
|
+
// sharpeRatio: 0.66, // Risk-adjusted return (higher is better)
|
|
538
|
+
// annualizedSharpeRatio: 12.61,// Sharpe ร โ365 (higher is better)
|
|
539
|
+
// certaintyRatio: 2.10, // avgWin / |avgLoss| (higher is better)
|
|
540
|
+
// expectedYearlyReturns: 365 // Estimated yearly trades (higher is better)
|
|
541
|
+
// }
|
|
542
|
+
|
|
543
|
+
// Generate markdown report (View)
|
|
490
544
|
const markdown = await Live.getReport("my-strategy");
|
|
491
545
|
|
|
492
546
|
// Save to disk (default: ./logs/live/my-strategy.md)
|
|
493
547
|
await Live.dump("my-strategy");
|
|
494
548
|
```
|
|
495
549
|
|
|
496
|
-
**
|
|
497
|
-
-
|
|
498
|
-
-
|
|
499
|
-
-
|
|
500
|
-
-
|
|
550
|
+
**getData() returns LiveStatistics:**
|
|
551
|
+
- `eventList` - Array of all events (idle, opened, active, closed)
|
|
552
|
+
- `totalEvents` - Total number of events
|
|
553
|
+
- `totalClosed` - Total number of closed signals
|
|
554
|
+
- `winCount` / `lossCount` - Number of winning/losing trades
|
|
555
|
+
- `winRate` - Win percentage (higher is better)
|
|
556
|
+
- `avgPnl` - Average PNL percentage (higher is better)
|
|
557
|
+
- `totalPnl` - Total PNL percentage (higher is better)
|
|
558
|
+
- `stdDev` - Standard deviation / volatility (lower is better)
|
|
559
|
+
- `sharpeRatio` - Risk-adjusted return (higher is better)
|
|
560
|
+
- `annualizedSharpeRatio` - Sharpe Ratio ร โ365 (higher is better)
|
|
561
|
+
- `certaintyRatio` - avgWin / |avgLoss| (higher is better)
|
|
562
|
+
- `expectedYearlyReturns` - Estimated number of trades per year (higher is better)
|
|
563
|
+
|
|
564
|
+
**getReport() includes:**
|
|
565
|
+
- All metrics from getData() formatted as markdown
|
|
501
566
|
- Signal-by-signal details with current state
|
|
567
|
+
- "Higher is better" / "Lower is better" annotations
|
|
502
568
|
|
|
503
569
|
**Report example:**
|
|
504
570
|
```markdown
|
|
@@ -506,8 +572,14 @@ await Live.dump("my-strategy");
|
|
|
506
572
|
|
|
507
573
|
Total events: 15
|
|
508
574
|
Closed signals: 5
|
|
509
|
-
Win rate: 60.00% (3W / 2L)
|
|
510
|
-
Average PNL: +1.23%
|
|
575
|
+
Win rate: 60.00% (3W / 2L) (higher is better)
|
|
576
|
+
Average PNL: +1.23% (higher is better)
|
|
577
|
+
Total PNL: +6.15% (higher is better)
|
|
578
|
+
Standard Deviation: 1.85% (lower is better)
|
|
579
|
+
Sharpe Ratio: 0.66 (higher is better)
|
|
580
|
+
Annualized Sharpe Ratio: 12.61 (higher is better)
|
|
581
|
+
Certainty Ratio: 2.10 (higher is better)
|
|
582
|
+
Expected Yearly Returns: 365 trades (higher is better)
|
|
511
583
|
|
|
512
584
|
| Timestamp | Action | Symbol | Signal ID | Position | ... | PNL (net) | Close Reason |
|
|
513
585
|
|-----------|--------|--------|-----------|----------|-----|-----------|--------------|
|
|
@@ -639,6 +711,8 @@ Live.background("BTCUSDT", {
|
|
|
639
711
|
- `listenSignalBacktestOnce(filter, callback)` - Subscribe to backtest signals once
|
|
640
712
|
- `listenSignalLive(callback)` - Subscribe to live signals only
|
|
641
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
|
|
642
716
|
- `listenError(callback)` - Subscribe to background execution errors
|
|
643
717
|
- `listenDone(callback)` - Subscribe to background completion events
|
|
644
718
|
- `listenDoneOnce(filter, callback)` - Subscribe to background completion once
|
|
@@ -744,7 +818,7 @@ const quantity = await formatQuantity("BTCUSDT", 0.123456789);
|
|
|
744
818
|
#### Backtest API
|
|
745
819
|
|
|
746
820
|
```typescript
|
|
747
|
-
import { Backtest } from "backtest-kit";
|
|
821
|
+
import { Backtest, BacktestStatistics } from "backtest-kit";
|
|
748
822
|
|
|
749
823
|
// Stream backtest results
|
|
750
824
|
Backtest.run(
|
|
@@ -762,7 +836,10 @@ Backtest.background(
|
|
|
762
836
|
context: { strategyName, exchangeName, frameName }
|
|
763
837
|
): Promise<() => void> // Returns cancellation function
|
|
764
838
|
|
|
765
|
-
//
|
|
839
|
+
// Get raw statistical data (Controller)
|
|
840
|
+
Backtest.getData(strategyName: string): Promise<BacktestStatistics>
|
|
841
|
+
|
|
842
|
+
// Generate markdown report (View)
|
|
766
843
|
Backtest.getReport(strategyName: string): Promise<string>
|
|
767
844
|
|
|
768
845
|
// Save report to disk
|
|
@@ -772,7 +849,7 @@ Backtest.dump(strategyName: string, path?: string): Promise<void>
|
|
|
772
849
|
#### Live Trading API
|
|
773
850
|
|
|
774
851
|
```typescript
|
|
775
|
-
import { Live } from "backtest-kit";
|
|
852
|
+
import { Live, LiveStatistics } from "backtest-kit";
|
|
776
853
|
|
|
777
854
|
// Stream live results (infinite)
|
|
778
855
|
Live.run(
|
|
@@ -789,27 +866,133 @@ Live.background(
|
|
|
789
866
|
context: { strategyName, exchangeName }
|
|
790
867
|
): Promise<() => void> // Returns cancellation function
|
|
791
868
|
|
|
792
|
-
//
|
|
869
|
+
// Get raw statistical data (Controller)
|
|
870
|
+
Live.getData(strategyName: string): Promise<LiveStatistics>
|
|
871
|
+
|
|
872
|
+
// Generate markdown report (View)
|
|
793
873
|
Live.getReport(strategyName: string): Promise<string>
|
|
794
874
|
|
|
795
875
|
// Save report to disk
|
|
796
876
|
Live.dump(strategyName: string, path?: string): Promise<void>
|
|
797
877
|
```
|
|
798
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
|
+
|
|
799
904
|
## Type Definitions
|
|
800
905
|
|
|
906
|
+
### Statistics Types
|
|
907
|
+
|
|
908
|
+
```typescript
|
|
909
|
+
// Backtest statistics (exported from "backtest-kit")
|
|
910
|
+
interface BacktestStatistics {
|
|
911
|
+
signalList: IStrategyTickResultClosed[]; // All closed signals
|
|
912
|
+
totalSignals: number;
|
|
913
|
+
winCount: number;
|
|
914
|
+
lossCount: number;
|
|
915
|
+
winRate: number | null; // Win percentage (higher is better)
|
|
916
|
+
avgPnl: number | null; // Average PNL % (higher is better)
|
|
917
|
+
totalPnl: number | null; // Total PNL % (higher is better)
|
|
918
|
+
stdDev: number | null; // Standard deviation (lower is better)
|
|
919
|
+
sharpeRatio: number | null; // Risk-adjusted return (higher is better)
|
|
920
|
+
annualizedSharpeRatio: number | null; // Sharpe ร โ365 (higher is better)
|
|
921
|
+
certaintyRatio: number | null; // avgWin / |avgLoss| (higher is better)
|
|
922
|
+
expectedYearlyReturns: number | null; // Estimated yearly trades (higher is better)
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
// Live statistics (exported from "backtest-kit")
|
|
926
|
+
interface LiveStatistics {
|
|
927
|
+
eventList: TickEvent[]; // All events (idle, opened, active, closed)
|
|
928
|
+
totalEvents: number;
|
|
929
|
+
totalClosed: number;
|
|
930
|
+
winCount: number;
|
|
931
|
+
lossCount: number;
|
|
932
|
+
winRate: number | null; // Win percentage (higher is better)
|
|
933
|
+
avgPnl: number | null; // Average PNL % (higher is better)
|
|
934
|
+
totalPnl: number | null; // Total PNL % (higher is better)
|
|
935
|
+
stdDev: number | null; // Standard deviation (lower is better)
|
|
936
|
+
sharpeRatio: number | null; // Risk-adjusted return (higher is better)
|
|
937
|
+
annualizedSharpeRatio: number | null; // Sharpe ร โ365 (higher is better)
|
|
938
|
+
certaintyRatio: number | null; // avgWin / |avgLoss| (higher is better)
|
|
939
|
+
expectedYearlyReturns: number | null; // Estimated yearly trades (higher is better)
|
|
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
|
|
979
|
+
```
|
|
980
|
+
|
|
801
981
|
### Signal Data
|
|
802
982
|
|
|
803
983
|
```typescript
|
|
804
984
|
interface ISignalRow {
|
|
805
|
-
id: string;
|
|
985
|
+
id: string; // UUID v4 auto-generated
|
|
806
986
|
position: "long" | "short";
|
|
807
|
-
note
|
|
987
|
+
note?: string;
|
|
808
988
|
priceOpen: number;
|
|
809
989
|
priceTakeProfit: number;
|
|
810
990
|
priceStopLoss: number;
|
|
811
991
|
minuteEstimatedTime: number;
|
|
812
|
-
|
|
992
|
+
exchangeName: string;
|
|
993
|
+
strategyName: string;
|
|
994
|
+
timestamp: number; // Signal creation timestamp
|
|
995
|
+
symbol: string; // Trading pair (e.g., "BTCUSDT")
|
|
813
996
|
}
|
|
814
997
|
```
|
|
815
998
|
|
|
@@ -817,9 +1000,27 @@ interface ISignalRow {
|
|
|
817
1000
|
|
|
818
1001
|
```typescript
|
|
819
1002
|
type IStrategyTickResult =
|
|
820
|
-
| {
|
|
821
|
-
|
|
822
|
-
|
|
1003
|
+
| {
|
|
1004
|
+
action: "idle";
|
|
1005
|
+
signal: null;
|
|
1006
|
+
strategyName: string;
|
|
1007
|
+
exchangeName: string;
|
|
1008
|
+
currentPrice: number;
|
|
1009
|
+
}
|
|
1010
|
+
| {
|
|
1011
|
+
action: "opened";
|
|
1012
|
+
signal: ISignalRow;
|
|
1013
|
+
strategyName: string;
|
|
1014
|
+
exchangeName: string;
|
|
1015
|
+
currentPrice: number;
|
|
1016
|
+
}
|
|
1017
|
+
| {
|
|
1018
|
+
action: "active";
|
|
1019
|
+
signal: ISignalRow;
|
|
1020
|
+
currentPrice: number;
|
|
1021
|
+
strategyName: string;
|
|
1022
|
+
exchangeName: string;
|
|
1023
|
+
}
|
|
823
1024
|
| {
|
|
824
1025
|
action: "closed";
|
|
825
1026
|
signal: ISignalRow;
|
|
@@ -827,10 +1028,12 @@ type IStrategyTickResult =
|
|
|
827
1028
|
closeReason: "take_profit" | "stop_loss" | "time_expired";
|
|
828
1029
|
closeTimestamp: number;
|
|
829
1030
|
pnl: {
|
|
830
|
-
priceOpenWithCosts: number;
|
|
831
|
-
priceCloseWithCosts: number;
|
|
832
1031
|
pnlPercentage: number;
|
|
1032
|
+
priceOpen: number; // Entry price adjusted with slippage and fees
|
|
1033
|
+
priceClose: number; // Exit price adjusted with slippage and fees
|
|
833
1034
|
};
|
|
1035
|
+
strategyName: string;
|
|
1036
|
+
exchangeName: string;
|
|
834
1037
|
};
|
|
835
1038
|
```
|
|
836
1039
|
|
|
@@ -909,6 +1112,72 @@ Backtest.background("BTCUSDT", {
|
|
|
909
1112
|
});
|
|
910
1113
|
```
|
|
911
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
|
+
|
|
912
1181
|
### Early Termination
|
|
913
1182
|
|
|
914
1183
|
**Using async generator with break:**
|