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: