Overview
Update: September 2021.
Websocket API for Pricing Streaming and Real-Time Service aka Websocket API enables easy integration into a multitude of client technology environments such as scripting and web. This API runs directly on your Refinitiv Real-Time Distribution System and presents data in an open (JSON) readable format. The API supports all Refinitiv data models and can be integrated into multiple client technology standards e.g. JavaScript, Python, R, .Net, etc.
TypeScript programming language is a typed superset of JavaScript that compiles to readable, standards-based JavaScript. The language is designed for application-scale JavaScript by adding optional types, classes, modules, ECMAScript 2015 features, and future proposals to JavaScript. TypeScript supports tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript is a first-class programming language in Microsoft Visual Studio, Angular web application platform. It also supported by various application frameworks like React, NodeJS and Express framework, ASP.Net Core, Vue.js, and more.
This example shows how to implement the WebSocket API JavaScript web application with TypeScript. The web application source codes are implemented in TypeScript language to connect, consume and display data from the Refinitiv Real-Time Advanced Distribution server via the WebSocket API in the web browsers. All source codes will be compiled to readable JavaScript with Webpack JavaScript module bundler.
*Note: The initial release of this API is for deployed Refinitiv Real-Time Advanced Distribution Server customers only (i.e. to use it you will need an installed version of Refinitiv Real-Time Advanced Distribution Server 3.2.1 and above).
Supported Web Browsers
This example supports the following web browsers (based on the WebSocket and Web Workers browser supported)
- Chrome
- Firefox
- IE11
- Microsoft Edge (Chrome-based version)
Application files
The project includes complete TypeScript source codes, a simple Express.js web server application file, CSS files, and all required static dependencies. All dynamic dependencies for compiling and building JavaScript source files are defined in the package.json file which can be installed via npm install command.
- src/ folder: The folder that contains all TypeScript source code files
- web/ folder: The folder that contains all application web page files
- web/dist folder: The folder that the compiled JavaScript application file named web_app.js will be generated and deployed
- web/index.html: The application HTML page
- web/css/cover.css: The application CSS file
- web/libs/jquery-3.2.1.min.js: jQuery library file
- web/bootstrap/css, web/bootstarp/fonts and web/bootstrap/js folders: The folders for Bootstrap CSS and libraries files
- package.json: Project's NPM dependencies file
- tsconfig.json: Project's Typescript compiler options file
- webpack.config.js: Project's Webpack compiler options file
- server.js: Project's simple web server application file
Application Code Walkthrough
When building against the WebSocket API interface, you follow the standard HTML5 WebSocket connection initiation and event processing. That is, once you initiate a connection within your application, you define and process the events generated within standard WebSocket callbacks.
Establish a connection
The web_app.ts file implements the standard WebSocket required functions and callbacks to capture WebSocket events. Firstly, the application receives a connect command from the web UI's connect button. The application then establishes a connection with Real-Time Advanced Distribution Server in the connect() function.
//web_app.ts
//Initiate WebSocket connection
function connect(url: string): void {
ws = new WebSocket(url, protocol);
ws.onopen = onOpen;
ws.onmessage = onMessage;
ws.onerror = onError;
ws.onclose = onClose;
}
//indicates that the connection is ready to send and receive data
function onOpen(event: any): void {
console.log("connected");
//$("#btnConnect").html("Connected");
btnConnect.innerHTML = "Connected";
}
//An event listener to be called when a message is received from the server
function onMessage(event: any): void {
}
//An event listener to be called when an error occurs. This is a simple event named "error".
function onError(event: any): void {
console.log(JSON.stringify(event.data));
}
//An event listener to be called when the WebSocket connection's readyState changes to CLOSED.
function onClose(event: any): void {
console.log(JSON.stringify(event.data));
}
Sending Login request message
Next, the application sends the OMM Login request message to Real-Time Advanced Distribution Server in JSON format. In TypeScript, you can create JSON message with JSON object directly (like standard JavaScript) or via TypeScript's Interface and Class approaches. This application uses TypeScript's Interface and Class approach for creating the OMM JSON Login, Item request and Close request messages.
We define the Login interfaces for the OMM Login Request message in json_msg_interface.ts file. We use export keyword to export each JSON object into TypeScript module.
//json_msg_interface.ts
//Interface for Login domain JSON message
export interface JSONLogin {
ID:number;
Domain:string;
Key: JSONLoginKey;
}
//Interface for Login domain's Key attribute JSON message
export interface JSONLoginKey{
Elements: JSONLoginKeyElements;
Name: string;
}
//Interface for Login domain's Key's Elements attribute JSON message
export interface JSONLoginKeyElements{
ApplicationId: string;
Position: string;
}
Then we define the Login classes for the OMM Login Request message in json_msg_classes.ts file. The Login classes implement the Login interfaces based on json_msg_interface.ts file.
//json_msg_classes.ts
import { JSONLogin } from "./json_msg_interface";
import { JSONLoginKey } from "./json_msg_interface";
import { JSONLoginKeyElements } from "./json_msg_interface";
export { LoginMsg };
const loginDomain: string = "Login";
class LoginMsg implements JSONLogin {
ID: number;
Domain: string = loginDomain;
Key: JSONLoginKey;
constructor(
ID: number,
Name: string,
ApplicationId: string,
Position: string
) { this.ID = ID;
let loginMsgKey_class = new LoginMsgKey(Name, ApplicationId, Position);
this.Key = loginMsgKey_class;
}
}
class LoginKeyElements implements JSONLoginKeyElements {
ApplicationId: string;
Position: string;
constructor(ApplicationId: string, Position: string) {
this.ApplicationId = ApplicationId;
this.Position = Position;
}
}
class LoginMsgKey implements JSONLoginKey {
Name: string;
Elements: JSONLoginKeyElements;
constructor(Name: string, ApplicationId: string, Position: string) {
this.Name = Name;
let loginKeyElements_class = new loginKeyElements(ApplicationId, Position);
this.Elements = loginKeyElements_class;
}
}
The web_app.ts file initiates the LoginMsg object and then sends it to Real-Time Advanced Distribution Server 3.2 via WebSocket.send() function. This login object will be compiled to the standard Login JSON message in the compiled JavaScript we_app.js file.
//web_app.ts
import { LoginMsg } from "./json_msg_classes";
const loginID: number = 1;
//Create the Login JSON message from LoginMsg class and send it to Real-Time Advanced Distribution Server WebSocket
function sendLogin(username: string): void {
let login: LoginMsg = new LoginMsg(loginID, username, "777", "127.0.0.1");
ws.send(JSON.stringify(login));
}
Handling Login Refresh response message
All messages (Refresh, Update, Status, etc) from the Real-Time Advanced Distribution Server will be available via WebSocket.onMessage() callback function. We implement this onMessage() callback function in web_app.ts file to print out incoming messages from Real-Time Advanced Distribution Server in the web browser's inMessagePre <pre> element tag.
//web_app.ts
let inMessagePre: any;
//An event listener to be called when a message is received from the server
function onMessage(event: any): void {
let incomingdata = JSON.parse(event.data.toString());
//Iterate each incoming JSON message
let data:any = null;
for (let index = 0; index < incomingdata.length; index++) {
data = incomingdata[index];
display(inMessagePre,JSON.stringify(incomingdata[index], undefined, 2));
}
}
//display string value in selected element
function display(el:any, msg:string): void{
el.innerHTML = msg;
}
window.onload = () => {
...
inMessagePre = document.getElementById("inMessagePre");
}
Sending Item request message
Next, we implement the application source code to send the OMM Market Price request message to the Real-Time Advanced Distribution Server when users click the Subscribe button in the web UI.
We define the Item Request interfaces for the OMM Market Price Request message in json_msg_interface.ts file. We use export keyword to export each JSON object into TypeScript module. We set the Real-Time Advanced Distribution Server Service information as optional information in the JSONItemRequestKey interface because it is an optional parameter in the OMM Request message.
//json_msg_interface.ts
//Interface for Market Price domain item request JSON message
export interface JSONItemRequestMsg {
ID: number;
Key: JSONItemRequestKey;
}
//Interface for Market Price domain item request's key attribute JSON message, service name is an optional
export interface JSONItemRequestKey {
Name: string;
Service?: string;
}
Next we define the Item Request classes for the OMM Market Price request message in json_msg_classes.ts file. The Item Request classes implement the Item Request interfaces based on json_msg_interface.ts file.
//json_msg_interface.ts
import { JSONItemRequestMsg } from "./json_msg_interface";
import { JSONItemRequestKey } from "./json_msg_interface";
export { ItemRequestMsg };
class ItemRequestMsg implements JSONItemRequestMsg {
ID: number;
Key: JSONItemRequestKey;
constructor(
ID: number,
Name: string,
Service: string
) {
this.ID = ID;
let itemrequestKey_class = new ItemRequestMsgKey(Name, Service);
this.Key = itemrequestKey_class;
}
}
class ItemRequestMsgKey implements JSONItemRequestKey {
Name: string;
Service?: string;
constructor(
Name: string,
Service?: string
) {
this.Name = Name;
if (Service !==''){
this.Service = Service;
}
}
}
The web_app.ts file initiates the ItemRequestMsg object based on user input from the web UI and sends it to Real-Time Advanced Distribution Server via WebSocket.send() function. This item request message object will be compiled to a standard Item Request JSON message in the compiled JavaScript we_app.js file.
//web_app.ts
import { ItemRequestMsg } from "./json_msg_classes";
let itemID: number = 0;
let itemname:string = "";
//Create the Item Request JSON message from ItemRequestMsg class and send it to Real-Time Advanced Distribution Server WebSocket
function sendItemrequest(service: string, item_name: string): void {
//set Item ID value
if (itemID === 0) {
itemID = loginID + 1;
} else {
itemID += 1;
}
let itemrequest: ItemRequestMsg = new ItemRequestMsg(
itemID,
item_name,
service
);
ws.send(JSON.stringify(itemrequest));
display(outMessagePre,JSON.stringify(itemrequest));
}
window.onload = () => {
btnSubscribe.addEventListener("click", function() {
let txtServiceName:any = document.getElementById("txtServiceName");
let txtItemName:any = document.getElementById("txtItemName");
itemname = txtItemName.value;
sendItemrequest(txtServiceName.value, itemname);
});
};
Handling incoming Item response message
All messages (Refresh, Update, Status, etc) for all Domains from the Real-Time Advanced Distribution Server will be available in WebSocket.onMessage() callback function. We will modify the onMessage() callback function in web_app.ts file to support incoming Market Price data from Real-Time Advanced Distribution Server.
The onMessage() callback function verifies if an incoming message is an incoming Market Price Refresh Response message or not by checking the data type and data.Key.Name property. If it is the Market Price Refresh message, it calls the pushIDstoDropDownMenu() function to add this item name and subscription id to the web UI list box. These item names and subscription id information will be used for unsubscribing items.
The onMessage() callback function also prints out incoming Market Price messages from Real-Time Advanced Distribution Server in the web browser's inMessagePre <pre> element tag.
//web_app.ts
let inMessagePre: any;
let itemname:string = "";
//An event listener to be called when a message is received from the server
function onMessage(event: any): void {
let incomingdata = JSON.parse(event.data.toString());
//Iterate each JSON message and send it to market_price_app.js
let data:any = null;
for (let index = 0; index < incomingdata.length; index++) {
data = incomingdata[index];
if(data.Type === 'Refresh' && data.Key.Name === itemname){
//Push subscription item name and ID to selection box
pushIDstoDropDownMenu(data.Key.Name, data.ID);
}
display(inMessagePre,JSON.stringify(incomingdata[index], undefined, 2));
}
}
//display string value in selected element
function display(el:any, msg:string): void{
el.innerHTML = msg;
}
window.onload = () => {
...
inMessagePre = document.getElementById("inMessagePre");
}
function pushIDstoDropDownMenu(itemname:string, id:number):void {
let cb:any = document.getElementById("listenerCombo");
let opt:any = document.createElement("option");
opt.value = id;
opt.text = `${itemname} ID ${id}`;
cb.options.add(opt);
}
Unsubscribe Market Price Item
Next, we implement the application source code to send the OMM Market Price close request message to Real-Time Advanced Distribution Server (version 3.2.1 and above). We define the Close Request interfaces for the Close Request message in json_msg_interface.ts file. We use export keyword to export each JSON object into TypeScript module. We set the Domain information as an optional In the JSONClose interface because it is an optional parameter in the OMM Close message (Market Price by defaulted).
//json_msg_interface.ts
//Interface for close JSON message
export interface JSONClose {
Domain?: string;
ID: number;
Type: string;
}
Next we define the Close Message classe in json_msg_classes.ts file. The Close Message class implements the JSONClose interface based on json_msg_interface.ts file.
//json_msg_interface.ts
import { JSONClose } from "./json_msg_interface";
export {CloseMsg};
class CloseMsg implements JSONClose{
Domain?: string;
ID: number;
Type: string;
constructor(ID: number, Domain?: string) {
this.ID = ID;
this.Domain = Domain;
this.Type = "Close";
}
}
When unsubscribing item, users must choose the item name and subscription id that users want to unsubscribe from the web UI list box, then click the Unsubscribe button. We implement this UI interaction in web_app.ts file.
//web_app.ts
window.onload = () => {
btnUnSubscribe.addEventLisener("click", function() {
//get Selected ID to unsubscribe let cb:any = document.getElementById("listenerCombo");
if(cb.selectedIndex === -1){
//If user does not select any ID, alert user
window.alert("Select ID first"); return;
}
//get unsubscribe ID from HTML select element
let unsubID:number = parseInt(cb.options[cb.selectedIndex].value);
//Unsubscribe sendItemCloserequest(unsubID);
//remove unsubscribe ID from HTML select element
cb.removeChild(cb.options[cb.selectedIndex]);
});
};
The web_app.ts file initiates the closeitemrequestMsg object, receives user input from the web UI, and then sends it to Real-Time Advanced Distribution Server via WebSocket.send() standard function. This item close request message object will be compiled to a standard Item Close Request JSON message in the compiled JavaScript we_app.js file.
//web_app.ts
import { CloseMsg } from "./json_msg_classes";
//Create the Item Close Request JSON message from ItemCloseRequestMsg class and send it to ADS WebSocket
function sendItemCloserequest(unsubID:number): void {
//let closeitemrequestMsg: ItemCloseRequestMsg = new ItemCloseRequestMsg(itemID);
let closeitemrequestMsg: CloseMsg = new CloseMsg(unsubID);
ws.send(JSON.stringify(closeitemrequestMsg));
display(outMessagePre,JSON.stringify(closeitemrequestMsg));
}
Handling Ping Pong messages
The last step is handling the Ping and Pong messages. The Ping and Pong messages are the handshake message in JSON format ({ "Type": "Ping" } and { "Type": "Pong" }) between the Real-Time Advanced Distribution Server WebSocket server and client for monitoring connection health. The Real-Time Advanced Distribution Server periodically sends Ping messages to applications and applications must be prepared to send Pong messages as a response to any Ping message they receive.
The application will receive the Ping message via WebSocket.onMessage() callback function. We will modify this function to detect an incoming Ping message, then sends the Pong message back to Real-Time Advanced Distribution Server.
//web_app.ts
//An event listener to be called when a message is received from the server
function onMessage(event: any): void {
let incomingdata = JSON.parse(event.data.toString());
//Iterate each JSON message and send it to market_price_app.js
let data:any = null;
for (let index = 0; index < incomingdata.length; index++) {
data = incomingdata[index];
if(data.Type === 'Refresh' && data.Key.Name === itemname){
//Push subscription item name and ID to selection box
pushIDstoDropDownMenu(data.Key.Name, data.ID);
}
display(inMessagePre,JSON.stringify(incomingdata[index], undefined, 2));
//If incoming message is PING (server ping)
if (incomingdata[index].Type === "Ping") {
sendPong();
}
}
}
//Create the client PONG message and send it to Real-Time Advanced Distribution Server WebSocket
function sendPong(): void {
let pong: any = { Type: "Pong" };
ws.send(JSON.stringify(pong));
display(outMessagePre,JSON.stringify(pong));
}
Application Setup
The application source code is available at GitHub. You can get it via the following git command
$>git clonegit@github.com:Refinitiv-API-Samples/Example.EWA.TypeScript.WebApplication.git
This example requires the following dependencies software and runtime
- Node.js runtime - version 8.9.3 or higher.
- npm package manager (included in Node.js)
- TypeScript compiler (will be installed via npm install command)
- Express.js web framework (will be installed via npm install command)
This example also uses the following 3rd party libraries for UI presentation only.
- jQuery 3.2.1 JavaScript library
- Bootstrap 3.3.7 CSS library
please check the README.md in the source code project for more detail.
How to run this example
- Get the project via the above git command
- Run $> npm install command in the command prompt to install all the required dependencies to run the sample in a subdirectory called node_modules/.
3. If the machine is behind a proxy server, you need to configure Node.js uses proxy instead of a direct HTTP connection via the following command in command prompt:
set https_proxy=http://<proxy.server>:<port>
4. Run $> npx webpack command in the command prompt to build and compile all TypeScript files in src folder into JavaScript source file (/web/dist/ folder)
5. Run $> node server.js command in the command prompt to start the webserver at HTTP port 8080
6. Open a web browser (IE11, Chrome/Microsoft Edge (Chrome-based version), and Firefox), then navigate to index.html of the webserver at http://localhost:8080/index.html
References
For further details, please check out the following resources:
- Refinitiv Real-Time & Distribution Family page on the Refinitiv Developer Community web site.
- WebSocket API page.
- Developer Webinar Recording: Introduction to Electron Websocket API
- TypeScript programming language: Documentation.
- Mozilla Developer Network: WebSocket API page
- WebSocket technology web site.
- Developer Article: Consume Realtime data with Refinitiv Data Platform
- Developer Article: How to implement WebSocket API JavaScript application with HTML Web Workers
- Developer Article: How to implement WebSocket API JavaScript application with TypeScript
- Developer Article: Consuming Order Book Level 2 data with Websocket API
For any questions related to this article or the WebSocket API page, please use the Developer Community Q&A Forum.