Skip to content

Commit

Permalink
[GH-171] Add support for PVO (#172)
Browse files Browse the repository at this point in the history
The Percentage Volume Oscillator (PVO) is a momentum oscillator for volume.
The PVO measures the difference between two volume-based moving averages as
a percentage of the larger moving average.

https://school.stockcharts.com/doku.php?id=technical_indicators:percentage_volume_oscillator_pvo

Formular:

* Percentage Volume Oscillator (PVO):
  ((12-day EMA of Volume - 26-day EMA of Volume)/26-day EMA of Volume) x 100
* Signal Line: 9-day EMA of PVO
* PVO Histogram: PVO - Signal Line
  • Loading branch information
jealous committed Jul 10, 2023
1 parent ea89acb commit 493570b
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 7 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Supported statistics/indicators are:
* KST: Know Sure Thing
* PGO: Pretty Good Oscillator
* PSL: Psychological Line
* PVO: Percentage Volume Oscillator

## Installation

Expand Down Expand Up @@ -1022,6 +1023,27 @@ Example:
* `df['psl_10']` retrieves the PSL with window 10.
* `df['high_12_psl']` retrieves the PSL of high price with window 10.

#### [Percentage Volume Oscillator(PVO)](https://school.stockcharts.com/doku.php?id=technical_indicators:percentage_volume_oscillator_pvo)

The Percentage Volume Oscillator (PVO) is a momentum oscillator for volume.
The PVO measures the difference between two volume-based moving averages as
a percentage of the larger moving average.

Formular:

* Percentage Volume Oscillator (PVO):
((12-day EMA of Volume - 26-day EMA of Volume)/26-day EMA of Volume) x 100
* Signal Line: 9-day EMA of PVO
* PVO Histogram: PVO - Signal Line

Example:
* `df['pvo']` derives from the difference of 2 exponential moving average.
* `df['pvos]` is the signal line.
* `df['pvoh']` is he histogram line.

The period of short, long EMA and signal line can be tuned with
`set_dft_window('pvo', (short, long, signal))`. The default
windows are 12 and 26 and 9.

## Issues

Expand Down
41 changes: 34 additions & 7 deletions stockstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class StockStatsError(Exception):
'pdi': 14,
'pgo': 14,
'ppo': (12, 26, 9), # short, long, signal
'pvo': (12, 26, 9), # short, long, signal
'psl': 12,
'rsi': 14,
'rsv': 9,
Expand Down Expand Up @@ -1120,6 +1121,37 @@ def _get_macd(self, meta: _Meta):
self[macds] = self.ema(self[macd], signal_w)
self[macdh] = self[macd] - self[macds]

def _ppo_and_pvo(self, name: str, col_name: str, meta: _Meta):
volume = self[col_name]
short_w, long_w, signal_w = meta.int0, meta.int1, meta.int2
pvo_short = self.ema(volume, short_w)
pvo_long = self.ema(volume, long_w)
self[name] = (pvo_short - pvo_long) / pvo_long * 100
self[f'{name}s'] = self.ema(self[name], signal_w)
self[f'{name}h'] = self[name] - self[f'{name}s']

def _get_pvo(self, meta: _Meta):
""" Percentage Volume Oscillator
The Percentage Volume Oscillator (PVO) is a momentum oscillator for
volume. The PVO measures the difference between two volume-based
moving averages as a percentage of the larger moving average.
https://school.stockcharts.com/doku.php?id=technical_indicators:percentage_volume_oscillator_pvo
Percentage Volume Oscillator (PVO):
{(12_EOV - 26_EOV)/26_EOV} x 100
Where:
* 12_EOV is the 12-day EMA of Volume
* 26_EOV is the 26-day EMA of Volume
Signal Line: 9-day EMA of PVO
PVO Histogram: PVO - Signal Line
"""
return self._ppo_and_pvo('pvo', 'volume', meta)

def _get_ppo(self, meta: _Meta):
""" Percentage Price Oscillator
Expand All @@ -1132,13 +1164,7 @@ def _get_ppo(self, meta: _Meta):
PPO Histogram: PPO - Signal Line
"""
close = self['close']
short_w, long_w, signal_w = meta.int0, meta.int1, meta.int2
ppo_short = self.ema(close, short_w)
ppo_long = self.ema(close, long_w)
self['ppo'] = (ppo_short - ppo_long) / ppo_long * 100
self['ppos'] = self.ema(self['ppo'], signal_w)
self['ppoh'] = self['ppo'] - self['ppos']
return self._ppo_and_pvo('ppo', 'close', meta)

def _eri(self, window):
ema = self.ema(self['close'], window, adjust=False)
Expand Down Expand Up @@ -1738,6 +1764,7 @@ def handler(self):
('tp',): self._get_tp,
('boll', 'boll_ub', 'boll_lb'): self._get_boll,
('macd', 'macds', 'macdh'): self._get_macd,
('pvo', 'pvos', 'pvoh'): self._get_pvo,
('ppo', 'ppos', 'ppoh'): self._get_ppo,
('cr', 'cr-ma1', 'cr-ma2', 'cr-ma3'): self._get_cr,
('tr',): self._get_tr,
Expand Down
7 changes: 7 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1079,3 +1079,10 @@ def test_psl(self):
high_psl12 = stock['high_12_psl']
assert_that(high_psl12[20110118], near_to(41.666))
assert_that(high_psl12[20110127], near_to(41.666))

def test_pvo(self):
stock = self.get_stock_90days()
_ = stock[['pvo', 'pvos', 'pvoh']]
assert_that(stock['pvo'].loc[20110331], near_to(3.4708))
assert_that(stock['pvos'].loc[20110331], near_to(-3.7464))
assert_that(stock['pvoh'].loc[20110331], near_to(7.2173))

0 comments on commit 493570b

Please sign in to comment.