Exemple #1
0
def test_debug_does_not_log_until_debug_logging_is_enabled(capsys: CaptureFixture) -> None:
    logger = Logger()
    logger.debug("Message 1")
    logger.debug_logging_enabled = True
    logger.debug("Message 2")

    assert_stdout_stderr(capsys, "Message 2\n", "")
Exemple #2
0
def test_prompt_list_returns_id_of_selected_option(prompt: mock.Mock, capsys: CaptureFixture) -> None:
    logger = Logger()
    options = [Option(id=1, label="Option 1"), Option(id=2, label="Option 2"), Option(id=3, label="Option 3")]

    prompt.return_value = 3
    selected_option = logger.prompt_list("Select an option", options)

    assert selected_option == 3

    capsys.readouterr()
Exemple #3
0
def test_progress_creates_started_progress_instance(capsys: CaptureFixture) -> None:
    logger = Logger()
    progress = logger.progress()

    result = progress._started

    progress.stop()
    assert result

    capsys.readouterr()
Exemple #4
0
def test_prompt_returns_single_option_without_prompting_with_display_of_value(capsys: CaptureFixture) -> None:
    logger = Logger()
    options = [Option(id=1, label="Option 1")]

    selected_option = logger.prompt_list("Select an option", options)

    assert selected_option == 1

    stdout, stderr = capsys.readouterr()
    assert "Select an option: Option 1" in stdout
Exemple #5
0
def test_prompt_list_displays_all_options(prompt: mock.Mock, capsys: CaptureFixture) -> None:
    logger = Logger()
    options = [Option(id=1, label="Option 1"), Option(id=2, label="Option 2"), Option(id=3, label="Option 3")]

    prompt.return_value = 3
    logger.prompt_list("Select an option", options)

    stdout, stderr = capsys.readouterr()
    assert "Option 1" in stdout
    assert "Option 2" in stdout
    assert "Option 3" in stdout
Exemple #6
0
def test_error_logs_message(capsys: CaptureFixture) -> None:
    logger = Logger()
    logger.error("Message")

    assert_stdout_stderr(capsys, "Message\n", "")
Exemple #7
0
def test_debug_logs_message(capsys: CaptureFixture) -> None:
    logger = Logger()
    logger.debug_logging_enabled = True
    logger.debug("Message")

    assert_stdout_stderr(capsys, "Message\n", "")
