Skip to content

Backtest.plot() fails with resampling #987

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
bellerofonte opened this issue May 16, 2023 · 16 comments
Closed

Backtest.plot() fails with resampling #987

bellerofonte opened this issue May 16, 2023 · 16 comments
Labels
duplicate This issue or pull request already exists

Comments

@bellerofonte
Copy link

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

@DominikBerger01
Copy link
Contributor

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.

@eervin123
Copy link
Contributor

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
Copy link
Contributor

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

@DominikBerger01
Copy link
Contributor

@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

@eervin123
Copy link
Contributor

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

@eervin123
Copy link
Contributor

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

@eervin123
Copy link
Contributor

@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 😊

@DominikBerger01
Copy link
Contributor

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

@DominikBerger01
Copy link
Contributor

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: dfba461

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. :)

@bellerofonte
Copy link
Author

@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?

@eervin123
Copy link
Contributor

@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!

@DominikBerger01
Copy link
Contributor

DominikBerger01 commented Jun 1, 2023

@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. ;)

@bellerofonte
Copy link
Author

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!

@richieyoum
Copy link

richieyoum commented Jul 8, 2023

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

@dday1231
Copy link

@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.

@kernc
Copy link
Owner

kernc commented Feb 2, 2025

Index.get_loc() fixed in dfba461. Thanks!

@kernc kernc closed this as completed Feb 2, 2025
@kernc kernc added the duplicate This issue or pull request already exists label Feb 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests

6 participants