mirror of
https://github.com/Ichinga-Samuel/aiomql.git
synced 2026-08-04 15:57:44 +00:00
4.0.15b
This commit is contained in:
+3
-3
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
]
|
||||
@@ -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}")
|
||||
@@ -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
|
||||
@@ -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
@@ -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",
|
||||
]
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user