Back-test result

This module defines BacktestResult.

This is the object that is returned by the cvxportfolio.MarketSimulator.backtest() method, and also by the same method in derived classes of cvxportfolio.MarketSimulator. It contains all relevant information from a back-test and implements the logic to compute various performance metrics, in addition to the BacktestResult.plot() method for producing plots and __repr__ magic method, which is invoked when the user prints an instance.

Added in version 1.1.0: The BacktestResult.log property, which returns the logs produced during the back-test, at level INFO or higher. It works also for back-tests run in parallel!

class cvxportfolio.result.BacktestResult(universe, trading_calendar, costs)View on GitHub

Store the data from a back-test and produce metrics and plots.

Additionally, record all logs produced by the simulator, market data server, and policy object during the back-test. These are stored in the logs attribute as a newline separated string. This is done in a multi-process safe manner, so that if you run parallel back-tests with cvxportfolio.MarketSimulator.backtest_many(), only the logs from the right process are recorded.

Parameters:
  • universe (pandas.Index) – Best initial guess of the trading universe.

  • trading_calendar (pd.DateTimeIndex) – Trading calendar. Can be a best guess, but the first timestamp must be the actual.

  • costs (list) – Simulator cost objects whose value is logged. Note: we use only the classes’ names here.

Note

The initializer of this class is still experimental, we might still change its signature without the guarantee of semantic versioning.

plot(show=True, how_many_weights=7)View on GitHub

Make plot and show it.

Parameters:
  • show (bool) – if True, call matplotlib.Figure.show, helpful when running in the interpreter.

  • how_many_weights (int) – How many assets’ weights are shown in the weights plots. The ones with largest average absolute value are chosen.

Returns:

Resulting matplotlib figure.

Return type:

matplotlib.figure.Figure

times_plot(show=True)View on GitHub

Plot all execution times of the back-test.

Parameters:

show (bool) – if True, call matplotlib.Figure.show, helpful when running in the interpreter.

Returns:

Resulting matplotlib figure.

Return type:

matplotlib.figure.Figure

property logsView on GitHub

Logs from the policy, simulator, market data server, ….

Returns:

Logs produced during the back-test, newline separated.

Return type:

str

property cash_keyView on GitHub

The name of the cash unit used (e.g., USDOLLAR).

Returns:

Name of the cash accounting unit.

Return type:

str

property periods_per_yearView on GitHub

Average trading periods per year in this backtest (rounded).

Returns:

Average periods per year.

Return type:

int

property vView on GitHub

The total value (or NAV) of the portfolio at each period.

Returns:

Total value at each period.

Return type:

pandas.Series

property profitView on GitHub

The total profit (PnL) in this backtest.

Returns:

Total profit.

Return type:

float

property wView on GitHub

The weights of the portfolio at each period.

Returns:

Portfolio weights at each period.

Return type:

pandas.DataFrame

property h_plusView on GitHub

The post-trade portfolio (holdings) at each period.

Returns:

Post-trade holdings at each period.

Return type:

pandas.DataFrame

property w_plusView on GitHub

The post-trade weights of the portfolio at each period.

Returns:

Post-trade weights at each period.

Return type:

pandas.DataFrame

property leverageView on GitHub

Leverage of the portfolio at each period.

This is defined as:

\[\| {(h_t)}_{1:n} \|_1 / v_t,\]

where \(h_t\) is the portfolio (the holdings) at time \(t\), we exclude the cash account from the \(\ell_1\) norm, and \(v_t\) is the total value (NAV) of the portfolio at time \(t\).

Returns:

Leverage at each period.

Return type:

pandas.Series

property turnoverView on GitHub

The turnover of the portfolio at each period.

This is defined as:

\[\| {(u_t)}_{1:n} \|_1 / (2 v_t),\]

where \(u_t\) are the portfolio trades at time \(t\), we exclude the cash account from the \(\ell_1\) norm, and \(v_t\) is the total value (NAV) of the portfolio at time \(t\).

Returns:

Turnover at each period.

Return type:

pandas.Series

property returnsView on GitHub

The portfolio returns at each period.

This is defined as:

\[R_t^\text{p} = \frac{v_{t+1} - v_t}{v_t}\]

in terms of the portfolio value (NAV).

Returns:

Portfolio returns at each period.

Return type:

pandas.Series

property average_returnView on GitHub

The average realized return \(\overline{R^\text{p}}\).

Returns:

