def accbands(high, low, close, length=None, c=None, drift=None, mamode=None, offset=None, **kwargs): """Indicator: Acceleration Bands (ACCBANDS)""" # Validate arguments high = verify_series(high) low = verify_series(low) close = verify_series(close) high_low_range = non_zero_range(high, low) length = int(length) if length and length > 0 else 20 c = float(c) if c and c > 0 else 4 mamode = mamode if isinstance(mamode, str) else "sma" drift = get_drift(drift) offset = get_offset(offset) # Calculate Result hl_ratio = high_low_range / (high + low) hl_ratio *= c _lower = low * (1 - hl_ratio) _upper = high * (1 + hl_ratio) lower = ma(mamode, _lower, length=length) mid = ma(mamode, close, length=length) upper = ma(mamode, _upper, length=length) # Offset if offset != 0: lower = lower.shift(offset) mid = mid.shift(offset) upper = upper.shift(offset) # Handle fills if "fillna" in kwargs: lower.fillna(kwargs["fillna"], inplace=True) mid.fillna(kwargs["fillna"], inplace=True) upper.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: lower.fillna(method=kwargs["fill_method"], inplace=True) mid.fillna(method=kwargs["fill_method"], inplace=True) upper.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it lower.name = f"ACCBL_{length}" mid.name = f"ACCBM_{length}" upper.name = f"ACCBU_{length}" mid.category = upper.category = lower.category = "volatility" # Prepare DataFrame to return data = {lower.name: lower, mid.name: mid, upper.name: upper} accbandsdf = DataFrame(data) accbandsdf.name = f"ACCBANDS_{length}" accbandsdf.category = mid.category return accbandsdf
def kvo(high, low, close, volume, fast=None, slow=None, signal=None, mamode=None, drift=None, offset=None, **kwargs): """Indicator: Klinger Volume Oscillator (KVO)""" # Validate arguments fast = int(fast) if fast and fast > 0 else 34 slow = int(slow) if slow and slow > 0 else 55 signal = int(signal) if signal and signal > 0 else 13 mamode = mamode.lower() if mamode and isinstance(mamode, str) else "ema" _length = max(fast, slow, signal) high = verify_series(high, _length) low = verify_series(low, _length) close = verify_series(close, _length) volume = verify_series(volume, _length) drift = get_drift(drift) offset = get_offset(offset) if high is None or low is None or close is None or volume is None: return # Calculate Result signed_volume = volume * signed_series(hlc3(high, low, close), 1) sv = signed_volume.loc[signed_volume.first_valid_index():, ] kvo = ma(mamode, sv, length=fast) - ma(mamode, sv, length=slow) kvo_signal = ma(mamode, kvo.loc[kvo.first_valid_index():, ], length=signal) # Offset if offset != 0: kvo = kvo.shift(offset) kvo_signal = kvo_signal.shift(offset) # Handle fills if "fillna" in kwargs: kvo.fillna(kwargs["fillna"], inplace=True) kvo_signal.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: kvo.fillna(method=kwargs["fill_method"], inplace=True) kvo_signal.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it _props = f"_{fast}_{slow}_{signal}" kvo.name = f"KVO{_props}" kvo_signal.name = f"KVOs{_props}" kvo.category = kvo_signal.category = "volume" # Prepare DataFrame to return data = {kvo.name: kvo, kvo_signal.name: kvo_signal} df = DataFrame(data) df.name = f"KVO{_props}" df.category = kvo.category return df
def dm(high, low, length=None, mamode=None, talib=None, drift=None, offset=None, **kwargs): """Indicator: DM""" # Validate Arguments length = int(length) if length and length > 0 else 14 mamode = mamode.lower() if mamode and isinstance(mamode, str) else "rma" high = verify_series(high) low = verify_series(low) drift = get_drift(drift) offset = get_offset(offset) mode_tal = bool(talib) if isinstance(talib, bool) else True if high is None or low is None: return if Imports["talib"] and mode_tal: from talib import MINUS_DM, PLUS_DM pos = PLUS_DM(high, low, length) neg = MINUS_DM(high, low, length) else: up = high - high.shift(drift) dn = low.shift(drift) - low pos_ = ((up > dn) & (up > 0)) * up neg_ = ((dn > up) & (dn > 0)) * dn pos_ = pos_.apply(zero) neg_ = neg_.apply(zero) # Not the same values as TA Lib's -+DM (Good First Issue) pos = ma(mamode, pos_, length=length) neg = ma(mamode, neg_, length=length) # Offset if offset != 0: pos = pos.shift(offset) neg = neg.shift(offset) _params = f"_{length}" data = { f"DMP{_params}": pos, f"DMN{_params}": neg, } dmdf = DataFrame(data) # print(dmdf.head(20)) # print() dmdf.name = f"DM{_params}" dmdf.category = "trend" return dmdf
def kvo(high, low, close, volume, fast=None, slow=None, length_sig=None, mamode=None, drift=None, offset=None, **kwargs): """Indicator: Klinger Volume Oscillator (KVO)""" # Validate arguments fast = int(fast) if fast and fast > 0 else 34 slow = int(slow) if slow and slow > 0 else 55 length_sig = int(length_sig) if length_sig and length_sig > 0 else 13 mamode = mamode.lower() if mamode and isinstance(mamode, str) else "ema" _length = max(fast, slow, length_sig) high = verify_series(high, _length) low = verify_series(low, _length) close = verify_series(close, _length) volume = verify_series(volume, _length) drift = get_drift(drift) offset = get_offset(offset) if high is None or low is None or close is None or volume is None: return # Calculate Result mom = hlc3(high, low, close).diff(drift) trend = npWhere(mom > 0, 1, 0) + npWhere(mom < 0, -1, 0) dm = non_zero_range(high, low) m = high.size cm = [0] * m for i in range(1, m): cm[i] = (cm[i - 1] + dm[i]) if trend[i] == trend[i - 1] else (dm[i - 1] + dm[i]) vf = 100 * volume * trend * abs(2 * dm / cm - 1) kvo = ma(mamode, vf, length=fast) - ma(mamode, vf, length=slow) kvo_signal = ma(mamode, kvo, length=length_sig) # Offset if offset != 0: kvo = kvo.shift(offset) kvo_signal = kvo_signal.shift(offset) # Handle fills if "fillna" in kwargs: kvo.fillna(kwargs["fillna"], inplace=True) kvo_signal.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: kvo.fillna(method=kwargs["fill_method"], inplace=True) kvo_signal.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it kvo.name = f"KVO_{fast}_{slow}" kvo_signal.name = f"KVOSig_{length_sig}" kvo.category = kvo_signal.category = "volume" # Prepare DataFrame to return data = {kvo.name: kvo, kvo_signal.name: kvo_signal} kvoandsig = DataFrame(data) kvoandsig.name = f"KVO_{fast}_{slow}_{length_sig}" kvoandsig.category = kvo.category return kvoandsig
def kc(high, low, close, length=None, scalar=None, mamode=None, offset=None, **kwargs): """Indicator: Keltner Channels (KC)""" # Validate arguments length = int(length) if length and length > 0 else 20 scalar = float(scalar) if scalar and scalar > 0 else 2 mamode = mamode if isinstance(mamode, str) else "ema" high = verify_series(high, length) low = verify_series(low, length) close = verify_series(close, length) offset = get_offset(offset) if high is None or low is None or close is None: return # Calculate Result use_tr = kwargs.pop("tr", True) if use_tr: range_ = true_range(high, low, close) else: range_ = high_low_range(high, low) basis = ma(mamode, close, length=length) band = ma(mamode, range_, length=length) lower = basis - scalar * band upper = basis + scalar * band # Offset if offset != 0: lower = lower.shift(offset) basis = basis.shift(offset) upper = upper.shift(offset) # Handle fills if "fillna" in kwargs: lower.fillna(kwargs["fillna"], inplace=True) basis.fillna(kwargs["fillna"], inplace=True) upper.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: lower.fillna(method=kwargs["fill_method"], inplace=True) basis.fillna(method=kwargs["fill_method"], inplace=True) upper.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it _props = f"{mamode.lower()[0] if len(mamode) else ''}_{length}_{scalar}" lower.name = f"KCL{_props}" basis.name = f"KCB{_props}" upper.name = f"KCU{_props}" basis.category = upper.category = lower.category = "volatility" # Prepare DataFrame to return data = {lower.name: lower, basis.name: basis, upper.name: upper} kcdf = DataFrame(data) kcdf.name = f"KC{_props}" kcdf.category = basis.category return kcdf
def amat(close=None, fast=None, slow=None, lookback=None, mamode=None, offset=None, **kwargs): """Indicator: Archer Moving Averages Trends (AMAT)""" # Validate Arguments fast = int(fast) if fast and fast > 0 else 8 slow = int(slow) if slow and slow > 0 else 21 lookback = int(lookback) if lookback and lookback > 0 else 2 mamode = mamode.lower() if isinstance(mamode, str) else "ema" close = verify_series(close, max(fast, slow, lookback)) offset = get_offset(offset) if "length" in kwargs: kwargs.pop("length") if close is None: return # # Calculate Result fast_ma = ma(mamode, close, length=fast, **kwargs) slow_ma = ma(mamode, close, length=slow, **kwargs) mas_long = long_run(fast_ma, slow_ma, length=lookback) mas_short = short_run(fast_ma, slow_ma, length=lookback) # Offset if offset != 0: mas_long = mas_long.shift(offset) mas_short = mas_short.shift(offset) # # Handle fills if "fillna" in kwargs: mas_long.fillna(kwargs["fillna"], inplace=True) mas_short.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: mas_long.fillna(method=kwargs["fill_method"], inplace=True) mas_short.fillna(method=kwargs["fill_method"], inplace=True) # Prepare DataFrame to return amatdf = DataFrame({ f"AMAT{mamode[0]}_LR_{fast}_{slow}_{lookback}": mas_long, f"AMAT{mamode[0]}_SR_{fast}_{slow}_{lookback}": mas_short }) # Name and Categorize it amatdf.name = f"AMAT{mamode[0]}_{fast}_{slow}_{lookback}" amatdf.category = "trend" return amatdf
def stochrsi(close, length=None, rsi_length=None, k=None, d=None, mamode=None, offset=None, **kwargs): """Indicator: Stochastic RSI Oscillator (STOCHRSI)""" # Validate arguments length = length if length and length > 0 else 14 rsi_length = rsi_length if rsi_length and rsi_length > 0 else 14 k = k if k and k > 0 else 3 d = d if d and d > 0 else 3 close = verify_series(close, max(length, rsi_length, k, d)) offset = get_offset(offset) mamode = mamode if isinstance(mamode, str) else "sma" if close is None: return # Calculate Result rsi_ = rsi(close, length=rsi_length) lowest_rsi = rsi_.rolling(length).min() highest_rsi = rsi_.rolling(length).max() stoch = 100 * (rsi_ - lowest_rsi) stoch /= non_zero_range(highest_rsi, lowest_rsi) stochrsi_k = ma(mamode, stoch, length=k) stochrsi_d = ma(mamode, stochrsi_k, length=d) # Offset if offset != 0: stochrsi_k = stochrsi_k.shift(offset) stochrsi_d = stochrsi_d.shift(offset) # Handle fills if "fillna" in kwargs: stochrsi_k.fillna(kwargs["fillna"], inplace=True) stochrsi_d.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: stochrsi_k.fillna(method=kwargs["fill_method"], inplace=True) stochrsi_d.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it _name = "STOCHRSI" _props = f"_{length}_{rsi_length}_{k}_{d}" stochrsi_k.name = f"{_name}k{_props}" stochrsi_d.name = f"{_name}d{_props}" stochrsi_k.category = stochrsi_d.category = "momentum" # Prepare DataFrame to return data = {stochrsi_k.name: stochrsi_k, stochrsi_d.name: stochrsi_d} df = DataFrame(data) df.name = f"{_name}{_props}" df.category = stochrsi_k.category return df
def stoch(high, low, close, k=None, d=None, smooth_k=None, mamode=None, offset=None, **kwargs): """Indicator: Stochastic Oscillator (STOCH)""" # Validate arguments k = k if k and k > 0 else 14 d = d if d and d > 0 else 3 smooth_k = smooth_k if smooth_k and smooth_k > 0 else 3 _length = max(k, d, smooth_k) high = verify_series(high, _length) low = verify_series(low, _length) close = verify_series(close, _length) offset = get_offset(offset) mamode = mamode if isinstance(mamode, str) else "sma" if high is None or low is None or close is None: return # Calculate Result lowest_low = low.rolling(k).min() highest_high = high.rolling(k).max() stoch = 100 * (close - lowest_low) stoch /= non_zero_range(highest_high, lowest_low) stoch_k = ma(mamode, stoch.loc[stoch.first_valid_index():,], length=smooth_k) stoch_d = ma(mamode, stoch_k.loc[stoch_k.first_valid_index():,], length=d) # Offset if offset != 0: stoch_k = stoch_k.shift(offset) stoch_d = stoch_d.shift(offset) # Handle fills if "fillna" in kwargs: stoch_k.fillna(kwargs["fillna"], inplace=True) stoch_d.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: stoch_k.fillna(method=kwargs["fill_method"], inplace=True) stoch_d.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it _name = "STOCH" _props = f"_{k}_{d}_{smooth_k}" stoch_k.name = f"{_name}k{_props}" stoch_d.name = f"{_name}d{_props}" stoch_k.category = stoch_d.category = "momentum" # Prepare DataFrame to return data = {stoch_k.name: stoch_k, stoch_d.name: stoch_d} df = DataFrame(data) df.name = f"{_name}{_props}" df.category = stoch_k.category return df
def _rvi(source, length, scalar, mode, drift): """RVI""" std = stdev(source, length) pos, neg = unsigned_differences(source, amount=drift) pos_std = pos * std neg_std = neg * std pos_avg = ma(mode, pos_std, length=length) neg_avg = ma(mode, neg_std, length=length) result = scalar * pos_avg result /= pos_avg + neg_avg return result
def bias(close, length=None, mamode=None, offset=None, **kwargs): """Indicator: Bias (BIAS)""" # Validate Arguments close = verify_series(close) length = int(length) if length and length > 0 else 26 mamode = mamode if isinstance(mamode, str) else "sma" offset = get_offset(offset) # Calculate Result bma = ma(mamode, close, length=length, **kwargs) bias = (close / bma) - 1 # Offset if offset != 0: bias = bias.shift(offset) # Handle fills if "fillna" in kwargs: bias.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: bias.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it bias.name = f"BIAS_{bma.name}" bias.category = "momentum" return bias
def efi(close, volume, length=None, drift=None, mamode=None, offset=None, **kwargs): """Indicator: Elder's Force Index (EFI)""" # Validate arguments close = verify_series(close) volume = verify_series(volume) length = int(length) if length and length > 0 else 13 drift = get_drift(drift) mamode = mamode if isinstance(mamode, str) else "ema" offset = get_offset(offset) # Calculate Result pv_diff = close.diff(drift) * volume efi = ma(mamode, pv_diff, length=length) # Offset if offset != 0: efi = efi.shift(offset) # Handle fills if "fillna" in kwargs: efi.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: efi.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it efi.name = f"EFI_{length}" efi.category = "volume" return efi
def bbands(close, length=None, std=None, mamode=None, offset=None, **kwargs): """Indicator: Bollinger Bands (BBANDS)""" # Validate arguments close = verify_series(close) length = int(length) if length and length > 0 else 5 std = float(std) if std and std > 0 else 2.0 mamode = mamode if isinstance(mamode, str) else "sma" offset = get_offset(offset) # Calculate Result standard_deviation = stdev(close=close, length=length) deviations = std * standard_deviation mid = ma(mamode, close, length=length, **kwargs) lower = mid - deviations upper = mid + deviations bandwidth = 100 * (upper - lower) / mid # Offset if offset != 0: lower = lower.shift(offset) mid = mid.shift(offset) upper = upper.shift(offset) bandwidth = bandwidth.shift(offset) # Handle fills if "fillna" in kwargs: lower.fillna(kwargs["fillna"], inplace=True) mid.fillna(kwargs["fillna"], inplace=True) upper.fillna(kwargs["fillna"], inplace=True) bandwidth.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: lower.fillna(method=kwargs["fill_method"], inplace=True) mid.fillna(method=kwargs["fill_method"], inplace=True) upper.fillna(method=kwargs["fill_method"], inplace=True) bandwidth.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it lower.name = f"BBL_{length}_{std}" mid.name = f"BBM_{length}_{std}" upper.name = f"BBU_{length}_{std}" bandwidth.name = f"BBB_{length}_{std}" upper.category = lower.category = "volatility" mid.category = bandwidth.category = upper.category # Prepare DataFrame to return data = { lower.name: lower, mid.name: mid, upper.name: upper, bandwidth.name: bandwidth } bbandsdf = DataFrame(data) bbandsdf.name = f"BBANDS_{length}_{std}" bbandsdf.category = mid.category return bbandsdf
def apo(close, fast=None, slow=None, mamode=None, talib=None, offset=None, **kwargs): """Indicator: Absolute Price Oscillator (APO)""" # Validate Arguments fast = int(fast) if fast and fast > 0 else 12 slow = int(slow) if slow and slow > 0 else 26 if slow < fast: fast, slow = slow, fast close = verify_series(close, max(fast, slow)) mamode = mamode if isinstance(mamode, str) else "sma" offset = get_offset(offset) mode_tal = bool(talib) if isinstance(talib, bool) else True if close is None: return # Calculate Result if Imports["talib"] and mode_tal: from talib import APO apo = APO(close, fast, slow, tal_ma(mamode)) else: fastma = ma(mamode, close, length=fast) slowma = ma(mamode, close, length=slow) apo = fastma - slowma # Offset if offset != 0: apo = apo.shift(offset) # Handle fills if "fillna" in kwargs: apo.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: apo.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it apo.name = f"APO_{fast}_{slow}" apo.category = "momentum" return apo
def atr(high, low, close, length=None, mamode=None, talib=None, drift=None, offset=None, **kwargs): """Indicator: Average True Range (ATR)""" # Validate arguments length = int(length) if length and length > 0 else 14 mamode = mamode.lower() if mamode and isinstance(mamode, str) else "rma" high = verify_series(high, length) low = verify_series(low, length) close = verify_series(close, length) drift = get_drift(drift) offset = get_offset(offset) mode_tal = bool(talib) if isinstance(talib, bool) else True if high is None or low is None or close is None: return # Calculate Result if Imports["talib"] and mode_tal: from talib import ATR atr = ATR(high, low, close, length) else: tr = true_range(high=high, low=low, close=close, drift=drift) atr = ma(mamode, tr, length=length) percentage = kwargs.pop("percent", False) if percentage: atr *= 100 / close # Offset if offset != 0: atr = atr.shift(offset) # Handle fills if "fillna" in kwargs: atr.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: atr.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it atr.name = f"ATR{mamode[0]}_{length}{'p' if percentage else ''}" atr.category = "volatility" return atr
def thermo(high, low, length=None, long=None, short=None, mamode=None, drift=None, offset=None, **kwargs): """Indicator: Elders Thermometer (THERMO)""" # Validate arguments high = verify_series(high) low = verify_series(low) length = int(length) if length and length > 0 else 20 long = float(long) if long and long > 0 else 2 short = float(short) if short and short > 0 else 0.5 mamode = mamode if isinstance(mamode, str) else "ema" drift = get_drift(drift) offset = get_offset(offset) asint = kwargs.pop("asint", True) # Calculate Result thermoL = (low.shift(drift) - low).abs() thermoH = (high - high.shift(drift)).abs() thermo = thermoL thermo = thermo.where(thermoH < thermoL, thermoH) thermo.index = high.index thermo_ma = ma(mamode, thermo, length=length) # Create signals thermo_long = thermo < (thermo_ma * long) thermo_short = thermo > (thermo_ma * short) # Binary output, useful for signals if asint: thermo_long = thermo_long.astype(int) thermo_short = thermo_short.astype(int) # Offset if offset != 0: thermo = thermo.shift(offset) thermo_ma = thermo_ma.shift(offset) therthermo_longmo_ma = thermo_ma.shift(offset) thermo_short = thermo_ma.shift(offset) # Handle fills if "fillna" in kwargs: thermo.fillna(kwargs["fillna"], inplace=True) thermo_ma.fillna(kwargs["fillna"], inplace=True) thermo_long.fillna(kwargs["fillna"], inplace=True) thermo_short.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: thermo.fillna(method=kwargs["fill_method"], inplace=True) thermo_ma.fillna(method=kwargs["fill_method"], inplace=True) thermo_long.fillna(method=kwargs["fill_method"], inplace=True) thermo_short.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it _props = f"_{length}_{long}_{short}" thermo.name = f"THERMO{_props}" thermo_ma.name = f"THERMOma{_props}" thermo_long.name = f"THERMOl{_props}" thermo_short.name = f"THERMOs{_props}" thermo.category = thermo_ma.category = thermo_long.category = thermo_short.category = "volatility" # Prepare Dataframe to return data = { thermo.name: thermo, thermo_ma.name: thermo_ma, thermo_long.name: thermo_long, thermo_short.name: thermo_short } df = DataFrame(data) df.name = f"THERMO{_props}" df.category = thermo.category return df
def bbands(close, length=None, std=None, mamode=None, ddof=0, offset=None, **kwargs): """Indicator: Bollinger Bands (BBANDS)""" # Validate arguments length = int(length) if length and length > 0 else 5 std = float(std) if std and std > 0 else 2.0 mamode = mamode if isinstance(mamode, str) else "sma" ddof = int(ddof) if ddof >= 0 and ddof < length else 1 close = verify_series(close, length) offset = get_offset(offset) if close is None: return # Calculate Result if Imports["talib"]: from talib import BBANDS upper, mid, lower = BBANDS(close, length) else: standard_deviation = stdev(close=close, length=length, ddof=ddof) deviations = std * standard_deviation # deviations = std * standard_deviation.loc[standard_deviation.first_valid_index():,] mid = ma(mamode, close, length=length, **kwargs) lower = mid - deviations upper = mid + deviations ulr = non_zero_range(upper, lower) bandwidth = 100 * ulr / mid percent = non_zero_range(close, lower) / ulr # Offset if offset != 0: lower = lower.shift(offset) mid = mid.shift(offset) upper = upper.shift(offset) bandwidth = bandwidth.shift(offset) percent = bandwidth.shift(offset) # Handle fills if "fillna" in kwargs: lower.fillna(kwargs["fillna"], inplace=True) mid.fillna(kwargs["fillna"], inplace=True) upper.fillna(kwargs["fillna"], inplace=True) bandwidth.fillna(kwargs["fillna"], inplace=True) percent.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: lower.fillna(method=kwargs["fill_method"], inplace=True) mid.fillna(method=kwargs["fill_method"], inplace=True) upper.fillna(method=kwargs["fill_method"], inplace=True) bandwidth.fillna(method=kwargs["fill_method"], inplace=True) percent.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it lower.name = f"BBL_{length}_{std}" mid.name = f"BBM_{length}_{std}" upper.name = f"BBU_{length}_{std}" bandwidth.name = f"BBB_{length}_{std}" percent.name = f"BBP_{length}_{std}" upper.category = lower.category = "volatility" mid.category = bandwidth.category = upper.category # Prepare DataFrame to return data = { lower.name: lower, mid.name: mid, upper.name: upper, bandwidth.name: bandwidth, percent.name: percent } bbandsdf = DataFrame(data) bbandsdf.name = f"BBANDS_{length}_{std}" bbandsdf.category = mid.category return bbandsdf
def qqe(close, length=None, smooth=None, factor=None, mamode=None, drift=None, offset=None, **kwargs): """Indicator: Quantitative Qualitative Estimation (QQE)""" # Validate arguments close = verify_series(close) length = int(length) if length and length > 0 else 14 smooth = int(smooth) if smooth and smooth > 0 else 5 factor = float(factor) if factor else 4.236 mamode = mamode if isinstance(mamode, str) else "ema" drift = get_drift(drift) offset = get_offset(offset) # Calculate Result rsi_ = rsi(close, length) _mode = mamode.lower()[0] if mamode != "ema" else "" rsi_ma = ma(mamode, rsi_, length=smooth) # RSI MA True Range rsi_ma_tr = rsi_ma.diff(drift).abs() # Double Smooth the RSI MA True Range using Wilder's Length with a default # width of 4.236. wilders_length = 2 * length - 1 smoothed_rsi_tr_ma = ma("ema", rsi_ma_tr, length=wilders_length) dar = factor * ma("ema", smoothed_rsi_tr_ma, length=wilders_length) # Create the Upper and Lower Bands around RSI MA. upperband = rsi_ma + dar lowerband = rsi_ma - dar m = close.size long = Series(0, index=close.index) short = Series(0, index=close.index) trend = Series(1, index=close.index) qqe = Series(rsi_ma.iloc[0], index=close.index) qqe_long = Series(npNaN, index=close.index) qqe_short = Series(npNaN, index=close.index) for i in range(1, m): c_rsi, p_rsi = rsi_ma.iloc[i], rsi_ma.iloc[i - 1] c_long, p_long = long.iloc[i - 1], long.iloc[i - 2] c_short, p_short = short.iloc[i - 1], short.iloc[i - 2] # Long Line if p_rsi > c_long and c_rsi > c_long: long.iloc[i] = npMaximum(c_long, lowerband.iloc[i]) else: long.iloc[i] = lowerband.iloc[i] # Short Line if p_rsi < c_short and c_rsi < c_short: short.iloc[i] = npMinimum(c_short, upperband.iloc[i]) else: short.iloc[i] = upperband.iloc[i] # Trend & QQE Calculation # Long: Current RSI_MA value Crosses the Prior Short Line Value # Short: Current RSI_MA Crosses the Prior Long Line Value if (c_rsi > c_short and p_rsi < p_short) or (c_rsi <= c_short and p_rsi >= p_short): trend.iloc[i] = 1 qqe.iloc[i] = qqe_long.iloc[i] = long.iloc[i] elif (c_rsi > c_long and p_rsi < p_long) or (c_rsi <= c_long and p_rsi >= p_long): trend.iloc[i] = -1 qqe.iloc[i] = qqe_short.iloc[i] = short.iloc[i] else: trend.iloc[i] = trend.iloc[i - 1] if trend.iloc[i] == 1: qqe.iloc[i] = qqe_long.iloc[i] = long.iloc[i] else: qqe.iloc[i] = qqe_short.iloc[i] = short.iloc[i] # Offset if offset != 0: rsi_ma = rsi_ma.shift(offset) qqe = qqe.shift(offset) long = long.shift(offset) short = short.shift(offset) # Handle fills if "fillna" in kwargs: rsi_ma.fillna(kwargs["fillna"], inplace=True) qqe.fillna(kwargs["fillna"], inplace=True) qqe_long.fillna(kwargs["fillna"], inplace=True) qqe_short.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: rsi_ma.fillna(method=kwargs["fill_method"], inplace=True) qqe.fillna(method=kwargs["fill_method"], inplace=True) qqe_long.fillna(method=kwargs["fill_method"], inplace=True) qqe_short.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it _props = f"{_mode}_{length}_{smooth}_{factor}" qqe.name = f"QQE{_props}" rsi_ma.name = f"QQE{_props}_RSI{_mode.upper()}MA" qqe_long.name = f"QQEl{_props}" qqe_short.name = f"QQEs{_props}" qqe.category = rsi_ma.category = "momentum" qqe_long.category = qqe_short.category = qqe.category # Prepare DataFrame to return data = { qqe.name: qqe, rsi_ma.name: rsi_ma, # long.name: long, short.name: short qqe_long.name: qqe_long, qqe_short.name: qqe_short } df = DataFrame(data) df.name = f"QQE{_props}" df.category = qqe.category return df
def adx(high, low, close, length=None, lensig=None, mamode=None, scalar=None, drift=None, offset=None, **kwargs): """Indicator: ADX""" # Validate Arguments length = length if length and length > 0 else 14 lensig = lensig if lensig and lensig > 0 else length mamode = mamode if isinstance(mamode, str) else "rma" scalar = float(scalar) if scalar else 100 high = verify_series(high, length) low = verify_series(low, length) close = verify_series(close, length) drift = get_drift(drift) offset = get_offset(offset) if high is None or low is None or close is None: return # Calculate Result atr_ = atr(high=high, low=low, close=close, length=length) up = high - high.shift(drift) # high.diff(drift) dn = low.shift(drift) - low # low.diff(-drift).shift(drift) pos = ((up > dn) & (up > 0)) * up neg = ((dn > up) & (dn > 0)) * dn pos = pos.apply(zero) neg = neg.apply(zero) k = scalar / atr_ dmp = k * ma(mamode, pos, length=length) dmn = k * ma(mamode, neg, length=length) dx = scalar * (dmp - dmn).abs() / (dmp + dmn) adx = ma(mamode, dx, length=lensig) # Offset if offset != 0: dmp = dmp.shift(offset) dmn = dmn.shift(offset) adx = adx.shift(offset) # Handle fills if "fillna" in kwargs: adx.fillna(kwargs["fillna"], inplace=True) dmp.fillna(kwargs["fillna"], inplace=True) dmn.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: adx.fillna(method=kwargs["fill_method"], inplace=True) dmp.fillna(method=kwargs["fill_method"], inplace=True) dmn.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it adx.name = f"ADX_{lensig}" dmp.name = f"DMP_{length}" dmn.name = f"DMN_{length}" adx.category = dmp.category = dmn.category = "trend" # Prepare DataFrame to return data = {adx.name: adx, dmp.name: dmp, dmn.name: dmn} adxdf = DataFrame(data) adxdf.name = f"ADX_{lensig}" adxdf.category = "trend" return adxdf
def tsi(close, fast=None, slow=None, signal=None, scalar=None, mamode=None, drift=None, offset=None, **kwargs): """Indicator: True Strength Index (TSI)""" # Validate Arguments fast = int(fast) if fast and fast > 0 else 13 slow = int(slow) if slow and slow > 0 else 25 signal = int(signal) if signal and signal > 0 else 13 # if slow < fast: # fast, slow = slow, fast scalar = float(scalar) if scalar else 100 close = verify_series(close, max(fast, slow)) drift = get_drift(drift) offset = get_offset(offset) mamode = mamode if isinstance(mamode, str) else "ema" if "length" in kwargs: kwargs.pop("length") if close is None: return # Calculate Result diff = close.diff(drift) slow_ema = ema(close=diff, length=slow, **kwargs) fast_slow_ema = ema(close=slow_ema, length=fast, **kwargs) abs_diff = diff.abs() abs_slow_ema = ema(close=abs_diff, length=slow, **kwargs) abs_fast_slow_ema = ema(close=abs_slow_ema, length=fast, **kwargs) tsi = scalar * fast_slow_ema / abs_fast_slow_ema tsi_signal = ma(mamode, tsi, length=signal) # Offset if offset != 0: tsi = tsi.shift(offset) tsi_signal = tsi_signal.shift(offset) # Handle fills if "fillna" in kwargs: tsi.fillna(kwargs["fillna"], inplace=True) tsi_signal.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: tsi.fillna(method=kwargs["fill_method"], inplace=True) tsi_signal.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it tsi.name = f"TSI_{fast}_{slow}_{signal}" tsi_signal.name = f"TSIs_{fast}_{slow}_{signal}" tsi.category = tsi_signal.category = "momentum" # Prepare DataFrame to return df = DataFrame({tsi.name: tsi, tsi_signal.name: tsi_signal}) df.name = f"TSI_{fast}_{slow}_{signal}" df.category = "momentum" return df
def aobv(close, volume, fast=None, slow=None, mamode=None, max_lookback=None, min_lookback=None, offset=None, **kwargs): """Indicator: Archer On Balance Volume (AOBV)""" # Validate arguments close = verify_series(close) volume = verify_series(volume) offset = get_offset(offset) fast = int(fast) if fast and fast > 0 else 4 slow = int(slow) if slow and slow > 0 else 12 max_lookback = int(max_lookback) if max_lookback and max_lookback > 0 else 2 min_lookback = int(min_lookback) if min_lookback and min_lookback > 0 else 2 if slow < fast: fast, slow = slow, fast mamode = mamode if isinstance(mamode, str) else "ema" if "length" in kwargs: kwargs.pop("length") run_length = kwargs.pop("run_length", 2) # Calculate Result obv_ = obv(close=close, volume=volume, **kwargs) maf = ma(mamode, obv_, length=fast, **kwargs) mas = ma(mamode, obv_, length=slow, **kwargs) # When MAs are long and short obv_long = long_run(maf, mas, length=run_length) obv_short = short_run(maf, mas, length=run_length) # Offset if offset != 0: obv_ = obv_.shift(offset) maf = maf.shift(offset) mas = mas.shift(offset) obv_long = obv_long.shift(offset) obv_short = obv_short.shift(offset) # # Handle fills if "fillna" in kwargs: obv_.fillna(kwargs["fillna"], inplace=True) maf.fillna(kwargs["fillna"], inplace=True) mas.fillna(kwargs["fillna"], inplace=True) obv_long.fillna(kwargs["fillna"], inplace=True) obv_short.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: obv_.fillna(method=kwargs["fill_method"], inplace=True) maf.fillna(method=kwargs["fill_method"], inplace=True) mas.fillna(method=kwargs["fill_method"], inplace=True) obv_long.fillna(method=kwargs["fill_method"], inplace=True) obv_short.fillna(method=kwargs["fill_method"], inplace=True) # Prepare DataFrame to return _mode = mamode.lower()[0] if len(mamode) else "" data = { obv_.name: obv_, f"OBV_min_{min_lookback}": obv_.rolling(min_lookback).min(), f"OBV_max_{max_lookback}": obv_.rolling(max_lookback).max(), f"OBV{_mode}_{fast}": maf, f"OBV{_mode}_{slow}": mas, f"AOBV_LR_{run_length}": obv_long, f"AOBV_SR_{run_length}": obv_short, } aobvdf = DataFrame(data) # Name and Categorize it aobvdf.name = f"AOBV{_mode}_{fast}_{slow}_{min_lookback}_{max_lookback}_{run_length}" aobvdf.category = "volume" return aobvdf
def pmax(high, low, close, length=None, multiplier=None, mamode=None, offset=None, **kwargs): """Indicator: PMAX""" # Validate Arguments length = int(length) if length and length > 0 else 10 mamode = mamode.lower() if mamode and isinstance(mamode, str) else "ema" multiplier = float(multiplier) if multiplier and multiplier > 0 else 3.0 high = verify_series(high, length) low = verify_series(low, length) close = verify_series(close, length) offset = get_offset(offset) if high is None or low is None or close is None: return # Calculate Results m = close.size dir_, trend = [1] * m, [0] * m long, short = [npNaN] * m, [npNaN] * m hl2_ = hl2(high, low) mavg = ma(mamode, hl2_, length=length) matr = multiplier * atr(high, low, close, length) upperband = mavg + matr lowerband = mavg - matr for i in range(1, m): if mavg.iloc[i] > upperband.iloc[i - 1]: dir_[i] = 1 elif mavg.iloc[i] < lowerband.iloc[i - 1]: dir_[i] = -1 else: dir_[i] = dir_[i - 1] if dir_[i] > 0 and lowerband.iloc[i] < lowerband.iloc[i - 1]: lowerband.iloc[i] = lowerband.iloc[i - 1] if dir_[i] < 0 and upperband.iloc[i] > upperband.iloc[i - 1]: upperband.iloc[i] = upperband.iloc[i - 1] if dir_[i] > 0: trend[i] = long[i] = lowerband.iloc[i] else: trend[i] = short[i] = upperband.iloc[i] # Prepare DataFrame to return _props = f"_{length}_{multiplier}" df = DataFrame( { f"PMAX{_props}": trend, f"PMAXd{_props}": dir_, f"PMAXSL{_props}": mavg, f"PMAXlong{_props}": long, f"PMAXshort{_props}": short }, index=close.index) df.name = f"PMAX{_props}" df.category = "overlap" # Apply offset if needed 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) return df
def ppo(close, fast=None, slow=None, signal=None, scalar=None, mamode=None, offset=None, **kwargs): """Indicator: Percentage Price Oscillator (PPO)""" # Validate Arguments fast = int(fast) if fast and fast > 0 else 12 slow = int(slow) if slow and slow > 0 else 26 signal = int(signal) if signal and signal > 0 else 9 scalar = float(scalar) if scalar else 100 mamode = mamode if isinstance(mamode, str) else "sma" if slow < fast: fast, slow = slow, fast close = verify_series(close, max(fast, slow, signal)) offset = get_offset(offset) if close is None: return # Calculate Result if Imports["talib"]: from talib import PPO ppo = PPO(close, fast, slow) else: fastma = ma(mamode, close, length=fast) slowma = ma(mamode, close, length=slow) ppo = scalar * (fastma - slowma) ppo /= slowma signalma = ma("ema", ppo, length=signal) histogram = ppo - signalma # Offset if offset != 0: ppo = ppo.shift(offset) histogram = histogram.shift(offset) signalma = signalma.shift(offset) # Handle fills if "fillna" in kwargs: ppo.fillna(kwargs["fillna"], inplace=True) histogram.fillna(kwargs["fillna"], inplace=True) signalma.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: ppo.fillna(method=kwargs["fill_method"], inplace=True) histogram.fillna(method=kwargs["fill_method"], inplace=True) signalma.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it _props = f"_{fast}_{slow}_{signal}" ppo.name = f"PPO{_props}" histogram.name = f"PPOh{_props}" signalma.name = f"PPOs{_props}" ppo.category = histogram.category = signalma.category = "momentum" # Prepare DataFrame to return data = {ppo.name: ppo, histogram.name: histogram, signalma.name: signalma} df = DataFrame(data) df.name = f"PPO{_props}" df.category = ppo.category return df