コード例 #1
0
ファイル: live.py プロジェクト: whosurajnegi/lean-cli
def live(project: Path, environment: str, output: Optional[Path],
         image: Optional[str], update: bool) -> None:
    """Start live trading a project locally using Docker.

    \b
    If PROJECT is a directory, the algorithm in the main.py or Main.cs file inside it will be executed.
    If PROJECT is a file, the algorithm in the specified file will be executed.

    \b
    ENVIRONMENT must be the name of an environment in the Lean configuration file with live-mode set to true.

    By default the official LEAN engine image is used.
    You can override this using the --image option.
    Alternatively you can set the default engine image for all commands using `lean config set engine-image <image>`.
    """
    project_manager = container.project_manager()
    algorithm_file = project_manager.find_algorithm_file(Path(project))

    if output is None:
        output = algorithm_file.parent / "live" / datetime.now().strftime(
            "%Y-%m-%d_%H-%M-%S")

    lean_config_manager = container.lean_config_manager()
    lean_config = lean_config_manager.get_complete_lean_config(
        environment, algorithm_file, None)

    if "environments" not in lean_config or environment not in lean_config[
            "environments"]:
        lean_config_path = lean_config_manager.get_lean_config_path()
        raise MoreInfoError(
            f"{lean_config_path} does not contain an environment named '{environment}'",
            "https://www.quantconnect.com/docs/v2/lean-cli/tutorials/live-trading/local-live-trading"
        )

    if not lean_config["environments"][environment]["live-mode"]:
        raise MoreInfoError(
            f"The '{environment}' is not a live trading environment (live-mode is set to false)",
            "https://www.quantconnect.com/docs/v2/lean-cli/tutorials/live-trading/local-live-trading"
        )

    _raise_for_missing_properties(lean_config, environment,
                                  lean_config_manager.get_lean_config_path())
    _start_iqconnect_if_necessary(lean_config, environment)

    cli_config_manager = container.cli_config_manager()
    engine_image = cli_config_manager.get_engine_image(image)

    docker_manager = container.docker_manager()

    if update or not docker_manager.supports_dotnet_5(engine_image):
        docker_manager.pull_image(engine_image)

    lean_runner = container.lean_runner()
    lean_runner.run_lean(environment, algorithm_file, output, engine_image,
                         None)

    if str(engine_image) == DEFAULT_ENGINE_IMAGE and not update:
        update_manager = container.update_manager()
        update_manager.warn_if_docker_image_outdated(engine_image)
コード例 #2
0
ファイル: backtest.py プロジェクト: whosurajnegi/lean-cli
def backtest(project: Path, output: Optional[Path], debug: Optional[str],
             image: Optional[str], update: bool) -> None:
    """Backtest a project locally using Docker.

    \b
    If PROJECT is a directory, the algorithm in the main.py or Main.cs file inside it will be executed.
    If PROJECT is a file, the algorithm in the specified file will be executed.

    \b
    Go to the following url to learn how to debug backtests locally using the Lean CLI:
    https://www.quantconnect.com/docs/v2/lean-cli/tutorials/backtesting/debugging-local-backtests

    By default the official LEAN engine image is used.
    You can override this using the --image option.
    Alternatively you can set the default engine image for all commands using `lean config set engine-image <image>`.
    """
    project_manager = container.project_manager()
    algorithm_file = project_manager.find_algorithm_file(Path(project))

    if output is None:
        output = algorithm_file.parent / "backtests" / datetime.now().strftime(
            "%Y-%m-%d_%H-%M-%S")

    debugging_method = None
    if debug == "pycharm":
        debugging_method = DebuggingMethod.PyCharm
        _migrate_dotnet_5_python_pycharm(algorithm_file.parent)
    elif debug == "ptvsd":
        debugging_method = DebuggingMethod.PTVSD
        _migrate_dotnet_5_python_vscode(algorithm_file.parent)
    elif debug == "vsdbg":
        debugging_method = DebuggingMethod.VSDBG
        _migrate_dotnet_5_csharp_vscode(algorithm_file.parent)
    elif debug == "rider":
        debugging_method = DebuggingMethod.Rider
        _migrate_dotnet_5_csharp_rider(algorithm_file.parent)

    cli_config_manager = container.cli_config_manager()
    engine_image = cli_config_manager.get_engine_image(image)

    docker_manager = container.docker_manager()

    if update or not docker_manager.supports_dotnet_5(engine_image):
        docker_manager.pull_image(engine_image)

    lean_runner = container.lean_runner()
    lean_runner.run_lean("backtesting", algorithm_file, output, engine_image,
                         debugging_method)

    if str(engine_image) == DEFAULT_ENGINE_IMAGE and not update:
        update_manager = container.update_manager()
        update_manager.warn_if_docker_image_outdated(engine_image)
