diff --git a/pandas_ta/candles/cdl_doji.py b/pandas_ta/candles/cdl_doji.py index 6d41dee..336df04 100644 --- a/pandas_ta/candles/cdl_doji.py +++ b/pandas_ta/candles/cdl_doji.py @@ -2,9 +2,11 @@ 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_, high, low, close, length=None, factor=None, scalar=None, asint=True, offset=None, **kwargs): +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 diff --git a/pandas_ta/candles/cdl_inside.py b/pandas_ta/candles/cdl_inside.py index 1ade4c0..5b7c966 100644 --- a/pandas_ta/candles/cdl_inside.py +++ b/pandas_ta/candles/cdl_inside.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import candle_color, get_offset from pandas_ta.utils import verify_series +from pandas import Series -def cdl_inside(open_, high, low, close, asbool=False, offset=None, **kwargs): +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 diff --git a/pandas_ta/candles/cdl_pattern.py b/pandas_ta/candles/cdl_pattern.py index f806c35..b83f750 100644 --- a/pandas_ta/candles/cdl_pattern.py +++ b/pandas_ta/candles/cdl_pattern.py @@ -24,13 +24,13 @@ ALL_PATTERNS = [ def cdl_pattern( - open_, - high, - low, - close, - name: Union[str, Sequence[str]]="all", - scalar=None, - offset=None, + 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 @@ -121,4 +121,5 @@ def cdl_pattern( df.category = "candles" return df -cdl = cdl_pattern # Alias \ No newline at end of file + +cdl = cdl_pattern # Alias diff --git a/pandas_ta/candles/cdl_z.py b/pandas_ta/candles/cdl_z.py index b15cc4c..3b227b5 100644 --- a/pandas_ta/candles/cdl_z.py +++ b/pandas_ta/candles/cdl_z.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.statistics import zscore from pandas_ta.utils import get_offset, verify_series -def cdl_z(open_, high, low, close, length=None, full=None, ddof=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/candles/ha.py b/pandas_ta/candles/ha.py index 6473a6c..3cf0dc0 100644 --- a/pandas_ta/candles/ha.py +++ b/pandas_ta/candles/ha.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.utils import get_offset, verify_series -def ha(open_, high, low, close, offset=None, **kwargs): +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 diff --git a/pandas_ta/core.py b/pandas_ta/core.py index e0ff9c4..c6a9478 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -1,113 +1,22 @@ # -*- coding: utf-8 -*- -from dataclasses import dataclass, field +# from dataclasses import dataclass, field +from email.policy import default from multiprocessing import cpu_count, Pool from time import perf_counter -from typing import List, Tuple +from typing import Union from warnings import simplefilter +from numpy import log10, ndarray +from pandas.api.extensions import register_dataframe_accessor from pandas.core.base import PandasObject +from pandas.errors import PerformanceWarning +from pandas import DataFrame, Series -from pandas_ta import Category, Imports, np, pd, version -from pandas_ta.candles.cdl_pattern import ALL_PATTERNS -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 * + +from pandas_ta import * from pandas_ta.utils import * -df = pd.DataFrame() - -# Study DataClass -@dataclass -class Study: - """Study DataClass - Class to name and group indicators for processing - - Args: - name (str): Some short memorable string. Note: Case-insensitive "All" is reserved. - ta (list of dicts): A list of dicts containing keyword arguments where "kind" is the indicator. - description (str): A more detailed description of what the Study tries to capture. Default: None - created (str): At datetime string of when it was created. Default: Automatically generated. *Subject to change* - - Example TA: - ta = [ - {"kind": "sma", "length": 200}, - {"kind": "sma", "close": "volume", "length": 50}, - {"kind": "bbands", "length": 20}, - {"kind": "rsi"}, - {"kind": "macd", "fast": 8, "slow": 21}, - {"kind": "sma", "close": "volume", "length": 20, "prefix": "VOLUME"}, - ] - """ - name: str # = None # Required. - ta: List = field(default_factory=list) # Required. - description: str = "" # Helpful. More descriptive version or notes or w/e. - created: str = get_time(to_string=True) # Optional. Gets Exchange Time and Local Time execution time - - def __post_init__(self): - req_args = ["[X] Study requires the following argument(s):"] - - if self._is_name(): - req_args.append(' - name. Must be a string. Example: "My TA". Note: "all" is reserved.') - - if self.ta is None: - self.ta = None - elif not self._is_ta(): - s = " - ta. Format is a list of dicts. Example: [{'kind': 'sma', 'length': 10}]" - s += "\n Check the indicator for the correct arguments if you receive this error." - req_args.append(s) - - if len(req_args) > 1: - [print(_) for _ in req_args] - return None - - def _is_name(self): - return self.name is None or not isinstance(self.name, str) - - def _is_ta(self): - if isinstance(self.ta, list) and self.total_ta() > 0: - # Check that all elements of the list are dicts. - # Does not check if the dicts values are valid indicator kwargs - # User must check indicator documentation for all indicators args. - return all([isinstance(_, dict) and len(_.keys()) > 0 for _ in self.ta]) - return False - - def total_ta(self): - return len(self.ta) if self.ta is not None else 0 - - -# All Study -AllStudy = Study( - name="All", - description="All the indicators with their default settings. Pandas TA default.", - ta=None, -) - -# Default (Example) Study. -CommonStudy = Study( - name="Common Price and Volume SMAs", - description="Common Price SMAs: 10, 20, 50, 200 and Volume SMA: 20.", - ta=[ - {"kind": "sma", "length": 10}, - {"kind": "sma", "length": 20}, - {"kind": "sma", "length": 50}, - {"kind": "sma", "length": 200}, - {"kind": "sma", "close": "volume", "length": 20, "prefix": "VOL"} - ] -) - -# Temporary Strategy DataClass Alias -Strategy = Study -AllStrategy = AllStudy -CommonStrategy = CommonStudy - - # Base Class for extending a Pandas DataFrame class BasePandasObject(PandasObject): """Simple PandasObject Extension @@ -119,7 +28,7 @@ class BasePandasObject(PandasObject): df (pd.DataFrame): Extends Pandas DataFrame """ - def __init__(self, df, **kwargs): + def __init__(self, df: DataFrame, **kwargs): if df.empty: return print(f"\n[!] kwargs: {kwargs}\n") if len(df.columns) > 0: @@ -158,7 +67,7 @@ class BasePandasObject(PandasObject): # Pandas TA - DataFrame Analysis Indicators -@pd.api.extensions.register_dataframe_accessor("ta") +@register_dataframe_accessor("ta") class AnalysisIndicators(BasePandasObject): """ This Pandas Extension is named 'ta' for Technical Analysis. In other words, @@ -251,14 +160,14 @@ class AnalysisIndicators(BasePandasObject): _time_range = "years" _last_run = get_time(_exchange, to_string=True) - def __init__(self, pandas_obj): + def __init__(self, pandas_obj: Union[DataFrame, Series]): self._validate(pandas_obj) self._df = pandas_obj self._last_run = get_time(self._exchange, to_string=True) @staticmethod - def _validate(obj: Tuple[pd.DataFrame, pd.Series]): - if not isinstance(obj, pd.DataFrame) and not isinstance(obj, pd.Series): + def _validate(obj: Union[DataFrame, Series]): + if not isinstance(obj, DataFrame) and not isinstance(obj, Series): raise AttributeError("[X] Must be either a Pandas Series or DataFrame.") # DataFrame Behavioral Methods @@ -305,8 +214,8 @@ class AnalysisIndicators(BasePandasObject): self._adjusted = None @property - def cores(self) -> str: - """Returns the categories.""" + def cores(self) -> int: + """Returns the number of CPU cores.""" return self._cores @cores.setter @@ -347,7 +256,7 @@ class AnalysisIndicators(BasePandasObject): # Public Get DataFrame Properties @property - def categories(self) -> str: + def categories(self) -> list: """Returns the categories.""" return list(Category.keys()) @@ -360,7 +269,7 @@ class AnalysisIndicators(BasePandasObject): return hasdf @property - def reverse(self) -> pd.DataFrame: + def reverse(self) -> DataFrame: """Reverses the DataFrame. Simply: df.iloc[::-1]""" return self._df.iloc[::-1] @@ -401,7 +310,7 @@ class AnalysisIndicators(BasePandasObject): if "suffix" in kwargs: suffix = f"{delimiter}{kwargs['suffix']}" - if isinstance(result, pd.Series): + if isinstance(result, Series): result.name = prefix + result.name + suffix else: result.columns = [prefix + column + suffix for column in result.columns] @@ -412,11 +321,11 @@ class AnalysisIndicators(BasePandasObject): df = self._df if df is None or result is None: return else: - simplefilter(action="ignore", category=pd.errors.PerformanceWarning) + simplefilter(action="ignore", category=PerformanceWarning) if "col_names" in kwargs and not isinstance(kwargs["col_names"], tuple): kwargs["col_names"] = (kwargs["col_names"],) # Note: tuple(kwargs["col_names"]) doesn't work - if isinstance(result, pd.DataFrame): + if isinstance(result, DataFrame): # If specified in kwargs, rename the columns. # If not, use the default names. if "col_names" in kwargs and isinstance(kwargs["col_names"], tuple): @@ -440,13 +349,13 @@ class AnalysisIndicators(BasePandasObject): """Returns the columns in which all it's values are na.""" return [x for x in self._df.columns if all(self._df[x].isna())] - def _get_column(self, series): + def _get_column(self, series: Union[Series, str, None]): """Attempts to get the correct series or 'column' and return it.""" df = self._df if df is None: return # Explicitly passing a pd.Series to override default. - if isinstance(series, pd.Series): + if isinstance(series, Series): return series # Apply default if no series nor a default. elif series is None: @@ -463,7 +372,7 @@ class AnalysisIndicators(BasePandasObject): match = [i for i, x in enumerate(matches) if x] # If found, awesome. Return it or return the 'series'. cols = ", ".join(list(df.columns)) - NOT_FOUND = f"[X] Ooops!!! It's {series not in df.columns}, the column named '{series}' was not found in {cols}" + NOT_FOUND = f"[X] Ooops!!! It's {series not in df.columns}, the column '{series}' was not found in {cols}" return df.iloc[:, match[0]] if len(match) else print(NOT_FOUND) def _indicators_by_category(self, name: str) -> list: @@ -479,13 +388,13 @@ class AnalysisIndicators(BasePandasObject): else: return getattr(self, method)(*args, **kwargs)[0] - def _post_process(self, result, **kwargs) -> Tuple[pd.Series, pd.DataFrame]: + def _post_process(self, result: Union[Series, DataFrame], **kwargs) -> Union[Series, DataFrame]: """Applies any additional modifications to the DataFrame * Applies prefixes and/or suffixes * Appends the result to main DataFrame """ verbose = kwargs.pop("verbose", False) - if not isinstance(result, (pd.Series, pd.DataFrame)): + if not isinstance(result, (Series, DataFrame)): if verbose: print(f"[X] Oops! The result was not a Series or DataFrame.") return self._df @@ -493,7 +402,7 @@ class AnalysisIndicators(BasePandasObject): # Append only specific columns to the dataframe (via # 'col_numbers':(0,1,3) for example) result = (result.iloc[:, [int(n) for n in kwargs["col_numbers"]]] - if isinstance(result, pd.DataFrame) and + if isinstance(result, DataFrame) and "col_numbers" in kwargs and kwargs["col_numbers"] is not None else result) # Add prefix/suffix and append to the dataframe @@ -501,8 +410,9 @@ class AnalysisIndicators(BasePandasObject): self._append(result=result, **kwargs) return result - def _strategy_mode(self, *args) -> tuple: - """Helper method to determine the mode and name of the study. Returns tuple: (name:str, mode:dict)""" + def _study_mode(self, *args) -> tuple: + """Helper method to determine the mode and name of the study. + Returns tuple: (name:str, mode:dict)""" name = "All" mode = {"all": False, "category": False, "custom": False} @@ -556,7 +466,7 @@ class AnalysisIndicators(BasePandasObject): Returns nothing to the user. Either adds or removes constant ranges from the working DataFrame. """ - if isinstance(values, np.ndarray) or isinstance(values, list): + if isinstance(values, ndarray) or isinstance(values, list): if append: for x in values: self._df[f"{x}"] = x @@ -598,7 +508,7 @@ class AnalysisIndicators(BasePandasObject): ] # Public non-indicator methods - ta_indicators = list((x for x in dir(pd.DataFrame().ta) if not x.startswith("_") and not x.endswith("_"))) + ta_indicators = list((x for x in dir(DataFrame().ta) if not x.startswith("_") and not x.endswith("_"))) # Add Pandas TA methods and properties to be removed removed = helper_methods + ta_properties @@ -620,6 +530,7 @@ class AnalysisIndicators(BasePandasObject): s, _count = f"{header}\n", 0 if indicator_count > 0: + from pandas_ta.candles.cdl_pattern import ALL_PATTERNS s += f"\nIndicators and Utilities [{indicator_count}]:\n {', '.join(ta_indicators)}\n" _count += indicator_count if Imports["talib"]: @@ -628,14 +539,12 @@ class AnalysisIndicators(BasePandasObject): s += f"\nTotal Candles, Indicators and Utilities: {_count}" print(s) - def sample(self, **kwargs): """sample See help(ta.sample) for parameters. """ return sample(**kwargs) - def strategy(self, *args, **kwargs): """Strategy Method @@ -664,13 +573,13 @@ class AnalysisIndicators(BasePandasObject): """ # If True, it returns the resultant DataFrame. Default: False returns = kwargs.pop("returns", False) - # cpus = cpu_count() # Ensure indicators are appended to the DataFrame kwargs["append"] = True all_ordered = kwargs.pop("ordered", True) mp_chunksize = kwargs.pop("chunksize", self.cores) _depwarning = kwargs.pop("warning", True) + if _depwarning: print(f"\n[!] DEPRECIATION WARNING:\n Use study() instead of strategy().\n") @@ -692,7 +601,7 @@ class AnalysisIndicators(BasePandasObject): ] # Get the Study Name and mode - name, mode = self._strategy_mode(*args) + name, mode = self._study_mode(*args) # If All or a Category, exclude user list if any user_excluded = kwargs.pop("exclude", []) @@ -755,7 +664,7 @@ class AnalysisIndicators(BasePandasObject): _total_ta = len(ta) with Pool(self.cores) as pool: # Some magic to optimize chunksize for speed based on total ta indicators - _chunksize = mp_chunksize - 1 if mp_chunksize > _total_ta else int(np.log10(_total_ta)) + 1 + _chunksize = mp_chunksize - 1 if mp_chunksize > _total_ta else int(log10(_total_ta)) + 1 if verbose: print(f"[i] Multiprocessing {_total_ta} indicators with {_chunksize} chunks and {self.cores}/{cpu_count()} cpus.") @@ -909,16 +818,27 @@ class AnalysisIndicators(BasePandasObject): ds = ds.lower() if isinstance(ds, str) else self.ds strategy = kwargs.pop("strategy", None) study = kwargs.pop("study", strategy) + timed = kwargs.setdefault("timed", False) + if isinstance(ticker, str): tickers = [ticker] + if isinstance(ticker, list): + ticker = ticker.pop() + # Fetch Data if ds == "polygon": + if timed: stime = perf_counter() df = polygon_api(ticker, **kwargs) elif ds in ["yahoo", "yf"]: + if timed: stime = perf_counter() df = yf(ticker, **kwargs) else: return + if timed: + df.timed = final_time(stime) + print(f"[+] {ds} | {ticker}: {df.timed}") + if df is None: return elif df.empty: print(f"[X] DataFrame is empty: {df.shape}") @@ -932,10 +852,9 @@ class AnalysisIndicators(BasePandasObject): if study is not None: return self.study(study, returns=True, **kwargs) return df - # Public DataFrame Methods: Indicators and Utilities # Candles - def cdl_pattern(self, name="all", offset=None, **kwargs): + def cdl_pattern(self, name: str = "all", offset=None, **kwargs): open_ = self._get_column(kwargs.pop("open", "open")) high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) @@ -1064,9 +983,11 @@ class AnalysisIndicators(BasePandasObject): if refined is not None or thirds is not None: high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) - result = inertia(close=close, high=high, low=low, length=length, rvi_length=rvi_length, scalar=scalar, refined=refined, thirds=thirds, mamode=mamode, drift=drift, offset=offset, **kwargs) + result = inertia(close=close, high=high, low=low, length=length, rvi_length=rvi_length, scalar=scalar, + refined=refined, thirds=thirds, mamode=mamode, drift=drift, offset=offset, **kwargs) else: - result = inertia(close=close, length=length, rvi_length=rvi_length, scalar=scalar, refined=refined, thirds=thirds, mamode=mamode, drift=drift, offset=offset, **kwargs) + result = inertia(close=close, length=length, rvi_length=rvi_length, scalar=scalar, refined=refined, + thirds=thirds, mamode=mamode, drift=drift, offset=offset, **kwargs) return self._post_process(result, **kwargs) @@ -1079,7 +1000,8 @@ class AnalysisIndicators(BasePandasObject): def kst(self, roc1=None, roc2=None, roc3=None, roc4=None, sma1=None, sma2=None, sma3=None, sma4=None, signal=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = kst(close=close, roc1=roc1, roc2=roc2, roc3=roc3, roc4=roc4, sma1=sma1, sma2=sma2, sma3=sma3, sma4=sma4, signal=signal, offset=offset, **kwargs) + result = kst(close=close, roc1=roc1, roc2=roc2, roc3=roc3, roc4=roc4, sma1=sma1, sma2=sma2, sma3=sma3, + sma4=sma4, signal=signal, offset=offset, **kwargs) return self._post_process(result, **kwargs) def macd(self, fast=None, slow=None, signal=None, offset=None, **kwargs): @@ -1142,7 +1064,8 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = rvgi(open_=open_, high=high, low=low, close=close, length=length, swma_length=swma_length, offset=offset, **kwargs) + result = rvgi(open_=open_, high=high, low=low, close=close, length=length, swma_length=swma_length, + offset=offset, **kwargs) return self._post_process(result, **kwargs) def slope(self, length=None, offset=None, **kwargs): @@ -1159,19 +1082,25 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = squeeze(high=high, low=low, close=close, bb_length=bb_length, bb_std=bb_std, kc_length=kc_length, kc_scalar=kc_scalar, mom_length=mom_length, mom_smooth=mom_smooth, use_tr=use_tr, mamode=mamode, offset=offset, **kwargs) + result = squeeze(high=high, low=low, close=close, bb_length=bb_length, bb_std=bb_std, kc_length=kc_length, + kc_scalar=kc_scalar, mom_length=mom_length, mom_smooth=mom_smooth, use_tr=use_tr, + mamode=mamode, offset=offset, **kwargs) return self._post_process(result, **kwargs) def squeeze_pro(self, bb_length=None, bb_std=None, kc_length=None, kc_scalar_wide=None, kc_scalar_normal=None, kc_scalar_narrow=None, mom_length=None, mom_smooth=None, use_tr=None, mamode=None, offset=None, **kwargs): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = squeeze_pro(high=high, low=low, close=close, bb_length=bb_length, bb_std=bb_std, kc_length=kc_length, kc_scalar_wide=kc_scalar_wide, kc_scalar_normal=kc_scalar_normal, kc_scalar_narrow=kc_scalar_narrow, mom_length=mom_length, mom_smooth=mom_smooth, use_tr=use_tr, mamode=mamode, offset=offset, **kwargs) + result = squeeze_pro(high=high, low=low, close=close, bb_length=bb_length, bb_std=bb_std, kc_length=kc_length, + kc_scalar_wide=kc_scalar_wide, kc_scalar_normal=kc_scalar_normal, + kc_scalar_narrow=kc_scalar_narrow, mom_length=mom_length, mom_smooth=mom_smooth, + use_tr=use_tr, mamode=mamode, offset=offset, **kwargs) return self._post_process(result, **kwargs) def stc(self, ma1=None, ma2=None, osc=None, tclength=None, fast=None, slow=None, factor=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = stc(close=close, ma1=ma1, ma2=ma2, osc=osc, tclength=tclength, fast=fast, slow=slow, factor=factor, offset=offset, **kwargs) + result = stc(close=close, ma1=ma1, ma2=ma2, osc=osc, tclength=tclength, fast=fast, slow=slow, factor=factor, + offset=offset, **kwargs) return self._post_process(result, **kwargs) def stoch(self, k=None, d=None, smooth_k=None, mamode=None, talib=None, offset=None, **kwargs): @@ -1192,7 +1121,8 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = stochrsi(high=high, low=low, close=close, length=length, rsi_length=rsi_length, k=k, d=d, mamode=mamode, offset=offset, **kwargs) + result = stochrsi(high=high, low=low, close=close, length=length, rsi_length=rsi_length, k=k, d=d, + mamode=mamode, offset=offset, **kwargs) return self._post_process(result, **kwargs) def td_seq(self, asint=None, offset=None, show_all=None, **kwargs): @@ -1214,7 +1144,8 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = uo(high=high, low=low, close=close, fast=fast, medium=medium, slow=slow, fast_w=fast_w, medium_w=medium_w, slow_w=slow_w, drift=drift, offset=offset, **kwargs) + result = uo(high=high, low=low, close=close, fast=fast, medium=medium, slow=slow, fast_w=fast_w, + medium_w=medium_w, slow_w=slow_w, drift=drift, offset=offset, **kwargs) return self._post_process(result, **kwargs) def willr(self, length=None, percentage=True, offset=None, **kwargs): @@ -1232,7 +1163,8 @@ class AnalysisIndicators(BasePandasObject): def alma(self, length=None, sigma=None, distribution_offset=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = alma(close=close, length=length, sigma=sigma, distribution_offset=distribution_offset, offset=offset, **kwargs) + result = alma(close=close, length=length, sigma=sigma, distribution_offset=distribution_offset, offset=offset, + **kwargs) return self._post_process(result, **kwargs) def dema(self, length=None, offset=None, **kwargs): @@ -1294,7 +1226,8 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result, span = ichimoku(high=high, low=low, close=close, tenkan=tenkan, kijun=kijun, senkou=senkou, include_chikou=include_chikou, offset=offset, **kwargs) + result, span = ichimoku(high=high, low=low, close=close, tenkan=tenkan, kijun=kijun, senkou=senkou, + include_chikou=include_chikou, offset=offset, **kwargs) self._add_prefix_suffix(result, **kwargs) self._add_prefix_suffix(span, **kwargs) self._append(result, **kwargs) @@ -1369,7 +1302,8 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = supertrend(high=high, low=low, close=close, length=length, multiplier=multiplier, offset=offset, **kwargs) + result = supertrend(high=high, low=low, close=close, length=length, multiplier=multiplier, offset=offset, + **kwargs) return self._post_process(result, **kwargs) def swma(self, length=None, offset=None, **kwargs): @@ -1440,7 +1374,8 @@ class AnalysisIndicators(BasePandasObject): def percent_return(self, length=None, cumulative=False, percent=False, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = percent_return(close=close, length=length, cumulative=cumulative, percent=percent, offset=offset, **kwargs) + result = percent_return(close=close, length=length, cumulative=cumulative, percent=percent, offset=offset, + **kwargs) return self._post_process(result, **kwargs) # Statistics @@ -1499,7 +1434,8 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = adx(high=high, low=low, close=close, length=length, lensig=lensig, mamode=mamode, scalar=scalar, drift=drift, offset=offset, **kwargs) + result = adx(high=high, low=low, close=close, length=length, lensig=lensig, mamode=mamode, scalar=scalar, + drift=drift, offset=offset, **kwargs) return self._post_process(result, **kwargs) def amat(self, fast=None, slow=None, mamode=None, lookback=None, offset=None, **kwargs): @@ -1517,7 +1453,8 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = chop(high=high, low=low, close=close, length=length, atr_length=atr_length, scalar=scalar, drift=drift, offset=offset, **kwargs) + result = chop(high=high, low=low, close=close, length=length, atr_length=atr_length, scalar=scalar, + drift=drift, offset=offset, **kwargs) return self._post_process(result, **kwargs) def cksp(self, p=None, x=None, q=None, mamode=None, offset=None, **kwargs): @@ -1578,7 +1515,8 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = supertrend(high=high, low=low, close=close, period=period, multiplier=multiplier, mamode=mamode, drift=drift, offset=offset, **kwargs) + result = supertrend(high=high, low=low, close=close, period=period, multiplier=multiplier, mamode=mamode, + drift=drift, offset=offset, **kwargs) return self._post_process(result, **kwargs) def trendflex(self, close=None, length=None, smooth=None, alpha=None, pi=None, sqrt2=None, offset=None, **kwargs): @@ -1616,7 +1554,8 @@ class AnalysisIndicators(BasePandasObject): if signal is None: return self._df else: - result = xsignals(signal=signal, xa=xa, xb=xb, above=above, long=long, asbool=asbool, trend_offset=trend_offset, trend_reset=trend_reset, offset=offset, **kwargs) + result = xsignals(signal=signal, xa=xa, xb=xb, above=above, long=long, asbool=asbool, + trend_offset=trend_offset, trend_reset=trend_reset, offset=offset, **kwargs) return self._post_process(result, **kwargs) # Utility @@ -1658,7 +1597,8 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = aberration(high=high, low=low, close=close, length=length, atr_length=atr_length, offset=offset, **kwargs) + result = aberration(high=high, low=low, close=close, length=length, atr_length=atr_length, offset=offset, + **kwargs) return self._post_process(result, **kwargs) def accbands(self, length=None, c=None, mamode=None, offset=None, **kwargs): @@ -1683,7 +1623,8 @@ class AnalysisIndicators(BasePandasObject): def donchian(self, lower_length=None, upper_length=None, offset=None, **kwargs): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) - result = donchian(high=high, low=low, lower_length=lower_length, upper_length=upper_length, offset=offset, **kwargs) + result = donchian(high=high, low=low, lower_length=lower_length, upper_length=upper_length, offset=offset, + **kwargs) return self._post_process(result, **kwargs) def hwc(self, na=None, nb=None, nc=None, nd=None, scalar=None, offset=None, **kwargs): @@ -1695,7 +1636,8 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = kc(high=high, low=low, close=close, length=length, scalar=scalar, mamode=mamode, offset=offset, **kwargs) + result = kc(high=high, low=low, close=close, length=length, scalar=scalar, mamode=mamode, offset=offset, + **kwargs) return self._post_process(result, **kwargs) def massi(self, fast=None, slow=None, offset=None, **kwargs): @@ -1708,7 +1650,8 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = natr(high=high, low=low, close=close, length=length, mamode=mamode, scalar=scalar, offset=offset, **kwargs) + result = natr(high=high, low=low, close=close, length=length, mamode=mamode, scalar=scalar, offset=offset, + **kwargs) return self._post_process(result, **kwargs) def pdist(self, drift=None, offset=None, **kwargs): @@ -1723,13 +1666,15 @@ class AnalysisIndicators(BasePandasObject): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) - result = rvi(high=high, low=low, close=close, length=length, scalar=scalar, refined=refined, thirds=thirds, mamode=mamode, drift=drift, offset=offset, **kwargs) + result = rvi(high=high, low=low, close=close, length=length, scalar=scalar, refined=refined, thirds=thirds, + mamode=mamode, drift=drift, offset=offset, **kwargs) return self._post_process(result, **kwargs) def thermo(self, long=None, short= None, length=None, mamode=None, drift=None, offset=None, **kwargs): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) - result = thermo(high=high, low=low, long=long, short=short, length=length, mamode=mamode, drift=drift, offset=offset, **kwargs) + result = thermo(high=high, low=low, long=long, short=short, length=length, mamode=mamode, drift=drift, + offset=offset, **kwargs) return self._post_process(result, **kwargs) def true_range(self, drift=None, offset=None, **kwargs): @@ -1762,13 +1707,15 @@ class AnalysisIndicators(BasePandasObject): low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) volume = self._get_column(kwargs.pop("volume", "volume")) - result = adosc(high=high, low=low, close=close, volume=volume, open_=open_, fast=fast, slow=slow, signed=signed, offset=offset, **kwargs) + result = adosc(high=high, low=low, close=close, volume=volume, open_=open_, fast=fast, slow=slow, + signed=signed, offset=offset, **kwargs) return self._post_process(result, **kwargs) def aobv(self, fast=None, slow=None, mamode=None, max_lookback=None, min_lookback=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) volume = self._get_column(kwargs.pop("volume", "volume")) - result = aobv(close=close, volume=volume, fast=fast, slow=slow, mamode=mamode, max_lookback=max_lookback, min_lookback=min_lookback, offset=offset, **kwargs) + result = aobv(close=close, volume=volume, fast=fast, slow=slow, mamode=mamode, max_lookback=max_lookback, + min_lookback=min_lookback, offset=offset, **kwargs) return self._post_process(result, **kwargs) def cmf(self, open_=None, length=None, offset=None, **kwargs): @@ -1778,7 +1725,8 @@ class AnalysisIndicators(BasePandasObject): low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) volume = self._get_column(kwargs.pop("volume", "volume")) - result = cmf(high=high, low=low, close=close, volume=volume, open_=open_, length=length, offset=offset, **kwargs) + result = cmf(high=high, low=low, close=close, volume=volume, open_=open_, length=length, offset=offset, + **kwargs) return self._post_process(result, **kwargs) def efi(self, length=None, mamode=None, offset=None, drift=None, **kwargs): @@ -1792,7 +1740,8 @@ class AnalysisIndicators(BasePandasObject): low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) volume = self._get_column(kwargs.pop("volume", "volume")) - result = eom(high=high, low=low, close=close, volume=volume, length=length, divisor=divisor, offset=offset, drift=drift, **kwargs) + result = eom(high=high, low=low, close=close, volume=volume, length=length, divisor=divisor, offset=offset, + drift=drift, **kwargs) return self._post_process(result, **kwargs) def kvo(self, fast=None, slow=None, length_sig=None, mamode=None, offset=None, drift=None, **kwargs): @@ -1800,7 +1749,8 @@ class AnalysisIndicators(BasePandasObject): low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) volume = self._get_column(kwargs.pop("volume", "volume")) - result = kvo(high=high, low=low, close=close, volume=volume, fast=fast, slow=slow, length_sig=length_sig, mamode=mamode, offset=offset, drift=drift, **kwargs) + result = kvo(high=high, low=low, close=close, volume=volume, fast=fast, slow=slow, length_sig=length_sig, + mamode=mamode, offset=offset, drift=drift, **kwargs) return self._post_process(result, **kwargs) def mfi(self, length=None, drift=None, offset=None, **kwargs): @@ -1808,7 +1758,8 @@ class AnalysisIndicators(BasePandasObject): low = self._get_column(kwargs.pop("low", "low")) close = self._get_column(kwargs.pop("close", "close")) volume = self._get_column(kwargs.pop("volume", "volume")) - result = mfi(high=high, low=low, close=close, volume=volume, length=length, drift=drift, offset=offset, **kwargs) + result = mfi(high=high, low=low, close=close, volume=volume, length=length, drift=drift, offset=offset, + **kwargs) return self._post_process(result, **kwargs) def nvi(self, length=None, initial=None, signed=True, offset=None, **kwargs): diff --git a/pandas_ta/custom.py b/pandas_ta/custom.py index 4af5cc5..94aaac2 100644 --- a/pandas_ta/custom.py +++ b/pandas_ta/custom.py @@ -11,7 +11,7 @@ import pandas_ta from pandas_ta import AnalysisIndicators -def bind(function_name, function, method): +def bind(function_name: str, function: types.FunctionType, method: types.MethodType): """ Helper function to bind the function and class method defined in a custom indicator module to the active pandas_ta instance. @@ -25,7 +25,7 @@ def bind(function_name, function, method): setattr(AnalysisIndicators, function_name, method) -def create_dir(path, create_categories=True, verbose=True): +def create_dir(path: str, create_categories: bool = True, verbose: bool = True): """ Helper function to setup a suitable folder structure for working with custom indicators. You only need to call this once whenever you want to @@ -57,7 +57,7 @@ def create_dir(path, create_categories=True, verbose=True): print(f"[i] Created an empty sub-directory '{dirname}'.") -def get_module_functions(module): +def get_module_functions(module: types.ModuleType) -> dict: """ Helper function to get the functions of an imported module as a dictionary. @@ -80,7 +80,7 @@ def get_module_functions(module): return module_functions -def import_dir(path, verbose=True): +def import_dir(path: str, verbose: bool = True): # ensure that the passed directory exists / is readable if not exists(path): print(f"[X] Unable to read the directory '{path}'.") @@ -202,7 +202,7 @@ like all other native indicators in pandas_ta, including help functions. """ -def load_indicator_module(name): +def load_indicator_module(name: str) -> dict: """ Helper function to (re)load an indicator module. diff --git a/pandas_ta/cycles/ebsw.py b/pandas_ta/cycles/ebsw.py index 6917946..2548068 100644 --- a/pandas_ta/cycles/ebsw.py +++ b/pandas_ta/cycles/ebsw.py @@ -3,7 +3,8 @@ from pandas_ta import np, pd from pandas_ta.utils import get_offset, verify_series -def ebsw(close, length=None, bars=None, offset=None, initial_version=False, **kwargs): +def ebsw(close: Series, length: int = None, bars: int = None, offset: int = None, initial_version: bool = False, + **kwargs) -> Series: """Even Better SineWave (EBSW) This indicator measures market cycles and uses a low pass filter to remove noise. diff --git a/pandas_ta/cycles/reflex.py b/pandas_ta/cycles/reflex.py index 6d106a7..00358ec 100644 --- a/pandas_ta/cycles/reflex.py +++ b/pandas_ta/cycles/reflex.py @@ -1,23 +1,24 @@ # -*- coding: utf-8 -*- -from pandas_ta import np, pd +from numpy import cos, exp, nan, ndarray, sqrt, zeros_like +from pandas import Series from pandas_ta.utils import get_offset, verify_series + try: from numba import njit except ImportError: njit = lambda _: _ - @njit -def np_reflex(x: np.ndarray, n: int, k: int, alpha: float, pi: float, sqrt2: float): +def np_reflex(x: ndarray, n: int, k: int, alpha: float, pi: float, sqrt2: float): m, ratio = x.size, 2 * sqrt2 / k - a = np.exp(-pi * ratio) - b = 2 * a * np.cos(180 * ratio) + a = exp(-pi * ratio) + b = 2 * a * cos(180 * ratio) c = a * a - b + 1 - _f = np.zeros_like(x) - _ms = np.zeros_like(x) - result = np.zeros_like(x) + _f = zeros_like(x) + _ms = zeros_like(x) + result = zeros_like(x) for i in range(2, m): _f[i] = 0.5 * c * (x[i] + x[i - 1]) + b * _f[i - 1] - a * a * _f[i - 2] @@ -32,12 +33,17 @@ def np_reflex(x: np.ndarray, n: int, k: int, alpha: float, pi: float, sqrt2: flo _ms[i] = alpha * _sum * _sum + (1 - alpha) * _ms[i - 1] if _ms[i] != 0.0: - result[i] = _sum / np.sqrt(_ms[i]) + result[i] = _sum / sqrt(_ms[i]) return result -def reflex(close, length=None, smooth=None, alpha=None, pi=None, sqrt2=None, offset=None, **kwargs): +def reflex( + close: Series, length: int = None, + smooth: int = None, alpha: float = None, + pi: float = None, sqrt2: float = None, + offset: int = None, **kwargs + ) -> Series: """Reflex (reflex) John F. Ehlers introduced two indicators within the article @@ -73,7 +79,7 @@ def reflex(close, length=None, smooth=None, alpha=None, pi=None, sqrt2=None, off Returns: pd.Series: New feature generated. """ - # Validate arguments + # Validate length = int(length) if isinstance(length, int) and length > 0 else 20 smooth = int(smooth) if isinstance(smooth, int) and smooth > 0 else 20 alpha = float(alpha) if isinstance(alpha, float) and alpha > 0 else 0.04 @@ -82,23 +88,23 @@ def reflex(close, length=None, smooth=None, alpha=None, pi=None, sqrt2=None, off close = verify_series(close, max(length, smooth)) offset = get_offset(offset) - # Calculate Result + # Calculate np_close = close.values result = np_reflex(np_close, length, smooth, alpha, pi, sqrt2) - result[:length] = np.nan - result = pd.Series(result, index=close.index) + result[:length] = nan + result = Series(result, index=close.index) # Offset if offset != 0: result = result.shift(offset) - # Handle fills + # Fill if "fillna" in kwargs: result.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: result.fillna(method=kwargs["fill_method"], inplace=True) - # Name and Categorize it + # Name and Category result.name = f"REFLEX_{length}_{smooth}_{alpha}" result.category = "cycles" diff --git a/pandas_ta/momentum/ao.py b/pandas_ta/momentum/ao.py index 8d3bddc..bb9b010 100644 --- a/pandas_ta/momentum/ao.py +++ b/pandas_ta/momentum/ao.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta.overlap import sma from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def ao(high, low, fast=None, slow=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/momentum/apo.py b/pandas_ta/momentum/apo.py index 94a4e06..02c5dd4 100644 --- a/pandas_ta/momentum/apo.py +++ b/pandas_ta/momentum/apo.py @@ -2,9 +2,11 @@ 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 -def apo(close, fast=None, slow=None, mamode=None, talib=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/bias.py b/pandas_ta/momentum/bias.py index a70bbc2..9acb87e 100644 --- a/pandas_ta/momentum/bias.py +++ b/pandas_ta/momentum/bias.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta.overlap import ma from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def bias(close, length=None, mamode=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/momentum/bop.py b/pandas_ta/momentum/bop.py index 51ef086..364e14a 100644 --- a/pandas_ta/momentum/bop.py +++ b/pandas_ta/momentum/bop.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from pandas_ta import Imports from pandas_ta.utils import get_offset, non_zero_range, verify_series +from pandas import Series -def bop(open_, high, low, close, scalar=None, talib=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/momentum/brar.py b/pandas_ta/momentum/brar.py index eb8d258..cbf1b5a 100644 --- a/pandas_ta/momentum/brar.py +++ b/pandas_ta/momentum/brar.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series -def brar(open_, high, low, close, length=None, scalar=None, drift=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/cci.py b/pandas_ta/momentum/cci.py index d35743f..e87143a 100644 --- a/pandas_ta/momentum/cci.py +++ b/pandas_ta/momentum/cci.py @@ -3,9 +3,11 @@ 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 -def cci(high, low, close, length=None, c=None, talib=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/cfo.py b/pandas_ta/momentum/cfo.py index 38b9297..51557b6 100644 --- a/pandas_ta/momentum/cfo.py +++ b/pandas_ta/momentum/cfo.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from pandas_ta.overlap import linreg from pandas_ta.utils import get_drift, get_offset, verify_series +from pandas import Series -def cfo(close, length=None, scalar=None, drift=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/cg.py b/pandas_ta/momentum/cg.py index a6544f4..19ba343 100644 --- a/pandas_ta/momentum/cg.py +++ b/pandas_ta/momentum/cg.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, verify_series, weights +from pandas import Series -def cg(close, length=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/cmo.py b/pandas_ta/momentum/cmo.py index 9b425a4..ed7bcea 100644 --- a/pandas_ta/momentum/cmo.py +++ b/pandas_ta/momentum/cmo.py @@ -2,9 +2,11 @@ from pandas_ta 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, length=None, scalar=None, talib=None, drift=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/coppock.py b/pandas_ta/momentum/coppock.py index 12f8aa7..6705f8e 100644 --- a/pandas_ta/momentum/coppock.py +++ b/pandas_ta/momentum/coppock.py @@ -2,9 +2,11 @@ from .roc import roc from pandas_ta.overlap import wma from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def coppock(close, length=None, fast=None, slow=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/cti.py b/pandas_ta/momentum/cti.py index a55bb28..63a317a 100644 --- a/pandas_ta/momentum/cti.py +++ b/pandas_ta/momentum/cti.py @@ -4,7 +4,7 @@ from pandas_ta.overlap import linreg from pandas_ta.utils import get_offset, verify_series -def cti(close, length=None, offset=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. diff --git a/pandas_ta/momentum/dm.py b/pandas_ta/momentum/dm.py index c418302..855d687 100644 --- a/pandas_ta/momentum/dm.py +++ b/pandas_ta/momentum/dm.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta import Imports from pandas_ta.overlap import ma from pandas_ta.utils import get_offset, verify_series, get_drift, zero -def dm(high, low, length=None, mamode=None, talib=None, drift=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/er.py b/pandas_ta/momentum/er.py index e6bf876..1dec8a8 100644 --- a/pandas_ta/momentum/er.py +++ b/pandas_ta/momentum/er.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame, concat +from pandas import DataFrame, concat, Series from pandas_ta.utils import get_drift, get_offset, verify_series, signals -def er(close, length=None, drift=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/momentum/eri.py b/pandas_ta/momentum/eri.py index 46f3e52..e0d0f74 100644 --- a/pandas_ta/momentum/eri.py +++ b/pandas_ta/momentum/eri.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.overlap import ema from pandas_ta.utils import get_offset, verify_series -def eri(high, low, close, length=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/fisher.py b/pandas_ta/momentum/fisher.py index e6d3e73..6277e58 100644 --- a/pandas_ta/momentum/fisher.py +++ b/pandas_ta/momentum/fisher.py @@ -6,7 +6,8 @@ from pandas_ta.overlap import hl2 from pandas_ta.utils import get_offset, high_low_range, verify_series -def fisher(high, low, length=None, signal=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/inertia.py b/pandas_ta/momentum/inertia.py index 372f480..cdb1613 100644 --- a/pandas_ta/momentum/inertia.py +++ b/pandas_ta/momentum/inertia.py @@ -2,9 +2,12 @@ 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 -def inertia(close=None, high=None, low=None, length=None, rvi_length=None, scalar=None, refined=None, thirds=None, mamode=None, drift=None, offset=None, **kwargs): +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: """Inertia (INERTIA) Inertia was developed by Donald Dorsey and was introduced his article diff --git a/pandas_ta/momentum/kdj.py b/pandas_ta/momentum/kdj.py index f48ae4a..4ccf878 100644 --- a/pandas_ta/momentum/kdj.py +++ b/pandas_ta/momentum/kdj.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.utils import get_offset, non_zero_range, rma_pandas, verify_series -def kdj(high=None, low=None, close=None, length=None, signal=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/kst.py b/pandas_ta/momentum/kst.py index f38a298..79e9d13 100644 --- a/pandas_ta/momentum/kst.py +++ b/pandas_ta/momentum/kst.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from .roc import roc from pandas_ta.utils import get_drift, get_offset, verify_series -def kst(close, roc1=None, roc2=None, roc3=None, roc4=None, sma1=None, sma2=None, sma3=None, sma4=None, signal=None, drift=None, offset=None, **kwargs): +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: """'Know Sure Thing' (KST) The 'Know Sure Thing' is a momentum based oscillator and based on ROC. diff --git a/pandas_ta/momentum/macd.py b/pandas_ta/momentum/macd.py index e180387..fad5d58 100644 --- a/pandas_ta/momentum/macd.py +++ b/pandas_ta/momentum/macd.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import concat, DataFrame +from pandas import concat, DataFrame, Series from pandas_ta import Imports from pandas_ta.overlap import ema from pandas_ta.utils import get_offset, verify_series, signals -def macd(close, fast=None, slow=None, signal=None, talib=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/momentum/mom.py b/pandas_ta/momentum/mom.py index 3d5d502..e6fad12 100644 --- a/pandas_ta/momentum/mom.py +++ b/pandas_ta/momentum/mom.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def mom(close, length=None, talib=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/pgo.py b/pandas_ta/momentum/pgo.py index 882b53b..c57703a 100644 --- a/pandas_ta/momentum/pgo.py +++ b/pandas_ta/momentum/pgo.py @@ -2,9 +2,10 @@ 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 -def pgo(high, low, close, length=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/ppo.py b/pandas_ta/momentum/ppo.py index b729a36..658c01b 100644 --- a/pandas_ta/momentum/ppo.py +++ b/pandas_ta/momentum/ppo.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta import Imports from pandas_ta.overlap import ma from pandas_ta.utils import get_offset, tal_ma, verify_series -def ppo(close, fast=None, slow=None, signal=None, scalar=None, mamode=None, talib=None, offset=None, **kwargs): +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) diff --git a/pandas_ta/momentum/psl.py b/pandas_ta/momentum/psl.py index d732d31..1a746cc 100644 --- a/pandas_ta/momentum/psl.py +++ b/pandas_ta/momentum/psl.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from numpy import sign as npSign from pandas_ta.utils import get_drift, get_offset, verify_series +from pandas import Series -def psl(close, open_=None, length=None, scalar=None, drift=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/pvo.py b/pandas_ta/momentum/pvo.py index 1239128..3ce0950 100644 --- a/pandas_ta/momentum/pvo.py +++ b/pandas_ta/momentum/pvo.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.overlap import ema from pandas_ta.utils import get_offset, verify_series -def pvo(volume, fast=None, slow=None, signal=None, scalar=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/momentum/qqe.py b/pandas_ta/momentum/qqe.py index b730351..0340929 100644 --- a/pandas_ta/momentum/qqe.py +++ b/pandas_ta/momentum/qqe.py @@ -9,7 +9,8 @@ from pandas_ta.overlap import ma from pandas_ta.utils import get_drift, get_offset, verify_series -def qqe(close, length=None, smooth=None, factor=None, mamode=None, drift=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/momentum/roc.py b/pandas_ta/momentum/roc.py index c05912b..c34f527 100644 --- a/pandas_ta/momentum/roc.py +++ b/pandas_ta/momentum/roc.py @@ -2,9 +2,11 @@ from .mom import mom from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def roc(close, length=None, scalar=None, talib=None, offset=None, **kwargs): +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). diff --git a/pandas_ta/momentum/rsi.py b/pandas_ta/momentum/rsi.py index 8a4d208..a315e38 100644 --- a/pandas_ta/momentum/rsi.py +++ b/pandas_ta/momentum/rsi.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame, concat +from pandas import DataFrame, concat, Series from pandas_ta import Imports from pandas_ta.overlap import rma from pandas_ta.utils import get_drift, get_offset, verify_series, signals -def rsi(close, length=None, scalar=None, talib=None, drift=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/rsx.py b/pandas_ta/momentum/rsx.py index 58834b5..8e8e5bc 100644 --- a/pandas_ta/momentum/rsx.py +++ b/pandas_ta/momentum/rsx.py @@ -4,7 +4,7 @@ from pandas import concat, DataFrame, Series from pandas_ta.utils import get_drift, get_offset, verify_series, signals -def rsx(close, length=None, drift=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/rvgi.py b/pandas_ta/momentum/rvgi.py index 064d2a8..be48140 100644 --- a/pandas_ta/momentum/rvgi.py +++ b/pandas_ta/momentum/rvgi.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.overlap import swma from pandas_ta.utils import get_offset, non_zero_range, verify_series -def rvgi(open_, high, low, close, length=None, swma_length=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/slope.py b/pandas_ta/momentum/slope.py index b88525a..232af62 100644 --- a/pandas_ta/momentum/slope.py +++ b/pandas_ta/momentum/slope.py @@ -2,9 +2,11 @@ from numpy import arctan as npAtan from numpy import pi as npPi from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def slope( close, length=None, as_angle=None, to_degrees=None, vertical=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/momentum/smi.py b/pandas_ta/momentum/smi.py index 9e13695..24b4642 100644 --- a/pandas_ta/momentum/smi.py +++ b/pandas_ta/momentum/smi.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from .tsi import tsi from pandas_ta.overlap import ema from pandas_ta.utils import get_offset, verify_series -def smi(close, fast=None, slow=None, signal=None, scalar=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/squeeze.py b/pandas_ta/momentum/squeeze.py index e764b1f..f38f33f 100644 --- a/pandas_ta/momentum/squeeze.py +++ b/pandas_ta/momentum/squeeze.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from numpy import nan as npNaN -from pandas import DataFrame +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 @@ -9,7 +9,9 @@ from pandas_ta.utils import get_offset from pandas_ta.utils import unsigned_differences, verify_series -def squeeze(high, low, close, bb_length=None, bb_std=None, kc_length=None, kc_scalar=None, mom_length=None, mom_smooth=None, use_tr=None, mamode=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/squeeze_pro.py b/pandas_ta/momentum/squeeze_pro.py index 6f76bdb..e1facda 100644 --- a/pandas_ta/momentum/squeeze_pro.py +++ b/pandas_ta/momentum/squeeze_pro.py @@ -1,14 +1,22 @@ # -*- coding: utf-8 -*- -from pandas_ta import np, pd +from numpy import nan +from pandas import DataFrame, Series from pandas_ta.momentum import mom from pandas_ta.overlap import ema, sma from pandas_ta.trend import decreasing, increasing from pandas_ta.volatility import bbands, kc -from pandas_ta.utils import get_offset -from pandas_ta.utils import unsigned_differences, verify_series +from pandas_ta.utils import get_offset, unsigned_differences, verify_series -def squeeze_pro(high, low, close, bb_length=None, bb_std=None, kc_length=None, kc_scalar_wide=None, kc_scalar_normal=None, kc_scalar_narrow=None, mom_length=None, mom_smooth=None, use_tr=None, mamode=None, offset=None, **kwargs): +def squeeze_pro( + high: Series, low: Series, close: Series, + bb_length: int = None, bb_std: float = None, + kc_length: int = None, kc_scalar_wide: float = None, + kc_scalar_normal: float = None, kc_scalar_narrow: float = None, + mom_length: int = None, mom_smooth: int = None, + use_tr=None, mamode: str = None, + offset: int = None, **kwargs + ) -> DataFrame: """Squeeze PRO(SQZPRO) This indicator is an extended version of "TTM Squeeze" from John Carter. @@ -51,7 +59,7 @@ def squeeze_pro(high, low, close, bb_length=None, bb_std=None, kc_length=None, k pd.DataFrame: SQZPRO, SQZPRO_ON_WIDE, SQZPRO_ON_NORMAL, SQZPRO_ON_NARROW, SQZPRO_OFF_WIDE, SQZPRO_NO 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 @@ -81,7 +89,7 @@ def squeeze_pro(high, low, close, bb_length=None, bb_std=None, kc_length=None, k 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_wide = kc(high, low, close, length=kc_length, scalar=kc_scalar_wide, mamode=mamode, tr=use_tr) kch_normal = kc(high, low, close, length=kc_length, scalar=kc_scalar_normal, mamode=mamode, tr=use_tr) @@ -115,7 +123,7 @@ def squeeze_pro(high, low, close, bb_length=None, bb_std=None, kc_length=None, k squeeze_off_wide = squeeze_off_wide.shift(offset) no_squeeze = no_squeeze.shift(offset) - # Handle fills + # Fill if "fillna" in kwargs: squeeze.fillna(kwargs["fillna"], inplace=True) squeeze_on_wide.fillna(kwargs["fillna"], inplace=True) @@ -131,7 +139,7 @@ def squeeze_pro(high, low, close, bb_length=None, bb_std=None, kc_length=None, k squeeze_off_wide.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_wide}_{kc_scalar_normal}_{kc_scalar_narrow}" squeeze.name = f"SQZPRO{_props}" @@ -144,11 +152,11 @@ def squeeze_pro(high, low, close, bb_length=None, bb_std=None, kc_length=None, k f"SQZPRO_OFF": squeeze_off_wide.astype(int) if asint else squeeze_off_wide, f"SQZPRO_NO": no_squeeze.astype(int) if asint else no_squeeze, } - df = pd.DataFrame(data) + df = DataFrame(data) 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] @@ -161,17 +169,17 @@ def squeeze_pro(high, low, close, bb_length=None, bb_std=None, kc_length=None, k neg_dec *= squeeze neg_inc *= squeeze - pos_inc.replace(0, np.nan, inplace=True) - pos_dec.replace(0, np.nan, inplace=True) - neg_dec.replace(0, np.nan, inplace=True) - neg_inc.replace(0, np.nan, 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, np.nan, inplace=True) - sqz_dec.replace(0, np.nan, inplace=True) + sqz_inc.replace(0, nan, inplace=True) + sqz_dec.replace(0, nan, inplace=True) - # Handle fills + # Fill if "fillna" in kwargs: sqz_inc.fillna(kwargs["fillna"], inplace=True) sqz_dec.fillna(kwargs["fillna"], inplace=True) @@ -179,6 +187,7 @@ def squeeze_pro(high, low, close, bb_length=None, bb_std=None, kc_length=None, k pos_dec.fillna(kwargs["fillna"], inplace=True) neg_dec.fillna(kwargs["fillna"], inplace=True) neg_inc.fillna(kwargs["fillna"], inplace=True) + if "fill_method" in kwargs: sqz_inc.fillna(method=kwargs["fill_method"], inplace=True) sqz_dec.fillna(method=kwargs["fill_method"], inplace=True) diff --git a/pandas_ta/momentum/stc.py b/pandas_ta/momentum/stc.py index 0a0d6b1..ee1668d 100644 --- a/pandas_ta/momentum/stc.py +++ b/pandas_ta/momentum/stc.py @@ -4,7 +4,8 @@ from pandas_ta.overlap import ema from pandas_ta.utils import get_offset, non_zero_range, verify_series -def stc(close, tclength=None, fast=None, slow=None, factor=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/stoch.py b/pandas_ta/momentum/stoch.py index 7f5654a..0a85ddc 100644 --- a/pandas_ta/momentum/stoch.py +++ b/pandas_ta/momentum/stoch.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta import Imports from pandas_ta.overlap import ma from pandas_ta.utils import get_offset, non_zero_range, tal_ma, verify_series -def stoch(high, low, close, k=None, d=None, smooth_k=None, mamode=None, talib=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/momentum/stochf.py b/pandas_ta/momentum/stochf.py index bbd80c8..532d50c 100644 --- a/pandas_ta/momentum/stochf.py +++ b/pandas_ta/momentum/stochf.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta import Imports from pandas_ta.overlap import ma from pandas_ta.utils import get_offset, non_zero_range, tal_ma, verify_series -def stochf(high, low, close, k=None, d=None, mamode=None, talib=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/stochrsi.py b/pandas_ta/momentum/stochrsi.py index 23899e0..b9addd2 100644 --- a/pandas_ta/momentum/stochrsi.py +++ b/pandas_ta/momentum/stochrsi.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from .rsi import rsi from pandas_ta.overlap import ma from pandas_ta.utils import get_offset, non_zero_range, verify_series -def stochrsi(close, length=None, rsi_length=None, k=None, d=None, mamode=None, offset=None, **kwargs): +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) diff --git a/pandas_ta/momentum/td_seq.py b/pandas_ta/momentum/td_seq.py index de1babe..b38fc08 100644 --- a/pandas_ta/momentum/td_seq.py +++ b/pandas_ta/momentum/td_seq.py @@ -5,7 +5,7 @@ from pandas import DataFrame, Series from pandas_ta.utils import get_offset, verify_series -def td_seq(close, asint=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/trix.py b/pandas_ta/momentum/trix.py index e4a0dbe..2173f33 100644 --- a/pandas_ta/momentum/trix.py +++ b/pandas_ta/momentum/trix.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +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, length=None, signal=None, scalar=None, drift=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/momentum/tsi.py b/pandas_ta/momentum/tsi.py index 46fddeb..a2ac652 100644 --- a/pandas_ta/momentum/tsi.py +++ b/pandas_ta/momentum/tsi.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.overlap import ema, ma from pandas_ta.utils import get_drift, get_offset, verify_series -def tsi(close, fast=None, slow=None, signal=None, scalar=None, mamode=None, drift=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/uo.py b/pandas_ta/momentum/uo.py index 9b9fbbd..5094190 100644 --- a/pandas_ta/momentum/uo.py +++ b/pandas_ta/momentum/uo.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta import Imports from pandas_ta.utils import get_drift, get_offset, verify_series -def uo(high, low, close, fast=None, medium=None, slow=None, fast_w=None, medium_w=None, slow_w=None, talib=None, drift=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/momentum/willr.py b/pandas_ta/momentum/willr.py index 38eaa57..2489fb7 100644 --- a/pandas_ta/momentum/willr.py +++ b/pandas_ta/momentum/willr.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def willr(high, low, close, length=None, talib=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/alligator.py b/pandas_ta/overlap/alligator.py index 2ed3d4e..76e93c0 100644 --- a/pandas_ta/overlap/alligator.py +++ b/pandas_ta/overlap/alligator.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- # from numpy import nan as npNaN -from pandas import DataFrame +from pandas import DataFrame, Series from .smma import smma from pandas_ta.utils import get_offset, verify_series -def alligator(close, jaw=None, teeth=None, lips=None, talib=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/alma.py b/pandas_ta/overlap/alma.py index 72690c1..90ed17a 100644 --- a/pandas_ta/overlap/alma.py +++ b/pandas_ta/overlap/alma.py @@ -11,7 +11,8 @@ from pandas import Series from pandas_ta.utils import get_offset, strided_window, verify_series -def alma(close, length=None, sigma=None, dist_offset=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/dema.py b/pandas_ta/overlap/dema.py index 495b243..9f8f043 100644 --- a/pandas_ta/overlap/dema.py +++ b/pandas_ta/overlap/dema.py @@ -2,9 +2,10 @@ from .ema import ema from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def dema(close, length=None, talib=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/ema.py b/pandas_ta/overlap/ema.py index 3f5fbac..329bbc0 100644 --- a/pandas_ta/overlap/ema.py +++ b/pandas_ta/overlap/ema.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- -from pandas_ta import Imports, np +from numpy import nan +from pandas import Series +from pandas_ta.maps import Imports from pandas_ta.utils import get_offset, verify_series try: @@ -7,7 +9,6 @@ try: except ImportError: njit = lambda _: _ - # Almost there # @njit # def np_ema(x: np.ndarray, n: int): @@ -21,7 +22,11 @@ except ImportError: # # return np_prepend(result, n - 1) -def ema(close, length=None, talib=None, presma=None, offset=None, **kwargs): +def ema( + close: Series, length: int = None, + talib: bool = None, presma: bool = None, + offset: int = None, **kwargs + ) -> Series: """Exponential Moving Average (EMA) The Exponential Moving Average is more responsive moving average compared to the @@ -51,7 +56,7 @@ def ema(close, length=None, talib=None, presma=None, offset=None, **kwargs): Returns: pd.Series: New feature generated. """ - # Validate Arguments + # Validate length = int(length) if length and length > 0 else 10 presma = bool(presma) if isinstance(presma, bool) else True mode_tal = bool(talib) if isinstance(talib, bool) else True @@ -61,7 +66,7 @@ def ema(close, length=None, talib=None, presma=None, offset=None, **kwargs): if close is None: return - # Calculate Result + # Calculate if Imports["talib"] and mode_tal: from talib import EMA ema = EMA(close, length) @@ -69,7 +74,7 @@ def ema(close, length=None, talib=None, presma=None, offset=None, **kwargs): if presma: # TA Lib implementation close = close.copy() sma_nth = close[0:length].mean() - close[:length - 1] = np.nan + close[:length - 1] = nan close.iloc[length - 1] = sma_nth ema = close.ewm(span=length, adjust=adjust).mean() @@ -77,13 +82,13 @@ def ema(close, length=None, talib=None, presma=None, offset=None, **kwargs): if offset != 0: ema = ema.shift(offset) - # Handle fills + # Fill if "fillna" in kwargs: ema.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: ema.fillna(method=kwargs["fill_method"], inplace=True) - # Name & Category + # Name and Category ema.name = f"EMA_{length}" ema.category = "overlap" diff --git a/pandas_ta/overlap/fwma.py b/pandas_ta/overlap/fwma.py index cdd7733..7a9c8f9 100644 --- a/pandas_ta/overlap/fwma.py +++ b/pandas_ta/overlap/fwma.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import fibonacci, get_offset, verify_series, weights +from pandas import Series -def fwma(close, length=None, asc=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/hilo.py b/pandas_ta/overlap/hilo.py index c467442..fdc59a1 100644 --- a/pandas_ta/overlap/hilo.py +++ b/pandas_ta/overlap/hilo.py @@ -5,7 +5,8 @@ from .ma import ma from pandas_ta.utils import get_offset, verify_series -def hilo(high, low, close, high_length=None, low_length=None, mamode=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/hl2.py b/pandas_ta/overlap/hl2.py index a5236e9..675d10a 100644 --- a/pandas_ta/overlap/hl2.py +++ b/pandas_ta/overlap/hl2.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def hl2(high, low, offset=None, **kwargs): +def hl2(high: Series, low: Series, offset: int = None, **kwargs) -> Series: """HL2 HL2 is the midpoint/average of high and low. diff --git a/pandas_ta/overlap/hlc3.py b/pandas_ta/overlap/hlc3.py index 4c3b241..2c43135 100644 --- a/pandas_ta/overlap/hlc3.py +++ b/pandas_ta/overlap/hlc3.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def hlc3(high, low, close, talib=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/overlap/hma.py b/pandas_ta/overlap/hma.py index 05413bd..1ecd331 100644 --- a/pandas_ta/overlap/hma.py +++ b/pandas_ta/overlap/hma.py @@ -2,9 +2,10 @@ from numpy import sqrt as npSqrt from .wma import wma from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def hma(close, length=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/hwma.py b/pandas_ta/overlap/hwma.py index a03f769..5f3de6c 100644 --- a/pandas_ta/overlap/hwma.py +++ b/pandas_ta/overlap/hwma.py @@ -3,7 +3,7 @@ from pandas import Series from pandas_ta.utils import get_offset, verify_series -def hwma(close, na=None, nb=None, nc=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/ichimoku.py b/pandas_ta/overlap/ichimoku.py index f0cc4c1..79ef8d2 100644 --- a/pandas_ta/overlap/ichimoku.py +++ b/pandas_ta/overlap/ichimoku.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from pandas import date_range, DataFrame, RangeIndex, Timedelta +from pandas import date_range, DataFrame, RangeIndex, Timedelta, Series from .midprice import midprice from pandas_ta.utils import get_offset, verify_series -def ichimoku(high, low, close, tenkan=None, kijun=None, senkou=None, include_chikou=True, offset=None, **kwargs): +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. diff --git a/pandas_ta/overlap/jma.py b/pandas_ta/overlap/jma.py index ebd851b..24368d6 100644 --- a/pandas_ta/overlap/jma.py +++ b/pandas_ta/overlap/jma.py @@ -9,7 +9,7 @@ from pandas import Series from pandas_ta.utils import get_offset, verify_series -def jma(close, length=None, phase=None, offset=None, **kwargs): +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" diff --git a/pandas_ta/overlap/kama.py b/pandas_ta/overlap/kama.py index aa75b40..f11e898 100644 --- a/pandas_ta/overlap/kama.py +++ b/pandas_ta/overlap/kama.py @@ -5,7 +5,8 @@ from pandas_ta.overlap.ma import ma from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series -def kama(close, length=None, fast=None, slow=None, mamode=None, drift=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/linreg.py b/pandas_ta/overlap/linreg.py index f673ef2..d28e948 100644 --- a/pandas_ta/overlap/linreg.py +++ b/pandas_ta/overlap/linreg.py @@ -9,7 +9,7 @@ from pandas_ta import Imports from pandas_ta.utils import get_offset, strided_window, verify_series -def linreg(close, length=None, talib=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/ma.py b/pandas_ta/overlap/ma.py index c4b7134..b87346a 100644 --- a/pandas_ta/overlap/ma.py +++ b/pandas_ta/overlap/ma.py @@ -19,7 +19,7 @@ from .vidya import vidya from .wma import wma -def ma(name:str = None, source:Series = None, **kwargs) -> Series: +def ma(name: str = None, source: Series = None, **kwargs) -> Series: """Simple MA Utility for easier MA selection Available MAs: @@ -50,7 +50,7 @@ def ma(name:str = None, source:Series = None, **kwargs) -> Series: return _mas elif isinstance(name, str) and name.lower() in _mas: name = name.lower() - else: # "ema" + else: # "ema" name = _mas[1] if name == "dema": return dema(source, **kwargs) diff --git a/pandas_ta/overlap/mcgd.py b/pandas_ta/overlap/mcgd.py index 8607314..1f65d15 100644 --- a/pandas_ta/overlap/mcgd.py +++ b/pandas_ta/overlap/mcgd.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def mcgd(close, length=None, offset=None, c=None, **kwargs): +def mcgd(close: Series, length: int = None, offset: int = None, c: float = None, **kwargs) -> Series: """McGinley Dynamic Indicator The McGinley Dynamic looks like a moving average line, yet it is actually a diff --git a/pandas_ta/overlap/midpoint.py b/pandas_ta/overlap/midpoint.py index dd678d0..3fa7672 100644 --- a/pandas_ta/overlap/midpoint.py +++ b/pandas_ta/overlap/midpoint.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def midpoint(close, length=None, talib=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/overlap/midprice.py b/pandas_ta/overlap/midprice.py index 08d1aff..508f905 100644 --- a/pandas_ta/overlap/midprice.py +++ b/pandas_ta/overlap/midprice.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def midprice(high, low, length=None, talib=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/overlap/ohlc4.py b/pandas_ta/overlap/ohlc4.py index 02495aa..d7e4461 100644 --- a/pandas_ta/overlap/ohlc4.py +++ b/pandas_ta/overlap/ohlc4.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def ohlc4(open_, high, low, close, offset=None, **kwargs): +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. diff --git a/pandas_ta/overlap/pwma.py b/pandas_ta/overlap/pwma.py index c0fe60e..179e114 100644 --- a/pandas_ta/overlap/pwma.py +++ b/pandas_ta/overlap/pwma.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, pascals_triangle, verify_series, weights +from pandas import Series -def pwma(close, length=None, asc=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/rma.py b/pandas_ta/overlap/rma.py index 0e1e5fb..f208b72 100644 --- a/pandas_ta/overlap/rma.py +++ b/pandas_ta/overlap/rma.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def rma(close, length=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/sinwma.py b/pandas_ta/overlap/sinwma.py index f7a50bd..114cc35 100644 --- a/pandas_ta/overlap/sinwma.py +++ b/pandas_ta/overlap/sinwma.py @@ -5,7 +5,7 @@ from pandas import Series from pandas_ta.utils import get_offset, verify_series, weights -def sinwma(close, length=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/sma.py b/pandas_ta/overlap/sma.py index afa7c6d..267ba9c 100644 --- a/pandas_ta/overlap/sma.py +++ b/pandas_ta/overlap/sma.py @@ -1,17 +1,19 @@ # -*- coding: utf-8 -*- -from pandas_ta import Imports, np, pd +from numpy import convolve, ndarray, ones +from pandas import Series +from pandas_ta.maps import Imports from pandas_ta.utils import get_offset, np_prepend, verify_series + try: from numba import njit except ImportError: njit = lambda _: _ - @njit -def np_sma(x: np.ndarray, n: int): +def np_sma(x: ndarray, n: int): """https://github.com/numba/numba/issues/4119""" - result = np.convolve(np.ones(n) / n, x)[n - 1:1 - n] + result = convolve(ones(n) / n, x)[n - 1:1 - n] return np_prepend(result, n - 1) ## SMA: Alternative Implementations @@ -20,7 +22,6 @@ def np_sma(x: np.ndarray, n: int): # result = np.convolve(x, np.ones(n), mode="valid") / n # return np_prepend(result, n - 1) - # @njit # def np_sma(x: np.ndarray, n: int): # csum = np.cumsum(x, dtype=float) @@ -29,7 +30,11 @@ def np_sma(x: np.ndarray, n: int): # return np_prepend(result, n - 1) -def sma(close, length=None, talib=None, offset=None, **kwargs): +def sma( + close: Series, length: int = None, + talib: bool = None, + offset: int = None, **kwargs + ) -> Series: """Simple Moving Average (SMA) The Simple Moving Average is the classic moving average that is the equally @@ -54,7 +59,7 @@ def sma(close, length=None, talib=None, offset=None, **kwargs): 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 close = verify_series(close, max(length, min_periods)) @@ -63,26 +68,26 @@ def sma(close, length=None, talib=None, offset=None, **kwargs): if close is None: return - # Calculate Result + # Calculate if Imports["talib"] and mode_tal: from talib import SMA sma = SMA(close, length) else: np_close = close.values sma = np_sma(np_close, length) - sma = pd.Series(sma, index=close.index) + sma = Series(sma, index=close.index) # Offset if offset != 0: sma = sma.shift(offset) - # Handle fills + # Fill if "fillna" in kwargs: sma.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: sma.fillna(method=kwargs["fill_method"], inplace=True) - # Name & Category + # Name and Category sma.name = f"SMA_{length}" sma.category = "overlap" diff --git a/pandas_ta/overlap/smma.py b/pandas_ta/overlap/smma.py index 1c6f3cc..2d3dc02 100644 --- a/pandas_ta/overlap/smma.py +++ b/pandas_ta/overlap/smma.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- from numpy import nan as npNaN from pandas_ta.overlap.ma import ma +from pandas import Series from pandas_ta.utils import get_offset, verify_series -def smma(close, length=None, mamode=None, talib=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/ssf.py b/pandas_ta/overlap/ssf.py index 5a252a8..268db87 100644 --- a/pandas_ta/overlap/ssf.py +++ b/pandas_ta/overlap/ssf.py @@ -1,21 +1,22 @@ # -*- 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: from numba import njit except ImportError: njit = lambda _: _ - @njit -def np_ssf(x: np.ndarray, n: int, pi: float, sqrt2: float): +def np_ssf(x: ndarray, n: int, pi: float, sqrt2: float): """Ehler's Super Smoother Filter http://traders.com/documentation/feedbk_docs/2014/01/traderstips.html """ - m, ratio, result = x.size, sqrt2 / n, np.copy(x) - a = np.exp(-pi * ratio) - b = 2 * a * np.cos(180 * ratio) + m, ratio, result = x.size, sqrt2 / n, copy(x) + a = exp(-pi * ratio) + b = 2 * a * cos(180 * ratio) c = a * a - b + 1 for i in range(2, m): @@ -24,15 +25,14 @@ def np_ssf(x: np.ndarray, n: int, pi: float, sqrt2: float): return result - @njit -def np_ssf_everget(x: np.ndarray, n: int, pi: float, sqrt2: float): +def np_ssf_everget(x: ndarray, n: int, pi: float, sqrt2: float): """John F. Ehler's Super Smoother Filter by Everget (2 poles), Tradingview https://www.tradingview.com/script/VdJy0yBJ-Ehlers-Super-Smoother-Filter/ """ - m, arg, result = x.size, pi * sqrt2 / n, np.copy(x) - a = np.exp(-arg) - b = 2 * a * np.cos(arg) + m, arg, result = x.size, pi * sqrt2 / n, copy(x) + a = exp(-arg) + b = 2 * a * cos(arg) for i in range(2, m): result[i] = 0.5 * (a * a - b + 1) * (x[i] + x[i - 1]) \ @@ -41,7 +41,11 @@ def np_ssf_everget(x: np.ndarray, n: int, pi: float, sqrt2: float): return result -def ssf(close, length=None, everget=None, pi=None, sqrt2=None, offset=None, **kwargs): +def ssf( + close: Series, length: int = None, + everget: bool = None, pi: float = None, sqrt2: float = None, + offset: int = None, **kwargs + ) -> Series: """Ehler's Super Smoother Filter (SSF) © 2013 John F. Ehlers's solution to reduce lag and remove aliasing noise with his @@ -77,7 +81,7 @@ def ssf(close, length=None, everget=None, pi=None, sqrt2=None, offset=None, **kw Returns: pd.Series: New feature generated. """ - # Validate Arguments + # Validate length = int(length) if isinstance(length, int) and length > 0 else 20 everget = bool(everget) if isinstance(everget, bool) else False pi = float(pi) if isinstance(pi, float) and pi > 0 else 3.14159 @@ -87,25 +91,25 @@ def ssf(close, length=None, everget=None, pi=None, sqrt2=None, offset=None, **kw if close is None: return - # Calculate Result + # Calculate np_close = close.values if everget: ssf = np_ssf_everget(np_close, length, pi, sqrt2) else: ssf = np_ssf(np_close, length, pi, sqrt2) - 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"SSF{'e' if everget else ''}_{length}" ssf.category = "overlap" diff --git a/pandas_ta/overlap/supertrend.py b/pandas_ta/overlap/supertrend.py index d192eca..3756d32 100644 --- a/pandas_ta/overlap/supertrend.py +++ b/pandas_ta/overlap/supertrend.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- from numpy import nan as npNaN -from pandas import DataFrame +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 -def supertrend(high, low, close, length=None, multiplier=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/swma.py b/pandas_ta/overlap/swma.py index 91d32bb..d1df218 100644 --- a/pandas_ta/overlap/swma.py +++ b/pandas_ta/overlap/swma.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, symmetric_triangle, verify_series, weights +from pandas import Series -def swma(close, length=None, asc=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/t3.py b/pandas_ta/overlap/t3.py index 8b1b7e3..d675005 100644 --- a/pandas_ta/overlap/t3.py +++ b/pandas_ta/overlap/t3.py @@ -2,9 +2,10 @@ from .ema import ema from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def t3(close, length=None, a=None, talib=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/tema.py b/pandas_ta/overlap/tema.py index efd288e..af59904 100644 --- a/pandas_ta/overlap/tema.py +++ b/pandas_ta/overlap/tema.py @@ -2,9 +2,10 @@ from .ema import ema from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def tema(close, length=None, talib=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/overlap/trima.py b/pandas_ta/overlap/trima.py index 84658b9..29685ba 100644 --- a/pandas_ta/overlap/trima.py +++ b/pandas_ta/overlap/trima.py @@ -2,9 +2,10 @@ from .sma import sma from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def trima(close, length=None, talib=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/vidya.py b/pandas_ta/overlap/vidya.py index 0c0273a..32b6b5f 100644 --- a/pandas_ta/overlap/vidya.py +++ b/pandas_ta/overlap/vidya.py @@ -4,7 +4,7 @@ from pandas import Series from pandas_ta.utils import get_drift, get_offset, verify_series -def vidya(close, length=None, drift=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/vwap.py b/pandas_ta/overlap/vwap.py index 591a378..b328a11 100644 --- a/pandas_ta/overlap/vwap.py +++ b/pandas_ta/overlap/vwap.py @@ -1,8 +1,11 @@ # -*- coding: utf-8 -*- from .hlc3 import hlc3 from pandas_ta.utils import get_offset, is_datetime_ordered, verify_series +from pandas import Series -def vwap(high, low, close, volume, anchor=None, offset=None, **kwargs): + +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 diff --git a/pandas_ta/overlap/vwma.py b/pandas_ta/overlap/vwma.py index 205fa7c..6f531c7 100644 --- a/pandas_ta/overlap/vwma.py +++ b/pandas_ta/overlap/vwma.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from .sma import sma from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def vwma(close, volume, length=None, offset=None, **kwargs): +def vwma(close: Series, volume: Series, length: int = None, offset: int = None, **kwargs) -> Series: """Volume Weighted Moving Average (VWMA) Volume Weighted Moving Average. diff --git a/pandas_ta/overlap/wcp.py b/pandas_ta/overlap/wcp.py index a234eeb..e7c9471 100644 --- a/pandas_ta/overlap/wcp.py +++ b/pandas_ta/overlap/wcp.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def wcp(high, low, close, talib=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/overlap/wma.py b/pandas_ta/overlap/wma.py index b77a70a..6fbb943 100644 --- a/pandas_ta/overlap/wma.py +++ b/pandas_ta/overlap/wma.py @@ -4,7 +4,8 @@ from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series -def wma(close, length=None, asc=None, talib=None, offset=None, **kwargs): +def wma(close: Series, length: int = None, asc: bool = None, talib: bool = None, offset: int = None, + **kwargs) -> Series: """Weighted Moving Average (WMA) The Weighted Moving Average where the weights are linearly increasing and diff --git a/pandas_ta/overlap/zlma.py b/pandas_ta/overlap/zlma.py index a2a0c83..92425bb 100644 --- a/pandas_ta/overlap/zlma.py +++ b/pandas_ta/overlap/zlma.py @@ -4,9 +4,10 @@ # ) from pandas_ta.overlap import ma from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def zlma(close, length=None, mamode=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/performance/drawdown.py b/pandas_ta/performance/drawdown.py index 021da08..638e2b3 100644 --- a/pandas_ta/performance/drawdown.py +++ b/pandas_ta/performance/drawdown.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- from numpy import log as nplog from numpy import seterr -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.utils import get_offset, verify_series -def drawdown(close, offset=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, diff --git a/pandas_ta/performance/log_return.py b/pandas_ta/performance/log_return.py index 9c125d7..e46b361 100644 --- a/pandas_ta/performance/log_return.py +++ b/pandas_ta/performance/log_return.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from numpy import log as nplog from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def log_return(close, length=None, cumulative=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/performance/percent_return.py b/pandas_ta/performance/percent_return.py index 864a909..ea22ebd 100644 --- a/pandas_ta/performance/percent_return.py +++ b/pandas_ta/performance/percent_return.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def percent_return(close, length=None, cumulative=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/statistics/entropy.py b/pandas_ta/statistics/entropy.py index 0fa515a..a4822fe 100644 --- a/pandas_ta/statistics/entropy.py +++ b/pandas_ta/statistics/entropy.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from numpy import log as npLog from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def entropy(close, length=None, base=None, offset=None, **kwargs): +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 diff --git a/pandas_ta/statistics/kurtosis.py b/pandas_ta/statistics/kurtosis.py index a269c46..ba9f09f 100644 --- a/pandas_ta/statistics/kurtosis.py +++ b/pandas_ta/statistics/kurtosis.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def kurtosis(close, length=None, offset=None, **kwargs): +def kurtosis(close: Series, length: int = None, offset: int = None, **kwargs) -> Series: """Rolling Kurtosis Calculates the Kurtosis over a rolling period. diff --git a/pandas_ta/statistics/mad.py b/pandas_ta/statistics/mad.py index 57c9035..47c53d6 100644 --- a/pandas_ta/statistics/mad.py +++ b/pandas_ta/statistics/mad.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from numpy import fabs as npfabs from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def mad(close, length=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/statistics/median.py b/pandas_ta/statistics/median.py index f9757ba..fe45cbb 100644 --- a/pandas_ta/statistics/median.py +++ b/pandas_ta/statistics/median.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def median(close, length=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/statistics/quantile.py b/pandas_ta/statistics/quantile.py index 3d11ad7..a45f3dd 100644 --- a/pandas_ta/statistics/quantile.py +++ b/pandas_ta/statistics/quantile.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def quantile(close, length=None, q=None, offset=None, **kwargs): +def quantile(close: Series, length: int = None, q: float = None, offset: int = None, **kwargs) -> Series: """Rolling Quantile Calculates the Quantile over a rolling period. diff --git a/pandas_ta/statistics/skew.py b/pandas_ta/statistics/skew.py index f89e50f..8ddc9ed 100644 --- a/pandas_ta/statistics/skew.py +++ b/pandas_ta/statistics/skew.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def skew(close, length=None, offset=None, **kwargs): +def skew(close: Series, length: int = None, offset: int = None, **kwargs) -> Series: """Rolling Skew Calculates the Skew over a rolling period. diff --git a/pandas_ta/statistics/stdev.py b/pandas_ta/statistics/stdev.py index d61fea3..de4910d 100644 --- a/pandas_ta/statistics/stdev.py +++ b/pandas_ta/statistics/stdev.py @@ -3,9 +3,11 @@ 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 pandas import Series -def stdev(close, length=None, ddof=None, talib=None, offset=None, **kwargs): +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. diff --git a/pandas_ta/statistics/tos_stdevall.py b/pandas_ta/statistics/tos_stdevall.py index 64432f6..654efb6 100644 --- a/pandas_ta/statistics/tos_stdevall.py +++ b/pandas_ta/statistics/tos_stdevall.py @@ -7,7 +7,9 @@ from pandas import DataFrame, DatetimeIndex, Series from .stdev import stdev as stdev from pandas_ta.utils import get_offset, verify_series -def tos_stdevall(close, length=None, stds=None, ddof=None, offset=None, **kwargs): + +def tos_stdevall(close: Series, length: int = None, stds: list = None, ddof: int = None, offset: int = None, + **kwargs) -> DataFrame: """TD Ameritrade's Think or Swim Standard Deviation All (TOS_STDEV) A port of TD Ameritrade's Think or Swim Standard Deviation All indicator which diff --git a/pandas_ta/statistics/variance.py b/pandas_ta/statistics/variance.py index c4443e6..e3aa0ea 100644 --- a/pandas_ta/statistics/variance.py +++ b/pandas_ta/statistics/variance.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def variance(close, length=None, ddof=None, talib=None, offset=None, **kwargs): +def variance(close: Series, length: int = None, ddof: int = None, talib: bool = None, offset: int = None, + **kwargs) -> Series: """Rolling Variance Calculates the Variance over a rolling period. diff --git a/pandas_ta/statistics/zscore.py b/pandas_ta/statistics/zscore.py index c432a29..833d7aa 100644 --- a/pandas_ta/statistics/zscore.py +++ b/pandas_ta/statistics/zscore.py @@ -2,9 +2,10 @@ from pandas_ta.overlap import sma from .stdev import stdev from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def zscore(close, length=None, std=None, offset=None, **kwargs): +def zscore(close: Series, length: int = None, std: float = None, offset: int = None, **kwargs) -> Series: """Rolling Z Score Calculates the Z Score over a rolling period. diff --git a/pandas_ta/trend/adx.py b/pandas_ta/trend/adx.py index df0943c..570e9a6 100644 --- a/pandas_ta/trend/adx.py +++ b/pandas_ta/trend/adx.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.overlap import ma from pandas_ta.volatility import atr from pandas_ta.utils import get_drift, get_offset, verify_series, zero -def adx(high, low, close, length=None, lensig=None, scalar=None, mamode=None, drift=None, offset=None, **kwargs): +def adx(high: Series, low: Series, close: Series, length: int = None, lensig: int = None, scalar: float = None, + mamode: str = None, drift: int = None, offset: int = None, **kwargs) -> DataFrame: """Average Directional Movement (ADX) Average Directional Movement is meant to quantify trend strength by measuring diff --git a/pandas_ta/trend/amat.py b/pandas_ta/trend/amat.py index 77c33e4..ec469d7 100644 --- a/pandas_ta/trend/amat.py +++ b/pandas_ta/trend/amat.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from .long_run import long_run from .short_run import short_run from pandas_ta.overlap import ma from pandas_ta.utils import get_offset, verify_series -def amat(close=None, fast=None, slow=None, lookback=None, mamode=None, offset=None, **kwargs): +def amat(close: Series, fast: int = None, slow: int = None, lookback: int = None, mamode: str = None, + offset: int = None, **kwargs) -> DataFrame: """Archer Moving Averages Trends (AMAT) Archer Moving Averages Trends (AMAT) developed by Kevin Johnson provides diff --git a/pandas_ta/trend/aroon.py b/pandas_ta/trend/aroon.py index 28066b3..9cc02eb 100644 --- a/pandas_ta/trend/aroon.py +++ b/pandas_ta/trend/aroon.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta import Imports from pandas_ta.utils import get_offset, verify_series from pandas_ta.utils import recent_maximum_index, recent_minimum_index -def aroon(high, low, length=None, scalar=None, talib=None, offset=None, **kwargs): +def aroon(high: Series, low: Series, length: int = None, scalar: float = None, talib: bool = None, offset: int = None, + **kwargs) -> DataFrame: """Aroon & Aroon Oscillator (AROON) Aroon attempts to identify if a security is trending and how strong. diff --git a/pandas_ta/trend/chop.py b/pandas_ta/trend/chop.py index 6cc0798..9be91ee 100644 --- a/pandas_ta/trend/chop.py +++ b/pandas_ta/trend/chop.py @@ -3,9 +3,11 @@ from numpy import log10 as npLog10 from numpy import log as npLn from pandas_ta.volatility import atr from pandas_ta.utils import get_drift, get_offset, verify_series +from pandas import Series -def chop(high, low, close, length=None, atr_length=None, ln=None, scalar=None, drift=None, offset=None, **kwargs): +def chop(high: Series, low: Series, close: Series, length: int = None, atr_length: int = None, ln: bool = None, + scalar: float = None, drift: int = None, offset: int = None, **kwargs) -> Series: """Choppiness Index (CHOP) The Choppiness Index was created by Australian commodity trader diff --git a/pandas_ta/trend/cksp.py b/pandas_ta/trend/cksp.py index 56247f5..e7327db 100644 --- a/pandas_ta/trend/cksp.py +++ b/pandas_ta/trend/cksp.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.volatility import atr from pandas_ta.utils import get_offset, verify_series -def cksp(high, low, close, p=None, x=None, q=None, tvmode=None, offset=None, **kwargs): +def cksp(high: Series, low: Series, close: Series, p: int = None, x: float = None, q: int = None, tvmode: bool = None, + offset: int = None, **kwargs) -> DataFrame: """Chande Kroll Stop (CKSP) The Tushar Chande and Stanley Kroll in their book diff --git a/pandas_ta/trend/decay.py b/pandas_ta/trend/decay.py index fe20e7e..3d678fd 100644 --- a/pandas_ta/trend/decay.py +++ b/pandas_ta/trend/decay.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- from numpy import exp as npExp -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.utils import get_offset, verify_series -def decay(close, kind=None, length=None, mode=None, offset=None, **kwargs): +def decay(close: Series, kind=None, length: int = None, mode: str = None, offset: int = None, **kwargs) -> Series: """Decay Creates a decay moving forward from prior signals like crosses. The default is diff --git a/pandas_ta/trend/decreasing.py b/pandas_ta/trend/decreasing.py index b9a1b4c..8bc681a 100644 --- a/pandas_ta/trend/decreasing.py +++ b/pandas_ta/trend/decreasing.py @@ -1,7 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_drift, get_offset, is_percent, verify_series +from pandas import Series -def decreasing(close, length=None, strict=None, asint=None, percent=None, drift=None, offset=None, **kwargs): + +def decreasing(close: Series, length: int = None, strict: bool = None, asint: bool = None, percent: float = None, + drift: int = None, offset: int = None, **kwargs) -> Series: """Decreasing Returns True if the series is decreasing over a period, False otherwise. diff --git a/pandas_ta/trend/dpo.py b/pandas_ta/trend/dpo.py index 1a9b106..301b3b2 100644 --- a/pandas_ta/trend/dpo.py +++ b/pandas_ta/trend/dpo.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta.overlap import sma from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def dpo(close, length=None, centered=True, offset=None, **kwargs): +def dpo(close: Series, length: int = None, centered: bool = True, offset: int = None, **kwargs) -> Series: """Detrend Price Oscillator (DPO) Is an indicator designed to remove trend from price and make it easier to diff --git a/pandas_ta/trend/increasing.py b/pandas_ta/trend/increasing.py index bbbb691..98aa4f2 100644 --- a/pandas_ta/trend/increasing.py +++ b/pandas_ta/trend/increasing.py @@ -1,7 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_drift, get_offset, is_percent, verify_series +from pandas import Series -def increasing(close, length=None, strict=None, asint=None, percent=None, drift=None, offset=None, **kwargs): + +def increasing(close: Series, length: int = None, strict: bool = None, asint: bool = None, percent: float = None, + drift: int = None, offset: int = None, **kwargs) -> Series: """Increasing Returns True if the series is increasing over a period, False otherwise. diff --git a/pandas_ta/trend/long_run.py b/pandas_ta/trend/long_run.py index 9e13343..b763a56 100644 --- a/pandas_ta/trend/long_run.py +++ b/pandas_ta/trend/long_run.py @@ -2,9 +2,10 @@ from .decreasing import decreasing from .increasing import increasing from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def long_run(fast, slow, length=None, offset=None, **kwargs): +def long_run(fast: Series, slow: Series, length: int = None, offset: int = None, **kwargs) -> Series: """Long Run Long Run was developed by Kevin Johnson that returns a binary Series diff --git a/pandas_ta/trend/psar.py b/pandas_ta/trend/psar.py index f617038..f42b9e7 100644 --- a/pandas_ta/trend/psar.py +++ b/pandas_ta/trend/psar.py @@ -4,7 +4,8 @@ from pandas import DataFrame, Series from pandas_ta.utils import get_offset, verify_series, zero -def psar(high, low, close=None, af0=None, af=None, max_af=None, offset=None, **kwargs): +def psar(high: Series, low: Series, close: Series = None, af0: float = None, af: float = None, max_af: float = None, + offset: int = None, **kwargs) -> DataFrame: """Parabolic Stop and Reverse (psar) Parabolic Stop and Reverse (PSAR) was developed by J. Wells Wilder, that is used diff --git a/pandas_ta/trend/qstick.py b/pandas_ta/trend/qstick.py index 37cd46b..c89a5c6 100644 --- a/pandas_ta/trend/qstick.py +++ b/pandas_ta/trend/qstick.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta.overlap import dema, ema, hma, rma, sma from pandas_ta.utils import get_offset, non_zero_range, verify_series +from pandas import Series -def qstick(open_, close, length=None, offset=None, **kwargs): +def qstick(open_: Series, close: Series, length: int = None, offset: int = None, **kwargs) -> Series: """Q Stick The Q Stick indicator, developed by Tushar Chande, attempts to quantify and diff --git a/pandas_ta/trend/short_run.py b/pandas_ta/trend/short_run.py index 8d87788..cb03076 100644 --- a/pandas_ta/trend/short_run.py +++ b/pandas_ta/trend/short_run.py @@ -2,9 +2,10 @@ from .decreasing import decreasing from .increasing import increasing from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def short_run(fast, slow, length=None, offset=None, **kwargs): +def short_run(fast: Series, slow: Series, length: int = None, offset: int = None, **kwargs) -> Series: """Short Run Short Run was developed by Kevin Johnson that returns a binary Series diff --git a/pandas_ta/trend/trendflex.py b/pandas_ta/trend/trendflex.py index 6074e9c..2e158e9 100644 --- a/pandas_ta/trend/trendflex.py +++ b/pandas_ta/trend/trendflex.py @@ -1,25 +1,26 @@ # -*- coding: utf-8 -*- -from pandas_ta import np, pd +from numpy import cos, exp, nan, ndarray, sqrt, zeros_like +from pandas import Series from pandas_ta.utils import get_offset, verify_series + try: from numba import njit except ImportError: njit = lambda _: _ - @njit -def np_trendflex(x: np.ndarray, n: int, k: int, alpha: float, pi: float, sqrt2: float): +def np_trendflex(x: ndarray, n: int, k: int, alpha: float, pi: float, sqrt2: float): """Ehler's Trendflex http://traders.com/Documentation/FEEDbk_docs/2020/02/TradersTips.html""" m, ratio = x.size, 2 * sqrt2 / k - a = np.exp(-pi * ratio) - b = 2 * a * np.cos(180 * ratio) + a = exp(-pi * ratio) + b = 2 * a * cos(180 * ratio) c = a * a - b + 1 - _f = np.zeros_like(x) - _ms = np.zeros_like(x) - result = np.zeros_like(x) + _f = zeros_like(x) + _ms = zeros_like(x) + result = zeros_like(x) for i in range(2, m): _f[i] = 0.5 * c * (x[i] + x[i - 1]) + b * _f[i - 1] - a * a * _f[i - 2] @@ -32,12 +33,17 @@ def np_trendflex(x: np.ndarray, n: int, k: int, alpha: float, pi: float, sqrt2: _ms[i] = alpha * _sum * _sum + (1 - alpha) * _ms[i - 1] if _ms[i] != 0.0: - result[i] = _sum / np.sqrt(_ms[i]) + result[i] = _sum / sqrt(_ms[i]) return result -def trendflex(close, length=None, smooth=None, alpha=None, pi=None, sqrt2=None, offset=None, **kwargs): +def trendflex( + close: Series, length: int = None, + smooth: int = None, alpha: float = None, + pi: float = None, sqrt2: float = None, + offset: int = None, **kwargs + ) -> Series: """Trendflex (TRENDFLEX) John F. Ehlers introduced two indicators within the article "Reflex: A New @@ -73,7 +79,7 @@ def trendflex(close, length=None, smooth=None, alpha=None, pi=None, sqrt2=None, Returns: pd.Series: New feature generated. """ - # Validate arguments + # Validate length = int(length) if isinstance(length, int) and length > 0 else 20 smooth = int(smooth) if isinstance(smooth, int) and smooth > 0 else 20 alpha = float(alpha) if isinstance(alpha, float) and alpha > 0 else 0.04 @@ -84,25 +90,23 @@ def trendflex(close, length=None, smooth=None, alpha=None, pi=None, sqrt2=None, if close is None: return - # Calculate Result + # Calculate np_close = close.values result = np_trendflex(np_close, length, smooth, alpha, pi, sqrt2) - # print(f"\nresult:\n{result}\n") - result[:length] = np.nan - # print(f"result:\n{result}") - result = pd.Series(result, index=close.index) + result[:length] = nan + result = Series(result, index=close.index) # Offset if offset != 0: result = result.shift(offset) - # Handle fills + # Fill if "fillna" in kwargs: result.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: result.fillna(method=kwargs["fill_method"], inplace=True) - # Name and Categorize it + # Name and Category result.name = f"TRENDFLEX_{length}_{smooth}_{alpha}" result.category = "trend" diff --git a/pandas_ta/trend/tsignals.py b/pandas_ta/trend/tsignals.py index 91075ef..b11988b 100644 --- a/pandas_ta/trend/tsignals.py +++ b/pandas_ta/trend/tsignals.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.utils import get_drift, get_offset, verify_series -def tsignals(trend, asbool=None, trend_reset=0, trade_offset=None, drift=None, offset=None, **kwargs): +def tsignals(trend: Series, asbool: bool = None, trend_reset=0, trade_offset=None, drift: int = None, + offset: int = None, **kwargs) -> DataFrame: """Trend Signals Given a Trend, Trend Signals returns the Trend, Trades, Entries and Exits as diff --git a/pandas_ta/trend/ttm_trend.py b/pandas_ta/trend/ttm_trend.py index d755d21..0e8ad40 100644 --- a/pandas_ta/trend/ttm_trend.py +++ b/pandas_ta/trend/ttm_trend.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.overlap import hl2 from pandas_ta.utils import get_offset, verify_series -def ttm_trend(high, low, close, length=None, offset=None, **kwargs): +def ttm_trend(high: Series, low: Series, close: Series, length: int = None, offset: int = None, **kwargs) -> DataFrame: """TTM Trend (TTM_TRND) This indicator is from John Carters book “Mastering the Trade” and plots the diff --git a/pandas_ta/trend/vhf.py b/pandas_ta/trend/vhf.py index d461433..e63f146 100644 --- a/pandas_ta/trend/vhf.py +++ b/pandas_ta/trend/vhf.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from numpy import fabs as npFabs from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series +from pandas import Series -def vhf(close, length=None, drift=None, offset=None, **kwargs): +def vhf(close: Series, length: int = None, drift: int = None, offset: int = None, **kwargs) -> Series: """Vertical Horizontal Filter (VHF) VHF was created by Adam White to identify trending and ranging markets. diff --git a/pandas_ta/trend/vortex.py b/pandas_ta/trend/vortex.py index 929a817..a9fe1f8 100644 --- a/pandas_ta/trend/vortex.py +++ b/pandas_ta/trend/vortex.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.volatility import true_range from pandas_ta.utils import get_drift, get_offset, verify_series -def vortex(high, low, close, length=None, drift=None, offset=None, **kwargs): +def vortex(high: Series, low: Series, close: Series, length: int = None, drift: int = None, offset: int = None, + **kwargs) -> DataFrame: """Vortex Two oscillators that capture positive and negative trend movement. diff --git a/pandas_ta/trend/xsignals.py b/pandas_ta/trend/xsignals.py index 6962b1e..951e191 100644 --- a/pandas_ta/trend/xsignals.py +++ b/pandas_ta/trend/xsignals.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- from numpy import nan as npNaN -from pandas import DataFrame +from pandas import DataFrame, Series from .tsignals import tsignals from pandas_ta.utils._signals import cross_value from pandas_ta.utils import get_offset, verify_series -def xsignals(signal, xa, xb, above:bool=True, long:bool=True, asbool:bool=None, trend_reset:int=0, trade_offset:int=None, offset:int=None, **kwargs): +def xsignals(signal: Series, xa: Series, xb: Series, above: bool = True, long: bool = True, asbool: bool = None, + trend_reset: int = 0, trade_offset: int = None, offset: int = None, **kwargs) -> DataFrame: """Cross Signals (XSIGNALS) Cross Signals returns Trend Signal (TSIGNALS) results for Signal Crossings. This diff --git a/pandas_ta/utils/_core.py b/pandas_ta/utils/_core.py index b04e5c4..c5442b4 100644 --- a/pandas_ta/utils/_core.py +++ b/pandas_ta/utils/_core.py @@ -9,6 +9,8 @@ from pandas.api.types import is_datetime64_any_dtype from pandas_ta import Imports from pandas_ta import pd +from typing import Union + def _camelCase2Title(x: str): """https://stackoverflow.com/questions/5020906/python-convert-camel-case-to-space-delimited-using-regex-and-taking-acronyms-in""" @@ -35,7 +37,7 @@ def get_offset(x: int) -> int: return int(x) if isinstance(x, int) else 0 -def is_datetime_ordered(df: DataFrame or Series) -> bool: +def is_datetime_ordered(df: Union[DataFrame, Series]) -> bool: """Returns True if the index is a datetime and ordered.""" index_is_datetime = is_datetime64_any_dtype(df.index) try: @@ -48,7 +50,7 @@ def is_datetime_ordered(df: DataFrame or Series) -> bool: def is_percent(x: int or float) -> bool: if isinstance(x, (int, float)): - return x is not None and x >= 0 and x <= 100 + return x is not None and 0 <= x <= 100 return False @@ -61,21 +63,20 @@ def non_zero_range(high: Series, low: Series) -> Series: return diff -def recent_maximum_index(x): +def recent_maximum_index(x) -> int: return int(argmax(x[::-1])) -def recent_minimum_index(x): +def recent_minimum_index(x) -> int: return int(argmin(x[::-1])) -def rma_pandas(series, length): +def rma_pandas(series: Series, length: int): series = verify_series(series) alpha = (1.0 / length) if length > 0 else 0.5 return series.ewm(alpha=alpha, min_periods=length).mean() - def signed_series(series: Series, initial: int, lag: int = None) -> Series: """Returns a Signed Series with or without an initial value @@ -108,10 +109,10 @@ def tal_ma(name: str) -> int: elif name == "kama": return MA_Type.KAMA # 6 elif name == "mama": return MA_Type.MAMA # 7 elif name == "t3": return MA_Type.T3 # 8 - return 0 # Default: SMA -> 0 + return 0 # Default: SMA -> 0 -def unsigned_differences(series: Series, amount: int = None, **kwargs) -> Series: +def unsigned_differences(series: Series, amount: int = None, **kwargs) -> (Series, Series): """Unsigned Differences Returns two Series, an unsigned positive and unsigned negative series based on the differences of the original series. The positive series are only the diff --git a/pandas_ta/utils/_math.py b/pandas_ta/utils/_math.py index 1bc7332..17922ca 100644 --- a/pandas_ta/utils/_math.py +++ b/pandas_ta/utils/_math.py @@ -3,18 +3,20 @@ from functools import reduce from math import floor as mfloor from operator import mul from sys import float_info as sflt -from typing import List, Optional, Tuple +from typing import List, Optional, Union +from numpy import all, append, array, corrcoef, dot, exp, fabs +from numpy import log, nan, ndarray, ones, seterr, sqrt, sum, triu +# from numpy import array as npArray from pandas import DataFrame, Series - -from pandas_ta import Imports, np -from ._core import verify_series +from pandas_ta.maps import Imports +from pandas_ta.utils._core import verify_series -def combination(**kwargs: dict) -> int: +def combination(**kwargs) -> int: """https://stackoverflow.com/questions/4941753/is-there-a-math-ncr-function-in-python""" - n = int(np.fabs(kwargs.pop("n", 1))) - r = int(np.fabs(kwargs.pop("r", 0))) + n = int(fabs(kwargs.pop("n", 1))) + r = int(fabs(kwargs.pop("r", 0))) if kwargs.pop("repetition", False) or kwargs.pop("multichoose", False): n = n + r - 1 @@ -29,7 +31,7 @@ def combination(**kwargs: dict) -> int: return numerator // denominator -def erf(x: Tuple[int, float]): +def erf(x: Union[int, float]): """Error Function erf(x) The algorithm comes from Handbook of Mathematical Functions, formula 7.1.26. Source: https://stackoverflow.com/questions/457408/is-there-an-easily-available-implementation-of-erf-for-python @@ -48,13 +50,13 @@ def erf(x: Tuple[int, float]): # A&S formula 7.1.26 t = 1.0 / (1.0 + p * x) - y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * npExp(-x * x) - return sign * y # erf(-x) = -erf(x) + y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * exp(-x * x) + return sign * y # erf(-x) = -erf(x) -def fibonacci(n: int = 2, **kwargs: dict) -> np.ndarray: +def fibonacci(n: int = 2, **kwargs: dict) -> ndarray: """Fibonacci Sequence as a numpy array""" - n = int(np.fabs(n)) if n >= 0 else 2 + n = int(fabs(n)) if n >= 0 else 2 zero = kwargs.pop("zero", False) if zero: @@ -63,14 +65,14 @@ def fibonacci(n: int = 2, **kwargs: dict) -> np.ndarray: n -= 1 a, b = 1, 1 - result = np.array([a]) + result = array([a]) for _ in range(0, n): a, b = b, a + b - result = np.append(result, a) + result = append(result, a) weighted = kwargs.pop("weighted", False) if weighted: - fib_sum = np.sum(result) + fib_sum = sum(result) if fib_sum > 0: return result / fib_sum else: @@ -88,13 +90,13 @@ def geometric_mean(series: Series) -> float: has_zeros = 0 in series.values if has_zeros: series = series.fillna(0) + 1 - if np.all(series > 0): + if all(series > 0): mean = series.prod() ** (1 / n) return mean if not has_zeros else mean - 1 return 0 -def hpoly(array: np.array, x: Tuple[int, float]) -> float: +def hpoly(c: ndarray, x: Union[int, float]) -> float: """Horner Calculation for Polynomial Evaluation (hpoly) array: np.array of polynomial coefficients @@ -111,13 +113,13 @@ def hpoly(array: np.array, x: Tuple[int, float]) -> float: hpoly(coeffs_0, x) => -1224.25 hpoly(coeffs_1, x) or hpoly(coeffs_2, x) => -1224.25 # Faster """ - if not isinstance(array, np.ndarray): - array = np.array(array) + if not isinstance(c, ndarray): + c_ = array(c) - m, y = array.size, array[0] + m, y = c_.size, c_[0] for i in range(1, m): - y = array[i] + x * y + y = c_[i] + x * y return y @@ -142,12 +144,12 @@ def log_geometric_mean(series: Series) -> float: if n < 2: return 0 else: series = series.fillna(0) + 1 - if np.all(series > 0): - return np.exp(np.log(series).sum() / n) - 1 + if all(series > 0): + return exp(log(series).sum() / n) - 1 return 0 -def pascals_triangle(n: int = None, **kwargs: dict) -> np.ndarray: +def pascals_triangle(n: int = None, **kwargs: dict) -> ndarray: """Pascal's Triangle Returns a numpy array of the nth row of Pascal's Triangle. @@ -155,11 +157,11 @@ def pascals_triangle(n: int = None, **kwargs: dict) -> np.ndarray: => weighted: [0.0625, 0.25, 0.375, 0.25, 0.0625] => inverse weighted: [0.9375, 0.75, 0.625, 0.75, 0.9375] """ - n = int(np.fabs(n)) if n is not None else 0 + n = int(fabs(n)) if n is not None else 0 # Calculation - triangle = np.array([combination(n=n, r=i) for i in range(0, n + 1)]) - triangle_sum = np.sum(triangle) + triangle = array([combination(n=n, r=i) for i in range(0, n + 1)]) + triangle_sum = sum(triangle) triangle_weights = triangle / triangle_sum inverse_weights = 1 - triangle_weights @@ -175,7 +177,7 @@ def pascals_triangle(n: int = None, **kwargs: dict) -> np.ndarray: return triangle -def strided_window(array, length): +def strided_window(array, length: int): """as_strided creates a view into the array given the exact strides and shape. * Recommended to avoid when possible. @@ -196,7 +198,7 @@ def symmetric_triangle(n: int = None, **kwargs: dict) -> Optional[List[int]]: n=4 => triangle: [1, 2, 2, 1] => weighted: [0.16666667 0.33333333 0.33333333 0.16666667] """ - n = int(np.fabs(n)) if n is not None else 2 + n = int(fabs(n)) if n is not None else 2 triangle = None if n == 2: @@ -213,21 +215,19 @@ def symmetric_triangle(n: int = None, **kwargs: dict) -> Optional[List[int]]: triangle += front[::-1] if kwargs.pop("weighted", False) and isinstance(triangle, list): - triangle_sum = np.sum(triangle) - triangle_weights = triangle / triangle_sum - return triangle_weights + return triangle / sum(triangle) return triangle -def weights(w: np.ndarray): +def weights(w: ndarray): """Calculates the dot product of weights with values x""" def _dot(x): - return np.dot(w, x) + return dot(w, x) return _dot -def zero(x: Tuple[int, float]) -> Tuple[int, float]: +def zero(x: Union[int, float]) -> Union[int, float]: """If the value is close to zero, then return zero. Otherwise return itself.""" return 0 if abs(x) < sflt.epsilon else x @@ -235,7 +235,7 @@ def zero(x: Tuple[int, float]) -> Tuple[int, float]: # TESTING -def df_error_analysis(dfA: DataFrame, dfB: DataFrame, **kwargs: dict) -> DataFrame: +def df_error_analysis(dfA: DataFrame, dfB: DataFrame, **kwargs) -> DataFrame: """DataFrame Correlation Analysis helper""" corr_method = kwargs.pop("corr_method", "pearson") @@ -250,7 +250,7 @@ def df_error_analysis(dfA: DataFrame, dfB: DataFrame, **kwargs: dict) -> DataFra diff.plot(kind="kde") if kwargs.pop("triangular", False): - return corr.where(np.triu(np.ones(corr.shape)).astype(bool)) + return corr.where(triu(ones(corr.shape)).astype(bool)) return corr @@ -258,13 +258,13 @@ def df_error_analysis(dfA: DataFrame, dfB: DataFrame, **kwargs: dict) -> DataFra # PRIVATE def _linear_regression_np(x: Series, y: Series) -> dict: """Simple Linear Regression in Numpy for two 1d arrays for environments without the sklearn package.""" - result = {"a": np.nan, "b": np.nan, "r": np.nan, "t": np.nan, "line": np.nan} + result = {"a": nan, "b": nan, "r": nan, "t": nan, "line": nan} x_sum = x.sum() y_sum = y.sum() if int(x_sum) != 0: # 1st row, 2nd col value corr(x, y) - r = np.corrcoef(x, y)[0, 1] + r = corrcoef(x, y)[0, 1] m = x.size r_mix = m * (x * y).sum() - x_sum * y_sum @@ -272,17 +272,18 @@ def _linear_regression_np(x: Series, y: Series) -> dict: a = y.mean() - b * x.mean() line = a + b * x - _np_err = np.seterr() - np.seterr(divide="ignore", invalid="ignore") + _np_err = seterr() + seterr(divide="ignore", invalid="ignore") result = { "a": a, "b": b, "r": r, - "t": r / np.sqrt((1 - r * r) / (m - 2)), + "t": r / sqrt((1 - r * r) / (m - 2)), "line": line, } - np.seterr(divide=_np_err["divide"], invalid=_np_err["invalid"]) + seterr(divide=_np_err["divide"], invalid=_np_err["invalid"]) return result + def _linear_regression_sklearn(x: Series, y: Series) -> dict: """Simple Linear Regression in Scikit Learn for two 1d arrays for environments with the sklearn package.""" @@ -295,7 +296,7 @@ def _linear_regression_sklearn(x: Series, y: Series) -> dict: result = { "a": a, "b": b, "r": r, - "t": r / np.sqrt((1 - r * r) / (x.size - 2)), + "t": r / sqrt((1 - r * r) / (x.size - 2)), "line": a + b * x } return result diff --git a/pandas_ta/utils/_metrics.py b/pandas_ta/utils/_metrics.py index 349fd37..ec57936 100644 --- a/pandas_ta/utils/_metrics.py +++ b/pandas_ta/utils/_metrics.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from typing import Tuple +from typing import Union from pandas import Series, Timedelta @@ -100,7 +100,7 @@ def log_max_drawdown(close: Series) -> float: return log_return - max_drawdown(close, method="log") -def max_drawdown(close: Series, method:str = None, all:bool = False) -> float: +def max_drawdown(close: Series, method: str = None, all: bool = False) -> float: """Maximum Drawdown from close. Default: 'dollar'. Args: @@ -127,11 +127,9 @@ def max_drawdown(close: Series, method:str = None, all:bool = False) -> float: return max_dd_["dollar"] -def optimal_leverage( - close: Series, benchmark_rate: float = 0.0, - period: Tuple[float, int] = RATE["TRADING_DAYS_PER_YEAR"], - log: bool = False, capital: float = 1., **kwargs - ) -> float: +def optimal_leverage(close: Series, benchmark_rate: float = 0.0, + period: Union[float, int] = RATE["TRADING_DAYS_PER_YEAR"], + log: bool = False, capital: float = 1., **kwargs) -> float: """Optimal Leverage of a series. NOTE: Incomplete. Do NOT use. Args: @@ -162,7 +160,7 @@ def optimal_leverage( return amount -def pure_profit_score(close: Series) -> Tuple[float, int]: +def pure_profit_score(close: Series) -> Union[float, int]: """Pure Profit Score of a series. Args: @@ -179,7 +177,8 @@ def pure_profit_score(close: Series) -> Tuple[float, int]: return 0 -def sharpe_ratio(close: Series, benchmark_rate: float = 0.0, log: bool = False, use_cagr: bool = False, period: int = RATE["TRADING_DAYS_PER_YEAR"]) -> float: +def sharpe_ratio(close: Series, benchmark_rate: float = 0.0, log: bool = False, use_cagr: bool = False, + period: int = RATE["TRADING_DAYS_PER_YEAR"]) -> float: """Sharpe Ratio of a series. Args: diff --git a/pandas_ta/utils/_signals.py b/pandas_ta/utils/_signals.py index 4c90a52..320216d 100644 --- a/pandas_ta/utils/_signals.py +++ b/pandas_ta/utils/_signals.py @@ -5,7 +5,8 @@ from ._core import get_offset, verify_series from ._math import zero -def _above_below(series_a: Series, series_b: Series, above: bool = True, asint: bool = True, offset: int = None, **kwargs): +def _above_below(series_a: Series, series_b: Series, above: bool = True, asint: bool = True, offset: int = None, + **kwargs) -> Series: series_a = verify_series(series_a) series_b = verify_series(series_b) offset = get_offset(offset) @@ -33,11 +34,11 @@ def _above_below(series_a: Series, series_b: Series, above: bool = True, asint: return current -def above(series_a: Series, series_b: Series, asint: bool = True, offset: int = None, **kwargs): +def above(series_a: Series, series_b: Series, asint: bool = True, offset: int = None, **kwargs) -> Series: return _above_below(series_a, series_b, above=True, asint=asint, offset=offset, **kwargs) -def above_value(series_a: Series, value: float, asint: bool = True, offset: int = None, **kwargs): +def above_value(series_a: Series, value: float, asint: bool = True, offset: int = None, **kwargs) -> Series: if not isinstance(value, (int, float, complex)): print("[X] value is not a number") return @@ -46,11 +47,11 @@ def above_value(series_a: Series, value: float, asint: bool = True, offset: int return _above_below(series_a, series_b, above=True, asint=asint, offset=offset, **kwargs) -def below(series_a: Series, series_b: Series, asint: bool = True, offset: int = None, **kwargs): +def below(series_a: Series, series_b: Series, asint: bool = True, offset: int = None, **kwargs) -> Series: return _above_below(series_a, series_b, above=False, asint=asint, offset=offset, **kwargs) -def below_value(series_a: Series, value: float, asint: bool = True, offset: int = None, **kwargs): +def below_value(series_a: Series, value: float, asint: bool = True, offset: int = None, **kwargs) -> Series: if not isinstance(value, (int, float, complex)): print("[X] value is not a number") return @@ -58,13 +59,15 @@ def below_value(series_a: Series, value: float, asint: bool = True, offset: int return _above_below(series_a, series_b, above=False, asint=asint, offset=offset, **kwargs) -def cross_value(series_a: Series, value: float, above: bool = True, asint: bool = True, offset: int = None, **kwargs): +def cross_value(series_a: Series, value: float, above: bool = True, asint: bool = True, offset: int = None, + **kwargs) -> Series: series_b = Series(value, index=series_a.index, name=f"{value}".replace(".", "_")) return cross(series_a, series_b, above, asint, offset, **kwargs) -def cross(series_a: Series, series_b: Series, above: bool = True, asint: bool = True, offset: int = None, **kwargs): +def cross(series_a: Series, series_b: Series, above: bool = True, asint: bool = True, offset: int = None, + **kwargs) -> Series: series_a = verify_series(series_a) series_b = verify_series(series_b) offset = get_offset(offset) @@ -94,7 +97,8 @@ def cross(series_a: Series, series_b: Series, above: bool = True, asint: bool = return cross -def signals(indicator, xa, xb, cross_values, xserie, xserie_a, xserie_b, cross_series, offset) -> DataFrame: +def signals(indicator: Series, xa: float, xb: float, cross_values: bool, xserie: Series, xserie_a: Series, + xserie_b: Series, cross_series: bool, offset: int) -> DataFrame: df = DataFrame() if xa is not None and isinstance(xa, (int, float)): if cross_values: diff --git a/pandas_ta/utils/_stats.py b/pandas_ta/utils/_stats.py index fc65082..a04851a 100644 --- a/pandas_ta/utils/_stats.py +++ b/pandas_ta/utils/_stats.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -from typing import Tuple +from typing import Union from pandas_ta import Imports, np from ._math import hpoly -def _gaussian_poly_coefficients(): +def _gaussian_poly_coefficients() -> [npArray]: """Three pairs of Polynomial Approximation Coefficients for the Gaussian Normal CDF""" @@ -52,10 +52,10 @@ def _gaussian_poly_coefficients(): 6.79019408009981274425E-9 ]) - return p0, q0, p1, q1, p2, q2 + return [p0, q0, p1, q1, p2, q2] -def inv_norm(value: Tuple[float, int]) -> Tuple[float, None]: +def inv_norm(value: Union[float, int]) -> Union[float, None]: """Inverse Normal (inv_norm) Calculates the 'x' in which the area under the Gaussian PDF is equal to value. @@ -109,4 +109,4 @@ def inv_norm(value: Tuple[float, int]) -> Tuple[float, None]: y = y0 - y1 if negate: y = -y - return y \ No newline at end of file + return y diff --git a/pandas_ta/utils/_time.py b/pandas_ta/utils/_time.py index a5c3b38..f299a08 100644 --- a/pandas_ta/utils/_time.py +++ b/pandas_ta/utils/_time.py @@ -3,11 +3,11 @@ from datetime import datetime from time import localtime, perf_counter from typing import Tuple, Union -from pandas import Timestamp -from pandas_ta import EXCHANGE_TZ, pd, RATE +from pandas import DataFrame, Series, Timestamp, to_datetime +from pandas_ta.maps import EXCHANGE_TZ, RATE -def df_dates(df: pd.DataFrame, dates: Tuple[str, list] = None) -> pd.DataFrame: +def df_dates(df: DataFrame, dates: Tuple[str, list] = None) -> DataFrame: """Yields the DataFrame with the given dates""" if dates is None: return None if not isinstance(dates, list): @@ -15,14 +15,14 @@ def df_dates(df: pd.DataFrame, dates: Tuple[str, list] = None) -> pd.DataFrame: return df[df.index.isin(dates)] -def df_month_to_date(df: pd.DataFrame) -> pd.DataFrame: +def df_month_to_date(df: DataFrame) -> DataFrame: """Yields the Month-to-Date (MTD) DataFrame""" in_mtd = df.index >= Timestamp.now().strftime("%Y-%m-01") if any(in_mtd): return df[in_mtd] return df -def df_quarter_to_date(df: pd.DataFrame) -> pd.DataFrame: +def df_quarter_to_date(df: DataFrame) -> DataFrame: """Yields the Quarter-to-Date (QTD) DataFrame""" now = Timestamp.now() for m in [1, 4, 7, 10]: @@ -32,7 +32,7 @@ def df_quarter_to_date(df: pd.DataFrame) -> pd.DataFrame: return df[df.index >= now.strftime("%Y-%m-01")] -def df_year_to_date(df: pd.DataFrame) -> pd.DataFrame: +def df_year_to_date(df: DataFrame) -> DataFrame: """Yields the Year-to-Date (YTD) DataFrame""" in_ytd = df.index >= Timestamp.now().strftime("%Y-01-01") if any(in_ytd): return df[in_ytd] @@ -46,10 +46,10 @@ def final_time(stime: float) -> str: return f"{time_diff * 1000:2.4f} ms ({time_diff:2.4f} s)" -def get_time(exchange: str = "NYSE", full:bool = True, to_string:bool = False) -> Tuple[None, str]: +def get_time(exchange: str = "NYSE", full: bool = True, to_string: bool = False) -> Union[None, str]: """Returns Current Time, Day of the Year and Percentage, and the current time of the selected Exchange.""" - tz = EXCHANGE_TZ["NYSE"] # Default is NYSE (Eastern Time Zone) + tz = EXCHANGE_TZ["NYSE"] # Default is NYSE (Eastern Time Zone) if isinstance(exchange, str): exchange = exchange.upper() tz = EXCHANGE_TZ[exchange] @@ -74,7 +74,7 @@ def get_time(exchange: str = "NYSE", full:bool = True, to_string:bool = False) - return s if to_string else print(s) -def total_time(df: pd.DataFrame, tf: str = "years") -> float: +def total_time(df: DataFrame, tf: str = "years") -> float: """Calculates the total time of a DataFrame. Difference of the Last and First index. Options: 'months', 'weeks', 'days', 'hours', 'minutes' and 'seconds'. Default: 'years'. @@ -95,7 +95,7 @@ def total_time(df: pd.DataFrame, tf: str = "years") -> float: return TimeFrame["years"] -def to_utc(df: pd.DataFrame) -> pd.DataFrame: +def to_utc(df: DataFrame) -> DataFrame: """Either localizes the DataFrame Index to UTC or it applies tz_convert to set the Index to UTC. """ @@ -107,17 +107,18 @@ def to_utc(df: pd.DataFrame) -> pd.DataFrame: return df -def unix_convert(ts: Union[int, pd.Series]) -> Union[datetime, str]: +def unix_convert(ts: Union[int, Series]) -> Union[datetime, str]: """ Converts timestamps from polygon to readable datetime strings. :param ts: The timestamp(s). An integer posix timestamp or a pd.Series of timestamps :return: The converted datetime string """ - return pd.to_datetime(ts, unit="ms") + # return pd.to_datetime(ts, unit="ms") + return to_datetime(ts, unit="ms") # Aliases mtd = df_month_to_date qtd = df_quarter_to_date -ytd = df_year_to_date \ No newline at end of file +ytd = df_year_to_date diff --git a/pandas_ta/utils/data/__init__.py b/pandas_ta/utils/data/__init__.py index bb0411c..5b4b2e9 100644 --- a/pandas_ta/utils/data/__init__.py +++ b/pandas_ta/utils/data/__init__.py @@ -2,4 +2,4 @@ from .alphavantage import av from .polygon_api import polygon_api from .processes import sample -from .yahoofinance import yf \ No newline at end of file +from .yahoofinance import yf diff --git a/pandas_ta/utils/data/alphavantage.py b/pandas_ta/utils/data/alphavantage.py index 45a724e..0faf632 100644 --- a/pandas_ta/utils/data/alphavantage.py +++ b/pandas_ta/utils/data/alphavantage.py @@ -4,7 +4,7 @@ from pandas import DataFrame from pandas_ta import Imports, RATE, version -def av(ticker: str, **kwargs): +def av(ticker: str, **kwargs) -> DataFrame: print(f"[!] kwargs: {kwargs}") verbose = kwargs.pop("verbose", False) kind = kwargs.pop("kind", "history") @@ -38,4 +38,4 @@ def av(ticker: str, **kwargs): print(f"\n{df.name}\n{df.tail(show)}\n") return df - return DataFrame() \ No newline at end of file + return DataFrame() diff --git a/pandas_ta/utils/data/polygon_api.py b/pandas_ta/utils/data/polygon_api.py index b65bd9c..feaf271 100644 --- a/pandas_ta/utils/data/polygon_api.py +++ b/pandas_ta/utils/data/polygon_api.py @@ -5,7 +5,7 @@ from pandas_ta import Imports, pd, RATE, version from pandas_ta.utils import unix_convert -def polygon_api(ticker: str, **kwargs): +def polygon_api(ticker: str, **kwargs) -> DataFrame: r""" polygon_api - polygon.io API helper function. diff --git a/pandas_ta/utils/data/processes.py b/pandas_ta/utils/data/processes.py index a22975e..a89ba44 100644 --- a/pandas_ta/utils/data/processes.py +++ b/pandas_ta/utils/data/processes.py @@ -2,8 +2,12 @@ # -*- coding: utf-8 -*- import datetime as dt from random import choice as rChoice + +from numpy import absolute, any, concatenate, cumsum, flip, max +from numpy import mean, min, ndarray, std, sum, where, zeros +from numpy.random import choice, normal, randint from pandas import DataFrame, date_range -from pandas_ta import Imports, RATE, np +from ...maps import Imports, RATE class sample(object): @@ -132,7 +136,7 @@ class sample(object): _generate() method to build a sample realization with the given arguments. """ - _random_symbol = ''.join([rChoice("ABCDEFGHIJKLMNOPQRSTUVWXYZ") for _ in range(np.random.randint(3, 6))]) + _random_symbol = ''.join([rChoice("ABCDEFGHIJKLMNOPQRSTUVWXYZ") for _ in range(randint(3, 6))]) self._name = str(name) if name is not None and isinstance(name, str) else _random_symbol self._process = str(process).lower() if process is not None and isinstance(process, str) and process in self._processes else None self._noise = str(noise).lower() if noise is not None and isinstance(noise, str) and noise in self._noises else None @@ -163,15 +167,15 @@ class sample(object): self._verbose = verbose if verbose is not None and isinstance(verbose, bool) else False if self._process == "rand": - self._process = np.random.choice(self._processes[:-2]) + self._process = choice(self._processes[:-2]) if self._noise == "rand": - self._noise = np.random.choice(self._noises[:-1]) + self._noise = choice(self._noises[:-1]) self._generate() # Run it - def _bernoulli_mask(self, array: np.ndarray, percent:float = None, p:float = None): + def _bernoulli_mask(self, array: ndarray, percent:float = None, p:float = None): """Bernoulli Mask - Positive or Negative""" if array.size > 0: percent = float(percent) if percent is not None and isinstance(percent, float) else self.noise_percent @@ -179,11 +183,9 @@ class sample(object): return array * self.noise_percent * self._bernoulli_process() return array - def _bernoulli_process(self): """Bernoulli Process""" - return np.random.randint(2, size=self.length) - + return randint(2, size=self.length) def _generate(self): """A method to generate stochastic process realizations. @@ -219,20 +221,20 @@ class sample(object): _npns = f"{self.name} | {self.process} {self.noise+' ' if self.noise is not None else ''}{self.np.size}" _s0n = f"s0: {round(self.np[0], self._precision)}, sN: {round(self.np[-1], self._precision)}" - _msmm = f"mu: {round(np.mean(self.np), self._precision)}, sigma: {round(np.std(self.np), self._precision)}" + _msmm = f"mu: {round(mean(self.np), self._precision)}, sigma: {round(std(self.np), self._precision)}" self._dfname = f"{_npns} | {_s0n} | {_msmm}" if self._verbose: print(self._dfname) - def nonnegative(self, array: np.ndarray = None): + def nonnegative(self, array: ndarray = None): """Vertical Translation the 'array' where the resultant 'array' has non-negative values.""" - if isinstance(array, np.ndarray): + if isinstance(array, ndarray): return self._nonnegative(array) return array - def _nonnegative(self, array: np.ndarray): + def _nonnegative(self, array: ndarray): """Translates the array up by the minimum of the 'array' if any values are negative.""" if array.size > 0 and any(array < 0): @@ -241,25 +243,25 @@ class sample(object): return array - def _normal_mask(self, array: np.ndarray): + def _normal_mask(self, array: ndarray): """A method to add some additional randomness to the realized process. Applies a mask based on the Normal Distribution and the 'array's mean and standard deviation.""" if array.size > 0: - norm = np.random.normal(np.mean(array), np.std(array), size=self.length) + norm = normal(mean(array), std(array), size=self.length) return array * self.noise_percent * norm return array - def orientation(self, array: np.ndarray, mode: str = None): + def orientation(self, array: ndarray, mode: str = None): """Orients the 'array' either by Inversion, Reversal, or an Inverted Reversal.""" - if isinstance(array, np.ndarray): + if isinstance(array, ndarray): return self._orientation(array, mode=mode) return array - def _orientation(self, array: np.ndarray, mode: str = None): + def _orientation(self, array: ndarray, mode: str = None): """Orients the 'array' either by Inversion, Reversal, or an Inverted Reversal.""" _modes = ["i", "r", "ir", "ri", None, "rand"] @@ -267,16 +269,16 @@ class sample(object): result = array if mode is None: return result - if mode == "rand": mode = np.random.choice(_modes[3:]) + if mode == "rand": mode = choice(_modes[3:]) if mode == "i": - mid = 0.5 * (np.min(array) + np.max(array)) + mid = 0.5 * (min(array) + max(array)) inv = mid - array diff = inv - inv[0] result = array[0] + diff if array[0] > 0 else diff - array[0] if mode == "r": - result = np.flip(array) - (array[-1] - array[0]) + result = flip(array) - (array[-1] - array[0]) if mode in ["ir", "ri"]: result = self._orientation(self._orientation(array, "i"), "r") @@ -284,22 +286,22 @@ class sample(object): return result - def scale(self, array: np.ndarray, mode: str): + def scale(self, array: ndarray, mode: str): """Mean, Normal or Standard scaling of the 'array'.""" - if isinstance(array, np.ndarray): + if isinstance(array, ndarray): return self._scaler(array, mode=mode) return array - def _scaler(self, array: np.ndarray, mode: str): + def _scaler(self, array: ndarray, mode: str): """Scaling: mean, normal, standard""" result = array if mode is None: return result - if mode == "rand": mode = np.random.choice(self._scales[3:]) + if mode == "rand": mode = choice(self._scales[3:]) - min_, max_ = np.min(array), np.max(array) - range_ = np.absolute(max_ - min_) - mu_, std_ = np.mean(array), np.std(array) + min_, max_ = min(array), max(array) + range_ = absolute(max_ - min_) + mu_, std_ = mean(array), std(array) if mode == "m" and range_ > 0: # "mean" result = ((array - mu_) / range_) @@ -313,7 +315,7 @@ class sample(object): return result - def _simple_random_walk(self, up:float = None, down:float = None) -> np.array: + def _simple_random_walk(self, up:float = None, down:float = None) -> ndarray: """Simple Random Walk Sources: @@ -323,23 +325,21 @@ class sample(object): down = float(down) if down is not None and isinstance(down, (int, float)) else -1.0 if up < down: down, up = up, down - x = np.concatenate(([0.0], np.where(np.random.randint(0, 2, size=self.length - 1) == 0, down, up))) - return np.cumsum(x).astype(float) - + x = concatenate(([0.0], where(randint(0, 2, size=self.length - 1) == 0, down, up))) + return cumsum(x).astype(float) def _stoch_noise(self): """Method to apply noise from the stochastic package if installed. Otherwise, it returns 0 noise. """ _desc = f"[+] " - result = np.zeros(self.length, dtype=float) + result = zeros(self.length, dtype=float) if self._noise is not None and Imports["stochastic"]: from stochastic import random as st_random st_random.use_generator() st_random.seed(self.random_number) - if self._noise in ["blue", "b"]: from stochastic.processes.noise import BlueNoise result = BlueNoise(t=self.t).sample(self.length - 1) @@ -381,11 +381,10 @@ class sample(object): # Initial Value (s0) adjustment result = result + result[0] if result[0] > self.s0 else result - result[0] - if result is not None and np.any(result) and self._verbose: print(_desc) + if result is not None and any(result) and self._verbose: print(_desc) return result - def _stoch_process(self): """Method to return some realizations from the stochastic package. Otherwise, it returns a Simple Random Walk.""" @@ -445,8 +444,6 @@ class sample(object): return result - - @property def b(self): """The 'b' value for some stochastic processes.""" @@ -578,4 +575,4 @@ class sample(object): @property def volatility(self): """The 'volatility' value for some stochastic processes.""" - return self._volatility \ No newline at end of file + return self._volatility diff --git a/pandas_ta/utils/data/yahoofinance.py b/pandas_ta/utils/data/yahoofinance.py index 8752da2..a203994 100644 --- a/pandas_ta/utils/data/yahoofinance.py +++ b/pandas_ta/utils/data/yahoofinance.py @@ -5,7 +5,7 @@ from .._core import _camelCase2Title from .._time import ytd -def yf(ticker: str, **kwargs): +def yf(ticker: str, **kwargs) -> DataFrame: """yf - yfinance wrapper It retrieves market data (ohlcv) from Yahoo Finance using yfinance. diff --git a/pandas_ta/volatility/aberration.py b/pandas_ta/volatility/aberration.py index 4a9a61f..9434c71 100644 --- a/pandas_ta/volatility/aberration.py +++ b/pandas_ta/volatility/aberration.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- # from numpy import sqrt as npsqrt -from pandas import DataFrame +from pandas import DataFrame, Series from .atr import atr from pandas_ta.overlap import hlc3, sma from pandas_ta.utils import get_offset, verify_series -def aberration(high, low, close, length=None, atr_length=None, offset=None, **kwargs): +def aberration(high: Series, low: Series, close: Series, length: int = None, atr_length: int = None, + offset: int = None, **kwargs) -> DataFrame: """Aberration (ABER) A volatility indicator similar to Keltner Channels. diff --git a/pandas_ta/volatility/accbands.py b/pandas_ta/volatility/accbands.py index 28b7184..b4fbed7 100644 --- a/pandas_ta/volatility/accbands.py +++ b/pandas_ta/volatility/accbands.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.overlap import ma from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series -def accbands(high, low, close, length=None, c=None, drift=None, mamode=None, offset=None, **kwargs): +def accbands(high: Series, low: Series, close: Series, length: int = None, c: int = None, drift: int = None, + mamode: str = None, offset: int = None, **kwargs) -> DataFrame: """Acceleration Bands (ACCBANDS) Acceleration Bands created by Price Headley plots upper and lower envelope diff --git a/pandas_ta/volatility/atr.py b/pandas_ta/volatility/atr.py index 049527c..5b63403 100644 --- a/pandas_ta/volatility/atr.py +++ b/pandas_ta/volatility/atr.py @@ -3,9 +3,11 @@ from .true_range import true_range from pandas_ta import Imports from pandas_ta.overlap import ma from pandas_ta.utils import get_drift, get_offset, verify_series +from pandas import Series -def atr(high, low, close, length=None, mamode=None, talib=None, drift=None, offset=None, **kwargs): +def atr(high: Series, low: Series, close: Series, length: int = None, mamode: str = None, talib: bool = None, + drift: int = None, offset: int = None, **kwargs) -> Series: """Average True Range (ATR) Averge True Range is used to measure volatility, especially volatility caused by diff --git a/pandas_ta/volatility/bbands.py b/pandas_ta/volatility/bbands.py index c0bfc8f..42682e9 100644 --- a/pandas_ta/volatility/bbands.py +++ b/pandas_ta/volatility/bbands.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta import Imports from pandas_ta.overlap import ma from pandas_ta.statistics import stdev from pandas_ta.utils import get_offset, non_zero_range, tal_ma, verify_series -def bbands(close, length=None, std=None, ddof=0, mamode=None, talib=None, offset=None, **kwargs): +def bbands(close: Series, length: int = None, std: int = None, ddof: int = 0, mamode: str = None, talib: bool = None, + offset: int = None, **kwargs) -> DataFrame: """Bollinger Bands (BBANDS) A popular volatility indicator by John Bollinger. diff --git a/pandas_ta/volatility/donchian.py b/pandas_ta/volatility/donchian.py index 9b098eb..15663cf 100644 --- a/pandas_ta/volatility/donchian.py +++ b/pandas_ta/volatility/donchian.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.utils import get_offset, verify_series -def donchian(high, low, lower_length=None, upper_length=None, offset=None, **kwargs): +def donchian(high: Series, low: Series, lower_length: int = None, upper_length: int = None, offset: int = None, + **kwargs) -> DataFrame: """Donchian Channels (DC) Donchian Channels are used to measure volatility, similar to diff --git a/pandas_ta/volatility/hwc.py b/pandas_ta/volatility/hwc.py index 9bd08f6..b58a21f 100644 --- a/pandas_ta/volatility/hwc.py +++ b/pandas_ta/volatility/hwc.py @@ -4,7 +4,8 @@ from pandas import DataFrame, Series from pandas_ta.utils import get_offset, verify_series -def hwc(close, na=None, nb=None, nc=None, nd=None, scalar=None, channel_eval=None, offset=None, **kwargs): +def hwc(close: Series, na: float = None, nb: float = None, nc: float = None, nd: float = None, scalar: float = None, + channel_eval: bool = None, offset: int = None, **kwargs) -> DataFrame: """HWC (Holt-Winter Channel) Channel indicator HWC (Holt-Winters Channel) based on HWMA - a three-parameter diff --git a/pandas_ta/volatility/kc.py b/pandas_ta/volatility/kc.py index 176bb73..506210f 100644 --- a/pandas_ta/volatility/kc.py +++ b/pandas_ta/volatility/kc.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from .true_range import true_range from pandas_ta.overlap import ma from pandas_ta.utils import get_offset, high_low_range, verify_series -def kc(high, low, close, length=None, scalar=None, mamode=None, offset=None, **kwargs): +def kc(high: Series, low: Series, close: Series, length: int = None, scalar: float = None, mamode: str = None, + offset: int = None, **kwargs) -> DataFrame: """Keltner Channels (KC) A popular volatility indicator similar to Bollinger Bands and diff --git a/pandas_ta/volatility/massi.py b/pandas_ta/volatility/massi.py index da3c951..b0b95a9 100644 --- a/pandas_ta/volatility/massi.py +++ b/pandas_ta/volatility/massi.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta.overlap import ema from pandas_ta.utils import get_offset, non_zero_range, verify_series +from pandas import Series -def massi(high, low, fast=None, slow=None, offset=None, **kwargs): +def massi(high: Series, low: Series, fast: int = None, slow: int = None, offset: int = None, **kwargs) -> Series: """Mass Index (MASSI) The Mass Index is a non-directional volatility indicator that utilitizes the diff --git a/pandas_ta/volatility/natr.py b/pandas_ta/volatility/natr.py index 5d14596..d2a4c4f 100644 --- a/pandas_ta/volatility/natr.py +++ b/pandas_ta/volatility/natr.py @@ -2,9 +2,11 @@ from .atr import atr from pandas_ta import Imports from pandas_ta.utils import get_drift, get_offset, verify_series +from pandas import Series -def natr(high, low, close, length=None, scalar=None, mamode=None, talib=None, drift=None, offset=None, **kwargs): +def natr(high: Series, low: Series, close: Series, length: int = None, scalar: float = None, mamode: str = None, + talib: bool = None, drift: int = None, offset: int = None, **kwargs) -> Series: """Normalized Average True Range (NATR) Normalized Average True Range attempt to normalize the average true range. diff --git a/pandas_ta/volatility/pdist.py b/pandas_ta/volatility/pdist.py index 24c1d4c..1967121 100644 --- a/pandas_ta/volatility/pdist.py +++ b/pandas_ta/volatility/pdist.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series +from pandas import Series -def pdist(open_, high, low, close, drift=None, offset=None, **kwargs): +def pdist(open_: Series, high: Series, low: Series, close: Series, drift: int = None, offset: int = None, + **kwargs) -> Series: """Price Distance (PDIST) Measures the "distance" covered by price movements. diff --git a/pandas_ta/volatility/rvi.py b/pandas_ta/volatility/rvi.py index 857f5c9..06b7b95 100644 --- a/pandas_ta/volatility/rvi.py +++ b/pandas_ta/volatility/rvi.py @@ -3,9 +3,12 @@ from pandas_ta.overlap import ma from pandas_ta.statistics import stdev from pandas_ta.utils import get_drift, get_offset from pandas_ta.utils import unsigned_differences, verify_series +from pandas import Series -def rvi(close, high=None, low=None, length=None, scalar=None, refined=None, thirds=None, mamode=None, drift=None, offset=None, **kwargs): +def rvi(close: Series, high: Series = None, low: Series = None, length: int = None, scalar: float = None, + refined: bool = None, thirds: bool = None, mamode: str = None, drift: int = None, + offset: int = None, **kwargs) -> Series: """Relative Volatility Index (RVI) The Relative Volatility Index (RVI) was created in 1993 and revised in 1995. diff --git a/pandas_ta/volatility/thermo.py b/pandas_ta/volatility/thermo.py index 7a4b20d..dbc9978 100644 --- a/pandas_ta/volatility/thermo.py +++ b/pandas_ta/volatility/thermo.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.overlap import ma from pandas_ta.utils import get_offset, verify_series, get_drift -def thermo(high, low, length=None, long=None, short=None, mamode=None, drift=None, offset=None, **kwargs): +def thermo(high: Series, low: Series, length: int = None, long: int = None, short: int = None, mamode: str = None, + drift: int = None, offset: int = None, **kwargs) -> DataFrame: """Elders Thermometer (THERMO) Elder's Thermometer measures price volatility. diff --git a/pandas_ta/volatility/true_range.py b/pandas_ta/volatility/true_range.py index 9504d95..3153d0c 100644 --- a/pandas_ta/volatility/true_range.py +++ b/pandas_ta/volatility/true_range.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- from numpy import nan as npNaN -from pandas import concat +from pandas import concat, Series from pandas_ta import Imports from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series -def true_range(high, low, close, talib=None, drift=None, offset=None, **kwargs): +def true_range(high: Series, low: Series, close: Series, talib: bool = None, drift: int = None, offset: int = None, + **kwargs) -> Series: """True Range An method to expand a classical range (high minus low) to include diff --git a/pandas_ta/volatility/ui.py b/pandas_ta/volatility/ui.py index d10f390..edf29db 100644 --- a/pandas_ta/volatility/ui.py +++ b/pandas_ta/volatility/ui.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- from numpy import sqrt as npsqrt from pandas_ta.overlap import sma +from pandas import Series from pandas_ta.utils import get_offset, verify_series -def ui(close, length=None, scalar=None, offset=None, **kwargs): +def ui(close: Series, length: int = None, scalar: int = None, offset: int = None, **kwargs) -> Series: """Ulcer Index (UI) The Ulcer Index by Peter Martin measures the downside volatility with the use of diff --git a/pandas_ta/volume/ad.py b/pandas_ta/volume/ad.py index 4911380..519266d 100644 --- a/pandas_ta/volume/ad.py +++ b/pandas_ta/volume/ad.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from pandas_ta import Imports from pandas_ta.utils import get_offset, non_zero_range, verify_series +from pandas import Series -def ad(high, low, close, volume, open_=None, talib=None, offset=None, **kwargs): +def ad(high: Series, low: Series, close: Series, volume: Series, open_: Series = None, talib: bool = None, + offset: int = None, **kwargs) -> Series: """Accumulation/Distribution (AD) Accumulation/Distribution indicator utilizes the relative position @@ -17,7 +19,7 @@ def ad(high, low, close, volume, open_=None, talib=None, offset=None, **kwargs): low (pd.Series): Series of 'low's close (pd.Series): Series of 'close's volume (pd.Series): Series of 'volume's - open (pd.Series): Series of 'open's + open_ (pd.Series): Series of 'open's talib (bool): If TA Lib is installed and talib is True, Returns the TA Lib version. Default: True offset (int): How many periods to offset the result. Default: 0 diff --git a/pandas_ta/volume/adosc.py b/pandas_ta/volume/adosc.py index 39e1f48..ddcc8c2 100644 --- a/pandas_ta/volume/adosc.py +++ b/pandas_ta/volume/adosc.py @@ -3,9 +3,11 @@ from .ad import ad from pandas_ta import Imports from pandas_ta.overlap import ema from pandas_ta.utils import get_offset, verify_series +from pandas import Series -def adosc(high, low, close, volume, open_=None, fast=None, slow=None, talib=None, offset=None, **kwargs): +def adosc(high: Series, low: Series, close: Series, volume: Series, open_: Series = None, fast: int = None, + slow: int = None, talib: bool = None, offset: int = None, **kwargs) -> Series: """Accumulation/Distribution Oscillator or Chaikin Oscillator Accumulation/Distribution Oscillator indicator utilizes @@ -19,7 +21,7 @@ def adosc(high, low, close, volume, open_=None, fast=None, slow=None, talib=None high (pd.Series): Series of 'high's low (pd.Series): Series of 'low's close (pd.Series): Series of 'close's - open (pd.Series): Series of 'open's + open_ (pd.Series): Series of 'open's volume (pd.Series): Series of 'volume's fast (int): The short period. Default: 12 slow (int): The long period. Default: 26 diff --git a/pandas_ta/volume/aobv.py b/pandas_ta/volume/aobv.py index 3b8bc90..8a47f59 100644 --- a/pandas_ta/volume/aobv.py +++ b/pandas_ta/volume/aobv.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from .obv import obv from pandas_ta.overlap import ma from pandas_ta.trend import long_run, short_run from pandas_ta.utils import get_offset, verify_series -def aobv(close, volume, fast=None, slow=None, max_lookback=None, min_lookback=None, mamode=None, offset=None, **kwargs): +def aobv(close: Series, volume: Series, fast: int = None, slow: int = None, max_lookback: int = None, + min_lookback: int = None, mamode: str = None, offset: int = None, **kwargs) -> DataFrame: """Archer On Balance Volume (AOBV) Archer On Balance Volume (AOBV) developed by Kevin Johnson provides diff --git a/pandas_ta/volume/cmf.py b/pandas_ta/volume/cmf.py index c625c76..46d2f4d 100644 --- a/pandas_ta/volume/cmf.py +++ b/pandas_ta/volume/cmf.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, non_zero_range, verify_series +from pandas import Series -def cmf(high, low, close, volume, open_=None, length=None, offset=None, **kwargs): +def cmf(high: Series, low: Series, close: Series, volume: Series, open_: Series = None, length: int = None, + offset: int = None, **kwargs) -> Series: """Chaikin Money Flow (CMF) Chailin Money Flow measures the amount of money flow volume over a specific diff --git a/pandas_ta/volume/efi.py b/pandas_ta/volume/efi.py index 394efc7..473696b 100644 --- a/pandas_ta/volume/efi.py +++ b/pandas_ta/volume/efi.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from pandas_ta.overlap import ma from pandas_ta.utils import get_drift, get_offset, verify_series +from pandas import Series -def efi(close, volume, length=None, mamode=None, drift=None, offset=None, **kwargs): +def efi(close: Series, volume: Series, length: int = None, mamode: str = None, drift: int = None, offset: int = None, + **kwargs) -> Series: """Elder's Force Index (EFI) Elder's Force Index measures the power behind a price movement using price diff --git a/pandas_ta/volume/eom.py b/pandas_ta/volume/eom.py index 24ee77d..da12e86 100644 --- a/pandas_ta/volume/eom.py +++ b/pandas_ta/volume/eom.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from pandas_ta.overlap import hl2, sma from pandas_ta.utils import get_drift, get_offset, non_zero_range, verify_series +from pandas import Series -def eom(high, low, close, volume, length=None, divisor=None, drift=None, offset=None, **kwargs): +def eom(high: Series, low: Series, close: Series, volume: Series, length: int = None, divisor=None, drift: int = None, + offset: int = None, **kwargs) -> Series: """Ease of Movement (EOM) Ease of Movement is a volume based oscillator that is designed to measure the diff --git a/pandas_ta/volume/kvo.py b/pandas_ta/volume/kvo.py index 46a33a5..994688f 100644 --- a/pandas_ta/volume/kvo.py +++ b/pandas_ta/volume/kvo.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta.overlap import hlc3, ma from pandas_ta.utils import get_drift, get_offset, signed_series, verify_series -def kvo(high, low, close, volume, fast=None, slow=None, signal=None, mamode=None, drift=None, offset=None, **kwargs): +def kvo(high: Series, low: Series, close: Series, volume: Series, fast: int = None, slow: int = None, + signal=None, mamode: str = None, drift: int = None, offset: int = None, **kwargs) -> DataFrame: """Klinger Volume Oscillator (KVO) This indicator was developed by Stephen J. Klinger. It is designed to predict diff --git a/pandas_ta/volume/mfi.py b/pandas_ta/volume/mfi.py index 156fdec..974f44d 100644 --- a/pandas_ta/volume/mfi.py +++ b/pandas_ta/volume/mfi.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -from pandas import DataFrame +from pandas import DataFrame, Series from pandas_ta import Imports from pandas_ta.overlap import hlc3 from pandas_ta.utils import get_drift, get_offset, verify_series -def mfi(high, low, close, volume, length=None, talib=None, drift=None, offset=None, **kwargs): +def mfi(high: Series, low: Series, close: Series, volume: Series, length: int = None, talib: bool = None, + drift: int = None, offset: int = None, **kwargs) -> Series: """Money Flow Index (MFI) Money Flow Index is an oscillator indicator that is used to measure buying and diff --git a/pandas_ta/volume/nvi.py b/pandas_ta/volume/nvi.py index 6c324d1..cd6688a 100644 --- a/pandas_ta/volume/nvi.py +++ b/pandas_ta/volume/nvi.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from pandas_ta.momentum import roc from pandas_ta.utils import get_offset, signed_series, verify_series +from pandas import Series -def nvi(close, volume, length=None, initial=None, offset=None, **kwargs): +def nvi(close: Series, volume: Series, length: int = None, initial: int = None, offset: int = None, + **kwargs) -> Series: """Negative Volume Index (NVI) The Negative Volume Index is a cumulative indicator that uses volume change in diff --git a/pandas_ta/volume/obv.py b/pandas_ta/volume/obv.py index 0a19f7e..1fa2697 100644 --- a/pandas_ta/volume/obv.py +++ b/pandas_ta/volume/obv.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta import Imports from pandas_ta.utils import get_offset, signed_series, verify_series +from pandas import Series -def obv(close, volume, talib=None, offset=None, **kwargs): +def obv(close: Series, volume: Series, talib: bool = None, offset: int = None, **kwargs) -> Series: """On Balance Volume (OBV) On Balance Volume is a cumulative indicator to measure buying and selling diff --git a/pandas_ta/volume/pvi.py b/pandas_ta/volume/pvi.py index f2d4fce..17cf596 100644 --- a/pandas_ta/volume/pvi.py +++ b/pandas_ta/volume/pvi.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- from pandas_ta.momentum import roc from pandas_ta.utils import get_offset, signed_series, verify_series +from pandas import Series -def pvi(close, volume, length=None, initial=None, offset=None, **kwargs): +def pvi(close: Series, volume: Series, length: int = None, initial: int = None, offset: int = None, + **kwargs) -> Series: """Positive Volume Index (PVI) The Positive Volume Index is a cumulative indicator that uses volume change in diff --git a/pandas_ta/volume/pvol.py b/pandas_ta/volume/pvol.py index a3cbe05..963691a 100644 --- a/pandas_ta/volume/pvol.py +++ b/pandas_ta/volume/pvol.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- from pandas_ta.utils import get_offset, signed_series, verify_series +from pandas import Series -def pvol(close, volume, offset=None, **kwargs): +def pvol(close: Series, volume: Series, offset: int = None, **kwargs) -> Series: """Price-Volume (PVOL) Returns a series of the product of price and volume. diff --git a/pandas_ta/volume/pvr.py b/pandas_ta/volume/pvr.py index 7c9455e..fdd4cc2 100644 --- a/pandas_ta/volume/pvr.py +++ b/pandas_ta/volume/pvr.py @@ -4,7 +4,7 @@ from numpy import nan as npNaN from pandas import Series -def pvr(close, volume): +def pvr(close: Series, volume: Series) -> Series: """Price Volume Rank The Price Volume Rank was developed by Anthony J. Macek and is described in his diff --git a/pandas_ta/volume/pvt.py b/pandas_ta/volume/pvt.py index 10530ca..a603767 100644 --- a/pandas_ta/volume/pvt.py +++ b/pandas_ta/volume/pvt.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- from pandas_ta.momentum import roc from pandas_ta.utils import get_drift, get_offset, verify_series +from pandas import Series -def pvt(close, volume, drift=None, offset=None, **kwargs): +def pvt(close: Series, volume: Series, drift: int = None, offset: int = None, **kwargs) -> Series: """Price-Volume Trend (PVT) The Price-Volume Trend utilizes the Rate of Change with volume to diff --git a/pandas_ta/volume/vp.py b/pandas_ta/volume/vp.py index 6dd88ed..63d7e0c 100644 --- a/pandas_ta/volume/vp.py +++ b/pandas_ta/volume/vp.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- from numpy import array_split from numpy import mean -from pandas import cut, concat, DataFrame +from pandas import cut, concat, DataFrame, Series from pandas_ta.utils import signed_series, verify_series -def vp(close, volume, width=None, **kwargs): +def vp(close: Series, volume: Series, width: int = None, **kwargs) -> DataFrame: """Volume Profile (VP) Calculates the Volume Profile by slicing price into ranges. diff --git a/pandas_ta/volume/wb_tsv.py b/pandas_ta/volume/wb_tsv.py index bae6726..7cc875b 100644 --- a/pandas_ta/volume/wb_tsv.py +++ b/pandas_ta/volume/wb_tsv.py @@ -4,7 +4,8 @@ from pandas_ta.overlap import ma from pandas_ta.utils import get_drift, get_offset, verify_series, signed_series, zero -def wb_tsv(close=None, volume=None, length=None, signal=None, mamode=None, drift=None, offset=None, **kwargs): +def wb_tsv(close: Series, volume: Series, length: int = None, signal: int = None, mamode: str = None, + drift: int = None, offset: int = None, **kwargs) -> DataFrame: """Time Segmented Value (TSV) TSV is a proprietary technical indicator developed by Worden Brothers Inc., @@ -44,7 +45,7 @@ def wb_tsv(close=None, volume=None, length=None, signal=None, mamode=None, drift # Calculate Result signed_volume = volume * signed_series(close, 1) # > 0 - signed_volume[signed_volume < 0 ] = -signed_volume # < 0 + signed_volume[signed_volume < 0] = -signed_volume # < 0 signed_volume.apply(zero) # ~ 0 cvd = signed_volume * close.diff(drift) @@ -81,4 +82,4 @@ def wb_tsv(close=None, volume=None, length=None, signal=None, mamode=None, drift df.name = f"TSV{_props}" df.category = tsv.category - return df \ No newline at end of file + return df