Average portfolio return.

Return type:

float

property annualized_average_returnView on GitHub

The average realized return, annualized.

Returns:

Average portfolio return, annualized.

Return type:

float

property growth_ratesView on GitHub

The growth rate (or log-return) of the portfolio at each period.

This is defined as:

\[G^\text{p}_t = \log (v_{t+1} / v_t) = \log(1 + R^\text{p}_t).\]
Returns:

Growth rate of the portfolio value at each period.

Return type:

pandas.Series

property average_growth_rateView on GitHub

The average portfolio growth rate \(\overline{G^\text{p}}\).

Returns:

Average growth rate.

Return type:

float

property annualized_average_growth_rateView on GitHub

The average portfolio growth rate, annualized.

Returns:

Average growth rate, annualized.

Return type:

float

property volatilityView on GitHub

Realized volatility (standard deviation of the portfolio returns).

Returns:

Volatility.

Return type:

float

property annualized_volatilityView on GitHub

Realized volatility, annualized.

Returns:

Volatility, annualized.

Return type:

float

property quadratic_riskView on GitHub

Quadratic risk, square of the realized volatility.

Returns:

Quadratic risk.

Return type:

float

property annualized_quadratic_riskView on GitHub

Quadratic risk, annualized.

Returns:

Quadratic risk, annualized.

Return type:

float

property excess_returnsView on GitHub

Excess portfolio returns with respect to the cash returns.

Returns:

Excess returns at each period.

Return type:

pandas.Series

property excess_volatilityView on GitHub

Excess volatility (standard deviation of the excess returns).

Returns:

Average excess volatility.

Return type:

float

property average_excess_returnView on GitHub

The average excess return \(\overline{R^\text{e}}\).

Returns:

Average excess portfolio return.

Return type:

float

property annualized_average_excess_returnView on GitHub

The average excess return, annualized.

Returns:

Average excess portfolio return, annualized.

Return type:

float

property annualized_excess_volatilityView on GitHub

Annualized excess volatility.

Returns:

Average excess volatility, annualized.

Return type:

float

property active_returnsView on GitHub

Portfolio returns minus benchmark returns (if defined by policy).

Returns:

Active returns at each period if benchmark is defined, else nan.

Return type:

pandas.Series

property active_volatilityView on GitHub

Active volatility (standard deviation of the active returns).

Returns:

Average active volatility if benchmark is defined, else nan.

Return type:

float

property average_active_returnView on GitHub

The average active return \(\overline{R^\text{a}}\).

Returns:

Average active portfolio return if benchmark is defined, else nan.

Return type:

float

property annualized_average_active_returnView on GitHub

The average active return, annualized.

Returns:

Average active portfolio return, annualized. If benchmark is not defined, nan.

Return type:

float

property annualized_active_volatilityView on GitHub

Annualized active volatility.

Returns:

Average active volatility, annualized. If benchmark is not defined, nan.

Return type:

float

property sharpe_ratioView on GitHub

Sharpe ratio (using annualized excess portfolio returns).

This is defined as

\[\text{SR} = \overline{R^\text{e}}/\sigma^\text{e}\]

where \(\overline{R^\text{e}}\) is the average excess portfolio return and \(\sigma^\text{e}\) its standard deviation. Both are annualized.

Returns:

Sharpe Ratio.

Return type:

float

property information_ratioView on GitHub

Information ratio (using annualized active portfolio returns).

This is defined as

\[\text{IR} = \overline{R^\text{a}}/\sigma^\text{a}\]

where \(\overline{R^\text{a}}\) is the average active portfolio return and \(\sigma^\text{a}\) its standard deviation. Both are annualized.

Returns:

Information Ratio, nan if benchmark is not defined.

Return type:

float

property excess_growth_ratesView on GitHub

The growth rate of the portfolio, relative to cash.

This is defined as:

\[G^\text{e}_t = \log(1 + R^\text{e}_t)\]

where \(R^\text{e}_t\) are the excess portfolio returns.

Returns:

Excess growth rates at each period.

Return type:

pandas.Series

property average_excess_growth_rateView on GitHub

The average excess growth rate \(\overline{G^\text{e}}\).

Returns:

Average excess portfolio growth rates.

Return type:

float

property annualized_average_excess_growth_rateView on GitHub

The average excess growth rate, annualized.

Returns:

Average excess portfolio growth rates, annualized.

Return type:

float

property active_growth_ratesView on GitHub

