Skip to content

Commit

Permalink
Merge pull request #87 from rjra2611/feature-add-organization-id-nuge…
Browse files Browse the repository at this point in the history
…t-download-ib

Feature add support for organization id and nuget download
  • Loading branch information
Martin-Molinero authored Apr 7, 2022
2 parents b63537e + c2643a8 commit e5213ca
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 9 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,7 @@ Options:
The brokerage to use
--data-feed [Interactive Brokers|Tradier|OANDA|Bitfinex|Coinbase Pro|Binance|Zerodha|Samco|Terminal Link|Trading Technologies|Custom data only|Kraken|FTX|IQFeed]
The data feed to use
--ib-organization TEXT The name or id of the organization with the Interactive Brokers module subscription
--ib-user-name TEXT Your Interactive Brokers username
--ib-account TEXT Your Interactive Brokers account id
--ib-password TEXT Your Interactive Brokers password
Expand Down
14 changes: 10 additions & 4 deletions lean/commands/live.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ def _get_default_value(key: str) -> Optional[Any]:
@click.option("--data-feed",
type=click.Choice([d.get_name() for d in all_local_data_feeds], case_sensitive=False),
help="The data feed to use")
@click.option("--ib-organization",
type=str,
default=lambda: _get_default_value("job-organization-id"),
help="The name or id of the organization with the Interactive Brokers module subscription")
@click.option("--ib-user-name",
type=str,
default=lambda: _get_default_value("ib-user-name"),
Expand Down Expand Up @@ -621,6 +625,7 @@ def live(project: Path,
gui_organization: Optional[str],
brokerage: Optional[str],
data_feed: Optional[str],
ib_organization: Optional[str],
ib_user_name: Optional[str],
ib_account: Optional[str],
ib_password: Optional[str],
Expand Down Expand Up @@ -763,7 +768,7 @@ def live(project: Path,
brokerage_configurer = PaperTradingBrokerage()
elif brokerage == InteractiveBrokersBrokerage.get_name():
ensure_options(["ib_user_name", "ib_account", "ib_password"])
brokerage_configurer = InteractiveBrokersBrokerage(ib_user_name, ib_account, ib_password)
brokerage_configurer = InteractiveBrokersBrokerage(_get_organization_id(ib_organization, "Interactive Brokers"), ib_user_name, ib_account, ib_password)
elif brokerage == TradierBrokerage.get_name():
ensure_options(["tradier_account_id", "tradier_access_token", "tradier_use_sandbox"])
brokerage_configurer = TradierBrokerage(tradier_account_id, tradier_access_token, tradier_use_sandbox)
Expand Down Expand Up @@ -892,9 +897,10 @@ def live(project: Path,

if data_feed == InteractiveBrokersDataFeed.get_name():
ensure_options(["ib_user_name", "ib_account", "ib_password", "ib_enable_delayed_streaming_data"])
data_feed_configurer = InteractiveBrokersDataFeed(InteractiveBrokersBrokerage(ib_user_name,
ib_account,
ib_password),
data_feed_configurer = InteractiveBrokersDataFeed(InteractiveBrokersBrokerage(_get_organization_id(ib_organization, "Interactive Brokers"),
ib_user_name,
ib_account,
ib_password),
ib_enable_delayed_streaming_data)
elif data_feed == TradierDataFeed.get_name():
ensure_options(["tradier_account_id", "tradier_access_token", "tradier_use_sandbox"])
Expand Down
3 changes: 3 additions & 0 deletions lean/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@
# The product id of the SAMCO module
SAMCO_PRODUCT_ID = 173

# The product id of the Interactive Brokers module
INTERACTIVE_BROKERS_PRODUCT_ID = 181

# The product ids for which a valid subscription is seen as a valid GUI module subscription
GUI_PRODUCT_SUBSCRIPTION_IDS = [119, 120]

Expand Down
39 changes: 34 additions & 5 deletions lean/models/brokerages/local/interactive_brokers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@
from lean.models.brokerages.local.base import LocalBrokerage
from lean.models.config import LeanConfigConfigurer
from lean.models.errors import MoreInfoError
from lean.container import container
from lean.constants import INTERACTIVE_BROKERS_PRODUCT_ID
from lean.models.logger import Option


class InteractiveBrokersBrokerage(LocalBrokerage):
class InteractiveBrokersBrokerage(LocalBrokerage):
"""A LocalBrokerage implementation for the Interactive Brokers brokerage."""

def __init__(self, username: str, account_id: str, account_password: str) -> None:
_is_module_installed = False

def __init__(self, organization_id: str, username: str, account_id: str, account_password: str) -> None:
self._organization_id = organization_id
self._username = username
self._account_id = account_id
self._account_password = account_password
Expand Down Expand Up @@ -63,8 +68,23 @@ def __init__(self, username: str, account_id: str, account_password: str) -> Non
def get_name(cls) -> str:
return "Interactive Brokers"

@classmethod
def get_module_id(cls) -> int:
return INTERACTIVE_BROKERS_PRODUCT_ID

@classmethod
def _build(cls, lean_config: Dict[str, Any], logger: Logger) -> LocalBrokerage:

api_client = container.api_client()

organizations = api_client.organizations.get_all()
options = [Option(id=organization.id, label=organization.name) for organization in organizations]

organization_id = logger.prompt_list(
"Select the organization with the Interactive Brokers module subscription",
options
)

logger.info("""
To use IB with LEAN you must disable two-factor authentication or only use IBKR Mobile.
This is done from your IB Account Manage Account -> Settings -> User Settings -> Security -> Secure Login System.
Expand All @@ -76,26 +96,34 @@ def _build(cls, lean_config: Dict[str, Any], logger: Logger) -> LocalBrokerage:
account_id = click.prompt("Account id", cls._get_default(lean_config, "ib-account"))
account_password = logger.prompt_password("Account password", cls._get_default(lean_config, "ib-password"))

return InteractiveBrokersBrokerage(username, account_id, account_password)
return InteractiveBrokersBrokerage(organization_id, username, account_id, account_password)

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"] = "InteractiveBrokersBrokerage"
lean_config["environments"][environment_name]["transaction-handler"] = \
"QuantConnect.Lean.Engine.TransactionHandlers.BrokerageTransactionHandler"

def configure_credentials(self, lean_config: Dict[str, Any]) -> None:
lean_config["job-organization-id"] = self._organization_id
lean_config["ib-user-name"] = self._username
lean_config["ib-account"] = self._account_id
lean_config["ib-password"] = self._account_password
lean_config["ib-agent-description"] = self._agent_description
lean_config["ib-trading-mode"] = self._trading_mode

self._save_properties(lean_config, ["ib-user-name",
self._save_properties(lean_config, ["job-organization-id",
"ib-user-name",
"ib-account",
"ib-password",
"ib-agent-description",
"ib-trading-mode"])

def ensure_module_installed(self) -> None:
if not self._is_module_installed:
container.module_manager().install_module(self.__class__.get_module_id(), self._organization_id)
self._is_module_installed = True


class InteractiveBrokersDataFeed(LeanConfigConfigurer):
"""A LeanConfigConfigurer implementation for the Interactive Brokers data feed."""
Expand Down Expand Up @@ -125,6 +153,7 @@ def build(cls, lean_config: Dict[str, Any], logger: Logger) -> LeanConfigConfigu
return InteractiveBrokersDataFeed(brokerage, enable_delayed_streaming_data)

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"] = \
"QuantConnect.Brokerages.InteractiveBrokers.InteractiveBrokersBrokerage"
lean_config["environments"][environment_name]["history-provider"] = "BrokerageHistoryProvider"
Expand Down

0 comments on commit e5213ca

Please sign in to comment.