Exemple #8
0
def test_cli() -> None:
    """Tests the CLI by actually calling it like a real user would do.

    Unlike "normal" tests, this file only contains a single test method which steps through all commands.
    This is done on purpose to make the test as close to what real users do as possible.
    """
    user_id = USER_ID or os.getenv("QC_USER_ID", "")
    api_token = API_TOKEN or os.getenv("QC_API_TOKEN", "")

    if user_id == "" or api_token == "":
        pytest.skip("API credentials not specified")

    global_config_path = Path("~/.lean").expanduser()
    credentials_path = global_config_path / "credentials"

    # Create an empty directory to perform tests in
    test_dir = Path(tempfile.mkdtemp())

    # We use project names suffixed by a timestamp to prevent conflicts when we synchronize with the cloud
    timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
    python_project_name = f"Python Project {timestamp}"
    csharp_project_name = f"CSharp Project {timestamp}"

    # Unset all global configuration
    shutil.rmtree(global_config_path, ignore_errors=True)

    # Log in
    run_command(["lean", "login"], input=[user_id, api_token])
    assert credentials_path.exists()
    assert json.loads(credentials_path.read_text(encoding="utf-8")) == {
        "user-id": user_id,
        "api-token": api_token
    }

    # Download sample data and LEAN configuration file
    run_command(["lean", "init"], cwd=test_dir, input=["python"])
    assert (test_dir / "data").is_dir()
    assert (test_dir / "lean.json").is_file()

    # Generate random data
    # This is the first command that uses the LEAN Docker image, so we increase the timeout to have time to pull it
    generate_output = run_command([
        "lean", "data", "generate", "--start", "20150101", "--symbol-count",
        "1", "--resolution", "Daily"
    ],
                                  cwd=test_dir,
                                  timeout=600)
    matches = re.findall(
        r"Begin data generation of 1 randomly generated Equity assets\.\.\.\nSymbol\[1]: ([A-Z]+)",
        generate_output)
    assert len(matches) == 1
    assert (test_dir / "data" / "equity" / "usa" / "daily" /
            f"{matches[0].lower()}.zip").is_file()

    # Configure global settings
    run_command(["lean", "config", "set", "default-language", "csharp"])
    run_command(["lean", "config", "get", "default-language"],
                expected_output="csharp")
    run_command(["lean", "config", "unset", "default-language"])
    run_command(["lean", "config", "get", "default-language"],
                expected_return_code=1)
    run_command(["lean", "config", "set", "default-language", "python"])
    run_command(["lean", "config", "get", "default-language"],
                expected_output="python")
    list_output = run_command(["lean", "config", "list"])
    assert len(re.findall(r"default-language[ ]+[^ ] python",
                          list_output)) == 1

    # Create Python project
    run_command([
        "lean", "create-project", "--language", "python", python_project_name
    ],
                cwd=test_dir)
    python_project_dir = test_dir / python_project_name
    assert (python_project_dir / "main.py").is_file()
    assert (python_project_dir / "research.ipynb").is_file()
    assert (python_project_dir / "config.json").is_file()
    assert (python_project_dir / ".vscode" / "launch.json").is_file()
    assert (python_project_dir / ".vscode" / "settings.json").is_file()
    assert (python_project_dir / ".idea" /
            f"{python_project_name}.iml").is_file()
    assert (python_project_dir / ".idea" / "misc.xml").is_file()
    assert (python_project_dir / ".idea" / "modules.xml").is_file()
    assert (python_project_dir / ".idea" / "workspace.xml").is_file()

    # Create C# project
    run_command([
        "lean", "create-project", "--language", "csharp", csharp_project_name
    ],
                cwd=test_dir)
    csharp_project_dir = test_dir / csharp_project_name
    assert (csharp_project_dir / "Main.cs").is_file()
    assert (csharp_project_dir / "research.ipynb").is_file()
    assert (csharp_project_dir / "config.json").is_file()
    assert (csharp_project_dir / f"{csharp_project_name}.csproj").is_file()
    assert (csharp_project_dir / ".vscode" / "launch.json").is_file()

    # Copy over algorithms containing a SPY buy-and-hold strategy
    fixtures_dir = Path(__file__).parent / "fixtures"
    shutil.copy(fixtures_dir / "main.py", python_project_dir / "main.py")
    shutil.copy(fixtures_dir / "Main.cs", csharp_project_dir / "Main.cs")

    # Backtest Python project locally
    run_command(["lean", "backtest", python_project_name],
                cwd=test_dir,
                expected_output="Total Trades 1")
    python_backtest_dirs = list((python_project_dir / "backtests").iterdir())
    assert len(python_backtest_dirs) == 1

    # Backtest C# project locally
    run_command(["lean", "backtest", csharp_project_name],
                cwd=test_dir,
                expected_output="Total Trades 1")
    csharp_backtest_dirs = list((csharp_project_dir / "backtests").iterdir())
    assert len(csharp_backtest_dirs) == 1

    # Generate report
    run_command([
        "lean", "report", "--backtest-data-source-file",
        f"{python_project_name}/backtests/{python_backtest_dirs[0].name}/main.json"
    ],
                cwd=test_dir)
    assert (test_dir / "report.html").is_file()

    # Push projects to the cloud
    run_command(["lean", "cloud", "push", "--project", python_project_name],
                cwd=test_dir)
    run_command(["lean", "cloud", "push", "--project", csharp_project_name],
                cwd=test_dir)

    # Remove some files and see if we can successfully pull them from the cloud
    (python_project_dir / "main.py").unlink()
    (csharp_project_dir / "Main.cs").unlink()

    # Pull projects from the cloud
    run_command(["lean", "cloud", "pull", "--project", python_project_name],
                cwd=test_dir)
    run_command(["lean", "cloud", "pull", "--project", csharp_project_name],
                cwd=test_dir)

    # Ensure deleted files have been pulled
    (python_project_dir / "main.py").is_file()
    (csharp_project_dir / "Main.cs").is_file()

    # Run Python backtest in the cloud
    run_command(["lean", "cloud", "backtest", python_project_name],
                cwd=test_dir)

    # Run C# backtest in the cloud
    run_command(["lean", "cloud", "backtest", csharp_project_name],
                cwd=test_dir)

    # Log out
    run_command(["lean", "logout"])
    assert not credentials_path.exists()

    # Delete the test directory that we used
    shutil.rmtree(test_dir, ignore_errors=True)

    # Delete the cloud projects that we used
    api_client = APIClient(Logger(), user_id, api_token)
    cloud_projects = api_client.projects.get_all()
    api_client.projects.delete(
        next(p.projectId for p in cloud_projects
             if p.name == python_project_name))
    api_client.projects.delete(
        next(p.projectId for p in cloud_projects
             if p.name == csharp_project_name))
