ENH #164 #162 rolling cont inc/dec check for increasing decreasing DOC update

This commit is contained in:
Kevin Johnson
2020-11-24 10:47:42 -08:00
parent 5ff2af007b
commit a159e5cf99
23 changed files with 280 additions and 132 deletions
+1
View File
@@ -119,6 +119,7 @@ pandas_ta/_wrapper.py
AlphaVantageAPI/
data/datas.csv
data/f500.csv
data/GLD_D_tv.csv
data/SPY_5min.csv
data/SPY_1min.csv
+5 -5
View File
@@ -16,16 +16,16 @@ init:
pip install -r requirements.txt
test_ext:
python -m unittest -v tests/test_ext_indicator_*.py
python -m unittest -v -f tests/test_ext_indicator_*.py
test_metrics:
python -m unittest -v tests/test_utils_metrics.py
python -m unittest -v -f tests/test_utils_metrics.py
test_strats:
python -m unittest -v tests/test_strategy.py
python -m unittest -v -f tests/test_strategy.py
test_ta:
python -m unittest -v tests/test_indicator_*.py
python -m unittest -v -f tests/test_indicator_*.py
test_utils:
python -m unittest -v tests/test_utils.py
python -m unittest -v -f tests/test_utils.py
+53 -29
View File
@@ -39,11 +39,12 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever
* [Utility](#utility-5)
* [Volatility](#volatility-13)
* [Volume](#volume-13)
* [Performance Metrics](#performance-metrics)
* [Changes](#changes)
* [Recent](#recent)
* [Breaking](#breaking)
* [New](#new)
* [Updated](#updated)
* [General](#general)
* [Breaking Indicators](#breaking-indicators)
* [New Indicators](#new-indicators)
* [Updated Indicators](#updated-indicators)
<!--te-->
<!-- * [Specifying Strategies in **Pandas TA**](#specifying-strategies-in-pandas-ta) -->
@@ -56,9 +57,11 @@ _Pandas Technical Analysis_ (**Pandas TA**) is an easy to use library that lever
* Has 120+ indicators and utility functions.
* Indicators are tightly correlated with the de facto [TA Lib](https://mrjbq7.github.io/ta-lib/) if they share common indicators.
* Have the need for speed? By using the _strategy_ method, you get **multiprocessing** for free!
* Have the need for speed? By using the DataFrame _strategy_ method, you get **multiprocessing** for free!
* Easily add _prefixes_ or _suffixes_ or both to columns names. Useful for Custom Chained Strategies.
* Example Jupyter Notebooks under the [examples](https://github.com/twopirllc/pandas-ta/tree/master/examples) directory, including how to create Custom Strategies using the new [__Strategy__ Class](https://github.com/twopirllc/pandas-ta/tree/master/examples/PandaTA_Strategy_Examples.ipynb)
* **NEW** Performance Metrics
<!-- * **Performance Metrics** These metrics return a _float_ and are _not_ part of the _DataFrame_ Extension. They are called conventionally. Included Metrics: **cagr**, **calmar_ratio**, **downside_deviation**, **jensens_alpha**, **log_max_drawdown**, **max_drawdown**, **pure_profit_score**, **sharpe_ratio**, **sortino_ratio**, **volatility**. Example: -->
<br/>
@@ -160,7 +163,7 @@ Thanks for trying **Pandas TA**!
_Thank you for your contributions!_
[alexonab](https://github.com/alexonab) | [allahyarzadeh](https://github.com/allahyarzadeh) | [codesutras](https://github.com/codesutras) | [daikts](https://github.com/daikts) | [DrPaprikaa](https://github.com/DrPaprikaa) | [FGU1](https://github.com/FGU1) | [lluissalord](https://github.com/lluissalord) | [maxdignan](https://github.com/maxdignan) | [NkosenhleDuma](https://github.com/NkosenhleDuma) | [pbrumblay](https://github.com/pbrumblay) | [rluong003](https://github.com/rluong003) | [SoftDevDanial](https://github.com/SoftDevDanial) | [tg12](https://github.com/tg12) | [YuvalWein](https://github.com/YuvalWein)
[alexonab](https://github.com/alexonab) | [allahyarzadeh](https://github.com/allahyarzadeh) | [codesutras](https://github.com/codesutras) | [daikts](https://github.com/daikts) | [DrPaprikaa](https://github.com/DrPaprikaa) | [FGU1](https://github.com/FGU1) | [lluissalord](https://github.com/lluissalord) | [maxdignan](https://github.com/maxdignan) | [NkosenhleDuma](https://github.com/NkosenhleDuma) | [pbrumblay](https://github.com/pbrumblay) | [RajeshDhalange](https://github.com/RajeshDhalange) | [rluong003](https://github.com/rluong003) | [SoftDevDanial](https://github.com/SoftDevDanial) | [tg12](https://github.com/tg12) | [YuvalWein](https://github.com/YuvalWein)
<br/>
@@ -174,21 +177,21 @@ _Conventional_
You explicitly define the input columns and take care of the output.
* ```sma10 = ta.sma(df["Close"], length=10)```
* Returns a series with name: ```SMA_10```
* Returns a Series with name: ```SMA_10```
* ```donchiandf = ta.donchian(df["HIGH"], df["low"], lower_length=10, upper_length=15)```
* Returns a DataFrame named ```DC_10_15``` and column names: ```DCL_10_15, DCM_10_15, DCU_10_15```
* ```ema10_ohlc4 = ta.ema(ta.ohlc4(df["Open"], df["High"], df["Low"], df["Close"]), length=10)```
* Conventional Chaining is possible but more explicit.
* Since it returns a series named ```EMA_10```. If needed, you may need to uniquely name it.
* Since it returns a Series named ```EMA_10```. If needed, you may need to uniquely name it.
_Pandas TA DataFrame Extension_
====================
Calling ```df.ta``` will automatically lowercase _OHLCVA_ to _ohlcva_: _open, high, low, close, volume_, _adj_close_. By default, ```df.ta``` will use the _ohlcva_ for the indicator arguments removing the need to specify input columns directly.
* ```sma10 = df.ta.sma(length=10)```
* Returns a series with name: ```SMA_10```
* Returns a Series with name: ```SMA_10```
* ```ema10_ohlc4 = df.ta.ema(close=df.ta.ohlc4(), length=10, suffix="OHLC4")```
* Returns a series with name: ```EMA_10_OHLC4```
* Returns a Series with name: ```EMA_10_OHLC4```
* Chaining Indicators _require_ specifying the input like: ```close=df.ta.ohlc4()```.
* ```donchiandf = df.ta.donchian(lower_length=10, upper_length=15)```
* Returns a DataFrame named ```DC_10_15``` and column names: ```DCL_10_15, DCM_10_15, DCU_10_15```
@@ -356,18 +359,18 @@ df.ta.strategy(NonMPStrategy)
## **adjusted**
```python
# Set ta to default to an adjusted column, 'adj_close', overriding default 'close'
# Set ta to default to an adjusted column, 'adj_close', overriding default 'close'.
df.ta.adjusted = "adj_close"
df.ta.sma(length=10, append=True)
# To reset back to 'close', set adjusted back to None
# To reset back to 'close', set adjusted back to None.
df.ta.adjusted = None
```
## **categories**
```python
# List of Pandas TA categories
# List of Pandas TA categories.
df.ta.categories
```
@@ -375,7 +378,7 @@ df.ta.categories
```python
# Set the number of cores to use for strategy multiprocessing
# Defaults to the number of cpus you have
# Defaults to the number of cpus you have.
df.ta.cores = 4
# Returns the number of cores you set or your default number of cpus.
@@ -386,36 +389,31 @@ df.ta.cores
```python
# The 'datetime_ordered' property returns True if the DataFrame
# index is of Pandas datetime64 and df.index[0] < df.index[-1]
# Otherwise it returns False
# index is of Pandas datetime64 and df.index[0] < df.index[-1].
# Otherwise it returns False.
df.ta.datetime_ordered
```
## **reverse**
```python
# The 'datetime_ordered' property returns True if the DataFrame
# index is of Pandas datetime64 and df.index[0] < df.index[-1]
# Otherwise it returns False
df.ta.datetime_ordered
# The 'reverse' is a helper property that returns the DataFrame
# in reverse order
# in reverse order.
df.ta.reverse
```
## **prefix & suffix**
```python
# Applying a prefix to the name of an indicator
# Applying a prefix to the name of an indicator.
prehl2 = df.ta.hl2(prefix="pre")
print(prehl2.name) # "pre_HL2"
# Applying a suffix to the name of an indicator
# Applying a suffix to the name of an indicator.
endhl2 = df.ta.hl2(suffix="post")
print(endhl2.name) # "HL2_post"
# Applying a prefix and suffix to the name of an indicator
# Applying a prefix and suffix to the name of an indicator.
bothhl2 = df.ta.hl2(prefix="pre", suffix="post")
print(bothhl2.name) # "pre_HL2_post"
```
@@ -613,22 +611,45 @@ Use parameter: cumulative=**True** for cumulative results.
|:--------:|
| ![Example OBV](/images/SPY_OBV.png) |
<br/><br/>
# **Performance Metrics**
_Performance Metrics_ are a **new** addition to the package. These metrics return a _float_ and are _not_ part of the _DataFrame_ Extension. They are called conventionally. For Example:
```python
import pandas_ta as ta
result = ta.cagr(df.close)
```
### Available Metrics
* _Compounded Annual Growth Rate_: **cagr**
* _Calmar Ratio_: **calmar_ratio**
* _Downside Deviation_: **downside_deviation**
* _Jensen's Alpha_: **jensens_alpha**
* _Log Max Drawdown_: **log_max_drawdown**
* _Max Drawdown_: **max_drawdown**
* _Pure Profit Score_: **pure_profit_score**
* _Sharpe Ratio_: **sharpe_ratio**
* _Sortino Ratio_: **sortino_ratio**
* _Volatility_: **volatility**
<br/><br/>
# **Changes**
## **Recent**
## **General**
* A __Strategy__ Class to help name and group your favorite indicators.
* Some indicators have had their ```mamode``` _kwarg_ updated with more _moving average_ choices with the **Moving Average Utility** function ```ta.ma()```. For simplicity, all _choices_ are single source _moving averages_. This is primarily an internal utility used by indicators that have a ```mamode``` _kwarg_. This includes indicators: _accbands_, _amat_, _aobv_, _atr_, _bbands_, _bias_, _efi_, _hilo_, _kc_, _natr_, _qqe_, _rvi_, and _thermo_; the default ```mamode``` parameters have not changed. However, ```ta.ma()``` can be used by the user as well if needed. For more information: ```help(ta.ma)```
* **Moving Average Choices**: dema, ema, fwma, hma, linreg, midpoint, pwma, rma, sinwma, sma, swma, t3, tema, trima, vidya, wma, zlma.
* An _experimental_ and independent __Watchlist__ Class located in the [Examples](https://github.com/twopirllc/pandas-ta/tree/master/examples/watchlist.py) Directory that can be used in conjunction with the new __Strategy__ Class.
* _Linear Regression_ (**linear_regression**) is a new utility method for Simple Linear Regression using _Numpy_ or _Scikit Learn_'s implementation.
<br />
## **Breaking**
## **Breaking Indicators**
* _Bollinger Bands_ (**bbands**): New column 'bandwidth' appended to the returning DataFrame. See: ```help(ta.bbands)```
## **New**
## **New Indicators**
* _Drawdown_ (**drawdown**) It is a peak-to-trough decline during a specific period for an investment,
trading account, or fund. See: ```help(ta.drawdown)```
* _Gann High-Low Activator_ (**hilo**) The Gann High Low Activator Indicator was created by Robert Krausz in a 1998. See: ```help(ta.hilo)```
@@ -638,10 +659,13 @@ trading account, or fund. See: ```help(ta.drawdown)```
* _TTM Trend_ (**ttm_trend**). A trend indicator inspired from John Carter's book "Mastering the Trade" issue of Stocks & Commodities Magazine. It is a moving average based trend indicator consisting of two different simple moving averages. See: ```help(ta.ttm_trend)```
* _Variable Index Dynamic Average_ (**vidya**) A popular Dynamic Moving Average created by Tushar Chande. See: ```help(ta.vidya)```
## **Updated**
## **Updated Indicators**
* _Average True Range_ (**atr**): The default ```mamode``` is now "**RMA**" and with the same ```mamode``` options as TradingView. See ```help(ta.atr)```.
* _Decreasing_ (**decreasing**): New argument ```strict``` checks if the series is continuously decreasing over period ```length```. Default: ```False```. See ```help(ta.decreasing)```.
* _Increasing_ (**increasing**): New argument ```strict``` checks if the series is continuously increasing over period ```length```. Default: ```False```. See ```help(ta.increasing)```.
* _Trend Return_ (**trend_return**): Returns a DataFrame now instead of Series with pertinenet trade info for a _trend_. An example can be found in the [AI Example Notebook](https://github.com/twopirllc/pandas-ta/tree/master/examples/AIExample.ipynb). The notebook is still a work in progress and open to colloboration.
<br />
# **Sources**
* [Original TA-LIB](http://ta-lib.org/)
+9 -9
View File
@@ -185,17 +185,17 @@ class AnalysisIndicators(BasePandasObject):
>>> ichimoku, span = ta.ichimoku(df["High"], df["Low"], df["Close"])
Args:
kind (str, optional): Default: None. Kind is the 'name' of the indicator.
kind (str, optional): Default: None. Kind is the 'name' of the indicator.
It converts kind to lowercase before calling.
timed (bool, optional): Default: False. Curious about the execution
timed (bool, optional): Default: False. Curious about the execution
speed?
kwargs: Extension specific modifiers.
append (bool, optional): Default: False. When True, it appends the
append (bool, optional): Default: False. When True, it appends the
resultant column(s) to the DataFrame.
Returns:
Most Indicators will return a Pandas Series. Others like MACD, BBANDS,
KC, et al will return a Pandas DataFrame. Ichimoku on the other hand
Most Indicators will return a Pandas Series. Others like MACD, BBANDS,
KC, et al will return a Pandas DataFrame. Ichimoku on the other hand
will return two DataFrames, the Ichimoku DataFrame for the known period
and a Span DataFrame for the future of the Span values.
@@ -1239,9 +1239,9 @@ class AnalysisIndicators(BasePandasObject):
result = decay(close=close, length=length, mode=mode, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def decreasing(self, length=None, asint=True, offset=None, **kwargs):
def decreasing(self, length=None, strict=None, asint=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = decreasing(close=close, length=length, asint=asint, offset=offset, **kwargs)
result = decreasing(close=close, length=length, strict=strict, asint=asint, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def dpo(self, length=None, centered=True, offset=None, **kwargs):
@@ -1249,9 +1249,9 @@ class AnalysisIndicators(BasePandasObject):
result = dpo(close=close, length=length, centered=centered, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def increasing(self, length=None, asint=True, offset=None, **kwargs):
def increasing(self, length=None, strict=None, asint=None, offset=None, **kwargs):
close = self._get_column(kwargs.pop("close", "close"))
result = increasing(close=close, length=length, asint=asint, offset=offset, **kwargs)
result = increasing(close=close, length=length, strict=strict, asint=asint, offset=offset, **kwargs)
return self._post_process(result, **kwargs)
def long_run(self, fast=None, slow=None, length=None, offset=None, **kwargs):
-1
View File
@@ -14,7 +14,6 @@ def ppo(close, fast=None, slow=None, signal=None, scalar=None, offset=None, **kw
scalar = float(scalar) if scalar else 100
if slow < fast:
fast, slow = slow, fast
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else fast
offset = get_offset(offset)
# Calculate Result
-1
View File
@@ -7,7 +7,6 @@ def fwma(close, length=None, asc=None, offset=None, **kwargs):
# Validate Arguments
close = verify_series(close)
length = int(length) if length and length > 0 else 10
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
asc = asc if asc else True
offset = get_offset(offset)
-1
View File
@@ -8,7 +8,6 @@ def linreg(close, length=None, offset=None, **kwargs):
# Validate arguments
close = verify_series(close)
length = int(length) if length and length > 0 else 14
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
offset = get_offset(offset)
angle = kwargs.pop("angle", False)
intercept = kwargs.pop("intercept", False)
+3 -3
View File
@@ -45,9 +45,9 @@ Calculation:
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 10
base (float): Logarithmic Base. Default: 2
offset (int): How many periods to offset the result. Default: 0
length (int): It's period. Default: 10
base (float): Logarithmic Base. Default: 2
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
+2 -2
View File
@@ -36,8 +36,8 @@ Calculation:
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 30
offset (int): How many periods to offset the result. Default: 0
length (int): It's period. Default: 30
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
+2 -2
View File
@@ -41,8 +41,8 @@ Calculation:
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 30
offset (int): How many periods to offset the result. Default: 0
length (int): It's period. Default: 30
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
+2 -2
View File
@@ -39,8 +39,8 @@ Calculation:
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 30
offset (int): How many periods to offset the result. Default: 0
length (int): It's period. Default: 30
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
+3 -3
View File
@@ -37,9 +37,9 @@ Calculation:
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 30
q (float): The quantile. Default: 0.5
offset (int): How many periods to offset the result. Default: 0
length (int): It's period. Default: 30
q (float): The quantile. Default: 0.5
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
+2 -2
View File
@@ -42,8 +42,8 @@ Calculation:
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 30
offset (int): How many periods to offset the result. Default: 0
length (int): It's period. Default: 30
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
+2 -2
View File
@@ -39,11 +39,11 @@ Calculation:
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 30
length (int): It's period. Default: 30
ddof (int): Delta Degrees of Freedom.
The divisor used in calculations is N - ddof,
where N represents the number of elements. Default: 1
offset (int): How many periods to offset the result. Default: 0
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
+2 -2
View File
@@ -38,11 +38,11 @@ Calculation:
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 30
length (int): It's period. Default: 30
ddof (int): Delta Degrees of Freedom.
The divisor used in calculations is N - ddof,
where N represents the number of elements. Default: 1
offset (int): How many periods to offset the result. Default: 0
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
+3 -3
View File
@@ -44,9 +44,9 @@ Calculation:
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 30
std (float): It's period. Default: 1
offset (int): How many periods to offset the result. Default: 0
length (int): It's period. Default: 30
std (float): It's period. Default: 1
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
+23 -13
View File
@@ -2,18 +2,27 @@
from pandas_ta.utils import get_offset, verify_series
def decreasing(close, length=None, asint=True, strictly=False, offset=None, **kwargs):
def decreasing(close, length=None, strict=None, asint=None, offset=None, **kwargs):
"""Indicator: Decreasing"""
# Validate Arguments
close = verify_series(close)
length = int(length) if length and length > 0 else 1
strict = strict if isinstance(strict, bool) else False
asint = asint if isinstance(asint, bool) else True
offset = get_offset(offset)
def stricly_decreasing(series, n):
return all([i > j for i,j in zip(series[-n:], series[1:])])
# Calculate Result
if strictly:
decreasing = all(i > j for i, j in zip(close[-length:], close[1:]))
if strict:
# Returns value as float64? Have to cast to bool
decreasing = close.rolling(length, min_periods=length).apply(stricly_decreasing, args=(length,), raw=False)
decreasing.fillna(0, inplace=True)
decreasing = decreasing.astype(bool)
else:
decreasing = close.diff(length) < 0
if asint:
decreasing = decreasing.astype(int)
@@ -28,7 +37,7 @@ def decreasing(close, length=None, asint=True, strictly=False, offset=None, **kw
decreasing.fillna(method=kwargs["fill_method"], inplace=True)
# Name and Categorize it
decreasing.name = f"DEC_{length}"
decreasing.name = f"{'S' if strict else ''}DEC_{length}"
decreasing.category = "trend"
return decreasing
@@ -37,22 +46,23 @@ def decreasing(close, length=None, asint=True, strictly=False, offset=None, **kw
decreasing.__doc__ = \
"""Decreasing
Returns True or False if the series is decreasing over a periods. By default,
it returns True and False as 1 and 0 respectively with kwarg 'asint'.
Sources:
Returns True if the series is decreasing over a period, False otherwise. If the kwarg 'strict' is True, it returns True if it is continuously decreasing over the period. When using the kwarg 'asint', then it returns 1 for True or 0 for False.
Calculation:
decreasing = close.diff(length) < 0
if strict:
decreasing = all(i > j for i, j in zip(close[-length:], close[1:]))
else:
decreasing = close.diff(length) < 0
if asint:
decreasing = decreasing.astype(int)
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 1
asint (bool): Returns as binary. Default: True
strictly (bool): If True check for strictly continuous decreasing Default: False
offset (int): How many periods to offset the result. Default: 0
length (int): It's period. Default: 1
asint (bool): Returns as binary. Default: True
strict (bool): If True, checks if the series is continuously decreasing over the period. Default: False
offset (int): How many periods to offset the result. Default: 0
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
+41 -31
View File
@@ -2,22 +2,30 @@
from pandas_ta.utils import get_offset, verify_series
def increasing(close, length=None, asint=True, strictly=False, offset=None, **kwargs):
def increasing(close, length=None, strict=None, asint=None, offset=None, **kwargs):
"""Indicator: Increasing"""
# Validate Arguments
close = verify_series(close)
length = int(length) if length and length > 0 else 1
strict = strict if isinstance(strict, bool) else False
asint = asint if isinstance(asint, bool) else True
offset = get_offset(offset)
def stricly_increasing(series, n):
return all([i < j for i,j in zip(series[-n:], series[1:])])
# Calculate Result
if strictly:
increasing = all(i < j for i, j in zip(close[-length:], close[1:]))
if strict:
# Returns value as float64? Have to cast to bool
increasing = close.rolling(length, min_periods=length).apply(stricly_increasing, args=(length,), raw=False)
increasing.fillna(0, inplace=True)
increasing = increasing.astype(bool)
else:
increasing = close.diff(length) > 0
if asint:
increasing = increasing.astype(int)
# Offset
if offset != 0:
increasing = increasing.shift(offset)
@@ -29,35 +37,37 @@ def increasing(close, length=None, asint=True, strictly=False, offset=None, **kw
increasing.fillna(method=kwargs["fill_method"], inplace=True)
# Name and Categorize it
increasing.name = f"INC_{length}"
increasing.name = f"{'S' if strict else ''}INC_{length}"
increasing.category = "trend"
return increasing
increasing.__doc__ = """Increasing
increasing.__doc__ = \
"""Increasing
Returns True or False if the series is increasing over a periods. By default,
it returns True and False as 1 and 0 respectively with kwarg 'asint'.
Sources:
Calculation:
Returns True if the series is increasing over a period, False otherwise. If the kwarg 'strict' is True, it returns True if it is continuously increasing over the period. When using the kwarg 'asint', then it returns 1 for True or 0 for False.
Calculation:
if strict:
increasing = all(i < j for i, j in zip(close[-length:], close[1:]))
else:
increasing = close.diff(length) > 0
if asint:
increasing = increasing.astype(int)
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 1
asint (bool): Returns as binary. Default: True
strictly (bool): If True check for strictly continuous increasing Default: False
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.
"""
if asint:
increasing = increasing.astype(int)
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 1
asint (bool): Returns as binary. Default: True
strict (bool): If True, checks if the series is continuously increasing over the period. Default: False
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.
"""
+99 -20
View File
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from numpy import arange as npArange
# from numpy import arange as npArange
from numpy import log as npLog
from numpy import sqrt as npSqrt
from pandas import DataFrame, Series, Timedelta
@@ -11,15 +11,32 @@ from pandas_ta.performance import drawdown, log_return, percent_return
def cagr(close: Series) -> float:
"""Compounded Annual Growth Rate"""
"""Compounded Annual Growth Rate
Args:
close (pd.Series): Series of 'close's
>>> result = ta.cagr(df.close)
"""
close = verify_series(close)
start, end = close.iloc[0], close.iloc[-1]
return ((end / start) ** (1 / total_time(close))) - 1
def calmar_ratio(close: Series, method: str = "percent", years: int = 3, log: bool = False) -> float:
def calmar_ratio(close: Series, method: str = "percent", years: int = 3) -> float:
"""The Calmar Ratio is the percent Max Drawdown Ratio 'typically' over
the past three years."""
the past three years.
Args:
close (pd.Series): Series of 'close's
method (str): Max DD calculation options: 'dollar', 'percent', 'log'. Default: 'dollar'
years (int): The positive number of years to use. Default: 3
>>> result = ta.calmar_ratio(close, method="percent", years=3)
"""
if years <= 0:
print(f"[!] calmar_ratio 'years' argument must be greater than zero.")
return
close = verify_series(close)
n_years_ago = close.index[-1] - Timedelta(days=365.25 * years)
@@ -28,10 +45,18 @@ def calmar_ratio(close: Series, method: str = "percent", years: int = 3, log: bo
return cagr(close) / max_drawdown(close, method=method)
def downside_deviation(returns: Series, benchmark_rate: float = 0.0, log: bool = False, tf: str = "years") -> float:
def downside_deviation(returns: Series, benchmark_rate: float = 0.0, tf: str = "years") -> float:
"""Downside Deviation for the Sortino ratio.
Benchmark rate is assumed to be annualized. Adjusted according for the
number of periods per year seen in the data."""
number of periods per year seen in the data.
Args:
close (pd.Series): Series of 'close's
benchmark_rate (float): Benchmark Rate to use. Default: 0.0
tf (str): Time Frame options: 'days', 'weeks', 'months', and 'years'. Default: 'years'
>>> result = ta.downside_deviation(returns, benchmark_rate=0.0, tf="years")
"""
# For both de-annualizing the benchmark rate and annualizing result
returns = verify_series(returns)
days_per_year = returns.shape[0] / total_time(returns, tf)
@@ -44,8 +69,15 @@ def downside_deviation(returns: Series, benchmark_rate: float = 0.0, log: bool =
return downside_deviation * npSqrt(days_per_year)
def jensens_alpha(returns:Series, benchmark_returns:Series) -> float:
"""Jensen's 'Alpha' of a series and a benchmark."""
def jensens_alpha(returns: Series, benchmark_returns: Series) -> float:
"""Jensen's 'Alpha' of a series and a benchmark.
Args:
returns (pd.Series): Series of 'returns's
benchmark_returns (pd.Series): Series of 'benchmark_returns's
>>> result = ta.jensens_alpha(returns, benchmark_returns)
"""
returns = verify_series(returns)
benchmark_returns = verify_series(benchmark_returns)
@@ -53,15 +85,29 @@ def jensens_alpha(returns:Series, benchmark_returns:Series) -> float:
return linear_regression(benchmark_returns, returns)["a"]
def log_max_drawdown(close:Series):
"""Log Max Drawdown of a series."""
def log_max_drawdown(close: Series) -> float:
"""Log Max Drawdown of a series.
Args:
close (pd.Series): Series of 'close's
>>> result = ta.log_max_drawdown(close)
"""
close = verify_series(close)
log_return = npLog(close.iloc[-1]) - npLog(close.iloc[0])
return log_return - max_drawdown(close, method="log")
def max_drawdown(close: Series, method:str = None, all:bool = False) -> float:
"""Maximum Drawdown from close. Defaults to 'dollar'. """
"""Maximum Drawdown from close. Default: 'dollar'.
Args:
close (pd.Series): Series of 'close's
method (str): Max DD calculation options: 'dollar', 'percent', 'log'. Default: 'dollar'
all (bool): If True, it returns all three methods as a dict. Default: False
>>> result = ta.max_drawdown(close, method="dollar", all=False)
"""
close = verify_series(close)
max_dd = drawdown(close).max()
@@ -77,9 +123,15 @@ def max_drawdown(close: Series, method:str = None, all:bool = False) -> float:
return max_dd_["dollar"]
def pure_profit_score(close:Series) -> float:
"""Pure Profit Score of a series."""
from sklearn.linear_model import LinearRegression
def pure_profit_score(close: Series) -> float:
"""Pure Profit Score of a series.
Args:
close (pd.Series): Series of 'close's
>>> result = ta.pure_profit_score(df.close)
"""
# from sklearn.linear_model import LinearRegression
close = verify_series(close)
close_index = Series(0, index=close.reset_index().index)
@@ -87,8 +139,16 @@ def pure_profit_score(close:Series) -> float:
return r * cagr(close)
def sharpe_ratio(close:Series, benchmark_rate:float = 0.0, log:bool = False) -> float:
"""Sharpe Ratio of a series."""
def sharpe_ratio(close: Series, benchmark_rate: float = 0.0, log: bool = False) -> float:
"""Sharpe Ratio of a series.
Args:
close (pd.Series): Series of 'close's
benchmark_rate (float): Benchmark Rate to use. Default: 0.0
log (bool): If True, calculates log_return. Otherwise it returns percent_return. Default: False
>>> result = ta.sharpe_ratio(close, benchmark_rate=0.0, log=False)
"""
close = verify_series(close)
returns = percent_return(close=close) if not log else log_return(close=close)
@@ -97,8 +157,16 @@ def sharpe_ratio(close:Series, benchmark_rate:float = 0.0, log:bool = False) ->
return result
def sortino_ratio(close:Series, benchmark_rate:float = 0.0, log:bool = False) -> float:
"""Sortino Ratio of a series."""
def sortino_ratio(close: Series, benchmark_rate: float = 0.0, log: bool = False) -> float:
"""Sortino Ratio of a series.
Args:
close (pd.Series): Series of 'close's
benchmark_rate (float): Benchmark Rate to use. Default: 0.0
log (bool): If True, calculates log_return. Otherwise it returns percent_return. Default: False
>>> result = ta.sortino_ratio(close, benchmark_rate=0.0, log=False)
"""
close = verify_series(close)
returns = percent_return(close=close) if not log else log_return(close=close)
@@ -107,12 +175,23 @@ def sortino_ratio(close:Series, benchmark_rate:float = 0.0, log:bool = False) ->
return result
def volatility(close: Series, tf:str = "years", returns:bool = False, log: bool = False, **kwargs) -> float:
"""Volatility of a series. Default: 'years'"""
def volatility(close: Series, tf: str = "years", returns: bool = False, log: bool = False, **kwargs) -> float:
"""Volatility of a series. Default: 'years'
Args:
close (pd.Series): Series of 'close's
tf (str): Time Frame options: 'days', 'weeks', 'months', and 'years'. Default: 'years'
returns (bool): If True, then it replace the close Series with the user defined Series; typically user generated returns or percent returns or log returns. Default: False
log (bool): If True, calculates log_return. Otherwise it calculates percent_return. Default: False
>>> result = ta.volatility(close, tf="years", returns=False, log=False, **kwargs)
"""
close = verify_series(close)
if not returns:
returns = percent_return(close=close) if not log else log_return(close=close)
else:
returns = close
factor = returns.shape[0] / total_time(returns, tf)
if kwargs.pop("nearest_day", False) and tf.lower() == "years":
+1 -1
View File
@@ -17,7 +17,7 @@ setup(
"pandas_ta.volatility",
"pandas_ta.volume"
],
version=".".join(("0", "2", "29b")),
version=".".join(("0", "2", "30b")),
description=long_description,
long_description=long_description,
author="Kevin Johnson",
+8
View File
@@ -57,6 +57,10 @@ class TestTrendExtension(TestCase):
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "DEC_1")
self.data.ta.decreasing(length=3, strict=True, append=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "SDEC_3")
def test_dpo_ext(self):
self.data.ta.dpo(append=True)
self.assertIsInstance(self.data, DataFrame)
@@ -67,6 +71,10 @@ class TestTrendExtension(TestCase):
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "INC_1")
self.data.ta.increasing(length=3, strict=True, append=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], "SINC_3")
def test_long_run_ext(self):
# Nothing passed, return self
self.assertEqual(self.data.ta.long_run(append=True).shape, self.data.shape)
+8
View File
@@ -113,6 +113,10 @@ class TestTrend(TestCase):
self.assertIsInstance(result, Series)
self.assertEqual(result.name, "DEC_1")
result = pandas_ta.decreasing(self.close, length=3, strict=True)
self.assertIsInstance(result, Series)
self.assertEqual(result.name, "SDEC_3")
def test_dpo(self):
result = pandas_ta.dpo(self.close)
self.assertIsInstance(result, Series)
@@ -123,6 +127,10 @@ class TestTrend(TestCase):
self.assertIsInstance(result, Series)
self.assertEqual(result.name, "INC_1")
result = pandas_ta.increasing(self.close, length=3, strict=True)
self.assertIsInstance(result, Series)
self.assertEqual(result.name, "SINC_3")
def test_long_run(self):
result = pandas_ta.long_run(self.close, self.open)
self.assertIsInstance(result, Series)
+11
View File
@@ -36,6 +36,12 @@ class TestUtilityMetrics(TestCase):
self.assertIsInstance(result, float)
self.assertGreaterEqual(result, 0)
result = pandas_ta.calmar_ratio(self.close, years=0)
self.assertIsNone(result)
result = pandas_ta.calmar_ratio(self.close, years=-2)
self.assertIsNone(result)
def test_downside_deviation(self):
result = pandas_ta.downside_deviation(self.pctret)
self.assertIsInstance(result, float)
@@ -101,6 +107,11 @@ class TestUtilityMetrics(TestCase):
self.assertGreaterEqual(result, 0)
def test_volatility(self):
returns_ = pandas_ta.percent_return(self.close)
result = pandas_ta.utils.volatility(returns_, returns=True)
self.assertIsInstance(result, float)
self.assertGreaterEqual(result, 0)
for tf in ["years", "months", "weeks", "days", "hours", "minutes", "seconds"]:
result = pandas_ta.utils.volatility(self.close, tf)
self.assertIsInstance(result, float)