stoqey / ibkr Goto Github PK
View Code? Open in Web Editor NEWInteractive Brokers wrapper ๐ฉ
License: MIT License
Interactive Brokers wrapper ๐ฉ
License: MIT License
Currently the ibkr writes a lot of text with chalk on the console, which cannot be deactivated. Better would be an output with debug or a deactivation of the console log.
Hi,
Great work on this project! Thank you for making our lives easier!
I have a tiny bug to report, it seems that the "right" element of a contract is not captured for options. It's supposed to be "C" or "P" not sure if it's an IBKR issue or a library issue.
{
contract: {
symbol: 'TSLA',
secType: 'OPT',
lastTradeDateOrContractMonth: '20220617',
strike: 900,
right: undefined,
exchange: 'SMART',
currency: 'USD',
localSymbol: 'TSLA 220617P00900000',
tradingClass: 'TSLA',
conId: 442955799,
multiplier: 100,
primaryExch: ''
}
}
Hello, @ceddybi, good man!
I was just in the beginning of writing a npapi plugin based on TWS c++ client, when I finally found this, and it is so much better.
Did you run this client live?
Is it somewhat tested and reliable?
How do you plan to keep it up to date with the official protocol/clients?
I'll def be contributing if I end up using it for my algos.
Thank you!
If an invalid combination of parameters or invalid strings are passed to the reqHistoricalData function, there is a deadlook.
Example
import ibkr, { IBKRConnection, IbkrEvents, HistoricalData } from '@stoqey/ibkr';
const ibkrEvents = IbkrEvents.Instance;
const main = async () => {
// @ts-ignore
const connectionStatus = await ibkr({ port: 7496, host: '127.0.0.1' });
HistoricalData.Instance;
const symbol = 'aapl'
const data = await HistoricalData.Instance.reqHistoricalData({
symbol,
endDateTime: '',
durationStr: '1 W',
barSizeSetting: '1 day',
//whatToShow: 'TRADES',
whatToShow: 'YIELD_BID'
})
console.log(data)
IBKRConnection.Instance.disconnectIBKR()
};
main();
The log writes the error in debug mode, but there is no error from the Promise
Log
error Historical Market Data Service error message:No historical market data for AAPL/STK@NASDAQBBO BidYield 1d
I have not yet checked if there are exceptions in other functions.
this is a great project!
is there some document to start IB_PORT, IB_HOST?
Hello! Thanks for building such a great and thorough library. I'm seeing this bug when executing any order on my paper trading account:
const ib = new IBApi({port: PORT})
ib.connect(clientId)
ib.on(EventName.openOrder, (orderId, contract, order, orderState) => {
console.log(orderState)
})
=> {
status: 'PreSubmitted',
initMarginBefore: 1.7976931348623157e+308,
maintMarginBefore: 1.7976931348623157e+308,
equityWithLoanBefore: 1.7976931348623157e+308,
initMarginChange: 1.7976931348623157e+308,
maintMarginChange: 1.7976931348623157e+308,
equityWithLoanChange: 1.7976931348623157e+308,
initMarginAfter: 1.7976931348623157e+308,
maintMarginAfter: 1.7976931348623157e+308,
equityWithLoanAfter: 1.7976931348623157e+308,
commission: 1.7976931348623157e+308,
minCommission: 1.7976931348623157e+308,
maxCommission: 1.7976931348623157e+308,
commissionCurrency: '',
warningText: ''
}
If I was being charged fees totalling more than the GDP of every nation in the world, I'd be a bit annoyed ๐ I'm not sure what the issue is.
My real account is a standard individual's account with the Pro tier. Orders executed both in the API and manually produce actual fees of USD$1.00 (in the real account; not sure how to see fees in Trader Workstation to verify what the paper trading account's fees are.)
For clients with multiple accounts (eg separate IRA and brokerage accounts), the AccountSummary
class will only return data from one of their accounts, when issuing init
or getAccountSummary
Reason:
IBKR callback accountSummary
is called for each account, yet the result overwrites each time so only the data from the last account will be returned.
eg in src/account/AccountSummary
:
// Record values from here
ib.on('accountSummary', (reqId, account, tag, value, currency) => {
self.tickerId = reqId;
self.AccountId = account;
self.accountSummary.AccountId = account;
self.accountSummary[tag] = value; // set the account value
self.accountSummary.Currency = currency; // always set the account currency
// log('accountSummaryEnd', { account, tag, value, });
});
// Return values from here
ib.once('accountSummaryEnd', () => {
const {AccountId = 'unknown', tickerId, accountReady, accountSummary} = self;
log('accountSummaryEnd', {AccountId, tickerId, accountReady});
ib.cancelAccountSummary(tickerId);
publishDataToTopic({
topic: IBKREVENTS.ON_ACCOUNT_SUMMARY,
data: accountSummary,
});
self.accountReady = true;
});
Solution:
make self.accountSummary
an object with accountId
as keys. Store data from each accountSummary
callback under the right key (so adding a [tag] key under that accountId
). Only resolve promise of getAccountSummary
or call publishDataToTopic
after you receive an accountSummaryEnd
since only then will you have received data from all accountId
s for the currently logged in user.
Here is a gist with a corrected AccountSummary.ts
. Note that I also first-letter-lowercased the returned tags to be more JS-style compliant, so you will need to make some changes to the interface file and some other files also that use accountId
which has been replaced by accountIds
. For instance you will need to change Portfolios so it takes an accountId
as an input so you can return a portfolio per account.
https://gist.github.com/bayareacoder/0f173afe96f2a601e21d6f581396f739#file-accountsummary-ts
Hi there, and thanks for your work on this library! I was trying it out for saving some of my IB data, and it looks like the comboLegsDescrip
isn't always present for ContractObject
s. Would you be able to make it an option parameter in the interface?
Hi there,
is there a way to transmit a conditional order? could you provide an example?
I'm referring to https://interactivebrokers.github.io/tws-api/order_conditions.html
Once again, thanks for the awesome api wrapper
Hi there, first off I'd like to say amazing work on that wrapper so far. Being new to IBKR and their API I was wondering if there was any way to search securities via their API, and if there is does that wrapper implement it ?
Expected: Should add startDate and endDate, when requesting historical data
Current: IT fetches market data as 4 days ago from current date
It seems to me there are a few issues inPortfolio.ts
to retrieve an account's portfolio:
init
issues reqAccountUpdates
and listens for updatePortfolio
.updatePortfolio
(which is a callback for each position in the portfolio), there is a nested call to self.getPortfolios
which by itself then issues reqPositions
with separate listeners.Per: http://interactivebrokers.github.io/tws-api/account_updates.html, reqAccountUpdates
will already receive updatePortfolio
for each position in the account, and then a accountDownloadEnd
when all data is received (for which there is no listener in the current code). So it seems to me there is no need to also use reqPositions
.
It's not clear to me how to make a distinction between an initial snapshot and later updates to the portfolio as there is only 1 event topic IBKREVENTS.PORTFOLIO
. So how to know if it's a full portfolio download or an update with only a change?
I also wouldn't think you need separate listeners for filled orders, since I'd expect that would trigger updatePortfolio
callback automatically if a subscription is setup, see below.
Per #66 accountId
should also be an input to Portfolio class to support clients with multiple accounts so you can retrieve the portfolio per account (probably passing accountId
in the Portfolio constructor so you get a portfolio instance per accountId will be easiest, and then can do a subscription per accountId), see next point.
You should be able to either only get a full snapshot of the portfolio, or do a subscription so that after an initial snapshot you continue to get portfolio updates, and then be able to stop that subscription at a later time.
PS
I would call the class Portfolio
instead of Portfolios
if implemented per above (a portfolio assumes there are multiple positions).
I'm working on a re-write.
The code snippet given in the README.MD is giving several different errors. Tried with a fresh NodeJS TS project.
I spent quite some time tinkering aroud, first it said cannot use import statement outside a module
. Here's the error:
(node:11784) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
E:\work\robinhood\exp\exp1ts\dist\app.js:18
import ibkr, { IbkrEvents } from '@stoqey/ibkr';
^^^^^^
SyntaxError: Cannot use import statement outside a module
Later when you change the type
to module
, it says ibkr()
is not a function! Here's the error:
await ibkr();
^
TypeError: ibkr is not a function
Even if we decide to change the import to regular require()
statement, tsc
starts to complain about using TLA/top-level-awaits, because the file is not a module. The error given:
> tsc && node dist/app.js
src/app.ts:32:1 - error TS1375: 'await' expressions are only allowed at the top level of a file when that file is a module, but this file has no imports or exports. Consider adding an empty 'export {}' to make this file a module.
32 await ibkr();
After some tinkering around, I managed to make it work with the following code, which is not very pretty:
const ibkr = require('@stoqey/ibkr');
const { AccountSummary, IBKREVENTS, IbkrEvents, PortFolioUpdate, getContractDetails } = ibkr;
const ibkrEvents = IbkrEvents.Instance;
const IB_HOST = "localhost";
const IB_PORT = 7496;
ibkr.default({ port: IB_PORT, host: IB_HOST })
.then((v) => {
const accountId = AccountSummary.Instance.accountSummary.AccountId;
const totalCashValue = AccountSummary.Instance.accountSummary.TotalCashValue;
console.log("acc id: " + accountId)
console.log("cashVal: " + totalCashValue)
console.log(v)
})
.catch((e) => {
console.log("Connection Failed.");
console.log(e);
});
Can you please explain or please create a more detailed get started guide?
Also in my code, or in general, how can I handle/catch and sudden TWS/API connection lost? This is critical, because at the moment the program just ends.
Thanks for your support!
node: 14.15.1
os: win10pro 64bit
These modules are listed as dev dependencies, but they're not:
ibkr/src/connection/IBKRConnection.ts
Line 2 in 02a527a
Lines 9 to 11 in 02a527a
Hello,
I have a stock position for ticker X and a open option position for the same ticker X.
When I retrieve my portfolios, I can only see one.
Do you have any suggestions?
Cheers
Real time price updates
Line 521 in 689839b
should we change the API to allow pass any contract?
I have an immediate need to trade OPT and FOP
I have the following code:
const ibkr = require('@stoqey/ibkr').default
const {
AccountSummary,
IBKREVENTS,
IbkrEvents,
HistoricalData,
PriceUpdates,
Portfolios,
MosaicScanner,
} = require('@stoqey/ibkr')
const ibkrEvents = IbkrEvents.Instance
const getPriceUpdates = async (params) => {
try {
const connected = await ibkr()
console.log('Connected to IBKR API : ',connected)
PriceUpdates.Instance
ibkrEvents.on(IBKREVENTS.ON_PRICE_UPDATES, (priceUpdates) => {
// use the price updates here
console.log(priceUpdates)
})
ibkrEvents.emit(IBKREVENTS.SUBSCRIBE_PRICE_UPDATES, {
symbol: 'QQQ',
})
} catch (e) {
console.error(e)
}
}
When I call getPriceUpdates i get the following:
Connected to IBKR API : true
{ symbol: 'QQQ' }
No price updates or any data is logged...Any help?
ibkr/src/connection/IBKRConnection.ts
Line 135 in 665072e
The process will currently exit on a disconnect event which is not desirable for my use-case. Not sure what the best approach would be but the main one that comes to mind is to leave this as the default behavior and allow an option on initialization to change this behavior.
I want to get the high, low, open, close, etc prices for my portfolio. I didn't see a good way to do this with @stoqey/ibkr
, so I wrote the following self-contained prototype with @stoqey/ib
to figure out how thinks work on a lower level. It grabs the price data once for AAPL and prints it to the screen. I'd like to do something similar with ibkr
, but the current implementation of IBKREVENTS.SUBSCRIBE_PRICE_UPDATES
only seems to emit events for a single element of the price. Can you give me a tip for how to use ibrk
and tie into it's low-level interface when necessary?
import IB from "@stoqey/ib";
type Exchange =
| "SMART"
/** Forex cash exchange */
| "IDEALPRO"
/** Briefing Trader News */
| "BRF";
type PrimaryExchange = "IDEALPRO" | "NASDAQ" | "NYSE";
type SecType = "CASH" | "FUT" | "NEWS" | "OPT" | "STK";
interface MktData {
/** The last available closing price for the previous day. For US Equities, we use corporate action processing to get the closing price, so the close price is adjusted to reflect forward and reverse splits and cash and stock dividends. (9) */
readonly closePrice?: number;
/** Delayed bid price. (66) */
readonly delayedBid?: number;
/** Delayed ask price. (67) */
readonly delayedAsk?: number;
/** Delayed last traded price. (68) */
readonly delayedLast?: number;
/** Delayed highest price of the day. (72) */
readonly delayedHighPrice?: number;
/** Delayed lowest price of the day. (73) */
readonly delayedLowPrice?: number;
/** Delayed traded volume of the day. (74) */
readonly delayedVolume?: number;
/** The prior day's closing price. (75) */
readonly delayedClose?: number;
}
type ResultHandlerCallbackFunction = (requestId: number, args: readonly any[]) => void;
interface ResultHandlerCallbackMap {
[event: string]: {
[requestId: string]: ResultHandlerCallbackFunction;
};
}
let globalRequestIdNext = 10000;
const resultHandlerCallbacks: ResultHandlerCallbackMap = {};
async function run(): Promise<void> {
const ib = new IB({
// clientId: 0,
// host: '127.0.0.1',
// port: 7496
})
.on("connected", function () {
console.log("CONNECTED");
})
.on("disconnected", function () {
console.log("DISCONNECTED");
})
.on("received", function (tokens: any) {
console.info("%s %s", "<<< RECV <<<", JSON.stringify(tokens));
})
.on("sent", function (tokens: any) {
console.info("%s %s", ">>> SENT >>>", JSON.stringify(tokens));
})
.on("server", function (version: any, connectionTime: any) {
console.log(`Server Version: ${version}`);
console.log(`Server Connection Time: ${connectionTime}`);
})
.on("error", function (err: any) {
console.error(`@@@ ERROR: ${err.message} @@@`);
})
.on("result", function (event: string, args: readonly any[]) {
console.log(`======= ${event} =======`);
args.forEach(function (arg: any, i: number) {
console.log("%s %s", `[${i + 1}]`, JSON.stringify(arg));
});
if (typeof args[0] === "number") {
const requestId = args[0];
const [, ...rest] = args;
const callback = resultHandlerCallbacks[event]?.[requestId.toString()];
if (callback) {
callback(requestId, rest);
}
}
});
ib.connect();
ib.reqMarketDataType(4); // marketDataType (1=live, 2=frozen, 3=delayed, 4=delayed/frozen)
const result = await getMktData(ib, {
exchange: "SMART",
primaryExch: "NASDAQ",
secType: "STK",
symbol: "AAPL",
currency: "USD",
});
console.log("result:", result);
}
/**
* Requests market data (price, bid, ask)
*/
function getMktData(
ib: IB,
args: {
readonly exchange: Exchange;
readonly primaryExch: PrimaryExchange;
readonly secType: SecType;
readonly symbol: string;
readonly currency?: string;
}
): Promise<MktData> {
const requestId = globalRequestIdNext++;
return new Promise((res) => {
// see https://interactivebrokers.github.io/tws-api/tick_types.html
const tickPriceData: { [tickType: string]: number } = {};
const expecting = new Set<number>(
args.secType === "STK"
? [66, 67, 68, 72, 73, 74, 75, 88]
: args.secType === "CASH"
? [9]
: [-1]
);
let isDone = false;
const done = async () => {
if (!isDone) {
isDone = true;
unsetResultHandlerCallback("tickPrice", requestId);
unsetResultHandlerCallback("tickSize", requestId);
unsetResultHandlerCallback("tickString", requestId);
ib.cancelMktData(requestId);
res({
closePrice: tickPriceData["9"],
delayedBid: tickPriceData["66"],
delayedAsk: tickPriceData["67"],
delayedLast: tickPriceData["68"],
delayedHighPrice: tickPriceData["72"],
delayedLowPrice: tickPriceData["73"],
delayedVolume: tickPriceData["74"],
delayedClose: tickPriceData["75"],
});
}
};
const addNumber = async (tickType: number, value: number) => {
tickPriceData[tickType.toString()] = value;
expecting.delete(tickType);
if (expecting.size === 0) {
await done();
}
};
const addString = async (tickType: number, value: string) => {
// tickPriceData[tickType.toString()] = value;
expecting.delete(tickType);
if (expecting.size === 0) {
await done();
}
};
async function callback_tickPrice(
requestId: number,
[tickType, price, attrib]: [number, number, boolean]
): Promise<void> {
console.log("tickPrice:", { requestId, tickType, price, attrib });
await addNumber(tickType, price);
}
async function callback_tickSize(
requestId: number,
[tickType, size]: [number, number]
): Promise<void> {
console.log("tickSize:", { requestId, tickType, size });
await addNumber(tickType, size);
}
async function callback_tickString(
requestId: number,
[tickType, value]: [number, string]
): Promise<void> {
console.log("tickString:", { requestId, tickType, value });
await addString(tickType, value);
// console.log("DONE: ", requestId, tickPriceData);
}
setResultHandlerCallback("tickPrice", requestId, callback_tickPrice);
setResultHandlerCallback("tickSize", requestId, callback_tickSize);
setResultHandlerCallback("tickString", requestId, callback_tickString);
ib.reqMktData(
requestId,
{
currency: args.currency,
exchange: args.exchange,
primaryExch: args.primaryExch,
secType: args.secType,
symbol: args.symbol,
},
"",
false,
false
); // requestId, contract, genericTickList, snapshot, snapshot2
// Force the end in 10 seconds
setTimeout(done, 10 * 1000);
});
}
function setResultHandlerCallback(
event: string,
requestId: number,
callback: ResultHandlerCallbackFunction
): void {
if (!(event in resultHandlerCallbacks)) {
resultHandlerCallbacks[event] = {};
}
resultHandlerCallbacks[event][requestId] = callback;
}
function unsetResultHandlerCallback(event: string, requestId: number): void {
if (resultHandlerCallbacks[event]?.[requestId]) {
delete resultHandlerCallbacks[event][requestId];
}
}
run();
Hey,
I'm running IBeam and Node in Docker compose. When I try to connect to my IBeam gateway nothing happens. Even though I have set DEBUG=ibkr*
env variable.
I use the following code with node18:
import ibkr, { AccountSummary, IBKREVENTS, IbkrEvents, PortFolioUpdate, getContractDetails } from '@stoqey/ibkr';
const ibkrEvents = IbkrEvents.Instance;
console.log('connecting ibkr');
ibkr({ port: 5000, host: 'ibeam' })
.then(started => {
if (!started) {
// Error IBKR has not started
console.log('error cannot start ibkr');
// Not to proceed if not connected with interactive brokers
return process.exit(1);
} else {
console.log('ibkr agent started');
}
// subscribe for price updates
ibkrEvents.on(IBKREVENTS.ON_PRICE_UPDATES, priceUpdates => {
console.log(priceUpdates);
// use the price updates here
});
// Request price updates
ibkrEvents.emit(IBKREVENTS.SUBSCRIBE_PRICE_UPDATES, { symbol: 'AAPL' });
})
.catch(e => {
console.error(e);
});
What my console logs is only "connecting ibkr".
I've tried to wait for over 10 minutes. If I change the host or port, I get errors so I guess those are correct. Is there a timeout for the requests?
import ibkr,{IBKREVENTS, IbkrEvents, HistoricalData} from '@stoqey/ibkr';
const ibkrEvents = IbkrEvents.Instance;
ibkr({host:'127.0.0.1',port:4001}) // add your port and host if you want
.then((started: boolean) => {
console.log('done', started);
const symbol = 'PPSI';
HistoricalData.Instance; // initialize HistoricalData module
// Request for market data
ibkrEvents.emit(IBKREVENTS.GET_MARKET_DATA, {
symbol,
whatToShow: 'TRADES',
durationStr: '3600 S',
barSizeSetting: '5 secs',
});
// get the market data
ibkrEvents.on(
IBKREVENTS.ON_MARKET_DATA,
async ({symbol, marketData}: {symbol: string; marketData: any[]}) => {
console.log('data', {symbol, marketData: marketData.length});
}
);
})
.catch((e: Error) => {
console.log('error starting ibkr', e);
});
When getting a lot of market data, Historical data request pacing violation
error is thrown
I'd like to fix some stuff with historical Data, but current repo settings does not allow me to create push to separate branch for PR
Is there a way to enter order to be filled pre-open?
I'm testing the order entry and event that confirms Fill has MAX int values. What should I do?
trailStopPrice: 1.7976931348623157e+308,
trailingPercent: 1.7976931348623157e+308,
basisPoints: 1.7976931348623157e+308,
basisPointsType: 1.7976931348623157e+308,
scaleInitLevelSize: 1.7976931348623157e+308,
scaleSubsLevelSize: 1.7976931348623157e+308,
scalePriceIncrement: 1.7976931348623157e+308,
There is another deadlock in reqHistoricalData
Example code
import ibkr, { IBKRConnection, IbkrEvents, HistoricalData } from '@stoqey/ibkr';
const ibkrEvents = IbkrEvents.Instance;
const main = async () => {
try {
// @ts-ignore
const connectionStatus = await ibkr({ port: 7496, host: '127.0.0.1' });
HistoricalData.Instance;
const symbol = 'csco'
const data = await HistoricalData.Instance.reqHistoricalData({
symbol,
endDateTime: '',
durationStr: '1 W',
barSizeSetting: '1 day',
whatToShow: 'TRADES',
//whatToShow: 'YIELD_BID'
})
console.log(data)
IBKRConnection.Instance.disconnectIBKR()
} catch (error) {
console.error(error)
}
};
main();
Error in debug
ibkr:info error The contract description specified for CSCO is ambiguous. +304ms
With getContractDetails it is currently not possible to deliver multiple ContractDetails like an option chain.
{
currency: 'USD',
exchange: 'SMART',
//expiry: '202007',
multiplier: 100,
right: 'C',
secType: 'OPT',
strike: 300,
symbol: 'AAPL'
}
Currently this query returns 16 options in the TWS API but getContractDetails returns only one of them.
import ibkr, {AccountSummary, IBKREVENTS, IbkrEvents, PortFolioUpdate, getContractDetails,HistoricalData } from '@stoqey/ibkr';
const ibkrEvents = IbkrEvents.Instance;
// 3.2. Subscribe to market data results
ibkrEvents.on(IBKREVENTS.ON_MARKET_DATA, ({ symbol, marketData }) => {
// Use the data here
console.log("symbol:",symbol);
console.log("marketData:",marketData);
});
show error:
Property 'on' does not exist on type 'IbkrEvents'
Can this package be used without typescript?
const { ibkr, AccountSummary, IBKREVENTS, IbkrEvents, PortFolioUpdate, getContractDetails } = require('@stoqey/ibkr')
async f() {
await ibkr({ port: 7496, host: '127.0.0.1' })
const accountId = AccountSummary.Instance.accountSummary.AccountId;
const totalCashValue = AccountSummary.Instance.accountSummary.TotalCashValue;
console.log('acc id:' + accountId)
}
f()
(node:20068) UnhandledPromiseRejectionWarning: TypeError: ibkr is not a function
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.