Skip to content

Commit 9a4dc7b

Browse files
mvbergpilwon
authored andcommitted
Add support for reqHistoricalTicks & reqTickByTickData (#127)
* fix assert typos * add reqHistoricalTicks (requires TWS build 968+) https://interactivebrokers.github.io/tws-api/historical_time_and_sales.html * adds support for reqTickByTickData (time & sales) https://interactivebrokers.github.io/tws-api/tick_data.html
1 parent 11fecbd commit 9a4dc7b

7 files changed

+404
-2
lines changed

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ ib.connect()
6969
.cancelPositionsMulti(requestId)
7070
.cancelRealTimeBars(tickerId)
7171
.cancelScannerSubscription(tickerId)
72+
.cancelTickByTickData(tickerId)
7273
.exerciseOptions(tickerId, contract, exerciseAction, exerciseQuantity, account, override)
7374
.placeOrder(id, contract, order)
7475
.replaceFA(faDataType, xml)
@@ -83,6 +84,8 @@ ib.connect()
8384
.reqFundamentalData(reqId, contract, reportType)
8485
.reqGlobalCancel()
8586
.reqHistoricalData(tickerId, contract, endDateTime, durationStr, barSizeSetting, whatToShow, useRTH, formatDate, keepUpToDate)
87+
.reqHistoricalTicks(tickerId, contract, startDateTime, endDateTime, numberOfTicks, whatToShow, useRTH, ignoreSize)
88+
.reqTickByTickData(tickerId, contract, tickType, numberOfTicks, ignoreSize)
8689
.reqIds(numIds)
8790
.reqManagedAccts()
8891
.reqMarketDataType(marketDataType)
@@ -134,6 +137,12 @@ ib.connect()
134137
.on('execDetailsEnd', function (reqId))
135138
.on('fundamentalData', function (reqId, data))
136139
.on('historicalData', function (reqId, date, open, high, low, close, volume, count, WAP, hasGaps))
140+
.on('historicalTickTradeData', (reqId, timestamp, mask, price, size, exchange, specialConditions))
141+
.on('historicalTickBidAskData', (reqId, timestamp, mask, priceBid, priceAsk, sizeBid, sizeAsk))
142+
.on('historicalTickMidPointData', (reqId, timestamp, price, size))
143+
.on('tickByTickAllLast', reqId, tickType, time, price, size, { pastLimit, unreported }, exchange, specialConditions)
144+
.on('tickByTickBidAsk', reqId, time, bidPrice, askPrice, bidSize, askSize, { bidPastLow, askPastHigh })
145+
.on('tickByTickMidPoint', reqId, time, midPoint))
137146
.on('managedAccounts', function (accountsList))
138147
.on('marketDataType', function (reqId, marketDataType))
139148
.on('nextValidId', function (orderId))

examples/reqHistTickData.js

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
var _ = require('lodash');
2+
var chalk = require('chalk');
3+
4+
var ib = new(require('..'))({
5+
// clientId: 0,
6+
// host: '127.0.0.1',
7+
// port: 4003
8+
}).on('error', (err) => {
9+
console.error(chalk.red(err.message));
10+
}).on('historicalTickTradeData', (...data) => {
11+
console.log(
12+
'%s %s%d %s%s %s%d %s%d %s%d %s%s %s%s',
13+
chalk.cyan('[historicalTickTradeData]'),
14+
chalk.bold('reqId='), data[0],
15+
chalk.bold('timestamp='), data[1],
16+
chalk.bold('mask='), data[2],
17+
chalk.bold('price='), data[3],
18+
chalk.bold('size='), data[4],
19+
chalk.bold('exchange='), data[5],
20+
chalk.bold('specialConditions='), data[6],
21+
);
22+
}).on('historicalTickBidAskData', (...data) => {
23+
console.log(
24+
'%s %s%d %s%s %s%d %s%d %s%d %s%d %s%d',
25+
chalk.cyan('[historicalTickBidAskData]'),
26+
chalk.bold('reqId='), data[0],
27+
chalk.bold('timestamp='), data[1],
28+
chalk.bold('mask='), data[2],
29+
chalk.bold('priceBid='), data[3],
30+
chalk.bold('priceAsk='), data[4],
31+
chalk.bold('sizeBid='), data[5],
32+
chalk.bold('sizeAsk='), data[6],
33+
);
34+
}).on('historicalTickMidPointData', (...data) => {
35+
console.log(
36+
'%s %s%d %s%s %s%d %s%d',
37+
chalk.cyan('[historicalTickMidPointData]'),
38+
chalk.bold('reqId='), data[0],
39+
chalk.bold('timestamp='), data[1],
40+
chalk.bold('price='), data[2],
41+
chalk.bold('size='), data[3]
42+
);
43+
});
44+
45+
46+
ib.connect();
47+
48+
// tickerId, contract, startDateTime, endDateTime, numberOfTicks, whatToShow, useRTH, ignoreSize
49+
ib.reqHistoricalTicks(1, ib.contract.stock('SPY', 'SMART', 'USD'), '20180711 12:00:00', null, 10, 'TRADES', 1, false);
50+
ib.reqHistoricalTicks(2, ib.contract.stock('SPY', 'SMART', 'USD'), '20180711 12:00:00', null, 10, 'BID_ASK', 1, false);
51+
ib.reqHistoricalTicks(3, ib.contract.stock('SPY', 'SMART', 'USD'), '20180711 12:00:00', null, 10, 'MIDPOINT', 1, false);
52+
53+
54+
ib.on('historicalTickDataEnd', (reqId) => {
55+
console.log(
56+
'%s %s%d',
57+
chalk.cyan('[historicalTickDataEnd]'),
58+
chalk.bold('reqId='), reqId
59+
);
60+
});

