mirror of
https://github.com/wassname/pandas-ta.git
synced 2026-06-27 16:10:07 +08:00
volatility refactor
This commit is contained in:
@@ -135,5 +135,6 @@ reqs.txt
|
||||
requirements.txt
|
||||
qd.py
|
||||
_performance.py
|
||||
_volatility.py
|
||||
_volume.py
|
||||
simple.ipynb
|
||||
+12
-1
@@ -23,6 +23,16 @@ from .performance.log_return import log_return
|
||||
from .performance.percent_return import percent_return
|
||||
from .performance.trend_return import trend_return
|
||||
|
||||
# Volatility
|
||||
from .volatility.accbands import accbands
|
||||
from .volatility.atr import atr
|
||||
from .volatility.bbands import bbands
|
||||
from .volatility.donchian import donchian
|
||||
from .volatility.kc import kc
|
||||
from .volatility.massi import massi
|
||||
from .volatility.natr import natr
|
||||
from .volatility.true_range import true_range
|
||||
|
||||
# Volume
|
||||
from .volume.ad import ad
|
||||
from .volume.adosc import adosc
|
||||
@@ -39,4 +49,5 @@ from .volume.pvt import pvt
|
||||
from .volume.vp import vp
|
||||
|
||||
# DataFrame Extension
|
||||
from .core import *
|
||||
from .core import *
|
||||
|
||||
|
||||
+8
-1
@@ -8,7 +8,6 @@ from .overlap import *
|
||||
from .statistics import *
|
||||
from .trend import *
|
||||
from .utils import *
|
||||
from .volatility import *
|
||||
|
||||
class BasePandasObject(PandasObject):
|
||||
"""Simple PandasObject Extension
|
||||
@@ -664,6 +663,7 @@ class AnalysisIndicators(BasePandasObject):
|
||||
high = self._get_column(high, 'high')
|
||||
low = self._get_column(low, 'low')
|
||||
close = self._get_column(close, 'close')
|
||||
from pandas_ta.volatility.accbands import accbands
|
||||
result = accbands(high=high, low=low, close=close, length=length, c=c, mamode=mamode, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
@@ -672,18 +672,21 @@ class AnalysisIndicators(BasePandasObject):
|
||||
high = self._get_column(high, 'high')
|
||||
low = self._get_column(low, 'low')
|
||||
close = self._get_column(close, 'close')
|
||||
from pandas_ta.volatility.atr import atr
|
||||
result = atr(high=high, low=low, close=close, length=length, mamode=mamode, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
|
||||
def bbands(self, close=None, length=None, stdev=None, mamode=None, offset=None, **kwargs):
|
||||
close = self._get_column(close, 'close')
|
||||
from pandas_ta.volatility.bbands import bbands
|
||||
result = bbands(close=close, length=length, stdev=stdev, mamode=mamode, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
|
||||
def donchian(self, close=None, length=None, offset=None, **kwargs):
|
||||
close = self._get_column(close, 'close')
|
||||
from pandas_ta.volatility.donchian import donchian
|
||||
result = donchian(close=close, length=length, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
@@ -692,6 +695,7 @@ class AnalysisIndicators(BasePandasObject):
|
||||
high = self._get_column(high, 'high')
|
||||
low = self._get_column(low, 'low')
|
||||
close = self._get_column(close, 'close')
|
||||
from pandas_ta.volatility.kc import kc
|
||||
result = kc(high=high, low=low, close=close, length=length, scalar=scalar, mamode=mamode, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
@@ -699,6 +703,7 @@ class AnalysisIndicators(BasePandasObject):
|
||||
def massi(self, high=None, low=None, fast=None, slow=None, offset=None, **kwargs):
|
||||
high = self._get_column(high, 'high')
|
||||
low = self._get_column(low, 'low')
|
||||
from pandas_ta.volatility.massi import massi
|
||||
result = massi(high=high, low=low, fast=fast, slow=slow, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
@@ -707,6 +712,7 @@ class AnalysisIndicators(BasePandasObject):
|
||||
high = self._get_column(high, 'high')
|
||||
low = self._get_column(low, 'low')
|
||||
close = self._get_column(close, 'close')
|
||||
from pandas_ta.volatility.natr import natr
|
||||
result = natr(high=high, low=low, close=close, length=length, mamode=mamode, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
@@ -715,6 +721,7 @@ class AnalysisIndicators(BasePandasObject):
|
||||
high = self._get_column(high, 'high')
|
||||
low = self._get_column(low, 'low')
|
||||
close = self._get_column(close, 'close')
|
||||
from pandas_ta.volatility.true_range import true_range
|
||||
result = true_range(high=high, low=low, close=close, drift=drift, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
|
||||
+2
-1
@@ -5,7 +5,8 @@ import pandas as pd
|
||||
from .momentum import roc
|
||||
from .overlap import dema, ema, hma, midprice, rma, sma
|
||||
from .utils import get_drift, get_offset, verify_series, zero
|
||||
from .volatility import atr, true_range
|
||||
from .volatility.true_range import true_range
|
||||
from .volatility.atr import atr
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,662 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from .overlap import ema, hlc3, sma
|
||||
from .statistics import variance, stdev
|
||||
from .utils import *
|
||||
|
||||
|
||||
|
||||
def accbands(high, low, close, length=None, c=None, drift=None, mamode=None, offset=None, **kwargs):
|
||||
"""Indicator: Acceleration Bands (ACCBANDS)
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/acceleration-bands-abands/
|
||||
"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 20
|
||||
c = float(c) if c and c > 0 else 4
|
||||
min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length
|
||||
mamode = mamode.lower() if mamode else 'sma'
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
hl_ratio = (high - low) / (high + low)
|
||||
hl_ratio *= c
|
||||
_lower = low * (1 - hl_ratio)
|
||||
_upper = high * (1 + hl_ratio)
|
||||
|
||||
if mamode is None or mamode == 'sma':
|
||||
lower = _lower.rolling(length, min_periods=min_periods).mean()
|
||||
mid = close.rolling(length, min_periods=min_periods).mean()
|
||||
upper = _upper.rolling(length, min_periods=min_periods).mean()
|
||||
elif mamode == 'ema':
|
||||
lower = _lower.ewm(span=length, min_periods=min_periods).mean()
|
||||
mid = close.ewm(span=length, min_periods=min_periods).mean()
|
||||
upper = _upper.ewm(span=length, min_periods=min_periods).mean()
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
lower = lower.shift(offset)
|
||||
mid = mid.shift(offset)
|
||||
upper = upper.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
lower.fillna(kwargs['fillna'], inplace=True)
|
||||
mid.fillna(kwargs['fillna'], inplace=True)
|
||||
upper.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
lower.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
mid.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
upper.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
lower.name = f"ACCBL_{length}"
|
||||
mid.name = f"ACCBM_{length}"
|
||||
upper.name = f"ACCBU_{length}"
|
||||
mid.category = upper.category = lower.category = 'volatility'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {lower.name: lower, mid.name: mid, upper.name: upper}
|
||||
accbandsdf = pd.DataFrame(data)
|
||||
accbandsdf.name = f"ACCBANDS_{length}"
|
||||
accbandsdf.category = 'volatility'
|
||||
|
||||
return accbandsdf
|
||||
|
||||
|
||||
def atr(high, low, close, length=None, mamode=None, drift=None, offset=None, **kwargs):
|
||||
"""Indicator: Average True Range (ATR)"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
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
|
||||
mamode = mamode.lower() if mamode else 'ema'
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
tr = true_range(high=high, low=low, close=close, drift=drift)
|
||||
if mamode == 'ema':
|
||||
atr = tr.ewm(span=length, min_periods=min_periods).mean()
|
||||
else:
|
||||
atr = tr.rolling(length, min_periods=min_periods).mean()
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
atr = atr.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
atr.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
atr.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
atr.name = f"ATR_{length}"
|
||||
atr.category = 'volatility'
|
||||
|
||||
return atr
|
||||
|
||||
|
||||
def bbands(close, length=None, std=None, mamode=None, offset=None, **kwargs):
|
||||
"""Indicator: Bollinger Bands (BBANDS)"""
|
||||
# Validate arguments
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 20
|
||||
min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length
|
||||
std = float(std) if std and std > 0 else 2.
|
||||
mamode = mamode.lower() if mamode else 'sma'
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
standard_deviation = stdev(close=close, length=length)
|
||||
deviations = std * standard_deviation
|
||||
|
||||
if mamode is None or mamode == 'sma':
|
||||
mid = sma(close=close, length=length)
|
||||
elif mamode == 'ema':
|
||||
mid = ema(close=close, length=length, **kwargs)
|
||||
|
||||
lower = mid - deviations
|
||||
upper = mid + deviations
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
lower = lower.shift(offset)
|
||||
mid = mid.shift(offset)
|
||||
upper = upper.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
lower.fillna(kwargs['fillna'], inplace=True)
|
||||
mid.fillna(kwargs['fillna'], inplace=True)
|
||||
upper.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
lower.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
mid.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
upper.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
lower.name = f"BBL_{length}"
|
||||
mid.name = f"BBM_{length}"
|
||||
upper.name = f"BBU_{length}"
|
||||
mid.category = upper.category = lower.category = 'volatility'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {lower.name: lower, mid.name: mid, upper.name: upper}
|
||||
bbandsdf = pd.DataFrame(data)
|
||||
bbandsdf.name = f"BBANDS_{length}"
|
||||
bbandsdf.category = 'volatility'
|
||||
|
||||
return bbandsdf
|
||||
|
||||
|
||||
def donchian(close, lower_length=None, upper_length=None, offset=None, **kwargs):
|
||||
"""Indicator: Donchian Channels (DC)"""
|
||||
# Validate arguments
|
||||
close = verify_series(close)
|
||||
lower_length = int(lower_length) if lower_length and lower_length > 0 else 10
|
||||
upper_length = int(upper_length) if upper_length and upper_length > 0 else 20
|
||||
lower_min_periods = int(kwargs['lower_min_periods']) if 'lower_min_periods' in kwargs and kwargs['lower_min_periods'] is not None else lower_length
|
||||
upper_min_periods = int(kwargs['upper_min_periods']) if 'upper_min_periods' in kwargs and kwargs['upper_min_periods'] is not None else upper_length
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
lower = close.rolling(lower_length, min_periods=lower_min_periods).min()
|
||||
upper = close.rolling(upper_length, min_periods=upper_min_periods).max()
|
||||
mid = 0.5 * (lower + upper)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
lower.fillna(kwargs['fillna'], inplace=True)
|
||||
mid.fillna(kwargs['fillna'], inplace=True)
|
||||
upper.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
lower.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
mid.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
upper.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
lower = lower.shift(offset)
|
||||
mid = mid.shift(offset)
|
||||
upper = upper.shift(offset)
|
||||
|
||||
# Name and Categorize it
|
||||
lower.name = f"DCL_{lower_length}_{upper_length}"
|
||||
mid.name = f"DCM_{lower_length}_{upper_length}"
|
||||
upper.name = f"DCU_{lower_length}_{upper_length}"
|
||||
mid.category = upper.category = lower.category = 'volatility'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {lower.name: lower, mid.name: mid, upper.name: upper}
|
||||
dcdf = pd.DataFrame(data)
|
||||
dcdf.name = f"DC_{lower_length}_{upper_length}"
|
||||
dcdf.category = 'volatility'
|
||||
|
||||
return dcdf
|
||||
|
||||
|
||||
def kc(high, low, close, length=None, scalar=None, mamode=None, offset=None, **kwargs):
|
||||
"""Indicator: Keltner Channels (KC)"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 20
|
||||
min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length
|
||||
scalar = float(scalar) if scalar and scalar > 0 else 2
|
||||
mamode = mamode.lower() if mamode else None
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
std = variance(close=close, length=length).apply(np.sqrt)
|
||||
|
||||
if mamode == 'ema':
|
||||
basis = close.ewm(span=length, min_periods=min_periods).mean()
|
||||
band = atr(high=high, low=low, close=close)
|
||||
else:
|
||||
hl_range = high - low
|
||||
typical_price = hlc3(high=high, low=low, close=close)
|
||||
basis = typical_price.rolling(length, min_periods=min_periods).mean()
|
||||
band = hl_range.rolling(length, min_periods=min_periods).mean()
|
||||
|
||||
lower = basis - scalar * band
|
||||
upper = basis + scalar * band
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
lower = lower.shift(offset)
|
||||
basis = basis.shift(offset)
|
||||
upper = upper.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
lower.fillna(kwargs['fillna'], inplace=True)
|
||||
basis.fillna(kwargs['fillna'], inplace=True)
|
||||
upper.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
lower.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
basis.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
upper.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
lower.name = f"KCL_{length}"
|
||||
basis.name = f"KCB_{length}"
|
||||
upper.name = f"KCU_{length}"
|
||||
basis.category = upper.category = lower.category = 'volatility'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {lower.name: lower, basis.name: basis, upper.name: upper}
|
||||
kcdf = pd.DataFrame(data)
|
||||
kcdf.name = f"KC_{length}"
|
||||
kcdf.category = 'volatility'
|
||||
|
||||
return kcdf
|
||||
|
||||
|
||||
def massi(high, low, fast=None, slow=None, offset=None, **kwargs):
|
||||
"""Indicator: Mass Index (MASSI)"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
fast = int(fast) if fast and fast > 0 else 9
|
||||
slow = int(slow) if slow and slow > 0 else 25
|
||||
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
|
||||
hl_range = high - low
|
||||
hl_ema1 = ema(close=hl_range, length=fast, **kwargs)
|
||||
hl_ema2 = ema(close=hl_ema1, length=fast, **kwargs)
|
||||
|
||||
hl_ratio = hl_ema1 / hl_ema2
|
||||
massi = hl_ratio.rolling(slow, min_periods=slow).sum()
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
massi = massi.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
massi.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
massi.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
massi.name = f"MASSI_{fast}_{slow}"
|
||||
massi.category = 'volatility'
|
||||
|
||||
return massi
|
||||
|
||||
|
||||
def natr(high, low, close, length=None, mamode=None, drift=None, offset=None, **kwargs):
|
||||
"""Indicator: Normalized Average True Range (NATR)"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 14
|
||||
mamode = mamode.lower() if mamode else 'ema'
|
||||
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)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
natr = natr.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
natr.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
natr.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
natr.name = f"NATR_{length}"
|
||||
natr.category = 'volatility'
|
||||
|
||||
return natr
|
||||
|
||||
|
||||
def true_range(high, low, close, drift=None, offset=None, **kwargs):
|
||||
"""Indicator: True Range"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
prev_close = close.shift(drift)
|
||||
ranges = [high - low, high - prev_close, low - prev_close]
|
||||
true_range = pd.DataFrame(ranges).T
|
||||
true_range = true_range.abs().max(axis=1)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
true_range = true_range.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
true_range.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
true_range.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
true_range.name = f"TRUERANGE_{drift}"
|
||||
true_range.category = 'volatility'
|
||||
|
||||
return true_range
|
||||
|
||||
|
||||
|
||||
# Volatility Documentation
|
||||
accbands.__doc__ = \
|
||||
"""Acceleration Bands (ACCBANDS)
|
||||
|
||||
Acceleration Bands created by Price Headley plots upper and lower envelope
|
||||
bands around a simple moving average.
|
||||
|
||||
Sources:
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/acceleration-bands-abands/
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=10, c=4
|
||||
EMA = Exponential Moving Average
|
||||
SMA = Simple Moving Average
|
||||
HL_RATIO = c * (high - low) / (high + low)
|
||||
LOW = low * (1 - HL_RATIO)
|
||||
HIGH = high * (1 + HL_RATIO)
|
||||
|
||||
if 'ema':
|
||||
LOWER = EMA(LOW, length)
|
||||
MID = EMA(close, length)
|
||||
UPPER = EMA(HIGH, length)
|
||||
else:
|
||||
LOWER = SMA(LOW, length)
|
||||
MID = SMA(close, length)
|
||||
UPPER = SMA(HIGH, length)
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): It's period. Default: 10
|
||||
c (int): Multiplier. Default: 4
|
||||
mamode (str): Two options: None or 'ema'. Default: 'ema'
|
||||
drift (int): The difference period. 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: lower, mid, upper columns.
|
||||
"""
|
||||
|
||||
|
||||
atr.__doc__ = \
|
||||
"""Average True Range (ATR)
|
||||
|
||||
Averge True Range is used to measure volatility, especially
|
||||
volatility caused by gaps or limit moves.
|
||||
|
||||
Sources:
|
||||
https://www.tradingview.com/wiki/Average_True_Range_(ATR)
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=14, drift=1
|
||||
SMA = Simple Moving Average
|
||||
EMA = Exponential Moving Average
|
||||
TR = True Range
|
||||
tr = TR(high, low, close, drift)
|
||||
if 'ema':
|
||||
ATR = EMA(tr, length)
|
||||
else:
|
||||
ATR = SMA(tr, length)
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): It's period. Default: 14
|
||||
mamode (str): Two options: None or 'ema'. Default: 'ema'
|
||||
drift (int): The difference period. 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.Series: New feature generated.
|
||||
"""
|
||||
|
||||
|
||||
bbands.__doc__ = \
|
||||
"""Bollinger Bands (BBANDS)
|
||||
|
||||
A popular volatility indicator.
|
||||
|
||||
Sources:
|
||||
https://www.tradingview.com/wiki/Bollinger_Bands_(BB)
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=20, std=2
|
||||
EMA = Exponential Moving Average
|
||||
SMA = Simple Moving Average
|
||||
STDEV = Standard Deviation
|
||||
stdev = STDEV(close, length)
|
||||
if 'ema':
|
||||
MID = EMA(close, length)
|
||||
else:
|
||||
MID = SMA(close, length)
|
||||
|
||||
LOWER = MID - std * stdev
|
||||
UPPER = MID + std * stdev
|
||||
|
||||
Args:
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): The short period. Default: 20
|
||||
std (int): The long period. Default: 2
|
||||
mamode (str): Two options: None or 'ema'. Default: 'ema'
|
||||
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: lower, mid, upper columns.
|
||||
"""
|
||||
|
||||
|
||||
donchian.__doc__ = \
|
||||
"""Donchian Channels (DC)
|
||||
|
||||
Donchian Channels are used to measure volatility, similar to
|
||||
Bollinger Bands and Keltner Channels.
|
||||
|
||||
Sources:
|
||||
https://www.tradingview.com/wiki/Donchian_Channels_(DC)
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=20
|
||||
LOWER = close.rolling(length).min()
|
||||
UPPER = close.rolling(length).max()
|
||||
MID = 0.5 * (LOWER + UPPER)
|
||||
|
||||
Args:
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): The short period. Default: 20
|
||||
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: lower, mid, upper columns.
|
||||
"""
|
||||
|
||||
|
||||
kc.__doc__ = \
|
||||
"""Keltner Channels (KC)
|
||||
|
||||
A popular volatility indicator similar to Bollinger Bands and
|
||||
Donchian Channels.
|
||||
|
||||
Sources:
|
||||
https://www.tradingview.com/wiki/Keltner_Channels_(KC)
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=20, scalar=2
|
||||
ATR = Average True Range
|
||||
EMA = Exponential Moving Average
|
||||
SMA = Simple Moving Average
|
||||
if 'ema':
|
||||
BASIS = EMA(close, length)
|
||||
BAND = ATR(high, low, close)
|
||||
else:
|
||||
hl_range = high - low
|
||||
tp = typical_price = hlc3(high, low, close)
|
||||
BASIS = SMA(tp, length)
|
||||
BAND = SMA(hl_range, length)
|
||||
|
||||
LOWER = BASIS - scalar * BAND
|
||||
UPPER = BASIS + scalar * BAND
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): The short period. Default: 20
|
||||
scalar (float): A positive float to scale the bands. Default: 2
|
||||
mamode (str): Two options: None or 'ema'. Default: 'ema'
|
||||
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: lower, basis, upper columns.
|
||||
"""
|
||||
|
||||
|
||||
massi.__doc__ = \
|
||||
"""Mass Index (MASSI)
|
||||
|
||||
The Mass Index is a non-directional volatility indicator that utilitizes the
|
||||
High-Low Range to identify trend reversals based on range expansions.
|
||||
|
||||
Sources:
|
||||
https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:mass_index
|
||||
mi = sum(ema(high - low, 9) / ema(ema(high - low, 9), 9), length)
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
fast: 9, slow: 25
|
||||
EMA = Exponential Moving Average
|
||||
hl = high - low
|
||||
hl_ema1 = EMA(hl, fast)
|
||||
hl_ema2 = EMA(hl_ema1, fast)
|
||||
hl_ratio = hl_ema1 / hl_ema2
|
||||
MASSI = SUM(hl_ratio, slow)
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
fast (int): The short period. Default: 9
|
||||
slow (int): The long period. Default: 25
|
||||
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.
|
||||
"""
|
||||
|
||||
|
||||
natr.__doc__ = \
|
||||
"""Normalized Average True Range (NATR)
|
||||
|
||||
Normalized Average True Range attempt to normalize the average
|
||||
true range.
|
||||
|
||||
Sources:
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/normalized-average-true-range-natr/
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=20
|
||||
ATR = Average True Range
|
||||
NATR = (100 / close) * ATR(high, low, close)
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): The short period. Default: 20
|
||||
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
|
||||
"""
|
||||
|
||||
|
||||
true_range.__doc__ = \
|
||||
"""True Range
|
||||
|
||||
An method to expand a classical range (high minus low) to include
|
||||
possible gap scenarios.
|
||||
|
||||
Sources:
|
||||
https://www.macroption.com/true-range/
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
drift=1
|
||||
ABS = Absolute Value
|
||||
prev_close = close.shift(drift)
|
||||
TRUE_RANGE = ABS([high - low, high - prev_close, low - prev_close])
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
close (pd.Series): Series of 'close's
|
||||
drift (int): The shift period. 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.Series: New feature
|
||||
"""
|
||||
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
@@ -0,0 +1,108 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame
|
||||
from ..utils import get_drift, get_offset, verify_series
|
||||
|
||||
def accbands(high, low, close, length=None, c=None, drift=None, mamode=None, offset=None, **kwargs):
|
||||
"""Indicator: Acceleration Bands (ACCBANDS)"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 20
|
||||
c = float(c) if c and c > 0 else 4
|
||||
min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length
|
||||
mamode = mamode.lower() if mamode else 'sma'
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
hl_ratio = (high - low) / (high + low)
|
||||
hl_ratio *= c
|
||||
_lower = low * (1 - hl_ratio)
|
||||
_upper = high * (1 + hl_ratio)
|
||||
|
||||
if mamode is None or mamode == 'sma':
|
||||
lower = _lower.rolling(length, min_periods=min_periods).mean()
|
||||
mid = close.rolling(length, min_periods=min_periods).mean()
|
||||
upper = _upper.rolling(length, min_periods=min_periods).mean()
|
||||
elif mamode == 'ema':
|
||||
lower = _lower.ewm(span=length, min_periods=min_periods).mean()
|
||||
mid = close.ewm(span=length, min_periods=min_periods).mean()
|
||||
upper = _upper.ewm(span=length, min_periods=min_periods).mean()
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
lower = lower.shift(offset)
|
||||
mid = mid.shift(offset)
|
||||
upper = upper.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
lower.fillna(kwargs['fillna'], inplace=True)
|
||||
mid.fillna(kwargs['fillna'], inplace=True)
|
||||
upper.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
lower.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
mid.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
upper.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
lower.name = f"ACCBL_{length}"
|
||||
mid.name = f"ACCBM_{length}"
|
||||
upper.name = f"ACCBU_{length}"
|
||||
mid.category = upper.category = lower.category = 'volatility'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {lower.name: lower, mid.name: mid, upper.name: upper}
|
||||
accbandsdf = DataFrame(data)
|
||||
accbandsdf.name = f"ACCBANDS_{length}"
|
||||
accbandsdf.category = 'volatility'
|
||||
|
||||
return accbandsdf
|
||||
|
||||
|
||||
|
||||
accbands.__doc__ = \
|
||||
"""Acceleration Bands (ACCBANDS)
|
||||
|
||||
Acceleration Bands created by Price Headley plots upper and lower envelope
|
||||
bands around a simple moving average.
|
||||
|
||||
Sources:
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/acceleration-bands-abands/
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=10, c=4
|
||||
EMA = Exponential Moving Average
|
||||
SMA = Simple Moving Average
|
||||
HL_RATIO = c * (high - low) / (high + low)
|
||||
LOW = low * (1 - HL_RATIO)
|
||||
HIGH = high * (1 + HL_RATIO)
|
||||
|
||||
if 'ema':
|
||||
LOWER = EMA(LOW, length)
|
||||
MID = EMA(close, length)
|
||||
UPPER = EMA(HIGH, length)
|
||||
else:
|
||||
LOWER = SMA(LOW, length)
|
||||
MID = SMA(close, length)
|
||||
UPPER = SMA(HIGH, length)
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): It's period. Default: 10
|
||||
c (int): Multiplier. Default: 4
|
||||
mamode (str): Two options: None or 'ema'. Default: 'ema'
|
||||
drift (int): The difference period. 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: lower, mid, upper columns.
|
||||
"""
|
||||
@@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .true_range import true_range
|
||||
from ..utils import get_drift, get_offset, verify_series
|
||||
|
||||
def atr(high, low, close, length=None, mamode=None, drift=None, offset=None, **kwargs):
|
||||
"""Indicator: Average True Range (ATR)"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
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
|
||||
mamode = mamode.lower() if mamode else 'ema'
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
tr = true_range(high=high, low=low, close=close, drift=drift)
|
||||
if mamode == 'ema':
|
||||
atr = tr.ewm(span=length, min_periods=min_periods).mean()
|
||||
else:
|
||||
atr = tr.rolling(length, min_periods=min_periods).mean()
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
atr = atr.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
atr.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
atr.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
atr.name = f"ATR_{length}"
|
||||
atr.category = 'volatility'
|
||||
|
||||
return atr
|
||||
|
||||
|
||||
|
||||
atr.__doc__ = \
|
||||
"""Average True Range (ATR)
|
||||
|
||||
Averge True Range is used to measure volatility, especially
|
||||
volatility caused by gaps or limit moves.
|
||||
|
||||
Sources:
|
||||
https://www.tradingview.com/wiki/Average_True_Range_(ATR)
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=14, drift=1
|
||||
SMA = Simple Moving Average
|
||||
EMA = Exponential Moving Average
|
||||
TR = True Range
|
||||
tr = TR(high, low, close, drift)
|
||||
if 'ema':
|
||||
ATR = EMA(tr, length)
|
||||
else:
|
||||
ATR = SMA(tr, length)
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): It's period. Default: 14
|
||||
mamode (str): Two options: None or 'ema'. Default: 'ema'
|
||||
drift (int): The difference period. 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.Series: New feature generated.
|
||||
"""
|
||||
@@ -0,0 +1,97 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame
|
||||
from ..overlap import ema, sma
|
||||
from ..statistics import stdev
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
def bbands(close, length=None, std=None, mamode=None, offset=None, **kwargs):
|
||||
"""Indicator: Bollinger Bands (BBANDS)"""
|
||||
# Validate arguments
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 20
|
||||
min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length
|
||||
std = float(std) if std and std > 0 else 2.
|
||||
mamode = mamode.lower() if mamode else 'sma'
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
standard_deviation = stdev(close=close, length=length)
|
||||
deviations = std * standard_deviation
|
||||
|
||||
if mamode is None or mamode == 'sma':
|
||||
mid = sma(close=close, length=length)
|
||||
elif mamode == 'ema':
|
||||
mid = ema(close=close, length=length, **kwargs)
|
||||
|
||||
lower = mid - deviations
|
||||
upper = mid + deviations
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
lower = lower.shift(offset)
|
||||
mid = mid.shift(offset)
|
||||
upper = upper.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
lower.fillna(kwargs['fillna'], inplace=True)
|
||||
mid.fillna(kwargs['fillna'], inplace=True)
|
||||
upper.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
lower.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
mid.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
upper.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
lower.name = f"BBL_{length}"
|
||||
mid.name = f"BBM_{length}"
|
||||
upper.name = f"BBU_{length}"
|
||||
mid.category = upper.category = lower.category = 'volatility'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {lower.name: lower, mid.name: mid, upper.name: upper}
|
||||
bbandsdf = DataFrame(data)
|
||||
bbandsdf.name = f"BBANDS_{length}"
|
||||
bbandsdf.category = 'volatility'
|
||||
|
||||
return bbandsdf
|
||||
|
||||
|
||||
|
||||
bbands.__doc__ = \
|
||||
"""Bollinger Bands (BBANDS)
|
||||
|
||||
A popular volatility indicator.
|
||||
|
||||
Sources:
|
||||
https://www.tradingview.com/wiki/Bollinger_Bands_(BB)
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=20, std=2
|
||||
EMA = Exponential Moving Average
|
||||
SMA = Simple Moving Average
|
||||
STDEV = Standard Deviation
|
||||
stdev = STDEV(close, length)
|
||||
if 'ema':
|
||||
MID = EMA(close, length)
|
||||
else:
|
||||
MID = SMA(close, length)
|
||||
|
||||
LOWER = MID - std * stdev
|
||||
UPPER = MID + std * stdev
|
||||
|
||||
Args:
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): The short period. Default: 20
|
||||
std (int): The long period. Default: 2
|
||||
mamode (str): Two options: None or 'ema'. Default: 'ema'
|
||||
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: lower, mid, upper columns.
|
||||
"""
|
||||
@@ -0,0 +1,79 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
def donchian(close, lower_length=None, upper_length=None, offset=None, **kwargs):
|
||||
"""Indicator: Donchian Channels (DC)"""
|
||||
# Validate arguments
|
||||
close = verify_series(close)
|
||||
lower_length = int(lower_length) if lower_length and lower_length > 0 else 10
|
||||
upper_length = int(upper_length) if upper_length and upper_length > 0 else 20
|
||||
lower_min_periods = int(kwargs['lower_min_periods']) if 'lower_min_periods' in kwargs and kwargs['lower_min_periods'] is not None else lower_length
|
||||
upper_min_periods = int(kwargs['upper_min_periods']) if 'upper_min_periods' in kwargs and kwargs['upper_min_periods'] is not None else upper_length
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
lower = close.rolling(lower_length, min_periods=lower_min_periods).min()
|
||||
upper = close.rolling(upper_length, min_periods=upper_min_periods).max()
|
||||
mid = 0.5 * (lower + upper)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
lower.fillna(kwargs['fillna'], inplace=True)
|
||||
mid.fillna(kwargs['fillna'], inplace=True)
|
||||
upper.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
lower.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
mid.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
upper.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
lower = lower.shift(offset)
|
||||
mid = mid.shift(offset)
|
||||
upper = upper.shift(offset)
|
||||
|
||||
# Name and Categorize it
|
||||
lower.name = f"DCL_{lower_length}_{upper_length}"
|
||||
mid.name = f"DCM_{lower_length}_{upper_length}"
|
||||
upper.name = f"DCU_{lower_length}_{upper_length}"
|
||||
mid.category = upper.category = lower.category = 'volatility'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {lower.name: lower, mid.name: mid, upper.name: upper}
|
||||
dcdf = DataFrame(data)
|
||||
dcdf.name = f"DC_{lower_length}_{upper_length}"
|
||||
dcdf.category = 'volatility'
|
||||
|
||||
return dcdf
|
||||
|
||||
|
||||
|
||||
donchian.__doc__ = \
|
||||
"""Donchian Channels (DC)
|
||||
|
||||
Donchian Channels are used to measure volatility, similar to
|
||||
Bollinger Bands and Keltner Channels.
|
||||
|
||||
Sources:
|
||||
https://www.tradingview.com/wiki/Donchian_Channels_(DC)
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=20
|
||||
LOWER = close.rolling(length).min()
|
||||
UPPER = close.rolling(length).max()
|
||||
MID = 0.5 * (LOWER + UPPER)
|
||||
|
||||
Args:
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): The short period. Default: 20
|
||||
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: lower, mid, upper columns.
|
||||
"""
|
||||
@@ -0,0 +1,111 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import sqrt as npsqrt
|
||||
from pandas import DataFrame
|
||||
from .atr import atr
|
||||
from ..overlap import hlc3
|
||||
from ..statistics import variance
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
|
||||
def kc(high, low, close, length=None, scalar=None, mamode=None, offset=None, **kwargs):
|
||||
"""Indicator: Keltner Channels (KC)"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 20
|
||||
min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length
|
||||
scalar = float(scalar) if scalar and scalar > 0 else 2
|
||||
mamode = mamode.lower() if mamode else None
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
std = variance(close=close, length=length).apply(npsqrt)
|
||||
|
||||
if mamode == 'ema':
|
||||
basis = close.ewm(span=length, min_periods=min_periods).mean()
|
||||
band = atr(high=high, low=low, close=close)
|
||||
else:
|
||||
hl_range = high - low
|
||||
typical_price = hlc3(high=high, low=low, close=close)
|
||||
basis = typical_price.rolling(length, min_periods=min_periods).mean()
|
||||
band = hl_range.rolling(length, min_periods=min_periods).mean()
|
||||
|
||||
lower = basis - scalar * band
|
||||
upper = basis + scalar * band
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
lower = lower.shift(offset)
|
||||
basis = basis.shift(offset)
|
||||
upper = upper.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
lower.fillna(kwargs['fillna'], inplace=True)
|
||||
basis.fillna(kwargs['fillna'], inplace=True)
|
||||
upper.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
lower.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
basis.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
upper.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
lower.name = f"KCL_{length}"
|
||||
basis.name = f"KCB_{length}"
|
||||
upper.name = f"KCU_{length}"
|
||||
basis.category = upper.category = lower.category = 'volatility'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {lower.name: lower, basis.name: basis, upper.name: upper}
|
||||
kcdf = DataFrame(data)
|
||||
kcdf.name = f"KC_{length}"
|
||||
kcdf.category = 'volatility'
|
||||
|
||||
return kcdf
|
||||
|
||||
|
||||
|
||||
kc.__doc__ = \
|
||||
"""Keltner Channels (KC)
|
||||
|
||||
A popular volatility indicator similar to Bollinger Bands and
|
||||
Donchian Channels.
|
||||
|
||||
Sources:
|
||||
https://www.tradingview.com/wiki/Keltner_Channels_(KC)
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=20, scalar=2
|
||||
ATR = Average True Range
|
||||
EMA = Exponential Moving Average
|
||||
SMA = Simple Moving Average
|
||||
if 'ema':
|
||||
BASIS = EMA(close, length)
|
||||
BAND = ATR(high, low, close)
|
||||
else:
|
||||
hl_range = high - low
|
||||
tp = typical_price = hlc3(high, low, close)
|
||||
BASIS = SMA(tp, length)
|
||||
BAND = SMA(hl_range, length)
|
||||
|
||||
LOWER = BASIS - scalar * BAND
|
||||
UPPER = BASIS + scalar * BAND
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): The short period. Default: 20
|
||||
scalar (float): A positive float to scale the bands. Default: 2
|
||||
mamode (str): Two options: None or 'ema'. Default: 'ema'
|
||||
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: lower, basis, upper columns.
|
||||
"""
|
||||
@@ -0,0 +1,76 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from ..overlap import ema
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
def massi(high, low, fast=None, slow=None, offset=None, **kwargs):
|
||||
"""Indicator: Mass Index (MASSI)"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
fast = int(fast) if fast and fast > 0 else 9
|
||||
slow = int(slow) if slow and slow > 0 else 25
|
||||
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
|
||||
hl_range = high - low
|
||||
hl_ema1 = ema(close=hl_range, length=fast, **kwargs)
|
||||
hl_ema2 = ema(close=hl_ema1, length=fast, **kwargs)
|
||||
|
||||
hl_ratio = hl_ema1 / hl_ema2
|
||||
massi = hl_ratio.rolling(slow, min_periods=slow).sum()
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
massi = massi.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
massi.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
massi.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
massi.name = f"MASSI_{fast}_{slow}"
|
||||
massi.category = 'volatility'
|
||||
|
||||
return massi
|
||||
|
||||
|
||||
|
||||
massi.__doc__ = \
|
||||
"""Mass Index (MASSI)
|
||||
|
||||
The Mass Index is a non-directional volatility indicator that utilitizes the
|
||||
High-Low Range to identify trend reversals based on range expansions.
|
||||
|
||||
Sources:
|
||||
https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:mass_index
|
||||
mi = sum(ema(high - low, 9) / ema(ema(high - low, 9), 9), length)
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
fast: 9, slow: 25
|
||||
EMA = Exponential Moving Average
|
||||
hl = high - low
|
||||
hl_ema1 = EMA(hl, fast)
|
||||
hl_ema2 = EMA(hl_ema1, fast)
|
||||
hl_ratio = hl_ema1 / hl_ema2
|
||||
MASSI = SUM(hl_ratio, slow)
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
fast (int): The short period. Default: 9
|
||||
slow (int): The long period. Default: 25
|
||||
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.
|
||||
"""
|
||||
@@ -0,0 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
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):
|
||||
"""Indicator: Normalized Average True Range (NATR)"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 14
|
||||
mamode = mamode.lower() if mamode else 'ema'
|
||||
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)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
natr = natr.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
natr.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
natr.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
natr.name = f"NATR_{length}"
|
||||
natr.category = 'volatility'
|
||||
|
||||
return natr
|
||||
|
||||
|
||||
|
||||
natr.__doc__ = \
|
||||
"""Normalized Average True Range (NATR)
|
||||
|
||||
Normalized Average True Range attempt to normalize the average
|
||||
true range.
|
||||
|
||||
Sources:
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/normalized-average-true-range-natr/
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=20
|
||||
ATR = Average True Range
|
||||
NATR = (100 / close) * ATR(high, low, close)
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): The short period. Default: 20
|
||||
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
|
||||
"""
|
||||
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame
|
||||
from ..utils import get_drift, get_offset, verify_series
|
||||
|
||||
def true_range(high, low, close, drift=None, offset=None, **kwargs):
|
||||
"""Indicator: True Range"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
prev_close = close.shift(drift)
|
||||
ranges = [high - low, high - prev_close, low - prev_close]
|
||||
true_range = DataFrame(ranges).T
|
||||
true_range = true_range.abs().max(axis=1)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
true_range = true_range.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
true_range.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
true_range.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
true_range.name = f"TRUERANGE_{drift}"
|
||||
true_range.category = 'volatility'
|
||||
|
||||
return true_range
|
||||
|
||||
|
||||
|
||||
true_range.__doc__ = \
|
||||
"""True Range
|
||||
|
||||
An method to expand a classical range (high minus low) to include
|
||||
possible gap scenarios.
|
||||
|
||||
Sources:
|
||||
https://www.macroption.com/true-range/
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
drift=1
|
||||
ABS = Absolute Value
|
||||
prev_close = close.shift(drift)
|
||||
TRUE_RANGE = ABS([high - low, high - prev_close, low - prev_close])
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
close (pd.Series): Series of 'close's
|
||||
drift (int): The shift period. 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.Series: New feature
|
||||
"""
|
||||
@@ -6,7 +6,7 @@ long_description = "An easy to use Python 3 Pandas Extension of Technical Analys
|
||||
setup(
|
||||
name = "pandas_ta",
|
||||
packages = ["pandas_ta"],
|
||||
version = "0.1.21b",
|
||||
version = "0.1.22b",
|
||||
description=long_description,
|
||||
long_description=long_description,
|
||||
author = "Kevin Johnson",
|
||||
|
||||
@@ -30,20 +30,17 @@ class TestVolatility(TestCase):
|
||||
del cls.data
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.volatility = pandas_ta.volatility
|
||||
|
||||
def tearDown(self):
|
||||
del self.volatility
|
||||
def setUp(self): pass
|
||||
def tearDown(self): pass
|
||||
|
||||
|
||||
def test_accbands(self):
|
||||
result = self.volatility.accbands(self.high, self.low, self.close)
|
||||
result = pandas_ta.accbands(self.high, self.low, self.close)
|
||||
self.assertIsInstance(result, DataFrame)
|
||||
self.assertEqual(result.name, 'ACCBANDS_20')
|
||||
|
||||
def test_atr(self):
|
||||
result = self.volatility.atr(self.high, self.low, self.close)
|
||||
result = pandas_ta.atr(self.high, self.low, self.close)
|
||||
self.assertIsInstance(result, Series)
|
||||
self.assertEqual(result.name, 'ATR_14')
|
||||
|
||||
@@ -58,7 +55,7 @@ class TestVolatility(TestCase):
|
||||
error_analysis(result, CORRELATION, ex)
|
||||
|
||||
def test_bbands(self):
|
||||
result = self.volatility.bbands(self.close)
|
||||
result = pandas_ta.bbands(self.close)
|
||||
self.assertIsInstance(result, DataFrame)
|
||||
self.assertEqual(result.name, 'BBANDS_20')
|
||||
|
||||
@@ -86,27 +83,27 @@ class TestVolatility(TestCase):
|
||||
error_analysis(result.iloc[:,2], CORRELATION, ex, newline=False)
|
||||
|
||||
def test_donchian(self):
|
||||
result = self.volatility.donchian(self.close)
|
||||
result = pandas_ta.donchian(self.close)
|
||||
self.assertIsInstance(result, DataFrame)
|
||||
self.assertEqual(result.name, 'DC_10_20')
|
||||
|
||||
result = self.volatility.donchian(self.close, lower_length=20, upper_length=5)
|
||||
result = pandas_ta.donchian(self.close, lower_length=20, upper_length=5)
|
||||
self.assertIsInstance(result, DataFrame)
|
||||
self.assertEqual(result.name, 'DC_20_5')
|
||||
|
||||
|
||||
def test_kc(self):
|
||||
result = self.volatility.kc(self.high, self.low, self.close)
|
||||
result = pandas_ta.kc(self.high, self.low, self.close)
|
||||
self.assertIsInstance(result, DataFrame)
|
||||
self.assertEqual(result.name, 'KC_20')
|
||||
|
||||
def test_massi(self):
|
||||
result = self.volatility.massi(self.high, self.low)
|
||||
result = pandas_ta.massi(self.high, self.low)
|
||||
self.assertIsInstance(result, Series)
|
||||
self.assertEqual(result.name, 'MASSI_9_25')
|
||||
|
||||
def test_natr(self):
|
||||
result = self.volatility.natr(self.high, self.low, self.close)
|
||||
result = pandas_ta.natr(self.high, self.low, self.close)
|
||||
self.assertIsInstance(result, Series)
|
||||
self.assertEqual(result.name, 'NATR_14')
|
||||
|
||||
@@ -121,7 +118,7 @@ class TestVolatility(TestCase):
|
||||
error_analysis(result, CORRELATION, ex)
|
||||
|
||||
def test_true_range(self):
|
||||
result = self.volatility.true_range(self.high, self.low, self.close)
|
||||
result = pandas_ta.true_range(self.high, self.low, self.close)
|
||||
self.assertIsInstance(result, Series)
|
||||
self.assertEqual(result.name, 'TRUERANGE_1')
|
||||
|
||||
|
||||
@@ -16,11 +16,8 @@ class TestVolatilityExtension(TestCase):
|
||||
del cls.data
|
||||
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
def setUp(self): pass
|
||||
def tearDown(self): pass
|
||||
|
||||
|
||||
def test_accbands_ext(self):
|
||||
@@ -31,6 +28,8 @@ class TestVolatilityExtension(TestCase):
|
||||
def test_atr_ext(self):
|
||||
self.data.ta.atr(append=True)
|
||||
self.assertIsInstance(self.data, DataFrame)
|
||||
print()
|
||||
print(self.data[self.data.columns[-1]].tail())
|
||||
self.assertEqual(self.data.columns[-1], 'ATR_14')
|
||||
|
||||
def test_bbands_ext(self):
|
||||
@@ -61,4 +60,6 @@ class TestVolatilityExtension(TestCase):
|
||||
def test_true_range_ext(self):
|
||||
self.data.ta.true_range(append=True)
|
||||
self.assertIsInstance(self.data, DataFrame)
|
||||
print()
|
||||
print(self.data[self.data.columns[-1]].tail())
|
||||
self.assertEqual(self.data.columns[-1], 'TRUERANGE_1')
|
||||
Reference in New Issue
Block a user