diff --git a/backtesting/backtesting.py b/backtesting/backtesting.py index d356b211..d84dd875 100644 --- a/backtesting/backtesting.py +++ b/backtesting/backtesting.py @@ -1224,10 +1224,15 @@ 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.dtype) and (data.index > pd.Timestamp('1975').timestamp()).mean() > .8)): + index_magnitude = np.nanmedian(np.abs(data.index)) + # Infer seconds, milliseconds, microseconds, or nanoseconds by magnitude + unit = next((unit for unit, threshold in ( + ('s', 1e11), ('ms', 1e14), ('us', 1e17) + ) if index_magnitude < threshold), 'ns') try: - data.index = pd.to_datetime(data.index, infer_datetime_format=True) + data.index = pd.to_datetime(data.index, unit=unit) except ValueError: pass diff --git a/backtesting/test/_test.py b/backtesting/test/_test.py index d74fde9f..53025409 100644 --- a/backtesting/test/_test.py +++ b/backtesting/test/_test.py @@ -131,6 +131,24 @@ def test_data_invalid(self): with self.assertRaises(ValueError): Backtest(GOOG.iloc[:0], SmaCross).run() + def test_data_numeric_timestamp_index(self): + timestamps = { + 's': [1609459200, 1609545600], + 'ms': [1609459200000, 1609545600000], + 'us': [1609459200000000, 1609545600000000], + 'ns': [1609459200000000000, 1609545600000000000], + } + expected = pd.DatetimeIndex(['2021-01-01', '2021-01-02']) + + for unit, index in timestamps.items(): + with self.subTest(unit=unit): + data = GOOG.iloc[:2].copy() + data.index = index + + bt = Backtest(data, SmaCross) + + self.assertTrue(bt._data.index.equals(expected)) + def test_assertions(self): class Assertive(Strategy): def init(self):