Strategy API usage examples

This chapter contains code listings of simple demo strategies along with description and examples of logger output. Demo strategies demonstrate various functionality of strategy API and Execution Platform behind the API.

The original source code can be found in DevTools distribution.

Scheduling Events

public class EventSchedulingStrategyExt extends StrategyExt {

   private final long ONE_SECOND = 1000L;
   private final long THREE_SECONDS = 3000L;

   @Override
   protected void initialize(StrategyIntializer strategy) throws PropertyValidationException {

      ActionBuilder oneSecondAction = createActionBuilder();
      ActionBuilder threeSecondAction = createActionBuilder();

      strategy.action(oneSecondAction
            .frequency(ONE_SECOND)
            .execute(ONE_SECOND_ACTION))
            .action(threeSecondAction
                  .frequency(THREE_SECONDS)
                  .execute(THREE_SECOND_ACTION));
   }

   protected Runnable ONE_SECOND_ACTION = () -> getPlatform().sendInfo("Event received after one second.");

   protected Runnable THREE_SECOND_ACTION = () -> getPlatform().sendInfo("Event recieved after three seconds.");

}

Sample output:

[INFO] Event received after one second.
[INFO] Event received after one second.
[INFO] Event received after one second.
[INFO] Event received after three seconds.

Strategy prints sample messages at frequency of one and three seconds. It contains two ActionBuilders, which are initialized with frequencies and different instructions to execute. Instructions are implemented inside Runnable’s class object run() method or as Lambda expression. They are executed separately with specified frequency.

Handling exceptions

public class ExceptionThrowingStrategyExt extends StrategyExt {

        @Override
        protected void initialize(StrategyIntializer builder) throws PropertyValidationException {
                invokeMethodWithUnexpectedException(null);
                getPlatform().sendInfo("Some actions after exception.");
        }

        private void invokeMethodWithUnexpectedException(Object someObject) {
                getPlatform().sendInfo("Some actions before exception.");
                getPlatform().sendInfo(someObject.toString());
        }

}

Sample output:

[INFO] Some actions before exception.

Strategy on initialization calls method that calls function with null argument, which in turn results in throwing NullPointerException from initialize method. Thrown exception immediately interrupts execution of initialize method, therefore second message does not get displayed.

Developer can throw and catch java exceptions inside strategy functions without any limitations as long as any uncaught exception will not get thrown outside function scope. If during strategy lifetime unforeseen exception is thrown, strategy gets terminated with status “failed” and no other functions on strategy are called by Execution Platform. Exception to this rule is throwing PropertyValidationException from functions initialize and propertyModificationRequested, which is described later.

Handling broker and feed status

public class FeedBrokerStatusStrategyExt extends StrategyExt {

   private boolean feedOnline;
   private boolean brokerOnline;

   @Symbol
   private MarketSymbol marketSymbol;

   @Override
   protected void initialize(StrategyIntializer builder) throws PropertyValidationException {
      ActionBuilder reportingAction = createActionBuilder();
      builder
            .brokerStatusChanged((brokerStatus) -> brokerOnline = brokerStatus)
            .feedStatusChanged((feedStatus) -> feedOnline = feedStatus)
            .action(reportingAction
                  .frequency(1000)
                  .execute(() -> getPlatform().sendInfo(
                        "Broker status: " + (brokerOnline ? "online" : "offline") +
                              " | Feed status: " + (feedOnline ? "online" : "offline"))));
   }
}

Sample output:

[INFO] Broker status: online | Feed status: online
[INFO] Broker status: online | Feed status: online

Strategy is printing messages about feed and broker status at frequency of 1 second. Strategy keeps track of feed or broker status after initializing strategy with feedStatusChanged() or brokerStatusChanged. Each time feed or broker changes his status, strategy reacts to this and executes Runnable object’s instructions given as method’s parameter.

Keeping track of feed and broker availability status is essential to proper operation of any strategy. If connection to feed is lost, it should suspend its actions until connection is restored due to outdated information regarding market state. If such situation lasts for a long time, strategy should consider withdrawing its orders. If connection to feed or broker is lost and then re-established after some time, strategy could behave differently depending on time it spent offline.

Initializing strategy

public class InitializationStrategyExt extends StrategyExt {

   @Property(label = "Numeric parameter")
   private String prop_someProperty = "2";

   @Override
   protected void initialize(StrategyIntializer builder) throws PropertyValidationException {
      getPlatform().sendInfo("Strategy property value: " + prop_someProperty);
      try {
         BigDecimal numberProperty = new BigDecimal(prop_someProperty);
         if (numberProperty.compareTo(BigDecimal.ZERO) <= 0) {
            throw new PropertyValidationException("Value must be greater than zero!");
         }
      } catch (Exception e) {
         throw new PropertyValidationException("Property is invalid. " + e.getMessage());
      }
   }
}

Sample output of succeeded initialization:

[INFO] Strategy property value: 10

Sample output of failed initialization:

[INFO] Strategy property value: null

Strategy reads and interprets initialization property values passed by user. Values are stored by the platform as Strings and need to be parsed into desired type. In this example it is property of BigDecimal type, which has to exist and value of which must be greater than zero. If these conditions are met, strategy is initialized. If not, strategy fails to start and no further output is displayed.

It is recommended practice to read and verify set of strategy properties stored by execution platform at strategy initialization in order to determine if strategy has all data necessary for proper operation. Those property values should be then stored in strategy variables and used during its operation.

Processing orders

