ARTICLE

A simple MarketPrice object for EMA - Part 2

Olivier Davant
Product Manager Product Manager

Overview

In this second article I present a simple MarketPrice class that implements the concepts explained in the A simple MarketPrice object for EMA – Part1 article. The MarketPrice class presented here is a component of an abstraction layer built on top of the Enterprise Message API (EMA) Java edition and implemented in the ValueAddObjectsForEMA example library (read the Simplifying Content Access in EMA article to learn more about this set of value add objects).

More specifically, this second part presents:

  • The basic usage of MarketPrice objects.
  • An example application that demonstrates the different features exposed by the MarketPrice class.
  • A command line tool one can use to retrieve and output market prices in a human readable format or in a JSON format. This tool is a concrete example that leverages the MarketPrice class.
  • Some of the implementation details of the MarketPrice class and of the ValueAddObjectsForEMA example library.

Before you continue reading, I recommend you read the first part of this article and make sure you are familiar with the concepts explained in the 10 important things you need to know before you write a Real Time application article.

Disclaimer

The MarketPrice class and the ValueAddObjectsForEMA example library have been written by Refinitiv for the only purpose of illustrating a series of articles published on the Refinitiv Developer. The MarketPrice class and the ValueAddObjectsForEMA example library have not been tested for usage in production environments. Refinitiv cannot be held responsible for any issues that may happen if these objects or the related source code is used in production or any other client environment.

Content

About the MarketPrice class

How to use MarketPrice objects

Find out more in the example application

Experiment market prices with the ready to use MarketPriceSubscriber command line tool

Design notes and other implementation details

 

About the MarketPrice class

The MarketPrice class is an abstraction layer implemented on top of the EMA. Its purpose is to simplify the usage of this API for retrieving market price data items (MMT_MARKET_PRICE (6) domain type). The MarketPrice class implements the following features:

  • Synchronous and asynchronous item subscription
  • Partial subscription by field names or field IDs (EMA Views)
  • Item completion detection
  • Asynchronous notifications for Item completion, Images, Updates and Statuses.
  • In memory data cache (fields and states)
  • Random access (by name or by ID) to cached field values and field descriptions (dictionaries)
  • Partial updates management for partially updatable fields (e.g. ROW80_n fields)
  • Thread safety  

The MarketPrice class and the ValueAddObjectsForEMA example library are not a replacement layer for EMA, but must be used in conjunction with EMA. As you will see many EMA interfaces are used and exposed by the value add objects.

How to use MarketPrice objects

How to build a MarketPrice object for a particular instrument

MarketPrice objects are created using a builder class. This class allows you to set the different properties of a MarketPrice like the instrument name, the subscription mode or the functions that must be called for asynchronous notifications. MarketPrice objects have no setter methods.
Note: As the builder is the only way to set their properties, they can be considered as immutable objects – from the objects creation perspective at least. However, it is important to note that MarketPrice objects are not pure immutables because their logical state changes when they receive EMA events.

The following code snippet builds a MarketPrice for the AIRBUS stock traded on Euronext:

    	
            

​​    OmmConsumer ommConsumer = ...

    ...

    MarketPrice theMarketPrice = new MarketPrice.Builder()

                                          .withOmmConsumer(ommConsumer)

                                          .withName("AIR.PA")

                                          .withServiceName("ELEKTRON_DD")

                                          .build();

As you can see, MarketPrice properties are set by calling the different methods of the builder. In the above example these method calls are chained and organized in a block of several lines. This is done that way to ensure a better code readability. Once the properties are set in the builder, the MarketPrice object is created by a simple call of the build() method.
Note: A new object is created each time the build() method is called. Builder properties can be changed between these calls. That way you can use the same builder to build different MarketPrice objects.

Most of the properties are optional except for the OmmConsumer and the MarketPrice name. The OmmConsumer is used by the MarketPrice to send and receive messages from the real time platform. The name is obviously the name to the instrument to open (to subscribe to). If you do not set these properties, the build() method throws an IllegalStateException exception. The other properties have default values. You do not have to set these properties if you are fine with their default values.

Once the object built, nothing special happens. Indeed, at that stage the MarketPrice object is not yet opened. No subscription request has been issued yet to the platform.

Other builder calls are described in the sections below. The complete list is available in the ValueAddObjectForEMA reference guide.

How to open a MarketPrice synchronously?

