Merge branch 'pr/465' into development

This commit is contained in:
Kevin Johnson
2022-01-24 10:10:09 -08:00
7 changed files with 255 additions and 10 deletions
+15 -7
View File
@@ -59,6 +59,7 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever
* [Overlap](#overlap-36)
* [Performance](#performance-3)
* [Statistics](#statistics-11)
* [Transform](#transform-3)
* [Trend](#trend-19)
* [Utility](#utility-5)
* [Volatility](#volatility-14)
@@ -81,11 +82,11 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever
# **Features**
* Over 140 indicators and utility functions.
* Over 140+ indicators and utility functions.
* **TA Lib** indicators (```pip install ta-lib```).
* TA Lib's 63 Chart Patterns
* Python Indicators are tightly correlated with the _de facto_ [TA Lib](https://github.com/mrjbq7/ta-lib).
* TA Lib computations are by default **enabled**. They can be disabled disabled per indicator by using the argument ```talib=False```.
* TA Lib computations are by default **enabled**. They can be disabled per indicator by using the argument ```talib=False```.
* For example to disable TA Lib calculation for **stdev**: ```ta.stdev(df["close"], length=30, talib=False)```.
* **Stochastic Sample Realizations** with the [stochastic](https://github.com/crflynn/stochastic) package (```pip install stochastic```). See the [Stochastic Samples](#stochastic-samples) section below.
* **External Custom Indicators Directory** independent of the builtin Pandas TA indicators. For more information, see ```import_dir``` documentation under ```/pandas_ta/custom.py```.
@@ -298,7 +299,7 @@ df.ta.study(MyStudy, **kwargs)
<br/>
The _Study_ Class is a simple way to name and group your favorite TA Indicators by using a _Data Class_. **Pandas TA** comes with two prebuilt basic Studies to help you get started: __AllStudy__ and __CommonStudy__. A _Study_ can be as simple as the __CommonStudy__ or as complex as needed using Composition/Chaining.
The _Study_ Class is a simple way to name and group your favorite TA Indicators by using a _Data Class_. **Pandas TA** comes with two prebuilt basic Studies to help you get started: __AllStudy__ and __CommonStudy__. A _Study_ can be as simple as the __CommonStudy__ or as complex as needed using Composition/Chaining.
* When using the _study_ method, **all** indicators will be automatically appended to the DataFrame ```df```.
* You are using a Chained Study when you have the output of one indicator as input into one or more indicators in the same _Study_.
@@ -856,6 +857,14 @@ Use parameter: cumulative=**True** for cumulative results.
<br/>
### **Transform** (3)
* _Cube Transform_: **cube**
* _Inverse Fisher Transform_: **ifisher**
* _ReMap_: **remap**
<br/>
### **Trend** (19)
* _Average Directional Movement Index_: **adx**
@@ -961,7 +970,7 @@ import vectorbt as vbt
df = pd.DataFrame().ta.ticker("AAPL") # requires 'yfinance' installed
# Create the "Golden Cross"
# Create the "Golden Cross"
df["GC"] = df.ta.sma(50, append=True) > df.ta.sma(200, append=True)
# Create boolean Signals(TS_Entries, TS_Exits) for vectorbt
@@ -1005,7 +1014,7 @@ result = ta.cagr(df.close)
# **Stochastic Samples** &nbsp; _BETA_
Pandas TA can utilize the [stochastic](https://github.com/crflynn/stochastic) package (```pip install stochastic```) to Generate Sample Processes. For arguments and features, see ```help(ta.sample)```
In short, when you create a Stochastic Sample,
In short, when you create a Stochastic Sample,
```python
# Returns a Sample Realization Object
@@ -1091,11 +1100,10 @@ help(ta.sample)
# **Support**
Feeling generous, like the package or want to see it become more a mature package?
* Donations help cover data and API costs so platform indicataors (like [TradingView](https://github.com/tradingview/)) are accurate.
* Donations help cover data and API costs so platform indicators (like [TradingView](https://github.com/tradingview/)) are accurate.
* I appreciate **ALL** of those that have bought me Coffee/Beer/Wine et al. I greatly appreciate it! 😎
<br/>
### Consider
[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/twopirllc)
+2 -1
View File
@@ -2,6 +2,7 @@ name = "pandas_ta"
"""
.. moduleauthor:: Kevin Johnson
"""
# Dictionaries and version
from pandas_ta.maps import EXCHANGE_TZ, RATE, Category, Imports, version
from pandas_ta.utils import *
@@ -28,4 +29,4 @@ from pandas_ta.custom import create_dir, import_dir
# Empty DataFrame Alias. Example:
# >> ta.df.ta.ticker("spy")
df = DataFrame()
df = DataFrame()
+17 -2
View File
@@ -12,9 +12,8 @@ from pandas.core.base import PandasObject
from pandas.errors import PerformanceWarning
from pandas import DataFrame, Series
from pandas_ta import *
from pandas_ta.utils import *
# from pandas_ta.utils import *
# Base Class for extending a Pandas DataFrame
@@ -1428,6 +1427,22 @@ class AnalysisIndicators(BasePandasObject):
result = zscore(close=close, length=length, std=std, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
# Transform
def cube(self, cubing_exponent=None, signal_offset=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = cube(close=close, cubing_exponent=cubing_exponent, signal_offset=signal_offset, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def ifisher(self, amplifying_factor=None, signal_offset=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = ifisher(close=close, amplifying_factor=amplifying_factor, signal_offset=signal_offset, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def remap(self, from_min=None, from_max=None, to_min=None, to_max=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = remap(close=close, from_min=from_min, from_max=from_max, to_min=to_min, to_max=to_max, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
# Trend
def adx(self, length=None, lensig=None, mamode=None, scalar=None, drift=None, offset=None, **kwargs):
high = self._get_column(kwargs.pop("high", "high"))
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from .cube import cube
from .ifisher import ifisher
from .remap import remap
+71
View File
@@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta.utils import get_offset, verify_series
def cube(close: Series, cubing_exponent: float = None, signal_offset: int = None, offset: int = None, **kwargs) -> DataFrame:
"""
Indicator: Cube Transform
John Ehlers describes this indicator to be useful in compressing signals near zero for a normalized oscillator
like the Inverse Fisher Transform. In conjunction to that, values close to -1 and 1 are nearly unchanged,
whereas the ones near zero are reduced regarding their amplitude.
From the input data the effects of spectral dilation should have been removed (i.e. roofing filter).
Sources:
Book: Cycle Analytics for Traders, 2014, written by John Ehlers, page 200
Implemented by rengel8 for Pandas TA based on code of Markus K. (cryptocoinserver)
Args:
close (pd.Series): Series of 'close's
cubing_exponent (float): Use this exponent 'wisely' to increase the impact of the soft limiter. Default: 3
signal_offset (int): Offset the signal line. Default: -1
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
fill_method (value, optional): Type of fill method
Returns:
pd.DataFrame: New feature generated.
"""
# Validate arguments
close = verify_series(close)
cubing_exponent = float(cubing_exponent) if cubing_exponent and cubing_exponent >= 3.0 else 3.0
signal_offset = int(signal_offset) if signal_offset and signal_offset > 0 else 1
offset = get_offset(offset)
# Calculate Result
result = close ** cubing_exponent
cube_transform = Series(result, index=close.index)
cube_transform_signal = Series(result, index=close.index)
# Offset
if offset != 0:
cube_transform = cube_transform.shift(offset)
cube_transform_signal = cube_transform_signal.shift(offset)
if signal_offset != 0:
cube_transform_signal = cube_transform_signal.shift(signal_offset)
# Handle fills
if "fillna" in kwargs:
cube_transform.fillna(kwargs["fillna"], inplace=True)
cube_transform_signal.fillna(kwargs["fillna"], inplace=True)
if "fill_method" in kwargs:
cube_transform.fillna(method=kwargs["fill_method"], inplace=True)
cube_transform_signal.fillna(method=kwargs["fill_method"], inplace=True)
# Name and Categorize it
cube_transform.name = f"CUBE"
cube_transform_signal.name = f"CUBE_SIGNAL"
cube_transform.category = cube_transform_signal.category = "transform"
# Prepare DataFrame to return
data = {cube_transform.name: cube_transform, cube_transform_signal.name: cube_transform_signal}
df = DataFrame(data)
df.name = f"CUBE_TRANSFORM"
df.category = cube_transform.category
return df
+81
View File
@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
from numpy import exp as npExp
from pandas import DataFrame, Series
from pandas_ta.utils import get_offset, verify_series
def ifisher(close: Series, amplifying_factor: float = None, signal_offset: int = None, offset: int = None,
**kwargs) -> DataFrame:
"""
Indicator: Inverse Fisher Transform
John Ehlers describes this indicator as a tool to change the "Probability Distribution Function (PDF)" for
the results of known oscillator-indicators (time series) to receive clearer signals.
Its input needs to be normalized into the range from -1 to 1. Input data in the range of -0.5 to 0.5
would not have a significant impact. Ehlers note's as an important fact that larger values will be transformed
or compressed stronger to the underlying unity of -1 to 1.
Preparation Examples (or use 'remap'-indicator for this preparation):
(RSI - 50) * 0.1 RSI [0 to 100] -> -5 to 5
(RSI - 50) * 0.02 RSI [0 to 100] -> -1 to 1, use amplifying_factor of 5 to match input of example above
Sources:
https://www.mesasoftware.com/papers/TheInverseFisherTransform.pdf,
Book: Cycle Analytics for Traders, 2014, written by John Ehlers, page 198
Implemented by rengel8 for Pandas TA based on code of Markus K. (cryptocoinserver)
Args:
close (pd.Series): Series of 'close's
amplifying_factor (float): Use this factor to increase the impact of the soft limiter. Default: 1
signal_offset (int): Offset the signal line. Default: -1
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
fill_method (value, optional): Type of fill method
Returns:
pd.DataFrame: New feature generated.
"""
# Validate arguments
close = verify_series(close)
amplifying_factor = float(amplifying_factor) if amplifying_factor and amplifying_factor != 0 else 1.0
signal_offset = int(signal_offset) if signal_offset and signal_offset > 0 else 1
offset = get_offset(offset)
# Calculate Result
series = close.to_numpy()
result = (npExp(amplifying_factor * series) - 1) / (npExp(amplifying_factor * series) + 1)
# Series
inv_fisher = Series(result, index=close.index)
inv_fisher_signal = Series(result, index=close.index)
# Offset
if offset != 0:
inv_fisher = inv_fisher.shift(offset)
inv_fisher_signal = inv_fisher_signal.shift(offset)
if signal_offset != 0:
inv_fisher_signal = inv_fisher_signal.shift(signal_offset) # !!!!
# Handle fills
if "fillna" in kwargs:
inv_fisher.fillna(kwargs["fillna"], inplace=True)
inv_fisher_signal.fillna(kwargs["fillna"], inplace=True)
if "fill_method" in kwargs:
inv_fisher.fillna(method=kwargs["fill_method"], inplace=True)
inv_fisher_signal.fillna(method=kwargs["fill_method"], inplace=True)
# Name and Categorize it
inv_fisher.name = f"INV_FISHER"
inv_fisher_signal.name = f"INV_FISHER_SIGNAL"
inv_fisher.category = inv_fisher_signal.category = "transform"
# Prepare DataFrame to return
data = {inv_fisher.name: inv_fisher, inv_fisher_signal.name: inv_fisher_signal}
df = DataFrame(data)
df.name = f"INVERSE_FISHER_TRANSFORM"
df.category = inv_fisher.category
return df
+65
View File
@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta.utils import get_offset, verify_series
def remap(close: Series, from_min: float = None, from_max: float = None, to_min: float = None, to_max: float = None,
offset: int = None, **kwargs) -> Series:
"""
Indicator: ReMap (REMAP)
Basically a static normalizer, which maps the input min and max to a given output range. Many range bound
oscillators move between 0 and 100, but there are also other variants. Refer to the example below or add more the
list.
Examples:
RSI -> IFISHER from_min=0, from_max=100, to_min=-1, to_max=1.0
Sources:
rengel8 for Pandas TA
Args:
close (pd.Series): Series of 'close's
from_min (float): Input minimum. Default: 0
from_max (float): Input maximum. Default: 100
to_min (float): Output minimum. Default: 0
to_max (float): Output maximum. Default: 100
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
fill_method (value, optional): Type of fill method
Returns:
pd.Series: New feature generated.
"""
# Validate arguments
close = verify_series(close)
from_min = float(from_min) if from_min and from_min != 0.0 else 0.0
from_max = float(from_max) if from_max and from_max != 0.0 else 100.0
to_min = float(to_min) if to_min and to_min != 0.0 else -1.0
to_max = float(to_max) if to_max and to_max != 0.0 else 1.0
offset = get_offset(offset)
# Calculate Result
result = ((close - from_min) / (from_max - from_min)) * (to_max - to_min) + to_min
# get Series
result = Series(result, index=close.index)
# Offset
if offset != 0:
result = result.shift(offset)
# Handle fills
if "fillna" in kwargs:
result.fillna(kwargs["fillna"], inplace=True)
if "fill_method" in kwargs:
result.fillna(method=kwargs["fill_method"], inplace=True)
# Name and Categorize it
result.name = f"REMAP_{from_min}_{from_max}_{to_min}_{to_max}"
result.category = "transform"
return result