コード例 #3
0
def backtest(project: Path, output: Optional[Path], update: bool,
             version: Optional[int], debug: Optional[str]) -> None:
    """Backtest a project locally using Docker.

    \b
    If PROJECT is a directory, the algorithm in the main.py or Main.cs file inside it will be executed.
    If PROJECT is a file, the algorithm in the specified file will be executed.

    \b
    Go to the following url to learn how to debug backtests using the Lean CLI:
    https://github.com/QuantConnect/lean-cli#debugging-backtests
    """
    project_manager = container.project_manager()
    algorithm_file = project_manager.find_algorithm_file(Path(project))

    if output is None:
        output = algorithm_file.parent / "backtests" / datetime.now().strftime(
            "%Y-%m-%d_%H-%M-%S")

    lean_runner = container.lean_runner()

    if update:
        lean_runner.force_update()

    # Detect the debugging method to use based on the editor and project language
    debugging_method = None

    if debug == "pycharm":
        debugging_method = "PyCharm"

    if debug == "vs":
        debugging_method = "VisualStudio"

    if debug == "vscode":
        debugging_method = "PTVSD" if algorithm_file.name.endswith(
            ".py") else "VisualStudio"

    lean_runner.run_lean("backtesting", algorithm_file, output, version,
                         debugging_method)
