Introduction

This post implements a risk premia strategy that enters long-short positions in the forex market based on signals from a skewness indicator. The strategy is derived from the paper Risk Premia: Asymmetric Tail Risks and Excess Returns.  One of the pillars in modern finance theory is the concept of risk premium, which states the riskier an investment is today the more profitable it should be in the long run. Risk premia strategies aim to profit from risk premiums. Lemperiere et al. describe a positive linear relationship between the Sharpe ratio of risk premia strategies and their negative skewness. It provides extensive evidence that risk premium is indeed strongly correlated with the skewness of a strategy, not only in the equity world but also in currencies, options, credit, etc.

Method

Step 1: In this algorithm, we should a fixed forex universe which contains four symbols: "EURUSD", "AUDUSD", "USDCAD" and "USDJPY". By using self.AddForex(), we add the requested forex data into the data feed.

# Add forex data of the following symbols
for pair in ['EURUSD', 'AUDUSD', 'USDCAD', 'USDJPY']:
self.AddForex(pair, Resolution.Hour, Market.FXCM)

Step 2: In probability theory and statistics, skewness is a measure of the asymmetry of the probability distribution of a real-valued random variable about its mean. Since the paper indicates that a positive relationship between risk premia strategies and their negative skewness, we could come up with a trading logic based on it:  we go long for a forex pair when it is lower than a threshold (-0.6) while short it when it exceeds another threshold (0.6).  For each forex pair in the universe, we will calculate the skewness indicator with historical close prices and choose the symbols as follows.
 

### In OnData()
# Get historical close data for the symbols
history = self.History(self.Securities.Keys, self.lookback, Resolution.Daily)
history = history.drop_duplicates().close.unstack(level=0)

# Get the skewness of the historical data
skewness = self.GetSkewness(history)

longSymbols = [k for k,v in skewness.items() if v < self.longSkewLevel]
shortSymbols = [k for k,v in skewness.items() if v > self.shortSkewLevel]

def GetSkewness(self, values):
'''
Get the skewness for all forex symbols based on its historical data
Ref: https://www.itl.nist.gov/div898/handbook/eda/section3/eda35b.htm
'''
# Get the numerator of the skewness
numer = ((values - values.mean()) ** 3).sum()

# Get the denominator of the skewness
denom = self.lookback * values.std() ** 3

# Return the skewness
return (numer/denom).to_dict()

Step 3: We rebalance every week, liquidate the forex pairs that are not on the trading list, then repeat steps 1-2. We use equal weights for the long and short positions of securities in our portfolio.

Results
In this case, our backtest results in a low Sharpe Ratio of -0.06 over a decade while the SPY500 gains a Sharpe Ratio of around 0.9. The poor performance may be due to several reasons:

  1. The fixed forex universe chosen is not large enough to properly diversify market risk
  2. The thresholds for entering long and short positions (0.6, -0.6) may need adjustment
  3. The length of historical data might be not large enough for this weekly-rebalanced strategy.

We encourage you to further develop this strategy by testing out different symbols, thresholds, and historical data lengths.

Author