Class Layer1ApiSoundAlertMessage

java.lang.Object
velox.api.layer1.messages.Layer1ApiSoundAlertMessage
All Implemented Interfaces:
Layer1ApiStrategiesEchoMessagesLayer.StrategyEchoMessageFromLayer

public class Layer1ApiSoundAlertMessage
extends java.lang.Object
implements Layer1ApiStrategiesEchoMessagesLayer.StrategyEchoMessageFromLayer
This message triggers Bookmap to show a notification/play sound alert.

Notification system examples

If you want to jump right into examples, take a look at DemoStrategies project. Here is a brief explanation of what you can find:

  • velox.api.layer1.simpledemo.alerts.tradeprice.SimplePriceAlertDemo - a basic demo that shows how you can create and send a single alert. Its creation is described step-by-step below.
  • velox.api.layer1.simpledemo.alerts.simplegui.SimpleAlertGuiDemo - a "Hello-World" style demo showing how to incorporate your own GUI for managing notifications. Implemented step-by-step in Layer1ApiAlertGuiMessage javadoc.
  • velox.api.layer1.simpledemo.alerts.tradeprice.CustomPriceAlertDemo - a more elaborate example of the notification system usage. Shows how an addon can create alerts dynamically, using its own GUI.
  • velox.api.layer1.simpledemo.alerts.manual.Layer1ApiAlertDemo - "synthetic" example, allowing you to see as many notification systems as possible. Alerts are sent manually from the strategies dialog.

Notification system step-by-step guide

The notification system has multiple moving parts and might seem intimidating at first glance. Below is a step-by-step guide to creating a simple addon that sends alerts based on some market event.

First, let's define the framework of working with the notification system.

Just like in many other parts of Bookmap, an addon communicates with the Bookmap via special messages objects. That is, your addon should listen for messages via Layer1ApiAdminListener.onUserMessage(Object) and send them with Layer1ApiAdminProvider.sendUserMessage(Object)

Notification system workflow

The workflow looks like this:

Example of an addon leveraging notification system

Now, lets see how it all works together in a more real example

Lets say, that we want to develop an addon that tracks the trades, and if there is a trade with price larger than 10 - we notify a user.

First, we should create an entrypoint class, so it can be loaded by the Bookmap (this is the same process as for any other addon).

 @Layer1Attachable
 @Layer1StrategyName("Price alert demo")
 @Layer1ApiVersion(Layer1ApiVersionValue.VERSION2)
 public class SimplePriceAlertDemo implements
     Layer1ApiAdminAdapter,
     Layer1ApiDataAdapter,
     Layer1ApiFinishable {

     private Layer1ApiProvider provider;

     public SimplePriceAlertDemo(Layer1ApiProvider provider) {
         this.provider = provider;

         ListenableHelper.addListeners(provider, this);
     }

     @Override
     public void finish() {
     }
 }
 

Now, our addon can be loaded, and it also gets injected the Layer1ApiProvider - the key entity in Bookmap API, which gives access to various types of events (note the subscription with ListenableHelper). Later we will also use the provider to send messages to BM. In addition, we need to implement Layer1ApiFinishable, but we don't need to add any implementation for now

Also, our class implements Layer1ApiAdminAdapter - which gives us ability to listen for messages coming from BM by implementing Layer1ApiAdminAdapter.onUserMessage(Object)

In addition, we want to listen for trades, an for that we implement Layer1ApiDataAdapter, as its method Layer1ApiDataAdapter.onTrade(String, double, int, TradeInfo) does exactly what we want.

Next step - lets listen for the trades, and start simple - log into console a message, when the trade with price > 10 occurred:

 @Override
 public void onTrade(String alias, double price, int size, TradeInfo tradeInfo) {
     if (price > 10) {
         // Here, instead of printing to console, we later want to send an alert
         System.out.printf("Trade of price > 10 occurred, actual price={%f}, size={%d}%n", price, size);
     }
 }
 

Simple, isn't it? Now, instead of plain logging, we want to use the notification system. Lets go through its workflow:

To keep this example simple, we won't implement GUI for it. For an example of an addon that creates its GUI - take a look at Layer1ApiAlertGuiMessage javadoc

Lets think about our alert - how we want our user to see it. For this simple example, our alert will show popup, won't have sound notification and will be a single-shot (non-repeated) alert. It will be linked to all available instruments aliases. A description for the trigger of this alert might be simply - "Trade price > 10"

With this description in mind lets create a declaration message and send it from the class constructor. Also, we want to store the created declaration for later use as a class field:

 private Layer1ApiSoundAlertDeclarationMessage declarationMessage;

 public SimplePriceAlertDemo(Layer1ApiProvider provider) {
     this.provider = provider;

     ListenableHelper.addListeners(provider, this);

     declarationMessage = Layer1ApiSoundAlertDeclarationMessage.builder()
         .setTriggerDescription("Trade price > 10")
         .setSource(SimplePriceAlertDemo.class)
         .setPopupAllowed(true)
         .setAliasMatcher(Layer1ApiSoundAlertDeclarationMessage.ALIAS_MATCH_ALL)
         .build();
     provider.sendUserMessage(declarationMessage);
 }
 

