diff --git a/README.md b/README.md index fdf97ce..9c968c9 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,8 @@ Supported statistics/indicators are: * FTR: the Gaussian Fisher Transform Price Reversals indicator * RVGI: Relative Vigor Index * Inertia: Inertia Indicator +* KST: Know Sure Thing +* PGO: Pretty Good Oscillator ## Installation @@ -990,6 +992,23 @@ Where: * RCMA3=10-period SMA of 20-period ROC * RCMA4=15-period SMA of 30-period ROC +Example: +* `df['kst']` retrieves the KST. + +#### [Pretty Good Oscillator (PGO)](https://library.tradingtechnologies.com/trade/chrt-ti-pretty-good-oscillator.html) + +The Pretty Good Oscillator indicator by Mark Johnson measures the +distance of the current close from its N-day simple moving average, +expressed in terms of an average true range over a similar period. + +Formular: +* PGO = (Close - SMA) / (EMA of TR) + +Example: +* `df['pgo']` retrieves the PGO with default window 14. +* `df['pgo_10']` retrieves the PGO with window 10. + + ## Issues We use [Github Issues](https://github.com/jealous/stockstats/issues) to track diff --git a/stockstats.py b/stockstats.py index cab7f86..267dd9f 100644 --- a/stockstats.py +++ b/stockstats.py @@ -71,6 +71,7 @@ class StockStatsError(Exception): 'mfi': 14, 'ndi': 14, 'pdi': 14, + 'pgo': 14, 'ppo': (12, 26, 9), # short, long, signal 'rsi': 14, 'rsv': 9, @@ -1581,6 +1582,29 @@ def _kst(self) -> pd.Series: def _get_kst(self, meta: _Meta): self[meta.name] = self._kst() + def _pgo(self, window: int) -> pd.Series: + """ Pretty Good Oscillator (PGO) + + https://library.tradingtechnologies.com/trade/chrt-ti-pretty-good-oscillator.html + + The Pretty Good Oscillator indicator by Mark Johnson measures the + distance of the current close from its N-day simple moving average, + expressed in terms of an average true range over a similar period. + + Formular: + * PGO = (Close - SMA) / (EMA of TR) + + Where: + * SMA = Simple Moving Average of Close over N periods + * EMA of TR = Exponential Moving Average of True Range over N periods + """ + up = self.close - self.sma(self.close, window) + down = self.ema(self._tr(), window) + return up / down + + def _get_pgo(self, meta: _Meta): + self[meta.name] = self._pgo(meta.int) + @staticmethod def parse_column_name(name): m = re.match(r'(.*)_([\d\-+~,.]+)_(\w+)', name) @@ -1727,6 +1751,7 @@ def handler(self): ('rvgi', 'rvgis'): self._get_rvgi, ('inertia',): self._get_inertia, ('kst',): self._get_kst, + ('pgo',): self._get_pgo, } def __init_not_exist_column(self, key): diff --git a/test.py b/test.py index 8225392..8b58da0 100644 --- a/test.py +++ b/test.py @@ -1044,3 +1044,17 @@ def test_kst(self): assert_that(kst[20110117], equal_to(0)) assert_that(kst[20110118], near_to(0.063442)) assert_that(kst[20110131], near_to(-2.519985)) + + def test_pgo(self): + stock = self.get_stock_90days() + pgo = stock['pgo'] + assert_that(pgo[20110117], near_to(-0.968845)) + assert_that(pgo[20110214], near_to(1.292029)) + + pgo14 = stock['pgo_14'] + assert_that(pgo14[20110117], near_to(-0.968845)) + assert_that(pgo14[20110214], near_to(1.292029)) + + pgo10 = stock['pgo_10'] + assert_that(pgo10[20110117], near_to(-0.959768)) + assert_that(pgo10[20110214], near_to(1.214206))