examples/reqTickByTickData.js

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
var _ = require('lodash');
2+
var chalk = require('chalk');
3+
4+
var ib = new(require('..'))({
5+
// clientId: 0,
6+
// host: '127.0.0.1',
7+
// port: 4003
8+
}).on('error', (err) => {
9+
console.error(chalk.red(err.message));
10+
}).on('tickByTickAllLast', (...data) => {
11+
console.log(
12+
'%s %s%d %s%d %s%d %s%d %s%s %s%s %s%s',
13+
chalk.cyan('[tickByTickAllLast]'),
14+
chalk.bold('reqId='), data[0],
15+
chalk.bold('tickType='), data[1],
16+
chalk.bold('timestamp='), data[2],
17+
chalk.bold('price='), data[3],
18+
chalk.bold('size='), data[4],
19+
chalk.bold('attributes='), JSON.stringify(data[5]),
20+
chalk.bold('exchange='), data[6],
21+
chalk.bold('specialConditions='), data[7],
22+
);
23+
}).on('tickByTickBidAsk', (...data) => {
24+
console.log(
25+
'%s %s%d %s%s %s%d %s%d %s%d %s%d %s%s',
26+
chalk.cyan('[tickByTickBidAsk]'),
27+
chalk.bold('reqId='), data[0],
28+
chalk.bold('timestamp='), data[1],
29+
chalk.bold('priceBid='), data[2],
30+
chalk.bold('priceAsk='), data[3],
31+
chalk.bold('sizeBid='), data[4],
32+
chalk.bold('sizeAsk='), data[5],
33+
chalk.bold('attributes='), JSON.stringify(data[6]),
34+
);
35+
}).on('tickByTickMidPoint', (...data) => {
36+
console.log(
37+
'%s %s%d %s%s %s%d',
38+
chalk.cyan('[tickByTickMidPoint]'),
39+
chalk.bold('reqId='), data[0],
40+
chalk.bold('timestamp='), data[1],
41+
chalk.bold('midpoint='), data[2],
42+
);
43+
});
44+
45+
ib.connect();
46+
47+
// tickerId, contract, tick type (BidAsk, Last, AllLast, MidPoint), numberOfTicks, ignoreSize
48+
ib.reqTickByTickData(1, ib.contract.stock('SPY', 'SMART', 'USD'), 'BidAsk', 0, false)
49+
//ib.reqTickByTickData(1, ib.contract.stock('SPY', 'SMART', 'USD'), 'AllLast', 0, false)
50+
//ib.reqTickByTickData(1, ib.contract.stock('SPY', 'SMART', 'USD'), 'Last', 0, false)
51+
//ib.reqTickByTickData(1, ib.contract.stock('SPY', 'SMART', 'USD'), 'MidPoint', 0, false)
52+
53+
setTimeout(() => {
54+
console.log(
55+
'%s %s%d',
56+
chalk.cyan('sending [cancelTickByTickData]'),
57+
chalk.bold('reqId='), 1);
58+
59+
ib.cancelTickByTickData(1);
60+
ib.disconnect();
61+
}, 5000);
62+
63+
//
64+
// [tickByTickBidAsk] reqId=1 timestamp=1531426792 priceBid=279.15 priceAsk=279.18 sizeBid=200 sizeAsk=3100 attributes={"bidPastLow":false,"askPastHigh":false}
65+
// [tickByTickAllLast] reqId=1 tickType=2 timestamp=1531426753 price=279.15 size=200 attributes={"pastLimit":false,"unreported":false} exchange=BATS specialConditions= T
66+
// [tickByTickAllLast] reqId=1 tickType=1 timestamp=1531426672 price=279.15 size=213 attributes={"pastLimit":false,"unreported":false} exchange=BATS specialConditions= FT
67+
// [tickByTickMidPoint] reqId=1 timestamp=1531426697 midpoint=279.16
68+
//

