Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
efed71c
feat: CharlesSchwab in Readme
Romazes Nov 14, 2024
8c2d4e4
remove: obsolete TDAmeritrade
Romazes Nov 14, 2024
d2c1bac
feat: add project_id to authorize in auth0_client
Romazes Dec 18, 2024
aeded88
feat: use default project id like 0 if configuration is none
Romazes Dec 19, 2024
4de30a4
Revert "feat: use default project id like 0 if configuration is none"
Romazes Dec 19, 2024
259f317
Revert "feat: add project_id to authorize in auth0_client"
Romazes Dec 19, 2024
0828c0e
feat: add project_id to authorize in auth0_client
Romazes Dec 19, 2024
18ce5bd
refactor: set project_id in config_manager
Romazes Dec 20, 2024
168e375
remove: not used imports
Romazes Dec 20, 2024
e0e6b54
refactor: use negative local id if cloud id is not provided
Romazes Dec 20, 2024
be9d33f
rename: get project id method
Romazes Dec 20, 2024
bb45cfe
refactor: get project id
Romazes Dec 23, 2024
75dad58
refactor: Readme
Romazes Dec 23, 2024
f38c779
feat: set project id always in default configs
Romazes Dec 23, 2024
e2e8c2b
feat: validate config value on empty
Romazes Dec 23, 2024
4c4569a
refactor: description of project-id in Readme
Romazes Dec 23, 2024
d7255de
feat: new 'require_project_id' property in AuthConfiguration
Romazes Dec 24, 2024
81428e7
refactor: carry out prompt import into get_project_id
Romazes Dec 24, 2024
ad54c9a
refactor: remove extra project id from Readme
Romazes Dec 24, 2024
98307de
refactor: use int type implicitly
Romazes Dec 24, 2024
67d879c
fix: underscore in "require-project-id" param
Romazes Dec 24, 2024
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
40 changes: 21 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ Options:
-d, --detach Run the backtest in a detached Docker container and return immediately
--debug [pycharm|ptvsd|debugpy|vsdbg|rider|local-platform]
Enable a certain debugging method (see --help for more information)
--data-provider-historical [Interactive Brokers|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Kraken|IQFeed|Polygon|FactSet|IEX|AlphaVantage|CoinApi|ThetaData|QuantConnect|Local|Terminal Link|Bybit|TradeStation|Alpaca]
--data-provider-historical [Interactive Brokers|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Kraken|CharlesSchwab|IQFeed|Polygon|FactSet|IEX|AlphaVantage|CoinApi|ThetaData|QuantConnect|Local|Terminal Link|Bybit|TradeStation|Alpaca]
Update the Lean configuration file to retrieve data from the given historical provider
--ib-user-name TEXT Your Interactive Brokers username
--ib-account TEXT Your Interactive Brokers account id
Expand Down Expand Up @@ -178,6 +178,8 @@ Options:
--kraken-api-secret TEXT Your Kraken API secret
--kraken-verification-tier [Starter|Intermediate|Pro]
Your Kraken Verification Tier
--charles-schwab-account-number TEXT
The CharlesSchwab account number
--iqfeed-iqconnect TEXT The path to the IQConnect binary
--iqfeed-username TEXT Your IQFeed username
--iqfeed-password TEXT Your IQFeed password
Expand Down Expand Up @@ -349,9 +351,9 @@ Usage: lean cloud live deploy [OPTIONS] PROJECT
--notify-insights.

