From 41c153b09d14ccfe8aa052f0ff373863752b5f51 Mon Sep 17 00:00:00 2001 From: xvruipeng-cyber <317357528+xvruipeng-cyber@users.noreply.github.com> Date: Fri, 23 Oct 2026 00:25:12 +0800 Subject: [PATCH] Index ATR prices by position so point-and-figure box sizes survive datetime indexes. --- src/mplfinance/_utils.py | 5 +++++ tests/test_atr.py | 15 +++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/test_atr.py diff --git a/src/mplfinance/_utils.py b/src/mplfinance/_utils.py index 024b966e..a1d96890 100644 --- a/src/mplfinance/_utils.py +++ b/src/mplfinance/_utils.py @@ -124,6 +124,11 @@ def _calculate_atr(atr_length, highs, lows, closes): raise ValueError("Specified atr_length may not be less than 1") elif atr_length >= len(closes): raise ValueError("Specified atr_length is larger than the length of the dataset: " + str(len(closes))) + # Point & Figure passes DatetimeIndex Series. Integer keys on those + # Series are labels, not positions, so index by a plain array. + highs = np.asarray(highs) + lows = np.asarray(lows) + closes = np.asarray(closes) atr = 0 for i in range(len(highs)-atr_length, len(highs)): high = highs[i] diff --git a/tests/test_atr.py b/tests/test_atr.py new file mode 100644 index 00000000..7640e37e --- /dev/null +++ b/tests/test_atr.py @@ -0,0 +1,15 @@ +import pandas as pd + +from mplfinance._utils import _calculate_atr + + +def test_atr_indexes_datetime_series_by_position(): + index = pd.date_range("2020-01-01", periods=5, freq="D") + high = pd.Series([10, 12, 11, 13, 12], index=index) + low = pd.Series([8, 9, 9, 10, 9], index=index) + close = pd.Series([9, 11, 10, 12, 11], index=index) + + # Bars 4 and 5 each have a true range of 3, so a 2-bar ATR is 3. + assert _calculate_atr(2, high, low, close) == 3 + assert _calculate_atr(2, high.to_numpy(), low.to_numpy(), close.to_numpy()) == 3 + assert _calculate_atr(2, list(high), list(low), list(close)) == 3