Receiving Market Data
Real time data is accessed in the API using a subscribe-and-publish model that is also used with other functionality such as retrieving position or account values. After a streaming market data subscription request for a particular contract in IB's database is made, a continuous stream of market data is returned by TWS and separated to different functions in EWrapper depending on the data type (integer vs decimal vs string). Since the subscribe-and-publish model is inherently asynchronous, the returned data is linked to the initial request using a numerical label specified in the request called the request ID (reqID).
After subscribing to the ticker stream, your API client will receive the data supplied by the TWS API server via several methods. In your API client code, you will need to implement these methods to manipulate the data relayed back to the client. Notice how market data callbacks such as IBApi.EWrapper.tickPrice and IBApi.EWrapper.tickSize methods contain the request id with which the response can be mapped to its originating request. By default, there are certain 'default tick types' that are always returned. These include fields such as the bid price, ask price, bid size, ask size, etc. There are additional types of data available with the real time data that are requested by specifying certain "generic tick types" in the market data request. For more information, Generic Tick Types.
When IBApi::EWrapper::tickPrice and IBApi::EWrapper::tickSize are reported as -1, this indicates that there is no data currently available. Most commonly this occurs when requesting data from markets that are closed. It can also occur for infrequently trading instruments which do not have open bids or offers at that time of the request. To receive frozen quotes (the last available bid/ask recorded by the system) invoke the function IBApi.EClient.reqMarketDataType with argument 2. Alternatively, to receive "delayed frozen" data from tickers without holding market data subscriptions, specify a market data type of 4. Frozen data is exclusive to default tick types- Generic Tick Types are not available- and requires market data subscriptions.
For bid, ask, and last data, there will always be a IBApi::EWrapper::tickSize callback following each IBApi::EWrapper::tickPrice. This is important because with combo contracts, an actively trading contract can have a price of zero. In this case it will have a positive IBApi::EWrapper::tickSize value. IBApi::EWrapper::tickSize is also invoked every time there is a change in the size of the last traded price. For that reason, there will be duplicate tickSize events when both the price and size of a trade changes.
-
public class EWrapperImpl : EWrapper
{
...
public virtual void tickPrice(int tickerId, int field, double price, TickAttrib attribs)
{
Console.WriteLine("Tick Price. Ticker Id:" + tickerId + ", Field: " + field + ", Price: " + Util.DoubleMaxString(price) + ", CanAutoExecute: " + attribs.CanAutoExecute +
", PastLimit: " + attribs.PastLimit + ", PreOpen: " + attribs.PreOpen);
}
- The boolean value canAutoExecute in tickPrice is intended to indicate that a quote is available for immediate execution. The boolean value pastLimit indicates whether the bid price is lower than the day's lowest value or the ask price is higher than the highest ask.
- Beginning in API v973.04 the pre-open attribute is available in the API. This attribute indicates that bid/ask quotes released by futures exchanges are in the pre-open period.
public virtual void tickSize(int tickerId, int field, decimal size)
{
Console.WriteLine("Tick Size. Ticker Id:" + tickerId + ", Field: " + field + ", Size: " + Util.DecimalMaxString(size));
}
....
public virtual void tickString(int tickerId, int tickType, string value)
{
Console.WriteLine("Tick string. Ticker Id:" + tickerId + ", Type: " + tickType + ", Value: " + value);
}
....
public virtual void tickGeneric(int tickerId, int field, double value)
{
Console.WriteLine("Tick Generic. Ticker Id:" + tickerId + ", Field: " + field + ", Value: " + Util.DoubleMaxString(value));
}
-
public class EWrapperImpl implements EWrapper {
...
@Override
public void tickPrice(int tickerId, int field, double price, TickAttrib attribs) {
System.out.println("Tick Price: " + EWrapperMsgGenerator.tickPrice( tickerId, field, price, attribs));
}
- The boolean value canAutoExecute in tickPrice is intended to indicate that a quote is available for immediate execution. The boolean value pastLimit indicates whether the bid price is lower than the day's lowest value or the ask price is higher than the highest ask.
- Beginning in API v973.04 the pre-open attribute is available in the API. This attribute indicates that bid/ask quotes released by futures exchanges are in the pre-open period.
@Override
public void tickSize(int tickerId, int field, Decimal size) {
System.out.println("Tick Size: " + EWrapperMsgGenerator.tickSize( tickerId, field, size));
}
...
@Override
public void tickString(int tickerId, int tickType, String value) {
System.out.println("Tick String: " + EWrapperMsgGenerator.tickString(tickerId, tickType, value));
}
...
@Override
public void tickGeneric(int tickerId, int tickType, double value) {
System.out.println("Tick Generic: " + EWrapperMsgGenerator.tickGeneric(tickerId, tickType, value));
}
-
Public Class EWrapperImpl
Implements EWrapper
...
Public Sub tickPrice(tickerId As Integer, field As Integer, price As Double, attribs As TickAttrib) Implements IBApi.EWrapper.tickPrice
Console.WriteLine("TickPrice - TickerId [" & CStr(tickerId) & "] Field [" & TickType.getField(field) & "] Price [" & Util.DoubleMaxString(price) & "] PreOpen [" & attribs.PreOpen & "]")
End Sub
- The boolean value canAutoExecute in tickPrice is intended to indicate that a quote is available for immediate execution. The boolean value pastLimit indicates whether the bid price is lower than the day's lowest value or the ask price is higher than the highest ask.
- Beginning in API v973.04 the pre-open attribute is available in the API. This attribute indicates that bid/ask quotes released by futures exchanges are in the pre-open period.
Public Sub tickSize(tickerId As Integer, field As Integer, size As Decimal) Implements IBApi.EWrapper.tickSize
Console.WriteLine("Tick Size. Ticker Id:" & CStr(tickerId) & ", Field: " & TickType.getField(field) & ", Size: " & Util.DecimalMaxString(size))
End Sub
....
Public Sub tickString(tickerId As Integer, field As Integer, value As String) Implements IBApi.EWrapper.tickString
Console.WriteLine("Tick string. Ticker Id:" & CStr(tickerId) & ", Type: " & TickType.getField(field) & ", Value: " & value)
End Sub
....
Public Sub tickGeneric(tickerId As Integer, field As Integer, value As Double) Implements IBApi.EWrapper.tickGeneric
Console.WriteLine("Tick Generic. Ticker Id:" & tickerId & ", Field: " & field & ", Value: " & Util.DoubleMaxString(value))
End Sub
-
class TestCppClient : public EWrapper
{
...
void TestCppClient::tickPrice( TickerId tickerId, TickType field, double price, const TickAttrib& attribs) {
printf( "Tick Price. Ticker Id: %ld, Field: %d, Price: %s, CanAutoExecute: %d, PastLimit: %d, PreOpen: %d\n", tickerId, (int)field, Utils::doubleMaxString(price).c_str(), attribs.canAutoExecute, attribs.pastLimit, attribs.preOpen);
}
- The boolean value canAutoExecute in tickPrice is intended to indicate that a quote is available for immediate execution. The boolean value pastLimit indicates whether the bid price is lower than the day's lowest value or the ask price is higher than the highest ask.
- Beginning in API v973.04 the pre-open attribute is available in the API. This attribute indicates that bid/ask quotes released by futures exchanges are in the pre-open period.
void TestCppClient::tickSize( TickerId tickerId, TickType field, Decimal size) {
printf( "Tick Size. Ticker Id: %ld, Field: %d, Size: %s\n", tickerId, (int)field, DecimalFunctions::decimalStringToDisplay(size).c_str());
}
....
void TestCppClient::tickString(TickerId tickerId, TickType tickType, const std::string& value) {
printf( "Tick String. Ticker Id: %ld, Type: %d, Value: %s\n", tickerId, (int)tickType, value.c_str());
}
....
void TestCppClient::tickGeneric(TickerId tickerId, TickType tickType, double value) {
printf( "Tick Generic. Ticker Id: %ld, Type: %d, Value: %s\n", tickerId, (int)tickType, Utils::doubleMaxString(value).c_str());
}
-
1 class TestWrapper(wrapper.EWrapper):
...
1 def tickPrice(self, reqId: TickerId, tickType: TickType, price: float,
3 super().tickPrice(reqId, tickType, price, attrib)
4 print(
"TickPrice. TickerId:", reqId,
"tickType:", tickType,
5 "Price:", floatMaxString(price),
"CanAutoExecute:", attrib.canAutoExecute,
6 "PastLimit:", attrib.pastLimit, end=
' ')
7 if tickType == TickTypeEnum.BID
or tickType == TickTypeEnum.ASK:
8 print(
"PreOpen:", attrib.preOpen)
- The boolean value canAutoExecute in tickPrice is intended to indicate that a quote is available for immediate execution. The boolean value pastLimit indicates whether the bid price is lower than the day's lowest value or the ask price is higher than the highest ask.
- Beginning in API v973.04 the pre-open attribute is available in the API. This attribute indicates that bid/ask quotes released by futures exchanges are in the pre-open period.
1 def tickSize(self, reqId: TickerId, tickType: TickType, size: Decimal):
2 super().tickSize(reqId, tickType, size)
3 print(
"TickSize. TickerId:", reqId,
"TickType:", tickType,
"Size: ", decimalMaxString(size))
....
1 def tickString(self, reqId: TickerId, tickType: TickType, value: str):
2 super().tickString(reqId, tickType, value)
3 print(
"TickString. TickerId:", reqId,
"Type:", tickType,
"Value:", value)
....
1 def tickGeneric(self, reqId: TickerId, tickType: TickType, value: float):
2 super().tickGeneric(reqId, tickType, value)
3 print(
"TickGeneric. TickerId:", reqId,
"TickType:", tickType,
"Value:", floatMaxString(value))
Exchange Component Mapping
A market data request is able to return data from multiple exchanges. Beginning in TWS/IBG v963 and API v973.02, after a market data request is made for an instrument covered by market data subscriptions, a message will be sent to function IBApi::EWrapper::tickReqParams with information about 'minTick', BBO exchange mapping, and available snapshot permissions.
-
public void tickReqParams(int tickerId, double minTick, string bboExchange, int snapshotPermissions)
{
Console.WriteLine("id={0} minTick = {1} bboExchange = {2} snapshotPermissions = {3}", tickerId, Util.DoubleMaxString(minTick), bboExchange, Util.IntMaxString(snapshotPermissions));
BboExchange = bboExchange;
}
-
@Override
public void tickReqParams(int tickerId, double minTick, String bboExchange, int snapshotPermissions) {
System.out.println("Tick req params: " + EWrapperMsgGenerator.tickReqParams(tickerId, minTick, bboExchange, snapshotPermissions));
}
-
Public Sub tickReqParams(tickerId As Integer, minTick As Double, bboExchange As String, snapshotPermissions As Integer) Implements EWrapper.tickReqParams
Console.WriteLine("id={0} minTick = {1} bboExchange = {2} snapshotPermissions = {3}", tickerId, Util.DoubleMaxString(minTick), bboExchange, Util.IntMaxString(snapshotPermissions))
Me.BboExchange = bboExchange
End Sub
-
void TestCppClient::tickReqParams(int tickerId, double minTick, const std::string& bboExchange, int snapshotPermissions) {
printf("tickerId: %d, minTick: %s, bboExchange: %s, snapshotPermissions: %u\n", tickerId, Utils::doubleMaxString(minTick).c_str(), bboExchange.c_str(), snapshotPermissions);
m_bboExchange = bboExchange;
}
-
1 def tickReqParams(self, tickerId:int, minTick:float,
2 bboExchange:str, snapshotPermissions:int):
3 super().tickReqParams(tickerId, minTick, bboExchange, snapshotPermissions)
4 print(
"TickReqParams. TickerId:", tickerId,
"MinTick:", floatMaxString(minTick),
5 "BboExchange:", bboExchange,
"SnapshotPermissions:", intMaxString(snapshotPermissions))
The exchange mapping identifier bboExchange will be a symbol such as "a6" which can be used to decode the single letter exchange abbreviations returned to the bidExch, askExch, and lastExch fields by invoking the function IBApi::EClient::reqSmartComponents. More information about Component Exchanges.
The minTick returned to tickReqParams indicates the minimum increment in market data values returned to the API. It can differ from the minTick value in the ContractDetails class. For instance, combos will often have a minimum increment of 0.01 for market data and a minTick of 0.05 for order placement.
Re-Routing CFDs
IB does not provide market data for certain types of instruments, such as stock CFDs and forex CFDs. If a stock CFD or forex CFD is entered into a TWS watchlist, TWS will automatically display market data for the underlying ticker and show a 'U' icon next to the instrument name to indicate that the data is for the underlying instrument.
From the API, when level 1 or level 2 market data is requested for a stock CFD or a forex CFD, a callback is made to the functions IBApi::EWrapper::rerouteMktDataReq or IBApi::EWrapper::rerouteMktDepthReq respectively with details about the underlying instrument in IB's database which does have market data.
-
public void rerouteMktDataReq(int reqId, int conId, string exchange)
{
Console.WriteLine("Re-route market data request. Req Id: {0}, ConId: {1}, Exchange: {2}", reqId, conId, exchange);
}
... public void rerouteMktDepthReq(int reqId, int conId, string exchange)
{
Console.WriteLine("Re-route market depth request. Req Id: {0}, ConId: {1}, Exchange: {2}", reqId, conId, exchange);
}
-
@Override
public void rerouteMktDataReq(int reqId, int conId, String exchange) {
System.out.println(EWrapperMsgGenerator.rerouteMktDataReq(reqId, conId, exchange));
}
... @Override
public void rerouteMktDepthReq(int reqId, int conId, String exchange) {
System.out.println(EWrapperMsgGenerator.rerouteMktDepthReq(reqId, conId, exchange));
}
-
Public Sub rerouteMktDataReq(reqId As Integer, conId As Integer, exchange As String) Implements IBApi.EWrapper.rerouteMktDataReq
Console.WriteLine("Re-route market data request. Req Id: {0}, Con Id: {1}, Exchange: {2}", reqId, conId, exchange)
End Sub
... Public Sub rerouteMktDepthReq(reqId As Integer, conId As Integer, exchange As String) Implements IBApi.EWrapper.rerouteMktDepthReq
Console.WriteLine("Re-route market depth request. Req Id: {0}, Con Id: {1}, Exchange: {2}", reqId, conId, exchange)
End Sub
-
void TestCppClient::rerouteMktDataReq(int reqId, int conid, const std::string& exchange) {
printf( "Re-route market data request. ReqId: %d, ConId: %d, Exchange: %s\n", reqId, conid, exchange.c_str());
}
... void TestCppClient::rerouteMktDepthReq(int reqId, int conid, const std::string& exchange) {
printf( "Re-route market depth request. ReqId: %d, ConId: %d, Exchange: %s\n", reqId, conid, exchange.c_str());
}
-
1 def rerouteMktDataReq(self, reqId: int, conId: int, exchange: str):
2 super().rerouteMktDataReq(reqId, conId, exchange)
3 print(
"Re-route market data request. ReqId:", reqId,
"ConId:", conId,
"Exchange:", exchange)
... 1 def rerouteMktDepthReq(self, reqId: int, conId: int, exchange: str):
2 super().rerouteMktDataReq(reqId, conId, exchange)
3 print(
"Re-route market depth request. ReqId:", reqId,
"ConId:", conId,
"Exchange:", exchange)