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_riskis 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 toos.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_riskis specified, viaload_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 │ └────────────┴─────────┴───────────┴───────┴──────────────┘