sf_quant.backtester.backtest_sequential#

sf_quant.backtester.backtest_sequential(data: DataFrame, constraints: list[Constraint], gamma: float = 2) DataFrame#

Run a sequential backtest of portfolio optimization.

This function iterates through unique dates in the input dataset, constructs the covariance matrix for each date, and solves a mean– variance optimization (MVE) problem subject to user-specified constraints. The optimized portfolios are concatenated into a single Polars DataFrame.

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]

A list of constraints (e.g., FullInvestment, LongOnly) to be enforced in the optimization.

gammafloat, optional

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

Returns#

pl.DataFrame

A PortfolioSchema-validated Polars DataFrame containing optimized portfolio weights for each date, with the following columns:

  • date : datetime, backtest date.

  • barrid : str, asset identifier.

  • weight : float, optimized portfolio weight.

Notes#

  • The optimization is solved using mve_optimizer().

  • The factor model components are constructed using construct_factor_model_components().

  • Results are validated against PortfolioSchema.

See Also#

backtest_parallel : Parallelized version using Ray for faster execution. 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_sequential(
...     data=data,
...     constraints=constraints,
...     gamma=2,
... )
>>> weights.head()
shape: (5, 3)
┌────────────┬─────────┬───────────┐
│ date       ┆ barrid  ┆ weight    │
│ ---        ┆ ---     ┆ ---       │
│ date       ┆ str     ┆ f64       │
╞════════════╪═════════╪═══════════╡
│ 2024-01-02 ┆ USA06Z1 ┆ -0.000639 │
│ 2024-01-03 ┆ USA06Z1 ┆ -0.000644 │
│ 2024-01-04 ┆ USA06Z1 ┆ -0.000641 │
│ 2024-01-05 ┆ USA06Z1 ┆ -0.000666 │
│ 2024-01-08 ┆ USA06Z1 ┆ -0.000666 │
└────────────┴─────────┴───────────┘