class EhlersWayBandpass(Function): def __init__(self, input_, period, delta): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) self.__delta = FunctionInput(delta) Function.__init__(self) def _next(self): self.inputs.update() if len(self) >= min(len(self.__period), len( self.__delta)) or len(self) + 5 > len(self.__input): raise StopIteration self.inputs.sync({self.__input: 5}) period = self.__period.consume() delta = self.__delta.consume() beta = math.cos(2 * math.pi / period) gamma = 1 / math.cos(4 * math.pi * delta / period) alpha = gamma - math.sqrt(gamma * gamma - 1) i = self.__input.consumed bp2 = self._values[-min(len(self), 2)] if len(self) >= 1 else 0.5 * ( 1 - alpha) * (self.__input[i - 2] - self.__input[i - 4]) bp1 = self._values[-1] if len(self) >= 2 else 0.5 * (1 - alpha) * ( self.__input[i - 1] - self.__input[i - 3]) + beta * (1 + alpha) * bp2 bp = 0.5 * (1 - alpha) * (self.__input[i] - self.__input[i - 2] ) + beta * (1 + alpha) * bp1 - alpha * bp2 self._values.append(bp) self.__input.consume()
class ZScore(Function): def __init__(self, input_, period): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) Function.__init__(self) def _next(self): self.inputs.update() if len(self) >= len(self.__period) or len(self) + int( self.__period.max) > len(self.__input): raise StopIteration self.inputs.sync({self.__input: int(self.__period.max)}) latest_input = self.__input.consume() period = int(min(self.__period.consume(), self.__period.max)) input_ = self.__input[self.__input.consumed - period:self.__input.consumed] mean = np.mean(input_) std = np.std(input_) self._values.append((latest_input - mean) / std)
class Ema(Function): def __init__(self, input_, period): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) Function.__init__(self) def _first(self): self.inputs.update() if len(self.__period) == 0 or int(self.__period.max) > len(self.__input): raise StopIteration self.inputs.sync({self.__input: int(self.__period.max)}) self.__input.consume() period = int(min(self.__period.consume(), self.__period.max)) self._values.append(np.mean(self.__input[self.__input.consumed - period:self.__input.consumed])) def _next(self): self.inputs.update() if len(self) >= len(self.__period) or len(self) + int(self.__period.max) > len(self.__input): raise StopIteration input_ = self.__input.consume() period = int(min(self.__period.consume(), self.__period.max)) weight = 2 / (period + 1) self._values.append((input_ - self._values[-1]) * weight + self._values[-1])
class Low(Function): def __init__(self, input_, period): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) self.__last_boundary = 0 self.__last_period = None self.lows = [] Function.__init__(self) def _next(self): self.inputs.update() if len(self) >= len(self.__period) or len(self) + int( self.__period.max) > len(self.__input): raise StopIteration self.inputs.sync({self.__input: int(self.__period.max)}) self.__input.consume() period = int(min(self.__period.consume(), self.__period.max)) if self.__last_period is None: self.__last_period = period if (len(self) - self.__last_boundary) % self.__last_period > 0: self._values.append(self._values[-1]) else: self.__last_boundary = len(self) self._values.append( min(self.__input[self.__input.consumed - self.__last_period:self.__input.consumed])) self.__last_period = period self.lows.append((self._values[-1], self.__last_boundary))
class StandardDeviation(Function): def __init__(self, input_, period): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) self.__mean = 0.0 self.__error_sum = 0.0 self.__variance = 0.0 Function.__init__(self) def _first(self): self.inputs.update() if len(self.__period) == 0 or int(self.__period.max) > len( self.__input): raise StopIteration self.inputs.sync({self.__input: int(self.__period.max)}) self.__input.consume() period = int(min(self.__period.consume(), self.__period.max)) input_ = np.array(self.__input[self.__input.consumed - period:self.__input.consumed]) self.__mean = np.mean(input_) self.__error_sum = np.sum((input_ - self.__mean)**2) self.__variance = self.__error_sum / period self._values.append(np.sqrt(self.__variance)) def _next(self): self.inputs.update() if len(self) >= len(self.__period) or len(self) + int( self.__period.max) > len(self.__input): raise StopIteration value_discard = self.__input[self.__input.consumed - 1] value_new = self.__input.consume() period_discard = int( min(self.__period[self.__period.consumed - 1], self.__period.max)) period_new = int(min(self.__period.consume(), self.__period.max)) delta_new_discard = value_new - value_discard delta_discard_mean = value_discard - self.__mean delta_new_mean_before = value_new - self.__mean self.__mean += delta_new_discard / period_new delta_new_mean_after = value_new - self.__mean self.__error_sum -= ( (delta_discard_mean * delta_discard_mean - delta_new_mean_before * delta_new_mean_after) * period_discard + delta_new_discard * delta_new_mean_after) / (period_discard - 1) self.__variance = abs(self.__error_sum / period_new) self._values.append(np.sqrt(self.__variance))
def __init__(self, input_, period): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) self.__last_boundary = 0 self.__last_period = None self.lows = [] Function.__init__(self)
def __init__(self, input_, period): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) self.__mean = 0.0 self.__error_sum = 0.0 self.__variance = 0.0 Function.__init__(self)
def __init__(self, input_, period, zrange, alpha): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) self.__zrange = FunctionInput(zrange) self.__alpha = FunctionInput(alpha) self.__zscore = FunctionInput(ZScore(input_, period)) self.__entropy = 1.0 self.__ema = 0 Function.__init__(self)
class Rsi(Function): def __init__(self, input_, period): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) self.__slope = FunctionInput(Slope(input_)) self.__average_gain = None self.__average_loss = None Function.__init__(self) def _first(self): self.inputs.update() if len(self.__period) == 0 or int(self.__period.max) > len( self.__slope): raise StopIteration self.inputs.sync({self.__slope: int(self.__period.max)}) self.__input.consume() period = int(min(self.__period.consume(), self.__period.max)) self.__slope.consume() self.__average_gain = np.mean([ max(d, 0) for d in self.__slope[self.__slope.consumed - period:self.__slope.consumed] ]) self.__average_loss = np.mean([ min(d, 0) for d in self.__slope[self.__slope.consumed - period:self.__slope.consumed] ]) self._values.append( 100 - 100 / (1 + self.__average_gain / -self.__average_loss) ) # XXX We'll need some kind of zero division protection def _next(self): self.inputs.update() if len(self) >= len(self.__period) or len(self) + int( self.__period.max) > len(self.__slope): raise StopIteration self.__input.consume() period = int(min(self.__period.consume(), self.__period.max)) slope = self.__slope.consume() self.__average_gain = (self.__average_gain * (period - 1) + max(slope, 0)) / period self.__average_loss = (self.__average_loss * (period - 1) + min(slope, 0)) / period self._values.append(100 - 100 / (1 + self.__average_gain / -self.__average_loss))
class ChangePeriod(Function): def __init__(self, input_, change, period): self.__input = FunctionInput(input_) self.__change = FunctionInput(change) self.__period = FunctionInput(period) Function.__init__(self) def _next(self): self.inputs.update() if len(self) >= min(len(self.__change), len( self.__period)) or len(self) + int(self.__period.max) > len( self.__input): raise StopIteration self.inputs.sync({self.__input: int(self.__period.max)}) self.__input.consume() change = self.__change.consume() period = int(min(self.__period.consume(), self.__period.max)) frequencies = np.fft.rfftfreq(period)[::-1] samples = np.array(self.__input[self.__input.consumed - period:self.__input.consumed]) min_ = np.min(samples) range_ = np.max(samples) - min_ fourier_transform = np.fft.rfft(2 * (samples - min_) / range_ - 1) / (0.5 * period) magnitude = np.abs(fourier_transform)[::-1] target_magnitude = np.mean(samples) * abs(0.5 * change) / range_ if target_magnitude <= np.max(magnitude): if target_magnitude > magnitude[0]: for i, m in enumerate(magnitude): if m >= target_magnitude: x = (m - target_magnitude) / (m - magnitude[i - 1]) frequency = frequencies[i - 1] * x + frequencies[i] * ( 1 - x) self._values.append(min(1 / frequency, period)) break else: self._values.append(1 / frequencies[0]) else: self._values.append(period)
def __init__(self, input_, std_dev_period, band_min, band_max, band_count, interpolate): self.__input = FunctionInput(input_) self.__interpolate = interpolate self.__band_periods = np.geomspace(band_min, band_max, band_count) self.__bands = list( FunctionInput( StandardDeviation(EhlersWayBandpass(input_, period, 0.5), std_dev_period)) for period in self.__band_periods) for i, band in enumerate(self.__bands): setattr(self, "_DominantBand__band{}".format(i), band) Function.__init__(self)
class Slope(Function): def __init__(self, input_): self.__input = FunctionInput(input_) Function.__init__(self) def _next(self): self.inputs.update() if len(self) + 2 > len(self.__input): raise StopIteration self.inputs.sync({self.__input: 2}) self._values.append(self.__input[self.__input.consumed] - self.__input[self.__input.consumed - 1]) self.__input.consume()
def __init__(self, input_, pooling_period, period_count, multiplier): self.__period_count = FunctionInput(period_count) self.__multiplier = FunctionInput(multiplier) self.__low = FunctionInput(Low(Skip(input_, 1), pooling_period)) self.__atr = FunctionInput(Atr(input_, pooling_period, period_count)) self.__current_period = 0 self.__current_period_count = None Function.__init__(self)
class AroonOscillator(Function): def __init__(self, input_, period): self.__up = FunctionInput(AroonUp(input_, period)) self.__down = FunctionInput(AroonDown(input_, period)) Function.__init__(self) def _next(self): self.inputs.update() if len(self) >= len(self.__up): raise StopIteration self.inputs.sync() up = self.__up.consume() down = self.__down.consume() self._values.append(up - down)
class Subtract(Function): def __init__(self, input1, input2): self.input1 = FunctionInput(input1) self.input2 = FunctionInput(input2) Function.__init__(self) def _next(self): self.inputs.update() if len(self) >= len(self.input1) or len(self) >= len(self.input2): raise StopIteration self.inputs.sync() a = self.input1.consume() b = self.input2.consume() self._values.append(a - b)
class ChandelierExitLong(Function): def __init__(self, input_, pooling_period, period_count, multiplier): self.__period_count = FunctionInput(period_count) self.__multiplier = FunctionInput(multiplier) self.__high = FunctionInput(High(Skip(input_, 1), pooling_period)) self.__atr = FunctionInput(Atr(input_, pooling_period, period_count)) self.__current_period = 0 self.__current_period_count = None Function.__init__(self) def _next(self): self.inputs.update() if len(self) >= min(len(self.__atr), len(self.__high), len(self.__multiplier)): raise StopIteration self.inputs.sync() period_count = int( min(self.__period_count.consume(), self.__period_count.max)) self.__high.consume() atr = self.__atr.consume() multiplier = self.__multiplier.consume() current_period = max(period + self.__current_period for period, ( high, index) in enumerate(self.__high.highs[self.__current_period:]) if index < self.__high.consumed) if self.__current_period != current_period: self.__current_period = current_period self.__current_period_count = period_count period_high = max( high for high, _ in self.__high.highs[self.__current_period - self.__current_period_count + 1:self.__current_period + 1]) self._values.append(period_high - atr * multiplier)
class Divide(Function): def __init__(self, input1, input2): self.input1 = FunctionInput(input1) self.input2 = FunctionInput(input2) Function.__init__(self) def _next(self): self.inputs.update() if len(self) >= len(self.input1) or len(self) >= len(self.input2): raise StopIteration self.inputs.sync() a = self.input1.consume() b = self.input2.consume() b = b if b != 0 else 0.00000001 self._values.append(a / b)
class Lerp(Function): def __init__(self, input1, input2, x): self.input1 = FunctionInput(input1) self.input2 = FunctionInput(input2) self.x = FunctionInput(x) Function.__init__(self) def _next(self): self.inputs.update() if len(self) >= min(len(self.input1), len(self.input2), len(self.x)): raise StopIteration self.inputs.sync() a = self.input1.consume() b = self.input2.consume() x = self.x.consume() self._values.append(a * (1 - x) + b * x)
class DominantBand(Function): def __init__(self, input_, std_dev_period, band_min, band_max, band_count, interpolate): self.__input = FunctionInput(input_) self.__interpolate = interpolate self.__band_periods = np.geomspace(band_min, band_max, band_count) self.__bands = list( FunctionInput( StandardDeviation(EhlersWayBandpass(input_, period, 0.5), std_dev_period)) for period in self.__band_periods) for i, band in enumerate(self.__bands): setattr(self, "_DominantBand__band{}".format(i), band) Function.__init__(self) def _next(self): self.inputs.update() if len(self) >= len(self.__bands[0]): raise StopIteration self.inputs.sync() self.__input.consume() std_devs = [band.consume() for band in self.__bands] value = 0 if self.__interpolate: value = 1 normalized_std_devs = np.array(std_devs) / np.sum(std_devs) for i in range(0, len(self.__bands)): value *= self.__band_periods[i]**normalized_std_devs[i] else: value = self.__band_periods[np.argmax(std_devs)] self._values.append(value)
def __init__(self, input_, period): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) self.__slope = FunctionInput(Slope(input_)) self.__average_gain = None self.__average_loss = None Function.__init__(self)
class AroonDown(Function): def __init__(self, input_, period): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) Function.__init__(self) def _next(self): self.inputs.update() if len(self) >= len(self.__period) or len(self) + int(self.__period.max) > len(self.__input): raise StopIteration self.inputs.sync({self.__input: int(self.__period.max)}) self.__input.consume() period = int(min(self.__period.consume(), self.__period.max)) low = (None, None) for i in range(self.__input.consumed - period - 1, self.__input.consumed): if low[0] is not None and self.__input[i] < low[0] or low[0] is None: low = (self.__input[i], self.__input.consumed - i) self._values.append(100 * (period - low[1]) / period)
class Skip(Function): def __init__(self, input_, skip): self.__input = FunctionInput(input_) self.__skip = int(skip) Function.__init__(self) def _next(self): self.inputs.update() if len(self) + self.__skip > len(self.__input): raise StopIteration self.inputs.sync({self.__input: self.__skip}) self._values.append(self.__input.consume())
def __init__(self, input_, pooling_period, period_count): self.__input = FunctionInput(input_) self.__pooling_period = FunctionInput(pooling_period) self.__period_count = FunctionInput(period_count) self.__last_pool_boundary = None self.__last_pooling_period = None self.__trs = [] self.atrs = [] Function.__init__(self)
def __init__(self, input_, period, delta): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) self.__delta = FunctionInput(delta) Function.__init__(self)
class EntropyEma(Function): def __init__(self, input_, period, zrange, alpha): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) self.__zrange = FunctionInput(zrange) self.__alpha = FunctionInput(alpha) self.__zscore = FunctionInput(ZScore(input_, period)) self.__entropy = 1.0 self.__ema = 0 Function.__init__(self) def _first(self): self.inputs.update() if len(self.__period) == 0 or int(self.__period.max) > len(self.__input): # XXX Add other inputs raise StopIteration self.inputs.sync({self.__input: int(self.__period.max)}) self.__input.consume() period = int(min(self.__period.consume(), self.__period.max)) self.__zrange.consume() self.__alpha.consume() self.__ema = np.mean(self.__input[self.__input.consumed - period:self.__input.consumed]) self._values.append(self.__entropy) def _next(self): self.inputs.update() if len(self) >= len(self.__period) or len(self) + int(self.__period.max) > len(self.__input): # XXX raise StopIteration input_ = self.__input.consume() period = int(min(self.__period.consume(), self.__period.max)) zrange = self.__zrange.consume() alpha = self.__alpha.consume() old_z = self.__zscore[self.__zscore.consumed - 1] z = self.__zscore.consume() action = max((abs(z) - abs(old_z)) / zrange, 0) if action > 0: self.__entropy = max(self.__entropy - action, 0) else: self.__entropy = 1 - (1 - self.__entropy) * alpha weight = 2 / (period + 1) #self._values.append((input_ - self._values[-1]) * weight + self._values[-1]) self._values.append(self.__entropy)
class Atr(Function): def __init__(self, input_, pooling_period, period_count): self.__input = FunctionInput(input_) self.__pooling_period = FunctionInput(pooling_period) self.__period_count = FunctionInput(period_count) self.__last_pool_boundary = None self.__last_pooling_period = None self.__trs = [] self.atrs = [] Function.__init__(self) def _first(self): self.inputs.update() if min(len(self.__pooling_period), len(self.__period_count) ) == 0 or int(self.__pooling_period.max) * int( self.__period_count.max) + 1 > len(self.__input): raise StopIteration self.inputs.sync({ self.__input: int(self.__pooling_period.max) * int(self.__period_count.max) + 1 }) self.__input.consume() pooling_period = int( min(self.__pooling_period.consume(), self.__pooling_period.max)) period_count = int( max(min(self.__period_count.consume(), self.__period_count.max), 1)) for period in range(period_count): input_ = self.__input[self.__input.consumed - (period_count - period) * pooling_period:self.__input.consumed - (period_count - period - 1) * pooling_period] high = max(input_) low = min(input_) close = self.__input[self.__input.consumed - (period_count - period) * pooling_period - 1] self.__trs.append( max(high - low, abs(high - close), abs(low - close))) self.__last_pool_boundary = 0 self.__last_pooling_period = pooling_period self._values.append(np.mean(self.__trs)) self.atrs.append(self._values[-1]) def _next(self): self.inputs.update() if len(self) >= min( len(self.__pooling_period), len(self.__period_count) ) or len(self) + int(self.__pooling_period.max) * int( self.__period_count.max) + 1 > len(self.__input): raise StopIteration self.__input.consume() pooling_period = int( min(self.__pooling_period.consume(), self.__pooling_period.max)) period_count = int( max(min(self.__period_count.consume(), self.__period_count.max), 1)) if len(self) - self.__last_pool_boundary < self.__last_pooling_period: self._values.append(self._values[-1]) else: input_ = self.__input[self.__input.consumed - self.__last_pooling_period:self.__input. consumed] high = max(input_) low = min(input_) close = self.__input[self.__input.consumed - self.__last_pooling_period - 1] self.__last_pool_boundary = len(self) self.__last_pooling_period = pooling_period self.__trs.append( max(high - low, abs(high - close), abs(low - close))) self._values.append( (self._values[-1] * (period_count - 1) + self.__trs[-1]) / period_count) self.atrs.append(self._values[-1])
def __init__(self, value): FunctionInput.__init__(self, value)
def __init__(self, input_, skip): self.__input = FunctionInput(input_) self.__skip = int(skip) Function.__init__(self)
def __init__(self, input_, period): self.__input = FunctionInput(input_) self.__period = FunctionInput(period) Function.__init__(self)
def __init__(self, input_): self.__input = FunctionInput(input_) Function.__init__(self)