From 78306b627f997aa57777d64a413414eb88d79fef Mon Sep 17 00:00:00 2001 From: Cedric Zhuang Date: Fri, 16 Jun 2023 20:54:18 +0800 Subject: [PATCH] [GH-141] Add BOP (#142) Add Balance of Power. Balance of Power (BOP) measures the strength of the bulls vs. bears. Formular: ``` BOP = (close - open) / (high - low) ``` Example: * `df['bop']` returns the Balance of Power --- README.md | 17 ++++++++++++++++- stockstats.py | 17 +++++++++++++++-- test.py | 6 ++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3e681d8..946e357 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Supported statistics/indicators are: * Aroon: Aroon Oscillator * Z: Z-Score * AO: Awesome Oscillator +* BOP: Balance of Power ## Installation @@ -682,7 +683,9 @@ There is no default column name or window for Z-Score. The statistical formula for a value's z-score is calculated using the following formula: -```z = ( x - μ ) / σ``` +``` +z = ( x - μ ) / σ +``` Where: @@ -710,6 +713,18 @@ Examples: * `df['ao']` returns the Awesome Oscillator with default windows (5, 34) * `df['ao_3,10']` returns the Awesome Oscillator with a window of 3 and 10 +#### [Balance of Power](https://school.stockcharts.com/doku.php?id=technical_indicators:balance_of_power) + +Balance of Power (BOP) measures the strength of the bulls vs. bears. + +Formular: +``` +BOP = (close - open) / (high - low) +``` + +Example: +* `df['bop']` returns the Balance of Power + ## Issues We use [Github Issues](https://github.com/jealous/stockstats/issues) to track diff --git a/stockstats.py b/stockstats.py index b0641f0..b597f69 100644 --- a/stockstats.py +++ b/stockstats.py @@ -1203,6 +1203,18 @@ def _get_ao(self, windows=None): ao = self._sma(median_price, fast) - self._sma(median_price, slow) self[column_name] = ao + def _get_bop(self): + """ get balance of power + + The Balance of Power indicator measures the strength of the bulls. + https://school.stockcharts.com/doku.php?id=technical_indicators:balance_of_power + + BOP = (close - open) / (high - low) + """ + dividend = self['close'] - self['open'] + divisor = self['high'] - self['low'] + self['bop'] = dividend / divisor + def _get_kama(self, column, windows, fasts=None, slows=None): """ get Kaufman's Adaptive Moving Average. Implemented after @@ -1210,8 +1222,8 @@ def _get_kama(self, column, windows, fasts=None, slows=None): :param column: column to calculate :param windows: collection of window of exponential moving average - :param fasts: fastest EMA constant - :param slows: slowest EMA constant + :param fasts: faster EMA constant + :param slows: slower EMA constant :return: None """ window = self.get_int_positive(windows) @@ -1382,6 +1394,7 @@ def handler(self): 'supertrend_ub'): self._get_supertrend, ('aroon',): self._get_aroon, ('ao',): self._get_ao, + ('bop',): self._get_bop, } def __init_not_exist_column(self, key): diff --git a/test.py b/test.py index f2368f3..bffea14 100644 --- a/test.py +++ b/test.py @@ -681,6 +681,12 @@ def test_ao(self): assert_that(ao1[idx], equal_to(ao[idx])) assert_that(ao2[idx], near_to(-0.071)) + def test_bop(self): + stock = self.get_stock_30day() + bop = stock['bop'] + assert_that(bop[20110104], near_to(0.5)) + assert_that(bop[20110106], near_to(-0.207)) + def test_drop_column_inplace(self): stock = self._supor[:20] stock.columns.name = 'Luke'