Order Request Transformer
Overview
Ember provides several APIs to customize default behavior. One of them is the Order Request Transformer. This hook lets you mutate an inbound order request before it is processed further, for example, to correct a destination, fill in a missing field, or strip out attributes that should not propagate downstream.
A custom Order Request Transformer can be plugged in at three different points:
- Per algorithm — between a custom algorithm and the Ember OMS. Configured under the algorithm's own
transformerblock. - Per gateway — between the FIX Order Entry Gateway and the Ember OMS. Configured under
gateways.trade.<gateway-id>.settings.transformer. - Engine-wide — applied to every inbound order request regardless of its source (gateways, algorithms, or the cluster proxy). Configured under
engine.transformer.
Use the engine-wide transformer with care. Ember stores the result of the transformation in the Ember Journal — there is no durable trace of the original, pre-transform request.
Ember's FIX Drop Copy gateway has an unrelated concept that is also configured as a transformer { factory = ... } block (for example, deltix.ember.service.dropcopy.deltix.ExchangeExecIdTransformerFactory). It implements a different interface and serves a different purpose — do not confuse it with the Order Request Transformer described here.
API
Here as in many other cases in Ember, the API operates on mutable request objects — an implementation modifies the request it is given in place and does not return a value.
/**
* A hook that allows to transform request. Can be plugged between algorithm and Ember OMS;
* or between FIX Order Entry Gateway and Ember OMS
*/
public interface OrderRequestTransformer {
boolean isReuseMutableRequest();
void onNewOrderRequest(final MutableOrderNewRequest request);
void onCancelOrderRequest(final MutableOrderCancelRequest request);
void onReplaceOrderRequest(final MutableOrderReplaceRequest request);
void onOrderStatusRequest(final MutableOrderStatusRequest request);
void onMassCancelOrderRequest(final MutableOrderMassCancelRequest request);
void onMassOrderStatusRequest(final MutableOrderMassStatusRequest request);
}
Each on*Request method is invoked once for every request of the matching type flowing through the pipeline. An implementation that has nothing to do for a given request type simply leaves the corresponding method body empty.
The Mutable*Request types expose setters for the fields most commonly touched by a transformer, including:
setDestinationId/setExchangeId— re-route a request to a different destination or exchange.setTraderId/setAccount/setParty/setClearingBroker/setClearingAccount— adjust trader and account attribution.setAttributes— attach or replace custom order attributes.setSettlementDate— fill in a settlement date on new order requests.
isReuseMutableRequest()
This method is a performance hint. It tells Ember whether the transformer can be handed the caller's own request object to mutate directly, or whether Ember must first copy the request into a private mutable buffer before invoking the transformer:
- Return
trueif the transformer does not need to preserve the original request object — Ember will cast and pass the original instance directly, avoiding an extra allocation and copy on every request. - Return
falseif the original request object must remain unmodified — Ember will copy it into a reusable mutable buffer first, and only that copy is passed to the transformer.
Most built-in transformers return true, since they only touch a small number of fields and have no need to preserve the original request.
API Contract
- Implementations must be fast and non-blocking — the transformer runs inline on the OMS, gateway, or algorithm thread that is processing the request.
- Mutations made by a transformer are visible to all downstream consumers, including the Ember Journal. Once a request has been processed at the engine level, there is no way to recover its pre-transform state.
MutableOrderStatusRequestdoes not exposesetExchangeId, unlike the other request types. A transformer that remaps destination/exchange across all request types needs to special-case order status requests.- Implementation is discouraged from performing blocking I/O (network calls, database queries) directly on the request-processing thread; any such lookups should be precomputed or cached ahead of time.
Sample
Here is a sample implementation of an Order Request Transformer in Java. It defaults the time-in-force of a market order to IOC when the order arrives without one set:
class MarketOrderIOCTransformer implements OrderRequestTransformer {
@Override
public boolean isReuseMutableRequest() {
return true;
}
@Override
public void onNewOrderRequest(final MutableOrderNewRequest request) {
if (request.getOrderType() == OrderType.MARKET && !request.hasTimeInForce()) {
request.setTimeInForce(TimeInForce.IMMEDIATE_OR_CANCEL);
}
}
@Override
public void onCancelOrderRequest(final MutableOrderCancelRequest request) {
// no-op
}
@Override
public void onReplaceOrderRequest(final MutableOrderReplaceRequest request) {
// no-op
}
@Override
public void onOrderStatusRequest(final MutableOrderStatusRequest request) {
// no-op
}
@Override
public void onMassCancelOrderRequest(final MutableOrderMassCancelRequest request) {
// no-op
}
@Override
public void onMassOrderStatusRequest(final MutableOrderMassStatusRequest request) {
// no-op
}
}
In this implementation, onNewOrderRequest() is the only method with real logic — it checks whether the incoming order is a market order without a time-in-force set, and if so, defaults it to IOC. All other methods are no-ops, since this transformer has no work to do for cancel, replace, status, or mass request types. isReuseMutableRequest() returns true because the transformer does not need to preserve the original request object.
Deployment of a Custom Order Request Transformer
A custom Order Request Transformer is deployed via a factory, the same way as other customizable Ember components. Your factory class must implement Factory<deltix.ember.service.OrderRequestTransformer>; see Custom Services for the general factory/settings pattern (bean-style setters, @Optional/@Required settings injection, etc.).
Depending on which stage of the pipeline you want to affect, wire the transformer block into one of three places:
Per algorithm — nested inside the algorithm's own configuration block:
algorithms {
MyAlgo: {
...
transformer: {
factory = "com.example.MyCustomTransformerFactory"
settings {
...
}
}
}
}
Per gateway — nested under gateways.trade.<gateway-id>.settings:
gateways {
trade {
OE1: {
...
settings {
transformer: {
factory = "deltix.ember.service.engine.transform.CaseTableMessageTransformerFactory"
settings {
rules: [
// (Destination1)? | (Exchange1)? => (Destination2)? | (Exchange2)?
"*|DELTIXMM => DELTIXMM|DELTIXMM",
"*|HEHMSESS1 => HEHMSESS1|HEHMSESS1",
"*|HEHMSESS2 => HEHMSESS2|HEHMSESS2"
]
}
}
}
}
}
}
Engine-wide — applies to every inbound order request regardless of source:
engine {
transformer: {
factory = "com.example.MyCustomTransformerFactory"
settings {
...
}
}
}
As noted above, an engine-wide transformer's output — not the original request — is what gets written to the Ember Journal.
Refer to the Ember Configuration Guide for more detailed information on Ember's configuration options.
Predefined Order Request Transformers
Ember provides several predefined Order Request Transformer implementations that you can use out-of-the-box, or use as a reference when writing your own.
-
deltix.ember.service.engine.transform.CaseTableMessageTransformerFactory- Remaps a request's Destination and Exchange fields using an ordered table of rules, each in the form
(Destination1)? | (Exchange1)? => (Destination2)? | (Exchange2)?. The first matching rule wins;*matches anything, and an empty side means "unset." - This is the best transformer to use as a model when implementing your own rule-based transformer.
- Remaps a request's Destination and Exchange fields using an ordered table of rules, each in the form
-
deltix.ember.service.engine.transform.MarketOrderGetsIOCTransformerFactory- Sets time-in-force to IOC on market orders that arrive without one set. No settings.
-
deltix.ember.service.engine.transform.NaiveSettlementDateTransformerFactory- Fills in
settlementDateon new order requests that don't already have one, based on a configurable timezone and end-of-day cutoff. - Settings:
timezone(defaultAmerica/New_York),endOfDay(default17:00:00).
- Fills in
-
deltix.ember.service.engine.transform.PartyGroupErasureTransformerFactory- Removes order request attributes derived from the FIX Party group (
party,clearingAccount,clearingBroker). No settings.
- Removes order request attributes derived from the FIX Party group (
-
deltix.ember.service.engine.transform.SessionTraderTransformerFactory- Sets
traderIdfrom the request'ssourceId. - Settings:
force(optional, defaultfalse) — whentrue, overwritestraderIdeven if it is already set.
- Sets
-
deltix.ember.service.engine.transform.OrderAttributesTransformerFactory- Attaches custom order attributes, and optionally overrides trader/account, based on rules matched against a request's symbol, destination, and/or trader.
- Settings:
rules(required) — a list of{ condition: { symbol?, destination?, trader? }, attributes?: [...], trader?, account? }entries.
Example:
transformer: {factory = "deltix.ember.service.engine.transform.OrderAttributesTransformerFactory"settings {rules: [{ condition: { destination: "TT", trader: "TTUser" },attributes: [{ key: 8453, value: "448=90000064|452=122|2376=24|447=P" }] }]}}
Adding or changing a transformer alters system behavior for all requests processed after the change. For an engine-wide transformer, this also applies to historical requests replayed from the Ember Journal after a restart.