11 Processing IB events

11.1 Processing events in IB-Matlab

IB uses an asynchronous event-based mechanism for sending information to clients. This means that we do not simply send a request to IB and wait for the answer. Instead, we send a request, and when IB is ready it will send us one or more (or zero) events in response. These events carry data, and by analyzing the stored event data we (hopefully) receive the answer that we were waiting for.

These callbacks are constantly being “fired” (i.e., invoked) by asynchronous messages from IB, ranging from temporary market connection losses/reconnections, to error messages and responses to market queries. Some of the events are triggered by user actions (market or portfolio queries, for example), while others are triggered by IB (e.g., disconnection notifications). The full list of IB events (and their data) is documented in the online API documentation.127

Matlab has built-in support for asynchronous events, called callbacks in Matlab jargon.128 Whereas Matlab callbacks are normally used in conjunction with Graphical User Interfaces (GUI), they can also be used with IB-Matlab, which automatically converts all the Java events received from IB into Matlab callbacks.

There are two types of callbacks that you can use in IB-Matlab:

Generic callback – this is a catch-all callback function that is triggered upon any IB event. Within this callback, you would need to write some code to distinguish between the different event types in order to process the events’ data. A skeleton for this is given below. The parameter controlling this callback in IBMatlab is called CallbackFunction.

Specific callback – this is a callback function that is only triggered when the specific event type is received from IB. Since the event type is known, you can process its event data more easily than in the generic callback case. However, you would need to specify a different specific callback for each of the event types that you wish to process.

The parameters controlling the specific callbacks in IBMatlab are called CallbackXXX, where XXX is the name of the IB event (the only exception to this rule is CallbackMessage, which handles the IB error event – the reason is that this event sends informational messages in addition to errors,129 so IB’s event name is misleading in this specific case).

When you specify any callback function to IBMatlab, either the generic kind (CallbackFunction) or a specific kind (CallbackXXX), the command action does not even need to be related to the callback (for example, you can set CallbackExecDetails together with Action='query').

data = IBMatlab('action','query', ..., ...
'CallbackExecDetails',@IBMatlab_CallbackExecDetails);

where IBMatlab_CallbackExecDetails() is a Matlab function created by you that accepts two input arguments (which are automatically populated in run-time):

ibConnector – the Java connector object that is described in §15 below

eventData – a Matlab struct that contains the event’s data in separate fields130

An example for specifying a Matlab callback function is:

function IBMatlab_CallbackExecDetails(ibConnector, eventData)

% do the callback processing here

end

You can pass external data to your callback functions using the callback cell-array format. For example, to pass two extra data values:131

callbackDetails = {@IBMatlab_CallbackExecDetails, 123, 'abc'};
IBMatlab(
'action','query',..., 'CallbackExecDetails',callbackDetails);

function IBMatlab_CallbackExecDetails(ibConn,eventData,extra1,extra2)

% do the callback processing here

end

When you specify any callback function to IBMatlab, you only need to set it once, in any IBMatlab command. Unlike most IBMatlab parameters, which are not remembered across IBMatlab commands and need to be re-specified, callbacks do not need to be re-specified. They are remembered from the moment they are first set, until such time as Matlab exits or the callback parameter is changed.132

To reset a callback (i.e., remove the callback invocation), simply set the callback parameter value to [] (empty square brackets) or '' (empty string):

data = IBMatlab('action','query', ..., 'CallbackExecDetails','');

Matlab callbacks are invoked even if you use the Java connector object (see §15) for requesting data from IB. This is actually very useful: we can use the connector object to send a request to IB, and then process the results in a Matlab callback function.

Using Matlab callbacks with the Java connector object can be used, for example, to implement combo trades,133 as an alternative to the built-in mechanism described in §9.5 above. In this case, separate contracts are created for the separate combo legs, then submitted to IB via the Java connector’s reqContractDetails() method, awaiting the returned IDs via the Matlab callback to the ContractDetails event (see CallbackContractDetails in the table below). Once the IDs for all the legs are received, com.ib.client.ComboLeg objects134 are created. The completed order can then be submitted to IB for trading via the Java connector’s placeOrder() method. All this may appear a bit difficult to implement, but in fact can be achieved in only a few dozen lines of code. This example illustrates how Matlab callbacks can seamlessly interact with the underlying Java connector’s methods.

