This commit is contained in:
Ichinga Samuel
2025-08-10 18:36:08 +01:00
parent b93ff61aa7
commit 0d166565f2
188 changed files with 999 additions and 21263 deletions
+3 -3
View File
@@ -18,9 +18,9 @@ keywords = ["MetaTrader5", "Asynchronous", "Algorithmic Trading", "Trading Bot",
dependencies = [
"MetaTrader5>=5.0.5200",
"pandas>=1.5.0",
"mplfinance>=0.10.1",
"pandas-stubs==2.3.0.250703",
"pandas>=2.0.0",
"mplfinance>=0.12.10b0",
"numba>=0.61.2",
"tqdm>=4.67.1",
]
-160
View File
@@ -1,160 +0,0 @@
-e git+ssh://git@github.com/Chimezirim-Bassey/aiomql.git@bae2983bded5b3912f7234fd0acaa89ab84fa1c4#egg=aiomql
anyio==4.3.0
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
arrow==1.3.0
asttokens==2.4.1
async-lru==2.0.4
attrs==23.2.0
Babel==2.14.0
beautifulsoup4==4.12.3
black==23.9.1
bleach==6.1.0
build==1.2.2.post1
certifi==2023.7.22
cffi==1.16.0
charset-normalizer==3.3.0
click==8.1.7
colorama==0.4.6
comm==0.2.2
contourpy==1.2.1
cycler==0.12.1
databind.core==4.4.1
databind.json==4.4.1
debugpy==1.8.1
decorator==5.1.1
defusedxml==0.7.1
Deprecated==1.2.14
docspec==2.2.1
docspec-python==2.2.1
docstring-parser==0.11
docutils==0.20.1
docutils-stubs==0.0.22
executing==2.0.1
fastjsonschema==2.19.1
fonttools==4.51.0
fqdn==1.5.1
h11==0.14.0
httpcore==1.0.5
httpx==0.27.0
id==1.5.0
idna==3.4
importlib-metadata==6.8.0
iniconfig==2.0.0
ipykernel==6.29.4
ipython==8.23.0
ipywidgets==8.1.2
isoduration==20.11.0
jaraco.classes==3.3.0
jedi==0.19.1
Jinja2==3.1.2
json5==0.9.24
jsonpointer==2.4
jsonschema==4.21.1
jsonschema-specifications==2023.12.1
jupyter==1.0.0
jupyter-console==6.6.3
jupyter-events==0.10.0
jupyter-lsp==2.2.5
jupyter_client==8.6.1
jupyter_core==5.7.2
jupyter_server==2.13.0
jupyter_server_terminals==0.5.3
jupyterlab==4.1.6
jupyterlab_pygments==0.3.0
jupyterlab_server==2.26.0
jupyterlab_widgets==3.0.10
keyring==24.2.0
kiwisolver==1.4.5
markdown-it-py==3.0.0
MarkupSafe==2.1.3
matplotlib==3.8.4
matplotlib-inline==0.1.6
mdurl==0.1.2
MetaTrader5==5.0.5200
mistune==3.0.2
more-itertools==10.1.0
mplfinance==0.12.10b0
mypy-extensions==1.0.0
nbclient==0.10.0
nbconvert==7.16.3
nbformat==5.10.4
nest-asyncio==1.6.0
nh3==0.2.14
notebook==7.1.2
notebook_shim==0.2.4
nr-date==2.1.0
nr-stream==1.1.5
nr.util==0.8.12
numpy==1.26.0
overrides==7.7.0
packaging==25.0
pandas==2.1.1
pandas-ta==0.3.14b0
pandocfilters==1.5.1
parso==0.8.4
pathspec==0.11.2
pillow==10.3.0
pkginfo==1.9.6
platformdirs==3.11.0
pluggy==1.5.0
prometheus_client==0.20.0
prompt-toolkit==3.0.43
psutil==5.9.8
pure-eval==0.2.2
pycparser==2.22
pydoc-markdown==4.8.2
Pygments==2.16.1
pyparsing==3.1.2
pyproject_hooks==1.0.0
pytest==8.3.3
pytest-asyncio==0.24.0
pytest-order==1.3.0
python-dateutil==2.8.2
python-json-logger==2.0.7
python-telegram-bot==21.0.1
pytz==2023.3.post1
pywin32==306
pywin32-ctypes==0.2.2
pywinpty==2.0.13
PyYAML==6.0.1
pyzmq==25.1.2
qtconsole==5.5.1
QtPy==2.4.1
readme-renderer==42.0
referencing==0.34.0
requests==2.31.0
requests-toolbelt==1.0.0
rfc3339-validator==0.1.4
rfc3986==2.0.0
rfc3986-validator==0.1.1
rich==13.6.0
rpds-py==0.18.0
ruff==0.7.3
Send2Trash==1.8.3
six==1.16.0
sniffio==1.3.1
soupsieve==2.5
stack-data==0.6.3
terminado==0.18.1
tinycss2==1.2.1
tomli==2.0.1
tomli_w==1.0.0
tornado==6.4
traitlets==5.14.2
twine==6.1.0
typeapi==2.1.1
types-python-dateutil==2.9.0.20240316
typing_extensions==4.6.3
tzdata==2023.3
uri-template==1.3.0
urllib3==2.0.6
watchdog==3.0.0
wcwidth==0.2.13
webcolors==1.13
webencodings==0.5.1
websocket-client==1.7.0
widgetsnbextension==4.0.10
wrapt==1.15.0
yapf==0.40.2
zipp==3.17.0
+3 -1
View File
@@ -58,6 +58,7 @@ class OpenPosition:
logger.error("%s: Unable to remove closed position from state", exe)
async def update_position(self) -> bool:
# ToDo: remove pending order
pos = await self.positions.get_position_by_ticket(ticket=self.ticket)
if pos is not None:
self.position = pos
@@ -69,6 +70,7 @@ class OpenPosition:
async def modify_stops(self, *, sl: float = None, tp: float = None,
use_stop_levels=False) -> tuple[bool, OrderSendResult | None]:
try:
# todo: add stops
tick = await self.symbol.info_tick()
# modify stop_loss
@@ -147,7 +149,7 @@ class OpenPosition:
logger.error("%s: Error occurred in track method of Open Position for %d:%s",
exe, self.symbol.name, self.ticket)
async def get_price_from_profit(self, profit):
async def profit_to_price(self, profit):
action = OrderType.BUY if self.position.type == 0 else OrderType.SELL
volume = self.position.volume
price_open = self.position.price_open
+3 -2
View File
@@ -80,9 +80,10 @@ class Order(_Base, TradeRequest):
return tuple()
@classmethod
async def cancel_order(cls, *, ticket: int, symbol: str) -> TradeOrder | None:
async def cancel_order(cls, *, order: int, symbol: str) -> OrderSendResult:
"""Cancel an active pending order by ticket number."""
order = cls.mt5.order_send({"symbol": symbol, "ticket": ticket, "action": TradeAction.REMOVE})
res = await cls.mt5.order_send({"symbol": symbol, "order": order, "action": TradeAction.REMOVE})
return res
async def check(self, **kwargs) -> OrderCheckResult:
"""Check funds sufficiency for performing a required trading operation and the possibility of executing it.
-60
View File
@@ -1,60 +0,0 @@
from .maps import EXCHANGE_TZ, RATE, Category, Imports
from .utils import *
from .utils import __all__ as utils_all
# Flat Structure. Supports ta.ema() or ta.overlap.ema()
from .candle import *
from .cycle import *
from .momentum import *
from .overlap import *
from .performance import *
from .statistics import *
from .trend import *
from .volatility import *
from .volume import *
from .candle import __all__ as candle_all
from .cycle import __all__ as cycle_all
from .momentum import __all__ as momentum_all
from .overlap import __all__ as overlap_all
from .performance import __all__ as performance_all
from .statistics import __all__ as statistics_all
from .trend import __all__ as trend_all
from .volatility import __all__ as volatility_all
from .volume import __all__ as volume_all
# Common Averages useful for Indicators
# with a mamode argument, like ta.adx()
from .ma import ma
# Custom External Directory Commands. See help(import_dir)
from .custom import create_dir, import_dir
# Enable "ta" DataFrame Extension
from .core import AnalysisIndicators
__all__ = [
# "name",
"EXCHANGE_TZ",
"RATE",
"Category",
"Imports",
"ma",
"create_dir",
"import_dir",
"AnalysisIndicators",
"AllStudy",
"CommonStudy",
]
__all__ += [
utils_all
+ candle_all
+ cycle_all
+ momentum_all
+ overlap_all
+ performance_all
+ statistics_all
+ trend_all
+ volatility_all
+ volume_all
]
-7
View File
@@ -1,7 +0,0 @@
#-*- coding: utf-8 -*-
from pandas_ta import version
SUPPORT="http://www.pandas-ta.dev/support"
if __name__ == "__main__":
print(f"Pandas TA: {version}\nSupport: {SUPPORT}")
-70
View File
@@ -1,70 +0,0 @@
from pathlib import Path
from typing import (
Any,
Dict,
Iterable,
List,
Optional,
Sequence,
TextIO,
Tuple,
TypeVar,
Union
)
from numpy import ndarray, recarray, void
from numpy import bool_ as np_bool_
from numpy import floating as np_floating
from numpy import generic as np_generic
from numpy import integer as np_integer
from numpy import number as np_number
from pandas import DataFrame, Series
# Generic types
T = TypeVar("T")
# Scalars
Scalar = Union[str, float, int, complex, bool, object, np_generic]
Number = Union[int, float, complex, np_number, np_bool_]
Int = int | np_integer
Float = Union[float, np_floating]
IntFloat = Union[Int, Float]
# Basic sequences
MaybeTuple = Union[T, Tuple[T, ...]]
MaybeList = Union[T, List[T]]
TupleList = Union[List[T], Tuple[T, ...]]
MaybeTupleList = Union[T, List[T], Tuple[T, ...]]
MaybeIterable = Union[T, Iterable[T]]
MaybeSequence = Union[T, Sequence[T]]
ListStr = List[str]
DictLike = Union[None, dict]
DictLikeSequence = MaybeSequence[DictLike]
Args = Tuple[Any, ...]
ArgsLike = Union[None, Args]
Kwargs = Dict[str, Any]
KwargsLike = Union[None, Kwargs]
KwargsLikeSequence = MaybeSequence[KwargsLike]
FileName = Union[str, Path]
DTypeLike = Any
PandasDTypeLike = Any
Shape = Tuple[int, ...]
RelaxedShape = Union[int, Shape]
Array = ndarray
Array1d = ndarray
Array2d = ndarray
Array3d = ndarray
Record = void
RecordArray = ndarray
RecArray = recarray
MaybeArray = Union[T, Array]
SeriesFrame = Union[Series, DataFrame]
MaybeSeries = Union[T, Series]
MaybeSeriesFrame = Union[T, Series, DataFrame]
AnyArray = Union[Array, Series, DataFrame]
AnyArray1d = Union[Array1d, Series]
AnyArray2d = Union[Array2d, DataFrame]
@@ -1,16 +0,0 @@
# -*- coding: utf-8 -*-
from .cdl_doji import cdl_doji
from .cdl_inside import cdl_inside
from .cdl_pattern import cdl_pattern, cdl, ALL_PATTERNS as CDL_PATTERN_NAMES
from .cdl_z import cdl_z
from .ha import ha
__all__ = [
"cdl_doji",
"cdl_inside",
"cdl_pattern",
"cdl",
"CDL_PATTERN_NAMES",
"cdl_z",
"ha",
]
@@ -1,90 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from ..overlap import sma
from .._typing import Int, IntFloat
from ..utils import high_low_range, v_percent
from ..utils import real_body, v_offset, v_pos_default
from ..utils import v_bool, v_scalar, v_series
def cdl_doji(
open_: Series, high: Series, low: Series, close: Series,
length: Int = None, factor: IntFloat = None,
scalar: IntFloat = None, asint: bool = None,
offset: Int = None, **kwargs: dict | None
) -> Series:
"""Doji
Attempts to identify a "Doji" candle which is shorter than 10% of
the average of the 10 previous bars High-Low range.
Sources:
* [TA Lib](https://github.com/TA-Lib/ta-lib/blob/main/src/ta_func/ta_CDLDOJI.c)
Parameters:
open_ (pd.Series): ```open``` Series
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
factor (float): Doji value. Default: ```100```
scalar (float): Scalar. Default: ```100```
asint (bool): Returns as ```Int```. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
naive (bool): Prefills potential Doji; bodies that are less
than a percentage, ```factor```, of it's High-Low range.
Default: ```False```
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(pd.Series): 1 column
Warning:
TA-Lib Correlation: ```np.float64(0.9434563530497265)```
Tip:
Corrective contributions welcome!
"""
# Validate
length = v_pos_default(length, 10)
open_ = v_series(open_, length)
high = v_series(high, length)
low = v_series(low, length)
close = v_series(close, length)
if open_ is None or high is None or low is None or close is None:
return
factor = v_scalar(factor, 10) if v_percent(factor) else 10
scalar = v_scalar(scalar, 100)
asint = v_bool(asint, True)
offset = v_offset(offset)
naive = kwargs.pop("naive", False)
# Calculate
body = real_body(open_, close).abs()
hl_range = high_low_range(high, low).abs()
hl_range_avg = sma(hl_range, length)
doji = body < 0.01 * factor * hl_range_avg
if naive:
doji.iat[:length] = body < 0.01 * factor * hl_range
if asint:
doji = scalar * doji.astype(int)
# Offset
if offset != 0:
doji = doji.shift(offset)
# Fill
if "fillna" in kwargs:
doji.fillna(kwargs["fillna"], inplace=True)
# Name and Category
doji.name = f"CDL_DOJI_{length}_{0.01 * factor}"
doji.category = "candle"
return doji
@@ -1,77 +0,0 @@
from numpy import roll, where
from numba import njit
from pandas import Series
from .._typing import Int, IntFloat
from ..utils import v_bool, v_offset, v_offset, v_scalar, v_series
@njit(cache=True)
def np_cdl_inside(high, low):
hdiff = where(high - roll(high, 1) < 0, 1, 0)
ldiff = where(low - roll(low, 1) > 0, 1, 0)
return hdiff & ldiff
def cdl_inside(
open_: Series, high: Series, low: Series, close: Series,
asbool: bool = None, scalar: IntFloat = None,
offset: Int = None, **kwargs: dict | None
) -> Series:
"""Inside Bar
Attempts to identify an "Inside" candle which is smaller than it's
previous candle.
Sources:
* [TA Lib](https://github.com/TA-Lib/ta-lib/blob/main/src/ta_func/ta_CDL3INSIDE.c)
* [tradingview](https://www.tradingview.com/script/IyIGN1WO-Inside-Bar/)
Parameters:
open_ (pd.Series): ```open``` Series
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
asbool (bool): Return booleans. Default: ```False```
scalar (float): Scalar. Default: ```100```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(pd.Series): 1 column
"""
# Validate
open_ = v_series(open_)
high = v_series(high)
low = v_series(low)
close = v_series(close)
if open_ is None or high is None or low is None or close is None:
return
asbool = v_bool(asbool, False)
scalar = v_scalar(scalar, 100)
offset = v_offset(offset)
# Calculate
np_high, np_low = high.to_numpy(), low.to_numpy()
np_inside = np_cdl_inside(np_high, np_low)
inside = Series(np_inside, index=close.index, dtype=bool)
if not asbool:
inside = scalar * inside.astype(int)
# Offset
if offset != 0:
inside = inside.shift(offset)
# Fill
if "fillna" in kwargs:
inside.fillna(kwargs["fillna"], inplace=True)
# Name and Category
inside.name = f"CDL_INSIDE"
inside.category = "candle"
return inside
@@ -1,125 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series, DataFrame
from numpy import integer
from ..maps import Imports
from ..utils import v_offset, v_scalar, v_series
from ..candle import cdl_doji, cdl_inside
ALL_PATTERNS = [
"2crows", "3blackcrows", "3inside", "3linestrike", "3outside",
"3starsinsouth", "3whitesoldiers", "abandonedbaby", "advanceblock",
"belthold", "breakaway", "closingmarubozu", "concealbabyswall",
"counterattack", "darkcloudcover", "doji", "dojistar", "dragonflydoji",
"engulfing", "eveningdojistar", "eveningstar", "gapsidesidewhite",
"gravestonedoji", "hammer", "hangingman", "harami", "haramicross",
"highwave", "hikkake", "hikkakemod", "homingpigeon", "identical3crows",
"inneck", "inside", "invertedhammer", "kicking", "kickingbylength",
"ladderbottom", "longleggeddoji", "longline", "marubozu", "matchinglow",
"mathold", "morningdojistar", "morningstar", "onneck", "piercing",
"rickshawman", "risefall3methods", "separatinglines", "shootingstar",
"shortline", "spinningtop", "stalledpattern", "sticksandwich", "takuri",
"tasukigap", "thrusting", "tristar", "unique3river", "upsidegap2crows",
"xsidegap3methods"
]
def cdl_pattern(
open_: Series, high: Series, low: Series, close: Series,
name: str | list[str] = "all", scalar: int | float = None,
offset: int | integer = None, **kwargs: dict | None
) -> DataFrame:
"""Candle Pattern
This function wraps TA Lib candle patterns.
Sources:
* [TA Lib](https://ta-lib.org)
Parameters:
open_ (pd.Series): ```open``` Series
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
name (str | list[str]): Pattern name or a list of pattern names.
Default: ```"all"```
scalar (float): Scalar. Default: ```100```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(pd.DataFrame): Pattern Column(s)
Warning: TA Lib
TA Lib must be installed
"""
# Validate Arguments
open_ = v_series(open_, 1)
high = v_series(high, 1)
low = v_series(low, 1)
close = v_series(close, 1)
if open_ is None or high is None or low is None or close is None:
return
offset = v_offset(offset)
scalar = v_scalar(scalar, 100)
pta_patterns = {"doji": cdl_doji, "inside": cdl_inside}
if name == "all":
name = ALL_PATTERNS
if isinstance(name, str):
name = [name]
if Imports["talib"]:
import talib.abstract as tala
result = {}
for n in name:
if n not in ALL_PATTERNS:
print(f"[X] There is no candle pattern named {n} available!")
continue
if n in pta_patterns:
pattern_result = pta_patterns[n](
open_, high, low, close, offset=offset, scalar=scalar, **kwargs
)
if not isinstance(pattern_result, Series):
continue
result[pattern_result.name] = pattern_result
else:
if not Imports["talib"]:
print(f"[i] Requires TA-Lib to use {n}. (pip install TA-Lib)")
continue
pf = tala.Function(f"CDL{n.upper()}")
pattern_result = Series(
0.01 * scalar * pf(open_, high, low, close, **kwargs)
)
pattern_result.index = close.index
# Offset
if offset != 0:
pattern_result = pattern_result.shift(offset)
# Fill
if "fillna" in kwargs:
pattern_result.fillna(kwargs["fillna"], inplace=True)
result[f"CDL_{n.upper()}"] = pattern_result
if len(result) == 0:
return
# Name and Category
df = DataFrame(result)
df.name = "CDL_PATTERN"
df.category = "candle"
return df
cdl = cdl_pattern # Alias
@@ -1,92 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from .._typing import Int
from ..statistics import zscore
from ..utils import v_bool, v_offset, v_pos_default, v_series
def cdl_z(
open_: Series, high: Series, low: Series, close: Series,
length: Int = None, full: bool = None, ddof: Int = None,
offset: Int = None, **kwargs: dict | None
) -> DataFrame:
"""Z Candles
Creates candlesticks using a rolling Z Score.
Sources:
* Kevin Johnson
Parameters:
open_ (pd.Series): ```open``` Series
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
full (bool): Apply ```length``` to whole DataFrame.
Default: ```False```
ddof (int): By default, uses Pandas ```ddof=1```.
For Numpy calculation, use ```0```. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
naive (bool): If ```True```, prefills potential Doji less
than the length if it less than a percentage of it's
High-Low range. Default: ```False```
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(pd.DataFrame): 4 columns
Note:
* Numpy ```std()``` [ddof](https://numpy.org/doc/stable/reference/generated/numpy.std.html) explanation.
"""
# Validate
length = v_pos_default(length, 30)
open_ = v_series(open_, length)
high = v_series(high, length)
low = v_series(low, length)
close = v_series(close, length)
if open_ is None or high is None or low is None or close is None:
return
full = v_bool(full, False)
ddof = int(ddof) if isinstance(ddof, int) and 0 <= ddof < length else 1
offset = v_offset(offset)
# Calculate
if full:
length = close.size
z_open = zscore(open_, length=length, ddof=ddof)
z_high = zscore(high, length=length, ddof=ddof)
z_low = zscore(low, length=length, ddof=ddof)
z_close = zscore(close, length=length, ddof=ddof)
_full = "a" if full else ""
_props = _full if full else f"_{length}_{ddof}"
data = {
f"open_Z{_props}": z_open,
f"high_Z{_props}": z_high,
f"low_Z{_props}": z_low,
f"close_Z{_props}": z_close,
}
df = DataFrame(data, index=close.index)
if full:
df.fillna(method="backfill", axis=0, inplace=True)
# Offset
if offset != 0:
df = df.shift(offset)
# Fill
if "fillna" in kwargs:
df.fillna(kwargs["fillna"], inplace=True)
# Name and Category
df.name = f"CDL_Z{_props}"
df.category = "candle"
return df
-86
View File
@@ -1,86 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import empty_like, maximum, minimum
from numba import njit
from pandas import DataFrame, Series
from .._typing import Int
from ..utils import v_offset, v_series
@njit(cache=True)
def np_ha(np_open, np_high, np_low, np_close):
ha_close = 0.25 * (np_open + np_high + np_low + np_close)
ha_open = empty_like(ha_close)
ha_open[0] = 0.5 * (np_open[0] + np_close[0])
m = np_close.size
for i in range(1, m):
ha_open[i] = 0.5 * (ha_open[i - 1] + ha_close[i - 1])
ha_high = maximum(maximum(ha_open, ha_close), np_high)
ha_low = minimum(minimum(ha_open, ha_close), np_low)
return ha_open, ha_high, ha_low, ha_close
def ha(
open_: Series, high: Series, low: Series, close: Series,
offset: Int = None, **kwargs: dict | None
) -> DataFrame:
"""Heikin Ashi Candles
Creates Japanese _ohlc_ candlesticks that attempts to filter out market
noise. Developed by Munehisa Homma in the 1700s, Heikin Ashi Candles share
some characteristics with standard candlestick charts but creates a
smoother candlestick appearance.
Sources:
* [Investopedia](https://www.investopedia.com/terms/h/heikinashi.asp)
Parameters:
open_ (pd.Series): ```open``` Series
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(pd.DataFrame): 4 columns
"""
# Validate
open_ = v_series(open_, 1)
high = v_series(high, 1)
low = v_series(low, 1)
close = v_series(close, 1)
offset = v_offset(offset)
if open_ is None or high is None or low is None or close is None:
return
# Calculate
np_open, np_high = open_.to_numpy(), high.to_numpy()
np_low, np_close = low.to_numpy(), close.to_numpy()
ha_open, ha_high, ha_low, ha_close = np_ha(np_open, np_high, np_low, np_close)
df = DataFrame({
"HA_open": ha_open,
"HA_high": ha_high,
"HA_low": ha_low,
"HA_close": ha_close,
}, index=close.index)
# Offset
if offset != 0:
df = df.shift(offset)
# Fill
if "fillna" in kwargs:
df.fillna(kwargs["fillna"], inplace=True)
# Name and Category
df.name = "Heikin-Ashi"
df.category = "candle"
return df
File diff suppressed because it is too large Load Diff
-177
View File
@@ -1,177 +0,0 @@
import importlib
import os
import sys
import types
from glob import glob
from os.path import abspath, basename, exists, join, splitext
from . import __all__ as pandas_ta
from ._typing import DictLike
def bind(name: str, fn: types.FunctionType, method: types.MethodType = None):
"""Bind
Helper function to bind the function and class method defined in a custom
indicator module to the active pandas_ta instance.
Parameters:
name (str): The name of the indicator within pandas_ta
fn (types.FunctionType): The indicator function
method (types.MethodType): The class method corresponding to the passed function
"""
setattr(pandas_ta, name, fn)
setattr(pandas_ta.AnalysisIndicators, name, method)
def create_dir(path: str, categories: bool = True, verbose: bool = True):
"""Create Dir
Sets up a suitable folder structure for working with custom indicators.
Use it **once** to setup and initialize the custom folder.
Parameters:
path (str): Indicator directory full path
categories (bool): Create category sub-folders
verbose (bool): Verbose output
"""
# ensure that the passed directory exists / is readable
if not exists(path):
os.makedirs(path)
if verbose:
print(f"[i] Created main directory '{path}'.")
# list the contents of the directory
# dirs = glob(abspath(join(path, '*')))
# optionally add any missing category subdirectories
if categories:
for _ in [*pandas_ta.Category]:
d = abspath(join(path, _))
if not exists(d):
os.makedirs(d)
if verbose:
dirname = basename(d)
print(f"[i] Created an empty sub-directory '{dirname}'.")
def get_module_functions(module: types.ModuleType) -> DictLike:
"""Get Module Functions
Returns a dictionary with the mapping: "name" to a _function_.
Parameters:
module (types.ModuleType): python module
Returns:
(DictLike): Returns a dictionary with the mapping: "name" to a _function_
Example:
Example return
```py
{
"func1_name": func1,
"func2_name": func2, # ...
}
```
"""
module_functions = {}
for name, item in vars(module).items():
if isinstance(item, types.FunctionType):
module_functions[name] = item
return module_functions
def import_dir(path: str, verbose: bool = True):
"""Import Dir
Import a directory of custom (proprietary) indicators into Pandas TA.
Parameters:
path (str): Full path to indicator directory.
verbose (bool): Output process to STDOUT.
"""
# ensure that the passed directory exists / is readable
if not exists(path):
print(f"[X] Unable to read the directory '{path}'.")
return
# list the contents of the directory
dirs = glob(abspath(join(path, "*")))
# traverse full directory, importing all modules found there
for d in dirs:
dirname = basename(d)
# only look in directories which are valid pandas_ta categories
if dirname not in [*pandas_ta.Category]:
if verbose and dirname not in ["__pycache__", "__init__.py"]:
print(
f"[i] Skipping the sub-directory '{dirname}' since it's not a valid pandas_ta category."
)
continue
# for each module found in that category (directory)...
for module in glob(abspath(join(path, dirname, "*.py"))):
module_name = splitext(basename(module))[0]
if module_name not in ["__init__"]:
# ensure that the supplied path is included in our python path
if d not in sys.path:
sys.path.append(d)
# (re)load the indicator module
module_functions = load_indicator_module(module_name)
# figure out which of the modules functions to bind to pandas_ta
_callable = module_functions.get(module_name, None)
_method_callable = module_functions.get(f"{module_name}_method", None)
if _callable == None:
print(
f"[X] Unable to find a function named '{module_name}' in the module '{module_name}.py'."
)
continue
if _method_callable == None:
missing_method = f"{module_name}_method"
print(
f"[X] Unable to find a method function named '{missing_method}' in the module '{module_name}.py'."
)
continue
# add it to the correct category if it's not there yet
if module_name not in pandas_ta.Category[dirname]:
pandas_ta.Category[dirname].append(module_name)
bind(module_name, _callable, _method_callable)
if verbose:
print(
f"[i] Successfully imported the custom indicator '{module}' into category '{dirname}'."
)
def load_indicator_module(name: str) -> dict:
"""
Helper function to (re)load an indicator module.
Returns:
dict: module functions mapping
```{
"func1_name": func1,
"func2_name": func2, # ...
}```
"""
try:
module = importlib.import_module(name)
except Exception as ex:
print(f"[X] An error occurred when attempting to load module {name}: {ex}")
sys.exit(1)
# reload to refresh previously loaded module
module = importlib.reload(module)
return get_module_functions(module)
@@ -1,8 +0,0 @@
# -*- coding: utf-8 -*-
from .ebsw import ebsw
from .reflex import reflex
__all__ = [
"ebsw",
"reflex",
]
-141
View File
@@ -1,141 +0,0 @@
from numpy import cos, exp, mean, nan, pi, roll, sin, sqrt, zeros
from pandas import Series
from .._typing import Int
from ..utils import v_bool, v_offset, v_pos_default, v_series
def ebsw(
close: Series, length: Int = None, bars: Int = None,
initial_version: bool = None,
offset: Int = None, **kwargs: dict | None
) -> Series:
"""Even Better SineWave
This indicator attempts to quantify market cycles using a low pass filter.
Sources:
* [rengel8](https://github.com/rengel8)
* J.F.Ehlers 'Cycle Analytics for Traders', 2014
* [Pandas TA Issue #350](https://github.com/twopirllc/pandas-ta/issues/350)
* [Proreal Code](https://www.prorealcode.com/prorealtime-indicators/even-better-sinewave/)
Parameters:
close (pd.Series): ```close``` Series
length (int): Max cycle/trend period. Values between ```40-48``` work
as expected with minimum value: ```39```. Default: ```40```
bars (int): Period of low pass filtering. Default: ```10```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(pd.Series): 1 column
Note:
The _default_ is more cycle oriented and seems to be less
whipsaw-prune. The older version might offer earlier signals at medium
and stronger reversals. Compared to TradingView, returns very close
results but appears to be one bar earlier.
"""
# Validate
length = v_pos_default(length, 40)
close = v_series(close, length)
if close is None:
return
initial_version = v_bool(initial_version, False)
bars = v_pos_default(bars, 10)
offset = v_offset(offset)
# Calculate
# allow initial version to be used (more responsive/caution!)
m = close.size
if isinstance(initial_version, bool) and initial_version:
# not the default version that is active
alpha1 = hp = 0 # alpha and HighPass
a1 = b1 = c1 = c2 = c3 = 0
filter_ = power_ = wave = 0
lastClose = lastHP = 0
filtHist = [0, 0] # Filter history
result = [nan for _ in range(0, length - 1)] + [0]
for i in range(length, m):
# HighPass filter cyclic components whose periods are shorter than
# Duration input
alpha1 = (1 - sin(360 / length)) / cos(360 / length)
hp = 0.5 * (1 + alpha1) * (close.iloc[i] - lastClose) + alpha1 * lastHP
# Smooth with a Super Smoother Filter from equation 3-3
a1 = exp(-sqrt(2) * pi / bars)
b1 = 2 * a1 * cos(sqrt(2) * 180 / bars)
c2 = b1
c3 = -1 * a1 * a1
c1 = 1 - c2 - c3
filter_ = 0.5 * c1 * (hp + lastHP) + c2 * \
filtHist[1] + c3 * filtHist[0]
# filter_ = float("{:.8f}".format(float(filter_))) # to fix for
# small scientific notations, the big ones fail
# 3 Bar average of wave amplitude and power
wave = (filter_ + filtHist[1] + filtHist[0]) / 3
power_ = (filter_ * filter_ + filtHist[1] * filtHist[1] \
+ filtHist[0] * filtHist[0]) / 3
# Normalize the Average Wave to Square Root of the Average Power
wave = wave / sqrt(power_)
# update storage, result
filtHist.append(filter_) # append new filter_ value
# remove first element of list (left) -> updating/trim
filtHist.pop(0)
lastHP = hp
lastClose = close.iloc[i]
result.append(wave)
else: # Default
lastHP = lastClose = 0
filtHist = zeros(3)
result = [nan] * (length - 1) + [0]
angle = 2 * pi / length
alpha1 = (1 - sin(angle)) / cos(angle)
ang = 2 ** .5 * pi / bars
a1 = exp(-ang)
c2 = 2 * a1 * cos(ang)
c3 = -a1 ** 2
c1 = 1 - c2 - c3
for i in range(length, m):
hp = 0.5 * (1 + alpha1) * (close.iloc[i] - lastClose) + alpha1 * lastHP
# Rotate filters to overwrite oldest value
filtHist = roll(filtHist, -1)
filtHist[-1] = 0.5 * c1 * \
(hp + lastHP) + c2 * filtHist[1] + c3 * filtHist[0]
# Wave calculation
wave = mean(filtHist)
rms = sqrt(mean(filtHist ** 2))
wave = wave / rms
# Update past values
lastHP = hp
lastClose = close.iloc[i]
result.append(wave)
ebsw = Series(result, index=close.index)
# Offset
if offset != 0:
ebsw = ebsw.shift(offset)
# Fill
if "fillna" in kwargs:
ebsw.fillna(kwargs["fillna"], inplace=True)
# Name and Category
ebsw.name = f"EBSW_{length}_{bars}"
ebsw.category = "cycle"
return ebsw
@@ -1,114 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import cos, exp, nan, sqrt, zeros_like
from numba import njit
from pandas import Series
from .._typing import Int, IntFloat
from ..utils import v_offset, v_pos_default, v_series
@njit(cache=True)
def np_reflex(x, n, k, alpha, pi, sqrt2):
m, ratio = x.size, 2 * sqrt2 / k
a = exp(-pi * ratio)
b = 2 * a * cos(180 * ratio)
c = a * a - b + 1
_f = zeros_like(x)
_ms = zeros_like(x)
result = zeros_like(x)
for i in range(2, m):
_f[i] = 0.5 * c * (x[i] + x[i - 1]) + b * _f[i - 1] - a * a * _f[i - 2]
for i in range(n, m):
slope = (_f[i - n] - _f[i]) / n
_sum = 0
for j in range(1, n):
_sum += _f[i] - _f[i - j] + j * slope
_sum /= n
_ms[i] = alpha * _sum * _sum + (1 - alpha) * _ms[i - 1]
if _ms[i] != 0.0:
result[i] = _sum / sqrt(_ms[i])
return result
def reflex(
close: Series, length: Int = None,
smooth: Int = None, alpha: IntFloat = None,
pi: IntFloat = None, sqrt2: IntFloat = None,
offset: Int = None, **kwargs: dict | None
) -> Series:
"""Reflex
This cycle indicator, by John F. Ehlers, attempts to reduce lag.
Sources:
* [rengel8](https://github.com/rengel8) (2021-08-11) based on the
implementation from "ProRealCode"
* [traders.com](http://traders.com/Documentation/FEEDbk_docs/2020/02/TradersTips.html)
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/reflex-and-trendflex-indicators-john-f-ehlers/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```20```
smooth (int): SuperSmoother period. Default: ```20```
alpha (float): Alpha weight of Difference Sums. Default: ```0.04```
pi (float): Ehlers's truncated value: ```3.14159```.
Default: ```3.14159```
sqrt2 (float): Ehlers's truncated value: ```1.414```.
Default: ```1.414```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): Replaces ```na```'s with ```value```.
Returns:
(pd.Series): 1 column
Tip:
This implementation has a separate control parameter for the
internal applied SuperSmoother.
Note:
John F. Ehlers introduced two indicators within the article
"Reflex: A New Zero-Lag Indicator” in February 2020, TASC magazine.
One of which is Reflex, a lag reduced cycle indicator. Both indicators
(Reflex/Trendflex) are oscillators that complement each other with the
focus for cycle and trend.
"""
# Validate
length = v_pos_default(length, 20)
smooth = v_pos_default(smooth, 20)
_length = max(length, smooth) + 1
close = v_series(close, _length)
if close is None:
return
alpha = v_pos_default(alpha, 0.04)
pi = v_pos_default(pi, 3.14159)
sqrt2 = v_pos_default(sqrt2, 1.414)
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
result = np_reflex(np_close, length, smooth, alpha, pi, sqrt2)
result[:length] = nan
result = Series(result, index=close.index)
# Offset
if offset != 0:
result = result.shift(offset)
# Fill
if "fillna" in kwargs:
result.fillna(kwargs["fillna"], inplace=True)
# Name and Category
result.name = f"REFLEX_{length}_{smooth}_{alpha}"
result.category = "cycle"
return result
-72
View File
@@ -1,72 +0,0 @@
from pandas import Series
from ._typing import DictLike
from .overlap.dema import dema
from .overlap.ema import ema
from .overlap.fwma import fwma
from .overlap.hma import hma
from .overlap.linreg import linreg
from .overlap.midpoint import midpoint
from .overlap.pwma import pwma
from .overlap.rma import rma
from .overlap.sinwma import sinwma
from .overlap.sma import sma
from .overlap.ssf import ssf
from .overlap.swma import swma
from .overlap.t3 import t3
from .overlap.tema import tema
from .overlap.trima import trima
from .overlap.vidya import vidya
from .overlap.wma import wma
def ma(name: str = None, source: Series = None, **kwargs: DictLike) -> Series:
"""MA Selection Utility
Available MAs: dema, ema, fwma, hma, linreg, midpoint, pwma, rma,
sinwma, sma, ssf, swma, t3, tema, trima, vidya, wma.
Parameters:
name (str): One of the Available MAs. Default: "ema"
source (pd.Series): Input Series ```source```.
Other Parameters:
kwargs (**kwargs): Additional args for the MA.
Returns:
(pd.Series): Selected MA
Esourceample:
```py linenums="0"
ema8 = ta.ma("ema", df.close, length=8)
sma50 = ta.ma("sma", df.close, length=50)
pwma10 = ta.ma("pwma", df.close, length=10, asc=False)
```
"""
_mas = [
"dema", "ema", "fwma", "hma", "linreg", "midpoint", "pwma", "rma",
"sinwma", "sma", "ssf", "swma", "t3", "tema", "trima", "vidya", "wma"
]
if name is None and source is None:
return _mas
elif isinstance(name, str) and name.lower() in _mas:
name = name.lower()
else: # "ema"
name = _mas[1]
if name == "dema": return dema(source, **kwargs)
elif name == "fwma": return fwma(source, **kwargs)
elif name == "hma": return hma(source, **kwargs)
elif name == "linreg": return linreg(source, **kwargs)
elif name == "midpoint": return midpoint(source, **kwargs)
elif name == "pwma": return pwma(source, **kwargs)
elif name == "rma": return rma(source, **kwargs)
elif name == "sinwma": return sinwma(source, **kwargs)
elif name == "sma": return sma(source, **kwargs)
elif name == "ssf": return ssf(source, **kwargs)
elif name == "swma": return swma(source, **kwargs)
elif name == "t3": return t3(source, **kwargs)
elif name == "tema": return tema(source, **kwargs)
elif name == "trima": return trima(source, **kwargs)
elif name == "vidya": return vidya(source, **kwargs)
elif name == "wma": return wma(source, **kwargs)
else: return ema(source, **kwargs)
-90
View File
@@ -1,90 +0,0 @@
# -*- coding: utf-8 -*-
from importlib.util import find_spec
from ._typing import Dict, IntFloat, ListStr
Imports: Dict[str, bool] = {
"talib": find_spec("talib") is not None,
"vectorbt": find_spec("vectorbt") is not None,
"yfinance": find_spec("yfinance") is not None,
}
# Not ideal and not dynamic but it works.
# TODO: find a dynamic solution later.
Category: Dict[str, ListStr] = {
"candle": [
"cdl_pattern", "cdl_z", "ha"
],
"cycle": ["ebsw", "reflex"],
"momentum": [
"ao", "apo", "bias", "bop", "brar", "cci", "cfo", "cg", "cmo",
"coppock", "crsi", "cti", "er", "eri", "exhc", "fisher",
"inertia", "kdj", "kst", "macd", "mom", "pgo", "ppo", "psl", "qqe",
"roc", "rsi", "rsx", "rvgi", "slope", "smc", "smi", "squeeze",
"squeeze_pro", "stc", "stoch", "stochf", "stochrsi", "tmo", "trix",
"tsi", "uo", "willr"
],
"overlap": [
"alligator", "alma", "dema", "ema", "fwma", "hilo", "hl2", "hlc3",
"hma", "hwma", "ichimoku", "jma", "kama", "linreg", "mama",
"mcgd", "midpoint", "midprice", "ohlc4", "pivots", "pwma", "rma",
"sinwma", "sma", "smma", "ssf", "ssf3", "supertrend", "swma", "t3",
"tema", "trima", "vidya", "wcp", "wma", "zlma"
],
"performance": ["log_return", "percent_return"],
"statistics": [
"entropy", "kurtosis", "mad", "median", "quantile", "skew", "stdev",
"tos_stdevall", "variance", "zscore"
],
"trend": [
"adx", "alphatrend", "amat", "aroon", "chop", "cksp", "decay",
"decreasing", "dpo", "ht_trendline", "increasing",
"long_run", "psar", "qstick", "rwi", "short_run", "trendflex",
"vhf", "vortex", "zigzag"
],
"volatility": [
"aberration", "accbands", "atr", "atrts", "bbands", "chandelier_exit",
"donchian", "hwc", "kc", "massi", "natr", "pdist", "rvi", "thermo",
"true_range", "ui"
],
# Note: "vp" or "Volume Profile" is excluded since it does not
# return a Time Series
"volume": [
"ad", "adosc", "aobv", "cmf", "efi", "eom", "kvo", "mfi", "nvi",
"obv", "pvi", "pvo", "pvol", "pvr", "pvt", "tsv", "vhm", "vwap",
"vwma"
],
}
CANDLE_AGG: Dict[str, str] = {
"open": "first",
"high": "max",
"low": "min",
"close": "last",
"volume": "sum"
}
# https://www.worldtimezone.com/markets24.php
EXCHANGE_TZ: Dict[str, IntFloat] = {
"NZSX": 12, "ASX": 11,
"TSE": 9, "HKE": 8, "SSE": 8, "SGX": 8,
"NSE": 5.5, "DIFX": 4, "RTS": 3,
"JSE": 2, "FWB": 1, "LSE": 1,
"BMF": -2, "NYSE": -4, "TSX": -4,
"GENR": 0 # Generated Data
}
RATE: Dict[str, IntFloat] = {
"DAYS_PER_MONTH": 21,
"MINUTES_PER_HOUR": 60,
"MONTHS_PER_YEAR": 12,
"QUARTERS_PER_YEAR": 4,
"TRADING_DAYS_PER_YEAR": 252, # Keep even
"TRADING_HOURS_PER_DAY": 6.5,
"WEEKS_PER_YEAR": 52,
"YEARLY": 1,
}
@@ -1,93 +0,0 @@
# -*- coding: utf-8 -*-
from .ao import ao
from .apo import apo
from .bias import bias
from .bop import bop
from .brar import brar
from .cci import cci
from .cfo import cfo
from .cg import cg
from .cmo import cmo
from .coppock import coppock
from .crsi import crsi
from .cti import cti
from .dm import dm
from .er import er
from .eri import eri
from .exhc import exhc
from .fisher import fisher
from .inertia import inertia
from .kdj import kdj
from .kst import kst
from .macd import macd
from .mom import mom
from .pgo import pgo
from .ppo import ppo
from .psl import psl
from .qqe import qqe
from .roc import roc
from .rsi import rsi
from .rsx import rsx
from .rvgi import rvgi
from .slope import slope
from .smc import smc
from .smi import smi
from .squeeze import squeeze
from .squeeze_pro import squeeze_pro
from .stc import stc
from .stoch import stoch
from .stochf import stochf
from .stochrsi import stochrsi
from .tmo import tmo
from .trix import trix
from .tsi import tsi
from .uo import uo
from .willr import willr
__all__ = [
"ao",
"apo",
"bias",
"bop",
"brar",
"cci",
"cfo",
"cg",
"cmo",
"coppock",
"crsi",
"cti",
"dm",
"er",
"eri",
"exhc",
"fisher",
"inertia",
"kdj",
"kst",
"macd",
"mom",
"pgo",
"ppo",
"psl",
"qqe",
"roc",
"rsi",
"rsx",
"rvgi",
"slope",
"smc",
"smi",
"squeeze",
"squeeze_pro",
"stc",
"stoch",
"stochf",
"stochrsi",
"tmo",
"trix",
"tsi",
"uo",
"willr",
]
@@ -1,65 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from .._typing import Int
from ..overlap import sma
from ..utils import v_offset, v_pos_default, v_series
def ao(
high: Series, low: Series, fast: Int = None, slow: Int = None,
offset: Int = None, **kwargs: dict | None
) -> Series:
"""Awesome Oscillator
This indicator attempts to identify momentum with the intention to
affirm trends or anticipate possible reversals.
Sources:
* [ifcm](https://www.ifcm.co.uk/ntx-indicators/awesome-oscillator)
* [tradingview](https://www.tradingview.com/wiki/Awesome_Oscillator_(AO))
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
fast (int): Fast period. Default: ```5```
slow (int): Slow period. Default: ```34```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
fast = v_pos_default(fast, 5)
slow = v_pos_default(slow, 34)
if slow < fast:
fast, slow = slow, fast
_length = max(fast, slow)
high = v_series(high, _length)
low = v_series(low, _length)
if high is None or low is None:
return
offset = v_offset(offset)
# Calculate
median_price = 0.5 * (high + low)
fast_sma = sma(median_price, fast)
slow_sma = sma(median_price, slow)
ao = fast_sma - slow_sma
# Offset
if offset != 0:
ao = ao.shift(offset)
# Fill
if "fillna" in kwargs:
ao.fillna(kwargs["fillna"], inplace=True)
# Name and Category
ao.name = f"AO_{fast}_{slow}"
ao.category = "momentum"
return ao
@@ -1,74 +0,0 @@
from pandas import Series
from .._typing import DictLike, Int
from ..ma import ma
from ..maps import Imports
from ..utils import tal_ma, v_mamode, v_offset
from ..utils import v_pos_default, v_series, v_talib
def apo(
close: Series, fast: Int = None, slow: Int = None,
mamode: str = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Absolute Price Oscillator
This indicator attempts to quantify momentum.
Sources:
* [tradingtechnologies](https://www.tradingtechnologies.com/xtrader-help/x-study/technical-indicator-definitions/absolute-price-oscillator-apo/)
Parameters:
close (pd.Series): ```close``` Series
fast (int): Fast period. Default: ```12```
slow (int): Slow period. Default: ```26```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note:
* Simply the difference of two different EMAs.
* APO and MACD lines are equivalent.
"""
# Validate
fast = v_pos_default(fast, 12)
slow = v_pos_default(slow, 26)
if slow < fast:
fast, slow = slow, fast
close = v_series(close, max(fast, slow))
if close is None:
return
mamode = v_mamode(mamode, "sma")
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import APO
apo = APO(close, fast, slow, tal_ma(mamode))
else:
fastma = ma(mamode, close, length=fast, talib=mode_tal)
slowma = ma(mamode, close, length=slow, talib=mode_tal)
apo = fastma - slowma
# Offset
if offset != 0:
apo = apo.shift(offset)
# Fill
if "fillna" in kwargs:
apo.fillna(kwargs["fillna"], inplace=True)
# Name and Category
apo.name = f"APO_{fast}_{slow}"
apo.category = "momentum"
return apo
@@ -1,58 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from .._typing import DictLike, Int
from ..ma import ma
from ..utils import v_mamode, v_offset, v_pos_default, v_series
def bias(
close: Series, length: Int = None, mamode: str = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Bias
This indicator computes the Rate of Change between the source and a
moving average.
Sources:
* Few internet resources on definitive definition.
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```26```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 26)
close = v_series(close, length)
if close is None:
return
mamode = v_mamode(mamode, "sma")
offset = v_offset(offset)
# Calculate
bma = ma(mamode, close, length=length, **kwargs)
bias = (close / bma) - 1
# Offset
if offset != 0:
bias = bias.shift(offset)
# Fill
if "fillna" in kwargs:
bias.fillna(kwargs["fillna"], inplace=True)
# Name and Category
bias.name = f"BIAS_{bma.name}"
bias.category = "momentum"
return bias
@@ -1,70 +0,0 @@
from pandas import Series
from .._typing import DictLike, Int, IntFloat
from ..maps import Imports
from ..utils import (
non_zero_range,
v_offset,
v_scalar,
v_series,
v_talib
)
def bop(
open_: Series, high: Series, low: Series, close: Series,
scalar: IntFloat = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Balance of Power
This indicator attempts to quantify the market strength of buyers
versus sellers.
Sources:
* [worden](http://www.worden.com/TeleChartHelp/Content/Indicators/Balance_of_Power.htm)
Parameters:
open_ (pd.Series): ```open``` Series
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
scalar (float): Scalar. Default: ```1```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
open_ = v_series(open_)
high = v_series(high)
low = v_series(low)
close = v_series(close)
scalar = v_scalar(scalar, 1)
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal and close.size:
from talib import BOP
bop = BOP(open_, high, low, close)
else:
high_low_range = non_zero_range(high, low)
close_open_range = non_zero_range(close, open_)
bop = scalar * close_open_range / high_low_range
# Offset
if offset != 0:
bop = bop.shift(offset)
# Fill
if "fillna" in kwargs:
bop.fillna(kwargs["fillna"], inplace=True)
# Name and Category
bop.name = f"BOP"
bop.category = "momentum"
return bop
@@ -1,91 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import (
non_zero_range,
v_drift,
v_offset,
v_pos_default,
v_scalar,
v_series
)
def brar(
open_: Series, high: Series, low: Series, close: Series,
length: Int = None, scalar: IntFloat = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""BRAR
BR and AR
Sources:
* No internet resources on definitive definition.
Parameters:
open_ (pd.Series): ```open``` Series
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
length (int): The period. Default: ```26```
scalar (float): Scalar. Default: ```100```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 2 columns
"""
# Validate
length = v_pos_default(length, 26)
open_ = v_series(open_, length)
high = v_series(high, length)
low = v_series(low, length)
close = v_series(close, length)
if open_ is None or high is None or low is None or close is None:
return
scalar = v_scalar(scalar, 100)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
high_open_range = non_zero_range(high, open_)
open_low_range = non_zero_range(open_, low)
hcy = non_zero_range(high, close.shift(drift))
cyl = non_zero_range(close.shift(drift), low)
hcy[hcy < 0] = 0 # Zero negative values
cyl[cyl < 0] = 0 # ""
ar = scalar * high_open_range.rolling(length).sum() \
/ open_low_range.rolling(length).sum()
br = scalar * hcy.rolling(length).sum() \
/ cyl.rolling(length).sum()
# Offset
if offset != 0:
ar = ar.shift(offset)
br = ar.shift(offset)
# Fill
if "fillna" in kwargs:
ar.fillna(kwargs["fillna"], inplace=True)
br.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{length}"
ar.name = f"AR{_props}"
br.name = f"BR{_props}"
ar.category = br.category = "momentum"
data = {ar.name: ar, br.name: br}
df = DataFrame(data, index=close.index)
df.name = f"BRAR{_props}"
df.category = "momentum"
return df
@@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.overlap import hlc3, sma
from pandas_ta.statistics import mad
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
def cci(
high: Series, low: Series, close: Series, length: Int = None,
c: IntFloat = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Commodity Channel Index
This indicator attempts to identify "overbought" and "oversold" levels
relative to a mean.
Sources:
* [tradingview](https://www.tradingview.com/wiki/Commodity_Channel_Index_(CCI))
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
length (int): The period. Default: ```14```
c (float): Scaling Constant. Default: ```0.015```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 14)
high = v_series(high, length)
low = v_series(low, length)
close = v_series(close, length)
if high is None or low is None or close is None:
return
c = v_pos_default(c, 0.015)
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import CCI
cci = CCI(high, low, close, length)
else:
typical_price = hlc3(high=high, low=low, close=close, talib=mode_tal)
mean_typical_price = sma(typical_price, length=length, talib=mode_tal)
mad_typical_price = mad(typical_price, length=length)
cci = typical_price - mean_typical_price / (c * mad_typical_price)
# Offset
if offset != 0:
cci = cci.shift(offset)
# Fill
if "fillna" in kwargs:
cci.fillna(kwargs["fillna"], inplace=True)
# Name and Category
cci.name = f"CCI_{length}_{c}"
cci.category = "momentum"
return cci
@@ -1,69 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.overlap import linreg
from pandas_ta.utils import (
v_drift,
v_offset,
v_pos_default,
v_scalar,
v_series
)
def cfo(
close: Series, length: Int = None,
scalar: IntFloat = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Chande Forcast Oscillator
This indicator attempts to calculate the percentage difference between
the actual price and the Time Series Forecast (the endpoint of a
linear regression line).
Sources:
* [fmlabs](https://www.fmlabs.com/reference/default.htm?url=ForecastOscillator.htm)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```9```
scalar (float): Scalar. Default: ```100```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 9)
close = v_series(close, length)
if close is None:
return
scalar = v_scalar(scalar, 100)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
# Finding linear regression of Series
cfo = scalar * (close - linreg(close, length=length, tsf=True)) / close
# Offset
if offset != 0:
cfo = cfo.shift(offset)
# Fill
if "fillna" in kwargs:
cfo.fillna(kwargs["fillna"], inplace=True)
# Name and Category
cfo.name = f"CFO_{length}"
cfo.category = "momentum"
return cfo
@@ -1,57 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_offset, v_pos_default, v_series, weights
def cg(
close: Series, length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Center of Gravity
This indicator, by John Ehlers, attempts to identify turning points with
minimal to zero lag and smoothing.
Sources:
* [MESA Software](http://www.mesasoftware.com/papers/TheCGOscillator.pdf)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
offset = v_offset(offset)
# Calculate
coefficients = range(1, length + 1)
numerator = close.rolling(length).apply(weights(coefficients), raw=True)
cg = -numerator / close.rolling(length).sum()
# Offset
if offset != 0:
cg = cg.shift(offset)
# Fill
if "fillna" in kwargs:
cg.fillna(kwargs["fillna"], inplace=True)
# Name and Category
cg.name = f"CG_{length}"
cg.category = "momentum"
return cg
@@ -1,90 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.overlap import rma
from pandas_ta.utils import (
v_drift,
v_offset,
v_pos_default,
v_scalar,
v_series,
v_talib
)
def cmo(
close: Series, length: Int = None, scalar: IntFloat = None,
talib: bool = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Chande Momentum Oscillator
This indicator attempts to capture momentum.
Sources:
* [tradingtechnologies](https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/chande-momentum-oscillator-cmo/)
* [tradingview](https://www.tradingview.com/script/hdrf0fXV-Variable-Index-Dynamic-Average-VIDYA/)
Parameters:
close (pd.Series): ```close``` Series
scalar (float): Scalar. Default: ```100```
talib (bool): If installed, use TA Lib. Uses EMA if ```False```.
Default: ```True```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note:
* Overbought around 50
* Oversold around -50.
"""
# Validate
length = v_pos_default(length, 14)
close = v_series(close, length + 1)
if close is None:
return
scalar = v_scalar(scalar, 100)
mode_tal = v_talib(talib)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import CMO
cmo = CMO(close, length)
else:
mom = close.diff(drift)
positive = mom.copy().clip(lower=0)
negative = mom.copy().clip(upper=0).abs()
if mode_tal:
pos_ = rma(positive, length)
neg_ = rma(negative, length)
else:
pos_ = positive.rolling(length).sum()
neg_ = negative.rolling(length).sum()
cmo = scalar * (pos_ - neg_) / (pos_ + neg_)
# Offset
if offset != 0:
cmo = cmo.shift(offset)
# Fill
if "fillna" in kwargs:
cmo.fillna(kwargs["fillna"], inplace=True)
# Name and Category
cmo.name = f"CMO_{length}"
cmo.category = "momentum"
return cmo
@@ -1,70 +0,0 @@
# -*- coding: utf-8 -*-
# from numpy import isnan
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import wma
from pandas_ta.utils import v_offset, v_pos_default, v_series
from .roc import roc
def coppock(
close: Series, length: Int = None, fast: Int = None, slow: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Coppock Curve
This indicator, by Edwin Coppock 1962, was originally called the
"Trendex Model", attempts to identify major upturns and downturns.
Sources:
* [wikipedia](https://en.wikipedia.org/wiki/Coppock_curve)
Parameters:
close (pd.Series): ```close``` Series
length (int): WMA period. Default: ```10```
fast (int): Fast ROC period. Default: ```11```
slow (int): Slow ROC period. Default: ```14```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note:
Although designed for monthly use, a daily calculation over the same
period length can be made, converting the periods to 294-day and
231-day rate of changes, and a 210-day WMA.
"""
# Validate
length = v_pos_default(length, 10)
fast = v_pos_default(fast, 11)
slow = v_pos_default(slow, 14)
_length = length + fast + slow
close = v_series(close, _length)
if close is None:
return
offset = v_offset(offset)
# Calculate
total_roc = roc(close, fast) + roc(close, slow)
coppock = wma(total_roc, length)
# Offset
if offset != 0:
coppock = coppock.shift(offset)
# Fill
if "fillna" in kwargs:
coppock.fillna(kwargs["fillna"], inplace=True)
# Name and Category
coppock.name = f"COPC_{fast}_{slow}_{length}"
coppock.category = "momentum"
return coppock
@@ -1,105 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import Array, DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.momentum.rsi import rsi
from pandas_ta.utils import (
consecutive_streak,
percent_rank,
v_drift,
v_offset,
v_pos_default,
v_scalar,
v_series,
v_talib,
)
def crsi(
close: Series, rsi_length: Int = None, streak_length: Int = None,
rank_length: Int = None, scalar: IntFloat = None, talib: bool = None,
drift: Int = None, offset: Int = None, **kwargs: DictLike,
) -> Series:
"""Connors Relative Strength Index
This indicator attempts to identify momentum and potential reversals at
"overbought" or "oversold" conditions.
Sources:
* [alvarezquanttrading](https://alvarezquanttrading.com/blog/connorsrsi-analysis/)
* [tradingview](https://www.tradingview.com/support/solutions/43000502017-connors-rsi-crsi/)
* An Introduction to ConnorsRSI. Connors Research Trading Strategy Series.
Connors, L., Alvarez, C., & Radtke, M. (2012). ISBN 978-0-9853072-9-5.
Parameters:
close (pd.Series): ```close``` Series
rsi_length (int): The RSI period. Default: ```3```
streak_length (int): Streak RSI period. Default: ```2```
rank_length (int): Percent Rank length. Default: ```100```
scalar (float): Scalar. Default: ```100```
talib (bool): If installed, use TA Lib. Default: ```True```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
rsi_length = v_pos_default(rsi_length, 3)
streak_length = v_pos_default(streak_length, 2)
rank_length = v_pos_default(rank_length, 100)
_length = max(rsi_length, streak_length, rank_length)
close = v_series(close, _length)
if "length" in kwargs:
kwargs.pop("length")
if close is None:
return None
scalar = v_scalar(scalar, 100)
mode_tal = v_talib(talib)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
streak = Series(consecutive_streak(np_close), index=close.index)
if Imports["talib"] and mode_tal:
from talib import RSI
_rsi = RSI(close, rsi_length)
_streak_rsi = RSI(streak, streak_length)
else:
# Both TA-lib and Pandas-TA use the Wilder's RSI
# and its smoothing function
_rsi = rsi(
close, length=rsi_length, scalar=scalar, talib=talib,
drift=drift, offset=offset, **kwargs
)
_streak_rsi = rsi(
streak, length=streak_length, scalar=scalar, talib=talib,
drift=drift, offset=offset, **kwargs
)
_crsi = (_rsi + _streak_rsi + percent_rank(close, rank_length)) / 3.0
crsi = Series(_crsi, index=close.index)
# Offset
if offset != 0:
crsi = crsi.shift(offset)
# Fill
if "fillna" in kwargs:
crsi.fillna(kwargs["fillna"], inplace=True)
# Name and Category
crsi.name = f"CRSI_{rsi_length}_{streak_length}_{rank_length}"
crsi.category = "momentum"
return crsi
@@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import linreg
from pandas_ta.utils import v_offset, v_pos_default, v_series
def cti(
close: Series, length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Correlation Trend Indicator
This oscillator, by John Ehlers' in 2020, attempts to identify the
magnitude and direction of a trend using linear regession.
Note:
This is a wrapper for ```ta.linreg(close, r=True)```.
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```12```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 12)
close = v_series(close, length)
if close is None:
return
offset = v_offset(offset)
# Calculate
cti = linreg(close, length=length, r=True)
# Offset
if offset != 0:
cti = cti.shift(offset)
# Fill
if "fillna" in kwargs:
cti.fillna(method=kwargs["fillna"], inplace=True)
# Name and Category
cti.name = f"CTI_{length}"
cti.category = "momentum"
return cti
@@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.maps import Imports
from pandas_ta.utils import (
v_drift,
v_mamode,
v_offset,
v_pos_default,
v_series,
v_talib,
zero
)
def dm(
high: Series, low: Series, length: Int = None,
mamode: str = None, talib: bool = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Directional Movement
This indicator, by J. Welles Wilder in 1978, attempts to
determine direction.
Sources:
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=24&Name=Directional_Movement_Index)
* [tradingview](https://www.tradingview.com/pine-script-reference/#fun_dmi)
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
mamode (str): See ```help(ta.ma)```. Default: ```"rma"```
talib (bool): If installed, use TA Lib. Default: ```True```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 2 columns
"""
# Validate
length = v_pos_default(length, 14)
high = v_series(high, length)
low = v_series(low, length)
if high is None or low is None:
return
mamode = v_mamode(mamode, "rma")
mode_tal = v_talib(talib)
drift = v_drift(drift)
offset = v_offset(offset)
if Imports["talib"] and mode_tal and high.size and low.size:
from talib import MINUS_DM, PLUS_DM
pos = PLUS_DM(high, low, length)
neg = MINUS_DM(high, low, length)
else:
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)
# Not the same values as TA Lib's -+DM (Good First Issue)
pos = ma(mamode, pos_, length=length, talib=mode_tal)
neg = ma(mamode, neg_, length=length, talib=mode_tal)
# Offset
if offset != 0:
pos = pos.shift(offset)
neg = neg.shift(offset)
# Fill
if "fillna" in kwargs:
pos.fillna(kwargs["fillna"], inplace=True)
neg.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{length}"
data = {f"DMP{_props}": pos, f"DMN{_props}": neg}
df = DataFrame(data, index=high.index)
df.name = f"DM{_props}"
df.category = "momentum"
return df
@@ -1,93 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, concat, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
signals,
v_drift,
v_offset,
v_pos_default,
v_series
)
def er(
close: Series, length: Int = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Efficiency Ratio
This indicator, by Perry J. Kaufman, attempts to identify market noise
or volatility.
Sources:
* "New Trading Systems and Methods", Perry J. Kaufman
* [tc2000](https://help.tc2000.com/m/69404/l/749623-kaufman-efficiency-ratio)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note:
It is calculated by dividing the net change in price movement over
```n``` periods by the sum of the absolute net changes over the
same ```n``` periods.
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length + 1)
if close is None:
return
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
abs_diff = close.diff(length).abs()
abs_volatility = close.diff(drift).abs()
abs_volatility_rsum = abs_volatility.rolling(window=length).sum()
er = abs_diff / abs_volatility_rsum
# Offset
if offset != 0:
er = er.shift(offset)
# Fill
if "fillna" in kwargs:
er.fillna(kwargs["fillna"], inplace=True)
# Name and Category
er.name = f"ER_{length}"
er.category = "momentum"
signal_indicators = kwargs.pop("signal_indicators", False)
if not signal_indicators:
return er
else:
signalsdf = concat(
[
DataFrame({er.name: er}),
signals(
indicator=er,
xa=kwargs.pop("xa", 80),
xb=kwargs.pop("xb", 20),
xseries=kwargs.pop("xseries", None),
xseries_a=kwargs.pop("xseries_a", None),
xseries_b=kwargs.pop("xseries_b", None),
cross_values=kwargs.pop("cross_values", False),
cross_series=kwargs.pop("cross_series", True),
offset=offset,
),
],
axis=1,
)
return signalsdf
@@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import ema
from pandas_ta.utils import v_offset, v_pos_default, v_series
def eri(
high: Series, low: Series, close: Series, length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Elder Ray Index
This indicator, by Dr Alexander Elder, attempts to identify market
strength.
Sources:
* [admiralmarkets](https://admiralmarkets.com/education/articles/forex-indicators/bears-and-bulls-power-indicator)
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
length (int): The period. Default: ```14```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 2 columns
Note:
* Possible entry signals when used in combination with a trend,
* Bear Power attempts to quantify lower value appeal.
* Bull Power attempts the to quantify higher value appeal.
"""
# Validate
length = v_pos_default(length, 13)
high = v_series(high, length)
low = v_series(low, length)
close = v_series(close, length)
if high is None or low is None or close is None:
return
offset = v_offset(offset)
# Calculate
ema_ = ema(close, length)
bull = high - ema_
bear = low - ema_
# Offset
if offset != 0:
bull = bull.shift(offset)
bear = bear.shift(offset)
# Fill
if "fillna" in kwargs:
bull.fillna(kwargs["fillna"], inplace=True)
bear.fillna(kwargs["fillna"], inplace=True)
# Name and Category
bull.name = f"BULLP_{length}"
bear.name = f"BEARP_{length}"
bull.category = bear.category = "momentum"
data = {bull.name: bull, bear.name: bear}
df = DataFrame(data, index=close.index)
df.name = f"ERI_{length}"
df.category = bull.category
return df
@@ -1,123 +0,0 @@
# -*- coding: utf-8 -*-
from math import isnan
from numpy import (
clip,
cumsum,
diff,
float64,
int64,
isnan,
nan,
nan_to_num,
where,
zeros_like
)
from numba import njit
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
nb_ffill,
nb_idiff,
nb_shift,
v_bool,
v_int,
v_offset,
v_pos_default,
v_series
)
@njit(cache=True)
def nb_exhc(x, n, cap, lb, ub, show_all):
x_diff = nb_idiff(x, n)
neg_diff, pos_diff = x_diff < 0, x_diff > 0
dn_csum = cumsum(neg_diff)
up_csum = cumsum(pos_diff)
dn = dn_csum - nb_ffill(where(~neg_diff, dn_csum, nan))
up = up_csum - nb_ffill(where(~pos_diff, up_csum, nan))
if cap > 0:
dn = clip(dn, 0, cap)
up = clip(up, 0, cap)
if show_all:
dn = where(dn == 0, 0, dn)
up = where(up == 0, 0, up)
else:
between_lu = (dn >= lb) & (dn <= ub)
dn = where(between_lu, dn, 0)
up = where(between_lu, up, 0)
return dn, up
def exhc(
close: Series, length: Int = None, cap: Int = None,
asint: bool = None, show_all: bool = None, nozeros: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Exhaustion Count
This indicator attempts to identify rising/falling exhaustion.
Sources:
* [demark](https://demark.com)
* [practicaltechnicalanalysis](http://practicaltechnicalanalysis.blogspot.com/2013/01/tom-demark-sequential.html)
Parameters:
close (pd.Series): Series of close's
length (int): The period. Default: ```4```
cap (int): Count cap. For no cap, set to ```0```. Default: ```13```
show_all (bool): Counts 1 - 13. For 6 - 9, set to ```False```.
Default: ```True```
asint (bool): Returns as ```Int```. Default: ```False```
nozeros (bool): Replace zeros with ```np.nan```. Default: ```False```
offset (int): Post shift. Default: ```0```
Returns:
(pd.DataFrame): 2 columns
Note:
Similar to TD Sequential
"""
# Validate
length = v_pos_default(length, 4)
close = v_series(close, length + 1)
if close is None:
return
cap = v_int(cap, 13, -1)
show_all = v_bool(show_all, True)
asint = v_bool(asint, False)
nozeros = v_bool(nozeros, False)
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
dn, up = nb_exhc(np_close, length, cap, 6, 9, show_all)
if asint:
dn = dn.astype(int64)
up = up.astype(int64)
# Name and Category
data = {
"EXHC_DNa" if show_all else "EXHC_DN": dn,
"EXHC_UPa" if show_all else "EXHC_UP": up
}
df = DataFrame(data, index=close.index)
df.name = "EXHCa" if show_all else "EXHC"
df.category = "momentum"
if nozeros:
df.replace({0: nan}, inplace=True)
# Offset
if offset != 0:
df = df.shift(offset)
return df
@@ -1,95 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan, log, nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import hl2
from pandas_ta.utils import high_low_range, v_offset, v_pos_default, v_series
def fisher(
high: Series, low: Series, length: Int = None, signal: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Fisher Transform
This indicator attempts to identify significant reversals through
normalization.
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
length (int): The period. Default: ```9```
signal (int): Signal period. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 2 columns
Tip: Reversal Signal
When the two lines cross.
"""
# Validate
length = v_pos_default(length, 9)
signal = v_pos_default(signal, 1)
_length = max(length, signal)
high = v_series(high, _length)
low = v_series(low, _length)
if high is None or low is None:
return
offset = v_offset(offset)
# Calculate
hl2_ = hl2(high, low)
highest_hl2 = hl2_.rolling(length).max()
lowest_hl2 = hl2_.rolling(length).min()
hlr = high_low_range(highest_hl2, lowest_hl2)
hlr[hlr < 0.001] = 0.001
position = ((hl2_ - lowest_hl2) / hlr) - 0.5
v = 0
m = high.size
result = [nan for _ in range(0, length - 1)] + [0]
for i in range(length, m):
v = 0.66 * position.iat[i] + 0.67 * v
if v < -0.99:
v = -0.999
if v > 0.99:
v = 0.999
result.append(0.5 * (log((1 + v) / (1 - v)) + result[i - 1]))
fisher = Series(result, index=high.index)
if all(isnan(fisher)):
return # Emergency Break
signalma = fisher.shift(signal)
# Offset
if offset != 0:
fisher = fisher.shift(offset)
signalma = signalma.shift(offset)
# Fill
if "fillna" in kwargs:
fisher.fillna(kwargs["fillna"], inplace=True)
signalma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{length}_{signal}"
fisher.name = f"FISHERT{_props}"
signalma.name = f"FISHERTs{_props}"
fisher.category = signalma.category = "momentum"
data = {fisher.name: fisher, signalma.name: signalma}
df = DataFrame(data, index=high.index)
df.name = f"FISHERT{_props}"
df.category = fisher.category
return df
@@ -1,118 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.overlap import linreg
from pandas_ta.utils import (
v_bool,
v_drift,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series
)
from pandas_ta.volatility import rvi
def inertia(
close: Series, high: Series = None, low: Series = None,
length: Int = None, rvi_length: Int = None, scalar: IntFloat = None,
refined: bool = None, thirds: bool = None,
drift: Int = None, mamode: str = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Inertia
This indicator, by Donald Dorsey, is the _rvi_ smoothed by the Least Squares
MA.
Sources:
* Donald Dorsey, some article in September, 1995.
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=285&Name=Inertia)
* [tradingview](https://www.tradingview.com/script/mLZJqxKn-Relative-Volatility-Index/)
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
length (int): The period. Default: ```20```
rvi_length (int): RVI period. Default: ```14```
refined (bool): Use 'refined' calculation. Default: ```False```
thirds (bool): Use 'thirds' calculation. Default: ```False```
mamode (str): See ```help(ta.ma)```. Default: ```"ema"```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note:
* Negative Inertia when less than 50.
* Positive Inertia when greater than 50.
"""
# Validate
length = v_pos_default(length, 20)
rvi_length = v_pos_default(rvi_length, 14)
_length = 2 * max(length, rvi_length) - min(length, rvi_length) // 2 - 1
close = v_series(close, _length)
if close is None:
return
refined = v_bool(refined, False)
thirds = v_bool(thirds, False)
if refined or thirds:
high = v_series(high, _length)
low = v_series(low, _length)
if high is None or low is None:
return
scalar = v_scalar(scalar, 100)
mamode = v_mamode(mamode, "ema")
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
if refined:
_mode = "r"
rvi_ = rvi(
close, high=high, low=low, length=rvi_length,
scalar=scalar, refined=refined, mamode=mamode
)
elif thirds:
_mode = "t"
rvi_ = rvi(
close, high=high, low=low, length=rvi_length,
scalar=scalar, thirds=thirds, mamode=mamode
)
else:
_mode = ""
rvi_ = rvi(close, length=rvi_length, scalar=scalar, mamode=mamode)
if all(isnan(rvi_)):
return # Emergency Break
inertia = linreg(rvi_, length=length)
if all(isnan(inertia)):
return # Emergency Break
# Offset
if offset != 0:
inertia = inertia.shift(offset)
# Fill
if "fillna" in kwargs:
inertia.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{length}_{rvi_length}"
inertia.name = f"INERTIA{_mode}{_props}"
inertia.category = "momentum"
return inertia
@@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
non_zero_range,
pd_rma,
v_offset,
v_pos_default,
v_series
)
def kdj(
high: Series, low: Series, close: Series,
length: Int = None, signal: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""KDJ
This indicator, derived from the Slow Stochastic, includes an
extra signal named the J line. The J line represents the divergence
of the %D value from the %K.
Sources:
* [anychart](https://docs.anychart.com/Stock_Charts/Technical_Indicators/Mathematical_Description#kdj)
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/kdj/)
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
length (int): The period. Default: ```9```
signal (int): Signal period. Default: ```3```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 3 columns
Note:
The J can go beyond ```[0, 100]``` for %K and %D lines when charted.
"""
# Validate
length = v_pos_default(length, 9)
signal = v_pos_default(signal, 3)
_length = length + signal + 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
offset = v_offset(offset)
# Calculate
highest_high = high.rolling(length).max()
lowest_low = low.rolling(length).min()
fastk = 100 * (close - lowest_low) / \
non_zero_range(highest_high, lowest_low)
k = pd_rma(fastk, n=signal)
d = pd_rma(k, n=signal)
j = 3 * k - 2 * d
# Offset
if offset != 0:
k = k.shift(offset)
d = d.shift(offset)
j = j.shift(offset)
# Fill
if "fillna" in kwargs:
k.fillna(kwargs["fillna"], inplace=True)
d.fillna(kwargs["fillna"], inplace=True)
j.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{length}_{signal}"
k.name = f"K{_props}"
d.name = f"D{_props}"
j.name = f"J{_props}"
k.category = d.category = j.category = "momentum"
data = {k.name: k, d.name: d, j.name: j}
df = DataFrame(data, index=close.index)
df.name = f"KDJ{_props}"
df.category = "momentum"
return df
@@ -1,97 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_drift, v_offset, v_pos_default, v_series
from .roc import roc
def kst(
close: Series, signal: Int = None,
roc1: Int = None, roc2: Int = None, roc3: Int = None, roc4: Int = None,
sma1: Int = None, sma2: Int = None, sma3: Int = None, sma4: Int = None,
drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""'Know Sure Thing'
This indicator, by Martin Pring, attempts to capture trends using a
smoothed indicator of four different smoothed ROCs.
Sources:
* [incrediblecharts](https://www.incrediblecharts.com/indicators/kst.php)
* [tradingview](https://www.tradingview.com/wiki/Know_Sure_Thing_(KST))
Parameters:
close (pd.Series): ```close``` Series
roc1 (int): ROC 1 period. Default: ```10```
roc2 (int): ROC 2 period. Default: ```15```
roc3 (int): ROC 3 period. Default: ```20```
roc4 (int): ROC 4 period. Default: ```30```
sma1 (int): SMA 1 period. Default: ```10```
sma2 (int): SMA 2 period. Default: ```10```
sma3 (int): SMA 3 period. Default: ```10```
sma4 (int): SMA 4 period. Default: ```15```
signal (int): Signal period. Default: ```9```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 2 columns
"""
# Validate
roc1 = int(roc1) if roc1 and roc1 > 0 else 10
roc2 = int(roc2) if roc2 and roc2 > 0 else 15
roc3 = int(roc3) if roc3 and roc3 > 0 else 20
roc4 = int(roc4) if roc4 and roc4 > 0 else 30
sma1 = int(sma1) if sma1 and sma1 > 0 else 10
sma2 = int(sma2) if sma2 and sma2 > 0 else 10
sma3 = int(sma3) if sma3 and sma3 > 0 else 10
sma4 = int(sma4) if sma4 and sma4 > 0 else 15
signal = v_pos_default(signal, 9)
_rmax = max(roc1, roc2, roc3, roc4)
_smax = max(sma1, sma2, sma3, sma4)
_length = _rmax + _smax
close = v_series(close, _length)
if close is None:
return
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
rocma1 = roc(close, roc1).rolling(sma1).mean()
rocma2 = roc(close, roc2).rolling(sma2).mean()
rocma3 = roc(close, roc3).rolling(sma3).mean()
rocma4 = roc(close, roc4).rolling(sma4).mean()
kst = 100 * (rocma1 + 2 * rocma2 + 3 * rocma3 + 4 * rocma4)
kst_signal = kst.rolling(signal).mean()
# Offset
if offset != 0:
kst = kst.shift(offset)
kst_signal = kst_signal.shift(offset)
# Fill
if "fillna" in kwargs:
kst.fillna(kwargs["fillna"], inplace=True)
kst_signal.fillna(kwargs["fillna"], inplace=True)
# Name and Category
kst.name = f"KST_{roc1}_{roc2}_{roc3}_{roc4}_{sma1}_{sma2}_{sma3}_{sma4}"
kst_signal.name = f"KSTs_{signal}"
kst.category = kst_signal.category = "momentum"
data = {kst.name: kst, kst_signal.name: kst_signal}
df = DataFrame(data, index=close.index)
df.name = f"KST_{roc1}_{roc2}_{roc3}_{roc4}_{sma1}_{sma2}_{sma3}_{sma4}_{signal}"
df.category = "momentum"
return df
@@ -1,141 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import concat, DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.overlap import ema
from pandas_ta.utils import (
signals,
v_offset,
v_pos_default,
v_series,
v_talib
)
def macd(
close: Series, fast: Int = None, slow: Int = None,
signal: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Moving Average Convergence Divergence
This indicator attempts to identify trends.
Sources:
* [tradingview](https://www.tradingview.com/wiki/MACD_(Moving_Average_Convergence/Divergence))
* [tradingview (AS Mode)](https://tr.tradingview.com/script/YFlKXHnP/)
Parameters:
close (pd.Series): ```close``` Series
fast (int): Fast MA period. Default: ```12```
slow (int): Slow MA period. Default: ```26```
signal (int): Signal period. Default: ```9```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
asmode (value): Enable AS version of MACD. Default: ```False```
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 3 columns
"""
# Validate
fast = v_pos_default(fast, 12)
slow = v_pos_default(slow, 26)
signal = v_pos_default(signal, 9)
if slow < fast:
fast, slow = slow, fast
_length = slow + signal - 1
close = v_series(close, _length)
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
as_mode = kwargs.setdefault("asmode", False)
# Calculate
if Imports["talib"] and mode_tal:
from talib import MACD
macd, signalma, histogram = MACD(close, fast, slow, signal)
else:
fastma = ema(close, length=fast, talib=mode_tal)
slowma = ema(close, length=slow, talib=mode_tal)
macd = fastma - slowma
macd_fvi = macd.loc[macd.first_valid_index():, ]
signalma = ema(close=macd_fvi, length=signal, talib=mode_tal)
histogram = macd - signalma
if as_mode:
macd = macd - signalma
macd_fvi = macd.loc[macd.first_valid_index():, ]
signalma = ema(close=macd_fvi, length=signal, talib=mode_tal)
histogram = macd - signalma
# Offset
if offset != 0:
macd = macd.shift(offset)
histogram = histogram.shift(offset)
signalma = signalma.shift(offset)
# Fill
if "fillna" in kwargs:
macd.fillna(kwargs["fillna"], inplace=True)
histogram.fillna(kwargs["fillna"], inplace=True)
signalma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_asmode = "AS" if as_mode else ""
_props = f"_{fast}_{slow}_{signal}"
macd.name = f"MACD{_asmode}{_props}"
histogram.name = f"MACD{_asmode}h{_props}"
signalma.name = f"MACD{_asmode}s{_props}"
macd.category = histogram.category = signalma.category = "momentum"
data = {
macd.name: macd,
histogram.name: histogram,
signalma.name: signalma
}
df = DataFrame(data, index=close.index)
df.name = f"MACD{_asmode}{_props}"
df.category = macd.category
signal_indicators = kwargs.pop("signal_indicators", False)
if not signal_indicators:
return df
else:
signalsdf = concat(
[
df,
signals(
indicator=histogram,
xa=kwargs.pop("xa", 0),
xb=kwargs.pop("xb", None),
xseries=kwargs.pop("xseries", None),
xseries_a=kwargs.pop("xseries_a", None),
xseries_b=kwargs.pop("xseries_b", None),
cross_values=kwargs.pop("cross_values", True),
cross_series=kwargs.pop("cross_series", True),
offset=offset,
),
signals(
indicator=macd,
xa=kwargs.pop("xa", 0),
xb=kwargs.pop("xb", None),
xseries=kwargs.pop("xseries", None),
xseries_a=kwargs.pop("xseries_a", None),
xseries_b=kwargs.pop("xseries_b", None),
cross_values=kwargs.pop("cross_values", False),
cross_series=kwargs.pop("cross_series", True),
offset=offset,
),
],
axis=1,
)
return signalsdf
@@ -1,76 +0,0 @@
# -*- coding: utf-8 -*-
from numba import njit
from pandas import Series
from pandas_ta._typing import Array, DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import (
nb_idiff,
v_offset,
v_pos_default,
v_series,
v_talib
)
@njit(cache=True)
def nb_mom(x, n):
return nb_idiff(x, n)
def mom(
close: Series, length: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Momentum
This indicator attempts to quantify speed by using the differences over
a bar length.
Sources:
* [onlinetradingconcepts](http://www.onlinetradingconcepts.com/TechnicalAnalysis/Momentum.html)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```1```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length + 1)
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import MOM
mom = MOM(close, length)
else:
np_close = close.to_numpy()
_mom = nb_mom(np_close, length)
mom = Series(_mom, index=close.index)
# Offset
if offset != 0:
mom = mom.shift(offset)
# Fill
if "fillna" in kwargs:
mom.fillna(kwargs["fillna"], inplace=True)
# Name and Category
mom.name = f"MOM_{length}"
mom.category = "momentum"
return mom
@@ -1,68 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import ema, sma
from pandas_ta.utils import v_offset, v_pos_default, v_series
from pandas_ta.volatility import atr
def pgo(
high: Series, low: Series, close: Series, length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Pretty Good Oscillator
This indicator, by Mark Johnson, attempts to identify breakouts for longer
time periods based on the distance of the current bar to its N-day
SMA, expressed in terms of an ATR over a similar length.
Sources:
* [tradingtechnologies](https://library.tradingtechnologies.com/trade/chrt-ti-pretty-good-oscillator.html)
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
length (int): The period. Default: ```14```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note: Entry
* Long when greater than 3.
* Short when less than -3.
"""
# Validate
length = v_pos_default(length, 14)
_length = 2 * length
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
offset = v_offset(offset)
# Calculate
pgo = (close - sma(close, length)) \
/ ema(atr(high, low, close, length), length)
# Offset
if offset != 0:
pgo = pgo.shift(offset)
# Fill
if "fillna" in kwargs:
pgo.fillna(kwargs["fillna"], inplace=True)
# Name and Category
pgo.name = f"PGO_{length}"
pgo.category = "momentum"
return pgo
@@ -1,107 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.ma import ma
from pandas_ta.maps import Imports
from pandas_ta.utils import (
tal_ma,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series,
v_talib
)
def ppo(
close: Series, fast: Int = None, slow: Int = None, signal: Int = None,
scalar: IntFloat = None, mamode: str = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Percentage Price Oscillator
Similar to MACD.
Sources:
* [investopedia](https://www.investopedia.com/terms/p/ppo.asp)
Parameters:
close (pd.Series): ```close``` Series
fast (int): Fast MA period. Default: ```12```
slow (int): Slow MA period. Default: ```26```
signal (int): Signal period. Default: ```9```
scalar (float): Scalar. Default: ```100```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 3 columns
"""
# Validate
fast = v_pos_default(fast, 12)
slow = v_pos_default(slow, 26)
signal = v_pos_default(signal, 9)
if slow < fast:
fast, slow = slow, fast
_length = max(fast, slow, signal)
close = v_series(close, _length)
if close is None:
return
scalar = v_scalar(scalar, 100)
mamode = v_mamode(mamode, "sma")
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import PPO
ppo = PPO(close, fast, slow, tal_ma(mamode))
else:
fastma = ma(mamode, close, length=fast, talib=mode_tal)
slowma = ma(mamode, close, length=slow, talib=mode_tal)
ppo = scalar * (fastma - slowma) / slowma
if all(isnan(ppo)):
return # Emergency Break
signalma = ma("ema", ppo, length=signal, talib=mode_tal)
histogram = ppo - signalma
# Offset
if offset != 0:
ppo = ppo.shift(offset)
histogram = histogram.shift(offset)
signalma = signalma.shift(offset)
# Fill
if "fillna" in kwargs:
ppo.fillna(kwargs["fillna"], inplace=True)
histogram.fillna(kwargs["fillna"], inplace=True)
signalma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{fast}_{slow}_{signal}"
ppo.name = f"PPO{_props}"
histogram.name = f"PPOh{_props}"
signalma.name = f"PPOs{_props}"
ppo.category = histogram.category = signalma.category = "momentum"
data = {
ppo.name: ppo,
histogram.name: histogram,
signalma.name: signalma
}
df = DataFrame(data, index=close.index)
df.name = f"PPO{_props}"
df.category = ppo.category
return df
@@ -1,80 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import sign
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import (
nb_idiff,
v_drift,
v_offset,
v_pos_default,
v_scalar,
v_series
)
def psl(
close: Series, open_: Series = None,
length: Int = None, scalar: IntFloat = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Psychological Line
This indicator compares the number of the rising bars to the total number
of bars. In other words, it is the percentage of bars that are above the
previous bar over a given length.
Sources:
* [quantshare](https://www.quantshare.com/item-851-psychological-line)
Parameters:
close (pd.Series): ```close``` Series
open_ (pd.Series): ```open``` Series
length (int): The period. Default: ```12```
scalar (float): Scalar. Default: ```100```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 12)
close = v_series(close, length)
if close is None:
return
scalar = v_scalar(scalar, 100)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
if open_ is not None:
open_ = v_series(open_)
diff = sign(close - open_)
else:
diff = sign(close.diff(drift))
diff.fillna(0, inplace=True)
diff[diff <= 0] = 0 # Set negative values to zero
psl = scalar * diff.rolling(length).sum() / length
# Offset
if offset != 0:
psl = psl.shift(offset)
# Fill
if "fillna" in kwargs:
psl.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{length}"
psl.name = f"PSL{_props}"
psl.category = "momentum"
return psl
@@ -1,174 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan, maximum, minimum, nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.ma import ma
from pandas_ta.utils import (
v_drift,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series
)
from .rsi import rsi
def qqe(
close: Series, length: Int = None,
smooth: Int = None, factor: IntFloat = None,
mamode: str = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Quantitative Qualitative Estimation
This indicator is similar to SuperTrend but uses a Smoothed ```rsi```
with upper and lower bands.
Sources:
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/qqe-quantitative-qualitative-estimation/)
* [tradingpedia](https://www.tradingpedia.com/forex-trading-indicators/quantitative-qualitative-estimation)
* [tradingview](https://www.tradingview.com/script/IYfA9R2k-QQE-MT4/)
Parameters:
close (pd.Series): ```close``` Series
length (int): RSI period. Default: ```14```
smooth (int): RSI smoothing period. Default: ```5```
factor (float): QQE Factor. Default: ```4.236```
mamode (str): See ```help(ta.ma)```. Default: ```"ema"```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 4 columns
Tip: Trend
* Long: When the Smoothed RSI crosses the previous upperband.
* Short: When the Smoothed RSI crosses the previous lowerband.
Note: See also
* QQE.mq5 by EarnForex Copyright © 2010
* Tim Hyder (2008) version
* Roman Ignatov (2006) version
"""
# Validate
length = v_pos_default(length, 14)
smooth = v_pos_default(smooth, 5)
wilders_length = 2 * length - 1
_length = wilders_length + smooth
close = v_series(close, _length)
if close is None:
return
factor = v_scalar(factor, 4.236)
mamode = v_mamode(mamode, "ema")
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
rsi_ = rsi(close, length)
_mode = mamode.lower()[0] if mamode != "ema" else ""
rsi_ma = ma(mamode, rsi_, length=smooth)
# RSI MA True Range
rsi_ma_tr = rsi_ma.diff(drift).abs()
if all(isnan(rsi_ma_tr)):
return
# Double Smooth the RSI MA True Range using Wilder's Length with a default
# width of 4.236.
smoothed_rsi_tr_ma = ma("ema", rsi_ma_tr, length=wilders_length)
if all(isnan(smoothed_rsi_tr_ma)):
return # Emergency Break
dar = factor * ma("ema", smoothed_rsi_tr_ma, length=wilders_length)
if all(isnan(dar)):
return # Emergency Break
# Create the Upper and Lower Bands around RSI MA.
upperband = rsi_ma + dar
lowerband = rsi_ma - dar
m = close.size
long = Series(0, index=close.index)
short = Series(0, index=close.index)
trend = Series(1, index=close.index)
qqe = Series(rsi_ma.iat[0], index=close.index)
qqe_long = Series(nan, index=close.index)
qqe_short = Series(nan, index=close.index)
for i in range(1, m):
c_rsi, p_rsi = rsi_ma.iat[i], rsi_ma.iat[i - 1]
c_long, p_long = long.iat[i - 1], long.iat[i - 2]
c_short, p_short = short.iat[i - 1], short.iat[i - 2]
# Long Line
if p_rsi > c_long and c_rsi > c_long:
long.iat[i] = maximum(c_long, lowerband.iat[i])
else:
long.iat[i] = lowerband.iat[i]
# Short Line
if p_rsi < c_short and c_rsi < c_short:
short.iat[i] = minimum(c_short, upperband.iat[i])
else:
short.iat[i] = upperband.iat[i]
# Trend & QQE Calculation
# Long: Current RSI_MA value Crosses the Prior Short Line Value
# Short: Current RSI_MA Crosses the Prior Long Line Value
if (c_rsi > c_short and p_rsi < p_short) or \
(c_rsi <= c_short and p_rsi >= p_short):
trend.iat[i] = 1
qqe.iat[i] = qqe_long.iat[i] = long.iat[i]
elif (c_rsi > c_long and p_rsi < p_long) or \
(c_rsi <= c_long and p_rsi >= p_long):
trend.iat[i] = -1
qqe.iat[i] = qqe_short.iat[i] = short.iat[i]
else:
trend.iat[i] = trend.iat[i - 1]
if trend.iat[i] == 1:
qqe.iat[i] = qqe_long.iat[i] = long.iat[i]
else:
qqe.iat[i] = qqe_short.iat[i] = short.iat[i]
# Offset
if offset != 0:
rsi_ma = rsi_ma.shift(offset)
qqe = qqe.shift(offset)
long = long.shift(offset)
short = short.shift(offset)
# Fill
if "fillna" in kwargs:
rsi_ma.fillna(kwargs["fillna"], inplace=True)
qqe.fillna(kwargs["fillna"], inplace=True)
qqe_long.fillna(kwargs["fillna"], inplace=True)
qqe_short.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"{_mode}_{length}_{smooth}_{factor}"
qqe.name = f"QQE{_props}"
rsi_ma.name = f"QQE{_props}_RSI{_mode.upper()}MA"
qqe_long.name = f"QQEl{_props}"
qqe_short.name = f"QQEs{_props}"
qqe.category = rsi_ma.category = "momentum"
qqe_long.category = qqe_short.category = qqe.category
data = {
qqe.name: qqe,
rsi_ma.name: rsi_ma,
# long.name: long,
# short.name: short
qqe_long.name: qqe_long,
qqe_short.name: qqe_short
}
df = DataFrame(data, index=close.index)
df.name = f"QQE{_props}"
df.category = qqe.category
return df
@@ -1,84 +0,0 @@
# -*- coding: utf-8 -*-
from numba import njit
from pandas import Series
from pandas_ta._typing import Array, DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.utils import (
nb_idiff,
nb_shift,
v_offset,
v_pos_default,
v_scalar,
v_series,
v_talib
)
from .mom import mom
@njit(cache=True)
def nb_roc(x, n, k):
return k * nb_idiff(x, n) / nb_shift(x, n)
def roc(
close: Series, length: Int = None,
scalar: IntFloat = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Rate of Change
This indicator, also (confusingly) known as Momentum, is a pure
oscillator that quantifies the percent change.
Sources:
* [tradingview](https://www.tradingview.com/wiki/Rate_of_Change_(ROC))
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
scalar (float): Scalar. Default: ```100```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length + 1)
if close is None:
return
scalar = v_scalar(scalar, 100)
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import ROC
roc = ROC(close, length)
else:
# roc = scalar * mom(close=close, length=length, talib=mode_tal) \
# / close.shift(length)
np_close = close.to_numpy()
_roc = nb_roc(np_close, length, scalar)
roc = Series(_roc, index=close.index)
# Offset
if offset != 0:
roc = roc.shift(offset)
# Fill
if "fillna" in kwargs:
roc.fillna(kwargs["fillna"], inplace=True)
# Name and Category
roc.name = f"ROC_{length}"
roc.category = "momentum"
return roc
@@ -1,115 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, concat, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.ma import ma
from pandas_ta.utils import (
signals,
v_drift,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series,
v_talib
)
def rsi(
close: Series, length: Int = None, scalar: IntFloat = None,
mamode: str = None, talib: bool = None,
drift: Int = None, offset: Int = None,
**kwargs: DictLike
) -> Series:
"""Relative Strength Index
This oscillator used to attempts to quantify "velocity" and "magnitude".
Sources:
* [tradingview](https://www.tradingview.com/wiki/Relative_Strength_Index_(RSI))
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```14```
scalar (float): Scalar. Default: ```100```
mamode (str): See ```help(ta.ma)```. Default: ```"rma"```
talib (bool): If installed, use TA Lib. Default: ```True```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Warning:
TA-Lib Correlation: ```np.float64(0.9289853267851295)```
Tip:
Corrective contributions welcome!
"""
# Validate
length = v_pos_default(length, 14)
close = v_series(close, length + 1)
if close is None:
return
scalar = v_scalar(scalar, 100)
mamode = v_mamode(mamode, "rma")
mode_tal = v_talib(talib)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import RSI
rsi = RSI(close, length)
else:
negative = close.diff(drift)
positive = negative.copy()
positive[positive < 0] = 0 # Make negatives 0 for the positive series
negative[negative > 0] = 0 # Make positives 0 for the negative series
positive_avg = ma(mamode, positive, length=length, talib=mode_tal)
negative_avg = ma(mamode, negative, length=length, talib=mode_tal)
rsi = scalar * positive_avg / (positive_avg + negative_avg.abs())
# Offset
if offset != 0:
rsi = rsi.shift(offset)
# Fill
if "fillna" in kwargs:
rsi.fillna(kwargs["fillna"], inplace=True)
# Name and Category
rsi.name = f"RSI_{length}"
rsi.category = "momentum"
signal_indicators = kwargs.pop("signal_indicators", False)
if not signal_indicators:
return rsi
else:
signalsdf = concat(
[
DataFrame({rsi.name: rsi}),
signals(
indicator=rsi,
xa=kwargs.pop("xa", 80),
xb=kwargs.pop("xb", 20),
xseries=kwargs.pop("xseries", None),
xseries_a=kwargs.pop("xseries_a", None),
xseries_b=kwargs.pop("xseries_b", None),
cross_values=kwargs.pop("cross_values", False),
cross_series=kwargs.pop("cross_series", True),
offset=offset,
),
],
axis=1,
)
return signalsdf
@@ -1,149 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas_ta._typing import DictLike, Int
from pandas import concat, DataFrame, Series
from pandas_ta.utils import (
signals,
v_drift,
v_offset,
v_pos_default,
v_series
)
from shutil import which
def rsx(
close: Series, length: Int = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Relative Strength Xtra
This indicator, by Jurik Research, is an enhanced version of the RSI which
attemps to reduce noise and provide a clearer, though slightly
delayed, signal.
Sources:
* [jurikres](http://www.jurikres.com/catalog1/ms_rsx.htm)
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/jurik-rsx/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```14```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 14)
close = v_series(close, length)
if close is None:
return
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
m = close.size
vC, v1C = 0, 0
v4, v8, v10, v14, v18, v20 = 0, 0, 0, 0, 0, 0
f0, f8, f10, f18, f20, f28, f30, f38 = 0, 0, 0, 0, 0, 0, 0, 0
f40, f48, f50, f58, f60, f68, f70, f78 = 0, 0, 0, 0, 0, 0, 0, 0
f80, f88, f90 = 0, 0, 0
result = [nan for _ in range(0, length - 1)] + [50]
for i in range(length, m):
if f90 == 0:
f90 = 1.0
f0 = 0.0
if length - 1.0 >= 5:
f88 = length - 1.0
else:
f88 = 5.0
f8 = 100.0 * close.iat[i]
f18 = 3.0 / (length + 2.0)
f20 = 1.0 - f18
else:
if f88 <= f90:
f90 = f88 + 1
else:
f90 = f90 + 1
f10 = f8
f8 = 100 * close.iat[i]
v8 = f8 - f10
f28 = f20 * f28 + f18 * v8
f30 = f18 * f28 + f20 * f30
vC = 1.5 * f28 - 0.5 * f30
f38 = f20 * f38 + f18 * vC
f40 = f18 * f38 + f20 * f40
v10 = 1.5 * f38 - 0.5 * f40
f48 = f20 * f48 + f18 * v10
f50 = f18 * f48 + f20 * f50
v14 = 1.5 * f48 - 0.5 * f50
f58 = f20 * f58 + f18 * abs(v8)
f60 = f18 * f58 + f20 * f60
v18 = 1.5 * f58 - 0.5 * f60
f68 = f20 * f68 + f18 * v18
f70 = f18 * f68 + f20 * f70
v1C = 1.5 * f68 - 0.5 * f70
f78 = f20 * f78 + f18 * v1C
f80 = f18 * f78 + f20 * f80
v20 = 1.5 * f78 - 0.5 * f80
if f88 >= f90 and f8 != f10:
f0 = 1.0
if f88 == f90 and f0 == 0.0:
f90 = 0.0
if f88 < f90 and v20 > 0.0000000001:
v4 = (v14 / v20 + 1.0) * 50.0
if v4 > 100.0:
v4 = 100.0
if v4 < 0.0:
v4 = 0.0
else:
v4 = 50.0
result.append(v4)
rsx = Series(result, index=close.index)
# Offset
if offset != 0:
rsx = rsx.shift(offset)
# Fill
if "fillna" in kwargs:
rsx.fillna(kwargs["fillna"], inplace=True)
# Name and Category
rsx.name = f"RSX_{length}"
rsx.category = "momentum"
signal_indicators = kwargs.pop("signal_indicators", False)
if not signal_indicators:
return rsx
else:
signalsdf = concat(
[
DataFrame({rsx.name: rsx}),
signals(
indicator=rsx,
xa=kwargs.pop("xa", 80),
xb=kwargs.pop("xb", 20),
xseries=kwargs.pop("xseries", None),
xseries_a=kwargs.pop("xseries_a", None),
xseries_b=kwargs.pop("xseries_b", None),
cross_values=kwargs.pop("cross_values", False),
cross_series=kwargs.pop("cross_series", True),
offset=offset,
),
],
axis=1
)
return signalsdf
@@ -1,87 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.overlap import swma
from pandas_ta.utils import non_zero_range, v_offset, v_pos_default, v_series
def rvgi(
open_: Series, high: Series, low: Series, close: Series,
length: Int = None, swma_length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Relative Vigor Index
This indicator attempts to quantify the strength of a trend relative to
its trading range.
Sources:
* [investopedia](https://www.investopedia.com/terms/r/relative_vigor_index.asp)
Parameters:
open_ (pd.Series): ```open``` Series
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
length (int): The period. Default: ```14```
swma_length (int): SWMA period. Default: ```4```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 14)
swma_length = v_pos_default(swma_length, 4)
_length = length + swma_length - 1
open_ = v_series(open_, _length)
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if open_ is None or high is None or low is None or close is None:
return
offset = v_offset(offset)
# Calculate
high_low_range = non_zero_range(high, low)
close_open_range = non_zero_range(close, open_)
numerator = swma(close_open_range, length=swma_length) \
.rolling(length).sum()
denominator = swma(high_low_range, length=swma_length) \
.rolling(length).sum()
rvgi = numerator / denominator
signal = swma(rvgi, length=swma_length)
if all(isnan(signal.to_numpy())):
return # Emergency Break
# Offset
if offset != 0:
rvgi = rvgi.shift(offset)
signal = signal.shift(offset)
# Fill
if "fillna" in kwargs:
rvgi.fillna(kwargs["fillna"], inplace=True)
signal.fillna(kwargs["fillna"], inplace=True)
# Name and Category
rvgi.name = f"RVGI_{length}_{swma_length}"
signal.name = f"RVGIs_{length}_{swma_length}"
rvgi.category = signal.category = "momentum"
data = {rvgi.name: rvgi, signal.name: signal}
df = DataFrame(data, index=close.index)
df.name = f"RVGI_{length}_{swma_length}"
df.category = rvgi.category
return df
@@ -1,71 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import arctan, pi, rad2deg
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
nb_idiff,
v_bool,
v_offset,
v_pos_default,
v_series
)
def slope(
close: Series, length: Int = None,
as_angle: bool = None, to_degrees: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Slope
Calculates a rolling slope.
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```1```
as_angle (bool): Converts slope to an angle in radians
per ```np.arctan()```. Default: ```False```
to_degrees (value): If ```as_angle=True```, converts radians to
degrees. Default: ```False```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 1)
close = v_series(close, length + 1)
if close is None:
return
as_angle = v_bool(as_angle, False)
to_degrees = v_bool(to_degrees, False)
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
_slope = nb_idiff(np_close, length) / length
if as_angle:
_slope = arctan(_slope)
if to_degrees:
_slope = rad2deg(_slope)
slope = Series(_slope, index=close.index)
# Offset
if offset != 0:
slope = slope.shift(offset)
# Fill
if "fillna" in kwargs:
slope.fillna(kwargs["fillna"], inplace=True)
# Name and Category
slope.name = f"SLOPE_{length}" if not as_angle else f"ANGLE{'d' if to_degrees else 'r'}_{length}"
slope.category = "momentum"
return slope
@@ -1,118 +0,0 @@
# -*- coding: utf-8 -*-
from sys import float_info as sflt
from numpy import isnan, maximum, minimum, nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.ma import ma
from pandas_ta.utils import (
v_bool,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series,
v_talib
)
def smc(
open_: Series, high: Series, low: Series, close: Series,
abr_length: Int = None, close_length: Int = None, vol_length: Int = None,
percent: Int = None, vol_ratio: IntFloat = None, asint: bool = None,
mamode: str = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DictLike:
"""Smart Money Concept
This indicator combines several techniques in an attempt to identify
significant movements that might indicate "smart money" actions.
It uses candlestick patterns, moving averages, and imbalance calculations.
Sources:
* [tradingview](https://www.tradingview.com/script/CnB3fSph-Smart-Money-Concepts-LuxAlgo/)
Parameters:
abr_length (int): ABR length. Default: ```14```
close_length (int): The ```close``` MA period. Default: ```50```
vol_length (int): Volatility period. Default: ```20```
percent (int): Percent of wick that exceeds the body. Default: ```5```
vol_ratio (float): Volatility ratio (high) limit. Default: ```1.5```
asint (bool): Returns as ```Int```. Default: ```True```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Returns:
(pd.DataFrame): 7 columns
"""
# Validate
abr_length = v_pos_default(abr_length, 14)
close_length = v_pos_default(close_length, 50)
vol_length = v_pos_default(vol_length, 20)
if close_length < abr_length:
abr_length, close_length = close_length, abr_length
_length = max(abr_length, close_length, vol_length) + 1
open_ = v_series(open_, _length)
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if open_ is None or high is None or low is None or close is None:
return
percent = v_pos_default(percent, 5)
body_percent = 0.01 * percent
vol_ratio = v_scalar(vol_ratio, 1.5)
asint = v_bool(asint)
mamode = v_mamode(mamode, "sma")
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
body_high, body_low = maximum(open_, close), minimum(open_, close)
body = body_high - body_low + sflt.epsilon
close_ma = ma(mamode, body, length=close_length, talib=mode_tal)
# Calculate imbalance sizes and percentages based on Average Bar Range (abr)
abr = high.rolling(window=abr_length).max() - low.rolling(window=abr_length).min()
top_imbalance = low.shift(2) - high
btm_imbalance = low - high.shift(2)
top_imbalance_pct = 100 * top_imbalance / abr
btm_imbalance_pct = 100 * btm_imbalance / abr
hld = high - low + sflt.epsilon
high_volatility = hld > vol_ratio * ma(mamode, hld, length=vol_length, talib=mode_tal)
btm_imbalance_flag = (btm_imbalance > 0) & (btm_imbalance_pct > 1)
top_imbalance_flag = (top_imbalance > 0) & (top_imbalance_pct > 1)
if asint:
high_volatility = high_volatility.astype(int)
btm_imbalance_flag = btm_imbalance_flag.astype(int)
top_imbalance_flag = top_imbalance_flag.astype(int)
_props = f"_{abr_length}_{close_length}_{vol_length}_{percent}"
data = {
f"SMChv{_props}": high_volatility,
f"SMCbf{_props}": btm_imbalance_flag,
f"SMCbi{_props}": btm_imbalance,
f"SMCbp{_props}": btm_imbalance_pct,
f"SMCtf{_props}": top_imbalance_flag,
f"SMCti{_props}": top_imbalance,
f"SMCtp{_props}": top_imbalance_pct,
}
df = DataFrame(data, index=close.index)
# Offset
if offset != 0:
df = df.shift(offset)
# Fill
df.ffill(inplace=True)
df.bfill(inplace=True)
# Name and Category
df.name = f"SMC{_props}"
df.category = "momentum"
return df
@@ -1,92 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import v_offset, v_pos_default, v_scalar, v_series
from .tsi import tsi
def smi(
close: Series, fast: Int = None, slow: Int = None,
signal: Int = None, scalar: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""SMI Ergodic Indicator
This indicator, by William Blau, is the same as the TSI except the SMI
includes a signal line. A trend is considered bullish when crossing above
zero and bearish when crossing below zero. This implementation includes
both the SMI Ergodic Indicator and SMI Ergodic Oscillator.
Sources:
* [motivewave](https://www.motivewave.com/studies/smi_ergodic_indicator.htm)
* [tradingview A](https://www.tradingview.com/script/Xh5Q0une-SMI-Ergodic-Oscillator/)
* [tradingview B](https://www.tradingview.com/script/cwrgy4fw-SMIIO/)
Parameters:
close (pd.Series): ```close``` Series
fast (int): The short period. Default: ```5```
slow (int): The long period. Default: ```20```
signal (int): Signal period. Default: ```5```
scalar (float): Scalar. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 3 columns
"""
# Validate
fast = v_pos_default(fast, 5)
slow = v_pos_default(slow, 20)
signal = v_pos_default(signal, 5)
if slow < fast:
fast, slow = slow, fast
_length = slow + signal + 1
close = v_series(close, _length)
if close is None:
return
scalar = v_scalar(scalar, 1)
offset = v_offset(offset)
# Calculate
tsi_df = tsi(close, fast=fast, slow=slow, signal=signal, scalar=scalar)
if tsi_df is None:
return # Emergency Break
smi = tsi_df.iloc[:, 0]
signalma = tsi_df.iloc[:, 1]
if all(isnan(signalma)):
return # Emergency Break
osc = smi - signalma
# Offset
if offset != 0:
smi = smi.shift(offset)
signalma = signalma.shift(offset)
osc = osc.shift(offset)
# Fill
if "fillna" in kwargs:
smi.fillna(kwargs["fillna"], inplace=True)
signalma.fillna(kwargs["fillna"], inplace=True)
osc.fillna(kwargs["fillna"], inplace=True)
# Name and Category
# _scalar = f"_{scalar}" if scalar != 1 else ""
_props = f"_{fast}_{slow}_{signal}_{scalar}"
smi.name = f"SMI{_props}"
signalma.name = f"SMIs{_props}"
osc.name = f"SMIo{_props}"
smi.category = signalma.category = osc.category = "momentum"
data = {smi.name: smi, signalma.name: signalma, osc.name: osc}
df = DataFrame(data, index=close.index)
df.name = f"SMI{_props}"
df.category = smi.category
return df
@@ -1,205 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.overlap import ema, linreg, sma
from pandas_ta.trend import decreasing, increasing
from pandas_ta.utils import (
simplify_columns,
unsigned_differences,
v_bool,
v_mamode,
v_offset,
v_pos_default,
v_series
)
from pandas_ta.volatility import bbands, kc
from .mom import mom
def squeeze(
high: Series, low: Series, close: Series,
bb_length: Int = None, bb_std: IntFloat = None,
kc_length: Int = None, kc_scalar: IntFloat = None,
mom_length: Int = None, mom_smooth: Int = None,
use_tr: bool = None, mamode: str = None,
prenan: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Squeeze
This indicator, based on John Carter's "TTM Squeeze" indicator, attempts
identify momentum using volatility.
Sources:
* "Mastering the Trade" (chapter 11), John Carter
* [thinkorswim](https://tlc.thinkorswim.com/center/reference/Tech-Indicators/studies-library/T-U/TTM-Squeeze)
* [tradestation](https://tradestation.tradingappstore.com/products/TTMSqueeze)
* [tradingview](https://www.tradingview.com/scripts/lazybear/)
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
bb_length (int): BB period. Default: ```20```
bb_std (float): BB Std. Dev. Default: ```2```
kc_length (int): KC period. Default: ```20```
kc_scalar (float): KC scalar. Default: ```1.5```
mom_length (int): Momentum Period. Default: ```12```
mom_smooth (int): Momentum Smoothing period. Default: ```6```
mamode (str): One of: "ema" or "sma". Default: ```"sma"```
prenan (bool): Apply prenans. Default: ```False```
offset (int): Post shift. Default: ```0```
Other Parameters:
tr (value): Use True Range for Keltner Channels. Default: ```True```
asint (bool): Returns as ```Int```. Default: ```True```
lazybear (value): LazyBear's TradingView. Default: ```False```
detailed (value): Extra detailed. Default: ```False```
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame):
* Default: 4 columns
* Detailed: 10 columns
Note: Volatility
* Increasing: ```kc``` and ```bbands``` difference increases
* Decreasing: ```kc``` and ```bbands``` difference decreases
"""
# Validate
bb_length = v_pos_default(bb_length, 20)
kc_length = v_pos_default(kc_length, 20)
mom_length = v_pos_default(mom_length, 12)
mom_smooth = v_pos_default(mom_smooth, 6)
_length = max(bb_length, kc_length, mom_length, mom_smooth) + 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
bb_std = v_pos_default(bb_std, 2.0)
kc_scalar = v_pos_default(kc_scalar, 1.5)
mamode = v_mamode(mamode, "sma")
prenan = v_bool(prenan, False)
offset = v_offset(offset)
use_tr = kwargs.pop("tr", True)
asint = kwargs.pop("asint", True)
detailed = kwargs.pop("detailed", False)
lazybear = kwargs.pop("lazybear", False)
# Calculate
bbd = bbands(close, length=bb_length, std=bb_std, mamode=mamode)
kch = kc(
high, low, close, length=kc_length, scalar=kc_scalar,
mamode=mamode, tr=use_tr
)
# Simplify KC and BBAND column names for dynamic access
bbd.columns = simplify_columns(bbd)
kch.columns = simplify_columns(kch)
if lazybear:
highest_high = high.rolling(kc_length).max()
lowest_low = low.rolling(kc_length).min()
avg_ = 0.5 * (0.5 * (highest_high + lowest_low) + kch.b)
squeeze = linreg(close - avg_, length=kc_length)
else:
momo = mom(close, length=mom_length)
if mamode.lower() == "ema":
squeeze = ema(momo, length=mom_smooth)
else: # "sma"
squeeze = sma(momo, length=mom_smooth)
# Classify Squeezes
squeeze_on = (bbd.l > kch.l) & (bbd.u < kch.u)
squeeze_off = (bbd.l < kch.l) & (bbd.u > kch.u)
no_squeeze = ~squeeze_on & ~squeeze_off
# Offset
if offset != 0:
squeeze = squeeze.shift(offset)
squeeze_on = squeeze_on.shift(offset)
squeeze_off = squeeze_off.shift(offset)
no_squeeze = no_squeeze.shift(offset)
# Fill
if "fillna" in kwargs:
squeeze.fillna(kwargs["fillna"], inplace=True)
squeeze_on.fillna(kwargs["fillna"], inplace=True)
squeeze_off.fillna(kwargs["fillna"], inplace=True)
no_squeeze.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = "" if use_tr else "hlr"
_props += f"_{bb_length}_{bb_std}_{kc_length}_{kc_scalar}"
_props += "_LB" if lazybear else ""
squeeze.name = f"SQZ{_props}"
if asint:
squeeze_on = squeeze_on.astype(int)
squeeze_off = squeeze_off.astype(int)
no_squeeze = no_squeeze.astype(int)
if prenan:
nanlength = max(bb_length, kc_length) - 2
squeeze_on[:nanlength] = nan
squeeze_off[:nanlength] = nan
no_squeeze[:nanlength] = nan
data = {
squeeze.name: squeeze,
f"SQZ_ON": squeeze_on,
f"SQZ_OFF": squeeze_off,
f"SQZ_NO": no_squeeze
}
df = DataFrame(data, index=close.index)
df.name = squeeze.name
df.category = squeeze.category = "momentum"
# More Detail
if detailed:
pos_squeeze = squeeze[squeeze >= 0]
neg_squeeze = squeeze[squeeze < 0]
pos_inc, pos_dec = unsigned_differences(pos_squeeze, asint=True)
neg_inc, neg_dec = unsigned_differences(neg_squeeze, asint=True)
pos_inc *= squeeze
pos_dec *= squeeze
neg_dec *= squeeze
neg_inc *= squeeze
pos_inc.replace(0, nan, inplace=True)
pos_dec.replace(0, nan, inplace=True)
neg_dec.replace(0, nan, inplace=True)
neg_inc.replace(0, nan, inplace=True)
sqz_inc = squeeze * increasing(squeeze)
sqz_dec = squeeze * decreasing(squeeze)
sqz_inc.replace(0, nan, inplace=True)
sqz_dec.replace(0, nan, inplace=True)
# Handle fills
if "fillna" in kwargs:
sqz_inc.fillna(kwargs["fillna"], inplace=True)
sqz_dec.fillna(kwargs["fillna"], inplace=True)
pos_inc.fillna(kwargs["fillna"], inplace=True)
pos_dec.fillna(kwargs["fillna"], inplace=True)
neg_dec.fillna(kwargs["fillna"], inplace=True)
neg_inc.fillna(kwargs["fillna"], inplace=True)
df[f"SQZ_INC"] = sqz_inc
df[f"SQZ_DEC"] = sqz_dec
df[f"SQZ_PINC"] = pos_inc
df[f"SQZ_PDEC"] = pos_dec
df[f"SQZ_NDEC"] = neg_dec
df[f"SQZ_NINC"] = neg_inc
return df
@@ -1,222 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.ma import ma
from pandas_ta.momentum import mom
from pandas_ta.trend import decreasing, increasing
from pandas_ta.utils import (
simplify_columns,
unsigned_differences,
v_bool,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series
)
from pandas_ta.volatility import bbands, kc
def squeeze_pro(
high: Series, low: Series, close: Series,
bb_length: Int = None, bb_std: IntFloat = None,
kc_length: Int = None, kc_scalar_narrow: IntFloat = None,
kc_scalar_normal: IntFloat = None, kc_scalar_wide: IntFloat = None,
mom_length: Int = None, mom_smooth: Int = None,
use_tr: bool = None, mamode: str = None,
prenan: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Squeeze Pro
This indicator, based on John Carter's "TTM Squeeze" indicator, attempts
identify momentum using volatility with additional details.
Sources:
* [usethinkscript](https://usethinkscript.com/threads/john-carters-squeeze-pro-indicator-for-thinkorswim-free.4021/)
* [tradingview](https://www.tradingview.com/script/TAAt6eRX-Squeeze-PRO-Indicator-Makit0/)
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
bb_length (int): BB period. Default: ```20```
bb_std (float): BB Std. Dev. Default: ```2```
kc_length (int): KC period. Default: ```20```
kc_scalar_normal (float): Keltner Channel scalar for normal channel.
Default: ```1.5```
kc_scalar_narrow (float): Narrow channel KC scalar. Default: ```1```
kc_scalar_wide (float): Wide channel KC scalar. Default: ```2```
mom_length (int): Momentum Period. Default: ```12```
mom_smooth (int): Momentum Smoothing period. Default: ```6```
mamode (str): One of: "ema" or "sma". Default: ```"sma"```
prenan (bool): Apply prenans. Default: ```False```
offset (int): Post shift. Default: ```0```
Other Parameters:
tr (value): Use True Range for Keltner Channels.
Default: ```True```
asint (bool): Returns as ```Int```. Default: ```True```
mamode (value): Which MA to use. Default: ```"sma"```
detailed (value): Extra detailed. Default: ```False```
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 6 columns (_default_) or 12 columns if ```detailed=True```
Warning:
May be depreciated in the future and combined with ```squeeze```.
"""
# Validate
bb_length = v_pos_default(bb_length, 20)
kc_length = v_pos_default(kc_length, 20)
mom_length = v_pos_default(mom_length, 12)
mom_smooth = v_pos_default(mom_smooth, 6)
_length = max(bb_length, kc_length, mom_length, mom_smooth) + 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
kc_scalar_narrow = v_scalar(kc_scalar_narrow, 1)
kc_scalar_normal = v_scalar(kc_scalar_normal, 1.5)
kc_scalar_wide = v_scalar(kc_scalar_wide, 2)
prenan = v_bool(prenan, False)
valid_kc_scaler = kc_scalar_wide > kc_scalar_normal \
and kc_scalar_normal > kc_scalar_narrow
if not valid_kc_scaler:
return
bb_std = v_pos_default(bb_std, 2.0)
mamode = v_mamode(mamode, "sma")
offset = v_offset(offset)
use_tr = kwargs.pop("tr", True)
asint = kwargs.pop("asint", True)
detailed = kwargs.pop("detailed", False)
# Calculate
bbd = bbands(close, length=bb_length, std=bb_std, mamode=mamode)
kch_wide = kc(
high, low, close, length=kc_length, scalar=kc_scalar_wide,
mamode=mamode, tr=use_tr
)
kch_normal = kc(
high, low, close, length=kc_length, scalar=kc_scalar_normal,
mamode=mamode, tr=use_tr
)
kch_narrow = kc(
high, low, close, length=kc_length, scalar=kc_scalar_narrow,
mamode=mamode, tr=use_tr
)
# Simplify KC and BBAND column names for dynamic access
bbd.columns = simplify_columns(bbd)
kch_wide.columns = simplify_columns(kch_wide)
kch_normal.columns = simplify_columns(kch_normal)
kch_narrow.columns = simplify_columns(kch_narrow)
momo = mom(close, length=mom_length)
squeeze = ma(mamode, momo, length=mom_smooth)
# Classify Squeezes
squeeze_on_wide = (bbd.l > kch_wide.l) & (bbd.u < kch_wide.u)
squeeze_on_normal = (bbd.l > kch_normal.l) & (bbd.u < kch_normal.u)
squeeze_on_narrow = (bbd.l > kch_narrow.l) & (bbd.u < kch_narrow.u)
squeeze_off_wide = (bbd.l < kch_wide.l) & (bbd.u > kch_wide.u)
no_squeeze = ~squeeze_on_wide & ~squeeze_off_wide
# Offset
if offset != 0:
squeeze = squeeze.shift(offset)
squeeze_on_wide = squeeze_on_wide.shift(offset)
squeeze_on_normal = squeeze_on_normal.shift(offset)
squeeze_on_narrow = squeeze_on_narrow.shift(offset)
squeeze_off_wide = squeeze_off_wide.shift(offset)
no_squeeze = no_squeeze.shift(offset)
# Fill
if "fillna" in kwargs:
squeeze.fillna(kwargs["fillna"], inplace=True)
squeeze_on_wide.fillna(kwargs["fillna"], inplace=True)
squeeze_on_normal.fillna(kwargs["fillna"], inplace=True)
squeeze_on_narrow.fillna(kwargs["fillna"], inplace=True)
squeeze_off_wide.fillna(kwargs["fillna"], inplace=True)
no_squeeze.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = "" if use_tr else "hlr"
_props += f"_{bb_length}_{bb_std}_{kc_length}_{kc_scalar_wide}_{kc_scalar_normal}_{kc_scalar_narrow}"
squeeze.name = f"SQZPRO{_props}"
if asint:
squeeze_on_wide = squeeze_on_wide.astype(int)
squeeze_on_narrow = squeeze_on_narrow.astype(int)
squeeze_on_normal = squeeze_on_normal.astype(int)
squeeze_off_wide = squeeze_off_wide.astype(int)
no_squeeze = no_squeeze.astype(int)
if prenan:
nanlength = max(bb_length, kc_length) - 2
squeeze_on_wide[:nanlength] = nan
squeeze_on_narrow[:nanlength] = nan
squeeze_on_normal[:nanlength] = nan
squeeze_off_wide[:nanlength] = nan
no_squeeze[:nanlength] = nan
data = {
squeeze.name: squeeze,
f"SQZPRO_ON_WIDE": squeeze_on_wide,
f"SQZPRO_ON_NORMAL": squeeze_on_normal,
f"SQZPRO_ON_NARROW": squeeze_on_narrow,
f"SQZPRO_OFF": squeeze_off_wide,
f"SQZPRO_NO": no_squeeze
}
df = DataFrame(data, index=close.index)
df.name = squeeze.name
df.category = squeeze.category = "momentum"
# More Detail
if detailed:
pos_squeeze = squeeze[squeeze >= 0]
neg_squeeze = squeeze[squeeze < 0]
pos_inc, pos_dec = unsigned_differences(pos_squeeze, asint=True)
neg_inc, neg_dec = unsigned_differences(neg_squeeze, asint=True)
pos_inc *= squeeze
pos_dec *= squeeze
neg_dec *= squeeze
neg_inc *= squeeze
pos_inc.replace(0, nan, inplace=True)
pos_dec.replace(0, nan, inplace=True)
neg_dec.replace(0, nan, inplace=True)
neg_inc.replace(0, nan, inplace=True)
sqz_inc = squeeze * increasing(squeeze)
sqz_dec = squeeze * decreasing(squeeze)
sqz_inc.replace(0, nan, inplace=True)
sqz_dec.replace(0, nan, inplace=True)
# Fill
if "fillna" in kwargs:
sqz_inc.fillna(kwargs["fillna"], inplace=True)
sqz_dec.fillna(kwargs["fillna"], inplace=True)
pos_inc.fillna(kwargs["fillna"], inplace=True)
pos_dec.fillna(kwargs["fillna"], inplace=True)
neg_dec.fillna(kwargs["fillna"], inplace=True)
neg_inc.fillna(kwargs["fillna"], inplace=True)
df[f"SQZPRO_INC"] = sqz_inc
df[f"SQZPRO_DEC"] = sqz_dec
df[f"SQZPRO_PINC"] = pos_inc
df[f"SQZPRO_PDEC"] = pos_dec
df[f"SQZPRO_NDEC"] = neg_dec
df[f"SQZPRO_NINC"] = neg_inc
return df
@@ -1,175 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.overlap import ema
from pandas_ta.utils import (
non_zero_range,
v_offset,
v_pos_default,
v_series
)
def schaff_tc(close: Series, seed: Series, tc_length: int, factor: IntFloat):
lowest_xmacd = seed.rolling(tc_length).min()
xmacd_range = non_zero_range(seed.rolling(tc_length).max(), lowest_xmacd)
m = len(seed)
# Initialize lists
stoch1, pf = [0] * m, [0] * m
stoch2, pff = [0] * m, [0] * m
for i in range(1, m):
# %Fast K of MACD
if lowest_xmacd.iloc[i] > 0:
stoch1[i] = 100 * ((seed.iloc[i] - lowest_xmacd.iloc[i]) / xmacd_range.iloc[i])
else:
stoch1[i] = stoch1[i - 1]
# Smoothed Calculation for % Fast D of MACD
pf[i] = round(pf[i - 1] + (factor * (stoch1[i] - pf[i - 1])), 8)
# find min and max so far
if i < tc_length:
# If there are not enough elements for a full tclength window,
# use what is available
lowest_pf = min(pf[:i+1])
highest_pf = max(pf[:i+1])
else:
lowest_pf = min(pf[i - tc_length + 1:i + 1])
highest_pf = max(pf[i - tc_length + 1:i + 1])
# Ensure non-zero range
pf_range = highest_pf - lowest_pf if highest_pf - lowest_pf > 0 else 1
# % of Fast K of PF
if pf_range > 0:
stoch2[i] = 100 * ((pf[i] - lowest_pf) / pf_range)
else:
stoch2[i] = stoch2[i - 1]
pff[i] = round(pff[i - 1] + (factor * (stoch2[i] - pff[i - 1])), 8)
pf_series = Series(pf, index=close.index)
pff_series = Series(pff, index=close.index)
return pff_series, pf_series
def stc(
close: Series, tc_length: Int = None,
fast: Int = None, slow: Int = None, factor: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Schaff Trend Cycle
This indicator is an evolved MACD with additional smoothing.
Sources:
* [rengel8](https://github.com/rengel8)
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/schaff-trend-cycle2/)
Parameters:
close (pd.Series): ```close``` Series
tc_length (int): TC period. (Adjust to the half of cycle)
Default: ```10```
fast (int): Fast MA period. Default: ```12```
slow (int): Slow MA period. Default: ```26```
factor (float): Smoothing factor for last stoch. calculation.
Default: ```0.5```
offset (int): How many bars to shift the results. Default: ```0``
Other Parameters:
ma1 (Series): User chosen MA. Default: ```False```
ma2 (Series): User chosen MA. Default: ```False```
osc (Series): User chosen oscillator. Default: ```False```
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 3 columns
Note:
Can also seed STC with two MAs, ```ma1``` and ```ma2```, or an oscillator ```osc```.
* ```ma1``` and ```ma2``` are **both** required if this option is used.
"""
# Validate
fast = v_pos_default(fast, 12)
slow = v_pos_default(slow, 26)
tc_length = v_pos_default(tc_length, 10)
if slow < fast:
fast, slow = slow, fast
_length = max(tc_length, fast, slow)
close = v_series(close, _length)
if close is None:
return
factor = v_pos_default(factor, 0.5)
offset = v_offset(offset)
# Calculate
# kwargs allows for three more series (ma1, ma2 and osc) which can be passed
# here ma1 and ma2 input negate internal ema calculations, osc substitutes
# both ma's.
ma1 = kwargs.pop("ma1", False)
ma2 = kwargs.pop("ma2", False)
osc = kwargs.pop("osc", False)
if isinstance(ma1, Series) and isinstance(ma2, Series) and not osc:
ma1 = v_series(ma1, _length)
ma2 = v_series(ma2, _length)
if ma1 is None or ma2 is None:
return
seed = ma1 - ma2
elif isinstance(osc, Series):
osc = v_series(osc, _length)
if osc is None:
return
seed = osc
else:
fastma = ema(close, length=fast)
slowma = ema(close, length=slow)
seed = fastma - slowma
pff, pf = schaff_tc(close, seed, tc_length, factor)
pf[:_length - 1] = nan
stc = Series(pff, index=close.index)
macd = Series(seed, index=close.index)
stoch = Series(pf, index=close.index)
stc.iloc[:_length - 1] = nan
# Offset
if offset != 0:
stc = stc.shift(offset)
macd = macd.shift(offset)
stoch = stoch.shift(offset)
# Fill
if "fillna" in kwargs:
stc.fillna(kwargs["fillna"], inplace=True)
macd.fillna(kwargs["fillna"], inplace=True)
stoch.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{tc_length}_{fast}_{slow}_{factor}"
stc.name = f"STC{_props}"
macd.name = f"STCmacd{_props}"
stoch.name = f"STCstoch{_props}"
stc.category = macd.category = stoch.category = "momentum"
data = {
stc.name: stc,
macd.name: macd,
stoch.name: stoch
}
df = DataFrame(data, index=close.index)
df.name = f"STC{_props}"
df.category = stc.category
return df
@@ -1,121 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.maps import Imports
from pandas_ta.utils import (
non_zero_range,
tal_ma,
v_mamode,
v_offset,
v_pos_default,
v_series,
v_talib
)
def stoch(
high: Series, low: Series, close: Series,
k: Int = None, d: Int = None, smooth_k: Int = None,
mamode: str = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Stochastic
This indicator, by George Lane in the 1950's, attempts to identify and
quantify momentum; it assumes that momentum precedes value change.
Sources:
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=332&Name=KD_-_Slow)
* [tradingview](https://www.tradingview.com/wiki/Stochastic_(STOCH))
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
k (int): The Fast %K period. Default: ```14```
d (int): The Slow %D period. Default: ```3```
smooth_k (int): The Slow %K period. Default: ```3```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 3 columns
"""
# Validate
k = v_pos_default(k, 14)
d = v_pos_default(d, 3)
smooth_k = v_pos_default(smooth_k, 3)
_length = k + d + smooth_k
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
mode_tal = v_talib(talib)
mamode = v_mamode(mamode, "sma")
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal and smooth_k > 2:
from talib import STOCH
stoch_ = STOCH(
high, low, close, k, d, tal_ma(mamode), d, tal_ma(mamode)
)
stoch_k, stoch_d = stoch_[0], stoch_[1]
else:
ll = low.rolling(k).min()
hh = high.rolling(k).max()
stoch = 100 * (close - ll) / non_zero_range(hh, ll)
if stoch is None: return
stoch_fvi = stoch.loc[stoch.first_valid_index():, ]
if smooth_k == 1:
stoch_k = stoch
else:
stoch_k = ma(mamode, stoch_fvi, length=smooth_k)
stochk_fvi = stoch_k.loc[stoch_k.first_valid_index():, ]
stoch_d = ma(mamode, stochk_fvi, length=d)
stoch_h = stoch_k - stoch_d # Histogram
# Offset
if offset != 0:
stoch_k = stoch_k.shift(offset)
stoch_d = stoch_d.shift(offset)
stoch_h = stoch_h.shift(offset)
# Fill
if "fillna" in kwargs:
stoch_k.fillna(kwargs["fillna"], inplace=True)
stoch_d.fillna(kwargs["fillna"], inplace=True)
stoch_h.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_name = "STOCH"
_props = f"_{k}_{d}_{smooth_k}"
stoch_k.name = f"{_name}k{_props}"
stoch_d.name = f"{_name}d{_props}"
stoch_h.name = f"{_name}h{_props}"
stoch_k.category = stoch_d.category = stoch_h.category = "momentum"
data = {
stoch_k.name: stoch_k,
stoch_d.name: stoch_d,
stoch_h.name: stoch_h
}
df = DataFrame(data, index=close.index)
df.name = f"{_name}{_props}"
df.category = stoch_k.category
return df
@@ -1,100 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.maps import Imports
from pandas_ta.utils import (
non_zero_range,
tal_ma,
v_mamode,
v_offset,
v_pos_default,
v_series,
v_talib
)
def stochf(
high: Series, low: Series, close: Series,
k: Int = None, d: Int = None,
mamode: str = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Fast Stochastic
This indicator, by George Lane in the 1950's, attempts to identify and
quantify momentum like STOCH, but is more volatile.
Sources:
* [corporatefinanceinstitute](https://corporatefinanceinstitute.com/resources/knowledge/trading-investing/fast-stochastic-indicator/)
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=333&Name=KD_-_Fast)
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
k (int): The Fast %K period. Default: ```14```
d (int): The Slow %D period. Default: ```3```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 2 columns
"""
# Validate
k = v_pos_default(k, 14)
d = v_pos_default(d, 3)
_length = k + d - 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
mamode = v_mamode(mamode, "sma")
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import STOCHF
stochf_ = STOCHF(high, low, close, k, d, tal_ma(mamode))
stochf_k, stochf_d = stochf_[0], stochf_[1]
else:
lowest_low = low.rolling(k).min()
highest_high = high.rolling(k).max()
stochf_k = 100 * (close - lowest_low) \
/ non_zero_range(highest_high, lowest_low)
stochfk_fvi = stochf_k.loc[stochf_k.first_valid_index():, ]
stochf_d = ma(mamode, stochfk_fvi, length=d, talib=mode_tal)
# Offset
if offset != 0:
stochf_k = stochf_k.shift(offset)
stochf_d = stochf_d.shift(offset)
# Fill
if "fillna" in kwargs:
stochf_k.fillna(kwargs["fillna"], inplace=True)
stochf_d.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_name = "STOCHF"
_props = f"_{k}_{d}"
stochf_k.name = f"{_name}k{_props}"
stochf_d.name = f"{_name}d{_props}"
stochf_k.category = stochf_d.category = "momentum"
data = {stochf_k.name: stochf_k, stochf_d.name: stochf_d}
df = DataFrame(data, index=close.index)
df.name = f"{_name}{_props}"
df.category = stochf_k.category
return df
@@ -1,104 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.maps import Imports
from pandas_ta.momentum import rsi
from pandas_ta.utils import (
non_zero_range,
v_mamode,
v_offset,
v_pos_default,
v_series,
v_talib
)
def stochrsi(
close: Series, length: Int = None, rsi_length: Int = None,
k: Int = None, d: Int = None, mamode: str = None,
talib: bool = None, offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Stochastic RSI
This indicator attempts to quantify RSI relative to its High-Low range.
Sources:
* "Stochastic RSI and Dynamic Momentum Index", Tushar Chande and
Stanley Kroll, Stock & Commodities V.11:5 (189-199)
* [tradingview](https://www.tradingview.com/wiki/Stochastic_(STOCH))
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```14```
rsi_length (int): RSI period. Default: ```14```
k (int): The Fast %K period. Default: ```3```
d (int): The Slow %K period. Default: ```3```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 2 columns
Note:
May be more sensitive to RSI and thus identify potential "overbought"
or "oversold" signals.
"""
# Validate
length = v_pos_default(length, 14)
rsi_length = v_pos_default(rsi_length, 14)
k = v_pos_default(k, 3)
d = v_pos_default(d, 3)
_length = length + rsi_length + 2
close = v_series(close, _length)
if close is None:
return
mamode = v_mamode(mamode, "sma")
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
# if Imports["talib"] and mode_tal:
# from talib import RSI
# rsi_ = RSI(close, length)
# else:
rsi_ = rsi(close, length=rsi_length)
lowest_rsi = rsi_.rolling(length).min()
highest_rsi = rsi_.rolling(length).max()
stoch = 100 * (rsi_ - lowest_rsi) / non_zero_range(highest_rsi, lowest_rsi)
stochrsi_k = ma(mamode, stoch, length=k)
stochrsi_d = ma(mamode, stochrsi_k, length=d)
# Offset
if offset != 0:
stochrsi_k = stochrsi_k.shift(offset)
stochrsi_d = stochrsi_d.shift(offset)
# Fill
if "fillna" in kwargs:
stochrsi_k.fillna(kwargs["fillna"], inplace=True)
stochrsi_d.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_name = "STOCHRSI"
_props = f"_{length}_{rsi_length}_{k}_{d}"
stochrsi_k.name = f"{_name}k{_props}"
stochrsi_d.name = f"{_name}d{_props}"
stochrsi_k.category = stochrsi_d.category = "momentum"
data = {stochrsi_k.name: stochrsi_k, stochrsi_d.name: stochrsi_d}
df = DataFrame(data, index=close.index)
df.name = f"{_name}{_props}"
df.category = stochrsi_k.category
return df
@@ -1,130 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan, zeros
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.utils import (
sum_signed_rolling_deltas,
v_bool,
v_mamode,
v_offset,
v_pos_default,
v_series
)
def tmo(
open_: Series, close: Series,
tmo_length: Int = None, calc_length: Int = None, smooth_length: Int = None,
momentum: bool = None, normalize: bool = None, exclusive: bool = None,
mamode: str = None, offset: Int = None, **kwargs: DictLike,
) -> DataFrame:
"""True Momentum Oscillator
This indicator attempts to quantify momentum.
Sources:
* [tradingview A](https://www.tradingview.com/script/VRwDppqd-True-Momentum-Oscillator/)
* [tradingview B](https://www.tradingview.com/script/65vpO7T5-True-Momentum-Oscillator-Universal-Edition/)
Parameters:
open_ (pd.Series): ```open``` Series
close (pd.Series): ```close``` Series
tmo_length (int): TMO period. Default: ```14```
calc_length (int): Initial MA period. Default: ```5```
smooth_length (int): Main and smooth signal MA period. Default: ```3```
mamode (str): See ```help(ta.ma)```. Default: ```"ema"```
momentum (bool): Compute main and smooth momentum. Default: ```False```
normalize (bool): Normalize. Default: ```False```
exclusive (bool): Exclusive period over ```n``` bars, or inclusively
over ```n-1``` bars. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): DataFrame.fillna(value)
Returns:
(pd.DataFrame): 4 columns
"""
# Validate
tmo_length = v_pos_default(tmo_length, 14)
calc_length = v_pos_default(calc_length, 5)
smooth_length = v_pos_default(smooth_length, 3)
_length = max(tmo_length, calc_length, smooth_length)
open_ = v_series(open_, _length)
close = v_series(close, _length)
offset = v_offset(offset)
if "length" in kwargs:
kwargs.pop("length")
if open_ is None or close is None:
return None
mamode = v_mamode(mamode, "ema")
compute_momentum = v_bool(momentum, False)
normalize_signal = v_bool(normalize, False)
exclusive = v_bool(exclusive, True)
signed_diff_sum = sum_signed_rolling_deltas(
open_, close, tmo_length, exclusive=exclusive
)
if all(isnan(signed_diff_sum)):
return None # Emergency Break
initial_ma = ma(mamode, signed_diff_sum, length=calc_length)
if all(isnan(initial_ma)):
return None # Emergency Break
main = ma(mamode, initial_ma, length=smooth_length)
if all(isnan(main)):
return None # Emergency Break
smooth = ma(mamode, main, length=smooth_length)
if all(isnan(smooth)):
return None # Emergency Break
if compute_momentum:
mom_main = main - main.shift(tmo_length)
mom_smooth = smooth - smooth.shift(tmo_length)
else:
zero_array = zeros(main.size)
mom_main = Series(zero_array, index=main.index)
mom_smooth = Series(zero_array, index=smooth.index)
# Offset
if offset != 0:
main = main.shift(offset)
smooth = smooth.shift(offset)
mom_main = mom_main.shift(offset)
mom_smooth = mom_smooth.shift(offset)
# Fill
if "fillna" in kwargs:
main.fillna(kwargs["fillna"], inplace=True)
smooth.fillna(kwargs["fillna"], inplace=True)
mom_main.fillna(kwargs["fillna"], inplace=True)
mom_smooth.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{tmo_length}_{calc_length}_{smooth_length}"
main.name = f"TMO{_props}"
smooth.name = f"TMOs{_props}"
mom_main.name = f"TMOM{_props}"
mom_smooth.name = f"TMOMs{_props}"
main.category = smooth.category = "momentum"
mom_main.category = mom_smooth.category = main.category
data = {
main.name: main,
smooth.name: smooth,
mom_main.name: mom_main,
mom_smooth.name: mom_smooth,
}
df = DataFrame(data, index=close.index)
df.name = f"TMO{_props}"
df.category = main.category
return df
@@ -1,93 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.overlap.ema import ema
from pandas_ta.utils import (
v_drift,
v_offset,
v_pos_default,
v_scalar,
v_series
)
def trix(
close: Series, length: Int = None, signal: Int = None,
scalar: IntFloat = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Trix
This indicator attempts to identify divergences as an oscillator.
Sources:
* [tradingview](https://www.tradingview.com/wiki/TRIX)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```18```
signal (int): Signal period. Default: ```9```
scalar (float): Scalar. Default: ```100```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 30)
signal = v_pos_default(signal, 9)
if length < signal:
length, signal = signal, length
_length = 3 * length - 1
close = v_series(close, _length)
if close is None:
return
scalar = v_scalar(scalar, 100)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
ema1 = ema(close=close, length=length, **kwargs)
if all(isnan(ema1)):
return # Emergency Break
ema2 = ema(close=ema1, length=length, **kwargs)
if all(isnan(ema2)):
return # Emergency Break
ema3 = ema(close=ema2, length=length, **kwargs)
if all(isnan(ema3)):
return # Emergency Break
trix = scalar * ema3.pct_change(drift)
trix_signal = trix.rolling(signal).mean()
# Offset
if offset != 0:
trix = trix.shift(offset)
trix_signal = trix_signal.shift(offset)
# Fill
if "fillna" in kwargs:
trix.fillna(kwargs["fillna"], inplace=True)
trix_signal.fillna(kwargs["fillna"], inplace=True)
# Name and Category
trix.name = f"TRIX_{length}_{signal}"
trix_signal.name = f"TRIXs_{length}_{signal}"
trix.category = trix_signal.category = "momentum"
data = {trix.name: trix, trix_signal.name: trix_signal}
df = DataFrame(data, index=close.index)
df.name = f"TRIX_{length}_{signal}"
df.category = "momentum"
return df
@@ -1,106 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.ma import ma
from pandas_ta.overlap import ema
from pandas_ta.utils import (
v_drift,
v_mamode,
v_offset,
v_pos_default,
v_scalar,
v_series
)
def tsi(
close: Series, fast: Int = None, slow: Int = None,
signal: Int = None, scalar: IntFloat = None,
mamode: str = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""True Strength Index
This indicator attempts to identify short-term swings in trend direction
as well as identifying possible "overbought" and "oversold" signals.
Sources:
* [investopedia](https://www.investopedia.com/terms/t/tsi.asp)
Parameters:
close (pd.Series): ```close``` Series
fast (int): Fast MA period. Default: ```13```
slow (int): Slow MA period. Default: ```25```
signal (int): Signal period. Default: ```13```
scalar (float): Scalar. Default: ```100```
mamode (str): Signal MA. See ```help(ta.ma)```. Default: ```"ema"```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 2 columns
"""
# Validate
fast = v_pos_default(fast, 13)
slow = v_pos_default(slow, 25)
signal = v_pos_default(signal, 13)
if slow < fast:
fast, slow = slow, fast
_length = slow + signal + 1
close = v_series(close, _length)
if "length" in kwargs:
kwargs.pop("length")
if close is None:
return
scalar = v_scalar(scalar, 100)
mamode = v_mamode(mamode, "ema")
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
diff = close.diff(drift)
slow_ema = ema(close=diff, length=slow, **kwargs)
if all(isnan(slow_ema)):
return # Emergency Break
fast_slow_ema = ema(close=slow_ema, length=fast, **kwargs)
abs_diff = diff.abs()
abs_slow_ema = ema(close=abs_diff, length=slow, **kwargs)
if all(isnan(abs_slow_ema)):
return # Emergency Break
abs_fast_slow_ema = ema(close=abs_slow_ema, length=fast, **kwargs)
tsi = scalar * fast_slow_ema / abs_fast_slow_ema
if all(isnan(tsi)):
return # Emergency Break
tsi_signal = ma(mamode, tsi, length=signal)
# Offset
if offset != 0:
tsi = tsi.shift(offset)
tsi_signal = tsi_signal.shift(offset)
# Fill
if "fillna" in kwargs:
tsi.fillna(kwargs["fillna"], inplace=True)
tsi_signal.fillna(kwargs["fillna"], inplace=True)
# Name and Category
tsi.name = f"TSI_{fast}_{slow}_{signal}"
tsi_signal.name = f"TSIs_{fast}_{slow}_{signal}"
tsi.category = tsi_signal.category = "momentum"
data = {tsi.name: tsi, tsi_signal.name: tsi_signal}
df = DataFrame(data, index=close.index)
df.name = f"TSI_{fast}_{slow}_{signal}"
df.category = "momentum"
return df
-105
View File
@@ -1,105 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.utils import (
v_drift,
v_offset,
v_pos_default,
v_series,
v_talib
)
def uo(
high: Series, low: Series, close: Series,
fast: Int = None, medium: Int = None, slow: Int = None,
fast_w: IntFloat = None, medium_w: IntFloat = None, slow_w: IntFloat = None,
talib: bool = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Ultimate Oscillator
This indicator, by Larry Williams, attempts to identify momentum.
Sources:
* [tradingview](https://www.tradingview.com/wiki/Ultimate_Oscillator_(UO))
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
fast (int): The Fast %K period. Default: ```7```
medium (int): The Slow %K period. Default: ```14```
slow (int): The Slow %D period. Default: ```28```
fast_w (float): The Fast %K period. Default: ```4.0```
medium_w (float): The Slow %K period. Default: ```2.0```
slow_w (float): The Slow %D period. Default: ```1.0```
talib (bool): If installed, use TA Lib. Default: ```True```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
fast = v_pos_default(fast, 7)
medium = v_pos_default(medium, 14)
slow = v_pos_default(slow, 28)
_length = max(fast, medium, slow) + 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
fast_w = v_pos_default(fast_w, 4.0)
medium_w = v_pos_default(medium_w, 2.0)
slow_w = v_pos_default(slow_w, 1.0)
mode_tal = v_talib(talib)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import ULTOSC
uo = ULTOSC(high, low, close, fast, medium, slow)
else:
close_drift = close.shift(drift)
tdf = DataFrame({
"high": high, "low": low, f"close_{drift}": close_drift
})
max_h_or_pc = tdf.loc[:, ["high", f"close_{drift}"]].max(axis=1)
min_l_or_pc = tdf.loc[:, ["low", f"close_{drift}"]].min(axis=1)
del tdf
bp = close - min_l_or_pc
tr = max_h_or_pc - min_l_or_pc
fast_avg = bp.rolling(fast).sum() / tr.rolling(fast).sum()
medium_avg = bp.rolling(medium).sum() / tr.rolling(medium).sum()
slow_avg = bp.rolling(slow).sum() / tr.rolling(slow).sum()
total_weight = fast_w + medium_w + slow_w
weights = (fast_w * fast_avg) + (medium_w * medium_avg) \
+ (slow_w * slow_avg)
uo = 100 * weights / total_weight
# Offset
if offset != 0:
uo = uo.shift(offset)
# Fill
if "fillna" in kwargs:
uo.fillna(kwargs["fillna"], inplace=True)
# Name and Category
uo.name = f"UO_{fast}_{medium}_{slow}"
uo.category = "momentum"
return uo
@@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
def willr(
high: Series, low: Series, close: Series,
length: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""William's Percent R
This indicator attempts to identify "overbought" and "oversold"
conditions similar to the RSI.
Sources:
* [tradingview](https://www.tradingview.com/wiki/Williams_%25R_(%25R))
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
length (int): The period. Default: ```14```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 14)
if "min_periods" in kwargs and kwargs["min_periods"] is not None:
min_periods = int(kwargs["min_periods"])
else:
min_periods = length
_length = max(length, min_periods)
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import WILLR
willr = WILLR(high, low, close, length)
else:
lowest_low = low.rolling(length, min_periods=min_periods).min()
highest_high = high.rolling(length, min_periods=min_periods).max()
willr = 100 * ((close - lowest_low) / (highest_high - lowest_low) - 1)
# Offset
if offset != 0:
willr = willr.shift(offset)
# Fill
if "fillna" in kwargs:
willr.fillna(kwargs["fillna"], inplace=True)
# Name and Category
willr.name = f"WILLR_{length}"
willr.category = "momentum"
return willr
@@ -1,77 +0,0 @@
# -*- coding: utf-8 -*-
from .alligator import alligator
from .alma import alma
from .dema import dema
from .ema import ema
from .fwma import fwma
from .hilo import hilo
from .hl2 import hl2
from .hlc3 import hlc3
from .hma import hma
from .hwma import hwma
from .ichimoku import ichimoku
from .jma import jma
from .kama import kama
from .linreg import linreg
from .mama import mama
from .mcgd import mcgd
from .midpoint import midpoint
from .midprice import midprice
from .ohlc4 import ohlc4
from .pivots import pivots
from .pwma import pwma
from .rma import rma
from .sinwma import sinwma
from .sma import sma
from .smma import smma
from .ssf import ssf
from .ssf3 import ssf3
from .supertrend import supertrend
from .swma import swma
from .t3 import t3
from .tema import tema
from .trima import trima
from .vidya import vidya
from .wcp import wcp
from .wma import wma
from .zlma import zlma
__all__ = [
"alligator",
"alma",
"dema",
"ema",
"fwma",
"hilo",
"hl2",
"hlc3",
"hma",
"hwma",
"ichimoku",
"jma",
"kama",
"linreg",
"mama",
"mcgd",
"midpoint",
"midprice",
"ohlc4",
"pivots",
"pwma",
"rma",
"sinwma",
"sma",
"smma",
"ssf",
"ssf3",
"supertrend",
"swma",
"t3",
"tema",
"trima",
"vidya",
"wcp",
"wma",
"zlma",
]
@@ -1,87 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
from .smma import smma
# from posix import pread
def alligator(
close: Series, jaw: Int = None, teeth: Int = None, lips: Int = None,
talib: bool = None, offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Bill Williams Alligator
This indicator, by Bill Williams, attempts to identify trends.
Sources:
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=175&Name=Bill_Williams_Alligator)
* [tradingview](https://www.tradingview.com/scripts/alligator/)
Parameters:
close (pd.Series): ```close``` Series
jaw (int): Jaw period. Default: ```13```
teeth (int): Teeth period. Default: ```8```
lips (int): Lips period. Default: ```5```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 3 columns
Tip:
To avoid data leaks, offsets are to be done manually.
Note:
Williams believed the fx market trends between 15% and 30% of the
time. Otherwise it is range bound. Inspired by fractal geometry,
where the outputs are meant to resemble an alligator opening and
closing its mouth. It It consists of 3 lines: Jaw, Teeth, and
Lips which each have differing lengths.
"""
# Validate
jaw = v_pos_default(jaw, 13)
teeth = v_pos_default(teeth, 8)
lips = v_pos_default(lips, 5)
close = v_series(close, max(jaw, teeth, lips))
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
gator_jaw = smma(close, length=jaw, talib=mode_tal)
gator_teeth = smma(close, length=teeth, talib=mode_tal)
gator_lips = smma(close, length=lips, talib=mode_tal)
# Offset
if offset != 0:
gator_jaw = gator_jaw.shift(offset)
gator_teeth = gator_teeth.shift(offset)
gator_lips = gator_lips.shift(offset)
# Fill
if "fillna" in kwargs:
gator_jaw.fillna(kwargs["fillna"], inplace=True)
gator_teeth.fillna(kwargs["fillna"], inplace=True)
gator_lips.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{jaw}_{teeth}_{lips}"
data = {
f"AGj{_props}": gator_jaw,
f"AGt{_props}": gator_teeth,
f"AGl{_props}": gator_lips
}
df = DataFrame(data, index=close.index)
df.name = f"AG{_props}"
df.category = "overlap"
return df
@@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import append, arange, array, exp, floor, nan, tensordot
from numpy.version import version as np_version
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas import Series
from pandas_ta.utils import strided_window, v_offset, v_pos_default, v_series
def alma(
close: Series, length: Int = None,
sigma: IntFloat = None, dist_offset: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Arnaud Legoux Moving Average
This indicator attempts to reduce lag with Gaussian smoothing.
Sources:
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/alma-arnaud-legoux-moving-average/)
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=475&Name=Moving_Average_-_Arnaud_Legoux)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```9```
sigma (float): Smoothing value. Default ```6.0```
dist_offset (float): Distribution offset, range ```[0, 1]```.
Default ```0.85```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 9)
close = v_series(close, length)
if close is None:
return
sigma = v_pos_default(sigma, 6.0)
if isinstance(dist_offset, float) and 0 <= dist_offset <= 1:
offset_ = float(dist_offset)
else:
offset_ = 0.85
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
x = arange(length)
k = floor(offset_ * (length - 1))
weights = exp(-0.5 * ((sigma / length) * (x - k)) ** 2)
weights /= weights.sum()
if np_version >= "1.20.0":
from numpy.lib.stride_tricks import sliding_window_view
window = sliding_window_view(np_close, length)
else:
window = strided_window(np_close, length)
result = append(array([nan] * (length - 1)),
tensordot(window, weights, axes=1))
alma = Series(result, index=close.index)
# Offset
if offset != 0:
alma = alma.shift(offset)
# Fill
if "fillna" in kwargs:
alma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
alma.name = f"ALMA_{length}_{sigma}_{offset_}"
alma.category = "overlap"
return alma
@@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
from .ema import ema
def dema(
close: Series, length: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Double Exponential Moving Average
This indicator attempts to create a smoother average with less lag than
the EMA.
Sources:
* [tradingtechnologies](https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/double-exponential-moving-average-dema/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Warning:
TA-Lib Correlation: ```np.float64(0.9999894518202522)```
Tip:
Corrective contributions welcome!
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import DEMA
dema = DEMA(close, length)
else:
ema1 = ema(close=close, length=length, talib=mode_tal)
ema2 = ema(close=ema1, length=length, talib=mode_tal)
dema = 2 * ema1 - ema2
if all(isnan(dema.to_numpy())):
return # Emergency Break
# Offset
if offset != 0:
dema = dema.shift(offset)
# Fill
if "fillna" in kwargs:
dema.fillna(kwargs["fillna"], inplace=True)
# Name and Category
dema.name = f"DEMA_{length}"
dema.category = "overlap"
return dema
@@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from numba import njit
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import (
v_bool,
v_offset,
v_pos_default,
v_series,
v_talib
)
def ema(
close: Series, length: Int = None,
talib: bool = None, presma: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Exponential Moving Average
This Moving Average is more responsive than the Simple Moving
Average (SMA).
Sources:
* [investopedia](https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp)
* [stockcharts](https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_averages)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
talib (bool): If installed, use TA Lib. Default: ```True```
presma (bool): Initialize with SMA like TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
adjust (bool): Default: ```False```
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
mode_tal = v_talib(talib)
presma = v_bool(presma, True)
offset = v_offset(offset)
adjust = kwargs.setdefault("adjust", False)
# Calculate
if Imports["talib"] and mode_tal and length > 1:
from talib import EMA
ema = EMA(close, length)
else:
if presma: # TA Lib implementation
close = close.copy()
sma_nth = close.iloc[0:length].mean()
close.iloc[:length - 1] = nan
close.iloc[length - 1] = sma_nth
ema = close.ewm(span=length, adjust=adjust).mean()
# Offset
if offset != 0:
ema = ema.shift(offset)
# Fill
if "fillna" in kwargs:
ema.fillna(kwargs["fillna"], inplace=True)
# Name and Category
ema.name = f"EMA_{length}"
ema.category = "overlap"
return ema
@@ -1,66 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
fibonacci,
v_ascending,
v_offset,
v_pos_default,
v_series,
weights
)
def fwma(
close: Series, length: Int = None, asc: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Fibonacci's Weighted Moving Average
This indicator, by Kevin Johnson, is similar to a Weighted Moving Average
(WMA) where the weights are based on the Fibonacci Sequence.
Sources:
* Kevin Johnson
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
asc (bool): Recent values weigh more. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
asc = v_ascending(asc)
offset = v_offset(offset)
# Calculate
fibs = fibonacci(n=length, weighted=True)
fwma = close.rolling(length, min_periods=length) \
.apply(weights(fibs), raw=True)
# Offset
if offset != 0:
fwma = fwma.shift(offset)
# Fill
if "fillna" in kwargs:
fwma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
fwma.name = f"FWMA_{length}"
fwma.category = "overlap"
return fwma
@@ -1,100 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.utils import v_mamode, v_offset, v_pos_default, v_series
def hilo(
high: Series, low: Series, close: Series,
high_length: Int = None, low_length: Int = None, mamode: str = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Gann HiLo Activator
This indicator, by Robert Krausz, uses two different Moving Averages to
identify trends.
Sources:
* Gann HiLo Activator, , Stocks & Commodities Magazine, 1998
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=447&Name=Gann_HiLo_Activator)
* [tradingview](https://www.tradingview.com/script/XNQSLIYb-Gann-High-Low/)
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
high_length (int): High period. Default: ```13```
low_length (int): Low period. Default: ```21```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 3 columns
Note:
Increasing ```high_length``` and decreasing ```low_length``` is
better for short trades and vice versa for long trades.
"""
# Validate
high_length = v_pos_default(high_length, 13)
low_length = v_pos_default(low_length, 21)
_length = max(high_length, low_length) + 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
mamode = v_mamode(mamode, "sma")
offset = v_offset(offset)
# Calculate
m = close.size
hilo = Series(nan, index=close.index)
long = Series(nan, index=close.index)
short = Series(nan, index=close.index)
high_ma = ma(mamode, high, length=high_length)
low_ma = ma(mamode, low, length=low_length)
for i in range(1, m):
if close.iat[i] > high_ma.iat[i - 1]:
hilo.iat[i] = long.iat[i] = low_ma.iat[i]
elif close.iat[i] < low_ma.iat[i - 1]:
hilo.iat[i] = short.iat[i] = high_ma.iat[i]
else:
hilo.iat[i] = hilo.iat[i - 1]
long.iat[i] = short.iat[i] = hilo.iat[i - 1]
# Offset
if offset != 0:
hilo = hilo.shift(offset)
long = long.shift(offset)
short = short.shift(offset)
# Fill
if "fillna" in kwargs:
hilo.fillna(kwargs["fillna"], inplace=True)
long.fillna(kwargs["fillna"], inplace=True)
short.fillna(kwargs["fillna"], inplace=True)
# Name and Category
_props = f"_{high_length}_{low_length}"
data = {
f"HILO{_props}": hilo,
f"HILOl{_props}": long,
f"HILOs{_props}": short
}
df = DataFrame(data, index=close.index)
df.name = f"HILO{_props}"
df.category = "overlap"
return df
@@ -1,52 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_offset, v_series
def hl2(
high: Series, low: Series,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""HL2
HL2 is the midpoint/average of high and low.
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```.
Only works when offset.
Returns:
(pd.Series): 1 column
"""
# Validate
high = v_series(high)
low = v_series(low)
offset = v_offset(offset)
if high is None or low is None:
return
# Calculate
avg = 0.5 * (high.to_numpy() + low.to_numpy())
hl2 = Series(avg, index=high.index)
# Offset
if offset != 0:
hl2 = hl2.shift(offset)
# Fill
if "fillna" in kwargs:
hl2.fillna(kwargs["fillna"], inplace=True)
# Name and Category
hl2.name = "HL2"
hl2.category = "overlap"
return hl2
@@ -1,60 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_series, v_talib
def hlc3(
high: Series, low: Series, close: Series, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""HLC3
HLC3 is the average of high, low and close.
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```.
Only works when offset.
Returns:
(pd.Series): 1 column
"""
# Validate
high = v_series(high)
low = v_series(low)
close = v_series(close)
mode_tal = v_talib(talib)
offset = v_offset(offset)
if high is None or low is None or close is None:
return
# Calculate
if Imports["talib"] and mode_tal and close.size:
from talib import TYPPRICE
hlc3 = TYPPRICE(high, low, close)
else:
avg = (high.to_numpy() + low.to_numpy() + close.to_numpy()) / 3.0
hlc3 = Series(avg, index=close.index)
# Offset
if offset != 0:
hlc3 = hlc3.shift(offset)
# Fill
if "fillna" in kwargs:
hlc3.fillna(kwargs["fillna"], inplace=True)
# Name and Category
hlc3.name = "HLC3"
hlc3.category = "overlap"
return hlc3
@@ -1,71 +0,0 @@
from sys import modules as module_
from numpy import sqrt
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_mamode, v_offset, v_pos_default, v_series
from .ema import ema
from .sma import sma
from .wma import wma
def hma(
close: Series, length: Int = None, mamode: str = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Hull Moving Average
This indicator, by Alan Hull, attempts to reduce lag compared to
classical moving averages.
Sources:
* [Alan Hull](https://alanhull.com/hull-moving-average)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
mamode (str): One of: 'ema', 'sma', or 'wma'. Default: ```"wma"```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length + 2)
if close is None:
return
mamode = v_mamode(mamode, "wma")
offset = v_offset(offset)
if mamode not in ["ema", "sma", "wma"]:
return
_ma = getattr(module_[__name__], mamode)
# Calculate
half_length = int(length / 2)
sqrt_length = int(sqrt(length))
maf = _ma(close, length=half_length)
mas = _ma(close, length=length)
hma = _ma(close=2 * maf - mas, length=sqrt_length)
# Offset
if offset != 0:
hma = hma.shift(offset)
# Fill
if "fillna" in kwargs:
hma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
hma.name = f"HMA{'' if mamode == 'wma' else mamode[0]}_{length}"
hma.category = "overlap"
return hma
@@ -1,74 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import v_offset, v_series
from shutil import which
def hwma(
close: Series,
na: IntFloat = None, nb: IntFloat = None, nc: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Holt-Winter Moving Average
This indicator uses a three parameter Holt-Winter Moving Average for
smoothing.
Sources:
* [rengel8](https://github.com/rengel8) based on a publication for
MetaTrader 5.
* [mql5](https://www.mql5.com/en/code/20856)
Parameters:
close (pd.Series): ```close``` Series
na (float): Smoothed series parameter (from 0 to 1). Default: 0.2
nb (float): Trend parameter (from 0 to 1). Default: 0.1
nc (float): Seasonality parameter (from 0 to 1). Default: 0.1
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
pd.Series: hwma
"""
# Validate
close = v_series(close, 1)
na = float(na) if isinstance(na, float) and 0 < na < 1 else 0.2
nb = float(nb) if isinstance(nb, float) and 0 < nb < 1 else 0.1
nc = float(nc) if isinstance(nc, float) and 0 < nc < 1 else 0.1
offset = v_offset(offset)
if close is None:
return
# Calculate
last_a = last_v = 0
last_f = close.iloc[0]
result = []
m = close.size
for i in range(m):
F = (1.0 - na) * (last_f + last_v + 0.5 * last_a) + na * close.iloc[i]
V = (1.0 - nb) * (last_v + last_a) + nb * (F - last_f)
A = (1.0 - nc) * last_a + nc * (V - last_v)
result.append((F + V + 0.5 * A))
last_a, last_f, last_v = A, F, V # update values
hwma = Series(result, index=close.index)
# Offset
if offset != 0:
hwma = hwma.shift(offset)
# Fill
if "fillna" in kwargs:
hwma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
hwma.name = f"HWMA_{na}_{nb}_{nc}"
hwma.category = "overlap"
return hwma
@@ -1,131 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import DataFrame, RangeIndex, Timedelta, Series, concat, date_range
from pandas_ta._typing import DictLike, Int, Tuple
from pandas_ta.utils import v_offset, v_pos_default, v_series
from .midprice import midprice
def ichimoku(
high: Series, low: Series, close: Series,
tenkan: Int = None, kijun: Int = None, senkou: Int = None,
include_chikou: bool = True,
offset: Int = None, **kwargs: DictLike
) -> Tuple[DataFrame, DataFrame]:
"""Ichimoku Kinkō Hyō
A forecasting model used in Japaese financial markets Pre WWII.
Sources:
* [tradingtechnologies](https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/ichimoku-ich/)
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
tenkan (int): Tenkan period. Default: ```9```
kijun (int): Kijun period. Default: ```26```
senkou (int): Senkou period. Default: ```52```
include_chikou (bool): Whether to include chikou component.
Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
lookahead (value): To avoid data leakage set to ```False```.
Returns:
(Tuple[pd.DataFrame, pd.DataFrame]):
* Historical DataFrame, 5 columns
* Forward Looking DataFrame, 2 columns
Danger: Possible Data Leak
Set ```lookahead=False``` to avoid data leakage. Issue [#60](https://github.com/twopirllc/pandas-ta/issues/60#).
"""
# Validate
tenkan = v_pos_default(tenkan, 9)
kijun = v_pos_default(kijun, 26)
senkou = v_pos_default(senkou, 52)
_length = max(tenkan, kijun, senkou)
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return None, None
offset = v_offset(offset)
if not kwargs.get("lookahead", True):
include_chikou = False
# Calculate
tenkan_sen = midprice(high=high, low=low, length=tenkan)
kijun_sen = midprice(high=high, low=low, length=kijun)
span_a = 0.5 * (tenkan_sen + kijun_sen)
span_b = midprice(high=high, low=low, length=senkou)
# Copy Span A and B values before their shift
_span_a = span_a[-kijun:].shift(-1).copy()
_span_b = span_b[-kijun:].shift(-1).copy()
span_a = span_a.shift(kijun - 1)
span_b = span_b.shift(kijun - 1)
chikou_span = close.shift(-kijun + 1)
# Offset
if offset != 0:
tenkan_sen = tenkan_sen.shift(offset)
kijun_sen = kijun_sen.shift(offset)
span_a = span_a.shift(offset)
span_b = span_b.shift(offset)
chikou_span = chikou_span.shift(offset)
# Fill
if "fillna" in kwargs:
span_a.fillna(kwargs["fillna"], inplace=True)
span_b.fillna(kwargs["fillna"], inplace=True)
chikou_span.fillna(kwargs["fillna"], inplace=True)
# Name and Category
span_a.name = f"ISA_{tenkan}"
span_b.name = f"ISB_{kijun}"
tenkan_sen.name = f"ITS_{tenkan}"
kijun_sen.name = f"IKS_{kijun}"
chikou_span.name = f"ICS_{kijun}"
chikou_span.category = kijun_sen.category = tenkan_sen.category = "overlap"
span_b.category = span_a.category = chikou_span
# Prepare Ichimoku DataFrame
data = {
span_a.name: span_a,
span_b.name: span_b,
tenkan_sen.name: tenkan_sen,
kijun_sen.name: kijun_sen,
}
if include_chikou:
data[chikou_span.name] = chikou_span
ichimokudf = DataFrame(data, index=close.index)
ichimokudf.name = f"ICHIMOKU_{tenkan}_{kijun}_{senkou}"
ichimokudf.category = "overlap"
# Prepare Span DataFrame
last = close.index[-1]
if close.index.dtype == "int64":
ext_index = RangeIndex(start=last + 1, stop=last + kijun + 1)
spandf = DataFrame(index=ext_index, columns=[span_a.name, span_b.name])
_span_a.index = _span_b.index = ext_index
else:
df_freq = close.index.value_counts().mode()[0]
tdelta = Timedelta(df_freq, unit="d")
new_dt = date_range(start=last + tdelta, periods=kijun, freq="B")
spandf = DataFrame(index=new_dt, columns=[span_a.name, span_b.name])
_span_a.index = _span_b.index = new_dt
spandf[span_a.name] = _span_a
spandf[span_b.name] = _span_b
spandf.name = f"ICHISPAN_{tenkan}_{kijun}"
spandf.category = "overlap"
return ichimokudf, spandf
-118
View File
@@ -1,118 +0,0 @@
# -*- coding: utf-8 -*-
# from numpy import average, log, nan, power, sqrt, zeros_like
from numpy import average, log, nan, sqrt, zeros_like
from numpy import power as np_power
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import v_float, v_offset, v_pos_default, v_series
def jma(
close: Series, length: IntFloat = None, phase: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Jurik Moving Average Average
This indicator, by Mark Jurik, attempts to eliminate noise. It claims
to have extremely low lag, is very smooth and is responsive to gaps.
Sources:
* [mql5](https://c.mql5.com/forextsd/forum/164/jurik_1.pdf)
* [prorealcode](https://www.prorealcode.com/prorealtime-indicators/jurik-volatility-bands/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```7```
phase (float): Phase value between [-100, 100]. Default: ```0```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
_length = v_pos_default(length, 7)
close = v_series(close, _length)
if close is None:
return
phase = v_float(phase, 0.0)
offset = v_offset(offset)
# Calculate
jma = zeros_like(close)
volty = zeros_like(close)
v_sum = zeros_like(close)
kv = det0 = det1 = ma2 = 0.0
jma[0] = ma1 = uBand = lBand = close.iloc[0]
# Static variables
sum_length = 10
length = 0.5 * (_length - 1)
pr = 0.5 if phase < -100 else 2.5 if phase > 100 else 1.5 + phase * 0.01
length1 = max((log(sqrt(length)) / log(2.0)) + 2.0, 0)
pow1 = max(length1 - 2.0, 0.5)
length2 = length1 * sqrt(length)
bet = length2 / (length2 + 1)
beta = 0.45 * (_length - 1) / (0.45 * (_length - 1) + 2.0)
m = close.shape[0]
for i in range(1, m):
price = close.iloc[i]
# Price volatility
del1 = price - uBand
del2 = price - lBand
volty[i] = max(abs(del1), abs(del2)) if abs(del1) != abs(del2) else 0
# Relative price volatility factor
v_sum[i] = v_sum[i - 1] + \
(volty[i] - volty[max(i - sum_length, 0)]) / sum_length
avg_volty = average(v_sum[max(i - 65, 0):i + 1])
d_volty = 0 if avg_volty == 0 else volty[i] / avg_volty
r_volty = max(1.0, min(np_power(length1, 1 / pow1), d_volty))
# r_volty = max(1.0, min(length1 **(1 / pow1), d_volty))
# Jurik volatility bands
pow2 = np_power(r_volty, pow1)
kv = np_power(bet, sqrt(pow2))
uBand = price if (del1 > 0) else price - (kv * del1)
lBand = price if (del2 < 0) else price - (kv * del2)
# Jurik Dynamic Factor
power = np_power(r_volty, pow1)
alpha = np_power(beta, power)
# 1st stage - preliminary smoothing by adaptive EMA
ma1 = (1 - alpha) * price + alpha * ma1
# 2nd stage - one more preliminary smoothing by Kalman filter
det0 = (1 - beta) * (price - ma1) + beta * det0
ma2 = ma1 + pr * det0
# 3rd stage - final smoothing by unique Jurik adaptive filter
det1 = ((ma2 - jma[i - 1]) * (1 - alpha) * \
(1 - alpha)) + (alpha * alpha * det1)
jma[i] = jma[i - 1] + det1
jma = Series(jma, index=close.index)
jma.iloc[0:_length - 1] = nan
# Offset
if offset != 0:
jma = jma.shift(offset)
# Fill
if "fillna" in kwargs:
jma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
jma.name = f"JMA_{_length}_{phase}"
jma.category = "overlap"
return jma
@@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.utils import (
non_zero_range,
v_drift,
v_mamode,
v_offset,
v_pos_default,
v_series
)
def kama(
close: Series, length: Int = None, fast: Int = None, slow: Int = None,
mamode: str = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Kaufman's Adaptive Moving Average
This indicator, by Perry Kaufman, attempts to find the overall trend by
adapting to volatility.
Sources:
* [stockcharts](https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:kaufman_s_adaptive_moving_average)
* [tradingview](https://www.tradingview.com/script/wZGOIz9r-REPOST-Indicators-3-Different-Adaptive-Moving-Averages/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
fast (int): Fast MA period. Default: ```2```
slow (int): Slow MA period. Default: ```30```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
fast = v_pos_default(fast, 2)
slow = v_pos_default(slow, 30)
close = v_series(close, max(fast, slow, length))
if close is None:
return
mamode = v_mamode(mamode, "sma")
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
def weight(length: int) -> float:
return 2 / (length + 1)
fr = weight(fast)
sr = weight(slow)
abs_diff = non_zero_range(close, close.shift(length)).abs()
peer_diff = non_zero_range(close, close.shift(drift)).abs()
peer_diff_sum = peer_diff.rolling(length).sum()
er = abs_diff / peer_diff_sum
x = er * (fr - sr) + sr
sc = x * x
m = close.size
ma0 = ma(mamode, close.iloc[:length], length=length, **kwargs).iloc[-1]
result = [nan for _ in range(0, length - 1)] + [ma0]
for i in range(length, m):
result.append(sc.iat[i] * close.iat[i] \
+ (1 - sc.iat[i]) * result[i - 1])
kama = Series(result, index=close.index)
# Offset
if offset != 0:
kama = kama.shift(offset)
# Fill
if "fillna" in kwargs:
kama.fillna(kwargs["fillna"], inplace=True)
# Name and Category
kama.name = f"KAMA_{length}_{fast}_{slow}"
kama.category = "overlap"
return kama
@@ -1,164 +0,0 @@
# -*- coding: utf-8 -*-
from sys import float_info as sflt
from numpy import arctan, nan, pi, zeros_like
from numpy.version import version as np_version
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import (
strided_window,
v_offset,
v_pos_default,
v_series,
v_talib,
zero
)
def linreg(
close: Series, length: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Linear Regression Moving Average
This indicator is a simplified version of Standard Linear Regression. It is
one variable rolling regression whereas a Standard Linear Regression is
between two or more variables.
Sources:
* [TA Lib](https://ta-lib.org)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
angle (bool): Returns the slope angle in radians.
Default: ```False```
degrees (bool): Return the slope angle in degrees.
Default: ```False```
intercept (bool): Return the intercept. Default: ```False```
r (bool): Return the 'r' correlation. Default: ```False```
slope (bool): Return the slope. Default: ```False```
tsf (bool): Return the Time Series Forecast value.
Default: ```False```
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Warning:
TA-Lib Correlation: ```np.float64(0.9985638477660118)```
Tip:
Corrective contributions welcome!
"""
# Validate
length = v_pos_default(length, 14)
close = v_series(close, length)
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
angle = kwargs.pop("angle", False)
intercept = kwargs.pop("intercept", False)
degrees = kwargs.pop("degrees", False)
r = kwargs.pop("r", False)
slope = kwargs.pop("slope", False)
tsf = kwargs.pop("tsf", False)
# Calculate
np_close = close.to_numpy()
if Imports["talib"] and mode_tal and not r:
from talib import LINEARREG, LINEARREG_ANGLE, LINEARREG_INTERCEPT, LINEARREG_SLOPE, TSF
if tsf:
linreg = TSF(close, timeperiod=length)
elif slope:
linreg = LINEARREG_SLOPE(close, timeperiod=length)
elif intercept:
linreg = LINEARREG_INTERCEPT(close, timeperiod=length)
elif angle:
linreg = LINEARREG_ANGLE(close, timeperiod=length)
else:
linreg = LINEARREG(close, timeperiod=length)
else:
linreg_ = zeros_like(np_close)
# [1, 2, ..., n] from 1 to n keeps Sum(xy) low
x = range(1, length + 1)
x_sum = 0.5 * length * (length + 1)
x2_sum = x_sum * (2 * length + 1) / 3
divisor = length * x2_sum - x_sum * x_sum
# Needs to be reworked outside the method
def linear_regression(series):
y_sum = series.sum()
xy_sum = (x * series).sum()
m = (length * xy_sum - x_sum * y_sum) / divisor
if slope:
return m
b = (y_sum * x2_sum - x_sum * xy_sum) / divisor
if intercept:
return b
if angle:
theta = arctan(m)
if degrees:
theta *= 180 / pi
return theta
if r:
y2_sum = (series * series).sum()
rn = length * xy_sum - x_sum * y_sum
rd = (divisor * (length * y2_sum - y_sum * y_sum)) ** 0.5
if zero(rd) == 0:
rd = sflt.epsilon
return rn / rd
return m * length + b if not tsf else m * (length - 1) + b
if np_version >= "1.20.0":
from numpy.lib.stride_tricks import sliding_window_view
linreg_ = [
linear_regression(_) for _ in sliding_window_view(
np_close, length)
]
else:
linreg_ = [
linear_regression(_) for _ in strided_window(
np_close, length)
]
linreg = Series([nan] * (length - 1) + linreg_, index=close.index)
# Offset
if offset != 0:
linreg = linreg.shift(offset)
# Fill
if "fillna" in kwargs:
linreg.fillna(kwargs["fillna"], inplace=True)
# Name and Category
linreg.name = f"LINREG"
if slope:
linreg.name += "m"
if intercept:
linreg.name += "b"
if angle:
linreg.name += "a"
if r:
linreg.name += "r"
linreg.name += f"_{length}"
linreg.category = "overlap"
return linreg
@@ -1,167 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import arctan, isnan, nan, zeros_like
from numba import njit
from pandas import DataFrame, Series
from pandas_ta._typing import Array, DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
# Ehler's Mother of Adaptive Moving Averages
# http://traders.com/documentation/feedbk_docs/2014/01/traderstips.html
@njit(cache=True)
def nb_mama(x, fastlimit, slowlimit, prenan):
a, b, m = 0.0962, 0.5769, x.size
p_w, smp_w, smp_w_c = 0.2, 0.33, 0.67
wma4 = zeros_like(x)
dt, smp = zeros_like(x), zeros_like(x)
i1, i2 = zeros_like(x), zeros_like(x)
ji, jq = zeros_like(x), zeros_like(x)
q1, q2 = zeros_like(x), zeros_like(x)
re, im, alpha = zeros_like(x), zeros_like(x), zeros_like(x)
period, phase = zeros_like(x), zeros_like(x)
mama, fama = zeros_like(x), zeros_like(x)
# Ehler's starts from 6, TV-LB from 3, TALib from 32
for i in range(3, m):
adj_prev_period = 0.075 * period[i - 1] + 0.54
# WMA(x,4) & Detrended WMA(x,4)
wma4[i] = 0.4 * x[i] + 0.3 * x[i - 1] + 0.2 * x[i - 2] + 0.1 * x[i - 3]
dt[i] = adj_prev_period * (a * wma4[i] + b * wma4[i - 2] - b * wma4[i - 4] - a * wma4[i - 6])
# Quadrature(Detrender) and In Phase Component
q1[i] = adj_prev_period * (a * dt[i] + b * dt[i - 2] - b * dt[i - 4] - a * dt[i - 6])
i1[i] = dt[i - 3]
# Phase Q1 and I1 by 90 degrees
ji[i] = adj_prev_period * (a * i1[i] + b * i1[i - 2] - b * i1[i - 4] - a * i1[i - 6])
jq[i] = adj_prev_period * (a * q1[i] + b * q1[i - 2] - b * q1[i - 4] - a * q1[i - 6])
# Phasor Addition for 3 Bar Averaging
i2[i] = i1[i] - jq[i]
q2[i] = q1[i] + ji[i]
# Smooth I2 & Q2
i2[i] = p_w * i2[i] + (1 - p_w) * i2[i - 1]
q2[i] = p_w * q2[i] + (1 - p_w) * q2[i - 1]
# Homodyne Discriminator
re[i] = i2[i] * i2[i - 1] + q2[i] * q2[i - 1]
im[i] = i2[i] * q2[i - 1] + q2[i] * i2[i - 1]
# Smooth Re & Im
re[i] = p_w * re[i] + (1 - p_w) * re[i - 1]
im[i] = p_w * im[i] + (1 - p_w) * im[i - 1]
if im[i] != 0.0 and re[i] != 0.0:
period[i] = 360 / arctan(im[i] / re[i])
else:
period[i] = 0
if period[i] > 1.5 * period[i - 1]:
period[i] = 1.5 * period[i - 1]
if period[i] < 0.67 * period[i - 1]:
period[i] = 0.67 * period[i - 1]
if period[i] < 6:
period[i] = 6
if period[i] > 50:
period[i] = 50
period[i] = p_w * period[i] + (1 - p_w) * period[i - 1]
smp[i] = smp_w * period[i] + smp_w_c * smp[i - 1]
if i1[i] != 0.0:
phase[i] = arctan(q1[i] / i1[i])
dphase = phase[i - 1] - phase[i]
if dphase < 1:
dphase = 1
alpha[i] = fastlimit / dphase
if alpha[i] > fastlimit:
alpha[i] = fastlimit
if alpha[i] < slowlimit:
alpha[i] = slowlimit
mama[i] = alpha[i] * x[i] + (1 - alpha[i]) * mama[i - 1]
fama[i] = 0.5 * alpha[i] * mama[i] + (1 - 0.5 * alpha[i]) * fama[i - 1]
mama[:prenan], fama[:prenan] = nan, nan
return mama, fama
def mama(
close: Series, fastlimit: IntFloat = None, slowlimit: IntFloat = None,
prenan: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""MESA Adaptive Moving Average
This indicator, aka the Mother of All Moving Averages by John Ehlers,
attempts to adapt to volatility by using a Hilbert Transform Discriminator
Sources:
* [Ehlers's Mother of Adaptive Moving Averages](http://traders.com/documentation/feedbk_docs/2014/01/traderstips.html)
* [tradingview](https://www.tradingview.com/script/foQxLbU3-Ehlers-MESA-Adaptive-Moving-Average-LazyBear/)
Parameters:
close (pd.Series): ```close``` Series
fastlimit (float): Fast limit. Default: ```0.5```
slowlimit (float): Slow limit. Default: ```0.05```
prenan (int): Prenans to apply. TV-LB ```3```, Ehler's ```6```,
TA Lib ```32```. Default: ```3```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 2 columns
Tip:
**FAMA** also included
"""
# Validate
close = v_series(close, 1)
if close is None:
return
fastlimit = v_pos_default(fastlimit, 0.5)
slowlimit = v_pos_default(slowlimit, 0.05)
prenan = v_pos_default(prenan, 3)
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
if Imports["talib"] and mode_tal:
from talib import MAMA
mama, fama = MAMA(np_close, fastlimit, slowlimit)
else:
mama, fama = nb_mama(np_close, fastlimit, slowlimit, prenan)
if all(isnan(mama)) or all(isnan(fama)):
return # Emergency Break
# Name and Category
_props = f"_{fastlimit}_{slowlimit}"
data = {f"MAMA{_props}": mama, f"FAMA{_props}": fama}
df = DataFrame(data, index=close.index)
df.name = f"MAMA{_props}"
df.category = "overlap"
# Offset
if offset != 0:
df = df.shift(offset)
# Fill
if "fillna" in kwargs:
df.fillna(kwargs["fillna"], inplace=True)
return df
@@ -1,72 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.utils import v_offset, v_pos_default, v_series
def _mcgd(x, n, k):
d = (k * n * (x[1] / x[0]) ** 4)
x[1] = (x[0] + ((x[1] - x[0]) / d))
return x[1]
def mcgd(
close: Series, length: Int = None, c: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""McGinley Dynamic Indicator
This indicator, by John R. McGinley, is not a moving average but a
differential smoothing technique.
Sources:
* John R. McGinley, a Certified Market Technician (CMT) and former
editor of the Market Technicians Association's Journal of
Technical Analysis.
* [investopedia](https://www.investopedia.com/articles/forex/09/mcginley-dynamic-indicator.asp)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
c (float): Denominator multiplier. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note:
Sometimes ```c``` is set to ```0.6```.
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
c = float(c) if isinstance(c, float) and 0 < c <= 1 else 1
offset = v_offset(offset)
# Calculate
close = close.copy()
mcg_ds = close[0:].rolling(2, min_periods=2) \
.apply(_mcgd, kwargs={"n": length, "k": c}, raw=True)
# Offset
if offset != 0:
mcg_ds = mcg_ds.shift(offset)
# Fill
if "fillna" in kwargs:
mcg_ds.fillna(kwargs["fillna"], inplace=True)
# Name and Category
mcg_ds.name = f"MCGD_{length}"
mcg_ds.category = "overlap"
return mcg_ds
@@ -1,64 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
def midpoint(
close: Series, length: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Midpoint
The Midpoint is the average of the rolling high and low of period length.
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```2```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 2)
if "min_periods" in kwargs and kwargs["min_periods"] is not None:
min_periods = int(kwargs["min_periods"])
else:
min_periods = length
close = v_series(close, max(length, min_periods))
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import MIDPOINT
midpoint = MIDPOINT(close, length)
else:
lowest = close.rolling(length, min_periods=min_periods).min()
highest = close.rolling(length, min_periods=min_periods).max()
midpoint = 0.5 * (lowest + highest)
# Offset
if offset != 0:
midpoint = midpoint.shift(offset)
# Fill
if "fillna" in kwargs:
midpoint.fillna(kwargs["fillna"], inplace=True)
# Name and Category
midpoint.name = f"MIDPOINT_{length}"
midpoint.category = "overlap"
return midpoint
@@ -1,67 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
def midprice(
high: Series, low: Series, length: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Midprice
The Midprice is the average of the rolling high and low of period length.
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
length (int): The period. Default: ```2```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 2)
if "min_periods" in kwargs and kwargs["min_periods"] is not None:
min_periods = int(kwargs["min_periods"])
else:
min_periods = length
_length = max(length, min_periods)
high = v_series(high, _length)
low = v_series(low, _length)
if high is None or low is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import MIDPRICE
midprice = MIDPRICE(high, low, length)
else:
lowest_low = low.rolling(length, min_periods=min_periods).min()
highest_high = high.rolling(length, min_periods=min_periods).max()
midprice = 0.5 * (lowest_low + highest_high)
# Offset
if offset != 0:
midprice = midprice.shift(offset)
# Fill
if "fillna" in kwargs:
midprice.fillna(kwargs["fillna"], inplace=True)
# Name and Category
midprice.name = f"MIDPRICE_{length}"
midprice.category = "overlap"
return midprice
@@ -1,53 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_offset, v_series
def ohlc4(
open_: Series, high: Series, low: Series, close: Series,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""OHLC4
OHLC4 is the average of open, high, low and close.
Parameters:
open_ (pd.Series): ```open``` Series
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```.
Only works when offset.
Returns:
(pd.Series): 1 column
"""
# Validate
open_ = v_series(open_)
high = v_series(high)
low = v_series(low)
close = v_series(close)
offset = v_offset(offset)
# Calculate
avg = 0.25 * (open_.to_numpy() + high.to_numpy() + low.to_numpy() + close.to_numpy())
ohlc4 = Series(avg, index=close.index)
# Offset
if offset != 0:
ohlc4 = ohlc4.shift(offset)
# Fill
if "fillna" in kwargs:
ohlc4.fillna(kwargs["fillna"], inplace=True)
# Name and Category
ohlc4.name = "OHLC4"
ohlc4.category = "overlap"
return ohlc4
@@ -1,264 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import greater, nan, zeros_like
from numba import njit
from pandas import DataFrame, DateOffset, Series, infer_freq
from pandas_ta._typing import DictLike
from pandas_ta.utils import (
nb_nonzero_range,
v_datetime_ordered,
v_series,
v_str
)
# Support for Pandas v1.4.x and v2.2.x
td_mapping = {
'Y': 'years',
'YE': 'years',
'M': 'months',
'ME': 'months',
'D': 'days',
}
@njit(cache=True)
def pivot_camarilla(high, low, close):
tp = (high + low + close) / 3
hl_range = nb_nonzero_range(high, low)
s1 = close - 11 / 120 * hl_range
s2 = close - 11 / 60 * hl_range
s3 = close - 0.275 * hl_range
s4 = close - 0.55 * hl_range
r1 = close + 11 / 120 * hl_range
r2 = close + 11 / 60 * hl_range
r3 = close + 0.275 * hl_range
r4 = close + 0.55 * hl_range
return tp, s1, s2, s3, s4, r1, r2, r3, r4
@njit(cache=True)
def pivot_classic(high, low, close):
tp = (high + low + close) / 3
hl_range = nb_nonzero_range(high, low)
s1 = 2 * tp - high
s2 = tp - hl_range
s3 = tp - 2 * hl_range
s4 = tp - 3 * hl_range
r1 = 2 * tp - low
r2 = tp + hl_range
r3 = tp + 2 * hl_range
r4 = tp + 3 * hl_range
return tp, s1, s2, s3, s4, r1, r2, r3, r4
@njit(cache=True)
def pivot_demark(open_, high, low, close):
if (open_ == close).all():
tp = 0.25 * (high + low + 2 * close)
elif greater(close, open_).all():
tp = 0.25 * (2 * high + low + close)
else:
tp = 0.25 * (high + 2 * low + close)
s1 = 2 * tp - high
r1 = 2 * tp - low
return tp, s1, r1
@njit(cache=True)
def pivot_fibonacci(high, low, close):
tp = (high + low + close) / 3
hl_range = nb_nonzero_range(high, low)
s1 = tp - 0.382 * hl_range
s2 = tp - 0.618 * hl_range
s3 = tp - hl_range
r1 = tp + 0.382 * hl_range
r2 = tp + 0.618 * hl_range
r3 = tp + hl_range
return tp, s1, s2, s3, r1, r2, r3
@njit(cache=True)
def pivot_traditional(high, low, close):
tp = (high + low + close) / 3
hl_range = nb_nonzero_range(high, low)
s1 = 2 * tp - high
s2 = tp - hl_range
s3 = tp - 2 * hl_range
s4 = tp - 2 * hl_range
r1 = 2 * tp - low
r2 = tp + hl_range
r3 = tp + 2 * hl_range
r4 = tp + 2 * hl_range
return tp, s1, s2, s3, s4, r1, r2, r3, r4
@njit(cache=True)
def pivot_woodie(open_, high, low):
tp = (2 * open_ + high + low) / 4
hl_range = nb_nonzero_range(high, low)
s1 = 2 * tp - high
s2 = tp - hl_range
s3 = low - 2 * (high - tp)
s4 = s3 - hl_range
r1 = 2 * tp - low
r2 = tp + hl_range
r3 = high + 2 * (tp - low)
r4 = r3 + hl_range
return tp, s1, s2, s3, s4, r1, r2, r3, r4
def pivots(
open_: Series, high: Series,
low: Series, close: Series,
method: str = None, anchor: str = None,
**kwargs: DictLike
) -> DataFrame:
"""Pivot Points
Pivot Points attempt to identify support and resistance levels.
There are many different methods of calculating Pivot Points. The most
common (and default) method is: Traditional. Other methods include:
Camarilla, Classic, Demark, Fibonacci, and Woodie.
Sources:
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/PivotPoints.html)
Parameters:
open_ (pd.Series): ```open``` Series
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
method (str): Pivot methode. Default: ```'traditional'```
anchor (str): Anchor frequency. Default: ```'D'```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 3, 7 or 9 columns
Note:
[Pandas Offset Aliases](https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#timeseries-offset-aliases)
"""
# Validate
open_ = v_series(open_)
high = v_series(high)
low = v_series(low)
close = v_series(close)
if open_ is None or high is None or low is None or close is None:
return None
methods = [
"traditional", "fibonacci", "woodie", "classic", "demark", "camarilla"
]
method = v_str(method, methods[0])
if close.index.size < 3:
return # Emergency Break
if not v_datetime_ordered(close):
print("[!] Pivots requires an ordered DatetimeIndex.")
return
dt_index = close.index
freq = infer_freq(dt_index)
if anchor and isinstance(anchor, str) and len(anchor) >= 1:
anchor = anchor.upper()
else:
anchor = "D"
# Resample if freq does not match the anchor
if freq is not anchor:
df = DataFrame(
data={
"open": open_.resample(anchor).first(),
"high": high.resample(anchor).max(),
"low": low.resample(anchor).min(),
"close": close.resample(anchor).last()
}
)
df.dropna(inplace=True)
else:
df = DataFrame(
data={"open": open_, "high": high, "low": low, "close": close},
index=dt_index
)
np_open = df.open.to_numpy()
np_high = df.high.to_numpy()
np_low = df.low.to_numpy()
np_close = df.close.to_numpy()
# Create nan arrays for "demark" and "fibonacci" pivots
_nan_array = zeros_like(np_close)
_nan_array[:] = nan
tp = s1 = s2 = s3 = s4 = r1 = r2 = r3 = r4 = _nan_array
# Calculate
if method == "camarilla":
tp, s1, s2, s3, s4, r1, r2, r3, r4 = \
pivot_camarilla(np_high, np_low, np_close)
elif method == "classic":
tp, s1, s2, s3, s4, r1, r2, r3, r4 = \
pivot_classic(np_high, np_low, np_close)
elif method == "demark":
tp, s1, r1 = pivot_demark(np_open, np_high, np_low, np_close)
elif method == "fibonacci":
tp, s1, s2, s3, r1, r2, r3 = pivot_fibonacci(np_high, np_low, np_close)
elif method == "woodie":
tp, s1, s2, s3, s4, r1, r2, r3, r4 = \
pivot_woodie(np_open, np_high, np_low)
else: # Traditional
tp, s1, s2, s3, s4, r1, r2, r3, r4 = \
pivot_traditional(np_high, np_low, np_close)
# Name and Category
_props = f"PIVOTS_{method[:4].upper()}_{anchor}"
df[f"{_props}_P"] = tp
df[f"{_props}_S1"], df[f"{_props}_S2"] = s1, s2
df[f"{_props}_S3"], df[f"{_props}_S4"] = s3, s4
df[f"{_props}_R1"], df[f"{_props}_R2"] = r1, r2
df[f"{_props}_R3"], df[f"{_props}_R4"] = r3, r4
time_unit = td_mapping.get(anchor.upper(), None)
if time_unit:
time_delta = DateOffset(**{time_unit: 1})
df.index = df.index + time_delta
else:
print(f"[!] Unsupported time anchor {anchor}.")
if freq is not anchor:
df = df.reindex(dt_index, method="ffill")
df = df.iloc[:,4:]
if method in ["demark", "fibonacci"]:
df.drop(columns=[x for x in df.columns if all(df[x].isna())], inplace=True)
df.name = _props
df.category = "overlap"
return df
@@ -1,67 +0,0 @@
# -*- coding: utf-8 -*-
# from numpy.version import version as np_version
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
pascals_triangle,
v_offset,
v_ascending,
v_pos_default,
v_series,
weights
)
def pwma(
close: Series, length: Int = None, asc: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Pascal's Weighted Moving Average
This indicator, by Kevin Johnson, creates a weighted moving average using
Pascal's Triangle.
Sources:
* Kevin Johnson
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
asc (bool): Ascending. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
asc = v_ascending(asc)
offset = v_offset(offset)
# Calculate
triangle = pascals_triangle(n=length - 1, weighted=True)
pwma = close.rolling(length, min_periods=length) \
.apply(weights(triangle), raw=True)
# Offset
if offset != 0:
pwma = pwma.shift(offset)
# Fill
if "fillna" in kwargs:
pwma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
pwma.name = f"PWMA_{length}"
pwma.category = "overlap"
return pwma
@@ -1,56 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_offset, v_pos_default, v_series
def rma(
close: Series, length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""wildeR's Moving Average
This indicator, by Wilder, is simply an EMA where _alpha_ is
the recipical of its _length_.
Sources:
* [incrediblecharts](https://www.incrediblecharts.com/indicators/wilder_moving_average.php)
* [thinkorswim](https://tlc.thinkorswim.com/center/reference/Tech-Indicators/studies-library/V-Z/WildersSmoothing)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
alpha = (1.0 / length) if length > 0 else 0.5
offset = v_offset(offset)
rma = close.ewm(alpha=alpha, adjust=False).mean()
# Offset
if offset != 0:
rma = rma.shift(offset)
# Fill
if "fillna" in kwargs:
rma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
rma.name = f"RMA_{length}"
rma.category = "overlap"
return rma
@@ -1,63 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import pi, sin
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import v_offset, v_pos_default, v_series, weights
def sinwma(
close: Series, length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Sine Weighted Moving Average
This indicator is a weighted average using sine cycles where the central
values have greater weight.
Source:
* [Everget](https://www.tradingview.com/u/everget/)
* [tradingview](https://www.tradingview.com/script/6MWFvnPO-Sine-Weighted-Moving-Average/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 14)
close = v_series(close, length)
if close is None:
return
offset = v_offset(offset)
# Calculate
sines = Series(
[sin((i + 1) * pi / (length + 1)) for i in range(0, length)]
)
w = sines / sines.sum()
sinwma = close.rolling(length, min_periods=length) \
.apply(weights(w), raw=True)
# Offset
if offset != 0:
sinwma = sinwma.shift(offset)
# Fill
if "fillna" in kwargs:
sinwma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
sinwma.name = f"SINWMA_{length}"
sinwma.category = "overlap"
return sinwma
@@ -1,85 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import convolve, ones
from numba import njit
from pandas import Series
from .._typing import Array, DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import (
nb_prepend,
v_offset,
v_pos_default,
v_series,
v_talib
)
# Fast SMA Options: https://github.com/numba/numba/issues/4119
@njit(cache=True)
def nb_sma(x, n):
result = convolve(ones(n) / n, x)[n - 1:1 - n]
return nb_prepend(result, n - 1)
def sma(
close: Series, length: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Simple Moving Average
This indicator is the the textbook moving average, a rolling sum of
values divided by the window period (or length).
Sources:
* [tradingtechnologies](https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/simple-moving-average-sma/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
adjust (bool): Adjust the values. Default: ```True```
presma (bool): If True, uses SMA for initial value.
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
length = v_pos_default(length, 10)
if "min_periods" in kwargs and kwargs["min_periods"] is not None:
min_periods = int(kwargs["min_periods"])
else:
min_periods = length
close = v_series(close, max(length, min_periods))
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal and length > 1:
from talib import SMA
sma = SMA(close, length)
else:
np_close = close.to_numpy()
sma = nb_sma(np_close, length)
sma = Series(sma, index=close.index)
# Offset
if offset != 0:
sma = sma.shift(offset)
# Fill
if "fillna" in kwargs:
sma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
sma.name = f"SMA_{length}"
sma.category = "overlap"
return sma
@@ -1,82 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.ma import ma
from pandas_ta.utils import (
v_mamode,
v_offset,
v_pos_default,
v_series,
v_talib
)
def smma(
close: Series, length: Int = None,
mamode: str = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""SMoothed Moving Average
This indicator attempts to confirm trends and identify support and
resistance areas. It tries to reduce noise in contrast to reducing lag.
Sources:
* [sierrachart](https://www.sierrachart.com/index.php?page=doc/StudiesReference.php&ID=173&Name=Moving_Average_-_Smoothed)
* [tradingview](https://www.tradingview.com/scripts/smma/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
mamode (str): See ```help(ta.ma)```. Default: ```"sma"```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note:
A core component of Bill Williams Alligator indicator.
"""
# Validate
length = v_pos_default(length, 7)
if "min_periods" in kwargs and kwargs["min_periods"] is not None:
min_periods = int(kwargs["min_periods"])
else:
min_periods = length
close = v_series(close, max(length, min_periods))
if close is None:
return
mamode = v_mamode(mamode, "sma")
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
m = close.size
smma = close.copy()
smma[:length - 1] = nan
smma.iloc[length - 1] = ma(mamode, close[0:length], length=length, talib=mode_tal).iloc[-1]
for i in range(length, m):
smma.iat[i] = ((length - 1) * smma.iat[i - 1] + smma.iat[i]) / length
# Offset
if offset != 0:
smma = smma.shift(offset)
# Fill
if "fillna" in kwargs:
smma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
smma.name = f"SMMA_{length}"
smma.category = "overlap"
return smma
-115
View File
@@ -1,115 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import copy, cos, exp, zeros_like
from numba import njit
from pandas import Series
from pandas_ta._typing import Array, DictLike, Int, IntFloat
from pandas_ta.utils import v_bool, v_offset, v_pos_default, v_series
# Ehlers's Super Smoother Filter
# http://traders.com/documentation/feedbk_docs/2014/01/traderstips.html
@njit(cache=True)
def nb_ssf(x, n, pi, sqrt2):
m, ratio, result = x.size, sqrt2 / n, copy(x)
a = exp(-pi * ratio)
b = 2 * a * cos(180 * ratio)
c = a * a - b + 1
# result[:2] = x[:2]
for i in range(2, m):
result[i] = 0.5 * c * (x[i] + x[i - 1]) + b * result[i - 1] \
- a * a * result[i - 2]
return result
# John F. Ehlers's Super Smoother Filter by Everget (2 poles), Tradingview
# https://www.tradingview.com/script/VdJy0yBJ-Ehlers-Super-Smoother-Filter/
@njit(cache=True)
def nb_ssf_everget(x, n, pi, sqrt2):
m, arg, result = x.size, pi * sqrt2 / n, copy(x)
a = exp(-arg)
b = 2 * a * cos(arg)
# result[:2] = x[:2]
for i in range(2, m):
result[i] = 0.5 * (a * a - b + 1) * (x[i] + x[i - 1]) \
+ b * result[i - 1] - a * a * result[i - 2]
return result
def ssf(
close: Series, length: Int = None,
everget: bool = None, pi: IntFloat = None, sqrt2: IntFloat = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Ehlers's Super Smoother Filter
This indicator, by John F. Ehlers's © 2013, is a (Recursive) Digital
Filter that attempts to reduce lag and remove aliases. This version
has two poles.
Sources:
* [mql5](https://www.mql5.com/en/code/588)
* [traders.com](http://traders.com/documentation/feedbk_docs/2014/01/traderstips.html)
* [tradingview](https://www.tradingview.com/script/VdJy0yBJ-Ehlers-Super-Smoother-Filter/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```20```
everget (bool): Everget's implementation of ssf that uses pi
instead of 180 for the b factor of ssf. Default: ```False```
pi (float): The default is Ehlers's truncated value: ```3.14159```.
Default: ```3.14159```
sqrt2 (float): The default is Ehlers's truncated value: ```1.414```.
Default: ```1.414```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note:
* Everget's calculation on TradingView:
```pi=np.pi```, ```sqrt2=np.sqrt(2)```
Danger:
Possible Data Leak
"""
# Validate
length = v_pos_default(length, 20)
close = v_series(close, length)
if close is None:
return
pi = v_pos_default(pi, 3.14159)
sqrt2 = v_pos_default(sqrt2, 1.414)
everget = v_bool(everget, False)
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
if everget:
ssf = nb_ssf_everget(np_close, length, pi, sqrt2)
else:
ssf = nb_ssf(np_close, length, pi, sqrt2)
ssf = Series(ssf, index=close.index)
# Offset
if offset != 0:
ssf = ssf.shift(offset)
# Fill
if "fillna" in kwargs:
ssf.fillna(kwargs["fillna"], inplace=True)
# Name and Category
ssf.name = f"SSF{'e' if everget else ''}_{length}"
ssf.category = "overlap"
return ssf
@@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import copy, cos, exp, zeros_like
from numba import njit
from pandas import Series
from pandas_ta._typing import Array, DictLike, Int, IntFloat
from pandas_ta.utils import v_offset, v_pos_default, v_series
# John F. Ehler's Super Smoother Filter by Everget (3 poles), Tradingview
# https://www.tradingview.com/script/VdJy0yBJ-Ehlers-Super-Smoother-Filter/
@njit(cache=True)
def nb_ssf3(x, n, pi, sqrt3):
m, result = x.size, copy(x)
a = exp(-pi / n)
b = 2 * a * cos(-pi * sqrt3 / n)
c = a * a
d4 = c * c
d3 = -c * (1 + b)
d2 = b + c
d1 = 1 - d2 - d3 - d4
# result[:3] = x[:3]
for i in range(3, m):
result[i] = d1 * x[i] + d2 * result[i - 1] \
+ d3 * result[i - 2] + d4 * result[i - 3]
return result
def ssf3(
close: Series, length: Int = None,
pi: IntFloat = None, sqrt3: IntFloat = None,
offset: Int = None, **kwargs: DictLike
):
"""Ehlers's 3 Pole Super Smoother Filter
This indicator, by John F. Ehlers's © 2013, is a (Recursive) Digital
Filter that attempts to reduce lag and remove aliases. This version
has two poles.
Sources:
* [mql5](https://www.mql5.com/en/code/589)
* [tradingview](https://www.tradingview.com/script/VdJy0yBJ-Ehlers-Super-Smoother-Filter/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```20```
pi (float): The value of ```PI```. The default is Ehler's truncated
value: ```3.14159```. Default: ```3.14159```
sqrt3 (float): The value of ```sqrt(3)``` to use. The default is
Ehler's truncated value: ```1.732```. Default: ```1.732```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note:
* Everget's calculation on TradingView:
```pi=np.pi```, ```sqrt2=np.sqrt(2)```
"""
# Validate
length = v_pos_default(length, 20)
close = v_series(close, length)
if close is None:
return
pi = v_pos_default(pi, 3.14159)
sqrt3 = v_pos_default(sqrt3, 1.732)
offset = v_offset(offset)
# Calculate
np_close = close.to_numpy()
ssf = nb_ssf3(np_close, length, pi, sqrt3)
ssf = Series(ssf, index=close.index)
# Offset
if offset != 0:
ssf = ssf.shift(offset)
# Fill
if "fillna" in kwargs:
ssf.fillna(kwargs["fillna"], inplace=True)
# Name and Category
ssf.name = f"SSF3_{length}"
ssf.category = "overlap"
return ssf
@@ -1,108 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import DataFrame, Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.overlap import hl2
from pandas_ta.utils import v_mamode, v_offset, v_pos_default, v_series
from pandas_ta.volatility import atr
def supertrend(
high: Series, low: Series, close: Series,
length: Int = None, atr_length: Int = None,
multiplier: IntFloat = None,
atr_mamode : str = None,
offset: Int = None, **kwargs: DictLike
) -> DataFrame:
"""Supertrend
This indicator attempts to identify trend direction as well as support and
resistance levels.
Sources:
* [freebsensetips](http://www.freebsensetips.com/blog/detail/7/What-is-supertrend-indicator-its-calculation)
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
length (int): The period. Default: ```7```
atr_length (int): ATR period. Default: ```length```
multiplier (float): Coefficient for upper and lower band distance to
midrange. Default: ```3.0```
atr_mamode (str) : MA type to be used for ATR calculation.
See ```help(ta.ma)```. Default: ```"rma"```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.DataFrame): 4 columns
"""
# Validate
length = v_pos_default(length, 7)
atr_length = v_pos_default(atr_length, length)
high = v_series(high, length + 1)
low = v_series(low, length + 1)
close = v_series(close, length + 1)
if high is None or low is None or close is None:
return
multiplier = v_pos_default(multiplier, 3.0)
atr_mamode = v_mamode(atr_mamode, "rma")
offset = v_offset(offset)
# Calculate
m = close.size
dir_, trend = [1] * m, [0] * m
long, short = [nan] * m, [nan] * m
hl2_ = hl2(high, low)
matr = multiplier * atr(high, low, close, atr_length, mamode=atr_mamode)
lb = hl2_ - matr
ub = hl2_ + matr
for i in range(1, m):
if close.iat[i] > ub.iat[i - 1]:
dir_[i] = 1
elif close.iat[i] < lb.iat[i - 1]:
dir_[i] = -1
else:
dir_[i] = dir_[i - 1]
if dir_[i] > 0 and lb.iat[i] < lb.iat[i - 1]:
lb.iat[i] = lb.iat[i - 1]
if dir_[i] < 0 and ub.iat[i] > ub.iat[i - 1]:
ub.iat[i] = ub.iat[i - 1]
if dir_[i] > 0:
trend[i] = long[i] = lb.iat[i]
else:
trend[i] = short[i] = ub.iat[i]
trend[0] = nan
dir_[:length] = [nan] * length
_props = f"_{length}_{multiplier}"
data = {
f"SUPERT{_props}": trend,
f"SUPERTd{_props}": dir_,
f"SUPERTl{_props}": long,
f"SUPERTs{_props}": short
}
df = DataFrame(data, index=close.index)
df.name = f"SUPERT{_props}"
df.category = "overlap"
# Offset
if offset != 0:
df = df.shift(offset)
# Fill
if "fillna" in kwargs:
df.fillna(kwargs["fillna"], inplace=True)
return df
@@ -1,68 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.utils import (
symmetric_triangle,
v_offset,
v_pos_default,
v_series,
weights
)
def swma(
close: Series, length: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Symmetric Weighted Moving Average
This indicator is based on a Symmetric Weighted Moving Average where
weights are based on a symmetric triangle.
Source:
* [tradingview](https://www.tradingview.com/study-script-reference/#fun_swma)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note:
* ```n=3``` -> ```[1, 2, 1]```
* ```n=4``` -> ```[1, 2, 2, 1]```
* etc...
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
offset = v_offset(offset)
# Calculate
triangle = symmetric_triangle(length, weighted=True)
swma = close.rolling(length, min_periods=length) \
.apply(weights(triangle), raw=True)
# Offset
if offset != 0:
swma = swma.shift(offset)
# Fill
if "fillna" in kwargs:
swma.fillna(kwargs["fillna"], inplace=True)
# Name and Category
swma.name = f"SWMA_{length}"
swma.category = "overlap"
return swma
@@ -1,85 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import isnan
from pandas import Series
from pandas_ta._typing import DictLike, Int, IntFloat
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
from .ema import ema
def t3(
close: Series, length: Int = None, a: IntFloat = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""T3
This indicator, by Tim Tillson, attempts to be smoother and more
responsive relative to other moving averages.
Sources:
* [binarytribune](http://www.binarytribune.com/forex-trading-indicators/t3-moving-average-indicator/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
a (float): The a factor, 0 < a < 1. Default: ```0.7```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
adjust (bool): Default: True
presma (bool): If True, uses SMA for initial value.
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Warning:
TA-Lib Correlation: ```np.float64(0.9999994265973177)```
Tip:
Corrective contributions welcome!
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, 5 * (length + 1))
if close is None:
return
a = float(a) if isinstance(a, float) and 0 < a < 1 else 0.7
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import T3
t3 = T3(close, length, a)
else:
c1 = -a * a**2
c2 = 3 * a**2 + 3 * a**3
c3 = -6 * a**2 - 3 * a - 3 * a**3
c4 = a**3 + 3 * a**2 + 3 * a + 1
e1 = ema(close=close, length=length, talib=mode_tal, **kwargs)
e2 = ema(close=e1, length=length, talib=mode_tal, **kwargs)
e3 = ema(close=e2, length=length, talib=mode_tal, **kwargs)
e4 = ema(close=e3, length=length, talib=mode_tal, **kwargs)
e5 = ema(close=e4, length=length, talib=mode_tal, **kwargs)
e6 = ema(close=e5, length=length, talib=mode_tal, **kwargs)
t3 = c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3
# Offset
if offset != 0:
t3 = t3.shift(offset)
# Fill
if "fillna" in kwargs:
t3.fillna(kwargs["fillna"], inplace=True)
# Name and Category
t3.name = f"T3_{length}_{a}"
t3.category = "overlap"
return t3
@@ -1,73 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
from .ema import ema
def tema(
close: Series, length: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Triple Exponential Moving Average
This indicator attempts to be less laggy than the EMA.
Sources:
* [tradingtechnologies](https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triple-exponential-moving-average-tema/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
adjust (bool): Default: ```True```
presma (bool): If True, uses SMA for initial value.
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Warning:
TA-Lib Correlation: ```np.float64(0.9999355450605516)```
Tip:
Corrective contributions welcome!
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, 3 * length)
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import TEMA
tema = TEMA(close, length)
else:
ema1 = ema(close=close, length=length, talib=mode_tal, **kwargs)
ema2 = ema(close=ema1, length=length, talib=mode_tal, **kwargs)
ema3 = ema(close=ema2, length=length, talib=mode_tal, **kwargs)
tema = 3 * (ema1 - ema2) + ema3
# Offset
if offset != 0:
tema = tema.shift(offset)
# Fill
if "fillna" in kwargs:
tema.fillna(kwargs["fillna"], inplace=True)
# Name and Category
tema.name = f"TEMA_{length}"
tema.category = "overlap"
return tema
@@ -1,77 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_pos_default, v_series, v_talib
from .sma import sma
def trima(
close: Series, length: Int = None, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Triangular Moving Average
This indicator is a weighted moving average where the shape of the
weights are triangular with the greatest weight is in the middle
of the period.
Sources:
* [tradingtechnologies](https://www.tradingtechnologies.com/help/x-study/technical-indicator-definitions/triangular-moving-average-trima/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```10```
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
adjust (bool): Default: True
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note:
tma = sma(sma(src, ceil(length / 2)), floor(length / 2) + 1) # Tradingview
trima = sma(sma(x, n), n) # Tradingview
Warning:
TA-Lib Correlation: ```np.float64(0.9991752493891967)```
Tip:
Corrective contributions welcome!
"""
# Validate
length = v_pos_default(length, 10)
close = v_series(close, length)
if close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import TRIMA
trima = TRIMA(close, length)
else:
half_length = round(0.5 * (length + 1))
sma1 = sma(close, length=half_length, talib=mode_tal)
trima = sma(sma1, length=half_length, talib=mode_tal)
# Offset
if offset != 0:
trima = trima.shift(offset)
# Fill
if "fillna" in kwargs:
trima.fillna(kwargs["fillna"], inplace=True)
# Name and Category
trima.name = f"TRIMA_{length}"
trima.category = "overlap"
return trima
@@ -1,113 +0,0 @@
# -*- coding: utf-8 -*-
from numpy import nan
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import (
v_drift,
v_offset,
v_pos_default,
v_series,
v_talib
)
def vidya(
close: Series, length: Int = None,
talib: bool = None, drift: Int = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Variable Index Dynamic Average
This indicator, by Tushar Chande, is similar to an EMA but it has a
dynamically adjusted lookback period dependent based on CMO.
Sources:
* [perfecttrendsystem](https://www.perfecttrendsystem.com/blog_mt4_2/en/vidya-indicator-for-mt4)
* [tradingview](https://www.tradingview.com/script/hdrf0fXV-Variable-Index-Dynamic-Average-VIDYA/)
Parameters:
close (pd.Series): ```close``` Series
length (int): The period. Default: ```14```
talib (bool): If installed, use TA Lib. Default: ```True```
drift (int): Difference amount. Default: ```1```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
Note:
Sometimes used as a moving average or a trend identifier.
"""
# Validate
length = v_pos_default(length, 14)
close = v_series(close, length + 1)
if close is None:
return
mode_tal = v_talib(talib)
drift = v_drift(drift)
offset = v_offset(offset)
# Calculate
m = close.size
alpha = 2 / (length + 1)
if Imports["talib"] and mode_tal:
from talib import CMO
cmo_ = 0.01 * CMO(close, length)
else:
cmo_ = _cmo(close, length, drift)
abs_cmo = cmo_.abs().astype(float)
vidya = Series(0.0, index=close.index)
for i in range(length, m):
vidya.iloc[i] = alpha * abs_cmo.iloc[i] * close.iloc[i] + \
vidya.iloc[i - 1] * (1 - alpha * abs_cmo.iloc[i])
vidya.replace({0: nan}, inplace=True)
# Offset
if offset != 0:
vidya = vidya.shift(offset)
# Fill
if "fillna" in kwargs:
vidya.fillna(kwargs["fillna"], inplace=True)
# Name and Category
vidya.name = f"VIDYA_{length}"
vidya.category = "overlap"
return vidya
def _cmo(x: Series, length: Int, drift: Int):
"""Chande Momentum Oscillator Patch
Unguarded CMO Patch
Parameters:
x (pd.Series): ```x``` Series
length (int): The period.
drift (int): Difference amount.
Returns:
(pd.Series): 1 column
Info: Weird Circular TypeError!?
For some reason: from pandas_ta.momentum import cmo causes
pandas_ta.momentum.coppock to not be able to import it's _wma_ like
from pandas_ta.overlap import wma?
"""
mom = x.diff(drift)
positive = mom.copy().clip(lower=0)
negative = mom.copy().clip(upper=0).abs()
pos_sum = positive.rolling(length).sum()
neg_sum = negative.rolling(length).sum()
return (pos_sum - neg_sum) / (pos_sum + neg_sum)
@@ -1,65 +0,0 @@
# -*- coding: utf-8 -*-
from pandas import Series
from pandas_ta._typing import DictLike, Int
from pandas_ta.maps import Imports
from pandas_ta.utils import v_offset, v_series, v_talib
def wcp(
high: Series, low: Series, close: Series, talib: bool = None,
offset: Int = None, **kwargs: DictLike
) -> Series:
"""Weighted Closing Price
This indicator is a weighted value of: high, low and twice the close.
Sources:
* [fmlabs](https://www.fmlabs.com/reference/default.htm?url=WeightedCloses.htm)
Parameters:
high (pd.Series): ```high``` Series
low (pd.Series): ```low``` Series
close (pd.Series): ```close``` Series
talib (bool): If installed, use TA Lib. Default: ```True```
offset (int): Post shift. Default: ```0```
Other Parameters:
fillna (value): ```pd.DataFrame.fillna(value)```
Returns:
(pd.Series): 1 column
"""
# Validate
_length = 1
high = v_series(high, _length)
low = v_series(low, _length)
close = v_series(close, _length)
if high is None or low is None or close is None:
return
mode_tal = v_talib(talib)
offset = v_offset(offset)
# Calculate
if Imports["talib"] and mode_tal:
from talib import WCLPRICE
wcp = WCLPRICE(high, low, close)
else:
weight = high.to_numpy() + low.to_numpy() + 2 * close.to_numpy()
wcp = Series(weight, index=close.index)
# Offset
if offset != 0:
wcp = wcp.shift(offset)
# Fill
if "fillna" in kwargs:
wcp.fillna(kwargs["fillna"], inplace=True)
# Name and Category
wcp.name = "WCP"
wcp.category = "overlap"
return wcp

Some files were not shown because too many files have changed in this diff Show More