sf_quant.backtester.backtest_parallel#

sf_quant.backtester.backtest_parallel(data: DataFrame, constraints: list[Constraint], gamma: float = 2, n_cpus: int | None = None) DataFrame#

Run a parallelized backtest of portfolio optimization using Ray.

This function distributes portfolio construction tasks across multiple CPUs with Ray, solving mean–variance optimization problems for each date in parallel. A Ray-based progress bar tracks computation progress.

Parameters#

datapl.DataFrame

Input dataset containing at least the following columns:

  • date : datetime-like, the date of each observation.

  • barrid : str, unique identifier for each asset.

  • alpha : float, expected return (alpha) for each asset.

  • predicted_beta : float, optional, factor exposures for constraints.

constraintslist[Constraint]

List of portfolio constraints to enforce during optimization.

gammafloat, optional

Risk aversion parameter. Higher values penalize portfolio variance more strongly. Default is 2.

n_cpusint, optional

Number of CPUs to allocate to Ray. If None, defaults to os.cpu_count() but is capped at the number of unique dates.

Returns#

pl.DataFrame

A PortfolioSchema-validated Polars DataFrame containing optimized portfolio weights across all backtest dates, with columns:

  • date : datetime, portfolio date.

  • barrid : str, asset identifier.

  • weight : float, optimized portfolio weight.

Notes#

  • Ray is initialized with ignore_reinit_error=True, allowing safe re-invocation within the same process.

  • The number of CPUs is automatically limited to the number of unique dates to avoid unnecessary idle workers.

See Also#

backtest_sequential : Sequential version for smaller datasets or debugging. dynamic_backtest_parallel : Parallel version with active risk calibration.

Examples#

>>> import sf_quant.data as sfd
>>> import sf_quant.backtester as sfb
>>> import sf_quant.optimizer as sfo
>>> import polars as pl
>>> import datetime as dt
>>> start = dt.date(2024, 1, 1)
>>> end = dt.date(2024, 1, 10)
>>> columns = [
...     'date',
...     'barrid',
... ]
>>> data = (
...     sfd.load_assets(
...         start=start,
...         end=end,
...         in_universe=True,
...         columns=columns
...     )
...     .with_columns(
...         pl.lit(0).alias('alpha')
...     )
... )
>>> constraints = [
...     sfo.FullInvestment()
... ]
>>> weights = sfb.backtest_parallel(
...     data=data,
...     constraints=constraints,
...     gamma=2,
... )
shape: (5, 3)
┌────────────┬─────────┬───────────┐
│ date       ┆ barrid  ┆ weight    │
│ ---        ┆ ---     ┆ ---       │
│ date       ┆ str     ┆ f64       │
╞════════════╪═════════╪═══════════╡
│ 2024-01-02 ┆ USA06Z1 ┆ -0.000639 │
│ 2024-01-02 ┆ USA0771 ┆ -0.000083 │
│ 2024-01-02 ┆ USA0C11 ┆ -0.003044 │
│ 2024-01-02 ┆ USA0SY1 ┆ -0.002177 │
│ 2024-01-02 ┆ USA11I1 ┆ 0.001475  │
└────────────┴─────────┴───────────┘