コード例 #4
0
def optimize(project: Path, output: Optional[Path],
             optimizer_config: Optional[Path], image: Optional[str],
             update: bool) -> None:
    """Optimize a project's parameters locally using Docker.

    \b
    If PROJECT is a directory, the algorithm in the main.py or Main.cs file inside it will be executed.
    If PROJECT is a file, the algorithm in the specified file will be executed.

    \b
    The --optimizer-config option can be used to specify the configuration to run the optimizer with.
    When using the option it should point to a file like this (the algorithm-* properties should be omitted):
    https://github.com/QuantConnect/Lean/blob/master/Optimizer.Launcher/config.json

    When --optimizer-config is not set, an interactive prompt will be shown to configure the optimizer.

    By default the official LEAN engine image is used.
    You can override this using the --image option.
    Alternatively you can set the default engine image for all commands using `lean config set engine-image <image>`.
    """
    project_manager = container.project_manager()
    algorithm_file = project_manager.find_algorithm_file(project)

    if output is None:
        output = algorithm_file.parent / "optimizations" / datetime.now(
        ).strftime("%Y-%m-%d_%H-%M-%S")

    if optimizer_config is None:
        project_config_manager = container.project_config_manager()
        project_config = project_config_manager.get_project_config(
            algorithm_file.parent)
        project_parameters = [
            QCParameter(key=k, value=v)
            for k, v in project_config.get("parameters", {}).items()
        ]

        if len(project_parameters) == 0:
            raise MoreInfoError(
                "The given project has no parameters to optimize",
                "https://www.lean.io/docs/lean-cli/tutorials/optimization/project-parameters"
            )

        optimizer_config_manager = container.optimizer_config_manager()
        optimization_strategy = optimizer_config_manager.configure_strategy(
            cloud=False)
        optimization_target = optimizer_config_manager.configure_target()
        optimization_parameters = optimizer_config_manager.configure_parameters(
            project_parameters, cloud=False)
        optimization_constraints = optimizer_config_manager.configure_constraints(
        )

        config = {
            "optimization-strategy":
            optimization_strategy,
            "optimization-strategy-settings": {
                "$type":
                "QuantConnect.Optimizer.Strategies.StepBaseOptimizationStrategySettings, QuantConnect.Optimizer",
                "default-segment-amount": 10
            },
            "optimization-criterion": {
                "target": optimization_target.target,
                "extremum": optimization_target.extremum.value
            },
            "parameters":
            [parameter.dict() for parameter in optimization_parameters],
            "constraints": [
                constraint.dict(by_alias=True)
                for constraint in optimization_constraints
            ]
        }
    else:
        config = json5.loads(optimizer_config.read_text(encoding="utf-8"))

        # Remove keys which are configured in the Lean config
        for key in [
                "algorithm-type-name", "algorithm-language",
                "algorithm-location"
        ]:
            config.pop(key, None)

    config["optimizer-close-automatically"] = True
    config["results-destination-folder"] = "/Results"

    config_path = output / "optimizer-config.json"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    with config_path.open("w+", encoding="utf-8") as file:
        file.write(json.dumps(config, indent=4) + "\n")

    cli_config_manager = container.cli_config_manager()
    engine_image = cli_config_manager.get_engine_image(image)

    lean_config_manager = container.lean_config_manager()
    lean_config = lean_config_manager.get_complete_lean_config(
        "backtesting", algorithm_file, None, None)

    lean_runner = container.lean_runner()
    run_options = lean_runner.get_basic_docker_config(lean_config,
                                                      algorithm_file, output,
                                                      None)

    run_options["working_dir"] = "/Lean/Optimizer.Launcher/bin/Debug"
    run_options["commands"].append(
        "dotnet QuantConnect.Optimizer.Launcher.dll")
    run_options["mounts"].append(
        Mount(target="/Lean/Optimizer.Launcher/bin/Debug/config.json",
              source=str(config_path),
              type="bind",
              read_only=True))

    docker_manager = container.docker_manager()

    if update or not docker_manager.supports_dotnet_5(engine_image):
        docker_manager.pull_image(engine_image)

    success = docker_manager.run_image(engine_image, **run_options)

    cli_root_dir = container.lean_config_manager().get_cli_root_directory()
    relative_project_dir = project.relative_to(cli_root_dir)
    relative_output_dir = output.relative_to(cli_root_dir)

    if success:
        logger = container.logger()

        optimizer_logs = (output / "log.txt").read_text(encoding="utf-8")
        groups = re.findall(r"ParameterSet: \(([^)]+)\) backtestId '([^']+)'",
                            optimizer_logs)

        if len(groups) > 0:
            optimal_parameters, optimal_id = groups[0]

            optimal_results = json.loads(
                (output / optimal_id /
                 f"{optimal_id}.json").read_text(encoding="utf-8"))
            optimal_backtest = QCBacktest(
                backtestId=optimal_id,
                projectId=1,
                status="",
                name=optimal_id,
                created=datetime.now(),
                completed=True,
                progress=1.0,
                runtimeStatistics=optimal_results["RuntimeStatistics"],
                statistics=optimal_results["Statistics"])

            logger.info(
                f"Optimal parameters: {optimal_parameters.replace(':', ': ').replace(',', ', ')}"
            )
            logger.info(f"Optimal backtest results:")
            logger.info(optimal_backtest.get_statistics_table())

        logger.info(
            f"Successfully optimized '{relative_project_dir}' and stored the output in '{relative_output_dir}'"
        )
    else:
        raise RuntimeError(
            f"Something went wrong while running the optimization, the output is stored in '{relative_output_dir}'"
        )

    if str(engine_image) == DEFAULT_ENGINE_IMAGE and not update:
        update_manager = container.update_manager()
        update_manager.warn_if_docker_image_outdated(engine_image)
