├── .github ├── FUNDING.yml └── workflows │ ├── python-publish.yml │ └── python-package.yml ├── .vscode ├── settings.json └── launch.json ├── samples ├── use_client.py ├── use_customer.py ├── use_accounts.py ├── use_trades.py ├── use_pnl.py ├── use_market_data.py ├── use_alerts.py ├── use_data.py ├── use_auth.py ├── use_portfolio_analysis.py ├── use_contracts.py ├── use_scanners.py ├── use_portfolio_accounts.py └── use_orders.py ├── LICENSE ├── ibc ├── rest │ ├── pnl.py │ ├── customer.py │ ├── trades.py │ ├── accounts.py │ ├── alert.py │ ├── scanner.py │ ├── data.py │ ├── portfolio_analysis.py │ ├── contract.py │ ├── market_data.py │ ├── orders.py │ └── portfolio.py ├── utils │ ├── gateway.py │ ├── enums.py │ └── auth.py ├── session.py └── client.py ├── setup.py ├── .gitignore ├── README.md └── tests └── test_client.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [areed1192] 4 | patreon: sigmacoding 5 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.linting.enabled": true, 3 | "python.linting.pylintEnabled": true, 4 | "python.testing.unittestArgs": [ 5 | "-v", 6 | "-s", 7 | "./tests", 8 | "-p", 9 | "test_*.py" 10 | ], 11 | "python.testing.pytestEnabled": false, 12 | "python.testing.nosetestsEnabled": false, 13 | "python.testing.unittestEnabled": true 14 | } 15 | -------------------------------------------------------------------------------- /samples/use_client.py: -------------------------------------------------------------------------------- 1 | from configparser import ConfigParser 2 | from ibc.client import InteractiveBrokersClient 3 | 4 | # Initialize the Parser. 5 | config = ConfigParser() 6 | 7 | # Read the file. 8 | config.read('config/config.ini') 9 | 10 | # Get the specified credentials. 11 | account_number = config.get('interactive_brokers_paper', 'paper_account') 12 | account_password = config.get('interactive_brokers_paper', 'paper_password') 13 | 14 | # Initialize the `InteractiveBrokersClient` object. 15 | ibc_client = InteractiveBrokersClient( 16 | account_number=account_number, 17 | password=account_password 18 | ) 19 | 20 | # Ensure the portal files are set up. 21 | ibc_client.client_portal.setup() 22 | 23 | # Log the user in to the Client Portal Gateway. 24 | ibc_client.authentication.login() 25 | -------------------------------------------------------------------------------- /samples/use_customer.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ibc.client import InteractiveBrokersClient 4 | 5 | # Initialize the Parser. 6 | config = ConfigParser() 7 | 8 | # Read the file. 9 | config.read('config/config.ini') 10 | 11 | # Get the specified credentials. 12 | account_number = config.get('interactive_brokers_paper', 'paper_account') 13 | account_password = config.get('interactive_brokers_paper', 'paper_password') 14 | 15 | # Initialize the client. 16 | ibc_client = InteractiveBrokersClient( 17 | account_number=account_number, 18 | password=account_password 19 | ) 20 | 21 | # Login to a new session. 22 | ibc_client.authentication.login() 23 | 24 | # Grab the `Customer` Service. 25 | ib_customer_service = ibc_client.customers 26 | 27 | # Grab customer entity info. 28 | pprint( 29 | ib_customer_service.customer_info() 30 | ) 31 | -------------------------------------------------------------------------------- /samples/use_accounts.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ibc.client import InteractiveBrokersClient 4 | 5 | # Initialize the Parser. 6 | config = ConfigParser() 7 | 8 | # Read the file. 9 | config.read('config/config.ini') 10 | 11 | # Get the specified credentials. 12 | account_number = config.get('interactive_brokers_paper', 'paper_account') 13 | account_password = config.get('interactive_brokers_paper', 'paper_password') 14 | 15 | # Initialize the client. 16 | ibc_client = InteractiveBrokersClient( 17 | account_number=account_number, 18 | password=account_password 19 | ) 20 | 21 | # Login first. 22 | ibc_client.authentication.login() 23 | 24 | # Grab the `Accounts` Service. 25 | accounts_services = ibc_client.accounts 26 | 27 | # Grab the User's Accounts. 28 | pprint( 29 | accounts_services.accounts() 30 | ) 31 | 32 | # Grab the Pnl for the Server Portfolio.. 33 | pprint( 34 | accounts_services.pnl_server_account() 35 | ) 36 | -------------------------------------------------------------------------------- /samples/use_trades.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ibc.client import InteractiveBrokersClient 4 | 5 | # Initialize the Parser. 6 | config = ConfigParser() 7 | 8 | # Read the file. 9 | config.read('config/config.ini') 10 | 11 | # Get the specified credentials. 12 | account_number = config.get('interactive_brokers_paper', 'paper_account') 13 | account_password = config.get('interactive_brokers_paper', 'paper_password') 14 | 15 | # Initialize the client. 16 | ibc_client = InteractiveBrokersClient( 17 | account_number=account_number, 18 | password=account_password 19 | ) 20 | 21 | # Initialize the Authentication Service. 22 | auth_service = ibc_client.authentication 23 | 24 | # Login 25 | auth_service.login() 26 | 27 | # Wait for the user to login. 28 | while not auth_service.authenticated: 29 | auth_service.check_auth() 30 | 31 | # Grab the `Trades` Service. 32 | trades_service = ibc_client.trades 33 | 34 | # Get the trades for the last week. 35 | pprint( 36 | trades_service.get_trades() 37 | ) 38 | -------------------------------------------------------------------------------- /samples/use_pnl.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ibc.client import InteractiveBrokersClient 4 | 5 | # Initialize the Parser. 6 | config = ConfigParser() 7 | 8 | # Read the file. 9 | config.read('config/config.ini') 10 | 11 | # Get the specified credentials. 12 | account_number = config.get('interactive_brokers_paper', 'paper_account') 13 | account_password = config.get('interactive_brokers_paper', 'paper_password') 14 | 15 | # Initialize the client. 16 | ibc_client = InteractiveBrokersClient( 17 | account_number=account_number, 18 | password=account_password 19 | ) 20 | 21 | # Initialize the Authentication Service. 22 | auth_service = ibc_client.authentication 23 | 24 | # Login 25 | auth_service.login() 26 | 27 | # Wait for the user to login. 28 | while not auth_service.authenticated: 29 | auth_service.check_auth() 30 | 31 | # Grab the `Pnl` Service. 32 | pnl_service = ibc_client.pnl 33 | 34 | # Grab the Pnl for the selected server account. 35 | pprint( 36 | pnl_service.pnl_server_account() 37 | ) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alex Reed 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /samples/use_market_data.py: -------------------------------------------------------------------------------- 1 | 2 | from pprint import pprint 3 | from configparser import ConfigParser 4 | from ibc.client import InteractiveBrokersClient 5 | from ibc.utils.enums import BarTypes 6 | 7 | # Initialize the Parser. 8 | config = ConfigParser() 9 | 10 | # Read the file. 11 | config.read('config/config.ini') 12 | 13 | # Get the specified credentials. 14 | account_number = config.get('interactive_brokers_paper', 'paper_account') 15 | account_password = config.get('interactive_brokers_paper', 'paper_password') 16 | 17 | # Initialize the client. 18 | ibc_client = InteractiveBrokersClient( 19 | account_number=account_number, 20 | password=account_password 21 | ) 22 | 23 | # Login first. 24 | ibc_client.authentication.login() 25 | 26 | # Grab the `MarketData` Service. 27 | market_data_services = ibc_client.market_data 28 | 29 | # Grab a Quote Snapshot. 30 | pprint( 31 | market_data_services.snapshot( 32 | contract_ids=['265598'] 33 | ) 34 | ) 35 | 36 | # Grab Market History. 37 | pprint( 38 | market_data_services.market_history( 39 | contract_id='265598', 40 | period='5d', 41 | bar=BarTypes.FiveMinute 42 | ) 43 | ) 44 | -------------------------------------------------------------------------------- /samples/use_alerts.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ibc.client import InteractiveBrokersClient 4 | 5 | # Initialize the Parser. 6 | config = ConfigParser() 7 | 8 | # Read the file. 9 | config.read('config/config.ini') 10 | 11 | # Get the specified credentials. 12 | account_number = config.get('interactive_brokers_paper', 'paper_account') 13 | account_password = config.get('interactive_brokers_paper', 'paper_password') 14 | 15 | # Initialize the client. 16 | ibc_client = InteractiveBrokersClient( 17 | account_number=account_number, 18 | password=account_password 19 | ) 20 | 21 | # Initialize the Authentication Service. 22 | auth_service = ibc_client.authentication 23 | 24 | # Login 25 | auth_service.login() 26 | 27 | # Wait for the user to login. 28 | while not auth_service.authenticated: 29 | auth_service.check_auth() 30 | 31 | # Grab the `Alerts` Service. 32 | alerts_service = ibc_client.alerts 33 | 34 | # Grab the Available alerts for our account. 35 | pprint( 36 | alerts_service.available_alerts( 37 | account_id=ibc_client.account_number 38 | ) 39 | ) 40 | 41 | # Grab the MTA Alerts. 42 | pprint( 43 | alerts_service.mta_alerts() 44 | ) 45 | -------------------------------------------------------------------------------- /ibc/rest/pnl.py: -------------------------------------------------------------------------------- 1 | from ibc.session import InteractiveBrokersSession 2 | 3 | 4 | class PnL(): 5 | 6 | def __init__(self, ib_client: object, ib_session: InteractiveBrokersSession) -> None: 7 | """Initializes the `PnL` client. 8 | 9 | ### Parameters 10 | ---- 11 | ib_client : object 12 | The `InteractiveBrokersClient` Python Client. 13 | 14 | ib_session : InteractiveBrokersSession 15 | The IB session handler. 16 | """ 17 | 18 | from ibc.client import InteractiveBrokersClient 19 | 20 | self.client: InteractiveBrokersClient = ib_client 21 | self.session: InteractiveBrokersSession = ib_session 22 | self._has_portfolio_been_called = False 23 | self._has_sub_portfolio_been_called = False 24 | 25 | def pnl_server_account(self) -> dict: 26 | """Returns an object containing PnL for the selected account 27 | and its models (if any). 28 | 29 | ### Returns 30 | ---- 31 | dict: 32 | An `AccountPnL` resource. 33 | """ 34 | 35 | content = self.session.make_request( 36 | method='get', 37 | endpoint='/api/iserver/account/pnl/partitioned' 38 | ) 39 | 40 | return content 41 | -------------------------------------------------------------------------------- /ibc/rest/customer.py: -------------------------------------------------------------------------------- 1 | from ibc.session import InteractiveBrokersSession 2 | 3 | 4 | class Customer(): 5 | 6 | def __init__(self, ib_client: object, ib_session: InteractiveBrokersSession) -> None: 7 | """Initializes the `InteractiveBrokersCustomer` client. 8 | 9 | ### Parameters 10 | ---- 11 | ib_client : object 12 | The `InteractiveBrokersClient` Python Client. 13 | 14 | ib_session : InteractiveBrokersSession 15 | The IB session handler. 16 | """ 17 | 18 | from ibc.client import InteractiveBrokersClient 19 | 20 | self.client: InteractiveBrokersClient = ib_client 21 | self.session: InteractiveBrokersSession = ib_session 22 | 23 | def customer_info(self) -> dict: 24 | """Returns Applicant Id with all owner related entities. 25 | 26 | ### Returns 27 | ---- 28 | dict: 29 | A customer resource object. 30 | 31 | ### Usage 32 | ---- 33 | >>> customers_service = ibc_client.customers 34 | >>> customers_service.customer_info() 35 | """ 36 | 37 | content = self.session.make_request( 38 | method='get', 39 | endpoint='/api/ibcust/entity/info' 40 | ) 41 | 42 | return content 43 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | deploy: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Python 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: "3.7" 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install setuptools wheel twine 24 | - name: Publish to TestPyPI 25 | env: 26 | TWINE_USERNAME: "__token__" 27 | TWINE_PASSWORD: ${{ secrets.test_pypi_token }} 28 | run: | 29 | python setup.py sdist bdist_wheel 30 | twine upload --repository testpypi --verbose dist/* 31 | - name: Publish to PyPI 32 | env: 33 | TWINE_USERNAME: "__token__" 34 | TWINE_PASSWORD: ${{ secrets.pypi_token }} 35 | run: | 36 | python setup.py sdist bdist_wheel 37 | twine upload --verbose dist/* 38 | -------------------------------------------------------------------------------- /samples/use_data.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ibc.client import InteractiveBrokersClient 4 | 5 | # Initialize the Parser. 6 | config = ConfigParser() 7 | 8 | # Read the file. 9 | config.read('config/config.ini') 10 | 11 | # Get the specified credentials. 12 | account_number = config.get('interactive_brokers_paper', 'paper_account') 13 | account_password = config.get('interactive_brokers_paper', 'paper_password') 14 | 15 | # Initialize the client. 16 | ibc_client = InteractiveBrokersClient( 17 | account_number=account_number, 18 | password=account_password 19 | ) 20 | 21 | # Initialize the Authentication Service. 22 | auth_service = ibc_client.authentication 23 | 24 | # Login 25 | auth_service.login() 26 | 27 | # Wait for the user to login. 28 | while not auth_service.authenticated: 29 | auth_service.check_auth() 30 | 31 | # Grab the `Data` Service. 32 | data_service = ibc_client.data_services 33 | 34 | # Grab a summary for the company Microsoft. 35 | pprint(data_service.summary(contract_id='265598')) 36 | 37 | # Grab news articles related to your portfolio. 38 | pprint(data_service.portfolio_news()) 39 | 40 | # Grab the top news articles. 41 | pprint(data_service.top_news()) 42 | 43 | # Grab the top news articles. 44 | pprint(data_service.news_briefings()) 45 | -------------------------------------------------------------------------------- /ibc/rest/trades.py: -------------------------------------------------------------------------------- 1 | from ibc.session import InteractiveBrokersSession 2 | 3 | 4 | class Trades(): 5 | 6 | def __init__(self, ib_client: object, ib_session: InteractiveBrokersSession) -> None: 7 | """Initializes the `Trades` client. 8 | 9 | ### Parameters 10 | ---- 11 | ib_client : object 12 | The `InteractiveBrokersClient` Python Client. 13 | 14 | ib_session : InteractiveBrokersSession 15 | The IB session handler. 16 | """ 17 | 18 | from ibc.client import InteractiveBrokersClient 19 | 20 | self.client: InteractiveBrokersClient = ib_client 21 | self.session: InteractiveBrokersSession = ib_session 22 | 23 | def get_trades(self) -> list: 24 | """Returns a list of trades for the currently selected 25 | account for current day and six previous days. 26 | 27 | ### Returns 28 | ---- 29 | list: 30 | A collection of `Trade` resources. 31 | 32 | ### Usage 33 | ---- 34 | >>> trades_service = ibc_client.trades 35 | >>> trades_service.get_trades() 36 | """ 37 | 38 | content = self.session.make_request( 39 | method='get', 40 | endpoint='/api/iserver/account/trades' 41 | ) 42 | 43 | return content 44 | -------------------------------------------------------------------------------- /samples/use_auth.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ibc.client import InteractiveBrokersClient 4 | 5 | # Initialize the Parser. 6 | config = ConfigParser() 7 | 8 | # Read the file. 9 | config.read('config/config.ini') 10 | 11 | # Get the specified credentials. 12 | account_number = config.get('interactive_brokers_paper', 'paper_account') 13 | account_password = config.get('interactive_brokers_paper', 'paper_password') 14 | 15 | # Initialize the client. 16 | ibc_client = InteractiveBrokersClient( 17 | account_number=account_number, 18 | password=account_password 19 | ) 20 | 21 | # Grab the `Authentication` Service. 22 | auth_service = ibc_client.authentication 23 | 24 | # Login 25 | auth_service.login() 26 | 27 | # Wait for the user to login. 28 | while not auth_service.authenticated: 29 | auth_service.check_auth() 30 | 31 | # check if we are authenticated. 32 | pprint( 33 | auth_service.is_authenticated() 34 | ) 35 | 36 | # Validate the current session. 37 | pprint( 38 | auth_service.sso_validate() 39 | ) 40 | 41 | # Reauthenticate the session. 42 | pprint( 43 | auth_service.reauthenticate() 44 | ) 45 | 46 | # Set the account for the server. 47 | pprint( 48 | auth_service.update_server_account( 49 | account_id=ibc_client.account_number 50 | ) 51 | ) 52 | -------------------------------------------------------------------------------- /samples/use_portfolio_analysis.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ibc.client import InteractiveBrokersClient 4 | from ibc.utils.enums import Frequency 5 | 6 | # Initialize the Parser. 7 | config = ConfigParser() 8 | 9 | # Read the file. 10 | config.read('config/config.ini') 11 | 12 | # Get the specified credentials. 13 | account_number = config.get('interactive_brokers_paper', 'paper_account') 14 | account_password = config.get('interactive_brokers_paper', 'paper_password') 15 | 16 | # Initialize the client. 17 | ibc_client = InteractiveBrokersClient( 18 | account_number=account_number, 19 | password=account_password 20 | ) 21 | 22 | # Grab the `PortfolioAnalysis` Service. 23 | ib_portfolio_analysis = ibc_client.portfolio_analysis 24 | 25 | # Grab our account summary. 26 | pprint( 27 | ib_portfolio_analysis.account_summary( 28 | account_ids=[ibc_client.account_number] 29 | ) 30 | ) 31 | 32 | # Grab the account performance. 33 | pprint( 34 | ib_portfolio_analysis.account_performance( 35 | account_ids=[ibc_client.account_number], 36 | frequency=Frequency.Quarterly 37 | ) 38 | ) 39 | 40 | # Grab the account performance. 41 | pprint( 42 | ib_portfolio_analysis.transactions_history( 43 | account_ids=[ibc_client.account_number] 44 | ) 45 | ) 46 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [master] 9 | pull_request: 10 | branches: [master] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | python-version: [3.6, 3.7, 3.8] 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v2 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | 26 | - name: Install Dependencies from requirements.txt File. 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install flake8 pytest 30 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 31 | 32 | - name: Lint with flake8 33 | run: | 34 | # stop the build if there are Python syntax errors or undefined names 35 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 36 | 37 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 38 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 39 | 40 | - name: Test with Unittest. 41 | run: | 42 | # If we have a testing folder, then run with unittest. 43 | if [ -f tests/test_clients.py ]; then python -m unittest tests/test_client.py; fi 44 | -------------------------------------------------------------------------------- /samples/use_contracts.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ibc.client import InteractiveBrokersClient 4 | 5 | # Initialize the Parser. 6 | config = ConfigParser() 7 | 8 | # Read the file. 9 | config.read('config/config.ini') 10 | 11 | # Get the specified credentials. 12 | account_number = config.get('interactive_brokers_paper', 'paper_account') 13 | account_password = config.get('interactive_brokers_paper', 'paper_password') 14 | 15 | # Initialize the client. 16 | ibc_client = InteractiveBrokersClient( 17 | account_number=account_number, 18 | password=account_password 19 | ) 20 | 21 | # Initialize the Authentication Service. 22 | auth_service = ibc_client.authentication 23 | 24 | # Login 25 | auth_service.login() 26 | 27 | # Wait for the user to login. 28 | while not auth_service.authenticated: 29 | auth_service.check_auth() 30 | 31 | # Grab the `Contracts` Service. 32 | contracts_service = ibc_client.contracts 33 | 34 | # Grab the info for a specific contract. 35 | pprint( 36 | contracts_service.contract_info( 37 | contract_id='265598' 38 | ) 39 | ) 40 | 41 | # Search for Futures Contracts. 42 | pprint( 43 | contracts_service.search_futures( 44 | symbols=['CL', 'ES'] 45 | ) 46 | ) 47 | 48 | # Search for multiple contracts. 49 | pprint( 50 | contracts_service.search_multiple_contracts( 51 | contract_ids=[265598] 52 | ) 53 | ) 54 | 55 | # Search for a company by it's ticker. 56 | pprint( 57 | contracts_service.search_symbol( 58 | symbol='MSFT', 59 | name=False 60 | ) 61 | ) 62 | 63 | # Search for a company by it's name. 64 | pprint( 65 | contracts_service.search_symbol( 66 | symbol='Microsoft', 67 | name=True 68 | ) 69 | ) 70 | -------------------------------------------------------------------------------- /samples/use_scanners.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ibc.client import InteractiveBrokersClient 4 | 5 | # Initialize the Parser. 6 | config = ConfigParser() 7 | 8 | # Read the file. 9 | config.read('config/config.ini') 10 | 11 | # Get the specified credentials. 12 | account_number = config.get('interactive_brokers_paper', 'paper_account') 13 | account_password = config.get('interactive_brokers_paper', 'paper_password') 14 | 15 | # Initialize the client. 16 | ibc_client = InteractiveBrokersClient( 17 | account_number=account_number, 18 | password=account_password 19 | ) 20 | 21 | # Initialize the Authentication Service. 22 | auth_service = ibc_client.authentication 23 | 24 | # Login 25 | auth_service.login() 26 | 27 | # Wait for the user to login. 28 | while not auth_service.authenticated: 29 | auth_service.check_auth() 30 | 31 | # Grab the `Scanners` Service. 32 | scanners_service = ibc_client.scanners 33 | 34 | # Grab the different scanners. 35 | pprint( 36 | scanners_service.scanners() 37 | ) 38 | 39 | # Define a scanner. 40 | scanner = { 41 | "instrument": "STK", 42 | "type": "NOT_YET_TRADED_TODAY", 43 | "filter": [ 44 | { 45 | "code": "priceAbove", 46 | "value": 50 47 | }, 48 | { 49 | "code": "priceBelow", 50 | "value": 70 51 | }, 52 | { 53 | "code": "volumeAbove", 54 | "value": None 55 | }, 56 | { 57 | "code": "volumeBelow", 58 | "value": None 59 | } 60 | ], 61 | "location": "STK.US.MAJOR", 62 | "size": "25" 63 | } 64 | 65 | # Run that scanner. 66 | pprint( 67 | scanners_service.run_scanner( 68 | scanner=scanner 69 | ) 70 | ) 71 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from setuptools import find_namespace_packages 3 | 4 | # Open the README file. 5 | with open(file="README.md", mode="r") as fh: 6 | long_description = fh.read() 7 | 8 | setup( 9 | 10 | name='ibc-api', 11 | 12 | # Define Author Info. 13 | author='Alex Reed', 14 | author_email='coding.sigma@gmail.com', 15 | 16 | # Define Version Info. 17 | version='0.1.0', 18 | 19 | # Define descriptions. 20 | description='', 21 | long_description=long_description, 22 | long_description_content_type="text/markdown", 23 | 24 | # Define repo location. 25 | url='https://github.com/areed1192/interactive-brokers-api', 26 | 27 | # Define dependencies. 28 | install_requires=[ 29 | 'requests==2.24.0', 30 | 'fake-useragent' 31 | ], 32 | 33 | # Specify folder content. 34 | packages=find_namespace_packages( 35 | include=['ibc'] 36 | ), 37 | 38 | # Define the python version. 39 | python_requires='>3.7', 40 | 41 | # Define our classifiers. 42 | classifiers=[ 43 | 44 | # Phase of development my library is in. 45 | 'Development Status :: 3 - Alpha', 46 | 47 | # Audience this library is intended for. 48 | 'Intended Audience :: Developers', 49 | 'Intended Audience :: Science/Research', 50 | 'Intended Audience :: Financial and Insurance Industry', 51 | 52 | # License that guides my library. 53 | 'License :: OSI Approved :: MIT License', 54 | 55 | # Package was written in English. 56 | 'Natural Language :: English', 57 | 58 | # Operating systems. 59 | 'Operating System :: OS Independent', 60 | 61 | # Programming Languages Used.. 62 | 'Programming Language :: Python :: 3.7', 63 | 'Programming Language :: Python :: 3.8', 64 | 65 | # Topics. 66 | 'Topic :: Database', 67 | 'Topic :: Education', 68 | 'Topic :: Office/Business' 69 | ] 70 | 71 | ) 72 | -------------------------------------------------------------------------------- /ibc/rest/accounts.py: -------------------------------------------------------------------------------- 1 | from ibc.session import InteractiveBrokersSession 2 | 3 | 4 | class Accounts(): 5 | 6 | def __init__(self, ib_client: object, ib_session: InteractiveBrokersSession) -> None: 7 | """Initializes the `Accounts` client. 8 | 9 | ### Parameters 10 | ---- 11 | ib_client : object 12 | The `InteractiveBrokersClient` Python Client. 13 | 14 | ib_session : InteractiveBrokersSession 15 | The IB session handler. 16 | """ 17 | 18 | from ibc.client import InteractiveBrokersClient 19 | 20 | self.client: InteractiveBrokersClient = ib_client 21 | self.session: InteractiveBrokersSession = ib_session 22 | self._has_portfolio_been_called = False 23 | self._has_sub_portfolio_been_called = False 24 | 25 | def accounts(self) -> dict: 26 | """Returns the Users Accounts. 27 | 28 | ### Overview 29 | ---- 30 | Returns a list of accounts the user has trading access to, 31 | their respective aliases and the currently selected account. 32 | Note this endpoint must be called before modifying an order 33 | or querying open orders. 34 | 35 | ### Returns 36 | ---- 37 | dict: 38 | A collection of `Account` resources. 39 | 40 | ### Usage 41 | ---- 42 | >>> accounts_services = ibc_client.accounts 43 | >>> accounts_services.accounts() 44 | """ 45 | 46 | content = self.session.make_request( 47 | method='get', 48 | endpoint='/api/iserver/accounts' 49 | ) 50 | 51 | return content 52 | 53 | def pnl_server_account(self) -> dict: 54 | """Returns an object containing PnL for the selected account 55 | and its models (if any). 56 | 57 | ### Returns 58 | ---- 59 | dict: 60 | An `AccountPnL` resource. 61 | 62 | ### Usage 63 | ---- 64 | >>> accounts_services = ibc_client.accounts 65 | >>> accounts_services.pnl_server_account() 66 | """ 67 | 68 | content = self.session.make_request( 69 | method='get', 70 | endpoint='/api/iserver/account/pnl/partitioned' 71 | ) 72 | 73 | return content 74 | -------------------------------------------------------------------------------- /ibc/rest/alert.py: -------------------------------------------------------------------------------- 1 | from ibc.session import InteractiveBrokersSession 2 | 3 | 4 | class Alerts(): 5 | 6 | def __init__(self, ib_client: object, ib_session: InteractiveBrokersSession) -> None: 7 | """Initializes the `Alerts` client. 8 | 9 | ### Parameters 10 | ---- 11 | ib_client : object 12 | The `InteractiveBrokersClient` Python Client. 13 | 14 | ib_session : InteractiveBrokersSession 15 | The IB session handler. 16 | """ 17 | 18 | from ibc.client import InteractiveBrokersClient 19 | 20 | self.client: InteractiveBrokersClient = ib_client 21 | self.session: InteractiveBrokersSession = ib_session 22 | 23 | def available_alerts(self, account_id: str) -> list: 24 | """Returns Applicant Id with all owner related entities. 25 | 26 | ### Parameters 27 | ---- 28 | account_id : str 29 | The account ID you want a list of alerts for. 30 | 31 | ### Returns 32 | ---- 33 | list: 34 | A collection of `Alert` resources. 35 | 36 | ### Usage 37 | ---- 38 | >>> alerts_service = ibc_client.alerts 39 | >>> alerts_service.available_alerts( 40 | account_id=ibc_client.account_number 41 | ) 42 | """ 43 | 44 | content = self.session.make_request( 45 | method='get', 46 | endpoint=f'/api/iserver/account/{account_id}/alerts' 47 | ) 48 | 49 | return content 50 | 51 | def mta_alerts(self) -> list: 52 | """Returns the Mobile Trading Assistant Alert. 53 | 54 | ### Overview 55 | ---- 56 | Each login user only has one mobile trading assistant (MTA) 57 | alert with it's own unique tool id. The tool id cannot be 58 | changed. When modified a new order Id is generated. MTA alerts 59 | can not be created or deleted. If you call delete 60 | /iserver/account/:accountId/alert/:alertId, it will reset MTA 61 | to default. See here for more information on MTA alerts. 62 | 63 | ### Returns 64 | ---- 65 | list: 66 | A collection of `MobileTradingAssistantAlert` resource. 67 | 68 | ### Usage 69 | ---- 70 | >>> alerts_service = ibc_client.alerts 71 | >>> alerts_service.mta_alerts() 72 | """ 73 | 74 | content = self.session.make_request( 75 | method='get', 76 | endpoint=f'/api/iserver/account/mta' 77 | ) 78 | 79 | return content -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Alex Folder. 132 | vscode/ 133 | config/ 134 | .resources and notes/ 135 | ibc/resources -------------------------------------------------------------------------------- /ibc/rest/scanner.py: -------------------------------------------------------------------------------- 1 | from ibc.session import InteractiveBrokersSession 2 | 3 | 4 | class Scanners(): 5 | 6 | def __init__(self, ib_client: object, ib_session: InteractiveBrokersSession) -> None: 7 | """Initializes the `Scanners` client. 8 | 9 | ### Parameters 10 | ---- 11 | ib_client : object 12 | The `InteractiveBrokersClient` Python Client. 13 | 14 | ib_session : InteractiveBrokersSession 15 | The IB session handler. 16 | """ 17 | 18 | from ibc.client import InteractiveBrokersClient 19 | 20 | self.client: InteractiveBrokersClient = ib_client 21 | self.session: InteractiveBrokersSession = ib_session 22 | 23 | def scanners(self) -> dict: 24 | """Returns an object contains four lists contain all parameters 25 | for scanners. 26 | 27 | ### Returns 28 | ---- 29 | dict: 30 | A collection of `Scanner` resources. 31 | 32 | ### Usage 33 | ---- 34 | >>> scanners_service = ibc_client.scanners 35 | >>> scanners_service.scanners() 36 | """ 37 | 38 | content = self.session.make_request( 39 | method='get', 40 | endpoint='/api/iserver/scanner/params' 41 | ) 42 | 43 | return content 44 | 45 | def run_scanner(self, scanner: dict) -> dict: 46 | """Runs scanner to get a list of contracts. 47 | 48 | ### Parameters 49 | ---- 50 | scanner : dict 51 | A scanner definition that you want to run. 52 | 53 | ### Returns 54 | ---- 55 | dict: 56 | A collection of `contract` resources. 57 | 58 | ### Usage 59 | ---- 60 | >>> scanners_service = ibc_client.scanners 61 | >>> scanners_service.run_scanner( 62 | scanner={ 63 | "instrument": "STK", 64 | "type": "NOT_YET_TRADED_TODAY", 65 | "filter": [ 66 | { 67 | "code": "priceAbove", 68 | "value": 50 69 | }, 70 | { 71 | "code": "priceBelow", 72 | "value": 70 73 | }, 74 | { 75 | "code": "volumeAbove", 76 | "value": None 77 | }, 78 | { 79 | "code": "volumeBelow", 80 | "value": None 81 | } 82 | ], 83 | "location": "STK.US.MAJOR", 84 | "size": "25" 85 | } 86 | ) 87 | """ 88 | 89 | content = self.session.make_request( 90 | method='post', 91 | endpoint='/api/iserver/scanner/run', 92 | json_payload=scanner 93 | ) 94 | 95 | return content 96 | -------------------------------------------------------------------------------- /samples/use_portfolio_accounts.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ibc.client import InteractiveBrokersClient 4 | from ibc.utils.enums import SortDirection 5 | from ibc.utils.enums import SortFields 6 | 7 | # Initialize the Parser. 8 | config = ConfigParser() 9 | 10 | # Read the file. 11 | config.read('config/config.ini') 12 | 13 | # Get the specified credentials. 14 | account_number = config.get('interactive_brokers_paper', 'paper_account') 15 | account_password = config.get('interactive_brokers_paper', 'paper_password') 16 | 17 | # Initialize the client. 18 | ibc_client = InteractiveBrokersClient( 19 | account_number=account_number, 20 | password=account_password 21 | ) 22 | 23 | # Initialize the Authentication Service. 24 | auth_service = ibc_client.authentication 25 | 26 | # Login 27 | auth_service.login() 28 | 29 | # Wait for the user to login. 30 | while not auth_service.authenticated: 31 | auth_service.check_auth() 32 | 33 | # Grab the `PortfolioAccounts` Service. 34 | portfolio_accounts_services = ibc_client.portfolio_accounts 35 | 36 | # Grab the Portfolio Accounts. 37 | pprint( 38 | portfolio_accounts_services.accounts() 39 | ) 40 | 41 | # Grab the Portfolio SubAccounts. 42 | pprint( 43 | portfolio_accounts_services.subaccounts() 44 | ) 45 | 46 | # Grab the Account metadata. 47 | pprint( 48 | portfolio_accounts_services.account_metadata(account_id=account_number) 49 | ) 50 | 51 | # Grab the Account Summary. 52 | pprint( 53 | portfolio_accounts_services.account_summary(account_id=account_number) 54 | ) 55 | 56 | # Grab the Account Ledger. 57 | pprint( 58 | portfolio_accounts_services.account_ledger(account_id=account_number) 59 | ) 60 | 61 | # Grab the Account Allocation. 62 | pprint( 63 | portfolio_accounts_services.account_allocation(account_id=account_number) 64 | ) 65 | 66 | # Grab a consolidated view. 67 | pprint( 68 | portfolio_accounts_services.portfolio_allocation( 69 | account_ids=[ibc_client.account_number] 70 | ) 71 | ) 72 | 73 | # Grab postions from our Portfolio. 74 | pprint( 75 | portfolio_accounts_services.portfolio_positions( 76 | account_id=ibc_client.account_number, 77 | page_id=0, 78 | sort=SortFields.BaseUnrealizedPnl, 79 | direction=SortDirection.Descending 80 | ) 81 | ) 82 | 83 | # Grab positions that fall under a certain contract ID, for a specific account. 84 | pprint( 85 | portfolio_accounts_services.position_by_contract_id( 86 | account_id=ibc_client.account_number, 87 | contract_id='251962528' 88 | ) 89 | ) 90 | 91 | # Grab positions that fall under a certain contract ID, for all accounts. 92 | pprint( 93 | portfolio_accounts_services.positions_by_contract_id( 94 | contract_id='251962528' 95 | ) 96 | ) 97 | 98 | # Invalidate the backend positions cahce. 99 | pprint( 100 | portfolio_accounts_services.invalidate_positions_cache( 101 | account_id=ibc_client.account_number 102 | ) 103 | ) 104 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug: Authentication Service", 6 | "type": "python", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/samples/use_auth.py", 9 | "console": "integratedTerminal" 10 | }, 11 | { 12 | "name": "Debug: Accounts Service", 13 | "type": "python", 14 | "request": "launch", 15 | "program": "${workspaceFolder}/samples/use_accounts.py", 16 | "console": "integratedTerminal" 17 | }, 18 | { 19 | "name": "Debug: Market Data Service", 20 | "type": "python", 21 | "request": "launch", 22 | "program": "${workspaceFolder}/samples/use_market_data.py", 23 | "console": "integratedTerminal" 24 | }, 25 | { 26 | "name": "Debug: Alerts Service", 27 | "type": "python", 28 | "request": "launch", 29 | "program": "${workspaceFolder}/samples/use_alerts.py", 30 | "console": "integratedTerminal" 31 | }, 32 | { 33 | "name": "Debug: Contracts Service", 34 | "type": "python", 35 | "request": "launch", 36 | "program": "${workspaceFolder}/samples/use_contracts.py", 37 | "console": "integratedTerminal" 38 | }, 39 | { 40 | "name": "Debug: Customers Service", 41 | "type": "python", 42 | "request": "launch", 43 | "program": "${workspaceFolder}/samples/use_customers.py", 44 | "console": "integratedTerminal" 45 | }, 46 | { 47 | "name": "Debug: Orders Service", 48 | "type": "python", 49 | "request": "launch", 50 | "program": "${workspaceFolder}/samples/use_orders.py", 51 | "console": "integratedTerminal" 52 | }, 53 | { 54 | "name": "Debug: PnL Service", 55 | "type": "python", 56 | "request": "launch", 57 | "program": "${workspaceFolder}/samples/use_pnl.py", 58 | "console": "integratedTerminal" 59 | }, 60 | { 61 | "name": "Debug: Portfolio Account Service", 62 | "type": "python", 63 | "request": "launch", 64 | "program": "${workspaceFolder}/samples/use_portfolio_accounts.py", 65 | "console": "integratedTerminal" 66 | }, 67 | { 68 | "name": "Debug: Portfolio Analysis Service", 69 | "type": "python", 70 | "request": "launch", 71 | "program": "${workspaceFolder}/samples/use_portfolio_analysis.py", 72 | "console": "integratedTerminal" 73 | }, 74 | { 75 | "name": "Debug: Scanners Service", 76 | "type": "python", 77 | "request": "launch", 78 | "program": "${workspaceFolder}/samples/use_scanners.py", 79 | "console": "integratedTerminal" 80 | }, 81 | { 82 | "name": "Debug: Trades Service", 83 | "type": "python", 84 | "request": "launch", 85 | "program": "${workspaceFolder}/samples/use_trades.py", 86 | "console": "integratedTerminal" 87 | }, 88 | { 89 | "name": "Debug: Data Service", 90 | "type": "python", 91 | "request": "launch", 92 | "program": "${workspaceFolder}/samples/use_trades.py", 93 | "console": "integratedTerminal" 94 | } 95 | ] 96 | } 97 | -------------------------------------------------------------------------------- /ibc/utils/gateway.py: -------------------------------------------------------------------------------- 1 | import io 2 | import pathlib 3 | import zipfile 4 | import requests 5 | import textwrap 6 | 7 | 8 | class ClientPortalGateway(): 9 | 10 | def __init__(self) -> None: 11 | """Initializes the client portal object. """ 12 | 13 | self._resources_folder = pathlib.Path(__file__).parents[1].joinpath( 14 | 'resources' 15 | ).resolve() 16 | 17 | def _make_resources_directory(self) -> bool: 18 | """Makes the resource folder if it doesn't exist. 19 | 20 | ### Returns 21 | ---- 22 | bool: 23 | `True` if additional steps need to be executed. 24 | `False` otherwise. 25 | """ 26 | 27 | if not self._resources_folder.exists(): 28 | print("Gateway folder does not exists, downloading files...") 29 | self._resources_folder.mkdir(parents=True) 30 | return True 31 | else: 32 | return False 33 | 34 | def _download_client_portal(self) -> requests.Response: 35 | """Downloads the Client Portal from Interactive Brokers. 36 | 37 | ### Returns 38 | ---- 39 | requests.Response: 40 | A response object with clientportal content. 41 | """ 42 | 43 | # Request the Client Portal 44 | response = requests.get( 45 | url='https://download2.interactivebrokers.com/portal/clientportal.beta.gw.zip' 46 | ) 47 | 48 | return response 49 | 50 | def _create_zip_file(self, response_content: requests.Response) -> zipfile.ZipFile: 51 | """Creates a zip file to house the client portal content. 52 | 53 | ### Parameters 54 | ---- 55 | response_content: requests.Response 56 | The response object with the client portal content. 57 | 58 | ### Returns 59 | ---- 60 | zipfile.ZipFile: 61 | A zip file object with the Client Portal. 62 | """ 63 | 64 | # Download the Zip File. 65 | zip_file_content = zipfile.ZipFile( 66 | io.BytesIO(response_content.content) 67 | ) 68 | 69 | return zip_file_content 70 | 71 | def _extract_zip_file(self, zip_file: zipfile.ZipFile) -> None: 72 | """Extracts the Zip File. 73 | 74 | ### Parameters 75 | ---- 76 | zip_file: zipfile.ZipFile: 77 | The client portal zip file to be extracted. 78 | """ 79 | 80 | # Extract the Content to the new folder. 81 | zip_file.extractall(path="ibc/resources/clientportal.beta.gw") 82 | 83 | def setup(self) -> None: 84 | """Downloads and extracts the client portal object.""" 85 | 86 | # Make the resource directory if needed. 87 | if self._make_resources_directory() == False: 88 | return 89 | 90 | # Download it. 91 | client_portal_response = self._download_client_portal() 92 | 93 | # Create a zip file. 94 | client_portal_zip = self._create_zip_file( 95 | response_content=client_portal_response 96 | ) 97 | print("Zip folder created...") 98 | 99 | # Extract it. 100 | self._extract_zip_file(zip_file=client_portal_zip) 101 | print(textwrap.dedent(f"""Files extracted... 102 | New Folder is: {self._resources_folder} 103 | """)) 104 | -------------------------------------------------------------------------------- /ibc/rest/data.py: -------------------------------------------------------------------------------- 1 | from ibc.session import InteractiveBrokersSession 2 | 3 | 4 | class Data(): 5 | 6 | def __init__(self, ib_client: object, ib_session: InteractiveBrokersSession) -> None: 7 | """Initializes the `Data` client. 8 | 9 | ### Parameters 10 | ---- 11 | ib_client : object 12 | The `InteractiveBrokersClient` Python Client. 13 | 14 | ib_session : InteractiveBrokersSession 15 | The IB session handler. 16 | """ 17 | 18 | from ibc.client import InteractiveBrokersClient 19 | 20 | self.client: InteractiveBrokersClient = ib_client 21 | self.session: InteractiveBrokersSession = ib_session 22 | 23 | def portfolio_news(self) -> dict: 24 | """Returns a news summary for your portfolio. 25 | 26 | ### Returns 27 | ---- 28 | list: 29 | A collection of `NewsArticle` resources. 30 | 31 | ### Usage 32 | ---- 33 | >>> data_services = ibc_client.data_services 34 | >>> data_services.portfolio_news() 35 | """ 36 | 37 | content = self.session.make_request( 38 | method='get', 39 | endpoint=f'/api/iserver/news/portfolio' 40 | ) 41 | 42 | return content 43 | 44 | def top_news(self) -> dict: 45 | """Returns the top news articles. 46 | 47 | ### Returns 48 | ---- 49 | list: 50 | A collection of `NewsArticle` resources. 51 | 52 | ### Usage 53 | ---- 54 | >>> data_services = ibc_client.data_services 55 | >>> data_services.top_news() 56 | """ 57 | 58 | content = self.session.make_request( 59 | method='get', 60 | endpoint=f'/api/iserver/news/top' 61 | ) 62 | 63 | return content 64 | 65 | def news_sources(self) -> dict: 66 | """Returns news sources. 67 | 68 | ### Returns 69 | ---- 70 | list: 71 | A collection of `Sources` resources. 72 | 73 | ### Usage 74 | ---- 75 | >>> data_services = ibc_client.data_services 76 | >>> data_services.news_sources() 77 | """ 78 | 79 | content = self.session.make_request( 80 | method='get', 81 | endpoint=f'/api/iserver/news/top' 82 | ) 83 | 84 | return content 85 | 86 | def news_briefings(self) -> dict: 87 | """Returns news briefings. 88 | 89 | ### Returns 90 | ---- 91 | list: 92 | A collection of `Briefings` resources. 93 | 94 | ### Usage 95 | ---- 96 | >>> data_services = ibc_client.data_services 97 | >>> data_services.news_briefings() 98 | """ 99 | 100 | content = self.session.make_request( 101 | method='get', 102 | endpoint=f'/api/iserver/news/briefing' 103 | ) 104 | 105 | return content 106 | 107 | def summary(self, contract_id: str) -> dict: 108 | """Returns a summary of the contract ID, items include 109 | company description and more. 110 | 111 | ### Parameters 112 | ---- 113 | contract_id : str 114 | The contract Id you want to query. 115 | 116 | ### Returns 117 | ---- 118 | list: 119 | A collection of `Summary` resources. 120 | 121 | ### Usage 122 | ---- 123 | >>> data_services = ibc_client.data_services 124 | >>> data_services.summary( 125 | contract_id='265598' 126 | ) 127 | """ 128 | 129 | content = self.session.make_request( 130 | method='get', 131 | endpoint=f'/api/iserver/fundamentals/{contract_id}/summary' 132 | ) 133 | 134 | return content 135 | -------------------------------------------------------------------------------- /ibc/rest/portfolio_analysis.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from typing import List 3 | from enum import Enum 4 | from ibc.session import InteractiveBrokersSession 5 | 6 | 7 | class PortfolioAnalysis(): 8 | 9 | def __init__(self, ib_client: object, ib_session: InteractiveBrokersSession) -> None: 10 | """Initializes the `PortfolioAnalysis` client. 11 | 12 | ### Parameters 13 | ---- 14 | ib_client : object 15 | The `InteractiveBrokersClient` Python Client. 16 | 17 | ib_session : InteractiveBrokersSession 18 | The IB session handler. 19 | """ 20 | 21 | from ibc.client import InteractiveBrokersClient 22 | 23 | self.client: InteractiveBrokersClient = ib_client 24 | self.session: InteractiveBrokersSession = ib_session 25 | 26 | def account_performance(self, account_ids: List[str], frequency: Union[str, Enum]) -> dict: 27 | """Returns the performance (MTM) for the given accounts, if more than one account 28 | is passed, the result is consolidated. 29 | 30 | ### Parameters 31 | ---- 32 | account_ids : List[str] 33 | A list of account Numbers. 34 | 35 | frequency : Union[str, Enum] 36 | Frequency of cumulative performance data 37 | points: 'D'aily, 'M'onthly,'Q'uarterly. Can 38 | be one of 3 possible values: "D" "M" "Q". 39 | 40 | ### Returns 41 | ---- 42 | dict: A performance resource. 43 | """ 44 | 45 | # Grab the Order Status. 46 | if isinstance(frequency, Enum): 47 | frequency = frequency.value 48 | 49 | payload = { 50 | 'acctIds': account_ids, 51 | 'freq': frequency 52 | } 53 | 54 | content = self.session.make_request( 55 | method='post', 56 | endpoint='/api/pa/performance', 57 | json_payload=payload 58 | ) 59 | 60 | return content 61 | 62 | def account_summary(self, account_ids: List[str]) -> dict: 63 | """Returns a summary of all account balances for the given accounts, 64 | if more than one account is passed, the result is consolidated. 65 | 66 | ### Parameters 67 | ---- 68 | account_ids : List[str] 69 | A list of account Numbers. 70 | 71 | ### Returns 72 | ---- 73 | dict: A performance resource. 74 | """ 75 | 76 | payload = { 77 | 'acctIds': account_ids 78 | } 79 | 80 | content = self.session.make_request( 81 | method='post', 82 | endpoint='/api/pa/summary', 83 | json_payload=payload 84 | ) 85 | 86 | return content 87 | 88 | def transactions_history(self, account_ids: List[str] = None, contract_ids: List[str] = None, currency: str = 'USD', days: int = 90) -> dict: 89 | """Transaction history for a given number of conids and accounts. Types of transactions 90 | include dividend payments, buy and sell transactions, transfers. 91 | 92 | ### Parameters 93 | ---- 94 | account_ids : List[str] 95 | A list of account Numbers. 96 | 97 | contract_ids : List[str] 98 | A list contract IDs. 99 | 100 | currency : str (optional, Default='USD') 101 | The currency for which to return values. 102 | 103 | days : int (optional, Default=90) 104 | The number of days to return. 105 | 106 | ### Returns 107 | ---- 108 | dict : 109 | A collection of `Transactions` resource. 110 | """ 111 | 112 | payload = { 113 | 'acctIds': account_ids, 114 | 'conids': contract_ids, 115 | 'currency': currency, 116 | 'days': days 117 | } 118 | 119 | content = self.session.make_request( 120 | method='post', 121 | endpoint='/api/pa/summary', 122 | json_payload=payload 123 | ) 124 | 125 | return content 126 | -------------------------------------------------------------------------------- /samples/use_orders.py: -------------------------------------------------------------------------------- 1 | from email import message 2 | from pprint import pprint 3 | from configparser import ConfigParser 4 | 5 | from numpy import isin 6 | from ibc.client import InteractiveBrokersClient 7 | from ibc.utils.enums import SortDirection 8 | from ibc.utils.enums import SortFields 9 | 10 | # Initialize the Parser. 11 | config = ConfigParser() 12 | 13 | # Read the file. 14 | config.read('config/config.ini') 15 | 16 | # Get the specified credentials. 17 | account_number = config.get('interactive_brokers_paper', 'paper_account') 18 | account_password = config.get('interactive_brokers_paper', 'paper_password') 19 | 20 | # Initialize the client. 21 | ibc_client = InteractiveBrokersClient( 22 | account_number=account_number, 23 | password=account_password 24 | ) 25 | 26 | # Initialize the Authentication Service. 27 | auth_service = ibc_client.authentication 28 | 29 | # Login 30 | auth_service.login() 31 | 32 | # Wait for the user to login. 33 | while not auth_service.authenticated: 34 | auth_service.check_auth() 35 | 36 | # Grab the `Orders` Service. 37 | orders_services = ibc_client.orders 38 | 39 | # Grab all the orders we have. 40 | pprint( 41 | orders_services.orders() 42 | ) 43 | 44 | # Define an order. 45 | order_template = { 46 | "conid": 251962528, 47 | "secType": "362673777:STK", 48 | # Keep in mind that this order ID is valid for 24 hours, can't use it twice. 49 | "cOID": "limit-buy-order-v1", 50 | "orderType": "LMT", 51 | "price": 5.00, 52 | "side": "BUY", 53 | "quantity": 1, 54 | "tif": "DAY" 55 | } 56 | 57 | 58 | # Place an order, good chance I will get `Reply` back. 59 | order_placement_response = orders_services.place_order( 60 | account_id=ibc_client.account_number, 61 | order=order_template 62 | ) 63 | 64 | pprint( 65 | order_placement_response 66 | ) 67 | 68 | if isinstance(order_placement_response, list): 69 | order_response = order_placement_response[0] 70 | 71 | if 'id' in order_response: 72 | message_id = order_response['id'] 73 | 74 | # Reply to the message. 75 | pprint( 76 | orders_services.reply( 77 | reply_id=message_id, 78 | message={ 79 | "confirmed": True 80 | } 81 | ) 82 | ) 83 | 84 | bracket_order_template = { 85 | "orders": [ 86 | { 87 | "conid": 251962528, 88 | "secType": "362673777:FUT", 89 | "cOID": "buy-1", 90 | "orderType": "LMT", 91 | "side": "BUY", 92 | "price": 9.00, 93 | "quantity": 1, 94 | "tif": "DAY" 95 | }, 96 | { 97 | "conid": 251962528, 98 | "secType": "362673777:STK", 99 | # This MUST match the `cOID` of the first order. 100 | "parentId": "buy-1", 101 | "orderType": "LMT", 102 | "side": "BUY", 103 | "price": 7.00, 104 | "quantity": 2, 105 | "tif": "DAY" 106 | } 107 | ] 108 | } 109 | 110 | # Place a bracket order, good chance I will get `Reply` back. 111 | pprint( 112 | orders_services.place_bracket_order( 113 | account_id=ibc_client.account_number, 114 | orders=bracket_order_template 115 | ) 116 | ) 117 | 118 | # Delete an order. 119 | pprint( 120 | orders_services.delete_order( 121 | account_id=ibc_client.account_number, 122 | order_id='1915650541' 123 | ) 124 | ) 125 | 126 | # Modify an order. 127 | modify_order_response = orders_services.modify_order( 128 | account_id=ibc_client.account_number, 129 | order_id='1915650539', 130 | order={ 131 | "conid": 251962528, 132 | "secType": "362673777:STK", 133 | "cOID": "limit-buy-order-3", 134 | "orderType": "LMT", 135 | "price": 7.00, 136 | "side": "BUY", 137 | "quantity": 1, 138 | "tif": "DAY" 139 | } 140 | ) 141 | 142 | # Print the response. 143 | pprint( 144 | modify_order_response 145 | ) 146 | 147 | # Place an order, good chance I will get `Reply` back. 148 | pprint( 149 | orders_services.place_whatif_order( 150 | account_id=ibc_client.account_number, 151 | order=order_template 152 | ) 153 | ) 154 | -------------------------------------------------------------------------------- /ibc/rest/contract.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from ibc.session import InteractiveBrokersSession 3 | 4 | 5 | class Contracts(): 6 | 7 | def __init__(self, ib_client: object, ib_session: InteractiveBrokersSession) -> None: 8 | """Initializes the `Contracts` client. 9 | 10 | ### Parameters 11 | ---- 12 | ib_client : object 13 | The `InteractiveBrokersClient` Python Client. 14 | 15 | ib_session : InteractiveBrokersSession 16 | The IB session handler. 17 | """ 18 | 19 | from ibc.client import InteractiveBrokersClient 20 | 21 | self.client: InteractiveBrokersClient = ib_client 22 | self.session: InteractiveBrokersSession = ib_session 23 | 24 | def contract_info(self, contract_id: str) -> dict: 25 | """Get contract details, you can use this to prefill your 26 | order before you submit an order. 27 | 28 | ### Parameters 29 | ---- 30 | contract_id : str 31 | The contract ID you want details for. 32 | 33 | ### Returns 34 | ---- 35 | list: 36 | A `Contract` resource. 37 | 38 | ### Usage 39 | ---- 40 | >>> contracts_service = ibc_client.contracts 41 | >>> contracts_service.contract_info( 42 | contract_id='265598' 43 | ) 44 | """ 45 | 46 | content = self.session.make_request( 47 | method='get', 48 | endpoint=f'/api/iserver/contract/{contract_id}/info' 49 | ) 50 | 51 | return content 52 | 53 | def search_futures(self, symbols: List[str]) -> dict: 54 | """Returns a list of non-expired future contracts 55 | for given symbol(s). 56 | 57 | ### Parameters 58 | ---- 59 | symbols : str 60 | List of case-sensitive symbols separated by comma 61 | 62 | ### Returns 63 | ---- 64 | list: 65 | A collection of `Futures` resource. 66 | 67 | ### Usage 68 | ---- 69 | >>> contracts_service = ibc_client.contracts 70 | >>> contracts_service.search_futures( 71 | symbols=['CL', 'ES'] 72 | ) 73 | """ 74 | 75 | content = self.session.make_request( 76 | method='get', 77 | endpoint=f'/api/trsrv/futures', 78 | params={ 79 | 'symbols': ','.join(symbols) 80 | } 81 | ) 82 | 83 | return content 84 | 85 | def search_symbol(self, symbol: str, name: str = False, security_type: str = None) -> list: 86 | """Search by symbol or name. 87 | 88 | ### Parameters 89 | ---- 90 | symbol : str 91 | The symbol to be searched. 92 | 93 | name : bool (optional, Default=False) 94 | Set to `True` if searching by name, `False` if searching 95 | by symbol. 96 | 97 | security_type : str (optional, default=True) 98 | The security type of the symbol. 99 | 100 | ### Returns 101 | ---- 102 | list: 103 | A collection of `Contract` resources. 104 | 105 | ### Usage 106 | ---- 107 | >>> contracts_service = ibc_client.contracts 108 | >>> contracts_service.search_symbol( 109 | symbol='AAPL', 110 | name='Apple' 111 | ) 112 | """ 113 | 114 | payload = { 115 | 'symbol': symbol, 116 | 'name': name, 117 | 'secType': security_type 118 | } 119 | 120 | content = self.session.make_request( 121 | method='post', 122 | endpoint=f'/api/iserver/secdef/search', 123 | json_payload=payload 124 | ) 125 | 126 | return content 127 | 128 | def search_multiple_contracts(self, contract_ids: List[int]) -> list: 129 | """Returns a list of security definitions for the given conids. 130 | 131 | ### Parameters 132 | ---- 133 | contract_ids : List[str] 134 | A list of Contract IDs. 135 | 136 | ### Returns 137 | ---- 138 | list: 139 | A collection of `Contract` resources. 140 | 141 | ### Usage 142 | ---- 143 | >>> contracts_service = ibc_client.contracts 144 | >>> contracts_service.search_multiple_contracts( 145 | contract_ids=['265598'] 146 | ) 147 | """ 148 | 149 | payload = { 150 | "conids": contract_ids 151 | } 152 | 153 | content = self.session.make_request( 154 | method='post', 155 | endpoint=f'/api/trsrv/secdef', 156 | json_payload=payload 157 | ) 158 | 159 | return content 160 | -------------------------------------------------------------------------------- /ibc/rest/market_data.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from typing import List 3 | from enum import Enum 4 | from ibc.session import InteractiveBrokersSession 5 | 6 | 7 | class MarketData(): 8 | 9 | def __init__(self, ib_client, ib_session: InteractiveBrokersSession) -> None: 10 | """Initializes the `MarketData` client. 11 | 12 | ### Parameters 13 | ---- 14 | ib_client : object 15 | The `InteractiveBrokersClient` Python Client. 16 | 17 | ib_session : InteractiveBrokersSession 18 | The IB session handler. 19 | """ 20 | 21 | from ibc.client import InteractiveBrokersClient 22 | 23 | self.client: InteractiveBrokersClient = ib_client 24 | self.session: InteractiveBrokersSession = ib_session 25 | 26 | if self.client.accounts._has_portfolio_been_called: 27 | self._has_servers_been_called = True 28 | else: 29 | print("Calling Accounts Endpoint, so we can pull data.") 30 | self.client.accounts.accounts() 31 | 32 | def snapshot(self, contract_ids: List[str], since: int = None, fields: Union[str, Enum] = None) -> dict: 33 | """Get Market Data for the given conid(s). 34 | 35 | ### Overview 36 | ---- 37 | The end-point will return by default bid, ask, last, change, change pct, close, 38 | listing exchange. The endpoint /iserver/accounts should be called prior to 39 | /iserver/marketdata/snapshot. To receive all available fields the /snapshot 40 | endpoint will need to be called several times. 41 | 42 | ### Parameters 43 | ---- 44 | contract_ids : List[str] 45 | A list of contract Ids. 46 | 47 | frequency : Union[str, Enum] 48 | Frequency of cumulative performance data 49 | points: 'D'aily, 'M'onthly,'Q'uarterly. Can 50 | be one of 3 possible values: "D" "M" "Q". 51 | 52 | ### Returns 53 | ---- 54 | dict: A `MarketSnapshot` resource. 55 | 56 | ### Usage 57 | ---- 58 | >>> market_data_services = ibc_client.market_data 59 | >>> market_data_services.snapshot(contract_ids=['265598']) 60 | """ 61 | 62 | new_fields = [] 63 | 64 | if fields: 65 | # Check for Enums. 66 | for field in fields: 67 | 68 | if isinstance(field, Enum): 69 | field = field.value 70 | new_fields.append(field) 71 | 72 | fields = ','.join(new_fields) 73 | else: 74 | fields = None 75 | 76 | # Define the payload. 77 | params = { 78 | 'conids': ','.join(contract_ids), 79 | 'since': since, 80 | 'fields': fields 81 | } 82 | 83 | content = self.session.make_request( 84 | method='get', 85 | endpoint='/api/iserver/marketdata/snapshot', 86 | params=params 87 | ) 88 | 89 | return content 90 | 91 | def market_history( 92 | self, 93 | contract_id: str, 94 | period: str, bar: Union[str, Enum] = None, 95 | exchange: str = None, 96 | outside_regular_trading_hours: bool = True 97 | ) -> dict: 98 | """Get historical market Data for given conid, length of data 99 | is controlled by 'period' and 'bar'. 100 | 101 | ### Parameters 102 | ---- 103 | contract_id : str 104 | A contract Id. 105 | 106 | period : str 107 | Available time period: {1-30}min, {1-8}h, 108 | {1-1000}d, {1-792}w, {1-182}m, {1-15}y 109 | 110 | bar : Union[str, Enum] (optional, Default=None): 111 | The bar type you want the data in. 112 | 113 | exchange : str (optional, Default=None): 114 | Exchange of the conid. 115 | 116 | outside_regular_trading_hours : bool (optional, Default=True) 117 | For contracts that support it, will determine if historical 118 | data includes outside of regular trading hours. 119 | 120 | ### Returns 121 | ---- 122 | dict: A collection `Bar` resources. 123 | 124 | ### Usage 125 | ---- 126 | >>> market_data_services = ibc_client.market_data 127 | >>> market_data_services.snapshot(contract_ids=['265598']) 128 | """ 129 | 130 | if isinstance(bar, Enum): 131 | bar = bar.value 132 | 133 | payload = { 134 | 'conid': contract_id, 135 | 'period': period, 136 | 'bar': bar, 137 | 'exchange': exchange, 138 | 'outsideRth': outside_regular_trading_hours 139 | } 140 | 141 | content = self.session.make_request( 142 | method='get', 143 | endpoint='/api/iserver/marketdata/history', 144 | params=payload 145 | ) 146 | 147 | return content 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unofficial Interactive Brokers API 2 | 3 | ## Table of Contents 4 | 5 | - [Overview](#overview) 6 | - [What's in the API](#whats-in-the-api) 7 | - [Requirements](#requirements) 8 | - [Usage](#usage) 9 | - [Documentation & Resources](#documentation-and-resources) 10 | - [Support These Projects](#support-these-projects) 11 | 12 | ## Overview 13 | 14 | The unofficial Python API client library for Interactive Broker Client Portal Web API allows individuals with Interactive Broker accounts to manage trades, pull historical and real-time data, manage their accounts, create and modify orders all using the Python programming language. 15 | 16 | Interactive Broker offers multiple APIs for their clients. If you would like to learn more about their API offerings click on the links below: 17 | 18 | - Trade Workstation API, please refer to the [official documentation](http://interactivebrokers.github.io/tws-api/) 19 | - Client Portal API, please refer to the [official documentation](https://interactivebrokers.github.io/cpwebapi/) 20 | - Third Party API, plesfe refer to the [official documentation](https://www.interactivebrokers.com/webtradingapi/) 21 | 22 | ## Requirements 23 | 24 | The following requirements must be met to use this API: 25 | 26 | - A Interactive Broker account, you'll need your account password and account number to use the API. 27 | - [Java 8](https://developers.redhat.com/products/openjdk/download) update 192 or higher installed (gateway is compatible with higher Java versions including OpenJDK 11). 28 | - Download the [Beta Client Portal Gateway](https://www.interactivebrokers.com/en/index.php?f=45185) 29 | 30 | ## Setup 31 | 32 | **Setup - Requirements Install:*** 33 | 34 | For this particular project, you only need to install the dependencies, to use the project. The dependencies 35 | are listed in the `requirements.txt` file and can be installed by running the following command: 36 | 37 | ```console 38 | pip install -r requirements.txt 39 | ``` 40 | 41 | After running that command, the dependencies should be installed. 42 | 43 | **Setup - Local Install:** 44 | 45 | If you are planning to make modifications to this project or you would like to access it 46 | before it has been indexed on `PyPi`. I would recommend you either install this project 47 | in `editable` mode or do a `local install`. For those of you, who want to make modifications 48 | to this project. I would recommend you install the library in `editable` mode. 49 | 50 | If you want to install the library in `editable` mode, make sure to run the `setup.py` 51 | file, so you can install any dependencies you may need. To run the `setup.py` file, 52 | run the following command in your terminal. 53 | 54 | ```console 55 | pip install -e . 56 | ``` 57 | 58 | If you don't plan to make any modifications to the project but still want to use it across 59 | your different projects, then do a local install. 60 | 61 | ```console 62 | pip install . 63 | ``` 64 | 65 | This will install all the dependencies listed in the `setup.py` file. Once done 66 | you can use the library wherever you want. 67 | 68 | 83 | 84 | ## Documentation and Resources 85 | 86 | - [Getting Started](https://interactivebrokers.github.io/cpwebapi/index.html#login) 87 | - [Endpoints](https://interactivebrokers.com/api/doc.html) 88 | - [Websockets](https://interactivebrokers.github.io/cpwebapi/RealtimeSubscription.html) 89 | 90 | ## Usage 91 | 92 | Here is a simple example of using the `ibc-api` library. 93 | 94 | ```python 95 | from pprint import pprint 96 | from configparser import ConfigParser 97 | from ibc.client import InteractiveBrokersClient 98 | 99 | # Initialize the Parser. 100 | config = ConfigParser() 101 | 102 | # Read the file. 103 | config.read('config/config.ini') 104 | 105 | # Get the specified credentials. 106 | account_number = config.get('interactive_brokers_paper', 'paper_account') 107 | account_password = config.get('interactive_brokers_paper', 'paper_password') 108 | 109 | # Initialize the client. 110 | ibc_client = InteractiveBrokersClient( 111 | account_number=account_number, 112 | password=account_password 113 | ) 114 | 115 | # Grab the Auth Service. 116 | auth_service = ibc_client.authentication 117 | 118 | # Login 119 | auth_service.login() 120 | 121 | # check if we are authenticated. 122 | pprint( 123 | auth_service.is_authenticated() 124 | ) 125 | 126 | # Validate the current session. 127 | pprint( 128 | auth_service.sso_validate() 129 | ) 130 | ``` 131 | 132 | ## Support These Projects 133 | 134 | **Patreon:** 135 | Help support this project and future projects by donating to my [Patreon Page](https://www.patreon.com/sigmacoding). I'm 136 | always looking to add more content for individuals like yourself, unfortuantely some of the APIs I would require me to 137 | pay monthly fees. 138 | 139 | **YouTube:** 140 | If you'd like to watch more of my content, feel free to visit my YouTube channel [Sigma Coding](https://www.youtube.com/c/SigmaCoding). 141 | -------------------------------------------------------------------------------- /tests/test_client.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from unittest import TestCase 4 | from configparser import ConfigParser 5 | from ibc.client import InteractiveBrokersClient 6 | from ibc.rest.market_data import MarketData 7 | from ibc.rest.accounts import Accounts 8 | from ibc.rest.portfolio_analysis import PortfolioAnalysis 9 | from ibc.rest.customer import Customer 10 | from ibc.session import InteractiveBrokersSession 11 | from ibc.utils.gateway import ClientPortalGateway 12 | from ibc.rest.pnl import PnL 13 | from ibc.rest.contract import Contracts 14 | from ibc.rest.alert import Alerts 15 | from ibc.rest.scanner import Scanners 16 | from ibc.rest.trades import Trades 17 | from ibc.rest.portfolio import PortfolioAccounts 18 | from ibc.rest.orders import Orders 19 | from ibc.rest.data import Data 20 | 21 | 22 | class InteractiveBrokersClientTest(TestCase): 23 | 24 | """Will perform a unit test for the `InteractiveBrokersClient` session.""" 25 | 26 | def setUp(self) -> None: 27 | """Set up the InteractiveBroker Client.""" 28 | 29 | # Initialize the Parser. 30 | config = ConfigParser() 31 | 32 | # Read the file. 33 | config.read('config/config.ini') 34 | 35 | # Get the specified credentials. 36 | account_number = config.get( 37 | 'interactive_brokers_paper', 'paper_account') 38 | account_password = config.get( 39 | 'interactive_brokers_paper', 'paper_password') 40 | 41 | # Initialize the client. 42 | self.ibc_client = InteractiveBrokersClient( 43 | account_number=account_number, 44 | password=account_password 45 | ) 46 | 47 | def test_creates_instance_of_client(self): 48 | """Create an instance and make sure it's a `InteractiveBrokerClient`.""" 49 | 50 | self.assertIsInstance( 51 | self.ibc_client, 52 | InteractiveBrokersClient 53 | ) 54 | 55 | def test_creates_instance_of_session(self): 56 | """Create an instance and make sure it's a `InteractiveBrokerSession`.""" 57 | 58 | self.assertIsInstance( 59 | self.ibc_client._session, 60 | InteractiveBrokersSession 61 | ) 62 | 63 | def test_creates_instance_of_gateway(self): 64 | """Create an instance and make sure it's a `ClientPortalGateway`.""" 65 | 66 | self.assertIsInstance( 67 | self.ibc_client.client_portal, 68 | ClientPortalGateway 69 | ) 70 | 71 | def test_creates_instance_of_market_data(self): 72 | """Create an instance and make sure it's a `MarketData`client.""" 73 | 74 | self.assertIsInstance( 75 | self.ibc_client.market_data, 76 | MarketData 77 | ) 78 | 79 | def test_creates_instance_of_accounts(self): 80 | """Create an instance and make sure it's a `Accounts`client.""" 81 | 82 | self.assertIsInstance( 83 | self.ibc_client.accounts, 84 | Accounts 85 | ) 86 | 87 | def test_creates_instance_of_portfolio_analysis(self): 88 | """Create an instance and make sure it's a `PortfolioAnalysis`client.""" 89 | 90 | self.assertIsInstance( 91 | self.ibc_client.portfolio_analysis, 92 | PortfolioAnalysis 93 | ) 94 | 95 | def test_creates_instance_of_customer(self): 96 | """Create an instance and make sure it's a `Customer`client.""" 97 | 98 | self.assertIsInstance( 99 | self.ibc_client.customers, 100 | Customer 101 | ) 102 | 103 | def test_creates_instance_of_pnl(self): 104 | """Create an instance and make sure it's a `PNL`client.""" 105 | 106 | self.assertIsInstance( 107 | self.ibc_client.pnl, 108 | PnL 109 | ) 110 | 111 | def test_creates_instance_of_contracts(self): 112 | """Create an instance and make sure it's a `Contracts`client.""" 113 | 114 | self.assertIsInstance( 115 | self.ibc_client.contracts, 116 | Contracts 117 | ) 118 | 119 | def test_creates_instance_of_alerts(self): 120 | """Create an instance and make sure it's a `Alerts`client.""" 121 | 122 | self.assertIsInstance( 123 | self.ibc_client.alerts, 124 | Alerts 125 | ) 126 | 127 | def test_creates_instance_of_scanners(self): 128 | """Create an instance and make sure it's a `Scanners`client.""" 129 | 130 | self.assertIsInstance( 131 | self.ibc_client.scanners, 132 | Scanners 133 | ) 134 | 135 | def test_creates_instance_of_trades(self): 136 | """Create an instance and make sure it's a `Trades`client.""" 137 | 138 | self.assertIsInstance( 139 | self.ibc_client.trades, 140 | Trades 141 | ) 142 | 143 | def test_creates_instance_of_portfolios(self): 144 | """Create an instance and make sure it's a `PortfoliosAccounts`client.""" 145 | 146 | self.assertIsInstance( 147 | self.ibc_client.portfolio_accounts, 148 | PortfolioAccounts 149 | ) 150 | 151 | def test_creates_instance_of_orders(self): 152 | """Create an instance and make sure it's a `Orders`client.""" 153 | 154 | self.assertIsInstance( 155 | self.ibc_client.orders, 156 | Orders 157 | ) 158 | 159 | def test_creates_instance_of_data(self): 160 | """Create an instance and make sure it's a `Data`client.""" 161 | 162 | self.assertIsInstance( 163 | self.ibc_client.data_services, 164 | Data 165 | ) 166 | 167 | def tearDown(self) -> None: 168 | """Teardown the `InteractiveBroker` Client.""" 169 | 170 | del self.ibc_client 171 | 172 | 173 | if __name__ == '__main__': 174 | unittest.main() 175 | -------------------------------------------------------------------------------- /ibc/session.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | import logging 4 | import pathlib 5 | import urllib3 6 | 7 | from typing import Dict 8 | from urllib3.exceptions import InsecureRequestWarning 9 | from fake_useragent import UserAgent 10 | 11 | urllib3.disable_warnings(category=InsecureRequestWarning) 12 | 13 | class InteractiveBrokersSession(): 14 | 15 | """Serves as the Session for the Interactive Brokers API.""" 16 | 17 | def __init__(self, ib_client: object) -> None: 18 | """Initializes the `InteractiveBrokersSession` client. 19 | 20 | ### Overview 21 | ---- 22 | The `InteractiveBrokersSession` object handles all the requests made 23 | for the different endpoints on the Interactive Brokers API. 24 | 25 | ### Parameters 26 | ---- 27 | client : object 28 | The `InteractiveBrokersClient` Python Client. 29 | 30 | ### Usage: 31 | ---- 32 | >>> ib_session = InteractiveBrokersSession() 33 | """ 34 | 35 | from ibc.client import InteractiveBrokersClient 36 | 37 | # We can also add custom formatting to our log messages. 38 | log_format = '%(asctime)-15s|%(filename)s|%(message)s' 39 | 40 | self.client: InteractiveBrokersClient = ib_client 41 | 42 | self.resource_url = "https://localhost:5000/v1" 43 | 44 | if not pathlib.Path('logs').exists(): 45 | pathlib.Path('logs').mkdir() 46 | pathlib.Path('logs/log_file_custom.log').touch() 47 | 48 | logging.basicConfig( 49 | filename="logs/log_file_custom.log", 50 | level=logging.INFO, 51 | encoding="utf-8", 52 | format=log_format 53 | ) 54 | 55 | def build_headers(self) -> Dict: 56 | """Used to build the headers needed to make the request. 57 | 58 | ### Parameters 59 | ---- 60 | mode: str, optional 61 | The content mode the headers is being built for, by default `json`. 62 | 63 | ### Returns 64 | ---- 65 | Dict: 66 | A dictionary containing all the components. 67 | """ 68 | 69 | # Fake the headers. 70 | headers = { 71 | "Content-Type": "application/json", 72 | "User-Agent": UserAgent().edge 73 | } 74 | 75 | return headers 76 | 77 | def build_url(self, endpoint: str) -> str: 78 | """Build the URL used the make string. 79 | 80 | ### Parameters 81 | ---- 82 | endpoint : str 83 | The endpoint used to make the full URL. 84 | 85 | ### Returns 86 | ---- 87 | str: 88 | The full URL with the endpoint needed. 89 | """ 90 | 91 | url = self.resource_url + endpoint 92 | 93 | return url 94 | 95 | def make_request( 96 | self, 97 | method: str, 98 | endpoint: str, 99 | params: dict = None, 100 | json_payload: dict = None 101 | ) -> Dict: 102 | """Handles all the requests in the library. 103 | 104 | ### Overview 105 | --- 106 | A central function used to handle all the requests made in the library, 107 | this function handles building the URL, defining Content-Type, passing 108 | through payloads, and handling any errors that may arise during the 109 | request. 110 | 111 | ### Parameters 112 | ---- 113 | method : str 114 | The Request method, can be one of the following: 115 | ['get','post','put','delete','patch'] 116 | 117 | endpoint : str 118 | The API URL endpoint, example is 'quotes' 119 | 120 | params : dict (optional, Default={}) 121 | The URL params for the request. 122 | 123 | data : dict (optional, Default={}) 124 | A data payload for a request. 125 | 126 | json_payload : dict (optional, Default={}) 127 | A json data payload for a request 128 | 129 | ### Returns 130 | ---- 131 | Dict: 132 | A Dictionary object containing the 133 | JSON values. 134 | """ 135 | 136 | # Build the URL. 137 | url = self.build_url(endpoint=endpoint) 138 | headers = self.build_headers() 139 | 140 | logging.info( 141 | msg="------------------------" 142 | ) 143 | 144 | logging.info( 145 | msg=f"JSON Payload: {json_payload}" 146 | ) 147 | 148 | logging.info( 149 | msg=f"Request Method: {method}" 150 | ) 151 | 152 | # Make the request. 153 | if method == 'post': 154 | response = requests.post(url=url, params=params, json=json_payload, verify=False, headers=headers) 155 | elif method == 'get': 156 | response = requests.get(url=url, params=params, json=json_payload, verify=False, headers=headers) 157 | elif method == 'delete': 158 | response = requests.delete(url=url, params=params, json=json_payload, verify=False, headers=headers) 159 | 160 | logging.info( 161 | msg="URL: {url}".format(url=url) 162 | ) 163 | 164 | logging.info( 165 | msg=f'Response Status Code: {response.status_code}' 166 | ) 167 | 168 | logging.info( 169 | msg=f'Response Content: {response.text}' 170 | ) 171 | 172 | # If it's okay and no details. 173 | if response.ok and len(response.content) > 0: 174 | 175 | return response.json() 176 | 177 | elif len(response.content) > 0 and response.ok: 178 | 179 | return { 180 | 'message': 'response successful', 181 | 'status_code': response.status_code 182 | } 183 | 184 | elif not response.ok and endpoint =='/api/iserver/account': 185 | return response.json() 186 | 187 | elif not response.ok: 188 | 189 | if len(response.content) == 0: 190 | response_data = '' 191 | else: 192 | try: 193 | response_data = response.json() 194 | except: 195 | response_data = {'content': response.text} 196 | 197 | # Define the error dict. 198 | error_dict = { 199 | 'error_code': response.status_code, 200 | 'response_url': response.url, 201 | 'response_body': response_data, 202 | 'response_request': dict(response.request.headers), 203 | 'response_method': response.request.method, 204 | } 205 | 206 | # Log the error. 207 | logging.error( 208 | msg=json.dumps(obj=error_dict, indent=4) 209 | ) 210 | 211 | raise requests.HTTPError() 212 | -------------------------------------------------------------------------------- /ibc/utils/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class Frequency(Enum): 5 | """Represents the frequency options for the 6 | `PortfolioAnalysis` service. 7 | 8 | ### Usage 9 | ---- 10 | >>> from ibc.enums import Frequency 11 | >>> Frequency.Daily.value 12 | """ 13 | 14 | Daily = 'D' 15 | Monthly = 'M' 16 | Quarterly = 'Q' 17 | 18 | 19 | class MarketDataFields(Enum): 20 | """Represents the fields for the 21 | `MarketDataSnapshot` service. 22 | 23 | ### Usage 24 | ---- 25 | >>> from ibc.enums import MarketDataFields 26 | >>> MarketDataFields.Symbol.value 27 | """ 28 | 29 | LastPrice = '31' 30 | Symbol = '55' 31 | Text = '58' 32 | High = '70' 33 | Low = '71' 34 | Position = '72' 35 | MarketValue = '73' 36 | AvgPrice = '74' 37 | UnrealizedPnl = '75' 38 | FormattedPosition = '76' 39 | FormattedUnrealizedPnl = '77' 40 | DailyPnl = '78' 41 | Change = '82' 42 | ChangePercent = '83' 43 | BidPrice = '84' 44 | AskSize = '85' 45 | AskPrice = '86' 46 | Volume = '87' 47 | BidSize = '88' 48 | Exchange = '6004' 49 | Conid = '6008' 50 | SecType = '6070' 51 | Months = '6072' 52 | RegularExpiry = '6073' 53 | Marker = '6119' 54 | UnderlyingContract = '6457' 55 | MarketDataAvailability = '6509' 56 | CompanyName = '7051' 57 | AskExch = '7057' 58 | LastExch = '7058' 59 | LastSize = '7059' 60 | BidExch = '7068' 61 | MarketDataAvailabilityOther = '7084' 62 | PutCallInterest = '7085' 63 | PutCallVolume = '7086' 64 | HistoricVolumePercent = '7087' 65 | HistoricVolumeClosePercent = '7088' 66 | OptionVolume = '7089' 67 | ContractIdAndExchange = '7094' 68 | ContractDescription = '7219' 69 | ContractDescriptionOther = '7220' 70 | ListingExchange = '7221' 71 | Industry = '7280' 72 | Category = '7281' 73 | AverageVolume = '7282' 74 | OptionImpliedVolatilityPercent = '7283' 75 | HistoricVolume = '7284' 76 | PutCallRatio = '7285' 77 | DividendAmount = '7286' 78 | DividentYield = '7287' 79 | Ex = '7288' 80 | MarketCap = '7289' 81 | PriceEarningsRatio = '7290' 82 | EarningsPerShare = '7291' 83 | CostBasis = '7292' 84 | FiftyTwoWeekLow = '7293' 85 | FiftyTwoWeekHigh = '7294' 86 | Open = '7295' 87 | Close = '7296' 88 | Delta = '7308' 89 | Gamma = '7309' 90 | Theta = '7310' 91 | Vega = '7311' 92 | OptionVolumeChangePercent = '7607' 93 | ImpliedVolatilityPercent = '7633' 94 | Mark = '7635' 95 | ShortableShares = '7636' 96 | FeeRate = '7637' 97 | OptionOpenInterest = '7638' 98 | PercentOfMarketValue = '7639' 99 | Shortable = '7644' 100 | MorningstarRating = '7655' 101 | Dividends = '7671' 102 | DividendsTtm = '7672' 103 | EMATwoHundred = '7674' 104 | EMAOneHundred = '7675' 105 | EMAFiftyDay = '7676' 106 | EMATwentyDay = '7677' 107 | PriceEMATwoHundredDay = '7678' 108 | PriceEMAOneHundredDay = '7679' 109 | PriceEMAFiftyDay = '7680' 110 | PriceEMATwentyDay = '7681' 111 | ChangeSinceOpen = '7682' 112 | UpcomingEvent = '7683' 113 | UpcomingEventDate = '7684' 114 | UpcomingAnalystMeeting = '7685' 115 | UpcomingEarnings = '7686' 116 | UpcomingMiscEvents = '7687' 117 | RecentAnalystMeeting = '7688' 118 | RecentEarnings = '7689' 119 | RecentMiscEvents = '7690' 120 | ProbabilityOfMaxReturnCustomer = '7694' 121 | BreakEven = '7695' 122 | SpxDelta = '7696' 123 | FuturesOpenInterest = '7697' 124 | LastYield = '7698' 125 | BidYield = '7699' 126 | ProbabilityMaxReturn = '7700' 127 | ProbabilityMaxLoss = '7702' 128 | ProfitProbability = '7703' 129 | OrganizationType = '7704' 130 | DebtClass = '7705' 131 | Ratings = '7706' 132 | BondStateCode = '7707' 133 | BondType = '7708' 134 | LastTradingDate = '7714' 135 | IssueDate = '7715' 136 | Beta = '7718' 137 | AskYield = '7720' 138 | PriorClose = '7741' 139 | VolumeLong = '7762' 140 | All = [ 141 | '31', '55', '58', '70', '71', '72', '73', '74', '75', '76', '77', '78', '82', '83', '84', '85', '86', '87', '88', '6004', '6008', '6070', '6072', '6073', '6119', '6457', '6509', '7051', '7057', '7058', '7059', '7068', '7084', '7085', '7086', '7087', '7088', '7089', '7094', '7219', '7220', '7221', '7280', '7281', '7282', '7283', '7284', '7285', '7286', '7287', '7288', '7289', '7290', '7291', '7292', '7293', '7294', '7295', 142 | '7296', '7308', '7309', '7310', '7311', '7607', '7633', '7635', '7636', '7637', '7638', '7639', '7644', '7655', '7671', '7672', '7674', '7675', '7676', '7677', '7678', '7679', '7680', '7681', '7682', '7683', '7684', '7685', '7686', '7687', '7688', '7689', '7690', '7694', '7695', '7696', '7697', '7698', '7699', '7700', '7702', '7703', '7704', '7705', '7706', '7707', '7708', '7714', '7715', '7718', '7720', '7741', '7762' 143 | ] 144 | 145 | 146 | class BarTypes(Enum): 147 | """Represents the bar types for the 148 | `MarketDataHistory` service. 149 | 150 | ### Usage 151 | ---- 152 | >>> from ibc.enums import BarTypes 153 | >>> BarTypes.OneMinute.value 154 | """ 155 | 156 | OneMinute = '1min' 157 | TwoMinute = '2min' 158 | ThreeMinute = '3min' 159 | FiveMinute = '5min' 160 | TenMinute = '10min' 161 | FifteenMinute = '15min' 162 | ThirtyMinute = '30min' 163 | OneHour = '1h' 164 | TwoHour = '2h' 165 | ThreeHour = '3h' 166 | FourHour = '4h' 167 | EightHour = '8h' 168 | OneDay = '1d' 169 | OneWeek = '1w' 170 | OneMonth = '1m' 171 | 172 | 173 | class SortDirection(Enum): 174 | """Represents the sort directions for the 175 | `PortfolioPositions` service. 176 | 177 | ### Usage 178 | ---- 179 | >>> from ibc.enums import SortDirection 180 | >>> SortDirection.Ascending.value 181 | """ 182 | 183 | Ascending = 'a' 184 | Descending = 'd' 185 | 186 | 187 | class SortFields(Enum): 188 | """Represents the sort fields for the 189 | `PortfolioPositions` service. 190 | 191 | ### Usage 192 | ---- 193 | >>> from ibc.enums import SortFields 194 | >>> SortFields.MarketPrice.value 195 | """ 196 | 197 | AccountId = 'acctId' 198 | ContractId = 'conid' 199 | ContractDescription = 'contractDesc' 200 | Position = 'position' 201 | MarketPrice = 'mktPrice' 202 | MarketValue = 'MktValue' 203 | Currency = 'USD' 204 | AverageCost = 'avgCost' 205 | AveragePrice = 'avgPrice' 206 | RealizedPnl = 'realizedPnl' 207 | UnrealizedPnl = 'unrealizedPnl' 208 | Exchanges = 'exchs' 209 | ExpirationDate = 'expiry' 210 | PutOrCall = 'putOrCall' 211 | Multiplier = 'multiplier' 212 | Strike = 'strike' 213 | ExerciseStyle = 'exerciseStyle' 214 | AssetClass = 'assetClass' 215 | Model = 'model' 216 | UnderlyingContractId = 'undConid' 217 | BaseMarketValue = 'baseMktValue' 218 | BaseMarketPrice = 'baseMktPrice' 219 | BaseAverageCost = 'BaseAvgCost' 220 | BaseAveragePrice = 'BaseAvgPrice' 221 | BaseRealizedPnl = 'baseRealizedPnl' 222 | BaseUnrealizedPnl = 'baseUnrealizedPnl' 223 | -------------------------------------------------------------------------------- /ibc/utils/auth.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import requests 3 | import subprocess 4 | import webbrowser 5 | from ibc.session import InteractiveBrokersSession 6 | 7 | 8 | class InteractiveBrokersAuthentication(): 9 | 10 | def __init__(self, ib_client: object, ib_session: InteractiveBrokersSession) -> None: 11 | """Initializes the `InteractiveBrokersAuthentication` client. 12 | 13 | ### Parameters 14 | ---- 15 | ib_client : object 16 | The `InteractiveBrokersClient` Python Client. 17 | 18 | ib_session : InteractiveBrokersSession 19 | The IB session handler. 20 | """ 21 | 22 | from ibc.client import InteractiveBrokersClient 23 | 24 | self.client: InteractiveBrokersClient = ib_client 25 | self.session: InteractiveBrokersSession = ib_session 26 | self.authenticated = False 27 | self.server_process_id = None 28 | 29 | def login(self, _use_selenium: bool = False) -> dict: 30 | """Logs the user in to the Client Portal Gateway. 31 | 32 | ### Parameters 33 | ---- 34 | _use_selenium (bool, optional, Default=False): 35 | If set to `True` will use Selenium to pass through your username and password 36 | to the login page. Can only be done if use with paper trading account, otherwise 37 | it will default to `False`. If set to `False` user will be redirected to the 38 | login form where the user will need to provide their credentials. 39 | 40 | ### Returns 41 | ---- 42 | dict: 43 | The process resource with the process ID of the client portal 44 | gateway. 45 | """ 46 | 47 | is_running_response = self._is_already_running() 48 | 49 | if not is_running_response['is_running']: 50 | self._startup_gateway() 51 | else: 52 | print("Gateway already running, no need to start back up.") 53 | 54 | def _startup_gateway(self) -> None: 55 | """Starts the Client Portal Up so the user can authenticate.""" 56 | 57 | # Starts a new process where the window is called `Interactive Brokers Python API` 58 | # and runs the bin files. 59 | IB_WEB_API_PROC = [ 60 | "cmd", "/k", "start", "Interactive Brokers Python API", r"bin\run.bat", r"root\conf.yaml" 61 | ] 62 | 63 | # Open in a new window. 64 | server_process = subprocess.Popen( 65 | args=IB_WEB_API_PROC, 66 | cwd="ibc/resources/clientportal.beta.gw", 67 | creationflags=subprocess.CREATE_NEW_CONSOLE, 68 | ) 69 | 70 | # Store the process ID just to make sure we kill it. 71 | self.server_process_id = server_process.pid 72 | 73 | webbrowser.open(url='https://localhost:5000') 74 | 75 | def _is_already_running(self) -> dict: 76 | """Checks whether the gateway is already running. 77 | 78 | ### Returns 79 | ---- 80 | dict: 81 | A response containing the process ID of the gateway 82 | if any, or a message saying the process wasn't found. 83 | """ 84 | 85 | IB_WEB_CHECK = [ 86 | 'tasklist', '/fi', "WindowTitle eq Interactive Brokers Python API*", '/FO', 'CSV' 87 | ] 88 | 89 | # Grab the output and make sure it's a string. 90 | content = subprocess.run( 91 | args=IB_WEB_CHECK, 92 | capture_output=True 93 | ).stdout.decode() 94 | 95 | if 'INFO:' in content: 96 | data = content 97 | else: 98 | content = content.splitlines() 99 | headers = content[0].replace('"', '').split(',') 100 | data = content[1:] 101 | data = list(csv.DictReader(f=data, fieldnames=headers)) 102 | 103 | if 'PID' in data[0]: 104 | self.server_process_id = data[0]['PID'] 105 | 106 | return { 107 | 'is_running': True, 108 | 'data': data 109 | } 110 | else: 111 | return { 112 | 'is_running': False, 113 | 'data': data 114 | } 115 | 116 | def close_gateway(self, pid: int = None) -> str: 117 | """Closes down the Client Portal Gateway. 118 | 119 | ### Parameters 120 | ---- 121 | pid : int (optional, Default=None) 122 | If you'd like you can manually close the process. 123 | 124 | ### Returns 125 | ---- 126 | str: 127 | A message will be return if the termination process 128 | was successful. 129 | """ 130 | 131 | if pid is None: 132 | pid = self.server_process_id 133 | 134 | content = subprocess.run( 135 | args=['Taskkill', '/F', '/PID', str(pid)], 136 | capture_output=True 137 | ) 138 | 139 | return content.stdout.decode() 140 | 141 | def is_authenticated(self, check: bool = False) -> dict: 142 | """Checks if session is authenticated. 143 | 144 | ### Overview 145 | ---- 146 | Current Authentication status to the Brokerage system. Market Data and 147 | Trading is not possible if not authenticated, e.g. authenticated 148 | shows `False`. 149 | 150 | ### Returns 151 | ---- 152 | dict: 153 | A dictionary with an authentication flag. 154 | """ 155 | 156 | # Make the request. 157 | content = self.session.make_request( 158 | method='post', 159 | endpoint='/api/iserver/auth/status' 160 | ) 161 | 162 | return content 163 | 164 | def update_server_account(self, account_id: str) -> dict: 165 | """Sets the account for the session. 166 | 167 | ### Overview 168 | ---- 169 | If an user has multiple accounts, and user wants to get orders, trades, 170 | etc. of an account other than currently selected account, then user 171 | can update the currently selected account using this API and then can 172 | fetch required information for the newly updated account. 173 | 174 | ### Parameters 175 | ---- 176 | account_id : str 177 | The account ID you wish to set for the API Session. This will be used to 178 | grab historical data and make orders. 179 | 180 | ### Returns 181 | ---- 182 | dict: 183 | A `ServerAccount` resource. 184 | """ 185 | 186 | payload = { 187 | 'acctId': account_id 188 | } 189 | 190 | # Make the request. 191 | content = self.session.make_request( 192 | method='post', 193 | endpoint='/api/iserver/account', 194 | json_payload=payload 195 | ) 196 | 197 | return content 198 | 199 | def sso_validate(self) -> dict: 200 | """Validates the current session for the SSO user. 201 | 202 | ### Returns 203 | ---- 204 | dict : 205 | A `Validation` resource. 206 | """ 207 | 208 | # Make the request. 209 | content = self.session.make_request( 210 | method='post', 211 | endpoint='/api/sso/validate' 212 | ) 213 | 214 | return content 215 | 216 | def reauthenticate(self) -> dict: 217 | """When using the CP Gateway, this endpoint provides a way to 218 | reauthenticate to the Brokerage system as long as there is a 219 | valid SSO session, see /sso/validate. 220 | 221 | ### Returns 222 | ---- 223 | dict : 224 | An `Authentication` resource. 225 | """ 226 | 227 | # Make the request. 228 | content = self.session.make_request( 229 | method='post', 230 | endpoint='/api/iserver/reauthenticate' 231 | ) 232 | 233 | return content 234 | 235 | def check_auth(self) -> None: 236 | """Checks the authentication of the user to see 237 | if they've logged in. 238 | """ 239 | 240 | print("Checking authentication status...") 241 | 242 | try: 243 | response = self.is_authenticated() 244 | if response['authenticated'] == True: 245 | self.authenticated = True 246 | return 247 | except: 248 | return 249 | -------------------------------------------------------------------------------- /ibc/rest/orders.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from ibc.session import InteractiveBrokersSession 3 | 4 | 5 | class Orders(): 6 | 7 | def __init__(self, ib_client: object, ib_session: InteractiveBrokersSession) -> None: 8 | """Initializes the `Orders` client. 9 | 10 | ### Parameters 11 | ---- 12 | ib_client : object 13 | The `InteractiveBrokersClient` Python Client. 14 | 15 | ib_session : InteractiveBrokersSession 16 | The IB session handler. 17 | """ 18 | 19 | from ibc.client import InteractiveBrokersClient 20 | 21 | self.client: InteractiveBrokersClient = ib_client 22 | self.session: InteractiveBrokersSession = ib_session 23 | 24 | def orders(self) -> dict: 25 | """The end-point is meant to be used in polling mode, e.g. requesting 26 | every x seconds. 27 | 28 | ### Overview 29 | ---- 30 | The response will contain two objects, one is notification, 31 | the other is orders. Orders is the list of orders (cancelled, 32 | filled, submitted) with activity in the current day. Notifications 33 | contains information about execute orders as they happen, see 34 | status field. 35 | 36 | ### Returns 37 | ---- 38 | dict: 39 | A collection of `Order` resources. 40 | 41 | ### Usage 42 | ---- 43 | >>> orders_services = ibc_client.orders 44 | >>> orders_services.orders() 45 | """ 46 | 47 | content = self.session.make_request( 48 | method='get', 49 | endpoint='/api/iserver/account/orders' 50 | ) 51 | 52 | return content 53 | 54 | def place_order(self, account_id: str, order: dict) -> dict: 55 | """Places an order. 56 | 57 | ### Overview 58 | ---- 59 | Please note here, sometimes this end-point alone can’t make sure 60 | you submit the order successfully, you could receive some questions 61 | in the response, you have to to answer them in order to submit the order 62 | successfully. You can use `/iserver/reply/{replyid}` end-point to answer 63 | questions. 64 | 65 | ### Parameters 66 | ---- 67 | account_id : str 68 | The account you want the order placed on. 69 | 70 | order : dict 71 | The order payload. 72 | 73 | ### Returns 74 | ---- 75 | dict: 76 | A `Reply` resource or a `Order` resource. 77 | 78 | ### Usage 79 | ---- 80 | >>> orders_services = ibc_client.orders 81 | >>> orders_services.place_order( 82 | account_id=ibc_client.account_number, 83 | order={ 84 | "conid": 251962528, 85 | "secType": "362673777:STK", 86 | "cOID": "limit-buy-order-1", 87 | "orderType": "LMT", 88 | "price": 5.00, 89 | "side": "BUY", 90 | "quantity": 1, 91 | "tif": "DAY" 92 | } 93 | ) 94 | """ 95 | 96 | content = self.session.make_request( 97 | method='post', 98 | endpoint=f'/api/iserver/account/{account_id}/order', 99 | json_payload=order 100 | ) 101 | 102 | return content 103 | 104 | def place_bracket_order(self, account_id: str, orders: dict) -> dict: 105 | """Places multiple orders at once. 106 | 107 | ### Parameters 108 | ---- 109 | account_id : str 110 | The account you want the orders placed on. 111 | 112 | orders : dict 113 | The orders payload. 114 | 115 | ### Returns 116 | ---- 117 | dict: 118 | A `Reply` resource or a `Order` resource. 119 | 120 | ### Usage 121 | ---- 122 | >>> orders_services = ibc_client.orders 123 | >>> orders_services.place_bracket_order( 124 | account_id=ibc_client.account_number, 125 | order={ 126 | "orders": [ 127 | { 128 | "conid": 251962528, 129 | "secType": "362673777:FUT", 130 | "cOID": "buy-1", 131 | "orderType": "LMT", 132 | "side": "BUY", 133 | "price": 9.00, 134 | "quantity": 1, 135 | "tif": "DAY" 136 | }, 137 | { 138 | "conid": 251962528, 139 | "secType": "362673777:STK", 140 | # This MUST match the `cOID` of the first order. 141 | "parentId": "buy-1", 142 | "orderType": "LMT", 143 | "side": "BUY", 144 | "price": 7.00, 145 | "quantity": 2, 146 | "tif": "DAY" 147 | } 148 | ] 149 | } 150 | ) 151 | """ 152 | 153 | content = self.session.make_request( 154 | method='post', 155 | endpoint=f'/api/iserver/account/{account_id}/orders', 156 | json_payload=orders 157 | ) 158 | 159 | return content 160 | 161 | def modify_order(self, account_id: str, order_id: str, order: dict) -> dict: 162 | """Modifies an open order. 163 | 164 | ### Overview 165 | ---- 166 | The `/iserver/accounts` endpoint must first be called. 167 | 168 | ### Parameters 169 | ---- 170 | account_id : str 171 | The account which has the order you want to be 172 | modified. 173 | 174 | order_id : str 175 | The id of the order you want to be modified. 176 | 177 | order : dict 178 | The new order payload. 179 | 180 | ### Returns 181 | ---- 182 | dict: 183 | A `Reply` resource or a `Order` resource. 184 | 185 | ### Usage 186 | ---- 187 | >>> orders_services = ibc_client.orders 188 | >>> orders_services.modify_order( 189 | account_id=ibc_client.account_number, 190 | order_id='1915650539', 191 | order={ 192 | "conid": 251962528, 193 | "secType": "362673777:STK", 194 | "cOID": "limit-buy-order-1", 195 | "orderType": "LMT", 196 | "price": 5.00, 197 | "side": "BUY", 198 | "quantity": 1, 199 | "tif": "DAY" 200 | } 201 | ) 202 | """ 203 | 204 | content = self.session.make_request( 205 | method='post', 206 | endpoint=f'/api/iserver/account/{account_id}/order', 207 | json_payload=order 208 | ) 209 | 210 | return content 211 | 212 | def delete_order(self, account_id: str, order_id: str) -> Union[list, dict]: 213 | """Deletes an order. 214 | 215 | ### Parameters 216 | ---- 217 | account_id : str 218 | The account that contains the order you want to 219 | delete. 220 | 221 | order_id : str 222 | The id of the order you want to delete. 223 | 224 | ### Returns 225 | ---- 226 | Union[list, dict]: 227 | A `OrderResponse` resource or a collection of them. 228 | 229 | ### Usage 230 | ---- 231 | >>> orders_services = ibc_client.orders 232 | >>> orders_services.delete_order( 233 | account_id=ibc_client.account_number, 234 | order_id= 235 | ) 236 | """ 237 | 238 | content = self.session.make_request( 239 | method='delete', 240 | endpoint=f'/api/iserver/account/{account_id}/order/{order_id}' 241 | ) 242 | 243 | return content 244 | 245 | def place_whatif_order(self, account_id: str, order: dict) -> dict: 246 | """This end-point allows you to preview order without actually 247 | submitting the order and you can get commission information in 248 | the response. 249 | 250 | ### Parameters 251 | ---- 252 | account_id : str 253 | The account you want the order placed on. 254 | 255 | order : dict 256 | The order payload. 257 | 258 | ### Returns 259 | ---- 260 | dict: 261 | A `OrderCommission` resource. 262 | 263 | ### Usage 264 | ---- 265 | >>> orders_services = ibc_client.orders 266 | >>> orders_services.place_whatif_order( 267 | account_id=ibc_client.account_number, 268 | order={ 269 | "conid": 251962528, 270 | "secType": "362673777:STK", 271 | "cOID": "limit-buy-order-1", 272 | "orderType": "LMT", 273 | "price": 5.00, 274 | "side": "BUY", 275 | "quantity": 1, 276 | "tif": "DAY" 277 | } 278 | ) 279 | """ 280 | 281 | content = self.session.make_request( 282 | method='post', 283 | endpoint=f'/api/iserver/account/{account_id}/order/whatif', 284 | json_payload=order 285 | ) 286 | 287 | return content 288 | 289 | def reply(self, reply_id: str, message: dict) -> Union[list, dict]: 290 | """Reply to questions when placing orders and submit orders. 291 | 292 | ### Parameters 293 | ---- 294 | reply_id : str 295 | The `ID` from the response of `Place Order` end-point 296 | 297 | message : dict 298 | The answer to question. 299 | 300 | ### Returns 301 | ---- 302 | Union[list, dict]: 303 | A list when the order is submitted, a dictionary 304 | with an error message if not confirmed. 305 | 306 | ### Usage 307 | ---- 308 | >>> orders_services = ibc_client.orders 309 | >>> orders_services.reply( 310 | reply_id='5050c104-1276-4483-8be8-ca598e698766', 311 | message={ 312 | "confirmed": True 313 | } 314 | ) 315 | """ 316 | 317 | content = self.session.make_request( 318 | method='post', 319 | endpoint=f'/api/iserver/reply/{reply_id}', 320 | json_payload=message 321 | ) 322 | 323 | return content 324 | -------------------------------------------------------------------------------- /ibc/client.py: -------------------------------------------------------------------------------- 1 | from ibc.rest.accounts import Accounts 2 | from ibc.rest.alert import Alerts 3 | from ibc.rest.contract import Contracts 4 | from ibc.rest.customer import Customer 5 | from ibc.rest.market_data import MarketData 6 | from ibc.rest.orders import Orders 7 | from ibc.rest.pnl import PnL 8 | from ibc.rest.portfolio import PortfolioAccounts 9 | from ibc.rest.portfolio_analysis import PortfolioAnalysis 10 | from ibc.rest.scanner import Scanners 11 | from ibc.rest.trades import Trades 12 | from ibc.session import InteractiveBrokersSession 13 | from ibc.utils.auth import InteractiveBrokersAuthentication 14 | from ibc.utils.gateway import ClientPortalGateway 15 | from ibc.rest.data import Data 16 | 17 | 18 | class InteractiveBrokersClient(): 19 | 20 | def __init__(self, account_number: str, password: str) -> None: 21 | """Initializes the `InteractiveBrokersClient` object. 22 | 23 | ### Parameters 24 | ---- 25 | account_number (str): 26 | The User's account number they wish to use during the 27 | session. Can be either their paper trading account or 28 | their regular account. 29 | 30 | password (str): 31 | The password associated with the account they've chosen. 32 | 33 | ### Usage 34 | ---- 35 | >>> ibc_client = InteractiveBrokersClient( 36 | account_number=account_number, 37 | password=account_password 38 | ) 39 | """ 40 | 41 | self._account_number = account_number 42 | self._password = password 43 | 44 | # Initialize the services that need to start up together. 45 | self._session = InteractiveBrokersSession(ib_client=self) 46 | self._auth_service = InteractiveBrokersAuthentication( 47 | ib_client=self, 48 | ib_session=self._session 49 | ) 50 | 51 | # Client portal stuff. 52 | self._client_portal = ClientPortalGateway() 53 | self._client_portal.setup() 54 | 55 | @property 56 | def account_number(self) -> str: 57 | """The User's Interactive Brokers Account Number. 58 | 59 | ### Returns 60 | ---- 61 | str: 62 | The account number. 63 | 64 | ### Usage 65 | ---- 66 | >>> ibc_client = InteractiveBrokersClient( 67 | account_number=account_number, 68 | password=account_password 69 | ) 70 | >>> ibc_client.account_number 71 | """ 72 | 73 | return self._account_number 74 | 75 | @property 76 | def client_portal(self) -> ClientPortalGateway: 77 | """Initializes the `ClientPortalGateway` object. 78 | 79 | ### Returns 80 | ---- 81 | `ClientPortalGateway`: 82 | The Interactive Brokers Client Portal Gateway, which is used 83 | to download the required files needed to access the API. 84 | 85 | ### Usage 86 | ---- 87 | >>> ibc_client = InteractiveBrokersClient( 88 | account_number=account_number, 89 | password=account_password 90 | ) 91 | >>> ibc_client.authentication.login() 92 | >>> ibc_client_portal = ibc_client.client_portal 93 | """ 94 | 95 | return self._client_portal 96 | 97 | def session(self) -> InteractiveBrokersSession: 98 | """Initializes the `InteractiveBrokersSession` object. 99 | 100 | ### Returns 101 | ---- 102 | `InteractiveBrokersSession`: 103 | Handles all the requests made during your session with 104 | the Interactive Brokers API. 105 | 106 | ### Usage 107 | ---- 108 | >>> ibc_client = InteractiveBrokersClient( 109 | account_number=account_number, 110 | password=account_password 111 | ) 112 | >>> ibc_client.authentication.login() 113 | >>> ibc_session = ibc_client.session 114 | """ 115 | 116 | return self._session 117 | 118 | @property 119 | def authentication(self) -> InteractiveBrokersAuthentication: 120 | """Initializes the `InteractiveBrokersAuthentication` object. 121 | 122 | ### Returns 123 | ---- 124 | `InteractiveBrokersAuthentication`: 125 | Handles authenticating the User so that they can make 126 | requests to the Interactive Brokers API. 127 | 128 | ### Usage 129 | ---- 130 | >>> ibc_client = InteractiveBrokersClient( 131 | account_number=account_number, 132 | password=account_password 133 | ) 134 | >>> ibc_client.authentication.login() 135 | >>> authentication_service = ibc_client.authentication 136 | """ 137 | 138 | return self._auth_service 139 | 140 | @property 141 | def customers(self) -> Customer: 142 | """Initializes the `Customer` object. 143 | 144 | ### Returns 145 | ---- 146 | `Customer`: 147 | Used to grab customer information. 148 | 149 | ### Usage 150 | ---- 151 | >>> ibc_client = InteractiveBrokersClient( 152 | account_number=account_number, 153 | password=account_password 154 | ) 155 | >>> ibc_client.authentication.login() 156 | >>> customer_service = ibc_client.customers 157 | """ 158 | 159 | return Customer(ib_client=self, ib_session=self._session) 160 | 161 | @property 162 | def portfolio_analysis(self) -> PortfolioAnalysis: 163 | """Initializes the `PortfolioAnalysis` object. 164 | 165 | ### Returns 166 | ---- 167 | `PortfolioAnalysis`: 168 | Used to interact with the Portfolio Analysis 169 | service. 170 | 171 | ### Usage 172 | ---- 173 | >>> ibc_client = InteractiveBrokersClient( 174 | account_number=account_number, 175 | password=account_password 176 | ) 177 | >>> ibc_client.authentication.login() 178 | >>> portfolio_analysis_service = ibc_client.portfolio_analysis 179 | """ 180 | 181 | return PortfolioAnalysis(ib_client=self, ib_session=self._session) 182 | 183 | @property 184 | def accounts(self) -> Accounts: 185 | """Initializes the `Accounts` object. 186 | 187 | ### Returns 188 | ---- 189 | `Accounts`: 190 | Used to interact with the Accounts 191 | service. 192 | 193 | ### Usage 194 | ---- 195 | >>> ibc_client = InteractiveBrokersClient( 196 | account_number=account_number, 197 | password=account_password 198 | ) 199 | >>> ibc_client.authentication.login() 200 | >>> accounts_services = ibc_client.accounts 201 | """ 202 | 203 | return Accounts(ib_client=self, ib_session=self._session) 204 | 205 | @property 206 | def market_data(self) -> MarketData: 207 | """Initializes the `MarketData` object. 208 | 209 | ### Returns 210 | ---- 211 | `MarketData`: 212 | Used to market quotes and historical prices. 213 | 214 | ### Usage 215 | ---- 216 | >>> ibc_client = InteractiveBrokersClient( 217 | account_number=account_number, 218 | password=account_password 219 | ) 220 | >>> ibc_client.authentication.login() 221 | >>> market_data_services = ibc_client.market_data 222 | """ 223 | 224 | return MarketData(ib_client=self, ib_session=self._session) 225 | 226 | @property 227 | def pnl(self) -> PnL: 228 | """Initializes the `PnL` object. 229 | 230 | ### Returns 231 | ---- 232 | `PnL`: 233 | Used to grab Account PNL information. 234 | 235 | ### Usage 236 | ---- 237 | >>> ibc_client = InteractiveBrokersClient( 238 | account_number=account_number, 239 | password=account_password 240 | ) 241 | >>> ibc_client.authentication.login() 242 | >>> pnl_services = ibc_client.pnl 243 | """ 244 | 245 | return PnL(ib_client=self, ib_session=self._session) 246 | 247 | @property 248 | def alerts(self) -> Alerts: 249 | """Initializes the `Alerts` object. 250 | 251 | ### Returns 252 | ---- 253 | `Alerts`: 254 | Used to grab, update, and delete Alerts 255 | associated with your account. 256 | 257 | ### Usage 258 | ---- 259 | >>> ibc_client = InteractiveBrokersClient( 260 | account_number=account_number, 261 | password=account_password 262 | ) 263 | >>> ibc_client.authentication.login() 264 | >>> alerts_services = ibc_client.alerts 265 | """ 266 | 267 | return Alerts(ib_client=self, ib_session=self._session) 268 | 269 | @property 270 | def contracts(self) -> Contracts: 271 | """Initializes the `Contracts` object. 272 | 273 | ### Returns 274 | ---- 275 | `Contracts`: 276 | Used to search for contract information. 277 | 278 | ### Usage 279 | ---- 280 | >>> ibc_client = InteractiveBrokersClient( 281 | account_number=account_number, 282 | password=account_password 283 | ) 284 | >>> ibc_client.authentication.login() 285 | >>> contracts_services = ibc_client.contracts 286 | """ 287 | 288 | return Contracts(ib_client=self, ib_session=self._session) 289 | 290 | @property 291 | def scanners(self) -> Scanners: 292 | """Initializes the `Scanners` object. 293 | 294 | ### Returns 295 | ---- 296 | `Scanners`: 297 | Used to create market scanners that can 298 | be used to filter instruments. 299 | 300 | ### Usage 301 | ---- 302 | >>> ibc_client = InteractiveBrokersClient( 303 | account_number=account_number, 304 | password=account_password 305 | ) 306 | >>> ibc_client.authentication.login() 307 | >>> scanners_services = ibc_client.scanners 308 | """ 309 | 310 | return Scanners(ib_client=self, ib_session=self._session) 311 | 312 | @property 313 | def trades(self) -> Trades: 314 | """Initializes the `Trades` object. 315 | 316 | ### Returns 317 | ---- 318 | `Trades`: 319 | Used to query active trades on your 320 | account. 321 | 322 | ### Usage 323 | ---- 324 | >>> ibc_client = InteractiveBrokersClient( 325 | account_number=account_number, 326 | password=account_password 327 | ) 328 | >>> ibc_client.authentication.login() 329 | >>> trades_services = ibc_client.trades 330 | """ 331 | 332 | return Trades(ib_client=self, ib_session=self._session) 333 | 334 | @property 335 | def portfolio_accounts(self) -> PortfolioAccounts: 336 | """Initializes the `PortfolioAccounts` object. 337 | 338 | ### Returns 339 | ---- 340 | `PortfolioAccounts`: 341 | Used to query portfolio account information 342 | including ledger data, allocation, and positions. 343 | 344 | ### Usage 345 | ---- 346 | >>> ibc_client = InteractiveBrokersClient( 347 | account_number=account_number, 348 | password=account_password 349 | ) 350 | >>> ibc_client.authentication.login() 351 | >>> portfolio_accounts_service = ibc_client.portfolio_accounts 352 | """ 353 | 354 | return PortfolioAccounts(ib_client=self, ib_session=self._session) 355 | 356 | @property 357 | def orders(self) -> Orders: 358 | """Initializes the `Orders` object. 359 | 360 | ### Returns 361 | ---- 362 | `Orders`: 363 | Used to query, create, update, and delete 364 | orders with Interactive Brokers. 365 | 366 | ### Usage 367 | ---- 368 | >>> ibc_client = InteractiveBrokersClient( 369 | account_number=account_number, 370 | password=account_password 371 | ) 372 | >>> ibc_client.authentication.login() 373 | >>> orders_service = ibc_client.orders 374 | """ 375 | 376 | return Orders(ib_client=self, ib_session=self._session) 377 | 378 | @property 379 | def data_services(self) -> Data: 380 | """Initializes the `Data` object. 381 | 382 | ### Returns 383 | ---- 384 | `Data`: 385 | Used to query different kinds of data 386 | for instruments. 387 | 388 | ### Usage 389 | ---- 390 | >>> ibc_client = InteractiveBrokersClient( 391 | account_number=account_number, 392 | password=account_password 393 | ) 394 | >>> ibc_client.authentication.login() 395 | >>> data_service = ibc_client.data_services 396 | """ 397 | 398 | return Data(ib_client=self, ib_session=self._session) 399 | -------------------------------------------------------------------------------- /ibc/rest/portfolio.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from typing import List 3 | from enum import Enum 4 | from ibc.session import InteractiveBrokersSession 5 | 6 | 7 | class PortfolioAccounts(): 8 | 9 | def __init__(self, ib_client: object, ib_session: InteractiveBrokersSession) -> None: 10 | """Initializes the `PortfolioAccounts` client. 11 | 12 | ### Parameters 13 | ---- 14 | ib_client : object 15 | The `InteractiveBrokersClient` Python Client. 16 | 17 | ib_session : InteractiveBrokersSession 18 | The IB session handler. 19 | """ 20 | 21 | from ibc.client import InteractiveBrokersClient 22 | 23 | self.client: InteractiveBrokersClient = ib_client 24 | self.session: InteractiveBrokersSession = ib_session 25 | self._has_portfolio_been_called = False 26 | self._has_sub_portfolio_been_called = False 27 | 28 | def accounts(self) -> list: 29 | """Returns the portfolio accounts 30 | 31 | ### Overview 32 | ---- 33 | In non-tiered account structures, returns a list of accounts 34 | for which the user can view position and account information. 35 | This endpoint must be called prior to calling other /portfolio 36 | endpoints for those accounts. For querying a list of accounts 37 | which the user can trade, see /iserver/accounts. For a list 38 | of subaccounts in tiered account structures (e.g. financial 39 | advisor or ibroker accounts) see /portfolio/subaccounts. 40 | 41 | ### Returns 42 | ---- 43 | list: 44 | A collection of `PortfolioAccount` resources. 45 | 46 | ### Usage 47 | ---- 48 | >>> portfolio_accounts_service = ibc_client.portfolio_accounts 49 | >>> portfolio_accounts_services.accounts() 50 | """ 51 | 52 | content = self.session.make_request( 53 | method='get', 54 | endpoint='/api/portfolio/accounts' 55 | ) 56 | 57 | self._has_portfolio_been_called = True 58 | 59 | return content 60 | 61 | def subaccounts(self) -> list: 62 | """Returns the portfolio subaccounts 63 | 64 | ### Overview 65 | ---- 66 | Used in tiered account structures (such as financial advisor 67 | and ibroker accounts) to return a list of sub-accounts for 68 | which the user can view position and account-related information. 69 | This endpoint must be called prior to calling other /portfolio 70 | endpoints for those subaccounts. To query a list of accounts 71 | the user can trade, see /iserver/accounts. 72 | 73 | ### Returns 74 | ---- 75 | list: 76 | A collection of `PortfolioSubAccount` resources. 77 | 78 | ### Usage 79 | ---- 80 | >>> portfolio_accounts_service = ibc_client.portfolio_accounts 81 | >>> portfolio_accounts_services.subaccounts() 82 | """ 83 | 84 | content = self.session.make_request( 85 | method='get', 86 | endpoint='/api/portfolio/subaccounts' 87 | ) 88 | 89 | self._has_sub_portfolio_been_called = True 90 | 91 | return content 92 | 93 | def account_metadata(self, account_id: str) -> dict: 94 | """Account information related to account Id. 95 | 96 | ### Overview 97 | --- 98 | /portfolio/accounts or /portfolio/subaccounts 99 | must be called prior to this endpoint. 100 | 101 | ### Returns 102 | ---- 103 | dict: 104 | A `AccountInfo` resource. 105 | 106 | ### Usage 107 | ---- 108 | >>> portfolio_accounts_service = ibc_client.portfolio_accounts 109 | >>> portfolio_accounts_services.account_metadata( 110 | account_id=ibc_client.account_number 111 | ) 112 | """ 113 | 114 | if not self._has_portfolio_been_called: 115 | self.accounts() 116 | 117 | if not self._has_sub_portfolio_been_called: 118 | self.subaccounts() 119 | 120 | content = self.session.make_request( 121 | method='get', 122 | endpoint=f'/api/portfolio/{account_id}/meta' 123 | ) 124 | 125 | return content 126 | 127 | def account_summary(self, account_id: str) -> dict: 128 | """Returns information about margin, cash balances 129 | and other information related to specified account. 130 | 131 | ### Overview 132 | ---- 133 | `/portfolio/accounts` or `/portfolio/subaccounts` 134 | must be called prior to this endpoint. 135 | 136 | ### Returns 137 | ---- 138 | dict: 139 | A `AccountSummary` resource. 140 | 141 | ### Usage 142 | ---- 143 | >>> portfolio_accounts_service = ibc_client.portfolio_accounts 144 | >>> portfolio_accounts_services.account_summary( 145 | account_id=ibc_client.account_number 146 | ) 147 | """ 148 | 149 | if not self._has_portfolio_been_called: 150 | self.accounts() 151 | 152 | if not self._has_sub_portfolio_been_called: 153 | self.subaccounts() 154 | 155 | content = self.session.make_request( 156 | method='get', 157 | endpoint=f'/api/portfolio/{account_id}/summary' 158 | ) 159 | 160 | return content 161 | 162 | def account_ledger(self, account_id: str) -> dict: 163 | """Information regarding settled cash, cash balances, 164 | etc. in the account’s base currency and any other cash 165 | balances hold in other currencies. 166 | 167 | ### Overview 168 | --- 169 | `/portfolio/accounts` or `/portfolio/subaccounts` 170 | must be called prior to this endpoint. The list of 171 | supported currencies is available at: 172 | https://www.interactivebrokers.com/en/index.php?f=3185 173 | 174 | ### Returns 175 | ---- 176 | dict: 177 | A `AccountLedger` resource. 178 | 179 | ### Usage 180 | ---- 181 | >>> portfolio_accounts_service = ibc_client.portfolio_accounts 182 | >>> portfolio_accounts_services.account_ledger( 183 | account_id=ibc_client.account_number 184 | ) 185 | """ 186 | 187 | if not self._has_portfolio_been_called: 188 | self.accounts() 189 | 190 | if not self._has_sub_portfolio_been_called: 191 | self.subaccounts() 192 | 193 | content = self.session.make_request( 194 | method='get', 195 | endpoint=f'/api/portfolio/{account_id}/ledger' 196 | ) 197 | 198 | return content 199 | 200 | def account_allocation(self, account_id: str) -> dict: 201 | """Information about the account’s portfolio 202 | by Asset Class, Industry and Category. 203 | 204 | ### Overview 205 | --- 206 | /portfolio/accounts or /portfolio/subaccounts 207 | must be called prior to this endpoint. The list of 208 | supported currencies is available at: 209 | https://www.interactivebrokers.com/en/index.php?f=3185 210 | 211 | ### Returns 212 | ---- 213 | dict: 214 | A `AccountAllocation` resource. 215 | 216 | ### Usage 217 | ---- 218 | >>> portfolio_accounts_service = ibc_client.portfolio_accounts 219 | >>> portfolio_accounts_services.account_allocation( 220 | account_id=ibc_client.account_number 221 | ) 222 | """ 223 | 224 | if not self._has_portfolio_been_called: 225 | self.accounts() 226 | 227 | if not self._has_sub_portfolio_been_called: 228 | self.subaccounts() 229 | 230 | content = self.session.make_request( 231 | method='get', 232 | endpoint=f'/api/portfolio/{account_id}/allocation' 233 | ) 234 | 235 | return content 236 | 237 | def portfolio_allocation(self, account_ids: List[str]) -> dict: 238 | """Similar to /portfolio/{accountId}/allocation but 239 | returns a consolidated view of of all the accounts 240 | returned by /portfolio/accounts 241 | 242 | ### Overview 243 | --- 244 | /portfolio/accounts or /portfolio/subaccounts 245 | must be called prior to this endpoint. 246 | 247 | ### Parameters 248 | ---- 249 | account_ids : List[str] 250 | A list of accounts that you want to be consolidated 251 | into the view. 252 | 253 | ### Returns 254 | ---- 255 | dict: 256 | A consolidated `AccountAllocation` resource. 257 | 258 | ### Usage 259 | ---- 260 | >>> portfolio_accounts_service = ibc_client.portfolio_accounts 261 | >>> portfolio_accounts_services.portfolio_allocation( 262 | account_ids=[ibc_client.account_number] 263 | ) 264 | """ 265 | 266 | if not self._has_portfolio_been_called: 267 | self.accounts() 268 | 269 | if not self._has_sub_portfolio_been_called: 270 | self.subaccounts() 271 | 272 | payload = { 273 | 'acctIds': account_ids 274 | } 275 | 276 | content = self.session.make_request( 277 | method='post', 278 | endpoint=f'/api/portfolio/allocation', 279 | json_payload=payload 280 | ) 281 | 282 | return content 283 | 284 | def portfolio_positions( 285 | self, 286 | account_id: str, 287 | page_id: int = 0, 288 | sort: Union[str, Enum] = None, 289 | direction: Union[str, Enum] = None, 290 | period: str = None 291 | ) -> dict: 292 | """Returns a list of positions for the given account. 293 | The endpoint supports paging, page’s default size is 294 | 30 positions. 295 | 296 | ### Overview 297 | --- 298 | /portfolio/accounts or /portfolio/subaccounts 299 | must be called prior to this endpoint. 300 | 301 | ### Parameters 302 | ---- 303 | account_id : str 304 | The account you want to query for positions. 305 | 306 | page_id : int (optional, Default=0) 307 | The page you want to query. 308 | 309 | sort : Union[str, Enum] (optional, Default=None) 310 | The field on which to sort the data on. 311 | 312 | direction : Union[str, Enum] (optional, Default=None) 313 | The order of the sort, `a` means ascending and 314 | `d` means descending 315 | 316 | period : str (optional, Default=None) 317 | The period for pnl column, can be 1D, 7D, 1M... 318 | 319 | ### Returns 320 | ---- 321 | dict: 322 | A collection of `PortfolioPosition` resources. 323 | 324 | ### Usage 325 | ---- 326 | >>> portfolio_accounts_service = ibc_client.portfolio_accounts 327 | >>> portfolio_accounts_services.portfolio_positions( 328 | account_id=ibc_client.account_number, 329 | page_id=0, 330 | sort=SortFields.BaseUnrealizedPnl, 331 | direction=SortDirection.Descending 332 | ) 333 | """ 334 | 335 | if not self._has_portfolio_been_called: 336 | self.accounts() 337 | 338 | if not self._has_sub_portfolio_been_called: 339 | self.subaccounts() 340 | 341 | if isinstance(sort, Enum): 342 | sort = sort.value 343 | 344 | if isinstance(direction, Enum): 345 | direction = direction.value 346 | 347 | params = { 348 | 'sort': sort, 349 | 'direction': direction, 350 | 'period': period 351 | } 352 | 353 | content = self.session.make_request( 354 | method='get', 355 | endpoint=f'/api/portfolio/{account_id}/positions/{page_id}', 356 | params=params 357 | ) 358 | 359 | return content 360 | 361 | def position_by_contract_id( 362 | self, 363 | account_id: str, 364 | contract_id: str 365 | ) -> dict: 366 | """Returns a list of all positions matching the conid. For portfolio models the 367 | conid could be in more than one model, returning an array with the name of 368 | model it belongs to. 369 | 370 | ### Overview 371 | --- 372 | /portfolio/accounts or /portfolio/subaccounts 373 | must be called prior to this endpoint. 374 | 375 | ### Parameters 376 | ---- 377 | account_id : str 378 | The account you want to query for positions. 379 | 380 | contract_id : str 381 | The contract ID you want to query. 382 | 383 | ### Returns 384 | ---- 385 | dict: 386 | A collection of `PortfolioPosition` resources. 387 | 388 | ### Usage 389 | ---- 390 | >>> portfolio_accounts_service = ibc_client.portfolio_accounts 391 | >>> portfolio_accounts_services.position_by_contract_id( 392 | account_id=ibc_client.account_number, 393 | contract_id='251962528' 394 | ) 395 | """ 396 | 397 | if not self._has_portfolio_been_called: 398 | self.accounts() 399 | 400 | if not self._has_sub_portfolio_been_called: 401 | self.subaccounts() 402 | 403 | content = self.session.make_request( 404 | method='get', 405 | endpoint=f'/api/portfolio/{account_id}/position/{contract_id}' 406 | ) 407 | 408 | return content 409 | 410 | def positions_by_contract_id( 411 | self, 412 | contract_id: str 413 | ) -> dict: 414 | """Returns an object of all positions matching the conid for all 415 | the selected accounts. For portfolio models the conid could be in 416 | more than one model, returning an array with the name of the model 417 | it belongs to. 418 | 419 | ### Overview 420 | --- 421 | /portfolio/accounts or /portfolio/subaccounts 422 | must be called prior to this endpoint. 423 | 424 | ### Parameters 425 | ---- 426 | contract_id : str 427 | The contract ID you want to query. 428 | 429 | ### Returns 430 | ---- 431 | dict: 432 | A collection of `PortfolioPosition` resources. 433 | 434 | ### Usage 435 | ---- 436 | >>> portfolio_accounts_service = ibc_client.portfolio_accounts 437 | >>> portfolio_accounts_services.positions_by_contract_id( 438 | contract_id='251962528' 439 | ) 440 | """ 441 | 442 | if not self._has_portfolio_been_called: 443 | self.accounts() 444 | 445 | if not self._has_sub_portfolio_been_called: 446 | self.subaccounts() 447 | 448 | content = self.session.make_request( 449 | method='get', 450 | endpoint=f'/api/portfolio/positions/{contract_id}' 451 | ) 452 | 453 | return content 454 | 455 | def invalidate_positions_cache( 456 | self, 457 | account_id: str 458 | ) -> Union[dict, None]: 459 | """Invalidates the backend cache of the Portfolio. 460 | 461 | ### Parameters 462 | ---- 463 | account_id : str 464 | The account you want to query for positions. 465 | 466 | ### Returns 467 | ---- 468 | Union[dict, None]: 469 | Nothing is returned if successful. 470 | 471 | ### Usage 472 | ---- 473 | >>> portfolio_accounts_service = ibc_client.portfolio_accounts 474 | >>> portfolio_accounts_services.invalidate_positions_cache() 475 | """ 476 | 477 | content = self.session.make_request( 478 | method='post', 479 | endpoint=f'/api/portfolio/{account_id}/positions/invalidate' 480 | ) 481 | 482 | return content 483 | --------------------------------------------------------------------------------