Article

How to build a Quote Assist App from scratch using App Studio HTML 5 SDK

Rustam Guseynov
Manager - Application Development Manager - Application Development
Alexander Ozerov
Senior Consultant - Solutions Integ... Senior Consultant - Solutions Integration

Abstract

In this article we present a Quote Assist App built using App Studio HTML 5 SDK. We will demonstrate how to use JET toolkit to achieve seamless integration with Refinitiv Eikon.

Introduction

The idea of the app is built around one of the key business processes on the client facing desks - providing quotes to bank's counterparties, that consist of the current market levels and a spread;

The following factors are usually priced in:

  • counterparty risk;
  • fees;
  • negative price movement during the time between the quotation and the trade.

So, essentially the trader has a list of clients with corresponding spread margins. Every time the trader is asked for a quote, she needs to look up the current spread for the specific customer. Traders are generally quite busy and have to keep in mind a lot of things when quoting the market, and an incorrect quote can cause a lot of problems.

Some of our customers try to solve this issue by maintaining a spreadsheet with basic data, however, this poses several issues in itself, like maintenance, distribution, etc. In our case, we got an opportunity to satisfy the client's need with a quick and easy solution.

App Studio

Although the app was developed using an internal SDK, running it as an App Studio app is fairly straight forward, as we provide our customers with the same set of tools that we use internally, the app container is slightly different.

We would like demonstrate how a simple application may improve your end-user workflows, and is essentially free.

Solution

Requirements

Here is the list of informal requirements that we drafted together with our customer:

  • The app should present a simplified quotation workflow;
  • The trader should have a record of counterparties with their details, described below;
  • The trader will manually edit information on counterparties;
  • When editing the list, the trader must see the quotes clearly on the screen in a standard quotation convention;
  • The information on counterparties must be available anywhere when the trader logs in to Refinitiv Eikon;
  • Pricing information should be streaming in real-time;

Data model

  • Name;
  • RIC;
  • Buy/sell spreads;
  • Phone number;
  • E-mail address.

App construction and points of interest

Framework

This a typical Single Page Application written in AngularJs. Our team developed a web application, which runs as Eikon application.

Framework:

  • Programming language: JavaScript
  • Server-side environment: NodeJs
  • Client-side environment: Chrome browser
  • UI and dependency injection: AngularJs
  • Auxiliary tools: jQuery
  • Build system: Grunt
  • Eikon integration: JET

Application structure

Based on requirement above one can sketch up a data flow diagram:

 

 

This diagram shows two screens the user will access, namely quote window and contacts editor. Next, it shows general idea on how the application is functioning: the data from contact list and from Eikon are fed into calculation core, which provides recalculated quotes to quote window.

The following diagram will be helpful in order to understand which entities are employed to deliver this functionality:

 

 

Names of the files which contain modules, controllers and services shown on the diagram are shown in block headers. Here's a structure of application folders for your reference:

  • web
    • components
      • calculator
        • template files
        • calculator.js   ←   CalculatorCtrl module
      • clients
        • template files
        • clients.js   ←   ClientsCtrl module
      • quotes
        • quotes.js   ←   quoteService angular.js service
      • utils
        • utils.js
    • css
  • index.html   ←   app bootstrap
  • trapp.js   ←   TradeAssistApp module

Application startup process

Application startup is performed in three major steps:

  1. Bootstrap AngularJs in regular way (by specifying ng-app and ng-controller attributes in <html> and <body> tags);
  2. Wait for DOM to load. This is done by adding native onload event handler in <body> tag, like this: <body onload="onloadHandler()";
  3. Wait for JET to load using JET.onLoad();
  4. Next, register application with JET by calling JET.init()

Here's a code snippet from index.html:

HTML part:

    	
            

    <body id='body' class="content-block" onload="onloadHandler()" ng-controller="AppCtrl">

        <!-- show either quote window or client list editor -->

        <div ng-include="getInclude()"></div>

        <!-- other stuff -->

    </body>

JavaScript part:

    	
            

    function onloadHandler() {

        // ... other stuff here

        JET.onLoad(function() {

            // choose application mode: if it is a quote window or a contact list editor

            if (isCalculator) JET.resizeTo(420, 423);

 

            // set up a communication channel with other app instances

            var subscription = JET.subscribe("/counterparties", updateCounterparties, null);

 

            // read list of counterparties

            updateCounterparties();

        });

    }

Important to note

