def test_get_csharp_libraries_skips_invalid_package_reference_tags() -> None: create_fake_lean_cli_directory() with (Path.cwd() / "CSharp Project" / "CSharp Project.csproj").open("w+", encoding="utf-8") as file: file.write(""" <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <TargetFramework>net5.0</TargetFramework> <LangVersion>9</LangVersion> <OutputPath>bin/$(Configuration)</OutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <NoWarn>CS0618</NoWarn> </PropertyGroup> <ItemGroup> <PackageReference Include="QuantConnect.Lean" Version="2.5.11586"/> <PackageReference Include="Microsoft.ML"/> <PackageReference Version="0.94.0"/> <PackageReference/> </ItemGroup> </Project> """) project_config_manager = ProjectConfigManager(XMLManager()) libraries = project_config_manager.get_csharp_libraries(Path.cwd() / "CSharp Project") assert len(libraries) == 1 assert CSharpLibrary(name="QuantConnect.Lean", version="2.5.11586") in libraries
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 test_get_project_config_returns_storage_instance_of_correct_file() -> None: create_fake_lean_cli_directory() project_config_manager = ProjectConfigManager(XMLManager()) project_config = project_config_manager.get_project_config(Path.cwd() / "Python Project") assert project_config.file == Path.cwd() / "Python Project" / "config.json"
def test_live_non_interactive_aborts_when_missing_data_feed_options(data_feed: str) -> None: create_fake_lean_cli_directory() required_options = data_feed_required_options[data_feed].items() for length in range(len(required_options)): for current_options in itertools.combinations(required_options, length): docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) lean_runner = mock.Mock() container.lean_runner.override(providers.Object(lean_runner)) options = [] for key, value in current_options: options.extend([f"--{key}", value]) result = CliRunner().invoke(lean, ["live", "Python Project", "--brokerage", "Paper Trading", "--data-feed", data_feed, *options]) assert result.exit_code != 0 lean_runner.run_lean.assert_not_called()
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 test_get_known_lean_config_path_returns_previously_used_lean_config_path( ) -> None: create_fake_lean_cli_directory() manager = _create_lean_config_manager() assert manager.get_lean_config_path() == Path.cwd() / "lean.json"
def test_get_cli_root_directory_returns_path_to_directory_containing_config_file( ) -> None: create_fake_lean_cli_directory() manager = _create_lean_config_manager() assert manager.get_cli_root_directory() == Path.cwd()
def test_cloud_optimize_displays_optimal_backtest_results( optimizer_config_manager_mock: mock.Mock, target: str, extremum: OptimizationExtremum) -> None: create_fake_lean_cli_directory() project = create_api_project(1, "My Project") optimization = create_api_optimization() optimization.backtests["1"] = create_api_optimization_backtest( 1, True, True, True) optimization.backtests["2"] = create_api_optimization_backtest( 2, True, True, False) api_client = mock.Mock() api_client.projects.get_all.return_value = [project] api_client.optimizations.estimate.return_value = QCOptimizationEstimate( estimateId="x", time=10, balance=1000) container.api_client.override(providers.Object(api_client)) cloud_runner = mock.Mock() cloud_runner.run_optimization.return_value = optimization container.cloud_runner.override(providers.Object(cloud_runner)) optimizer_config_manager_mock.configure_target.return_value = OptimizationTarget( target=f"TotalPerformance.PortfolioStatistics.{target}", extremum=extremum, ) result = CliRunner().invoke(lean, ["cloud", "optimize", "My Project"]) assert result.exit_code == 0 if extremum == OptimizationExtremum.Maximum: assert "id: 1" in result.output else: assert "id: 2" in result.output
def test_live_passes_custom_image_to_lean_runner_when_given_as_option() -> None: create_fake_lean_cli_directory() create_fake_environment("live-paper", True) docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) lean_runner = mock.Mock() container.lean_runner.override(providers.Object(lean_runner)) container.cli_config_manager().engine_image.set_value("custom/lean:123") result = CliRunner().invoke(lean, ["live", "Python Project", "--environment", "live-paper", "--image", "custom/lean:456"]) assert result.exit_code == 0 lean_runner.run_lean.assert_called_once_with(mock.ANY, "live-paper", Path("Python Project/main.py").resolve(), mock.ANY, DockerImage(name="custom/lean", tag="456"), None, False, False)
def test_backtest_auto_updates_outdated_csharp_csproj() -> None: create_fake_lean_cli_directory() csproj_path = Path.cwd() / "CSharp Project" / "CSharp Project.csproj" _generate_file( csproj_path, """ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <TargetFramework>net5.0</TargetFramework> <LangVersion>9</LangVersion> <OutputPath>bin/$(Configuration)</OutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <NoWarn>CS0618</NoWarn> </PropertyGroup> <ItemGroup> <PackageReference Include="QuantConnect.Lean" Version="2.5.11940"/> </ItemGroup> </Project> """) docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) lean_runner = mock.Mock() container.lean_runner.override(providers.Object(lean_runner)) result = CliRunner().invoke(lean, ["backtest", "CSharp Project"]) assert result.exit_code == 0 csproj = XMLManager().parse(csproj_path.read_text(encoding="utf-8")) assert csproj.find(".//PropertyGroup/DefaultItemExcludes") is not None
def test_research_adds_credentials_to_project_config() -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) container.cli_config_manager().user_id.set_value("123") container.cli_config_manager().api_token.set_value("456") result = CliRunner().invoke(lean, ["research", "Python Project"]) assert result.exit_code == 0 docker_manager.run_image.assert_called_once() args, kwargs = docker_manager.run_image.call_args mount = [ m for m in kwargs["mounts"] if m["Target"] == "/Lean/Launcher/bin/Debug/Notebooks/config.json" ][0] with open(mount["Source"]) as file: config = json.load(file) assert config["job-user-id"] == "123" assert config["api-access-token"] == "456"
def test_cloud_optimize_does_not_display_backtest_results_when_none_meet_constraints( ) -> None: create_fake_lean_cli_directory() project = create_api_project(1, "My Project") optimization = create_api_optimization() optimization.backtests["1"] = create_api_optimization_backtest( 1, True, False, True) optimization.backtests["2"] = create_api_optimization_backtest( 2, True, False, False) api_client = mock.Mock() api_client.projects.get_all.return_value = [project] api_client.optimizations.estimate.return_value = QCOptimizationEstimate( estimateId="x", time=10, balance=1000) api_client.organizations.get.return_value = create_api_organization() container.api_client.override(providers.Object(api_client)) cloud_runner = mock.Mock() cloud_runner.run_optimization.return_value = optimization container.cloud_runner.override(providers.Object(cloud_runner)) result = CliRunner().invoke(lean, ["cloud", "optimize", "My Project"]) assert result.exit_code == 0 assert "id: 1" not in result.output assert "id: 2" not in result.output
def test_cloud_optimize_pushes_nothing_when_project_does_not_exist_locally( ) -> None: create_fake_lean_cli_directory() project = create_api_project(1, "My Project") optimization = create_api_optimization() api_client = mock.Mock() api_client.projects.get_all.return_value = [project] api_client.optimizations.estimate.return_value = QCOptimizationEstimate( estimateId="x", time=10, balance=1000) api_client.organizations.get.return_value = create_api_organization() container.api_client.override(providers.Object(api_client)) cloud_runner = mock.Mock() cloud_runner.run_optimization.return_value = optimization container.cloud_runner.override(providers.Object(cloud_runner)) push_manager = mock.Mock() container.push_manager.override(providers.Object(push_manager)) result = CliRunner().invoke(lean, ["cloud", "optimize", "My Project", "--push"]) assert result.exit_code == 0 push_manager.push_projects.assert_not_called()
def test_cloud_optimize_passes_given_config_to_cloud_runner() -> None: create_fake_lean_cli_directory() project = create_api_project(1, "My Project") optimization = create_api_optimization() api_client = mock.Mock() api_client.projects.get_all.return_value = [project] api_client.optimizations.estimate.return_value = QCOptimizationEstimate( estimateId="x", time=10, balance=1000) api_client.organizations.get.return_value = create_api_organization() container.api_client.override(providers.Object(api_client)) cloud_runner = mock.Mock() cloud_runner.run_optimization.return_value = optimization container.cloud_runner.override(providers.Object(cloud_runner)) result = CliRunner().invoke( lean, ["cloud", "optimize", "My Project", "--name", "My Name"]) assert result.exit_code == 0 optimizer_config_manager = container.optimizer_config_manager() cloud_runner.run_optimization.assert_called_once_with( project, mock.ANY, "My Name", optimizer_config_manager.configure_strategy(cloud=True), optimizer_config_manager.configure_target(), optimizer_config_manager.configure_parameters([]), optimizer_config_manager.configure_constraints(), optimizer_config_manager.configure_node()[0].name, optimizer_config_manager.configure_node()[1])
def test_get_cli_root_directory_returns_path_to_directory_containing_config_file( ) -> None: create_fake_lean_cli_directory() manager = LeanConfigManager(mock.Mock(), ProjectConfigManager()) assert manager.get_cli_root_directory() == Path.cwd()
def test_backtest_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)) lean_runner = mock.Mock() container.lean_runner.override(providers.Object(lean_runner)) options = [] if image_option is not None: options.extend(["--image", image_option]) if update_flag: options.extend(["--update"]) result = CliRunner().invoke(lean, ["backtest", "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 test_cloud_optimize_runs_optimization_by_project_id() -> None: create_fake_lean_cli_directory() project = create_api_project(1, "My Project") optimization = create_api_optimization() api_client = mock.Mock() api_client.projects.get_all.return_value = [project] api_client.optimizations.estimate.return_value = QCOptimizationEstimate( estimateId="x", time=10, balance=1000) api_client.organizations.get.return_value = create_api_organization() container.api_client.override(providers.Object(api_client)) cloud_runner = mock.Mock() cloud_runner.run_optimization.return_value = optimization container.cloud_runner.override(providers.Object(cloud_runner)) result = CliRunner().invoke(lean, ["cloud", "optimize", "1"]) assert result.exit_code == 0 cloud_runner.run_optimization.assert_called_once() args, kwargs = cloud_runner.run_optimization.call_args assert args[0] == project
def test_cloud_backtest_logs_statistics() -> None: create_fake_lean_cli_directory() project = create_api_project(1, "My Project") backtest = create_api_backtest() backtest.statistics = {"stat1": "1.0", "stat2": "-1.0", "stat3": "0.0"} backtest.runtimeStatistics = { "stat3": "1.0", "stat4": "-1.0", "stat5": "0.0" } api_client = mock.Mock() api_client.projects.get_all.return_value = [project] container.api_client.override(providers.Object(api_client)) cloud_runner = mock.Mock() cloud_runner.run_backtest.return_value = backtest container.cloud_runner.override(providers.Object(cloud_runner)) result = CliRunner().invoke(lean, ["cloud", "backtest", "My Project"]) assert result.exit_code == 0 for i in range(1, 6): assert f"stat{i}" in result.output
def test_create_project_aborts_when_default_language_not_set_and_language_not_given( ) -> None: create_fake_lean_cli_directory() result = CliRunner().invoke(lean, ["create-project", "My First Project"]) assert result.exit_code != 0
def test_research_opens_browser_when_container_started(open) -> None: create_fake_lean_cli_directory() docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) result = CliRunner().invoke(lean, ["research", "Python Project"]) assert result.exit_code == 0 docker_manager.run_image.assert_called_once() args, kwargs = docker_manager.run_image.call_args logs = """ [I 21:06:21.500 LabApp] Writing notebook server cookie secret to /root/.local/share/jupyter/runtime/notebook_cookie_secret [W 21:06:21.692 LabApp] All authentication is disabled. Anyone who can connect to this server will be able to run code. [I 21:06:21.698 LabApp] JupyterLab extension loaded from /opt/miniconda3/lib/python3.6/site-packages/jupyterlab [I 21:06:21.698 LabApp] JupyterLab application directory is /opt/miniconda3/share/jupyter/lab [I 21:06:21.700 LabApp] Serving notebooks from local directory: /Lean/Launcher/bin/Debug/Notebooks [I 21:06:21.700 LabApp] The Jupyter Notebook is running at: [I 21:06:21.700 LabApp] http://290fd51da0b5:8888/ """.strip() assert "on_output" in kwargs for line in logs.splitlines(): kwargs["on_output"](line) open.assert_called_once_with("http://localhost:8888/")
def test_live_non_interactive_falls_back_to_lean_config_for_brokerage_settings(brokerage: str) -> None: create_fake_lean_cli_directory() required_options = brokerage_required_options[brokerage].items() for length in range(len(required_options)): for current_options in itertools.combinations(required_options, length): docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) lean_runner = mock.Mock() container.lean_runner.override(providers.Object(lean_runner)) api_client = mock.MagicMock() api_client.organizations.get_all.return_value = [ QCMinimalOrganization(id="abc", name="abc", type="type", ownerName="You", members=1, preferred=True) ] container.api_client.override(providers.Object(api_client)) options = [] for key, value in current_options: options.extend([f"--{key}", value]) missing_options_config = {key: value for key, value in set(required_options) - set(current_options)} with (Path.cwd() / "lean.json").open("w+", encoding="utf-8") as file: file.write(json.dumps({ **missing_options_config, "data-folder": "data" })) if brokerage == "Binance": data_feed = "Bitfinex" options.extend(["--bitfinex-api-key", "123", "--bitfinex-api-secret", "456"]) elif brokerage == "FTX": data_feed = "Binance" options.extend(["--ftx-exchange-name", "abc", "--binance-api-key", "123", "--binance-api-secret", "456", "--binance-use-testnet", "no"]) else: data_feed = "Binance" options.extend(["--binance-api-key", "123", "--binance-api-secret", "456", "--binance-use-testnet", "no"]) result = CliRunner().invoke(lean, ["live", "Python Project", "--brokerage", brokerage, "--data-feed", data_feed, *options]) assert result.exit_code == 0 lean_runner.run_lean.assert_called_once_with(mock.ANY, "lean-cli", Path("Python Project/main.py").resolve(), mock.ANY, ENGINE_IMAGE, None, False, False)
def test_backtest_passes_data_purchase_limit_to_lean_runner() -> None: create_fake_lean_cli_directory() _generate_file( Path.cwd() / "lean.json", """ { // data-folder documentation "data-folder": "data", "data-provider": "QuantConnect.Lean.Engine.DataFeeds.ApiDataProvider" } """) docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) lean_runner = mock.Mock() container.lean_runner.override(providers.Object(lean_runner)) result = CliRunner().invoke( lean, ["backtest", "Python Project", "--data-purchase-limit", "1000"]) assert result.exit_code == 0 lean_runner.run_lean.assert_called_once() args, _ = lean_runner.run_lean.call_args assert args[0]["data-purchase-limit"] == 1000
def test_live_non_interactive_aborts_when_missing_brokerage_options(brokerage: str) -> None: create_fake_lean_cli_directory() required_options = brokerage_required_options[brokerage].items() for length in range(len(required_options)): for current_options in itertools.combinations(required_options, length): docker_manager = mock.Mock() container.docker_manager.override(providers.Object(docker_manager)) lean_runner = mock.Mock() container.lean_runner.override(providers.Object(lean_runner)) options = [] for key, value in current_options: options.extend([f"--{key}", value]) if brokerage == "Binance": data_feed = "Bitfinex" options.extend(["--bitfinex-api-key", "123", "--bitfinex-api-secret", "456"]) else: data_feed = "Binance" options.extend(["--binance-api-key", "123", "--binance-api-secret", "456", "--binance-use-testnet", "no"]) result = CliRunner().invoke(lean, ["live", "Python Project", "--brokerage", brokerage, "--data-feed", data_feed, *options]) assert result.exit_code != 0 lean_runner.run_lean.assert_not_called()
def test_find_algorithm_file_returns_input_when_input_is_file() -> None: create_fake_lean_cli_directory() project_manager = ProjectManager(ProjectConfigManager()) result = project_manager.find_algorithm_file(Path.cwd() / "Python Project" / "main.py") assert result == Path.cwd() / "Python Project" / "main.py"
def test_find_algorithm_file_returns_main_cs_when_input_directory_contains_it( ) -> None: create_fake_lean_cli_directory() project_manager = ProjectManager(ProjectConfigManager()) result = project_manager.find_algorithm_file(Path.cwd() / "CSharp Project") assert result == Path.cwd() / "CSharp Project" / "Main.cs"
def test_create_project_creates_subdirectories() -> None: create_fake_lean_cli_directory() result = CliRunner().invoke(lean, ["create-project", "--language", "python", "My First Category/My First Project"]) assert result.exit_code == 0 assert (Path.cwd() / "My First Category" / "My First Project").exists()
def test_get_backtest_by_id_raises_when_backtest_with_given_id_does_not_exist( ) -> None: create_fake_lean_cli_directory() manager = _create_output_config_manager() with pytest.raises(Exception): manager.get_backtest_by_id(123)
def test_create_project_creates_csharp_project_when_language_csharp(path: str) -> None: create_fake_lean_cli_directory() result = CliRunner().invoke(lean, ["create-project", "--language", "csharp", path]) assert result.exit_code == 0 assert_csharp_project_exists(path)
def test_find_algorithm_file_returns_main_py_when_input_directory_contains_it( ) -> None: create_fake_lean_cli_directory() project_manager = _create_project_manager() result = project_manager.find_algorithm_file(Path.cwd() / "Python Project") assert result == Path.cwd() / "Python Project" / "main.py"
def test_create_project_aborts_when_project_already_exists() -> None: create_fake_lean_cli_directory() (Path.cwd() / "My First Project").mkdir() result = CliRunner().invoke(lean, ["create-project", "--language", "python", "My First Project"]) assert result.exit_code != 0