sf_quant.backtester.dynamic_backtest_parallel#

sf_quant.backtester.dynamic_backtest_parallel(data: DataFrame, constraints: list[Constraint], initial_gamma: float = 100, target_active_risk: float = 0.05, n_cpus: int | None = None, active_weights: bool = False) DataFrame#

Run a parallelized backtest of portfolio optimization using Ray.

This function distributes portfolio construction tasks across multiple CPUs using 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.

initial_gammafloat, optional

Risk aversion parameter. Higher values penalize portfolio variance more strongly. Used as the starting gamma for calibration if target_active_risk is specified. Default is 100.

target_active_riskfloat, optional

If specified, automatically calibrate gamma for each date to achieve this target annualized active risk (e.g., 0.05 for 5%). Requires benchmark data to be available via load_benchmark().

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.

active_weightsbool

Flag indicating how to treat output weights of optimizer. False (default) means that we subtract of benchmark weights before computing active risk.

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.

  • gamma : float, calibrated risk aversion parameter for each date.

  • active_risk : float, achieved annualized active risk for each date.

Notes#

  • Benchmark data is automatically loaded when target_active_risk is specified, via load_benchmark().

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

See Also#

backtest_parallel : Sequential version without active risk calibration. dynamic_mve_optimizer : Underlying optimizer 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)
>>> benchmark_weights = sfd.load_benchmark(start, end).rename({'weight': 'benchmark_weight'})
>>> data = (
...     sfd.load_assets(
...         start=start,
...         end=end,
...         in_universe=True,
...         columns=['date', 'barrid']
...     )
...     .with_columns(
...         pl.lit(0).alias('alpha')
...     )
...     .join(
...         other=benchmark_weights,
...         on=['date', 'barrid'],
...         how='left'
...     )
... )
>>> constraints = [sfo.FullInvestment(), sfo.LongOnly()]
>>> weights = sfb.dynamic_backtest_parallel(
...     data=data,
...     constraints=constraints,
...     initial_gamma=100,
...     target_active_risk=0.05,
...     active_weights=False
... )
shape: (5, 5)
┌────────────┬─────────┬───────────┬───────┬──────────────┐
│ date       ┆ barrid  ┆ weight    ┆ gamma ┆ active_risk  │
│ ---        ┆ ---     ┆ ---       ┆ ---   ┆ ---          │
│ date       ┆ str     ┆ f64       ┆ f64   ┆ f64          │
╞════════════╪═════════╪═══════════╪═══════╪══════════════╡
│ 2024-01-02 ┆ USA06Z1 ┆ -0.000639 ┆ 100.0 ┆ 0.047        │
│ 2024-01-02 ┆ USA0771 ┆ -0.000083 ┆ 100.0 ┆ 0.047        │
│ 2024-01-02 ┆ USA0C11 ┆ -0.003044 ┆ 100.0 ┆ 0.047        │
│ 2024-01-02 ┆ USA0SY1 ┆ -0.002177 ┆ 100.0 ┆ 0.047        │
│ 2024-01-02 ┆ USA11I1 ┆ 0.001475  ┆ 100.0 ┆ 0.047        │
└────────────┴─────────┴───────────┴───────┴──────────────┘