The growth rate of the portfolio, relative to benchmark.

This is defined as:

\[G^\text{a}_t = \log(1 + R^\text{a}_t)\]

where \(R^\text{a}_t\) are the active portfolio returns.

Returns:

Active growth rates at each period. If benchmark is not defined, nan.

Return type:

pandas.Series

property average_active_growth_rateView on GitHub

The average active growth rate \(\overline{G^\text{a}}\).

Returns:

Average active portfolio growth rates. If benchmark is not defined, nan.

Return type:

float

property annualized_average_active_growth_rateView on GitHub

The average active growth rate, annualized.

Returns:

Average active portfolio growth rates, annualized. If benchmark is not defined, nan.

Return type:

float

property drawdownView on GitHub

The drawdown of the portfolio value over time.

Returns:

Drawdown of portfolio value at each period.

Return type:

pandas.Series

property policy_timesView on GitHub

The computation time of the policy object at each period.

Returns:

Policy time in seconds at each period.

Return type:

pandas.Series

property simulator_timesView on GitHub

The computation time of the simulator object at each period.

Returns:

Simulator time in seconds at each period.

Return type:

pandas.Series

property market_data_timesView on GitHub

The computation time of the market data server at each period.

This is already included in simulator_times !

Returns:

Market data server time in seconds at each period.

Return type:

pandas.Series

property result_timesView on GitHub

The computation time of the back-test result (this) at each period.

This is already included in simulator_times !

Returns:

Back-test result time in seconds at each period.

Return type:

pandas.Series

Interface methods with the market simulator

class cvxportfolio.result.BacktestResult(universe, trading_calendar, costs)View on GitHub

Store the data from a back-test and produce metrics and plots.

Additionally, record all logs produced by the simulator, market data server, and policy object during the back-test. These are stored in the logs attribute as a newline separated string. This is done in a multi-process safe manner, so that if you run parallel back-tests with cvxportfolio.MarketSimulator.backtest_many(), only the logs from the right process are recorded.

Parameters:
  • universe (pandas.Index) – Best initial guess of the trading universe.

  • trading_calendar (pd.DateTimeIndex) – Trading calendar. Can be a best guess, but the first timestamp must be the actual.

  • costs (list) – Simulator cost objects whose value is logged. Note: we use only the classes’ names here.

Note

The initializer of this class is still experimental, we might still change its signature without the guarantee of semantic versioning.

log_trading(t: pd.Timestamp, h: pd.Series[float], u: pd.Series[float], z: pd.Series[float], costs: Dict[str, float], cash_return: float, benchmark_return: float or None, policy_time: float, simulator_time: float, market_data_time: float)View on GitHub

Log one trading period.

Parameters:
  • t (pd.Timestamp) – Timestamp of execution.

  • h (pd.Series) – Initial holdings.

  • u (pd.Series) – Trade vectors in (e.g.) dollars.

  • z (pd.Series) – Trade weight vectors requested by the policy. Can be different from the actual trades because of rounding or any other filtering applied by the simulator. They are recorded but not used in accounting.

  • costs (dict) – Dictionary indexed by the cost class names with the current values of each. They are recorded but not used for accounting, that is done directly in the simulator and already included in the computed holdings.

  • cash_return (float) – Current return of the cash account (interest rate), used for excess metrics (e.g., Sharpe ratio).

  • benchmark_return (float or None) – Current return of the benchmark, if defined, otherwise None.

  • policy_time (float) – Time spent inside the policy object in this period.

  • simulator_time (float) – Time spent to back-test this period outside of the policy object.

  • market_data_time (float) – Time spent inside cvxportfolio.data.MarketData() for this period (also already included in simulator_time).

Note

This method is still experimental, we might change its signature without the guarantee of semantic versioning.

log_final(t, t_next, h, extra_simulator_time)View on GitHub

Log final elements and (if necessary) clean up.

Also clean up if the back-test finishes before it was expected to (e.g., bankruptcy).

Parameters:
  • t (pd.Timestamp) – Last execution time.

  • t_next (pd.Timestamp) – Next execution time, at which we don’t run the policy but we do record the holdings.

  • h (pd.Series) – Last value of the holdings, at time t_next.

  • extra_simulator_time (float) – Any extra time, in seconds, spent in the simulator to propagate the holdings from t to t_next (other times are already accounted for).

Note

This method is still experimental, we might change its signature without the guarantee of semantic versioning.