def _update_activation_config(to_update_data, current_config, config_file_path, config_file, deactivate_others): from octobot_commons.tentacles_management.class_inspector import get_class_from_string, evaluator_parent_inspection something_changed = False for element_name, activated in to_update_data.items(): if element_name in current_config: active = activated if isinstance( activated, bool) else activated.lower() == "true" current_activation = current_config[element_name] if current_activation != active: get_logger().info( f"{config_file} updated: {element_name} " f"{'activated' if active else 'deactivated'}") current_config[element_name] = active something_changed = True if deactivate_others: import evaluator.Strategies as strategies for element_name, activated in current_config.items(): if element_name not in to_update_data: if current_config[element_name]: # do not deactivate strategies config_class = get_class_from_string( element_name, strategies.StrategiesEvaluator, strategies, evaluator_parent_inspection) if config_class is None: get_logger().info( f"{config_file} updated: {element_name} " f"{'deactivated'}") current_config[element_name] = False something_changed = True if something_changed: with open(config_file_path, "w+") as config_file_w: config_file_w.write(dump_json(current_config))
def get_class(config, class_type) -> object: classes = get_classes(config, class_type) if classes and len(classes) > 1: get_logger(__name__).warning( f"More than one instance of {class_type} available, " f"using {classes[0]}.") return classes[0]
def check_last_prices(self, last_prices, price_to_check, inferior, simulated_time=False) -> bool: if last_prices: prices = [ p[ECOC.PRICE.value] for p in last_prices if not math.isnan(p[ECOC.PRICE.value]) and ( p[ECOC.TIMESTAMP.value] >= self.creation_time or simulated_time) ] if prices: if inferior: if float(min(prices)) < price_to_check: get_logger(self.get_name()).debug( f"{self.symbol} last prices: {prices}, " f"ask for {'inferior' if inferior else 'superior'} " f"to {price_to_check}") return True else: if float(max(prices)) > price_to_check: get_logger(self.get_name()).debug( f"{self.symbol} last prices: {prices}, " f"ask for {'inferior' if inferior else 'superior'} " f"to {price_to_check}") return True return False
def parse_order_type(raw_order): try: side: TradeOrderSide = TradeOrderSide( raw_order[ExchangeConstantsOrderColumns.SIDE.value]) order_type: TradeOrderType = TradeOrderType( raw_order[ExchangeConstantsOrderColumns.TYPE.value]) if side == TradeOrderSide.BUY: if order_type == TradeOrderType.LIMIT or order_type == TradeOrderType.LIMIT_MAKER: order_type = TraderOrderType.BUY_LIMIT elif order_type == TradeOrderType.MARKET: order_type = TraderOrderType.BUY_MARKET else: order_type = _get_sell_and_buy_types(order_type) elif side == TradeOrderSide.SELL: if order_type == TradeOrderType.LIMIT or order_type == TradeOrderType.LIMIT_MAKER: order_type = TraderOrderType.SELL_LIMIT elif order_type == TradeOrderType.MARKET: order_type = TraderOrderType.SELL_MARKET else: order_type = _get_sell_and_buy_types(order_type) return side, order_type except KeyError: get_logger( Order.__class__.__name__).error("Failed to parse order type") return None, None
async def _handle_creation(bot_id, action, data): if action == OctoBotChannelTradingActions.EXCHANGE.value: try: config = data[OctoBotChannelTradingDataKeys.EXCHANGE_CONFIG.value] exchange_builder = create_exchange_builder(config, data[OctoBotChannelTradingDataKeys.EXCHANGE_NAME.value]) \ .has_matrix(data[OctoBotChannelTradingDataKeys.MATRIX_ID.value]) \ .use_tentacles_setup_config(data[OctoBotChannelTradingDataKeys.TENTACLES_SETUP_CONFIG.value]) \ .set_bot_id(bot_id) \ .is_rest_only() if is_trader_enabled_in_config(config): exchange_builder.is_real() elif is_trader_simulator_enabled_in_config(config): exchange_builder.is_simulated() backtesting = data[OctoBotChannelTradingDataKeys.BACKTESTING.value] if backtesting is not None: exchange_builder.is_backtesting(backtesting) await exchange_builder.build() await get_chan_at_id(OctoBotChannelsName.OCTOBOT_CHANNEL.value, bot_id).get_internal_producer() \ .send(bot_id=bot_id, subject=OctoBotChannelSubjects.NOTIFICATION.value, action=action, data={OctoBotChannelTradingDataKeys.EXCHANGE_ID.value: exchange_builder.exchange_manager.id}) except Exception as e: get_logger(OCTOBOT_CHANNEL_TRADING_CONSUMER_LOGGER_TAG).error(f"Error when creating new exchange {e}")
def _is_docker_available() -> bool: try: _get_client() return True except docker.errors.NotFound: get_logger().error("Docker service not found") return False
def create_venv_if_necessary(venv_path): venv_abs_path = os.path.abspath(venv_path) if not _is_venv_installed(venv_path=venv_path): get_logger().info(f"Creating a virtual env in {venv_abs_path}") _create_venv(venv_path=venv_path) else: get_logger().info(f"Using virtual env in {venv_abs_path}")
async def _handle_creation(bot_id, action, data): if action == OctoBotChannelTradingActions.EXCHANGE.value: try: config = data[OctoBotChannelTradingDataKeys.EXCHANGE_CONFIG.value] exchange_builder = create_exchange_builder(config, data[OctoBotChannelTradingDataKeys.EXCHANGE_NAME.value]) \ .has_matrix(data[OctoBotChannelTradingDataKeys.MATRIX_ID.value]) \ .use_tentacles_setup_config(data[OctoBotChannelTradingDataKeys.TENTACLES_SETUP_CONFIG.value]) \ .set_bot_id(bot_id) _set_exchange_type_details( exchange_builder, config, data[OctoBotChannelTradingDataKeys.BACKTESTING.value]) await exchange_builder.build() await get_chan_at_id(OctoBotChannelsName.OCTOBOT_CHANNEL.value, bot_id).get_internal_producer() \ .send(bot_id=bot_id, subject=OctoBotChannelSubjects.NOTIFICATION.value, action=action, data={OctoBotChannelTradingDataKeys.EXCHANGE_ID.value: exchange_builder.exchange_manager.id}) except TradingModeIncompatibility as e: get_logger(OCTOBOT_CHANNEL_TRADING_CONSUMER_LOGGER_TAG).error( f"Error when initializing trading mode, {data[OctoBotChannelTradingDataKeys.EXCHANGE_NAME.value]} " f"exchange connection is closed to increase performances: {e}") except Exception as e: get_logger(OCTOBOT_CHANNEL_TRADING_CONSUMER_LOGGER_TAG).error( f"Error when creating new exchange: {e}")
async def on_fill_complete(self): """ Post fill actions """ try: # compute order fees self.fee = self.get_computed_fee() get_logger(self.get_logger_name()).debug( f"{self.symbol} of size {self.origin_quantity} {self.currency} " f"filled {self.filled_quantity} {self.currency} " f"on {self.exchange_manager.exchange_name} " f"at {self.filled_price}") await get_chan(ORDERS_CHANNEL, self.exchange_manager.id).get_internal_producer() \ .send(cryptocurrency=self.currency, symbol=self.symbol, order=self.to_dict(), is_from_bot=True, is_closed=True, is_updated=False) await self.exchange_manager.trader.close_filled_order(self) except Exception as e: get_logger(self.get_logger_name()).exception( e, True, f"Fail to execute fill complete action : {e}.")
async def create_trading_mode( trading_mode_class: AbstractTradingMode.__class__, config: dict, exchange_manager: ExchangeManager, cryptocurrency: str = None, symbol: str = None, time_frame: object = None, bot_id: str = None) -> AbstractTradingMode: try: trading_mode: AbstractTradingMode = trading_mode_class( config, exchange_manager) trading_mode.cryptocurrency = cryptocurrency trading_mode.symbol = symbol trading_mode.time_frame = time_frame trading_mode.bot_id = bot_id await trading_mode.initialize() get_logger(f"{LOGGER_TAG}[{exchange_manager.exchange_name}]") \ .debug(f"{trading_mode.get_name()} started for " f"[cryptocurrency={cryptocurrency if cryptocurrency else CONFIG_WILDCARD}," f" symbol={symbol if symbol else CONFIG_WILDCARD}," f" time_frame={time_frame if time_frame else CONFIG_WILDCARD}]") return trading_mode except RuntimeError as e: get_logger(LOGGER_TAG).error(e.args[0]) raise e
def _get_tentacles_values(evaluations, tentacle_type_node, exchange): try: from octobot_evaluators.api.matrix import get_children_list, has_children, get_value except ImportError: get_logger("InterfaceUtil").error( "_get_tentacles_values requires OctoBot-Evaluators package installed" ) return {} for tentacle_name, tentacle_name_node in get_children_list( tentacle_type_node).items(): evaluations[exchange][tentacle_name] = {} for cryptocurrency, cc_node in get_children_list( tentacle_name_node).items(): evaluations[exchange][tentacle_name][cryptocurrency] = {} if has_children(cc_node): for symbol, symbol_node in get_children_list(cc_node).items(): if has_children(symbol_node): evaluations[exchange][tentacle_name][symbol] = {} for time_frame, time_frame_node in get_children_list( symbol_node).items(): evaluations[exchange][tentacle_name][symbol][time_frame] = \ get_value(time_frame_node) else: evaluations[exchange][tentacle_name][ symbol] = get_value(symbol_node) else: evaluations[exchange][tentacle_name][ cryptocurrency] = get_value(cc_node)
def get_activated_trading_mode( config, tentacles_setup_config) -> AbstractTradingMode.__class__: if tentacles_setup_config is not None: try: trading_modes = [ tentacle_class for tentacle_class in get_activated_tentacles( tentacles_setup_config) if get_deep_class_from_parent_subclasses( tentacle_class, AbstractTradingMode) ] if len(trading_modes) > 1: raise ConfigTradingError( f"More than one activated trading mode found in your tentacle configuration, " f"please activate only one") elif trading_modes: trading_mode_class = get_deep_class_from_parent_subclasses( trading_modes[0], AbstractTradingMode) if trading_mode_class is not None: return get_class(config, trading_mode_class) except ModuleNotFoundError as e: get_logger("get_activated_trading_mode").error( f"Error when loading the activated trading mode: {e}") raise ConfigTradingError( f"Please ensure your tentacles configuration file is valid and at least one trading " f"mode is activated")
async def run_independent_backtesting(data_files, timeout=10, use_loggers=True, run_on_common_part_only=True): independent_backtesting = None try: config_to_use = load_test_config() if use_loggers: init_logger() independent_backtesting = create_independent_backtesting( config_to_use, load_test_tentacles_config(), data_files, "", run_on_common_part_only=run_on_common_part_only) await initialize_and_run_independent_backtesting( independent_backtesting, log_errors=False) await independent_backtesting.join_backtesting_updater(timeout) return independent_backtesting except MissingTimeFrame: # ignore this exception: is due to missing of the only required time frame return independent_backtesting except asyncio.TimeoutError as e: get_logger().exception( e, True, f"Timeout after waiting for backtesting for {timeout} seconds.") # stop backtesting to prevent zombie tasks await stop_independent_backtesting(independent_backtesting) raise except Exception as e: get_logger().exception(e, True, str(e)) # stop backtesting to prevent zombie tasks await stop_independent_backtesting(independent_backtesting) raise
def get_class(config, class_type): classes = AdvancedManager.get_classes(config, class_type) if classes and len(classes) > 1: get_logger(AdvancedManager.__name__).warning( f"More than one instance of {class_type} available, " f"using {classes[0]}.") return classes[0]
async def _start_service_feed(service_feed, edited_config): if not await start_service_feed(service_feed, False, edited_config): get_logger(OCTOBOT_CHANNEL_SERVICE_CONSUMER_LOGGER_TAG).error( f"Failed to start {service_feed.get_name()}. Evaluators requiring this service feed " f"might not work properly") return False return True
def get_activated_trading_mode(config): if CONFIG_TRADING_TENTACLES in config: try: trading_modes = [ class_str for class_str, activated in config[CONFIG_TRADING_TENTACLES].items() if activated and get_deep_class_from_parent_subclasses( class_str, AbstractTradingMode) ] if len(trading_modes) > 1: raise ConfigTradingError( f"More than one activated trading mode found in your {CONFIG_TRADING_FILE_PATH} file, " f"please activate only one") elif trading_modes: trading_mode_class = get_deep_class_from_parent_subclasses( trading_modes[0], AbstractTradingMode) if trading_mode_class is not None: return get_class(config, trading_mode_class) except ModuleNotFoundError as e: get_logger("get_activated_trading_mode").error( f"Error when loading a trading mode: {e} " f"referenced in {CONFIG_TRADING_FILE_PATH} file") raise ConfigTradingError( f"Please ensure your {CONFIG_TRADING_FILE_PATH} file is valid and at least one trading " f"mode is activated")
def update_from_raw(self, raw_order): if self.side is None or self.order_type is None: try: self._update_type_from_raw(raw_order) if self.taker_or_maker is None: self._update_taker_maker() except KeyError: get_logger(self.__class__.__name__).warning( "Failed to parse order side and type") return self.update( symbol=str( raw_order.get(ExchangeConstantsOrderColumns.SYMBOL.value, None)), current_price=raw_order.get( ExchangeConstantsOrderColumns.PRICE.value, 0.0), quantity=raw_order.get(ExchangeConstantsOrderColumns.AMOUNT.value, 0.0), price=raw_order.get(ExchangeConstantsOrderColumns.PRICE.value, 0.0), status=parse_order_status(raw_order), order_id=str( raw_order.get(ExchangeConstantsOrderColumns.ID.value, None)), quantity_filled=raw_order.get( ExchangeConstantsOrderColumns.FILLED.value, 0.0), filled_price=raw_order.get( ExchangeConstantsOrderColumns.PRICE.value, 0.0), total_cost=raw_order.get(ExchangeConstantsOrderColumns.COST.value, 0.0), fee=raw_order.get(ExchangeConstantsOrderColumns.FEE.value, None), timestamp=raw_order.get( ExchangeConstantsOrderColumns.TIMESTAMP.value, None))
async def _on_price_hit(self): """ Is called when the trailing price is hit """ prices_manager = self.exchange_manager.exchange_symbols_data. \ get_exchange_symbol_data(self.symbol).prices_manager get_logger(self.get_logger_name()).debug(f"New price hit {prices_manager.mark_price}, replacing stop...") await self._reset_events(prices_manager.mark_price, prices_manager.mark_price_set_time)
def _handle_exception(exception, resource_key, catch_exception, default_response): if catch_exception: get_logger("ExternalResourcesManager") \ .error(f"Exception when calling get_external_resource for {resource_key} key: {exception}") return default_response else: raise exception
def get_single_deepest_child_class(clazz) -> object: children_classes = clazz.__subclasses__() if len(children_classes) == 0: return clazz if len(children_classes) > 1: get_logger(__name__).error( f"More than one child class of {clazz}, expecting one, " f"using {children_classes[0]}") return get_single_deepest_child_class(children_classes[0])
def del_exchange(self, exchange_name, exchange_manager_id) -> None: try: self.exchanges[exchange_name].pop(exchange_manager_id, None) if not self.exchanges[exchange_name]: self.exchanges.pop(exchange_name, None) except KeyError: get_logger(self.__class__.__name__).warning(f"Can't del exchange {exchange_name} " f"with id {exchange_manager_id}")
async def on_fill(self, force_fill=False, is_from_exchange_data=False): if self.is_open(): self.state = FillOrderState( self, is_from_exchange_data=is_from_exchange_data) await self.state.initialize(forced=force_fill) else: get_logger(self.get_logger_name()).debug( f"Trying to fill a previously filled or canceled order: " f"ignored fill call for {self}")
async def create_trading_mode(config, exchange_manager) -> None: try: trading_mode = get_activated_trading_mode(config)(config, exchange_manager) await trading_mode.initialize() get_logger(f"{LOGGER_TAG}[{exchange_manager.exchange.name}]")\ .debug(f"Using {trading_mode.get_name()} trading mode") except RuntimeError as e: get_logger(LOGGER_TAG).error(e.args[0]) raise e
def create_advanced_types_list(clazz, config) -> list: advanced_class_list = [ class_type for subclass in clazz.__subclasses__() for class_type in get_classes(config, subclass) ] if not __check_duplicate(advanced_class_list): get_logger(__name__).warning("Duplicate class name.") return advanced_class_list
def parse_time_frames(time_frames_string_list): result_list = [] for time_frame_string in time_frames_string_list: try: result_list.append(TimeFrames(time_frame_string)) except ValueError: get_logger(LOGGER_TAG).error( "No time frame available for: '{0}'. Available time " "frames are: {1}. '{0}' time frame requirement " "ignored.".format(time_frame_string, [t.value for t in TimeFrames])) return result_list
def install(image_name=OCTOBOT_IMAGE, image_tag=OCTOBOT_STABLE_TAG, container_name=OCTOBOT_CONTAINER_NAME, use_arm_image=False): if _is_docker_available() and not _is_container_running(container_name=container_name): if use_arm_image: image_name = OCTOBOT_PI_IMAGE _pull_octobot_image(_get_complete_image(image_name, image_tag)) _run_octobot_container(_get_complete_image(image_name, image_tag), container_name=container_name) else: get_logger().error("Docker container already exists")
def _run_on_octobot_container(args: list, image_name=OCTOBOT_IMAGE, image_tag=OCTOBOT_STABLE_TAG, container_name=OCTOBOT_CONTAINER_NAME, use_arm_image=False): if _is_docker_available() and _is_container_running(container_name=container_name): if use_arm_image: image_name = OCTOBOT_PI_IMAGE return _run_octobot_container(_get_complete_image(image_name, image_tag), container_name=container_name, args=args) else: get_logger().error("Docker container not found")
def create_advanced_evaluator_types_list(evaluator_class, config): evaluator_advanced_eval_class_list = [] for evaluator_subclass in evaluator_class.__subclasses__(): for eval_class in evaluator_subclass.__subclasses__(): for eval_class_type in AdvancedManager.get_classes( config, eval_class): evaluator_advanced_eval_class_list.append(eval_class_type) if not AdvancedManager._check_duplicate( evaluator_advanced_eval_class_list): get_logger( AdvancedManager.__name__).warning("Duplicate evaluator name.") return evaluator_advanced_eval_class_list
def clear_dictionaries_by_keys(dict_dest, dict_src): for key in dict_src: src_val = dict_src[key] if key in dict_dest: dest_val = dict_dest[key] if src_val == DELETE_ELEMENT_VALUE: dict_dest.pop(key) elif isinstance(dest_val, dict) and isinstance(src_val, dict): dict_dest[key] = clear_dictionaries_by_keys(dest_val, src_val) else: get_logger().error( f"Conflict when deleting dict element with key : {key}") return dict_dest
def get_matrix_list(): try: from octobot_evaluators.api.matrix import get_matrix, get_node_children_by_names, get_children_list except ImportError: get_logger("InterfaceUtil").error( "get_matrix_list requires OctoBot-Evaluators package installed") return {} evaluations = {} matrix = get_matrix(get_bot_api().get_matrix_id()) for exchange, exchange_node in get_node_children_by_names(matrix).items(): evaluations[exchange] = {} for tentacle_type_node in get_children_list(exchange_node).values(): _get_tentacles_values(evaluations, tentacle_type_node, exchange) return evaluations