Options chains, or more generally a list of derivatives on an underlying index, stock, or future, can be queried from the API. This can be done with either the function reqContractDetails or the function reqSecDefOptParams. Each method has advantages in different situations.
The function reqContractDetails is invoked with an object of the Contract class as an argument and in response full details for matching contracts in the Interactive Brokers’ database is returned. It is the only function in the API which can be used with incompletely-defined Contract objects. From a single reqContractDetails request,
- If there is one matching contract, full details about that contract (including information such as the primary exchange, instrument name, valid order types, and trading hours) will be returned to the contractDetails callback function in EWrapper.
- If there are multiple matching contracts, e.g. options with the same underlying symbol and expiry date, but different strike prices, there will be separate callbacks to contractDetails with details for each individual option contract.
- If there are no matching contracts, the error “200: No security definition has been found” will be returned.
It is important to note that some underlying instruments may have thousands of derivatives. In this case it would not be possible to request a complete chain at once with details of all the derivates on the underlying. Instead its necessary to narrow down the requested option chain as much as possible, for instance by defining an expiry date and only returning contracts expiring on that particular date or month. Requests for option chains using reqContractDetails that use a Contract object without fields such as the expiry, strike, or right defined will be throttled, such that the results will take longer to be returned than if all fields were defined. For that reason it is also best to create a local cache with returned contract details rather than to make the same requests repeatedly. Below is an example of reqContractDetails to query an option chain for AAPL options expiring in June 2020.
Example using reqContractDetails to retrieve an option chain:
from ibapi.client import EClient from ibapi.wrapper import EWrapper from ibapi.contract import Contract from threading import Timer class TestApp(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) def error(self, reqId, errorCode, errorString): print("Error: ", reqId, " ", errorCode, " ", errorString) def nextValidId(self, orderId): self.start() def contractDetails(self, reqId, contractDetails): print("contractDetails: ", reqId, " ", contractDetails, "\n") def contractDetailsEnd(self, reqId): print("\ncontractDetails End\n") def start(self): contract = Contract() contract.symbol = "AAPL" contract.secType = "OPT" contract.exchange = "SMART" contract.currency = "USD" contract.lastTradeDateOrContractMonth = "202006" # June 2020 self.reqContractDetails(1, contract) def stop(self): self.done = True self.disconnect() def main(): app = TestApp() app.nextOrderId = 0 app.connect("127.0.0.1", 7497, 0) Timer(4, app.stop).start() app.run() if __name__ == "__main__": main()
To request a list of the expiries and strikes for derivatives on an underlying instrument, the function reqSecDefOptParams can be used instead.
When the function reqSecDefOptParams is invoked on an underlying instrument, a list of all possible strikes and a list of all possible expiries is returned. reqContractDetails can then be used to query particular individual combinations of strike and expiry. This avoids any possible throttling because reqContractDetails will only be used with Contract objects which define at most one unique contract.
Example using reqSecDefOptParams to retrieve option chain information:
from ibapi.client import EClient from ibapi.wrapper import EWrapper from ibapi.common import SetOfString from ibapi.common import SetOfFloat from threading import Timer class TestApp(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) def error(self, reqId, errorCode, errorString): print("Error: ", reqId, " ", errorCode, " ", errorString) def nextValidId(self, orderId): self.start() def securityDefinitionOptionParameter(self, reqId:int, exchange:str, underlyingConId:int, tradingClass:str, multiplier:str, expirations:SetOfString, strikes:SetOfFloat): print("SecurityDefinitionOptionParameter.", "ReqId:", reqId, "Exchange:", exchange, "Underlying conId:", underlyingConId, "TradingClass:", tradingClass, "Multiplier:", multiplier, "Expirations:", expirations, "Strikes:", str(strikes),"\n") def securityDefinitionOptionParameterEnd(self, reqId:int): print("SecurityDefinitionOptionParameterEnd. ReqId:", reqId) def start(self): # 265598 is the conId (contract ID) for AAPL Nasdaq stock self.reqSecDefOptParams(1, "AAPL", "", "STK", 265598) def stop(self): self.done = True self.disconnect() def main(): app = TestApp() app.nextOrderId = 0 app.connect("127.0.0.1", 7497, 0) Timer(4, app.stop).start() app.run() if __name__ == "__main__": main()
In the example above we invoke reqSecDefOptParams using the symbol of the underlying, underlying security type, and contract ID (or conId) of the underlying. The conId can be found either from right-clicking on a TWS watchlist and choosing Financial Instrument Info -> Details, or by using the function reqContractDetails as in the example above with stock contract.
Next we will discuss receiving portfolio data in the API.
Several functions in the API subscribe to position updates. Each follow the subscribe-and-publish model, where an initial subscription request is made, then TWS will send back a complete list of all positions matching the query, and afterwards continue to send back updates to the list as they occur in real time, until the subscription is cancelled.
- reqAccountUpdates causes both position and account information to be returned for a specified account. It can only be used with a single account at a time.
- reqPositions subscribes to position updates for up to 50 accounts simultaneously.
- reqPositionsMulti subscribes to position updates in an account and/or model portfolio.
It is important to keep in mind that only information about current account positions is available from the TWS API, since that is the only data available in TWS by design. Information about historical account positions can be obtained through flex queries or statements in Account Management. A common point of confusion is with cash balances. Virtual cash positions, which don’t represent real cash balances but are only bookmarks used by forex traders to track trades, are returned with position information and are represented by a forex pair, e.g. EUR.USD. Real cash balances are returned with the account information, discussed next in a single currency, not a pair.
Account information, such as the net liquidity, cash balances in different currencies, and required margin data are returned after calling several different functions:
- reqAccountUpdates, mentioned previously, requests both position and account information for a single account at a time.
- reqAccountSummary can be used to subscribe to account data updates for multiple accounts at once.
- reqAccountSummaryMulti can be used to subscribe to account value updates for a particular account or portfolio model.
The account data requested using these functions matches the data shown in a TWS Account Window. When requesting account data from the API, a complete list of all types of data (account keys) is initially returned, and then updates are sent either if there is a trade, or if the value has changed in three minutes. This corresponds to the same update pattern in the TWS Account Window.
Example using reqAccountUpdates to subscribe to account and portfolio data:
from ibapi.client import EClient from ibapi.wrapper import EWrapper from ibapi.contract import Contract from threading import Timer class TestApp(EWrapper, EClient): def __init__(self): EClient.__init__(self, self) def error(self, reqId, errorCode, errorString): print("Error: ", reqId, " ", errorCode, " ", errorString) def nextValidId(self, orderId): self.start() def updatePortfolio(self, contract: Contract, position: float, marketPrice: float, marketValue: float, averageCost: float, unrealizedPNL: float, realizedPNL: float, accountName: str): print("UpdatePortfolio.", "Symbol:", contract.symbol, "SecType:", contract.secType, "Exchange:", contract.exchange, "Position:", position, "MarketPrice:", marketPrice, "MarketValue:", marketValue, "AverageCost:", averageCost, "UnrealizedPNL:", unrealizedPNL, "RealizedPNL:", realizedPNL, "AccountName:", accountName) def updateAccountValue(self, key: str, val: str, currency: str, accountName: str): print("UpdateAccountValue. Key:", key, "Value:", val, "Currency:", currency, "AccountName:", accountName) def updateAccountTime(self, timeStamp: str): print("UpdateAccountTime. Time:", timeStamp) def accountDownloadEnd(self, accountName: str): print("AccountDownloadEnd. Account:", accountName) def start(self): # Account number can be omitted when using reqAccountUpdates with single account structure self.reqAccountUpdates(True, "") def stop(self): self.reqAccountUpdates(False, "") self.done = True self.disconnect() def main(): app = TestApp() app.nextOrderId = 0 app.connect("127.0.0.1", 7497, 0) Timer(5, app.stop).start() app.run() if __name__ == "__main__": main()
This example demonstrates how reqAccountUpdates starts a subscription to position and account value updates. There are callbacks to updateAccountValue with each account value, followed by callbacks to updatePortfolio with each position. Following the initial callbacks data for all account keys are positions are returned, there is a callback to accountDownloadEnd. Later, there are individual callbacks for items which have changed.
Disclosure: Interactive Brokers
The analysis in this material is provided for information only and is not and should not be construed as an offer to sell or the solicitation of an offer to buy any security. To the extent that this material discusses general market activity, industry or sector trends or other broad-based economic or political conditions, it should not be construed as research or investment advice. To the extent that it includes references to specific securities, commodities, currencies, or other instruments, those references do not constitute a recommendation by IBKR to buy, sell or hold such investments. This material does not and is not intended to take into account the particular financial conditions, investment objectives or requirements of individual customers. Before acting on this material, you should consider whether it is suitable for your particular circumstances and, as necessary, seek professional advice.
Supporting documentation for any claims and statistical information will be provided upon request.
Any stock, options or futures symbols displayed are for illustrative purposes only and are not intended to portray recommendations.
Disclosure: Options Trading
Options involve risk and are not suitable for all investors. For more information read the “Characteristics and Risks of Standardized Options” also known as the options disclosure document (ODD). To receive a copy of the ODD call 312-542-6901 or click here. Multiple leg strategies, including spreads, will incur multiple commission charges.
Disclosure: API Examples Discussed
Throughout the lesson, please keep in mind that the examples discussed are purely for technical demonstration purposes, and do not constitute trading advice. Also, it is important to remember that placing trades in a paper account is recommended before any live trading.