Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --data-provider option to the optimize command #409

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,10 @@ Options:
--parameter <TEXT FLOAT FLOAT FLOAT>...
The 'parameter min max step' pairs configuring the parameters to optimize
--constraint TEXT The 'statistic operator value' pairs configuring the constraints of the optimization
--data-provider [IQFeed|Polygon|QuantConnect|Local|Terminal Link]
Update the Lean configuration file to retrieve data from the given provider
--download-data Update the Lean configuration file to download data from the QuantConnect API, alias
for --data-provider QuantConnect
--release Compile C# projects in release configuration instead of debug
--image TEXT The LEAN engine image to use (defaults to quantconnect/lean:latest)
--update Pull the LEAN engine image before running the optimizer
Expand All @@ -1463,6 +1467,24 @@ Options:
--extra-docker-config TEXT Extra docker configuration as a JSON string. For more information https://docker-
py.readthedocs.io/en/stable/containers.html
--no-update Use the local LEAN engine image instead of pulling the latest version
--iqfeed-iqconnect TEXT The path to the IQConnect binary
--iqfeed-username TEXT Your IQFeed username
--iqfeed-password TEXT Your IQFeed password
--iqfeed-version TEXT The product version of your IQFeed developer account
--iqfeed-host TEXT The IQFeed host address
--polygon-api-key TEXT Your Polygon.io API Key
--terminal-link-connection-type [DAPI|SAPI]
Terminal Link Connection Type [DAPI, SAPI]
--terminal-link-server-auth-id TEXT
The Auth ID of the TerminalLink server
--terminal-link-environment [Production|Beta]
The environment to run in
--terminal-link-server-host TEXT
The host of the TerminalLink server
--terminal-link-server-port INTEGER
The port of the TerminalLink server
--terminal-link-openfigi-api-key TEXT
The Open FIGI API key to use for mapping options
--lean-config FILE The Lean configuration file that should be used (defaults to the nearest lean.json)
--verbose Enable debug logging
--help Show this message and exit.
Expand Down
32 changes: 29 additions & 3 deletions lean/commands/optimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
from lean.constants import DEFAULT_ENGINE_IMAGE
from lean.container import container
from lean.models.api import QCParameter, QCBacktest
from lean.models.click_options import options_from_json, get_configs_for_options
from lean.models.data_providers import all_data_providers, QuantConnectDataProvider, DataProvider
from lean.models.errors import MoreInfoError
from lean.models.optimizer import OptimizationTarget
from lean.components.util.json_modules_handler import build_and_configure_modules
from lean.components.util.json_modules_handler import build_and_configure_modules, get_and_build_module


def _get_latest_backtest_runtime(algorithm_directory: Path) -> timedelta:
from re import findall
Expand Down Expand Up @@ -94,6 +97,14 @@ def get_filename_timestamp(path: Path) -> datetime:
type=str,
multiple=True,
help="The 'statistic operator value' pairs configuring the constraints of the optimization")
@option("--data-provider",
type=Choice([dp.get_name() for dp in all_data_providers], case_sensitive=False),
default="Local",
help="Update the Lean configuration file to retrieve data from the given provider")
@option("--download-data",
is_flag=True,
default=False,
help="Update the Lean configuration file to download data from the QuantConnect API, alias for --data-provider QuantConnect")
@option("--release",
is_flag=True,
default=False,
Expand Down Expand Up @@ -129,6 +140,7 @@ def get_filename_timestamp(path: Path) -> datetime:
is_flag=True,
default=False,
help="Use the local LEAN engine image instead of pulling the latest version")
@options_from_json(get_configs_for_options("backtest"))
def optimize(project: Path,
output: Optional[Path],
detach: bool,
Expand All @@ -138,6 +150,8 @@ def optimize(project: Path,
target_direction: Optional[str],
parameter: List[Tuple[str, float, float, float]],
constraint: List[str],
data_provider: Optional[str],
download_data: bool,
release: bool,
image: Optional[str],
update: bool,
Expand All @@ -146,7 +160,8 @@ def optimize(project: Path,
addon_module: Optional[List[str]],
extra_config: Optional[Tuple[str, str]],
extra_docker_config: Optional[str],
no_update: bool) -> None:
no_update: bool,
**kwargs) -> None:
"""Optimize a project's parameters locally using Docker.

\b
Expand Down Expand Up @@ -281,6 +296,17 @@ def optimize(project: Path,
lean_config_manager = container.lean_config_manager
lean_config = lean_config_manager.get_complete_lean_config(environment, algorithm_file, None)

organization_id = container.organization_manager.try_get_working_organization_id()

if download_data:
data_provider = QuantConnectDataProvider.get_name()

if data_provider is not None:
data_provider_configurer: DataProvider = get_and_build_module(data_provider, all_data_providers, kwargs, logger)
data_provider_configurer.ensure_module_installed(organization_id)
data_provider_configurer.configure(lean_config, environment)
logger.info(lean_config)

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

Expand All @@ -301,7 +327,7 @@ def optimize(project: Path,
lean_config["algorithm-id"] = str(output_config_manager.get_optimization_id(output))

# Configure addon modules
build_and_configure_modules(addon_module, container.organization_manager.try_get_working_organization_id(), lean_config, logger, environment)
build_and_configure_modules(addon_module, organization_id, lean_config, logger, environment)

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

Expand Down
30 changes: 30 additions & 0 deletions tests/commands/test_optimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from click.testing import CliRunner

from lean.commands import lean
from lean.components.cloud.module_manager import ModuleManager
from lean.components.config.storage import Storage
from lean.constants import DEFAULT_ENGINE_IMAGE, LEAN_ROOT_PATH
from lean.container import container
Expand Down Expand Up @@ -786,3 +787,32 @@ def test_optimize_runs_lean_container_with_extra_docker_config() -> None:
volumes = kwargs["volumes"]
assert "extra/path" in volumes
assert volumes["extra/path"] == {"bind": "/extra/path", "mode": "rw"}


def test_optimize_used_data_downloader_specified_with_data_provider_option() -> None:
create_fake_lean_cli_directory()

docker_manager = mock.MagicMock()
docker_manager.run_image.side_effect = run_image
container.initialize(docker_manager=docker_manager)
container.optimizer_config_manager = _get_optimizer_config_manager_mock()

Storage(str(Path.cwd() / "Python Project" / "config.json")).set("parameters", {"param1": "1"})

with mock.patch.object(ModuleManager, "install_module"):
result = CliRunner().invoke(lean, ["optimize", "Python Project",
"--data-provider", "Polygon",
"--polygon-api-key", "my-key"])

assert result.exit_code == 0

docker_manager.run_image.assert_called_once()
args, kwargs = docker_manager.run_image.call_args
mounts = kwargs["mounts"]

lean_config_filename = next(mount["Source"] for mount in mounts if mount["Target"] == "/Lean/Launcher/bin/Debug/config.json")
assert lean_config_filename is not None

config = json.loads(Path(lean_config_filename).read_text(encoding="utf-8"))
assert "data-downloader" in config
assert config["data-downloader"] == "QuantConnect.Polygon.PolygonDataDownloader"
Loading