mirror of
https://github.com/wassname/pandas-ta.git
synced 2026-06-27 16:10:07 +08:00
ENH kvo indicator #280 MAINT refactoring
This commit is contained in:
@@ -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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+57
-57
File diff suppressed because one or more lines are too long
@@ -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"
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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.
|
||||
"""
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user