public class OrderSendingStrategyExt extends StrategyExt {

   @Symbol
   private MarketSymbol marketSymbol;

   private MarketDataType[] CONTENT = {MarketDataType.LATEST_TICK};

   @Override
   protected void initialize(StrategyIntializer builder) throws PropertyValidationException {
      ActionBuilder tradingAction = createTradingActionBuilder();
      OrderBuilder orderBuilder = createOrderBuilder(marketSymbol);

      builder
            .subscribeToFeed(marketSymbol, CONTENT)
            .action(tradingAction
                  .frequency(1000)
                  .actionType(TradingActionType.NEW_ORDER)
                  .createOrderWith(orderBuilder
                        .withSymbol(marketSymbol)
                        .side((s) -> Side.Buy)
                        .withPriceLimit((s) -> new BigDecimal("100.00"))
                        .withQuantity((s) -> BigDecimal.ONE))
                  .onOrderFilled((report) -> getPlatform().sendInfo("Trade event (ID:" + report.getClientOrderID() + ") has been filled.")));
   }
}

Sample output:

[INFO] NEW order sent for AA order id: s1 quantity: 1 price: 100.00
[INFO] Order s1 is New
[INFO] Order s1 is Filled
[INFO] Trade event (ID:s1) has been filled.

Strategy sends single order every second, then while order is processed by broker it receives set of reports describing current state of sent order. Order sending can fail e.g. because of setting illegal values in order or lack of connection with broker.

After order object is created, minimal set of values need to be set in order to make order valid. Order values like portfolioID or brokerID are filled by execution platform with use of market symbol mappings supplied along with initialization properties. Strategy can create, replace and cancel its orders by calling function actionType with parameters NEW_ORDER, REPLACE_ORDER, CANCEL_ORDER.

Stopping Strategies

public class StoppingStrategyExt extends StrategyExt {

   @Override
   protected void initialize(StrategyIntializer builder) throws PropertyValidationException {
   }

   @Override
   public void onStop() {
      getPlatform().sendInfo("Strategy has stopped.");
   }
}

Sample output:

[INFO] Strategy has stopped.

When strategy is properly initialized, it changes its state to “running”. Strategy can be manually stopped by user and change its state to “stopped”. This state is terminal and cannot be further changed. Developer can react on stopping strategy by overwriting onStop() method.

Modifying Strategy Properties

public class PropertyModificationStrategyExt extends StrategyExt {

   public final String NAME_PROPERTY = "prop_someProperty";

   @Property(label = "Numeric parameter: ")
   private BigDecimal prop_someProperty = new BigDecimal(2);

   @Override
   protected void initialize(StrategyIntializer builder) throws PropertyValidationException {
      try {
         getPlatform().sendInfo("Strategy started, parameter value: " + prop_someProperty);
      } catch (NumberFormatException e) {
         getPlatform().sendInfo("Parameter must be a number!");
      }
   }

   @Override
   public void propertyModificationRequested(StrategyProperties properties) throws PropertyValidationException {
      String newProp = properties.getProperty(NAME_PROPERTY);
      try {
         prop_someProperty = new BigDecimal(newProp);
         getPlatform().setProperty(NAME_PROPERTY, prop_someProperty.toPlainString());
      } catch (Exception e) {
         throw new PropertyValidationException("Property is invalid:" + e.getMessage());
      }
      getPlatform().sendInfo("Strategy property new value: " + prop_someProperty);
   }
}

Sample output of succeeded modification:

[INFO] Strategy started, parameter value: 20
[INFO] Strategy property new value: 21

Sample output of failed modification:

[INFO] Property is invalid: (...)

Reacting to strategy properties value change is very similar to strategy initialization. Difference is that values are not obtained from execution platform. Instead, they are obtained from separate object that stores modification properties. When modification properties are read interpreted and validated, new values should be set on execution platform, so that user would be notified about the fact that strategy accepted new values. Strategy can refuse to accept new values if they are not valid or situation on market has changed in meantime. In such case strategy should notify user about that by throwing PropertyValidationException from function propertyModificationRequested. This is only case when throwing exceptions outside strategy function scope will not result in strategy termination.

Subscribing to market data feeds

public class FeedBrokerStatusStrategyExt extends StrategyExt {

   private boolean feedOnline;
   private boolean brokerOnline;

   @Symbol
   private MarketSymbol marketSymbol;

   @Override
   protected void initialize(StrategyIntializer builder) throws PropertyValidationException {
      ActionBuilder reportingAction = createActionBuilder();
      builder
            .brokerStatusChanged((brokerStatus) -> brokerOnline = brokerStatus)
            .feedStatusChanged((feedStatus) -> feedOnline = feedStatus)
            .action(reportingAction
                  .frequency(1000)
                  .execute(() -> getPlatform().sendInfo(
                        "Broker status: " + (brokerOnline ? "online" : "offline") +
                              " | Feed status: " + (feedOnline ? "online" : "offline"))));
   }
}

Sample output:

[INFO] Trade event received with values:
symbol: AA @ ESIM
size: 56
price: 112.10

Logic of strategy relies on external events, this includes data received from market. In order to receive market events, strategy must subscribe to feed for defined set of event types. Market events of different types are handled by using appropriate builder methods, in this example we used tradeReceived method to react to received trade events. Feed to which given market symbol is subscribed is determined by market symbol mappings sent to strategy along with initialization properties.

It is worth noting that it is possible to customize message’s output by using html markups. We applied it in message presented above to make the output more clear.