backtesting.py icon indicating copy to clipboard operation
backtesting.py copied to clipboard

Backtest.plot() fails with resampling

Open bellerofonte opened this issue 2 years ago • 15 comments

Expected Behavior

charts plotted

Actual Behavior

following exception:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[52], line 1
----> 1 bt.plot(resample='2H')

File [~/miniforge3/lib/python3.10/site-packages/backtesting/backtesting.py:1592](https://file+.vscode-resource.vscode-cdn.net/Users/pgm/Projects/crypto-dl/src/model/~/miniforge3/lib/python3.10/site-packages/backtesting/backtesting.py:1592), in Backtest.plot(self, results, filename, plot_width, plot_equity, plot_return, plot_pl, plot_volume, plot_drawdown, smooth_equity, relative_equity, superimpose, resample, reverse_indicators, show_legend, open_browser)
   1589         raise RuntimeError('First issue `backtest.run()` to obtain results.')
   1590     results = self._results
-> 1592 return plot(
   1593     results=results,
   1594     df=self._data,
   1595     indicators=results._strategy._indicators,
   1596     filename=filename,
   1597     plot_width=plot_width,
   1598     plot_equity=plot_equity,
   1599     plot_return=plot_return,
   1600     plot_pl=plot_pl,
   1601     plot_volume=plot_volume,
   1602     plot_drawdown=plot_drawdown,
   1603     smooth_equity=smooth_equity,
   1604     relative_equity=relative_equity,
   1605     superimpose=superimpose,
   1606     resample=resample,
   1607     reverse_indicators=reverse_indicators,
   1608     show_legend=show_legend,
...
    147     mean_time = int(bars.loc[s.index].view(int).mean())
--> 148     new_bar_idx = new_index.get_loc(mean_time, method='nearest')
    149     return new_bar_idx

TypeError: Index.get_loc() got an unexpected keyword argument 'method'

Steps to Reproduce

class WhateverStrategy(Strategy):
    def init(self):
        pass
        

    def next(self):
        close = self.data.Close[-1]
        if close > 55 and (not self.position.is_short):
            self.sell()
        elif close < 25 and (not self.position.is_long):
            self.buy()


bt = Backtest(df, WhateverStrategy, commission=0.0002, cash=100)
bt.run()
bt.plot(resample='2H')

Additional info

- Backtesting: 0.3.3
- bokeh: 3.1.1
- pandas: 2.0.1
- OS: macOS Ventura 13.3.1 arm64

however, if I change next method to following:

    def next(self):
        pass

chart would be plotted successfully without equity curve

the dataset contains 800k+ of 1M candles

bellerofonte avatar May 16 '23 08:05 bellerofonte

Hi there @bellerofonte , I tried to reproduce your error but without success.

However this seems to be related to your pandas version as in version 2.0.1 the get_loc function no longer takes method as parameter

https://pandas.pydata.org/docs/reference/api/pandas.Index.get_loc.html

I used pandas version 1.4.4 https://pandas.pydata.org/pandas-docs/version/1.4/reference/api/pandas.Index.get_loc.html

However after upgrading to 2.0.1 the error still did not appear. Would need your data and code on how you created your dataframe to dive deeper.

You can try to downgrade pandas to 1.4.4 to see if it'll work.

Here is my code and data for reference.

CODE

from backtesting import Strategy, Backtest
import pandas as pd

link_to_issue = 'https://github.com/kernc/backtesting.py/issues/987'

df = pd.read_csv('../market_data/BTCUSDT-5m-data.csv', parse_dates=True, index_col=0)
df = df[['open', 'high', 'low', 'close']]
df.columns = map(lambda x: str(x).capitalize(), df.columns)

print(df)


class WhateverStrategy(Strategy):
    def init(self):
        pass

    def next(self):
        close = self.data.Close[-1]
        if close > 55 and (not self.position.is_short):
            self.sell()
        elif close < 25 and (not self.position.is_long):
            self.buy()


bt = Backtest(df, WhateverStrategy, commission=0.0002, cash=100)
bt.run()
bt.plot(resample='2H')

OUPUT

/Users/dominik/Desktop/vola_statistics/venv/bin/python /Users/dominik/Desktop/vola_statistics/tests/stackoverflow_issue.py 
                         Open      High       Low     Close
timestamp                                                  
2017-08-17 04:00:00   4261.48   4280.56   4261.48   4261.48
2017-08-17 04:05:00   4261.48   4261.48   4261.48   4261.48
2017-08-17 04:10:00   4261.48   4261.48   4261.48   4261.48
2017-08-17 04:15:00   4261.48   4264.88   4261.48   4261.48
2017-08-17 04:20:00   4264.88   4266.29   4264.88   4266.29
...                       ...       ...       ...       ...
2023-05-04 08:45:00  29060.07  29078.22  29058.24  29073.02
2023-05-04 08:50:00  29073.02  29085.62  29073.02  29078.52
2023-05-04 08:55:00  29078.51  29094.02  29077.75  29089.26
2023-05-04 09:00:00  29089.26  29109.65  29082.49  29084.84
2023-05-04 09:05:00  29084.83  29084.84  29078.18  29078.18

[599127 rows x 4 columns]
/Users/dominik/Desktop/vola_statistics/tests/stackoverflow_issue.py:24: UserWarning: Some prices are larger than initial cash value. Note that fractional trading is not supported. If you want to trade Bitcoin, increase initial cash, or trade μBTC or satoshis instead (GH-134).
  bt = Backtest(df, WhateverStrategy, commission=0.0002, cash=100)

Process finished with exit code 0

Candles have been successfully resampled to 2H

Screen Shot 2023-05-18 at 14 00 07 Screen Shot 2023-05-18 at 14 00 43 Screen Shot 2023-05-18 at 14 00 14

I used 5min data.

DominikBerger01 avatar May 18 '23 12:05 DominikBerger01

I'm getting the same error. Of note, if I reset the index in the dataframe from a datetime index to a regular integer index then the plot works. My pandas was also v 2.0.1

I just needed something quick so I haven't had a chance to deal with it and doubt I will. I just thought I would share here in case it helps you all to diagnose it.

eervin123 avatar May 29 '23 18:05 eervin123

Not sure if this would work, but it seems like they cover this issue here.

I will volunteer to do pr if it is as simple as changing the line in _plotting.py from new_bar_idx = new_index.get_loc(mean_time, method='nearest') to new_bar_idx = new_index.get_indexer(mean_time, method='nearest')[0] but I doubt anything is that simple.

cc @kernc

eervin123 avatar May 29 '23 19:05 eervin123

@eervin123 Thank you for sharing. Additionally could you send your code and data you used. Because I struggle to reproduce on my side. Will help me to better diagnose why it works with my data and df setup. I volunteer to create and propose some unit-tests for this. Regards, Dominik

DominikBerger01 avatar May 30 '23 07:05 DominikBerger01

Okay @DominikBerger01 I'll try to get to that tonight. PST

eervin123 avatar May 30 '23 16:05 eervin123

fyi, I was able to get it to work with bt.plot(resample=False)

eervin123 avatar May 30 '23 16:05 eervin123

@DominikBerger01 here you go. the error_example.ipynb file should be enough to isolate the problem. I also included two environment files, one which is my regular env. the other is a clean test environment where i just pip installed backtesting and scikit-optimize.

Happy hunting 😊

eervin123 avatar May 30 '23 19:05 eervin123

@eervin123 Many thanks, will start exploring tomorrow morning. ;)

DominikBerger01 avatar May 30 '23 21:05 DominikBerger01

I was able to reproduce the bug. After trying to solve it I have found that it already was solved and pushed to main back in Dezember 2022. But it is not yet reflected in the latest distributed version.

This was the PR: https://github.com/kernc/backtesting.py/commit/dfba4615e851e7895aab7456f909dad45e6a0134

Also looks to me like the correct implementation as all tests passed:

OLD (causing error and tests to fail) Screenshot 2023-05-31 at 18 20 52

NEW Screenshot 2023-05-31 at 18 21 44

So I guess we'll have to wait for the next release. Which in my opinion should happen sooner than later as most of the people getting the current version will be bugged by this. :)

DominikBerger01 avatar May 31 '23 16:05 DominikBerger01

@DominikBerger01 @eervin123 thank you for your effort spent on finding the problem!

Is it a good idea to add [0] directly into _plotting.py in my packages directory until new release appears?

bellerofonte avatar May 31 '23 18:05 bellerofonte

@bellerofonte You also would need to change it from get_loc to get_indexer then add the [0]

I haven't tried this yet but it is pretty simple to make the change and then try it out. If it doesn't work you can undo the change and all will be as before.

Thank you to you @DominikBerger01 for investigating. Open source FTW!

eervin123 avatar May 31 '23 18:05 eervin123

@DominikBerger01 @eervin123 thank you for your effort spent on finding the problem!

Is it a good idea to add [0] directly into _plotting.py in my packages directory until new release appears?

You could change the source code. However I wouldn't recommend it. A more common approach would be to pip install the latest changes directly from Github.

Here is how:

Step 1 Uninstall your current backtesting installation: pip uninstall Backtesting

Step 2 get the link from the repo and add git+ in front and pip install it pip install git+https://github.com/kernc/backtesting.py.git

This should install the latest changes from master branch

Step 3 run pip freeze and you should see Backtesting @ git+https://github.com/kernc/backtesting.py.git@0ce24d80b1bcb8120d95d31dc3bb351b1052a27d your new backtesting installation along with your other installed packages.

The hash you see at the end btw refers to the hash of the latest commit: (0ce24d80b1bcb8120d95d31dc3bb351b1052a27d) Screenshot 2023-06-01 at 10 32 44

Now you have the latest version with unreleased changes which should fix your bug. Hope this helps. If yes, you can accept the answer and close the issue. ;)