Here is the list of callback events in IBMatlab (for information about any IB event, see https://interactivebrokers.github.io/tws-api/interfaceIBApi_1_1EWrapper.html):

IBMatlab parameter

IB Event

Triggered by IBMatlab?

Called when?

CallbackAccountDownloadEnd

accountDownloadEnd

Yes

in response to account queries, after all UpdateAccount events were sent, to indicate end of data

CallbackAccountSummary

accountSummary

Yes

for every single field in the summary data when the account data is requested (see §4.1)

CallbackAccountSummaryEnd

accountSummaryEnd

Yes

indicates end of data after all AccountSummary events are sent

CallbackBondContractDetails

bondContractDetails

Yes

in response to market queries; not really used in IBMatlab

CallbackCommissionReport

commissionReport

Yes

immediately after a trade execution, or when requesting executions (see §12.1 below)

CallbackConnectionClosed

connectionClosed

Yes

when IB-Matlab loses its connection (or reconnects) to TWS/Gateway

CallbackContractDetails

contractDetails

Yes

in response to market queries; used in IBMatlab only to get the tick value

CallbackContractDetailsEnd

contractDetailsEnd

Yes

indicates end of data after all ContractDetails events were sent

CallbackCurrentTime

currentTime

Yes

numerous times during regular work; returns the current server system time

CallbackDeltaNeutralValidation

deltaNeutralValidation

No

in response to a Delta-Neutral (DN) RFQ

CallbackExecDetails

execDetails

Yes

whenever an order is partially or fully filled, or in response to the Java connector’s reqExecutions()

CallbackExecDetailsEnd

execDetailsEnd

Yes

indicates end of data after all the ExecDetails events were sent

CallbackFundamentalData

fundamentalData

Yes

in response to requesting fundamental data for a security

CallbackHistoricalData

historicalData

Yes

in response to a historical data request, for each of the result bars separately

CallbackManagedAccounts

managedAccounts

Yes

when a successful connection is made to a Financial Advisor account, or in response to calling the Java connector’s reqManagedAccts()

CallbackMarketDataType

marketDataType

No

when the market type is set to Frozen or RealTime, to announce the switch, or in response to calling the Java connector’s reqMarketDataType()

CallbackMessage

error

Yes

whenever IB wishes to send the user an error or informational message. See §14.1 below.

CallbackNextValidId

nextValidId

No

after connecting to IB

CallbackOpenOrder

openOrder

Yes

in response to a user query for open orders, for each open order

CallbackOpenOrderEnd

openOrderEnd

Yes

after all OpenOrder events have been sent for a request, to indicate end of data

CallbackOrderStatus

orderStatus

Yes

in response to a user query for open orders (for each open order), or when an order’s status changes

CallbackPosition

position

Yes

in response to a user query for portfolio positions (for each position in the portfolio)

CallbackPositionEnd

positionEnd

Yes

indicates end of data after all Position events have been sent

CallbackTickPrice

tickPrice

Yes

in response to a market query, for price fields (e.g., bid)

CallbackTickSize

tickSize

Yes

in response to a market query, for size fields (e.g., bidSize)

CallbackTickString

tickString

Yes

in response to a market query, for string fields (e.g., lastTimestamp)

CallbackTickGeneric

tickGeneric

Yes

in response to a query with a GenericTickList param

CallbackTickEFP

tickEFP

No

when the market data changes. Values are updated immediately with no delay

CallbackTickOptionComputation

tickOptionComputation

No

when the market of an option or its underlier moves. TWS’s option model volatilities, prices, and deltas, along with the present value of dividends expected on that underlier are received

CallbackTickSnapshotEnd

tickSnapshotEnd

Yes

when all events in response to a snapshot query request have been sent, to indicate end of data

CallbackRealtimeBar

realtimeBar

Yes

in response to a realtime bars request, for each bar separately

CallbackReceiveFA

receiveFA

No

in response to calling the Java connector’s requestFA()

CallbackScannerData

scannerData

Yes

in response to a user query for scanner data, for each result row

CallbackScannerDataEnd

scannerDataEnd

Yes

indicates end of data after the last scannerData event was sent

CallbackScannerParameters

scannerParameters

Yes

in response to a user query for scanner parameters XML

CallbackUpdateAccountTime

updateAccountTime

Yes

together with the Update-AccountValue callbacks, to report on the event time

CallbackUpdateAccountValue

updateAccountValue

Yes

for every single property in the list of account properties, when the account data is requested (see §4) or updated

CallbackUpdateMktDepth

updateMktDepth

Yes

when market depth has changed

CallbackUpdateMktDepthL2

updateMktDepthL2

Yes

when the Level II market depth has changed

CallbackUpdateNewsBulletin

updateNewsBulletin

No

for each new bulletin if the client has subscribed by calling the Java connector’s reqNewsBulletins()

CallbackUpdatePortfolio

updatePortfolio

Yes

when account updates are requested or occur

11.2 Example – using CallbackExecDetails to track executions

The execDetails event is triggered whenever an order is fully or partially executed. Let us trap this event and send the execution information into a CSV file for later use in Excel (also see §12 below):

orderId = IBMatlab('action','BUY', 'symbol','GOOG', 'quantity',1, ...
'limitPrice',600, ...
'CallbackExecDetails',@IBMatlab_CallbackExecDetails);

Where the function IBMatlab_CallbackExecDetails is defined as follows (for example, in a file called IBMatlab_CallbackExecDetails.m):135

%https://interactivebrokers.com/php/whiteLabel/Interoperability/Socket_Client_Java/java_properties.htm

function IBMatlab_CallbackExecDetails(ibConnector, eventData, varargin)

% Extract the basic event data components 136
contractData = eventData.contract;
executionData = eventData.execution;

% Example of extracting data from the contract object:
symbol = eventData.contract.symbol;
secType = eventData.contract.secType;
% ... several other contract data field available – see above webpage

% Example of extracting data from the execution object:
orderId = eventData.execution.orderId;
execId = eventData.execution.execId;
time = eventData.execution.time;
exchange = eventData.execution.exchange;
side = eventData.execution.side;
shares = eventData.execution.shares;
price = eventData.execution.price;
permId = eventData.execution.permId;
liquidation = eventData.execution.liquidation;
cumQty = eventData.execution.cumQty;
avgPrice = eventData.execution.avgPrice;
% ... several other execution data field available – see above webpage

% Convert the data elements into a comma-separated string
csvline = sprintf('%s,%d,%s,%d,%d,%f\n', time, orderId, symbol, ...
shares, cumQty, price);

% Now append this comma-separated string to the CSV file
fid = fopen(
'executions.csv', 'at'); % 'at' = append text
fprintf(fid, csvline);
fclose(fid);

end % IBMatlab_CallbackExecDetails

11.3 Example – using CallbackTickGeneric to check if a security is shortable

In this example, we attach a user callback function to tickGeneric events in order to check whether a security is shortable137 (also see §7.1 above).

Note: according to IB,138Generic Tick Tags cannot be specified if you elect to use the Snapshot market data subscription”, and therefore we need to use the streaming-quotes mechanism, so QuotesNumber>1:

orderId = IBMatlab('action','Query', 'symbol','GOOG', ...
'GenericTicklist','236', 'QuotesNumber',2, ...
'CallbackTickGeneric',@IBMatlab_CallbackTickGeneric);

where the function IBMatlab_CallbackTickGeneric is defined as follows:139

function IBMatlab_CallbackTickGeneric(ibConnector, eventData, varargin)

% Only check the shortable tick type =46, according to
%
https://interactivebrokers.github.io/tws-api/tick_types.html#shortable

if eventData.field == 46 % 46=Shortable (see footnote below)

% Get this event's tickerId (=orderId as returned from the
% original IBMatlab command)
tickerId = eventData.tickerId;

