Setup, Problem & Solution
We assume the FX market participant (FXMP hereafter, client of Tradefeedr) is trading with several liquidity providers (LP) via FX aggregator setup (see Definitions ). FXMP is a price taker. He observes FX quotes in real time from all his LPs and selects the best quote to trade on (highest bid to sell, lowest offer to buy). We assume for now that his trade size is smaller than the size attached of quotes so he does not have to hit several LP at once (so-called sweep). FXMP can be filled, partially filled or rejected as a results of his order.
FXMP is looking for a way to measure the performance of his LPs in the stack. Ideally he would like to have a framework to optimise trading costs.
The study assume the FXMP market data and trade messages (order, fills, rejects) are loaded in the Tradefeedr Platform
The prices and quantities observed by FXMP look like the schematic Figure 1.
|Figure 1: Liquidity Stack
Problem and Solution
FXMP would like to perform a basic “venue analysis”. That means he would like to compare his LPs, to understand which of its LPs provide a good service and which once can be dropped from the stack. FXMP uploads all its order data (and potentially market data if preferred) to the Tradefeedr Platform and access the results via GUI or API. API can be use to perform FXMP-specific proprietary analysis which goes beyond the common liquidity metrics.
Measuring Cost of Fills and Rejects
The trading cost of fills is just the Spread Paid (see Definitions). It is the difference between price paid and the prevailing “fair” mid-price adjusted by trade direction (so buying above (below) and selling below (above) the mid would suggest positive (negative) cost).
In this setup there will be latency slippage which can be defined as the difference between “fair” mid between the time order is sent and the time the order is executed. This is specifically addressed in the latency section (see Latency).
Quantifying rejection costs is typical more complex. If the data about order re-try is available (rejects and subsequent fills are aggregated under the same parentOrderID) than it is possible to calculate actual slippage due to rejects. However, in most cases this is not the case. The trade is retried under the new orderId and connection between current rejected order and future fills order is lost. In this case we resort to theoretical slippage which is defined mid-price over the next 500ms after the trade is rejected. In theoretical decay the time space for assumed market more (500ms or somethng else) depends on internal client system and trading style. Some clients can retry after 100ms. Some client would take 1s because one of their LPs delayed the response. This time delay is customizable in Tradefeedr platform.
Defining Execution Metric: Effective Spread
The ides is to combine Spread Paid and Reject costs into one metric to optimize. One possible way is Effective Spread which can be defined as
Effective Spread = Spread Paid on Fill + Reject Ratio * Reject Cost
- Spread Paid on fills is just a spread paid conditional on the trade being filled.
- Reject Ratio is the ratio of reject volume to fill volume
- And Reject Cost is the move in the mid-price over the next 500ms after the trade is rejected. It is assumed that the trade order will be re-tried 500ms after the reject report has been received. The 500ms number can be changed in Tradefeedr Platforms to accommodate exact use case/strategy of the end user.
Note that in the definition above, the RejectRatio is not limited by 100% as Reject Volume can exceed the Fill Volume. There are alternative definitions of Reject Ratio which can calculate it as percentage of total attempted volume (Volume Placed). We prefer the above definition as it treats Fill Volume as a business target and assumes it would take whatever number of rejects necessary to achieve the Fill Volume. Alternative definition would suggest that Volume Placed rather than Fill Volume is the business goal. It may well be the case sometime but likely in a minority of cases.
Results & Interpretation
Figure 2 shows the typical Effective Spread Analysis acoss LPs.
|Figure 2: Effective Spread Statistics
A typical reaction to Figure 2 would be to conclude LP1 is delivering a horrible service. However, as you trade against the stack you cannot really compare one LP against another directly. If you execute a trade against LP1 that means that LP1 showed you the best price at the time. Trading with any other LP would have made the situation worse.
Therefore, if LP1 is not rejecting your orders then LP1 is best price given the composition of your liquidity stack. The real question from the above is where are all your other LPs when you are crossing this big spread with LP1. If there are specific time periods where none of you other LPs want to make the market the execution can be improved by adding other LPs who will (changing the liquidity stack).
Figure 2A. LP comparison in tabular way
If however LP1 rejects frequently then selecting “the best” (best bid/offer) price is not optimal even for a given liquidity stack. The relevant analysis would require to collect a second best price at the time of the execution. It would also require to estimate expected rejection cost on this second best price.
If we cannot compare the LP directly what is the point of doing the analysis to start with?
The main goal is to analyse and optimise the reject cost. Simple “back of the envelope calculation” is as follows. FX prices are normally 0.1 pip apart (minimum tick size) which (for EURUSD) would be approximately 8 $/m given at the current price level. Therefore the quantity of interest is the probability of reject times the cost of reject. If this product reliably exceeds 8 $/m than we can potentially improve the execution by selecting the second best price rather than trading with this frequently rejecting LP at the first price. Therefore, the second best price would be the best execution.
Overall rejection probability is normally (but not always) a monitored variable as it is something which is easy to measure. The cost of reject is a bit harder to measure. Therefore the simple condition above would fail for LP as rejection cost would have to be extremely high. For example if the rejection probability if 5 percent than LP rejection cost would have to exceed 160 $/m (160 $/m x 5% = 8 $/m) to justify switching to second price in the stack.
However for active trader the rejection size and probability depends on some other factors like time of the day, event (like non-farm payroll proximity). Those are complex factors which need to taken into account when doing venue analysis.
Trading 1m EURUSD on a quiet morning and during Non-Farm Payroll (NFP) announcement are two different things. If we are to characterize EURUSD next-second volatility and jump risk they would be much higher around NFP. Therefore, wider spread around NFP is a norm and is justified.
If one LP has a better event risk management than the other then this LP would always win business around events. A simplistic calculation above would suggest that effective spread this LP delivers is bad (both due to wide visible spread and potentially huge reject cost). That would a mistake as this LP provides a very valuable service of liquidity in difficult time. Hence to compare across other LP the spread would have to be adjusted by spread determinants or conditioning factors
Spread should be proportional to volatility/jump risk and inversely proportional to order flow (as it means that it is easier for market maker to cover risk with offsetting trade). Volatility and order flows are in turn positively correlated thus making dependence function a complex one.
Conditioning Factors would include
- Macroeconomic events
- Time of the day and its corresponding volatility
- Unsystematic events we can adjust for ex-post (like Swiss National Bank events)
There are also specific factors such as order specific variables (like size and the way the order being places. For example placing several orders in a row increase the probabilty of reject)
Figure below shows the intraday dynamics of volatility (red like) on a 5 minute basis. The volatility fluctuates from 5% annualized in Asia morning to 30% during short active time period. The means that reject cost over active period might have to be scaled by up to 6 times (30%/5%) to be comparable. While the exact scaling is model dependent and will always be subject to debate it is clear that just averaging spread or mid moves across different zone is not a the best solution.
|Figure 3: Intraday Volatility Profile, EURUSD
Ranking Rejection Costs
The reject cost is a crucial element to decide whether the “best visible price” is actually the best price to trade upon (see discussion above). In this section we give an example how large the costs of rejection can potentially be. Again, we define the costs of reject as the mid-market move X milliseconds AFTER the reject messages has been received from LP.
Consider specific example of 200ms and assume “perfect foresight” liquidity provider in the sense that it only rejects the client is the market going to be move in client favor in the next 200ms. This liquidity provider cannot affect price action but has a perfect foresight in terms of direction. Therefore, the expected magnitude of the rejection cost is purely driven by price action.
The exact description of the empirical model is written up in our cases studies separately but for now it is important to highlight several numbers
- On average trading again “perfect foresight” LP would cost around 5$/m in rejection costs
- However, if “perfect foresight” LP only reject his client in 10% of worst cases (see 90% level) then the expected reject cost in 20$/m
- Finally if only 1% of worst case constitute a reject then expect cost is 50$/m.
It is should be noted that just because LP is reject in 1% of worst case during the day does not mean that you rejection ratio is expected to be 1%. If your trading style is around big moves you can natural capture big moves on your trades.
|Figure 4: Expected Reject Cost at different levels of Adverse Selection from LP
At the same time if your trading timing is uniform then ability to rank your toxicity again a “base case scenario” would allow to identify potential problem in execution stack.
Order Size and Order Flow Intensity
Spread order size is external conditioning factors. It is obvious that other things being equal it is difficult to expect that spread will be the same on 1m and 5m USD order. There are several ways to address a problem
- Track and optimize effective spread for 1m and 5m separately. In a broader scenario create some size bucket inside which is a reasonable to assume that spread is the same. An example would be (below 1m, 1m+-3m, 3m-5m, 5m-10m)
- Have some adjustment to make the comparison between 1m and 5m spread easily
The two solutions above are not mutually exclusive. In fact defining same-spread-buckets is the same as postulating a model that spread should only be changed outside those buckets.
A simple approach to modeling “fair” spread is to represent spread as option. The details are described in a separate study but the basic idea is as follows: if a market marker can purchase an option to sell at at bid (put option) and buy at offer (call option) that would fully cover him. Of course market maker is not purchasing short terms options. However, assuming that he is trying to minimize the loss from adverse price move (see Bollen and Whaley, 2004) it can be shown that the fair spread is similar to at the money options. Option expiration time would be equal to the expected time to cover the trade.
As the model in Figure 5 parametrized to EURUSD, it show that fair spread is about 1bps is it take 1 minute (60 seconds) to cover the positions. This is simply a price of volatility risk over this time period.
Figure 5: Required Spread as a function of time to cover
Order Flow Intensity
Another important variable is order flow intensity. If investor have been buy a currency pair in a aggressive fashion over short period of time the supply will start to look depleted. Therefore, lack of liquidity may force spread increase. Hence, 1m EURUSD bought as the 10th slice of 10m order in the last second it likely to have higher spread the first 1m in a bank.
It is obviously but your own order flow is something know with certainty. Hence we use it as a conditioning variable in Tradefeedr platform.
Conclusions and Liquidity Stack Implications
This use case illustrated a simple (if not simplistic) way to measure performance of different LPs in a liquidity stack (also called FX aggregator). The case study outlined more problems than it provided answers for. However it demonstrated how the basic statistics are constructed and what are the pitfalls in using them. This is normally a starting point for anyone commencing liquidity analysis.
Also just because liquidity provision is hard to quantify it does not mean it should not be tried. However, as follows from above it is improving overall liquidity stack which should be the end goal of liquidity optimization. The typical approach can be similar to the one used by other businesses: creating two alternative models (liquidity stacks) and applying them in randomized fashion to explore which approach is the best.
At the same time LP analysis described above can be a very useful first step to understand how to create an alternative version of LP stack for AB testing.
For example alternative stack excluding LPs with the following characterizes can be constructed:
- If an LP demonstrates a high reject ratio and high reject cost then an alternative liquidity stack without this LP can be setup.
- High market impact LP (even after fills) can be removed as having you market impact amplified by awkward execution is not good.
- LP which takes too long to come back with order confirmation (latency) as a locks resources and potential stops alternative executions.
Same approach can be taken when adding new promising LPs to the liquidity stack.
Trading can be randomized between two stacks (with and without problematic LPs) and effective spread should be run be calculated. Formal test can be performed to see if the change makes sense. This is described in “Optimizing LP Stack” use case