mirror of
https://github.com/wassname/pandas-ta.git
synced 2026-08-07 11:25:53 +08:00
trend refactoring
This commit is contained in:
+1
-3
@@ -134,8 +134,6 @@ pandas_pips
|
||||
reqs.txt
|
||||
requirements.txt
|
||||
qd.py
|
||||
_performance.py
|
||||
_trend.py
|
||||
_statistics.py
|
||||
_volatility.py
|
||||
_volume.py
|
||||
simple.ipynb
|
||||
@@ -33,6 +33,18 @@ from .statistics.stdev import stdev
|
||||
from .statistics.variance import variance
|
||||
from .statistics.zscore import zscore
|
||||
|
||||
# Trend
|
||||
from .trend.adx import adx
|
||||
from .trend.amat import amat
|
||||
from .trend.aroon import aroon
|
||||
from .trend.decreasing import decreasing
|
||||
from .trend.dpo import dpo
|
||||
from .trend.increasing import increasing
|
||||
from .trend.long_run import long_run
|
||||
from .trend.qstick import qstick
|
||||
from .trend.short_run import short_run
|
||||
from .trend.vortex import vortex
|
||||
|
||||
# Volatility
|
||||
from .volatility.accbands import accbands
|
||||
from .volatility.atr import atr
|
||||
|
||||
+10
-1
@@ -5,7 +5,6 @@ from pandas.core.base import PandasObject
|
||||
|
||||
from .momentum import *
|
||||
from .overlap import *
|
||||
from .trend import *
|
||||
from .utils import *
|
||||
|
||||
class BasePandasObject(PandasObject):
|
||||
@@ -584,36 +583,42 @@ class AnalysisIndicators(BasePandasObject):
|
||||
high = self._get_column(high, 'high')
|
||||
low = self._get_column(low, 'low')
|
||||
close = self._get_column(close, 'close')
|
||||
from .trend.adx import adx
|
||||
result = adx(high=high, low=low, close=close, drift=drift, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
|
||||
def amat(self, close=None, fast=None, slow=None, mamode=None, lookback=None, offset=None, **kwargs):
|
||||
close = self._get_column(close, 'close')
|
||||
from .trend.amat import amat
|
||||
result = amat(close=close, fast=fast, slow=slow, mamode=mamode, lookback=lookback, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
|
||||
def aroon(self, close=None, length=None, offset=None, **kwargs):
|
||||
close = self._get_column(close, 'close')
|
||||
from .trend.aroon import aroon
|
||||
result = aroon(close=close, length=length, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
|
||||
def decreasing(self, close=None, length=None, asint=True, offset=None, **kwargs):
|
||||
close = self._get_column(close, 'close')
|
||||
from .trend.decreasing import decreasing
|
||||
result = decreasing(close=close, length=length, asint=asint, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
|
||||
def dpo(self, close=None, length=None, centered=True, offset=None, **kwargs):
|
||||
close = self._get_column(close, 'close')
|
||||
from .trend.dpo import dpo
|
||||
result = dpo(close=close, length=length, centered=centered, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
|
||||
def increasing(self, close=None, length=None, asint=True, offset=None, **kwargs):
|
||||
close = self._get_column(close, 'close')
|
||||
from .trend.increasing import increasing
|
||||
result = increasing(close=close, length=length, asint=asint, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
@@ -623,6 +628,7 @@ class AnalysisIndicators(BasePandasObject):
|
||||
else:
|
||||
fast = self._get_column(fast, f"{fast}")
|
||||
slow = self._get_column(slow, f"{slow}")
|
||||
from .trend.long_run import long_run
|
||||
result = long_run(fast=fast, slow=slow, length=length, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
@@ -630,6 +636,7 @@ class AnalysisIndicators(BasePandasObject):
|
||||
def qstick(self, open_=None, close=None, length=None, offset=None, **kwargs):
|
||||
open_ = self._get_column(open_, 'open')
|
||||
close = self._get_column(close, 'close')
|
||||
from .trend.qstick import qstick
|
||||
result = qstick(open_=open_, close=close, length=length, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
@@ -639,6 +646,7 @@ class AnalysisIndicators(BasePandasObject):
|
||||
else:
|
||||
fast = self._get_column(fast, f"{fast}")
|
||||
slow = self._get_column(slow, f"{slow}")
|
||||
from .trend.short_run import short_run
|
||||
result = short_run(fast=fast, slow=slow, length=length, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
@@ -647,6 +655,7 @@ class AnalysisIndicators(BasePandasObject):
|
||||
high = self._get_column(high, 'high')
|
||||
low = self._get_column(low, 'low')
|
||||
close = self._get_column(close, 'close')
|
||||
from .trend.vortex import vortex
|
||||
result = vortex(high=high, low=low, close=close, drift=drift, offset=offset, **kwargs)
|
||||
self._append(result, **kwargs)
|
||||
return result
|
||||
|
||||
@@ -1,683 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import numpy as np
|
||||
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.true_range import true_range
|
||||
from .volatility.atr import atr
|
||||
|
||||
|
||||
|
||||
def adx(high, low, close, length=None, drift=None, offset=None, **kwargs):
|
||||
"""Indicator: ADX"""
|
||||
# Validate Arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
length = length if length and length > 0 else 14
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
_atr = atr(high=high, low=low, close=close, length=length)
|
||||
|
||||
up = high - high.shift(drift)
|
||||
dn = low.shift(drift) - low
|
||||
|
||||
pos = ((up > dn) & (up > 0)) * up
|
||||
neg = ((dn > up) & (dn > 0)) * dn
|
||||
|
||||
pos = pos.apply(zero)
|
||||
neg = neg.apply(zero)
|
||||
|
||||
dmp = (100 / _atr) * rma(close=pos, length=length)
|
||||
dmn = (100 / _atr) * rma(close=neg, length=length)
|
||||
|
||||
dx = 100 * (dmp - dmn).abs() / (dmp + dmn)
|
||||
adx = rma(close=dx, length=length)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
dmp = dmp.shift(offset)
|
||||
dmn = dmn.shift(offset)
|
||||
adx = adx.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
adx.fillna(kwargs['fillna'], inplace=True)
|
||||
dmp.fillna(kwargs['fillna'], inplace=True)
|
||||
dmn.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
adx.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
dmp.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
dmn.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
adx.name = f"ADX_{length}"
|
||||
dmp.name = f"DMP_{length}"
|
||||
dmn.name = f"DMN_{length}"
|
||||
|
||||
adx.category = dmp.category = dmn.category = 'trend'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {adx.name: adx, dmp.name: dmp, dmn.name: dmn}
|
||||
adxdf = pd.DataFrame(data)
|
||||
adxdf.name = f"ADX_{length}"
|
||||
adxdf.category = 'trend'
|
||||
|
||||
return adxdf
|
||||
|
||||
|
||||
def amat(close=None, fast=None, slow=None, mamode=None, lookback=None, offset=None, **kwargs):
|
||||
"""Indicator: Archer Moving Averages Trends (AMAT)"""
|
||||
# Validate Arguments
|
||||
close = verify_series(close)
|
||||
fast = int(fast) if fast and fast > 0 else 8
|
||||
slow = int(slow) if slow and slow > 0 else 21
|
||||
lookback = int(lookback) if lookback and lookback > 0 else 2
|
||||
mamode = mamode.upper() if mamode else 'EMA'
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
if mamode == 'EMA':
|
||||
fast_ma = ema(close=close, length=fast, **kwargs)
|
||||
slow_ma = ema(close=close, length=slow, **kwargs)
|
||||
elif mamode == 'HMA':
|
||||
fast_ma = hma(close=close, length=fast, **kwargs)
|
||||
slow_ma = hma(close=close, length=slow, **kwargs)
|
||||
elif mamode == 'LINREG':
|
||||
fast_ma = linreg(close=close, length=fast, **kwargs)
|
||||
slow_ma = linreg(close=close, length=slow, **kwargs)
|
||||
elif mamode == 'RMA':
|
||||
fast_ma = rma(close=close, length=fast, **kwargs)
|
||||
slow_ma = rma(close=close, length=slow, **kwargs)
|
||||
elif mamode == 'SMA':
|
||||
fast_ma = sma(close=close, length=fast, **kwargs)
|
||||
slow_ma = sma(close=close, length=slow, **kwargs)
|
||||
elif mamode == 'WMA':
|
||||
fast_ma = wma(close=close, length=fast, **kwargs)
|
||||
slow_ma = wma(close=close, length=slow, **kwargs)
|
||||
|
||||
mas_long = long_run(fast_ma, slow_ma, length=lookback)
|
||||
mas_short = short_run(fast_ma, slow_ma, length=lookback)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
mas_long = mas_long.shift(offset)
|
||||
mas_short = mas_short.shift(offset)
|
||||
|
||||
# # Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
mas_long.fillna(kwargs['fillna'], inplace=True)
|
||||
mas_short.fillna(kwargs['fillna'], inplace=True)
|
||||
|
||||
if 'fill_method' in kwargs:
|
||||
mas_long.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
mas_short.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Prepare DataFrame to return
|
||||
amatdf = pd.DataFrame({
|
||||
f"AMAT_{mas_long.name}": mas_long,
|
||||
f"AMAT_{mas_short.name}": mas_short
|
||||
})
|
||||
|
||||
# Name and Categorize it
|
||||
amatdf.name = f"AMAT_{mamode}_{fast}_{slow}_{lookback}"
|
||||
amatdf.category = 'trend'
|
||||
|
||||
return amatdf
|
||||
|
||||
|
||||
def aroon(close, length=None, offset=None, **kwargs):
|
||||
"""Indicator: Aroon Oscillator"""
|
||||
# Validate Arguments
|
||||
close = verify_series(close)
|
||||
length = 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)
|
||||
|
||||
# Calculate Result
|
||||
def maxidx(x):
|
||||
return 100 * (int(np.argmax(x)) + 1) / length
|
||||
|
||||
def minidx(x):
|
||||
return 100 * (int(np.argmin(x)) + 1) / length
|
||||
|
||||
_close = close.rolling(length, min_periods=min_periods)
|
||||
aroon_up = _close.apply(maxidx, raw=True)
|
||||
aroon_down = _close.apply(minidx, raw=True)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
aroon_up.fillna(kwargs['fillna'], inplace=True)
|
||||
aroon_down.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
aroon_up.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
aroon_down.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
aroon_up = aroon_up.shift(offset)
|
||||
aroon_down = aroon_down.shift(offset)
|
||||
|
||||
# Name and Categorize it
|
||||
aroon_up.name = f"AROONU_{length}"
|
||||
aroon_down.name = f"AROOND_{length}"
|
||||
|
||||
aroon_down.category = aroon_up.category = 'trend'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {aroon_down.name: aroon_down, aroon_up.name: aroon_up}
|
||||
aroondf = pd.DataFrame(data)
|
||||
aroondf.name = f"AROON_{length}"
|
||||
aroondf.category = 'trend'
|
||||
|
||||
return aroondf
|
||||
|
||||
|
||||
def decreasing(close, length=None, asint=True, offset=None, **kwargs):
|
||||
"""Indicator: Decreasing"""
|
||||
# Validate Arguments
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 1
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
decreasing = close.diff(length) < 0
|
||||
if asint:
|
||||
decreasing = decreasing.astype(int)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
decreasing = decreasing.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
decreasing.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
decreasing.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
decreasing.name = f"DEC_{length}"
|
||||
decreasing.category = 'trend'
|
||||
|
||||
return decreasing
|
||||
|
||||
|
||||
def dpo(close, length=None, centered=True, offset=None, **kwargs):
|
||||
"""Indicator: Detrend Price Oscillator (DPO)"""
|
||||
# Validate Arguments
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 1
|
||||
min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
drift = int(0.5 * length) + 1 # int((0.5 * length) + 1)
|
||||
dpo = close.shift(drift) - close.rolling(length, min_periods=min_periods).mean()
|
||||
if centered:
|
||||
dpo = dpo.shift(-drift)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
dpo = dpo.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
dpo.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
dpo.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
dpo.name = f"DPO_{length}"
|
||||
dpo.category = 'trend'
|
||||
|
||||
return dpo
|
||||
|
||||
|
||||
def increasing(close, length=None, asint=True, offset=None, **kwargs):
|
||||
"""Indicator: Increasing"""
|
||||
# Validate Arguments
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 1
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
increasing = close.diff(length) > 0
|
||||
if asint:
|
||||
increasing = increasing.astype(int)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
increasing = increasing.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
increasing.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
increasing.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
increasing.name = f"INC_{length}"
|
||||
increasing.category = 'trend'
|
||||
|
||||
return increasing
|
||||
|
||||
|
||||
def long_run(fast, slow, length=None, offset=None, **kwargs):
|
||||
"""Indicator: Long Run"""
|
||||
# Validate Arguments
|
||||
fast = verify_series(fast)
|
||||
slow = verify_series(slow)
|
||||
length = int(length) if length and length > 0 else 2
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
pb = increasing(fast, length) & decreasing(slow, length) # potential bottom or bottom
|
||||
bi = increasing(fast, length) & increasing(slow, length) # fast and slow are increasing
|
||||
long_run = pb | bi
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
long_run = long_run.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
long_run.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
long_run.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
long_run.name = f"LR_{length}"
|
||||
long_run.category = 'trend'
|
||||
|
||||
return long_run
|
||||
|
||||
|
||||
def qstick(open_, close, length=None, offset=None, **kwargs):
|
||||
"""Indicator: Q Stick"""
|
||||
# Validate Arguments
|
||||
open_ = verify_series(open_)
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 10
|
||||
offset = get_offset(offset)
|
||||
ma = kwargs.pop('ma', 'sma') if 'ma' in kwargs else 'sma'
|
||||
|
||||
# Calculate Result
|
||||
diff = close - open_
|
||||
|
||||
if ma in [None, 'sma']: qstick = sma(diff, length=length)
|
||||
if ma == 'dema': qstick = dema(diff, length=length, **kwargs)
|
||||
if ma == 'ema': qstick = ema(diff, length=length, **kwargs)
|
||||
if ma == 'hma': qstick = hma(diff, length=length)
|
||||
if ma == 'rma': qstick = rma(diff, length=length)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
qstick = qstick.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
qstick.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
qstick.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
qstick.name = f"QS_{length}"
|
||||
qstick.category = 'trend'
|
||||
|
||||
return qstick
|
||||
|
||||
|
||||
def short_run(fast, slow, length=None, offset=None, **kwargs):
|
||||
"""Indicator: Short Run"""
|
||||
# Validate Arguments
|
||||
fast = verify_series(fast)
|
||||
slow = verify_series(slow)
|
||||
length = int(length) if length and length > 0 else 2
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
pt = decreasing(fast, length) & increasing(slow, length) # potential top or top
|
||||
bd = decreasing(fast, length) & decreasing(slow, length) # fast and slow are decreasing
|
||||
short_run = pt | bd
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
short_run = short_run.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
short_run.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
short_run.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
short_run.name = f"SR_{length}"
|
||||
short_run.category = 'trend'
|
||||
|
||||
return short_run
|
||||
|
||||
|
||||
def vortex(high, low, close, length=None, drift=None, offset=None, **kwargs):
|
||||
"""Indicator: Vortex"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
length = 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
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
tr = true_range(high=high, low=low, close=close)
|
||||
tr_sum = tr.rolling(length, min_periods=min_periods).sum()
|
||||
|
||||
vmp = (high - low.shift(drift)).abs()
|
||||
vmm = (low - high.shift(drift)).abs()
|
||||
|
||||
vip = vmp.rolling(length, min_periods=min_periods).sum() / tr_sum
|
||||
vim = vmm.rolling(length, min_periods=min_periods).sum() / tr_sum
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
vip = vip.shift(offset)
|
||||
vim = vim.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
vip.fillna(kwargs['fillna'], inplace=True)
|
||||
vim.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
vip.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
vim.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
vip.name = f"VTXP_{length}"
|
||||
vim.name = f"VTXM_{length}"
|
||||
vip.category = vim.category = 'trend'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {vip.name: vip, vim.name: vim}
|
||||
vtxdf = pd.DataFrame(data)
|
||||
vtxdf.name = f"VTX_{length}"
|
||||
vtxdf.category = 'trend'
|
||||
|
||||
return vtxdf
|
||||
|
||||
|
||||
|
||||
# Trend Documentation
|
||||
adx.__doc__ = \
|
||||
"""Average Directional Movement (ADX)
|
||||
|
||||
Average Directional Movement is meant to quantify trend strength by measuring
|
||||
the amount of movement in a single direction.
|
||||
|
||||
Sources:
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/average-directional-movement-adx/
|
||||
|
||||
Calculation:
|
||||
DMI ADX TREND 2.0 by @TraderR0BERT, NETWORTHIE.COM
|
||||
//Created by @TraderR0BERT, NETWORTHIE.COM, last updated 01/26/2016
|
||||
//DMI Indicator
|
||||
//Resolution input option for higher/lower time frames
|
||||
study(title="DMI ADX TREND 2.0", shorttitle="ADX TREND 2.0")
|
||||
|
||||
adxlen = input(14, title="ADX Smoothing")
|
||||
dilen = input(14, title="DI Length")
|
||||
thold = input(20, title="Threshold")
|
||||
|
||||
threshold = thold
|
||||
|
||||
//Script for Indicator
|
||||
dirmov(len) =>
|
||||
up = change(high)
|
||||
down = -change(low)
|
||||
truerange = rma(tr, len)
|
||||
plus = fixnan(100 * rma(up > down and up > 0 ? up : 0, len) / truerange)
|
||||
minus = fixnan(100 * rma(down > up and down > 0 ? down : 0, len) / truerange)
|
||||
[plus, minus]
|
||||
|
||||
adx(dilen, adxlen) =>
|
||||
[plus, minus] = dirmov(dilen)
|
||||
sum = plus + minus
|
||||
adx = 100 * rma(abs(plus - minus) / (sum == 0 ? 1 : sum), adxlen)
|
||||
[adx, plus, minus]
|
||||
|
||||
[sig, up, down] = adx(dilen, adxlen)
|
||||
osob=input(40,title="Exhaustion Level for ADX, default = 40")
|
||||
col = sig >= sig[1] ? green : sig <= sig[1] ? red : gray
|
||||
|
||||
//Plot Definitions Current Timeframe
|
||||
p1 = plot(sig, color=col, linewidth = 3, title="ADX")
|
||||
p2 = plot(sig, color=col, style=circles, linewidth=3, title="ADX")
|
||||
p3 = plot(up, color=blue, linewidth = 3, title="+DI")
|
||||
p4 = plot(up, color=blue, style=circles, linewidth=3, title="+DI")
|
||||
p5 = plot(down, color=fuchsia, linewidth = 3, title="-DI")
|
||||
p6 = plot(down, color=fuchsia, style=circles, linewidth=3, title="-DI")
|
||||
h1 = plot(threshold, color=black, linewidth =3, title="Threshold")
|
||||
|
||||
trender = (sig >= up or sig >= down) ? 1 : 0
|
||||
bgcolor(trender>0?black:gray, transp=85)
|
||||
|
||||
//Alert Function for ADX crossing Threshold
|
||||
Up_Cross = crossover(up, threshold)
|
||||
alertcondition(Up_Cross, title="DMI+ cross", message="DMI+ Crossing Threshold")
|
||||
Down_Cross = crossover(down, threshold)
|
||||
alertcondition(Down_Cross, title="DMI- cross", message="DMI- Crossing Threshold")
|
||||
|
||||
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
|
||||
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: adx, dmp, dmn columns.
|
||||
"""
|
||||
|
||||
|
||||
aroon.__doc__ = \
|
||||
"""Aroon (AROON)
|
||||
|
||||
Aroon attempts to identify if a security is trending and how strong.
|
||||
|
||||
Sources:
|
||||
https://www.tradingview.com/wiki/Aroon
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/aroon-ar/
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=1
|
||||
def maxidx(x):
|
||||
return 100 * (int(np.argmax(x)) + 1) / length
|
||||
|
||||
def minidx(x):
|
||||
return 100 * (int(np.argmin(x)) + 1) / length
|
||||
|
||||
_close = close.rolling(length, min_periods=min_periods)
|
||||
aroon_up = _close.apply(maxidx, raw=True)
|
||||
aroon_down = _close.apply(minidx, raw=True)
|
||||
|
||||
Args:
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): It's 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: aroon_up, aroon_down columns.
|
||||
"""
|
||||
|
||||
|
||||
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:
|
||||
|
||||
Calculation:
|
||||
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
|
||||
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.
|
||||
"""
|
||||
|
||||
|
||||
dpo.__doc__ = \
|
||||
"""Detrend Price Oscillator (DPO)
|
||||
|
||||
Is an indicator designed to remove trend from price and make it easier to
|
||||
identify cycles.
|
||||
|
||||
Sources:
|
||||
http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:detrended_price_osci
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=1, centered=True
|
||||
SMA = Simple Moving Average
|
||||
drift = int(0.5 * length) + 1
|
||||
|
||||
DPO = close.shift(drift) - SMA(close, length)
|
||||
if centered:
|
||||
DPO = DPO.shift(-drift)
|
||||
|
||||
Args:
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): It's period. Default: 1
|
||||
centered (bool): Shift the dpo back by int(0.5 * length) + 1. Default: True
|
||||
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.
|
||||
"""
|
||||
|
||||
|
||||
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:
|
||||
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
|
||||
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.
|
||||
"""
|
||||
|
||||
|
||||
qstick.__doc__ = \
|
||||
"""Q Stick
|
||||
|
||||
The Q Stick indicator, developed by Tushar Chande, attempts to quantify and identify
|
||||
trends in candlestick charts.
|
||||
|
||||
Sources:
|
||||
https://library.tradingtechnologies.com/trade/chrt-ti-qstick.html
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=10
|
||||
xMA is one of: sma (default), dema, ema, hma, rma
|
||||
qstick = xMA(close - open, length)
|
||||
|
||||
Args:
|
||||
open (pd.Series): Series of 'open's
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): It's period. Default: 1
|
||||
ma (str): The type of moving average to use. Default: None, which is 'sma'
|
||||
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.
|
||||
"""
|
||||
|
||||
|
||||
vortex.__doc__ = \
|
||||
"""Vortex
|
||||
|
||||
Two oscillators that capture positive and negative trend movement.
|
||||
|
||||
Sources:
|
||||
https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:vortex_indicator
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=14, drift=1
|
||||
TR = True Range
|
||||
SMA = Simple Moving Average
|
||||
tr = TR(high, low, close)
|
||||
tr_sum = tr.rolling(length).sum()
|
||||
|
||||
vmp = (high - low.shift(drift)).abs()
|
||||
vmn = (low - high.shift(drift)).abs()
|
||||
|
||||
VIP = vmp.rolling(length).sum() / tr_sum
|
||||
VIM = vmn.rolling(length).sum() / tr_sum
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): ROC 1 period. Default: 14
|
||||
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: vip and vim columns
|
||||
"""
|
||||
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
@@ -0,0 +1,141 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame
|
||||
from ..overlap import rma
|
||||
from ..volatility.atr import atr
|
||||
from ..utils import get_drift, get_offset, verify_series, zero
|
||||
|
||||
def adx(high, low, close, length=None, drift=None, offset=None, **kwargs):
|
||||
"""Indicator: ADX"""
|
||||
# Validate Arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
length = length if length and length > 0 else 14
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
_atr = atr(high=high, low=low, close=close, length=length)
|
||||
|
||||
up = high - high.shift(drift)
|
||||
dn = low.shift(drift) - low
|
||||
|
||||
pos = ((up > dn) & (up > 0)) * up
|
||||
neg = ((dn > up) & (dn > 0)) * dn
|
||||
|
||||
pos = pos.apply(zero)
|
||||
neg = neg.apply(zero)
|
||||
|
||||
dmp = (100 / _atr) * rma(close=pos, length=length)
|
||||
dmn = (100 / _atr) * rma(close=neg, length=length)
|
||||
|
||||
dx = 100 * (dmp - dmn).abs() / (dmp + dmn)
|
||||
adx = rma(close=dx, length=length)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
dmp = dmp.shift(offset)
|
||||
dmn = dmn.shift(offset)
|
||||
adx = adx.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
adx.fillna(kwargs['fillna'], inplace=True)
|
||||
dmp.fillna(kwargs['fillna'], inplace=True)
|
||||
dmn.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
adx.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
dmp.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
dmn.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
adx.name = f"ADX_{length}"
|
||||
dmp.name = f"DMP_{length}"
|
||||
dmn.name = f"DMN_{length}"
|
||||
|
||||
adx.category = dmp.category = dmn.category = 'trend'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {adx.name: adx, dmp.name: dmp, dmn.name: dmn}
|
||||
adxdf = DataFrame(data)
|
||||
adxdf.name = f"ADX_{length}"
|
||||
adxdf.category = 'trend'
|
||||
|
||||
return adxdf
|
||||
|
||||
|
||||
|
||||
adx.__doc__ = \
|
||||
"""Average Directional Movement (ADX)
|
||||
|
||||
Average Directional Movement is meant to quantify trend strength by measuring
|
||||
the amount of movement in a single direction.
|
||||
|
||||
Sources:
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/average-directional-movement-adx/
|
||||
|
||||
Calculation:
|
||||
DMI ADX TREND 2.0 by @TraderR0BERT, NETWORTHIE.COM
|
||||
//Created by @TraderR0BERT, NETWORTHIE.COM, last updated 01/26/2016
|
||||
//DMI Indicator
|
||||
//Resolution input option for higher/lower time frames
|
||||
study(title="DMI ADX TREND 2.0", shorttitle="ADX TREND 2.0")
|
||||
|
||||
adxlen = input(14, title="ADX Smoothing")
|
||||
dilen = input(14, title="DI Length")
|
||||
thold = input(20, title="Threshold")
|
||||
|
||||
threshold = thold
|
||||
|
||||
//Script for Indicator
|
||||
dirmov(len) =>
|
||||
up = change(high)
|
||||
down = -change(low)
|
||||
truerange = rma(tr, len)
|
||||
plus = fixnan(100 * rma(up > down and up > 0 ? up : 0, len) / truerange)
|
||||
minus = fixnan(100 * rma(down > up and down > 0 ? down : 0, len) / truerange)
|
||||
[plus, minus]
|
||||
|
||||
adx(dilen, adxlen) =>
|
||||
[plus, minus] = dirmov(dilen)
|
||||
sum = plus + minus
|
||||
adx = 100 * rma(abs(plus - minus) / (sum == 0 ? 1 : sum), adxlen)
|
||||
[adx, plus, minus]
|
||||
|
||||
[sig, up, down] = adx(dilen, adxlen)
|
||||
osob=input(40,title="Exhaustion Level for ADX, default = 40")
|
||||
col = sig >= sig[1] ? green : sig <= sig[1] ? red : gray
|
||||
|
||||
//Plot Definitions Current Timeframe
|
||||
p1 = plot(sig, color=col, linewidth = 3, title="ADX")
|
||||
p2 = plot(sig, color=col, style=circles, linewidth=3, title="ADX")
|
||||
p3 = plot(up, color=blue, linewidth = 3, title="+DI")
|
||||
p4 = plot(up, color=blue, style=circles, linewidth=3, title="+DI")
|
||||
p5 = plot(down, color=fuchsia, linewidth = 3, title="-DI")
|
||||
p6 = plot(down, color=fuchsia, style=circles, linewidth=3, title="-DI")
|
||||
h1 = plot(threshold, color=black, linewidth =3, title="Threshold")
|
||||
|
||||
trender = (sig >= up or sig >= down) ? 1 : 0
|
||||
bgcolor(trender>0?black:gray, transp=85)
|
||||
|
||||
//Alert Function for ADX crossing Threshold
|
||||
Up_Cross = crossover(up, threshold)
|
||||
alertcondition(Up_Cross, title="DMI+ cross", message="DMI+ Crossing Threshold")
|
||||
Down_Cross = crossover(down, threshold)
|
||||
alertcondition(Down_Cross, title="DMI- cross", message="DMI- Crossing Threshold")
|
||||
|
||||
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
|
||||
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: adx, dmp, dmn columns.
|
||||
"""
|
||||
@@ -0,0 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame
|
||||
from .long_run import long_run
|
||||
from ..overlap import ema, hma, linreg, rma, sma, wma
|
||||
from .short_run import short_run
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
def amat(close=None, fast=None, slow=None, mamode=None, lookback=None, offset=None, **kwargs):
|
||||
"""Indicator: Archer Moving Averages Trends (AMAT)"""
|
||||
# Validate Arguments
|
||||
close = verify_series(close)
|
||||
fast = int(fast) if fast and fast > 0 else 8
|
||||
slow = int(slow) if slow and slow > 0 else 21
|
||||
lookback = int(lookback) if lookback and lookback > 0 else 2
|
||||
mamode = mamode.upper() if mamode else 'EMA'
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
if mamode == 'EMA':
|
||||
fast_ma = ema(close=close, length=fast, **kwargs)
|
||||
slow_ma = ema(close=close, length=slow, **kwargs)
|
||||
elif mamode == 'HMA':
|
||||
fast_ma = hma(close=close, length=fast, **kwargs)
|
||||
slow_ma = hma(close=close, length=slow, **kwargs)
|
||||
elif mamode == 'LINREG':
|
||||
fast_ma = linreg(close=close, length=fast, **kwargs)
|
||||
slow_ma = linreg(close=close, length=slow, **kwargs)
|
||||
elif mamode == 'RMA':
|
||||
fast_ma = rma(close=close, length=fast, **kwargs)
|
||||
slow_ma = rma(close=close, length=slow, **kwargs)
|
||||
elif mamode == 'SMA':
|
||||
fast_ma = sma(close=close, length=fast, **kwargs)
|
||||
slow_ma = sma(close=close, length=slow, **kwargs)
|
||||
elif mamode == 'WMA':
|
||||
fast_ma = wma(close=close, length=fast, **kwargs)
|
||||
slow_ma = wma(close=close, length=slow, **kwargs)
|
||||
|
||||
mas_long = long_run(fast_ma, slow_ma, length=lookback)
|
||||
mas_short = short_run(fast_ma, slow_ma, length=lookback)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
mas_long = mas_long.shift(offset)
|
||||
mas_short = mas_short.shift(offset)
|
||||
|
||||
# # Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
mas_long.fillna(kwargs['fillna'], inplace=True)
|
||||
mas_short.fillna(kwargs['fillna'], inplace=True)
|
||||
|
||||
if 'fill_method' in kwargs:
|
||||
mas_long.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
mas_short.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Prepare DataFrame to return
|
||||
amatdf = DataFrame({
|
||||
f"AMAT_{mas_long.name}": mas_long,
|
||||
f"AMAT_{mas_short.name}": mas_short
|
||||
})
|
||||
|
||||
# Name and Categorize it
|
||||
amatdf.name = f"AMAT_{mamode}_{fast}_{slow}_{lookback}"
|
||||
amatdf.category = 'trend'
|
||||
|
||||
return amatdf
|
||||
@@ -0,0 +1,88 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import argmax as npargmax
|
||||
from numpy import argmin as npargmin
|
||||
from pandas import DataFrame
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
def aroon(close, length=None, offset=None, **kwargs):
|
||||
"""Indicator: Aroon Oscillator"""
|
||||
# Validate Arguments
|
||||
close = verify_series(close)
|
||||
length = 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)
|
||||
|
||||
# Calculate Result
|
||||
def maxidx(x):
|
||||
return 100 * (int(npargmax(x)) + 1) / length
|
||||
|
||||
def minidx(x):
|
||||
return 100 * (int(npargmin(x)) + 1) / length
|
||||
|
||||
_close = close.rolling(length, min_periods=min_periods)
|
||||
aroon_up = _close.apply(maxidx, raw=True)
|
||||
aroon_down = _close.apply(minidx, raw=True)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
aroon_up.fillna(kwargs['fillna'], inplace=True)
|
||||
aroon_down.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
aroon_up.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
aroon_down.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
aroon_up = aroon_up.shift(offset)
|
||||
aroon_down = aroon_down.shift(offset)
|
||||
|
||||
# Name and Categorize it
|
||||
aroon_up.name = f"AROONU_{length}"
|
||||
aroon_down.name = f"AROOND_{length}"
|
||||
|
||||
aroon_down.category = aroon_up.category = 'trend'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {aroon_down.name: aroon_down, aroon_up.name: aroon_up}
|
||||
aroondf = DataFrame(data)
|
||||
aroondf.name = f"AROON_{length}"
|
||||
aroondf.category = 'trend'
|
||||
|
||||
return aroondf
|
||||
|
||||
|
||||
|
||||
aroon.__doc__ = \
|
||||
"""Aroon (AROON)
|
||||
|
||||
Aroon attempts to identify if a security is trending and how strong.
|
||||
|
||||
Sources:
|
||||
https://www.tradingview.com/wiki/Aroon
|
||||
https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/aroon-ar/
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=1
|
||||
def maxidx(x):
|
||||
return 100 * (int(np.argmax(x)) + 1) / length
|
||||
|
||||
def minidx(x):
|
||||
return 100 * (int(np.argmin(x)) + 1) / length
|
||||
|
||||
_close = close.rolling(length, min_periods=min_periods)
|
||||
aroon_up = _close.apply(maxidx, raw=True)
|
||||
aroon_down = _close.apply(minidx, raw=True)
|
||||
|
||||
Args:
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): It's 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: aroon_up, aroon_down columns.
|
||||
"""
|
||||
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
def decreasing(close, length=None, asint=True, offset=None, **kwargs):
|
||||
"""Indicator: Decreasing"""
|
||||
# Validate Arguments
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 1
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
decreasing = close.diff(length) < 0
|
||||
if asint:
|
||||
decreasing = decreasing.astype(int)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
decreasing = decreasing.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
decreasing.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
decreasing.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
decreasing.name = f"DEC_{length}"
|
||||
decreasing.category = 'trend'
|
||||
|
||||
return decreasing
|
||||
|
||||
|
||||
|
||||
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:
|
||||
|
||||
Calculation:
|
||||
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
|
||||
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,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
def dpo(close, length=None, centered=True, offset=None, **kwargs):
|
||||
"""Indicator: Detrend Price Oscillator (DPO)"""
|
||||
# Validate Arguments
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 1
|
||||
min_periods = int(kwargs['min_periods']) if 'min_periods' in kwargs and kwargs['min_periods'] is not None else length
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
drift = int(0.5 * length) + 1 # int((0.5 * length) + 1)
|
||||
dpo = close.shift(drift) - close.rolling(length, min_periods=min_periods).mean()
|
||||
if centered:
|
||||
dpo = dpo.shift(-drift)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
dpo = dpo.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
dpo.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
dpo.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
dpo.name = f"DPO_{length}"
|
||||
dpo.category = 'trend'
|
||||
|
||||
return dpo
|
||||
|
||||
|
||||
|
||||
dpo.__doc__ = \
|
||||
"""Detrend Price Oscillator (DPO)
|
||||
|
||||
Is an indicator designed to remove trend from price and make it easier to
|
||||
identify cycles.
|
||||
|
||||
Sources:
|
||||
http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:detrended_price_osci
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=1, centered=True
|
||||
SMA = Simple Moving Average
|
||||
drift = int(0.5 * length) + 1
|
||||
|
||||
DPO = close.shift(drift) - SMA(close, length)
|
||||
if centered:
|
||||
DPO = DPO.shift(-drift)
|
||||
|
||||
Args:
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): It's period. Default: 1
|
||||
centered (bool): Shift the dpo back by int(0.5 * length) + 1. Default: True
|
||||
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,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
def increasing(close, length=None, asint=True, offset=None, **kwargs):
|
||||
"""Indicator: Increasing"""
|
||||
# Validate Arguments
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 1
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
increasing = close.diff(length) > 0
|
||||
if asint:
|
||||
increasing = increasing.astype(int)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
increasing = increasing.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
increasing.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
increasing.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
increasing.name = f"INC_{length}"
|
||||
increasing.category = 'trend'
|
||||
|
||||
return 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:
|
||||
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
|
||||
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,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .decreasing import decreasing
|
||||
from .increasing import increasing
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
def long_run(fast, slow, length=None, offset=None, **kwargs):
|
||||
"""Indicator: Long Run"""
|
||||
# Validate Arguments
|
||||
fast = verify_series(fast)
|
||||
slow = verify_series(slow)
|
||||
length = int(length) if length and length > 0 else 2
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
pb = increasing(fast, length) & decreasing(slow, length) # potential bottom or bottom
|
||||
bi = increasing(fast, length) & increasing(slow, length) # fast and slow are increasing
|
||||
long_run = pb | bi
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
long_run = long_run.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
long_run.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
long_run.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
long_run.name = f"LR_{length}"
|
||||
long_run.category = 'trend'
|
||||
|
||||
return long_run
|
||||
@@ -0,0 +1,69 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from ..overlap import dema, ema, hma, rma, sma
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
def qstick(open_, close, length=None, offset=None, **kwargs):
|
||||
"""Indicator: Q Stick"""
|
||||
# Validate Arguments
|
||||
open_ = verify_series(open_)
|
||||
close = verify_series(close)
|
||||
length = int(length) if length and length > 0 else 10
|
||||
offset = get_offset(offset)
|
||||
ma = kwargs.pop('ma', 'sma') if 'ma' in kwargs else 'sma'
|
||||
|
||||
# Calculate Result
|
||||
diff = close - open_
|
||||
|
||||
if ma in [None, 'sma']: qstick = sma(diff, length=length)
|
||||
if ma == 'dema': qstick = dema(diff, length=length, **kwargs)
|
||||
if ma == 'ema': qstick = ema(diff, length=length, **kwargs)
|
||||
if ma == 'hma': qstick = hma(diff, length=length)
|
||||
if ma == 'rma': qstick = rma(diff, length=length)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
qstick = qstick.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
qstick.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
qstick.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
qstick.name = f"QS_{length}"
|
||||
qstick.category = 'trend'
|
||||
|
||||
return qstick
|
||||
|
||||
|
||||
|
||||
qstick.__doc__ = \
|
||||
"""Q Stick
|
||||
|
||||
The Q Stick indicator, developed by Tushar Chande, attempts to quantify and identify
|
||||
trends in candlestick charts.
|
||||
|
||||
Sources:
|
||||
https://library.tradingtechnologies.com/trade/chrt-ti-qstick.html
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=10
|
||||
xMA is one of: sma (default), dema, ema, hma, rma
|
||||
qstick = xMA(close - open, length)
|
||||
|
||||
Args:
|
||||
open (pd.Series): Series of 'open's
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): It's period. Default: 1
|
||||
ma (str): The type of moving average to use. Default: None, which is 'sma'
|
||||
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,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .decreasing import decreasing
|
||||
from .increasing import increasing
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
def short_run(fast, slow, length=None, offset=None, **kwargs):
|
||||
"""Indicator: Short Run"""
|
||||
# Validate Arguments
|
||||
fast = verify_series(fast)
|
||||
slow = verify_series(slow)
|
||||
length = int(length) if length and length > 0 else 2
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
pt = decreasing(fast, length) & increasing(slow, length) # potential top or top
|
||||
bd = decreasing(fast, length) & decreasing(slow, length) # fast and slow are decreasing
|
||||
short_run = pt | bd
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
short_run = short_run.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
short_run.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
short_run.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
short_run.name = f"SR_{length}"
|
||||
short_run.category = 'trend'
|
||||
|
||||
return short_run
|
||||
@@ -0,0 +1,91 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame
|
||||
from ..volatility.true_range import true_range
|
||||
from ..utils import get_drift, get_offset, verify_series, zero
|
||||
|
||||
def vortex(high, low, close, length=None, drift=None, offset=None, **kwargs):
|
||||
"""Indicator: Vortex"""
|
||||
# Validate arguments
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
length = 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
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
tr = true_range(high=high, low=low, close=close)
|
||||
tr_sum = tr.rolling(length, min_periods=min_periods).sum()
|
||||
|
||||
vmp = (high - low.shift(drift)).abs()
|
||||
vmm = (low - high.shift(drift)).abs()
|
||||
|
||||
vip = vmp.rolling(length, min_periods=min_periods).sum() / tr_sum
|
||||
vim = vmm.rolling(length, min_periods=min_periods).sum() / tr_sum
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
vip = vip.shift(offset)
|
||||
vim = vim.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
if 'fillna' in kwargs:
|
||||
vip.fillna(kwargs['fillna'], inplace=True)
|
||||
vim.fillna(kwargs['fillna'], inplace=True)
|
||||
if 'fill_method' in kwargs:
|
||||
vip.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
vim.fillna(method=kwargs['fill_method'], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
vip.name = f"VTXP_{length}"
|
||||
vim.name = f"VTXM_{length}"
|
||||
vip.category = vim.category = 'trend'
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {vip.name: vip, vim.name: vim}
|
||||
vtxdf = DataFrame(data)
|
||||
vtxdf.name = f"VTX_{length}"
|
||||
vtxdf.category = 'trend'
|
||||
|
||||
return vtxdf
|
||||
|
||||
|
||||
|
||||
vortex.__doc__ = \
|
||||
"""Vortex
|
||||
|
||||
Two oscillators that capture positive and negative trend movement.
|
||||
|
||||
Sources:
|
||||
https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:vortex_indicator
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
length=14, drift=1
|
||||
TR = True Range
|
||||
SMA = Simple Moving Average
|
||||
tr = TR(high, low, close)
|
||||
tr_sum = tr.rolling(length).sum()
|
||||
|
||||
vmp = (high - low.shift(drift)).abs()
|
||||
vmn = (low - high.shift(drift)).abs()
|
||||
|
||||
VIP = vmp.rolling(length).sum() / tr_sum
|
||||
VIM = vmn.rolling(length).sum() / tr_sum
|
||||
|
||||
Args:
|
||||
high (pd.Series): Series of 'high's
|
||||
low (pd.Series): Series of 'low's
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): ROC 1 period. Default: 14
|
||||
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: vip and vim columns
|
||||
"""
|
||||
@@ -1,7 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .obv import obv
|
||||
from ..overlap import *
|
||||
from ..trend import long_run, short_run
|
||||
from ..trend.long_run import long_run
|
||||
from ..trend.short_run import short_run
|
||||
from ..utils import get_offset, verify_series
|
||||
|
||||
def aobv(close, volume, fast=None, slow=None, mamode=None, max_lookback=None, min_lookback=None, offset=None, **kwargs):
|
||||
|
||||
@@ -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.23b",
|
||||
version = "0.1.24b",
|
||||
description=long_description,
|
||||
long_description=long_description,
|
||||
author = "Kevin Johnson",
|
||||
|
||||
@@ -30,15 +30,12 @@ class TestTrend(TestCase):
|
||||
del cls.data
|
||||
|
||||
|
||||
def setUp(self):
|
||||
self.trend = pandas_ta.trend
|
||||
|
||||
def tearDown(self):
|
||||
del self.trend
|
||||
def setUp(self): pass
|
||||
def tearDown(self): pass
|
||||
|
||||
|
||||
def test_adx(self):
|
||||
result = self.trend.adx(self.high, self.low, self.close)
|
||||
result = pandas_ta.adx(self.high, self.low, self.close)
|
||||
self.assertIsInstance(result, DataFrame)
|
||||
self.assertEqual(result.name, 'ADX_14')
|
||||
|
||||
@@ -53,12 +50,12 @@ class TestTrend(TestCase):
|
||||
error_analysis(result, CORRELATION, ex)
|
||||
|
||||
def test_amat(self):
|
||||
result = self.trend.amat(self.close)
|
||||
result = pandas_ta.amat(self.close)
|
||||
self.assertIsInstance(result, DataFrame)
|
||||
self.assertEqual(result.name, 'AMAT_EMA_8_21_2')
|
||||
|
||||
def test_aroon(self):
|
||||
result = self.trend.aroon(self.close)
|
||||
result = pandas_ta.aroon(self.close)
|
||||
self.assertIsInstance(result, DataFrame)
|
||||
self.assertEqual(result.name, 'AROON_14')
|
||||
|
||||
@@ -80,36 +77,36 @@ class TestTrend(TestCase):
|
||||
error_analysis(result.iloc[:,1], CORRELATION, ex, newline=False)
|
||||
|
||||
def test_decreasing(self):
|
||||
result = self.trend.decreasing(self.close)
|
||||
result = pandas_ta.decreasing(self.close)
|
||||
self.assertIsInstance(result, Series)
|
||||
self.assertEqual(result.name, 'DEC_1')
|
||||
|
||||
def test_dpo(self):
|
||||
result = self.trend.dpo(self.close)
|
||||
result = pandas_ta.dpo(self.close)
|
||||
self.assertIsInstance(result, Series)
|
||||
self.assertEqual(result.name, 'DPO_1')
|
||||
|
||||
def test_increasing(self):
|
||||
result = self.trend.increasing(self.close)
|
||||
result = pandas_ta.increasing(self.close)
|
||||
self.assertIsInstance(result, Series)
|
||||
self.assertEqual(result.name, 'INC_1')
|
||||
|
||||
def test_long_run(self):
|
||||
result = self.trend.long_run(self.close, self.open)
|
||||
result = pandas_ta.long_run(self.close, self.open)
|
||||
self.assertIsInstance(result, Series)
|
||||
self.assertEqual(result.name, 'LR_2')
|
||||
|
||||
def test_qstick(self):
|
||||
result = self.trend.qstick(self.open, self.close)
|
||||
result = pandas_ta.qstick(self.open, self.close)
|
||||
self.assertIsInstance(result, Series)
|
||||
self.assertEqual(result.name, 'QS_10')
|
||||
|
||||
def test_short_run(self):
|
||||
result = self.trend.short_run(self.close, self.open)
|
||||
result = pandas_ta.short_run(self.close, self.open)
|
||||
self.assertIsInstance(result, Series)
|
||||
self.assertEqual(result.name, 'SR_2')
|
||||
|
||||
def test_vortex(self):
|
||||
result = self.trend.vortex(self.high, self.low, self.close)
|
||||
result = pandas_ta.vortex(self.high, self.low, self.close)
|
||||
self.assertIsInstance(result, DataFrame)
|
||||
self.assertEqual(result.name, 'VTX_14')
|
||||
Reference in New Issue
Block a user