% Get the corresponding shortable value
shortableValue = eventData.generic; % (see footnote below)

% Now check whether the security is shortable or not
title = sprintf('Shortable info for request %d', tickerId);
if (shortableValue > 2.5) % 3.0
msgbox('>1000 shares available for a short',title,'help');
elseif (shortableValue > 1.5) % 2.0
msgbox('This contract will be available for short sale if shares can be located', title, 'warn');
elseif (shortableValue > 0.5) % 1.0
msgbox('Not available for short sale', title, 'warn');
else
msg=sprintf(
'Unknown shortable value: %g',shortableValue);
msgbox(msg, title,
'error');
end
end % if shortable tickType

end % IBMatlab_CallbackTickGeneric

Note that in this particular example we could also have simply used the streaming quotes data, instead of using the callback:

>> dataS = IBMatlab('action','query','symbol','GOOG','quotesNumber',-1);
>> shortableValue = dataS.data.shortable;
% =3 for GOOG

11.4 Example – using CallbackContractDetails to get a contract’s full options chain

In this example, we attach a user callback function to contractDetails events in order to receive the full list of LocalSymbols and associated contract properties of an underlying security’s options chain.140

As noted in §5.4, it is not possible to receive the entire list of option prices in a single command; each market price requires a separate request with a specific LocalSymbol.