コード例 #5
0
def research(project: Path, port: int, data_provider: Optional[str],
             download_data: bool, data_purchase_limit: Optional[int],
             detach: bool, no_open: bool, image: Optional[str],
             update: bool) -> None:
    """Run a Jupyter Lab environment locally using Docker.

    By default the official LEAN research image is used.
    You can override this using the --image option.
    Alternatively you can set the default research image using `lean config set research-image <image>`.
    """
    project_manager = container.project_manager()
    algorithm_file = project_manager.find_algorithm_file(project)

    lean_config_manager = container.lean_config_manager()
    lean_config = lean_config_manager.get_complete_lean_config(
        "backtesting", algorithm_file, None)
    lean_config["composer-dll-directory"] = "/Lean/Launcher/bin/Debug"

    if download_data:
        data_provider = QuantConnectDataProvider.get_name()

    if data_provider is not None:
        data_provider = next(dp for dp in all_data_providers
                             if dp.get_name() == data_provider)
        data_provider.build(lean_config, container.logger()).configure(
            lean_config, "backtesting")

    lean_config_manager.configure_data_purchase_limit(lean_config,
                                                      data_purchase_limit)

    lean_runner = container.lean_runner()
    temp_manager = container.temp_manager()
    run_options = lean_runner.get_basic_docker_config(
        lean_config, algorithm_file, temp_manager.create_temporary_directory(),
        None, False, detach)

    # Mount the config in the notebooks directory as well
    local_config_path = next(m["Source"] for m in run_options["mounts"]
                             if m["Target"].endswith("config.json"))
    run_options["mounts"].append(
        Mount(target="/Lean/Launcher/bin/Debug/Notebooks/config.json",
              source=str(local_config_path),
              type="bind",
              read_only=True))

    # Jupyter Lab runs on port 8888, we expose it to the local port specified by the user
    run_options["ports"]["8888"] = str(port)

    # Open the browser as soon as Jupyter Lab has started
    if detach or not no_open:
        run_options["on_output"] = lambda chunk: _check_docker_output(
            chunk, port)

    # Give container an identifiable name when running it from the GUI
    if container.module_manager().is_module_installed(GUI_PRODUCT_INSTALL_ID):
        project_id = container.project_config_manager().get_local_id(
            algorithm_file.parent)
        run_options["name"] = f"lean_cli_gui_research_{project_id}"

    # Make Ctrl+C stop Jupyter Lab immediately
    run_options["stop_signal"] = "SIGKILL"

    # Mount the project to the notebooks directory
    run_options["volumes"][str(project)] = {
        "bind": "/Lean/Launcher/bin/Debug/Notebooks",
        "mode": "rw"
    }

    # Add references to all DLLs in QuantConnect.csx so custom C# libraries can be imported with using statements
    run_options["commands"].append(" && ".join([
        'find . -maxdepth 1 -iname "*.dll" | xargs -I _ echo \'#r "_"\' | cat - QuantConnect.csx > NewQuantConnect.csx',
        "mv NewQuantConnect.csx QuantConnect.csx"
    ]))

    # Allow notebooks to be embedded in iframes
    run_options["commands"].append("mkdir -p ~/.jupyter")
    run_options["commands"].append(
        'echo "c.NotebookApp.disable_check_xsrf = True\nc.NotebookApp.tornado_settings = {\'headers\': {\'Content-Security-Policy\': \'frame-ancestors self *\'}}" > ~/.jupyter/jupyter_notebook_config.py'
    )

    # Hide headers in notebooks
    run_options["commands"].append(
        "mkdir -p ~/.ipython/profile_default/static/custom")
    run_options["commands"].append(
        'echo "#header-container { display: none !important; }" > ~/.ipython/profile_default/static/custom/custom.css'
    )

    # Run the script that starts Jupyter Lab when all set up has been done
    run_options["commands"].append("./start.sh")

    project_config_manager = container.project_config_manager()
    cli_config_manager = container.cli_config_manager()

    project_config = project_config_manager.get_project_config(
        algorithm_file.parent)
    research_image = cli_config_manager.get_research_image(
        image or project_config.get("research-image", None))

    container.update_manager().pull_docker_image_if_necessary(
        research_image, update)

    try:
        container.docker_manager().run_image(research_image, **run_options)
    except APIError as error:
        msg = error.explanation
        if isinstance(msg, str) and any(m in msg.lower() for m in [
                "port is already allocated", "ports are not available"
                "an attempt was made to access a socket in a way forbidden by its access permissions"
        ]):
            raise RuntimeError(
                f"Port {port} is already in use, please specify a different port using --port <number>"
            )
        raise error

    if detach:
        temp_manager.delete_temporary_directories_when_done = False

        logger = container.logger()
        relative_project_dir = algorithm_file.parent.relative_to(
            lean_config_manager.get_cli_root_directory())

        logger.info(
            f"Successfully started Jupyter Lab environment for '{relative_project_dir}' in the '{run_options['name']}' container"
        )
        logger.info(
            "You can use Docker's own commands to manage the detached container"
        )
