def test_get_returns_default_when_key_not_set() -> None: path = Path.cwd() / "config.json" with path.open("w+", encoding="utf-8") as file: file.write('{ "key": "value" }') storage = Storage(str(path)) assert storage.get("key2", "my-default") == "my-default"
def test_has_returns_false_when_key_does_not_exist_in_file() -> None: path = Path.cwd() / "config.json" with path.open("w+", encoding="utf-8") as file: file.write('{ "key": "value" }') storage = Storage(str(path)) assert not storage.has("key2")
def test_get_reads_key_from_file() -> None: path = Path.cwd() / "config.json" with path.open("w+", encoding="utf-8") as file: file.write('{ "key": "value" }') storage = Storage(str(path)) assert storage.get("key") == "value"
def test_set_creates_new_file_when_file_does_not_exist() -> None: path = Path.cwd() / "config.json" storage = Storage(str(path)) storage.set("key", "value") data = json.loads(path.read_text(encoding="utf-8")) assert data == {"key": "value"}
def test_clear_deletes_file() -> None: path = Path.cwd() / "config.json" with path.open("w+", encoding="utf-8") as file: file.write('{ "key": "value" }') storage = Storage(str(path)) storage.clear() assert not path.exists()
def test_create_new_project_sets_parameters_in_project_config() -> None: project_path = Path.cwd() / "Python Project" project_manager = _create_project_manager() project_manager.create_new_project(project_path, QCLanguage.Python) config = Storage(str(project_path / "config.json")) assert config.get("parameters") == {}
def test_create_new_project_sets_description_in_project_config() -> None: project_path = Path.cwd() / "Python Project" project_manager = ProjectManager(ProjectConfigManager()) project_manager.create_new_project(project_path, QCLanguage.Python) config = Storage(str(project_path / "config.json")) assert config.get("description") == ""
def test_set_overrides_values_in_existing_file() -> None: path = Path.cwd() / "config.json" with path.open("w+", encoding="utf-8") as file: file.write('{ "key": "value" }') storage = Storage(str(path)) storage.set("key", "new-value") data = json.loads(path.read_text(encoding="utf-8")) assert data == {"key": "new-value"}
def test_create_new_project_sets_language_in_project_config( language: QCLanguage) -> None: project_path = Path.cwd() / f"{language.name} Project" project_manager = ProjectManager(ProjectConfigManager()) project_manager.create_new_project(project_path, language) config = Storage(str(project_path / "config.json")) assert config.get("algorithm-language") == language.name
def test_report_uses_description_from_config_when_strategy_description_not_given( ) -> None: docker_manager = mock.Mock() docker_manager.run_image.side_effect = run_image container.docker_manager.override(providers.Object(docker_manager)) Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "description", "My description") result = CliRunner().invoke(lean, [ "report", "--backtest-results", "Python Project/backtests/2020-01-01_00-00-00/results.json" ]) assert result.exit_code == 0 docker_manager.run_image.assert_called_once() args, kwargs = docker_manager.run_image.call_args config_mount = [ mount for mount in kwargs["mounts"] if mount["Target"] == "/Lean/Report/bin/Debug/config.json" ][0] config = json.loads( Path(config_mount["Source"]).read_text(encoding="utf-8")) assert config["strategy-description"] == "My description"
def get_output_config(self, output_directory: Path) -> Storage: """Returns a Storage instance to get/set the configuration of the contents of an output directory. :param output_directory: the path to the project to retrieve the configuration of :return: the Storage instance containing the configuration of the given backtest/optimization/live trading """ return Storage(str(output_directory / "config"))
def test_optimize_mounts_output_directory() -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() docker_manager.run_image.side_effect = run_image container.docker_manager.override(providers.Object(docker_manager)) Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", {"param1": "1"}) result = CliRunner().invoke( lean, ["optimize", "Python Project", "--output", "output"]) assert result.exit_code == 0 docker_manager.run_image.assert_called_once() args, kwargs = docker_manager.run_image.call_args assert any([ volume["bind"] == "/Results" for volume in kwargs["volumes"].values() ]) key = next(key for key in kwargs["volumes"].keys() if kwargs["volumes"][key]["bind"] == "/Results") assert key == str(Path.cwd() / "output")
def get_project_config(self, project_directory: Path) -> Storage: """Returns a Storage instance to get/set the configuration for a project. :param project_directory: the path to the project to retrieve the configuration of :return: the Storage instance containing the project-specific configuration of the given project """ return Storage(str(project_directory / PROJECT_CONFIG_FILE_NAME))
def create_lean_runner(docker_manager: mock.Mock) -> LeanRunner: logger = mock.Mock() logger.debug_logging_enabled = False cli_config_manager = mock.Mock() cli_config_manager.user_id.get_value.return_value = "123" cli_config_manager.api_token.get_value.return_value = "456" project_config_manager = ProjectConfigManager(XMLManager()) cache_storage = Storage(str(Path("~/.lean/cache").expanduser())) lean_config_manager = LeanConfigManager(logger, cli_config_manager, project_config_manager, mock.Mock(), cache_storage) output_config_manager = OutputConfigManager(lean_config_manager) module_manager = mock.Mock() module_manager.get_installed_packages.return_value = [ NuGetPackage(name="QuantConnect.Brokerages", version="1.0.0") ] xml_manager = XMLManager() project_manager = ProjectManager(project_config_manager, lean_config_manager, xml_manager, PlatformManager()) return LeanRunner(logger, project_config_manager, lean_config_manager, output_config_manager, docker_manager, module_manager, project_manager, TempManager(), xml_manager)
def test_optimize_checks_for_updates(update_manager_mock: mock.Mock, image_option: Optional[str], update_flag: bool, update_check_expected: bool) -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", {"param1": "1"}) options = [] if image_option is not None: options.extend(["--image", image_option]) if update_flag: options.extend(["--update"]) result = CliRunner().invoke(lean, ["optimize", "Python Project", *options]) assert result.exit_code == 0 if update_check_expected: update_manager_mock.warn_if_docker_image_outdated.assert_called_once_with( ENGINE_IMAGE) else: update_manager_mock.warn_if_docker_image_outdated.assert_not_called()
def create_objects() -> Tuple[mock.Mock, Storage, mock.Mock, UpdateManager]: logger = mock.Mock() storage = Storage(str(Path("~/.lean/cache").expanduser())) docker_manager = mock.Mock() update_manager = UpdateManager(logger, storage, docker_manager) return logger, storage, docker_manager, update_manager
def test_delete_unsets_value() -> None: path = Path.cwd() / "config.json" storage = Storage(str(path)) storage.set("key1", "value1") storage.set("key2", "value2") storage.set("key3", "value3") storage.delete("key2") data = json.loads(path.read_text(encoding="utf-8")) assert data == {"key1": "value1", "key3": "value3"}
def _create_project_manager() -> ProjectManager: xml_manager = XMLManager() project_config_manager = ProjectConfigManager(xml_manager) cache_storage = Storage(str(Path("~/.lean/cache").expanduser())) return ProjectManager( project_config_manager, LeanConfigManager(mock.Mock(), mock.Mock(), project_config_manager, mock.Mock(), cache_storage), xml_manager, PlatformManager())
def test_delete_deletes_file_when_last_key() -> None: path = Path.cwd() / "config.json" storage = Storage(str(path)) storage.set("key", "value") storage.delete("key") assert not path.exists()
def test_optimize_aborts_when_project_has_no_parameters() -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", {}) result = CliRunner().invoke(lean, ["optimize", "Python Project"]) assert result.exit_code != 0 docker_manager.run_image.assert_not_called()
def test_optimize_creates_output_directory_when_not_existing_yet() -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", {"param1": "1"}) result = CliRunner().invoke( lean, ["optimize", "Python Project", "--output", "output"]) assert result.exit_code == 0 assert (Path.cwd() / "output").is_dir()
def test_optimize_forces_update_when_update_option_given() -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", {"param1": "1"}) result = CliRunner().invoke(lean, ["optimize", "Python Project", "--update"]) assert result.exit_code == 0 docker_manager.pull_image.assert_called_once_with(ENGINE_IMAGE) docker_manager.run_image.assert_called_once()
def test_optimize_runs_lean_container() -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", {"param1": "1"}) result = CliRunner().invoke(lean, ["optimize", "Python Project"]) assert result.exit_code == 0 docker_manager.run_image.assert_called_once() args, kwargs = docker_manager.run_image.call_args assert args[0] == ENGINE_IMAGE
def test_optimize_creates_correct_config_from_optimizer_config_manager_output( ) -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() docker_manager.run_image.side_effect = run_image container.docker_manager.override(providers.Object(docker_manager)) Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", {"param1": "1"}) result = CliRunner().invoke(lean, ["optimize", "Python Project"]) assert result.exit_code == 0 docker_manager.run_image.assert_called_once() args, kwargs = docker_manager.run_image.call_args mount = next( m for m in kwargs["mounts"] if m["Target"] == "/Lean/Optimizer.Launcher/bin/Debug/config.json") config = json.loads(Path(mount["Source"]).read_text(encoding="utf-8")) assert config["results-destination-folder"] == "/Results" assert config[ "optimization-strategy"] == "QuantConnect.Optimizer.Strategies.GridSearchOptimizationStrategy" assert config["optimization-criterion"] == { "target": "TotalPerformance.PortfolioStatistics.SharpeRatio", "extremum": "max" } assert config["parameters"] == [{ "name": "param1", "min": 1.0, "max": 10.0, "step": 0.5 }] assert config["constraints"] == [{ "target": "TotalPerformance.PortfolioStatistics.Drawdown", "operator": "less", "target-value": 0.25 }]
def test_optimize_runs_optimizer() -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", {"param1": "1"}) result = CliRunner().invoke(lean, ["optimize", "Python Project"]) assert result.exit_code == 0 docker_manager.run_image.assert_called_once() args, kwargs = docker_manager.run_image.call_args assert kwargs["working_dir"] == "/Lean/Optimizer.Launcher/bin/Debug" assert kwargs["entrypoint"][0] == "dotnet" assert kwargs["entrypoint"][1] == "QuantConnect.Optimizer.Launcher.dll"
def test_optimize_runs_custom_image_when_set_in_config() -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) container.cli_config_manager().engine_image.set_value("custom/lean:123") Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", {"param1": "1"}) result = CliRunner().invoke(lean, ["optimize", "Python Project"]) assert result.exit_code == 0 docker_manager.run_image.assert_called_once() args, kwargs = docker_manager.run_image.call_args assert args[0] == DockerImage(name="custom/lean", tag="123")
def test_optimize_mounts_lean_config() -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", {"param1": "1"}) result = CliRunner().invoke(lean, ["optimize", "Python Project"]) assert result.exit_code == 0 docker_manager.run_image.assert_called_once() args, kwargs = docker_manager.run_image.call_args assert any([ mount["Target"] == "/Lean/Launcher/bin/Debug/config.json" for mount in kwargs["mounts"] ])
def test_get_complete_lean_config_sets_parameters() -> None: create_fake_lean_cli_directory() Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", { "key1": "value1", "key2": "value2", "key3": "value3" }) manager = LeanConfigManager(mock.Mock(), ProjectConfigManager()) config = manager.get_complete_lean_config( "my-environment", Path.cwd() / "Python Project" / "main.py", None) assert config["parameters"] == { "key1": "value1", "key2": "value2", "key3": "value3" }
def test_optimize_writes_config_to_output_directory() -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() docker_manager.run_image.side_effect = run_image container.docker_manager.override(providers.Object(docker_manager)) Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", {"param1": "1"}) result = CliRunner().invoke( lean, ["optimize", "Python Project", "--output", "output"]) assert result.exit_code == 0 assert (Path.cwd() / "output" / "optimizer-config.json").exists() config = json.loads((Path.cwd() / "output" / "optimizer-config.json").read_text(encoding="utf-8")) assert config["results-destination-folder"] == "/Results" assert config[ "optimization-strategy"] == "QuantConnect.Optimizer.Strategies.GridSearchOptimizationStrategy" assert config["optimization-criterion"] == { "target": "TotalPerformance.PortfolioStatistics.SharpeRatio", "extremum": "max" } assert config["parameters"] == [{ "name": "param1", "min": 1.0, "max": 10.0, "step": 0.5 }] assert config["constraints"] == [{ "target": "TotalPerformance.PortfolioStatistics.Drawdown", "operator": "less", "target-value": 0.25 }]
def test_optimize_copies_code_to_output_directory() -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() docker_manager.run_image.side_effect = run_image container.docker_manager.override(providers.Object(docker_manager)) project_manager = mock.Mock() project_manager.find_algorithm_file.return_value = Path.cwd( ) / "Python Project" / "main.py" container.project_manager.override(providers.Object(project_manager)) Storage(str(Path.cwd() / "Python Project" / "config.json")).set( "parameters", {"param1": "1"}) result = CliRunner().invoke( lean, ["optimize", "Python Project", "--output", "output"]) assert result.exit_code == 0 project_manager.copy_code.assert_called_once_with( Path.cwd() / "Python Project", Path.cwd() / "output" / "code")