The Risk Instance keeps order and position states, maintains the credit pool hierarchy, and performs all risk checks.
The output of the message parser consists of order actions. The risk instance determines the legality of those order actions. To accomplish this, Reflector acts as-if the following steps are taken:
Definitions:
There are two types of Groups:
Aggregate groups contain other groups.
User groups contain credentials. Credentials are uniquely identified by the ordered triple (Venue, CompID, SubID). Users may be associated with multiple credentials per venue and over multiple venues.
In the following diagram, the BROKER and FUND nodes are aggregates. The access, command/control, and semantics surrounding them are external to the runtime, and are handled elsewhere. The only constraints that Reflector enforces on Aggregates are that they can only contain other groups and their Position is the sum of the Positions of the groups contained by them.
User groups, represented by TRADER in this diagram, contain only credentials and represent the most granular division of risk available in Reflector. Individual credential entries do not keep their own positions.
![]()
Inside a risk pool, position is stored as an array of arrays.
Expressed more formally:
A position p is an aggregation of outlays of both open and filled portions of orders made by a set of traders.
A formula f is a mapping from positions to non-negative real numbers. f must be convex.
A limit l is an ordered pair of (f, v) where f is a formula and v is a non-negative real number.
A Position p is said to be in violation of limit l under formula f if and only if f(p) > v.
Volatility is essentially an additional and independent multiplier that is applied per-currency to allow Risk managers to treat certain currencies as particularly more or less risky than others.
Valid volatility values range from 0.01 to 100.00, inclusive.
All volatility values default to 1.00, and the USD (Reserve Currency) volatility is currently fixed at a baseline of 1.00
Each risk pool can define its own set of volatility values.
Limits may be categorized as follows:
The submission rate counter measures the number of risk carrying messages in a rolling window. The restraint placed on it is intended to act as a runaway-algo check.
Risk carrying messages include the following outbound messages:
Rejected risk-carrying orders still increment the count.
Messages that cancel orders are not considered risk-carrying.
A live order count is maintained and constrained to a live order count limit.
The absolute values of both buy and sell outlays are multiplied by their respective conversion rates to USD, then averaged.
This is similar to the Single Order Limit, except this is a summation over all unfilled portions of all orders:
A vector of exposure limits, each applying to a single currency, expressed in that currency. This limit can be expressed in either its native currency or in USD (the Reserve Currency), based on a global setting that is loaded at startup:
Two levels of enforcement are offered: nothing or everything.
By default, per-currency exposure limits are not enforced; meaning the trader is allowed to accumulate any position in any currency, provided that they do not violate other limits. When enabled, then currency limits become mandatory: traders will not be able to trade in a given currency without a limit. |
A currency basket is an ordered 3-tuple of (name, limit value, currency set).
Each risk can define up to 16 currency baskets.
The set of currency sets defined under one risk pool must be pairwise distinct (e.g.: no currency can appear in more than one basket under the same risk pool).
| By default, this option is disabled, and the trader is allowed to accumulate any position in any currency basket, provided that they do not violate other limits. If enabled, then currency basket limits become mandatory: traders will not be able to trade in currencies that do not belong to a basket. |
If ZEBRA_DSL is defined, then daily positions are kept. DSL uses whichever position tracking type is chosen for NOP.
Reflector uses the following terms to quantify NOP: Downside, Upside, Exposure, Displacement.
An upper bound on how much the trader can lose.
This limit first assumes that the upside of every pending trade is gone but the downside remains.
Then, it assumes that all the credited currencies are gone while the debited currencies remain.
Upside Limit
An upper bound on how much the trader can make.
This limit first assumes that the downside of every pending trade is gone but the upside remains.
Then, it assumes that all the debited currencies are gone while the credited currencies remain.
This formula nominates one currency (usually USD) as a risk-free asset, then considers positions in all other currencies as risk carrying, pursuant to their absolute value, then scaled by the exchange rate & volatility.
This formula is very similar to the Exposure Limit formula.
In the exposure formula, the upside and downside are reconciled per currency, then summed up. In this formula, they are summed up independently, then reconciled.
Reflector has four different risk modes as itemised below (ordered in increasing constriction):
Reflector implements an "Order Risk model", not an "Order model". The difference is that only the attributes of an order that has risk implications are modeled.
For taker orders, this means that open orders and fills are separate entities. A given open taker order has exactly 3 possible states: nonexistent, live, and dead. The order is live from the outbound NewOrderSingle until an inbound ExecReport confirms its death. The fills that the open order accumulates have 3 possible states too: nonexistent, filled, and rolled back.
For makers, the inbound order itself is assumed to carry no risk. It is the outbound ExecReport that is being examined for risk. We model that as a fill that can potentially be rolled back; because most maker venues do not ack on accept, and rejects are rather rare.
DSL is supported by Reflector, however it shoudl be noted that DSL is primarily a passive post-trade metric - as discussed in the following subsections.
DSL demands precision on a metric that is inherently ambiguous before an order executes: settlement date. There are three potential sources of truth with regard to settlement date on a spot order:
Implications:
| There is essentially no way to perfectly deduce settlement date pre-trade. |
Even if the precision described in the previous section can be captured correctly, the effort to do so may be in vain.
A trader wishing to load up on risk while evading DSL can simply make sure to spread his orders between different settlement days. Even a trader who is fully compliant in spirit will be incentivized to take on riskier bets with longer settlement terms in the event that his near term position is approaching its DSL limit.
In other words, DSL encourages in practice precisely the kind of behavior it purports to curtail in principle.
DSL is weakly ordered. Given two DSL positions, it is not possible in all cases to determine if one position is "more risky", "less risky", or "equally risky" as the other in terms of DSL. This matters a lot when a trader is put in any sort of "deleveraging mode", where only risk decreasing trades are allowed. Since we cannot determine which DSL is "worse" than another, we actually must also run something else to arbitrate, and when the arbitrating metric allows through a trade in deleveraging mode, it must be allowed to bypass DSL even if its limits are bust. DSL is therefore a rule that attempts to preempt extreme situations that actually must be discarded in precisely some of these extreme situations it is charged upon preventing.
| The current implementation of DSL deleveraging mode requires that an order not increase worst-case risk assessment in any settlement date it deals in. |
Implementing DSL requires us to multiply the memory required to store a position by 2N, where N is the average number of unsettled date buckets per pool of risk. This effect multiplies with other storage-multiplying features, such as counterparty position and multi-location. Worse yet, we cannot precompute all the dates; which means that we need additional synchronization during runtime to make sure that the web server read the correct memory locations. These factors have a direct impact for a real-time application.