lib/constants.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ exports.INCOMING = {
7575
HISTORICAL_NEWS_END : 87,
7676
HEAD_TIMESTAMP : 88,
7777
HISTOGRAM_DATA : 89,
78+
HISTORICAL_TICKS : 96,
79+
HISTORICAL_TICKS_BID_ASK : 97,
80+
HISTORICAL_TICKS_LAST : 98,
81+
TICK_BY_TICK : 99
7882
};
7983

8084
// outgoing msg id's
@@ -143,7 +147,10 @@ exports.OUTGOING = {
143147
REQ_HISTORICAL_NEWS : 86,
144148
REQ_HEAD_TIMESTAMP : 87,
145149
REQ_HISTOGRAM_DATA : 88,
146-
CANCEL_HISTOGRAM_DATA : 89
150+
CANCEL_HISTOGRAM_DATA : 89,
151+
REQ_HISTORICAL_TICKS : 96,
152+
REQ_TICK_BY_TICK_DATA : 97,
153+
CANCEL_TICK_BY_TICK_DATA : 98
147154
};
148155

149156
exports.MIN_SERVER_VER = {
@@ -219,7 +226,8 @@ exports.MIN_SERVER_VER = {
219226
MARKET_RULES : 126,
220227
PNL : 127,
221228
NEWS_QUERY_ORIGINS : 128,
222-
SERVER_VER_UNREALIZED_PNL : 129
229+
SERVER_VER_UNREALIZED_PNL : 129,
230+
MIN_SERVER_VER_HISTORICAL_TICKS: 130
223231
};
224232

225233

lib/incoming.js

+122
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,128 @@ Incoming.prototype._HISTORICAL_DATA = function () {
420420
this._emit('historicalData', reqId, completedIndicator, -1, -1, -1, -1, -1, -1, -1, false);
421421
};
422422

423+
Incoming.prototype._HISTORICAL_TICKS_LAST = function() {
424+
var reqId = this.dequeueInt();
425+
var tickCount = this.dequeueInt();
426+
427+
var date;
428+
var mask;
429+
var price;
430+
var size;
431+
var exchange;
432+
var specialConditions;
433+
434+
while (tickCount--) {
435+
date = this.dequeue();
436+
mask = this.dequeueInt();
437+
price = this.dequeueFloat();
438+
size = this.dequeueInt();
439+
exchange = this.dequeue();
440+
specialConditions = this.dequeue();
441+
442+
this._emit('historicalTickTradeData', reqId, date, mask, price, size, exchange, specialConditions);
443+
}
444+
445+
var done = this.dequeueBool();
446+
447+
if (done) {
448+
this._emit('historicalTickDataEnd', reqId);
449+
}
450+
};
451+
452+
Incoming.prototype._HISTORICAL_TICKS_BID_ASK = function() {
453+
var reqId = this.dequeueInt();
454+
var tickCount = this.dequeueInt();
455+
456+
var date;
457+
var mask;
458+
var priceBid;
459+
var sizeBid;
460+
var priceAsk;
461+
var sizeAsk;
462+
463+
while (tickCount--) {
464+
date = this.dequeue();
465+
mask = this.dequeueInt();
466+
priceBid = this.dequeueFloat();
467+
sizeBid = this.dequeueInt();
468+
priceAsk = this.dequeueFloat();
469+
sizeAsk = this.dequeueInt();
470+
this._emit('historicalTickBidAskData', reqId, date, mask, priceBid, priceAsk, sizeBid, sizeAsk);
471+
}
472+
473+
var done = this.dequeueBool();
474+
475+
if (done) {
476+
this._emit('historicalTickDataEnd', reqId);
477+
}
478+
};
479+
480+
Incoming.prototype._HISTORICAL_TICKS = function() { // MIDPOINT (size appears to always be zero)
481+
var reqId = this.dequeueInt();
482+
var tickCount = this.dequeueInt();
483+
484+
var date;
485+
var price;
486+
var size;
487+
488+
while (tickCount--) {
489+
date = this.dequeue();
490+
this.dequeueInt();//for consistency
491+
price = this.dequeueFloat();
492+
size = this.dequeueInt();
493+
494+
this._emit('historicalTickMidPointData', reqId, date, price, size);
495+
}
496+
497+
var done = this.dequeueBool();
498+
499+
if (done) {
500+
this._emit('historicalTickDataEnd', reqId);
501+
}
502+
};
503+
504+
Incoming.prototype._TICK_BY_TICK = function () {
505+
var reqId = this.dequeueInt();
506+
var tickType = this.dequeueInt();
507+
var time = this.dequeue();
508+
509+
var mask;
510+
511+
switch (tickType){
512+
case 0: // None
513+
break;
514+
case 1: // Last
515+
case 2: // Alllast
516+
var price = this.dequeueFloat();
517+
var size = this.dequeueInt();
518+
mask = this.dequeueInt();
519+
var pastLimit = (mask & (1 << 0)) !== 0;
520+
var unreported = (mask & (1 << 1)) !== 0;
521+
var exchange = this.dequeue();
522+
var specialConditions = this.dequeue();
523+
524+
this._emit('tickByTickAllLast', reqId, tickType, time, price, size, { pastLimit, unreported }, exchange, specialConditions);
525+
break;
526+
case 3: // BidAsk
527+
var bidPrice = this.dequeueFloat();
528+
var askPrice = this.dequeueFloat();
529+
var bidSize = this.dequeueInt();
530+
var askSize = this.dequeueInt();
531+
mask = this.dequeueInt();
532+
var bidPastLow = (mask & (1 << 0)) !== 0;
533+
var askPastHigh = (mask & (1 << 1)) !== 0;
534+
535+
this._emit('tickByTickBidAsk', reqId, time, bidPrice, askPrice, bidSize, askSize, { bidPastLow, askPastHigh });
536+
break;
537+
case 4: // MidPoint
538+
var midPoint = this.dequeueFloat();
539+
540+
this._emit('tickByTickMidPoint', reqId, time, midPoint);
541+
break;
542+
}
543+
};
544+
423545
Incoming.prototype._HEAD_TIMESTAMP = function() {
424546
var reqId = this.dequeueInt();
425547
var headTimestamp = this.dequeue();

lib/index.js

+38
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,44 @@ IB.prototype.reqHistoricalData = function (tickerId, contract, endDateTime, dura
313313
return this;
314314
};
315315

316+
IB.prototype.reqHistoricalTicks = function (tickerId, contract, startDateTime, endDateTime, numberOfTicks,
317+
whatToShow, useRTH, ignoreSize){
318+
assert(_.isNumber(tickerId), '"tickerId" must be an integer - ' + tickerId);
319+
assert(_.isPlainObject(contract), '"contract" must be a plain object - ' + contract);
320+
if (startDateTime && endDateTime || !startDateTime && !endDateTime) {
321+
assert.fail('specify one of "startDateTime" or "endDateTime" (as a string) but not both - ' + startDateTime + ':' + endDateTime);
322+
}
323+
assert(_.isNumber(numberOfTicks), '"numberOfTicks" must be a number - ' + numberOfTicks);
324+
assert(_.isString(whatToShow), '"whatToShow" must be a string - ' + whatToShow);
325+
assert(_.isNumber(useRTH), '"useRTH" must be an integer - ' + useRTH);
326+
assert(_.isBoolean(ignoreSize), '"ignoreSize" must be an boolean - ' + ignoreSize);
327+
328+
this._send('reqHistoricalTicks', tickerId, contract, startDateTime, endDateTime, numberOfTicks,
329+
whatToShow, useRTH, ignoreSize);
330+
331+
return this;
332+
};
333+
334+
IB.prototype.reqTickByTickData = function (tickerId, contract, tickType, numberOfTicks, ignoreSize) {
335+
assert(_.isNumber(tickerId), '"tickerId" must be an integer - ' + tickerId);
336+
assert(_.isPlainObject(contract), '"contract" must be a plain object - ' + contract);
337+
assert(_.isString(tickType), '"tickType" must be a string - ' + tickType);
338+
assert(_.isNumber(numberOfTicks), '"numberOfTicks" must be a number - ' + numberOfTicks);
339+
assert(_.isBoolean(ignoreSize), '"ignoreSize" must be an boolean - ' + ignoreSize);
340+
341+
this._send('reqTickByTickData', tickerId, contract, tickType, numberOfTicks, ignoreSize);
342+
343+
return this;
344+
};
345+
346+
IB.prototype.cancelTickByTickData = function (tickerId) {
347+
assert(_.isNumber(tickerId), '"tickerId" must be an integer - ' + tickerId);
348+
349+
this._send('cancelTickByTickData', tickerId);
350+
351+
return this;
352+
};
353+
316354
IB.prototype.reqIds = function (numIds) {
317355
assert(_.isNumber(numIds), '"numIds" must be an integer - ' + numIds);
318356

0 commit comments

Comments
 (0)