Integrating Bloomberg Terminal Connect
How it works
Bloomberg Terminal Connect (BTC) is an add-on feature for the Bloomberg Terminal that allows apps to connect to and integrate with the terminal so that a user of the app can access Bloomberg data and services and create workflows between their apps and the terminal.
To check out Bloomberg integration with Finsemble, you will need access to the Bloomberg Terminal Connect API, which is an add-on feature of Bloomberg Terminal.
An app service, known as the Bloomberg Bridge Service, communicates with BTC on behalf of your applications. User interface components of this service show the status of the connection and manage its configuration. Finally, the service provides a client that your applications, app services and the UI components can use to communicate with the app service.
API Keys and Whitelisting
In order to Bloomberg Terminal Connect v3 you will need a Bloomberg developer API key with Terminal Connect v3 enabled for it and for Bloomberg to whitelist the origins (hostname and port) from which you'll use the integration - please contact your Bloomberg rep to do so (and feel free to include support@finsemble.com in any email threads).
As Finsemble is making the connection to Terminal Connect for you, the origins to request whitelisting of are the same ones that will host your Finsemble environment. Hence, for development use make sure you request whitelisting of localhost:3375
(Finsemble's default local development server address) and then the final hostname(s) and port(s) you will host it at for staging and production use (if you don't know these yet don't worry, they can be added later by contacting your Bloomberg rep again).
You will need to provide your Terminal Connect API key to Finsemble, which is covered later in this document.
Setting up the app service
There are 2 ways to set up the app service. The easier one is to use the Smart Desktop Designer (SDD). You can also add the app service through code.
Smart Desktop Designer
You can enable the Bloomberg Bridge API Service via the Smart Desktop Designer by clicking the checkbox under the Bloomberg section of the Key Partner Apps tab.
Demo application
You can set up a demo application via the SDD by adding the app URL: https://assets.finsemble.com/components/bloombergdemo/index.html
Seed project
You can enable the Bloomberg Bridge API Service by adding this configuration to your apps.json:
{
"version": "1.0.0",
"appId": "bloombergBridgeService",
"type": "web",
"name": "bloombergBridgeService",
"description": "Bloomberg Bridge Service",
"contactEmail": "info@finsemble.com",
"supportEmail": "support@finsemble.com",
"publisher": "Finsemble",
"details": {
"url": "$moduleRoot/services/bloombergBridge/bloombergBridgeService.html"
},
"hostManifests": {
"Finsemble": {
"appService": true,
"waitForInitialization": true
}
}
}
You can also add a default configuration for the service to the Finsemble config (if dynamically configuring the terminal location, as described later, you can skip this step). Make this addition to your manifest file in the Finsemble element:
{
…
"finsemble": {
…
"custom": {
"//": "Customer macros definitions must go here …",
"bloomberg": {
"enabled": true,
"remote": false,
"remoteAddress": null,
"showStatus": true
},
},
…
}
}
Example apps
You can find example apps at https://github.com/ChartIQ/finsemble-bloomberg/tree/bloomberg-v3-api.
Setting up remote (non-localhost) access
If you’re running Finsemble on a different PC from your Bloomberg Terminal instance, you’ll need to configure BTC for remote access.
Before you begin, you need to contact your Bloomberg account representative so that they can enable remote access on your users’ accounts. You’ll need to provide them both the local (Bloomberg Terminal) PC device names and the remote (Finsemble) PC device names, which on Windows 10 you can find in 'System Properties' panel. (It‘s also labeled 'About this PC'.)
The BTC server runs under the same machine/domain user account as the Bloomberg terminal application and therefore usually doesn’t have local admin privileges. Listening on an external network interface requires elevated privileges that the BTC server process will not likely have. To enable this capability, administrators can use the netsh
command line utility that is part of the Windows operating system to reserve the URL that the BTC server will listen on. Reserving a URL using netsh
requires administrative privileges to use but your IT admin will do it only once. Here’s an example of the command to reserve a URL using netsh
:
netsh http add urlacl
url=http://[IP-ADDRESS-OR-HOST-NAME]:[PORT]/terminal_connect/v3 user=[DOMAIN\USER]
Configuring the terminal location in Finsemble
The Bloomberg Bridge service monitors a number of configuration settings related to connections, including whether it is making a remote connection and to what address. These are:
finsemble.custom.bloomberg.apiKey
: The Bloomberg developer API key for your company. This is required if you are using Terminal Connect version 3.finsemble.custom.bloomberg.enabled
(default true): Determines whether the Bloomberg Bridge should be connected to the terminal. Set to false before updating other settings at runtime, then set to true to connect to a new host.finsemble.custom.bloomberg.remote
(default false): Enables connection to a remote machine. If false, the value of remoteAddress is assumed to be "localhost".finsemble.custom.bloomberg.remoteAddress
(default null): The machine name or IP address of the remote machine with a terminal running on it.finsemble.custom.bloomberg.showStatus
(default false): Determines whether to display the Bloomberg connection status icon in the toolbar.
You can set these config values on behalf of the user via dynamic configuration. To do so, import configuration of this format via ConfigClient.processAndSet
:
{
"finsemble": {
"custom": {
"bloomberg": {
"apiKey": "YOUR_API_KEY_GOES_HERE",
"enabled": true,
"remote": true,
"remoteAddress": "mymachine.somedomain.com",
"showStatus": true
}
}
}
}
After you enable Bloomberg, a preference panel is provided. Users can then use this panel to configure Bloomberg preferences (including the device name of the PC running the Bloomberg Terminal) at runtime and the selected user choices will persist across sessions. This way users can specify preferences that override default settings or settings applied via dynamic configuration.
Providing an API key is required for Terminal Connect version 3. If you attempt to connect to Bloomberg without your company's assigned developer API key, your connection attempt may fail. If this happens, you should see an error message in the Central Logger like this:
Logger client caught ErrorEvent. Error: Uncaught ApolloError: Missing or invalid api key.
You can set your API key by editing your Finsemble config file.
After setting the key, it's possible you'll see an error message like this in the Central Logger:
Error: Uncaught ApolloError: Api key: EXAMPLE_INVALID_KEY does not exist
If you see that error message, it means that your API key is invalid, or doesn't match your Bloomberg account. Check with your Bloomberg account manager to confirm your developer API key.
Configuring your app to use BloombergBridgeClient
Before your app can use Bloomberg, you have to specify the Bloomberg preload in your app's config:
hostManifests.Finsemble.component.preload = ["$moduleRoot/preloads/BloombergBridgePreload.js"];
Or by configuring it to be loaded by all applications:
finsemble.extensions = {
"preloads": {
"bloombergBridgeClient": {
"url": "$moduleRoot/preloads/BloombergBridgePreload.js",
"autoload": "all"
}
}
}
Please see the Finsemble config reference for more details on individual app preloads or autoload preloads.
After Finsemble injects the Bloomberg preload in your app, the Bloomberg Client interface will be instantiated for you at:
window.FSBL.Clients.BloombergBridgeClient
In future versions, we are planning an FDC3-based interface that will replace the client, after which no preload will be needed.
Examples
The remainder of this topic contains examples that you can modify to use in your implementation. For simplicity, we created a reference to the Bloomberg Client interface, which we'll use in the examples:
let bbg = FSBL.Clients.BloombergBridgeClient;
Check whether we are connected to the BloombergBridgeClient
There are 2 ways to check the connection. You can either check the connection manually:
let checkConnectionHandler = (err, loggedIn) => {
if (!err && loggedIn) {
showConnectedIcon();
} else {
showDisconnectedIcon();
}};
bbg.checkConnection(checkConnectionHandler);
You can also check it by registering a handler for connection events:
let connectionEventHandler = (err, resp) => {
if (!err && resp && resp.loggedIn) {
showConnectedIcon();
} else {
showDisconnectedIcon();
}};
bbg.setConnectionEventListener(connectionEventHandler);
Data sharing with Launchpad groups
If you're new to Bloomberg Launchpad or need a refresher, see one of the getting started guides published elsewhere.
Retrieve a list of all Launchpad groups and their current context:
bbg.runGetAllGroups((err, response) => {
if (!err) {
if (response && response.groups && Array.isArray(response.groups)) {
//do something with the returned data
response.groups.forEach(group => {
let groupName = group.name;
let groupType = group.type;
let groupCurrentValue = group.value;
...
});
} else {
console.error("Invalid response returned from runGetAllGroups", response);
}
} else {
console.error("Error returned from runGetAllGroups", err);
}
});
Get the current state of a Launchpad group:
bbg.runGetGroupContext(groupName, (err, response) => {
if (!err) {
if (response && response.group) {
let groupName = response.group.name;
let groupType = group.type;
let groupCurrentValue = group.value;
...
} else {
console.error("Invalid response returned from runGetGroupContext", response);
}
} else {
console.error("Error returned from runGetGroupContext", err);
}
});
Set the state (value) of a Launchpad group:
bbg.runSetGroupContext(groupName, newValue, null, (err, response) => {
if (!err) {
/* You might want to retrieve the current state of Launchpad group here as Bloomberg
will resolve any security you’ve set and therefore its value might differ from
what you sent. */
bbg.runGetGroupContext(groupName, (err2, response2) => { ... });
} else {
console.error("Error returned from runSetGroupContext", err);
}
});
Register a listener for group events (e.g. creation or context change):
bbg.setGroupEventListener((err, response) => {
if (!err) {
if (response.data.group && response.data.group.type == "monitor") {
console.log("Monitor event:\n" + JSON.stringify(response.data, null, 2));
} else {
console.log("Security event:\n" + JSON.stringify(response.data, null, 2));
}
} else {
console.error("Error returned from setGroupEventListener", err);
}
});
Data sharing with worksheets
Retrieve all worksheets:
bbg.runGetAllWorksheets((err, response) => {
if (!err) {
if (response && response.worksheets && Array.isArray(response.worksheets)) {
response.worksheets.forEach(worksheet => {
let worksheetName = worksheet.name;
let worksheetId = worksheet.id;
...
});
} else {
console.error("invalid response from runGetAllWorksheets", response);
}
} else {
console.error("Error returned from runGetAllWorksheets", err);
}
});
Retrieve the content of an existing worksheet by ID:
bbg.runGetWorksheet(worksheetId, (err, response) => {
if (!err) {
if (response && response.worksheet && Array.isArray(response.worksheet.securities)) {
let worksheetName = response.worksheet.name;
let worksheetId = response.worksheet.id;
let workSheetSecurities = response.worksheet.securities;
...
} else {
console.error("invalid response from runGetWorksheet");
}
} else {
console.error("Error returned from runGetWorksheet", err);
}
});
Create a worksheet:
let securities = ["TSLA US Equity", "AMZN US Equity"];
bbg.runCreateWorksheet(worksheetName, securities, (err, response) => {
if (!err) {
if (response && response.worksheet) {
//Id assigned to the worksheet
let worksheetId = response.worksheet.id;
//List of securities resolved by Bloomberg from the input list, unresolvable securities will be removed
let workSheetSecurities = response.worksheet.securities;
...
} else {
console.error("invalid response from runCreateWorksheet", response);
}
} else {
console.error("Error returned from runCreateWorksheet", err);
}
});
Replace the content of a worksheet:
let securities = ["TSLA US Equity", "AMZN US Equity"];
bbg.runReplaceWorksheet(worksheetId, securities, (err, response) => {
if (!err) {
if (response && response.worksheet) {
//Return details of the updated worksheet
let worksheetName = response.worksheet.name;
//Return the list of securities that Bloomberg assigned to the worksheet.
let workSheetSecurities = response.worksheet.securities;
...
} else {
console.error("invalid response from runReplaceWorksheet", response);
}
} else {
console.error("Error returned from runReplaceWorksheet", err);
}
});
Searching for securities
You can search for Bloomberg securities via the Bloomberg Bridge, which usually returns results in around 120 to 150 ms. You can use these, for example, to power an autocomplete or typeahead search:
bbg.runSecurityLookup(security, (err, response) => {
if (!err) {
if (response && response.results) {
//do something with the results
response.results.forEach(result => {
console.log(result.name + " " + result.type);
...
});
} else {
console.error("invalid response from runSecurityLookup", response);
}
} else {
console.error("Error returned from runSecurityLookup", err);
}
});
Send commands to Bloomberg Terminal panels
You can send commands to the Bloomberg panels/tabs. Each Bloomberg command is made up of these elements:
mnemonic
: the specific command that would normally be entered into the terminal (for example DES, YAS, VCON)panel
: the Bloomberg panel number to send it tosecurities
: an array of 0 or more Bloomberg security stringstails
: arguments specific to the mnemonic (optional) - See the help page in the Terminal for each mnemonic to discover what options it supports.
At a minimum, a command must include a mnemonic
and the panel
number.
To run a command:
const mnemonic = "DES";
const securities = ["MSFT US Equity"];
const panel = 3;
let tails = null;
bbg.runBBGCommand(mnemonic, securities, panel, tails, (err, response) => {
if (!err) {
console.log(`Ran command "${mnemonic}" on panel ${panel}`);
} else {
console.error("Error returned from runBBGCommand", err);
}
});