Effect of Corruption on FX Markets
Log in to collectAcademic paper
Corruption, Carry Trades, and the Cross Section of Currency Returns
Klaus Grobys; Jari‐Pekka Heinonen
- FIUniversity of Vaasa
- FIUniversity of Jyväskylä
- ?University of Jyväskyla
- ?University of Vaasa - Department of Accounting and Finance
Strategy in a nutshell
This strategy invests in 17 liquid currency pairs against the US dollar. Each year, currencies are ranked by the previous year’s Corruption Perception Index (CPI). The portfolio goes long on the five least-corrupt currencies and short on the five most-corrupt, creating a zero-cost portfolio rebalanced annually to capture potential currency performance differences linked to corruption levels.
Economic rationale
While traditional FX models focus on dollar and carry risk, corruption remains an underexplored factor. Research shows high corruption can negatively impact GDP and currency value, introducing additional risk for investors and influencing market behavior. This strategy seeks to exploit these corruption-related effects on currency returns.
Backtest performance
Full Python code
from AlgorithmImports import *
#endregion
class EffectCorruptionFX(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2010, 1, 1)
self.SetCash(100000)
self.symbols = {'CAD' : 'USDCAD',
'GBP' : 'USDGBP',
'MXN' : 'USDMXN',
'EUR' : 'USDEUR',
'NOK' : 'USDNOK',
'CHF' : 'USDCHF',
'SEK' : 'USDSEK',
'AUD' : 'USDAUD',
'NZD' : 'NZDUSD',
'JPY' : 'USDJPY',
'HKD' : 'USDHKD',
'SGD' : 'USDSGD',
'ZAR' : 'USDZAR',
'INR' : 'USDINR'
}
for symbol in self.symbols:
data = self.AddForex(self.symbols[symbol], Resolution.Daily, Market.FXCM)
self.cpi = self.AddData(CPIData, 'CPI', Resolution.Daily).Symbol
self.quantile = 3
first_key = [x for x in self.symbols.keys()][0]
self.Schedule.On(self.DateRules.MonthStart(self.symbols[first_key]), self.TimeRules.AfterMarketOpen(self.symbols[first_key]), self.Rebalance)
def Rebalance(self):
if self.Time.month != 1: return
cpi = {}
for symbol in self.symbols:
if self.Securities[self.cpi].GetLastData():
cpi[symbol] = self.Securities['CPI'].GetLastData()[symbol]
if len(cpi) < self.quantile:
self.Liquidate()
return
sorted_by_cpi = sorted(cpi.items(), key = lambda x: x[1], reverse = True)
quantile = int(len(sorted_by_cpi) / self.quantile)
long = [x[0] for x in sorted_by_cpi[:quantile]]
short = [x[0] for x in sorted_by_cpi[-quantile:]]
self.Liquidate()
long_count = len(long)
short_count = len(short)
for symbol in long:
if symbol == 'NZD':
self.SetHoldings(self.symbols[symbol], 1/long_count)
else:
self.SetHoldings(self.symbols[symbol], -1/long_count)
for symbol in short:
if symbol == 'NZD':
self.SetHoldings(self.symbols[symbol], -1/short_count)
else:
self.SetHoldings(self.symbols[symbol], 1/short_count)
# CPI data
# NOTE: IMPORTANT: Data order must be ascending (date-wise)
from dateutil.relativedelta import relativedelta
class CPIData(PythonData):
def GetSource(self, config, date, isLiveMode):
return SubscriptionDataSource("data.quantpedia.com/backtesting_data/economic/CPI.csv", SubscriptionTransportMedium.RemoteFile, FileFormat.Csv)
def Reader(self, config, line, date, isLiveMode):
data = CPIData()
data.Symbol = config.Symbol
if not line[0].isdigit(): return None
# Example File Format:
# year;CAD;GBP;MXN;EUR;NOK;CHF;SEK;AUD;NZD;JPY;HKD;SGD;ZAR;INR
# 2013;84;74;34;79;85;86;88;85;90;74;77;87;43;36
#
# YEARLY DATA
split = line.split(';')
data.Time = datetime.strptime(split[0], "%Y") + relativedelta(months=12) # NOTE: Preventing of look ahaead bias. Add 12 months so this year we see last year's data.
data.Value = split[1]
data['CAD'] = int(split[1])
data['GBP'] = int(split[2])
data['MXN'] = int(split[3])
data['EUR'] = int(split[4])
data['NOK'] = int(split[5])
data['CHF'] = int(split[6])
data['SEK'] = int(split[7])
data['AUD'] = int(split[8])
data['NZD'] = int(split[9])
data['JPY'] = int(split[10])
data['HKD'] = int(split[11])
data['SGD'] = int(split[12])
data['ZAR'] = int(split[13])
data['INR'] = int(split[14])
return data
# Custom fee model.
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))