Skip to main content

Ember Risk and Positions Tutorial

Overview

This document describes the Ember Execution Server's risk control mechanism. By default, risk checks are disabled.

To start controlling for risk associated with real-time trading, you must explicitly define one or more risk rules.

To create a risk rule:

  1. Choose a projection.
  2. Configure a risk case table with the chosen projection(s) and risk limit(s).
  3. Fill out the risk case table with specific risk limit parameters.

Glossary

TermDefinition
AttributeA single property of an order
Case rowA row in a risk case table that defines an individual risk case
Condition columnA column in a risk case table containing a projection attribute
PositionAn amount of a security or asset owned by an individual or other entity
Risk case tableA table that defines limits for all possible projection values
Risk limitA user-defined safety limit for a specific risk rule that an order must adhere to in order to pass
Risk ruleA piece of business logic that checks one or more risk limits
Root limitA risk limit that applies to all orders regardless of order attributes
Trade projectionA designated group of attributes important for risk control

Quickstart

This section explains how to send an order. At least one risk rule must be set up before you can send an order. This process results in the creation of a risk case table where you can specify risk limits for a risk rule, for example:

SymbolMaxOrderSize
BTC/USD10
ETH/USD100
LTC/USD1000

The table above and the example below show how to create a risk rule limiting the maximum order size (MaxOrderSize) for each specified instrument.

To create a risk rule:

  1. Configure a risk case table.
    a. Open the Ember config file: $EMBER_HOME/ember.conf
    b. Append the following configuration fragment to the config file:
risk {
riskTables: {
Symbol:[MaxOrderSize]
}
}
note

This sample sets up one customizable field--the MaxOrderSize for a given position. For more information on adding or customizing projections and risk limits in the config file, see the Configuring Risk Case Tables section.

  1. Restart the Ember service.

  2. Open the Ember web UI in your browser. To run Ember locally on a default port, go to http://localhost:8988.

  3. Using the navigation panel on the left, go to the Risks page.

  4. Click Add Risk Rule.
    a. In the Symbol column, specify a given position's ticker symbol.
    b. In the MaxOrderSize column, add a value to set a limit.
    c. Repeat to add more symbols.

  5. Click Apply Changes.

    risk-rules-screenshot-example

  6. Try sending new orders. Any orders that exceed the specified maximum order sizes are rejected.

Projections

Overview

A projection is a set of attributes that is used to determine which set of orders is aggregated into a position.

A trade projection can consist of a single attribute: {Attribute1}, or multiple attributes: {Attribute1, Attribute2, ...}.