コード例 #6
0
def backtest(project: Path, output: Optional[Path], detach: bool,
             debug: Optional[str], data_provider: Optional[str],
             download_data: bool, data_purchase_limit: Optional[int],
             release: bool, image: Optional[str], update: bool) -> None:
    """Backtest a project locally using Docker.

    \b
    If PROJECT is a directory, the algorithm in the main.py or Main.cs file inside it will be executed.
    If PROJECT is a file, the algorithm in the specified file will be executed.

    \b
    Go to the following url to learn how to debug backtests locally using the Lean CLI:
    https://www.lean.io/docs/lean-cli/tutorials/backtesting/debugging

    By default the official LEAN engine image is used.
    You can override this using the --image option.
    Alternatively you can set the default engine image for all commands using `lean config set engine-image <image>`.
    """
    project_manager = container.project_manager()
    algorithm_file = project_manager.find_algorithm_file(Path(project))
    lean_config_manager = container.lean_config_manager()

    if output is None:
        output = algorithm_file.parent / "backtests" / datetime.now().strftime(
            "%Y-%m-%d_%H-%M-%S")

    debugging_method = None
    if debug == "pycharm":
        debugging_method = DebuggingMethod.PyCharm
        _migrate_python_pycharm(algorithm_file.parent)
    elif debug == "ptvsd":
        debugging_method = DebuggingMethod.PTVSD
        _migrate_python_vscode(algorithm_file.parent)
    elif debug == "vsdbg":
        debugging_method = DebuggingMethod.VSDBG
        _migrate_csharp_vscode(algorithm_file.parent)
    elif debug == "rider":
        debugging_method = DebuggingMethod.Rider
        _migrate_csharp_rider(algorithm_file.parent)

    if debugging_method is not None and detach:
        raise RuntimeError(
            "Running a debugging session in a detached container is not supported"
        )

    if algorithm_file.name.endswith(".cs"):
        _migrate_csharp_csproj(algorithm_file.parent)

    lean_config = lean_config_manager.get_complete_lean_config(
        "backtesting", algorithm_file, debugging_method)

    if download_data:
        data_provider = QuantConnectDataProvider.get_name()

    if data_provider is not None:
        data_provider = next(dp for dp in all_data_providers
                             if dp.get_name() == data_provider)
        data_provider.build(lean_config, container.logger()).configure(
            lean_config, "backtesting")

    lean_config_manager.configure_data_purchase_limit(lean_config,
                                                      data_purchase_limit)

    cli_config_manager = container.cli_config_manager()
    project_config_manager = container.project_config_manager()

    project_config = project_config_manager.get_project_config(
        algorithm_file.parent)
    engine_image = cli_config_manager.get_engine_image(
        image or project_config.get("engine-image", None))

    container.update_manager().pull_docker_image_if_necessary(
        engine_image, update)

    if not output.exists():
        output.mkdir(parents=True)

    output_config_manager = container.output_config_manager()
    lean_config["algorithm-id"] = str(
        output_config_manager.get_backtest_id(output))

    lean_runner = container.lean_runner()
    lean_runner.run_lean(lean_config, "backtesting", algorithm_file, output,
                         engine_image, debugging_method, release, detach)
コード例 #7
0
ファイル: backtest.py プロジェクト: AlphaShadows/lean-cli
def backtest(project: Path, output: Optional[Path], debug: Optional[str],
             download_data: bool, data_purchase_limit: Optional[int],
             image: Optional[str], update: bool) -> None:
    """Backtest a project locally using Docker.

    \b
    If PROJECT is a directory, the algorithm in the main.py or Main.cs file inside it will be executed.
    If PROJECT is a file, the algorithm in the specified file will be executed.

    \b
    Go to the following url to learn how to debug backtests locally using the Lean CLI:
    https://www.lean.io/docs/lean-cli/tutorials/backtesting/debugging-local-backtests

    By default the official LEAN engine image is used.
    You can override this using the --image option.
    Alternatively you can set the default engine image for all commands using `lean config set engine-image <image>`.
    """
    project_manager = container.project_manager()
    algorithm_file = project_manager.find_algorithm_file(Path(project))
    lean_config_manager = container.lean_config_manager()

    if output is None:
        output = algorithm_file.parent / "backtests" / datetime.now().strftime(
            "%Y-%m-%d_%H-%M-%S")

    debugging_method = None
    if debug == "pycharm":
        debugging_method = DebuggingMethod.PyCharm
        _migrate_dotnet_5_python_pycharm(algorithm_file.parent)
    elif debug == "ptvsd":
        debugging_method = DebuggingMethod.PTVSD
        _migrate_dotnet_5_python_vscode(algorithm_file.parent)
    elif debug == "vsdbg":
        debugging_method = DebuggingMethod.VSDBG
        _migrate_dotnet_5_csharp_vscode(algorithm_file.parent)
    elif debug == "rider":
        debugging_method = DebuggingMethod.Rider
        _migrate_dotnet_5_csharp_rider(algorithm_file.parent)

    if download_data:
        organization = _select_organization()
        lean_config_manager.set_property("job-organization-id",
                                         organization.id)
        lean_config_manager.set_property(
            "data-provider",
            "QuantConnect.Lean.Engine.DataFeeds.ApiDataProvider")
        lean_config_manager.set_property(
            "map-file-provider",
            "QuantConnect.Data.Auxiliary.LocalZipMapFileProvider")
        lean_config_manager.set_property(
            "factor-file-provider",
            "QuantConnect.Data.Auxiliary.LocalZipFactorFileProvider")

    if data_purchase_limit is not None:
        config = lean_config_manager.get_lean_config()
        if config.get(
                "data-provider",
                None) != "QuantConnect.Lean.Engine.DataFeeds.ApiDataProvider":
            container.logger().warn(
                "--data-purchase-limit is ignored because the data provider is not set to download from the API, use --download-data to set that up"
            )
            data_purchase_limit = None

    cli_config_manager = container.cli_config_manager()
    engine_image = cli_config_manager.get_engine_image(image)

    docker_manager = container.docker_manager()

    if update or not docker_manager.supports_dotnet_5(engine_image):
        docker_manager.pull_image(engine_image)

    lean_config = lean_config_manager.get_complete_lean_config(
        "backtesting", algorithm_file, debugging_method, data_purchase_limit)

    lean_runner = container.lean_runner()
    lean_runner.run_lean(lean_config, "backtesting", algorithm_file, output,
                         engine_image, debugging_method)

    if str(engine_image) == DEFAULT_ENGINE_IMAGE and not update:
        update_manager = container.update_manager()
        update_manager.warn_if_docker_image_outdated(engine_image)
