Skip to content
Open
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
14 changes: 9 additions & 5 deletions lean/commands/cloud/live/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ def _configure_auto_restart(logger: Logger) -> bool:
default=False,
help="Automatically open the live results in the browser once the deployment starts")
@option("--show-secrets", is_flag=True, show_default=True, default=False, help="Show secrets as they are input")
@option("--no-browser",
is_flag=True,
default=False,
help="Display OAuth URL without opening the browser.")
def deploy(project: str,
brokerage: str,
data_provider_live: Optional[str],
Expand All @@ -218,6 +222,7 @@ def deploy(project: str,
push: bool,
open_browser: bool,
show_secrets: bool,
no_browser: bool,
**kwargs) -> None:
"""Start live trading for a project in the cloud.

Expand Down Expand Up @@ -246,7 +251,7 @@ def deploy(project: str,
ensure_options(["brokerage", "node", "auto_restart", "notify_order_events", "notify_insights"])

brokerage_instance = non_interactive_config_build_for_name(lean_config, brokerage, cloud_brokerages,
kwargs, logger)
kwargs, logger, no_browser=no_browser)
notify_methods = []
if notify_emails is not None:
for config in notify_emails.split(","):
Expand Down Expand Up @@ -288,7 +293,7 @@ def deploy(project: str,
else:
# let the user choose the brokerage
brokerage_instance = interactive_config_build(lean_config, cloud_brokerages, logger, kwargs, show_secrets,
"Select a brokerage", multiple=False)
"Select a brokerage", multiple=False, no_browser=no_browser)

notify_order_events, notify_insights, notify_methods = _configure_notifications(logger)
auto_restart = _configure_auto_restart(logger)
Expand All @@ -304,13 +309,13 @@ def deploy(project: str,
# the user sent the live data provider to use
for data_provider in data_provider_live:
data_provider_instance = non_interactive_config_build_for_name(lean_config, data_provider,
cloud_data_queue_handlers, kwargs, logger)
cloud_data_queue_handlers, kwargs, logger, no_browser=no_browser)

live_data_provider_settings.update({data_provider_instance.get_id(): data_provider_instance.get_settings()})
else:
# let's ask the user which live data providers to use
data_feed_instances = interactive_config_build(lean_config, cloud_data_queue_handlers, logger, kwargs,
show_secrets, "Select a live data feed", multiple=True)
show_secrets, "Select a live data feed", multiple=True, no_browser=no_browser)
for data_feed in data_feed_instances:
settings = data_feed.get_settings()

Expand Down Expand Up @@ -354,7 +359,6 @@ def deploy(project: str,
live_holdings)

logger.info(f"Live url: {live_algorithm.get_url()}")

if open_browser:
from webbrowser import open
open(live_algorithm.get_url())
15 changes: 10 additions & 5 deletions lean/commands/live/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ def _get_history_provider_name(data_provider_live_names: [str]) -> [str]:
is_flag=True,
default=False,
help="Use the local LEAN engine image instead of pulling the latest version")
@option("--no-browser",
is_flag=True,
default=False,
help="Display OAuth URL without opening the browser.")
def deploy(project: Path,
environment: Optional[str],
output: Optional[Path],
Expand All @@ -132,6 +136,7 @@ def deploy(project: Path,
extra_config: Optional[Tuple[str, str]],
extra_docker_config: Optional[str],
no_update: bool,
no_browser: bool,
**kwargs) -> None:
"""Start live trading a project locally using Docker.

Expand Down Expand Up @@ -206,30 +211,30 @@ def deploy(project: Path,
if brokerage:
# user provided brokerage, check all arguments were provided
brokerage_instance = non_interactive_config_build_for_name(lean_config, brokerage, cli_brokerages, kwargs,
logger, environment_name)
logger, environment_name, no_browser=no_browser)
else:
# let the user choose the brokerage
brokerage_instance = interactive_config_build(lean_config, cli_brokerages, logger, kwargs, show_secrets,
"Select a brokerage", multiple=False,
environment_name=environment_name)
environment_name=environment_name, no_browser=no_browser)

if data_provider_live and len(data_provider_live) > 0:
for data_feed_name in data_provider_live:
data_feed = non_interactive_config_build_for_name(lean_config, data_feed_name, cli_data_queue_handlers,
kwargs, logger, environment_name)
kwargs, logger, environment_name, no_browser=no_browser)
data_provider_live_instances.append(data_feed)
else:
data_provider_live_instances = interactive_config_build(lean_config, cli_data_queue_handlers, logger, kwargs,
show_secrets, "Select a live data feed", multiple=True,
environment_name=environment_name)
environment_name=environment_name, no_browser=no_browser)

# based on the live data providers we set up the history providers
data_provider_live = [provider.get_name() for provider in data_provider_live_instances]
if data_provider_historical is None:
data_provider_historical = "Local"
data_downloader_instances = non_interactive_config_build_for_name(lean_config, data_provider_historical,
cli_data_downloaders, kwargs, logger,
environment_name)
environment_name, no_browser=no_browser)
if history_providers is None or len(history_providers) == 0:
history_providers = _get_history_provider_name(data_provider_live)
for history_provider in history_providers:
Expand Down
7 changes: 6 additions & 1 deletion lean/components/api/auth0_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,24 @@ def read(self, brokerage_id: str) -> QCAuth0Authorization:
return QCAuth0Authorization(authorization=None)

@staticmethod
def authorize(brokerage_id: str, logger: Logger, project_id: int) -> None:
def authorize(brokerage_id: str, logger: Logger, project_id: int, no_browser: bool = False) -> None:
"""Starts the authorization process for a brokerage.

:param brokerage_id: the id of the brokerage to start the authorization process for
:param logger: the logger instance to use
:param project_id: The local or cloud project_id
:param no_browser: whether to disable opening the browser
"""
from webbrowser import open

