ENH #262 cdl_z and cti DOC update MAINT refactoring

This commit is contained in:
Kevin Johnson
2021-04-17 08:42:03 -07:00
parent 681234648a
commit 4a5e72235d
16 changed files with 176 additions and 8709 deletions
+8 -3
View File
@@ -39,9 +39,9 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever
* [DataFrame Properties](#dataframe-properties)
* [DataFrame Methods](#dataframe-methods)
* [Indicators by Category](#indicators-by-category)
* [Candles](#candles-63)
* [Candles](#candles-64)
* [Cycles](#cycles-1)
* [Momentum](#momentum-38)
* [Momentum](#momentum-39)
* [Overlap](#overlap-31)
* [Performance](#performance-4)
* [Statistics](#statistics-9)
@@ -97,7 +97,7 @@ $ pip install pandas_ta
Latest Version
--------------
Best choice! Version: *0.2.69b*
Best choice! Version: *0.2.70b*
```sh
$ pip install -U git+https://github.com/twopirllc/pandas-ta
```
@@ -632,6 +632,7 @@ Patterns that are **not bold**, require TA-Lib to be installed: ```pip install T
* upsidegap2crows
* xsidegap3methods
* _Heikin-Ashi_: **ha**
* _Z Score_: **cdl_z**
```python
# Get all candle patterns (This is the default behaviour)
df = df.ta.cdl_pattern(name="all")
@@ -668,6 +669,8 @@ df.ta.cdl(["doji", "inside"], append=True)
* _Center of Gravity_: **cg**
* _Chande Momentum Oscillator_: **cmo**
* _Coppock Curve_: **coppock**
* _Correlation Trend Indicator_: **cti**
* A wrapper for ```ta.linreg(series, r=True)```
* _Efficiency Ratio_: **er**
* _Elder Ray Index_: **eri**
* _Fisher Transform_: **fisher**
@@ -907,6 +910,8 @@ result = ta.cagr(df.close)
* _Arnaud Legoux Moving Average_ (**alma**) uses the curve of the Normal (Gauss) distribution to allow regulating the smoothness and high sensitivity of the indicator. See: ```help(ta.alma)```
trading account, or fund. See: ```help(ta.drawdown)```
* _Candle Patterns_ (**cdl_pattern**) If TA Lib is installed, then all those Candle Patterns are available. See the list and examples above on how to call the patterns. See: ```help(ta.cdl_pattern)```
* _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)```
* _Schaff Trend Cycle_ (**stc**) is an evolution of the popular MACD incorportating two
cascaded stochastic calculations with additional smoothing. See: ```help(ta.stc)```
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -40,17 +40,17 @@ Imports = {
Category = {
# Candles
"candles": [
"cdl", "cdl_pattern", "ha"
"cdl", "cdl_pattern", "cdl_z", "ha"
],
# Cycles
"cycles": ["ebsw"],
# Momentum
"momentum": [
"ao", "apo", "bias", "bop", "brar", "cci", "cfo", "cg", "cmo",
"coppock", "er", "eri", "fisher", "inertia", "kdj", "kst", "macd",
"coppock", "cti", "er", "eri", "fisher", "inertia", "kdj", "kst", "macd",
"mom", "pgo", "ppo", "psl", "pvo", "qqe", "roc", "rsi", "rsx", "rvgi",
"slope", "smi", "squeeze", "stc", "stoch", "stochrsi", "td_seq", "trix", "tsi", "uo",
"willr"
"slope", "smi", "squeeze", "stc", "stoch", "stochrsi", "td_seq", "trix",
"tsi", "uo", "willr"
],
# Overlap
"overlap": [
+2 -1
View File
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from .ha import ha
from .cdl_doji import cdl_doji
from .cdl_inside import cdl_inside
from .cdl_pattern import cdl_pattern, cdl, ALL_PATTERNS as CDL_PATTERN_NAMES
from .cdl_z import cdl_z
from .ha import ha
+92
View File
@@ -0,0 +1,92 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame
from pandas_ta.statistics import zscore
from pandas_ta.utils import get_offset, verify_series
def cdl_z(open_, high, low, close, length=None, full=None, ddof=None, offset=None, **kwargs):
"""Candle Type: Z Score"""
# Validate Arguments
length = int(length) if length and length > 0 else 30
ddof = int(ddof) if ddof and ddof >= 0 and ddof < length else 1
open_ = verify_series(open_, length)
high = verify_series(high, length)
low = verify_series(low, length)
close = verify_series(close, length)
offset = get_offset(offset)
full = bool(full) if full is not None and full else False
if open_ is None or high is None or low is None or close is None: return
# Calculate Result
if full:
length = close.size
z_open = zscore(open_, length=length, ddof=ddof)
z_high = zscore(high, length=length, ddof=ddof)
z_low = zscore(low, length=length, ddof=ddof)
z_close = zscore(close, length=length, ddof=ddof)
_full = "a" if full else ""
_props = _full if full else f"_{length}_{ddof}"
df = DataFrame({
f"open_Z{_props}": z_open,
f"high_Z{_props}": z_high,
f"low_Z{_props}": z_low,
f"close_Z{_props}": z_close,
})
if full:
df.fillna(method="backfill", axis=0, inplace=True)
# Offset
if offset != 0:
df = df.shift(offset)
# Handle fills
if "fillna" in kwargs:
df.fillna(kwargs["fillna"], inplace=True)
if "fill_method" in kwargs:
df.fillna(method=kwargs["fill_method"], inplace=True)
# Name and Categorize it
df.name = f"CDL_Z{_props}"
df.category = "candles"
return df
cdl_z.__doc__ = \
"""Candle Type: Z
Normalizes OHLC Candles with a rolling Z Score.
Source: Kevin Johnson
Calculation:
Default values:
length=30, full=False, ddof=1
Z = ZSCORE
open = Z( open, length, ddof)
high = Z( high, length, ddof)
low = Z( low, length, ddof)
close = Z(close, length, ddof)
Args:
open_ (pd.Series): Series of 'open's
high (pd.Series): Series of 'high's
low (pd.Series): Series of 'low's
close (pd.Series): Series of 'close's
length (int): The period. Default: 10
Kwargs:
naive (bool, optional): If True, prefills potential Doji less than
the length if less than a percentage of it's high-low range.
Default: False
fillna (value, optional): pd.DataFrame.fillna(value)
fill_method (value, optional): Type of fill method
Returns:
pd.Series: CDL_DOJI column.
"""
+14 -1
View File
@@ -842,7 +842,15 @@ class AnalysisIndicators(BasePandasObject):
result = cdl_pattern(open_=open_, high=high, low=low, close=close, name=name, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
cdl = cdl_pattern
cdl = cdl_pattern # Alias for cdl_pattern
def cdl_z(self, full=None, offset=None, **kwargs):
open_ = self._get_column(kwargs.pop("open", "open"))
high = self._get_column(kwargs.pop("high", "high"))
low = self._get_column(kwargs.pop("low", "low"))
close = self._get_column(kwargs.pop("close", "close"))
result = cdl_z(open_=open_, high=high, low=low, close=close, full=full, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def ha(self, offset=None, **kwargs):
open_ = self._get_column(kwargs.pop("open", "open"))
@@ -918,6 +926,11 @@ class AnalysisIndicators(BasePandasObject):
result = coppock(close=close, length=length, fast=fast, slow=slow, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def cti(self, length=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = cti(close=close, length=length, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def er(self, length=None, drift=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = er(close=close, length=length, drift=drift, offset=offset, **kwargs)
+23 -47
View File
@@ -1,70 +1,46 @@
import pandas as pd
import numpy as np
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta.overlap import linreg
from pandas_ta.utils import get_offset, verify_series
def cti(close: pd.Series, length: int, offset=None, **kwargs) -> pd.Series:
def cti(close, length=None, offset=None, **kwargs) -> Series:
"""Indicator: Correlation Trend Indicator"""
close = verify_series(close)
length = int(length) if length and length > 0 else 12
close = verify_series(close, length)
offset = get_offset(offset)
def _cti(series: pd.Series) -> float:
"""
Provide cell CTI value for numpy strides.
if close is None: return
Args:
series (pd.Series): Rolling window of pd.Series.
Returns:
float: Value for cell.
"""
r = np.arange(0, length)
sx = sum(series)
sy = -sum(r)
sxx = sum(np.square(series))
sxy = sum(series * r * -1)
syy = sum(r ** 2)
x_denom = length * sxx - sx ** 2
y_denom = length * syy - sy ** 2
if x_denom > 0 and y_denom > 0:
return ((length * sxy - sx * sy) / (x_denom * y_denom) ** 0.5) * -1
return 0
values = [
_cti(each)
for each in np.lib.stride_tricks.sliding_window_view(np.array(close), length)
]
cti_ds = pd.Series([np.NaN] * (length - 1) + values)
cti_ds.index = close.index
cti = linreg(close, length=length, r=True)
# Offset
if offset != 0:
cti_ds = cti_ds.shift(offset)
cti = cti.shift(offset)
# Handle fills
if "fillna" in kwargs:
cti_ds.fillna(method=kwargs["fillna"], inplace=True)
cti.fillna(method=kwargs["fillna"], inplace=True)
if "fill_method" in kwargs:
cti_ds.fillna(method=kwargs["fill_method"], inplace=True)
cti.fillna(method=kwargs["fill_method"], inplace=True)
cti_ds.name = f"CTI_{length}"
cti_ds.category = "momentum"
return cti_ds
cti.name = f"CTI_{length}"
cti.category = "momentum"
return cti
cti.__doc__ = """
The Correlation Trend Indicator is an oscillating technical indicator created
by John Ehler in 2020. Assigns a value depending on how close prices in that
range are to following a positively- or negatively-sloping straight line.
Values range from -1 to 1.
cti.__doc__ = \
"""Correlation Trend Indicator (CTI)
The Correlation Trend Indicator is an oscillator created by John Ehler in 2020.
It assigns a value depending on how close prices in that range are to following
a positively- or negatively-sloping straight line. Values range from -1 to 1.
This is a wrapper for ta.linreg(close, r=True).
Args:
close (pd.Series): The dataseries of close prices for the selected instrument.
length (int): The window to be taking values from for the indicator. Default is 12.
offset ([type], optional): If there is an offset of the series to be applied.
Default is None.
close (pd.Series): Series of 'close's
length (int): It's period. Default: 12
offset (int): How many periods to offset the result. Default: 0
Returns:
pd.Series: Series of the CTI values for the given period.
+8 -3
View File
@@ -1,7 +1,10 @@
# -*- coding: utf-8 -*-
from numpy import array as npArray
from numpy import arctan as npAtan
from numpy import NaN as npNaN
from numpy import pi as npPi
from numpy import sqrt as npSqrt
from numpy.lib.stride_tricks import sliding_window_view
from pandas import Series
from pandas_ta.utils import get_offset, verify_series
@@ -46,12 +49,13 @@ def linreg(close, length=None, offset=None, **kwargs):
if r:
y2_sum = (series * series).sum()
rn = length * xy_sum - x_sum * y_sum
rd = npSqrt(divisor * (length * y2_sum - y_sum * y_sum))
rd = (divisor * (length * y2_sum - y_sum * y_sum)) ** 0.5
return rn / rd
return m * length + b if tsf else m * (length - 1) + b
linreg = close.rolling(length, min_periods=length).apply(linear_regression, raw=False)
linreg_ = [linear_regression(_) for _ in sliding_window_view(npArray(close), length)]
linreg = Series([npNaN] * (length - 1) + linreg_, index=close.index)
# Offset
if offset != 0:
@@ -69,6 +73,7 @@ def linreg(close, length=None, offset=None, **kwargs):
if intercept: linreg.name += "b"
if angle: linreg.name += "a"
if r: linreg.name += "r"
linreg.name += f"_{length}"
linreg.category = "overlap"
+2 -2
View File
@@ -4,11 +4,11 @@ from .variance import variance
from pandas_ta.utils import get_offset, verify_series
def stdev(close, length=None, ddof=1, offset=None, **kwargs):
def stdev(close, length=None, ddof=None, offset=None, **kwargs):
"""Indicator: Standard Deviation"""
# Validate Arguments
length = int(length) if length and length > 0 else 30
ddof = int(ddof) if ddof >= 0 and ddof < length else 1
ddof = int(ddof) if ddof and ddof >= 0 and ddof < length else 1
close = verify_series(close, length)
offset = get_offset(offset)
+1 -13
View File
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
from packaging import version
from pandas import DataFrame
from pandas_ta import Imports, RATE, version
from ._core import _camelCase2Title
@@ -134,17 +133,6 @@ def yf(ticker: str, **kwargs):
except KeyError as ke:
print(f"[X] Ticker '{ticker}' not found.")
return
# print(f"[X] ticker_info[{type(ticker_info)}:{len(ticker_info.keys())}]\n{ticker_info}\n")
try:
infodf = DataFrame.from_dict(ticker_info, orient="index")
except TypeError as te:
print(f"[X] TypeError: {te}")
# print(f"[X] infodf.empty: {infodf.empty}")
if infodf.empty: return
# print(f"[X] infodf[{type(infodf)}:{len(infodf.keys())}]\n{infodf}\n")
infodf.name, infodf.columns = ticker, [ticker]
# Dividends and Splits
dividends, splits = yfd.splits, yfd.dividends
@@ -156,7 +144,7 @@ def yf(ticker: str, **kwargs):
print("\n==== Company Information " + div)
print(f"{ticker_info['longName']} ({ticker_info['shortName']}) [{ticker_info['symbol']}]")
print(f"[i] {type(ticker_info['longBusinessSummary'])}: {ticker_info['longBusinessSummary']}")
if description:
print(f"{ticker_info['longBusinessSummary']}\n")
if "address1" in ticker_info and len(ticker_info["address1"]):
+1 -1
View File
@@ -3,7 +3,7 @@ from datetime import datetime
from time import localtime, perf_counter
from typing import Tuple
from pandas import DataFrame, DatetimeIndex, Timestamp
from pandas import DataFrame, Timestamp
from pandas_ta import EXCHANGE_TZ, RATE
from pandas_ta.utils import verify_series
+1 -1
View File
@@ -18,7 +18,7 @@ setup(
"pandas_ta.volatility",
"pandas_ta.volume"
],
version=".".join(("0", "2", "69b")),
version=".".join(("0", "2", "70b")),
description=long_description,
long_description=long_description,
author="Kevin Johnson",
+5
View File
@@ -28,6 +28,11 @@ class TestCandleExtension(TestCase):
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "CDL_INSIDE")
def test_cdl_z_ext(self):
self.data.ta.cdl_z(append=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(list(self.data.columns[-4:]), ["open_Z_30_1", "high_Z_30_1", "low_Z_30_1", "close_Z_30_1"])
def test_ha_ext(self):
self.data.ta.ha(append=True)
self.assertIsInstance(self.data, DataFrame)
+5
View File
@@ -68,6 +68,11 @@ class TestMomentumExtension(TestCase):
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "COPC_11_14_10")
def test_cti_ext(self):
self.data.ta.cti(append=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "CTI_12")
def test_er_ext(self):
self.data.ta.er(append=True)
self.assertIsInstance(self.data, DataFrame)
+5
View File
@@ -73,3 +73,8 @@ class TestCandle(TestCase):
result = pandas_ta.cdl_inside(self.open, self.high, self.low, self.close, asbool=True)
self.assertIsInstance(result, Series)
self.assertEqual(result.name, "CDL_INSIDE")
def test_cdl_z(self):
result = pandas_ta.cdl_z(self.open, self.high, self.low, self.close)
self.assertIsInstance(result, DataFrame)
self.assertEqual(result.name, "CDL_Z_30_1")
+5
View File
@@ -149,6 +149,11 @@ class TestMomentum(TestCase):
self.assertIsInstance(result, Series)
self.assertEqual(result.name, "COPC_11_14_10")
def test_cti(self):
result = pandas_ta.cti(self.close)
self.assertIsInstance(result, Series)
self.assertEqual(result.name, "CTI_12")
def test_er(self):
result = pandas_ta.er(self.close)
self.assertIsInstance(result, Series)