ENH kvo indicator #280 MAINT refactoring

This commit is contained in:
Kevin Johnson
2021-05-05 09:55:49 -07:00
parent 45b2d8d632
commit 5ed5d6049c
14 changed files with 559 additions and 537 deletions
+13 -10
View File
@@ -42,13 +42,13 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever
* [Candles](#candles-64)
* [Cycles](#cycles-1)
* [Momentum](#momentum-39)
* [Overlap](#overlap-31)
* [Overlap](#overlap-32)
* [Performance](#performance-3)
* [Statistics](#statistics-9)
* [Trend](#trend-16)
* [Utility](#utility-5)
* [Volatility](#volatility-13)
* [Volume](#volume-14)
* [Volatility](#volatility-14)
* [Volume](#volume-15)
* [Performance Metrics](#performance-metrics)
* [Changes](#changes)
* [General](#general)
@@ -97,7 +97,7 @@ $ pip install pandas_ta
Latest Version
--------------
Best choice! Version: *0.2.76b*
Best choice! Version: *0.2.77b*
```sh
$ pip install -U git+https://github.com/twopirllc/pandas-ta
```
@@ -195,7 +195,7 @@ Thanks for using **Pandas TA**!
_Thank you for your contributions!_
[alexonab](https://github.com/alexonab) | [allahyarzadeh](https://github.com/allahyarzadeh) | [codesutras](https://github.com/codesutras) | [DrPaprikaa](https://github.com/DrPaprikaa) | [daikts](https://github.com/daikts) | [dorren](https://github.com/dorren) | [edwardwang1](https://github.com/edwardwang1) | [ffhirata](https://github.com/ffhirata) | [FGU1](https://github.com/FGU1) | [JoeSchr](https://github.com/JoeSchr) | [lluissalord](https://github.com/lluissalord) | [luisbarrancos](https://github.com/luisbarrancos) |[M6stafa](https://github.com/M6stafa) | [maxdignan](https://github.com/maxdignan) | [mchant](https://github.com/mchant) | [moritzgun](https://github.com/moritzgun) | [NkosenhleDuma](https://github.com/NkosenhleDuma) | [pbrumblay](https://github.com/pbrumblay) | [RajeshDhalange](https://github.com/RajeshDhalange) | [rengel8](https://github.com/rengel8) | [rluong003](https://github.com/rluong003) | [SoftDevDanial](https://github.com/SoftDevDanial) | [tg12](https://github.com/tg12) | [twrobel](https://github.com/twrobel) | [whubsch](https://github.com/whubsch) | [witokondoria](https://github.com/witokondoria) | [wouldayajustlookatit](https://github.com/wouldayajustlookatit) | [YuvalWein](https://github.com/YuvalWein)
[alexonab](https://github.com/alexonab) | [allahyarzadeh](https://github.com/allahyarzadeh) | [codesutras](https://github.com/codesutras) | [DrPaprikaa](https://github.com/DrPaprikaa) | [daikts](https://github.com/daikts) | [dorren](https://github.com/dorren) | [edwardwang1](https://github.com/edwardwang1) | [ffhirata](https://github.com/ffhirata) | [FGU1](https://github.com/FGU1) | [GSlinger](https://github.com/gslinger) | [JoeSchr](https://github.com/JoeSchr) | [lluissalord](https://github.com/lluissalord) | [luisbarrancos](https://github.com/luisbarrancos) |[M6stafa](https://github.com/M6stafa) | [maxdignan](https://github.com/maxdignan) | [mchant](https://github.com/mchant) | [moritzgun](https://github.com/moritzgun) | [NkosenhleDuma](https://github.com/NkosenhleDuma) | [pbrumblay](https://github.com/pbrumblay) | [RajeshDhalange](https://github.com/RajeshDhalange) | [rengel8](https://github.com/rengel8) | [rluong003](https://github.com/rluong003) | [SoftDevDanial](https://github.com/SoftDevDanial) | [tg12](https://github.com/tg12) | [twrobel](https://github.com/twrobel) | [whubsch](https://github.com/whubsch) | [witokondoria](https://github.com/witokondoria) | [wouldayajustlookatit](https://github.com/wouldayajustlookatit) | [YuvalWein](https://github.com/YuvalWein)
<br/>
@@ -566,7 +566,7 @@ help(ta.yf)
<br/><br/>
# **Indicators** (_by Category_)
### **Candles** (63)
### **Candles** (64)
Patterns that are **not bold**, require TA-Lib to be installed: ```pip install TA-Lib```
* 2crows
@@ -651,7 +651,7 @@ df = df.ta.cdl_pattern(name=["doji", "inside"])
<br/>
### **Momentum** (38)
### **Momentum** (39)
* _Awesome Oscillator_: **ao**
* _Absolute Price Oscillator_: **apo**
* _Bias_: **bias**
@@ -702,7 +702,7 @@ df = df.ta.cdl_pattern(name=["doji", "inside"])
<br/>
### **Overlap** (31)
### **Overlap** (32)
* _Arnaud Legoux Moving Average_: **alma**
* _Double Exponential Moving Average_: **dema**
@@ -815,13 +815,14 @@ Use parameter: cumulative=**True** for cumulative results.
<br/>
### **Volatility** (13)
### **Volatility** (14)
* _Aberration_: **aberration**
* _Acceleration Bands_: **accbands**
* _Average True Range_: **atr**
* _Bollinger Bands_: **bbands**
* _Donchian Channel_: **donchian**
* _Holt-Winter Channel_: **hwc**
* _Keltner Channel_: **kc**
* _Mass Index_: **massi**
* _Normalized Average True Range_: **natr**
@@ -837,7 +838,7 @@ Use parameter: cumulative=**True** for cumulative results.
<br/>
### **Volume** (14)
### **Volume** (15)
* _Accumulation/Distribution Index_: **ad**
* _Accumulation/Distribution Oscillator_: **adosc**
@@ -845,6 +846,7 @@ Use parameter: cumulative=**True** for cumulative results.
* _Chaikin Money Flow_: **cmf**
* _Elder's Force Index_: **efi**
* _Ease of Movement_: **eom**
* _Klinger Volume Oscillator_: **kvo**
* _Money Flow Index_: **mfi**
* _Negative Volume Index_: **nvi**
* _On-Balance Volume_: **obv**
@@ -907,6 +909,7 @@ trading account, or fund. See: ```help(ta.drawdown)```
* _Candle Z Score_ (**cdl_z**) normalizes OHLC Candles with a rolling Z Score. See: ```help(ta.cdl_z)```
* _Correlation Trend Indicator_ (**cti**) is an oscillator created by John Ehler in 2020. See: ```help(ta.cti)```
* _Even Better Sinewave_ (**ebsw**) measures market cycles and uses a low pass filter to remove noise. See: ```help(ta.ebsw)```
* _Klinger Volume Oscillator_ (**kvo**) was developed by Stephen J. Klinger. It is designed to predict price reversals in a market by comparing volume to price.. See: ```help(ta.kvo)```
* _Schaff Trend Cycle_ (**stc**) is an evolution of the popular MACD incorportating two
cascaded stochastic calculations with additional smoothing. See: ```help(ta.stc)```
* _Tom DeMark's Sequential_ (**td_seq**) attempts to identify a price point where an uptrend or a downtrend exhausts itself and reverses. Currently exlcuded from ```df.ta.strategy()``` for performance reasons. See: ```help(ta.td_seq)```
+145 -145
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+57 -57
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -81,8 +81,8 @@ Category = {
# Volume, "vp" or "Volume Profile" is unique
"volume": [
"ad", "adosc", "aobv", "cmf", "efi", "eom", "mfi", "nvi", "obv", "pvi",
"pvol", "pvr", "pvt"
"ad", "adosc", "aobv", "cmf", "efi", "eom", "kvo", "mfi", "nvi", "obv",
"pvi", "pvol", "pvr", "pvt"
],
}
+8
View File
@@ -1647,6 +1647,14 @@ class AnalysisIndicators(BasePandasObject):
result = eom(high=high, low=low, close=close, volume=volume, length=length, divisor=divisor, offset=offset, drift=drift, **kwargs)
return self._post_process(result, **kwargs)
def kvo(self, fast=None, slow=None, length_sig=None, mamode=None, offset=None, drift=None, **kwargs):
high = self._get_column(kwargs.pop("high", "high"))
low = self._get_column(kwargs.pop("low", "low"))
close = self._get_column(kwargs.pop("close", "close"))
volume = self._get_column(kwargs.pop("volume", "volume"))
result = kvo(high=high, low=low, close=close, volume=volume, fast=fast, slow=slow, length_sig=length_sig, mamode=mamode, offset=offset, drift=drift, **kwargs)
return self._post_process(result, **kwargs)
def mfi(self, length=None, drift=None, offset=None, **kwargs):
high = self._get_column(kwargs.pop("high", "high"))
low = self._get_column(kwargs.pop("low", "low"))
+1 -1
View File
@@ -8,8 +8,8 @@ from .hl2 import hl2
from .hlc3 import hlc3
from .hma import hma
from .hwma import hwma
from .kama import kama
from .ichimoku import ichimoku
from .kama import kama
from .linreg import linreg
from .ma import ma
from .mcgd import mcgd
+1 -1
View File
@@ -87,7 +87,7 @@ def unsigned_differences(series: Series, amount: int = None, **kwargs) -> Series
"""Unsigned Differences
Returns two Series, an unsigned positive and unsigned negative series based
on the differences of the original series. The positive series are only the
increases and the negative series is only the decreases.
increases and the negative series are only the decreases.
Default Example:
series = Series([3, 2, 2, 1, 1, 5, 6, 6, 7, 5, 3]) and returns
+1 -1
View File
@@ -66,7 +66,7 @@ def get_time(exchange: str = "NYSE", full:bool = True, to_string:bool = False) -
s = f"{date}, {exchange_}, {local_}, {doy}"
else:
s = f"{exchange}: {exchange_time}"
s = f"{date}, {exchange}: {exchange_time}"
return s if to_string else print(s)
+1 -1
View File
@@ -7,8 +7,8 @@ from .donchian import donchian
from .hwc import hwc
from .kc import kc
from .massi import massi
from .pdist import pdist
from .natr import natr
from .pdist import pdist
from .rvi import rvi
from .thermo import thermo
from .true_range import true_range
+27 -26
View File
@@ -1,35 +1,38 @@
# -*- coding: utf-8 -*-
from numpy import where as npWhere
from pandas import DataFrame
from pandas_ta.overlap import ma
from pandas_ta.utils import get_offset, verify_series
from pandas_ta.overlap import hlc3, ma
from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series
def kvo(high, low, close, volume, fast=None, slow=None, length_sig=None, mamode=None, offset=None, **kwargs):
def kvo(high, low, close, volume, fast=None, slow=None, length_sig=None, mamode=None, drift=None, offset=None, **kwargs):
"""Indicator: Klinger Volume Oscillator (KVO)"""
# Validate arguments
fast = int(fast) if fast and fast > 0 else 34
slow = int(slow) if slow and slow > 0 else 55
length_sig = int(length_sig) if length_sig and length_sig > 0 else 13
mamode = mamode.lower() if mamode and isinstance(mamode, str) else "ema"
high = verify_series(high, max(fast, slow) + length_sig)
low = verify_series(low, max(fast, slow) + length_sig)
close = verify_series(close, max(fast, slow) + length_sig)
volume = verify_series(volume, max(fast, slow) + length_sig)
_length = max(fast, slow, length_sig)
high = verify_series(high, _length)
low = verify_series(low, _length)
close = verify_series(close, _length)
volume = verify_series(volume, _length)
drift = get_drift(drift)
offset = get_offset(offset)
if high is None or low is None or close is None or volume is None: return
# Calculate Result
mom = (high + low + close).diff(1)
mom = hlc3(high, low, close).diff(drift)
trend = npWhere(mom > 0, 1, 0) + npWhere(mom < 0, -1, 0)
dm = high - low
dm = non_zero_range(high, low)
cm = [0.0] * len(high)
for i in range(1, len(high)):
m = high.size
cm = [0] * m
for i in range(1, m):
cm[i] = (cm[i - 1] + dm[i]) if trend[i] == trend[i - 1] else (dm[i - 1] + dm[i])
vf = volume * trend * abs(dm / cm * 2 - 1) * 100
vf = 100 * volume * trend * abs(2 * dm / cm - 1)
kvo = ma(mamode, vf, length=fast) - ma(mamode, vf, length=slow)
kvo_signal = ma(mamode, kvo, length=length_sig)
@@ -64,8 +67,8 @@ def kvo(high, low, close, volume, fast=None, slow=None, length_sig=None, mamode=
kvo.__doc__ = \
"""Klinger Volume Oscillator (KVO)
This indicator was developed by Stephen J. Klinger. It is designed to predict price reversals in a market
by comparing volume to price.
This indicator was developed by Stephen J. Klinger. It is designed to predict
price reversals in a market by comparing volume to price.
Sources:
https://www.tradingview.com/script/Qnn7ymRK-Klinger-Volume-Oscillator/
@@ -73,20 +76,18 @@ Sources:
Calculation:
Default Inputs:
fast = 34, slow = 55, length_sig = 13.
HLC3 = (h + l + c) / 3
MOM = HLC3t - HLC3t-1
TREND = { 1 if MOM > 0 \
-1 if MOM < 0 \
0 otherwise
DM = h - l
CM = { CMt-1 + DMt if TRENDt == TRENDt-1 \
DMt-1 + DMt otherwise
fast=34, slow=55, length_sig=13, drift=1
MOM = HLC3.diff(drift)
NEG_TREND = -1 if MOM < 0 else 0
POS_TREND = 1 if MOM > 0 else 0
TREND = POS_TREND + NEG_TREND
DM = high - low
CM = [CMt-1 + DMt if TRENDt == TRENDt-1 else DMt-1 + DMt]
vf = 100 * v * TREND * abs(2 * dm / cm - 1)
vf = 100 * volume * TREND * abs(2 * dm / cm - 1)
kvo = ema(vf, fast) - ema(vf, slow)
kvo_signal = ema(kvo, length_sig)
Args:
high (pd.Series): Series of 'high's
@@ -104,5 +105,5 @@ Kwargs:
fill_method (value, optional): Type of fill method
Returns:
pd.DataFrame: kvo and kvo_signal columns.
pd.DataFrame: kvo and kvo_signal columns.
"""
+1 -1
View File
@@ -18,7 +18,7 @@ setup(
"pandas_ta.volatility",
"pandas_ta.volume"
],
version=".".join(("0", "2", "76b")),
version=".".join(("0", "2", "77b")),
description=long_description,
long_description=long_description,
author="Kevin Johnson",
+5
View File
@@ -60,6 +60,11 @@ class TestVolumeExtension(TestCase):
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "EOM_14_100000000")
def test_kvo_ext(self):
self.data.ta.kvo(append=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "KVOSig_13")
def test_mfi_ext(self):
self.data.ta.mfi(append=True)
self.assertIsInstance(self.data, DataFrame)
+5
View File
@@ -89,6 +89,11 @@ class TestVolume(TestCase):
self.assertIsInstance(result, Series)
self.assertEqual(result.name, "EOM_14_100000000")
def test_kvo(self):
result = pandas_ta.kvo(self.high, self.low, self.close, self.volume_)
self.assertIsInstance(result, DataFrame)
self.assertEqual(result.name, "KVO_34_55_13")
def test_mfi(self):
result = pandas_ta.mfi(self.high, self.low, self.close, self.volume_)
self.assertIsInstance(result, Series)