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
예제 #2
0
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"
예제 #4
0
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()
예제 #5
0
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()
예제 #6
0
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"
예제 #7
0
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()
예제 #8
0
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
예제 #9
0
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)
예제 #10
0
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
예제 #11
0
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"
예제 #12
0
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
예제 #13
0
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()
예제 #14
0
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])
예제 #15
0
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()
예제 #16
0
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()
예제 #17
0
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
예제 #18
0
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
예제 #19
0
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
예제 #20
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/")
예제 #21
0
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)
예제 #22
0
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
예제 #23
0
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()
예제 #24
0
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"
예제 #25
0
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"
예제 #26
0
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)
예제 #28
0
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)
예제 #29
0
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"
예제 #30
0
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