The MarketPrice opening mode (a.k.a. subscription mode) must be indicated to the builder before the object creation. So, to use the MarketPrice class synchronously you must first create a synchronous MarketPrice object and then call its open() method. Like this:

    	
            

   MarketPrice theMarketPrice = new MarketPrice.Builder()

                                         .withOmmConsumer(ommConsumer)

                                         .withName("AIR.PA")

                                         .withSynchronousMode()

                                         .build();

   theMarketPrice.open();

It is important to note that for synchronous MarketPrice objects the open() method doesn’t return control to the calling tread before the MarketPrice is complete. This means that this method is blocking and may take a short while before returning. This also means that your application can use the data cached by the MarketPrice (fields and/or state) just after the open() method has returned control to your application thread.

In the above example, OmmConsumer events are supposed to be dispatched from another thread; either by an EMA thread (API_DISPATCH operation model) or by another thread of the application. If in the design of your application OmmConsumer events are dispatched from the current thread, you must call the withSynchronousMode() builder method with the autoDispatch parameter set to true, like this:

    	
            

​    boolean autoDispatch = true;

    MarketPrice theMarketPrice = new MarketPrice.Builder()

                                         .withOmmConsumer(ommConsumer)

                                         .withName("AIR.PA")

                                         .withSynchronousMode(autoDispatch)

                                         .build();

    theMarketPrice.open();

When the autoDispatch parameter is set to true, the open() method dispatches events of the OmmConsumer until the is MarketPrice complete.

Once the MarketPrice is opened, if you indicated an interest for updates and if the events of the OmmConsumer are properly dispatched, the cached image is automatically updated with the latest events received from the real time platform.

How to open a MarketPrice asynchronously?

By default MarketPrice objects use the asynchronous opening mode. So, you don’t need to do anything special to open a MarketPrice asynchronously. Just call the open() method once the object is created. Like this:

    	
            

    MarketPrice theMarketPrice = new MarketPrice.Builder()

                                         .withOmmConsumer(ommConsumer)

                                         .withName("AIR.PA")

                                         .build();

    theMarketPrice.open();

It is important to note that when the open() method of an asynchronous MarketPrice object returns, the internal cache is not yet populated. This is because at this stage no message has been received from the platform yet. With the asynchronous mode, the open() method returns with no delay. This means that the getFields(), getField(String), getField(int) and getState() methods will probably return a null value if you call them just after the open() method has returned control to the calling thread.

Once the MarketPrice is opened, if you indicated an interest for updates and if the events of the OmmConsumer are properly dispatched, the cached image is automatically updated with the latest events received from the real time platform.

How to close a MarketPrice?

In order to close a MarketPrice, just call the close() method. This closes the subscription to the instrument.

    	
            

    MarketPrice theMarketPrice = new MarketPrice.Builder()

                                         .withOmmConsumer(ommConsumer)

                                         .withName("AIR.PA")

                                         .build();

    theMarketPrice.open();

...

    theMarketPrice.close();

Once closed, the MarketPrice stops receiving events from EMA. The cached image and item state are cleared out.

When is my MarketPrice complete?

A MarketPrice is said complete after it received the required elements that allows your application to use it. In other words, it is complete after it received either a first image or a closed status (that could be caused by an error, for example).

You have 3 different ways to know if the MarketPrice is complete:

  • Either you subscribed with the synchronous mode. In that case the MarkePrice is complete as soon as the open() method returns. Then, you can call the getField(String), getField(int), getFields() or getStatus() methods to retrieve the items fields or the status.
  • Or you subscribed with the asynchronous mode and registered an OnCompleteFunction. In that case the function is called as soon as the MarketPrice is complete.
  • Or you subscribed with the asynchronous mode but did not register any OnCompleteFunction. In that case you must poll the isComplete() method to determine if the MarketPrice is complete or not.

The code snippets below illustrate these three different methods:

Synchronous mode

    	
            

​    MarketPrice theMarketPrice = new MarketPrice.Builder()

                                         .withOmmConsumer(ommConsumer)

                                         .withName("AIR.PA")

                                         .withSynchronousMode()

                                         .build();

    theMarketPrice.open();

    out.println("BID = " + theMarketPrice.getField("BID").value());

    out.println("ASK = " + theMarketPrice.getField("ASK").value());

Asynchronous mode with OnCompleteFunction set

    	
            

    // Lambda expression that will be called when the MarketPrice is complete

    MarketPrice.OnCompleteFunction printBidAndAsk = (marketPrice) -> 

    {

        out.println("BID = " + marketPrice.getField("BID").value());

        out.println("ASK = " + marketPrice.getField("ASK").value());

    };

        

    MarketPrice theMarketPrice = new MarketPrice.Builder()

        .withOmmConsumer(ommConsumer)

        .withName("EUR=")

        .onComplete(printBidAndAsk)                

        .build();

    theMarketPrice.open();                                    

    ...

