diff --git a/README.md b/README.md index c793a5a1..443c5f61 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,14 @@ Options: --binance-api-secret TEXT Your Binance API secret --binance-environment [demo|real] The environment to run in, demo for testnet, real for the production environment + --ftx-api-key TEXT Your FTX API key + --ftx-api-secret TEXT Your FTX API secret + --ftx-account-tier TEXT Your FTX Account Tier + --ftx-exchange-name TEXT Your FTX Exchange [FTX|FTX.US] + --kraken-api-key TEXT Your Kraken API key + --kraken-api-secret TEXT Your Kraken API secret + --kraken-verification-tier TEXT + Your Kraken Verification Tier --node TEXT The name or id of the live node to run on --auto-restart BOOLEAN Whether automatic algorithm restarting must be enabled --notify-order-events BOOLEAN Whether notifications must be sent for order events @@ -832,6 +840,7 @@ Options: --ftx-api-key TEXT Your FTX API key --ftx-api-secret TEXT Your FTX API secret --ftx-account-tier TEXT Your FTX Account Tier + --ftx-exchange-name TEXT Your FTX Exchange [FTX|FTX.US] --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 starting live trading diff --git a/announcements.json b/announcements.json index 736d39d7..3647c57d 100644 --- a/announcements.json +++ b/announcements.json @@ -1,8 +1,8 @@ { "announcements": [ { - "date": "2022-01-25", - "message": "We've added support for Samco brokerage for India Market" + "date": "2022-01-26", + "message": "We've added support for Samco brokerage for India Market and FTX.US Exchange" }, { "date": "2021-11-17", diff --git a/lean/commands/live.py b/lean/commands/live.py index b9367982..265ad676 100644 --- a/lean/commands/live.py +++ b/lean/commands/live.py @@ -591,6 +591,9 @@ def _get_default_value(key: str) -> Optional[Any]: type=str, default=lambda: _get_default_value("ftx-account-tier"), help="Your FTX Account Tier") +@click.option("--ftx-exchange-name", + type=str, + help="FTX exchange name [FTX, FTXUS]") @click.option("--release", is_flag=True, default=False, @@ -690,6 +693,7 @@ def live(project: Path, ftx_api_key: Optional[str], ftx_api_secret: Optional[str], ftx_account_tier: Optional[str], + ftx_exchange_name: Optional[str], release: bool, image: Optional[str], update: bool) -> None: @@ -865,11 +869,12 @@ def live(project: Path, kraken_api_secret, kraken_verification_tier) elif brokerage == FTXBrokerage.get_name(): - ensure_options(["ftx_api_key", "ftx_api_secret", "ftx_account_tier"]) + ensure_options(["ftx_api_key", "ftx_api_secret", "ftx_account_tier", "ftx_exchange_name"]) brokerage_configurer = FTXBrokerage(_get_organization_id(ftx_organization, "FTX"), ftx_api_key, ftx_api_secret, - ftx_account_tier) + ftx_account_tier, + ftx_exchange_name) if data_feed == InteractiveBrokersDataFeed.get_name(): ensure_options(["ib_user_name", "ib_account", "ib_password", "ib_enable_delayed_streaming_data"]) @@ -995,12 +1000,13 @@ def live(project: Path, kraken_api_secret, kraken_verification_tier)) elif data_feed == FTXDataFeed.get_name(): - ensure_options(["ftx_api_key", "ftx_api_secret", "ftx_account_tier"]) + ensure_options(["ftx_api_key", "ftx_api_secret", "ftx_account_tier", "ftx_echange_name"]) data_feed_configurer = FTXDataFeed( FTXBrokerage(_get_organization_id(ftx_organization, "FTX"), ftx_api_key, ftx_api_secret, - ftx_account_tier)) + ftx_account_tier, + ftx_exchange_name)) environment_name = "lean-cli" lean_config = lean_config_manager.get_complete_lean_config(environment_name, algorithm_file, None) diff --git a/lean/models/brokerages/cloud/ftx.py b/lean/models/brokerages/cloud/ftx.py index c38cd3a9..7b5cb8f9 100644 --- a/lean/models/brokerages/cloud/ftx.py +++ b/lean/models/brokerages/cloud/ftx.py @@ -22,10 +22,11 @@ class FTXBrokerage(CloudBrokerage): """A CloudBrokerage implementation for FTX.""" - def __init__(self, api_key: str, secret_key: str, account_tier: str) -> None: + def __init__(self, api_key: str, secret_key: str, account_tier: str, exchange_name: str) -> None: self._api_key = api_key self._secret_key = secret_key self._account_tier = account_tier + self._exchange_name = exchange_name @classmethod def get_id(cls) -> str: @@ -37,19 +38,38 @@ def get_name(cls) -> str: @classmethod def build(cls, logger: Logger) -> CloudBrokerage: + exchange_name = click.prompt("FTX Exchange [FTX|FTXUS]") + exchange = FTXExchange() if exchange_name.casefold() == "FTX".casefold() else FTXUSExchange() + logger.info(""" -Create an API key by logging in and accessing the FTX Profile page (https://ftx.com/profile). - """.strip()) +Create an API key by logging in and accessing the {} Profile page (https://{}/profile). + """.format(exchange.get_name(), exchange.get_domain()).strip()) api_key = click.prompt("API key") secret_key = logger.prompt_password("Secret key") account_tier = click.prompt("Account Tier") - return FTXBrokerage(api_key, secret_key, account_tier) + return FTXBrokerage(api_key, secret_key, account_tier, exchange_name) def _get_settings(self) -> Dict[str, str]: return { "key": self._api_key, "secret": self._secret_key, - "accountTier": self._account_tier + "tier": self._account_tier, + "exchange": self._exchange_name, + "environment": "live" } + +class FTXExchange: + def get_name(self) -> str: + return "FTX" + + def get_domain(self) -> str: + return "ftx.com" + +class FTXUSExchange(FTXExchange): + def get_name(self) -> str: + return "FTXUS" + + def get_domain(self) -> str: + return "ftx.us" diff --git a/lean/models/brokerages/cloud/kraken.py b/lean/models/brokerages/cloud/kraken.py index 82b7365e..0875900a 100644 --- a/lean/models/brokerages/cloud/kraken.py +++ b/lean/models/brokerages/cloud/kraken.py @@ -51,5 +51,6 @@ def _get_settings(self) -> Dict[str, str]: return { "key": self._api_key, "secret": self._secret_key, - "verificationTier": self._verification_tier + "verificationTier": self._verification_tier, + "environment": "live" } diff --git a/lean/models/brokerages/local/ftx.py b/lean/models/brokerages/local/ftx.py index 107ba3d2..1f754a24 100644 --- a/lean/models/brokerages/local/ftx.py +++ b/lean/models/brokerages/local/ftx.py @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Dict +from typing import Any, Dict, List import click @@ -22,17 +22,79 @@ from lean.models.config import LeanConfigConfigurer from lean.models.logger import Option +class FTXExchange: + def get_name(self) -> str: + return "FTX" + + def live_mode_brokerage(self) -> str: + return "FTXBrokerage" + + def data_queue_handler_name(self) -> str: + return "FTXBrokerage" + + def get_domain(self) -> str: + return "ftx.com" + + def prefix(self) -> str: + return "ftx" + + def account_tier_options(self) -> List[Option]: + return [Option(id="Tier1", label="Tier1"), + Option(id="Tier2", label="Tier2"), + Option(id="Tier3", label="Tier3"), + Option(id="Tier4", label="Tier4"), + Option(id="Tier5", label="Tier5"), + Option(id="Tier6", label="Tier6"), + Option(id="VIP1", label="VIP1"), + Option(id="VIP2", label="VIP2"), + Option(id="VIP3", label="VIP3"), + Option(id="MM1", label="MM1"), + Option(id="MM2", label="MM2"), + Option(id="MM3", label="MM3")] + +class FTXUSExchange(FTXExchange): + def get_name(self) -> str: + return "FTXUS" + + def live_mode_brokerage(self) -> str: + return "FTXUSBrokerage" + + def data_queue_handler_name(self) -> str: + return "FTXUSBrokerage" + + def get_domain(self) -> str: + return "ftx.us" + + def prefix(self) -> str: + return "ftxus" + + def account_tier_options(self) -> List[Option]: + return [Option(id="Tier1", label="Tier1"), + Option(id="Tier2", label="Tier2"), + Option(id="Tier3", label="Tier3"), + Option(id="Tier4", label="Tier4"), + Option(id="Tier5", label="Tier5"), + Option(id="Tier6", label="Tier6"), + Option(id="Tier7", label="Tier7"), + Option(id="Tier8", label="Tier8"), + Option(id="Tier9", label="Tier9"), + Option(id="VIP1", label="VIP1"), + Option(id="VIP2", label="VIP2"), + Option(id="MM1", label="MM1"), + Option(id="MM2", label="MM2"), + Option(id="MM3", label="MM3")] class FTXBrokerage(LocalBrokerage): """A LocalBrokerage implementation for the FTX brokerage.""" - + _exchange: FTXExchange _is_module_installed = False - def __init__(self, organization_id: str, api_key: str, api_secret: str, account_tier: str) -> None: + def __init__(self, organization_id: str, api_key: str, api_secret: str, account_tier: str, exchange: FTXExchange) -> None: self._api_key = api_key self._api_secret = api_secret self._account_tier = account_tier self._organization_id = organization_id + self._exchange = exchange @classmethod def get_name(cls) -> str: @@ -46,50 +108,45 @@ def _build(cls, lean_config: Dict[str, Any], logger: Logger) -> LocalBrokerage: options = [Option(id=organization.id, label=organization.name) for organization in organizations] organization_id = logger.prompt_list( - "Select the organization with the FTX module subscription", + "Select the organization with the {} module subscription".format(cls.get_name()), options ) + exchange_name = click.prompt("FTX Exchange [FTX|FTXUS]", cls._get_default(lean_config, "ftx-exchange-name")) + exchange = FTXExchange() if exchange_name.casefold() == "FTX".casefold() else FTXUSExchange() + logger.info(""" -Create an API key by logging in and accessing the FTX Profile page (https://ftx.com/profile). - """.strip()) +Create an API key by logging in and accessing the {} Profile page (https://{}/profile). + """.format(exchange.get_name(), exchange.get_domain()).strip()) - api_key = click.prompt("API key", cls._get_default(lean_config, "ftx-api-key")) - api_secret = logger.prompt_password("API secret", cls._get_default(lean_config, "ftx-api-secret")) + prefix = exchange.prefix() + api_key = click.prompt("API key", cls._get_default(lean_config, f'{prefix}-api-key')) + api_secret = logger.prompt_password("API secret", cls._get_default(lean_config, f'{prefix}-api-secret')) account_tier = logger.prompt_list( "Select the Account Tier", - [Option(id="Tier1", label="Tier1"), - Option(id="Tier2", label="Tier2"), - Option(id="Tier3", label="Tier3"), - Option(id="Tier4", label="Tier4"), - Option(id="Tier5", label="Tier5"), - Option(id="Tier6", label="Tier6"), - Option(id="VIP1", label="VIP1"), - Option(id="VIP2", label="VIP2"), - Option(id="VIP3", label="VIP3"), - Option(id="MM1", label="MM1"), - Option(id="MM2", label="MM2"), - Option(id="MM3", label="MM3")], - cls._get_default(lean_config, "ftx-account-tier") + exchange.account_tier_options(), + cls._get_default(lean_config, f'{prefix}-account-tier') ) - return FTXBrokerage(organization_id, api_key, api_secret, account_tier) + return FTXBrokerage(organization_id, api_key, api_secret, account_tier, exchange) def _configure_environment(self, lean_config: Dict[str, Any], environment_name: str) -> None: self.ensure_module_installed() - lean_config["environments"][environment_name]["live-mode-brokerage"] = "FTXBrokerage" + lean_config["environments"][environment_name]["live-mode-brokerage"] = self._exchange.live_mode_brokerage() lean_config["environments"][environment_name]["transaction-handler"] = \ "QuantConnect.Lean.Engine.TransactionHandlers.BrokerageTransactionHandler" def configure_credentials(self, lean_config: Dict[str, Any]) -> None: - lean_config["ftx-api-key"] = self._api_key - lean_config["ftx-api-secret"] = self._api_secret - lean_config["ftx-account-tier"] = self._account_tier + prefix = self._exchange.prefix() + + lean_config[f'{prefix}-api-key'] = self._api_key + lean_config[f'{prefix}-api-secret'] = self._api_secret + lean_config[f'{prefix}-account-tier'] = self._account_tier lean_config["job-organization-id"] = self._organization_id - self._save_properties(lean_config, ["job-organization-id", "ftx-api-key", "ftx-api-secret", "ftx-account-tier"]) + self._save_properties(lean_config, ["job-organization-id", f'{prefix}-api-key', f'{prefix}-api-secret', f'{prefix}-account-tier']) def ensure_module_installed(self) -> None: if not self._is_module_installed: @@ -98,6 +155,7 @@ def ensure_module_installed(self) -> None: class FTXDataFeed(LeanConfigConfigurer): """A LeanConfigConfigurer implementation for the FTX data feed.""" + _brokerage: Any def __init__(self, brokerage: FTXBrokerage) -> None: self._brokerage = brokerage @@ -113,7 +171,7 @@ def build(cls, lean_config: Dict[str, Any], logger: Logger) -> LeanConfigConfigu def configure(self, lean_config: Dict[str, Any], environment_name: str) -> None: self._brokerage.ensure_module_installed() - lean_config["environments"][environment_name]["data-queue-handler"] = "FTXBrokerage" + lean_config["environments"][environment_name]["data-queue-handler"] = self._brokerage._exchange.data_queue_handler_name() lean_config["environments"][environment_name]["history-provider"] = "BrokerageHistoryProvider" self._brokerage.configure_credentials(lean_config)