This app uses JET 1.8.0, while at the moment when I write this, the latest version is 2.2.0. In latest version one should wait for not only JET to load, but also for each JET plug-in he or she uses. JET.Quote and JET.Settings are separate plug-ins, for example. And each plug-in has its own on-complete callback, which one has to use. In this case I suggest to wait unit JET is fully loaded with all its plug-ins and only then bootstrap the app explicitly.

Subscribing to a real-time quote stream

In order to start receiving real-time quotes on a given RIC one should use JET.Quotes plug-in, which provides a convenient fluent API:

    	
            

    _quoteSubscription = JET.Quotes.create()

        .rics([_currentRic])    //requested symbols

        .formattedFields(['BID', 'ASK', 'CURRENCY', 'TRD_UNITS', 'BASE_CCY', 'DSPLY_NAME']) // requested fields

        .onUpdate(onUpdate)     // update event handler

        .start();

In the code above onUpdate() function is an update callback. It's responsible for finding out what was updated and spreading this information further. It has a following definition:

    	
            

    onUpdate = function(subscription, ric, updatedValues) {

        // subscription: JET.subscription; an object with information on subscription;

        //    it allows to change or cancel subscription

        // ric: a ric which was updated

        // updatedValues: a dictionary [field name -> field value]

 

        // ... code here

    }

Instances interaction

After consulting with clients a decision was made that the trader should always have quote window on his screen, even when he's editing contact list. This means that there can be several app windows on same screen at the same time: few quote windows each with their own quote and client selected, and contact list editor.

Whenever the trader edits client list, the quote windows should receive updates immediately. This could be a tricky part in regular web development. Luckily, JET provides a very powerful feature call streams. In essence, it is an API to a named pipes facility, which provides a developer with two simple methods:

  • JET.subscribe(channel:string, handler:Function, context?:Object)
  • JET.publish(channel:string, message:string)

For both these calls a channel is a name of a channel you want to use. Be cautious: some names are reserved by the system. Hence I would suggest to create channel names like /<your app name>_<channel_name>

A handler is a function with the following signature: onMessage(message:string, channel:string). This function is called whenever a new message is sent to the channel, and it receives both channel name and message transmitted. A context object in subscribe method is an object which would become this for the handler function.

Finally, the message is a string containing the message being passed. It a string becomes a limitation, one could make use of JSON.stringify() and JSON.parse() in order to pass complex objects between applications.

Important to note

Using JET channels is restricted for App Studio applications. If you need to implement similar interaction I would suggest to do it either using polling (in our case we could create a process which would poll JET.Settings once per second), or use some sort of server-side interaction. Web sockets could be a good option. In this case a job of entering and updating contact list could be passed to back office staff.

Storing contact list

The contact list should be stored somewhere. It has to be available on any machine the where the user logs in. This capability is provided by JET.Settings plug-in.

There are two simple methods one can use:

  • JET.Settings.write({providerName: providerName, settingName: settingName, settingValue: settingValue});
  • JET.Settings.read(onRead, {providerName: providerName, settingName: settingName}, onError);

In these calls providerName must always be "Configuration", while settingName and settingValue are arbitrary strings. onRead and onError are callbacks which handle either successful read or failure.

Important to note

Do not confuse JET.Settings with JET.Archive. First one stores your data permanently and exists as a single entity, while JET.Archive is a session storage. Main use case for JET.Archive is a temporary storage for a data, which should not be lost should user restart the app by pressing F5.

Opening chart window

Another important feature our user asked for is opening chart for the currently selected RIC. This is done using JET.navigate(). Syntax is very simple:

    	
            

    JET.navigate({

        name: objectName,

        entities: [{

            RIC: ric

        }]

    })

For more details please refer to JET documentation.

Result

Here is what out main view would look like:

 

 

And here us the counterparty editor:

 

 

Conclusion

In this article we have shown that creating an app which uses JET and runs inside Eikon is very straightforward. We demonstrated how to:

  • bootstrap the app (JET.init() and JET.onLoad())
  • subscribe to quote updates (JET.Quotes object)
  • set up a channel of instances interactions (JET.publish() and JET.subscribe())
  • using persistent storage (JET.Settings object)
  • opening Eikon apps via JET.navigate()

Further enhancements:

As and App Studio developer you have a great flexibility when choosing your development stack. You can choose any server configuration you want and work with any type of database or external service.

It would be a great idea to:

  • create a database which will store contact list with all details;
  • create an administrative interface to that database and provide access to it for back office

or risk management staff;

  • create a web socket service which will send updates to traders when database is changed;
  • support trading with this app: automatically create tickets and send the the back office.

Links

DOWNLOADS

Article.AppStudio.Javascript.TradeAssist