Portfolio modules

Backtest engine

Backtesting utilities for Mapper-based portfolio strategies.

This module contains the functions used to simulate long-only portfolio strategies, compute performance metrics and compare Mapper-based portfolios against an equal-weight benchmark.

tda_finance.portfolio.backtest_engine.backtest_equal_weight_rebalanced(prices: DataFrame, lookback_days: int, rebalance_days: int, tc_bps: float = 0.0) DataFrame[source]

Run an equal-weight rebalanced benchmark.

The strategy starts after the same lookback period used by the Mapper strategy. At each rebalance date, all available assets in the current window receive the same weight and the portfolio is held until the next rebalance.

Transaction costs are applied proportionally to turnover, using the same convention as in backtest_tda.

Parameters:
  • prices (pandas.DataFrame) – Price matrix indexed by date, with one column per asset.

  • lookback_days (int) – Number of past observations used to define the available asset universe.

  • rebalance_days (int) – Number of observations between consecutive rebalances.

  • tc_bps (float, default=0.0) – Transaction cost in basis points, applied proportionally to turnover.

Returns:

DataFrame indexed by date with portfolio returns, NAV and turnover.

Return type:

pandas.DataFrame

tda_finance.portfolio.backtest_engine.backtest_tda(prices: DataFrame, lookback_days: int, rebalance_days: int, params: MapperParams, tc_bps: float = 0.0, use_ph_control: bool = False, ph_params: PHParams | None = None, ph_history_len: int = 12, ph_diagnostics_csv: str | None = None, weight_method: Literal['node_overlap', 'macro_cluster'] = 'node_overlap', regime_action: Literal['cash', 'equal_weight'] = 'cash', verbose: bool = False) DataFrame[source]

Run a causal long-only backtest using Mapper-based weights.

At each rebalance date, the function builds a Mapper representation from a historical price window, converts the resulting clusters into portfolio weights and applies those weights until the next rebalance. Optionally, a persistent-homology signal can be used as a regime-control mechanism.

Parameters:
  • prices (pandas.DataFrame) – Price matrix indexed by date, with one column per asset.

  • lookback_days (int) – Number of past observations used to build each Mapper graph.

  • rebalance_days (int) – Number of observations between consecutive rebalances.

  • params (MapperParams) – Parameters used in the Mapper construction.

  • tc_bps (float, default=0.0) – Transaction cost in basis points, applied proportionally to turnover.

  • use_ph_control (bool, default=False) – Whether to activate the persistent-homology regime control.

  • ph_params (PHParams, optional) – Parameters used to compute persistence diagrams. If None, default parameters are used.

  • ph_history_len (int, default=12) – Number of past PH scores used by the anomaly detector.

  • ph_diagnostics_csv (str, optional) – Path where PH diagnostics are saved. If None, diagnostics are not written to disk.

  • weight_method ({"node_overlap", "macro_cluster"}, default="node_overlap") – Rule used to transform Mapper clusters into portfolio weights.

  • regime_action ({"cash", "equal_weight"}, default="cash") – Action applied when PH control detects an anomalous regime.

  • verbose (bool, default=False) – Whether to print rebalance diagnostics.

Returns:

DataFrame indexed by date with portfolio returns, NAV and turnover. If PH control is active, additional diagnostic columns are included.

Return type:

pandas.DataFrame

Raises:

ValueError – If the input time series is too short or if regime_action is not supported.

Notes

The backtest is causal: weights computed at a rebalance date are applied only to subsequent observations.

tda_finance.portfolio.backtest_engine.perf_summary(port_ret: Series, periods_per_year: int = 12, rf: float | Series | None = 0.0, mar: float = 0.0) Dict[str, float][source]

Compute performance and risk metrics for a return series.

Parameters:
  • port_ret (pandas.Series) – Portfolio returns in decimal form.

  • periods_per_year (int, default=12) – Number of return observations per year.

  • rf (float or pandas.Series, optional) – Risk-free rate in the same frequency as port_ret. If a scalar is provided, it is treated as constant.

  • mar (float, default=0.0) – Minimum acceptable return used in the Sortino ratio, in the same frequency as port_ret.

Returns:

Dictionary containing total return, annualized return, annualized volatility, Sharpe ratio, Sortino ratio and maximum drawdown.

Return type:

dict of str to float