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
def _maybe_migrate_encrypted_confs(config_keys: BaseConnectorConfigMap) -> List[str]: cm = ClientConfigAdapter(config_keys) found_one = False files_to_remove = [] missing_fields = [] for el in cm.traverse(): if el.client_field_data is not None: if el.attr == "celo_address" and celo_address is not None: cm.setattr_no_validation(el.attr, celo_address) continue key_path = conf_dir_path / f"{encrypted_conf_prefix}{el.attr}{encrypted_conf_postfix}" if key_path.exists(): with open(key_path, 'r') as f: json_str = f.read() value = binascii.hexlify(json_str.encode()).decode() if not el.client_field_data.is_secure: value = Security.secrets_manager.decrypt_secret_value(el.attr, value) cm.setattr_no_validation(el.attr, value) files_to_remove.append(key_path) found_one = True else: missing_fields.append(el.attr) errors = [] if found_one: if len(missing_fields) != 0: errors = [f"{config_keys.connector} - missing fields: {missing_fields}"] if len(errors) == 0: errors = cm.validate_model() if errors: errors = [f"{config_keys.connector} - {e}" for e in errors] logging.getLogger().error(f"The migration of {config_keys.connector} failed with errors: {errors}") else: Security.update_secure_config(cm) logging.getLogger().info(f"Migrated secure keys for {config_keys.connector}") for f in files_to_remove: f.unlink() return errors
def test_initial_sequential_build(self): config_map = ClientConfigAdapter( AvellanedaMarketMakingConfigMap.construct()) config_settings = self.get_default_map() def build_config_map(cm: ClientConfigAdapter, cs: Dict): """This routine can be used in the create command, with slight modifications.""" for key in cm.keys(): client_data = cm.get_client_data(key) if client_data is not None and client_data.prompt_on_new: self.assertIsInstance(client_data.prompt(cm), str) if key == "execution_timeframe_model": setattr(cm, key, "daily_between_times") # simulate user input else: setattr(cm, key, cs[key]) new_value = getattr(cm, key) if isinstance(new_value, ClientConfigAdapter): build_config_map(new_value, cs[key]) build_config_map(config_map, config_settings) hb_config = config_map.hb_config validate_model(hb_config.__class__, hb_config.__dict__) self.assertEqual(0, len(config_map.validate_model()))
class AvellanedaMarketMakingConfigMapPydanticTest(unittest.TestCase): @classmethod def setUpClass(cls) -> None: super().setUpClass() cls.ev_loop = asyncio.get_event_loop() cls.exchange = "binance" cls.base_asset = "COINALPHA" cls.quote_asset = "HBOT" cls.trading_pair = f"{cls.base_asset}-{cls.quote_asset}" def setUp(self) -> None: super().setUp() config_settings = self.get_default_map() self.config_map = ClientConfigAdapter( AvellanedaMarketMakingConfigMap(**config_settings)) def async_run_with_timeout(self, coroutine: Awaitable, timeout: int = 1): ret = self.ev_loop.run_until_complete( asyncio.wait_for(coroutine, timeout)) return ret def get_default_map(self) -> Dict[str, str]: config_settings = { "exchange": self.exchange, "market": self.trading_pair, "execution_timeframe_mode": { "start_time": "09:30:00", "end_time": "16:00:00", }, "order_amount": "10", "order_optimization_enabled": "yes", "risk_factor": "0.5", "order_refresh_time": "60", "inventory_target_base_pct": "50", } return config_settings def test_initial_sequential_build(self): config_map = ClientConfigAdapter( AvellanedaMarketMakingConfigMap.construct()) config_settings = self.get_default_map() def build_config_map(cm: ClientConfigAdapter, cs: Dict): """This routine can be used in the create command, with slight modifications.""" for key in cm.keys(): client_data = cm.get_client_data(key) if client_data is not None and client_data.prompt_on_new: self.assertIsInstance(client_data.prompt(cm), str) if key == "execution_timeframe_model": setattr(cm, key, "daily_between_times") # simulate user input else: setattr(cm, key, cs[key]) new_value = getattr(cm, key) if isinstance(new_value, ClientConfigAdapter): build_config_map(new_value, cs[key]) build_config_map(config_map, config_settings) hb_config = config_map.hb_config validate_model(hb_config.__class__, hb_config.__dict__) self.assertEqual(0, len(config_map.validate_model())) def test_order_amount_prompt(self): prompt = self.async_run_with_timeout( self.config_map.get_client_prompt("order_amount")) expected = f"What is the amount of {self.base_asset} per order?" self.assertEqual(expected, prompt) def test_maker_trading_pair_prompt(self): exchange = self.config_map.exchange example = AllConnectorSettings.get_example_pairs().get(exchange) prompt = self.async_run_with_timeout( self.config_map.get_client_prompt("market")) expected = f"Enter the token trading pair you would like to trade on {exchange} (e.g. {example})" self.assertEqual(expected, prompt) def test_execution_time_prompts(self): self.config_map.execution_timeframe_mode = FromDateToDateModel.Config.title model = self.config_map.execution_timeframe_mode prompt = self.async_run_with_timeout( model.get_client_prompt("start_datetime")) expected = "Please enter the start date and time (YYYY-MM-DD HH:MM:SS)" self.assertEqual(expected, prompt) prompt = self.async_run_with_timeout( model.get_client_prompt("end_datetime")) expected = "Please enter the end date and time (YYYY-MM-DD HH:MM:SS)" self.assertEqual(expected, prompt) self.config_map.execution_timeframe_mode = DailyBetweenTimesModel.Config.title model = self.config_map.execution_timeframe_mode prompt = self.async_run_with_timeout( model.get_client_prompt("start_time")) expected = "Please enter the start time (HH:MM:SS)" self.assertEqual(expected, prompt) prompt = self.async_run_with_timeout( model.get_client_prompt("end_time")) expected = "Please enter the end time (HH:MM:SS)" self.assertEqual(expected, prompt) @patch( "hummingbot.client.config.config_data_types.validate_market_trading_pair" ) def test_validators(self, _): self.config_map.execution_timeframe_mode = "infinite" self.assertIsInstance( self.config_map.execution_timeframe_mode.hb_config, InfiniteModel) self.config_map.execution_timeframe_mode = "from_date_to_date" self.assertIsInstance( self.config_map.execution_timeframe_mode.hb_config, FromDateToDateModel) self.config_map.execution_timeframe_mode = "daily_between_times" self.assertIsInstance( self.config_map.execution_timeframe_mode.hb_config, DailyBetweenTimesModel) with self.assertRaises(ConfigValidationError) as e: self.config_map.execution_timeframe_mode = "XXX" error_msg = ( "Invalid timeframe, please choose value from ['infinite', 'from_date_to_date', 'daily_between_times']" ) self.assertEqual(error_msg, str(e.exception)) self.config_map.execution_timeframe_mode = "from_date_to_date" model = self.config_map.execution_timeframe_mode model.start_datetime = "2021-01-01 12:00:00" model.end_datetime = "2021-01-01 15:00:00" self.assertEqual(datetime(2021, 1, 1, 12, 0, 0), model.start_datetime) self.assertEqual(datetime(2021, 1, 1, 15, 0, 0), model.end_datetime) with self.assertRaises(ConfigValidationError) as e: model.start_datetime = "2021-01-01 30:00:00" error_msg = "Incorrect date time format (expected is YYYY-MM-DD HH:MM:SS)" self.assertEqual(error_msg, str(e.exception)) with self.assertRaises(ConfigValidationError) as e: model.start_datetime = "12:00:00" error_msg = "Incorrect date time format (expected is YYYY-MM-DD HH:MM:SS)" self.assertEqual(error_msg, str(e.exception)) self.config_map.execution_timeframe_mode = "daily_between_times" model = self.config_map.execution_timeframe_mode model.start_time = "12:00:00" self.assertEqual(time(12, 0, 0), model.start_time) with self.assertRaises(ConfigValidationError) as e: model.start_time = "30:00:00" error_msg = "Incorrect time format (expected is HH:MM:SS)" self.assertEqual(error_msg, str(e.exception)) with self.assertRaises(ConfigValidationError) as e: model.start_time = "2021-01-01 12:00:00" error_msg = "Incorrect time format (expected is HH:MM:SS)" self.assertEqual(error_msg, str(e.exception)) self.config_map.order_levels_mode = "multi_order_level" model = self.config_map.order_levels_mode with self.assertRaises(ConfigValidationError) as e: model.order_levels = 1 error_msg = "Value cannot be less than 2." self.assertEqual(error_msg, str(e.exception)) model.order_levels = 3 self.assertEqual(3, model.order_levels) self.config_map.hanging_orders_mode = "track_hanging_orders" model = self.config_map.hanging_orders_mode with self.assertRaises(ConfigValidationError) as e: model.hanging_orders_cancel_pct = "-1" error_msg = "Value must be between 0 and 100 (exclusive)." self.assertEqual(error_msg, str(e.exception)) model.hanging_orders_cancel_pct = "3" self.assertEqual(3, model.hanging_orders_cancel_pct) def test_load_configs_from_yaml(self): cur_dir = Path(__file__).parent f_path = cur_dir / "test_config.yml" with open(f_path, "r") as file: data = yaml.safe_load(file) loaded_config_map = ClientConfigAdapter( AvellanedaMarketMakingConfigMap(**data)) self.assertEqual(self.config_map, loaded_config_map) def test_configuring_execution_timeframe_mode(self): self.config_map.execution_timeframe_mode = InfiniteModel() self.config_map.execution_timeframe_mode = { "start_datetime": "2022-01-01 10:00:00", "end_datetime": "2022-01-02 10:00:00", } self.config_map.validate_model() self.assertIsInstance( self.config_map.execution_timeframe_mode.hb_config, FromDateToDateModel) self.assertEqual( self.config_map.execution_timeframe_mode.start_datetime, datetime(2022, 1, 1, 10)) self.assertEqual(self.config_map.execution_timeframe_mode.end_datetime, datetime(2022, 1, 2, 10)) self.config_map.execution_timeframe_mode = { "start_time": "10:00:00", "end_time": "11:00:00", } self.config_map.validate_model() self.assertIsInstance( self.config_map.execution_timeframe_mode.hb_config, DailyBetweenTimesModel) self.assertEqual(self.config_map.execution_timeframe_mode.start_time, time(10)) self.assertEqual(self.config_map.execution_timeframe_mode.end_time, time(11)) self.config_map.execution_timeframe_mode = {} self.config_map.validate_model() self.assertIsInstance( self.config_map.execution_timeframe_mode.hb_config, InfiniteModel) def test_configuring_order_levels_mode(self): self.config_map.order_levels_mode = SingleOrderLevelModel() self.config_map.order_levels_mode = { "order_levels": 2, "level_distances": 1, } self.config_map.validate_model() self.assertIsInstance(self.config_map.order_levels_mode.hb_config, MultiOrderLevelModel) self.assertEqual(self.config_map.order_levels_mode.order_levels, 2) self.assertEqual(self.config_map.order_levels_mode.level_distances, 1) self.config_map.order_levels_mode = {} self.config_map.validate_model() self.assertIsInstance(self.config_map.order_levels_mode.hb_config, SingleOrderLevelModel) def test_configuring_hanging_orders_mode(self): self.config_map.hanging_orders_mode = IgnoreHangingOrdersModel() self.config_map.hanging_orders_mode = {"hanging_orders_cancel_pct": 1} self.config_map.validate_model() self.assertIsInstance(self.config_map.hanging_orders_mode.hb_config, TrackHangingOrdersModel) self.assertEqual( self.config_map.hanging_orders_mode.hanging_orders_cancel_pct, Decimal("1")) self.config_map.hanging_orders_mode = {} self.config_map.validate_model() self.assertIsInstance(self.config_map.hanging_orders_mode.hb_config, IgnoreHangingOrdersModel)