mirror of
https://github.com/wassname/pandas-ta.git
synced 2026-08-11 11:22:48 +08:00
BUG ENH DEV PERF TST STY overhaul
This commit is contained in:
+357
-311
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+87
-87
File diff suppressed because one or more lines are too long
+22
-113
@@ -2,121 +2,30 @@ name = "pandas_ta"
|
||||
"""
|
||||
.. moduleauthor:: Kevin Johnson
|
||||
"""
|
||||
from importlib.util import find_spec
|
||||
from pathlib import Path
|
||||
from pkg_resources import get_distribution, DistributionNotFound
|
||||
# Dictionaries and version
|
||||
from pandas_ta.maps import EXCHANGE_TZ, RATE, Category, Imports, version
|
||||
from pandas_ta.utils import *
|
||||
|
||||
# Flat Structure. Supports ta.ema() or ta.overlap.ema() calls.
|
||||
from pandas_ta.candles import *
|
||||
from pandas_ta.cycles import *
|
||||
from pandas_ta.momentum import *
|
||||
from pandas_ta.overlap import *
|
||||
from pandas_ta.performance import *
|
||||
from pandas_ta.statistics import *
|
||||
from pandas_ta.trend import *
|
||||
from pandas_ta.volatility import *
|
||||
from pandas_ta.volume import *
|
||||
|
||||
_dist = get_distribution("pandas_ta")
|
||||
try:
|
||||
# Normalize case for Windows systems
|
||||
here = Path(_dist.location) / __file__
|
||||
if not here.exists():
|
||||
# not installed, but there is another version that *is*
|
||||
raise DistributionNotFound
|
||||
except DistributionNotFound:
|
||||
__version__ = "Please install this project with setup.py"
|
||||
# Common Averages useful for Indicators with a mamode argument, like ta.adx()
|
||||
from pandas_ta.ma import ma
|
||||
|
||||
version = __version__ = _dist.version
|
||||
# Enable "ta" DataFrame Extension
|
||||
from pandas_ta.core import AnalysisIndicators
|
||||
|
||||
Imports = {
|
||||
"alphaVantage-api": find_spec("alphaVantageAPI") is not None,
|
||||
"matplotlib": find_spec("matplotlib") is not None,
|
||||
"mplfinance": find_spec("mplfinance") is not None,
|
||||
"numba": find_spec("numba") is not None,
|
||||
"yaml": find_spec("yaml") is not None,
|
||||
"scipy": find_spec("scipy") is not None,
|
||||
"sklearn": find_spec("sklearn") is not None,
|
||||
"statsmodels": find_spec("statsmodels") is not None,
|
||||
"stochastic": find_spec("stochastic") is not None,
|
||||
"talib": find_spec("talib") is not None,
|
||||
"tqdm": find_spec("tqdm") is not None,
|
||||
"vectorbt": find_spec("vectorbt") is not None,
|
||||
"yfinance": find_spec("yfinance") is not None,
|
||||
"polygon": find_spec("polygon") is not None,
|
||||
}
|
||||
# Custom External Directory Commands. See help(import_dir)
|
||||
from pandas_ta.custom import create_dir, import_dir
|
||||
|
||||
# Not ideal and not dynamic but it works.
|
||||
# Will find a dynamic solution later.
|
||||
Category = {
|
||||
# Candles
|
||||
"candles": [
|
||||
"cdl_pattern", "cdl_z", "ha"
|
||||
],
|
||||
# Cycles
|
||||
"cycles": ["ebsw", "reflex"],
|
||||
# Momentum
|
||||
"momentum": [
|
||||
"ao", "apo", "bias", "bop", "brar", "cci", "cfo", "cg", "cmo",
|
||||
"coppock", "cti", "er", "eri", "fisher", "inertia", "kdj", "kst", "macd",
|
||||
"mom", "pgo", "ppo", "psl", "pvo", "qqe", "roc", "rsi", "rsx", "rvgi",
|
||||
"slope", "smi", "squeeze", "squeeze_pro", "stc", "stoch", "stochf",
|
||||
"stochrsi", "td_seq", "trix", "tsi", "uo", "willr"
|
||||
],
|
||||
# Overlap
|
||||
"overlap": [
|
||||
"alligator", "alma", "dema", "ema", "fwma", "hilo", "hl2", "hlc3",
|
||||
"hma", "hwma", "ichimoku", "jma", "kama", "linreg", "mcgd", "midpoint",
|
||||
"midprice", "ohlc4", "pwma", "rma", "sinwma", "sma", "smma", "ssf",
|
||||
"ssf3", "supertrend", "swma", "t3", "tema", "trima", "vidya", "vwap",
|
||||
"vwma", "wcp", "wma", "zlma"
|
||||
],
|
||||
# Performance
|
||||
"performance": ["log_return", "percent_return"],
|
||||
# Statistics
|
||||
"statistics": [
|
||||
"entropy", "kurtosis", "mad", "median", "quantile", "skew", "stdev",
|
||||
"tos_stdevall", "variance", "zscore"
|
||||
],
|
||||
# Trend
|
||||
"trend": [
|
||||
"adx", "amat", "aroon", "chop", "cksp", "decay", "decreasing", "dpo",
|
||||
"increasing", "long_run", "psar", "qstick", "short_run", "trendflex", "tsignals",
|
||||
"ttm_trend", "vhf", "vortex", "xsignals"
|
||||
],
|
||||
# Volatility
|
||||
"volatility": [
|
||||
"aberration", "accbands", "atr", "bbands", "donchian", "hwc", "kc", "massi",
|
||||
"natr", "pdist", "rvi", "thermo", "true_range", "ui"
|
||||
],
|
||||
|
||||
# Volume.
|
||||
# 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", "pvol", "pvr", "pvt", "wb_tsv"
|
||||
],
|
||||
}
|
||||
|
||||
CANGLE_AGG = {
|
||||
"open": "first",
|
||||
"high": "max",
|
||||
"low": "min",
|
||||
"close": "last",
|
||||
"volume": "sum"
|
||||
}
|
||||
|
||||
# https://www.worldtimezone.com/markets24.php
|
||||
EXCHANGE_TZ = {
|
||||
"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 = {
|
||||
"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,
|
||||
}
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from pandas_ta.core import *
|
||||
# Empty DataFrame Alias. Example:
|
||||
# >> ta.df.ta.ticker("spy")
|
||||
df = DataFrame()
|
||||
@@ -1,12 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import Series
|
||||
from pandas_ta.overlap import sma
|
||||
from pandas_ta.utils import get_offset, high_low_range, is_percent
|
||||
from pandas_ta.utils import real_body, verify_series
|
||||
from pandas import Series
|
||||
|
||||
|
||||
def cdl_doji(open_: Series, high: Series, low: Series, close: Series, length: int = None, factor: float = None,
|
||||
scalar: float = None, asint: bool = True, offset: int = None, **kwargs) -> Series:
|
||||
def cdl_doji(
|
||||
open_: Series, high: Series, low: Series, close: Series,
|
||||
length: int = None, factor: float = None, scalar: float = None,
|
||||
asint: bool = True,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Candle Type: Doji
|
||||
|
||||
A candle body is Doji, when it's shorter than 10% of the
|
||||
@@ -35,7 +39,7 @@ def cdl_doji(open_: Series, high: Series, low: Series, close: Series, length: in
|
||||
Returns:
|
||||
pd.Series: CDL_DOJI column.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
factor = float(factor) if is_percent(factor) else 10
|
||||
scalar = float(scalar) if scalar else 100
|
||||
@@ -48,7 +52,7 @@ def cdl_doji(open_: Series, high: Series, low: Series, close: Series, length: in
|
||||
|
||||
if open_ is None or high is None or low is None or close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
body = real_body(open_, close).abs()
|
||||
hl_range = high_low_range(high, low).abs()
|
||||
hl_range_avg = sma(hl_range, length)
|
||||
@@ -63,13 +67,13 @@ def cdl_doji(open_: Series, high: Series, low: Series, close: Series, length: in
|
||||
if offset != 0:
|
||||
doji = doji.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
doji.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
doji.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
doji.name = f"CDL_DOJI_{length}_{0.01 * factor}"
|
||||
doji.category = "candles"
|
||||
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import candle_color, get_offset
|
||||
from pandas_ta.utils import verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import candle_color, get_offset, verify_series
|
||||
|
||||
|
||||
def cdl_inside(open_: Series, high: Series, low: Series, close: Series, asbool: bool = False,
|
||||
offset: int = None, **kwargs) -> Series:
|
||||
def cdl_inside(
|
||||
open_: Series, high: Series, low: Series, close: Series,
|
||||
asbool: bool = False,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Candle Type: Inside Bar
|
||||
|
||||
An Inside Bar is a bar that is engulfed by the prior highs and lows of it's
|
||||
previous bar. In other words, the current bar is smaller than it's previous bar.
|
||||
previous bar. In other words, the current bar is smaller than it's previous
|
||||
bar.
|
||||
|
||||
Set asbool=True if you want to know if it is an Inside Bar. Note by default
|
||||
asbool=False so this returns a 0 if it is not an Inside Bar, 1 if it is an
|
||||
Inside Bar and close > open, and -1 if it is an Inside Bar but close < open.
|
||||
@@ -32,14 +36,14 @@ def cdl_inside(open_: Series, high: Series, low: Series, close: Series, asbool:
|
||||
Returns:
|
||||
pd.Series: New feature
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
open_ = verify_series(open_)
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
inside = (high.diff() < 0) & (low.diff() > 0)
|
||||
|
||||
if not asbool:
|
||||
@@ -49,13 +53,13 @@ def cdl_inside(open_: Series, high: Series, low: Series, close: Series, asbool:
|
||||
if offset != 0:
|
||||
inside = inside.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
inside.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
inside.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
inside.name = f"CDL_INSIDE"
|
||||
inside.category = "candles"
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from typing import Sequence, Union
|
||||
from pandas import Series, DataFrame
|
||||
|
||||
from . import cdl_doji, cdl_inside
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.candles import cdl_doji, cdl_inside
|
||||
|
||||
|
||||
ALL_PATTERNS = [
|
||||
@@ -24,14 +23,9 @@ ALL_PATTERNS = [
|
||||
|
||||
|
||||
def cdl_pattern(
|
||||
open_: Series,
|
||||
high: Series,
|
||||
low: Series,
|
||||
close: Series,
|
||||
name: Union[str, Sequence[str]] = "all",
|
||||
scalar: float = None,
|
||||
offset: int = None,
|
||||
**kwargs
|
||||
open_: Series, high: Series, low: Series, close: Series,
|
||||
name: Union[str, Sequence[str]] = "all", scalar: float = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""TA Lib Candle Patterns
|
||||
|
||||
@@ -105,7 +99,7 @@ def cdl_pattern(
|
||||
if offset != 0:
|
||||
pattern_result = pattern_result.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
pattern_result.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
@@ -115,7 +109,7 @@ def cdl_pattern(
|
||||
|
||||
if len(result) == 0: return
|
||||
|
||||
# Prepare DataFrame to return
|
||||
# Name and Category
|
||||
df = DataFrame(result)
|
||||
df.name = "CDL_PATTERN"
|
||||
df.category = "candles"
|
||||
|
||||
@@ -4,8 +4,11 @@ from pandas_ta.statistics import zscore
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def cdl_z(open_: Series, high: Series, low: Series, close: Series, length: int = None, full: bool = None,
|
||||
ddof=None, offset: int = None, **kwargs) -> DataFrame:
|
||||
def cdl_z(
|
||||
open_: Series, high: Series, low: Series, close: Series,
|
||||
length: int = None, full: bool = None, ddof=None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Candle Type: Z
|
||||
|
||||
Normalizes OHLC Candles with a rolling Z Score.
|
||||
@@ -29,7 +32,7 @@ def cdl_z(open_: Series, high: Series, low: Series, close: Series, length: int =
|
||||
Returns:
|
||||
pd.Series: CDL_DOJI column.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 30
|
||||
ddof = int(ddof) if ddof and ddof >= 0 and ddof < length else 1
|
||||
open_ = verify_series(open_, length)
|
||||
@@ -41,7 +44,7 @@ def cdl_z(open_: Series, high: Series, low: Series, close: Series, length: int =
|
||||
|
||||
if open_ is None or high is None or low is None or close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if full:
|
||||
length = close.size
|
||||
|
||||
@@ -66,13 +69,13 @@ def cdl_z(open_: Series, high: Series, low: Series, close: Series, length: int =
|
||||
if offset != 0:
|
||||
df = df.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
df.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
df.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
df.name = f"CDL_Z{_props}"
|
||||
df.category = "candles"
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@ from pandas import DataFrame, Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def ha(open_: Series, high: Series, low: Series, close: Series, offset: int = None, **kwargs) -> DataFrame:
|
||||
def ha(
|
||||
open_: Series, high: Series, low: Series, close: Series,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Heikin Ashi Candles (HA)
|
||||
|
||||
The Heikin-Ashi technique averages price data to create a Japanese
|
||||
@@ -32,14 +35,14 @@ def ha(open_: Series, high: Series, low: Series, close: Series, offset: int = No
|
||||
Returns:
|
||||
pd.DataFrame: ha_open, ha_high,ha_low, ha_close columns.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
open_ = verify_series(open_)
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
m = close.size
|
||||
df = DataFrame({
|
||||
"HA_open": 0.5 * (open_.iloc[0] + close.iloc[0]),
|
||||
@@ -58,13 +61,13 @@ def ha(open_: Series, high: Series, low: Series, close: Series, offset: int = No
|
||||
if offset != 0:
|
||||
df = df.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
df.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
df.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
df.name = "Heikin-Ashi"
|
||||
df.category = "candles"
|
||||
|
||||
|
||||
@@ -595,7 +595,6 @@ class AnalysisIndicators(BasePandasObject):
|
||||
# "data", # reserved
|
||||
"long_run",
|
||||
"short_run",
|
||||
"td_seq", # Performance exclusion
|
||||
"tsignals",
|
||||
"xsignals",
|
||||
]
|
||||
|
||||
+76
-81
@@ -8,10 +8,9 @@ from os.path import abspath, join, exists, basename, splitext
|
||||
from glob import glob
|
||||
|
||||
import pandas_ta
|
||||
from pandas_ta import AnalysisIndicators
|
||||
|
||||
|
||||
def bind(function_name: str, function: types.FunctionType, method: types.MethodType):
|
||||
def bind(name: str, f: types.FunctionType):#, method: types.MethodType = None):
|
||||
"""
|
||||
Helper function to bind the function and class method defined in a custom
|
||||
indicator module to the active pandas_ta instance.
|
||||
@@ -21,8 +20,8 @@ def bind(function_name: str, function: types.FunctionType, method: types.MethodT
|
||||
function (fcn): The indicator function
|
||||
method (fcn): The class method corresponding to the passed function
|
||||
"""
|
||||
setattr(pandas_ta, function_name, function)
|
||||
setattr(AnalysisIndicators, function_name, method)
|
||||
setattr(pandas_ta, name, f)
|
||||
setattr(pandas_ta.AnalysisIndicators, name, f)
|
||||
|
||||
|
||||
def create_dir(path: str, create_categories: bool = True, verbose: bool = True):
|
||||
@@ -81,6 +80,74 @@ def get_module_functions(module: types.ModuleType) -> dict:
|
||||
|
||||
|
||||
def import_dir(path: str, verbose: bool = True):
|
||||
"""
|
||||
Import a directory of custom indicators into pandas_ta
|
||||
|
||||
Args:
|
||||
path (str): Full path to your indicator tree
|
||||
verbose (bool): If True verbose output of results
|
||||
|
||||
This method allows you to experiment and develop your own technical analysis
|
||||
indicators in a separate local directory of your choice but use them seamlessly
|
||||
together with the existing pandas_ta functions just like if they were part of
|
||||
pandas_ta.
|
||||
|
||||
If you at some late point would like to push them into the pandas_ta library
|
||||
you can do so very easily by following the step by step instruction here
|
||||
https://github.com/twopirllc/pandas-ta/issues/355.
|
||||
|
||||
A brief example of usage:
|
||||
|
||||
1. Loading the 'ta' module:
|
||||
>>> import pandas as pd
|
||||
>>> import pandas_ta as ta
|
||||
|
||||
2. Create an empty directory on your machine where you want to work with your
|
||||
indicators. Invoke pandas_ta.custom.import_dir once to pre-populate it with
|
||||
sub-folders for all available indicator categories, e.g.:
|
||||
|
||||
>>> import os
|
||||
>>> from os.path import abspath, join, expanduser
|
||||
>>> from pandas_ta.custom import create_dir, import_dir
|
||||
>>> ta_dir = abspath(join(expanduser("~"), "my_indicators"))
|
||||
>>> create_dir(ta_dir)
|
||||
|
||||
3. You can now create your own custom indicator e.g. by copying existing
|
||||
ones from pandas_ta core module and modifying them.
|
||||
|
||||
IMPORTANT: Each custom indicator should have a unique name and have both
|
||||
a) a function named exactly as the module, e.g. 'ni' if the module is ni.py
|
||||
b) a matching method used by AnalysisIndicators named as the module but
|
||||
ending with '_method'. E.g. 'ni_method'
|
||||
|
||||
In essence these modules should look exactly like the standard indicators
|
||||
available in categories under the pandas_ta-folder. The only difference will
|
||||
be an addition of a matching class method.
|
||||
|
||||
For an example of the correct structure, look at the example ni.py in the
|
||||
examples folder.
|
||||
|
||||
The ni.py indicator is a trend indicator so therefore we drop it into the
|
||||
sub-folder named trend. Thus we have a folder structure like this:
|
||||
|
||||
~/my_indicators/
|
||||
│
|
||||
├── candles/
|
||||
.
|
||||
.
|
||||
└── trend/
|
||||
. └── ni.py
|
||||
.
|
||||
└── volume/
|
||||
|
||||
4. We can now dynamically load all our custom indicators located in our
|
||||
designated indicators directory like this:
|
||||
|
||||
>>> import_dir(ta_dir)
|
||||
|
||||
If your custom indicator(s) loaded succesfully then it should behave exactly
|
||||
like all other native indicators in pandas_ta, including help functions.
|
||||
"""
|
||||
# ensure that the passed directory exists / is readable
|
||||
if not exists(path):
|
||||
print(f"[X] Unable to read the directory '{path}'.")
|
||||
@@ -111,13 +178,13 @@ def import_dir(path: str, verbose: bool = True):
|
||||
module_functions = load_indicator_module(module_name)
|
||||
|
||||
# figure out which of the modules functions to bind to pandas_ta
|
||||
fcn_callable = module_functions.get(module_name, None)
|
||||
fcn_method_callable = module_functions.get(f"{module_name}_method", None)
|
||||
_callable = module_functions.get(module_name, None)
|
||||
_method_callable = module_functions.get(f"{module_name}_method", None)
|
||||
|
||||
if fcn_callable == None:
|
||||
if _callable == None:
|
||||
print(f"[X] Unable to find a function named '{module_name}' in the module '{module_name}.py'.")
|
||||
continue
|
||||
if fcn_method_callable == None:
|
||||
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
|
||||
@@ -126,82 +193,11 @@ def import_dir(path: str, verbose: bool = True):
|
||||
if module_name not in pandas_ta.Category[dirname]:
|
||||
pandas_ta.Category[dirname].append(module_name)
|
||||
|
||||
bind(module_name, fcn_callable, fcn_method_callable)
|
||||
bind(module_name, _callable, _method_callable)
|
||||
if verbose:
|
||||
print(f"[i] Successfully imported the custom indicator '{module}' into category '{dirname}'.")
|
||||
|
||||
|
||||
import_dir.__doc__ = \
|
||||
"""
|
||||
Import a directory of custom indicators into pandas_ta
|
||||
|
||||
Args:
|
||||
path (str): Full path to your indicator tree
|
||||
verbose (bool): If True verbose output of results
|
||||
|
||||
This method allows you to experiment and develop your own technical analysis
|
||||
indicators in a separate local directory of your choice but use them seamlessly
|
||||
together with the existing pandas_ta functions just like if they were part of
|
||||
pandas_ta.
|
||||
|
||||
If you at some late point would like to push them into the pandas_ta library
|
||||
you can do so very easily by following the step by step instruction here
|
||||
https://github.com/twopirllc/pandas-ta/issues/355.
|
||||
|
||||
A brief example of usage:
|
||||
|
||||
1. Loading the 'ta' module:
|
||||
>>> import pandas as pd
|
||||
>>> import pandas_ta as ta
|
||||
|
||||
2. Create an empty directory on your machine where you want to work with your
|
||||
indicators. Invoke pandas_ta.custom.import_dir once to pre-populate it with
|
||||
sub-folders for all available indicator categories, e.g.:
|
||||
|
||||
>>> import os
|
||||
>>> from os.path import abspath, join, expanduser
|
||||
>>> from pandas_ta.custom import create_dir, import_dir
|
||||
>>> ta_dir = abspath(join(expanduser("~"), "my_indicators"))
|
||||
>>> create_dir(ta_dir)
|
||||
|
||||
3. You can now create your own custom indicator e.g. by copying existing
|
||||
ones from pandas_ta core module and modifying them.
|
||||
|
||||
IMPORTANT: Each custom indicator should have a unique name and have both
|
||||
a) a function named exactly as the module, e.g. 'ni' if the module is ni.py
|
||||
b) a matching method used by AnalysisIndicators named as the module but
|
||||
ending with '_method'. E.g. 'ni_method'
|
||||
|
||||
In essence these modules should look exactly like the standard indicators
|
||||
available in categories under the pandas_ta-folder. The only difference will
|
||||
be an addition of a matching class method.
|
||||
|
||||
For an example of the correct structure, look at the example ni.py in the
|
||||
examples folder.
|
||||
|
||||
The ni.py indicator is a trend indicator so therefore we drop it into the
|
||||
sub-folder named trend. Thus we have a folder structure like this:
|
||||
|
||||
~/my_indicators/
|
||||
│
|
||||
├── candles/
|
||||
.
|
||||
.
|
||||
└── trend/
|
||||
. └── ni.py
|
||||
.
|
||||
└── volume/
|
||||
|
||||
4. We can now dynamically load all our custom indicators located in our
|
||||
designated indicators directory like this:
|
||||
|
||||
>>> import_dir(ta_dir)
|
||||
|
||||
If your custom indicator(s) loaded succesfully then it should behave exactly
|
||||
like all other native indicators in pandas_ta, including help functions.
|
||||
"""
|
||||
|
||||
|
||||
def load_indicator_module(name: str) -> dict:
|
||||
"""
|
||||
Helper function to (re)load an indicator module.
|
||||
@@ -214,7 +210,6 @@ def load_indicator_module(name: str) -> dict:
|
||||
}
|
||||
|
||||
"""
|
||||
# load module
|
||||
try:
|
||||
module = importlib.import_module(name)
|
||||
except Exception as ex:
|
||||
|
||||
+33
-30
@@ -1,10 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta import np, pd
|
||||
from numpy import cos, exp, mean, nan, pi, roll, sin, sqrt, zeros
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def ebsw(close: Series, length: int = None, bars: int = None, offset: int = None, initial_version: bool = False,
|
||||
**kwargs) -> Series:
|
||||
def ebsw(
|
||||
close: Series, length: int = None, bars: int = None,
|
||||
initial_version: bool = False,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Even Better SineWave (EBSW)
|
||||
|
||||
This indicator measures market cycles and uses a low pass filter to remove noise.
|
||||
@@ -23,7 +27,6 @@ def ebsw(close: Series, length: int = None, bars: int = None, offset: int = None
|
||||
faster, than the corresponding reference value. This might be pre-roll related and was not further investigated.
|
||||
* https://github.com/twopirllc/pandas-ta/issues/350
|
||||
|
||||
|
||||
Sources:
|
||||
- https://www.prorealcode.com/prorealtime-indicators/even-better-sinewave/
|
||||
- J.F.Ehlers 'Cycle Analytics for Traders', 2014
|
||||
@@ -43,7 +46,7 @@ def ebsw(close: Series, length: int = None, bars: int = None, offset: int = None
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if isinstance(length, int) and length > 10 else 40
|
||||
bars = int(bars) if isinstance(bars, int) and bars > 0 else 10
|
||||
close = verify_series(close, length)
|
||||
@@ -51,9 +54,12 @@ def ebsw(close: Series, length: int = None, bars: int = None, offset: int = None
|
||||
|
||||
if close is None: return
|
||||
|
||||
# allow initial version to be used (more responsive/caution!)
|
||||
initial_version = bool(initial_version) if isinstance(initial_version, bool) else False
|
||||
|
||||
# Calculate
|
||||
# allow initial version to be used (more responsive/caution!)
|
||||
m = close.size
|
||||
|
||||
initial_version = bool(initial_version) if isinstance(initial_version, bool) else False
|
||||
if initial_version:
|
||||
# not the default version that is active
|
||||
alpha1 = hp = 0 # alpha and HighPass
|
||||
@@ -62,17 +68,15 @@ def ebsw(close: Series, length: int = None, bars: int = None, offset: int = None
|
||||
lastClose = lastHP = 0
|
||||
filtHist = [0, 0] # Filter history
|
||||
|
||||
# Calculate Result
|
||||
m = close.size
|
||||
result = [np.nan for _ in range(0, length - 1)] + [0]
|
||||
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 - np.sin(360 / length)) / np.cos(360 / length)
|
||||
alpha1 = (1 - sin(360 / length)) / cos(360 / length)
|
||||
hp = 0.5 * (1 + alpha1) * (close[i] - lastClose) + alpha1 * lastHP
|
||||
|
||||
# Smooth with a Super Smoother Filter from equation 3-3
|
||||
a1 = np.exp(-np.sqrt(2) * np.pi / bars)
|
||||
b1 = 2 * a1 * np.cos(np.sqrt(2) * 180 / bars)
|
||||
a1 = exp(-sqrt(2) * pi / bars)
|
||||
b1 = 2 * a1 * cos(sqrt(2) * 180 / bars)
|
||||
c2 = b1
|
||||
c3 = -1 * a1 * a1
|
||||
c1 = 1 - c2 - c3
|
||||
@@ -93,31 +97,30 @@ def ebsw(close: Series, length: int = None, bars: int = None, offset: int = None
|
||||
lastClose = close[i]
|
||||
result.append(wave)
|
||||
|
||||
else: # this version is the default version
|
||||
# Instance Variables
|
||||
else: # this version is the default version
|
||||
# Calculate
|
||||
lastHP = lastClose = 0
|
||||
filtHist = np.zeros(3)
|
||||
result = [np.nan] * (length - 1) + [0]
|
||||
filtHist = zeros(3)
|
||||
result = [nan] * (length - 1) + [0]
|
||||
|
||||
# Calculate constants
|
||||
angle = 2 * np.pi / length
|
||||
alpha1 = (1 - np.sin(angle)) / np.cos(angle)
|
||||
ang = 2 ** .5 * np.pi / bars
|
||||
a1 = np.exp(-ang)
|
||||
c2 = 2 * a1 * np.cos(ang)
|
||||
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, close.size):
|
||||
for i in range(length, m):
|
||||
hp = 0.5 * (1 + alpha1) * (close[i] - lastClose) + alpha1 * lastHP
|
||||
|
||||
# Rotate filters to overwrite oldest value
|
||||
filtHist = np.roll(filtHist, -1)
|
||||
filtHist = roll(filtHist, -1)
|
||||
filtHist[-1] = 0.5 * c1 * (hp + lastHP) + c2 * filtHist[1] + c3 * filtHist[0]
|
||||
|
||||
# Wave calculation
|
||||
wave = np.mean(filtHist)
|
||||
rms = np.sqrt(np.mean(filtHist ** 2))
|
||||
wave = mean(filtHist)
|
||||
rms = sqrt(mean(filtHist ** 2))
|
||||
wave = wave / rms
|
||||
|
||||
# Update past values
|
||||
@@ -125,19 +128,19 @@ def ebsw(close: Series, length: int = None, bars: int = None, offset: int = None
|
||||
lastClose = close[i]
|
||||
result.append(wave)
|
||||
|
||||
ebsw = pd.Series(result, index=close.index)
|
||||
ebsw = Series(result, index=close.index)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
ebsw = ebsw.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
ebsw.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
ebsw.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
ebsw.name = f"EBSW_{length}_{bars}"
|
||||
ebsw.category = "cycles"
|
||||
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import Series
|
||||
|
||||
from .dema import dema
|
||||
from .ema import ema
|
||||
from .fwma import fwma
|
||||
from .hma import hma
|
||||
from .linreg import linreg
|
||||
from .midpoint import midpoint
|
||||
from .pwma import pwma
|
||||
from .rma import rma
|
||||
from .sinwma import sinwma
|
||||
from .sma import sma
|
||||
from .swma import swma
|
||||
from .t3 import t3
|
||||
from .tema import tema
|
||||
from .trima import trima
|
||||
from .vidya import vidya
|
||||
from .wma import wma
|
||||
from pandas_ta.overlap.dema import dema
|
||||
from pandas_ta.overlap.ema import ema
|
||||
from pandas_ta.overlap.fwma import fwma
|
||||
from pandas_ta.overlap.hma import hma
|
||||
from pandas_ta.overlap.linreg import linreg
|
||||
from pandas_ta.overlap.midpoint import midpoint
|
||||
from pandas_ta.overlap.pwma import pwma
|
||||
from pandas_ta.overlap.rma import rma
|
||||
from pandas_ta.overlap.sinwma import sinwma
|
||||
from pandas_ta.overlap.sma import sma
|
||||
from pandas_ta.overlap.ssf import ssf
|
||||
from pandas_ta.overlap.swma import swma
|
||||
from pandas_ta.overlap.t3 import t3
|
||||
from pandas_ta.overlap.tema import tema
|
||||
from pandas_ta.overlap.trima import trima
|
||||
from pandas_ta.overlap.vidya import vidya
|
||||
from pandas_ta.overlap.wma import wma
|
||||
|
||||
|
||||
def ma(name: str = None, source: Series = None, **kwargs) -> Series:
|
||||
"""Simple MA Utility for easier MA selection
|
||||
|
||||
Available MAs:
|
||||
dema, ema, fwma, hma, linreg, midpoint, pwma, rma,
|
||||
sinwma, sma, swma, t3, tema, trima, vidya, wma
|
||||
dema, ema, fwma, hma, linreg, midpoint, pwma, rma, sinwma, sma, ssf,
|
||||
swma, t3, tema, trima, vidya, wma
|
||||
|
||||
Examples:
|
||||
ema8 = ta.ma("ema", df.close, length=8)
|
||||
@@ -44,7 +44,7 @@ def ma(name: str = None, source: Series = None, **kwargs) -> Series:
|
||||
|
||||
_mas = [
|
||||
"dema", "ema", "fwma", "hma", "linreg", "midpoint", "pwma", "rma",
|
||||
"sinwma", "sma", "swma", "t3", "tema", "trima", "vidya", "wma"
|
||||
"sinwma", "sma", "ssf", "swma", "t3", "tema", "trima", "vidya", "wma"
|
||||
]
|
||||
if name is None and source is None:
|
||||
return _mas
|
||||
@@ -62,6 +62,7 @@ def ma(name: str = None, source: Series = None, **kwargs) -> Series:
|
||||
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)
|
||||
@@ -0,0 +1,115 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from importlib.util import find_spec
|
||||
from pathlib import Path
|
||||
from pkg_resources import get_distribution, DistributionNotFound
|
||||
|
||||
|
||||
_dist = get_distribution("pandas_ta")
|
||||
try:
|
||||
# Normalize case for Windows systems
|
||||
_here = Path(_dist.location) / __file__
|
||||
if not _here.exists():
|
||||
# not installed, but there is another version that *is*
|
||||
raise DistributionNotFound
|
||||
except DistributionNotFound:
|
||||
__version__ = "Please install this project with setup.py"
|
||||
|
||||
version = __version__ = _dist.version
|
||||
|
||||
Imports = {
|
||||
"alphaVantage-api": find_spec("alphaVantageAPI") is not None,
|
||||
"matplotlib": find_spec("matplotlib") is not None,
|
||||
"mplfinance": find_spec("mplfinance") is not None,
|
||||
"numba": find_spec("numba") is not None,
|
||||
"yaml": find_spec("yaml") is not None,
|
||||
"scipy": find_spec("scipy") is not None,
|
||||
"sklearn": find_spec("sklearn") is not None,
|
||||
"statsmodels": find_spec("statsmodels") is not None,
|
||||
"stochastic": find_spec("stochastic") is not None,
|
||||
"talib": find_spec("talib") is not None,
|
||||
"tqdm": find_spec("tqdm") is not None,
|
||||
"vectorbt": find_spec("vectorbt") is not None,
|
||||
"yfinance": find_spec("yfinance") is not None,
|
||||
"polygon": find_spec("polygon") is not None,
|
||||
}
|
||||
|
||||
# Not ideal and not dynamic but it works.
|
||||
# Will find a dynamic solution later.
|
||||
Category = {
|
||||
# Candles
|
||||
"candles": [
|
||||
"cdl_pattern", "cdl_z", "ha"
|
||||
],
|
||||
# Cycles
|
||||
"cycles": ["ebsw", "reflex"],
|
||||
# Momentum
|
||||
"momentum": [
|
||||
"ao", "apo", "bias", "bop", "brar", "cci", "cfo", "cg", "cmo",
|
||||
"coppock", "cti", "er", "eri", "fisher", "inertia", "kdj", "kst", "macd",
|
||||
"mom", "pgo", "ppo", "psl", "pvo", "qqe", "roc", "rsi", "rsx", "rvgi",
|
||||
"slope", "smi", "squeeze", "squeeze_pro", "stc", "stoch", "stochf",
|
||||
"stochrsi", "td_seq", "trix", "tsi", "uo", "willr"
|
||||
],
|
||||
# Overlap
|
||||
"overlap": [
|
||||
"alligator", "alma", "dema", "ema", "fwma", "hilo", "hl2", "hlc3",
|
||||
"hma", "hwma", "ichimoku", "jma", "kama", "linreg", "mcgd", "midpoint",
|
||||
"midprice", "ohlc4", "pwma", "rma", "sinwma", "sma", "smma", "ssf",
|
||||
"ssf3", "supertrend", "swma", "t3", "tema", "trima", "vidya", "vwap",
|
||||
"vwma", "wcp", "wma", "zlma"
|
||||
],
|
||||
# Performance
|
||||
"performance": ["log_return", "percent_return"],
|
||||
# Statistics
|
||||
"statistics": [
|
||||
"entropy", "kurtosis", "mad", "median", "quantile", "skew", "stdev",
|
||||
"tos_stdevall", "variance", "zscore"
|
||||
],
|
||||
# Trend
|
||||
"trend": [
|
||||
"adx", "amat", "aroon", "chop", "cksp", "decay", "decreasing", "dpo",
|
||||
"increasing", "long_run", "psar", "qstick", "short_run", "trendflex", "tsignals",
|
||||
"ttm_trend", "vhf", "vortex", "xsignals"
|
||||
],
|
||||
# Volatility
|
||||
"volatility": [
|
||||
"aberration", "accbands", "atr", "bbands", "donchian", "hwc", "kc", "massi",
|
||||
"natr", "pdist", "rvi", "thermo", "true_range", "ui"
|
||||
],
|
||||
|
||||
# Volume.
|
||||
# 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", "pvol", "pvr", "pvt", "wb_tsv"
|
||||
],
|
||||
}
|
||||
|
||||
CANDLE_AGG = {
|
||||
"open": "first",
|
||||
"high": "max",
|
||||
"low": "min",
|
||||
"close": "last",
|
||||
"volume": "sum"
|
||||
}
|
||||
|
||||
# https://www.worldtimezone.com/markets24.php
|
||||
EXCHANGE_TZ = {
|
||||
"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 = {
|
||||
"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,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import Series
|
||||
from pandas_ta.overlap import sma
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
|
||||
|
||||
def ao(high: Series, low: Series, fast: int = None, slow: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def ao(
|
||||
high: Series, low: Series, fast: int = None, slow: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Awesome Oscillator (AO)
|
||||
|
||||
The Awesome Oscillator is an indicator used to measure a security's momentum.
|
||||
@@ -28,7 +31,7 @@ def ao(high: Series, low: Series, fast: int = None, slow: int = None, offset: in
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
fast = int(fast) if fast and fast > 0 else 5
|
||||
slow = int(slow) if slow and slow > 0 else 34
|
||||
if slow < fast:
|
||||
@@ -40,7 +43,7 @@ def ao(high: Series, low: Series, fast: int = None, slow: int = None, offset: in
|
||||
|
||||
if high is None or low is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
median_price = 0.5 * (high + low)
|
||||
fast_sma = sma(median_price, fast)
|
||||
slow_sma = sma(median_price, slow)
|
||||
@@ -50,13 +53,13 @@ def ao(high: Series, low: Series, fast: int = None, slow: int = None, offset: in
|
||||
if offset != 0:
|
||||
ao = ao.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
ao.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
ao.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
ao.name = f"AO_{fast}_{slow}"
|
||||
ao.category = "momentum"
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.overlap import ma
|
||||
from pandas_ta.utils import get_offset, tal_ma, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.ma import ma
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, tal_ma, verify_series
|
||||
|
||||
|
||||
def apo(close: Series, fast: int = None, slow: int = None, mamode: str = None, talib: bool = None,
|
||||
offset: int = None, **kwargs) -> Series:
|
||||
def apo(
|
||||
close: Series, fast: int = None, slow: int = None,
|
||||
mamode: str = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Absolute Price Oscillator (APO)
|
||||
|
||||
The Absolute Price Oscillator is an indicator used to measure a security's
|
||||
@@ -32,7 +35,7 @@ def apo(close: Series, fast: int = None, slow: int = None, mamode: str = None, t
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
fast = int(fast) if fast and fast > 0 else 12
|
||||
slow = int(slow) if slow and slow > 0 else 26
|
||||
if slow < fast:
|
||||
@@ -44,7 +47,7 @@ def apo(close: Series, fast: int = None, slow: int = None, mamode: str = None, t
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import APO
|
||||
apo = APO(close, fast, slow, tal_ma(mamode))
|
||||
@@ -57,13 +60,13 @@ def apo(close: Series, fast: int = None, slow: int = None, mamode: str = None, t
|
||||
if offset != 0:
|
||||
apo = apo.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
apo.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
apo.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
apo.name = f"APO_{fast}_{slow}"
|
||||
apo.category = "momentum"
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.overlap import ma
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.ma import ma
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def bias(close: Series, length: int = None, mamode: str = None, offset: int = None, **kwargs) -> Series:
|
||||
def bias(
|
||||
close: Series, length: int = None, mamode: str = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Bias (BIAS)
|
||||
|
||||
Rate of change between the source and a moving average.
|
||||
@@ -17,7 +20,6 @@ def bias(close: Series, length: int = None, mamode: str = None, offset: int = No
|
||||
close (pd.Series): Series of 'close's
|
||||
length (int): The period. Default: 26
|
||||
mamode (str): See ```help(ta.ma)```. Default: 'sma'
|
||||
drift (int): The short period. Default: 1
|
||||
offset (int): How many periods to offset the result. Default: 0
|
||||
|
||||
Kwargs:
|
||||
@@ -27,7 +29,7 @@ def bias(close: Series, length: int = None, mamode: str = None, offset: int = No
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 26
|
||||
mamode = mamode if isinstance(mamode, str) else "sma"
|
||||
close = verify_series(close, length)
|
||||
@@ -35,7 +37,7 @@ def bias(close: Series, length: int = None, mamode: str = None, offset: int = No
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
bma = ma(mamode, close, length=length, **kwargs)
|
||||
bias = (close / bma) - 1
|
||||
|
||||
@@ -43,13 +45,13 @@ def bias(close: Series, length: int = None, mamode: str = None, offset: int = No
|
||||
if offset != 0:
|
||||
bias = bias.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
bias.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
bias.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
bias.name = f"BIAS_{bma.name}"
|
||||
bias.category = "momentum"
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, non_zero_range, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, non_zero_range, verify_series
|
||||
|
||||
|
||||
def bop(open_: Series, high: Series, low: Series, close: Series, scalar: float = None, talib: bool = None,
|
||||
offset: int = None, **kwargs) -> Series:
|
||||
def bop(
|
||||
open_: Series, high: Series, low: Series, close: Series,
|
||||
scalar: float = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Balance of Power (BOP)
|
||||
|
||||
Balance of Power measure the market strength of buyers against sellers.
|
||||
@@ -30,7 +33,7 @@ def bop(open_: Series, high: Series, low: Series, close: Series, scalar: float =
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
open_ = verify_series(open_)
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
@@ -39,7 +42,7 @@ def bop(open_: Series, high: Series, low: Series, close: Series, scalar: float =
|
||||
offset = get_offset(offset)
|
||||
mode_tal = bool(talib) if isinstance(talib, bool) else True
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import BOP
|
||||
bop = BOP(open_, high, low, close)
|
||||
@@ -52,13 +55,13 @@ def bop(open_: Series, high: Series, low: Series, close: Series, scalar: float =
|
||||
if offset != 0:
|
||||
bop = bop.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
bop.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
bop.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
bop.name = f"BOP"
|
||||
bop.category = "momentum"
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@ from pandas import DataFrame, Series
|
||||
from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series
|
||||
|
||||
|
||||
def brar(open_: Series, high: Series, low: Series, close: Series, length: int = None, scalar: float = None,
|
||||
drift: int = None, offset: int = None, **kwargs) -> DataFrame:
|
||||
def brar(
|
||||
open_: Series, high: Series, low: Series, close: Series,
|
||||
length: int = None, scalar: float = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""BRAR (BRAR)
|
||||
|
||||
BR and AR
|
||||
@@ -30,7 +33,7 @@ def brar(open_: Series, high: Series, low: Series, close: Series, length: int =
|
||||
Returns:
|
||||
pd.DataFrame: ar, br columns.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 26
|
||||
scalar = float(scalar) if scalar else 100
|
||||
high_open_range = non_zero_range(high, open_)
|
||||
@@ -44,7 +47,7 @@ def brar(open_: Series, high: Series, low: Series, close: Series, length: int =
|
||||
|
||||
if open_ is None or high is None or low is None or close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
hcy = non_zero_range(high, close.shift(drift))
|
||||
cyl = non_zero_range(close.shift(drift), low)
|
||||
|
||||
@@ -62,7 +65,7 @@ def brar(open_: Series, high: Series, low: Series, close: Series, length: int =
|
||||
ar = ar.shift(offset)
|
||||
br = ar.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
ar.fillna(kwargs["fillna"], inplace=True)
|
||||
br.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -70,13 +73,12 @@ def brar(open_: Series, high: Series, low: Series, close: Series, length: int =
|
||||
ar.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
br.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
_props = f"_{length}"
|
||||
ar.name = f"AR{_props}"
|
||||
br.name = f"BR{_props}"
|
||||
ar.category = br.category = "momentum"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
brardf = DataFrame({ar.name: ar, br.name: br})
|
||||
brardf.name = f"BRAR{_props}"
|
||||
brardf.category = "momentum"
|
||||
|
||||
+13
-10
@@ -1,13 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.overlap import hlc3, sma
|
||||
from pandas_ta.statistics.mad import mad
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.overlap import hlc3, sma
|
||||
from pandas_ta.statistics import mad
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def cci(high: Series, low: Series, close: Series, length: int = None, c: float = None,
|
||||
talib: bool = None, offset: int = None, **kwargs) -> Series:
|
||||
def cci(
|
||||
high: Series, low: Series, close: Series, length: int = None,
|
||||
c: float = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Commodity Channel Index (CCI)
|
||||
|
||||
Commodity Channel Index is a momentum oscillator used to primarily identify
|
||||
@@ -33,7 +36,7 @@ def cci(high: Series, low: Series, close: Series, length: int = None, c: float =
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 14
|
||||
c = float(c) if c and c > 0 else 0.015
|
||||
high = verify_series(high, length)
|
||||
@@ -44,7 +47,7 @@ def cci(high: Series, low: Series, close: Series, length: int = None, c: float =
|
||||
|
||||
if high is None or low is None or close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import CCI
|
||||
cci = CCI(high, low, close, length)
|
||||
@@ -60,13 +63,13 @@ def cci(high: Series, low: Series, close: Series, length: int = None, c: float =
|
||||
if offset != 0:
|
||||
cci = cci.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
cci.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
cci.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
cci.name = f"CCI_{length}_{c}"
|
||||
cci.category = "momentum"
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import Series
|
||||
from pandas_ta.overlap import linreg
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series
|
||||
from pandas import Series
|
||||
|
||||
|
||||
def cfo(close: Series, length: int = None, scalar: float = None, drift: int = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def cfo(
|
||||
close: Series, length: int = None,
|
||||
scalar: float = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Chande Forcast Oscillator (CFO)
|
||||
|
||||
The Forecast Oscillator calculates the percentage difference between the actual
|
||||
@@ -28,7 +31,7 @@ def cfo(close: Series, length: int = None, scalar: float = None, drift: int = No
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 9
|
||||
scalar = float(scalar) if scalar else 100
|
||||
close = verify_series(close, length)
|
||||
@@ -37,6 +40,7 @@ def cfo(close: Series, length: int = None, scalar: float = None, drift: int = No
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate
|
||||
# Finding linear regression of Series
|
||||
cfo = scalar * (close - linreg(close, length=length, tsf=True))
|
||||
cfo /= close
|
||||
@@ -45,13 +49,13 @@ def cfo(close: Series, length: int = None, scalar: float = None, drift: int = No
|
||||
if offset != 0:
|
||||
cfo = cfo.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
cfo.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
cfo.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
cfo.name = f"CFO_{length}"
|
||||
cfo.category = "momentum"
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import get_offset, verify_series, weights
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series, weights
|
||||
|
||||
|
||||
def cg(close: Series, length: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def cg(
|
||||
close: Series, length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Center of Gravity (CG)
|
||||
|
||||
The Center of Gravity Indicator by John Ehlers attempts to identify turning
|
||||
@@ -24,14 +27,14 @@ def cg(close: Series, length: int = None, offset: int = None, **kwargs) -> Serie
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
close = verify_series(close, length)
|
||||
offset = get_offset(offset)
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
coefficients = [length - i for i in range(0, length)]
|
||||
numerator = -close.rolling(length).apply(weights(coefficients), raw=True)
|
||||
cg = numerator / close.rolling(length).sum()
|
||||
@@ -40,13 +43,13 @@ def cg(close: Series, length: int = None, offset: int = None, **kwargs) -> Serie
|
||||
if offset != 0:
|
||||
cg = cg.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
cg.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
cg.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
cg.name = f"CG_{length}"
|
||||
cg.category = "momentum"
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta import Imports
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.overlap import rma
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series
|
||||
from pandas import Series
|
||||
|
||||
|
||||
def cmo(close: Series, length: int = None, scalar: float = None, talib: bool = None, drift: int = None,
|
||||
offset: int = None, **kwargs) -> Series:
|
||||
def cmo(
|
||||
close: Series, length: int = None, scalar: float = None,
|
||||
talib: bool = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Chande Momentum Oscillator (CMO)
|
||||
|
||||
Attempts to capture the momentum of an asset with overbought at 50 and
|
||||
@@ -33,7 +36,7 @@ def cmo(close: Series, length: int = None, scalar: float = None, talib: bool = N
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 14
|
||||
scalar = float(scalar) if scalar else 100
|
||||
close = verify_series(close, length)
|
||||
@@ -43,7 +46,7 @@ def cmo(close: Series, length: int = None, scalar: float = None, talib: bool = N
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import CMO
|
||||
cmo = CMO(close, length)
|
||||
@@ -65,13 +68,13 @@ def cmo(close: Series, length: int = None, scalar: float = None, talib: bool = N
|
||||
if offset != 0:
|
||||
cmo = cmo.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
cmo.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
cmo.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
cmo.name = f"CMO_{length}"
|
||||
cmo.category = "momentum"
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .roc import roc
|
||||
from pandas import Series
|
||||
from pandas_ta.overlap import wma
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from .roc import roc
|
||||
|
||||
|
||||
def coppock(close: Series, length: int = None, fast: int = None, slow: int = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def coppock(
|
||||
close: Series, length: int = None, fast: int = None, slow: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Coppock Curve (COPC)
|
||||
|
||||
Coppock Curve (originally called the "Trendex Model") is a momentum indicator
|
||||
@@ -32,7 +34,7 @@ def coppock(close: Series, length: int = None, fast: int = None, slow: int = Non
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
fast = int(fast) if fast and fast > 0 else 11
|
||||
slow = int(slow) if slow and slow > 0 else 14
|
||||
@@ -41,7 +43,7 @@ def coppock(close: Series, length: int = None, fast: int = None, slow: int = Non
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
total_roc = roc(close, fast) + roc(close, slow)
|
||||
coppock = wma(total_roc, length)
|
||||
|
||||
@@ -49,13 +51,13 @@ def coppock(close: Series, length: int = None, fast: int = None, slow: int = Non
|
||||
if offset != 0:
|
||||
coppock = coppock.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
coppock.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
coppock.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
coppock.name = f"COPC_{fast}_{slow}_{length}"
|
||||
coppock.category = "momentum"
|
||||
|
||||
|
||||
@@ -4,7 +4,10 @@ from pandas_ta.overlap import linreg
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def cti(close: Series, length: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def cti(
|
||||
close: Series, length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Correlation Trend Indicator (CTI)
|
||||
|
||||
The Correlation Trend Indicator is an oscillator created by John Ehler in 2020.
|
||||
@@ -24,24 +27,28 @@ def cti(close: Series, length: int = None, offset: int = None, **kwargs) -> Seri
|
||||
Returns:
|
||||
pd.Series: Series of the CTI values for the given period.
|
||||
"""
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 12
|
||||
close = verify_series(close, length)
|
||||
offset = get_offset(offset)
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate
|
||||
cti = linreg(close, length=length, r=True)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
cti = cti.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
cti.fillna(method=kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
cti.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Category
|
||||
cti.name = f"CTI_{length}"
|
||||
cti.category = "momentum"
|
||||
|
||||
return cti
|
||||
|
||||
+19
-12
@@ -1,12 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.overlap import ma
|
||||
from pandas_ta.ma import ma
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series, get_drift, zero
|
||||
|
||||
|
||||
def dm(high: Series, low: Series, length: int = None, mamode: str = None, talib: bool = None, drift: int = None,
|
||||
offset: int = None, **kwargs) -> DataFrame:
|
||||
def dm(
|
||||
high: Series, low: Series, length: int = None,
|
||||
mamode: str = None, talib: bool = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Directional Movement (DM)
|
||||
|
||||
The Directional Movement was developed by J. Welles Wilder in 1978 attempts to
|
||||
@@ -33,7 +36,7 @@ def dm(high: Series, low: Series, length: int = None, mamode: str = None, talib:
|
||||
Returns:
|
||||
pd.DataFrame: DMP (+DM) and DMN (-DM) columns.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 14
|
||||
mamode = mamode.lower() if mamode and isinstance(mamode, str) else "rma"
|
||||
high = verify_series(high)
|
||||
@@ -68,16 +71,20 @@ def dm(high: Series, low: Series, length: int = None, mamode: str = None, talib:
|
||||
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)
|
||||
if "fill_method" in kwargs:
|
||||
pos.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
neg.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Category
|
||||
_params = f"_{length}"
|
||||
data = {
|
||||
f"DMP{_params}": pos,
|
||||
f"DMN{_params}": neg,
|
||||
}
|
||||
data = {f"DMP{_params}": pos, f"DMN{_params}": neg,}
|
||||
|
||||
dmdf = DataFrame(data)
|
||||
# print(dmdf.head(20))
|
||||
# print()
|
||||
dmdf.name = f"DM{_params}"
|
||||
dmdf.category = "trend"
|
||||
dmdf.category = "momentum"
|
||||
|
||||
return dmdf
|
||||
|
||||
@@ -3,7 +3,10 @@ from pandas import DataFrame, concat, Series
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series, signals
|
||||
|
||||
|
||||
def er(close: Series, length: int = None, drift: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def er(
|
||||
close: Series, length: int = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Efficiency Ratio (ER)
|
||||
|
||||
The Efficiency Ratio was invented by Perry J. Kaufman and presented in his book "New Trading Systems and Methods". It is designed to account for market noise or volatility.
|
||||
@@ -25,7 +28,7 @@ def er(close: Series, length: int = None, drift: int = None, offset: int = None,
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
close = verify_series(close, length)
|
||||
offset = get_offset(offset)
|
||||
@@ -33,7 +36,7 @@ def er(close: Series, length: int = None, drift: int = None, offset: int = None,
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
abs_diff = close.diff(length).abs()
|
||||
abs_volatility = close.diff(drift).abs()
|
||||
|
||||
@@ -44,13 +47,13 @@ def er(close: Series, length: int = None, drift: int = None, offset: int = None,
|
||||
if offset != 0:
|
||||
er = er.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
er.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
er.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
er.name = f"ER_{length}"
|
||||
er.category = "momentum"
|
||||
|
||||
|
||||
@@ -4,7 +4,10 @@ from pandas_ta.overlap import ema
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def eri(high: Series, low: Series, close: Series, length: int = None, offset: int = None, **kwargs) -> DataFrame:
|
||||
def eri(
|
||||
high: Series, low: Series, close: Series, length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Elder Ray Index (ERI)
|
||||
|
||||
Elder's Bulls Ray Index contains his Bull and Bear Powers. Which are useful ways
|
||||
@@ -34,7 +37,7 @@ def eri(high: Series, low: Series, close: Series, length: int = None, offset: in
|
||||
Returns:
|
||||
pd.DataFrame: bull power and bear power columns.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 13
|
||||
high = verify_series(high, length)
|
||||
low = verify_series(low, length)
|
||||
@@ -43,7 +46,7 @@ def eri(high: Series, low: Series, close: Series, length: int = None, offset: in
|
||||
|
||||
if high is None or low is None or close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
ema_ = ema(close, length)
|
||||
bull = high - ema_
|
||||
bear = low - ema_
|
||||
@@ -53,7 +56,7 @@ def eri(high: Series, low: Series, close: Series, length: int = None, offset: in
|
||||
bull = bull.shift(offset)
|
||||
bear = bear.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
bull.fillna(kwargs["fillna"], inplace=True)
|
||||
bear.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -61,12 +64,11 @@ def eri(high: Series, low: Series, close: Series, length: int = None, offset: in
|
||||
bull.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
bear.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
bull.name = f"BULLP_{length}"
|
||||
bear.name = f"BEARP_{length}"
|
||||
bull.category = bear.category = "momentum"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {bull.name: bull, bear.name: bear}
|
||||
df = DataFrame(data)
|
||||
df.name = f"ERI_{length}"
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import log as nplog
|
||||
from numpy import nan as npNaN
|
||||
from numpy import log, nan
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta.overlap import hl2
|
||||
from pandas_ta.utils import get_offset, high_low_range, verify_series
|
||||
|
||||
|
||||
def fisher(high: Series, low: Series, length: int = None, signal: int = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def fisher(
|
||||
high: Series, low: Series, length: int = None, signal: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Fisher Transform (FISHT)
|
||||
|
||||
Attempts to identify significant price reversals by normalizing prices over a
|
||||
@@ -31,7 +32,7 @@ def fisher(high: Series, low: Series, length: int = None, signal: int = None, of
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 9
|
||||
signal = int(signal) if signal and signal > 0 else 1
|
||||
_length = max(length, signal)
|
||||
@@ -41,7 +42,7 @@ def fisher(high: Series, low: Series, length: int = None, signal: int = None, of
|
||||
|
||||
if high is None or low is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
hl2_ = hl2(high, low)
|
||||
highest_hl2 = hl2_.rolling(length).max()
|
||||
lowest_hl2 = hl2_.rolling(length).min()
|
||||
@@ -53,12 +54,12 @@ def fisher(high: Series, low: Series, length: int = None, signal: int = None, of
|
||||
|
||||
v = 0
|
||||
m = high.size
|
||||
result = [npNaN for _ in range(0, length - 1)] + [0]
|
||||
result = [nan for _ in range(0, length - 1)] + [0]
|
||||
for i in range(length, m):
|
||||
v = 0.66 * position.iloc[i] + 0.67 * v
|
||||
if v < -0.99: v = -0.999
|
||||
if v > 0.99: v = 0.999
|
||||
result.append(0.5 * (nplog((1 + v) / (1 - v)) + result[i - 1]))
|
||||
result.append(0.5 * (log((1 + v) / (1 - v)) + result[i - 1]))
|
||||
fisher = Series(result, index=high.index)
|
||||
signalma = fisher.shift(signal)
|
||||
|
||||
@@ -67,7 +68,7 @@ def fisher(high: Series, low: Series, length: int = None, signal: int = None, of
|
||||
fisher = fisher.shift(offset)
|
||||
signalma = signalma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
fisher.fillna(kwargs["fillna"], inplace=True)
|
||||
signalma.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -75,13 +76,12 @@ def fisher(high: Series, low: Series, length: int = None, signal: int = None, of
|
||||
fisher.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
signalma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
_props = f"_{length}_{signal}"
|
||||
fisher.name = f"FISHERT{_props}"
|
||||
signalma.name = f"FISHERTs{_props}"
|
||||
fisher.category = signalma.category = "momentum"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {fisher.name: fisher, signalma.name: signalma}
|
||||
df = DataFrame(data)
|
||||
df.name = f"FISHERT{_props}"
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.overlap import linreg
|
||||
from pandas_ta.volatility import rvi
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.overlap import linreg
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series
|
||||
from pandas_ta.volatility import rvi
|
||||
|
||||
|
||||
def inertia(close: Series, high: Series, low: Series, length: int = None, rvi_length: int = None, scalar: float = None,
|
||||
refined: bool = None, thirds: bool = None, mamode: str = None, drift: int = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def inertia(
|
||||
close: Series, high: Series = None, low: Series = None,
|
||||
length: int = None, rvi_length: int = None, scalar: float = None,
|
||||
refined: bool = None, thirds: bool = None,
|
||||
drift: int = None, mamode: str = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Inertia (INERTIA)
|
||||
|
||||
Inertia was developed by Donald Dorsey and was introduced his article
|
||||
@@ -38,7 +42,7 @@ def inertia(close: Series, high: Series, low: Series, length: int = None, rvi_le
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 20
|
||||
rvi_length = int(rvi_length) if rvi_length and rvi_length > 0 else 14
|
||||
scalar = float(scalar) if scalar and scalar > 0 else 100
|
||||
@@ -57,7 +61,7 @@ def inertia(close: Series, high: Series, low: Series, length: int = None, rvi_le
|
||||
low = verify_series(low, _length)
|
||||
if high is None or low is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if refined:
|
||||
_mode, rvi_ = "r", rvi(close, high=high, low=low, length=rvi_length, scalar=scalar, refined=refined, mamode=mamode)
|
||||
elif thirds:
|
||||
@@ -71,13 +75,13 @@ def inertia(close: Series, high: Series, low: Series, length: int = None, rvi_le
|
||||
if offset != 0:
|
||||
inertia = inertia.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
inertia.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
inertia.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
_props = f"_{length}_{rvi_length}"
|
||||
inertia.name = f"INERTIA{_mode}{_props}"
|
||||
inertia.category = "momentum"
|
||||
|
||||
@@ -3,8 +3,11 @@ from pandas import DataFrame, Series
|
||||
from pandas_ta.utils import get_offset, non_zero_range, rma_pandas, verify_series
|
||||
|
||||
|
||||
def kdj(high: Series, low: Series, close: Series, length: int = None, signal: int = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def kdj(
|
||||
high: Series, low: Series, close: Series,
|
||||
length: int = None, signal: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""KDJ (KDJ)
|
||||
|
||||
The KDJ indicator is actually a derived form of the Slow
|
||||
@@ -32,7 +35,7 @@ def kdj(high: Series, low: Series, close: Series, length: int = None, signal: in
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 9
|
||||
signal = int(signal) if signal and signal > 0 else 3
|
||||
_length = max(length, signal)
|
||||
@@ -43,7 +46,7 @@ def kdj(high: Series, low: Series, close: Series, length: int = None, signal: in
|
||||
|
||||
if high is None or low is None or close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
highest_high = high.rolling(length).max()
|
||||
lowest_low = low.rolling(length).min()
|
||||
|
||||
@@ -59,7 +62,7 @@ def kdj(high: Series, low: Series, close: Series, length: int = None, signal: in
|
||||
d = d.shift(offset)
|
||||
j = j.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
k.fillna(kwargs["fillna"], inplace=True)
|
||||
d.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -69,14 +72,13 @@ def kdj(high: Series, low: Series, close: Series, length: int = None, signal: in
|
||||
d.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
j.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
_params = f"_{length}_{signal}"
|
||||
k.name = f"K{_params}"
|
||||
d.name = f"D{_params}"
|
||||
j.name = f"J{_params}"
|
||||
k.category = d.category = j.category = "momentum"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
kdjdf = DataFrame({k.name: k, d.name: d, j.name: j})
|
||||
kdjdf.name = f"KDJ{_params}"
|
||||
kdjdf.category = "momentum"
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame, Series
|
||||
from .roc import roc
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series
|
||||
from .roc import roc
|
||||
|
||||
|
||||
def kst(close: Series, roc1: int = None, roc2: int = None, roc3: int = None, roc4: int = None, sma1: int = None,
|
||||
sma2: int = None, sma3: int = None, sma4: int = None, signal: int = None, drift: int = None,
|
||||
offset: int = None, **kwargs) -> DataFrame:
|
||||
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
|
||||
) -> DataFrame:
|
||||
"""'Know Sure Thing' (KST)
|
||||
|
||||
The 'Know Sure Thing' is a momentum based oscillator and based on ROC.
|
||||
@@ -36,7 +40,7 @@ def kst(close: Series, roc1: int = None, roc2: int = None, roc3: int = None, roc
|
||||
Returns:
|
||||
pd.DataFrame: kst and kst_signal columns
|
||||
"""
|
||||
# Validate arguments
|
||||
# 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
|
||||
@@ -55,7 +59,7 @@ def kst(close: Series, roc1: int = None, roc2: int = None, roc3: int = None, roc
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
rocma1 = roc(close, roc1).rolling(sma1).mean()
|
||||
rocma2 = roc(close, roc2).rolling(sma2).mean()
|
||||
rocma3 = roc(close, roc3).rolling(sma3).mean()
|
||||
@@ -69,7 +73,7 @@ def kst(close: Series, roc1: int = None, roc2: int = None, roc3: int = None, roc
|
||||
kst = kst.shift(offset)
|
||||
kst_signal = kst_signal.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
kst.fillna(kwargs["fillna"], inplace=True)
|
||||
kst_signal.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -77,12 +81,11 @@ def kst(close: Series, roc1: int = None, roc2: int = None, roc3: int = None, roc
|
||||
kst.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
kst_signal.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# 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"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {kst.name: kst, kst_signal.name: kst_signal}
|
||||
kstdf = DataFrame(data)
|
||||
kstdf.name = f"KST_{roc1}_{roc2}_{roc3}_{roc4}_{sma1}_{sma2}_{sma3}_{sma4}_{signal}"
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import concat, DataFrame, Series
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.overlap import ema
|
||||
from pandas_ta.utils import get_offset, verify_series, signals
|
||||
|
||||
|
||||
def macd(close: Series, fast: int = None, slow: int = None, signal: int = None, talib: bool = None,
|
||||
offset: int = None, **kwargs) -> DataFrame:
|
||||
def macd(
|
||||
close: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
talib: bool = None, offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Moving Average Convergence Divergence (MACD)
|
||||
|
||||
The MACD is a popular indicator to that is used to identify a security's trend.
|
||||
@@ -36,7 +38,7 @@ def macd(close: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
Returns:
|
||||
pd.DataFrame: macd, histogram, signal columns.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
fast = int(fast) if fast and fast > 0 else 12
|
||||
slow = int(slow) if slow and slow > 0 else 26
|
||||
signal = int(signal) if signal and signal > 0 else 9
|
||||
@@ -50,7 +52,7 @@ def macd(close: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
|
||||
as_mode = kwargs.setdefault("asmode", False)
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import MACD
|
||||
macd, signalma, histogram = MACD(close, fast, slow, signal)
|
||||
@@ -73,7 +75,7 @@ def macd(close: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
histogram = histogram.shift(offset)
|
||||
signalma = signalma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
macd.fillna(kwargs["fillna"], inplace=True)
|
||||
histogram.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -83,7 +85,7 @@ def macd(close: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
histogram.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
signalma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
_asmode = "AS" if as_mode else ""
|
||||
_props = f"_{fast}_{slow}_{signal}"
|
||||
macd.name = f"MACD{_asmode}{_props}"
|
||||
@@ -91,7 +93,6 @@ def macd(close: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
signalma.name = f"MACD{_asmode}s{_props}"
|
||||
macd.category = histogram.category = signalma.category = "momentum"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {macd.name: macd, histogram.name: histogram, signalma.name: signalma}
|
||||
df = DataFrame(data)
|
||||
df.name = f"MACD{_asmode}{_props}"
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def mom(close: Series, length: int = None, talib: bool = None, offset: int = None, **kwargs) -> Series:
|
||||
def mom(
|
||||
close: Series, length: int = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Momentum (MOM)
|
||||
|
||||
Momentum is an indicator used to measure a security's speed (or strength) of
|
||||
@@ -27,7 +30,7 @@ def mom(close: Series, length: int = None, talib: bool = None, offset: int = Non
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
close = verify_series(close, length)
|
||||
offset = get_offset(offset)
|
||||
@@ -35,7 +38,7 @@ def mom(close: Series, length: int = None, talib: bool = None, offset: int = Non
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import MOM
|
||||
mom = MOM(close, length)
|
||||
@@ -46,13 +49,13 @@ def mom(close: Series, length: int = None, talib: bool = None, offset: int = Non
|
||||
if offset != 0:
|
||||
mom = mom.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
mom.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
mom.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
mom.name = f"MOM_{length}"
|
||||
mom.category = "momentum"
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.overlap import ema, sma
|
||||
from pandas_ta.volatility import atr
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.overlap import ema, sma
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas_ta.volatility import atr
|
||||
|
||||
|
||||
def pgo(high: Series, low: Series, close: Series, length: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def pgo(
|
||||
high: Series, low: Series, close: Series, length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Pretty Good Oscillator (PGO)
|
||||
|
||||
The Pretty Good Oscillator indicator was created by Mark Johnson to measure the distance of the current close from its N-day Simple Moving Average, expressed in terms of an average true range over a similar period. Johnson's approach was to
|
||||
@@ -29,7 +32,7 @@ def pgo(high: Series, low: Series, close: Series, length: int = None, offset: in
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 14
|
||||
high = verify_series(high, length)
|
||||
low = verify_series(low, length)
|
||||
@@ -38,7 +41,7 @@ def pgo(high: Series, low: Series, close: Series, length: int = None, offset: in
|
||||
|
||||
if high is None or low is None or close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
pgo = close - sma(close, length)
|
||||
pgo /= ema(atr(high, low, close, length), length)
|
||||
|
||||
@@ -46,13 +49,13 @@ def pgo(high: Series, low: Series, close: Series, length: int = None, offset: in
|
||||
if offset != 0:
|
||||
pgo = pgo.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
pgo.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
pgo.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
pgo.name = f"PGO_{length}"
|
||||
pgo.category = "momentum"
|
||||
|
||||
|
||||
+11
-10
@@ -1,13 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.overlap import ma
|
||||
from pandas_ta.ma import ma
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, tal_ma, verify_series
|
||||
|
||||
|
||||
def ppo(close: Series, fast: int = None, slow: int = None, signal: int = None, scalar: float = None,
|
||||
mamode: str = None, talib: bool = None, offset: int = None, **kwargs) -> DataFrame:
|
||||
|
||||
def ppo(
|
||||
close: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
scalar: float = None, mamode: str = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Percentage Price Oscillator (PPO)
|
||||
|
||||
The Percentage Price Oscillator is similar to MACD in measuring momentum.
|
||||
@@ -33,7 +35,7 @@ def ppo(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
Returns:
|
||||
pd.DataFrame: ppo, histogram, signal columns
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
fast = int(fast) if fast and fast > 0 else 12
|
||||
slow = int(slow) if slow and slow > 0 else 26
|
||||
signal = int(signal) if signal and signal > 0 else 9
|
||||
@@ -47,7 +49,7 @@ def ppo(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import PPO
|
||||
ppo = PPO(close, fast, slow, tal_ma(mamode))
|
||||
@@ -66,7 +68,7 @@ def ppo(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
histogram = histogram.shift(offset)
|
||||
signalma = signalma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
ppo.fillna(kwargs["fillna"], inplace=True)
|
||||
histogram.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -76,14 +78,13 @@ def ppo(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
histogram.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
signalma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# 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"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {ppo.name: ppo, histogram.name: histogram, signalma.name: signalma}
|
||||
df = DataFrame(data)
|
||||
df.name = f"PPO{_props}"
|
||||
|
||||
+14
-11
@@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import sign as npSign
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series
|
||||
from numpy import sign
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series
|
||||
|
||||
|
||||
def psl(close: Series, open_: Series = None, length: int = None, scalar: float = None, drift: int = None,
|
||||
offset: int = None, **kwargs) -> Series:
|
||||
def psl(
|
||||
close: Series, open_: Series = None,
|
||||
length: int = None, scalar: float = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Psychological Line (PSL)
|
||||
|
||||
The Psychological Line is an oscillator-type indicator that compares the
|
||||
@@ -31,7 +34,7 @@ def psl(close: Series, open_: Series = None, length: int = None, scalar: float =
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 12
|
||||
scalar = float(scalar) if scalar and scalar > 0 else 100
|
||||
close = verify_series(close, length)
|
||||
@@ -40,15 +43,15 @@ def psl(close: Series, open_: Series = None, length: int = None, scalar: float =
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if open_ is not None:
|
||||
open_ = verify_series(open_)
|
||||
diff = npSign(close - open_)
|
||||
diff = sign(close - open_)
|
||||
else:
|
||||
diff = npSign(close.diff(drift))
|
||||
diff = sign(close.diff(drift))
|
||||
|
||||
diff.fillna(0, inplace=True)
|
||||
diff[diff <= 0] = 0 # Zero negative values
|
||||
diff[diff <= 0] = 0 # Set negative values to zero
|
||||
|
||||
psl = scalar * diff.rolling(length).sum()
|
||||
psl /= length
|
||||
@@ -57,13 +60,13 @@ def psl(close: Series, open_: Series = None, length: int = None, scalar: float =
|
||||
if offset != 0:
|
||||
psl = psl.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
psl.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
psl.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
_props = f"_{length}"
|
||||
psl.name = f"PSL{_props}"
|
||||
psl.category = "momentum"
|
||||
|
||||
@@ -4,8 +4,11 @@ from pandas_ta.overlap import ema
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def pvo(volume: Series, fast: int = None, slow: int = None, signal: int = None, scalar: float = None,
|
||||
offset: int = None, **kwargs) -> DataFrame:
|
||||
def pvo(
|
||||
volume: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
scalar: float = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Percentage Volume Oscillator (PVO)
|
||||
|
||||
Percentage Volume Oscillator is a Momentum Oscillator for Volume.
|
||||
@@ -28,7 +31,7 @@ def pvo(volume: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
Returns:
|
||||
pd.DataFrame: pvo, histogram, signal columns.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
fast = int(fast) if fast and fast > 0 else 12
|
||||
slow = int(slow) if slow and slow > 0 else 26
|
||||
signal = int(signal) if signal and signal > 0 else 9
|
||||
@@ -40,7 +43,7 @@ def pvo(volume: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
|
||||
if volume is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
fastma = ema(volume, length=fast)
|
||||
slowma = ema(volume, length=slow)
|
||||
pvo = scalar * (fastma - slowma)
|
||||
@@ -55,7 +58,7 @@ def pvo(volume: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
histogram = histogram.shift(offset)
|
||||
signalma = signalma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
pvo.fillna(kwargs["fillna"], inplace=True)
|
||||
histogram.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -65,14 +68,13 @@ def pvo(volume: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
histogram.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
signalma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
_props = f"_{fast}_{slow}_{signal}"
|
||||
pvo.name = f"PVO{_props}"
|
||||
histogram.name = f"PVOh{_props}"
|
||||
signalma.name = f"PVOs{_props}"
|
||||
pvo.category = histogram.category = signalma.category = "momentum"
|
||||
|
||||
#
|
||||
data = {pvo.name: pvo, histogram.name: histogram, signalma.name: signalma}
|
||||
df = DataFrame(data)
|
||||
df.name = pvo.name
|
||||
|
||||
+21
-18
@@ -1,16 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import maximum as npMaximum
|
||||
from numpy import minimum as npMinimum
|
||||
from numpy import nan as npNaN
|
||||
from numpy import isnan, maximum, minimum, nan
|
||||
from pandas import DataFrame, Series
|
||||
|
||||
from .rsi import rsi
|
||||
from pandas_ta.overlap import ma
|
||||
from pandas_ta.ma import ma
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series
|
||||
from .rsi import rsi
|
||||
|
||||
|
||||
def qqe(close: Series, length: int = None, smooth: int = None, factor: float = None, mamode: str = None,
|
||||
drift: int = None, offset: int = None, **kwargs) -> DataFrame:
|
||||
def qqe(
|
||||
close: Series, length: int = None,
|
||||
smooth: int = None, factor: float = None,
|
||||
mamode: str = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Quantitative Qualitative Estimation (QQE)
|
||||
|
||||
The Quantitative Qualitative Estimation (QQE) is similar to SuperTrend but uses a Smoothed RSI with an upper and lower bands. The band width is a combination of a one period True Range of the Smoothed RSI which is double smoothed using Wilder's smoothing length (2 * rsiLength - 1) and multiplied by the default factor of 4.236. A Long trend is determined when the Smoothed RSI crosses the previous upperband and a Short trend when the Smoothed RSI crosses the previous lowerband.
|
||||
@@ -38,30 +39,33 @@ def qqe(close: Series, length: int = None, smooth: int = None, factor: float = N
|
||||
Returns:
|
||||
pd.DataFrame: QQE, RSI_MA (basis), QQEl (long), and QQEs (short) columns.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if isinstance(length, int) and length > 0 else 14
|
||||
smooth = int(smooth) if isinstance(smooth, int) and smooth > 0 else 5
|
||||
factor = float(factor) if isinstance(factor, float) and factor else 4.236
|
||||
wilders_length = 2 * length - 1
|
||||
mamode = mamode if isinstance(mamode, str) else "ema"
|
||||
close = verify_series(close, max(length, smooth, wilders_length))
|
||||
close = verify_series(close, smooth + wilders_length)
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# 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
|
||||
@@ -72,8 +76,8 @@ def qqe(close: Series, length: int = None, smooth: int = None, factor: float = N
|
||||
short = Series(0, index=close.index)
|
||||
trend = Series(1, index=close.index)
|
||||
qqe = Series(rsi_ma.iloc[0], index=close.index)
|
||||
qqe_long = Series(npNaN, index=close.index)
|
||||
qqe_short = Series(npNaN, 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.iloc[i], rsi_ma.iloc[i - 1]
|
||||
@@ -82,13 +86,13 @@ def qqe(close: Series, length: int = None, smooth: int = None, factor: float = N
|
||||
|
||||
# Long Line
|
||||
if p_rsi > c_long and c_rsi > c_long:
|
||||
long.iloc[i] = npMaximum(c_long, lowerband.iloc[i])
|
||||
long.iloc[i] = maximum(c_long, lowerband.iloc[i])
|
||||
else:
|
||||
long.iloc[i] = lowerband.iloc[i]
|
||||
|
||||
# Short Line
|
||||
if p_rsi < c_short and c_rsi < c_short:
|
||||
short.iloc[i] = npMinimum(c_short, upperband.iloc[i])
|
||||
short.iloc[i] = minimum(c_short, upperband.iloc[i])
|
||||
else:
|
||||
short.iloc[i] = upperband.iloc[i]
|
||||
|
||||
@@ -115,7 +119,7 @@ def qqe(close: Series, length: int = None, smooth: int = None, factor: float = N
|
||||
long = long.shift(offset)
|
||||
short = short.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
rsi_ma.fillna(kwargs["fillna"], inplace=True)
|
||||
qqe.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -127,7 +131,7 @@ def qqe(close: Series, length: int = None, smooth: int = None, factor: float = N
|
||||
qqe_long.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
qqe_short.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
_props = f"{_mode}_{length}_{smooth}_{factor}"
|
||||
qqe.name = f"QQE{_props}"
|
||||
rsi_ma.name = f"QQE{_props}_RSI{_mode.upper()}MA"
|
||||
@@ -136,7 +140,6 @@ def qqe(close: Series, length: int = None, smooth: int = None, factor: float = N
|
||||
qqe.category = rsi_ma.category = "momentum"
|
||||
qqe_long.category = qqe_short.category = qqe.category
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {
|
||||
qqe.name: qqe, rsi_ma.name: rsi_ma,
|
||||
# long.name: long, short.name: short
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .mom import mom
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from .mom import mom
|
||||
|
||||
|
||||
def roc(close: Series, length: int = None, scalar: float = None, talib: bool = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def roc(
|
||||
close: Series, length: int = None,
|
||||
scalar: float = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Rate of Change (ROC)
|
||||
|
||||
Rate of Change is an indicator is also referred to as Momentum (yeah, confusingly).
|
||||
@@ -31,7 +34,7 @@ def roc(close: Series, length: int = None, scalar: float = None, talib: bool = N
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
scalar = float(scalar) if scalar and scalar > 0 else 100
|
||||
close = verify_series(close, length)
|
||||
@@ -40,7 +43,7 @@ def roc(close: Series, length: int = None, scalar: float = None, talib: bool = N
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import ROC
|
||||
roc = ROC(close, length)
|
||||
@@ -51,13 +54,13 @@ def roc(close: Series, length: int = None, scalar: float = None, talib: bool = N
|
||||
if offset != 0:
|
||||
roc = roc.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
roc.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
roc.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
roc.name = f"ROC_{length}"
|
||||
roc.category = "momentum"
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame, concat, Series
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.overlap import rma
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series, signals
|
||||
|
||||
|
||||
def rsi(close: Series, length: int = None, scalar: float = None, talib: bool = None, drift: int = None,
|
||||
offset: int = None, **kwargs) -> Series:
|
||||
def rsi(
|
||||
close: Series, length: int = None, scalar: float = None,
|
||||
talib: bool = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Relative Strength Index (RSI)
|
||||
|
||||
The Relative Strength Index is popular momentum oscillator used to measure the
|
||||
@@ -31,7 +34,7 @@ def rsi(close: Series, length: int = None, scalar: float = None, talib: bool = N
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 14
|
||||
scalar = float(scalar) if scalar else 100
|
||||
close = verify_series(close, length)
|
||||
@@ -41,7 +44,7 @@ def rsi(close: Series, length: int = None, scalar: float = None, talib: bool = N
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import RSI
|
||||
rsi = RSI(close, length)
|
||||
@@ -61,13 +64,13 @@ def rsi(close: Series, length: int = None, scalar: float = None, talib: bool = N
|
||||
if offset != 0:
|
||||
rsi = rsi.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
rsi.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
rsi.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
rsi.name = f"RSI_{length}"
|
||||
rsi.category = "momentum"
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import nan as npNaN
|
||||
from numpy import nan
|
||||
from pandas import concat, DataFrame, Series
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series, signals
|
||||
|
||||
|
||||
def rsx(close: Series, length: int = None, drift: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def rsx(
|
||||
close: Series, length: int = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Relative Strength Xtra (rsx)
|
||||
|
||||
The Relative Strength Xtra is based on the popular RSI indicator and inspired
|
||||
@@ -30,7 +33,7 @@ def rsx(close: Series, length: int = None, drift: int = None, offset: int = None
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 14
|
||||
close = verify_series(close, length)
|
||||
drift = get_drift(drift)
|
||||
@@ -38,7 +41,8 @@ def rsx(close: Series, length: int = None, drift: int = None, offset: int = None
|
||||
|
||||
if close is None: return
|
||||
|
||||
# variables
|
||||
# Calculate
|
||||
m = close.size
|
||||
vC, v1C = 0, 0
|
||||
v4, v8, v10, v14, v18, v20 = 0, 0, 0, 0, 0, 0
|
||||
|
||||
@@ -46,9 +50,7 @@ def rsx(close: Series, length: int = None, drift: int = None, offset: int = None
|
||||
f40, f48, f50, f58, f60, f68, f70, f78 = 0, 0, 0, 0, 0, 0, 0, 0
|
||||
f80, f88, f90 = 0, 0, 0
|
||||
|
||||
# Calculate Result
|
||||
m = close.size
|
||||
result = [npNaN for _ in range(0, length - 1)] + [0]
|
||||
result = [nan for _ in range(0, length - 1)] + [0]
|
||||
for i in range(length, m):
|
||||
if f90 == 0:
|
||||
f90 = 1.0
|
||||
@@ -107,13 +109,13 @@ def rsx(close: Series, length: int = None, drift: int = None, offset: int = None
|
||||
if offset != 0:
|
||||
rsx = rsx.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
rsx.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
rsx.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
rsx.name = f"RSX_{length}"
|
||||
rsx.category = "momentum"
|
||||
|
||||
|
||||
@@ -4,8 +4,11 @@ from pandas_ta.overlap import swma
|
||||
from pandas_ta.utils import get_offset, non_zero_range, verify_series
|
||||
|
||||
|
||||
def rvgi(open_: Series, high: Series, low: Series, close: Series, length: int = None, swma_length: int = None,
|
||||
offset: int = None, **kwargs) -> Series:
|
||||
def rvgi(
|
||||
open_: Series, high: Series, low: Series, close: Series,
|
||||
length: int = None, swma_length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Relative Vigor Index (RVGI)
|
||||
|
||||
The Relative Vigor Index attempts to measure the strength of a trend relative to
|
||||
@@ -32,7 +35,7 @@ def rvgi(open_: Series, high: Series, low: Series, close: Series, length: int =
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
high_low_range = non_zero_range(high, low)
|
||||
close_open_range = non_zero_range(close, open_)
|
||||
length = int(length) if length and length > 0 else 14
|
||||
@@ -46,7 +49,7 @@ def rvgi(open_: Series, high: Series, low: Series, close: Series, length: int =
|
||||
|
||||
if open_ is None or high is None or low is None or close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
numerator = swma(close_open_range, length=swma_length).rolling(length).sum()
|
||||
denominator = swma(high_low_range, length=swma_length).rolling(length).sum()
|
||||
|
||||
@@ -58,7 +61,7 @@ def rvgi(open_: Series, high: Series, low: Series, close: Series, length: int =
|
||||
rvgi = rvgi.shift(offset)
|
||||
signal = signal.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
rvgi.fillna(kwargs["fillna"], inplace=True)
|
||||
signal.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -66,12 +69,11 @@ def rvgi(open_: Series, high: Series, low: Series, close: Series, length: int =
|
||||
rvgi.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
signal.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
rvgi.name = f"RVGI_{length}_{swma_length}"
|
||||
signal.name = f"RVGIs_{length}_{swma_length}"
|
||||
rvgi.category = signal.category = "momentum"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
df = DataFrame({rvgi.name: rvgi, signal.name: signal})
|
||||
df.name = f"RVGI_{length}_{swma_length}"
|
||||
df.category = rvgi.category
|
||||
|
||||
+14
-12
@@ -1,18 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import arctan as npAtan
|
||||
from numpy import pi as npPi
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from numpy import arctan, pi
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def slope( close: Series, length: int = None, as_angle=None, to_degrees=None, vertical=None,
|
||||
offset: int = None, **kwargs) -> Series:
|
||||
def slope(
|
||||
close: Series, length: int = None,
|
||||
as_angle=None, to_degrees=None, vertical=None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Slope
|
||||
|
||||
Returns the slope of a series of length n. Can convert the slope to angle.
|
||||
Default: slope.
|
||||
|
||||
Sources: Algebra I
|
||||
Source: Algebra
|
||||
|
||||
Calculation:
|
||||
Default Inputs:
|
||||
@@ -38,7 +40,7 @@ def slope( close: Series, length: int = None, as_angle=None, to_degrees=None, ve
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 1
|
||||
as_angle = True if isinstance(as_angle, bool) else False
|
||||
to_degrees = True if isinstance(to_degrees, bool) else False
|
||||
@@ -47,24 +49,24 @@ def slope( close: Series, length: int = None, as_angle=None, to_degrees=None, ve
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
slope = close.diff(length) / length
|
||||
if as_angle:
|
||||
slope = slope.apply(npAtan)
|
||||
slope = slope.apply(arctan)
|
||||
if to_degrees:
|
||||
slope *= 180 / npPi
|
||||
slope *= 180 / pi
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
slope = slope.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
slope.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
slope.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# 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"
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame, Series
|
||||
from .tsi import tsi
|
||||
from pandas_ta.overlap import ema
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from .tsi import tsi
|
||||
|
||||
|
||||
def smi(close: Series, fast: int = None, slow: int = None, signal: int = None, scalar: float = None,
|
||||
offset: int = None, **kwargs) -> DataFrame:
|
||||
def smi(
|
||||
close: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
scalar: float = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""SMI Ergodic Indicator (SMI)
|
||||
|
||||
The SMI Ergodic Indicator is the same as the True Strength Index (TSI) developed
|
||||
@@ -37,7 +39,7 @@ def smi(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
Returns:
|
||||
pd.DataFrame: smi, signal, oscillator columns.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
fast = int(fast) if fast and fast > 0 else 5
|
||||
slow = int(slow) if slow and slow > 0 else 20
|
||||
signal = int(signal) if signal and signal > 0 else 5
|
||||
@@ -49,7 +51,7 @@ def smi(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
tsi_df = tsi(close, fast=fast, slow=slow, signal=signal, scalar=scalar)
|
||||
smi = tsi_df.iloc[:, 0]
|
||||
signalma = tsi_df.iloc[:, 1]
|
||||
@@ -61,7 +63,7 @@ def smi(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
signalma = signalma.shift(offset)
|
||||
osc = osc.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
smi.fillna(kwargs["fillna"], inplace=True)
|
||||
signalma.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -71,7 +73,7 @@ def smi(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
signalma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
osc.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
_scalar = f"_{scalar}" if scalar != 1 else ""
|
||||
_props = f"_{fast}_{slow}_{signal}{_scalar}"
|
||||
smi.name = f"SMI{_props}"
|
||||
@@ -79,7 +81,6 @@ def smi(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
osc.name = f"SMIo{_props}"
|
||||
smi.category = signalma.category = osc.category = "momentum"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {smi.name: smi, signalma.name: signalma, osc.name: osc}
|
||||
df = DataFrame(data)
|
||||
df.name = f"SMI{_props}"
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import nan as npNaN
|
||||
from numpy import nan
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta.momentum import mom
|
||||
from pandas_ta.overlap import ema, linreg, sma
|
||||
from pandas_ta.trend import decreasing, increasing
|
||||
from pandas_ta.utils import get_offset, unsigned_differences, verify_series
|
||||
from pandas_ta.volatility import bbands, kc
|
||||
from pandas_ta.utils import get_offset
|
||||
from pandas_ta.utils import unsigned_differences, verify_series
|
||||
from .mom import mom
|
||||
|
||||
|
||||
def squeeze(high: Series, low: Series, close: Series, bb_length: int = None, bb_std: float = None,
|
||||
kc_length: int = None, kc_scalar: float = None, mom_length: int = None, mom_smooth: int = None,
|
||||
use_tr=None, mamode: str = None, offset: int = None, **kwargs) -> DataFrame:
|
||||
def squeeze(
|
||||
high: Series, low: Series, close: Series,
|
||||
bb_length: int = None, bb_std: float = None,
|
||||
kc_length: int = None, kc_scalar: float = None,
|
||||
mom_length: int = None, mom_smooth: int = None,
|
||||
use_tr=None, mamode: str = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Squeeze (SQZ)
|
||||
|
||||
The default is based on John Carter's "TTM Squeeze" indicator, as discussed
|
||||
@@ -54,7 +58,7 @@ def squeeze(high: Series, low: Series, close: Series, bb_length: int = None, bb_
|
||||
pd.DataFrame: SQZ, SQZ_ON, SQZ_OFF, NO_SQZ columns by default. More
|
||||
detailed columns if 'detailed' kwarg is True.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
bb_length = int(bb_length) if bb_length and bb_length > 0 else 20
|
||||
bb_std = float(bb_std) if bb_std and bb_std > 0 else 2.0
|
||||
kc_length = int(kc_length) if kc_length and kc_length > 0 else 20
|
||||
@@ -79,7 +83,7 @@ def squeeze(high: Series, low: Series, close: Series, bb_length: int = None, bb_
|
||||
df.columns = df.columns.str.lower()
|
||||
return [c.split("_")[0][n - 1:n] for c in df.columns]
|
||||
|
||||
# Calculate Result
|
||||
# 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)
|
||||
|
||||
@@ -113,7 +117,7 @@ def squeeze(high: Series, low: Series, close: Series, bb_length: int = None, bb_
|
||||
squeeze_off = squeeze_off.shift(offset)
|
||||
no_squeeze = no_squeeze.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
squeeze.fillna(kwargs["fillna"], inplace=True)
|
||||
squeeze_on.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -125,7 +129,7 @@ def squeeze(high: Series, low: Series, close: Series, bb_length: int = None, bb_
|
||||
squeeze_off.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
no_squeeze.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
_props = "" if use_tr else "hlr"
|
||||
_props += f"_{bb_length}_{bb_std}_{kc_length}_{kc_scalar}"
|
||||
_props += "_LB" if lazybear else ""
|
||||
@@ -141,7 +145,7 @@ def squeeze(high: Series, low: Series, close: Series, bb_length: int = None, bb_
|
||||
df.name = squeeze.name
|
||||
df.category = squeeze.category = "momentum"
|
||||
|
||||
# Detailed Squeeze Series
|
||||
# More Detail
|
||||
if detailed:
|
||||
pos_squeeze = squeeze[squeeze >= 0]
|
||||
neg_squeeze = squeeze[squeeze < 0]
|
||||
@@ -154,15 +158,15 @@ def squeeze(high: Series, low: Series, close: Series, bb_length: int = None, bb_
|
||||
neg_dec *= squeeze
|
||||
neg_inc *= squeeze
|
||||
|
||||
pos_inc.replace(0, npNaN, inplace=True)
|
||||
pos_dec.replace(0, npNaN, inplace=True)
|
||||
neg_dec.replace(0, npNaN, inplace=True)
|
||||
neg_inc.replace(0, npNaN, inplace=True)
|
||||
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, npNaN, inplace=True)
|
||||
sqz_dec.replace(0, npNaN, inplace=True)
|
||||
sqz_inc.replace(0, nan, inplace=True)
|
||||
sqz_dec.replace(0, nan, inplace=True)
|
||||
|
||||
# Handle fills
|
||||
if "fillna" in kwargs:
|
||||
|
||||
+13
-18
@@ -4,8 +4,11 @@ from pandas_ta.overlap import ema
|
||||
from pandas_ta.utils import get_offset, non_zero_range, verify_series
|
||||
|
||||
|
||||
def stc(close: Series, tclength: int = None, fast: int = None, slow: int = None, factor: float = None,
|
||||
offset: int = None, **kwargs) -> DataFrame:
|
||||
def stc(
|
||||
close: Series, tclength: int = None,
|
||||
fast: int = None, slow: int = None, factor: float = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Schaff Trend Cycle (STC)
|
||||
|
||||
The Schaff Trend Cycle is an evolution of the popular MACD incorportating two
|
||||
@@ -49,7 +52,7 @@ def stc(close: Series, tclength: int = None, fast: int = None, slow: int = None,
|
||||
Returns:
|
||||
pd.DataFrame: stc, macd, stoch
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
tclength = int(tclength) if tclength and tclength > 0 else 10
|
||||
fast = int(fast) if fast and fast > 0 else 12
|
||||
slow = int(slow) if slow and slow > 0 else 26
|
||||
@@ -62,6 +65,7 @@ def stc(close: Series, tclength: int = None, fast: int = None, slow: int = None,
|
||||
|
||||
if close is None: return
|
||||
|
||||
# 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.
|
||||
@@ -75,30 +79,22 @@ def stc(close: Series, tclength: int = None, fast: int = None, slow: int = None,
|
||||
ma2 = verify_series(ma2, _length)
|
||||
|
||||
if ma1 is None or ma2 is None: return
|
||||
# Calculate Result based on external feeded series
|
||||
# According to external feeded series
|
||||
xmacd = ma1 - ma2
|
||||
# invoke shared calculation
|
||||
pff, pf = schaff_tc(close, xmacd, tclength, factor)
|
||||
|
||||
elif isinstance(osc, Series):
|
||||
osc = verify_series(osc, _length)
|
||||
if osc is None: return
|
||||
# Calculate Result based on feeded oscillator
|
||||
# (should be ranging around 0 x-axis)
|
||||
# According to feeded oscillator (should be ranging around 0 x-axis)
|
||||
xmacd = osc
|
||||
# invoke shared calculation
|
||||
pff, pf = schaff_tc(close, xmacd, tclength, factor)
|
||||
|
||||
else:
|
||||
# Calculate Result .. (traditionel/full)
|
||||
# MACD line
|
||||
# MACD (traditional/full)
|
||||
fastma = ema(close, length=fast)
|
||||
slowma = ema(close, length=slow)
|
||||
xmacd = fastma - slowma
|
||||
# invoke shared calculation
|
||||
pff, pf = schaff_tc(close, xmacd, tclength, factor)
|
||||
|
||||
# Resulting Series
|
||||
stc = Series(pff, index=close.index)
|
||||
macd = Series(xmacd, index=close.index)
|
||||
stoch = Series(pf, index=close.index)
|
||||
@@ -109,7 +105,7 @@ def stc(close: Series, tclength: int = None, fast: int = None, slow: int = None,
|
||||
macd = macd.shift(offset)
|
||||
stoch = stoch.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
stc.fillna(kwargs["fillna"], inplace=True)
|
||||
macd.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -119,14 +115,13 @@ def stc(close: Series, tclength: int = None, fast: int = None, slow: int = None,
|
||||
macd.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
stoch.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
_props = f"_{tclength}_{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"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {stc.name: stc, macd.name: macd, stoch.name: stoch}
|
||||
df = DataFrame(data)
|
||||
df.name = f"STC{_props}"
|
||||
@@ -170,4 +165,4 @@ def schaff_tc(close, xmacd, tclength, factor):
|
||||
# Smoothed Calculation for % Fast D of PF
|
||||
pff[i] = round(pff[i - 1] + (factor * (stoch2[i] - pff[i - 1])), 8)
|
||||
|
||||
return [pff, pf]
|
||||
return pff, pf
|
||||
@@ -1,12 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.overlap import ma
|
||||
from pandas_ta.ma import ma
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, non_zero_range, tal_ma, verify_series
|
||||
|
||||
|
||||
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) -> DataFrame:
|
||||
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
|
||||
) -> DataFrame:
|
||||
"""Stochastic (STOCH)
|
||||
|
||||
The Stochastic Oscillator (STOCH) was developed by George Lane in the 1950's.
|
||||
@@ -90,9 +94,9 @@ def stoch(high: Series, low: Series, close: Series, k: int = None, d: int = None
|
||||
stoch_d.name = f"{_name}d{_props}"
|
||||
stoch_k.category = stoch_d.category = "momentum"
|
||||
|
||||
# Return DataFrame
|
||||
data = {stoch_k.name: stoch_k, stoch_d.name: stoch_d}
|
||||
df = DataFrame(data, index=close.index)
|
||||
df.name = f"{_name}{_props}"
|
||||
df.category = stoch_k.category
|
||||
|
||||
return df
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.overlap import ma
|
||||
from pandas_ta.ma import ma
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, non_zero_range, tal_ma, verify_series
|
||||
|
||||
|
||||
def stochf(high: Series, low: Series, close: Series, k: int = None, d: int = None, mamode: str = None,
|
||||
talib: bool = None, offset: int = None, **kwargs) -> DataFrame:
|
||||
def stochf(
|
||||
high: Series, low: Series, close: Series, k: int = None, d: int = None,
|
||||
mamode: str = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Fast Stochastic (STOCHF)
|
||||
|
||||
The Fast Stochastic Oscillator (STOCHF) was developed by George Lane in the
|
||||
@@ -81,9 +84,9 @@ def stochf(high: Series, low: Series, close: Series, k: int = None, d: int = Non
|
||||
stochf_d.name = f"{_name}d{_props}"
|
||||
stochf_k.category = stochf_d.category = "momentum"
|
||||
|
||||
# Return DataFrame
|
||||
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,12 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame, Series
|
||||
from .rsi import rsi
|
||||
from pandas_ta.overlap import ma
|
||||
from pandas_ta.ma import ma
|
||||
from pandas_ta.momentum import rsi
|
||||
from pandas_ta.utils import get_offset, non_zero_range, verify_series
|
||||
|
||||
|
||||
def stochrsi(close: Series, length: int = None, rsi_length: int = None, k: int = None, d: int = None,
|
||||
mamode: str = None, offset: int = None, **kwargs) -> DataFrame:
|
||||
def stochrsi(
|
||||
close: Series, length: int = None, rsi_length: int = None,
|
||||
k: int = None, d: int = None, mamode: str = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Stochastic (STOCHRSI)
|
||||
|
||||
"Stochastic RSI and Dynamic Momentum Index" was created by Tushar Chande and Stanley Kroll and published in Stock & Commodities V.11:5 (189-199)
|
||||
@@ -72,14 +75,13 @@ def stochrsi(close: Series, length: int = None, rsi_length: int = None, k: int =
|
||||
stochrsi_k.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
stochrsi_d.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize
|
||||
# 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"
|
||||
|
||||
# Return DataFrame
|
||||
data = {stochrsi_k.name: stochrsi_k, stochrsi_d.name: stochrsi_d}
|
||||
df = DataFrame(data)
|
||||
df.name = f"{_name}{_props}"
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# import numpy as np
|
||||
from numpy import where as npWhere
|
||||
from numpy import where
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def td_seq(close: Series, asint: bool = None, offset: int = None, **kwargs) -> DataFrame:
|
||||
def td_seq(
|
||||
close: Series, asint: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""TD Sequential (TD_SEQ)
|
||||
|
||||
Tom DeMark's Sequential indicator attempts to identify a price point where an
|
||||
uptrend or a downtrend exhausts itself and reverses.
|
||||
Tom DeMark's Sequential indicator attempts to identify a price point
|
||||
where an uptrend or a downtrend exhausts itself and reverses.
|
||||
|
||||
Sources:
|
||||
https://tradetrekker.wordpress.com/tdsequential/
|
||||
|
||||
Args:
|
||||
close (pd.Series): Series of 'close's
|
||||
asint (bool): If True, fillnas with 0 and change type to int. Default: False
|
||||
asint (bool): If True, fillnas with 0 and change type to int.
|
||||
Default: False
|
||||
offset (int): How many periods to offset the result. Default: 0
|
||||
|
||||
Kwargs:
|
||||
@@ -26,35 +29,15 @@ def td_seq(close: Series, asint: bool = None, offset: int = None, **kwargs) -> D
|
||||
Returns:
|
||||
pd.DataFrame: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
close = verify_series(close)
|
||||
offset = get_offset(offset)
|
||||
asint = asint if isinstance(asint, bool) else False
|
||||
show_all = kwargs.setdefault("show_all", True)
|
||||
|
||||
def true_sequence_count(series: Series):
|
||||
index = series.where(series == False).last_valid_index()
|
||||
|
||||
if index is None:
|
||||
return series.count()
|
||||
else:
|
||||
s = series[series.index > index]
|
||||
return s.count()
|
||||
|
||||
def calc_td(series: Series, direction: str, show_all: bool):
|
||||
td_bool = series.diff(4) > 0 if direction=="up" else series.diff(4) < 0
|
||||
td_num = npWhere(
|
||||
td_bool, td_bool.rolling(13, min_periods=0).apply(true_sequence_count), 0
|
||||
)
|
||||
td_num = Series(td_num)
|
||||
|
||||
if show_all:
|
||||
td_num = td_num.mask(td_num == 0)
|
||||
else:
|
||||
td_num = td_num.mask(~td_num.between(6,9))
|
||||
|
||||
return td_num
|
||||
if close is None: return
|
||||
|
||||
# Calculate
|
||||
up_seq = calc_td(close, "up", show_all)
|
||||
down_seq = calc_td(close, "down", show_all)
|
||||
|
||||
@@ -70,7 +53,7 @@ def td_seq(close: Series, asint: bool = None, offset: int = None, **kwargs) -> D
|
||||
up_seq = up_seq.shift(offset)
|
||||
down_seq = down_seq.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
up_seq.fillna(kwargs["fillna"], inplace=True)
|
||||
down_seq.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -79,12 +62,11 @@ def td_seq(close: Series, asint: bool = None, offset: int = None, **kwargs) -> D
|
||||
up_seq.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
down_seq.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
up_seq.name = f"TD_SEQ_UPa" if show_all else f"TD_SEQ_UP"
|
||||
down_seq.name = f"TD_SEQ_DNa" if show_all else f"TD_SEQ_DN"
|
||||
up_seq.category = down_seq.category = "momentum"
|
||||
|
||||
# Prepare Dataframe to return
|
||||
data = {up_seq.name: up_seq, down_seq.name: down_seq}
|
||||
df = DataFrame(data)
|
||||
df.index = close.index # Only works here for some reason?
|
||||
@@ -92,3 +74,27 @@ def td_seq(close: Series, asint: bool = None, offset: int = None, **kwargs) -> D
|
||||
df.category = up_seq.category
|
||||
|
||||
return df
|
||||
|
||||
|
||||
def sequence_count(series: Series):
|
||||
index = series.where(series == False).last_valid_index()
|
||||
|
||||
if index is None:
|
||||
return series.count()
|
||||
else:
|
||||
s = series[series.index > index]
|
||||
return s.count()
|
||||
|
||||
def calc_td(series: Series, direction: str, show_all: bool):
|
||||
td_bool = series.diff(4) > 0 if direction=="up" else series.diff(4) < 0
|
||||
td_num = where(
|
||||
td_bool, td_bool.rolling(13, min_periods=0).apply(sequence_count), 0
|
||||
)
|
||||
td_num = Series(td_num)
|
||||
|
||||
if show_all:
|
||||
td_num = td_num.mask(td_num == 0)
|
||||
else:
|
||||
td_num = td_num.mask(~td_num.between(6,9))
|
||||
|
||||
return td_num
|
||||
|
||||
+20
-11
@@ -1,11 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# from numpy import isnan
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta.overlap.ema import ema
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series
|
||||
|
||||
|
||||
def trix(close: Series, length: int = None, signal: int = None, scalar: float = None, drift: int = None,
|
||||
offset: int = None, **kwargs) -> Series:
|
||||
def trix(
|
||||
close: Series, length: int = None, signal: int = None,
|
||||
scalar: float = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Trix (TRIX)
|
||||
|
||||
TRIX is a momentum oscillator to identify divergences.
|
||||
@@ -28,22 +32,28 @@ def trix(close: Series, length: int = None, signal: int = None, scalar: float =
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 30
|
||||
signal = int(signal) if signal and signal > 0 else 9
|
||||
scalar = float(scalar) if scalar else 100
|
||||
close = verify_series(close, max(length, signal))
|
||||
_length = 3 * length - 2
|
||||
close = verify_series(close, _length)
|
||||
drift = get_drift(drift)
|
||||
offset = get_offset(offset)
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
ema1 = ema(close=close, length=length, **kwargs)
|
||||
ema2 = ema(close=ema1, length=length, **kwargs)
|
||||
ema3 = ema(close=ema2, length=length, **kwargs)
|
||||
trix = scalar * ema3.pct_change(drift)
|
||||
# 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
|
||||
@@ -51,7 +61,7 @@ def trix(close: Series, length: int = None, signal: int = None, scalar: float =
|
||||
trix = trix.shift(offset)
|
||||
trix_signal = trix_signal.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
trix.fillna(kwargs["fillna"], inplace=True)
|
||||
trix_signal.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -59,12 +69,11 @@ def trix(close: Series, length: int = None, signal: int = None, scalar: float =
|
||||
trix.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
trix_signal.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
trix.name = f"TRIX_{length}_{signal}"
|
||||
trix_signal.name = f"TRIXs_{length}_{signal}"
|
||||
trix.category = trix_signal.category = "momentum"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {trix.name: trix, trix_signal.name: trix_signal}
|
||||
df = DataFrame(data, index=close.index)
|
||||
df.name = f"TRIX_{length}_{signal}"
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta.overlap import ema, ma
|
||||
from pandas_ta.ma import ma
|
||||
from pandas_ta.overlap import ema
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series
|
||||
|
||||
|
||||
def tsi(close: Series, fast: int = None, slow: int = None, signal: int = None, scalar: float = None,
|
||||
mamode: str = None, drift: int = None, offset: int = None, **kwargs) -> DataFrame:
|
||||
def tsi(
|
||||
close: Series, fast: int = None, slow: int = None, signal: int = None,
|
||||
scalar: float = None, mamode: str = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""True Strength Index (TSI)
|
||||
|
||||
The True Strength Index is a momentum indicator used to identify short-term
|
||||
@@ -33,7 +37,7 @@ def tsi(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
Returns:
|
||||
pd.DataFrame: tsi, signal.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
fast = int(fast) if fast and fast > 0 else 13
|
||||
slow = int(slow) if slow and slow > 0 else 25
|
||||
signal = int(signal) if signal and signal > 0 else 13
|
||||
@@ -48,7 +52,7 @@ def tsi(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
diff = close.diff(drift)
|
||||
slow_ema = ema(close=diff, length=slow, **kwargs)
|
||||
fast_slow_ema = ema(close=slow_ema, length=fast, **kwargs)
|
||||
@@ -65,7 +69,7 @@ def tsi(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
tsi = tsi.shift(offset)
|
||||
tsi_signal = tsi_signal.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
tsi.fillna(kwargs["fillna"], inplace=True)
|
||||
tsi_signal.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -73,12 +77,11 @@ def tsi(close: Series, fast: int = None, slow: int = None, signal: int = None, s
|
||||
tsi.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
tsi_signal.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
tsi.name = f"TSI_{fast}_{slow}_{signal}"
|
||||
tsi_signal.name = f"TSIs_{fast}_{slow}_{signal}"
|
||||
tsi.category = tsi_signal.category = "momentum"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
df = DataFrame({tsi.name: tsi, tsi_signal.name: tsi_signal})
|
||||
df.name = f"TSI_{fast}_{slow}_{signal}"
|
||||
df.category = "momentum"
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series
|
||||
|
||||
|
||||
def uo(high: Series, low: Series, close: Series, fast: int = None, medium: int = None, slow: int = None,
|
||||
fast_w: float = None, medium_w: float = None, slow_w: float = None, talib: bool = None, drift: int = None,
|
||||
offset: int = None, **kwargs) -> Series:
|
||||
def uo(
|
||||
high: Series, low: Series, close: Series,
|
||||
fast: int = None, medium: int = None, slow: int = None,
|
||||
fast_w: float = None, medium_w: float = None, slow_w: float = None,
|
||||
talib: bool = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Ultimate Oscillator (UO)
|
||||
|
||||
The Ultimate Oscillator is a momentum indicator over three different
|
||||
@@ -37,7 +41,7 @@ def uo(high: Series, low: Series, close: Series, fast: int = None, medium: int =
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
fast = int(fast) if fast and fast > 0 else 7
|
||||
fast_w = float(fast_w) if fast_w and fast_w > 0 else 4.0
|
||||
medium = int(medium) if medium and medium > 0 else 14
|
||||
@@ -54,7 +58,7 @@ def uo(high: Series, low: Series, close: Series, fast: int = None, medium: int =
|
||||
|
||||
if high is None or low is None or close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import ULTOSC
|
||||
uo = ULTOSC(high, low, close, fast, medium, slow)
|
||||
@@ -83,13 +87,13 @@ def uo(high: Series, low: Series, close: Series, fast: int = None, medium: int =
|
||||
if offset != 0:
|
||||
uo = uo.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
uo.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
uo.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
uo.name = f"UO_{fast}_{medium}_{slow}"
|
||||
uo.category = "momentum"
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def willr(high: Series, low: Series, close: Series, length: int = None, talib: bool = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def willr(
|
||||
high: Series, low: Series, close: Series,
|
||||
length: int = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""William's Percent R (WILLR)
|
||||
|
||||
William's Percent R is a momentum oscillator similar to the RSI that
|
||||
@@ -30,7 +33,7 @@ def willr(high: Series, low: Series, close: Series, length: int = None, talib: b
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 14
|
||||
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
|
||||
_length = max(length, min_periods)
|
||||
@@ -42,7 +45,7 @@ def willr(high: Series, low: Series, close: Series, length: int = None, talib: b
|
||||
|
||||
if high is None or low is None or close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import WILLR
|
||||
willr = WILLR(high, low, close, length)
|
||||
@@ -56,13 +59,13 @@ def willr(high: Series, low: Series, close: Series, length: int = None, talib: b
|
||||
if offset != 0:
|
||||
willr = willr.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
willr.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
willr.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
willr.name = f"WILLR_{length}"
|
||||
willr.category = "momentum"
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ from .ichimoku import ichimoku
|
||||
from .jma import jma
|
||||
from .kama import kama
|
||||
from .linreg import linreg
|
||||
from .ma import ma
|
||||
from .mcgd import mcgd
|
||||
from .midpoint import midpoint
|
||||
from .midprice import midprice
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# from numpy import nan as npNaN
|
||||
from pandas import DataFrame, Series
|
||||
from .smma import smma
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from .smma import smma
|
||||
|
||||
|
||||
def alligator(close: Series, jaw: int = None, teeth: int = None, lips: int = None, talib: bool = None,
|
||||
offset: int = None, **kwargs) -> DataFrame:
|
||||
def alligator(
|
||||
close: Series, jaw: int = None, teeth: int = None, lips: int = None,
|
||||
talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Bill Williams Alligator (ALLIGATOR)
|
||||
|
||||
The Alligator Indicator was developed by Bill Williams and combines moving
|
||||
@@ -37,7 +39,7 @@ def alligator(close: Series, jaw: int = None, teeth: int = None, lips: int = Non
|
||||
Returns:
|
||||
pd.DataFrame: JAW, TEETH, LIPS columns.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
jaw = int(jaw) if jaw and jaw > 0 else 13
|
||||
teeth = int(teeth) if teeth and teeth > 0 else 8
|
||||
lips = int(lips) if lips and lips > 0 else 5
|
||||
@@ -47,7 +49,7 @@ def alligator(close: Series, jaw: int = None, teeth: int = None, lips: int = Non
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# 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)
|
||||
@@ -58,7 +60,7 @@ def alligator(close: Series, jaw: int = None, teeth: int = None, lips: int = Non
|
||||
gator_teeth = gator_teeth.shift(offset)
|
||||
gator_lips = gator_lips.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
gator_jaw.fillna(kwargs["fillna"], inplace=True)
|
||||
gator_teeth.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -68,7 +70,7 @@ def alligator(close: Series, jaw: int = None, teeth: int = None, lips: int = Non
|
||||
gator_teeth.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
gator_lips.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
_props = f"_{jaw}_{teeth}_{lips}"
|
||||
data = {
|
||||
f"AGj{_props}": gator_jaw,
|
||||
|
||||
+17
-19
@@ -1,18 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import floor as npFloor
|
||||
from numpy import append as npAppend
|
||||
from numpy import arange as npArange
|
||||
from numpy import array as npArray
|
||||
from numpy import exp as npExp
|
||||
from numpy import nan as npNaN
|
||||
from numpy import tensordot as npTensordot
|
||||
from numpy import append, arange, array, exp, floor, nan, tensordot
|
||||
from numpy.version import version as npVersion
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, strided_window, verify_series
|
||||
|
||||
|
||||
def alma(close: Series, length: int = None, sigma: float = None, dist_offset: float = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def alma(
|
||||
close: Series, length: int = None,
|
||||
sigma: float = None, dist_offset: float = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Arnaud Legoux Moving Average (ALMA)
|
||||
|
||||
The ALMA moving average uses the curve of the Normal (Gauss) distribution, which
|
||||
@@ -40,7 +37,7 @@ def alma(close: Series, length: int = None, sigma: float = None, dist_offset: fl
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if isinstance(length, int) and length > 0 else 9
|
||||
sigma = float(sigma) if isinstance(sigma, float) and sigma > 0 else 6.0
|
||||
if isinstance(dist_offset, float) and dist_offset >= 0 and dist_offset <= 1:
|
||||
@@ -52,31 +49,32 @@ def alma(close: Series, length: int = None, sigma: float = None, dist_offset: fl
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
x = npArange(length)
|
||||
k = npFloor(offset_ * (length - 1))
|
||||
weights = npExp(-0.5 * ((sigma / length) * (x - k)) ** 2)
|
||||
# Calculate
|
||||
np_close = close.values
|
||||
x = arange(length)
|
||||
k = floor(offset_ * (length - 1))
|
||||
weights = exp(-0.5 * ((sigma / length) * (x - k)) ** 2)
|
||||
weights /= weights.sum()
|
||||
|
||||
if npVersion >= "1.20.0":
|
||||
from numpy.lib.stride_tricks import sliding_window_view
|
||||
window = sliding_window_view(npArray(close), length)
|
||||
window = sliding_window_view(np_close, length)
|
||||
else:
|
||||
window = strided_window(npArray(close), length)
|
||||
result = npAppend(npArray([npNaN] * (length - 1)), npTensordot(window, weights, axes=1))
|
||||
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)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
alma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
alma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
alma.name = f"ALMA_{length}_{sigma}_{offset_}"
|
||||
alma.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .ema import ema
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from .ema import ema
|
||||
|
||||
|
||||
def dema(close: Series, length: int = None, talib: bool = None, offset: int = None, **kwargs) -> Series:
|
||||
def dema(
|
||||
close: Series, length: int = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Double Exponential Moving Average (DEMA)
|
||||
|
||||
The Double Exponential Moving Average attempts to a smoother average with less
|
||||
@@ -28,7 +31,7 @@ def dema(close: Series, length: int = None, talib: bool = None, offset: int = No
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
close = verify_series(close, length)
|
||||
offset = get_offset(offset)
|
||||
@@ -36,7 +39,7 @@ def dema(close: Series, length: int = None, talib: bool = None, offset: int = No
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import DEMA
|
||||
dema = DEMA(close, length)
|
||||
@@ -49,13 +52,13 @@ def dema(close: Series, length: int = None, talib: bool = None, offset: int = No
|
||||
if offset != 0:
|
||||
dema = dema.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
dema.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
dema.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
dema.name = f"DEMA_{length}"
|
||||
dema.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import fibonacci, get_offset, verify_series, weights
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import fibonacci, get_offset, verify_series, weights
|
||||
|
||||
|
||||
def fwma(close: Series, length: int = None, asc: bool = None, offset: int = None, **kwargs) -> Series:
|
||||
def fwma(
|
||||
close: Series, length: int = None, asc: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Fibonacci's Weighted Moving Average (FWMA)
|
||||
|
||||
Fibonacci's Weighted Moving Average is similar to a Weighted Moving Average
|
||||
@@ -24,7 +27,7 @@ def fwma(close: Series, length: int = None, asc: bool = None, offset: int = None
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
asc = asc if asc else True
|
||||
close = verify_series(close, length)
|
||||
@@ -32,7 +35,7 @@ def fwma(close: Series, length: int = None, asc: bool = None, offset: int = None
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
fibs = fibonacci(n=length, weighted=True)
|
||||
fwma = close.rolling(length, min_periods=length).apply(weights(fibs), raw=True)
|
||||
|
||||
@@ -40,13 +43,13 @@ def fwma(close: Series, length: int = None, asc: bool = None, offset: int = None
|
||||
if offset != 0:
|
||||
fwma = fwma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
fwma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
fwma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
fwma.name = f"FWMA_{length}"
|
||||
fwma.category = "overlap"
|
||||
|
||||
|
||||
+14
-11
@@ -1,12 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import nan as npNaN
|
||||
from numpy import nan
|
||||
from pandas import DataFrame, Series
|
||||
from .ma import ma
|
||||
from pandas_ta.ma import ma
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def hilo(high: Series, low: Series, close: Series, high_length: int = None, low_length: int = None,
|
||||
mamode: str = None, offset: int = None, **kwargs) -> DataFrame:
|
||||
def hilo(
|
||||
high: Series, low: Series, close: Series,
|
||||
high_length: int = None, low_length: int = None, mamode: str = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Gann HiLo Activator(HiLo)
|
||||
|
||||
The Gann High Low Activator Indicator was created by Robert Krausz in a 1998
|
||||
@@ -42,7 +45,7 @@ def hilo(high: Series, low: Series, close: Series, high_length: int = None, low_
|
||||
Returns:
|
||||
pd.DataFrame: HILO (line), HILOl (long), HILOs (short) columns.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
high_length = int(high_length) if high_length and high_length > 0 else 13
|
||||
low_length = int(low_length) if low_length and low_length > 0 else 21
|
||||
mamode = mamode.lower() if isinstance(mamode, str) else "sma"
|
||||
@@ -54,11 +57,11 @@ def hilo(high: Series, low: Series, close: Series, high_length: int = None, low_
|
||||
|
||||
if high is None or low is None or close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
m = close.size
|
||||
hilo = Series(npNaN, index=close.index)
|
||||
long = Series(npNaN, index=close.index)
|
||||
short = Series(npNaN, index=close.index)
|
||||
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)
|
||||
@@ -78,7 +81,7 @@ def hilo(high: Series, low: Series, close: Series, high_length: int = None, low_
|
||||
long = long.shift(offset)
|
||||
short = short.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
hilo.fillna(kwargs["fillna"], inplace=True)
|
||||
long.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -88,7 +91,7 @@ def hilo(high: Series, low: Series, close: Series, high_length: int = None, low_
|
||||
long.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
short.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# 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)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def hl2(high: Series, low: Series, offset: int = None, **kwargs) -> Series:
|
||||
def hl2(
|
||||
high: Series, low: Series,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""HL2
|
||||
|
||||
HL2 is the midpoint/average of high and low.
|
||||
@@ -16,19 +19,19 @@ def hl2(high: Series, low: Series, offset: int = None, **kwargs) -> Series:
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
hl2 = 0.5 * (high + low)
|
||||
# Calculate
|
||||
hl2 = Series(0.5 * (high.values + low.values), index=high.index)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
hl2 = hl2.shift(offset)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
hl2.name = "HL2"
|
||||
hl2.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def hlc3(high: Series, low: Series, close: Series, talib: bool = None, offset: int = None, **kwargs) -> Series:
|
||||
def hlc3(
|
||||
high: Series, low: Series, close: Series, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""HLC3
|
||||
|
||||
HLC3 is the average of high, low and close.
|
||||
@@ -18,25 +21,25 @@ def hlc3(high: Series, low: Series, close: Series, talib: bool = None, offset: i
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
offset = get_offset(offset)
|
||||
mode_tal = bool(talib) if isinstance(talib, bool) else True
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import TYPPRICE
|
||||
hlc3 = TYPPRICE(high, low, close)
|
||||
else:
|
||||
hlc3 = (high + low + close) / 3.0
|
||||
hlc3 = Series((high.values + low.values + close.values) / 3.0, index=close.index)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
hlc3 = hlc3.shift(offset)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
hlc3.name = "HLC3"
|
||||
hlc3.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import sqrt as npSqrt
|
||||
from .wma import wma
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from numpy import sqrt
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from .wma import wma
|
||||
|
||||
|
||||
def hma(close: Series, length: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def hma(
|
||||
close: Series, length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Hull Moving Average (HMA)
|
||||
|
||||
The Hull Exponential Moving Average attempts to reduce or remove lag in moving
|
||||
@@ -26,16 +29,16 @@ def hma(close: Series, length: int = None, offset: int = None, **kwargs) -> Seri
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
close = verify_series(close, length)
|
||||
offset = get_offset(offset)
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
half_length = int(length / 2)
|
||||
sqrt_length = int(npSqrt(length))
|
||||
sqrt_length = int(sqrt(length))
|
||||
|
||||
wmaf = wma(close=close, length=half_length)
|
||||
wmas = wma(close=close, length=length)
|
||||
@@ -45,13 +48,13 @@ def hma(close: Series, length: int = None, offset: int = None, **kwargs) -> Seri
|
||||
if offset != 0:
|
||||
hma = hma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
hma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
hma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
hma.name = f"HMA_{length}"
|
||||
hma.category = "overlap"
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@ from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def hwma(close: Series, na: float = None, nb: float = None, nc: float = None, offset: int = None, **kwargs) -> Series:
|
||||
def hwma(
|
||||
close: Series, na: float = None, nb: float = None, nc: float = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""HWMA (Holt-Winter Moving Average)
|
||||
|
||||
Indicator HWMA (Holt-Winter Moving Average) is a three-parameter moving average
|
||||
@@ -30,14 +33,14 @@ def hwma(close: Series, na: float = None, nb: float = None, nc: float = None, of
|
||||
Returns:
|
||||
pd.Series: hwma
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
na = float(na) if na and na > 0 and na < 1 else 0.2
|
||||
nb = float(nb) if nb and nb > 0 and nb < 1 else 0.1
|
||||
nc = float(nc) if nc and nc > 0 and nc < 1 else 0.1
|
||||
close = verify_series(close)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
last_a = last_v = 0
|
||||
last_f = close.iloc[0]
|
||||
|
||||
@@ -56,13 +59,13 @@ def hwma(close: Series, na: float = None, nb: float = None, nc: float = None, of
|
||||
if offset != 0:
|
||||
hwma = hwma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
hwma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
hwma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
suffix = f"{na}_{nb}_{nc}"
|
||||
hwma.name = f"HWMA_{suffix}"
|
||||
hwma.category = "overlap"
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas import date_range, DataFrame, RangeIndex, Timedelta, Series
|
||||
from .midprice import midprice
|
||||
from pandas_ta.utils import get_offset, verify_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) -> DataFrame:
|
||||
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
|
||||
) -> DataFrame:
|
||||
"""Ichimoku Kinkō Hyō (ichimoku)
|
||||
|
||||
Developed Pre WWII as a forecasting model for financial markets.
|
||||
@@ -33,6 +37,7 @@ def ichimoku(high: Series, low: Series, close: Series, tenkan: int = None, kijun
|
||||
and chikou_span columns
|
||||
For the forward looking period: spanA and spanB columns
|
||||
"""
|
||||
# Validate
|
||||
tenkan = int(tenkan) if tenkan and tenkan > 0 else 9
|
||||
kijun = int(kijun) if kijun and kijun > 0 else 26
|
||||
senkou = int(senkou) if senkou and senkou > 0 else 52
|
||||
@@ -46,7 +51,7 @@ def ichimoku(high: Series, low: Series, close: Series, tenkan: int = None, kijun
|
||||
|
||||
if high is None or low is None or close is None: return None, None
|
||||
|
||||
# Calculate Result
|
||||
# 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)
|
||||
@@ -68,7 +73,7 @@ def ichimoku(high: Series, low: Series, close: Series, tenkan: int = None, kijun
|
||||
span_b = span_b.shift(offset)
|
||||
chikou_span = chikou_span.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
span_a.fillna(kwargs["fillna"], inplace=True)
|
||||
span_b.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -78,7 +83,7 @@ def ichimoku(high: Series, low: Series, close: Series, tenkan: int = None, kijun
|
||||
span_b.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
chikou_span.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
span_a.name = f"ISA_{tenkan}"
|
||||
span_b.name = f"ISB_{kijun}"
|
||||
tenkan_sen.name = f"ITS_{tenkan}"
|
||||
|
||||
+26
-26
@@ -1,15 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import average as npAverage
|
||||
from numpy import nan as npNaN
|
||||
from numpy import log as npLog
|
||||
from numpy import power as npPower
|
||||
from numpy import sqrt as npSqrt
|
||||
from numpy import zeros_like as npZeroslike
|
||||
# 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.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def jma(close: Series, length: int = None, phase: float = None, offset: int = None, **kwargs) -> Series:
|
||||
def jma(
|
||||
close: Series, length: int = None, phase: float = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Jurik Moving Average Average (JMA)
|
||||
|
||||
Mark Jurik's Moving Average (JMA) attempts to eliminate noise to see the "true"
|
||||
@@ -33,17 +33,17 @@ def jma(close: Series, length: int = None, phase: float = None, offset: int = No
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
_length = int(length) if length and length > 0 else 7
|
||||
phase = float(phase) if phase and phase != 0 else 0
|
||||
close = verify_series(close, _length)
|
||||
offset = get_offset(offset)
|
||||
if close is None: return
|
||||
|
||||
# Define base variables
|
||||
jma = npZeroslike(close)
|
||||
volty = npZeroslike(close)
|
||||
v_sum = npZeroslike(close)
|
||||
# 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[0]
|
||||
@@ -52,9 +52,9 @@ def jma(close: Series, length: int = None, phase: float = None, offset: int = No
|
||||
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((npLog(npSqrt(length)) / npLog(2.0)) + 2.0, 0)
|
||||
length1 = max((log(sqrt(length)) / log(2.0)) + 2.0, 0)
|
||||
pow1 = max(length1 - 2.0, 0.5)
|
||||
length2 = length1 * npSqrt(length)
|
||||
length2 = length1 * sqrt(length)
|
||||
bet = length2 / (length2 + 1)
|
||||
beta = 0.45 * (_length - 1) / (0.45 * (_length - 1) + 2.0)
|
||||
|
||||
@@ -69,46 +69,46 @@ def jma(close: Series, length: int = None, phase: float = None, offset: int = No
|
||||
|
||||
# Relative price volatility factor
|
||||
v_sum[i] = v_sum[i - 1] + (volty[i] - volty[max(i - sum_length, 0)]) / sum_length
|
||||
avg_volty = npAverage(v_sum[max(i - 65, 0):i + 1])
|
||||
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(npPower(length1, 1 / pow1), d_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 = npPower(r_volty, pow1)
|
||||
kv = npPower(bet, npSqrt(pow2))
|
||||
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 = npPower(r_volty, pow1)
|
||||
alpha = npPower(beta, power)
|
||||
power = np_power(r_volty, pow1)
|
||||
alpha = np_power(beta, power)
|
||||
|
||||
# 1st stage - prelimimary smoothing by adaptive EMA
|
||||
ma1 = ((1 - alpha) * price) + (alpha * ma1)
|
||||
ma1 = (1 - alpha) * price + alpha * ma1
|
||||
|
||||
# 2nd stage - one more prelimimary smoothing by Kalman filter
|
||||
det0 = ((price - ma1) * (1 - beta)) + (beta * det0)
|
||||
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
|
||||
|
||||
# Remove initial lookback data and convert to pandas frame
|
||||
jma = Series(jma, index=close.index)
|
||||
jma.iloc[0:_length - 1] = npNaN
|
||||
jma.iloc[0:_length - 1] = nan
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
jma = jma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
jma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
jma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
jma.name = f"JMA_{_length}_{phase}"
|
||||
jma.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import nan as npNaN
|
||||
from numpy import nan
|
||||
from pandas import Series
|
||||
from pandas_ta.overlap.ma import ma
|
||||
from pandas_ta.ma import ma
|
||||
from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series
|
||||
|
||||
|
||||
def kama(close: Series, length: int = None, fast: int = None, slow: int = None, mamode: str = None,
|
||||
drift: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def kama(
|
||||
close: Series, length: int = None, fast: int = None, slow: int = None,
|
||||
mamode: str = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Kaufman's Adaptive Moving Average (KAMA)
|
||||
|
||||
Developed by Perry Kaufman, Kaufman's Adaptive Moving Average (KAMA) is a moving average
|
||||
@@ -37,7 +40,7 @@ def kama(close: Series, length: int = None, fast: int = None, slow: int = None,
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
fast = int(fast) if fast and fast > 0 else 2
|
||||
slow = int(slow) if slow and slow > 0 else 30
|
||||
@@ -55,7 +58,7 @@ def kama(close: Series, length: int = None, fast: int = None, slow: int = None,
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
def weight(length: int) -> float:
|
||||
return 2 / (length + 1)
|
||||
|
||||
@@ -71,7 +74,7 @@ def kama(close: Series, length: int = None, fast: int = None, slow: int = None,
|
||||
|
||||
m = close.size
|
||||
ma0 = ma(mamode, close.iloc[:length], length=length, **kwargs).iloc[-1]
|
||||
result = [npNaN for _ in range(0, length - 1)] + [ma0]
|
||||
result = [nan for _ in range(0, length - 1)] + [ma0]
|
||||
for i in range(length, m):
|
||||
result.append(sc.iloc[i] * close.iloc[i] + (1 - sc.iloc[i]) * result[i - 1])
|
||||
|
||||
@@ -81,13 +84,13 @@ def kama(close: Series, length: int = None, fast: int = None, slow: int = None,
|
||||
if offset != 0:
|
||||
kama = kama.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
kama.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
kama.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
kama.name = f"KAMA_{length}_{fast}_{slow}"
|
||||
kama.category = "overlap"
|
||||
|
||||
|
||||
+23
-19
@@ -1,15 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import array as npArray
|
||||
from numpy import arctan as npAtan
|
||||
from numpy import nan as npNaN
|
||||
from numpy import pi as npPi
|
||||
from numpy.version import version as npVersion
|
||||
from numpy import arctan, nan, pi, zeros_like
|
||||
from numpy.version import version
|
||||
from pandas import Series
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, strided_window, verify_series
|
||||
|
||||
|
||||
def linreg(close: Series, length: int = None, talib: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def linreg(
|
||||
close: Series, length: int = None, talib: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Linear Regression Moving Average (linreg)
|
||||
|
||||
Linear Regression Moving Average (LINREG). This is a simplified version of a
|
||||
@@ -42,7 +42,7 @@ def linreg(close: Series, length: int = None, talib: int = None, offset: int = N
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 14
|
||||
close = verify_series(close, length)
|
||||
offset = get_offset(offset)
|
||||
@@ -56,7 +56,9 @@ def linreg(close: Series, length: int = None, talib: int = None, offset: int = N
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
np_close = close.values
|
||||
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import LINEARREG, LINEARREG_ANGLE, LINEARREG_INTERCEPT, LINEARREG_SLOPE, TSF
|
||||
if tsf:
|
||||
@@ -70,6 +72,7 @@ def linreg(close: Series, length: int = None, talib: int = None, offset: int = N
|
||||
else:
|
||||
linreg = LINEARREG(close, timeperiod=length)
|
||||
else:
|
||||
linreg_ = zeros_like(np_close)
|
||||
x = range(1, length + 1) # [1, 2, ..., n] from 1 to n keeps Sum(xy) low
|
||||
x_sum = 0.5 * length * (length + 1)
|
||||
x2_sum = x_sum * (2 * length + 1) / 3
|
||||
@@ -87,9 +90,9 @@ def linreg(close: Series, length: int = None, talib: int = None, offset: int = N
|
||||
return b
|
||||
|
||||
if angle:
|
||||
theta = npAtan(m)
|
||||
theta = arctan(m)
|
||||
if degrees:
|
||||
theta *= 180 / npPi
|
||||
theta *= 180 / pi
|
||||
return theta
|
||||
|
||||
if r:
|
||||
@@ -100,25 +103,26 @@ def linreg(close: Series, length: int = None, talib: int = None, offset: int = N
|
||||
|
||||
return m * length + b if not tsf else m * (length - 1) + b
|
||||
|
||||
if npVersion >= "1.20.0":
|
||||
if version >= "1.20.0":
|
||||
from numpy.lib.stride_tricks import sliding_window_view
|
||||
linreg_ = [linear_regression(_) for _ in sliding_window_view(npArray(close), length)]
|
||||
else:
|
||||
linreg_ = [linear_regression(_) for _ in strided_window(npArray(close), length)]
|
||||
linreg_ = [linear_regression(_) for _ in sliding_window_view(np_close, length)]
|
||||
|
||||
linreg = Series([npNaN] * (length - 1) + linreg_, index=close.index)
|
||||
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)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
linreg.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
linreg.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
linreg.name = f"LR"
|
||||
if slope: linreg.name += "m"
|
||||
if intercept: linreg.name += "b"
|
||||
@@ -128,4 +132,4 @@ def linreg(close: Series, length: int = None, talib: int = None, offset: int = N
|
||||
linreg.name += f"_{length}"
|
||||
linreg.category = "overlap"
|
||||
|
||||
return linreg
|
||||
return linreg
|
||||
@@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def mcgd(close: Series, length: int = None, offset: int = None, c: float = None, **kwargs) -> Series:
|
||||
def mcgd(
|
||||
close: Series, length: int = None, c: float = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""McGinley Dynamic Indicator
|
||||
|
||||
The McGinley Dynamic looks like a moving average line, yet it is actually a
|
||||
@@ -30,7 +33,7 @@ def mcgd(close: Series, length: int = None, offset: int = None, c: float = None,
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
c = float(c) if c and 0 < c <= 1 else 1
|
||||
close = verify_series(close, length)
|
||||
@@ -38,7 +41,7 @@ def mcgd(close: Series, length: int = None, offset: int = None, c: float = None,
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
close = close.copy()
|
||||
|
||||
def mcg_(series):
|
||||
@@ -53,13 +56,13 @@ def mcgd(close: Series, length: int = None, offset: int = None, c: float = None,
|
||||
if offset != 0:
|
||||
mcg_ds = mcg_ds.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
mcg_ds.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
mcg_ds.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
mcg_ds.name = f"MCGD_{length}"
|
||||
mcg_ds.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def midpoint(close: Series, length: int = None, talib: bool = None, offset: int = None, **kwargs) -> Series:
|
||||
def midpoint(
|
||||
close: Series, length: int = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Midpoint
|
||||
|
||||
The Midpoint is the average of the rolling high and low of period length.
|
||||
@@ -23,7 +26,7 @@ def midpoint(close: Series, length: int = None, talib: bool = None, offset: int
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 2
|
||||
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
|
||||
close = verify_series(close, max(length, min_periods))
|
||||
@@ -32,7 +35,7 @@ def midpoint(close: Series, length: int = None, talib: bool = None, offset: int
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import MIDPOINT
|
||||
midpoint = MIDPOINT(close, length)
|
||||
@@ -45,13 +48,13 @@ def midpoint(close: Series, length: int = None, talib: bool = None, offset: int
|
||||
if offset != 0:
|
||||
midpoint = midpoint.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
midpoint.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
midpoint.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
midpoint.name = f"MIDPOINT_{length}"
|
||||
midpoint.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def midprice(high: Series, low: Series, length: int = None, talib: bool = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def midprice(
|
||||
high: Series, low: Series, length: int = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Midprice
|
||||
|
||||
The Midprice is the average of the rolling high and low of period length.
|
||||
@@ -25,7 +27,7 @@ def midprice(high: Series, low: Series, length: int = None, talib: bool = None,
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 2
|
||||
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
|
||||
_length = max(length, min_periods)
|
||||
@@ -36,7 +38,7 @@ def midprice(high: Series, low: Series, length: int = None, talib: bool = None,
|
||||
|
||||
if high is None or low is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import MIDPRICE
|
||||
midprice = MIDPRICE(high, low, length)
|
||||
@@ -49,13 +51,13 @@ def midprice(high: Series, low: Series, length: int = None, talib: bool = None,
|
||||
if offset != 0:
|
||||
midprice = midprice.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
midprice.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
midprice.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
midprice.name = f"MIDPRICE_{length}"
|
||||
midprice.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def ohlc4(open_: Series, high: Series, low: Series, close: Series, offset: int = None, **kwargs) -> Series:
|
||||
def ohlc4(
|
||||
open_: Series, high: Series, low: Series, close: Series,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""OHLC4
|
||||
|
||||
OHLC4 is the average of open, high, low and close.
|
||||
@@ -18,21 +21,24 @@ def ohlc4(open_: Series, high: Series, low: Series, close: Series, offset: int =
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
open_ = verify_series(open_)
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
ohlc4 = 0.25 * (open_ + high + low + close)
|
||||
# Calculate
|
||||
ohlc4 = Series(
|
||||
0.25 * (open_.values + high.values + low.values + close.values),
|
||||
index=close.index
|
||||
)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
ohlc4 = ohlc4.shift(offset)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
ohlc4.name = "OHLC4"
|
||||
ohlc4.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import get_offset, pascals_triangle, verify_series, weights
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, pascals_triangle, verify_series, weights
|
||||
|
||||
|
||||
def pwma(close: Series, length: int = None, asc: bool = None, offset: bool = None, **kwargs) -> Series:
|
||||
def pwma(
|
||||
close: Series, length: int = None, asc: bool = None,
|
||||
offset: bool = None, **kwargs
|
||||
) -> Series:
|
||||
"""Pascal's Weighted Moving Average (PWMA)
|
||||
|
||||
Pascal's Weighted Moving Average is similar to a symmetric triangular window
|
||||
@@ -24,7 +27,7 @@ def pwma(close: Series, length: int = None, asc: bool = None, offset: bool = Non
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
asc = asc if asc else True
|
||||
close = verify_series(close, length)
|
||||
@@ -32,7 +35,7 @@ def pwma(close: Series, length: int = None, asc: bool = None, offset: bool = Non
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
triangle = pascals_triangle(n=length - 1, weighted=True)
|
||||
pwma = close.rolling(length, min_periods=length).apply(weights(triangle), raw=True)
|
||||
|
||||
@@ -40,13 +43,13 @@ def pwma(close: Series, length: int = None, asc: bool = None, offset: bool = Non
|
||||
if offset != 0:
|
||||
pwma = pwma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
pwma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
pwma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
pwma.name = f"PWMA_{length}"
|
||||
pwma.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def rma(close: Series, length: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def rma(
|
||||
close: Series, length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""wildeR's Moving Average (RMA)
|
||||
|
||||
The WildeR's Moving Average is simply an Exponential Moving Average (EMA) with
|
||||
@@ -25,7 +28,7 @@ def rma(close: Series, length: int = None, offset: int = None, **kwargs) -> Seri
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
alpha = (1.0 / length) if length > 0 else 0.5
|
||||
close = verify_series(close, length)
|
||||
@@ -33,20 +36,20 @@ def rma(close: Series, length: int = None, offset: int = None, **kwargs) -> Seri
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
rma = close.ewm(alpha=alpha, min_periods=length).mean()
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
rma = rma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
rma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
rma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
rma.name = f"RMA_{length}"
|
||||
rma.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import pi as npPi
|
||||
from numpy import sin as npSin
|
||||
from numpy import pi, sin
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series, weights
|
||||
|
||||
|
||||
def sinwma(close: Series, length: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def sinwma(
|
||||
close: Series, length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Sine Weighted Moving Average (SWMA)
|
||||
|
||||
A weighted average using sine cycles. The middle term(s) of the average have the
|
||||
@@ -27,15 +29,15 @@ def sinwma(close: Series, length: int = None, offset: int = None, **kwargs) -> S
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 14
|
||||
close = verify_series(close, length)
|
||||
offset = get_offset(offset)
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
sines = Series([npSin((i + 1) * npPi / (length + 1)) for i in range(0, length)])
|
||||
# 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)
|
||||
@@ -44,13 +46,13 @@ def sinwma(close: Series, length: int = None, offset: int = None, **kwargs) -> S
|
||||
if offset != 0:
|
||||
sinwma = sinwma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
sinwma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
sinwma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
sinwma.name = f"SINWMA_{length}"
|
||||
sinwma.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import nan as npNaN
|
||||
from pandas_ta.overlap.ma import ma
|
||||
from numpy import nan
|
||||
from pandas import Series
|
||||
from pandas_ta.ma import ma
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def smma(close: Series, length: int = None, mamode: str = None, talib: bool = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def smma(
|
||||
close: Series, length: int = None,
|
||||
mamode: str = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""SMoothed Moving Average (SMMA)
|
||||
|
||||
The SMoothed Moving Average (SMMA) is bootstrapped by default with a Simple
|
||||
@@ -37,7 +40,7 @@ def smma(close: Series, length: int = None, mamode: str = None, talib: bool = No
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 7
|
||||
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
|
||||
close = verify_series(close, max(length, min_periods))
|
||||
@@ -47,10 +50,10 @@ def smma(close: Series, length: int = None, mamode: str = None, talib: bool = No
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
m = close.size
|
||||
smma = close.copy()
|
||||
smma[:length - 1] = npNaN
|
||||
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):
|
||||
@@ -60,13 +63,13 @@ def smma(close: Series, length: int = None, mamode: str = None, talib: bool = No
|
||||
if offset != 0:
|
||||
smma = smma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
smma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
smma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
smma.name = f"SMMA_{length}"
|
||||
smma.category = "overlap"
|
||||
|
||||
|
||||
+16
-11
@@ -1,5 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta import np, pd
|
||||
from numpy import copy, cos, exp, ndarray
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
try:
|
||||
@@ -9,12 +10,12 @@ except ImportError:
|
||||
|
||||
|
||||
@njit
|
||||
def np_ssf3(x: np.ndarray, n: int, pi: float, sqrt3: float):
|
||||
def np_ssf3(x: ndarray, n: int, pi: float, sqrt3: float):
|
||||
"""John F. Ehler's Super Smoother Filter by Everget (3 poles), Tradingview
|
||||
https://www.tradingview.com/script/VdJy0yBJ-Ehlers-Super-Smoother-Filter/"""
|
||||
m, result = x.size, np.copy(x)
|
||||
a = np.exp(-pi / n)
|
||||
b = 2 * a * np.cos(-pi * sqrt3 / n)
|
||||
m, result = x.size, copy(x)
|
||||
a = exp(-pi / n)
|
||||
b = 2 * a * cos(-pi * sqrt3 / n)
|
||||
c = a * a
|
||||
|
||||
d4 = c * c
|
||||
@@ -29,7 +30,11 @@ def np_ssf3(x: np.ndarray, n: int, pi: float, sqrt3: float):
|
||||
return result
|
||||
|
||||
|
||||
def ssf3(close, length=None, pi=None, sqrt3=None, offset=None, **kwargs):
|
||||
def ssf3(
|
||||
close: Series, length: int = None,
|
||||
pi: float = None, sqrt3: float = None,
|
||||
offset=None, **kwargs
|
||||
):
|
||||
"""Ehler's 3 Pole Super Smoother Filter (SSF) © 2013
|
||||
|
||||
John F. Ehlers's solution to reduce lag and remove aliasing noise with his
|
||||
@@ -62,7 +67,7 @@ def ssf3(close, length=None, pi=None, sqrt3=None, offset=None, **kwargs):
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if isinstance(length, int) and length > 0 else 20
|
||||
pi = float(pi) if isinstance(pi, float) and pi > 0 else 3.14159
|
||||
sqrt3 = float(sqrt3) if isinstance(sqrt3, float) and sqrt3 > 0 else 1.732
|
||||
@@ -71,22 +76,22 @@ def ssf3(close, length=None, pi=None, sqrt3=None, offset=None, **kwargs):
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
np_close = close.values
|
||||
ssf = np_ssf3(np_close, length, pi, sqrt3)
|
||||
ssf = pd.Series(ssf, index=close.index)
|
||||
ssf = Series(ssf, index=close.index)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
ssf = ssf.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
ssf.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
ssf.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
ssf.name = f"SSF3_{length}"
|
||||
ssf.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import nan as npNaN
|
||||
from numpy import nan
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta.overlap import hl2
|
||||
from pandas_ta.volatility import atr
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas_ta.volatility import atr
|
||||
|
||||
|
||||
def supertrend(high: Series, low: Series, close: Series, length: int = None, multiplier: float = None,
|
||||
offset: int = None, **kwargs) -> DataFrame:
|
||||
def supertrend(
|
||||
high: Series, low: Series, close: Series,
|
||||
length: int = None, multiplier: float = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Supertrend (supertrend)
|
||||
|
||||
Supertrend is an overlap indicator. It is used to help identify trend
|
||||
@@ -33,7 +36,7 @@ def supertrend(high: Series, low: Series, close: Series, length: int = None, mul
|
||||
Returns:
|
||||
pd.DataFrame: SUPERT (trend), SUPERTd (direction), SUPERTl (long), SUPERTs (short) columns.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 7
|
||||
multiplier = float(multiplier) if multiplier and multiplier > 0 else 3.0
|
||||
high = verify_series(high, length)
|
||||
@@ -43,10 +46,10 @@ def supertrend(high: Series, low: Series, close: Series, length: int = None, mul
|
||||
|
||||
if high is None or low is None or close is None: return
|
||||
|
||||
# Calculate Results
|
||||
# Calculate
|
||||
m = close.size
|
||||
dir_, trend = [1] * m, [0] * m
|
||||
long, short = [npNaN] * m, [npNaN] * m
|
||||
long, short = [nan] * m, [nan] * m
|
||||
|
||||
hl2_ = hl2(high, low)
|
||||
matr = multiplier * atr(high, low, close, length)
|
||||
@@ -70,7 +73,6 @@ def supertrend(high: Series, low: Series, close: Series, length: int = None, mul
|
||||
else:
|
||||
trend[i] = short[i] = upperband.iloc[i]
|
||||
|
||||
# Prepare DataFrame to return
|
||||
_props = f"_{length}_{multiplier}"
|
||||
df = DataFrame({
|
||||
f"SUPERT{_props}": trend,
|
||||
@@ -82,11 +84,11 @@ def supertrend(high: Series, low: Series, close: Series, length: int = None, mul
|
||||
df.name = f"SUPERT{_props}"
|
||||
df.category = "overlap"
|
||||
|
||||
# Apply offset if needed
|
||||
# Offset
|
||||
if offset != 0:
|
||||
df = df.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
df.fillna(kwargs["fillna"], inplace=True)
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import get_offset, symmetric_triangle, verify_series, weights
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, symmetric_triangle, verify_series, weights
|
||||
|
||||
|
||||
def swma(close: Series, length: int = None, asc: bool = None, offset: int = None, **kwargs) -> Series:
|
||||
def swma(
|
||||
close: Series, length: int = None, asc: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Symmetric Weighted Moving Average (SWMA)
|
||||
|
||||
Symmetric Weighted Moving Average where weights are based on a symmetric
|
||||
@@ -27,31 +30,28 @@ def swma(close: Series, length: int = None, asc: bool = None, offset: int = None
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
# min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
|
||||
asc = asc if asc else True
|
||||
close = verify_series(close, length)
|
||||
offset = get_offset(offset)
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
triangle = symmetric_triangle(length, weighted=True)
|
||||
swma = close.rolling(length, min_periods=length).apply(weights(triangle), raw=True)
|
||||
# swma = close.rolling(length).apply(weights(triangle), raw=True)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
swma = swma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
swma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
swma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
swma.name = f"SWMA_{length}"
|
||||
swma.category = "overlap"
|
||||
|
||||
|
||||
+11
-8
@@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .ema import ema
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from .ema import ema
|
||||
|
||||
|
||||
def t3(close: Series, length: int = None, a: float = None, talib: bool = None, offset: int = None, **kwargs) -> Series:
|
||||
def t3(
|
||||
close: Series, length: int = None, a: float = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Tim Tillson's T3 Moving Average (T3)
|
||||
|
||||
Tim Tillson's T3 Moving Average is considered a smoother and more responsive
|
||||
@@ -31,7 +34,7 @@ def t3(close: Series, length: int = None, a: float = None, talib: bool = None, o
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
a = float(a) if a and a > 0 and a < 1 else 0.7
|
||||
close = verify_series(close, length)
|
||||
@@ -40,7 +43,7 @@ def t3(close: Series, length: int = None, a: float = None, talib: bool = None, o
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import T3
|
||||
t3 = T3(close, length, a)
|
||||
@@ -62,13 +65,13 @@ def t3(close: Series, length: int = None, a: float = None, talib: bool = None, o
|
||||
if offset != 0:
|
||||
t3 = t3.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
t3.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
t3.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
t3.name = f"T3_{length}_{a}"
|
||||
t3.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .ema import ema
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from .ema import ema
|
||||
|
||||
|
||||
def tema(close: Series, length: int = None, talib: bool = None, offset: int = None, **kwargs) -> Series:
|
||||
def tema(
|
||||
close: Series, length: int = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Triple Exponential Moving Average (TEMA)
|
||||
|
||||
A less laggy Exponential Moving Average.
|
||||
@@ -29,7 +32,7 @@ def tema(close: Series, length: int = None, talib: bool = None, offset: int = No
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
close = verify_series(close, length)
|
||||
offset = get_offset(offset)
|
||||
@@ -37,7 +40,7 @@ def tema(close: Series, length: int = None, talib: bool = None, offset: int = No
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import TEMA
|
||||
tema = TEMA(close, length)
|
||||
@@ -51,13 +54,13 @@ def tema(close: Series, length: int = None, talib: bool = None, offset: int = No
|
||||
if offset != 0:
|
||||
tema = tema.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
tema.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
tema.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
tema.name = f"TEMA_{length}"
|
||||
tema.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .sma import sma
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from .sma import sma
|
||||
|
||||
|
||||
def trima(close: Series, length: int = None, talib: bool = None, offset: int = None, **kwargs) -> Series:
|
||||
def trima(
|
||||
close: Series, length: int = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Triangular Moving Average (TRIMA)
|
||||
|
||||
A weighted moving average where the shape of the weights are triangular and the
|
||||
@@ -31,7 +34,7 @@ def trima(close: Series, length: int = None, talib: bool = None, offset: int = N
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
close = verify_series(close, length)
|
||||
offset = get_offset(offset)
|
||||
@@ -39,7 +42,7 @@ def trima(close: Series, length: int = None, talib: bool = None, offset: int = N
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import TRIMA
|
||||
trima = TRIMA(close, length)
|
||||
@@ -52,13 +55,13 @@ def trima(close: Series, length: int = None, talib: bool = None, offset: int = N
|
||||
if offset != 0:
|
||||
trima = trima.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
trima.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
trima.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
trima.name = f"TRIMA_{length}"
|
||||
trima.category = "overlap"
|
||||
|
||||
|
||||
+25
-21
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import nan as npNaN
|
||||
from numpy import nan
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_drift, get_offset, verify_series
|
||||
|
||||
|
||||
def vidya(close: Series, length: int = None, drift: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def vidya(
|
||||
close: Series, length: int = None, drift: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Variable Index Dynamic Average (VIDYA)
|
||||
|
||||
Variable Index Dynamic Average (VIDYA) was developed by Tushar Chande. It is
|
||||
@@ -32,7 +35,7 @@ def vidya(close: Series, length: int = None, drift: int = None, offset: int = No
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 14
|
||||
close = verify_series(close, length)
|
||||
drift = get_drift(drift)
|
||||
@@ -40,41 +43,42 @@ def vidya(close: Series, length: int = None, drift: int = None, offset: int = No
|
||||
|
||||
if close is None: return
|
||||
|
||||
def _cmo(source: Series, n:int , d: int):
|
||||
"""Chande Momentum Oscillator (CMO) Patch
|
||||
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?
|
||||
Weird Circular TypeError!?!
|
||||
"""
|
||||
mom = source.diff(d)
|
||||
positive = mom.copy().clip(lower=0)
|
||||
negative = mom.copy().clip(upper=0).abs()
|
||||
pos_sum = positive.rolling(n).sum()
|
||||
neg_sum = negative.rolling(n).sum()
|
||||
return (pos_sum - neg_sum) / (pos_sum + neg_sum)
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
m = close.size
|
||||
alpha = 2 / (length + 1)
|
||||
abs_cmo = _cmo(close, length, drift).abs()
|
||||
vidya = Series(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: npNaN}, inplace=True)
|
||||
vidya.replace({0: nan}, inplace=True)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
vidya = vidya.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
vidya.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
vidya.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
vidya.name = f"VIDYA_{length}"
|
||||
vidya.category = "overlap"
|
||||
|
||||
return vidya
|
||||
|
||||
|
||||
def _cmo(source: Series, n:int , d: int):
|
||||
"""Chande Momentum Oscillator (CMO) Patch
|
||||
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?
|
||||
Weird Circular TypeError!?
|
||||
"""
|
||||
mom = source.diff(d)
|
||||
positive = mom.copy().clip(lower=0)
|
||||
negative = mom.copy().clip(upper=0).abs()
|
||||
pos_sum = positive.rolling(n).sum()
|
||||
neg_sum = negative.rolling(n).sum()
|
||||
return (pos_sum - neg_sum) / (pos_sum + neg_sum)
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .hlc3 import hlc3
|
||||
from pandas_ta.utils import get_offset, is_datetime_ordered, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.overlap import hlc3
|
||||
from pandas_ta.utils import get_offset, is_datetime_ordered, verify_series
|
||||
|
||||
|
||||
def vwap(high: Series, low: Series, close: Series, volume: Series, anchor: str = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def vwap(
|
||||
high: Series, low: Series, close: Series, volume: Series,
|
||||
anchor: str = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Volume Weighted Average Price (VWAP)
|
||||
|
||||
The Volume Weighted Average Price that measures the average typical price
|
||||
@@ -35,7 +38,7 @@ def vwap(high: Series, low: Series, close: Series, volume: Series, anchor: str =
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
@@ -49,7 +52,7 @@ def vwap(high: Series, low: Series, close: Series, volume: Series, anchor: str =
|
||||
if not is_datetime_ordered(typical_price):
|
||||
print(f"[!] VWAP price series is not datetime ordered. Results may not be as expected.")
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
wp = typical_price * volume
|
||||
vwap = wp.groupby(wp.index.to_period(anchor)).cumsum()
|
||||
vwap /= volume.groupby(volume.index.to_period(anchor)).cumsum()
|
||||
@@ -58,13 +61,13 @@ def vwap(high: Series, low: Series, close: Series, volume: Series, anchor: str =
|
||||
if offset != 0:
|
||||
vwap = vwap.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
vwap.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
vwap.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
vwap.name = f"VWAP_{anchor}"
|
||||
vwap.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .sma import sma
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.overlap import sma
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def vwma(close: Series, volume: Series, length: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def vwma(
|
||||
close: Series, volume: Series, length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Volume Weighted Moving Average (VWMA)
|
||||
|
||||
Volume Weighted Moving Average.
|
||||
@@ -25,7 +28,7 @@ def vwma(close: Series, volume: Series, length: int = None, offset: int = None,
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
close = verify_series(close, length)
|
||||
volume = verify_series(volume, length)
|
||||
@@ -33,7 +36,7 @@ def vwma(close: Series, volume: Series, length: int = None, offset: int = None,
|
||||
|
||||
if close is None or volume is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
pv = close * volume
|
||||
vwma = sma(close=pv, length=length) / sma(close=volume, length=length)
|
||||
|
||||
@@ -41,13 +44,13 @@ def vwma(close: Series, volume: Series, length: int = None, offset: int = None,
|
||||
if offset != 0:
|
||||
vwma = vwma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
vwma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
vwma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
vwma.name = f"VWMA_{length}"
|
||||
vwma.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def wcp(high: Series, low: Series, close: Series, talib: bool = None, offset: int = None, **kwargs) -> Series:
|
||||
def wcp(
|
||||
high: Series, low: Series, close: Series, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Weighted Closing Price (WCP)
|
||||
|
||||
Weighted Closing Price is the weighted price given: high, low
|
||||
@@ -28,31 +31,31 @@ def wcp(high: Series, low: Series, close: Series, talib: bool = None, offset: in
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
high = verify_series(high)
|
||||
low = verify_series(low)
|
||||
close = verify_series(close)
|
||||
offset = get_offset(offset)
|
||||
mode_tal = bool(talib) if isinstance(talib, bool) else True
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import WCLPRICE
|
||||
wcp = WCLPRICE(high, low, close)
|
||||
else:
|
||||
wcp = (high + low + 2 * close) / 4
|
||||
wcp = Series((high.values + low.values + 2 * close.values), index=close.index)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
wcp = wcp.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
wcp.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
wcp.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
wcp.name = "WCP"
|
||||
wcp.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import arange, dot
|
||||
from pandas import Series
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
@@ -29,7 +30,7 @@ def wma(close: Series, length: int = None, asc: bool = None, talib: bool = None,
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
asc = asc if asc else True
|
||||
close = verify_series(close, length)
|
||||
@@ -38,21 +39,18 @@ def wma(close: Series, length: int = None, asc: bool = None, talib: bool = None,
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import WMA
|
||||
wma = WMA(close, length)
|
||||
else:
|
||||
from numpy import arange as npArange
|
||||
from numpy import dot as npDot
|
||||
|
||||
total_weight = 0.5 * length * (length + 1)
|
||||
weights_ = Series(npArange(1, length + 1))
|
||||
weights_ = Series(arange(1, length + 1))
|
||||
weights = weights_ if asc else weights_[::-1]
|
||||
|
||||
def linear(w):
|
||||
def _compute(x):
|
||||
return npDot(x, w) / total_weight
|
||||
return dot(x, w) / total_weight
|
||||
return _compute
|
||||
|
||||
close_ = close.rolling(length, min_periods=length)
|
||||
@@ -62,13 +60,13 @@ def wma(close: Series, length: int = None, asc: bool = None, talib: bool = None,
|
||||
if offset != 0:
|
||||
wma = wma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
wma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
wma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
wma.name = f"WMA_{length}"
|
||||
wma.category = "overlap"
|
||||
|
||||
|
||||
+52
-11
@@ -1,13 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# from . import (
|
||||
# dema, ema, hma, linreg, rma, sma, swma, t3, tema, trima, vidya, wma
|
||||
# )
|
||||
from pandas_ta.overlap import ma
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from .dema import dema
|
||||
from .ema import ema
|
||||
from .fwma import fwma
|
||||
from .hma import hma
|
||||
from .linreg import linreg
|
||||
from .midpoint import midpoint
|
||||
from .pwma import pwma
|
||||
from .rma import rma
|
||||
from .sinwma import sinwma
|
||||
from .sma import sma
|
||||
from .ssf import ssf
|
||||
from .swma import swma
|
||||
from .t3 import t3
|
||||
from .tema import tema
|
||||
from .trima import trima
|
||||
from .vidya import vidya
|
||||
from .wma import wma
|
||||
|
||||
|
||||
def zlma(close: Series, length: int = None, mamode: str = None, offset: int = None, **kwargs) -> Series:
|
||||
def zlma(
|
||||
close: Series, length: int = None, mamode: str = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Zero Lag Moving Average (ZLMA)
|
||||
|
||||
The Zero Lag Moving Average attempts to eliminate the lag associated
|
||||
@@ -29,7 +45,7 @@ def zlma(close: Series, length: int = None, mamode: str = None, offset: int = No
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
mamode = mamode.lower() if isinstance(mamode, str) else "ema"
|
||||
close = verify_series(close, length)
|
||||
@@ -37,22 +53,47 @@ def zlma(close: Series, length: int = None, mamode: str = None, offset: int = No
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
lag = int(0.5 * (length - 1))
|
||||
close_ = 2 * close - close.shift(lag)
|
||||
zlma = ma(mamode, close_, length=length, **kwargs)
|
||||
|
||||
kwargs.update({"close": close_})
|
||||
kwargs.update({"length": length})
|
||||
|
||||
# Not ideal but it works. Submit a PR for a better solution. =)
|
||||
# This design pattern is undesirable
|
||||
def _ma(**kwargs):
|
||||
if mamode == "dema": return dema(**kwargs)
|
||||
elif mamode == "fwma": return fwma(**kwargs)
|
||||
elif mamode == "hma": return hma(**kwargs)
|
||||
elif mamode == "linreg": return linreg(**kwargs)
|
||||
elif mamode == "midpoint": return midpoint(**kwargs)
|
||||
elif mamode == "pwma": return pwma(**kwargs)
|
||||
elif mamode == "rma": return rma(**kwargs)
|
||||
elif mamode == "sinwma": return sinwma(**kwargs)
|
||||
elif mamode == "sma": return sma(**kwargs)
|
||||
elif mamode == "ssf": return ssf(**kwargs)
|
||||
elif mamode == "swma": return swma(**kwargs)
|
||||
elif mamode == "t3": return t3(**kwargs)
|
||||
elif mamode == "tema": return tema(**kwargs)
|
||||
elif mamode == "trima": return trima(**kwargs)
|
||||
elif mamode == "vidya": return vidya(**kwargs)
|
||||
elif mamode == "wma": return wma(**kwargs)
|
||||
else: return ema(**kwargs)
|
||||
|
||||
zlma = _ma(**kwargs)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
zlma = zlma.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
zlma.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
zlma.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
zlma.name = f"ZL_{zlma.name}"
|
||||
zlma.category = "overlap"
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import log as nplog
|
||||
from numpy import seterr
|
||||
from numpy import log, seterr
|
||||
from pandas import DataFrame, Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def drawdown(close: Series, offset: int = None, **kwargs) -> DataFrame:
|
||||
def drawdown(
|
||||
close: Series, offset: int = None, **kwargs
|
||||
) -> DataFrame:
|
||||
"""Drawdown (DD)
|
||||
|
||||
Drawdown is a peak-to-trough decline during a specific period for an investment,
|
||||
@@ -26,18 +27,18 @@ def drawdown(close: Series, offset: int = None, **kwargs) -> DataFrame:
|
||||
Returns:
|
||||
pd.DataFrame: drawdown, drawdown percent, drawdown log columns
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
close = verify_series(close)
|
||||
offset = get_offset(offset)
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
max_close = close.cummax()
|
||||
dd = max_close - close
|
||||
dd_pct = 1 - (close / max_close)
|
||||
|
||||
_np_err = seterr()
|
||||
seterr(divide="ignore", invalid="ignore")
|
||||
dd_log = nplog(max_close) - nplog(close)
|
||||
dd_log = log(max_close) - log(close)
|
||||
seterr(divide=_np_err["divide"], invalid=_np_err["invalid"])
|
||||
|
||||
# Offset
|
||||
@@ -46,7 +47,7 @@ def drawdown(close: Series, offset: int = None, **kwargs) -> DataFrame:
|
||||
dd_pct = dd_pct.shift(offset)
|
||||
dd_log = dd_log.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
dd.fillna(kwargs["fillna"], inplace=True)
|
||||
dd_pct.fillna(kwargs["fillna"], inplace=True)
|
||||
@@ -56,13 +57,12 @@ def drawdown(close: Series, offset: int = None, **kwargs) -> DataFrame:
|
||||
dd_pct.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
dd_log.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name and Categorize it
|
||||
# Name and Category
|
||||
dd.name = "DD"
|
||||
dd_pct.name = f"{dd.name}_PCT"
|
||||
dd_log.name = f"{dd.name}_LOG"
|
||||
dd.category = dd_pct.category = dd_log.category = "performance"
|
||||
|
||||
# Prepare DataFrame to return
|
||||
data = {dd.name: dd, dd_pct.name: dd_pct, dd_log.name: dd_log}
|
||||
df = DataFrame(data)
|
||||
df.name = dd.name
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import log as nplog
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from numpy import log, nan, roll
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def log_return(close: Series, length: int = None, cumulative: bool = None, offset: int = None, **kwargs) -> Series:
|
||||
def log_return(
|
||||
close: Series, length: int = None, cumulative: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Log Return
|
||||
|
||||
Calculates the logarithmic return of a Series.
|
||||
@@ -26,7 +29,7 @@ def log_return(close: Series, length: int = None, cumulative: bool = None, offse
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 1
|
||||
cumulative = bool(cumulative) if cumulative is not None and cumulative else False
|
||||
close = verify_series(close, length)
|
||||
@@ -34,24 +37,26 @@ def log_return(close: Series, length: int = None, cumulative: bool = None, offse
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
np_close = close.values
|
||||
if cumulative:
|
||||
# log_return = nplog(close).diff(length).cumsum()
|
||||
log_return = nplog(close / close.iloc[0])
|
||||
r = np_close / np_close[0]
|
||||
else:
|
||||
log_return = nplog(close / close.shift(length)) # nplog(close).diff(length)
|
||||
r = np_close / roll(np_close, length)
|
||||
r[:length] = nan
|
||||
log_return = Series(log(r), index=close.index)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
log_return = log_return.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
log_return.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
log_return.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
log_return.name = f"{'CUM' if cumulative else ''}LOGRET_{length}"
|
||||
log_return.category = "performance"
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from numpy import nan, roll
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def percent_return(close: Series, length: int = None, cumulative: bool = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def percent_return(
|
||||
close: Series, length: int = None, cumulative: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Percent Return
|
||||
|
||||
Calculates the percent return of a Series.
|
||||
@@ -26,7 +29,7 @@ def percent_return(close: Series, length: int = None, cumulative: bool = None, o
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 1
|
||||
cumulative = bool(cumulative) if cumulative is not None and cumulative else False
|
||||
close = verify_series(close, length)
|
||||
@@ -34,23 +37,26 @@ def percent_return(close: Series, length: int = None, cumulative: bool = None, o
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
if cumulative:
|
||||
pct_return = (close / close.iloc[0]) - 1
|
||||
# Calculate
|
||||
np_close = close.values
|
||||
if True:#cumulative:
|
||||
pr = (np_close / np_close[0]) - 1
|
||||
else:
|
||||
pct_return = close.pct_change(length) # (close / close.shift(length)) - 1
|
||||
pr = (np_close / roll(np_close, length)) - 1
|
||||
pr[:length] = nan
|
||||
pct_return = Series(pr, index=close.index)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
pct_return = pct_return.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
pct_return.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
pct_return.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
pct_return.name = f"{'CUM' if cumulative else ''}PCTRET_{length}"
|
||||
pct_return.category = "performance"
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import log as npLog
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from numpy import log
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def entropy(close: Series, length: int = None, base: float = None, offset: int = None, **kwargs) -> Series:
|
||||
def entropy(
|
||||
close: Series, length: int = None, base: float = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Entropy (ENTP)
|
||||
|
||||
Introduced by Claude Shannon in 1948, entropy measures the unpredictability
|
||||
@@ -27,7 +30,7 @@ def entropy(close: Series, length: int = None, base: float = None, offset: int =
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 10
|
||||
base = float(base) if base and base > 0 else 2.0
|
||||
close = verify_series(close, length)
|
||||
@@ -35,21 +38,21 @@ def entropy(close: Series, length: int = None, base: float = None, offset: int =
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
p = close / close.rolling(length).sum()
|
||||
entropy = (-p * npLog(p) / npLog(base)).rolling(length).sum()
|
||||
entropy = (-p * log(p) / log(base)).rolling(length).sum()
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
entropy = entropy.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
entropy.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
entropy.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
entropy.name = f"ENTP_{length}"
|
||||
entropy.category = "statistics"
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def kurtosis(close: Series, length: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def kurtosis(
|
||||
close: Series, length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Rolling Kurtosis
|
||||
|
||||
Calculates the Kurtosis over a rolling period.
|
||||
@@ -20,7 +23,7 @@ def kurtosis(close: Series, length: int = None, offset: int = None, **kwargs) ->
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 30
|
||||
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
|
||||
close = verify_series(close, max(length, min_periods))
|
||||
@@ -28,20 +31,20 @@ def kurtosis(close: Series, length: int = None, offset: int = None, **kwargs) ->
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
kurtosis = close.rolling(length, min_periods=min_periods).kurt()
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
kurtosis = kurtosis.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
kurtosis.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
kurtosis.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
kurtosis.name = f"KURT_{length}"
|
||||
kurtosis.category = "statistics"
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import fabs as npfabs
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from numpy import fabs
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def mad(close: Series, length: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def mad(
|
||||
close: Series, length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Rolling Mean Absolute Deviation
|
||||
|
||||
Calculates the Mean Absolute Deviation over a rolling period.
|
||||
@@ -21,7 +24,7 @@ def mad(close: Series, length: int = None, offset: int = None, **kwargs) -> Seri
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 30
|
||||
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
|
||||
close = verify_series(close, max(length, min_periods))
|
||||
@@ -29,10 +32,10 @@ def mad(close: Series, length: int = None, offset: int = None, **kwargs) -> Seri
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
def mad_(series):
|
||||
"""Mean Absolute Deviation"""
|
||||
return npfabs(series - series.mean()).mean()
|
||||
return fabs(series - series.mean()).mean()
|
||||
|
||||
mad = close.rolling(length, min_periods=min_periods).apply(mad_, raw=True)
|
||||
|
||||
@@ -40,13 +43,13 @@ def mad(close: Series, length: int = None, offset: int = None, **kwargs) -> Seri
|
||||
if offset != 0:
|
||||
mad = mad.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
mad.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
mad.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
mad.name = f"MAD_{length}"
|
||||
mad.category = "statistics"
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def median(close: Series, length: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def median(
|
||||
close: Series, length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Rolling Median
|
||||
|
||||
Calculates the Median over a rolling period. Sibling of a Simple Moving Average.
|
||||
@@ -23,7 +26,7 @@ def median(close: Series, length: int = None, offset: int = None, **kwargs) -> S
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 30
|
||||
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
|
||||
close = verify_series(close, max(length, min_periods))
|
||||
@@ -31,20 +34,20 @@ def median(close: Series, length: int = None, offset: int = None, **kwargs) -> S
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
median = close.rolling(length, min_periods=min_periods).median()
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
median = median.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
median.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
median.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
median.name = f"MEDIAN_{length}"
|
||||
median.category = "statistics"
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def quantile(close: Series, length: int = None, q: float = None, offset: int = None, **kwargs) -> Series:
|
||||
def quantile(
|
||||
close: Series, length: int = None, q: float = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Rolling Quantile
|
||||
|
||||
Calculates the Quantile over a rolling period.
|
||||
@@ -21,7 +24,7 @@ def quantile(close: Series, length: int = None, q: float = None, offset: int = N
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 30
|
||||
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
|
||||
q = float(q) if q and q > 0 and q < 1 else 0.5
|
||||
@@ -30,20 +33,20 @@ def quantile(close: Series, length: int = None, q: float = None, offset: int = N
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
quantile = close.rolling(length, min_periods=min_periods).quantile(q)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
quantile = quantile.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
quantile.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
quantile.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
quantile.name = f"QTL_{length}_{q}"
|
||||
quantile.category = "statistics"
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from pandas import Series
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
|
||||
|
||||
def skew(close: Series, length: int = None, offset: int = None, **kwargs) -> Series:
|
||||
def skew(
|
||||
close: Series, length: int = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Rolling Skew
|
||||
|
||||
Calculates the Skew over a rolling period.
|
||||
@@ -20,7 +23,7 @@ def skew(close: Series, length: int = None, offset: int = None, **kwargs) -> Ser
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if length and length > 0 else 30
|
||||
min_periods = int(kwargs["min_periods"]) if "min_periods" in kwargs and kwargs["min_periods"] is not None else length
|
||||
close = verify_series(close, max(length, min_periods))
|
||||
@@ -28,20 +31,20 @@ def skew(close: Series, length: int = None, offset: int = None, **kwargs) -> Ser
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
skew = close.rolling(length, min_periods=min_periods).skew()
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
skew = skew.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
skew.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
skew.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
skew.name = f"SKEW_{length}"
|
||||
skew.category = "statistics"
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from numpy import sqrt as npsqrt
|
||||
from .variance import variance
|
||||
from pandas_ta import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from numpy import sqrt
|
||||
from pandas import Series
|
||||
from pandas_ta.maps import Imports
|
||||
from pandas_ta.utils import get_offset, verify_series
|
||||
from .variance import variance
|
||||
|
||||
|
||||
def stdev(close: Series, length: int = None, ddof: int = None, talib: bool = None, offset: int = None,
|
||||
**kwargs) -> Series:
|
||||
def stdev(
|
||||
close: Series, length: int = None,
|
||||
ddof: int = None, talib: bool = None,
|
||||
offset: int = None, **kwargs
|
||||
) -> Series:
|
||||
"""Rolling Standard Deviation
|
||||
|
||||
Calculates the Standard Deviation over a rolling period.
|
||||
@@ -30,7 +33,7 @@ def stdev(close: Series, length: int = None, ddof: int = None, talib: bool = Non
|
||||
Returns:
|
||||
pd.Series: New feature generated.
|
||||
"""
|
||||
# Validate Arguments
|
||||
# Validate
|
||||
length = int(length) if isinstance(length, int) and length > 0 else 30
|
||||
ddof = int(ddof) if isinstance(ddof, int) and ddof >= 0 and ddof < length else 1
|
||||
close = verify_series(close, length)
|
||||
@@ -39,24 +42,24 @@ def stdev(close: Series, length: int = None, ddof: int = None, talib: bool = Non
|
||||
|
||||
if close is None: return
|
||||
|
||||
# Calculate Result
|
||||
# Calculate
|
||||
if Imports["talib"] and mode_tal:
|
||||
from talib import STDDEV
|
||||
stdev = STDDEV(close, length)
|
||||
else:
|
||||
stdev = variance(close=close, length=length, ddof=ddof, talib=mode_tal).apply(npsqrt)
|
||||
stdev = variance(close=close, length=length, ddof=ddof, talib=mode_tal).apply(sqrt)
|
||||
|
||||
# Offset
|
||||
if offset != 0:
|
||||
stdev = stdev.shift(offset)
|
||||
|
||||
# Handle fills
|
||||
# Fill
|
||||
if "fillna" in kwargs:
|
||||
stdev.fillna(kwargs["fillna"], inplace=True)
|
||||
if "fill_method" in kwargs:
|
||||
stdev.fillna(method=kwargs["fill_method"], inplace=True)
|
||||
|
||||
# Name & Category
|
||||
# Name and Category
|
||||
stdev.name = f"STDEV_{length}"
|
||||
stdev.category = "statistics"
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user