In most cases, a projection includes an instrument (identified by the instrument's ticker symbol) and at least one other order attribute.

Let's consider several practical ways of grouping position attributes.

Server-wide Position

To monitor server-wide (or system-wide) positions where we don't need to distinguish between individual traders or accounts, the only attribute necessary for the project is the instrument symbol:

{Symbol}

Per-currency Position

Similarly, if we are trading in forex markets, we may want to monitor positions by currency:

{Currency}

Per-exchange Symbol Position

To track the per-exchange position size for each symbol, we can use a projection with two attributes:

{Exchange, Symbol}

Trader + Exchange + Symbol

If we want to further differentiate the individual positions of individual traders, we can add the {Trader} attribute to previous projection to narrow the scope:

{Trader, Exchange, Symbol}

Order of Attributes

Theoretically, the set of attributes defining a projection can be specified in any order. For example, {Trader, Exchange, Symbol} is identical to {Exchange, Trader, Symbol}.

However, there are two rules that the Deltix Execution Server follows:

  1. The instrument {Symbol}, {Currency}, or product root symbol ({Root}) must be listed last.
  2. For each system deployment, you must select one particular order of projection attributes and follow it.

Non-symbol Projections

In some cases, you may want to define a projection that does not contain any instrument-related attributes. For example, if you want to limit the trade order rate for specific traders, you can use a projection that only contains the {Trader} attribute:

{Trader}

Supported Attributes

  • Source - Order source, coming from a trading bot or order entry API
  • Destination - Order destination, see Exchange vs. Destination projections
  • Exchange - Order exchange
  • Account - Order account
  • Trader - Trader who sent the order
  • TraderGroup - C2 Trader Group that this trader belongs to
  • Symbol - Order symbol (contract symbol)
  • Currency - Base or term currency of the order for FX orders
  • RootSymbol - Root of FUTURE contract, as it is defined in instrument's security master. For example, "NG" (Natural Gas) for contracts like NGZ19 and NGM19.
  • Side - Order side (BUY, SELL, SELL_SHORT). Can be used for example to disable SELL_SHORT or BUY orders in some symbols
  • InstrumentType - For example, FX or SYNTHETICS
  • ModuleKey - Auto-trading strategy (e.g., QuantOffice Strategy ID)
  • PortfolioKey - Portfolio this order belongs to
  • SettlementDate - For FX domain only
  • UserData - Custom projection, used by QuantOffice Strategy or Market Maker
  • ClearingAccount
  • ClearingBroker
  • Party - Trading party

Exchange vs. Destination Projections

An order may flow through multiple intermediaries (each identified by the {Destination} attribute) before reaching an exchange.

For example, if a trader wants to trade on the CME exchange using a TWAP algo order:

  1. A trader sends a "parent" order to a TWAP destination ({Exchange} is set to CME).
  2. The TWAP algorithm sends a "child" order to a CME1 trading connector destination ({Exchange} is set to CME).

As far as position grouping is concerned, the system will see two orders going to the CME exchange, one order going to the TWAP {Destination} and one order going to the CME1 {Destination}.

Risk Rules & Risk Limits

Overview

Risk rules are a piece of business logic that specify checks that an order must adhere to in order to pass.

To create a working risk rule, you must configure a risk case table with a projection and risk limits. The projection and risk rules chosen for the risk case table determine what parameters you can specify values for during runtime to control risk.

Configuring Risk Case Tables

To create a risk case table, you must define a projection and choose risk limits to add the projection in the ES configuration file ember.conf.

The configuration example below defines three risk case tables:

risk {
riskTables: {
Symbol: [ MaxOrderSize ],
Account/Exchange: [ MaxRejectFrequency ],
Account/Exchange/Symbol: [ MaxPositionLong, MaxPositionShort ]
}   
}
  • The first risk case table allows you to control the maximum order size depending on the order symbol, using a {Symbol} projection and [MaxOrderSize] risk limit.
  • The third table allows you to control the maximum rejection frequency for an {Account, Exchange} projection using a [MaxRejectFrequency] risk limit.
  • The second table allows you to control maximum long and short positions for an {Account, Exchange, Symbol} projection.

System-wide risk limits that are not associated with a specific Symbol, Currency, or RootSymbol may use an empty projection (""). This is how this may be configured in ember.conf:

risk {
riskTables: {
"": [ MaxRejectFrequency ]
}
}

Settings

To customize your risk configuration further, you may include settings that fine-tune the risk behavior.

In the example below, three settings are introduced:

risk {
riskTables: {
Symbol: [ MaxOrderSize ],
Account/Exchange: [ MaxRejectFrequency ],
Account/Exchange/Symbol: [ MaxPositionLong, MaxPositionShort ]
}   
rejectUnmatchedOrders: true # default
allowUndefined: [ Account, Exchange ]
timeIntervalForFrequencyChecks: 1s
dailyPositionsResetTime: "17:00:00"
timeZone: "America/New_York"
}
  • rejectUnmatchedOrders - When set to "false", prevents the rejection of the order that order does not match any of defined the risk table cases, see the Case Tables chapter at the beginning of this document for more information. This mode is unsafe and should not be used in production without good reason. Default value of this setting is "true" (if an order is not covered by explicit risk case it will be rejected).
  • allowUndefined - Controls what happens when the order attribute used by the case table condition is not defined. By default, orders with missing risk-related attributes are rejected. For example, the order will be rejected if the order does not specify an exchange, but per-exchange position and tracking is enabled (like in the case tables above). If some of your orders may not have risk-related attributes defined you can override default behavior using this setting.
    This setting takes comma-separated list of risk-related order attributes that in some cases can come without values. For risk checking purposes an undefined case can be represented using the NULL condition value. See NULL condition cases section for more information.
  • timeIntervalForFrequencyChecks - Controls the time interval used by frequency-based risk checks. For example, when this parameter is set to "1s" (1 second) and MaxRequestFrequency is set to 100, the system will prevent sending more than 100 requests per second to a destination specified by each projection.
  • dailyPositionsResetTime - time of day when daily flow limits will be reset.
  • timeZone - timezone to interpret dailyPositionResetTime.

For information on additional settings, please get in touch with the support team.

Working with Risk Case Tables

Risk case tables consist of a projection and usually at least one risk limit. After configuring a table, you can use the risk case table to specify values for projection attributes and risk limits.

Header Row

Each of the attributes in the projection becomes a condition column and each of the risk limits become limit column in the header row of the risk case table.

In our examples, condition columns are highlighted orange and limit columns are highlighted blue. For example:

Condition1Condition2Limit1Limit2

Case Rows

Each individual risk case is defined in a case row.

E.g., a Trader "John" is limited to a MaxOrderSize of 100:

TraderMaxOrderSize
John100

General rules of the risk case tables:

  • Duplicate condition rows are not allowed.
  • The rows in a case table can appear in any order.
  • Each order is matched with exactly one case row.
  • If an order does not meet any of the conditions in a risk case table, it is rejected due to the "Unknown Risk Limit" reason by default. To change this, set rejectUnmatchedOrders setting to "true" (More information about this paramter can be found in the Settings section).

Basic Example

In this example, the risk case table uses a projection with one attribute, Account, and one risk limit, MaxOrderSize.

Three accounts are listed in the condition column: GOLD, SILVER, and BRONZE.

When the MaxOrderSize is 300 for a GOLD account, 200 for a SILVER account, and 100 for a BRONZE account, the case table is expressed like this:

AccountMaxOrderSize
GOLD300
SILVER200
BRONZE100

Orders are only fulfilled if the order size is within the limit set by MaxOrderSize for each account. For example, if someone wants to place an order for 400 on the GOLD account, the order is rejected because it exceeds the set MaxOrderSize risk limit of 300 for the GOLD account.

Additionally, if someone wants to trade on an IRON account, the order will be rejected regardless of its size because there is no risk limit set for an IRON account in the risk case table.

Wildcard Conditions

To apply the same risk limit value to multiple risk conditions, (e.g., multiple accounts), you can use a special wildcard symbol * (star).

AccountMaxOrderSize
GOLD300
SILVER200
BRONZE100
*50

The limit corresponding to the wildcard condition (*) is applied only when an order does not match the other, explicitly listed conditions. For example, the above table limits the order size for an IRON account to 50.

note

An explicit match is always preferred over a wildcard. See Order of condition columns.

Order of Rows

The order of the case rows is not important.

The following case table is effectively identical to the table in the previous section:

AccountMaxOrderSize
*50
GOLD300
SILVER200
BRONZE100

Multiple Conditions

Case tables may include more than one condition column and more than one limit column:

AccountExchangeMaxOrderSizeMaxNumberOfPositions
GOLD BINANCE 300 10
GOLD GDAX 200
BRONZE BINANCE 100 8
BRONZE GDAX 150 3

Order of Condition Columns

While the order of rows is not important, the order of condition columns is important.

The terms of the risk case table are evaluated from left to right.

Consider the following example:

AccountExchangeMaxOrderSize
* BINANCE 100
GOLD * 200

In the table above, a BINANCE order for a GOLD account can be matched with both case rows. However, since the Account column is evaluated first, and an explicit match is always preferred over a wildcard, the Execution Server selects the second case row to limit the risk and the order size is checked against a MaxOrderSize of 200.

Multiple Case Row Candidates

Consider a different example.

AccountExchange MaxOrderSize
* BINANCE 100
GOLD GDAX200

Let's say we want to place a BINANCE order for a GOLD account using the table above. The ES performs the following assessment:

  1. The ES evaluates the first condition column (Account):
    a. The ES can use either the first row (Account = *) or the second row (Account = GOLD).
    b. The second row (Account = GOLD) is chosen because wildcards are matched only if there is no exact match.
  2. The ES evaluates the second condition column (Exchange) of the second row:
    a. There is no match in the second row of the second condition column (Exchange = GDAX) for the BINANCE value.
  3. The ES returns to the first condition column (Account) to evaluate the remaining candidates.
    a. The ES now uses the first row where Account = * and Exchange = BINANCE.
    b. The order passes with a MaxOrderSize limit of <100.

NULL Condition Values

Orders may occasionally have missing fields. For example, the order Account may not be set. By default, the ES rejects orders with missing condition fields.

You can use a special condition keyword, NULL, to define how to handle missing order fields when processing the risk case tables. For an order with an unspecified attribute to match with a NULL condition, the allowUndefined setting must be activated.

AccountExchangeMaxOrderSize
GOLD BINANCE 100
NULL BINANCE 10

In the table above, an order that's missing a value in the account field is matched to the limit in the second row.

Columns Compatibility

Since some limits check very specific things, there are some restrictions:

  1. The Symbol and Currency condition columns cannot be used in the same case table.
  2. Some risk limits require either a Symbol or Currency condition column. These limits are: MaxLongPositionSize & MaxShortPositionSize.
  3. Some risk limits, like MaxNumberOfOpenPositions and DoNotTradeList, cannot be used in a risk case table containing the Symbol or Currency condition columns.

Case Table Identification

The system allows several case tables. However, it prohibits the definition of two case tables that contain the same sequence of conditions. For example, if you have a case table that uses {Exchange, Account} you cannot have another table with the same conditions, even if they have different case rows.

Special Cases

Root Limits

Root limits apply to all orders regardless of order attributes:

MaxOrderSizeMaxOrderFrequency
100 100
Projections Without Limits

A table without risk limits allows you to group orders under certain projections without applying limit checking. This is useful for tracking certain properties of your order flow, such as, realized PNL and average cost.

AccountSymbol
**

Predefined Risk Limits

The following is a list of Risk Limits provided out of the box.

note

Ember provides an API to develop custom risk rules and limits.

MaxOrderSize

Display name: Order Size

Description: Limits order quantity.

MaxOrderValue

Display name: Order Value Limit

Description: Limits order value (= quantity * price * contract multiplier).

MaxOpenOrders

Display name: Number of Open Orders

Description: Limits total number of active orders.

MaxPositionLong & MaxPositionShort

Display name: Instrument Long Position and Instrument Short Position

Description: Limits size of per-Symbol or per-Currency LONG and SHORT position (resp).

Restrictions: Only for instrument projections such as {Symbol}, {Currency}, and {RootSymbol}.

Tracking

positions-screenshot

The following information is available for each position:

  • Timestamp - time when position was opened
  • Size - actual position size (based on past trades)
  • Open BUY Size - total size of pending SELL orders (used to calculate "worst case scenario")
  • Open SELL Size - total size of pending SELL orders (used to calculate "worst case scenario")
  • Average Cost - Ember uses the Weighted Average method to calculate position cost
  • Realized P&L - Realized Profit and Loss
  • Market Value - computed based on position size multiplied by current price of security
  • Unrealized P&L - computed based on Market Value and cost

Size Limits

Position size limits verify the Worst Case Position (WCP) for each instrument, which determines if an order should be allowed to enter the market.

The following formula is used to calculate the Worst Case Position (WCP):

+ Current Position
+ Working Orders
+ Possible Fill from Current Order
----------------------------------
= Worst Case Position

Example:

The current position is LONG 10.

Working buys for a total of 4 contracts.

Working sells for a total of 3 contracts.

Scenario A:Scenario B:
Trader enters an order to BUY 7 contracts.Trader enters an order to SELL 7 contracts.
Worst Case LONG Position: (10 + 4 + 7) = 21Worst Case SHORT Position: (10 - 3 - 7) = 0

MaxNetPosition

Display name: Instrument Net Position

Description: Limits the size of per-Symbol or per-Currency NET positions.

Restrictions: Only for instrument projections {Symbol}, {Currency}, and {RootSymbol}.

MaxOpenQuantity

Display name: Max Open Quantity

Description: Limits the quantity of open BUY or SELL orders.

Restrictions: Only for instrument projections {Symbol}, {Currency}, and {RootSymbol}.

MaxDailyOrderCount

Display name: Daily Order Count

Description: Limits the number of orders that can be placed daily. End of day configuration is described here.

MaxDailyRequestCount

Display name: Daily Request Count

Description: Limits how many requests can be sent per trading day (order submission and modifications requests only, order cancellations are also counted but are exempt from this limit). End of day configuration is described here.

MaxSymbolPositions

Display name: Instrument Number Of Positions

Description: Limits the total number of open positions.

Restrictions: Since this limit counts the total number of open instrument positions, it cannot be set on an instrument projection and requires a projection with a Symbol sub-projection.

For example: `

risk {
riskTables: {
PortfolioKey/Exchange: [MaxSymbolPositions]
PortfolioKey/Exchange/Symbol: []
}
}

MaxCurrencyPositions

Display name: Currency Number Of Positions

Description: Limits the total number of open currency positions.

Restrictions: Since this limit counts the total number of open currency positions, it cannot be set on a currency projection and requires a projection with a currency sub-projection.

For example:

risk {
riskTables: {
PortfolioKey/Exchange: [MaxCurrencyPositions]
PortfolioKey/Exchange/Symbol: []
}
}

MaxDailyTradeVolume

Display name: Gross Daily Trade Volume

Description: Limits the daily gross trading volume. End of day configuration is described here.

MaxDailyPositionLong & MaxDailyPositionShort

Display name: Instrument Daily Long Position and Instrument Daily Short Position

Description: Limits size of per-Symbol or per-Currency daily LONG and SHORT position (resp).

Restrictions: Only for instrument projections such as {Symbol}, {Currency}, and {RootSymbol}.

MaxDailyNotionalValue

Diplay name: Gross Daily Notional Value

Description: Limits the daily gross notional value. The daily total includes the value of trades executed during the day and also of currently open orders. Order or trade value = quantity * price * contract multiplier.

Restrictions: Since each value included in total is in instrument quote currency,
this limit should only be used on projections where all trades are in the same quote currency.

MaxLoss

Display name: Maximum Loss

Halts trading if Profit and Loss (P&L) in associated position projection drops below configured limit. Limit is positive number that describes maximum loss amount in default system currency.

caution

Maximum Loss risk limit is computationally expensive and may slow down order processing and system restart time.

Restrictions: Since this limit counts the total P&L of open instrument positions, it cannot be set on an instrument projection and requires a projection with a Symbol sub-projection.

For example: `

risk {
riskTables: {
PortfolioKey/Exchange: [MaxLoss]
PortfolioKey/Exchange/Symbol: []
}
}

MaxPriceDifference

Display name: Order Price Limit

Description: Limits the difference of order price to reference market price. A recent BID is used as a reference price for BUY orders; and a recent OFFER is used as a reference price for SELL orders. If BID or OFFER are unknown this limit will fall back to the last TRADE price, or even opposite side of the market price.

This limit specifies the maximum difference in percentage. For example, a limit value of 0.15 means a 15% price difference (that is order price should be in 85%..115% range of reference price).

MaxAggressivePriceDifference

Display name: Order Aggressive Price Limit

Description: Same as the MaxPriceDifference risk limit but only limits the amount by which aggressive order price is allowed to cross the market.

MaxSettlementPriceDifference

Display name: Order Settlement Price Limit

Description: Same as the MaxPriceDifference risk limit but uses the previous day's settlement price as a reference point (rather than market price).

MaxSubmitFrequency

Display name: Order Submission Frequency Limit Per Sec

Description: Limits the frequency with which new orders requests are submitted.

note
  • MaxSubmitFrequency counts new orders only, if you want to limit rate of all trading requests consider using MaxRequestFrequency
  • MaxSubmitFrequency does not halt trading, but simply rejects orders that are over the limit.

Restriction: By default, all frequency limits use a 1-second time interval. There is a system level configuration parameter timeIntervalForFrequencyChecks that can change this time interval (see the Settings section for more information).

This limit uses rolling time window. For example, if MaxSubmitFrequency allows N orders per 1 second, for each new order it checks order count for the past second (relative to the last order submission time).

MaxReplaceFrequency

Display name: Order Modification Frequency Limit Per Sec

Description: Limits the frequency with which orders can be modified.

note

MaxReplaceFrequency does not halt trading, but simply rejects replacement requests that are over the limit.

Restriction: By default, all frequency limits use a 1-second time interval. There is a system level configuration parameter timeIntervalForFrequencyChecks that can change this time interval (see the Settings section for more information).

MaxRejectFrequency

Display name: Order Rejection Frequency Limit Per Sec

Description: Halts trading when the order rejection rate exceeds the limit. Trading is halted in projection path associated with the limit. For example, if MaxRejectFrequency limit is defined at trader level or source level (FIX session) then order flow only from this specific trader or source will be halted. Similarly, if the limit is defined at the top level - then system-wide trading halt will take place.

MaxRequestFrequency

Display name: Order Request Frequency Limit Per Sec

Description: Limits the frequency of all trading requests combined (order submissions, order replacements, and order cancellations).

note
  • MaxRequestFrequency counts all trading requests, including order replacement activity. If you want to limit submission rate of new orders using MaxSubmitFrequency.
  • MaxRequestFrequency does not halt trading, but simply rejects requests that are over the limit.

This limit uses rolling time window. For example, if MaxRequestFrequency allows N request per 1 second, for each new request it checks request count for the past second (relative to the last request submission time).

MinDuplicateInterval

Display name: Duplicate Order Interval

Risk Limit that checks if client is trying to submit identical orders in short interval. Identical in terms of Side, Quantity, Symbol, and Price.

note

This limit does not check for order ID duplication!!

OrderAckTimeout

Display name: Order ACK Timeout

Description: Halts projection path trading when an order remains unacknowledged for too long.

OrderLifetimeAlert

Display name: Order Lifetime Alert

Description: Specifies how long a MARKET/IOC order may remain active (in seconds). If an order exceeds this expected lifetime, the system logs an ERROR message (triggers alert).

OrderLifetimeTimeout

Display name: Order Lifetime Timeout

Description: Halts projection path trading when a MARKET/IOC order remains active for too long. Order lifetime limit is in seconds.

MaxLongExposure, MaxShortExposure, MaxNetExposure, MaxGrossExposure, MaxShortLongExposure

Display name: Max Long Exposure, Max Short Exposure, Max Net Exposure, Max Gross Exposure, Max Short and Long Exposure

Description: See the NOP Calculation Examples Microsoft Excel format for a complete description of these limits with examples.

Restrictions: Since this limit counts the total exposure over all currencies, it cannot be set on an instrument projection and requires a projection with a Currency sub-projection. For example:

risk {
riskTables: {
Account: [MaxLongExposure,MaxShortExposure]
Account/Currency: [] // you need to define ./Currency sub-projection, even if it will not have any limits
}
}

API

The API allows you to modify the risk limits while the server is running using the Create, Update, Delete row API calls.

  • The Update API call (UpdateRow) can only change limits.
  • Use the Delete (DeleteRow) and Create (AddRow) calls in tandem to make changes to condition columns.
enum ConditionKey {
Account, Exchange, Trader, Symbol, Currency, ...
}

CaseTableRow {
String [] conditionValues; // null element means "*" wildcard
String [] limitValues; // null element means unlimited
}

CaseTableDefinition {
ConditionKey [] conditions; // identifies each case table
LimitKey [] limits;
CaseTableRow [] rows;
}

RiskRuleMananager {
CaseTableDefinition [] list()
CaseTableDefinition get(ConditionKey[] caseTableId)
AddRow (ConditionKey[] caseTableId, CaseTableRow newRow)
UpdateRow (ConditionKey[] caseTableId, String [] oldConditions, CaseTableRow newRow)
DeleteRow (ConditionKey[] caseTableId, String [] conditions)
}
note

There is separate API to develop custom risk limits that is described here.

Example

The following example shows how updates to the wildcard conditions are handled, and how the UpdateRow, DeleteRow, and AddRow calls play out in the risk case table.

  1. A risk case table initially looks like this:
AccountMaxOrderSize
GOLD300
SILVER200
BRONZE100
*50
  1. After processing orders for the IRON and PLATINUM accounts, there is the following state in the memory. (The asterisk (*) indicates a wildcard condition.)
AccountMaxOrderSize
GOLD300
SILVER200
BRONZE100
PLATINUM*50* (default)
IRON*50* (default)
  1. When the API calls Insert ({Account=PLATINUM}, {MaxOrderSize=125}), it has the following effect:
AccountMaxOrderSize
GOLD300
SILVER200
BRONZE100
PLATINUM125
IRON*50* (default)
  1. When the API calls Update ({Account=\*}, {MaxOrderSize=0}), it has the following effect:
AccountMaxOrderSize
GOLD300
SILVER200
BRONZE100
PLATINUM125
IRON*0* (default)
  1. Since the ES does not allow modifying condition columns, a sequence of the following two API calls has the effect:
  • Delete ({Account=*})
  • Insert ({Account=DIAMOND}, {MaxOrderSize=50})
AccountMaxOrderSize
GOLD300
SILVER200
BRONZE100
PLATINUM125
DIAMOND50
  1. Orders for an IRON account are now rejected.

Limitations

  • Ember OMS supports only Average Price P&L calculation method. Other methods like FIFO, etc. are not supported.
  • Order attributes that participate in position projections are limited to use ASCII letters, digits, spaces, -(minus), _(underscore), and @ characters.

Advanced Topics

The Advanced Topics section includes the following topics:

Projection Reconfiguration

Special care must be taken when adding or removing a projection for a production trading server.

The Execution Server stores information about everything that happens in the trading system in a special set of files called the Journal. This includes past risk limit changes and trades that are part of the recorded history affecting risk and positions.

Adding New Projections

When adding a new projection, there is one important thing to remember: the initial position size calculated by the system may be inaccurate for the following reason.

The actual position size is calculated from past fills. As time goes by, the Ember journal may accumulate millions of fill messages. To keep the journal size within a reasonable limit, Ember uses snapshotting and compaction techniques. As a part of this process, the system aggregates fills into position snapshots. More specifically, the system remembers of current position sizes using special Position Snapshot messages. See Journal documentation for more information

note

Ember maintains position snapshots only for currently configured position projections.

When you add a new position projection to an existing system, some aggregated fills will be missed when the aggregation process did not know about them and there are no corresponding position snapshots.

To remedy this, you need to use either the Ember API or the Execution Server GUI to correct current positions for the newly added projection.

Removing Existing Projections

When you change the Ember configuration and remove some position projections, you may experience errors during system restart. More specifically, the Ember Journal may still contain some messages that describe removed projections.

Examples of these messages are:

  • Risk Limit definitions (somebody defined a risk limit for a projection that no longer exists).
  • Position Snapshot messages (recorded by the Journal compaction tool to aggregate multiple historical fills).

Use the journal-transformer tool to patch the journal and remove messages for obsolete projections. See Journal Transformer manual for examples.

Mutable Keys

Order attributes, like limit price or quantity, can be modified. In fact, there can be a chain of modification requests issued for an order and pending acknowledgement by the destination venue. Risk rules properly support this. For example, the largest pending order quantity is being used to calculate worst case scenario.

At the same time, risk rules do not allow changing order attributes that are used in projections. For example, an order's Exchange or Account attributes cannot be modified.

There are two special cases: the system supports the modification of an order's TraderId and UserData attributes. These exceptions can be enabled via the Ember configuration file:

engine {
validation {
settings {
allowReplaceTrader = true
allowReplaceUserData = true
}
}
}

Custom Risk Limits

You can specify the custom risk limits when configuring a risk case table in the ember.conf file. The configuration below includes entries for these limits under risk.riskLimits, along with the name of the factory class that is used to create the corresponding risk rules.

The example of the riskLimits member in the configuration:

risk {
riskTables: {
Account/Exchange/Currency: [ MaxPositionLong, MaxPositionShort ],
Account/Exchange/Symbol: [ MaxPositionLong, MaxPositionShort ],
Account/Exchange: [ MaxRejectFrequency, MaxCustomLimit, MinCustomLimit, MaxTradeLimit ]
}

customLimits: {
"MaxCustomLimit,MinCustomLimit": : {
factory: my.domain.CustomRiskRulesFactory
settings {
param1: param_value1,
param2: param_value2
}
},

MaxTradeLimit: {
factory: my.other.domain.OtherRiskRulesFactory,
settings {
}
}
}
}

The attribute name is the custom risk limit name that cannot match any of the built-in limits listed in the Supported Risk Limits section. The value is the fully qualified class name of the risk rules factory implementation class that must have an empty constructor and implement the following RiskRulesFactory interface:

public interface RiskRulesFactory {
/**
* Creates a new instance of RiskRule that handles specified risks limit.
* @param limitName Risk limit name
* @param path Projection path
* @param context Risk manager context
* @throws IllegalArgumentException if the limit is not supported by this factory
* @return RiskRule implementation that handles the limit specified by limitName parameter.
*/
public RiskRule create(String limitName, ProjectionPath path, RiskManagerContext context);

/**
* @param limitName Name of the limit handled by this factory
* @return RiskLimitDefinition of the limit specified by limitName parameter
* @throws IllegalArgumentException if the limit is not supported by this factory
*/
public RiskLimitDefinition getLimitDefinition(String limitName);
}

Where the RiskLimitDefinition class describes the limit of the value type, the RiskRule implementation class has the following attributes:

private String name;
private ValueType type;
private ValueCheck valueCheck;
private Class<? extends RiskRule> implClass;
private String displayName;
private RiskRulesFactory ruleFactory;
private final String description;
private final boolean daily;

Multiple entries in the customLimits member in the configuration can point to the same factory, and multiple limits handled by the same factory can share the same RiskRule instance. The instance is shared when the limits refer to the same RiskRule implementation class in their RiskLimitDefinition. When limits share a RiskRule implementation class, they must share a factory configuration and be listed in the factory key separated by comma. See MaxCustomLimit, MinCustomLimit under customLimits in the configuration example above.

To add configurable parameters to the custom risk rules factory, simply add a member variable with the same name and public set method.my.domain.CustomRiskRulesFactory, shown in the configuration example below, would have the param1 parameter variable and set method declared like this:

private String param1;

public void setParam1(String param1) {
this.param1 = param1;
}

Custom risk rules can be deployed in the same way as any other custom components. Simply place the JAR files containing your custom implementation (and the required dependency) in the /ember/lib/custom installation folder.

Deltix provides a Java sample of a custom risk rule upon request.

Importing Risk Limits from .CSV Files

This section describes how to import risk limits from CSV files.

  1. Place one or more .csv files into the risklimits directory under EMBER_HOME.
  2. Use the following file format:
  • The first line is a header. The header defines which risk case table this file will be loading. It contains projection attributes followed by limit names. The number of conditions and limit columns must match case table definition in ember.conf.
  • Each subsequent line represents a case row of the risk case table and is loaded using the INSERT command.
  • Use an asterisk (*) to define a wildcard condition. Use an empty value in the limit column to define an unlimited case.
  • Each file can load limits into the one existing case table. You can load multiple files into one case table.

Example:

Symbol, MaxOrderSize, MaxPositionLong, MaxPositionShort
BTCUSD,     10, 100, 10
ETHUSD,     50, 1000, 100
LTCUSD,     50, 1000, 100
*,,,

The last line in the table above is an example of an "otherwise there are no limits" case row.

Risk Update Utility

This section explains how to use the Risk Update utility. This utility lives in the class deltix.ember.sample.RiskUpdateSampleCSV. The source code is available as an Ember API sample.

caution

The utility uses the Ember RPC API to send several risk limit update messages to Ember. Make sure you feed the correct messages. The most common mistake is using the incorrect project (CSV headers).

note

The Ember journal keeps the messages you send, even erroneous ones. Use the Journal Transformer utility if you want to edit the Ember journal, for example, to delete an invalid risk limit update message.

To use the risk update utility:

  1. Place your risk limit CSVs under $EMBER_HOME/risklimits directory. File names are not important as long as each file has a .csv extension.

    ls $EMBER_HOME/risklimits
    source-limits.csv source-symbol-limits.csv

    cat $EMBER_HOME/risklimits/source-limits.csv
    Source, MaxSubmitFrequency, MaxRejectFrequency
    TUSER1, 550, 300

    cat $EMBER_HOME/risklimits/source-symbol-limits.csv
    Source, Symbol, MaxOrderSize
    TUSER1, BTCUSD, 10
    TUSER1, LTCUSD, 1000
note

Here we assume that Ember configuration has matching risk rules defined:

risk {
riskTables: {
Source: [ MaxSubmitFrequency, MaxRejectFrequency ],
Source/Symbol: [ MaxOrderSize ]
}
}
  1. Run this utility inside the Ember Docker container:

    java -cp /opt/deltix/ember/lib/deltix-ember-app*.jar deltix.ember.sample.RiskUpdateSampleCSV
  2. Sample output:

    12 Dec 22:55:17.938 INFO [main] Connecting to message bus with
    publication channel aeron:udp?endpoint=dev-ember-0:40450:1 and
    subscription channel aeron:udp?control=dev-ember-0:40452:2

    Sent risk update request
    {"$type":"RiskUpdateRequest","requestId":"1","projection":"Source","commands":[{"$type":"RiskTableCommand","cmdType":"UPDATE","conditions":[{"$type":"RiskCondition","projectionKey":"Source","value":"TUSER1"}],"limits":[{"$type":"RiskLimit","name":"MaxSubmitFrequency","value":"550"},{"$type":"RiskLimit","name":"MaxRejectFrequency","value":"300"}]}],"changedByUserId":"riskmanager","timestamp":"2019-12-12T22:55:18.102Z", "sourceId":"SAMPLE"}

    Sent risk update request
    {"$type":"RiskUpdateRequest","requestId":"2","projection":"Source/Symbol","commands":[{"$type":"RiskTableCommand","cmdType":"UPDATE","conditions":[{"$type":"RiskCondition","projectionKey":"Source","value":"*"},{"$type":"RiskCondition","projectionKey":"Symbol","value":"*"}],"limits":[{"$type":"RiskLimit","name":"MaxOrderSize","value":"100"},{"$type":"RiskLimit","name":"MaxPositionLong","value":"1500000"},{"$type":"RiskLimit","name":"MaxPositionShort","value":"1000000"}]}],"changedByUserId":"riskmanager","timestamp":"2019-12-12T22:55:18.104Z", "sourceId":"SAMPLE"}

DMA vs. Internal Order Flow

When the system processes the execution of algo orders implemented inside Ember, it may see multiple echoes of the same fill event.

For example, consider a situation when a TWAP order sends a child order to an exchange and receives a fill. This child fill event is translated into an additional fill event reported for the parent order. The risk rules need to be able to process these multiple fills and understand the actual position on the market. To tackle this, Ember uses the concept of Direct Market Access (DMA)­ orders, that are opposite to algo orders that go to an internal destination.

DMA orders have a trading connector, market simulator, or a matching engine as their destination. Custom Algorithms can also be tagged as DMA destinations.

  • Position projections that include an order destination or order source process the entire order flow.
  • All other projections process only DMA orders.

Position Keeping for Synthetic Instruments

For synthetics instruments (e.g., spreads), the Worst Case Position (WCP) is calculated for the instrument itself and for each individual leg.

For example, let's imagine we see a BUY 10 order for the synthetic spread A -- B:

  1. The initial state: The position for instrument A is LONG 25 and the position for instrument B is SHORT -50.
  2. The client places a BUY 10 order for a synthetic instrument "A -- B".
  3. To mitigate risk for the BUY 10 of A -- B:
  • The Worst Case LONG is LONG 35 (pertaining to instrument A).
  • The Worst Case SHORT is SHORT -60 (pertaining to instrument B).
InstrumentABA-B
Current position25-500
Order BUY 10 of A-B has the following effect on worst case positions:
Worst case LONG35010
Worst case SHORT0-600

Daily Limits

Some limits apply to daily flow (daily order count or trading volume). Daily limits are reset at the end of each day at the time specified in ember.config:

risk {
dailyPositionsResetTime: "17:00:00"
timeZone: "America/New_York"
}

End of day time must be configured if risk configuration includes any daily limits.

note

Changes to this parameter may have to take 24 hours to take effect. For example, on the day when this parameter is initially specified, daily limits will be reset immediately after Ember is restarted and then at the end of day according to the new setting. So the initial day period may span less than 24 hours

Known Issues

  • Projections with a lot of different keys (e.g., per-trader limits for 10000+ unique trader) take a performance toll (we still need to optimize it).
  • It may be hard to manage a large number of risk limits in the current GUI (ES Monitor, Risk Limits panel).

Appendix A. Risk Manager Diagram

risk manager diagram

Appendix B. GUI to Update Limits

In addition to the CLI and API interfaces, the Execution Server Monitor has a GUI to control and monitor limits. For each configured projection, a user can add a list of limits. Wildcards are supported.

risk rules screenshot

Position limits also can be seen on the Positions panel (columns on the right):

positions screenshot