Options:
--brokerage [Paper Trading|Interactive Brokers|Tradier|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Zerodha|Samco|Terminal Link|Trading Technologies|Kraken|TDAmeritrade|Bybit|TradeStation|Alpaca]
--brokerage [Paper Trading|Interactive Brokers|Tradier|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Zerodha|Samco|Terminal Link|Trading Technologies|Kraken|CharlesSchwab|Bybit|TradeStation|Alpaca]
The brokerage to use
--data-provider-live [QuantConnect|Interactive Brokers|Tradier|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Zerodha|Samco|Terminal Link|Trading Technologies|Kraken|TDAmeritrade|Polygon|IEX|CoinApi|Bybit|TradeStation|Alpaca]
--data-provider-live [QuantConnect|Interactive Brokers|Tradier|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Zerodha|Samco|Terminal Link|Trading Technologies|Kraken|CharlesSchwab|Polygon|IEX|CoinApi|Bybit|TradeStation|Alpaca]
The live data provider to use
--ib-user-name TEXT Your Interactive Brokers username
--ib-account TEXT Your Interactive Brokers account id
Expand Down Expand Up @@ -427,11 +429,8 @@ Options:
--kraken-api-secret TEXT Your Kraken API secret
--kraken-verification-tier [Starter|Intermediate|Pro]
Your Kraken Verification Tier
--tdameritrade-api-key TEXT Your TDAmeritrade API key
--tdameritrade-access-token TEXT
Your TDAmeritrade OAuth Access Token
--tdameritrade-account-number TEXT
Your TDAmeritrade account number
--charles-schwab-account-number TEXT
The CharlesSchwab account number
--bybit-api-key TEXT Your Bybit API key
--bybit-api-secret TEXT Your Bybit API secret
--bybit-vip-level [VIP0|VIP1|VIP2|VIP3|VIP4|VIP5|SupremeVIP|Pro1|Pro2|Pro3|Pro4|Pro5]
Expand Down Expand Up @@ -852,7 +851,7 @@ Usage: lean data download [OPTIONS]
https://www.quantconnect.com/datasets

Options:
--data-provider-historical [Interactive Brokers|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Kraken|IQFeed|Polygon|FactSet|IEX|AlphaVantage|CoinApi|ThetaData|QuantConnect|Local|Terminal Link|Bybit|TradeStation|Alpaca]
--data-provider-historical [Interactive Brokers|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Kraken|CharlesSchwab|IQFeed|Polygon|FactSet|IEX|AlphaVantage|CoinApi|ThetaData|QuantConnect|Local|Terminal Link|Bybit|TradeStation|Alpaca]
The name of the downloader data provider.
--ib-user-name TEXT Your Interactive Brokers username
--ib-account TEXT Your Interactive Brokers account id
Expand Down Expand Up @@ -880,6 +879,8 @@ Options:
--kraken-api-secret TEXT Your Kraken API secret
--kraken-verification-tier [Starter|Intermediate|Pro]
Your Kraken Verification Tier
--charles-schwab-account-number TEXT
The CharlesSchwab account number
--iqfeed-iqconnect TEXT The path to the IQConnect binary
--iqfeed-username TEXT Your IQFeed username
--iqfeed-password TEXT Your IQFeed password
Expand Down Expand Up @@ -1289,11 +1290,11 @@ Options:
--environment TEXT The environment to use
--output DIRECTORY Directory to store results in (defaults to PROJECT/live/TIMESTAMP)
-d, --detach Run the live deployment in a detached Docker container and return immediately
--brokerage [Paper Trading|Interactive Brokers|Tradier|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Zerodha|Samco|Terminal Link|Trading Technologies|Kraken|TDAmeritrade|Bybit|TradeStation|Alpaca]
--brokerage [Paper Trading|Interactive Brokers|Tradier|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Zerodha|Samco|Terminal Link|Trading Technologies|Kraken|CharlesSchwab|Bybit|TradeStation|Alpaca]
The brokerage to use
--data-provider-live [Interactive Brokers|Tradier|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Zerodha|Samco|Terminal Link|Trading Technologies|Kraken|TDAmeritrade|IQFeed|Polygon|IEX|CoinApi|ThetaData|Custom data only|Bybit|TradeStation|Alpaca]
--data-provider-live [Interactive Brokers|Tradier|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Zerodha|Samco|Terminal Link|Trading Technologies|Kraken|CharlesSchwab|IQFeed|Polygon|IEX|CoinApi|ThetaData|Custom data only|Bybit|TradeStation|Alpaca]
The live data provider to use
--data-provider-historical [Interactive Brokers|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Kraken|IQFeed|Polygon|FactSet|IEX|AlphaVantage|CoinApi|ThetaData|QuantConnect|Local|Bybit|TradeStation|Alpaca]
--data-provider-historical [Interactive Brokers|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Kraken|CharlesSchwab|IQFeed|Polygon|FactSet|IEX|AlphaVantage|CoinApi|ThetaData|QuantConnect|Local|Bybit|TradeStation|Alpaca]
Update the Lean configuration file to retrieve data from the given historical provider
--ib-user-name TEXT Your Interactive Brokers username
--ib-account TEXT Your Interactive Brokers account id
Expand Down Expand Up @@ -1382,11 +1383,8 @@ Options:
--kraken-api-secret TEXT Your Kraken API secret
--kraken-verification-tier [Starter|Intermediate|Pro]
Your Kraken Verification Tier
--tdameritrade-api-key TEXT Your TDAmeritrade API key
--tdameritrade-access-token TEXT
Your TDAmeritrade OAuth Access Token
--tdameritrade-account-number TEXT
Your TDAmeritrade account number
--charles-schwab-account-number TEXT
The CharlesSchwab account number
--bybit-api-key TEXT Your Bybit API key
--bybit-api-secret TEXT Your Bybit API secret
--bybit-vip-level [VIP0|VIP1|VIP2|VIP3|VIP4|VIP5|SupremeVIP|Pro1|Pro2|Pro3|Pro4|Pro5]
Expand Down Expand Up @@ -1730,7 +1728,7 @@ 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-historical [Interactive Brokers|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Kraken|IQFeed|Polygon|FactSet|IEX|AlphaVantage|CoinApi|ThetaData|QuantConnect|Local|Terminal Link|Bybit|TradeStation|Alpaca]
--data-provider-historical [Interactive Brokers|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Kraken|CharlesSchwab|IQFeed|Polygon|FactSet|IEX|AlphaVantage|CoinApi|ThetaData|QuantConnect|Local|Terminal Link|Bybit|TradeStation|Alpaca]
Update the Lean configuration file to retrieve data from the given historical provider
--download-data Update the Lean configuration file to download data from the QuantConnect API, alias
for --data-provider-historical QuantConnect
Expand Down Expand Up @@ -1771,6 +1769,8 @@ Options:
--kraken-api-secret TEXT Your Kraken API secret
--kraken-verification-tier [Starter|Intermediate|Pro]
Your Kraken Verification Tier
--charles-schwab-account-number TEXT
The CharlesSchwab account number
--iqfeed-iqconnect TEXT The path to the IQConnect binary
--iqfeed-username TEXT Your IQFeed username
--iqfeed-password TEXT Your IQFeed password
Expand Down Expand Up @@ -1989,7 +1989,7 @@ Usage: lean research [OPTIONS] PROJECT