Exemple #9
0
def test_cli() -> None:
    """Tests the CLI by actually calling it like a real user would do.

    Unlike "normal" tests, this file only contains a single test method which steps through all commands.
    This is done on purpose to make the test as close to what real users do as possible.
    """
    user_id = USER_ID or os.environ.get("QC_USER_ID", "")
    api_token = API_TOKEN or os.environ.get("QC_API_TOKEN", "")

    if user_id == "" or api_token == "":
        pytest.skip("API credentials not specified")

    credentials_path = Path("~/.lean").expanduser() / "credentials"

    # Create an empty directory to perform tests in
    test_dir = Path(tempfile.mkdtemp())

    # We use project names suffixed by a timestamp to prevent conflicts when we synchronize with the cloud
    timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
    python_project_name = f"Python Project {timestamp}"
    csharp_project_name = f"CSharp Project {timestamp}"

    # Log in
    run_command(["lean", "login"], input=[user_id, api_token])
    assert credentials_path.exists()
    assert json.loads(credentials_path.read_text(encoding="utf-8")) == {
        "user-id": user_id,
        "api-token": api_token
    }

    # Check that we are logged in
    run_command(["lean", "whoami"])

    # Download sample data and LEAN configuration file
    run_command(["lean", "init"], cwd=test_dir, input=["python"])
    assert (test_dir / "data").is_dir()
    assert (test_dir / "lean.json").is_file()

    # Generate random data
    # This is the first command that uses the LEAN Docker image, so we increase the timeout to have time to pull it
    generate_output = run_command([
        "lean", "data", "generate", "--start", "20150101", "--symbol-count",
        "1", "--resolution", "Daily"
    ],
                                  cwd=test_dir,
                                  timeout=600)
    matches = re.findall(
        r"Begin data generation of 1 randomly generated Equity assets\.\.\.\r?\n\s*Symbol\[1]: ([A-Z]+)",
        generate_output)
    assert len(matches) == 1
    assert (test_dir / "data" / "equity" / "usa" / "daily" /
            f"{matches[0].lower()}.zip").is_file()

    # Configure global settings
    run_command(["lean", "config", "set", "default-language", "csharp"])
    run_command(["lean", "config", "get", "default-language"],
                expected_output="csharp")
    run_command(["lean", "config", "unset", "default-language"])
    run_command(["lean", "config", "get", "default-language"],
                expected_return_code=1)
    run_command(["lean", "config", "set", "default-language", "python"])
    run_command(["lean", "config", "get", "default-language"],
                expected_output="python")
    list_output = run_command(["lean", "config", "list"])
    assert len(re.findall(r"default-language[ ]+[^ ] python",
                          list_output)) == 1

    # Create Python project
    run_command([
        "lean", "create-project", "--language", "python", python_project_name
    ],
                cwd=test_dir)
    python_project_dir = test_dir / python_project_name
    assert (python_project_dir / "main.py").is_file()
    assert (python_project_dir / "research.ipynb").is_file()
    assert (python_project_dir / "config.json").is_file()
    assert (python_project_dir / ".vscode" / "launch.json").is_file()
    assert (python_project_dir / ".vscode" / "settings.json").is_file()
    assert (python_project_dir / ".idea" /
            f"{python_project_name}.iml").is_file()
    assert (python_project_dir / ".idea" / "misc.xml").is_file()
    assert (python_project_dir / ".idea" / "modules.xml").is_file()
    assert (python_project_dir / ".idea" / "workspace.xml").is_file()

    # Create C# project
    run_command([
        "lean", "create-project", "--language", "csharp", csharp_project_name
    ],
                cwd=test_dir)
    csharp_project_dir = test_dir / csharp_project_name
    assert (csharp_project_dir / "Main.cs").is_file()
    assert (csharp_project_dir / "research.ipynb").is_file()
    assert (csharp_project_dir / "config.json").is_file()
    assert (csharp_project_dir / f"{csharp_project_name}.csproj").is_file()
    assert (csharp_project_dir / ".vscode" / "launch.json").is_file()

    # Add custom Python library
    run_command(["lean", "library", "add", python_project_name, "altair"],
                cwd=test_dir)
    assert (python_project_dir / "requirements.txt").is_file()
    assert f"altair==" in (python_project_dir /
                           "requirements.txt").read_text(encoding="utf-8")

    # Cannot add custom Python library incompatible with Python 3.6
    run_command(["lean", "library", "add", python_project_name, "PyS3DE"],
                cwd=test_dir,
                expected_return_code=1)

    # Cannot add custom Python library without version when it's not on PyPI
    run_command(
        ["lean", "library", "add", python_project_name,
         str(uuid.uuid4())],
        cwd=test_dir,
        expected_return_code=1)

    # Cannot add custom Python library with version when version is invalid
    run_command([
        "lean", "library", "add", python_project_name, "matplotlib",
        "--version", "0.0.0.0.0.1"
    ],
                cwd=test_dir,
                expected_return_code=1)

    # Cannot add custom Python library with version when version is incompatible with Python 3.6
    run_command([
        "lean", "library", "add", python_project_name, "matplotlib",
        "--version", "3.4.2"
    ],
                cwd=test_dir,
                expected_return_code=1)

    # Add custom C# library
    run_command(
        ["lean", "library", "add", csharp_project_name, "Microsoft.ML"],
        cwd=test_dir)
    csproj_file = csharp_project_dir / f"{csharp_project_name}.csproj"
    assert 'Include="Microsoft.ML"' in csproj_file.read_text(encoding="utf-8")

    # Cannot add custom C# library without version when it's not on NuGet
    run_command(
        ["lean", "library", "add", csharp_project_name,
         str(uuid.uuid4())],
        cwd=test_dir,
        expected_return_code=1)

    # Copy over algorithms containing a SPY buy-and-hold strategy with custom libraries
    fixtures_dir = Path(__file__).parent / "fixtures"
    shutil.copy(fixtures_dir / "local" / "main.py",
                python_project_dir / "main.py")
    shutil.copy(fixtures_dir / "local" / "Main.cs",
                csharp_project_dir / "Main.cs")

    # Backtest Python project locally
    run_command(["lean", "backtest", python_project_name],
                cwd=test_dir,
                expected_output="Total Trades 1")
    python_backtest_dirs = list((python_project_dir / "backtests").iterdir())
    assert len(python_backtest_dirs) == 1

    # Backtest C# project locally
    run_command(["lean", "backtest", csharp_project_name],
                cwd=test_dir,
                expected_output="Total Trades 1")
    csharp_backtest_dirs = list((csharp_project_dir / "backtests").iterdir())
    assert len(csharp_backtest_dirs) == 1

    # Remove custom Python library
    run_command(["lean", "library", "remove", python_project_name, "altair"],
                cwd=test_dir)
    assert f"altair==" not in (python_project_dir /
                               "requirements.txt").read_text(encoding="utf-8")

    # Remove custom C# library
    run_command(
        ["lean", "library", "remove", csharp_project_name, "Microsoft.ML"],
        cwd=test_dir)
    assert 'Include="Microsoft.ML"' not in csproj_file.read_text(
        encoding="utf-8")

    # Custom Python library is removed, so Python backtest should now fail
    run_command(["lean", "backtest", python_project_name],
                cwd=test_dir,
                expected_return_code=1)

    # Custom C# library is removed, so C# backtest should now fail
    run_command(["lean", "backtest", csharp_project_name],
                cwd=test_dir,
                expected_return_code=1)

    # Generate reports
    python_results_file = next(f for f in python_backtest_dirs[0].iterdir()
                               if f.name.endswith(".json")
                               and not f.name.endswith("-order-events.json"))
    run_command([
        "lean", "report", "--backtest-results",
        str(python_results_file), "--report-destination", "python.html"
    ],
                cwd=test_dir)

    csharp_results_file = next(f for f in csharp_backtest_dirs[0].iterdir()
                               if f.name.endswith(".json")
                               and not f.name.endswith("-order-events.json"))
    run_command([
        "lean", "report", "--backtest-results",
        str(csharp_results_file), "--report-destination", "csharp.html"
    ],
                cwd=test_dir)

    assert (test_dir / "python.html").is_file()
    assert (test_dir / "csharp.html").is_file()

    # Copy over algorithms containing a SPY buy-and-hold strategy without custom libraries
    shutil.copy(fixtures_dir / "cloud" / "main.py",
                python_project_dir / "main.py")
    shutil.copy(fixtures_dir / "cloud" / "Main.cs",
                csharp_project_dir / "Main.cs")

    # Push projects to the cloud
    run_command(["lean", "cloud", "push", "--project", python_project_name],
                cwd=test_dir)
    run_command(["lean", "cloud", "push", "--project", csharp_project_name],
                cwd=test_dir)

    # Remove some files and see if we can successfully pull them from the cloud
    (python_project_dir / "main.py").unlink()
    (csharp_project_dir / "Main.cs").unlink()

    # Pull projects from the cloud
    run_command(["lean", "cloud", "pull", "--project", python_project_name],
                cwd=test_dir)
    run_command(["lean", "cloud", "pull", "--project", csharp_project_name],
                cwd=test_dir)

    # Ensure deleted files have been pulled
    (python_project_dir / "main.py").is_file()
    (csharp_project_dir / "Main.cs").is_file()

    # Run Python backtest in the cloud
    run_command(["lean", "cloud", "backtest", python_project_name],
                cwd=test_dir)

    # Run C# backtest in the cloud
    run_command(["lean", "cloud", "backtest", csharp_project_name],
                cwd=test_dir)

    # Get cloud project status
    run_command(["lean", "cloud", "status", python_project_name], cwd=test_dir)
    run_command(["lean", "cloud", "status", csharp_project_name], cwd=test_dir)

    # Log out
    run_command(["lean", "logout"])
    assert not credentials_path.exists()

    # Delete the test directory that we used
    shutil.rmtree(test_dir, ignore_errors=True)

    # Delete the cloud projects that we used
    api_client = APIClient(Logger(), HTTPClient(Logger()), user_id, api_token)
    cloud_projects = api_client.projects.get_all()
    api_client.projects.delete(
        next(p.projectId for p in cloud_projects
             if p.name == python_project_name))
    api_client.projects.delete(
        next(p.projectId for p in cloud_projects
             if p.name == csharp_project_name))