Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/theonetrade/backtest-kit/llms.txt

Use this file to discover all available pages before exploring further.

The Performance class exposes aggregated execution timing statistics collected automatically during every Backtest and Live run. No setup is required: the engine emits PerformanceContract events via performanceEmitter after each measured operation, and PerformanceMarkdownService accumulates them into per-strategy buckets. Call Performance.getData, Performance.getReport, or Performance.dump at any point during or after a run to inspect timing data. Performance tracking is built-in and enabled by default. It is useful for identifying bottlenecks—slow getSignal implementations, expensive candle fetches, or oversized signal windows—before running large production backtests.

Metric Types

Four metric types are tracked automatically:
MetricDescription
backtest_totalTotal wall-clock duration of a complete backtest run from start to finish.
backtest_timeframeDuration to process a single timeframe iteration (one tick in the frame).
backtest_signalDuration to process one signal: tick() + getNextCandles() + backtest(). This is the hot-path metric.
live_tickDuration of a single live trading tick iteration: tick() + event emission + sleep calculation.

getData()

Performance.getData(
  symbol: string,
  context: { strategyName: string; exchangeName: string; frameName: string },
  backtest?: boolean
): Promise<PerformanceStatisticsModel>
Returns aggregated performance statistics for the specified context.

PerformanceStatisticsModel

strategyName
string
Strategy name associated with these metrics.
totalEvents
number
Total number of performance events recorded across all metric types.
totalDuration
number
Sum of all execution durations in milliseconds.
metricStats
Record<string, MetricStats>
Statistics grouped by metric type. Each key is one of the four PerformanceMetricType values.
events
PerformanceContract[]
All raw performance events in the order they were emitted.

MetricStats Fields

Each entry in metricStats is a MetricStats object:
metricType
PerformanceMetricType
The metric this entry describes.
count
number
Number of recorded samples for this metric.
avgDuration
number
Mean execution duration in milliseconds.
minDuration
number
Fastest recorded sample in milliseconds.
maxDuration
number
Slowest recorded sample in milliseconds.
stdDev
number
Standard deviation of durations. High stdDev indicates inconsistent execution time.
median
number
50th percentile duration in milliseconds.
p95
number
95th percentile duration. Useful for identifying occasional slow outliers.
p99
number
99th percentile duration. Identifies extreme tail latencies.
totalDuration
number
Total execution time across all samples for this metric.
avgWaitTime
number
Average time between consecutive events of this type.
minWaitTime
number
Minimum gap between consecutive events.
maxWaitTime
number
Maximum gap between consecutive events.

getReport()

Performance.getReport(
  symbol: string,
  context: { strategyName: string; exchangeName: string; frameName: string },
  backtest?: boolean
): Promise<string>
Returns a Markdown-formatted report with:
  • A summary table listing count, avg, min, max, stdDev, median, P95, and P99 for each metric type.
  • A bottleneck section highlighting which operations consumed the most total time.
  • Time-distribution percentages so you can see which phase dominates.

dump()

Performance.dump(
  symbol: string,
  context: { strategyName: string; exchangeName: string; frameName: string },
  backtest?: boolean,
  path?: string
): Promise<void>
Generates the Markdown report and writes it to disk. Default output path: ./dump/performance/.

Example

import { Backtest, Performance, listenDoneBacktest } from 'backtest-kit';

Backtest.background('BTCUSDT', {
  strategyName: 'rsi-trend',
  exchangeName: 'binance',
  frameName:    '2025-dec-01',
});

listenDoneBacktest(async (event) => {
  const stats = await Performance.getData(event.symbol, {
    strategyName: event.strategyName,
    exchangeName: event.exchangeName,
    frameName:    event.frameName,
  });

  // Print the slowest metric
  const sorted = Object.values(stats.metricStats)
    .sort((a, b) => b.avgDuration - a.avgDuration);

  if (sorted.length > 0) {
    const slowest = sorted[0];
    console.log(
      `Bottleneck: ${slowest.metricType} ` +
      `avg=${slowest.avgDuration.toFixed(1)}ms ` +
      `P95=${slowest.p95.toFixed(1)}ms ` +
      `P99=${slowest.p99.toFixed(1)}ms`
    );
  }

  // Save full report
  await Performance.dump(event.symbol, {
    strategyName: event.strategyName,
    exchangeName: event.exchangeName,
    frameName:    event.frameName,
  });
});

Listening to Raw Events

Subscribe to performanceEmitter directly for real-time metric streaming:
import { listenPerformance } from 'backtest-kit';

listenPerformance((event) => {
  if (event.duration > 500) {
    console.warn(
      `Slow operation: ${event.metricType} took ${event.duration.toFixed(0)}ms ` +
      `(${event.strategyName} @ ${new Date(event.timestamp).toISOString()})`
    );
  }
});
Performance tracking is completely passive—it adds no overhead to your strategy logic and requires no registration or opt-in. The PerformanceMarkdownService is subscribed to performanceEmitter automatically when the framework initializes. The maximum number of stored events is controlled by CC_MAX_PERFORMANCE_MARKDOWN_ROWS (default: 10,000), which is higher than other report limits to enable accurate statistical analysis over large backtests.

Build docs developers (and LLMs) love