kernc / backtesting.py

:mag_right: :chart_with_upwards_trend: :snake: :moneybag: Backtest trading strategies in Python.
https://kernc.github.io/backtesting.py/
GNU Affero General Public License v3.0
5.57k stars 1.07k forks source link

Issue with bt.plot() #1158

Open btk-42 opened 4 months ago

btk-42 commented 4 months ago

Expected Behavior

bt.plot() doesn't show up and returns error.

Actual Behavior

error:

/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/backtesting/test/__init__.py:8: FutureWarning: The argument 'infer_datetime_format' is deprecated and will be removed in a future version. A strict version of it is now the default, see https://pandas.pydata.org/pdeps/0004-consistent-to-datetime-parsing.html. You can safely remove this argument.
  return pd.read_csv(join(dirname(__file__), filename),
/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/backtesting/test/__init__.py:8: FutureWarning: The argument 'infer_datetime_format' is deprecated and will be removed in a future version. A strict version of it is now the default, see https://pandas.pydata.org/pdeps/0004-consistent-to-datetime-parsing.html. You can safely remove this argument.
  return pd.read_csv(join(dirname(__file__), filename),
Traceback (most recent call last):
  File "/home/my_login/test/app.py", line 28, in <module>
    bt.plot()
  File "/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/backtesting/backtesting.py", line 1592, in plot
    return plot(
           ^^^^^
  File "/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/backtesting/_plotting.py", line 250, in plot
    formatter=DatetimeTickFormatter(days=['%d %b', '%a %d'],
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/bokeh/models/formatters.py", line 593, in __init__
    super().__init__(*args, **kwargs)
  File "/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/bokeh/models/formatters.py", line 93, in __init__
    super().__init__(*args, **kwargs)
  File "/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/bokeh/model/model.py", line 119, in __init__
    super().__init__(**kwargs)
  File "/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/bokeh/core/has_props.py", line 304, in __init__
    setattr(self, name, value)
  File "/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/bokeh/core/has_props.py", line 336, in __setattr__
    return super().__setattr__(name, value)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/bokeh/core/property/descriptors.py", line 330, in __set__
    value = self.property.prepare_value(obj, self.name, value)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/bokeh/core/property/bases.py", line 363, in prepare_value
    raise ValueError(f"failed to validate {obj_repr}.{name}: {error}")
ValueError: failed to validate DatetimeTickFormatter(id='p1046', ...).days: expected a value of type str, got ['%d %b', '%a %d'] of type list

Steps to Reproduce

  1. Install backtesting
  2. run example code

code:

from backtesting import Backtest, Strategy
from backtesting.lib import crossover

from backtesting.test import SMA, GOOG

class SmaCross(Strategy):
    n1 = 10
    n2 = 20

    def init(self):
        close = self.data.Close
        self.sma1 = self.I(SMA, close, self.n1)
        self.sma2 = self.I(SMA, close, self.n2)

    def next(self):
        if crossover(self.sma1, self.sma2):
            self.buy()
        elif crossover(self.sma2, self.sma1):
            self.sell()

bt = Backtest(GOOG, SmaCross,
              cash=10000, commission=.002,
              exclusive_orders=True)

output = bt.run()
bt.plot()

Additional info

Installing bokeh==3.4.2 fix the issue but makes everything lag and some other errors appears

/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/backtesting/test/__init__.py:8: FutureWarning: The argument 'infer_datetime_format' is deprecated and will be removed in a future version. A strict version of it is now the default, see https://pandas.pydata.org/pdeps/0004-consistent-to-datetime-parsing.html. You can safely remove this argument.
  return pd.read_csv(join(dirname(__file__), filename),
/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/backtesting/test/__init__.py:8: FutureWarning: The argument 'infer_datetime_format' is deprecated and will be removed in a future version. A strict version of it is now the default, see https://pandas.pydata.org/pdeps/0004-consistent-to-datetime-parsing.html. You can safely remove this argument.
  return pd.read_csv(join(dirname(__file__), filename),
BokehDeprecationWarning: Passing lists of formats for DatetimeTickFormatter scales was deprecated in Bokeh 3.0. Configure a single string format for each scale
/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/backtesting/_plotting.py:250: UserWarning: DatetimeFormatter scales now only accept a single format. Using the first provided: '%d %b'
  formatter=DatetimeTickFormatter(days=['%d %b', '%a %d'],
BokehDeprecationWarning: Passing lists of formats for DatetimeTickFormatter scales was deprecated in Bokeh 3.0. Configure a single string format for each scale
/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/backtesting/_plotting.py:250: UserWarning: DatetimeFormatter scales now only accept a single format. Using the first provided: '%m/%Y'
  formatter=DatetimeTickFormatter(days=['%d %b', '%a %d'],
/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/backtesting/_plotting.py:456: FutureWarning: 'M' is deprecated and will be removed in a future version, please use 'ME' instead.
  .resample(resample_rule, label='left')
/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/backtesting/_plotting.py:659: UserWarning: found multiple competing values for 'toolbar.active_drag' property; using the latest value
  fig = gridplot(
/home/my_login/.virtualenvs/test-gwyt/lib/python3.12/site-packages/backtesting/_plotting.py:659: UserWarning: found multiple competing values for 'toolbar.active_scroll' property; using the latest value
  fig = gridplot(
sirkuredda commented 4 months ago

Install the latest version from GitHub to incorporate recent fixes: pip install git+https://github.com/kernc/backtesting.py.git

The PyPI version (0.3.3) may not include the necessary fix

Dante-Berth commented 3 months ago

The solution is : In the file named _plotting.py

if is_datetime_index:
       fig_ohlc.xaxis.formatter = CustomJSTickFormatter(
        args=dict(axis=fig_ohlc.xaxis[0],
                  formatter=DatetimeTickFormatter(
                      days="%d %b %Y",     # Example format for days
                      months="%b %Y",      # Example format for months
                      hours="%H:%M",       # Example format for hours
                      minutes="%H:%M",     # Example format for minutes
                      seconds="%H:%M:%S"   # Example format for seconds
                  ),
                  source=source)

Best regards

0xAquaWolf commented 3 months ago

Install the latest version from GitHub to incorporate recent fixes: pip install git+https://github.com/kernc/backtesting.py.git

The PyPI version (0.3.3) may not include the necessary fix

This helped me a lot, i was pulling out my hair yesterday trying to get this to work i tried everything under the sun, thank you so much, when will PyPi be updated to reflect the new changes?

kenho811 commented 2 months ago

I experienced the same issue and came across this thread.

THe solution here works perfectly (https://github.com/kernc/backtesting.py/issues/1158#issuecomment-2232993016)

When will we have the update in PyPI please?

Thank you!!

S1x-Data-Team commented 1 month ago

if is_datetime_index: fig_ohlc.xaxis.formatter = CustomJSTickFormatter( args=dict(axis=fig_ohlc.xaxis[0], formatter=DatetimeTickFormatter( days="%d %b %Y", # Example format for days months="%b %Y", # Example format for months hours="%H:%M", # Example format for hours minutes="%H:%M", # Example format for minutes seconds="%H:%M:%S" # Example format for seconds ),

installing from git returning same error

jkim12johnkim commented 1 week ago

Not sure if this is still relevant but I had the same error until I made some adjustments to the initial data frame.

Initially, df = yf.Ticker("SPY").history(start='2010-01-01', end='2021-01-01').tz_localize(None) df.drop('Dividends', axis=1, inplace=True) df.drop('Stock Splits', axis=1, inplace=True) df.drop('Capital Gains', axis=1, inplace=True) df=df[df.High!=df.Low]

returned "ValueError: failed to validate DatetimeTickFormatter(id='p1996', ...).days: expected a value of type str, got ['%d %b', '%a %d'] of type list" for bt.plot()

But once I reset the index:

df = yf.Ticker("SPY").history(start='2010-01-01', end='2021-01-01').tz_localize(None)

df=df[df.High!=df.Low] df.drop('Dividends', axis=1, inplace=True) df.drop('Stock Splits', axis=1, inplace=True) df.drop('Capital Gains', axis=1, inplace=True) df.reset_index(inplace=True)

bt.plot() returns the figures