0

I was trying to understand how the backtesting.py module is written and came across this code.

class Strategy(metaclass=ABCMeta):
    """
    A trading strategy base class. Extend this class and
    override methods
    `backtesting.backtesting.Strategy.init` and
    `backtesting.backtesting.Strategy.next` to define
    your own strategy.
    """
    def __init__(self, broker, data, params):
        self._indicators = []
        self._broker: _Broker = broker
        self._data: _Data = data
        self._params = self._check_params(params)



    def __repr__(self):
        return '<Strategy ' + str(self) + '>'

    def __str__(self):
        params = ','.join(f'{i[0]}={i[1]}' for i in zip(self._params.keys(),
                                                        map(_as_str, self._params.values())))
        if params:
            params = '(' + params + ')'
        return f'{self.__class__.__name__}{params}'

    def _check_params(self, params):
        for k, v in params.items():
            if not hasattr(self, k):
                raise AttributeError(
                    f"Strategy '{self.__class__.__name__}' is missing parameter '{k}'."
                    "Strategy class should define parameters as class variables before they "
                    "can be optimized or run with.")
            setattr(self, k, v)
        return params

From what I have understood it seems that the to instantiate an object of the strategy class we need three arguments broker, data, params It seems that valid form of the params is supposed to be a dictionary with keys already being present as attributes. So does that mean params = {broker : "blah", data: "blahblah"} is the form that needs to be passed? WHat is the point of doing this in such a manner instead of directly passing the values to data and broker?

I tried to instantiate an object of the Strategy class using arbitrary dictionaries, just to understand what's going on.

This code q = Strategy("nn", [],{"b":33, "a":44}) gives the following error:

Strategy 'Strategy' is missing parameter 'b'.Strategy class should define parameters as class variables before they can be optimized or run with.

However if I do this:

q = Strategy("nn", [],{"_broker":33, "_data":44})

The code runs without errors.

Whatpurpose does this serve?

  • Where does this code come from? This pattern is not very good – Tom McLean Jan 09 '23 at 17:50
  • https://github.com/kernc/backtesting.py/blob/master/backtesting/backtesting.py This is the source. It is quite a popular package though. – Abhishek Banerjee Jan 09 '23 at 17:51
  • 1
    It looks like you *could* pass `_broker` or `_data` as dict keys in `params`, but that's certainly not the intended usage. The concrete subclasses of this class will have one or more additional attributes defined, *those* are the keys that you would use in `params`. – jasonharper Jan 09 '23 at 17:51
  • The class inherits from `ABCMeta`, not `ABC` – Tom McLean Jan 09 '23 at 17:53
  • @jasonharper why not define the additional attributes in the concrete subclasses directly? and what's the point of ```_check_params()``` that only adds attributes from keys in params if they are already present as attributes? – Abhishek Banerjee Jan 09 '23 at 17:54
  • @TomMcLean yes sorry, I have edited the question to reflect that it inherits from ```ABCmeta``` – Abhishek Banerjee Jan 09 '23 at 17:56
  • It's also lazily checking if a *class* attribute exists by accessing the attribute via the *instance*. `_check_params` should probably be a class method that takes an instance as an explicit argument. – chepner Jan 09 '23 at 18:02
  • I can't say I'm really impressed with this approach - I guess the advantage is that each subclass doesn't have to define its own `__init__()`. The check for the attribute already existing makes sure that you don't pass an option not known to the subclass. – jasonharper Jan 09 '23 at 18:03
  • But doesn't this mean we have to pass the same attribute twice once outside the ```params``` dictionary to make sure that it's already an attribute and once inside the ```params``` as a key? Pardon me if this is a silly thing to get stuck on. Could you please provide an example where this roundabout way is helpful while creating concrete subclasses? – Abhishek Banerjee Jan 09 '23 at 18:08
  • The subclass itself would define the attributes it expects, in the class body. The user of the subclass would give those attributes values via `params`. – jasonharper Jan 09 '23 at 18:45

0 Answers0