If we did everything right - in Bookmap a new record should appear in the "Configure alerts" table - available via File -> Alerts -> Configure alerts. We WON'T see the notifications just yet, we are still setting them up.

Next step in the notifications workflow is greatly connected to this one:

  • Listen for declarations messages with a flag isAdd = false

The idea is - when your addon sees this type of declaration message - it should stop sending notifications of that type. Your addon gets this types of notifications when a user clicks on "Remove alert" button on Configure alerts table mentioned above. Thus, lets setup listening for this type of message:

 @Override
 public void onUserMessage(Object data) {
     Layer1ApiSoundAlertDeclarationMessage obtainedDeclarationMessage = (Layer1ApiSoundAlertDeclarationMessage) data;
     if (obtainedDeclarationMessage.source == SimplePriceAlertDemo.class && !obtainedDeclarationMessage.isAdd) {
         declarationMessage = null;
     }
 }
 

Note: Here we have only one declaration message, while in the real application you might have multiple - in that case you can store them in a Map using id as a key, and when a message with the same id arrives - remove the value from the Map. However, the main idea of this guide is to introduce concepts one-by-one, and use as little non-necessary functionality as possible, so we won't be bothering with the multiple declarations just yet.

As you might have noticed, we are not using the value of the declaration message - and it will be fixed soon.

For now, lets go to the next step in the notifications workflow:

The settings message is connected to a declaration message, and it can be used to notify your addon about settings changes, or, vice versa - your addon can notify Bookmap about the settings changes. Now, before we send the actual notifications, lets define the settings that will be used in the alert message, and send them to Bookmap. Also, we want to setup listening for the settings messages, which will look just like the listening for the declarations messages in the previous step. Below is the result of code changes:

 private Layer1ApiAlertSettingsMessage settingsMessage;

 public SimplePriceAlertDemo(Layer1ApiProvider provider) {
     this.provider = provider;

     ListenableHelper.addListeners(provider, this);

     declarationMessage = Layer1ApiSoundAlertDeclarationMessage.builder()
         .setTriggerDescription("Trade price > 10")
         .setSource(SimplePriceAlertDemo.class)
         .setPopupAllowed(true)
         .setAliasMatcher(Layer1ApiSoundAlertDeclarationMessage.ALIAS_MATCH_ALL)
         .build();
     provider.sendUserMessage(declarationMessage);

     settingsMessage = Layer1ApiAlertSettingsMessage
         .builder()
         .setDeclarationId(declarationMessage.id)
         .setPopup(true)
         .setSource(SimplePriceAlertDemo.class)
         .build();
     provider.sendUserMessage(settingsMessage);
 }

 @Override
 public void onUserMessage(Object data) {
     if (data instanceof Layer1ApiSoundAlertDeclarationMessage) {
         Layer1ApiSoundAlertDeclarationMessage obtainedDeclarationMessage = (Layer1ApiSoundAlertDeclarationMessage) data;
         if (obtainedDeclarationMessage.source == SimplePriceAlertDemo.class && !obtainedDeclarationMessage.isAdd) {
             declarationMessage = null;
         }
     } else if (data instanceof Layer1ApiAlertSettingsMessage) {
         Layer1ApiAlertSettingsMessage obtainedSettingsMessage = (Layer1ApiAlertSettingsMessage) data;
         if (obtainedSettingsMessage.source == SimplePriceAlertDemo.class) {
             settingsMessage = (Layer1ApiAlertSettingsMessage) data;
         }
     }
 }
 

Key points here: we are sending the settings message from the constructor, linking it to a specific declaration by id. As we decided in the very beginning, our notification has a popup, but no sound alert - and it is reflected in settings message creation.

Moving on to the next step in the workflow:

Now that we have everything in place, we are ready to send the actual notification:

 @Override
 public void onTrade(String alias, double price, int size, TradeInfo tradeInfo) {
     if (price > 10) {
         System.out.printf("Trade of price > 10 occurred, actual price={%.2f}, size={%d}%n", price, size);

         Layer1ApiSoundAlertMessage soundAlertMessage = Layer1ApiSoundAlertMessage.builder()
             .setAlias(alias)
             .setTextInfo(String.format("Trade actual price={%.2f}, size={%d}%n", price, size))
             .setAdditionalInfo("Trade of price > 10")
             .setShowPopup(settingsMessage.popup)
             .setAlertDeclarationId(declarationMessage.id)
             .setSource(SimplePriceAlertDemo.class)
             .build();
         provider.sendUserMessage(soundAlertMessage);
     }
 }
 

Here we specify all the data we want to show on the alert. Note that this message is linked with the Layer1ApiSoundAlertDeclarationMessage by the alertDeclarationId field, and the settings (popup/sound notifications status) are taken from the stored Layer1ApiAlertSettingsMessage. This ensures data integrity between Bookmap and your addon.

Note that although Layer1ApiSoundAlertMessage has many fields which might look intimidating, only the source and the declaration id are mandatory.

If you did everything correctly, the addon should be able to send notifications now, and you can also control the popup state (on/off) with the Bookmap GUI (by pressing the "toggle popup" button in "Configure alerts" table)

Full example source code can be found at DemoStrategies project on Github - check out velox.api.layer1.simpledemo.alerts.tradeprice.SimplePriceAlertDemo
See Also:
DemoStrategies