def get_moving_average_analysis(data, current_moving_average, time_period): time_period_unit_moving_average = tulipy.sma(data, time_period) # equalize array size min_len_arrays = min(len(time_period_unit_moving_average), len(current_moving_average)) # compute difference between 1 unit values and others ( >0 means currently up the other one) values_difference = (current_moving_average[-min_len_arrays:] - time_period_unit_moving_average[-min_len_arrays:]) values_difference = DataUtil.drop_nan(values_difference) if len(values_difference): # indexes where current_unit_moving_average crosses time_period_unit_moving_average crossing_indexes = TrendAnalysis.get_threshold_change_indexes(values_difference, 0) multiplier = 1 if values_difference[-1] > 0 else -1 # check at least some data crossed 0 if crossing_indexes: normalized_data = DataUtil.normalize_data(values_difference) current_value = min(abs(normalized_data[-1])*2, 1) if math.isnan(current_value): return 0 # check <= values_difference.count()-1if current value is max/min if current_value == 0 or current_value == 1: chances_to_be_max = TrendAnalysis.get_estimation_of_move_state_relatively_to_previous_moves_length( crossing_indexes, values_difference) return multiplier*current_value*chances_to_be_max # other case: maxima already reached => return distance to max else: return multiplier*current_value # just crossed the average => neutral return 0
def eval_impl(self): self.eval_note = START_PENDING_EVAL_NOTE long_period_length = 26 if len(self.data[PriceIndexes.IND_PRICE_CLOSE.value]) >= long_period_length: macd, macd_signal, macd_hist = tulipy.macd(self.data[PriceIndexes.IND_PRICE_CLOSE.value], 12, long_period_length, 9) # on macd hist => M pattern: bearish movement, W pattern: bullish movement # max on hist: optimal sell or buy macd_hist = DataUtil.drop_nan(macd_hist) zero_crossing_indexes = TrendAnalysis.get_threshold_change_indexes(macd_hist, 0) last_index = len(macd_hist) - 1 pattern, start_index, end_index = PatternAnalyser.find_pattern(macd_hist, zero_crossing_indexes, last_index) if pattern != PatternAnalyser.UNKNOWN_PATTERN: # set sign (-1 buy or 1 sell) sign_multiplier = -1 if pattern == "W" or pattern == "V" else 1 # set pattern time frame => W and M are on 2 time frames, others 1 pattern_move_time = 2 if (pattern == "W" or pattern == "M") and end_index == last_index else 1 # set weight according to the max value of the pattern and the current value current_pattern_start = start_index price_weight = macd_hist[-1] / macd_hist[current_pattern_start:].max() if sign_multiplier == 1 \ else macd_hist[-1] / macd_hist[current_pattern_start:].min() if not math.isnan(price_weight): self._analyse_pattern(pattern, macd_hist, zero_crossing_indexes, price_weight, pattern_move_time, sign_multiplier)
def get_average_strategy_eval(self, exchange, active_only=False): eval_list = [ s.get_eval_note() for s in self.get_strategies_eval_list(exchange, active_only) if isinstance(s.get_eval_note(), (int, float)) ] return DataUtil.mean(eval_list)
async def eval_impl(self): self.eval_note = False short_period = 35 # standard with klinger long_period = 55 # standard with klinger ema_signal_period = 13 # standard ema signal for klinger kvo = tulipy.kvo(self.data[PriceIndexes.IND_PRICE_HIGH.value], self.data[PriceIndexes.IND_PRICE_LOW.value], self.data[PriceIndexes.IND_PRICE_CLOSE.value], self.data[PriceIndexes.IND_PRICE_VOL.value], short_period, long_period) kvo = DataUtil.drop_nan(kvo) if len(kvo) >= ema_signal_period: kvo_ema = tulipy.ema(kvo, ema_signal_period) ema_difference = kvo - kvo_ema if len(ema_difference) > 1: zero_crossing_indexes = TrendAnalysis.get_threshold_change_indexes( ema_difference, 0) max_elements = 7 to_consider_kvo = min( max_elements, len(ema_difference) - zero_crossing_indexes[-1]) self.eval_note = TrendAnalysis.min_has_just_been_reached( ema_difference[-to_consider_kvo:], acceptance_window=0.9, delay=1)
def eval_impl(self): self.eval_note = START_PENDING_EVAL_NOTE period_length = 14 if len(self.data[ PriceIndexes.IND_PRICE_HIGH.value]) > period_length + 10: min_adx = 7.5 max_adx = 45 neutral_adx = 25 adx = tulipy.adx(self.data[PriceIndexes.IND_PRICE_HIGH.value], self.data[PriceIndexes.IND_PRICE_LOW.value], self.data[PriceIndexes.IND_PRICE_CLOSE.value], period_length) instant_ema = tulipy.ema( self.data[PriceIndexes.IND_PRICE_CLOSE.value], 2) slow_ema = tulipy.ema( self.data[PriceIndexes.IND_PRICE_CLOSE.value], 20) adx = DataUtil.drop_nan(adx) if len(adx): current_adx = adx[-1] current_slows_ema = slow_ema[-1] current_instant_ema = instant_ema[-1] multiplier = -1 if current_instant_ema < current_slows_ema else 1 # strong adx => strong trend if current_adx > neutral_adx: # if max adx already reached => when ADX forms a top and begins to turn down, you should look for a # retracement that causes the price to move toward its 20-day exponential moving average (EMA). adx_last_values = adx[-15:] adx_last_value = adx_last_values[-1] local_max_adx = adx_last_values.max() # max already reached => trend will slow down if adx_last_value < local_max_adx: self.eval_note = multiplier * ( current_adx - neutral_adx) / (local_max_adx - neutral_adx) # max not reached => trend will continue, return chances to be max now else: crossing_indexes = TrendAnalysis.get_threshold_change_indexes( adx, neutral_adx) chances_to_be_max = \ TrendAnalysis.get_estimation_of_move_state_relatively_to_previous_moves_length( crossing_indexes, adx) if len(crossing_indexes) > 2 \ else 0.75 proximity_to_max = min(1, current_adx / max_adx) self.eval_note = multiplier * proximity_to_max * chances_to_be_max # weak adx => change to come else: self.eval_note = multiplier * min(1, ( (neutral_adx - current_adx) / (neutral_adx - min_adx)))
def get_progress(self): if not self.min_time_frame_to_consider: return 0 else: progresses = [] for symbol in self.time_frame_get_times: if symbol in self.min_time_frame_to_consider: current = self.time_frame_get_times[symbol][self.min_time_frame_to_consider[symbol]] nb_max = len(self.data[symbol][self.min_time_frame_to_consider[symbol]]) progresses.append(current / nb_max) return int(DataUtil.mean(progresses) * 100)
def _get_rsi_averages(self): # compute the slow and fast RSI average if len(self.data[ PriceIndexes.IND_PRICE_CLOSE.value]) > self.period_length: rsi_v = tulipy.rsi(self.data[PriceIndexes.IND_PRICE_CLOSE.value], period=self.period_length) rsi_v = DataUtil.drop_nan(rsi_v) if len(rsi_v): slow_average = numpy.mean(rsi_v[-self.slow_eval_count:]) fast_average = numpy.mean(rsi_v[-self.fast_eval_count:]) return slow_average, fast_average, rsi_v return None, None, None
def _find_optimal_configuration_using_results(self): for time_frame in self.all_time_frames: time_frame_sorted_results = self.get_sorted_results(self.run_results, time_frame) self.sorted_results_by_time_frame[time_frame.value] = time_frame_sorted_results results_through_all_time_frame = {} for results in self.sorted_results_by_time_frame.values(): for rank, result in enumerate(results): result_summary = result.get_config_summary() if result_summary not in results_through_all_time_frame: results_through_all_time_frame[result_summary] = [[], 0] results_through_all_time_frame[result_summary][RANK] += rank results_through_all_time_frame[result_summary][TRADES] += result.trades_counts result_list = [(result, trades_and_rank[RANK], DataUtil.mean(trades_and_rank[TRADES])) for result, trades_and_rank in results_through_all_time_frame.items()] self.sorted_results_through_all_time_frame = sorted(result_list, key=lambda res: res[RANK])
def eval_impl(self): eval_proposition = START_PENDING_EVAL_NOTE short_period = 35 # standard with klinger long_period = 55 # standard with klinger ema_signal_period = 13 # standard ema signal for klinger kvo = tulipy.kvo(self.data[PriceIndexes.IND_PRICE_HIGH.value], self.data[PriceIndexes.IND_PRICE_LOW.value], self.data[PriceIndexes.IND_PRICE_CLOSE.value], self.data[PriceIndexes.IND_PRICE_VOL.value], short_period, long_period) kvo = DataUtil.drop_nan(kvo) if len(kvo) >= ema_signal_period: kvo_ema = tulipy.ema(kvo, ema_signal_period) ema_difference = kvo - kvo_ema if len(ema_difference) > 1: zero_crossing_indexes = TrendAnalysis.get_threshold_change_indexes( ema_difference, 0) current_difference = ema_difference[-1] significant_move_threshold = numpy.std(ema_difference) factor = 0.2 if TrendAnalysis.peak_has_been_reached_already( ema_difference[zero_crossing_indexes[-1]:]): if abs(current_difference) > significant_move_threshold: factor = 1 else: factor = 0.5 eval_proposition = current_difference * factor / significant_move_threshold if abs(eval_proposition) > 1: eval_proposition = 1 if eval_proposition > 0 else -1 self.eval_note = eval_proposition
def _create_orders(self, lower_bound, upper_bound, side, sorted_orders, portfolio, current_price, missing_orders, state): if lower_bound >= upper_bound: self.logger.warning(f"No {side} orders for {self.symbol} possible: current price beyond boundaries.") return [] orders = [] selling = side == TradeOrderSide.SELL currency, market = split_symbol(self.symbol) order_limiting_currency = currency if selling else market order_limiting_currency_amount = portfolio[order_limiting_currency][Portfolio.AVAILABLE] \ if order_limiting_currency in portfolio else 0 if state == self.NEW: # create staggered orders starting_bound = lower_bound * (1 + self.spread / 2) if selling else upper_bound * (1 - self.spread / 2) self.flat_spread = AbstractTradingModeCreator.adapt_price(self.symbol_market, current_price * self.spread) orders_count, average_order_quantity = \ self._get_order_count_and_average_quantity(current_price, selling, lower_bound, upper_bound, order_limiting_currency_amount, currency=order_limiting_currency) for i in range(orders_count): price = self._get_price_from_iteration(starting_bound, selling, i) if price is not None: quantity = self._get_quantity_from_iteration(average_order_quantity, self.mode, side, i, orders_count, price) if quantity is not None: orders.append(OrderData(side, quantity, price, self.symbol)) if not orders: self.logger.error(f"Not enough {order_limiting_currency} to create {side.name} orders. " f"For the strategy to work better, add {order_limiting_currency} funds or " f"change change the strategy settings to make less but bigger orders.") else: orders.reverse() if state == self.FILL: # complete missing orders if missing_orders: max_quant_per_order = order_limiting_currency_amount / len(missing_orders) missing_orders_around_spread = [] for missing_order_price, missing_order_side in missing_orders: if missing_order_side == side: previous_o = None following_o = None for o in sorted_orders: if previous_o is None: previous_o = o elif o.origin_price > missing_order_price: following_o = o break else: previous_o = o if previous_o.side == following_o.side: # missing order between similar orders quantity = min(DataUtil.mean([previous_o.origin_quantity, following_o.origin_quantity]), max_quant_per_order / missing_order_price) orders.append(OrderData(missing_order_side, quantity, missing_order_price, self.symbol, False)) self.logger.debug(f"Creating missing orders not around spread: {orders[-1]}") else: missing_orders_around_spread.append((missing_order_price, missing_order_side)) if missing_orders_around_spread: # missing order next to spread starting_bound = upper_bound if selling else lower_bound increment_window = self.flat_increment/2 order_limiting_currency_available_amount = \ portfolio[order_limiting_currency][Portfolio.AVAILABLE] \ if order_limiting_currency in portfolio else 0 portfolio_total = portfolio[order_limiting_currency][Portfolio.TOTAL] \ if order_limiting_currency in portfolio else 0 order_limiting_currency_amount = portfolio_total if order_limiting_currency_available_amount: orders_count, average_order_quantity = \ self._get_order_count_and_average_quantity(current_price, selling, lower_bound, upper_bound, portfolio_total, currency=order_limiting_currency) for missing_order_price, missing_order_side in missing_orders_around_spread: limiting_amount_from_this_order = order_limiting_currency_amount price = starting_bound found_order = False i = 0 while not found_order and i < orders_count: quantity = self._get_quantity_from_iteration(average_order_quantity, self.mode, side, i, orders_count, price) limiting_currency_quantity = quantity if selling else quantity / price if price is not None and limiting_amount_from_this_order > 0 and \ price-increment_window <= missing_order_price <= price+increment_window: if limiting_currency_quantity > limiting_amount_from_this_order or \ limiting_currency_quantity > order_limiting_currency_available_amount: limiting_currency_quantity = min(limiting_amount_from_this_order, order_limiting_currency_available_amount) found_order = True if limiting_currency_quantity is not None: orders.append(OrderData(side, limiting_currency_quantity, price, self.symbol, False)) self.logger.debug(f"Creating missing order around spread {orders[-1]}") price = price - self.flat_increment if selling else price + self.flat_increment limiting_amount_from_this_order -= limiting_currency_quantity i += 1 elif state == self.ERROR: self.logger.error("Impossible to create staggered orders when incompatible order are already in place. " "Cancel these orders of you want to use this trading mode.") return orders
def get_average_trades_count(self): return DataUtil.mean(self.trades_counts)
def get_average_score(self): bot_profitabilities = [ profitability_result[self.BOT_PROFITABILITY] - profitability_result[self.MARKET_PROFITABILITY] for profitability_result in self.run_profitabilities] return DataUtil.mean(bot_profitabilities)