跳轉到

Formal batch backtest screening

finlab.backtest.sim_batch screens a mapping of compatible positions with the same formal Cython engine used by sim. It does not implement Fast Mode, sampling, or approximate returns.

Fast Mode has been removed from both public APIs. Existing sim(..., fast_mode=True) and sim_batch(..., fast_mode=True) calls now raise TypeError; use the formal metrics_only=True paths for lightweight screening.

from finlab.backtest import sim_batch

items = sim_batch(
    {"variation-001": position_a, "variation-002": position_b},
    resample="M",
    metrics_only=True,
    progress_callback=on_progress,
)

One call is one compatibility group. Market, resample/resample offset, trade price, fees/tax, position limit, and all stop behavior are common keyword arguments. Callers must split configurations that differ. The batch shares the package/data freshness check, market dataset prefetch, prepared market price inputs, authentication, and (for identical position indexes) the formal rebalance calendar. Each engine run is isolated and produces a versioned, JSON-safe item. An invalid or failing position does not cancel its siblings.

Metrics for every completed item use the same common evaluation window: the overlap of the strategies' input periods on the shared market-price index. Leading inactive dates are retained as flat returns so entry timing does not silently shorten one strategy's sample. The exact start and end are exposed as comparison_period_start and comparison_period_end in item provenance. A batch whose valid positions have no overlapping price period raises ValueError; split those positions into separate comparison groups.

sim(position, metrics_only=True, upload=False) is the corresponding formal individual path. It skips Report construction, but runs the same validation, market preparation, schedule, authorization, and backtest engine. It returns the same Metrics type exposed by report.metrics:

metrics = sim(position, metrics_only=True, upload=False)
print(metrics.annual_return(), metrics.sharpe_ratio(), metrics.max_drawdown())

The batch JSON boundary serializes that same formal subset as cagr, sharpe_ratio, and max_drawdown.

Real native-kernel verification

scripts/benchmark_sim_batch.py runs a counterbalanced A/B with 20 real positions, real FinLab data, and the native Cython engine. On 2026-07-12 it reported:

Path 20-strategy elapsed (two orders) freshness checks/run market prefetch/run peak Python memory
Individual sim(metrics_only=True) 13.952 s, 11.096 s 20 20 42.40 MB, 1.19 MB
sim_batch(metrics_only=True) 11.888 s, 11.844 s 1 1 1.15 MB, 1.22 MB

All 60 metric comparisons (CAGR, Sharpe ratio, and maximum drawdown for 20 strategies) had absolute error 0.0; both paths constructed zero full Reports. A separate real full-Report comparison had maximum absolute error 4.440892098500626e-16, the expected floating-point rounding tolerance.

Run the benchmark only in an authenticated native environment:

python scripts/benchmark_sim_batch.py