def gatorosc(candles: np.ndarray, source_type="close", sequential=False) -> GATOR: """ Gator Oscillator by Bill M. Williams :param candles: np.ndarray :param source_type: str - default: "close" :param sequential: bool - default=False :return: GATOR(upper, lower, upper_change, lower_change) """ if not sequential and len(candles) > 240: candles = candles[-240:] source = get_candle_source(candles, source_type=source_type) jaw = np_shift(numpy_ewma(source, 13), 8, fill_value=np.nan) teeth = np_shift(numpy_ewma(source, 8), 5, fill_value=np.nan) lips = np_shift(numpy_ewma(source, 5), 3, fill_value=np.nan) upper = np.abs(jaw - teeth) lower = -np.abs(teeth - lips) upper_change = talib.MOM(upper, timeperiod=1) lower_change = -talib.MOM(lower, timeperiod=1) if sequential: return GATOR(upper, lower, upper_change, lower_change) else: return GATOR(upper[-1], lower[-1], upper_change[-1], lower_change[-1])
def alligator(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> AG: """ Alligator :param candles: np.ndarray :param source_type: str - default: "close" :param sequential: bool - default=False :return: AG(jaw, teeth, lips) """ if not sequential and len(candles) > 240: candles = candles[-240:] source = get_candle_source(candles, source_type=source_type) jaw = np_shift(numpy_ewma(source, 13), 8, fill_value=np.nan) teeth = np_shift(numpy_ewma(source, 8), 5, fill_value=np.nan) lips = np_shift(numpy_ewma(source, 5), 3, fill_value=np.nan) if sequential: return AG(jaw, teeth, lips) else: return AG(jaw[-1], teeth[-1], lips[-1])
def safezonestop(candles: np.ndarray, period: int = 22, mult: float = 2.5, max_lookback: int = 3, direction: str = "long", sequential: bool = False) -> Union[float, np.ndarray]: """ Safezone Stops :param candles: np.ndarray :param period: int - default=22 :param mult: float - default=2.5 :param max_lookback: int - default=3 :param direction: str - default=long :param sequential: bool - default=False :return: float | np.ndarray """ warmup_candles_num = get_config('env.data.warmup_candles_num', 240) if not sequential and len(candles) > warmup_candles_num: candles = candles[-warmup_candles_num:] high = candles[:, 3] low = candles[:, 4] last_high = np_shift(high, 1, fill_value=np.nan) last_low = np_shift(low, 1, fill_value=np.nan) if direction == "long": res = last_low - mult * talib.MINUS_DM(high, low, timeperiod=period) swv = sliding_window_view(res, window_shape=max_lookback) res = np.max(swv, axis=-1) else: res = last_high + mult * talib.PLUS_DM(high, low, timeperiod=period) swv = sliding_window_view(res, window_shape=max_lookback) res = np.min(swv, axis=-1) return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]
def safezonestop(candles: np.ndarray, period: int = 22, mult: float = 2.5, max_lookback: int = 3, direction: str = "long", sequential: bool = False) -> Union[float, np.ndarray]: """ Safezone Stops :param candles: np.ndarray :param period: int - default: 22 :param mult: float - default: 2.5 :param max_lookback: int - default: 3 :param direction: str - default: long :param sequential: bool - default: False :return: float | np.ndarray """ candles = slice_candles(candles, sequential) high = candles[:, 3] low = candles[:, 4] last_high = np_shift(high, 1, fill_value=np.nan) last_low = np_shift(low, 1, fill_value=np.nan) if direction == "long": res = talib.MAX(last_low - mult * talib.MINUS_DM(high, low, timeperiod=period), max_lookback) else: res = talib.MIN(last_high + mult * talib.PLUS_DM(high, low, timeperiod=period), max_lookback) return res if sequential else res[-1]
def crossed(series1: np.ndarray, series2: Union[float, int, np.ndarray], direction: str = None, sequential: bool = False) -> bool: """ Helper for detecion of crosses :param series1: np.ndarray :param series2: float, int, np.array :param direction: str - default: None - above or below :return: bool """ if sequential: series1_shifted = jh.np_shift(series1, 1, np.nan) if type(series2) is np.ndarray: series2_shifted = jh.np_shift(series2, 1, np.nan) else: series2_shifted = series2 if direction is None or direction == "above": cross_above = np.logical_and(series1 > series2, series1_shifted <= series2_shifted) if direction is None or direction == "below": cross_below = np.logical_and(series1 < series2, series1_shifted >= series2_shifted) if direction is None: cross_any = np.logical_or(cross_above, cross_below) return cross_any if direction == "above": return cross_above else: return cross_below else: if not type(series2) is np.ndarray: series2 = np.array([series2, series2]) if direction is None or direction == "above": cross_above = series1[-2] <= series2[-2] and series1[-1] > series2[ -1] if direction is None or direction == "below": cross_below = series1[-2] >= series2[-2] and series1[-1] < series2[ -1] if direction is None: return cross_above or cross_below if direction == "above": return cross_above else: return cross_below
def ichimoku_cloud_seq(candles: np.ndarray, conversion_line_period: int = 9, base_line_period: int = 26, lagging_line_period: int = 52, displacement: int = 26, sequential: bool = False) -> IchimokuCloud: """ Ichimoku Cloud :param candles: np.ndarray :param conversion_line_period: int - default=9 :param base_line_period: int - default=26 :param lagging_line_period: int - default=52 :param displacement: - default=26 :param sequential: bool - default=False :return: IchimokuCloud """ if len(candles) < lagging_line_period + displacement: raise ValueError( "Too few candles available for lagging_line_period + displacement." ) if not sequential and len(candles) > 240: candles = candles[-240:] small_ph = talib.MAX(candles[:, 3], conversion_line_period) small_pl = talib.MIN(candles[:, 4], conversion_line_period) conversion_line = (small_ph + small_pl) / 2 mid_ph = talib.MAX(candles[:, 3], base_line_period) mid_pl = talib.MIN(candles[:, 4], base_line_period) base_line = (mid_ph + mid_pl) / 2 long_ph = talib.MAX(candles[:, 3], lagging_line_period) long_pl = talib.MIN(candles[:, 4], lagging_line_period) span_b_pre = (long_ph + long_pl) / 2 span_b = np_shift(span_b_pre, displacement, fill_value=np.nan) span_a_pre = (conversion_line + base_line) / 2 span_a = np_shift(span_a_pre, displacement, fill_value=np.nan) lagging_line = np_shift(candles[:, 2], displacement - 1, fill_value=np.nan) if sequential: return IchimokuCloud(conversion_line, base_line, span_a, span_b, lagging_line, span_a_pre, span_b_pre) else: return IchimokuCloud(conversion_line[-1], base_line[-1], span_a[-1], span_b[-1], lagging_line[-1], span_a_pre[-1], span_b_pre[-1])
def correlation_cycle(candles: np.ndarray, period: int = 20, threshold: int = 9, source_type: str = "close", sequential: bool = False) -> CC: """ "Correlation Cycle, Correlation Angle, Market State - John Ehlers :param candles: np.ndarray :param period: int - default: 20 :param threshold: int - default: 9 :param source_type: str - default: "close" :param sequential: bool - default: False :return: CC(real, imag) """ candles = slice_candles(candles, sequential) source = get_candle_source(candles, source_type=source_type) realPart, imagPart, angle = go_fast(source, period, threshold) priorAngle = np_shift(angle, 1, fill_value=np.nan) angle = np.where(np.logical_and(priorAngle > angle, priorAngle - angle < 270.0), priorAngle, angle) # Market State Function state = np.where(np.abs(angle - priorAngle) < threshold, np.where(angle >= 0.0, 1, np.where(angle < 0.0, -1, 0)), 0) if sequential: return CC(realPart, imagPart, angle, state) else: return CC(realPart[-1], imagPart[-1], angle[-1], state[-1])
def maaq(candles: np.ndarray, period: int = 11, fast_period: int = 2, slow_period: int = 30, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]: """ Moving Average Adaptive Q :param candles: np.ndarray :param period: int - default: 11 :param fast_period: int - default: 2 :param slow_period: int - default: 30 :param source_type: str - default: "close" :param sequential: bool - default: False :return: float | np.ndarray """ # Accept normal array too. if len(candles.shape) == 1: source = candles else: candles = slice_candles(candles, sequential) source = get_candle_source(candles, source_type=source_type) source = source[~np.isnan(source)] diff = np.abs(source - np_shift(source, 1, np.nan)) signal = np.abs(source - np_shift(source, period, np.nan)) noise = talib.SUM(diff, period) with np.errstate(divide='ignore'): ratio = np.where(noise == 0, 0, signal / noise) fastSc = 2 / (fast_period + 1) slowSc = 2 / (slow_period + 1) temp = np.power((ratio * fastSc) + slowSc, 2) res = maaq_fast(source, temp, period) res = same_length(candles, res) return res if sequential else res[-1]
def dti(candles: np.ndarray, r: int = 14, s: int = 10, u: int = 5, sequential: bool = False) -> Union[float, np.ndarray]: """ DTI by William Blau :param candles: np.ndarray :param r: int - default=14 :param s: int - default=10 :param u: int - default=5 :param sequential: bool - default=False :return: float """ warmup_candles_num = get_config('env.data.warmup_candles_num', 240) if not sequential and len(candles) > warmup_candles_num: candles = candles[-warmup_candles_num:] high = candles[:, 3] low = candles[:, 4] high_1 = jh.np_shift(high, 1, np.nan) low_1 = jh.np_shift(low, 1, np.nan) xHMU = np.where(high - high_1 > 0, high - high_1, 0) xLMD = np.where(low - low_1 < 0, -(low - low_1), 0) xPrice = xHMU - xLMD xPriceAbs = np.absolute(xPrice) xuXA = talib.EMA(talib.EMA(talib.EMA(xPrice, r), s), u) xuXAAbs = talib.EMA(talib.EMA(talib.EMA(xPriceAbs, r), s), u) Val1 = 100 * xuXA Val2 = xuXAAbs dti_val = np.where(Val2 != 0, Val1 / Val2, 0) if sequential: return dti_val else: return None if np.isnan(dti_val[-1]) else dti_val[-1]
def vpt(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]: """ Volume Price Trend (VPT) :param candles: np.ndarray :param source_type: str - default: "close" :param sequential: bool - default: False :return: float | np.ndarray """ candles = slice_candles(candles, sequential) source = get_candle_source(candles, source_type=source_type) vpt = (candles[:, 5] * ((source - np_shift(source, 1, fill_value=np.nan)) / np_shift(source, 1, fill_value=np.nan))) res = np_shift(vpt, 1, fill_value=np.nan) + vpt return res if sequential else res[-1]
def safezonestop(candles: np.ndarray, period: int = 22, mult: float = 2.5, max_lookback: int = 3, direction: str = "long", sequential: bool = False) -> Union[float, np.ndarray]: """ Safezone Stops :param candles: np.ndarray :param period: int - default=22 :param mult: float - default=2.5 :param max_lookback: int - default=3 :param direction: str - default=long :param sequential: bool - default=False :return: float | np.ndarray """ warmup_candles_num = get_config('env.data.warmup_candles_num', 240) if not sequential and len(candles) > warmup_candles_num: candles = candles[-warmup_candles_num:] high = candles[:, 3] low = candles[:, 4] last_high = np_shift(high, 1, fill_value=np.nan) last_low = np_shift(low, 1, fill_value=np.nan) if direction == "long": res = talib.MAX( last_low - mult * talib.MINUS_DM(high, low, timeperiod=period), max_lookback) else: res = talib.MIN( last_high + mult * talib.PLUS_DM(high, low, timeperiod=period), max_lookback) if sequential: return res else: return None if np.isnan(res[-1]) else res[-1]
def ichimoku_cloud_seq(candles: np.ndarray, conversion_line_period: int = 9, base_line_period: int = 26, lagging_line_period: int = 52, displacement: int = 26, sequential: bool = False) -> IchimokuCloud: """ Ichimoku Cloud :param candles: np.ndarray :param conversion_line_period: int - default: 9 :param base_line_period: int - default: 26 :param lagging_line_period: int - default: 52 :param displacement: - default: 26 :param sequential: bool - default: False :return: IchimokuCloud """ if candles.shape[0] < lagging_line_period + displacement: raise ValueError( "Too few candles available for lagging_line_period + displacement." ) candles = slice_candles(candles, sequential) conversion_line = _line_helper(candles, conversion_line_period) base_line = _line_helper(candles, base_line_period) span_b_pre = _line_helper(candles, lagging_line_period) span_b = np_shift(span_b_pre, displacement, fill_value=np.nan) span_a_pre = (conversion_line + base_line) / 2 span_a = np_shift(span_a_pre, displacement, fill_value=np.nan) lagging_line = np_shift(candles[:, 2], displacement - 1, fill_value=np.nan) if sequential: return IchimokuCloud(conversion_line, base_line, span_a, span_b, lagging_line, span_a_pre, span_b_pre) else: return IchimokuCloud(conversion_line[-1], base_line[-1], span_a[-1], span_b[-1], lagging_line[-1], span_a_pre[-1], span_b_pre[-1])
def vpt(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]: """ Volume Price Trend (VPT) :param candles: np.ndarray :param source_type: str - default: "close" :param sequential: bool - default=False :return: float | np.ndarray """ warmup_candles_num = get_config('env.data.warmup_candles_num', 240) if not sequential and len(candles) > warmup_candles_num: candles = candles[-warmup_candles_num:] source = get_candle_source(candles, source_type=source_type) vpt = (candles[:, 5] * ((source - np_shift(source, 1, fill_value=np.nan)) / np_shift(source, 1, fill_value=np.nan))) res = np_shift(vpt, 1, fill_value=np.nan) + vpt return res if sequential else res[-1]
def append(self, item: np.ndarray) -> None: self.index += 1 # expand if the arr is almost full if self.index != 0 and (self.index + 1) % self.bucket_size == 0: new_bucket = np.zeros(self.shape) self.array = np.concatenate((self.array, new_bucket), axis=0) # drop N% of the beginning values to free memory if (self.drop_at is not None and self.index != 0 and (self.index + 1) % self.drop_at == 0): shift_num = int(self.drop_at / 2) self.index -= shift_num self.array = np_shift(self.array, -shift_num) self.array[self.index] = item
def jsa(candles: np.ndarray, period: int = 30, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]: """ Jsa Moving Average :param candles: np.ndarray :param period: int - default: 30 :param source_type: str - default: "close" :param sequential: bool - default: False :return: float | np.ndarray """ if len(candles.shape) == 1: source = candles else: candles = slice_candles(candles, sequential) source = get_candle_source(candles, source_type=source_type) res = (source + np_shift(source, period, np.nan)) / 2 return res if sequential else res[-1]
def test_np_shift(): arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]) res = jh.np_shift(arr, -3) expected = np.array([4, 5, 6, 7, 8, 9, 0, 0, 0]) np.equal(res, expected)
def correlation_cycle(candles: np.ndarray, period: int = 20, threshold: int = 9, source_type: str = "close", sequential: bool = False) -> CC: """ "Correlation Cycle, Correlation Angle, Market State - John Ehlers :param candles: np.ndarray :param period: int - default: 20 :param threshold: int - default: 9 :param source_type: str - default: "close" :param sequential: bool - default=False :return: CC(real, imag) """ if not sequential and len(candles) > 240: candles = candles[-240:] source = get_candle_source(candles, source_type=source_type) # Correlation Cycle Function PIx2 = 4.0 * math.asin(1.0) period = max(2, period) realPart = np.full_like(source, np.nan) imagPart = np.full_like(source, np.nan) for i in range(period, source.shape[0]): Rx = 0.0 Rxx = 0.0 Rxy = 0.0 Ryy = 0.0 Ry = 0.0 Ix = 0.0 Ixx = 0.0 Ixy = 0.0 Iyy = 0.0 Iy = 0.0 for j in range(period): jMinusOne = j + 1 if np.isnan(source[i - jMinusOne]): X = 0 else: X = source[i - jMinusOne] temp = PIx2 * jMinusOne / period Yc = np.cos(temp) Ys = -np.sin(temp) Rx = Rx + X Ix = Ix + X Rxx = Rxx + X * X Ixx = Ixx + X * X Rxy = Rxy + X * Yc Ixy = Ixy + X * Ys Ryy = Ryy + Yc * Yc Iyy = Iyy + Ys * Ys Ry = Ry + Yc Iy = Iy + Ys temp_1 = period * Rxx - Rx * Rx temp_2 = period * Ryy - Ry * Ry if (temp_1 > 0.0 and temp_2 > 0.0): realPart[i] = (period * Rxy - Rx * Ry) / np.sqrt(temp_1 * temp_2) temp_1 = period * Ixx - Ix * Ix temp_2 = period * Iyy - Iy * Iy if (temp_1 > 0.0 and temp_2 > 0.0): imagPart[i] = (period * Ixy - Ix * Iy) / np.sqrt(temp_1 * temp_2) # Correlation Angle Phasor HALF_OF_PI = math.asin(1.0) angle = np.where(imagPart == 0, 0.0, np.degrees(np.arctan(realPart / imagPart) + HALF_OF_PI)) angle = np.where(imagPart > 0.0, angle - 180.0, angle) priorAngle = np_shift(angle, 1, fill_value=np.nan) angle = np.where(np.logical_and(priorAngle > angle, priorAngle - angle < 270.0), priorAngle, angle) # Market State Function state = np.where(np.abs(angle - priorAngle) < threshold, np.where(angle >= 0.0, 1, np.where(angle < 0.0, -1, 0)), 0) if sequential: return CC(realPart, imagPart, angle, state) else: return CC(realPart[-1], imagPart[-1], angle[-1], state[-1])