def test_import_config_file_success( self, status_check_all_mock: AsyncMock, get_strategy_pydantic_config_cls: MagicMock): strategy_name = "perpetual_market_making" strategy_file_name = f"{strategy_name}.yml" status_check_all_mock.return_value = True dummy_strategy_config_cls = self.build_dummy_strategy_config_cls( strategy_name) get_strategy_pydantic_config_cls.return_value = dummy_strategy_config_cls cm = ClientConfigAdapter( dummy_strategy_config_cls(no_default="some value")) with TemporaryDirectory() as d: d = Path(d) import_command.STRATEGIES_CONF_DIR_PATH = d temp_file_name = d / strategy_file_name save_to_yml(temp_file_name, cm) self.async_run_with_timeout( self.app.import_config_file(strategy_file_name)) self.assertEqual(strategy_file_name, self.app.strategy_file_name) self.assertEqual(strategy_name, self.app.strategy_name) self.assertTrue( self.cli_mock_assistant.check_log_called_with( "\nEnter \"start\" to start market making.")) self.assertEqual(cm, self.app.strategy_config_map)
def migrate_global_config() -> List[str]: global celo_address logging.getLogger().info("\nMigrating the global config...") global_config_path = CONF_DIR_PATH / "conf_global.yml" errors = [] if global_config_path.exists(): with open(str(global_config_path), "r") as f: data = yaml.safe_load(f) del data["template_version"] client_config_map = ClientConfigAdapter(ClientConfigMap()) _migrate_global_config_modes(client_config_map, data) data.pop("kraken_api_tier", None) data.pop("key_file_path", None) celo_address = data.pop("celo_address", None) keys = list(data.keys()) for key in keys: if key in client_config_map.keys(): _migrate_global_config_field(client_config_map, data, key) for key in data: logging.getLogger().warning(f"Global ConfigVar {key} was not migrated.") errors.extend(client_config_map.validate_model()) if len(errors) == 0: save_to_yml(CLIENT_CONFIG_PATH, client_config_map) global_config_path.unlink() logging.getLogger().info("\nSuccessfully migrated the global config.") else: errors = [f"client_config_map - {e}" for e in errors] logging.getLogger().error(f"The migration of the global config map failed with errors: {errors}") return errors
async def connect_ethereum(self, # type: HummingbotApplication ): self.placeholder_mode = True self.app.hide_input = True ether_wallet = global_config_map["ethereum_wallet"].value to_connect = True if ether_wallet is not None: answer = await self.app.prompt(prompt=f"Would you like to replace your existing Ethereum wallet " f"{ether_wallet} (Yes/No)? >>> ") if self.app.to_stop_config: self.app.to_stop_config = False return if answer.lower() not in ("yes", "y"): to_connect = False if to_connect: private_key = await self.app.prompt(prompt="Enter your wallet private key >>> ", is_password=True) public_address = Security.add_private_key(private_key) global_config_map["ethereum_wallet"].value = public_address if global_config_map["ethereum_rpc_url"].value is None: await self.prompt_a_config(global_config_map["ethereum_rpc_url"]) if global_config_map["ethereum_rpc_ws_url"].value is None: await self.prompt_a_config(global_config_map["ethereum_rpc_ws_url"]) if self.app.to_stop_config: self.app.to_stop_config = False return save_to_yml(GLOBAL_CONFIG_PATH, global_config_map) err_msg = UserBalances.validate_ethereum_wallet() if err_msg is None: self._notify(f"Wallet {public_address} connected to hummingbot.") else: self._notify(f"\nError: {err_msg}") self.placeholder_mode = False self.app.hide_input = False self.app.change_prompt(prompt=">>> ")
async def connect_celo( self, # type: HummingbotApplication ): self.placeholder_mode = True self.app.hide_input = True celo_address = global_config_map["celo_address"].value to_connect = True if celo_address is not None: answer = await self.app.prompt( prompt= f"Would you like to replace your existing Celo account address " f"{celo_address} (Yes/No)? >>> ") if answer.lower() not in ("yes", "y"): to_connect = False if to_connect: await self.prompt_a_config(global_config_map["celo_address"]) await self.prompt_a_config(global_config_map["celo_password"]) save_to_yml(settings.GLOBAL_CONFIG_PATH, global_config_map) err_msg = await self.validate_n_connect_celo( True, global_config_map["celo_address"].value, global_config_map["celo_password"].value) if err_msg is None: self._notify("You are now connected to Celo network.") else: self._notify(err_msg) self.placeholder_mode = False self.app.hide_input = False self.app.change_prompt(prompt=">>> ")
def store_binance_config(self) -> ClientConfigAdapter: config_map = ClientConfigAdapter( BinanceConfigMap(binance_api_key=self.api_key, binance_api_secret=self.api_secret) ) file_path = get_connector_config_yml_path(self.connector) save_to_yml(file_path, config_map) return config_map
def migrate_xemm_confs(conf, new_path) -> List[str]: if "active_order_canceling" in conf: if conf["active_order_canceling"]: conf["order_refresh_mode"] = {} else: conf["order_refresh_mode"] = { "cancel_order_threshold": conf["cancel_order_threshold"], "limit_order_min_expiration": conf["limit_order_min_expiration"] } conf.pop("active_order_canceling") conf.pop("cancel_order_threshold") conf.pop("limit_order_min_expiration") if "use_oracle_conversion_rate" in conf: if conf["use_oracle_conversion_rate"]: conf["conversion_rate_mode"] = {} else: conf["conversion_rate_mode"] = { "taker_to_maker_base_conversion_rate": conf["taker_to_maker_base_conversion_rate"], "taker_to_maker_quote_conversion_rate": conf["taker_to_maker_quote_conversion_rate"] } conf.pop("use_oracle_conversion_rate") conf.pop("taker_to_maker_base_conversion_rate") conf.pop("taker_to_maker_quote_conversion_rate") if "template_version" in conf: conf.pop("template_version") try: config_map = ClientConfigAdapter(CrossExchangeMarketMakingConfigMap(**conf)) save_to_yml(new_path, config_map) errors = [] except Exception as e: logging.getLogger().error(str(e)) errors = [str(e)] return errors
async def set_paper_balance(self, asset, amount): self.app.clear_input() self.placeholder_mode = True self.app.hide_input = True answer = await self.app.prompt( prompt= "Setting a new paper balance on runtime requires to clear the current trading history. Would you like to do proceed (Yes/No)? >>> " ) if answer.lower() in ("yes", "y"): config_map = global_config_map file_path = GLOBAL_CONFIG_PATH config_var = config_map["paper_trade_account_balance"] paper_balances = dict(config_var.value) if config_var.value else {} paper_balances[asset] = amount config_var.value = paper_balances save_to_yml(file_path, config_map) self._notify(f"Paper balance for {asset} token set to {amount}") # Reset paper trade exchange class account balance to reflect the new balance on strategy start reset_paper_trade_account_balance() # Set new app init time to clear current trading history because the account balance has changed which will lead to wrong performance calculations self._main_app.init_time = time.time() self._notify("The trading history has been cleared!") else: self._notify("Your current paper balance has not been changed!") self.app.hide_input = False self.placeholder_mode = False self.app.change_prompt(prompt=">>> ")
async def prompt_for_configuration(self, # type: HummingbotApplication file_name): self.app.clear_input() self.placeholder_mode = True self.app.hide_input = True required_exchanges.clear() strategy_config = ConfigVar(key="strategy", prompt="What is your market making strategy? >>> ", validator=validate_strategy) await self.prompt_a_config(strategy_config) if self.app.to_stop_config: self.app.to_stop_config = False return strategy = strategy_config.value config_map = get_strategy_config_map(strategy) self._notify(f"Please see https://docs.hummingbot.io/strategies/{strategy.replace('_', '-')}/ " f"while setting up these below configuration.") # assign default values and reset those not required for config in config_map.values(): if config.required: config.value = config.default else: config.value = None for config in config_map.values(): if config.prompt_on_new and config.required: if not self.app.to_stop_config: await self.prompt_a_config(config) else: self.app.to_stop_config = False return else: config.value = config.default # catch a last key binding to stop config, if any if self.app.to_stop_config: self.app.to_stop_config = False return if file_name is None: file_name = await self.prompt_new_file_name(strategy) if self.app.to_stop_config: self.app.to_stop_config = False self.app.set_text("") return self.app.change_prompt(prompt=">>> ") strategy_path = os.path.join(CONF_FILE_PATH, file_name) template = get_strategy_template_path(strategy) shutil.copy(template, strategy_path) save_to_yml(strategy_path, config_map) self.strategy_file_name = file_name self.strategy_name = strategy # Reload completer here otherwise the new file will not appear self.app.input_field.completer = load_completer(self) self._notify(f"A new config file {self.strategy_file_name} created.") self.placeholder_mode = False self.app.hide_input = False if await self.status_check_all(): self._notify("\nEnter \"start\" to start market making.")
def balance(self, option: str = None, args: List[str] = None): if threading.current_thread() != threading.main_thread(): self.ev_loop.call_soon_threadsafe(self.balance, option, args) return self.app.clear_input() if option is None: safe_ensure_future(self.show_balances()) elif option in OPTIONS: config_map = global_config_map file_path = GLOBAL_CONFIG_PATH if option == "limit": config_var = config_map["balance_asset_limit"] if args is None or len(args) == 0: safe_ensure_future(self.show_asset_limits()) return if len(args) != 3 or validate_exchange( args[0]) is not None or validate_decimal( args[2]) is not None: self._notify("Error: Invalid command arguments") self.notify_balance_limit_set() return exchange = args[0] asset = args[1].upper() amount = float(args[2]) if exchange not in config_var.value or config_var.value[ exchange] is None: config_var.value[exchange] = {} if amount < 0 and asset in config_var.value[exchange].keys(): config_var.value[exchange].pop(asset) self._notify( f"Limit for {asset} on {exchange} exchange removed.") elif amount >= 0: config_var.value[exchange][asset] = amount self._notify( f"Limit for {asset} on {exchange} exchange set to {amount}" ) save_to_yml(file_path, config_map) elif option == "paper": config_var = config_map["paper_trade_account_balance"] if args is None or len(args) == 0: safe_ensure_future(self.show_paper_account_balance()) return if len(args) != 2 or validate_decimal(args[1]) is not None: self._notify("Error: Invalid command arguments") self.notify_balance_paper_set() return asset = args[0].upper() amount = float(args[1]) paper_balances = dict( config_var.value) if config_var.value else {} paper_balances[asset] = amount config_var.value = paper_balances self._notify( f"Paper balance for {asset} token set to {amount}") save_to_yml(file_path, config_map)
async def _config_single_key(self, # type: HummingbotApplication key: str, input_value): """ Configure a single variable only. Prompt the user to finish all configurations if there are remaining empty configs at the end. """ self.placeholder_mode = True self.app.hide_input = True try: config_var, config_map, file_path = None, None, None if key in global_config_map: config_map = global_config_map file_path = GLOBAL_CONFIG_PATH elif self.strategy_config_map is not None and key in self.strategy_config_map: config_map = self.strategy_config_map file_path = join(CONF_FILE_PATH, self.strategy_file_name) config_var = config_map[key] if input_value is None: self._notify("Please follow the prompt to complete configurations: ") if config_var.key == "inventory_target_base_pct": await self.asset_ratio_maintenance_prompt(config_map, input_value) elif config_var.key == "inventory_price": await self.inventory_price_prompt(config_map, input_value) else: await self.prompt_a_config(config_var, input_value=input_value, assign_default=False) if self.app.to_stop_config: self.app.to_stop_config = False return await self.update_all_secure_configs() missings = missing_required_configs(config_map) if missings: self._notify("\nThere are other configuration required, please follow the prompt to complete them.") missings = await self._prompt_missing_configs(config_map) save_to_yml(file_path, config_map) self._notify("\nNew configuration saved:") self._notify(f"{key}: {str(config_var.value)}") for config in missings: self._notify(f"{config.key}: {str(config.value)}") if isinstance(self.strategy, PureMarketMakingStrategy) or \ isinstance(self.strategy, PerpetualMarketMakingStrategy): updated = ConfigCommand.update_running_mm(self.strategy, key, config_var.value) if updated: self._notify(f"\nThe current {self.strategy_name} strategy has been updated " f"to reflect the new configuration.") except asyncio.TimeoutError: self.logger().error("Prompt timeout") except Exception as err: self.logger().error(str(err), exc_info=True) finally: self.app.hide_input = False self.placeholder_mode = False self.app.change_prompt(prompt=">>> ")
async def save_config_to_file( self, # type: HummingbotApplication file_name: Optional[str], config_map: ClientConfigAdapter, ) -> str: if file_name is None: file_name = await self.prompt_new_file_name(config_map.strategy) if self.app.to_stop_config: self.app.set_text("") return self.app.change_prompt(prompt=">>> ") strategy_path = Path(STRATEGIES_CONF_DIR_PATH) / file_name save_to_yml(strategy_path, config_map) return file_name
def reset_style(config_map=global_config_map, save=True): # Reset config config_map.get("top-pane").value = config_map.get("top-pane").default config_map.get("bottom-pane").value = config_map.get("bottom-pane").default config_map.get("output-pane").value = config_map.get("output-pane").default config_map.get("input-pane").value = config_map.get("input-pane").default config_map.get("logs-pane").value = config_map.get("logs-pane").default config_map.get("terminal-primary").value = config_map.get( "terminal-primary").default # Save configuration if save: file_path = GLOBAL_CONFIG_PATH save_to_yml(file_path, config_map) # Apply & return style return load_style(config_map)
def migrate_amm_confs(conf, new_path) -> List[str]: execution_timeframe = conf.pop("execution_timeframe") if execution_timeframe == "infinite": conf["execution_timeframe_mode"] = {} conf.pop("start_time") conf.pop("end_time") elif execution_timeframe == "from_date_to_date": conf["execution_timeframe_mode"] = { "start_datetime": conf.pop("start_time"), "end_datetime": conf.pop("end_time"), } else: assert execution_timeframe == "daily_between_times" conf["execution_timeframe_mode"] = { "start_time": conf.pop("start_time"), "end_time": conf.pop("end_time"), } order_levels = int(conf.pop("order_levels")) if order_levels == 1: conf["order_levels_mode"] = {} conf.pop("level_distances") else: conf["order_levels_mode"] = { "order_levels": order_levels, "level_distances": conf.pop("level_distances") } hanging_orders_enabled = conf.pop("hanging_orders_enabled") if not hanging_orders_enabled: conf["hanging_orders_mode"] = {} conf.pop("hanging_orders_cancel_pct") else: conf["hanging_orders_mode"] = { "hanging_orders_cancel_pct": conf.pop("hanging_orders_cancel_pct") } if "template_version" in conf: conf.pop("template_version") try: config_map = ClientConfigAdapter(AvellanedaMarketMakingConfigMap(**conf)) save_to_yml(new_path, config_map) errors = [] except Exception as e: logging.getLogger().error(str(e)) errors = [str(e)] return errors
def test_load_connector_config_map_from_file_with_secrets( self, get_connector_config_keys_mock: MagicMock): class DummyConnectorModel(BaseConnectorConfigMap): connector = "some-connector" secret_attr: Optional[SecretStr] = Field(default=None) password = "******" Security.secrets_manager = ETHKeyFileSecretManger(password) cm = ClientConfigAdapter( DummyConnectorModel(secret_attr="some_secret")) get_connector_config_keys_mock.return_value = DummyConnectorModel() with TemporaryDirectory() as d: d = Path(d) config_helpers.CONNECTORS_CONF_DIR_PATH = d temp_file_name = get_connector_config_yml_path(cm.connector) save_to_yml(temp_file_name, cm) cm_loaded = load_connector_config_map_from_file(temp_file_name) self.assertEqual(cm, cm_loaded)
def test_save_command_shortcuts_to_yml(self): class DummyStrategy(BaseClientModel): command_shortcuts: List[CommandShortcutModel] = Field(default=[ CommandShortcutModel( command="spreads", help="Set bid and ask spread", arguments=["Bid Spread", "Ask Spread"], output=["config bid_spread $1", "config ask_spread $2"]) ]) another_attr: Decimal = Field( default=Decimal("1.0"), description="Some other\nmultiline description", ) class Config: title = "dummy_global_config" cm = ClientConfigAdapter(DummyStrategy()) expected_str = ("######################################\n" "### dummy_global_config config ###\n" "######################################\n\n" "command_shortcuts:\n" "- command: spreads\n" " help: Set bid and ask spread\n" " arguments:\n" " - Bid Spread\n" " - Ask Spread\n" " output:\n" " - config bid_spread $1\n" " - config ask_spread $2\n\n" "# Some other\n" "# multiline description\n" "another_attr: 1.0\n") with TemporaryDirectory() as d: d = Path(d) temp_file_name = d / "cm.yml" save_to_yml(temp_file_name, cm) with open(temp_file_name) as f: actual_str = f.read() self.assertEqual(expected_str, actual_str)
def test_save_to_yml(self): class DummyStrategy(BaseStrategyConfigMap): class Config: title = "pure_market_making" strategy: str = "pure_market_making" cm = ClientConfigAdapter(DummyStrategy()) expected_str = """\ ##################################### ### pure_market_making config ### ##################################### strategy: pure_market_making """ with TemporaryDirectory() as d: d = Path(d) temp_file_name = d / "cm.yml" save_to_yml(temp_file_name, cm) with open(temp_file_name) as f: actual_str = f.read() self.assertEqual(expected_str, actual_str)
def reset_style(config_map: ClientConfigAdapter, save=True): # Reset config config_map.color.top_pane = config_map.color.get_default("top_pane") config_map.color.bottom_pane = config_map.color.get_default("bottom_pane") config_map.color.output_pane = config_map.color.get_default("output_pane") config_map.color.input_pane = config_map.color.get_default("input_pane") config_map.color.logs_pane = config_map.color.get_default("logs_pane") config_map.color.terminal_primary = config_map.color.get_default("terminal_primary") config_map.color.primary_label = config_map.color.get_default("primary_label") config_map.color.secondary_label = config_map.color.get_default("secondary_label") config_map.color.success_label = config_map.color.get_default("success_label") config_map.color.warning_label = config_map.color.get_default("warning_label") config_map.color.info_label = config_map.color.get_default("info_label") config_map.color.error_label = config_map.color.get_default("error_label") # Save configuration if save: save_to_yml(CLIENT_CONFIG_PATH, config_map) # Apply & return style return load_style(config_map)
async def _config_single_key( self, # type: HummingbotApplication key: str, input_value): """ Configure a single variable only. Prompt the user to finish all configurations if there are remaining empty configs at the end. """ self.placeholder_mode = True self.app.hide_input = True try: if (not isinstance(self.strategy_config_map, (type(None), ClientConfigAdapter)) and key in self.strategy_config_map): await self._config_single_key_legacy(key, input_value) else: client_config_key = key in self.client_config_map.config_paths( ) if client_config_key: config_map = self.client_config_map file_path = CLIENT_CONFIG_PATH elif self.strategy is not None: self.notify( "Configuring the strategy while it is running is not currently supported." ) return else: config_map = self.strategy_config_map file_path = STRATEGIES_CONF_DIR_PATH / self.strategy_file_name if input_value is None: self.notify( "Please follow the prompt to complete configurations: " ) if key == "inventory_target_base_pct": await self.asset_ratio_maintenance_prompt( config_map, input_value) elif key == "inventory_price": await self.inventory_price_prompt(config_map, input_value) else: await self.prompt_a_config(config_map, key, input_value, assign_default=False) if self.app.to_stop_config: self.app.to_stop_config = False return save_to_yml(file_path, config_map) self.notify("\nNew configuration saved.") if client_config_key: self.list_client_configs() else: self.list_strategy_configs() self.app.app.style = load_style(self.client_config_map) except asyncio.TimeoutError: self.logger().error("Prompt timeout") except Exception as err: self.logger().error(str(err), exc_info=True) finally: self.app.hide_input = False self.placeholder_mode = False self.app.change_prompt(prompt=">>> ")
async def _create_gateway(self): gateway_paths: GatewayPaths = get_gateway_paths() gateway_container_name: str = get_gateway_container_name() gateway_conf_mount_path: str = gateway_paths.mount_conf_path.as_posix() certificate_mount_path: str = gateway_paths.mount_certs_path.as_posix() logs_mount_path: str = gateway_paths.mount_logs_path.as_posix() gateway_port: int = get_default_gateway_port() # remove existing container(s) try: old_container = await docker_ipc( "containers", all=True, filters={"name": gateway_container_name} ) for container in old_container: self.notify(f"Removing existing gateway container with id {container['Id']}...") await docker_ipc( "remove_container", container["Id"], force=True ) except Exception: pass # silently ignore exception await self._generate_certs(from_client_password=True) # create cert if await self.check_gateway_image(GATEWAY_DOCKER_REPO, GATEWAY_DOCKER_TAG): self.notify("Found Gateway docker image. No image pull needed.") else: self.notify("Pulling Gateway docker image...") try: await self.pull_gateway_docker(GATEWAY_DOCKER_REPO, GATEWAY_DOCKER_TAG) self.logger().info("Done pulling Gateway docker image.") except Exception as e: self.notify("Error pulling Gateway docker image. Try again.") self.logger().network("Error pulling Gateway docker image. Try again.", exc_info=True, app_warning_msg=str(e)) return self.notify("Creating new Gateway docker container...") host_config: Dict[str, Any] = await docker_ipc( "create_host_config", port_bindings={5000: gateway_port}, binds={ gateway_conf_mount_path: { "bind": "/usr/src/app/conf/", "mode": "rw" }, certificate_mount_path: { "bind": "/usr/src/app/certs/", "mode": "rw" }, logs_mount_path: { "bind": "/usr/src/app/logs/", "mode": "rw" }, } ) container_info: Dict[str, str] = await docker_ipc( "create_container", image=f"{GATEWAY_DOCKER_REPO}:{GATEWAY_DOCKER_TAG}", name=gateway_container_name, ports=[5000], volumes=[ gateway_conf_mount_path, certificate_mount_path, logs_mount_path ], host_config=host_config, environment=[f"GATEWAY_PASSPHRASE={Security.password}"] ) self.notify(f"New Gateway docker container id is {container_info['Id']}.") # Save the gateway port number, if it's not already there. if global_config_map.get("gateway_api_port").value != gateway_port: global_config_map["gateway_api_port"].value = gateway_port global_config_map["gateway_api_host"].value = "localhost" save_to_yml(GLOBAL_CONFIG_PATH, global_config_map) GatewayHttpClient.get_instance().base_url = f"https://{global_config_map['gateway_api_host'].value}:" \ f"{global_config_map['gateway_api_port'].value}" await start_gateway() # create Gateway configs await self._generate_gateway_confs(container_id=container_info["Id"]) # Restarts the Gateway container to ensure that Gateway server reloads new configs try: await docker_ipc(method_name="restart", container=container_info["Id"]) except docker.errors.APIError as e: self.notify(f"Error restarting Gateway container. Error: {e}") self.notify(f"Loaded new configs into Gateway container {container_info['Id']}")
def update_secure_config(cls, connector_config: ClientConfigAdapter): connector_name = connector_config.connector file_path = get_connector_config_yml_path(connector_name) save_to_yml(file_path, connector_config) update_connector_hb_config(connector_config) cls._secure_configs[connector_name] = connector_config
async def prompt_for_configuration( self, # type: HummingbotApplication file_name): self.app.clear_input() self.placeholder_mode = True self.app.hide_input = True required_exchanges.clear() strategy_config = ConfigVar( key="strategy", prompt="What is your market making strategy? >>> ", validator=validate_strategy) await self.prompt_a_config(strategy_config) if self.app.to_stop_config: self.stop_config() return strategy = strategy_config.value config_map = get_strategy_config_map(strategy) config_map_backup = copy.deepcopy(config_map) self._notify( f"Please see https://docs.hummingbot.io/strategies/{strategy.replace('_', '-')}/ " f"while setting up these below configuration.") # assign default values and reset those not required for config in config_map.values(): if config.required: config.value = config.default else: config.value = None for config in config_map.values(): if config.prompt_on_new and config.required: if not self.app.to_stop_config: await self.prompt_a_config(config) else: break else: config.value = config.default if self.app.to_stop_config: self.stop_config(config_map, config_map_backup) return if file_name is None: file_name = await self.prompt_new_file_name(strategy) if self.app.to_stop_config: self.stop_config(config_map, config_map_backup) self.app.set_text("") return self.app.change_prompt(prompt=">>> ") strategy_path = os.path.join(CONF_FILE_PATH, file_name) template = get_strategy_template_path(strategy) shutil.copy(template, strategy_path) save_to_yml(strategy_path, config_map) self.strategy_file_name = file_name self.strategy_name = strategy # Reload completer here otherwise the new file will not appear self.app.input_field.completer = load_completer(self) self._notify(f"A new config file {self.strategy_file_name} created.") self.placeholder_mode = False self.app.hide_input = False try: timeout = float(global_config_map["create_command_timeout"].value) all_status_go = await asyncio.wait_for(self.status_check_all(), timeout) except asyncio.TimeoutError: self._notify( "\nA network error prevented the connection check to complete. See logs for more details." ) self.strategy_file_name = None self.strategy_name = None raise if all_status_go: self._notify("\nEnter \"start\" to start market making.")
def save_client_config(self): save_to_yml(CLIENT_CONFIG_PATH, self.client_config_map)
async def _create_gateway( self # type: HummingbotApplication ): gateway_paths: GatewayPaths = get_gateway_paths(self.client_config_map) gateway_container_name: str = get_gateway_container_name(self.client_config_map) gateway_conf_mount_path: str = gateway_paths.mount_conf_path.as_posix() certificate_mount_path: str = gateway_paths.mount_certs_path.as_posix() logs_mount_path: str = gateway_paths.mount_logs_path.as_posix() gateway_port: int = get_default_gateway_port(self.client_config_map) # remove existing container(s) try: old_container = await docker_ipc( "containers", all=True, filters={"name": gateway_container_name} ) for container in old_container: self.notify(f"Removing existing gateway container with id {container['Id']}...") await docker_ipc( "remove_container", container["Id"], force=True ) except Exception: pass # silently ignore exception await self._generate_certs(from_client_password=True) # create cert if await self.check_gateway_image(GATEWAY_DOCKER_REPO, GATEWAY_DOCKER_TAG): self.notify("Found Gateway docker image. No image pull needed.") else: self.notify("Pulling Gateway docker image...") try: await self.pull_gateway_docker(GATEWAY_DOCKER_REPO, GATEWAY_DOCKER_TAG) self.logger().info("Done pulling Gateway docker image.") except Exception as e: self.notify("Error pulling Gateway docker image. Try again.") self.logger().network("Error pulling Gateway docker image. Try again.", exc_info=True, app_warning_msg=str(e)) return self.notify("Creating new Gateway docker container...") host_config: Dict[str, Any] = await docker_ipc( "create_host_config", port_bindings={5000: gateway_port}, binds={ gateway_conf_mount_path: { "bind": "/usr/src/app/conf/", "mode": "rw" }, certificate_mount_path: { "bind": "/usr/src/app/certs/", "mode": "rw" }, logs_mount_path: { "bind": "/usr/src/app/logs/", "mode": "rw" }, } ) container_info: Dict[str, str] = await docker_ipc( "create_container", image=f"{GATEWAY_DOCKER_REPO}:{GATEWAY_DOCKER_TAG}", name=gateway_container_name, ports=[5000], volumes=[ gateway_conf_mount_path, certificate_mount_path, logs_mount_path ], host_config=host_config, environment=[f"GATEWAY_PASSPHRASE={Security.secrets_manager.password.get_secret_value()}"] ) self.notify(f"New Gateway docker container id is {container_info['Id']}.") # Save the gateway port number, if it's not already there. gateway_config_map = self.client_config_map.gateway if gateway_config_map.gateway_api_port != gateway_port: gateway_config_map.gateway_api_port = gateway_port gateway_config_map.gateway_api_host = "localhost" save_to_yml(CLIENT_CONFIG_PATH, self.client_config_map) self._get_gateway_instance().base_url = ( f"https://{gateway_config_map.gateway_api_host}:{gateway_config_map.gateway_api_port}" ) await start_gateway(self.client_config_map) # create Gateway configs await self._generate_gateway_confs(container_id=container_info["Id"]) self.notify("Gateway is starting, please wait a moment.") # wait about 30 seconds for the gateway to start docker_and_gateway_live = await self.ping_gateway_docker_and_api(30) if docker_and_gateway_live: self.notify("Gateway has started succesfully.") else: self.notify("Error starting Gateway container.")