Options:
--port INTEGER The port to run Jupyter Lab on (defaults to 8888)
--data-provider-historical [Interactive Brokers|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Kraken|IQFeed|Polygon|FactSet|IEX|AlphaVantage|CoinApi|ThetaData|QuantConnect|Local|Terminal Link|Bybit|TradeStation|Alpaca]
--data-provider-historical [Interactive Brokers|Oanda|Bitfinex|Coinbase Advanced Trade|Binance|Kraken|CharlesSchwab|IQFeed|Polygon|FactSet|IEX|AlphaVantage|CoinApi|ThetaData|QuantConnect|Local|Terminal Link|Bybit|TradeStation|Alpaca]
Update the Lean configuration file to retrieve data from the given historical provider
--ib-user-name TEXT Your Interactive Brokers username
--ib-account TEXT Your Interactive Brokers account id
Expand Down Expand Up @@ -2017,6 +2017,8 @@ Options:
--kraken-api-secret TEXT Your Kraken API secret
--kraken-verification-tier [Starter|Intermediate|Pro]
Your Kraken Verification Tier
--charles-schwab-account-number TEXT
The CharlesSchwab account number
--iqfeed-iqconnect TEXT The path to the IQConnect binary
--iqfeed-username TEXT Your IQFeed username
--iqfeed-password TEXT Your IQFeed password
Expand Down
6 changes: 4 additions & 2 deletions lean/components/api/auth0_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,17 @@ def read(self, brokerage_id: str) -> QCAuth0Authorization:
return QCAuth0Authorization(authorization=None)

