Finding Mean Reversion in FX

I have been told that my previous Neanderthals versus DeepQ Robotraders blog still is not very realistic as it uses Constant Maturity yields of US treasuries, which have the drawback of not being intuitive for non-FI managers.

I use that example because technically it was very easy to get the public data (one simple call to Quandl), but it is not difficult to extend the work to other assets classes.

Here I try the Foreign Exchange (FX) world. The ECB gathers FX rates for many currencies in the world; all we need is to use the list of available currencies and download each one:

Currencies available at ECB website

All the currencies are defined as EURCCY, meaning that you need CCY units to purchase one EUR.

Now that we have the data, let me show you how to find Mean Reverting trades, keeping in mind that:

  • This is only an educational example; for real trading conditions you need to know the funding costs for each currency,
  • Therefore I am going to assume a simpler world when the trading horizon is short and interest rates do not impact the rates. Yes, I know that Argentina has 60% rates which makes a mockery of this assumption — I will add the rates impact in a following blog. If anyone knows of a public source for FX forward rates please let me know.
  • I am also assuming the ECB is getting all the FX rates at the same time of the day. Again, in reality you have to carefully check that is the case — otherwise we might be committing the sin of ‘lookahead bias’
  • I am using way too many past historical days — but that could be changed in the code.

In fact, I want to emphasise the dangers of blind quantitative trading without domain knowledge, and I will raise the issues at each step.

If interest rates differentials are negligible, the Assets under Management (AUM) for a portfolio of currencies of an EUR investor is

AUM (in EUR)=MXNEUR∗MXNunits+USDEUR∗USDunits + etc

Think of it this way: if you have 1000 pesos and 50 dollars, today you will have the equivalent of 1000 * (1/22.21) + 50 * (1/1.16) = 88.13 euros. The EUR AUM equivalent will change every time the FX price changes.

If now we plug a matrix with all the timeseries of FX expressed as CCYEUR, Johansen (see my original blog “Finding Mean Reverting Signals (part 1)”) will return unit vectors that correspond to mean reverting signals.

Unfortunately, the python code I use only allows up to 12 timeseries. To get around this drawback, I added to the code the ability to test subsets of all the currencies.

  • for a semblance of reality, I run Johansen only until 1/1/2018. Still, you can change the code to define which time range you want to use.
  • I run subsets of one, two and three currencies. You can change the code to analyse subsets up to 12 — just remember the number of combinations is N! / (R! (N-R)!) where N is the available number of currencies and R the subset.
  • Because the Johansen method requires a full set of data, it only analyses mean reversion for the shortest available range.
  • By brute force, I check for all possible mean reverting signals (P-hacking alert!)
  • Display a set of signals where the current rate is 2 std above the mean.


Swiss Franc (CHF), Turkish Lira (TRY), Israeli Shekel (ILS)

And lo and behold, I find lots of mean reverting possible trades ! As an example, above I show that by holding 100 Euros of Israeli Shekels, versus shorting 57.58 Euros of Swiss Francs and 8.95 Euros of Turkish Lira (I express them in Euros as otherwise the units are not intuitive) we get a trade that looks quite mean reverting. The half life is 49 days (last number).

The full list of the 37 mean reverting signals I found is:

Party poopers:

  • I am guilty of P-hacking. the P-value of the Johansen test is 1%, and if you look at my code you will see that I run the test 696 times, therefore expecting about 7 false positives. I should have used the Bonferroni correction (dividing the P-value by the number of tests) — but the coded python test does not allow that.
  • I said interest rate differentials were ignored, but the subset of currencies I am using would not allow that.
  • The mean reverting signal could be having a ‘regime change’ episode (see “Why Financial Time Series Prediction fails”)
  • A FX analyst would laugh at some of the combinations (PLN, ILS, KRW) the same way we laugh spurious correlations (divorce rate in Maine correlated to per capita consumption of margarine)

On the bright side:

  • The ‘half_life’ for this signals is lower that the rates example in my previous blog, allowing us to use it as a cut-off time.
  • Combining the computational power with FX domain knowledge, quantitative/discretionary portfolio managers can find interesting alpha opportunities —
  • however, they need to provide an underlying framework to filter out false positives. This is why a Neanderthal/caveman (read Judea Pearl) that has an ‘intervention’ understanding of causality (‘what would happen to the Swiss Franc if the Turkish CB raises rates’) has a better chance of profiting than a pure RoboTrader.

In my code (see the bottom) I include all the code used (you will need to supply your own Quandl API access code — the data is public but using Quandl requires you to be registered).

Comments welcome.

Co-Founder of Lamat, a company specialized in solving high-value problems in finance by applying cutting edge numerical methods.