コード例 #8
0
ファイル: research.py プロジェクト: AlphaShadows/lean-cli
def research(project: Path, port: int, image: Optional[str], update: bool) -> None:
    """Run a Jupyter Lab environment locally using Docker.

    By default the official LEAN research image is used.
    You can override this using the --image option.
    Alternatively you can set the default research image using `lean config set research-image <image>`.
    """
    cli_config_manager = container.cli_config_manager()

    project_config_manager = container.project_config_manager()
    project_config = project_config_manager.get_project_config(project)

    # Copy the config to a temporary config file before we add some research-specific configuration to it
    config_path = container.temp_manager().create_temporary_directory() / "config.json"
    project_config.file = config_path

    project_config.set("composer-dll-directory", "/Lean/Launcher/bin/Debug")
    project_config.set("messaging-handler", "QuantConnect.Messaging.Messaging")
    project_config.set("job-queue-handler", "QuantConnect.Queues.JobQueue")
    project_config.set("api-handler", "QuantConnect.Api.Api")
    project_config.set("job-user-id", cli_config_manager.user_id.get_value("1"))
    project_config.set("api-access-token", cli_config_manager.api_token.get_value("default"))

    lean_config_manager = container.lean_config_manager()
    data_dir = lean_config_manager.get_data_directory()

    run_options: Dict[str, Any] = {
        "commands": [],
        "environment": {},
        "mounts": [
            Mount(target="/Lean/Launcher/bin/Debug/Notebooks/config.json",
                  source=str(config_path),
                  type="bind",
                  read_only=True)
        ],
        "volumes": {
            str(data_dir): {
                "bind": "/Lean/Launcher/Data",
                "mode": "rw"
            }
        },
        "ports": {
            "8888": str(port)
        },
        "on_output": lambda chunk: _check_docker_output(chunk, port)
    }

    lean_runner = container.lean_runner()
    if project_config.get("algorithm-language", "Python") == "Python":
        lean_runner.set_up_python_options(project, "/Lean/Launcher/bin/Debug/Notebooks", run_options)
    else:
        lean_runner.set_up_csharp_options(project, run_options)
        run_options["volumes"][str(project)] = {
            "bind": "/Lean/Launcher/bin/Debug/Notebooks",
            "mode": "rw"
        }

    # Add references to all DLLs in QuantConnect.csx so custom C# libraries can be imported with using statements
    run_options["commands"].append(" && ".join([
        'find . -maxdepth 1 -iname "*.dll" | xargs -I _ echo \'#r "_"\' | cat - QuantConnect.csx > NewQuantConnect.csx',
        "mv NewQuantConnect.csx QuantConnect.csx"
    ]))

    # Run the script that starts Jupyter Lab when all set up has been done
    run_options["commands"].append("./start.sh")

    cli_config_manager = container.cli_config_manager()
    research_image = cli_config_manager.get_research_image(image)

    docker_manager = container.docker_manager()

    if update or not docker_manager.supports_dotnet_5(research_image):
        docker_manager.pull_image(research_image)

    if str(research_image) == DEFAULT_RESEARCH_IMAGE and not update:
        update_manager = container.update_manager()
        update_manager.warn_if_docker_image_outdated(research_image)

    try:
        docker_manager.run_image(research_image, **run_options)
    except APIError as error:
        msg = error.explanation
        if isinstance(msg, str) and "port is already allocated" in msg:
            raise RuntimeError(f"Port {port} is already in use, please specify a different port using --port <number>")
        raise error
