def rsi(close, length=None, scalar=None, drift=None, offset=None, **kwargs): """Indicator: Relative Strength Index (RSI)""" # Validate arguments length = int(length) if length and length > 0 else 14 scalar = float(scalar) if scalar else 100 close = verify_series(close, length) drift = get_drift(drift) offset = get_offset(offset) if close is None: return # Calculate Result negative = close.diff(drift) positive = negative.copy() positive[positive < 0] = 0 # Make negatives 0 for the postive series negative[negative > 0] = 0 # Make postives 0 for the negative series positive_avg = rma(positive, length=length) negative_avg = rma(negative, length=length) rsi = scalar * positive_avg / (positive_avg + negative_avg.abs()) # Offset if offset != 0: rsi = rsi.shift(offset) # Handle fills if "fillna" in kwargs: rsi.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: rsi.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it rsi.name = f"RSI_{length}" rsi.category = "momentum" signal_indicators = kwargs.pop("signal_indicators", False) if signal_indicators: signalsdf = concat( [ DataFrame({rsi.name: rsi}), signals( indicator=rsi, xa=kwargs.pop("xa", 80), xb=kwargs.pop("xb", 20), xserie=kwargs.pop("xserie", None), xserie_a=kwargs.pop("xserie_a", None), xserie_b=kwargs.pop("xserie_b", None), cross_values=kwargs.pop("cross_values", False), cross_series=kwargs.pop("cross_series", True), offset=offset, ), ], axis=1, ) return signalsdf else: return rsi
def adx(high, low, close, length=None, scalar=None, drift=None, offset=None, **kwargs): """Indicator: ADX""" # Validate Arguments high = verify_series(high) low = verify_series(low) close = verify_series(close) length = length if length and length > 0 else 14 scalar = float(scalar) if scalar else 100 drift = get_drift(drift) offset = get_offset(offset) # 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 * rma(close=pos, length=length) dmn = k * rma(close=neg, length=length) dx = scalar * (dmp - dmn).abs() / (dmp + dmn) adx = rma(close=dx, length=length) # 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_{length}" 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_{length}" adxdf.category = "trend" return adxdf
def amat(close=None, fast=None, slow=None, mamode=None, lookback=None, offset=None, **kwargs): """Indicator: Archer Moving Averages Trends (AMAT)""" # Validate Arguments close = verify_series(close) 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 mamode else "ema" offset = get_offset(offset) # Calculate Result if mamode == "hma": fast_ma = hma(close=close, length=fast, **kwargs) slow_ma = hma(close=close, length=slow, **kwargs) elif mamode == "linreg": fast_ma = linreg(close=close, length=fast, **kwargs) slow_ma = linreg(close=close, length=slow, **kwargs) elif mamode == "rma": fast_ma = rma(close=close, length=fast, **kwargs) slow_ma = rma(close=close, length=slow, **kwargs) elif mamode == "sma": fast_ma = sma(close=close, length=fast, **kwargs) slow_ma = sma(close=close, length=slow, **kwargs) elif mamode == "wma": fast_ma = wma(close=close, length=fast, **kwargs) slow_ma = wma(close=close, length=slow, **kwargs) else: # "ema" fast_ma = ema(close=close, length=fast, **kwargs) slow_ma = ema(close=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_{mas_long.name}": mas_long, f"AMAT_{mas_short.name}": mas_short }) # Name and Categorize it amatdf.name = f"AMAT_{mamode.upper()}_{fast}_{slow}_{lookback}" amatdf.category = "trend" return amatdf
def kdj(high=None, low=None, close=None, length=None, signal=None, offset=None, **kwargs): """Indicator: KDJ (KDJ)""" # Validate Arguments high = verify_series(high) low = verify_series(low) close = verify_series(close) length = int(length) if length and length > 0 else 9 signal = int(signal) if signal and signal > 0 else 3 offset = get_offset(offset) # Calculate Result highest_high = high.rolling(length).max() lowest_low = low.rolling(length).min() fastk = 100 * (close - lowest_low) / non_zero_range( highest_high, lowest_low) k = rma(fastk, length=signal) d = rma(k, length=signal) j = 3 * k - 2 * d # Offset if offset != 0: k = k.shift(offset) d = d.shift(offset) j = j.shift(offset) # Handle fills if "fillna" in kwargs: k.fillna(kwargs["fillna"], inplace=True) d.fillna(kwargs["fillna"], inplace=True) j.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: k.fillna(method=kwargs["fill_method"], inplace=True) d.fillna(method=kwargs["fill_method"], inplace=True) j.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it _params = f"_{length}_{signal}" k.name = f"K{_params}" d.name = f"D{_params}" j.name = f"J{_params}" k.category = d.category = j.category = "momentum" # Prepare DataFrame to return kdjdf = DataFrame({k.name: k, d.name: d, j.name: j}) kdjdf.name = f"KDJ{_params}" kdjdf.category = "momentum" return kdjdf
def cmo(close, length=None, scalar=None, talib=None, drift=None, offset=None, **kwargs): """Indicator: Chande Momentum Oscillator (CMO)""" # Validate Arguments length = int(length) if length and length > 0 else 14 scalar = float(scalar) if scalar else 100 close = verify_series(close, length) drift = get_drift(drift) 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 CMO cmo = CMO(close, length) else: mom = close.diff(drift) positive = mom.copy().clip(lower=0) negative = mom.copy().clip(upper=0).abs() if mode_tal: pos_ = rma(positive, length) neg_ = rma(negative, length) else: pos_ = positive.rolling(length).sum() neg_ = negative.rolling(length).sum() cmo = scalar * (pos_ - neg_) / (pos_ + neg_) # Offset if offset != 0: cmo = cmo.shift(offset) # Handle fills if "fillna" in kwargs: cmo.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: cmo.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it cmo.name = f"CMO_{length}" cmo.category = "momentum" return cmo
def cmo(close, length=None, scalar=None, drift=None, offset=None, **kwargs): """Indicator: Chande Momentum Oscillator (CMO)""" # Validate Arguments close = verify_series(close) length = int(length) if length and length > 0 else 14 scalar = float(scalar) if scalar else 100 talib = kwargs.pop("talib", True) drift = get_drift(drift) offset = get_offset(offset) # Calculate Result mom = close.diff(drift) positive = mom.copy().clip(lower=0) negative = mom.copy().clip(upper=0).abs() if talib: pos_ = rma(positive, length) neg_ = rma(negative, length) else: pos_ = positive.rolling(length).sum() neg_ = negative.rolling(length).sum() cmo = scalar * (pos_ - neg_) / (pos_ + neg_) # Offset if offset != 0: cmo = cmo.shift(offset) # Handle fills if "fillna" in kwargs: cmo.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: cmo.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it cmo.name = f"CMO_{length}" cmo.category = "momentum" return cmo
def atr(high, low, close, length=None, mamode=None, drift=None, offset=None, **kwargs): """Indicator: Average True Range (ATR)""" # Validate arguments high = verify_series(high) low = verify_series(low) close = verify_series(close) length = int(length) if length and length > 0 else 14 mamode = mamode = mamode.lower() if mamode else "rma" drift = get_drift(drift) offset = get_offset(offset) # Calculate Result _mode = "" tr = true_range(high=high, low=low, close=close, drift=drift) if mamode == "ema": atr, _mode = ema(tr, length=length), "ema" elif mamode == "sma": atr, _mode = sma(tr, length=length), "sma" elif mamode == "wma": atr, _mode = wma(tr, length=length), "wma" else: # "rma" atr = rma(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{_mode}_{length}{'p' if percentage else ''}" atr.category = "volatility" return atr
def qstick(open_, close, length=None, offset=None, **kwargs): """Indicator: Q Stick""" # Validate Arguments length = int(length) if length and length > 0 else 10 ma = kwargs.pop("ma", "sma") open_ = verify_series(open_, length) close = verify_series(close, length) offset = get_offset(offset) if open_ is None or close is None: return # Calculate Result diff = non_zero_range(close, open_) if ma == "dema": qstick = dema(diff, length=length, **kwargs) elif ma == "ema": qstick = ema(diff, length=length, **kwargs) elif ma == "hma": qstick = hma(diff, length=length) elif ma == "rma": qstick = rma(diff, length=length) else: # "sma" qstick = sma(diff, length=length) # Offset if offset != 0: qstick = qstick.shift(offset) # Handle fills if "fillna" in kwargs: qstick.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: qstick.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it qstick.name = f"QS_{length}" qstick.category = "trend" return qstick
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.lower() if mamode else None offset = get_offset(offset) # Calculate Result if mamode == "ema": ma = ema(close, length=length, **kwargs) elif mamode == "hma": ma = hma(close, length=length, **kwargs) elif mamode == "rma": ma = rma(close, length=length, **kwargs) elif mamode == "wma": ma = wma(close, length=length, **kwargs) else: # "sma" ma = sma(close, length=length, **kwargs) bias = (close / ma) - 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_{ma.name}" bias.category = "momentum" return bias
def adx(data, length=None, scalar=None, drift=None, offset=None, **kwargs): """Indicator: ADX""" import pandas as pd from pandas import DataFrame from pandas_ta.overlap import rma from pandas_ta.volatility import atr from pandas_ta.utils import get_drift, get_offset, verify_series, zero #transform de DataFrame into sub DF high = data['High'] low = data['Low'] close = data['Close'] open = data['Open'] volume = data['Volume'] # Validate Arguments high = verify_series(high) low = verify_series(low) close = verify_series(close) length = length if length and length > 0 else 14 scalar = float(scalar) if scalar else 100 drift = get_drift(drift) offset = get_offset(offset) # 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 * rma(close=pos, length=length) dmn = k * rma(close=neg, length=length) dx = scalar * (dmp - dmn).abs() / (dmp + dmn) adx = rma(close=dx, length=length) # 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_{length}" #dmp.name = f"DMP_{length}" dmp.name = f"posDI_{length}" #dmn.name = f"DMN_{length}" dmn.name = f"negDI_{length}" adx.category = dmp.category = dmn.category = 'trend' # Prepare DataFrame to return #data = {adx.name: adx, dmp.name: dmp, dmn.name: dmn} #data = {'Open': open, 'High': high, 'Low': low, 'Close': close, 'Volume': volume, dmp.name: dmp, dmn.name: dmn, adx.name: adx} data = pd.concat( [data, dmp, dmn, adx], axis=1 ) #le agregue esta fila para que me tome el DF inicial y le agregue el DMI y ADX #adxdf = DataFrame(data) #adxdf.name = f"ADX_{length}" #adxdf.category = 'trend' #return adxdf return data
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.lower() if mamode else "ema" drift = get_drift(drift) offset = get_offset(offset) # Calculate Result rsi_ = rsi(close, length) if mamode == "hma": rsi_ma, _mode = hma(rsi_, length=smooth), "h" elif mamode == "rma": rsi_ma, _mode = rma(rsi_, length=smooth), "r" elif mamode == "sma": rsi_ma, _mode = sma(rsi_, length=smooth), "s" elif mamode == "wma": rsi_ma, _mode = wma(rsi_, length=smooth), "w" else: # "ema" rsi_ma, _mode = ema(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 = ema(rsi_ma_tr, length=wilders_length) dar = factor * 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) long.fillna(kwargs["fillna"], inplace=True) 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) long.fillna(method=kwargs["fill_method"], inplace=True) 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" long.name = f"QQEl{_props}" short.name = f"QQEs{_props}" qqe.category = rsi_ma.category = "momentum" long.category = short.category = qqe.category # Prepare DataFrame to return data = { qqe.name: qqe, rsi_ma.name: rsi_ma, # long.name: long, short.name: short long.name: qqe_long, short.name: qqe_short } df = DataFrame(data) df.name = f"QQE{_props}" df.category = qqe.category return df