Asynchronous mode without OnCompleteFunction

    	
            

    MarketPrice theMarketPrice = new MarketPrice.Builder()

        .withOmmConsumer(ommConsumer)

        .withName("EUR=")

        .build();

    theMarketPrice.open();                                    

    do{

        try{

            Thread.sleep(WAITING_LOOP_TIMEOUT_IN_MS);

        } 

        catch (InterruptedException exception) {}

    }while (!theMarketPrice.isComplete());

    out.println("BID = " + theMarketPrice.getField("BID").value());

    out.println("ASK = " + theMarketPrice.getField("ASK").value());​

How to retrieve the cached fields?

Once your MarketPrice is complete, it is automatically kept up-to-date with the latest values received from the real time platform (except if you did not register an interest for updates). You can retrieve the MarketPrice cached image at any moment. To this aim you can call either the getFields() method that returns the complete fields list (the image), or the getField(String) to retrieve a specific field by its name, or the getField(int) to retrieve a field by its ID. The following code snippets illustrate these three options:

Full image retrieval

    	
            

   ...

Collection<Field> fields = theMarketPrice.getFields();

fields.forEach(

        (field) ->

            out.println(

                   field.description().acronym()

                   + " (" + field.description().fid()+ ") = " +

                   field.value()

          )

);

Field by name retrieval

    	
            

​    ...

out.println("BID (22) = " + theMarketPrice.getField("BID").value());

out.println("ASK (25) = " + theMarketPrice.getField("ASK").value());​

Field by ID retrieval

    	
            

    ​ ...

out.println("BID (22) = " + theMarketPrice.getField(22).value());

out.println("ASK (25) = " + theMarketPrice.getField(25).value());

It is important to note that these methods return a null value if the MarketPrice cache has not been populated yet.

How to be notified asynchronously of new images, updates and statuses

If the purpose of your application is to use the MarketPrice real time data as soon as it changes on the platform, then you need to be notified of these changes. To this aim you must indicate to the MarketPrice builder the functions or lambda expressions that you want to be called asynchronously when new images, updates or statuses are received. The following code snippet demonstrates how to do it:

    	
            

// Lambda expression that prints an image

    MarketPrice.OnImageFunction printImage = (marketPrice, image, state) -> 

    {

        out.println("New image received.");

        out.println("Image: ");

      image.forEach(

            (field) -> out.println(field)

        );

out.println("State: " + state);

    };

 

    // Lambda expression that prints an update

    MarketPrice.OnUpdateFunction printUpdate = (marketPrice, update) -> 

    {

        out.println("New update received: ");

        out.println("Update: ");

      update.forEach(

        (field) -> out.println(field)

        );

out.println("State: " + state);

    };

 

    // Lambda expression that prints a status

    MarketPrice.OnStatusFunction printStatus = (marketPrice, state) -> 

        out.println("New Status received: " + state);

 

    // MarktePrice that uses the lambdas defined above

    MarketPrice theMarketPrice = new MarketPrice.Builder()

        .withOmmConsumer(ommConsumer)

        .withName("EUR=")

        .onImage(printAllFields)  // Indicates that printAllFields must be called on new images

        .onUpdate(printBidAndAsk) // Indicates that printBidAndAsk must be called on new updates

        .onStatus(printStatus)    // Indicates that printStatus must be called on new statuses

        .build();

    theMarketPrice.open();                                    

        ...

In the above example, the printAllFields(), printBidAndAsk() and printStatus() lambdas will respectively be called when new images, updates and statuses are received from the platform.

How to open a MarketPrice in snapshot mode?

If you are only interested by the image of a MarketPrice at a specific moment in time but do not want to receive subsequent images, updates or statuses, you can use the snapshot mode. That way, you only receive the first image of the instrument after open() is called. The MarketPrice will not be maintained up-to-date by other images and updates. Here is how to open a MarketPrice in snapshot mode:

    	
            

    MarketPrice theMarketPrice = new MarketPrice.Builder()

        .withOmmConsumer(ommConsumer)

        .withName("EUR=")

        . withUpdates(false)

        .build();

    theMarketPrice.open();  

        ...

How to specify the fields you want to receive?

If you are only interested in a limited list of fields, you have the option to specify these fields to the MarketPrice builder. As a result, the built MarketPrice will only receive these fields. It will also consume less memory and less processing time because it will receive less events and smaller messages from the real time platform.

The following code snippet creates and opens a MarketPrice object for BID and ASK fields only:

    	
            

