-
Notifications
You must be signed in to change notification settings - Fork 17
Ibkr Client
The IbkrClient
class provides an interface to the IBKR REST API.
It shares some basic configuration with the IbkrWsClient
. See IBind Configuration - Construction Parameters section for more.
Basic usage of IbkrClient
:
from ibind import IbkrClient
ibkr_client = IbkrClient()
ibkr_client.tickle()
See example "rest_01_basic" for a quick showcase of the basic REST setup.
The IbkrClient
class is a concrete implementation of the abstract RestClient
class.
The RestClient
provides a boilerplate support for common REST methods: GET
, POST
and DELETE
, and handles the request/response processing of all API methods declared in the IbkrClient
.
See API Reference - IbkrClient for more.
Almost all endpoints defined in the IBKR REST API are mapped to IbkrClient
methods. Currently, the endpoint sections that are still NOT mapped are:
- Alerts
- FA Allocation Management
- FYIs and Notifications
Note:
- IBKR endpoints are not documented in this documentation. Please refer to the official IBKR REST API reference for full documentation.
- Endpoints' API implementation is categorised into Python class mixins. See API Reference - IbkrClient for full list of mixins and their methods.
Almost all methods in the IbkrClient
return an instance of Result
object. It is a dataclass containing two fields:
-
data
- the data returned from the operation -
request
- details of the request that resulted in this data, usually containing the URL and arguments used in the REST request to IBKR
This interface gives you additional information as to how IbkrClient
communicated with the IBKR API. The request
field facilitates debugging and ensuring the client library operates according to our expectations.
What this implies, is that to access the returned data of the REST API calls, you need to use the .data
accessor:
authentication_data = IbkrClient.authentication_status().data
There are very few methods that do not return Result
. In all such cases it is explicitly stated in the documentation.
Majority of the IBKR endpoints' mapping is implemented by performing a simple REST request. For example:
class SessionMixin():
def authentication_status(self: 'IbkrClient') -> Result:
return self.post('iserver/auth/status')
...
In several cases, IbkrClient implements additional logic to allow more sophisticated interaction with the IBKR endpoints.
The advanced API methods are:
- security_stocks_by_symbol (contract mixin)
- stock_conid_by_symbol (contract mixin)
- marketdata_history_by_symbol (marketdata mixin)
- marketdata_history_by_symbols (marketdata mixin)
- marketdata_unsubscribe (marketdata mixin)
- place_order (order mixin)
- modify_order (order mixin)
- check_health (session mixin)
IBKR provides access to multiple exchanges, markets and instruments for most symbols. As a result, when searching for a stock by symbol using the trsrv/stocks
endpoint, the API will frequently return more than one contract.
{
"AAPL": [
{
"assetClass": "STK",
"chineseName": "苹果公司",
"contracts": [
{"conid": 265598, "exchange": "NASDAQ", "isUS": True},
{"conid": 38708077, "exchange": "MEXI", "isUS": False},
{"conid": 273982664, "exchange": "EBS", "isUS": False},
],
"name": "APPLE INC",
},
{
"assetClass": "STK",
"chineseName": None,
"contracts": [{"conid": 493546048, "exchange": "LSEETF", "isUS": False}],
"name": "LS 1X AAPL",
},
{
"assetClass": "STK",
"chineseName": "苹果公司",
"contracts": [{"conid": 532640894, "exchange": "AEQLIT", "isUS": False}],
"name": "APPLE INC-CDR",
},
]
}
To facilitate specifying which security we want to operate on, the security_stocks_by_symbol
provides a filtering mechanism. To use it, each stock that you'd want to search for can be expressed as an instance of the StockQuery
dataclass:
@dataclass
class StockQuery():
symbol: str
name_match: Optional[str] = field(default=None) # ie. filter for instrument name
instrument_conditions: Optional[dict] = field(default=None) # ie. filters for instrument fields
contract_conditions: Optional[dict] = field(default=None) # ie. filters for contract fields
When using the StockQuery
, apart from specifying the symbol
to search for, we can provide additional filters that will be used to narrow down our stock search query, eg.:
from ibind import IbkrClient, StockQuery
ibkr_client = IbkrClient()
queries = [StockQuery('AAPL', contract_conditions={'exchange': 'MEXI'})]
stocks = ibkr_client.security_stocks_by_symbol(queries, default_filtering=False)
This will call the trsrv/stocks
endpoint and filter the result to contracts whose exchange is equal to MEXI
:
{
"AAPL": [
{
"assetClass": "STK",
"chineseName": "苹果公司",
"contracts": [{"conid": 38708077, "exchange": "MEXI", "isUS": False}],
"name": "APPLE INC",
}
]
}
Note:
- A
isUS=True
contract condition is applied to all calls of this method by default. Disable by settingsecurity_stocks_by_symbol(..., default_filtering=False)
. - You can call this method with
str
arguments instead ofStockQuery
. These will be interpreted asStockQuery
arguments with no filtering. - You can call this method with one or many arguments, eg.
security_stocks_by_symbol('AAPL')
orsecurity_stocks_by_symbol(['AAPL', 'GOOG'])
- You can mix
str
andStockQuery
arguments, eg.:security_stocks_by_symbol(['AAPL', StockQuery('GOOG')])
- Same rules apply to all other methods using
StockQuery
parameters.
See example "rest_03_stock_querying" which demonstrates various usages of StockQuery.
Most of the IBKR endpoints require us to specify securities' numerical contract IDs (usually shortened to conid) rather than symbols.
ibkr_client.contract_information_by_conid('AAPL') # INVALID
ibkr_client.contract_information_by_conid(265598) # VALID
stock_conid_by_symbol
method facilitates acquiring conids for stock symbols using the same filtering functionality as the security_stocks_by_symbol
.
- Importantly, this method will raise a
RuntimeError
unless all of the filtered stocks return exactly one instrument and one contract. - As a result, exactly one conid can be acquired for each symbol, prompting the users to tweak the
StockQuery
arguments until this is true. - This ensures that there is no ambiguity when searching for conids.
Note:
- This "one-instrument and one-contract" limitation is not present in the
security_stocks_by_symbol
method.
See example "rest_03_stock_querying" for querying conid from a symbol.
Eg.:
from ibind import IbkrClient, StockQuery
stock_queries = [
StockQuery('AAPL', contract_conditions={'exchange': 'MEXI'}),
'HUBS',
StockQuery('GOOG', name_match='ALPHABET INC - CDR')
]
conids = ibkr_client.stock_conid_by_symbol(stock_queries, default_filtering=False).data
print(conids)
# outputs:
{'AAPL': 38708077, 'GOOG': 532638805, 'HUBS': 169544810}
marketdata_history_by_symbol
is a small wrapper around the simple marketdata_history_by_conid
method.
It utilises the stock_conid_by_symbol
to query stock conids automatically before calling the marketdata_history_by_conid
.
marketdata_history_by_symbols
is an extended version of the marketdata_history_by_symbol
method.
For each StockQuery
provided, it queries the marketdata history for the specified symbols in parallel. The results are then cleaned up and unified. Due to this grouping and post-processing, this method returns data directly without the Result
dataclass.
ibkr_client.marketdata_history_by_symbols('AAPL', period='1min', bar='1min', outside_rth=True)
# outputs:
{
"AAPL": [
{
"open": 169.15,
"high": 169.15,
"low": 169.15,
"close": 169.15,
"volume": 0,
"date": datetime.datetime(2024, 4, 25, 19, 56),
},
]
}
ibkr_client.marketdata_history_by_symbols(['AAPL', 'MSFT'], period='1min', bar='1min', outside_rth=True)
# outputs:
{
"AAPL": [
{
"open": 169.15,
"high": 169.15,
"low": 169.15,
"close": 169.15,
"volume": 0,
"date": datetime.datetime(2024, 4, 25, 19, 56),
},
],
"MSFT": [
{
"open": 400.75,
"high": 400.75,
"low": 400.75,
"close": 400.75,
"volume": 0,
"date": datetime.datetime(2024, 4, 25, 19, 56),
},
]
}
Both of these requests took approximately the same amount of time to execute, due to parallel execution.
This method imposes a limit of 5 parallel requests per second due to IBKR rate limiting.
See example "rest_05_marketdata_history" for various example of Market Data History queries and the execution time differences between these.
The /iserver/marketdata/unsubscribe
endpoint allows for only one conid to be unsubscribed from at a time.
marketdata_unsubscribe
method utilises parallel execution to unsubscribe from multiple marketdata history conids at the same time.
From placing order docs:
In some cases the response to an order submission request might not deliver an acknowledgment. Instead, it might contain an "order reply message" - essentially a notice — which must be confirmed via a second request before our order ticket can go to work.
When placing an order using place_order
method, you are to provide a dictionary of question-answer pairs. The method will then perform a back-and-forth message exchange with IBKR servers to reply to the questions and complete the order submission process automatically.
from ibind import IbkrClient, QuestionType
answers = {
QuestionType.PRICE_PERCENTAGE_CONSTRAINT: True,
QuestionType.ORDER_VALUE_LIMIT: True,
"Unforeseen new question": True,
}
ibkr_client = IbkrClient()
ibkr_client.place_order(..., answers, ...)
Observe that:
- Questions-answer pairs are expected to be made of
QuestionType
enum and a boolean. - You can provide a
str
instead ofQuestionType
as key in order to answer questions that have not been previously observed. - You may chose to use
str
in every case, althoughQuestionType
enum is preferred.
If a question is found without an answer or the answer to the question is negative, an exception is raised.
Additionally, make_order_request
utility function is provided to facilitate creating the order_request
dictionary. It ensures all necessary fields are specified when placing an order using place_order
method.
import datetime
from ibind import IbkrClient, make_order_request
conid = '265598' # AAPL
side = 'BUY'
size = 1
order_type = 'MARKET'
price = 100
order_tag = f'my_order-{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}'
order_request = make_order_request(
conid=conid,
side=side,
quantity=size,
order_type=order_type,
price=price,
acct_id=account_id,
coid=order_tag
)
ibkr_client = IbkrClient()
ibkr_client.place_order(order_request, ...)
See example "rest_04_place_order" which demonstrates creating an order_request and placing an order.
modify_order
method follows the same question-handling logic as place_order
.
check_health
method is a wrapper around the tickle
method, providing additional interpretation to its response.
The outcomes of check_health
are:
-
True
- all good. We can call the Gateway and verify that it has a non-competing, authenticated session. -
False
- something is wrong. We either cannot call the Gateway (in which case an additional log is produced) or the session is either competing, disconnected or unauthenticated. -
AttributeError
-tickle
endpoint returned unexpected data.
Learn about the IbkrWsClient.
See any error on this page? Create an Issue and let us know.