@staticmethod
def authorize(brokerage_id: str, logger: Logger) -> None:
def authorize(brokerage_id: str, logger: Logger, project_id: int) -> 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
"""
from webbrowser import open

full_url = f"{API_BASE_URL}live/auth0/authorize?brokerage={brokerage_id}"
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)
open(full_url)
Expand Down
2 changes: 2 additions & 0 deletions lean/components/config/lean_config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,8 @@ def get_complete_lean_config(self,
"job-user-id": self._cli_config_manager.user_id.get_value(default="0"),
"api-access-token": self._cli_config_manager.api_token.get_value(default=""),
"job-organization-id": get_organization(config),
"project-id": self._project_config_manager.get_project_id_from_project_config(
algorithm_file.parent if algorithm_file else None),

"ib-host": "127.0.0.1",
"ib-port": "4002",
Expand Down
28 changes: 28 additions & 0 deletions lean/components/config/project_config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,34 @@ def get_local_id(self, project_directory: Path) -> int:

return project_id

def get_project_id_from_project_config(self, project_directory: Path) -> int:
"""
Resolves the project ID from the configuration.

Args:
project_directory (Path): The directory of the project. If None,
it indicates the directory is unavailable.

Returns:
int: Returns the 'cloud-id' if available.
If 'cloud-id' is missing, returns the negative of 'local-id'.
If neither is found nor if project_directory is None, returns -1.
"""
if project_directory is None:
return -1

project_config = self.get_project_config(project_directory)

cloud_id = project_config.get("cloud-id")
if cloud_id is not None:
return cloud_id

local_id = project_config.get("local-id")
if local_id is not None:
return -local_id # Local ID must be negative.

return -1 # Return -1 if no valid IDs are found

def get_latest_live_directory(self, project_directory: Path) -> Path:
"""Returns the path of the latest live directory.

Expand Down
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,12 +16,13 @@
from lean.components.util.logger import Logger


def get_authorization(auth0_client: Auth0Client, brokerage_id: str, logger: Logger) -> QCAuth0Authorization:
def get_authorization(auth0_client: Auth0Client, brokerage_id: str, logger: Logger, project_id: int) -> 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.
:return: The authorization data for the specified brokerage.
"""
from time import time, sleep
Expand All @@ -31,7 +32,7 @@ def get_authorization(auth0_client: Auth0Client, brokerage_id: str, logger: Logg
return data

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

# keep checking for new data every 5 seconds for 7 minutes
while time() - start_time < 420:
Expand Down
1 change: 1 addition & 0 deletions lean/models/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ class AuthConfiguration(InternalInputUserInput):

def __init__(self, config_json_object):
super().__init__(config_json_object)
self.require_project_id = config_json_object.get("require-project-id", False)

def factory(config_json_object) -> 'AuthConfiguration':
"""Creates an instance of the child classes.
Expand Down
20 changes: 19 additions & 1 deletion lean/models/json_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,20 @@ def convert_variable_to_lean_key(self, variable_key: str) -> str:
"""
return variable_key.replace('_', '-')

def get_project_id(self, default_project_id: int, require_project_id: bool) -> int:
"""Retrieve the project ID, prompting the user if required and default is invalid.

:param default_project_id: The default project ID to use.
:param require_project_id: Flag to determine if prompting is necessary.
:return: A valid project ID.
"""
from click import prompt
project_id: int = default_project_id
if require_project_id and project_id <= 0:
project_id = prompt("Please enter any cloud project ID to proceed with Auth0 authentication",
-1, show_default=False)
return project_id

def config_build(self,
lean_config: Dict[str, Any],
logger: Logger,
Expand Down Expand Up @@ -219,7 +233,11 @@ def config_build(self,
logger.debug(f"skipping configuration '{configuration._id}': no choices available.")
continue
elif isinstance(configuration, AuthConfiguration):
auth_authorizations = get_authorization(container.api_client.auth0, self._display_name.lower(), logger)
lean_config["project-id"] = self.get_project_id(lean_config["project-id"],
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.debug(f'auth: {auth_authorizations}')
configuration._value = auth_authorizations.get_authorization_config_without_account()
for inner_config in self._lean_configs:
Expand Down