From 45a216b940824f38b4a133a1e3b7a60a72611ef6 Mon Sep 17 00:00:00 2001 From: Cedric Zhuang Date: Mon, 12 Jun 2023 22:07:22 +0800 Subject: [PATCH] [GH-121] Add aroon indicator Add aroon oscillator. * Aroon Oscillator = Aroon Up - Aroon Down * Aroon Up = 100 * (n - periods since n-period high) / n * Aroon Down = 100 * (n - periods since n-period low) / n * n = window size https://www.investopedia.com/terms/a/aroonoscillator.asp The default window is 25. --- README.md | 20 +++++++++++++++++++- stockstats.py | 26 ++++++++++++++++++++++++++ test.py | 12 ++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 149cc8e..a70be88 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![codecov](https://codecov.io/gh/jealous/stockstats/branch/master/graph/badge.svg?token=IFMD1pVJ7T)](https://codecov.io/gh/jealous/stockstats) [![pypi](https://img.shields.io/pypi/v/stockstats.svg)](https://pypi.python.org/pypi/stockstats) -VERSION: 0.5.2 +VERSION: 0.5.3 ## Introduction @@ -54,6 +54,7 @@ Supported statistics/indicators are: * StochRSI: Stochastic RSI * WT: LazyBear's Wave Trend * Supertrend: with the Upper Band and Lower Band +* Aroon: Aroon Oscillator ## Installation @@ -653,6 +654,23 @@ Examples: * `kdjk_xu_kdjd` returns a series that marks where KDJK crosses up KDJD * `kdjk_xd_kdjd` returns a series that marks where KDJD crosses down KDJD +#### [Aroon Oscillator](https://www.investopedia.com/terms/a/aroonoscillator.asp) + +The Aroon Oscillator measures the strength of a trend and +the likelihood that it will continue. + +The default window is 25. + +* Aroon Oscillator = Aroon Up - Aroon Down +* Aroon Up = 100 * (n - periods since n-period high) / n +* Aroon Down = 100 * (n - periods since n-period low) / n +* n = window size + +Examples: +* `df['aroon']` returns Aroon oscillator with a window of 25 +* `df['aroon_14']` returns Aroon oscillator with a window of 14 + + ## Issues We use [Github Issues](https://github.com/jealous/stockstats/issues) to track diff --git a/stockstats.py b/stockstats.py index 37767b6..1971cc0 100644 --- a/stockstats.py +++ b/stockstats.py @@ -603,6 +603,31 @@ def _get_supertrend(self, window=None): self['supertrend_lb'] = lb self['supertrend'] = st + def _get_aroon(self, window=None): + if window is None: + window = 25 + column_name = 'aroon' + else: + window = self.get_int_positive(window) + column_name = 'aroon_{}'.format(window) + + def _window_pct(s): + n = float(window) + return (n - (n - (s + 1))) / n * 100 + + high_since = self['high'].rolling( + min_periods=1, + window=window, + center=False).apply(np.argmax) + low_since = self['low'].rolling( + min_periods=1, + window=window, + center=False).apply(np.argmin) + + aroon_up = _window_pct(high_since) + aroon_down = _window_pct(low_since) + self[column_name] = aroon_up - aroon_down + def _atr(self, window): tr = self._tr() return self._smma(tr, window) @@ -1278,6 +1303,7 @@ def handler(self): ('supertrend', 'supertrend_lb', 'supertrend_ub'): self._get_supertrend, + ('aroon',): self._get_aroon, } def __init_not_exist_column(self, key): diff --git a/test.py b/test.py index 33b0d18..c2b6524 100644 --- a/test.py +++ b/test.py @@ -726,3 +726,15 @@ def test_drop_tail(self): assert_that(ret.iloc[-1].name, equal_to(20040831)) assert_that(stock, has_length(20)) assert_that(stock.iloc[-1].name, equal_to(20040913)) + + def test_aroon(self): + stock = self._supor[:50] + _ = stock['aroon'] + assert_that(stock.loc[20040924, 'aroon'], equal_to(28)) + + _ = stock['aroon_25'] + assert_that(stock.loc[20040924, 'aroon_25'], equal_to(28)) + + _ = stock['aroon_5'] + assert_that(stock.loc[20040924, 'aroon_5'], equal_to(40)) + assert_that(stock.loc[20041020, 'aroon_5'], equal_to(-80))