cvxgrp/cvxportfolio

suggestion for splitting backtest() method into single and multiple

Closed this issue · 2 comments

quant5 commented

Currently the backtest() method tries to support both single policy + multiple policies.
The API of backtest() could get confusing as more functionality is built out with single backtest vs multi backtest in mind. And the fact that it returns either an object or list of objects is generally frowned upon.

Suggest breaking backtest() into:

  • backtest() - keep the current API (except parallel) but only accept single policy and single objects for the rest of the parameters
  • backtest_multiple() - only support lists of policies. Just a suggestion but perhaps have some kwargs implementation so one can link the policies with holdings (and potentially other params), i.e.,:
multiple_policies = [
  {'policy': policy_1,'h': holdings_1,},
  {'policy': policy_2,'h': holdings_2,}, ...
]
backtest_multiple(multiple_policies)  # would call backtest() with the first and second dicts as kwargs
enzbus commented

Ok, maybe you have memories of the original API: We had MarketSimulator.run_backtest and MarketSimulator.run_multiple_backtests. I wanted to simplify the interface and so I merged them. Yes, currently when doing many backtests it returns a list of results, but the plan is to have a CombinedResults class with __iter__ and __getitem__ (so it acts like a list) but also methods specific for combined results, like making a risk-reward plot, getting the Pareto-optimal backtests, etc. Again, I refer to what was implemented in the 2017 examples in https://github.com/cvxgrp/cvxportfolio/tree/0.0.X ), which should become part of the main api. But your point is valid, and we may split the two functionalities in two methods. I suggest MarketSimulator.backtest and MarketSimulator.backtest_many (multiple is too long a word). Regarding args, I would do each of: policies, start_times, end_times, h, value_init, ... take either an iterable or a non-iterable. If you pass more than one iterable they must have the same length. All non-iterables are broadcasted.

enzbus commented

Actually, although I do agree with your concern, let me think about it. Numpy-style broadcasting is a powerful and very natural syntax, I think once we have the combined results rather than a list of results as return value the current syntax won't be too bad (and simpler than having 2 methods).