Traditional carry trade strategies are based on differences in short-term interest rates, neglecting any other information embedded in yield curves. We derive return distributions of carry trade portfolios among G10 currencies, where the signals to buy and sell currencies are based on summary measures of the yield curve, the Nelson-Siegel factors. We nd that a strategy based on the relative curvature factor, the curvy trade, yields higher Sharpe ratios and a smaller return skewness than traditional carry trade strategies. Curvy trades build less upon the typical carry currencies, like the Japanese yen and the Swiss franc, and are hence less susceptible to crash risk. In line with that, standard pricing factors of traditional carry trade returns, such as exchange rate volatility, fail to explain curvy trade returns in a linear asset pricing framework. Our findings are in line with recent interpretations of the curvature factor. A relatively high curvature signals a relatively higher path of future short-term rates over the medium-term putting upward pressure on the currency.
- Targeted assets: Currencies from the G10 nations.
- Calculate Nelson-Siegel yield curve factors (refer to Equation 1, page 8)
- Note: Slight differences in parametrization and method (OLS vs. non-linear least squares)
- Focus on curvature factor (Ct)
- Trading approach:
- Rank currencies by comparing their relative curvature factor (Ct).
- Go short on currencies with low curvature.
- Go long on currencies with high curvature.
- Portfolio layout:
- Long and short portfolios each have three currencies.
- Adjust the holdings on a monthly cycle.
- For alternative portfolio structures, consider holding 1, 2, or 4 currencies, as discussed in the research article.
import backtrader as bt import numpy as np from scipy.optimize import curve_fit class NelsonSiegelYieldCurve(bt.Indicator): lines = ('curvature',) params = (('tau', 2),) def ns_curve(self, t, beta0, beta1, beta2): return beta0 + beta1 * (1 - np.exp(-t/self.p.tau)) / (t/self.p.tau) + beta2 * ((1 - np.exp(-t/self.p.tau)) / (t/self.p.tau) - np.exp(-t/self.p.tau)) def next(self): t = np.arange(len(self.data.close)) y = self.data.close.array popt, _ = curve_fit(self.ns_curve, t, y, bounds=(0, [100, 100, 100])) self.lines.curvature = popt class CurvatureStrategy(bt.Strategy): def __init__(self): self.curvatures = [NelsonSiegelYieldCurve(d) for d in self.datas] def next(self): if self.datetime.date(0).day != 1: # Rebalance on first day of the month return curvatures = np.array([c for c in self.curvatures]) sorted_indices = np.argsort(curvatures) n_currencies = 3 # Adjust this value for alternative portfolio sizes (1, 2, or 4) short_indices = sorted_indices[:n_currencies] long_indices = sorted_indices[-n_currencies:] for i, d in enumerate(self.datas): if i in short_indices: self.sell(data=d) elif i in long_indices: self.buy(data=d) if __name__ == '__main__': cerebro = bt.Cerebro() cerebro.addstrategy(CurvatureStrategy) # Add G10 currency data feeds # ... cerebro.run()
Note that this code assumes you have already added G10 currency data feeds to the
cerebro instance. You’ll need to replace the
# Add G10 currency data feeds comment with code to load the necessary data.