def pdist(open_, high, low, close, drift=None, offset=None, **kwargs): """Indicator: Price Distance (PDIST)""" # Validate Arguments open_ = verify_series(open_) high = verify_series(high) low = verify_series(low) close = verify_series(close) drift = get_drift(drift) offset = get_offset(offset) # Calculate Result pdist = 2 * non_zero_range(high, low) pdist += non_zero_range(open_, close.shift(drift)).abs() pdist -= non_zero_range(close, open_).abs() # Offset if offset != 0: pdist = pdist.shift(offset) # Handle fills if "fillna" in kwargs: pdist.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: pdist.fillna(method=kwargs["fill_method"], inplace=True) # Name & Category pdist.name = "PDIST" pdist.category = "volatility" return pdist
def bop(open_, high, low, close, scalar=None, offset=None, **kwargs): """Indicator: Balance of Power (BOP)""" # Validate Arguments open_ = verify_series(open_) high = verify_series(high) low = verify_series(low) close = verify_series(close) scalar = float(scalar) if scalar else 1 offset = get_offset(offset) # Calculate Result if Imports["talib"]: from talib import BOP bop = BOP(open_, high, low, close) else: high_low_range = non_zero_range(high, low) close_open_range = non_zero_range(close, open_) bop = scalar * close_open_range / high_low_range # Offset if offset != 0: bop = bop.shift(offset) # Handle fills if "fillna" in kwargs: bop.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: bop.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it bop.name = f"BOP" bop.category = "momentum" return bop
def ad(high, low, close, volume, open_=None, offset=None, **kwargs): """Indicator: Accumulation/Distribution (AD)""" # Validate Arguments high = verify_series(high) low = verify_series(low) close = verify_series(close) volume = verify_series(volume) offset = get_offset(offset) # Calculate Result if open_ is not None: open_ = verify_series(open_) ad = non_zero_range(close, open_) # AD with Open else: ad = 2 * close - (high + low) # AD with High, Low, Close high_low_range = non_zero_range(high, low) ad *= volume / high_low_range ad = ad.cumsum() # Offset if offset != 0: ad = ad.shift(offset) # Handle fills if "fillna" in kwargs: ad.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: ad.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it ad.name = "AD" if open_ is None else "ADo" ad.category = "volume" return ad
def brar(open_, high, low, close, length=None, scalar=None, drift=None, offset=None, **kwargs): """Indicator: BRAR (BRAR)""" # Validate Arguments open_ = verify_series(open_) high = verify_series(high) low = verify_series(low) close = verify_series(close) length = int(length) if length and length > 0 else 26 scalar = float(scalar) if scalar else 100 high_open_range = non_zero_range(high, open_) open_low_range = non_zero_range(open_, low) drift = get_drift(drift) offset = get_offset(offset) # Calculate Result hcy = non_zero_range(high, close.shift(drift)) cyl = non_zero_range(close.shift(drift), low) hcy[hcy < 0] = 0 # Zero negative values cyl[cyl < 0] = 0 # "" ar = scalar * high_open_range.rolling(length).sum() ar /= open_low_range.rolling(length).sum() br = scalar * hcy.rolling(length).sum() br /= cyl.rolling(length).sum() # Offset if offset != 0: ar = ar.shift(offset) br = ar.shift(offset) # Handle fills if "fillna" in kwargs: ar.fillna(kwargs["fillna"], inplace=True) br.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: ar.fillna(method=kwargs["fill_method"], inplace=True) br.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it _props = f"_{length}" ar.name = f"AR{_props}" br.name = f"BR{_props}" ar.category = br.category = "momentum" # Prepare DataFrame to return brardf = DataFrame({ar.name: ar, br.name: br}) brardf.name = f"BRAR{_props}" brardf.category = "momentum" return brardf
def kama(close, length=None, fast=None, slow=None, drift=None, offset=None, **kwargs): """Indicator: Kaufman's Adaptive Moving Average (KAMA)""" # Validate Arguments length = int(length) if length and length > 0 else 10 fast = int(fast) if fast and fast > 0 else 2 slow = int(slow) if slow and slow > 0 else 30 close = verify_series(close, max(fast, slow, length)) drift = get_drift(drift) offset = get_offset(offset) if close is None: return # Calculate Result def weight(length: int) -> float: return 2 / (length + 1) fr = weight(fast) sr = weight(slow) abs_diff = non_zero_range(close, close.shift(length)).abs() peer_diff = non_zero_range(close, close.shift(drift)).abs() peer_diff_sum = peer_diff.rolling(length).sum() er = abs_diff / peer_diff_sum x = er * (fr - sr) + sr sc = x * x m = close.size result = [npNaN for _ in range(0, length - 1)] + [0] for i in range(length, m): result.append(sc.iloc[i] * close.iloc[i] + (1 - sc.iloc[i]) * result[i - 1]) kama = Series(result, index=close.index) # Offset if offset != 0: kama = kama.shift(offset) # Handle fills if "fillna" in kwargs: kama.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: kama.fillna(method=kwargs["fill_method"], inplace=True) # Name & Category kama.name = f"KAMA_{length}_{fast}_{slow}" kama.category = "overlap" return kama
def rvgi(open_, high, low, close, length=None, swma_length=None, offset=None, **kwargs): """Indicator: Relative Vigor Index (RVGI)""" # Validate Arguments open_ = verify_series(open_) high = verify_series(high) low = verify_series(low) close = verify_series(close) high_low_range = non_zero_range(high, low) close_open_range = non_zero_range(close, open_) length = int(length) if length and length > 0 else 14 swma_length = int(swma_length) if swma_length and swma_length > 0 else 4 offset = get_offset(offset) # Calculate Result numerator = swma(close_open_range, length=swma_length).rolling(length).sum() denominator = swma(high_low_range, length=swma_length).rolling(length).sum() rvgi = numerator / denominator signal = swma(rvgi, length=swma_length) # Offset if offset != 0: rvgi = rvgi.shift(offset) signal = signal.shift(offset) # Handle fills if "fillna" in kwargs: rvgi.fillna(kwargs["fillna"], inplace=True) signal.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: rvgi.fillna(method=kwargs["fill_method"], inplace=True) signal.fillna(method=kwargs["fill_method"], inplace=True) # Name & Category rvgi.name = f"RVGI_{length}_{swma_length}" signal.name = f"RVGIs_{length}_{swma_length}" rvgi.category = signal.category = "momentum" # Prepare DataFrame to return df = DataFrame({rvgi.name: rvgi, signal.name: signal}) df.name = f"RVGI_{length}_{swma_length}" df.category = rvgi.category return df
def cmf(high, low, close, volume, open_=None, length=None, offset=None, **kwargs): """Indicator: Chaikin Money Flow (CMF)""" # Validate Arguments length = int(length) if length and length > 0 else 20 min_periods = int( kwargs["min_periods"]) if "min_periods" in kwargs and kwargs[ "min_periods"] is not None else length _length = max(length, min_periods) high = verify_series(high, _length) low = verify_series(low, _length) close = verify_series(close, _length) volume = verify_series(volume, _length) offset = get_offset(offset) if high is None or low is None or close is None or volume is None: return # Calculate Result if open_ is not None: open_ = verify_series(open_) ad = non_zero_range(close, open_) # AD with Open else: ad = 2 * close - (high + low) # AD with High, Low, Close ad *= volume / non_zero_range(high, low) cmf = ad.rolling(length, min_periods=min_periods).sum() cmf /= volume.rolling(length, min_periods=min_periods).sum() # Offset if offset != 0: cmf = cmf.shift(offset) # Handle fills if "fillna" in kwargs: cmf.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: cmf.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it cmf.name = f"CMF_{length}" cmf.category = "volume" return cmf
def vhf(close, length=None, drift=None, offset=None, **kwargs): """Indicator: Vertical Horizontal Filter (VHF)""" # Validate arguments length = int(length) if length and length > 0 else 28 close = verify_series(close, length) drift = get_drift(drift) offset = get_offset(offset) if close is None: return # Calculate Result hcp = close.rolling(length).max() lcp = close.rolling(length).min() diff = npFabs(close.diff(drift)) vhf = npFabs(non_zero_range(hcp, lcp)) / diff.rolling(length).sum() # Offset if offset != 0: vhf = vhf.shift(offset) # Handle fills if "fillna" in kwargs: vhf.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: vhf.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it vhf.name = f"VHF_{length}" vhf.category = "trend" return vhf
def massi(high, low, fast=None, slow=None, offset=None, **kwargs): """Indicator: Mass Index (MASSI)""" # Validate arguments high = verify_series(high) low = verify_series(low) high_low_range = non_zero_range(high, low) fast = int(fast) if fast and fast > 0 else 9 slow = int(slow) if slow and slow > 0 else 25 if slow < fast: fast, slow = slow, fast offset = get_offset(offset) if "length" in kwargs: kwargs.pop("length") # Calculate Result hl_ema1 = ema(close=high_low_range, length=fast, **kwargs) hl_ema2 = ema(close=hl_ema1, length=fast, **kwargs) hl_ratio = hl_ema1 / hl_ema2 massi = hl_ratio.rolling(slow, min_periods=slow).sum() # Offset if offset != 0: massi = massi.shift(offset) # Handle fills if "fillna" in kwargs: massi.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: massi.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it massi.name = f"MASSI_{fast}_{slow}" massi.category = "volatility" return massi
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 true_range(high, low, close, drift=None, offset=None, **kwargs): """Indicator: True Range""" # Validate arguments high = verify_series(high) low = verify_series(low) close = verify_series(close) drift = get_drift(drift) offset = get_offset(offset) # Calculate Result high_low_range = non_zero_range(high, low) prev_close = close.shift(drift) ranges = [high_low_range, high - prev_close, prev_close - low] true_range = concat(ranges, axis=1) true_range = true_range.abs().max(axis=1) true_range.iloc[:drift] = npNaN # Offset if offset != 0: true_range = true_range.shift(offset) # Handle fills if "fillna" in kwargs: true_range.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: true_range.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it true_range.name = f"TRUERANGE_{drift}" true_range.category = "volatility" return true_range
def stoch(high, low, close, k=None, d=None, smooth_k=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) 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 = sma(stoch, length=smooth_k) stoch_d = sma(stoch_k, 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 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 stochrsi(close, length=None, rsi_length=None, k=None, d=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) 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 = sma(stoch, length=k) stochrsi_d = sma(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 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 schaff_tc(close, xmacd, tclength, factor): # ACTUAL Calculation part, which is shared between operation modes # 1St : Stochastic of MACD lowest_xmacd = xmacd.rolling(tclength).min() # min value in interval tclen xmacd_range = non_zero_range(xmacd.rolling(tclength).max(), lowest_xmacd) m = len(xmacd) # %Fast K of MACD stoch1, pf = list(xmacd), list(xmacd) stoch1[0], pf[0] = 0, 0 for i in range(1, m): if lowest_xmacd[i] > 0: stoch1[i] = 100 * ((xmacd[i] - lowest_xmacd[i]) / xmacd_range[i]) else: stoch1[i] = stoch1[i - 1] # Smoothed Calculation for % Fast D of MACD pf[i] = round(pf[i - 1] + (factor * (stoch1[i] - pf[i - 1])), 8) pf = Series(pf, index=close.index) # 2nd : Stochastic of smoothed Percent Fast D, 'PF', above lowest_pf = pf.rolling(tclength).min() pf_range = non_zero_range(pf.rolling(tclength).max(), lowest_pf) # % of Fast K of PF stoch2, pff = list(xmacd), list(xmacd) stoch2[0], pff[0] = 0, 0 for i in range(1, m): if pf_range[i] > 0: stoch2[i] = 100 * ((pf[i] - lowest_pf[i]) / pf_range[i]) else: stoch2[i] = stoch2[i - 1] # Smoothed Calculation for % Fast D of PF pff[i] = round(pff[i - 1] + (factor * (stoch2[i] - pff[i - 1])), 8) return [pff, pf]
def eom( high, low, close, volume, length=None, divisor=None, drift=None, offset=None, **kwargs, ): """Indicator: Ease of Movement (EOM)""" # Validate arguments high = verify_series(high) low = verify_series(low) close = verify_series(close) volume = verify_series(volume) length = int(length) if length and length > 0 else 14 divisor = divisor if divisor and divisor > 0 else 100000000 drift = get_drift(drift) offset = get_offset(offset) # Calculate Result high_low_range = non_zero_range(high, low) distance = hl2(high=high, low=low) - hl2(high=high.shift(drift), low=low.shift(drift)) box_ratio = volume / divisor box_ratio /= high_low_range eom = distance / box_ratio eom = sma(eom, length=length) # Offset if offset != 0: eom = eom.shift(offset) # Handle fills if "fillna" in kwargs: eom.fillna(kwargs["fillna"], inplace=True) if "fill_method" in kwargs: eom.fillna(method=kwargs["fill_method"], inplace=True) # Name and Categorize it eom.name = f"EOM_{length}_{divisor}" eom.category = "volume" return eom
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 high_low_range(high, low): return non_zero_range(high, low)
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 real_body(open_, close): return non_zero_range(open_, close)