full_url = f"{API_BASE_URL}live/auth0/authorize?brokerage={brokerage_id}&projectId={project_id}"

logger.info(f"Please open the following URL in your browser to authorize the LEAN CLI.")
logger.info(full_url)

if no_browser:
return

open(full_url)


5 changes: 3 additions & 2 deletions lean/components/util/auth0_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@
from lean.components.util.logger import Logger


def get_authorization(auth0_client: Auth0Client, brokerage_id: str, logger: Logger, project_id: int) -> QCAuth0Authorization:
def get_authorization(auth0_client: Auth0Client, brokerage_id: str, logger: Logger, project_id: int, no_browser: bool = False) -> QCAuth0Authorization:
"""Gets the authorization data for a brokerage, authorizing if necessary.

:param auth0_client: An instance of Auth0Client, containing methods to interact with live/auth0/* API endpoints.
:param brokerage_id: The ID of the brokerage to get the authorization data for.
:param logger: An instance of Logger, handling all output printing.
:param project_id: The local or cloud project_id.
:param no_browser: whether to disable opening the browser
:return: The authorization data for the specified brokerage.
"""
from time import time, sleep
Expand All @@ -32,7 +33,7 @@ def get_authorization(auth0_client: Auth0Client, brokerage_id: str, logger: Logg
return data

start_time = time()
auth0_client.authorize(brokerage_id, logger, project_id)
auth0_client.authorize(brokerage_id, logger, project_id, no_browser)

# keep checking for new data every 5 seconds for 7 minutes
while time() - start_time < 420:
Expand Down
13 changes: 7 additions & 6 deletions lean/components/util/json_modules_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ def build_and_configure_modules(target_modules: List[str], module_list: List[Jso

def non_interactive_config_build_for_name(lean_config: Dict[str, Any], target_module_name: str,
module_list: List[JsonModule], properties: Dict[str, Any], logger: Logger,
environment_name: str = None) -> JsonModule:
environment_name: str = None, no_browser: bool = False) -> JsonModule:
return config_build_for_name(lean_config, target_module_name, module_list, properties, logger, interactive=False,
environment_name=environment_name)
environment_name=environment_name, no_browser=no_browser)


def find_module(target_module_name: str, module_list: List[JsonModule], logger: Logger) -> JsonModule:
Expand Down Expand Up @@ -79,17 +79,17 @@ def find_module(target_module_name: str, module_list: List[JsonModule], logger:

def config_build_for_name(lean_config: Dict[str, Any], target_module_name: str, module_list: List[JsonModule],
properties: Dict[str, Any], logger: Logger, interactive: bool,
environment_name: str = None) -> JsonModule:
environment_name: str = None, no_browser: bool = False) -> JsonModule:
target_module = find_module(target_module_name, module_list, logger)
target_module.config_build(lean_config, logger, interactive=interactive, properties=properties,
environment_name=environment_name)
environment_name=environment_name, no_browser=no_browser)
_update_settings(logger, environment_name, target_module, lean_config)
return target_module


def interactive_config_build(lean_config: Dict[str, Any], models: [JsonModule], logger: Logger,
user_provided_options: Dict[str, Any], show_secrets: bool, select_message: str,
multiple: bool, environment_name: str = None) -> [JsonModule]:
multiple: bool, environment_name: str = None, no_browser: bool = False) -> [JsonModule]:
"""Interactively configures the brokerage to use.

:param lean_config: the LEAN configuration that should be used
Expand All @@ -100,6 +100,7 @@ def interactive_config_build(lean_config: Dict[str, Any], models: [JsonModule],
:param select_message: the user facing selection message
:param multiple: true if multiple selections are allowed
:param environment_name: the target environment name
:param no_browser: whether to disable opening the browser
:return: the brokerage the user configured
"""
options = [Option(id=b, label=b.get_name()) for b in models]
Expand All @@ -113,7 +114,7 @@ def interactive_config_build(lean_config: Dict[str, Any], models: [JsonModule],

for module in modules:
module.config_build(lean_config, logger, interactive=True, properties=user_provided_options,
hide_input=not show_secrets, environment_name=environment_name)
hide_input=not show_secrets, environment_name=environment_name, no_browser=no_browser)
_update_settings(logger, environment_name, module, lean_config)
if multiple:
return modules
Expand Down
6 changes: 4 additions & 2 deletions lean/models/json_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@ def config_build(self,
interactive: bool,
properties: Dict[str, Any] = {},
hide_input: bool = False,
environment_name: str = None) -> 'JsonModule':
environment_name: str = None,
no_browser: bool = False) -> 'JsonModule':
"""Builds a new instance of this class, prompting the user for input when necessary.

:param lean_config: the Lean configuration dict to read defaults from
Expand All @@ -204,6 +205,7 @@ def config_build(self,
:param properties: the properties that passed as options
:param hide_input: whether to hide secrets inputs
:param environment_name: the target environment name
:param no_browser: whether to disable opening the browser
:return: self
"""
logger.debug(f'Configuring {self._display_name}')
Expand Down Expand Up @@ -237,7 +239,7 @@ def config_build(self,
configuration.require_project_id)
logger.debug(f'project_id: {lean_config["project-id"]}')
auth_authorizations = get_authorization(container.api_client.auth0, self._display_name.lower(),
logger, lean_config["project-id"])
logger, lean_config["project-id"], no_browser=no_browser)
logger.debug(f'auth: {auth_authorizations}')
configuration._value = auth_authorizations.get_authorization_config_without_account()
for inner_config in self._lean_configs:
Expand Down
Loading