This tutorial 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” by Lemperiere, Deremble, Nguyen, Seager, Potters, and Bouchaud.

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.


Step 1: Select our forex universe

In this algorithm, we use 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: Calculate the skewness indicator

In statistics, skewness is a measure of the asymmetry of the probability distribution of a real-valued random variable about its mean. Lemperiere et al. suggest there is a positive relationship between risk premia strategies and their negative skewness. We will use this relationship in our trading logic. Our implementation goes long for a Forex pair when the skewness indicator is lower than a minimum threshold (-0.6) and short the pair when the indicator exceeds a maximum threshold (0.6). For each Forex pair in the universe, we will calculate the skewness indicator with historical close prices and select 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
    # 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: Rebalance weekly

We rebalance every week, liquidate the forex pairs 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.

# Liquidate the holdings for pairs that will not trade
for holding in self.Portfolio.Values:
    symbol = holding.Symbol
    if holding.Invested and symbol.Value not in longSymbols + shortSymbols:
        self.Liquidate(symbol, 'Not selected pair')

# Open positions for the symbols with equal weights
count = len(longSymbols) + len(shortSymbols)

for pair in longSymbols:
    self.SetHoldings(pair, 1/count)

for pair in shortSymbols:
    self.SetHoldings(pair, -1/count)

# Set next rebalance time
self.nextRebalance += timedelta(self.rebalanceDays)    


In this case our backtest results in a low annual return of approximately -0.330% over a decade. The poor performance may be due to several reasons:

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

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


  1. Risk Premia: Asymmetric Tail Risks and Excess Returns