However, we can use the contractDetails event to extract the full list of option LocalSymbols in a single command. This relies on the fact that when the Right and Strike parameters of an option security are empty, IB returns the full list of contracts matching the other specifications.

We first define our callback function for the event:

function IBCallbackContractDetails(ibConnector, eventData)

contract = eventData.contractDetails.summary;

fprintf([contract.localSymbol '\t' ...
contract.secType
'\t' ...
contract.symbol
'\t' ...
contract.expiry
'\t' ...
contract.right
'\t' ...
contract.multiplier
'\t' ...
num2str(contract.strike)
'\n']);

end % IBCallbackContractDetails

Now we ask IB for the current market data of the futures options for Light Sweet Crude Oil (CL) with empty Right and Strike. We can safely ignore the IB warning about ambiguous or missing security definition:

>> data=IBMatlab('action','query', 'symbol','CL', 'secType','FOP',...
'exchange','NYMEX', 'currency','USD', ...
'expiry','201306', 'right','', 'strike',0.0, ...
'CallbackContractDetails',@IBCallbackContractDetails)

[API.msg2] The contract description specified for CL is ambiguous;
you must specify the multiplier. {286356018, 200}

LOM3 P6650 FOP CL 20130516 P 1000 66.5
LOM3 P8900 FOP CL 20130516 P 1000 89
LOM3 P11150 FOP CL 20130516 P 1000 111.5
LOM3 C6400 FOP CL 20130516 C 1000 64
LOM3 C8650 FOP CL 20130516 C 1000 86.5
LOM3 C10900 FOP CL 20130516 C 1000 109
LOM3 C6650 FOP CL 20130516 C 1000 66.5
LOM3 C8900 FOP CL 20130516 C 1000 89
... (over 400 different contracts)

The returned data struct will naturally contain empty market data, but its contractDetails field will contain useful data about the requested security: 141

>> data
data =
reqId: 286356019
reqTime: '30-Apr-2013 12:55:28'
dataTime: '30-Apr-2013 12:55:31'
dataTimestamp: 735354.538562743
lastEventTime: 735354.538562743
ticker: 'CL'
bidPrice: -1
askPrice: -1
open: -1
close: -1
low: -1
high: -1
lastPrice: -1
volume: -1
tick: 0.01
contract: [1x1 struct]
contractDetails: [1x1 struct]

>> data.contract % these are the details for only one of the options
ans =
conId: 50318947
symbol: 'CL'
secType: 'FOP'
expiry: '20130516'
strike: 111.5
right: 'P'
multiplier: '1000'
exchange: 'NYMEX'
currency: 'USD'
localSymbol: 'LOM3 P11150'
...

>> data.contractDetails
ans =
summary: [1x1 struct]
marketName: 'LO'
tradingClass: 'LO'
minTick: 0.01
priceMagnifier: 1
orderTypes: [1x205 char]
validExchanges: 'NYMEX'
underConId: 43635367
longName: 'Light Sweet Crude Oil'
contractMonth: '201306'
industry: []
category: []
subcategory: []
timeZoneId: 'EST'
tradingHours: '20130430:1800-1715;20130501:1800-1715'
liquidHours: '20130430:0000-1715,1800-2359;20130501:0000-1715,1800-2359'
...