​    MarketPrice theMarketPrice = new MarketPrice.Builder()

        .withOmmConsumer(ommConsumer)

        .withName("EUR=")

        .withField("BID")

        .withField("ASK")

        .build();

    theMarketPrice.open();

After open() is called the MarketPrice object only receives images and updates for the BID and ASK fields. The cached image is made of these two fields only.

Find out more with the example application

The above section explained the basic usage of the MarketPrice class. If you want to find out more, please refer to the reference guide and the MarketPriceStepByStep example application that comes with the ValueAddObjectsForEMA example library . This application demonstrates the different capabilities implemented by the MarketPrice class in 17 individual steps. Before each step, explanatory text is displayed to explain what will be demonstrated. You are then prompted to press <Enter> to start the step.

The source code of this application is a good resource to understand how to use MarketPrice objects as well as other value add objects (e.g. Dispatcher).

Building and running instructions are provided in the README.md file that comes with the ValueAddObjectsForEMA GitHub project.

Experiment market prices with the ready to use MarketPriceSubscriber command line tool

The MarketPriceSubscriber is a command line tool you can use to retrieve market prices in text or JSON format. This is a good example of a simple but real application that relies on the ValueAddObjectsForEMA example library and the MarketPrice class.

The source code of the tool is provided in the Article.EMA.Java.MarketPriceSubscriber GitHub repository. Building and running instructions are provided in the README.md file of this project.

The tool is also provided as a ready to use binary pack available for download at the bottom of this article.

Design notes and other implementation details

This section contains design notes and other details that should help you to understand, reuse, maintain or enhance the MarketPrice implementation classes provided by the ValueAddObjectsForEMA example library.

General comment

MarketPrice objects and the ValueAddObjectsForEMA example library in general have been designed as a set of objects and interfaces to be used in conjunction with EMA. These objects and interfaces provide a higher level of abstraction where it is needed, but they also rely on existing EMA interfaces and objects as much as possible. No need to reinvent the wheel. For example, MarketPrice images are made of Field objects defined by ValueAddObjectsForEMA example library. However, each Field exposes a description and a value made of interfaces and objects (ema.rdm.DictionaryEntry and ema.access.Data) defined within the EMA space.

objects.marketprice.MarketPrice

This is the interface of MarketPrice objects. This is the main interface used by client applications to manipulate market prices. This interface is implemented by the MarketPriceImpl class that encapsulates all the implementation details with the exception of the builder.

objects.marketprice.MarketPrice.Builder

The MarketPrice builder is the only exposed part of the MarketPrice implementation. This builder is implemented as an inner class of the MarketPrice interface. It contains a very basic logic that mainly consists of:

  • Holding the different properties of the MarketPrice object to be built
  • Providing default values for the unset properties
  • Checking that the mandatory parameters are set before building MarketPrice objects

The builder has been exposed that way, in inner class, to enable simple code writing that clearly links the builder to the interface of the objects it builds. Like this:

    	
            

​    MarketPrice mp = new MarketPrice.Builder()

            ...

        .build();

This makes code writing more elegant but also limits MarketPrices to a single implementation which is not really an issue in our case.

objects.marketprice.MarketPriceImpl

This class implements the MarketPrice interface and contain the main logic of MarketPrice objects:

  • Interface with EMA
  • Completion detection
  • Synchronous mode
  • Cached image management

This class also relies on the Field interface and FieldImpl class for holding field descriptions, values and managing partial updates.

objects.data.Field

This interface is used to the represent the individual fields of a MarketPrice. A Field exposes a description and a value:

  • The field description is represented by an ema.rdm.DictionaryEntry that fully describes the field based on the information contained in the fields’ dictionary used by the platform and EMA.
  • The field value is contained in a sub-type of the ema.access.Data as defined within the EMA space (e.g. OmmDate, OmmReal, OmmInt, OmmRmtes).

objects.data.FieldImpl

This class is the implementation of the Field interface. This class holds the field description, the field value and implements the logic to manage partial updates using the ema.access.RmtesBuffer interface.

This class is used to build the internal representation of the MarketPrice image. To this aim it preserves the Omm.Data received from EMA. This is done by cloning the received Omm.Data in a local and very basic implementation of the Omm.Data sub-types (OmmDateImpl, OmmRealImpl, OmmIntImpl, OmmRmtesImpl, etc). These classes have been re-implemented in the ValueAddObjectsForEMA example librarybecause the EMA implementations cannot be duplicated without an underlying message from the platform.

objects.data.Util

This helper class contains the logic for cloning the Omm.Data received from EMA into their in-memory representation used by MarketPrice images.