コード例 #9
0
def optimize(project: Path, output: Optional[Path], detach: bool,
             optimizer_config: Optional[Path], strategy: Optional[str],
             target: Optional[str], target_direction: Optional[str],
             parameter: List[Tuple[str, float, float,
                                   float]], constraint: List[str],
             release: bool, image: Optional[str], update: bool) -> None:
    """Optimize a project's parameters locally using Docker.

    \b
    If PROJECT is a directory, the algorithm in the main.py or Main.cs file inside it will be executed.
    If PROJECT is a file, the algorithm in the specified file will be executed.

    By default an interactive wizard is shown letting you configure the optimizer.
    If --optimizer-config or --strategy is given the command runs in non-interactive mode.
    In this mode the CLI does not prompt for input.

    \b
    The --optimizer-config option can be used to specify the configuration to run the optimizer with.
    When using the option it should point to a file like this (the algorithm-* properties should be omitted):
    https://github.com/QuantConnect/Lean/blob/master/Optimizer.Launcher/config.json

    If --strategy is given the optimizer configuration is read from the given options.
    In this case --strategy, --target, --target-direction and --parameter become required.

    \b
    In non-interactive mode the --parameter option can be provided multiple times to configure multiple parameters:
    - --parameter <name> <min value> <max value> <step size>
    - --parameter my-first-parameter 1 10 0.5 --parameter my-second-parameter 20 30 5

    \b
    In non-interactive mode the --constraint option can be provided multiple times to configure multiple constraints:
    - --constraint "<statistic> <operator> <value>"
    - --constraint "Sharpe Ratio >= 0.5" --constraint "Drawdown < 0.25"

    By default the official LEAN engine image is used.
    You can override this using the --image option.
    Alternatively you can set the default engine image for all commands using `lean config set engine-image <image>`.
    """
    project_manager = container.project_manager()
    algorithm_file = project_manager.find_algorithm_file(project)

    if output is None:
        output = algorithm_file.parent / "optimizations" / datetime.now(
        ).strftime("%Y-%m-%d_%H-%M-%S")

    optimizer_config_manager = container.optimizer_config_manager()
    config = None

    if optimizer_config is not None and strategy is not None:
        raise RuntimeError(
            "--optimizer-config and --strategy are mutually exclusive")

    if optimizer_config is not None:
        config = json5.loads(optimizer_config.read_text(encoding="utf-8"))

        # Remove keys which are configured in the Lean config
        for key in [
                "algorithm-type-name", "algorithm-language",
                "algorithm-location"
        ]:
            config.pop(key, None)
    elif strategy is not None:
        ensure_options(["strategy", "target", "target_direction", "parameter"])

        optimization_strategy = f"QuantConnect.Optimizer.Strategies.{strategy.replace(' ', '')}OptimizationStrategy"
        optimization_target = OptimizationTarget(
            target=optimizer_config_manager.parse_target(target),
            extremum=target_direction)
        optimization_parameters = optimizer_config_manager.parse_parameters(
            parameter)
        optimization_constraints = optimizer_config_manager.parse_constraints(
            constraint)
    else:
        project_config_manager = container.project_config_manager()
        project_config = project_config_manager.get_project_config(
            algorithm_file.parent)
        project_parameters = [
            QCParameter(key=k, value=v)
            for k, v in project_config.get("parameters", {}).items()
        ]

        if len(project_parameters) == 0:
            raise MoreInfoError(
                "The given project has no parameters to optimize",
                "https://www.lean.io/docs/lean-cli/optimization/parameters")

        optimization_strategy = optimizer_config_manager.configure_strategy(
            cloud=False)
        optimization_target = optimizer_config_manager.configure_target()
        optimization_parameters = optimizer_config_manager.configure_parameters(
            project_parameters, cloud=False)
        optimization_constraints = optimizer_config_manager.configure_constraints(
        )

    if config is None:
        # noinspection PyUnboundLocalVariable
        config = {
            "optimization-strategy":
            optimization_strategy,
            "optimization-strategy-settings": {
                "$type":
                "QuantConnect.Optimizer.Strategies.StepBaseOptimizationStrategySettings, QuantConnect.Optimizer",
                "default-segment-amount": 10
            },
            "optimization-criterion": {
                "target": optimization_target.target,
                "extremum": optimization_target.extremum.value
            },
            "parameters":
            [parameter.dict() for parameter in optimization_parameters],
            "constraints": [
                constraint.dict(by_alias=True)
                for constraint in optimization_constraints
            ]
        }

    config["optimizer-close-automatically"] = True
    config["results-destination-folder"] = "/Results"

    config_path = output / "optimizer-config.json"
    config_path.parent.mkdir(parents=True, exist_ok=True)
    with config_path.open("w+", encoding="utf-8") as file:
        file.write(json.dumps(config, indent=4) + "\n")

    project_config_manager = container.project_config_manager()
    cli_config_manager = container.cli_config_manager()

    project_config = project_config_manager.get_project_config(
        algorithm_file.parent)
    engine_image = cli_config_manager.get_engine_image(
        image or project_config.get("engine-image", None))

    lean_config_manager = container.lean_config_manager()
    lean_config = lean_config_manager.get_complete_lean_config(
        "backtesting", algorithm_file, None)

    if not output.exists():
        output.mkdir(parents=True)

    output_config_manager = container.output_config_manager()
    lean_config["algorithm-id"] = str(
        output_config_manager.get_optimization_id(output))
    lean_config["messaging-handler"] = "QuantConnect.Messaging.Messaging"

    lean_runner = container.lean_runner()
    run_options = lean_runner.get_basic_docker_config(lean_config,
                                                      algorithm_file, output,
                                                      None, release, detach)

    run_options["working_dir"] = "/Lean/Optimizer.Launcher/bin/Debug"
    run_options["commands"].append(
        "dotnet QuantConnect.Optimizer.Launcher.dll")
    run_options["mounts"].append(
        Mount(target="/Lean/Optimizer.Launcher/bin/Debug/config.json",
              source=str(config_path),
              type="bind",
              read_only=True))

    container.update_manager().pull_docker_image_if_necessary(
        engine_image, update)

    project_manager.copy_code(algorithm_file.parent, output / "code")

    success = container.docker_manager().run_image(engine_image, **run_options)

    logger = container.logger()
    cli_root_dir = container.lean_config_manager().get_cli_root_directory()
    relative_project_dir = project.relative_to(cli_root_dir)
    relative_output_dir = output.relative_to(cli_root_dir)

    if detach:
        temp_manager = container.temp_manager()
        temp_manager.delete_temporary_directories_when_done = False

        logger.info(
            f"Successfully started optimization for '{relative_project_dir}' in the '{run_options['name']}' container"
        )
        logger.info(f"The output will be stored in '{relative_output_dir}'")
        logger.info(
            "You can use Docker's own commands to manage the detached container"
        )
    elif success:
        optimizer_logs = (output / "log.txt").read_text(encoding="utf-8")
        groups = re.findall(r"ParameterSet: \(([^)]+)\) backtestId '([^']+)'",
                            optimizer_logs)

        if len(groups) > 0:
            optimal_parameters, optimal_id = groups[0]

            optimal_results = json.loads(
                (output / optimal_id /
                 f"{optimal_id}.json").read_text(encoding="utf-8"))
            optimal_backtest = QCBacktest(
                backtestId=optimal_id,
                projectId=1,
                status="",
                name=optimal_id,
                created=datetime.now(),
                completed=True,
                progress=1.0,
                runtimeStatistics=optimal_results["RuntimeStatistics"],
                statistics=optimal_results["Statistics"])

            logger.info(
                f"Optimal parameters: {optimal_parameters.replace(':', ': ').replace(',', ', ')}"
            )
            logger.info(f"Optimal backtest results:")
            logger.info(optimal_backtest.get_statistics_table())

        logger.info(
            f"Successfully optimized '{relative_project_dir}' and stored the output in '{relative_output_dir}'"
        )
    else:
        raise RuntimeError(
            f"Something went wrong while running the optimization, the output is stored in '{relative_output_dir}'"
        )