11.5 Example – using CallbackUpdateMktDepth for realtime order-book GUI update

In this example, we wish to update a real-time GUI display of the order-book (at least the top few rows of the book), based on Level II data.

As noted in §7.3 above, market-depth events may be sent at a very high rate from the IB server, and so it is not feasible or useful to update the Matlab GUI for each update. Instead, we update the GUI with the latest data at a steady rate of 2 Hz (twice a second). This can be achieved in two different ways: one alternative is to set-up a periodic timer that will run our GUI-update callback every 0.5 secs, which will call IBMatlab(…,’QuotesNumber’,-1) to fetch the latest data and update the GUI.

Another alternative, shown here below, is to attach a user callback function to updateMktDepth142 and updateMktDepthL2143 events, updating an internal data struct, but only updating the GUI if 0.5 secs or more have passed since the last GUI update:

% IBMatlab_MktDepth - sample Market-Depth usage function
function IBMatlab_MktDepth(varargin)

% Initialize data
numRows = 5;
depthData = cell(numRows,6);
lastUpdateTime = -1;
GUI_refresh_period = 0.5 * 1/24/60/60;
% =0.5 secs

% Prepare the GUI
hFig = figure('Name','IB-Matlab market-depth example', ...
'NumberTitle','off','CloseReq',@figClosedCallback,...
'Menubar','none', 'Toolbar','none', ...
'Resize','off', 'Pos',[100,200,520,170]);
color = get(hFig,
'Color');
headers = {
'Ask exch.','Ask size','Ask price', ...
'Bid price','Bid size','Bid exch.'};
formats = {
'char','numeric','long', 'long','numeric','char'};
hTable = uitable(
'Parent',hFig, 'Pos',[10,40,500,120], ...
'Data',depthData, ...
'ColumnName',headers, 'ColumnFormat',formats);
hButton = uicontrol(
'Parent',hFig, 'Pos',[50,10,60,20], ...
'String','Start', 'Callback',@buttonCallback);
hLabel1 = uicontrol(
'Parent',hFig, 'Pos',[120,10,100,17], ...
'Style','text', 'String','Last updated:', ...
'Horizontal','right', 'Background',color);
hLabelTime = uicontrol(
'Parent',hFig, 'Pos',[225,10,100,17], ...
'Style','text', 'String','(not yet)', ...
'Horizontal','left', 'Background',color);

% Send the market-depth request to IB using IB-Matlab
contractParams = {'symbol','EUR', 'localSymbol','EUR.USD', ...
'secType','cash', 'exchange','idealpro', ...
'NumberOfRows',5, varargin{:}};
reqId = IBMatlab(
'action','query', 'QuotesNumber',inf, ...
contractParams{:},
...
'CallbackUpdateMktDepth', @mktDepthCallbackFcn,...
'CallbackUpdateMktDepthL2',@mktDepthCallbackFcn);

% Figure close callback function - stop the market-depth streaming
function figClosedCallback(hFig, eventData)
% Delete the figure window and stop any pending data streaming
delete(hFig);
IBMatlab(
'action','query', contractParams{:}, 'QuotesNumber',0);
end % figClosedCallback

% Start/stop button callback function
function buttonCallback(hButton, eventData)
currentString = get(hButton,
'String');
if strcmp(currentString,'Start')
set(hButton,
'String','Stop');
else
set(hButton,'String','Start');
end
end % buttonCallback

% Callback functions to handle IB Market Depth update events
function mktDepthCallbackFcn(ibConnObj, eventData)

% Ensure that it's the correct MktDepth event
if eventData.tickerId == reqId

% Get the updated data row
% Note: Java indices start at 0, Matlab starts at 1
row = eventData.position + 1;

% Get the size & price data fields from the event's data
size = eventData.size;
price = eventData.price;

% Prevent extra LS digits in uitable display
price = single(price + 0.00000001);

% Exchange (marketMaker) data is only available in L2:
try
exchange = eventData.marketMaker;
catch
exchange = '';
end

