From 15c5163508ef655766349666854c95f10ba13049 Mon Sep 17 00:00:00 2001 From: David-Wu1119 <133224895+David-Wu1119@users.noreply.github.com> Date: Wed, 23 Sep 2026 13:05:03 -0400 Subject: [PATCH] BUG: Fix Backtest() with a non-datetime index on pandas 3 `Backtest.__init__` checks `data.index.is_numeric()` for every index that is neither a DatetimeIndex nor a RangeIndex. pandas 3.0 removed `Index.is_numeric()`, so `Backtest(data, ...)` raised AttributeError for data indexed by epoch timestamps or by date strings. The conversion call also passed `infer_datetime_format=True`, which pandas 3.0 removed as well (TypeError, not caught by the surrounding `except ValueError`). Use `pd.api.types.is_numeric_dtype()` (available in every supported pandas) and drop `infer_datetime_format`, which has no effect on numeric input. Co-Authored-By: Claude Opus 5.5 --- backtesting/backtesting.py | 4 ++-- backtesting/test/_test.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/backtesting/backtesting.py b/backtesting/backtesting.py index d356b211..38e94f73 100644 --- a/backtesting/backtesting.py +++ b/backtesting/backtesting.py @@ -1224,10 +1224,10 @@ def __init__(self, if (not isinstance(data.index, pd.DatetimeIndex) and not isinstance(data.index, pd.RangeIndex) and # Numeric index with most large numbers - (data.index.is_numeric() and + (pd.api.types.is_numeric_dtype(data.index) and (data.index > pd.Timestamp('1975').timestamp()).mean() > .8)): try: - data.index = pd.to_datetime(data.index, infer_datetime_format=True) + data.index = pd.to_datetime(data.index) except ValueError: pass diff --git a/backtesting/test/_test.py b/backtesting/test/_test.py index d74fde9f..f507ac45 100644 --- a/backtesting/test/_test.py +++ b/backtesting/test/_test.py @@ -1283,6 +1283,20 @@ def test_optimize_range_index(self): res = bt.optimize(fast=range(2, 3), slow=range(4, 5)) self.assertGreater(res['# Trades'], 0) + def test_numeric_and_string_index(self): + # `Index.is_numeric()` and `to_datetime(infer_datetime_format=)` were + # removed in pandas 3.0, so any index that was neither a DatetimeIndex + # nor a RangeIndex made `Backtest()` raise. + data = GOOG.iloc[:100].copy() + data.index = pd.Index(data.index.values.astype('datetime64[ns]').astype('int64')) + bt = Backtest(data, SmaCross) + self.assertIsInstance(bt._data.index, pd.DatetimeIndex) + self.assertTrue(bt._data.index.equals(GOOG.index[:100])) + + data.index = GOOG.index[:100].strftime('%Y-%m-%d') + with self.assertWarnsRegex(UserWarning, 'index is not datetime'): + Backtest(data, SmaCross) + def test_sl_tp_values_in_trades_df(self): class S(_S): def next(self):