Interactive Brokers Google Sheets
Receive almost any data from IBKR right inside Google Sheets
What
Why
DYI portfolio tracker, dividends calendar, trades profitability calculation, ...
How
Step 1: Enable Flex Web Services under Settings menu, copy token
Step 2: Create Flex Query under Performance & Reports menu
Step 3: Create Google Sheet with App Script
Paste code and deploy
New function should appear
Ideas
You may want to:
- join IBKR with GOOGLEFINANCE
- get additional data from finviz, yahoo finance, ...
- build dividends calendar
- ...
IBKR FLEX QUERY
/**
* IBRK Flex Query.
*
* From IBKR side you need enable flex service - this can be done in "Flex Web Service" in settings menu.
* While enabling flex service - copy your token.
* After enabling flex service go to "Flex Queries" under "Performance & Reports" menu and create one.
* Each created report will have its own query id - copy it as well.
* Keep in mind that this function will grab only first section of report, so if you want multiple sections just create multiple queries.
* The report retrieval is done in two steps:
* - step 1: request report generation, requires token and query id, will return report code and url
* - step 2: retrieve generated report
* Notes:
* - when you prepared your report it is a good idea to enable cache so everything will work much faster
* - if report is really huge try to increase to something like 5 seconds
*
* @param {number} token Token from Settings \ Flex Services menu.
* @param {number} query Query ID of Flex Query from Reports \ Flex Query menu.
* @param {bool} headers Add header names to result. Default TRUE.
* @param {bool} cache Cache results for 6 hours which is maximum for Google Sheets. Default FALSE.
* @param {number} sleep Report generation takes some time, so we are waiting given amount of milliseconds before proceeding. Default is 2000 which is 2 seconds.
* @return Query results.
* @customfunction
*/
function IBKRFLEXQUERY(token, query, headers=true, cache=false, sleep=2000) {
// Step 0: optional, return cached results
var cacheService = CacheService.getDocumentCache();
var cacheKey = token + "." + query;
if (cache === true && cacheService.get(cacheKey)) {
return JSON.parse(cacheService.get(cacheKey));
}
// Step 1: request report generation
var url = "https://gdcdyn.interactivebrokers.com/Universal/servlet/FlexStatementService.SendRequest?v=3&t="+token+"&q="+query;
var xml = UrlFetchApp.fetch(url).getContentText();
var root = XmlService.parse(xml).getRootElement();
var code = root.getChild("ReferenceCode").getValue();
url = root.getChild("Url").getValue();
Utilities.sleep(sleep);
// Step 2: retrieve generated report
url = url + "?v=3&t="+token+"&q=" + code
xml = UrlFetchApp.fetch(url).getContentText();
var nodes = XmlService.parse(xml).getRootElement().getChild("FlexStatements").getChild("FlexStatement").getChildren()[0].getChildren();
var items = [];
for(var node of nodes) {
// optional: add headers
if (items.length === 0 && headers) { items.push(node.getAttributes().map(attr => attr.getName())); }
var item = [];
for(var attr of node.getAttributes()) {
var text = attr.getValue();
if (text.match(/^-?[\d,\.]+$/)) {
item.push(parseFloat(text));
} else {
item.push(text);
}
}
items.push(item);
}
// Step 0: optional, store results in cache for subsequent requests
if (cache === true) {
cacheService.put(cacheKey, JSON.stringify(items), 21600);
}
return items;
}
How it works
Two step process:
- Send request asking to generate report
- Retrieve generated report
Suppose we have an query with id 866660
TOKEN="1417**************"
QUERY_ID="866660"
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "q=$QUERY_ID&v=3&t=$TOKEN" \
"https://gdcdyn.interactivebrokers.com/Universal/servlet/FlexStatementService.SendRequest"
Response will be something like
<FlexStatementResponse timestamp='14 October, 2023 03:57 AM EDT'>
<Status>Success</Status>
<ReferenceCode>5045169185</ReferenceCode>
<Url>https://gdcdyn.interactivebrokers.com/Universal/servlet/FlexStatementService.GetStatement</Url>
</FlexStatementResponse>
From response we will receive url and reference code to download generated report
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "q=5045169185&v=3&t=$TOKEN" \
"https://gdcdyn.interactivebrokers.com/Universal/servlet/FlexStatementService.GetStatement"
Notes:
- depending on report size it may require some time for generation to complete
- url is different and taken from response
- instead of query id we passing reference code
Sample response
<FlexQueryResponse queryName="portfolio" type="AF">
<FlexStatements count="1">
<FlexStatement fromDate="2023-10-13" toDate="2023-10-13" period="LastBusinessDay" whenGenerated="2023-10-14 03:57:57">
<OpenPositions>
<OpenPosition symbol="VZ" position="10" />
<OpenPosition symbol="XRX" position="4" />
</OpenPositions>
</FlexStatement>
</FlexStatements>
</FlexQueryResponse>
Notes:
- just for simplicity have removed everything, in your flex query you may add ass many fields as you want, aka prices, p&l, market value, price change and so on
- with flex query you may receive not only positions but also trades history, dividends, taxes and many many more
- flex queries are not supposed to receive input parameters so if you need YTD data just set it while creating query