ENH + MAINT added Heikin-Ashi and Supertrend

This commit is contained in:
Kevin Johnson
2020-06-01 11:00:32 -07:00
parent 6e6a5f7a34
commit 5997536da6
25 changed files with 398 additions and 256 deletions
+3
View File
@@ -1,6 +1,9 @@
clean:
find . -name '*.pyc' -exec rm -f {} +
caches:
find ./pandas_ta | grep -E "(__pycache__|\.pyc|\.pyo$\)"
init:
pip install -r requirements.txt
+11 -4
View File
@@ -33,10 +33,12 @@ All the indicators return a named Series or a DataFrame in uppercase underscore
Choppiness Index (chop)
Chande Kroll Stop (cksp)
Entropy (entropy)
Heikin-Ashi Candles (ha)
KDJ (kdj)
Parabolic Stop and Reverse (psar)
Price Distance (pdist)
Psycholigical Line (psl)
Supertrend (supertrend)
Weighted Closing Price (wcp)
### __Added utilities:__
Above (above)
@@ -182,6 +184,10 @@ df.ta.adjusted = None
# __Technical Analysis Indicators__ (_by Category_)
## _Candles_ (1)
* _Heikin-Ashi_: **ha**
## _Momentum_ (25)
* _Awesome Oscillator_: **ao**
@@ -215,7 +221,7 @@ df.ta.adjusted = None
|:--------:|
| ![Example MACD](/images/SPY_MACD.png) |
## _Overlap_ (25)
## _Overlap_ (26)
* _Double Exponential Moving Average_: **dema**
* _Exponential Moving Average_: **ema**
@@ -224,17 +230,18 @@ df.ta.adjusted = None
* _High-Low-Close Average_: **hlc3**
* Commonly known as 'Typical Price' in Technical Analysis literature
* _Hull Exponential Moving Average_: **hma**
* _Kaufman's Adaptive Moving Average_: **kama**
* _Ichimoku Kinkō Hyō_: **ichimoku**
* Use: help(ta.ichimoku). Returns two DataFrames.
* _Kaufman's Adaptive Moving Average_: **kama**
* _Linear Regression_: **linreg**
* _Midpoint_: **midpoint**
* _Midprice_: **midprice**
* _Open-High-Low-Close Average_: **ohlc4**
* _Pascal's Weighted Moving Average_: **pwma**
* _William's Moving Average_: **rma**
* _Simple Moving Average_: **sma**
* _Sine Weighted Moving Average_: **sinwma**
* _Simple Moving Average_: **sma**
* _Supertrend_: **supertrend**
* _Symmetric Weighted Moving Average_: **swma**
* _T3 Moving Average_: **t3**
* _Triple Exponential Moving Average_: **tema**
@@ -355,4 +362,4 @@ Use parameter: cumulative=**True** for cumulative results.
* Original TA-LIB: http://ta-lib.org/
* Bukosabino: https://github.com/bukosabino/ta
Please leave any comments, feedback, or suggestions.
Please leave any comments, feedback, suggestions, or indicator requests.
+2
View File
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from .ha import ha
+95
View File
@@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
import numpy as np
from pandas import DataFrame
from pandas_ta.utils import get_offset, verify_series
def ha(open_, high, low, close, offset=None, **kwargs):
"""Indicator: Heikin Ashi"""
# Validate Arguments
open_ = verify_series(open_)
high = verify_series(high)
low = verify_series(low)
close = verify_series(close)
offset = get_offset(offset)
# Calculate Result
m = close.size
df = DataFrame({
"HA_open": 0.5 * (open_.iloc[0] + close.iloc[0]),
"HA_high": high,
"HA_low": low,
"HA_close": 0.25 * (open_ + high + low + close)
})
for i in range(1, m):
df["HA_open"][i] = 0.5 * (df["HA_open"][i - 1] + df["HA_close"][i - 1])
df["HA_high"] = df[["HA_open", "HA_high", "HA_close"]].max(axis=1)
df["HA_low"] = df[["HA_open", "HA_low", "HA_close"]].min(axis=1)
# 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 = "Heikin-Ashi"
df.category = "candles"
return df
ha.__doc__ = \
"""Heikin Ashi (HA)
The Heikin-Ashi technique averages price data to create a Japanese
candlestick chart that filters out market noise. Heikin-Ashi charts,
developed by Munehisa Homma in the 1700s, share some characteristics
with standard candlestick charts but differ based on the values used
to create each candle. Instead of using the open, high, low, and close
like standard candlestick charts, the Heikin-Ashi technique uses a
modified formula based on two-period averages. This gives the chart a
smoother appearance, making it easier to spots trends and reversals,
but also obscures gaps and some price data.
Sources:
https://www.investopedia.com/terms/h/heikinashi.asp
Calculation:
HA_OPEN[0] = (open[0] + close[0]) / 2
HA_CLOSE = (open[0] + high[0] + low[0] + close[0]) / 4
for i > 1 in df.index:
HA_OPEN = (HA_OPEN[i1] + HA_CLOSE[i1]) / 2
HA_HIGH = MAX(HA_OPEN, HA_HIGH, HA_CLOSE)
HA_LOW = MIN(HA_OPEN, HA_LOW, HA_CLOSE)
How to Calculate Heikin-Ashi
Use one period to create the first Heikin-Ashi (HA) candle, using
the formulas. For example use the high, low, open, and close to
create the first HA close price. Use the open and close to create
the first HA open. The high of the period will be the first HA high,
and the low will be the first HA low. With the first HA calculated,
it is now possible to continue computing the HA candles per the formulas.
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
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
fill_method (value, optional): Type of fill method
Returns:
pd.DataFrame: ha_open, ha_high,ha_low, ha_close columns.
"""
+29 -21
View File
@@ -5,6 +5,7 @@ from functools import wraps
import pandas as pd
from pandas.core.base import PandasObject
from pandas_ta.candles import *
from pandas_ta.momentum import *
from pandas_ta.overlap import *
from pandas_ta.performance import *
@@ -14,7 +15,7 @@ from pandas_ta.volatility import *
from pandas_ta.volume import *
from pandas_ta.utils import *
version = ".".join(("0", "1", "64b"))
version = ".".join(("0", "1", "65b"))
def finalize(method):
@wraps(method)
@@ -204,6 +205,7 @@ class AnalysisIndicators(BasePandasObject):
if result is None: return
else:
prefix = suffix = ""
# delimiter = kwargs.pop("delimiter", "_")
if "prefix" in kwargs:
prefix = f"{kwargs['prefix']}_"
@@ -367,6 +369,17 @@ class AnalysisIndicators(BasePandasObject):
self._all(**kwargs) if name == "all" else None
# Candles
@finalize
def ha(self, open_=None, high=None, low=None, close=None, offset=None, **kwargs):
open_ = self._get_column(open_, 'open')
high = self._get_column(high, 'high')
low = self._get_column(low, 'low')
close = self._get_column(close, 'close')
result = ha(open_=open_, high=high, low=low, close=close, offset=offset, **kwargs)
return result
# Momentum Indicators
@finalize
def ao(self, high=None, low=None, fast=None, slow=None, offset=None, **kwargs):
@@ -534,10 +547,10 @@ class AnalysisIndicators(BasePandasObject):
return result
@finalize
def trix(self, close=None, length=None, drift=None, offset=None, **kwargs):
def trix(self, close=None, length=None, signal=None, scalar=None, drift=None, offset=None, **kwargs):
close = self._get_column(close, 'close')
result = trix(close=close, length=length, drift=drift, offset=offset, **kwargs)
result = trix(close=close, length=length, signal=signal, scalar=scalar, drift=drift, offset=offset, **kwargs)
return result
@finalize
@@ -691,6 +704,15 @@ class AnalysisIndicators(BasePandasObject):
result = sma(close=close, length=length, offset=offset, **kwargs)
return result
@finalize
def supertrend(self, high=None, low=None, close=None, length=None, multiplier=None, offset=None, **kwargs):
high = self._get_column(high, 'high')
low = self._get_column(low, 'low')
close = self._get_column(close, 'close')
result = supertrend(high=high, low=low, close=close, length=length, multiplier=multiplier, offset=offset, **kwargs)
return result
@finalize
def swma(self, close=None, length=None, offset=None, **kwargs):
close = self._get_column(close, 'close')
@@ -912,17 +934,6 @@ class AnalysisIndicators(BasePandasObject):
result = dpo(close=close, length=length, centered=centered, offset=offset, **kwargs)
return result
def ha(self, open=None, high=None, low=None, close=None, offset=None, **kwargs):
open = self._get_column(open, 'open')
high = self._get_column(high, 'high')
low = self._get_column(low, 'low')
close = self._get_column(close, 'close')
result = ha(open=open, high=high, low=low, close=close, offset=offset, **kwargs)
self._add_prefix_suffix(result, **kwargs)
self._append(result, **kwargs)
return result
@finalize
def increasing(self, close=None, length=None, asint=True, offset=None, **kwargs):
close = self._get_column(close, 'close')
@@ -979,18 +990,15 @@ class AnalysisIndicators(BasePandasObject):
self._append(result, **kwargs)
return result
def supertrend(self, high=None, low=None, close=None, period=None, multiplier=None, mamode=None, drift=None,
offset=None, **kwargs):
@finalize
def supertrend(self, high=None, low=None, close=None, period=None, multiplier=None, mamode=None, drift=None, offset=None, **kwargs):
high = self._get_column(high, 'high')
low = self._get_column(low, 'low')
close = self._get_column(close, 'close')
result = supertrend(high=high, low=low, close=close, period=period, multiplier=multiplier, mamode=mamode, drift=drift, offset=offset, **kwargs)
self._add_prefix_suffix(result, **kwargs)
self._append(result, **kwargs)
return result
@finalize
def vortex(self, high=None, low=None, close=None, drift=None, offset=None, **kwargs):
high = self._get_column(high, 'high')
@@ -1115,12 +1123,12 @@ class AnalysisIndicators(BasePandasObject):
return result
@finalize
def natr(self, high=None, low=None, close=None, length=None, mamode=None, offset=None, **kwargs):
def natr(self, high=None, low=None, close=None, length=None, mamode=None, scalar=None, offset=None, **kwargs):
high = self._get_column(high, 'high')
low = self._get_column(low, 'low')
close = self._get_column(close, 'close')
result = natr(high=high, low=low, close=close, length=length, mamode=mamode, offset=offset, **kwargs)
result = natr(high=high, low=low, close=close, length=length, mamode=mamode, scalar=scalar, offset=offset, **kwargs)
return result
@finalize
+10 -4
View File
@@ -7,6 +7,7 @@ def cmo(close, length=None, scalar=None, drift=None, offset=None, **kwargs):
close = verify_series(close)
length = int(length) if length and length > 0 else 14
scalar = float(scalar) if scalar else 100
talib = kwargs.pop("talib", True)
drift = get_drift(drift)
offset = get_offset(offset)
@@ -17,11 +18,15 @@ def cmo(close, length=None, scalar=None, drift=None, offset=None, **kwargs):
positive[positive < 0] = 0 # Make negatives 0 for the postive series
negative[negative > 0] = 0 # Make postives 0 for the negative series
positive_avg = positive.ewm(com=length, adjust=False).mean()
negative_avg = negative.ewm(com=length, adjust=False).mean().abs()
if talib:
pos_ = positive.ewm(com=length, adjust=False).mean()
neg_ = negative.ewm(com=length, adjust=False).mean().abs()
else:
pos_ = positive.rolling(length).sum()
neg_ = negative.abs().rolling(length).sum()
# Previous steps same as RSI
cmo = scalar * (positive_avg - negative_avg) / (positive_avg + negative_avg)
cmo = scalar * (pos_ - neg_)
cmo /= pos_ + neg_
# Offset
if offset != 0:
@@ -60,6 +65,7 @@ Calculation:
Args:
close (pd.Series): Series of 'close's
scalar (float): How much to magnify. Default: 100
talib (bool): If True, uses TA-Libs implementation. Otherwise uses EMA version. Default: True
drift (int): The short period. Default: 1
offset (int): How many periods to offset the result. Default: 0
+3 -3
View File
@@ -45,14 +45,14 @@ def kst(close, roc1=None, roc2=None, roc3=None, roc4=None, sma1=None, sma2=None,
# Name and Categorize it
kst.name = f"KST_{roc1}_{roc2}_{roc3}_{roc4}_{sma1}_{sma2}_{sma3}_{sma4}"
kst_signal.name = f"KSTS_{signal}"
kst.category = kst_signal.category = 'momentum'
kst_signal.name = f"KSTs_{signal}"
kst.category = kst_signal.category = "momentum"
# Prepare DataFrame to return
data = {kst.name: kst, kst_signal.name: kst_signal}
kstdf = DataFrame(data)
kstdf.name = f"KST_{roc1}_{roc2}_{roc3}_{roc4}_{sma1}_{sma2}_{sma3}_{sma4}_{signal}"
kstdf.category = 'momentum'
kstdf.category = "momentum"
return kstdf
+29 -7
View File
@@ -1,12 +1,15 @@
# -*- coding: utf-8 -*-
from ..overlap.ema import ema
from ..utils import get_drift, get_offset, verify_series
from pandas import DataFrame
from pandas_ta.overlap.ema import ema
from pandas_ta.utils import get_drift, get_offset, verify_series
def trix(close, length=None, drift=None, offset=None, **kwargs):
def trix(close, length=None, signal=None, scalar=None, drift=None, offset=None, **kwargs):
"""Indicator: Trix (TRIX)"""
# Validate Arguments
close = verify_series(close)
length = int(length) if length and length > 0 else 30
signal = int(signal) if signal and signal > 0 else 9
scalar = float(scalar) if scalar else 100
min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length
drift = get_drift(drift)
offset = get_offset(offset)
@@ -15,17 +18,34 @@ def trix(close, length=None, drift=None, offset=None, **kwargs):
ema1 = ema(close=close, length=length, **kwargs)
ema2 = ema(close=ema1, length=length, **kwargs)
ema3 = ema(close=ema2, length=length, **kwargs)
trix = 100 * ema3.pct_change(drift)
trix = scalar * ema3.pct_change(drift)
trix_signal = trix.rolling(signal).mean()
# Offset
if offset != 0:
trix = trix.shift(offset)
trix_signal = trix_signal.shift(offset)
# Handle fills
if 'fillna' in kwargs:
trix.fillna(kwargs['fillna'], inplace=True)
trix_signal.fillna(kwargs['fillna'], inplace=True)
if 'fill_method' in kwargs:
trix.fillna(method=kwargs['fill_method'], inplace=True)
trix_signal.fillna(method=kwargs['fill_method'], inplace=True)
# Name & Category
trix.name = f"TRIX_{length}"
trix.category = 'momentum'
trix.name = f"TRIX_{length}_{signal}"
trix_signal.name = f"TRIXs_{length}_{signal}"
trix.category = trix_signal.category = "momentum"
return trix
# Prepare DataFrame to return
df = DataFrame({trix.name: trix, trix_signal.name: trix_signal})
df.name = f"TRIX_{length}_{signal}"
df.category = "momentum"
return df
@@ -50,6 +70,8 @@ Calculation:
Args:
close (pd.Series): Series of 'close's
length (int): It's period. Default: 18
signal (int): It's period. Default: 9
scalar (float): How much to magnify. Default: 100
drift (int): The difference period. Default: 1
offset (int): How many periods to offset the result. Default: 0
+1
View File
@@ -15,6 +15,7 @@ from .pwma import pwma
from .rma import rma
from .sinwma import sinwma
from .sma import sma
from .supertrend import supertrend
from .swma import swma
from .t3 import t3
from .tema import tema
+1 -1
View File
@@ -10,7 +10,7 @@ def hlc3(high, low, close, offset=None, **kwargs):
offset = get_offset(offset)
# Calculate Result
hlc3 = (high + low + close) / 3
hlc3 = (high + low + close) / 3.
# Offset
if offset != 0:
+119
View File
@@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
from numpy import NaN as npNaN
from pandas import DataFrame
from pandas_ta.overlap import hl2
from pandas_ta.volatility import atr
from pandas_ta.utils import get_offset, verify_series
def supertrend(high, low, close, length=None, multiplier=None, offset=None, **kwargs):
"""Indicator: Supertrend"""
# Validate Arguments
high = verify_series(high)
low = verify_series(low)
close = verify_series(close)
length = int(length) if length and length > 0 else 7
multiplier = float(multiplier) if multiplier and multiplier > 0 else 3.
offset = get_offset(offset)
# Calculate Results
m = close.size
dir_, trend = [0] * m, [0] * m
long, short = [npNaN] * m, [npNaN] * m
hl2_ = hl2(high, low)
matr = multiplier * atr(high, low, close, length)
upperband = hl2_ + matr
lowerband = hl2_ - matr
for i in range(1, m):
if close.iloc[i] > upperband.iloc[i - 1]:
dir_[i] = 1
elif close.iloc[i] < lowerband.iloc[i - 1]:
dir_[i] = -1
else:
dir_[i] = dir_[i - 1]
if dir_[i] > 0 and lowerband.iloc[i] < lowerband.iloc[i - 1]:
lowerband.iloc[i] = lowerband.iloc[i - 1]
if dir_[i] < 0 and upperband.iloc[i] > upperband.iloc[i - 1]:
upperband.iloc[i] = upperband.iloc[i - 1]
if dir_[i] > 0:
trend[i] = long[i] = lowerband.iloc[i]
else:
trend[i] = short[i] = upperband.iloc[i]
# Prepare DataFrame to return
_props = f"_{length}_{multiplier}"
df = DataFrame({
f"SUPERT{_props}": trend,
f"SUPERTd{_props}": dir_,
f"SUPERTl{_props}": long,
f"SUPERTs{_props}": short
}, index=close.index)
df.name = f"SUPERT{_props}"
df.category = "overlap"
# Apply offset if needed
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)
return df
supertrend.__doc__ = \
"""Supertrend (supertrend)
Supertrend is an overlap indicator. It is used to help identify trend
direction, setting stop loss, identify support and resistance, and/or
generate buy & sell signals.
Sources:
http://www.freebsensetips.com/blog/detail/7/What-is-supertrend-indicator-its-calculation
Calculation:
Default Inputs:
length=7, multiplier=3.0
MID = multiplier * ATR
LOWERBAND = HL2 - MID
UPPERBAND = HL2 + MID
if UPPERBAND[i] < FINAL_UPPERBAND[i-1] and close[i-1] > FINAL_UPPERBAND[i-1]:
FINAL_UPPERBAND[i] = UPPERBAND[i]
else:
FINAL_UPPERBAND[i] = FINAL_UPPERBAND[i-1])
if LOWERBAND[i] > FINAL_LOWERBAND[i-1] and close[i-1] < FINAL_LOWERBAND[i-1]:
FINAL_LOWERBAND[i] = LOWERBAND[i]
else:
FINAL_LOWERBAND[i] = FINAL_LOWERBAND[i-1])
if close[i] <= FINAL_UPPERBAND[i]:
SUPERTREND[i] = FINAL_UPPERBAND[i]
else:
SUPERTREND[i] = FINAL_LOWERBAND[i]
Args:
high (pd.Series): Series of 'high's
low (pd.Series): Series of 'low's
close (pd.Series): Series of 'close's
length (int) : length for ATR calculation. Default: 7
multiplier (float): Coefficient for upper and lower band distance to midrange. Default: 3.0
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: SUPERT (trend), SUPERTd (direction), SUPERTl (long), SUPERTs (short) columns.
"""
+1
View File
@@ -40,6 +40,7 @@ direction.
Sources:
https://www.tradingview.com/wiki/Volume_Weighted_Average_Price_(VWAP)
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/volume-weighted-average-price-vwap/
https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:vwap_intraday
Calculation:
tp = typical_price = hlc3(high, low, close)
+1 -1
View File
@@ -14,8 +14,8 @@ def trend_return(close, trend, log=True, cumulative=None, offset=None, trend_res
# Calculate Result
returns = log_return(close, cumulative=False) if log else percent_return(close, cumulative=False)
m = trend.size
tsum = 0
m = trend.size
trend = trend.astype(int)
returns = (trend * returns).apply(zero)
-2
View File
@@ -6,12 +6,10 @@ from .chop import chop
from .cksp import cksp
from .decreasing import decreasing
from .dpo import dpo
from .ha import ha
from .increasing import increasing
from .linear_decay import linear_decay
from .long_run import long_run
from .psar import psar
from .qstick import qstick
from .short_run import short_run
from .supertrend import supertrend
from .vortex import vortex
-99
View File
@@ -1,99 +0,0 @@
# -*- coding: utf-8 -*-
import numpy as np
from pandas import DataFrame
from pandas_ta.utils import get_offset, verify_series
def ha(open, high, low, close, offset=None, **kwargs):
# indicator : Heikin Ashi
# Validate Arguments
open_ = verify_series(open)
high = verify_series(high)
low = verify_series(low)
close = verify_series(close)
offset = get_offset(offset)
# calculate ha_close
ha_close = 0.25 * (open_ + high + low + close)
# Initialization of the ha_open array
ha_open = np.zeros(shape=(len(close)))
# ha_open of the first element
ha_open[0] = 0.5 * (open_[0] + close[0])
# calculate ha_open. Based on previous ha_open & ha_close
for i in range(1, len(close)):
ha_open[i] = 0.5 * (ha_open[i-1] + ha_close[i-1])
# calculation of ha_high & ha_low
ha_high = np.maximum.reduce([high, ha_open, ha_close])
ha_low = np.minimum.reduce([low, ha_open, ha_close])
# Prepare DataFrame to return
data = {'ha_open': ha_open, 'ha_high': ha_high, 'ha_low': ha_low, 'ha_close': ha_close}
hadf = DataFrame(data)
hadf.name = "Heikin-Ashi"
hadf.category = 'trend'
# Apply offset if needed
if offset != 0:
hadf = hadf.shift(offset)
# Handle fills
if 'fillna' in kwargs:
hadf.fillna(kwargs['fillna'], inplace=True)
if 'fill_method' in kwargs:
hadf.fillna(method=kwargs['fill_method'], inplace=True)
return hadf
ha.__doc__ = \
"""Heikin Ashi (HA)
The Heikin-Ashi technique averages price data to create a Japanese candlestick chart that filters out market noise.
Heikin-Ashi charts, developed by Munehisa Homma in the 1700s,
share some characteristics with standard candlestick charts but differ based on the values used to create each candle.
Instead of using the open, high, low, and close like standard candlestick charts,
the Heikin-Ashi technique uses a modified formula based on two-period averages.
This gives the chart a smoother appearance, making it easier to spots trends and reversals,
but also obscures gaps and some price data.
Sources:
https://www.investopedia.com/terms/h/heikinashi.asp
Calculation:
The Formula for the Heikin-Ashi technique is:
Heikin-Ashi Close=(Open0+High0+Low0+Close0)/4
Heikin-Ashi Open=(HA Open1+HA Close1)/2
Heikin-Ashi High=Max (High0,HA Open0,HA Close0)
Heikin-Ashi Low=Min (Low0,HA Open0,HA Close0)
where:Open0 etc.=Values from the current period
Open1 etc.=Values from the prior period
HA=Heikin-Ashi
How to Calculate Heikin-Ashi
Use one period to create the first Heikin-Ashi (HA) candle, using the formulas.
For example use the high, low, open, and close to create the first HA close price.
Use the open and close to create the first HA open.
The high of the period will be the first HA high, and the low will be the first HA low.
With the first HA calculated, it is now possible to continue computing the HA candles per the formulas.
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
Kwargs:
fillna (value, optional): pd.DataFrame.fillna(value)
fill_method (value, optional): Type of fill method
Returns:
pd.DataFrame: ha_open, ha_high,ha_low, ha_close columns.
"""
-104
View File
@@ -1,104 +0,0 @@
# -*- coding: utf-8 -*-
import numpy as np
from pandas import DataFrame
from ..utils import get_offset, verify_series
from ..volatility import atr
def supertrend(high, low, close, length=None, multiplier=None, mamode=None, drift=None, offset=None, **kwargs):
# indicator : supertrend
# Validate Arguments
high = verify_series(high)
low = verify_series(low)
close = verify_series(close)
offset = get_offset(offset)
length = int(length) if length and length > 0 else 10
multiplier = float(multiplier) if multiplier and multiplier > 0 else 3
min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs[
'min_periods'] is not None else length
supertrend_dir = np.zeros(shape=(len(close)))
strend = np.zeros(shape=(len(close)))
# Bands initial calculation
midrange = 0.5 * (high + low)
distance = multiplier * atr(high, low, close, length, mamode, drift, offset, min_periods=min_periods)
lowerband = midrange - distance
upperband = midrange + distance
# final calculation loop
for i in range(1, len(close)):
if close[i] > upperband[i - 1]:
supertrend_dir[i] = 1
elif close[i] < lowerband[i - 1]:
supertrend_dir[i] = -1
else:
supertrend_dir[i] = supertrend_dir[i - 1]
if supertrend_dir[i] > 0 and lowerband[i] < lowerband[i - 1]:
lowerband[i] = lowerband[i - 1]
if supertrend_dir[i] < 0 and upperband[i] > upperband[i - 1]:
upperband[i] = upperband[i - 1]
if supertrend_dir[i] < 0:
strend[i] = upperband[i]
else:
strend[i] = lowerband[i]
# Prepare DataFrame to return
data = {f"supertrend_{length}_{multiplier}": strend, f"supertrend_dir_{length}_{multiplier}": supertrend_dir}
supertrend_df = DataFrame(data)
supertrend_df.name = f"supertrend_{length}_{multiplier}"
supertrend_df.category = 'trend'
# Apply offset if needed
if offset != 0:
supertrend_df = supertrend_df.shift(offset)
# Handle fills
if 'fillna' in kwargs:
supertrend_df.fillna(kwargs['fillna'], inplace=True)
if 'fill_method' in kwargs:
supertrend_df.fillna(method=kwargs['fill_method'], inplace=True)
return supertrend_df
supertrend.__doc__ = \
"""Supertrend (supertrend)
Supertrend is a trend indicator. It is usually used to help identify trend direction, setting stop loss,
identify support and resistance, and / or generate buy & sell signals.
Calculation is in 2 steps : first a multiple of ATR is added and substracted to the middle of the high - low range.
This gives the upperband and lowerband.
The direction of the trend is then calculated : if close > previous upperband or < previous lowerband,
then trend direction is changed, else it is the same as previous value.
If trend direction is unchanged and down, upperband is set to minimum between current and previous value
If trend direction is unchanged and up, lowerband is set to maximum between current and previous value.
The final band is then choosen according to the direction of the trend : upperband if trend is downward,
lowerband if trend is upward.
Returned values are : float for final band level, int (1 : upward trend, -1 : downward trend) for trend direction
Calculation:
Default Inputs:
length = 10
multiplier = 3
Args:
high (pd.Series): Series of 'high's
low (pd.Series): Series of 'low's
close (pd.Series): Series of 'close's
length (int) : length for ATR calculation. Default : 10
multiplier : coefficient for upper and lower band distance to midrange. Default : 3
mamode: parameter used for ATR calculation. See ATR documentation. Default : None (= ema)
drift : parameter used for ATR calculation. See ATR documentation. Default : None (= 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
min_periods (int, optional) : parameter used for ATR calculation. See ATR documentation. Default : length
Returns:
pd.DataFrame: supertrend (float), supertrend_dir (int) columns.
"""
+5 -2
View File
@@ -2,7 +2,7 @@
from .atr import atr
from ..utils import get_drift, get_offset, verify_series
def natr(high, low, close, length=None, mamode=None, drift=None, offset=None, **kwargs):
def natr(high, low, close, length=None, mamode=None, scalar=None, drift=None, offset=None, **kwargs):
"""Indicator: Normalized Average True Range (NATR)"""
# Validate arguments
high = verify_series(high)
@@ -10,11 +10,13 @@ def natr(high, low, close, length=None, mamode=None, drift=None, offset=None, **
close = verify_series(close)
length = int(length) if length and length > 0 else 14
mamode = mamode.lower() if mamode else 'ema'
scalar = float(scalar) if scalar else 100
drift = get_drift(drift)
offset = get_offset(offset)
# Calculate Result
natr = (100 / close) * atr(high=high, low=low, close=close, length=length, mamode=mamode, drift=drift, offset=offset, **kwargs)
natr = scalar / close
natr *= atr(high=high, low=low, close=close, length=length, mamode=mamode, drift=drift, offset=offset, **kwargs)
# Offset
if offset != 0:
@@ -54,6 +56,7 @@ Args:
low (pd.Series): Series of 'low's
close (pd.Series): Series of 'close's
length (int): The short period. Default: 20
scalar (float): How much to magnify. Default: 100
offset (int): How many periods to offset the result. Default: 0
Kwargs:
+3 -3
View File
@@ -20,9 +20,9 @@ def cmf(high, low, close, volume, open_=None, length=None, offset=None, **kwargs
else:
ad = 2 * close - (high + low) # AD with High, Low, Close
hl_range = high_low_range
ad *= volume / hl_range
cmf = ad.rolling(length, min_periods=min_periods).sum() / volume.rolling(length, min_periods=min_periods).sum()
ad *= volume / high_low_range
cmf = ad.rolling(length, min_periods=min_periods).sum()
cmf /= volume.rolling(length, min_periods=min_periods).sum()
# Offset
if offset != 0:
+2 -1
View File
@@ -18,7 +18,8 @@ def eom(high, low, close, volume, length=None, divisor=None, drift=None, offset=
# Calculate Result
distance = hl2(high=high, low=low) - hl2(high=high.shift(drift), low=low.shift(drift))
box_ratio = (volume / divisor) / high_low_range
box_ratio = volume / divisor
box_ratio /= high_low_range
eom = distance / box_ratio
eom = eom.rolling(length, min_periods=min_periods).mean()
+40
View File
@@ -0,0 +1,40 @@
from .config import error_analysis, sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from .context import pandas_ta
from unittest import TestCase, skip
import pandas.testing as pdt
from pandas import DataFrame, Series
import talib as tal
class TestCandle(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data.columns = cls.data.columns.str.lower()
cls.open = cls.data['open']
cls.high = cls.data['high']
cls.low = cls.data['low']
cls.close = cls.data['close']
if 'volume' in cls.data.columns: cls.volume = cls.data['volume']
@classmethod
def tearDownClass(cls):
del cls.open
del cls.high
del cls.low
del cls.close
if hasattr(cls, 'volume'): del cls.volume
del cls.data
def setUp(self): pass
def tearDown(self): pass
def test_ha(self):
result = pandas_ta.ha(self.open, self.high, self.low, self.close)
self.assertIsInstance(result, DataFrame)
self.assertEqual(result.name, "Heikin-Ashi")
+29
View File
@@ -0,0 +1,29 @@
from .config import sample_data
from .context import pandas_ta
from unittest import TestCase
from pandas import DataFrame
class TestCandleExtension(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
@classmethod
def tearDownClass(cls):
del cls.data
def setUp(self):
pass
def tearDown(self):
pass
def test_ha_ext(self):
self.data.ta.ha(append=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(list(self.data.columns[-4:]), ['HA_open', 'HA_high', 'HA_low', 'HA_close'])
+2 -2
View File
@@ -314,8 +314,8 @@ class TestMomentum(TestCase):
def test_trix(self):
result = pandas_ta.trix(self.close)
self.assertIsInstance(result, Series)
self.assertEqual(result.name, 'TRIX_30')
self.assertIsInstance(result, DataFrame)
self.assertEqual(result.name, 'TRIX_30_9')
def test_tsi(self):
result = pandas_ta.tsi(self.close)
+2 -2
View File
@@ -81,7 +81,7 @@ class TestMomentumExtension(TestCase):
def test_kst_ext(self):
self.data.ta.kst(append=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(list(self.data.columns[-2:]), ['KST_10_15_20_30_10_10_10_15', 'KSTS_9'])
self.assertEqual(list(self.data.columns[-2:]), ['KST_10_15_20_30_10_10_10_15', 'KSTs_9'])
def test_macd_ext(self):
self.data.ta.macd(append=True)
@@ -139,7 +139,7 @@ class TestMomentumExtension(TestCase):
def test_trix_ext(self):
self.data.ta.trix(append=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], 'TRIX_30')
self.assertEqual(list(self.data.columns[-2:]), ['TRIX_30_9', 'TRIXs_30_9'])
def test_tsi_ext(self):
self.data.ta.tsi(append=True)
+5
View File
@@ -243,6 +243,11 @@ class TestOverlap(TestCase):
self.assertIsInstance(result, Series)
self.assertEqual(result.name, 'SWMA_10')
def test_supertrend(self):
result = pandas_ta.supertrend(self.high, self.low, self.close)
self.assertIsInstance(result, DataFrame)
self.assertEqual(result.name, 'SUPERT_7_3.0')
def test_t3(self):
result = pandas_ta.t3(self.close)
self.assertIsInstance(result, Series)
+5
View File
@@ -108,6 +108,11 @@ class TestOverlapExtension(TestCase):
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(self.data.columns[-1], 'SWMA_10')
def test_supertrend_ext(self):
self.data.ta.supertrend(append=True)
self.assertIsInstance(self.data, DataFrame)
self.assertEqual(list(self.data.columns[-4:]), ["SUPERT_7_3.0", "SUPERTd_7_3.0", "SUPERTl_7_3.0", "SUPERTs_7_3.0"])
def test_t3_ext(self):
self.data.ta.t3(append=True)
self.assertIsInstance(self.data, DataFrame)