% Update the internal data table
if eventData.side == 0 % ask
if eventData.operation == 0 % insert
depthData(row+1:end,1:3) = depthData(row:end-1,1:3);
depthData(row,1:3) = {exchange, size, price};
elseif eventData.operation == 1 % update
depthData(row,1:3) = {exchange, size, price};
elseif eventData.operation == 2 % delete
depthData(row:end-1,1:3) = depthData(row+1:end,1:3);
depthData(end,1:3) = {[],[],[]};
else
% should never happen!
end
else % bid (same as ask but the data columns are reversed)
if eventData.operation == 0 % insert
depthData(row+1:end,4:6) = depthData(row:end-1,4:6);
depthData(row,4:6) = {price, size, exchange};
elseif eventData.operation == 1 % update
depthData(row,4:6) = {price, size, exchange};
elseif eventData.operation == 2 % delete
depthData(row:end-1,4:6) = depthData(row+1:end,4:6);
depthData(end,4:6) = {[],[],[]};
else
% should never happen!
end
end

% Update the GUI if more than 0.5 secs have passed and
% the <Stop> button was not pressed
isStopped = strcmp(get(hButton,'String'),'Start');
if now - lastUpdateTime > GUI_refresh_period && ~isStopped
set(hTable,
'Data',depthData);
set(hLabelTime,
'String',datestr(now,'HH:MM:SS'));
lastUpdateTime = now;
end
end
end % mktDepthCallbackFcn

end % IBMatlab_MktDepth

???


127 https://interactivebrokers.github.io/tws-api/interfaceIBApi_1_1EWrapper.html

128 http://www.mathworks.com/help/matlab/creating_guis/writing-code-for-callbacks.html

129 http://interactivebrokers.github.io/tws-api/error_handling.html

130 Until IBMatlab v2.00, eventData contained Java objects whose fields could be inspected via the struct function e.g. struct(eventData.contract). Starting in v2.01, such objects are regular Matlab structs. In IB-Matlab v2.19 the struct field names changed, by removing the m_ field prefix (e.g. eventData.contract.m_symbol à .symbol).

131 http://www.mathworks.com/help/matlab/creating_guis/writing-code-for-callbacks.html#brqow8p

132 It is not an error to re-specify the callbacks in each IBMatlab command, it is simply useless and makes the code less readable

133 http://interactivebrokers.github.io/tws-api/basic_orders.html#combolimit

134 http://interactivebrokers.github.io/tws-api/classIBApi_1_1ComboLeg.html; http://interactivebrokers.com/php/whiteLabel/Interoperability/Socket_Client_Java/java_properties.htm

135 This file can be downloaded from: http://UndocumentedMatlab.com/files/IBMatlab_CallbackExecDetails.m

136 Until IBMatlab v2.00, eventData contained Java objects whose fields could be inspected via the struct function e.g. struct(eventData.contract). Starting in v2.01, such objects are regular Matlab structs. In IB-Matlab v2.19 the struct field names changed, by removing the m_ field prefix (e.g. eventData.contract.m_symbol à .symbol).

137 https://interactivebrokers.github.io/tws-api/tick_types.html#shortable. Additional details: https://ibkr.info/it/article/2024

138 https://investors.interactivebrokers.com/php/apiguide/interoperability/generictick.htm

139 This code is downloadable from: http://UndocumentedMatlab.com/files/IBMatlab_CallbackTickGeneric.m. Depending on your TWS/IBG installation version, tickGeneric’s eventData double-precision value field is called either “generic” or “value”.

140 A synchronous alternative for retrieving the options chain is explained in §5.4 above

141 Until IBMatlab v2.00, eventData contained Java objects whose fields could be inspected via the struct function e.g. struct(eventData.contract). Starting in v2.01, such objects are regular Matlab structs. In IB-Matlab v2.19 the struct field names changed, by removing the m_ field prefix (e.g. eventData.contract.m_symbol à .symbol).

142 http://interactivebrokers.github.io/tws-api/interfaceIBApi_1_1EWrapper.html#ab0d68c4cf7093f105095d72dd7e7a912

143 http://interactivebrokers.github.io/tws-api/interfaceIBApi_1_1EWrapper.html#ad8afb71cd866b423a84555f500992968