Ember Configuration Reference
Overview
The Ember can be used in diverse solutions: as a matching engine, as a FIX Gateway, as a platform to host trading SELL side and BUY algorithms and trading connectors, etc.
The module design and configuration flexibility make these diverse solutions possible.
Format
Ember configuration uses text files in the HOCON (Human-Optimized Config Object Notation) format described here.
HOCON is more flexible than the JSON (JavaScript Object Notation) format in that there are several ways to write valid HOCON. Below are two examples of valid HOCON.
Example #1:
settings: {
topic: "NIAGARA",
maxSnapshotDepth: 30 # This is a comment
}
Example #2:
settings {
topic = NIAGARA
maxSnapshotDepth = 30 // This is also a comment
}
If a HOCON configuration does not appear to be working, check the following.
- Curly brackets must be balanced. (Keep identation and formatting neat. This helps spot errors.)
- Quotation marks must be balanced.
- Duplicate keys that appear later take precedence (override earlier values).
General Pattern
The most pluggable services in Ember follow the declaration pattern shown below:
MYSERVICE {
factory: <fully-qualified-name-of-Java-implementation>
settings {
parameter1: value1 // custom
parameter2: value2 // parameters
}
}
This Spring Beans-like approach allows extending the system with custom-build services. See the Custom Service settings section for more information.
Configuration files
Most components have the default configuration described in the “template” configuration file ember-default.conf. The main configuration file ember.conf overrides and refines the default configuration. For example:
On startup, the Ember searches the following locations for configuration files:
- Java System properties (For example,
-Dember.home=/home/deltix/emberhome). - The file ember.conf in Ember home directory.
- The resource ember.conf in CLASSPATH.
- Resource ember-default.conf.
The last resource (ember-default.conf) is shipped with installation and defines default values for all server configuration parameters.
Configuration secrets
Currently, there are several methods of storing sensitive information in configuration files (passwords, API keys, etc.).
- Secret as a Service: Ember is integrated with:
- Hashicorp Vault - This separately installed service is free to use for smaller deployments. See this article for more information.
- AWS Secret Manager. See this article for more information.
- Azure Key Vault. See this article for more information.
- Hashing Passwords can be hashed using a special utility (
bin/mangle). Ember automatically detects an encrypted secret and decrypt it at runtime. See this article for more information. - Environment variables. Sometimes secrets can be securely injected into environment variables. You can reference environment variables in HOCON using
${ENV_VARIABLE_NAME}notation.
Configuration Reference
TimeBase
Almost every deployment of Ember requires the Deltix TimeBase service:
timebase.settings {
url = dxtick://timebasehost1:8011
}
If your TimeBase has User Access control enabled, you need to define them as well:
timebase.settings {
url = dxtick://timebasehost1:8011
username = ember
password = EVcec95166e01fcf5792b8fc215a5dfb62 # NB: encrypted
}
If TimeBase is configured to use TLS(SSL), use dstick:// in URL schema:
timebase.settings {
url = dstick://timebasehost1:8011
}
When using self-signed certificate on TimeBase server side be sure to configure Ember per instructions here (as TimeBase client).
OAUTH2 TimeBase connection
When TimeBase is using OAUTH2 authentication, use the following configuration stanza to define connection parameters:
timebase {
factory = "deltix.ember.app.Oauth2TimeBaseFactory"
settings = null
settings {
url = "dxtick://localhost:8011"
oauth2url = "http://localhost:8282/realms/timebase/protocol/openid-connect/token"
clientId = "client id goes here"
clientSecret = "client secret" # use hashed value or vault
scope = "api://quantoffice/shell openid profile" # optional
}
}
For more information about configuring OAUTH2 authentication read documentation for TimeBase and TimeBase Admin.
OMS settings
Validation Logic
The configuration stanza engine.validation configures various parameters that affect the validation of inbound trading requests:
- allowCancelOfFinalOrders – When set to true, OMS rejects attempts to cancel orders that are in a completed state. By default, ember forwards such requests to a destination venue even if the order seems to be complete (according to state maintained by Ember OMS).
- allowFastCancel - Allows cancelling of unacknowledged orders (some destination venues does not allow this).
- allowFastReplace - Allows replacing of unacknowledged orders or orders that have pending replacement requests (pessimistic/optimistic approach to cancel replace chains).
- maxPendingReplaceCount – When fast replace is allowed, this parameter controls how many order replacement requests may be pending (unacknowledged by order destination venue).
- pricesMustBePositive – When set, OMS rejects orders that have a negative limit price (negative prices may be normal in some markets, e.g., exchange-traded synthetics). Default value is true.
- maxOrderRequestTimeDifference –
This parameter helps to detect order requests that spent too much time in transit to Ember OMS. Make sure the order source periodically synchronizes the local clock with some reliable source. This setting was removed in Ember 1.10.38+. You can now control this behavior via a similar setting in the FIX Order Entry gateway. - allowReplaceTrader – Allows modification of order’s trader (e.g., to follow the CME tag 50 requirement). Mutually exclusive with using Trader as a risk projection key: if Trader is configured as a projection key, replace requests that attempt to change the trader will be rejected regardless of this setting. See Mutable Keys.
- allowReplaceUserData - Allows modification of an order’s user data (e.g., when users put some free text notes into this field). When the modification of an order’s user data is allowed, the UserData projection cannot be used in risk rules.
- nullDestinations – An array of destinations that are no longer needed but might have accumulated orders and positions (this setting simply suppresses ember startup warnings).
The following example shows default values for each parameter:
engine {
validation {
settings {
allowCancelOfFinalOrders = true
allowFastCancel = true
allowFastReplace = false
pricesMustBePositive = true
maxPendingReplaceCount = 10
}
}
nullDestinations = [ “CME”, “ILINK2” ]
}
Order Router
Order Router is an OMS component that controls where trading requests flow. Normally, requests are routed according to their destination. The custom order router can handle requests with an undefined destination or even override the intended destination in certain cases.
Here is an example of a simple order router:
engine.router {
factory = "deltix.ember.service.engine.router.SimpleOrderRouterFactory"
settings {
defaultDestination = SIMULATOR
# ‘true’ routes ALL trading request to destination defined as defaultDestination
force: false
# if destination is not provided, tries to use request’s exchange as destination
fallbackToExchange: true
}
}
This is another example of a custom order router that re-routes trading requests that are designated to KRAKEN to one of the trading connectors, depending on the trader’s group:
engine {
router {
factory = "deltix.ember.service.engine.router.custom.TraderGroupOrderRouterFactory"
settings {
interceptedDestination = "KRAKEN"
traderGroupToDestination : [
"NewYorkGroup : KRAKEN-US",
"LondonGroup : KRAKEN-GB",
"SingaporeGroup : KRAKEN-SG"
]
}
}
}
Changing the router on a production system may result in a change in system behavior:
All historical trading requests that relied on the previous routing destinations are routed according to the new router logic after restart.
Cache
The configuration stanza engine.cache allows tuning internal OMS cache parameters:
engine {
cache {
orderCacheCapacity = 16K
maxInactiveOrdersCacheSize = 4K
initialActiveOrdersCacheSize = 4K
initialClientsCapacity = 16
hashFunction = DEFAULT
mapType = CHAINING
}
}
Where:
- initialClientsCapacity – How many sources of orders to expect (not a limit, just a hint).
- initialActiveOrdersCacheSize – How many active orders to expect (not a limit, just a hint).
- orderCacheCapacity – Defines the total expected order count that ember keeps in memory at any moment in time (not a limit). This is a hint to the order pool to pre-allocate a given number of blank order instances.
- maxInactiveOrdersCacheSize – Defines how many inactive (REJECTED/CANCELLED/COMPLETELY_FILLED) orders ember keeps in memory per order source.
- hashFunction - Defines hash function used, one of DEFAULT | NATIVE | XXHASH | METRO. Default is DEFAULT. Since Ember 1.15.
- mapType - Defines underlying data structure used for cache, one of CHAINING | LINEAR_PROBING | ROBIN_HOOD (default is CHAINING). Since Ember 1.15
** hashFunction = DEFAULT** indicates that Java Default hash function will be used for the cache.
** hashFunction = NATIVE** indicates that native hash from this blog will be used for the cache.
** hashFunction = XXHASH** indicates that xxHash from implementation here will be used for the cache.
** hashFunction = METRO** indicates that metro hash from implementation here will be used for the cache.
Ember OMS reloads journal on startup, but once started it but relies solely on in-memory cache of orders. This cache contains all active and last N inactive orders for each order source. The idea is to be able to process rare cases of "fill after cancel". For some exchanges Fill events may take slightly longer path than other order events like Cancel (and hence may arrive out of normal order lifecycle sequence). This out-of-sequence fill usually happens within a second or two of order completion.
We suggest the following math: let's say each order source (each algorithm, or API client) sends us up to 1000 orders per second. We want to be able to process late fills reported within 5 seconds of order completion. In this case inactive (completed) order cache size is set to 5000. Default value is 4096
# Maximum amount of inactive (complete) orders to keep in cache (per source)
engine.cache.maxInactiveOrdersCacheSize = 5000
We strongly advise not to set this to larger numbers - increased cache size leads to excessive memory consumption and slows down OMS order processing.
Engine Order Transformer
You can define a custom transformer of in-bound order requests.
Here is an example:
engine {
transformer: {
factory = …
settings {
…
}
}
}
Use this option with care. Ember stores the result of this transformation into Ember Journal. There is no durable trace of the original order request.
Transformation of inbound events or other message types is not yet supported.
Custom Instrument Metadata
In some rare cases, the engine needs access to additional information about each instrument. For example, a custom risk rule may want to keep track of some extra information like the instrument industry sector or per-exchange minNotional. This can be achieved using a custom Instrument Factory:
engine {
instrumentInfoFactory {
factory = "deltix.ember.service.engine.CustomExtendedInstrumentInfoFactory"
settings {}
}
}
Error Handling
The engine’s configuration stanza exceptions {} defines the system reaction to abnormal situations.
TimeBase Disconnect
Ember components (algorithms and connectors) can recover from the loss of a TimeBase connection, however, there may be a gap in market data, message loss in output channels, and other side effects. You can define how Ember should react to the loss of a TimeBase connection:
engine.exceptions.timebaseDisconnect = HaltTrading
Possible values are:
Continue- Logs the error, let Ember recover.HaltTrading- Halts Trading (forces the operator to confirm that the system fully recovered on TimeBase reconnect).Shutdown- Graceful Ember shutdown.ShutdownLeaderWithFollower- Shutdown when leader-with-follower, continue when follower, or leader without follower.
Prior to Ember 1.10.14:
exceptions.haltOnTimebaseLoss (true)- Halts trading every time Ember loses its TimeBase connection.
Misc parameters
This section describes the remaining parameters. If you want to change these, you can place them inside the engine configuration stanza:
-
maxNumberOfRequestErrorsToLog (64) - Threshold on the amount of exceptions Ember prints into the output log (subsequent errors are logged as request reject reasons on messages). The engine has a performance counter for it.
-
maxInactiveUnknownWarningsToLog (16) - Maximum number of warnings about events for no-longer active (or unknown) orders. These events cannot be processed. The engine has a performance counter for it.
-
maxRiskVetosToLog (16) - Maximum number of risk rule vetos (order rejects) to log. The engine has a performance counter for it.
-
convertStatusEventsToNormalEvents (true) - A "do not send Order Status to algorithms" event. Convert them to normal events that report incremental differences.
-
useMaxRemainingQuantity (false) - Instruct OMS to enrich messages using
maxRemainingQuantityrather than the remaining quantity of the working order. Slower. -
allowUnrestrictedPositionRequests (false) - By default system is configured in "single API client can only see own positions" mode. Clients are identified by API keys that are mapped to order Source IDs hence by default Source ID is a required part of projection when requesting positions. When this security-driven restriction is removed, different API Clients can see the positions of each other (as well as system-wide positions).
Security Metadata (Instruments)
By default, Ember uses the local TimeBase stream “securities” to learn about Security Metadata (AKA security master). This stream is supported by most QuantServer/QuantOffice/CryptoCortex ecosystems.
On startup, Ember reads a copy of the security metadata from the “securities” stream. During run time, Ember watches this stream for updates (only for inserts/updates; deletes are ignored). You can override the stream name as follows:
instruments {
subscription {
stream = "securities"
filter = null # SELECT * FROM $stream WHERE $filter
}
useCentralSMD = true
}
Instrument filter
You can also define a filter that is used to skip some instruments. For example, the following line excludes instruments that do not have a TradeableSymbol column defined:
instruments.subscription.filter = "TradeableSymbol != NULL"
Instrument attributes filter
By default Ember loads all available non-empty instrument attributes. The non-standard attributes are exposed as key-value style custom attributes in case they are required for some custom logic. If security metadata stream contains a lot of unneeded custom attributes, you could optimize memory usage by by limiting the number of custom attributes loaded for each instrument. For example, you can do this by explicitly listing only the custom attributes that should be loaded:
instruments.subscripion.includeCustomAttributes = [ "minNotionalApplyToMarket" ]
minNotionalApplyToMarket is the only custom attribute used by Ember at this point. So unless your custom logic is using other custom attributes, you could safely apply this setting.
Instead of whitelisting custom attributes you could also blacklist them as follows:
instruments.subscripion.excludeCustomAttributes = [ "someUnneededAttribute" ]
Central Security Master
Ember can also provide an API wrapper to access the Deltix Central Security MetaData service. This centrally maintained service contains detailed information about FX, Crypto, and some FUTURE markets. This information can be used by algorithms like the Smart Order Router. If your environment has no access to the internet, or is not covered by this service, you can disable this feature in the following way:
instruments.useCentralSMD = false
Security Metadata Watch service
By default Ember loads all instruments from "securities" stream on startup and keeps live TimeBase cursor that listens for any changes (mostly additions of new instruments).
However when Ember is used in tandem with Universe Configurator you may want to switch to canonical approach: whoever wants to change "securities" stream must do it under write lock, Ember is notified every time write lock on "securities" is released and performs full re-load of instruments under read lock. To enable this mode add the following configuration stanza to your ember.conf:
instrumentsWorker {
factory = "deltix.ember.service.smd.CanonicalInstrumentServiceWorkerFactory"
settings {
}
}
-
Advantages of canonical approach - this is how other TimeBase clients watch securities changes.
-
Disadvantages of canonical approach - it is less efficient, it results in full reload of securities stream on any change.
Pricing Service
While trading algorithms receive market data prices directly from TimeBase, other components like position and risk use a pricing service. The pricing service provides access to recent BBO prices. This is a performance trade off - a direct subscription for market data requires extra CPU and memory resources.
Here is an example of a pricing service configuration for a simple case when market data comes from three TimeBase streams:
pricing {
settings {
liveSubscription.streams = [COINBASE, BINANCE, BITFINEX]
}
}
Alternatively, if market data is distributed via high-performance TimeBase topics, configuration looks like this:
pricing {
settings {
liveSubscription.topics = [COINBASE, BINANCE, BITFINEX]
}
}
Topics and Streams cannot be mixed.
If market data streams or topics have a lot of irrelevant instruments, you can limit your subscription by specifying what symbols and/or types of messages you want to subscribe for.
pricing {
settings {
liveSubscription {
streams = [COINBASE, BINANCE, BITFINEX]
symbols = [BTCUSD, LTCUSD, ETHUSD]
types = [deltix.timebase.api.messages.universal.PackageHeader]
}
}
}
Settlement Prices
The pricing service also provides previous day settlement prices for interested consumers.
To enable this data, add the settlementPriceSubscription configuration stanza as shown in this example:
pricing {
settings {
…
settlementPriceSubscription {
streams: [ "settlementPrices" ]
symbols: [ "TSLA", "AMZN", …] // Optional, all symbols if omitted
types: [ "deltix.timebase.api.messages.TradeMessage" ] // Optional, all message types if omitted
}
}
}
Ember expects the settlement prices stream to have the last known message broadcast enabled (so-called TimeBase unique streams).
Ember can extract settlement prices from traditional TradeMessages that have been around since QuantServer 1.0 or from the Universal Market Data format introduced in version 5.X. (In the latter case, StatisticsEntry[type = SETTLEMENT_PRICE] is used).
Historical Prices
The pricing service receives and caches live market data. However, for traditional markets that have periodic trading sessions, or for rarely traded instruments, there may be a need to warm-up a cache of BBO prices using historical data. Warm up is performed during server startup. The following configuration snippet shows how to warm up the pricing service with 1 hour of historical data:
pricing {
settings {
liveSubscription {
streams = [COINBASE, BINANCE, BITFINEX]
}
historicalDepth = 1h # specifies duration
}
}
A historical subscription can be fine-tuned with its own subscription stanza:
pricing {
settings {
liveSubscription {
streams = [COINBASE, BINANCE, BITFINEX]
symbols = [BTCUSD, LTCUSD, ETHUSD]
}
historicalSubscription {
streams = [COINBASE, BINANCE, BITFINEX]
symbols = [BTCUSD]
}
historicalDepth = 3d # specifies duration
}
}
Mockup Pricing Service
In some rare cases, you may need to run Ember without real market data.
There is a mockup version of a pricing service that can be configured as follows:
pricing {
factory = "deltix.ember.app.price.FakePricingServiceFactory"
settings {
fixedPrices: {
BTCUSD: 3909.70
ETHUSD: 133
LTCUSD: 31.899
LTCBTC: 0.008148
BCHUSD: 170.27
BCHBTC: 0.046
ETHBTC: 0.034045
XRPUSD: 0.39287
}
}
}
Main currency
The pricing service also helps some OMS and disk components with currency conversion. Risk limits, like the Net Open Position limit, covert positions in each individual currency to the main currency. The default main currency is USD.
pricing {
…
settings {
…
mainCurrency: USD
}
}
Advanced Pricing Service Settings
pricing {
settings {
exchanges = 32 # how many exchanges L1 NBBO aggregator can expect at most
useFullOrderBookProcessor = false # false = process only snapshots but save system resources; true = process every message, NBBO L2
reconnectDelay { # TimeBase reconnect settings: exponential delay with base 2
min = 5s
max = 60s
}
}
}
Algorithms
This section describes the configuration options for deploying Ember algorithms. See the Algorithm Developer’s Guide for more information about algorithm capabilities.
Algorithms are the building blocks of the custom business logic inside Ember. The following example shows the configuration of the BOLLINGER algorithm. As you can see below, each algorithm must be defined under the algorithms { } stanza.
algorithms {
BOLLINGER: ${template.algorithm.default} {
factory = deltix.ember.service.algorithm.samples.bollinger.BollingerBandAlgorithmFactory
settings {
bandSettings {
numStdDevs: 1.5
commissionPerShare: 0.001
openOrderSizeCoefficient: 1
profitPercent: 20
stopLossPercent: 40
numPeriods: 100
enableShort: true
enableLong: true
}
}
subscription { # input channels
streams = ["sine"]
symbols = ["BTCUSD", "LTCUSD"]
types = ["deltix.timebase.api.messages.TradeMessage"]
typeLoader: {
factory: deltix.ember.algorithm.pack.sor.balance.SORMessageTypeLoaderFactory
settings { }
}
}
}
}
Each algorithm configuration stanza has the following parts:
- Algorithm identifier: BOLLINGER or NIAGARA in this case. This identifier must be a valid
ALPHANUMERIC(10)text value. Basically, the identifier may use capital English letters, digits, and punctuation characters, and must not exceed 10 characters (See Deltix Trading Model for more information about theALPHANUMERIC(10)data type). - Algorithm Factory Class: This parameter tells Ember where an algorithm’s code can be found during startup.
- Settings: This configuration block defines the custom deployment parameters of each algorithm. Each setting in this block corresponds to a property of algorithm factory class.
- Subscription: If an algorithm uses a standard way of receiving market data, this section references the source of it. Typically, this is a set of TimeBase Streams or Topics that broadcast market data inside Deltix infrastructure. Optionally, a subscription can be limited to a specific set of instruments (defined by the
symbolsparameter).
Let's review one more example of an algorithm definition:
NIAGARA: ${template.algorithm.NIAGARA} {
settings {
symbols: ${contractsString}
bookSnapshotInterval: 15s
maxSnapshotDepth: 64
feedTopicKey = "l2feed" #output channel
validateFeed = true
}
}
Algorithm Market Data Subscription
In the above sample, the subscription stanza defines what market data is available to an algorithm.
Note the difference between Stream-based and Topic based market data configuration - streams are defined inside subscription { } stanza and can include symbol and message type filters. Algorithm cannot define symbol or message type filters for topics - you have to consume entire topic feed.
An algorithm can subscribe for market data from TimeBase streams:
BOLLINGER: ${template.algorithm.default} {
subscription {
streams = ["stream1", "stream2"]
symbols = ["BTCUSD", "LTCUSD"]
types = ["deltix.timebase.api.messages.TradeMessage"]
channelPerformance = MIN_CPU_USAGE
allInstruments = false
}
}
A subscription stanza may have the following optional parameters:
- symbols - Defines a symbol filter (a list of instrument symbols that the algorithm receives, usually a subset of what is available in the market data streams). If this parameter is missing or set to null then algorithm will subscribe for all symbols in the provided TimeBase streams. Special case is empty symbols array - this configuration will deploy algorithm with empty initial subscription (the algorithm may still add symbols during runtime).
- note: missing
subscriptionstanza deploys an algorithm without market data. - types - Defines a type filter - a list of message types that the algorithm receives. When this setting is skipped, the algorithm gets messages of all types. Types are identified by the TimeBase type name, which usually matches the name of the corresponding Java message class, for example "
deltix.timebase.api.messages.TradeMessage". - typeLoader - Defines a custom TimeBase type loader. Type loaders allow mapping TimeBase message types to Java message classes available in the Ember CLASSPATH. The type loader is defined by the factory class (as usual for config). See the Type Loader section in Algorithm Developer's Guide for more information.
- channelPerformance - defines TimeBase market data cursor operation mode. Possible values are:
MIN_CPU_USAGE- Prefer to minimize CPU usage. Don't do anything extra to minimize latency (default mode).LOW_LATENCY- Prefer to minimize latency at the expense of higher CPU usage (one CPU core per process).LATENCY_CRITICAL- Focus on minimizing latency even if this means heavy load on CPU. Danger! Read Timebase cursor API before using.HIGH_THROUGHPUT- Prefer to maximize messages throughput. For loopback connections IPC communication will be used.
- allInstruments - normally when algorithm tracks instrument metadata (from "securities" TimeBase stream) only for instrument set that matches market data subscription. This flag configures algorithm to track instrument metadata for all symbols in securities stream. In other words, when "allInstruments = true", algorithm will track securities metadata for all available instruments in configured securities stream. When "allInstruments = false" or not specified, algorithm will only track securities metadata for instruments that it is subscribed to.
Subscription Examples
In the following example algorithm subscribes to AAPL and MSFT instruments from the NASDAQ TimeBase market data stream:
subscription {
streams = ["NASDAQ"]
symbols = ["AAPL", "MSFT"]
}
In the following example algorithm subscribes to market data for all symbols (all instruments) from NASDAQ stream:
subscription {
streams = ["NASDAQ"]
}
Identical configuration:
subscription {
streams = ["NASDAQ"]
symbols = null
}
In many cases, trading algorithms only need market data for instruments that this algorithm wants to trade. Avoid subscribing to entire stream of market data where possible, as this may be wasteful.
In the next example you can see initially empty market data subscription:
subscription {
streams = ["stream1", "stream2"]
symbols = [] # empty filter means "no market data initially"
}
an algorithm may control market data subscription during runtime using special API.
And finally, algorithms that do not need market data may skip the subscription section completely:
NOMARKET: ${template.algorithm.default} {
// subscription block is not present
}
Market Data from TimeBase Topics
As an option, an algorithm may receive market data from a TimeBase topic:
BOLLINGER: ${template.algorithm.default} {
topic = "topic1"
}
or multiple topics:
BOLLINGER: ${template.algorithm.default} {
topics = ["topic1", "topic2"]
}
There is no per-symbol filtering capability for topics.
Key differences:
- Topics are defined outside the subscription stanza
- Topics cannot define type/symbol filters. The entire content of the topic is fed into the algorithm.
Starting from Ember version 1.14.67 algorithm receive data from both topics and streams:
BOLLINGER: ${template.algorithm.default} {
topics = ["topic1", "topic2"]
subscription {
streams = ["stream1", "stream2"]
}
...
Furthermore, Deltix Universe Configurator has special convention - when market data connector output name starts with % sign it automatically creates a topic with provided name (and also instructs TimeBase to keep carbon-copy stream. For example, when Coinbase exchange connector uses %COINBASE as output name, the data will be broadcasted into %COINBASE TimeBase topic and also recorded into COINBASE TimeBase stream.
Ember supports this naming convention. In the following configuration algorithm will be receiving market data from to %COINBASE Topic.
BOLLINGER: ${template.algorithm.default} {
subscription {
streams = ["%COINBASE%"]
}
...
This was done to simplify conversion of existing setups from streams to topics for market data delivery. One can simply add % prefix to stream name in Universe Configurator and Ember configuration to switch existing setup to Topic. More about this subject can be found in Algorithm Developer's Guide.
Algorithm Configuration Template
The template define low-level settings available for each algorithm. For example:
- CPU idle strategies
- Default order cache capacity
- Interval of reconnection to TimeBase market data in case of failures
template.algorithm.default {
queueCapacity = ${template.queueCapacity.default}
idleStrategy = ${template.idleStrategy.default}
# exponential delay with base 2
reconnectDelay {
min = 5s
max = 60s
}
settings {
orderCacheCapacity = 4096
maxInactiveOrdersCacheSize = 1024
initialActiveOrdersCacheSize = 1024
initialClientsCapacity = 16
}
}
To be accurate, we need to mention that in the first example, NIAGARA used a custom template. Algorithms like NIAGARA that Deltix provides out-of-the-box typically have a default set of settings defined in ember-default.conf.
Here is how this looks:
template.algorithm.NIAGARA: ${template.algorithm.default} {
factory = deltix.ember.quoteflow.niagara.NiagaraMatchingAlgorithmFactory
settings {
ackOrders: false
preventSelfTrading: true
approxNumberOfOrdersPerSymbol: 512
bookSnapshotInterval: 15s
maxSnapshotDepth: 100
feedTopicKey: null
feedStreamKey: null
marketCloseInterval: 0s # debug only
validateFeed: false # Enables Data Validator of market data feed published to stream/topic
}
}
Here is another example of deploying a PVOL algorithm using the template:
PVOL: ${template.algorithm.default} {
factory = "deltix.ember.algorithm.pack.pvol.PVOLAlgorithmFactory"
settings {
defaultOrderDestination = SOR
}
subscription {
streams = ["COINBASE", "BINANCE", "KRAKEN"]
symbols = [BTC/USD, LTC/USD, ETH/USD, LTC/BTC, BCH/USD, BCH/BTC, ETH/BTC]
}
}
Latency tracing
To enable latency tracing in your algorithm you need to add latencyTracer stanza like shown below:
algorithms {
PVOL: ${template.algorithm.default} {
...
latencyTracer {
statInterval = 15s
maxExpectedLatency = 100ms
maxErrorsToLog = 100
}
}
}
Here we are showing how to do this for PVOL algorithm.
- statInterval - How often algorithm will collect latency metrics and publish them into counters (which exposes them to downstream processing in Prometheus/Grafana). when this parameter is set to zero (default) latency tracing is disabled.
- maxExpectedLatency - internally this component uses HDR histogram to record each latency measurement, so in order to store this in memory efficiently we need to know latency range. This parameter helps to define maximum expected latency value. Default is 100 milliseconds. When a specific latency recording exceeds this maximum, an error is logged and the value is overridden with given maximum.
- maxErrorsToLog - throttles how many errors the Latency Tracer can log. Default is 100. Once this value of log messages is reached further logging is suppressed.
- ordersUseOriginalTimestamp - Normally tick-to-order requests use
OrderRequest.originalTimestampto correlate order submission time with tick time. That is the Tracer exects that fieldoriginalTimestampcontains market data (tick) as-received timestamp. However in some cases this timestamp is not available. This setting switches this latency meter to use main timestamp field of the request (OrderRequest.timestamp). Default is 'true'. Do not flip this setting to 'false' unless you understand the nature of your market data. - warmupCount - After system restart exclude first group of orders specified by this count from measurement. JVM Warm Up. Default is 1000.
In most cases you do not really need to change most of these settings. The simplest way to turn on latency tracing is to write
algorithms {
PVOL: ${template.algorithm.default} {
...
latencyTracer {
statInterval = 15s
}
}
}
Or in shorter form:
algorithms.PVOL.latencyTracer.statInterval = 15s
Latency values are reported in nanoseconds.
Trading Connectors
Trading connectors are configured like algorithms. Key differences:
- Each connector typically requires connection parameters to the execution venue to be explicitly defined. This could range from FIX Session credentials for FIX-based venues, to API keys for crypto venues. Remember that you can hash sensitive information or use the secrets as a service approach described above.
- To make each connector configuration more compact, Deltix provides connector configuration templates that predefine most common configuration parameters. For example, an API gateway host and port or trading session schedules.
Here is an example of a connector configuration:
include classpath("fix-connectors.conf") // (1)
connectors {
ES : ${template.connector.fix.es} { // (2)
settings : {
host = localhost
port = 9998
senderCompId = EMBER
targetCompId = DELTIX
password = null
execBrokerId = "TWAP"
schedule = {
zoneId = "America/Chicago"
intervals = [
{
startTime = "17:00:00",
endTime = "16:00:00",
startDay = SUNDAY,
endDay = FRIDAY
}
]
}
}
}
}
Notes:
- Here we import connector configuration templates.
- This line defines the trading connector “ES” based on the
connector.fix.esconfiguration defined in above template file.
See the Trading Connector Developer’s Guide for more information.
Fee Schedule
You can define a custom fee schedule (commissions) for a trading connector. See the Deltix Commission Rules Engine document for more information.
Here, we just provide a sample of a configuration with this feature:
COINBASE: ${template.connector.COINBASE} {
...
feeSchedule {
fees: [
{ tradingVolume = 0, takerFee = 0.25, makerFee = 0.15 }
{ tradingVolume = 100000, takerFee = 0.20, makerFee = 0.10 }
{ tradingVolume = 1000000, takerFee = 0.18, makerFee = 0.08 }
{ tradingVolume = 10000000, takerFee = 0.15, makerFee = 0.50 }
{ tradingVolume = 50000000, takerFee = 0.10, makerFee = 0.00 }
// etc ...
]
}
}
Fee Conversion
In addition to computing an exchange commission (fees), Ember can be configured to convert commissions to the order currency (term/quoted currency). For example, for BINANCE, the commission is provided in BNB currency. This feature converts all BNB-based commission amounts to the base currency of the trade. For example, BTC for a LINK/BTC trade, or USDT for a BTC/USDT trade:
BINANCE: ${template.connector.binance} {
…
feeConversion: true
}
DMA vs. Non-DMA Connectors
Most trade connectors provide direct access to the market (execution venue). We call these connectors DMA (Direct Market Access) connectors. In some special cases, an Ember connector may send orders to a remote destination that is served by another Ember instance. The difference may be important for risk computations.
For example, we may have a distributed system where a NY4 Ember server (hosted in Secaucus, NJ) that submits orders to the Nasdaq and NYSE exchanges, but also sends orders to another Ember Server co-located with CME (hosted in Aurora, IL). In this case, the NY4 Ember may want to consider the NYSE and NASDAQ connectors as DMA, but consider the CME connector, (which is actually a “proxy” connector to another Ember instance), as non-DMA.
By default, all connectors are considered DMA. If you want to change that, use the isDMA flag in the connector configuration:
CME: ${template.connector.delitxes} {
isDMA: false
settings { ... }
}
Please note that connectors may transmit either simple orders (MARKET, LIMIT, etc.) or complex algo orders (e.g., TWAP). The former orders are sometimes called DMA orders. There is some similarity, but these notions are not related: a DMA connector may send algo orders; also, a non-DMA connector may send DMA orders.
Trading Simulators
The Ember includes two simulators:
-
SIM - Executes limit orders according to their price and supports simple play scripts. SIM can be enabled as follows:
connectors {SIM: ${sim}}More information about SIM can be found here.
-
SIMULATOR - Uses market data to realistically execute orders. Note: This simulator is actually an execution algorithm.
algorithms {SIMULATOR: ${template.algorithm.SIMULATOR} {subscription {streams = ["COINBASE", "BINANCE", "BITFINEX", "GEMINI", "KRAKEN"]}}}More information about the SIMULATOR can be found here.
Ember Monitor Balance Publisher
This Ember Monitor backend component, when present, streams information about available balance on exchanges to WebSocket subscribers (such as Ember Monitor app). It relies on TimeBase stream produced by similarly named Balance Publisher (also known as Exchange Publisher). EmberMonitor displays available per-exchange balances on Exchange/Currency position tables inside Positions panel.
Configuration stanza below describes what TimeBase should be used, in which TimeBase stream to find balance information, and how often WebSocket subscribers should be updated. Default values should work for most cases:
balancePublisher {
timebaseUrl = ${timebase.settings.url}
streamKey = balances
publishInterval = 15s
}
Risk and Positions
There is a separate document that describes how positions are tracked and how risk limits are applied.
Here, we simply show one example of such a configuration:
risk {
riskTables: {
Destination/Currency: [MaxOrderSize, MaxPositionLong, MaxPositionShort]
Trader/Destination/Currency: []
TraderGroup/Destination/Currency: [MaxOrderSize,MaxPositionLong,MaxPositionShort]
Trader/Symbol: [MaxOrderSize,MaxPositionLong,MaxPositionShort]
}
allowUndefined: [Account, Exchange, Trader]
timeIntervalForFrequencyChecks: 1m
dailyPositionsResetTime: "17:00:00"
timeZone: "America/New_York"
}
See the settings section in the Risk and Positions tutorial for more information on these settings.
FIX API Gateway
The FIX Gateway configuration consists of three main pieces:
- The client database that defines where FIX sessions are defined
- The Market Data gateway that broadcasts market data to subscribers using FIX protocol
- The Order Entry gateway that processes trading flow from clients using FIX protocol
Market Data and Order Entry FIX Gateways implement Executable Streaming Prices (ESP) data flow. There is a forth component - RFQ Gateway that may compliment or replace Market Data gateway and implement RFQ/RFS flow.
Here is an example of a simple configuration:
gateways {
clientdb { // (1)
factory = deltix.ember.service.gateway.clientdb.test.TestGatewayClientDatabaseFactory
settings {
clientCount = 50
tradeCompIdPrefix: "TCLIENT"
tradeBasePort: 9001
marketDataCompIdPrefix: "DCLIENT"
marketDataBasePort: 7001
}
}
marketdata { // (2)
PRICEGRP1 {
settings {
topic: "NIAGARA"
}
}
}
trade { // (3)
TRADEGRP1 {
}
}
}
Remember that gateway names should use alphanumeric names (all capital, not more than 10 characters).
FIX Market Data Gateway
gateways {
marketdata {
MD1 {
settings {
host: "10.10.1.15" # interface on which gateway opens server socket
senderCompId = DELTIX
topic: "DARKPOOL" #Alternative: streams: [“CBOT”, “CME”, “NYMEX”]
maxLevelsToPublish = 64
minimumUpdateInterval = 50ms
outputFeedType = LEVEL_2
useSyntheticLevel2QuoteIds = true
schedule = {
zoneId = "America/New_York"
intervals = [
{
startTime = "17:05:00",
endTime = "17:00:00",
startDay = SUNDAY,
endDay = SUNDAY
}
]
}
}
}
MD2 {
..
}
}
}
Settings
Brief description of Market Data gateway settings:
- host - server network interface to bind to. Will bind to all if unspecified. Note that each FIX session will use unique port (see client database description).
- senderCompId - specifies FIX tag SenderCompId(49) to be used by FIX session. Default is "DELTIX".
- topic or streams - specifies where market data will come from (single TimeBase topic or one or more TimeBase streams).
- maxLevelsToPublish - See FIX Gateway documentation.
- expectedMaxBookLevels - See FIX Gateway documentation
- maxInputLevels - See FIX Gateway documentation
- minimumUpdateInterval - for snapshot-only market data gateway mode this parameter controls throttling.
- types and symbols - limit market data that market gateway to specific message types and contract symbols. No filtering by default.
- nanosecondTimestamps - configures gateway to use include nanoseconds in UTCTimestamp tags like TransactTime(60) and SendingTime(52).
- incrementalMode
- addTransactTime
- addExchangeId - see "Exchange ID in Quotes" section below
- addTradeId - enables transaction ID in trades (communicated via MDEntryID(276) tag)
- transportIdleStrategy
You may configure multiple Market Data Gateways with different data sources, order book depth, etc.
Additional information about Market Gateway configuration parameters
Market Data Source
Each Market Data gateway can re-broadcast market data from one or several TimeBase streams, or a single TimeBase topic.
For TimeBase streams, pass a list of stream keys. Also, a TimeBase streams subscription can define optional filters by symbols or TimeBase message types (e.g., to filter out only Level2 market data for streams that contain both Level1 and Level2).
For example:
marketdata {
MD1 {
settings {
streams: [ "CBOT", “NYMEX” ]
symbols: “CLZ20, CLF21, CLG21, CLH21, CLJ21”
types: [ “deltix.timebase.api.messages.universal.PackageHeader”]
...
The symbols filter is optional.
Market Data Type

The following market data types are supported as inbound/outbound feeds:
- Level 3 – Market by Order: This is the most detailed representation of a market state where one can see individual orders sitting on the market.
- Level 2 – Market by Level: One can see how much liquidity in total we have at each price level. In this case, all same-priced orders are grouped into a single market data entry and their combined quantity is reported for each price level.
- Level 1 – Top of the Book: One can only see the best bid and best ask (BBO). The same as Level2 with the Order Book Depth limit set to 1.