Kismuz / btgym

Scalable, event-driven, deep-learning-friendly backtesting library
https://kismuz.github.io/btgym/
GNU Lesser General Public License v3.0
979 stars 259 forks source link

Feature Request: Enable Custom Sizers #116

Closed mobias17 closed 4 years ago

mobias17 commented 5 years ago

In the Version 0.0.7 the second basic assumption is "broker actions are fixed-size market orders (buy, sell, close); short selling is permitted" what works fine.

With the motivation of keeping every action to a relative risk relation twoards my cash/value it would be nice to enable custom sizers that would calculate the right position.

The code that would have to be adjusted is the following in btgym/btgym/strategy/base.py line 231 - 245:

Try to define stake, if no self.p.order_size dict has been set:

    if self.p.order_size is None:
        # If no order size has been set for every data_line,
        # try to infer stake size from sizer set by bt.Cerebro.addsizer() method:
        try:
            assert len(list(self.env.sizers.values())) == 1
            env_sizer_params = list(self.env.sizers.values())[0][-1]  # pull dict of outer set sizer params
            assert 'stake' in env_sizer_params.keys()

        except (AssertionError, KeyError) as e:
            msg = 'Order stake is not set neither via strategy.param.order_size nor via bt.Cerebro.addsizer method.'
            self.log.error(msg)
            raise ValueError(msg)

        self.p.order_size = {name: env_sizer_params['stake'] for name in self.p.asset_names}

To use custom sizers from Backtrader it needs to be possible to pass size=None to the buy/sell order. In that case backtrader will call the sizer specified in the engine.

Proposed work around for those who wish to expiriment with that:

overwrite the Base Strategy method " def _next_discrete" and replace the lines: self.order = self.buy(data=key, size=self.p.order_size[key]) self.order = self.sell(data=key, size=self.p.order_size[key])

with

self.order = self.buy(data=key) self.order = self.sell(data=key)

Even if your custom sizer does not require a stake size one need to be passed, so that the strategy does not get caught in the well working "except (AssertionError, KeyError)" error.

Kismuz commented 5 years ago

@mobias17, you are correct but fixed sizes are just basic assumption; you can check: https://github.com/Kismuz/btgym/blob/master/btgym/research/model_based/strategy.py#L333 for example of dynamic order sizing based on current account value; those are still market orders though, limit orders will require it's own logic and action space.

mobias17 commented 4 years ago

Great, Thank you for the hint. haven't passed this part of yet.