
The strategy shorts USD via TWI futures during FOMC meetings, applies 10:1 leverage, and remains neutral otherwise, targeting USD volatility around key economic announcements.
ASSET CLASS: CFDs, ETFs, futures | REGION: United States | FREQUENCY:
Daily | MARKET: currencies | KEYWORD: Federal Open Market Committee ,Meeting Effect , US Dollar
I. STRATEGY IN A NUTSHELL
This strategy shorts the USD against a basket of global currencies using USD TWI futures, trading only on the eight annual FOMC meeting days. A 10:1 leverage is applied, with neutral positions maintained outside these events to limit exposure.
II. ECONOMIC RATIONALE
FOMC announcement days create uncertainty about future monetary policy, prompting investors to demand a risk premium. This leads to higher expected foreign exchange returns during these announcement periods.
III. SOURCE PAPER
Policy Announcements in FX Markets [Click to Open PDF]
Philippe Mueller, London School of Economics; Paolo Porchia, Instituto Empresa; Andrea Vedolin, London School of Economics
<Abstract>
A strategy that is short the US dollar and long the rest of the world earns large excess returns on days of scheduled meetings of the Federal Open Market Committee (FOMC). Moreover, the difference between announcement and non announcement returns becomes larger during periods of high uncertainty and bad economic conditions. To reconcile our findings, we develop a model of an international long-run risk economy in which asset prices respond to revisions of monetary policy. Monetary policy uncertainty commands a risk premium that is larger in weaker economic conditions. A calibrated version is consistent with the cross-sectional pattern of currency risk premia observed in the data.


IV. BACKTEST PERFORMANCE
| Annualised Return | 7.71% |
| Volatility | 13.78% |
| Beta | 0.004 |
| Sharpe Ratio | 0.56 |
| Sortino Ratio | -0.328 |
| Maximum Drawdown | N/A |
| Win Rate | 56% |
V. FULL PYTHON CODE
from AlgorithmImports import *
from pandas.tseries.offsets import BDay
class FederalOpenMarketCommitteeMeetingEffectUSDollar(QCAlgorithm):
def initialize(self):
self.set_start_date(2004, 1, 1)
self.set_cash(100_000)
# fed days
csv_string_file: str = self.download('data.quantpedia.com/backtesting_data/economic/fed_days.csv')
dates: List[str] = csv_string_file.split('\r\n')
dates: List[datetime.date] = [(datetime.strptime(x, "%Y-%m-%d") - BDay(1)).date() for x in dates]
# import futures data
data: Security = self.add_data(QuantpediaFutures, "ICE_DX1", Resolution.DAILY)
data.set_fee_model(CustomFeeModel())
self.symbol: Symbol = data.symbol
self.close_next_day: bool = False
self.schedule.on(self.date_rules.on(dates), self.time_rules.at(0, 0), self.day_before_fed)
self.schedule.on(self.date_rules.every_day(self.symbol), self.time_rules.at(0, 0), self.rebalance)
def day_before_fed(self) -> None:
if self.time.date() >= QuantpediaFutures.get_last_update_date()[self.symbol.value]:
return
if not self.portfolio[self.symbol].is_short:
self.set_holdings(self.symbol, -1)
def rebalance(self) -> None:
if self.close_next_day:
self.liquidate(self.symbol)
self.close_next_day = False
if self.portfolio[self.symbol].is_short:
self.close_next_day = True
# Custom fee model
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))
# Quantpedia data.
# NOTE: IMPORTANT: Data order must be ascending (datewise)
class QuantpediaFutures(PythonData):
_last_update_date: Dict[str, datetime.date] = {}
@staticmethod
def get_last_update_date() -> Dict[str, datetime.date]:
return QuantpediaFutures._last_update_date
def GetSource(self, config:SubscriptionDataConfig, date:datetime, isLiveMode:bool) -> SubscriptionDataSource:
return SubscriptionDataSource("data.quantpedia.com/backtesting_data/futures/{0}.csv".format(config.Symbol.Value), SubscriptionTransportMedium.RemoteFile, FileFormat.Csv)
def Reader(self, config:SubscriptionDataConfig, line:str, date:datetime, isLiveMode:bool) -> BaseData:
data = QuantpediaFutures()
data.Symbol = config.Symbol
if not line[0].isdigit(): return None
split = line.split(';')
data.Time = datetime.strptime(split[0], "%d.%m.%Y") + timedelta(days=1)
data['back_adjusted'] = float(split[1])
data['spliced'] = float(split[2])
data.Value = float(split[1])
# store last update date
if config.Symbol.Value not in QuantpediaFutures._last_update_date:
QuantpediaFutures._last_update_date[config.Symbol.Value] = datetime(1,1,1).date()
if data.Time.date() > QuantpediaFutures._last_update_date[config.Symbol.Value]:
QuantpediaFutures._last_update_date[config.Symbol.Value] = data.Time.date()
return data
VI. Backtest Performance