DominikBerger01 avatar Jun 01 '23 08:06 DominikBerger01

Step 2 get the link from the repo and add git+ in front and pip install it pip install git+https://github.com/kernc/backtesting.py.git

@DominikBerger01 I can confirm your method solves the issue. Thanks a lot!

bellerofonte avatar Jun 01 '23 10:06 bellerofonte

Just wanted to help out, Index.get_loc() method got deprecated after pandas 1.4. You can just downgrade pandas to 1.4.0 and it should work :)

Source: https://pandas.pydata.org/pandas-docs/version/1.5/reference/api/pandas.Index.get_loc.html

richieyoum avatar Jul 08 '23 04:07 richieyoum

@DominikBerger01 @eervin123 thank you for your effort spent on finding the problem! Is it a good idea to add [0] directly into _plotting.py in my packages directory until new release appears?

You could change the source code. However I wouldn't recommend it. A more common approach would be to pip install the latest changes directly from Github.

Here is how:

Step 1 Uninstall your current backtesting installation: pip uninstall Backtesting

Step 2 get the link from the repo and add git+ in front and pip install it pip install git+https://github.com/kernc/backtesting.py.git

This should install the latest changes from master branch

Step 3 run pip freeze and you should see Backtesting @ git+https://github.com/kernc/backtesting.py.git@0ce24d80b1bcb8120d95d31dc3bb351b1052a27d your new backtesting installation along with your other installed packages.

The hash you see at the end btw refers to the hash of the latest commit: (0ce24d8) Screenshot 2023-06-01 at 10 32 44

Now you have the latest version with unreleased changes which should fix your bug. Hope this helps. If yes, you can accept the answer and close the issue. ;)

I just got this error, and the steps quoted above from a prior post fixed my issue. Thanks! I'm looking forward to the next release so this issue is fixed without the work around.

dday1231 avatar Jul 16 '23 19:07 dday1231