Skip to content

Commit

Permalink
[GH-160] Add Relative Vigor Index (#161)
Browse files Browse the repository at this point in the history
The Relative Vigor Index (RVI) is a momentum indicator
used in technical analysis that measures the strength
of a trend by comparing a security's closing price to
its trading range while smoothing the results using a
simple moving average (SMA).

https://www.investopedia.com/terms/r/relative_vigor_index.asp

Formular

* NUMERATOR= (a+(2×b)+(2×c)+d) / 6
* DENOMINATOR= (e+(2×f)+(2×g)+h) / 6
* RVI= SMA-N of DENOMINATOR / SMA-N of NUMERATOR
* Signal Line = (RVI+(2×i)+(2×j)+k) / 6

where:

* a=Close−Open
* b=Close−Open One Bar Prior to a
* c=Close−Open One Bar Prior to b
* d=Close−Open One Bar Prior to c
* e=High−Low of Bar a
* f=High−Low of Bar b
* g=High−Low of Bar c
* h=High−Low of Bar d
* i=RVI Value One Bar Prior
* j=RVI Value One Bar Prior to i
* k=RVI Value One Bar Prior to j
* N=Minutes/Hours/Days/Weeks/Months

Examples:
* `df['rvgi']` retrieves the RVGI line of window 14
* `df['rvgis']` retrieves the RVGI signal line of window 14
* `df['rvgi_5']` retrieves the RVGI line of window 5
* `df['rvgis_5']` retrieves the RVGI signal line of window 5
  • Loading branch information
jealous committed Jun 24, 2023
1 parent abe781d commit e486d15
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 1 deletion.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ Supported statistics/indicators are:
* LRMA: Linear Regression Moving Average
* ERI: Elder-Ray Index
* FTR: the Gaussian Fisher Transform Price Reversals indicator
* RVGI: Relative Vigor Index

## Installation

Expand Down Expand Up @@ -923,6 +924,42 @@ Examples:
* `df['ftr']` returns the FTR with window 9
* `df['ftr_20']` returns the FTR with window 20

#### [Relative Vigor Index (RVGI)](https://www.investopedia.com/terms/r/relative_vigor_index.asp)

The Relative Vigor Index (RVI) is a momentum indicator
used in technical analysis that measures the strength
of a trend by comparing a security's closing price to
its trading range while smoothing the results using a
simple moving average (SMA).

Formular

* NUMERATOR= (a+(2×b)+(2×c)+d) / 6
* DENOMINATOR= (e+(2×f)+(2×g)+h) / 6
* RVI= SMA-N of DENOMINATOR / SMA-N of NUMERATOR
* Signal Line = (RVI+(2×i)+(2×j)+k) / 6

where:

* a=Close−Open
* b=Close−Open One Bar Prior to a
* c=Close−Open One Bar Prior to b
* d=Close−Open One Bar Prior to c
* e=High−Low of Bar a
* f=High−Low of Bar b
* g=High−Low of Bar c
* h=High−Low of Bar d
* i=RVI Value One Bar Prior
* j=RVI Value One Bar Prior to i
* k=RVI Value One Bar Prior to j
* N=Minutes/Hours/Days/Weeks/Months

Examples:
* `df['rvgi']` retrieves the RVGI line of window 14
* `df['rvgis']` retrieves the RVGI signal line of window 14
* `df['rvgi_5']` retrieves the RVGI line of window 5
* `df['rvgis_5']` retrieves the RVGI signal line of window 5

## Issues

We use [Github Issues](https://github.com/jealous/stockstats/issues) to track
Expand Down
67 changes: 66 additions & 1 deletion stockstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class StockStatsError(Exception):
'ppo': (12, 26, 9), # short, long, signal
'rsi': 14,
'rsv': 9,
'rvgi': 14,
'stochrsi': 14,
'supertrend': 14,
'tema': 5,
Expand Down Expand Up @@ -198,6 +199,10 @@ def name(self):
return f'{self._name}_{self._windows}'
return f'{self.column}_{self.windows}_{self._name}'

def set_name(self, name: str):
self._name = name
return self

def name_ex(self, ex):
ret = f'{self._name}{ex}'
if self._windows is None:
Expand Down Expand Up @@ -1429,7 +1434,7 @@ def _get_kama(self, meta: _Meta):
last_kama = cur
self[meta.name] = kama

def _ftr(self, window: int):
def _ftr(self, window: int) -> pd.Series:
mp = (self.high + self.low) * 0.5
highest = mp.rolling(window).max()
lowest = mp.rolling(window).min()
Expand Down Expand Up @@ -1470,6 +1475,65 @@ def _get_ftr(self, meta: _Meta):
"""
self[meta.name] = self._ftr(meta.int)

@staticmethod
def sym_wma4(series: pd.Series) -> pd.Series:
arr = np.array([1, 2, 2, 1])
weights = arr / sum(arr)
rolled = series.rolling(arr.size)
ret = rolled.apply(lambda x: np.dot(x, weights), raw=True)
ret.iloc[:arr.size - 1] = 0.0
return ret

def _rvgi(self, window: int) -> pd.Series:
""" Relative Vigor Index (RVGI)
The Relative Vigor Index (RVI) is a momentum indicator
used in technical analysis that measures the strength
of a trend by comparing a security's closing price to
its trading range while smoothing the results using a
simple moving average (SMA).
https://www.investopedia.com/terms/r/relative_vigor_index.asp
Formular
* NUMERATOR= (a+(2×b)+(2×c)+d) / 6
* DENOMINATOR= (e+(2×f)+(2×g)+h) / 6
* RVI= SMA-N of DENOMINATOR / SMA-N of NUMERATOR
* Signal Line = (RVI+(2×i)+(2×j)+k) / 6
where:
* a=Close−Open
* b=Close−Open One Bar Prior to a
* c=Close−Open One Bar Prior to b
* d=Close−Open One Bar Prior to c
* e=High−Low of Bar a
* f=High−Low of Bar b
* g=High−Low of Bar c
* h=High−Low of Bar d
* i=RVI Value One Bar Prior
* j=RVI Value One Bar Prior to i
* k=RVI Value One Bar Prior to j
* N=Minutes/Hours/Days/Weeks/Months
"""
co = self.close - self.open
hl = self.high - self.low

nu = self.sym_wma4(co)
de = self.sym_wma4(hl)
ret = self.sma(nu, window) / self.sma(de, window)
return ret

def _get_rvgis(self, meta: _Meta):
self._get_rvgi(meta.set_name('rvgi'))

def _get_rvgi(self, meta: _Meta):
rvgi = self._rvgi(meta.int)
rvgi.iloc[:3] = 0.0
rvgi_s = self.sym_wma4(rvgi)
rvgi_s.iloc[:6] = 0.0
self[meta.name] = rvgi
self[meta.name_ex('s')] = rvgi_s

@staticmethod
def parse_column_name(name):
m = re.match(r'(.*)_([\d\-+~,.]+)_(\w+)', name)
Expand Down Expand Up @@ -1613,6 +1677,7 @@ def handler(self):
('ker',): self._get_ker,
('eribull', 'eribear'): self._get_eri,
('ftr',): self._get_ftr,
('rvgi', 'rvgis'): self._get_rvgi,
}

def __init_not_exist_column(self, key):
Expand Down
24 changes: 24 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,15 @@ def test_mad_raw():
res = StockDataFrame._mad(series, 6)
assert_that(res[5], near_to(2.667))

@staticmethod
def test_sym_wma4():
series = pd.Series([4, 2, 2, 4, 8])
res = StockDataFrame.sym_wma4(series)
assert_that(res[0], equal_to(0))
assert_that(res[2], equal_to(0))
assert_that(res[3], near_to(2.666))
assert_that(res[4], near_to(3.666))

def test_ichimoku(self):
stock = self.get_stock_90days()
i0 = stock['ichimoku']
Expand Down Expand Up @@ -977,6 +986,21 @@ def test_ftr(self):
f = stock['ftr_15']
assert_that(f[20110128], near_to(-1.005))

def test_rvgi(self):
stock = self.get_stock_30days()
r, s = stock['rvgi'], stock['rvgis']
r14, s14 = stock['rvgi_14'], stock['rvgis_14']
assert_that(r[20110128], equal_to(r14[20110128]))
assert_that(s[20110128], equal_to(s14[20110128]))
assert_that(r[20110106], equal_to(0))
assert_that(r[20110107], near_to(0.257))
assert_that(s[20110111], equal_to(0))
assert_that(s[20110112], near_to(0.303))

s10, r10 = stock['rvgis_10'], stock['rvgi_10']
assert_that(r10[20110128], near_to(-0.056))
assert_that(s10[20110128], near_to(-0.043))

def test_change_group_window_defaults(self):
stock = self.get_stock_90days()
macd = stock['macd']
Expand Down

0 comments on commit e486d15

Please sign in to comment.