From eece46fc16a8fbc09be29a12252c909d35b49ec2 Mon Sep 17 00:00:00 2001 From: Adewale-1 Date: Fri, 24 May 2024 22:31:20 -0400 Subject: [PATCH] Issue #781 fixed --- README.md | 1 + pandas_ta/__init__.py | 174 +++- pandas_ta/core.py | 1360 +++++++++++++++++++++----- pandas_ta/momentum/__init__.py | 2 + pandas_ta/momentum/smc.py | 129 +++ tests/test_ext_indicator_momentum.py | 131 ++- tests/test_indicator_momentum.py | 120 ++- 7 files changed, 1622 insertions(+), 295 deletions(-) create mode 100644 pandas_ta/momentum/smc.py diff --git a/README.md b/README.md index fe3e28b7..b2c811b9 100644 --- a/README.md +++ b/README.md @@ -703,6 +703,7 @@ df = df.ta.cdl_pattern(name=["doji", "inside"]) * _Relative Vigor Index_: **rvgi** * _Schaff Trend Cycle_: **stc** * _Slope_: **slope** +* _Smart Money Concept_: **smc** * _SMI Ergodic_ **smi** * _Squeeze_: **squeeze** * Default is John Carter's. Enable Lazybear's with ```lazybear=True``` diff --git a/pandas_ta/__init__.py b/pandas_ta/__init__.py index 0e740b84..63d277a9 100644 --- a/pandas_ta/__init__.py +++ b/pandas_ta/__init__.py @@ -39,49 +39,157 @@ # Will find a dynamic solution later. Category = { # Candles - "candles": [ - "cdl_pattern", "cdl_z", "ha" - ], + "candles": ["cdl_pattern", "cdl_z", "ha"], # Cycles "cycles": ["ebsw"], # Momentum "momentum": [ - "ao", "apo", "bias", "bop", "brar", "cci", "cfo", "cg", "cmo", - "coppock", "cti", "er", "eri", "fisher", "inertia", "kdj", "kst", "macd", - "mom", "pgo", "ppo", "psl", "pvo", "qqe", "roc", "rsi", "rsx", "rvgi", - "slope", "smi", "squeeze", "squeeze_pro", "stc", "stoch", "stochrsi", "td_seq", "trix", - "tsi", "uo", "willr" + "ao", + "apo", + "bias", + "bop", + "brar", + "cci", + "cfo", + "cg", + "cmo", + "coppock", + "cti", + "er", + "eri", + "fisher", + "inertia", + "kdj", + "kst", + "macd", + "mom", + "pgo", + "ppo", + "psl", + "pvo", + "qqe", + "roc", + "rsi", + "rsx", + "rvgi", + "slope", + "smc", + "smi", + "squeeze", + "squeeze_pro", + "stc", + "stoch", + "stochrsi", + "td_seq", + "trix", + "tsi", + "uo", + "willr", ], # Overlap "overlap": [ - "alma", "dema", "ema", "fwma", "hilo", "hl2", "hlc3", "hma", "ichimoku", - "jma", "kama", "linreg", "mcgd", "midpoint", "midprice", "ohlc4", - "pwma", "rma", "sinwma", "sma", "ssf", "supertrend", "swma", "t3", - "tema", "trima", "vidya", "vwap", "vwma", "wcp", "wma", "zlma" + "alma", + "dema", + "ema", + "fwma", + "hilo", + "hl2", + "hlc3", + "hma", + "ichimoku", + "jma", + "kama", + "linreg", + "mcgd", + "midpoint", + "midprice", + "ohlc4", + "pwma", + "rma", + "sinwma", + "sma", + "ssf", + "supertrend", + "swma", + "t3", + "tema", + "trima", + "vidya", + "vwap", + "vwma", + "wcp", + "wma", + "zlma", ], # Performance "performance": ["log_return", "percent_return"], # Statistics "statistics": [ - "entropy", "kurtosis", "mad", "median", "quantile", "skew", "stdev", - "tos_stdevall", "variance", "zscore" + "entropy", + "kurtosis", + "mad", + "median", + "quantile", + "skew", + "stdev", + "tos_stdevall", + "variance", + "zscore", ], # Trend "trend": [ - "adx", "amat", "aroon", "chop", "cksp", "decay", "decreasing", "dpo", - "increasing", "long_run", "psar", "qstick", "short_run", "tsignals", - "ttm_trend", "vhf", "vortex", "xsignals" + "adx", + "amat", + "aroon", + "chop", + "cksp", + "decay", + "decreasing", + "dpo", + "increasing", + "long_run", + "psar", + "qstick", + "short_run", + "tsignals", + "ttm_trend", + "vhf", + "vortex", + "xsignals", ], # Volatility "volatility": [ - "aberration", "accbands", "atr", "bbands", "donchian", "hwc", "kc", "massi", - "natr", "pdist", "rvi", "thermo", "true_range", "ui" + "aberration", + "accbands", + "atr", + "bbands", + "donchian", + "hwc", + "kc", + "massi", + "natr", + "pdist", + "rvi", + "thermo", + "true_range", + "ui", ], - # Volume, "vp" or "Volume Profile" is unique "volume": [ - "ad", "adosc", "aobv", "cmf", "efi", "eom", "kvo", "mfi", "nvi", "obv", - "pvi", "pvol", "pvr", "pvt" + "ad", + "adosc", + "aobv", + "cmf", + "efi", + "eom", + "kvo", + "mfi", + "nvi", + "obv", + "pvi", + "pvol", + "pvr", + "pvt", ], } @@ -90,16 +198,26 @@ "high": "max", "low": "min", "close": "last", - "volume": "sum" + "volume": "sum", } # https://www.worldtimezone.com/markets24.php EXCHANGE_TZ = { - "NZSX": 12, "ASX": 11, - "TSE": 9, "HKE": 8, "SSE": 8, "SGX": 8, - "NSE": 5.5, "DIFX": 4, "RTS": 3, - "JSE": 2, "FWB": 1, "LSE": 1, - "BMF": -2, "NYSE": -4, "TSX": -4 + "NZSX": 12, + "ASX": 11, + "TSE": 9, + "HKE": 8, + "SSE": 8, + "SGX": 8, + "NSE": 5.5, + "DIFX": 4, + "RTS": 3, + "JSE": 2, + "FWB": 1, + "LSE": 1, + "BMF": -2, + "NYSE": -4, + "TSX": -4, } RATE = { diff --git a/pandas_ta/core.py b/pandas_ta/core.py index fdee64ab..c215a100 100644 --- a/pandas_ta/core.py +++ b/pandas_ta/core.py @@ -27,6 +27,7 @@ df = pd.DataFrame() + # Strategy DataClass @dataclass class Strategy: @@ -66,7 +67,9 @@ def __post_init__(self): ta_is_list = isinstance(self.ta, list) if self.name is None or not name_is_str: - required_args.append(' - name. Must be a string. Example: "My TA". Note: "all" is reserved.') + required_args.append( + ' - name. Must be a string. Example: "My TA". Note: "all" is reserved.' + ) has_name != has_name if self.ta is None: @@ -105,8 +108,8 @@ def total_ta(self): {"kind": "sma", "length": 20}, {"kind": "sma", "length": 50}, {"kind": "sma", "length": 200}, - {"kind": "sma", "close": "volume", "length": 20, "prefix": "VOL"} - ] + {"kind": "sma", "close": "volume", "length": 20, "prefix": "VOL"}, + ], ) @@ -122,7 +125,8 @@ class BasePandasObject(PandasObject): """ def __init__(self, df, **kwargs): - if df.empty: return + if df.empty: + return if len(df.columns) > 0: common_names = { "Date": "date", @@ -263,10 +267,10 @@ def _validate(obj: Tuple[pd.DataFrame, pd.Series]): # DataFrame Behavioral Methods def __call__( - self, kind: str = None, - timed: bool = False, version: bool = False, **kwargs - ): - if version: print(f"Pandas TA - Technical Analysis Indicators - v{self.version}") + self, kind: str = None, timed: bool = False, version: bool = False, **kwargs + ): + if version: + print(f"Pandas TA - Technical Analysis Indicators - v{self.version}") try: if isinstance(kind, str): kind = kind.lower() @@ -277,7 +281,9 @@ def __call__( # Run the indicator result = fn(**kwargs) # = getattr(self, kind)(**kwargs) - self._last_run = get_time(self.exchange, to_string=True) # Save when it completed it's run + self._last_run = get_time( + self.exchange, to_string=True + ) # Save when it completed it's run if timed: result.timed = final_time(stime) @@ -399,29 +405,38 @@ def _append(self, result=None, **kwargs) -> None: """Appends a Pandas Series or DataFrame columns to self._df.""" if "append" in kwargs and kwargs["append"]: df = self._df - if df is None or result is None: return + if df is None or result is None: + return else: simplefilter(action="ignore", category=pd.errors.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 + kwargs["col_names"] = ( + kwargs["col_names"], + ) # Note: tuple(kwargs["col_names"]) doesn't work if isinstance(result, pd.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): if len(kwargs["col_names"]) >= len(result.columns): - for col, ind_name in zip(result.columns, kwargs["col_names"]): + for col, ind_name in zip( + result.columns, kwargs["col_names"] + ): df[ind_name] = result.loc[:, col] else: - print(f"Not enough col_names were specified : got {len(kwargs['col_names'])}, expected {len(result.columns)}.") + print( + f"Not enough col_names were specified : got {len(kwargs['col_names'])}, expected {len(result.columns)}." + ) return else: for i, column in enumerate(result.columns): df[column] = result.iloc[:, i] else: ind_name = ( - kwargs["col_names"][0] if "col_names" in kwargs and - isinstance(kwargs["col_names"], tuple) else result.name + kwargs["col_names"][0] + if "col_names" in kwargs + and isinstance(kwargs["col_names"], tuple) + else result.name ) df[ind_name] = result @@ -432,7 +447,8 @@ def _check_na_columns(self, stdout: bool = True): def _get_column(self, series): """Attempts to get the correct series or 'column' and return it.""" df = self._df - if df is None: return + if df is None: + return # Explicitly passing a pd.Series to override default. if isinstance(series, pd.Series): @@ -481,10 +497,13 @@ def _post_process(self, result, **kwargs) -> Tuple[pd.Series, pd.DataFrame]: else: # 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 - "col_numbers" in kwargs and - kwargs["col_numbers"] is not None else result) + result = ( + result.iloc[:, [int(n) for n in kwargs["col_numbers"]]] + if isinstance(result, pd.DataFrame) + and "col_numbers" in kwargs + and kwargs["col_numbers"] is not None + else result + ) # Add prefix/suffix and append to the dataframe self._add_prefix_suffix(result=result, **kwargs) self._append(result=result, **kwargs) @@ -549,7 +568,7 @@ def constants(self, append: bool, values: list): if append: for x in values: self._df[f"{x}"] = x - return self._df[self._df.columns[-len(values):]] + return self._df[self._df.columns[-len(values) :]] else: for x in values: del self._df[f"{x}"] @@ -585,7 +604,13 @@ def indicators(self, **kwargs): ] # 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(pd.DataFrame().ta) + if not x.startswith("_") and not x.endswith("_") + ) + ) # Add Pandas TA methods and properties to be removed removed = helper_methods + ta_properties @@ -606,7 +631,9 @@ def indicators(self, **kwargs): header = f"Pandas TA - Technical Analysis Indicators - v{self.version}" s = f"{header}\nTotal Indicators & Utilities: {total_indicators + len(ALL_PATTERNS)}\n" if total_indicators > 0: - print(f"{s}Abbreviations:\n {', '.join(ta_indicators)}\n\nCandle Patterns:\n {', '.join(ALL_PATTERNS)}") + print( + f"{s}Abbreviations:\n {', '.join(ta_indicators)}\n\nCandle Patterns:\n {', '.join(ALL_PATTERNS)}" + ) else: print(s) @@ -654,7 +681,7 @@ def strategy(self, *args, **kwargs): # "data", # reserved "long_run", "short_run", - "td_seq", # Performance exclusion + "td_seq", # Performance exclusion "tsignals", "vp", "xsignals", @@ -687,9 +714,12 @@ def strategy(self, *args, **kwargs): removal = [] for kwds in ta: _ = False - if "length" in kwds and kwds["length"] > self._df.shape[0]: _ = True - if _: removal.append(kwds) - if len(removal) > 0: [ta.remove(x) for x in removal] + if "length" in kwds and kwds["length"] > self._df.shape[0]: + _ = True + if _: + removal.append(kwds) + if len(removal) > 0: + [ta.remove(x) for x in removal] verbose = kwargs.pop("verbose", False) if verbose: @@ -708,10 +738,17 @@ def strategy(self, *args, **kwargs): if use_multiprocessing and mode["custom"]: # Determine if the Custom Model has 'col_names' parameter - has_col_names = (True if len([ - True for x in ta - if "col_names" in x and isinstance(x["col_names"], tuple) - ]) else False) + has_col_names = ( + True + if len( + [ + True + for x in ta + if "col_names" in x and isinstance(x["col_names"], tuple) + ] + ) + else False + ) if has_col_names: use_multiprocessing = False @@ -724,18 +761,31 @@ def strategy(self, *args, **kwargs): _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(npLog10(_total_ta)) + 1 + _chunksize = ( + mp_chunksize - 1 + if mp_chunksize > _total_ta + else int(npLog10(_total_ta)) + 1 + ) if verbose: - print(f"[i] Multiprocessing {_total_ta} indicators with {_chunksize} chunks and {self.cores}/{cpu_count()} cpus.") + print( + f"[i] Multiprocessing {_total_ta} indicators with {_chunksize} chunks and {self.cores}/{cpu_count()} cpus." + ) results = None if mode["custom"]: # Create a list of all the custom indicators into a list - custom_ta = [( - ind["kind"], - ind["params"] if "params" in ind and isinstance(ind["params"], tuple) else (), - {**ind, **kwargs}, - ) for ind in ta] + custom_ta = [ + ( + ind["kind"], + ( + ind["params"] + if "params" in ind and isinstance(ind["params"], tuple) + else () + ), + {**ind, **kwargs}, + ) + for ind in ta + ] # Custom multiprocessing pool. Must be ordered for Chained Strategies # May fix this to cpus if Chaining/Composition if it remains results = pool.imap(self._mp_worker, custom_ta, _chunksize) @@ -744,14 +794,24 @@ def strategy(self, *args, **kwargs): # All and Categorical multiprocessing pool. if all_ordered: if Imports["tqdm"]: - results = tqdm(pool.imap(self._mp_worker, default_ta, _chunksize)) # Order over Speed + results = tqdm( + pool.imap(self._mp_worker, default_ta, _chunksize) + ) # Order over Speed else: - results = pool.imap(self._mp_worker, default_ta, _chunksize) # Order over Speed + results = pool.imap( + self._mp_worker, default_ta, _chunksize + ) # Order over Speed else: if Imports["tqdm"]: - results = tqdm(pool.imap_unordered(self._mp_worker, default_ta, _chunksize)) # Speed over Order + results = tqdm( + pool.imap_unordered( + self._mp_worker, default_ta, _chunksize + ) + ) # Speed over Order else: - results = pool.imap_unordered(self._mp_worker, default_ta, _chunksize) # Speed over Order + results = pool.imap_unordered( + self._mp_worker, default_ta, _chunksize + ) # Speed over Order if results is None: print(f"[X] ta.strategy('{name}') has no results.") return @@ -765,18 +825,28 @@ def strategy(self, *args, **kwargs): if verbose: _col_msg = f"[i] No mulitproccessing (cores = 0)." if has_col_names: - _col_msg = f"[i] No mulitproccessing support for 'col_names' option." + _col_msg = ( + f"[i] No mulitproccessing support for 'col_names' option." + ) print(_col_msg) if mode["custom"]: if Imports["tqdm"] and verbose: pbar = tqdm(ta, f"[i] Progress") for ind in pbar: - params = ind["params"] if "params" in ind and isinstance(ind["params"], tuple) else tuple() + params = ( + ind["params"] + if "params" in ind and isinstance(ind["params"], tuple) + else tuple() + ) getattr(self, ind["kind"])(*params, **{**ind, **kwargs}) else: for ind in ta: - params = ind["params"] if "params" in ind and isinstance(ind["params"], tuple) else tuple() + params = ( + ind["params"] + if "params" in ind and isinstance(ind["params"], tuple) + else tuple() + ) getattr(self, ind["kind"])(*params, **{**ind, **kwargs}) else: if Imports["tqdm"] and verbose: @@ -798,8 +868,8 @@ def strategy(self, *args, **kwargs): if timed: print(f"[i] Runtime: {final_time(stime)}") - if returns: return self._df - + if returns: + return self._df def ticker(self, ticker: str, **kwargs): """ticker @@ -850,7 +920,8 @@ def ticker(self, ticker: str, **kwargs): # df = av(ticker, **kwargs) if ds and ds == "av" else yf(ticker, **kwargs) df = yf(ticker, **kwargs) - if df is None: return + if df is None: + return elif df.empty: print(f"[X] DataFrame is empty: {df.shape}") return @@ -860,10 +931,10 @@ def ticker(self, ticker: str, **kwargs): df.columns = df.columns.str.lower() self._df = df - if strategy is not None: self.strategy(strategy, **kwargs) + if strategy is not None: + self.strategy(strategy, **kwargs) return df - # Public DataFrame Methods: Indicators and Utilities # Candles def cdl_pattern(self, name="all", offset=None, **kwargs): @@ -871,7 +942,15 @@ def cdl_pattern(self, name="all", 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 = cdl_pattern(open_=open_, high=high, low=low, close=close, name=name, offset=offset, **kwargs) + result = cdl_pattern( + open_=open_, + high=high, + low=low, + close=close, + name=name, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def cdl_z(self, full=None, offset=None, **kwargs): @@ -879,7 +958,15 @@ def cdl_z(self, full=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 = cdl_z(open_=open_, high=high, low=low, close=close, full=full, offset=offset, **kwargs) + result = cdl_z( + open_=open_, + high=high, + low=low, + close=close, + full=full, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def ha(self, offset=None, **kwargs): @@ -887,7 +974,9 @@ def ha(self, 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 = ha(open_=open_, high=high, low=low, close=close, offset=offset, **kwargs) + result = ha( + open_=open_, high=high, low=low, close=close, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) # Cycles @@ -905,12 +994,16 @@ def ao(self, fast=None, slow=None, offset=None, **kwargs): def apo(self, fast=None, slow=None, mamode=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = apo(close=close, fast=fast, slow=slow, mamode=mamode, offset=offset, **kwargs) + result = apo( + close=close, fast=fast, slow=slow, mamode=mamode, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def bias(self, length=None, mamode=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = bias(close=close, length=length, mamode=mamode, offset=offset, **kwargs) + result = bias( + close=close, length=length, mamode=mamode, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def bop(self, percentage=False, offset=None, **kwargs): @@ -918,7 +1011,15 @@ def bop(self, percentage=False, 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 = bop(open_=open_, high=high, low=low, close=close, percentage=percentage, offset=offset, **kwargs) + result = bop( + open_=open_, + high=high, + low=low, + close=close, + percentage=percentage, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def brar(self, length=None, scalar=None, drift=None, offset=None, **kwargs): @@ -926,14 +1027,26 @@ def brar(self, length=None, scalar=None, drift=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 = brar(open_=open_, high=high, low=low, close=close, length=length, scalar=scalar, drift=drift, offset=offset, **kwargs) + result = brar( + open_=open_, + high=high, + low=low, + close=close, + length=length, + scalar=scalar, + drift=drift, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def cci(self, length=None, c=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 = cci(high=high, low=low, close=close, length=length, c=c, offset=offset, **kwargs) + result = cci( + high=high, low=low, close=close, length=length, c=c, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def cfo(self, length=None, offset=None, **kwargs): @@ -948,12 +1061,21 @@ def cg(self, length=None, offset=None, **kwargs): def cmo(self, length=None, scalar=None, drift=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = cmo(close=close, length=length, scalar=scalar, drift=drift, offset=offset, **kwargs) + result = cmo( + close=close, + length=length, + scalar=scalar, + drift=drift, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def coppock(self, length=None, fast=None, slow=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = coppock(close=close, length=length, fast=fast, slow=slow, offset=offset, **kwargs) + result = coppock( + close=close, length=length, fast=fast, slow=slow, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def cti(self, length=None, offset=None, **kwargs): @@ -964,7 +1086,9 @@ def cti(self, length=None, offset=None, **kwargs): def dm(self, drift=None, offset=None, mamode=None, **kwargs): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) - result = dm(high=high, low=low, drift=drift, mamode=mamode, offset=offset, **kwargs) + result = dm( + high=high, low=low, drift=drift, mamode=mamode, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def er(self, length=None, drift=None, offset=None, **kwargs): @@ -976,23 +1100,62 @@ def eri(self, length=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 = eri(high=high, low=low, close=close, length=length, offset=offset, **kwargs) + result = eri( + high=high, low=low, close=close, length=length, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def fisher(self, length=None, signal=None, offset=None, **kwargs): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) - result = fisher(high=high, low=low, length=length, signal=signal, offset=offset, **kwargs) - return self._post_process(result, **kwargs) - - def inertia(self, length=None, rvi_length=None, scalar=None, refined=None, thirds=None, mamode=None, drift=None, offset=None, **kwargs): + result = fisher( + high=high, low=low, length=length, signal=signal, offset=offset, **kwargs + ) + return self._post_process(result, **kwargs) + + def inertia( + self, + length=None, + rvi_length=None, + scalar=None, + refined=None, + thirds=None, + mamode=None, + drift=None, + offset=None, + **kwargs, + ): close = self._get_column(kwargs.pop("close", "close")) 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) @@ -1000,17 +1163,53 @@ def kdj(self, length=None, signal=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 = kdj(high=high, low=low, close=close, length=length, signal=signal, offset=offset, **kwargs) - return self._post_process(result, **kwargs) - - 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 = kdj( + high=high, + low=low, + close=close, + length=length, + signal=signal, + offset=offset, + **kwargs, + ) + return self._post_process(result, **kwargs) + + 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, + ) return self._post_process(result, **kwargs) def macd(self, fast=None, slow=None, signal=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = macd(close=close, fast=fast, slow=slow, signal=signal, offset=offset, **kwargs) + result = macd( + close=close, fast=fast, slow=slow, signal=signal, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def mom(self, length=None, offset=None, **kwargs): @@ -1022,30 +1221,72 @@ def pgo(self, length=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 = pgo(high=high, low=low, close=close, length=length, offset=offset, **kwargs) + result = pgo( + high=high, low=low, close=close, length=length, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) - def ppo(self, fast=None, slow=None, scalar=None, mamode=None, offset=None, **kwargs): + def ppo( + self, fast=None, slow=None, scalar=None, mamode=None, offset=None, **kwargs + ): close = self._get_column(kwargs.pop("close", "close")) - result = ppo(close=close, fast=fast, slow=slow, scalar=scalar, mamode=mamode, offset=offset, **kwargs) + result = ppo( + close=close, + fast=fast, + slow=slow, + scalar=scalar, + mamode=mamode, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) - def psl(self, open_=None, length=None, scalar=None, drift=None, offset=None, **kwargs): + def psl( + self, open_=None, length=None, scalar=None, drift=None, offset=None, **kwargs + ): if open_ is not None: open_ = self._get_column(kwargs.pop("open", "open")) close = self._get_column(kwargs.pop("close", "close")) - result = psl(close=close, open_=open_, length=length, scalar=scalar, drift=drift, offset=offset, **kwargs) + result = psl( + close=close, + open_=open_, + length=length, + scalar=scalar, + drift=drift, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) - def pvo(self, fast=None, slow=None, signal=None, scalar=None, offset=None, **kwargs): + def pvo( + self, fast=None, slow=None, signal=None, scalar=None, offset=None, **kwargs + ): volume = self._get_column(kwargs.pop("volume", "volume")) - result = pvo(volume=volume, fast=fast, slow=slow, signal=signal, scalar=scalar, offset=offset, **kwargs) - return self._post_process(result, **kwargs) - - def qqe(self, length=None, smooth=None, factor=None, mamode=None, offset=None, **kwargs): - close = self._get_column(kwargs.pop("close", "close")) - result = qqe(close=close, length=length, smooth=smooth, factor=factor, mamode=mamode, offset=offset, **kwargs) + result = pvo( + volume=volume, + fast=fast, + slow=slow, + signal=signal, + scalar=scalar, + offset=offset, + **kwargs, + ) + return self._post_process(result, **kwargs) + + def qqe( + self, length=None, smooth=None, factor=None, mamode=None, offset=None, **kwargs + ): + close = self._get_column(kwargs.pop("close", "close")) + result = qqe( + close=close, + length=length, + smooth=smooth, + factor=factor, + mamode=mamode, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def roc(self, length=None, offset=None, **kwargs): @@ -1055,7 +1296,14 @@ def roc(self, length=None, offset=None, **kwargs): def rsi(self, length=None, scalar=None, drift=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = rsi(close=close, length=length, scalar=scalar, drift=drift, offset=offset, **kwargs) + result = rsi( + close=close, + length=length, + scalar=scalar, + drift=drift, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def rsx(self, length=None, drift=None, offset=None, **kwargs): @@ -1068,7 +1316,16 @@ def rvgi(self, length=None, swma_length=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 = 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): @@ -1076,77 +1333,272 @@ def slope(self, length=None, offset=None, **kwargs): result = slope(close=close, length=length, offset=offset, **kwargs) return self._post_process(result, **kwargs) - def smi(self, fast=None, slow=None, signal=None, scalar=None, offset=None, **kwargs): - close = self._get_column(kwargs.pop("close", "close")) - result = smi(close=close, fast=fast, slow=slow, signal=signal, scalar=scalar, offset=offset, **kwargs) - return self._post_process(result, **kwargs) - def squeeze(self, 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 smc(self, fast=None, slow=None, signal=None, scalar=None, offset=None, **kwargs): + close = self._get_column(kwargs.pop("close", "close")) + result = smc( + close=close, + fast=fast, + slow=slow, + signal=signal, + scalar=scalar, + offset=offset, + **kwargs, + ) + return self._post_process(result, **kwargs) + + def smi( + self, fast=None, slow=None, signal=None, scalar=None, offset=None, **kwargs + ): + close = self._get_column(kwargs.pop("close", "close")) + result = smi( + close=close, + fast=fast, + slow=slow, + signal=signal, + scalar=scalar, + offset=offset, + **kwargs, + ) + return self._post_process(result, **kwargs) + + def squeeze( + self, + 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, + ): 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) - 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): + 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) - 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) - return self._post_process(result, **kwargs) - - def stoch(self, fast_k=None, slow_k=None, slow_d=None, mamode=None, offset=None, **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, + ) + return self._post_process(result, **kwargs) + + def stoch( + self, fast_k=None, slow_k=None, slow_d=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 = stoch(high=high, low=low, close=close, fast_k=fast_k, slow_k=slow_k, slow_d=slow_d, mamode=mamode, offset=offset, **kwargs) - return self._post_process(result, **kwargs) - - def stochrsi(self, length=None, rsi_length=None, k=None, d=None, mamode=None, offset=None, **kwargs): + result = stoch( + high=high, + low=low, + close=close, + fast_k=fast_k, + slow_k=slow_k, + slow_d=slow_d, + mamode=mamode, + offset=offset, + **kwargs, + ) + return self._post_process(result, **kwargs) + + def stochrsi( + self, + length=None, + rsi_length=None, + k=None, + d=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 = 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): close = self._get_column(kwargs.pop("close", "close")) - result = td_seq(close=close, asint=asint, offset=offset, show_all=show_all, **kwargs) + result = td_seq( + close=close, asint=asint, offset=offset, show_all=show_all, **kwargs + ) return self._post_process(result, **kwargs) - def trix(self, length=None, signal=None, scalar=None, drift=None, offset=None, **kwargs): + def trix( + self, length=None, signal=None, scalar=None, drift=None, offset=None, **kwargs + ): close = self._get_column(kwargs.pop("close", "close")) - result = trix(close=close, length=length, signal=signal, scalar=scalar, drift=drift, offset=offset, **kwargs) + result = trix( + close=close, + length=length, + signal=signal, + scalar=scalar, + drift=drift, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def tsi(self, fast=None, slow=None, drift=None, mamode=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = tsi(close=close, fast=fast, slow=slow, drift=drift, mamode=mamode, offset=offset, **kwargs) - return self._post_process(result, **kwargs) - - def uo(self, fast=None, medium=None, slow=None, fast_w=None, medium_w=None, slow_w=None, drift=None, offset=None, **kwargs): + result = tsi( + close=close, + fast=fast, + slow=slow, + drift=drift, + mamode=mamode, + offset=offset, + **kwargs, + ) + return self._post_process(result, **kwargs) + + def uo( + self, + fast=None, + medium=None, + slow=None, + fast_w=None, + medium_w=None, + slow_w=None, + drift=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 = 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): 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 = willr(high=high, low=low, close=close, length=length, percentage=percentage, offset=offset, **kwargs) + result = willr( + high=high, + low=low, + close=close, + length=length, + percentage=percentage, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) # Overlap - def alma(self, length=None, sigma=None, distribution_offset=None, offset=None, **kwargs): + 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): @@ -1164,11 +1616,22 @@ def fwma(self, length=None, offset=None, **kwargs): result = fwma(close=close, length=length, offset=offset, **kwargs) return self._post_process(result, **kwargs) - def hilo(self, high_length=None, low_length=None, mamode=None, offset=None, **kwargs): + def hilo( + self, high_length=None, low_length=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 = hilo(high=high, low=low, close=close, high_length=high_length, low_length=low_length, mamode=mamode, offset=offset, **kwargs) + result = hilo( + high=high, + low=low, + close=close, + high_length=high_length, + low_length=low_length, + mamode=mamode, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def hl2(self, offset=None, **kwargs): @@ -1201,14 +1664,34 @@ def jma(self, length=None, phase=None, offset=None, **kwargs): def kama(self, length=None, fast=None, slow=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = kama(close=close, length=length, fast=fast, slow=slow, offset=offset, **kwargs) + result = kama( + close=close, length=length, fast=fast, slow=slow, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) - def ichimoku(self, tenkan=None, kijun=None, senkou=None, include_chikou=True, offset=None, **kwargs): + def ichimoku( + self, + tenkan=None, + kijun=None, + senkou=None, + include_chikou=True, + 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, 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) @@ -1217,7 +1700,9 @@ def ichimoku(self, tenkan=None, kijun=None, senkou=None, include_chikou=True, of def linreg(self, length=None, offset=None, adjust=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = linreg(close=close, length=length, offset=offset, adjust=adjust, **kwargs) + result = linreg( + close=close, length=length, offset=offset, adjust=adjust, **kwargs + ) return self._post_process(result, **kwargs) def mcgd(self, length=None, offset=None, **kwargs): @@ -1241,7 +1726,9 @@ def ohlc4(self, 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 = ohlc4(open_=open_, high=high, low=low, close=close, offset=offset, **kwargs) + result = ohlc4( + open_=open_, high=high, low=low, close=close, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def pwma(self, length=None, offset=None, **kwargs): @@ -1273,7 +1760,15 @@ def supertrend(self, length=None, multiplier=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 = 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): @@ -1310,13 +1805,23 @@ def vwap(self, anchor=None, offset=None, **kwargs): if not self.datetime_ordered: volume.index = self._df.index - result = vwap(high=high, low=low, close=close, volume=volume, anchor=anchor, offset=offset, **kwargs) + result = vwap( + high=high, + low=low, + close=close, + volume=volume, + anchor=anchor, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def vwma(self, volume=None, length=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) volume = self._get_column(kwargs.pop("volume", "volume")) - result = vwma(close=close, volume=volume, length=length, offset=offset, **kwargs) + result = vwma( + close=close, volume=volume, length=length, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def wcp(self, offset=None, **kwargs): @@ -1333,18 +1838,38 @@ def wma(self, length=None, offset=None, **kwargs): def zlma(self, length=None, mamode=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = zlma(close=close, length=length, mamode=mamode, offset=offset, **kwargs) + result = zlma( + close=close, length=length, mamode=mamode, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) # Performance - def log_return(self, length=None, cumulative=False, percent=False, offset=None, **kwargs): - close = self._get_column(kwargs.pop("close", "close")) - result = log_return(close=close, length=length, cumulative=cumulative, percent=percent, offset=offset, **kwargs) - return self._post_process(result, **kwargs) - - 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) + def log_return( + self, length=None, cumulative=False, percent=False, offset=None, **kwargs + ): + close = self._get_column(kwargs.pop("close", "close")) + result = log_return( + close=close, + length=length, + cumulative=cumulative, + percent=percent, + offset=offset, + **kwargs, + ) + return self._post_process(result, **kwargs) + + 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, + ) return self._post_process(result, **kwargs) # Statistics @@ -1385,7 +1910,9 @@ def stdev(self, length=None, offset=None, **kwargs): def tos_stdevall(self, length=None, stds=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = tos_stdevall(close=close, length=length, stds=stds, offset=offset, **kwargs) + result = tos_stdevall( + close=close, length=length, stds=stds, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def variance(self, length=None, offset=None, **kwargs): @@ -1399,36 +1926,96 @@ def zscore(self, length=None, std=None, offset=None, **kwargs): return self._post_process(result, **kwargs) # Trend - def adx(self, length=None, lensig=None, mamode=None, scalar=None, drift=None, offset=None, **kwargs): + def adx( + self, + length=None, + lensig=None, + mamode=None, + scalar=None, + drift=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 = 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): - close = self._get_column(kwargs.pop("close", "close")) - result = amat(close=close, fast=fast, slow=slow, mamode=mamode, lookback=lookback, 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 + ): + close = self._get_column(kwargs.pop("close", "close")) + result = amat( + close=close, + fast=fast, + slow=slow, + mamode=mamode, + lookback=lookback, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def aroon(self, length=None, scalar=None, offset=None, **kwargs): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) - result = aroon(high=high, low=low, length=length, scalar=scalar, offset=offset, **kwargs) - return self._post_process(result, **kwargs) - - def chop(self, length=None, atr_length=None, scalar=None, drift=None, offset=None, **kwargs): + result = aroon( + high=high, low=low, length=length, scalar=scalar, offset=offset, **kwargs + ) + return self._post_process(result, **kwargs) + + def chop( + self, + length=None, + atr_length=None, + scalar=None, + drift=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 = 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): 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 = cksp(high=high, low=low, close=close, p=p, x=x, q=q, mamode=mamode, offset=offset, **kwargs) + result = cksp( + high=high, + low=low, + close=close, + p=p, + x=x, + q=q, + mamode=mamode, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def decay(self, length=None, mode=None, offset=None, **kwargs): @@ -1438,65 +2025,131 @@ def decay(self, length=None, mode=None, offset=None, **kwargs): def decreasing(self, length=None, strict=None, asint=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = decreasing(close=close, length=length, strict=strict, asint=asint, offset=offset, **kwargs) + result = decreasing( + close=close, + length=length, + strict=strict, + asint=asint, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def dpo(self, length=None, centered=True, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = dpo(close=close, length=length, centered=centered, offset=offset, **kwargs) + result = dpo( + close=close, length=length, centered=centered, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def increasing(self, length=None, strict=None, asint=None, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) - result = increasing(close=close, length=length, strict=strict, asint=asint, offset=offset, **kwargs) + result = increasing( + close=close, + length=length, + strict=strict, + asint=asint, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def long_run(self, fast=None, slow=None, length=None, offset=None, **kwargs): if fast is None and slow is None: return self._df else: - result = long_run(fast=fast, slow=slow, length=length, offset=offset, **kwargs) + result = long_run( + fast=fast, slow=slow, length=length, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def psar(self, af0=None, af=None, max_af=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", None)) - result = psar(high=high, low=low, close=close, af0=af0, af=af, max_af=max_af, offset=offset, **kwargs) + result = psar( + high=high, + low=low, + close=close, + af0=af0, + af=af, + max_af=max_af, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def qstick(self, length=None, offset=None, **kwargs): open_ = self._get_column(kwargs.pop("open", "open")) close = self._get_column(kwargs.pop("close", "close")) - result = qstick(open_=open_, close=close, length=length, offset=offset, **kwargs) + result = qstick( + open_=open_, close=close, length=length, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def short_run(self, fast=None, slow=None, length=None, offset=None, **kwargs): if fast is None and slow is None: return self._df else: - result = short_run(fast=fast, slow=slow, length=length, offset=offset, **kwargs) + result = short_run( + fast=fast, slow=slow, length=length, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) - def supertrend(self, period=None, multiplier=None, mamode=None, drift=None, offset=None, **kwargs): + def supertrend( + self, + period=None, + multiplier=None, + mamode=None, + drift=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 = 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 tsignals(self, trend=None, asbool=None, trend_reset=None, trend_offset=None, offset=None, **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 tsignals( + self, + trend=None, + asbool=None, + trend_reset=None, + trend_offset=None, + offset=None, + **kwargs, + ): if trend is None: return self._df else: - result = tsignals(trend, asbool=asbool, trend_offset=trend_offset, trend_reset=trend_reset, offset=offset, **kwargs) + result = tsignals( + trend, + asbool=asbool, + trend_offset=trend_offset, + trend_reset=trend_reset, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def ttm_trend(self, length=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 = ttm_trend(high=high, low=low, close=close, length=length, offset=offset, **kwargs) + result = ttm_trend( + high=high, low=low, close=close, length=length, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def vhf(self, length=None, drift=None, offset=None, **kwargs): @@ -1508,14 +2161,39 @@ def vortex(self, drift=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 = vortex(high=high, low=low, close=close, drift=drift, offset=offset, **kwargs) - return self._post_process(result, **kwargs) - - def xsignals(self, signal=None, xa=None, xb=None, above=None, long=None, asbool=None, trend_reset=None, trend_offset=None, offset=None, **kwargs): + result = vortex( + high=high, low=low, close=close, drift=drift, offset=offset, **kwargs + ) + return self._post_process(result, **kwargs) + + def xsignals( + self, + signal=None, + xa=None, + xb=None, + above=None, + long=None, + asbool=None, + trend_reset=None, + trend_offset=None, + offset=None, + **kwargs, + ): 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 @@ -1527,7 +2205,9 @@ def above(self, asint=True, offset=None, **kwargs): def above_value(self, value=None, asint=True, offset=None, **kwargs): a = self._get_column(kwargs.pop("close", "a")) - result = above_value(series_a=a, value=value, asint=asint, offset=offset, **kwargs) + result = above_value( + series_a=a, value=value, asint=asint, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def below(self, asint=True, offset=None, **kwargs): @@ -1538,19 +2218,25 @@ def below(self, asint=True, offset=None, **kwargs): def below_value(self, value=None, asint=True, offset=None, **kwargs): a = self._get_column(kwargs.pop("close", "a")) - result = below_value(series_a=a, value=value, asint=asint, offset=offset, **kwargs) + result = below_value( + series_a=a, value=value, asint=asint, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def cross(self, above=True, asint=True, offset=None, **kwargs): a = self._get_column(kwargs.pop("close", "a")) b = self._get_column(kwargs.pop("close", "b")) - result = cross(series_a=a, series_b=b, above=above, asint=asint, offset=offset, **kwargs) + result = cross( + series_a=a, series_b=b, above=above, asint=asint, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def cross_value(self, value=None, above=True, asint=True, offset=None, **kwargs): a = self._get_column(kwargs.pop("close", "a")) # a = self._get_column(a, f"{a}") - result = cross_value(series_a=a, value=value, above=above, asint=asint, offset=offset, **kwargs) + result = cross_value( + series_a=a, value=value, above=above, asint=asint, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) # Volatility @@ -1558,57 +2244,122 @@ def aberration(self, length=None, atr_length=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 = 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): 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 = accbands(high=high, low=low, close=close, length=length, c=c, mamode=mamode, offset=offset, **kwargs) + result = accbands( + high=high, + low=low, + close=close, + length=length, + c=c, + mamode=mamode, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def atr(self, length=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 = atr(high=high, low=low, close=close, length=length, mamode=mamode, offset=offset, **kwargs) + result = atr( + high=high, + low=low, + close=close, + length=length, + mamode=mamode, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def bbands(self, length=None, std=None, mamode=None, offset=None, **kwargs): - close = self._get_column(kwargs.pop("close", "close")) - result = bbands(close=close, length=length, std=std, mamode=mamode, offset=offset, **kwargs) + close = self._get_column(kwargs.pop("close", "close")) + result = bbands( + close=close, length=length, std=std, mamode=mamode, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) 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) - return self._post_process(result, **kwargs) - - def hwc(self, na=None, nb=None, nc=None, nd=None, scalar=None, offset=None, **kwargs): - close = self._get_column(kwargs.pop("close", "close")) - result = hwc(close=close, na=na, nb=nb, nc=nc, nd=nd, scalar=scalar, 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 + ): + close = self._get_column(kwargs.pop("close", "close")) + result = hwc( + close=close, + na=na, + nb=nb, + nc=nc, + nd=nd, + scalar=scalar, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def kc(self, length=None, scalar=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 = 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): high = self._get_column(kwargs.pop("high", "high")) low = self._get_column(kwargs.pop("low", "low")) - result = massi(high=high, low=low, fast=fast, slow=slow, offset=offset, **kwargs) + result = massi( + high=high, low=low, fast=fast, slow=slow, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def natr(self, length=None, mamode=None, scalar=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 = 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): @@ -1616,27 +2367,78 @@ def pdist(self, drift=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 = pdist(open_=open_, high=high, low=low, close=close, drift=drift, offset=offset, **kwargs) - return self._post_process(result, **kwargs) - - def rvi(self, length=None, scalar=None, refined=None, thirds=None, mamode=None, drift=None, offset=None, **kwargs): + result = pdist( + open_=open_, + high=high, + low=low, + close=close, + drift=drift, + offset=offset, + **kwargs, + ) + return self._post_process(result, **kwargs) + + def rvi( + self, + length=None, + scalar=None, + refined=None, + thirds=None, + mamode=None, + drift=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 = 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): + 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): 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 = true_range(high=high, low=low, close=close, drift=drift, offset=offset, **kwargs) + result = true_range( + high=high, low=low, close=close, drift=drift, offset=offset, **kwargs + ) return self._post_process(result, **kwargs) def ui(self, length=None, scalar=None, offset=None, **kwargs): @@ -1652,23 +2454,64 @@ def ad(self, open_=None, signed=True, offset=None, **kwargs): 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 = ad(high=high, low=low, close=close, volume=volume, open_=open_, signed=signed, offset=offset, **kwargs) - return self._post_process(result, **kwargs) - - def adosc(self, open_=None, fast=None, slow=None, signed=True, offset=None, **kwargs): + result = ad( + high=high, + low=low, + close=close, + volume=volume, + open_=open_, + signed=signed, + offset=offset, + **kwargs, + ) + return self._post_process(result, **kwargs) + + def adosc( + self, open_=None, fast=None, slow=None, signed=True, offset=None, **kwargs + ): if open_ is not None: open_ = self._get_column(kwargs.pop("open", "open")) high = self._get_column(kwargs.pop("high", "high")) 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) - return self._post_process(result, **kwargs) - - def aobv(self, fast=None, slow=None, mamode=None, max_lookback=None, min_lookback=None, offset=None, **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): @@ -1678,13 +2521,30 @@ def cmf(self, open_=None, length=None, offset=None, **kwargs): 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): close = self._get_column(kwargs.pop("close", "close")) volume = self._get_column(kwargs.pop("volume", "volume")) - result = efi(close=close, volume=volume, length=length, offset=offset, mamode=mamode, drift=drift, **kwargs) + result = efi( + close=close, + volume=volume, + length=length, + offset=offset, + mamode=mamode, + drift=drift, + **kwargs, + ) return self._post_process(result, **kwargs) def eom(self, length=None, divisor=None, offset=None, drift=None, **kwargs): @@ -1692,15 +2552,46 @@ def eom(self, length=None, divisor=None, offset=None, drift=None, **kwargs): 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) - return self._post_process(result, **kwargs) - - def kvo(self, fast=None, slow=None, length_sig=None, mamode=None, offset=None, drift=None, **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, + ): high = self._get_column(kwargs.pop("high", "high")) 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): @@ -1708,13 +2599,30 @@ def mfi(self, length=None, drift=None, offset=None, **kwargs): 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): close = self._get_column(kwargs.pop("close", "close")) volume = self._get_column(kwargs.pop("volume", "volume")) - result = nvi(close=close, volume=volume, length=length, initial=initial, signed=signed, offset=offset, **kwargs) + result = nvi( + close=close, + volume=volume, + length=length, + initial=initial, + signed=signed, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def obv(self, offset=None, **kwargs): @@ -1726,7 +2634,15 @@ def obv(self, offset=None, **kwargs): def pvi(self, length=None, initial=None, signed=True, offset=None, **kwargs): close = self._get_column(kwargs.pop("close", "close")) volume = self._get_column(kwargs.pop("volume", "volume")) - result = pvi(close=close, volume=volume, length=length, initial=initial, signed=signed, offset=offset, **kwargs) + result = pvi( + close=close, + volume=volume, + length=length, + initial=initial, + signed=signed, + offset=offset, + **kwargs, + ) return self._post_process(result, **kwargs) def pvol(self, volume=None, offset=None, **kwargs): diff --git a/pandas_ta/momentum/__init__.py b/pandas_ta/momentum/__init__.py index 2915bce1..be5baaa0 100644 --- a/pandas_ta/momentum/__init__.py +++ b/pandas_ta/momentum/__init__.py @@ -29,6 +29,7 @@ from .rsx import rsx from .rvgi import rvgi from .slope import slope +from .smc import smc from .smi import smi from .squeeze import squeeze from .squeeze_pro import squeeze_pro @@ -40,3 +41,4 @@ from .tsi import tsi from .uo import uo from .willr import willr +from .smc import smc diff --git a/pandas_ta/momentum/smc.py b/pandas_ta/momentum/smc.py new file mode 100644 index 00000000..fd33510f --- /dev/null +++ b/pandas_ta/momentum/smc.py @@ -0,0 +1,129 @@ +import pandas as pd +from pandas_ta.utils import get_offset, verify_series + + +# def smc(df, length=None, offset=None, **kwargs): +# """Indicator: Smart Money Indicator""" +# # Validate Arguments +# length = int(length) if length and length > 0 else 14 + +# offset = get_offset(offset) + +# if df is None: +# return + +# # Calculate Result +# df["up"] = df["open"] < df["close"] +# df["down"] = df["open"] > df["close"] +# df["doji"] = df["open"] == df["close"] +# df["body_hi"] = df[["open", "close"]].max(axis=1) +# df["body_lo"] = df[["open", "close"]].min(axis=1) +# df["body"] = df["body_hi"] - df["body_lo"] +# df["body_avg"] = df["body"].rolling(window=length).mean() +# df["has_up_shadow"] = (df["high"] - df["body_hi"]) > 0.05 * df["body"] +# df["has_dn_shadow"] = (df["body_lo"] - df["low"]) > 0.05 * df["body"] +# df["down_trend"] = df["close"] < df["close"].rolling(window=50).mean() + +# # Imbalance calculations +# df["top_imbalance_size"] = df["low"].shift(2) - df["high"] +# df["bottom_imbalance_size"] = df["low"] - df["high"].shift(2) +# day_adr = ( +# df["high"].rolling(window=length).max() - df["low"].rolling(window=length).min() +# ) +# df["top_imbalance_percentage"] = (df["top_imbalance_size"] / day_adr) * 100 +# df["bottom_imbalance_percentage"] = (df["bottom_imbalance_size"] / day_adr) * 100 + +# # Adding flags for significant imbalances +# df["top_imbalance_flag"] = (df["top_imbalance_size"] > 0) & ( +# df["top_imbalance_percentage"] > 1 +# ) +# df["bottom_imbalance_flag"] = (df["bottom_imbalance_size"] > 0) & ( +# df["bottom_imbalance_percentage"] > 1 +# ) + +# # Offset +# if offset != 0: +# df = df.shift(offset) + +# # Handle fills +# if "fillna" in kwargs: +# df.fillna(kwargs["fillna"], inplace=True) +# if "fill_method" in kwargs: +# df.fillna(method=kwargs["fill_method"], inplace=True) + +# # Name and Categorize it +# df.name = f"SMI_{length}" +# df.category = "momentum" + + + +def smc(df): + # Temporarily rename columns for compatibility + # df = df.rename( + # columns={"bid_o": "open", "bid_c": "close", "bid_h": "high", "bid_l": "low"} + # ) + + df["up"] = df["open"] < df["close"] + df["down"] = df["open"] > df["close"] + df["doji"] = df["open"] == df["close"] + df["body_hi"] = df[["open", "close"]].max(axis=1) + df["body_lo"] = df[["open", "close"]].min(axis=1) + df["body"] = df["body_hi"] - df["body_lo"] + df["body_avg"] = df["body"].rolling(window=14).mean() + df["small_body"] = df["body"] < df["body_avg"] + df["long_body"] = df["body"] > df["body_avg"] + df["white_body"] = df["open"] < df["close"] + df["black_body"] = df["open"] > df["close"] + df["up_shadow"] = df["high"] - df["body_hi"] + df["dn_shadow"] = df["body_lo"] - df["low"] + df["has_up_shadow"] = df["up_shadow"] > 0.05 * df["body"] + df["has_dn_shadow"] = df["dn_shadow"] > 0.05 * df["body"] + df["down_trend"] = df["close"] < df["close"].rolling(window=50).mean() + + # Calculate imbalance sizes and percentages based on day average range + df["top_imbalance_size"] = df["low"].shift(2) - df["high"] + df["bottom_imbalance_size"] = df["low"] - df["high"].shift(2) + day_adr = df["high"].rolling(window=14).max() - df["low"].rolling(window=14).min() + df["top_imbalance_percentage"] = (df["top_imbalance_size"] / day_adr) * 100 + df["bottom_imbalance_percentage"] = (df["bottom_imbalance_size"] / day_adr) * 100 + + # Adding flags for significant imbalances + df["top_imbalance_flag"] = (df["top_imbalance_size"] > 0) & ( + df["top_imbalance_percentage"] > 1 + ) + df["bottom_imbalance_flag"] = (df["bottom_imbalance_size"] > 0) & ( + df["bottom_imbalance_percentage"] > 1 + ) + + # Correct way to fill NaN values + df.ffill(inplace=True) # Forward fill + df.bfill(inplace=True) # Backward fill if any NaN at the start + + return df + + +smc.__doc__ = """Smart Money Comcept (SMC) + +The Smart Money concept combines several techniques to identify significant +price movements that might indicate 'smart money' actions. It uses candlestick +patterns, moving averages, and imbalance calculations. + +Sources: + None (custom method) + +Calculation: + Default Inputs: + length=14 + +Args: + df (pd.DataFrame): DataFrame containing 'open', 'high', 'low', 'close' data + length (int): The length of the period for rolling calculations. Default: 14 + offset (int): How many periods to offset the result. Default: 0 + +Kwargs: + fillna (value, optional): pd.DataFrame.fillna(value) + fill_method (value, optional): Type of fill method + +Returns: + pd.DataFrame: Original DataFrame with new columns for the indicator. +""" diff --git a/tests/test_ext_indicator_momentum.py b/tests/test_ext_indicator_momentum.py index fee7d091..b1344fda 100644 --- a/tests/test_ext_indicator_momentum.py +++ b/tests/test_ext_indicator_momentum.py @@ -3,6 +3,7 @@ from unittest import skip, TestCase from pandas import DataFrame +import pandas as pd class TestMomentumExtension(TestCase): @@ -14,9 +15,11 @@ def setUpClass(cls): def tearDownClass(cls): del cls.data - def setUp(self): pass - def tearDown(self): pass + def setUp(self): + pass + def tearDown(self): + pass def test_ao_ext(self): self.data.ta.ao(append=True) @@ -111,12 +114,17 @@ def test_kdj_ext(self): def test_kst_ext(self): self.data.ta.kst(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-2:]), ["KST_10_15_20_30_10_10_10_15", "KSTs_9"]) + self.assertEqual( + list(self.data.columns[-2:]), ["KST_10_15_20_30_10_10_10_15", "KSTs_9"] + ) def test_macd_ext(self): self.data.ta.macd(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ["MACD_12_26_9", "MACDh_12_26_9", "MACDs_12_26_9"]) + self.assertEqual( + list(self.data.columns[-3:]), + ["MACD_12_26_9", "MACDh_12_26_9", "MACDs_12_26_9"], + ) def test_mom_ext(self): self.data.ta.mom(append=True) @@ -131,7 +139,10 @@ def test_pgo_ext(self): def test_ppo_ext(self): self.data.ta.ppo(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ["PPO_12_26_9", "PPOh_12_26_9", "PPOs_12_26_9"]) + self.assertEqual( + list(self.data.columns[-3:]), + ["PPO_12_26_9", "PPOh_12_26_9", "PPOs_12_26_9"], + ) def test_psl_ext(self): self.data.ta.psl(append=True) @@ -141,12 +152,23 @@ def test_psl_ext(self): def test_pvo_ext(self): self.data.ta.pvo(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ["PVO_12_26_9", "PVOh_12_26_9", "PVOs_12_26_9"]) + self.assertEqual( + list(self.data.columns[-3:]), + ["PVO_12_26_9", "PVOh_12_26_9", "PVOs_12_26_9"], + ) def test_qqe_ext(self): self.data.ta.qqe(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-4:]), ["QQE_14_5_4.236", "QQE_14_5_4.236_RSIMA", "QQEl_14_5_4.236", "QQEs_14_5_4.236"]) + self.assertEqual( + list(self.data.columns[-4:]), + [ + "QQE_14_5_4.236", + "QQE_14_5_4.236_RSIMA", + "QQEl_14_5_4.236", + "QQEs_14_5_4.236", + ], + ) def test_roc_ext(self): self.data.ta.roc(append=True) @@ -184,49 +206,73 @@ def test_slope_ext(self): def test_smi_ext(self): self.data.ta.smi(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ["SMI_5_20_5", "SMIs_5_20_5", "SMIo_5_20_5"]) + self.assertEqual( + list(self.data.columns[-3:]), ["SMI_5_20_5", "SMIs_5_20_5", "SMIo_5_20_5"] + ) self.data.ta.smi(scalar=10, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ["SMI_5_20_5_10.0", "SMIs_5_20_5_10.0", "SMIo_5_20_5_10.0"]) + self.assertEqual( + list(self.data.columns[-3:]), + ["SMI_5_20_5_10.0", "SMIs_5_20_5_10.0", "SMIo_5_20_5_10.0"], + ) def test_squeeze_ext(self): self.data.ta.squeeze(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-4:]), ["SQZ_20_2.0_20_1.5", "SQZ_ON", "SQZ_OFF", "SQZ_NO"]) + self.assertEqual( + list(self.data.columns[-4:]), + ["SQZ_20_2.0_20_1.5", "SQZ_ON", "SQZ_OFF", "SQZ_NO"], + ) self.data.ta.squeeze(tr=False, append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-4:]), - ["SQZ_ON", "SQZ_OFF", "SQZ_NO", "SQZhlr_20_2.0_20_1.5"] + self.assertEqual( + list(self.data.columns[-4:]), + ["SQZ_ON", "SQZ_OFF", "SQZ_NO", "SQZhlr_20_2.0_20_1.5"], ) def test_squeeze_pro_ext(self): self.data.ta.squeeze_pro(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-4:]), ["SQZPRO_ON_NORMAL", "SQZPRO_ON_NARROW", "SQZPRO_OFF", "SQZPRO_NO"]) + self.assertEqual( + list(self.data.columns[-4:]), + ["SQZPRO_ON_NORMAL", "SQZPRO_ON_NARROW", "SQZPRO_OFF", "SQZPRO_NO"], + ) self.data.ta.squeeze_pro(tr=False, append=True) self.assertIsInstance(self.data, DataFrame) self.assertEqual( list(self.data.columns[-4:]), - ["SQZPRO_ON_NARROW", "SQZPRO_OFF", "SQZPRO_NO", "SQZPROhlr_20_2.0_20_2_1.5_1"] + [ + "SQZPRO_ON_NARROW", + "SQZPRO_OFF", + "SQZPRO_NO", + "SQZPROhlr_20_2.0_20_2_1.5_1", + ], ) def test_stc_ext(self): self.data.ta.stc(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-3:]), ["STC_10_12_26_0.5", "STCmacd_10_12_26_0.5", "STCstoch_10_12_26_0.5"]) + self.assertEqual( + list(self.data.columns[-3:]), + ["STC_10_12_26_0.5", "STCmacd_10_12_26_0.5", "STCstoch_10_12_26_0.5"], + ) def test_stoch_ext(self): self.data.ta.stoch(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-2:]), ["STOCHk_14_3_3", "STOCHd_14_3_3"]) + self.assertEqual( + list(self.data.columns[-2:]), ["STOCHk_14_3_3", "STOCHd_14_3_3"] + ) def test_stochrsi_ext(self): self.data.ta.stochrsi(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-2:]), ["STOCHRSIk_14_14_3_3", "STOCHRSId_14_14_3_3"]) + self.assertEqual( + list(self.data.columns[-2:]), ["STOCHRSIk_14_14_3_3", "STOCHRSId_14_14_3_3"] + ) @skip def test_td_seq_ext(self): @@ -247,7 +293,9 @@ def test_trix_ext(self): def test_tsi_ext(self): self.data.ta.tsi(append=True) self.assertIsInstance(self.data, DataFrame) - self.assertEqual(list(self.data.columns[-2:]), ["TSI_13_25_13", "TSIs_13_25_13"]) + self.assertEqual( + list(self.data.columns[-2:]), ["TSI_13_25_13", "TSIs_13_25_13"] + ) def test_uo_ext(self): self.data.ta.uo(append=True) @@ -258,3 +306,50 @@ def test_willr_ext(self): self.data.ta.willr(append=True) self.assertIsInstance(self.data, DataFrame) self.assertEqual(self.data.columns[-1], "WILLR_14") + + def test_smc(self): + result = pandas_ta.momentum.smc(self.data.copy()) + # Filter out the columns specific to the SMC results + result_filtered = result[ + [ + "up", + "down", + "doji", + "body_hi", + "body_lo", + "body", + "body_avg", + "has_up_shadow", + "has_dn_shadow", + "down_trend", + "top_imbalance_size", + "bottom_imbalance_size", + "top_imbalance_percentage", + "bottom_imbalance_percentage", + "top_imbalance_flag", + "bottom_imbalance_flag", + ] + ] + expected_columns = [ + "up", + "down", + "doji", + "body_hi", + "body_lo", + "body", + "body_avg", + "has_up_shadow", + "has_dn_shadow", + "down_trend", + "top_imbalance_size", + "bottom_imbalance_size", + "top_imbalance_percentage", + "bottom_imbalance_percentage", + "top_imbalance_flag", + "bottom_imbalance_flag", + ] + self.assertEqual( + sorted(list(result_filtered.columns)), sorted(expected_columns) + ) + self.assertTrue(result_filtered.notnull().all().all()) + self.assertTrue(result.notnull().all().all()) diff --git a/tests/test_indicator_momentum.py b/tests/test_indicator_momentum.py index a11e7872..3ea0d485 100644 --- a/tests/test_indicator_momentum.py +++ b/tests/test_indicator_momentum.py @@ -1,9 +1,16 @@ -from .config import error_analysis, sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE +from .config import ( + error_analysis, + sample_data, + CORRELATION, + CORRELATION_THRESHOLD, + VERBOSE, +) from .context import pandas_ta from unittest import TestCase, skip import pandas.testing as pdt from pandas import DataFrame, Series +import pandas as pd import talib as tal @@ -30,9 +37,11 @@ def tearDownClass(cls): del cls.volume del cls.data - def setUp(self): pass - def tearDown(self): pass + def setUp(self): + pass + def tearDown(self): + pass def test_datetime_ordered(self): # Test if datetime64 index and ordered @@ -74,7 +83,9 @@ def test_apo(self): pdt.assert_series_equal(result, expected, check_names=False) except AssertionError: try: - corr = pandas_ta.utils.df_error_analysis(result, expected, col=CORRELATION) + corr = pandas_ta.utils.df_error_analysis( + result, expected, col=CORRELATION + ) self.assertGreater(corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result, CORRELATION, ex) @@ -98,7 +109,9 @@ def test_bop(self): pdt.assert_series_equal(result, expected, check_names=False) except AssertionError: try: - corr = pandas_ta.utils.df_error_analysis(result, expected, col=CORRELATION) + corr = pandas_ta.utils.df_error_analysis( + result, expected, col=CORRELATION + ) self.assertGreater(corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result, CORRELATION, ex) @@ -122,7 +135,9 @@ def test_cci(self): pdt.assert_series_equal(result, expected, check_names=False) except AssertionError: try: - corr = pandas_ta.utils.df_error_analysis(result, expected, col=CORRELATION) + corr = pandas_ta.utils.df_error_analysis( + result, expected, col=CORRELATION + ) self.assertGreater(corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result, CORRELATION, ex) @@ -151,7 +166,9 @@ def test_cmo(self): pdt.assert_series_equal(result, expected, check_names=False) except AssertionError: try: - corr = pandas_ta.utils.df_error_analysis(result, expected, col=CORRELATION) + corr = pandas_ta.utils.df_error_analysis( + result, expected, col=CORRELATION + ) self.assertGreater(corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result, CORRELATION, ex) @@ -187,13 +204,17 @@ def test_dm(self): pdt.assert_frame_equal(result, expecteddf) except AssertionError: try: - dmp = pandas_ta.utils.df_error_analysis(result.iloc[:,0], expecteddf.iloc[:,0], col=CORRELATION) + dmp = pandas_ta.utils.df_error_analysis( + result.iloc[:, 0], expecteddf.iloc[:, 0], col=CORRELATION + ) self.assertGreater(dmp, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result, CORRELATION, ex) try: - dmn = pandas_ta.utils.df_error_analysis(result.iloc[:,1], expecteddf.iloc[:,1], col=CORRELATION) + dmn = pandas_ta.utils.df_error_analysis( + result.iloc[:, 1], expecteddf.iloc[:, 1], col=CORRELATION + ) self.assertGreater(dmn, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result, CORRELATION, ex) @@ -242,23 +263,35 @@ def test_macd(self): try: expected = tal.MACD(self.close) - expecteddf = DataFrame({"MACD_12_26_9": expected[0], "MACDh_12_26_9": expected[2], "MACDs_12_26_9": expected[1]}) + expecteddf = DataFrame( + { + "MACD_12_26_9": expected[0], + "MACDh_12_26_9": expected[2], + "MACDs_12_26_9": expected[1], + } + ) pdt.assert_frame_equal(result, expecteddf) except AssertionError: try: - macd_corr = pandas_ta.utils.df_error_analysis(result.iloc[:, 0], expecteddf.iloc[:, 0], col=CORRELATION) + macd_corr = pandas_ta.utils.df_error_analysis( + result.iloc[:, 0], expecteddf.iloc[:, 0], col=CORRELATION + ) self.assertGreater(macd_corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result.iloc[:, 0], CORRELATION, ex) try: - history_corr = pandas_ta.utils.df_error_analysis(result.iloc[:, 1], expecteddf.iloc[:, 1], col=CORRELATION) + history_corr = pandas_ta.utils.df_error_analysis( + result.iloc[:, 1], expecteddf.iloc[:, 1], col=CORRELATION + ) self.assertGreater(history_corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result.iloc[:, 1], CORRELATION, ex, newline=False) try: - signal_corr = pandas_ta.utils.df_error_analysis(result.iloc[:, 2], expecteddf.iloc[:, 2], col=CORRELATION) + signal_corr = pandas_ta.utils.df_error_analysis( + result.iloc[:, 2], expecteddf.iloc[:, 2], col=CORRELATION + ) self.assertGreater(signal_corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result.iloc[:, 2], CORRELATION, ex, newline=False) @@ -282,7 +315,9 @@ def test_mom(self): pdt.assert_series_equal(result, expected, check_names=False) except AssertionError: try: - corr = pandas_ta.utils.df_error_analysis(result, expected, col=CORRELATION) + corr = pandas_ta.utils.df_error_analysis( + result, expected, col=CORRELATION + ) self.assertGreater(corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result, CORRELATION, ex) @@ -306,7 +341,9 @@ def test_ppo(self): pdt.assert_series_equal(result["PPO_12_26_9"], expected, check_names=False) except AssertionError: try: - corr = pandas_ta.utils.df_error_analysis(result["PPO_12_26_9"], expected, col=CORRELATION) + corr = pandas_ta.utils.df_error_analysis( + result["PPO_12_26_9"], expected, col=CORRELATION + ) self.assertGreater(corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result["PPO_12_26_9"], CORRELATION, ex) @@ -340,7 +377,9 @@ def test_roc(self): pdt.assert_series_equal(result, expected, check_names=False) except AssertionError: try: - corr = pandas_ta.utils.df_error_analysis(result, expected, col=CORRELATION) + corr = pandas_ta.utils.df_error_analysis( + result, expected, col=CORRELATION + ) self.assertGreater(corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result, CORRELATION, ex) @@ -359,7 +398,9 @@ def test_rsi(self): pdt.assert_series_equal(result, expected, check_names=False) except AssertionError: try: - corr = pandas_ta.utils.df_error_analysis(result, expected, col=CORRELATION) + corr = pandas_ta.utils.df_error_analysis( + result, expected, col=CORRELATION + ) self.assertGreater(corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result, CORRELATION, ex) @@ -393,6 +434,11 @@ def test_slope_as_angle_to_degrees(self): self.assertIsInstance(result, Series) self.assertEqual(result.name, "ANGLEd_1") + def test_smc(self): + result = pandas_ta.smc(self.data) + self.assertIsInstance(result, DataFrame) + self.assertTrue(result.notnull().all().all()) + def test_smi(self): result = pandas_ta.smi(self.close) self.assertIsInstance(result, DataFrame) @@ -418,7 +464,9 @@ def test_squeeze(self): self.assertIsInstance(result, DataFrame) self.assertEqual(result.name, "SQZ_20_2.0_20_1.5_LB") - result = pandas_ta.squeeze(self.high, self.low, self.close, tr=False, lazybear=True) + result = pandas_ta.squeeze( + self.high, self.low, self.close, tr=False, lazybear=True + ) self.assertIsInstance(result, DataFrame) self.assertEqual(result.name, "SQZhlr_20_2.0_20_1.5_LB") @@ -431,11 +479,15 @@ def test_squeeze_pro(self): self.assertIsInstance(result, DataFrame) self.assertEqual(result.name, "SQZPROhlr_20_2.0_20_2_1.5_1") - result = pandas_ta.squeeze_pro(self.high, self.low, self.close, 20, 2, 20, 3, 2, 1) + result = pandas_ta.squeeze_pro( + self.high, self.low, self.close, 20, 2, 20, 3, 2, 1 + ) self.assertIsInstance(result, DataFrame) self.assertEqual(result.name, "SQZPRO_20_2.0_20_3.0_2.0_1.0") - result = pandas_ta.squeeze_pro(self.high, self.low, self.close, 20, 2, 20, 3, 2, 1, tr=False) + result = pandas_ta.squeeze_pro( + self.high, self.low, self.close, 20, 2, 20, 3, 2, 1, tr=False + ) self.assertIsInstance(result, DataFrame) self.assertEqual(result.name, "SQZPROhlr_20_2.0_20_3.0_2.0_1.0") @@ -452,17 +504,23 @@ def test_stoch(self): try: expected = tal.STOCH(self.high, self.low, self.close, 14, 3, 0, 3, 0) - expecteddf = DataFrame({"STOCHk_14_3_0_3_0": expected[0], "STOCHd_14_3_0_3": expected[1]}) + expecteddf = DataFrame( + {"STOCHk_14_3_0_3_0": expected[0], "STOCHd_14_3_0_3": expected[1]} + ) pdt.assert_frame_equal(result, expecteddf) except AssertionError: try: - stochk_corr = pandas_ta.utils.df_error_analysis(result.iloc[:, 0], expecteddf.iloc[:, 0], col=CORRELATION) + stochk_corr = pandas_ta.utils.df_error_analysis( + result.iloc[:, 0], expecteddf.iloc[:, 0], col=CORRELATION + ) self.assertGreater(stochk_corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result.iloc[:, 0], CORRELATION, ex) try: - stochd_corr = pandas_ta.utils.df_error_analysis(result.iloc[:, 1], expecteddf.iloc[:, 1], col=CORRELATION) + stochd_corr = pandas_ta.utils.df_error_analysis( + result.iloc[:, 1], expecteddf.iloc[:, 1], col=CORRELATION + ) self.assertGreater(stochd_corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result.iloc[:, 1], CORRELATION, ex, newline=False) @@ -475,11 +533,15 @@ def test_stochrsi(self): try: expected = tal.STOCHRSI(self.close, 14, 14, 3, 0) - expecteddf = DataFrame({"STOCHRSIk_14_14_0_3": expected[0], "STOCHRSId_14_14_3_0": expected[1]}) + expecteddf = DataFrame( + {"STOCHRSIk_14_14_0_3": expected[0], "STOCHRSId_14_14_3_0": expected[1]} + ) pdt.assert_frame_equal(result, expecteddf) except AssertionError: try: - stochrsid_corr = pandas_ta.utils.df_error_analysis(result.iloc[:, 0], expecteddf.iloc[:, 1], col=CORRELATION) + stochrsid_corr = pandas_ta.utils.df_error_analysis( + result.iloc[:, 0], expecteddf.iloc[:, 1], col=CORRELATION + ) self.assertGreater(stochrsid_corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result.iloc[:, 0], CORRELATION, ex, newline=False) @@ -511,7 +573,9 @@ def test_uo(self): pdt.assert_series_equal(result, expected, check_names=False) except AssertionError: try: - corr = pandas_ta.utils.df_error_analysis(result, expected, col=CORRELATION) + corr = pandas_ta.utils.df_error_analysis( + result, expected, col=CORRELATION + ) self.assertGreater(corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result, CORRELATION, ex) @@ -530,7 +594,9 @@ def test_willr(self): pdt.assert_series_equal(result, expected, check_names=False) except AssertionError: try: - corr = pandas_ta.utils.df_error_analysis(result, expected, col=CORRELATION) + corr = pandas_ta.utils.df_error_analysis( + result, expected, col=CORRELATION + ) self.assertGreater(corr, CORRELATION_THRESHOLD) except Exception as ex: error_analysis(result, CORRELATION, ex)