pmorissette / bt

bt - flexible backtesting for Python
http://pmorissette.github.io/bt
MIT License
2.17k stars 418 forks source link

BT seems unable to composite three or more layers strategy, ZeroDivisionError #433

Open tunkills opened 7 months ago

tunkills commented 7 months ago

When I only composite two layers strategy,it woks well, but when I add more layer strategy,such as there is a grandson strategy, threre is something wrong! "ZeroDivisionError: Could not update third on 2017-01-02 00:00:00. Last value was 0 and net flows were 0. Currentvalue is 1000000.0. Therefore, we are dividing by zero to obtain the return for the period."


import numpy as np
import pandas as pd
import bt
import warnings

warnings.simplefilter('ignore', FutureWarning)
warnings.simplefilter('ignore', UserWarning)

# create fake index data

names = ['foo', 'bar', 'rf']

dates = pd.date_range('20170101', '20171231', freq=pd.tseries.offsets.BDay())
n = len(dates)

rdf = pd.DataFrame(np.zeros((n, len(names))), index=dates, columns=names)

np.random.seed(1)
rdf['foo'] = np.random.normal(loc=0.1/n, scale=0.2/np.sqrt(n), size=n)
rdf['bar'] = np.random.normal(loc=0.04/n, scale=0.05/np.sqrt(n), size=n)
rdf['rf'] = 0.0

pdf = 100 * np.cumprod(1+rdf)

# composite three layer strategy
weights = pd.Series([0.6, 0.2, 0.1], index=rdf.columns)
strat = bt.Strategy('static',
                    [bt.algos.RunMonthly(run_on_first_date=True),
                     bt.algos.WeighSpecified(**weights),
                     bt.algos.Rebalance()]
                    )

second_strat = bt.Strategy('second',
                           [bt.algos.RunMonthly(run_on_first_date=True),
                            bt.algos.SelectAll(),
                            bt.algos.WeighEqually(),
                            bt.algos.Rebalance()],
                           children=[strat, 'foo'])

third_strat = bt.Strategy('third',
                          [bt.algos.RunMonthly(run_on_first_date=True),
                           bt.algos.SelectAll(),
                           bt.algos.WeighEqually(),
                           bt.algos.Rebalance()],
                          children=[second_strat, 'bar'])
third_bk = bt.Backtest(third_strat, pdf, integer_positions=False)
third_re = bt.run(third_bk)

``

File "bt/core.py", line 777, in bt.core.StrategyBase.update

ZeroDivisionError: Could not update third on 2017-01-02 00:00:00. Last value was 0 and net flows were 0. Currentvalue is 1000000.0. Therefore, we are dividing by zero to obtain the return for the period. `

I find the problem may be caused by


class StrategyBase(Node):
......
    @cy.locals(
        newpt=cy.bint,
        val=cy.double,
        ret=cy.double,
        coupons=cy.double,
        notl_val=cy.double,
        bidoffer_paid=cy.double,
    )
    def update(self, date, data=None, inow=None):
    .......
     # update paper trade if necessary
        if self._paper_trade:
            if newpt:
                self._paper.update(date)
                self._paper.run()
                self._paper.update(date)
            # update price
            self._price = self._paper.price
            self._prices.values[inow] = self._price`

when I comment out the lines “if self._paper_trade:” and below, it works well. But I'm not sure if this modification will cause other issues.

timkpaine commented 7 months ago

If I recall, usually this occurs when you don't take any positions over the whole backtest.

tunkills commented 7 months ago

If I recall, usually this occurs when you don't take any positions over the whole backtest.

it happens at the first day, the strategy just begin, no matter what strategy, if the layer is three or more, it will give the sample error, you can just backtest and run from the second layer, it works well, so its not the problem of positions!If you have a three layers strategy, and it works well, could you provide me with the code, I'd appreciate it

tunkills commented 7 months ago

If I recall, usually this occurs when you don't take any positions over the whole backtest.

In the file core.py: if the strategy is not the root strategy, the strategy will create a paper trade。 at the line 571:


class StrategyBase(Node):
......
    def setup(self, universe, **kwargs):
.....
        if self is not self.parent:  
            self._paper_trade = True
            self._paper_amount = 1000000

            paper = deepcopy(self)
            paper.parent = paper  
            paper.root = paper
            paper._paper_trade = False
            paper.setup(self._original_data, **kwargs) 
            paper.adjust(self._paper_amount)
            self._paper = paper

but we see when a children strategy update itself, the paper update iteself too。 at the line 767:

        self._price = self._last_price + ret
        self._prices.values[inow] = self._price

and at the line 827:

# update paper trade if necessary
        if self._paper_trade:
            if newpt:
                self._paper.update(date)
                self._paper.run()
                self._paper.update(date)
            # update price
            self._price = self._paper.price
            self._prices.values[inow] = self._price

that make me confused, if the self._paper_trade, it seems the paper just overwritten the original update。And I don't think the paper trading is required!