Tutorial: Implementing a Simple TWAP Algorithm
This section describes how to develop an execution algorithm using a TWAP (Time Weighted Average Price) order as an example. To keep code sections brief, only critical pieces are shown. Deltix provides the full source code of this example with the ES package.
Each execution Algorithm in Ember uses four classes:
- ExecutionAlgorithmFactory defines the static parameters of the algorithm instance and creates each instance. In most cases, this is a boilerplate code.
- ExecutionAlgorithm defines the algorithm execution logic. This is the main event handler for market data, orders, trading events, and timers.
- AlgoOrder (sometimes referred to as Parent order) defines the state of each algo order executed by the algorithm. Typically, each execution algorithm processes one specific type of algo order, such as TWAP. Since each algorithm handles multiple instances of algo orders, each instance captures the state of a specific order, including its input parameter, current state, etc.
- ChildOrder defines the state of orders emitted by the execution algorithm. Usually, child orders are DMA (Direct Market Access) orders. However, in more complex cases, they could be a different type of algo order that are routed to another execution venue or algorithm. Child orders typically belong to a specific parent algo order and the parent order is considered incomplete until it has at least one active child order.
Algo Order
The algo order, also known as the parent order, represents the state of an inbound trading request.
In this example, we will illustrate a simplified version of the TWAP order that takes three parameters:
- Start Time - Specifies when the order should enter the market.
- End Time - Specifies when the order should complete and aim to finish executing the entire order quantity.
- Drip Percentage - Defines the percentage by which the entire order size should be sliced (e.g., setting it to 5% results in executing the order in 5% slices). This parameter determines the interval between child orders slices.
To start, create a class called TWAPOrder
that extends the standard AlgoOrder
class and defines the parameters mentioned above:
class TwapOrder extends AlgoOrder {
private @Timestamp long startTime;
private @Timestamp long endTime;
private int dripPercentage;
...
}
Inheriting from the AlgoOrder
class provides a basic runtime state. For example, a list of child orders and helper methods to retrieve information like the currently executed quantity, etc.
Custom Order Parameter Parsing
The Ember API is influenced by the FIX protocol. In the case of custom order parameters, the API uses numeric keys associated with textual values (similar to tags and values in FIX).
Let's define these keys and implement the parsing code in our TWAPOrder
class:
static final int START_TIME = 8800;
static final int END_TIME = 8801;
static final int DRIP_PERCENTAGE = 8802;
void copyExtraAttributes(OrderRequest request) {
if (request.hasAttributes()) {
List<TradeAttribute> attributes = request.getAttributes();
for (int i = 0, size = attributes.size(); i < size; i++) {
TradeAttribute attr = attributes.get(i);
switch (attr.getKey()) {
case START_TIME:
startTime = TimestampParser.parseTimestamp(attr.getValue());
break;
case END_TIME:
endTime = TimestampParser.parseTimestamp(attr.getValue());
break;
case DRIP_PERCENTAGE:
dripPercentage = CharSequenceParser.parseInt(attr.getValue());
break;
}
}
}
}
In the copyExtraAttributes
method, we handle the parsing of custom order parameters. If the order request contains attributes, we iterate through them and switch on the attribute key. Based on the key, we extract the corresponding value and parse it accordingly. In this case, we parse the start time and end time as timestamps using the TimestampParser
, and the drip percentage as an integer using the CharSequenceParser
.
We will come back to the algo order later in this chapter when we go deeper into TWAP logic. For now, let's focus on child orders, which are slices of our parent TWAP Order.
Child Order
In the example described in this document, we use the standard class ChildOrder
to represent the state of child orders emitted by the TWAP execution algorithm. In more complex cases, you can subclass ChildOrder
to track an additional state.
Each child order can belong to at most one parent algo order. Parent algo orders may emit zero or more child orders. In special cases, the execution algorithm may send a child order that does not belong to any specific parent order (for example, an order that closes a combined position).
Execution Algorithm
Now let's define the class for the TWAP execution Algorithm itself:
class TwapAlgorithm extends AbstractAlgorithmEx<TwapOrder> {
TwapAlgorithm(String name, AlgorithmContext context, OrdersCacheSettings cacheSettings) {
super(name, context, TwapOrder::new, ChildOrder::new, cacheSettings);
}
...
}
In this example, we extended the standard class AbstractAlgorithm
, which provides boilerplate code to process trading events.
When calling the parent constructor, we pass in the following:
name
: The name of the algorithm.context
: The algorithm context.cacheSettings
: Settings for the orders cache.
We also provide two factory methods as arguments:
TwapOrder::new
: Used to construct the parent order (TwapOrder
).ChildOrder::new
: Used to construct the child order (standard classChildOrder
).
The upcoming sections will describe the parameters of the algorithm constructor in more detail.
The following sections describe the parameters of the algorithm constructor in more detail.
Algorithm Name
Each instance of execution algorithm has a unique name. This name identifies the algorithm among other order destinations defined in the system. The name is typically specified in the deployment configuration file, which will be described later. It is possible to deploy multiple instances of the same algorithm, each running with different parameters, by giving them distinct names.
Algorithm Context
The algorithm context provides various container services to the algorithm, including:
- Ember service - Allows the algorithm to emit trading events.
- Timers - Used to define periodic jobs or scheduled actions.
- Market Data - The algorithm can access market data to make informed trading decisions.
- Pricing Service - Provides functionality to lookup prices or retrieve security metadata about a contract.
- TimeBase API - Enables reading market data from a TimeBase database.
- Counters/Unique Sequence Generators - Generate unique identifiers.
Orders Cache
The AbstractAlgorithm
class uses two separate caches for inbound and outbound orders. In this example, these are represented by the TWAPOrder
and ChildOrder
classes, respectively.
Each order cache consists of two areas:
- Active Orders Cache - This cache stores active orders and is implemented as an unbounded cache.
- Inactive Orders Cache - This cache stores recent inactive orders and is implemented as a fixed-size cache.
Each cache uses object pool to avoid memory allocations (and associated GC) when processing new orders.
The order object lifecyle is as follows:
- When a new order request is received, the algorithm takes an order instance from the object pool and places it into the active orders cache. The order remains in this cache while the algorithm processes order events.
- Once an order reaches its final state, such as being
CANCELLED
orCOMPLETELY_FILLED
, it becomes inactive. The algorithm then moves it to the inactive orders cache. Inactive orders remain in this cache for a certain period of time and can be used to process out-of-band requests and events. - Eventually, inactive orders are pushed out of the cache and recycled back to the object pool.
Please note the following:
- Once an order is recycled, it loses all stored information.
- Avoid keeping references to inactive orders in your algorithm state.
- Recycled order objects may contain information about different orders as they are reused.
Additional Algorithm Parameters
An algorithm instance can be deployed with custom configuration parameters. For more information on these parameters and how to configure them, please refer to the AlgorithmFactory
section below.
Algorithm Factory
In the simplest form, the Algorithm Factory is a boilerplate for creating instances of the algorithm:
public class TwapAlgorithmFactory extends AbstractAlgorithmFactory<TwapAlgorithm> {
@Override
public TwapAlgorithm create(String name, AlgorithmContext context) {
return new TwapAlgorithm(name, context, getCacheSettings());
}
}
This factory class provides a way to specify deployment parameters.
For example, if we want to define a minimum order duration allowed by the algorithm:
public class TwapAlgorithmFactory extends AbstractAlgorithmFactory<TwapAlgorithm> {
private Duration minimumOrderDuration;
public void setMinimumOrderDuration(Duration minimumOrderDuration) {
this.minimumOrderDuration = minimumOrderDuration;
}
@Override
public TwapAlgorithm create(String name, AlgorithmContext context) {
return new TwapAlgorithm(name, context, minimumOrderDuration, getCacheSettings());
}
}
In the updated factory class, we have added a minimumOrderDuration
parameter and a corresponding setter method. This allows us to set the value for this deployment-specific configuration parameter.
Now, let's explore how to set values for these deployment-specific configuration parameters in the next section.
Algorithm Deployment
To deploy an algorithm, follow these steps:
Add the algorithm library to the Execution Server runtime CLASSPATH. One way to do this is by placing the algorithm and its supporting classes in a library (JAR) file and put in the lib/custom folder of your Ember installation. All JAR files found in this folder are automatically added to Java CLASSPATH by Ember startup scripts.
Define the algorithm in the ember.conf configuration file as shown below. (See the Ember Configuration Reference for more information about ember.conf format).
algorithms { // you may already have this section in your ember.conf
TWAP : ${template.algorithm.default} {
factory = "deltix.ember.service.algorithm.samples.twap.TwapAlgorithmFactory"
}
}
The above configuration tells Ember that there is a new algorithm instance called "TWAP" and specifies the factory class that can create an instance of this algorithm.
If you want to define additional settings, expand the configuration as follows:
algorithms { // you may already have this section in your ember.conf
TWAP : ${template.algorithm.default} {
factory = "deltix.ember.service.algorithm.samples.twap.TwapAlgorithmFactory"
settings {
minimumOrderDuration = 5 seconds // custom parameter
}
}
}
Make sure to set values for all parameters defined in the algorithm factory, or you will receive a "Required property is missing" error during Ember startup.
If you want to allow some of the algorithm factory parameters to have default values, you need to mark them as `@Optional' in the factory class as follows:
public class TwapAlgorithmFactory extends AbstractAlgorithmFactory<TwapAlgorithm> {
@Optional
private Duration minimumOrderDuration;
For a successful deployment, ensure that the algorithm library is in the CLASSPATH and the configuration in the ember.conf file is correctly specified. Ember will then be able to create an instance of the algorithm based on the provided factory class and configuration settings.
Custom Docker Image
When using Ember in the Deltix CryptoCortex product, there is a special docker container called “emberpack” that contains Ember itself and library of standard connectors. You can extend this library with your custom algorithm by creating a Docker image.
Here's an example of a Dockerfile that demonstrates how to generate a custom Docker image:
FROM https://nexus.deltixhub.com/deltix.docker/anvil/deltix-ember-pack:1.6.3
COPY custom /opt/deltix/ember/lib/custom
In this example, the base image is the "deltix-ember-pack" image from the Deltix Nexus repository. You can replace the version (1.6.3
) with the appropriate version you require. The COPY
command copies the contents of the custom
directory to the /opt/deltix/ember/lib/custom
directory in the Docker image.
Using a Gradle project to build the Docker image, the script relies on the following environment variables that define credentials for accessing the Deltix Docker repository:
export DOCKER_USER=***************
export DOCKER_PASS=***************
export DOCKER_REPOSITORY=registry.deltixhub.com
Once you have defined the environment variables, you can run the following command to build the Docker image:
./gradlew dockerBuildImage -PdebugDockerBuild=true
This command builds the Docker image based on the Dockerfile and pushes it to the specified Docker repository, using the provided credentials.