
Short commodities on June 30 before a U.S. presidential election quarter, cover on September 30, leveraging potential election-driven market dynamics using a commodity index instrument.
ASSET CLASS: CFDs, ETFs, forwards, futures, swaps | REGION: Global | FREQUENCY: Quarterly | MARKET: commodities | KEYWORD: Politics
I. STRATEGY IN A NUTSHELL
Targets 78 commodities (via ETF, futures, or swaps). Short commodities on June 30 before a U.S. presidential election quarter and cover on September 30, exploiting election-driven market uncertainty.
II. ECONOMIC RATIONALE
Commodity prices fall before U.S. elections due to reduced demand and policy uncertainty. The effect is strongest in globally integrated commodities, close elections, and recessions, reflecting political uncertainty’s impact on consumption and pricing.
III. SOURCE PAPER
Political Uncertainty and Commodity Prices[Click to Open PDF]
Kewei Hou, Ohio State University (OSU) – Department of Finance; Ke Tang, Institute of Economics, School of Social Sciences, Tsinghua University; Bohui Zhang, The Chinese University of Hong Kong, Shenzhen
<Abstract>
We study the effects of political uncertainty on commodity markets from both theoretical and empirical perspectives. Consistent with our theoretical predictions, commodity prices and inventories decline by 6.6% and 5.7%, respectively, and convenience yields increase by 1.9% in the quarter leading up to U.S. presidential elections, our proxy for political uncertainty on the demand side. Opposite results are obtained for political uncertainty on the supply side using national elections in major commodity-producing countries. We do not observe significant changes in risk premiums before elections. Furthermore, we show that gold is not an effective hedge against political uncertainty.


IV. BACKTEST PERFORMANCE
| Annualised Return | 6.4% |
| Volatility | N/A |
| Beta | -0.007 |
| Sharpe Ratio | -0.47 |
| Sortino Ratio | -0.174 |
| Maximum Drawdown | N/A |
| Win Rate | 38% |
V. FULL PYTHON CODE
from AlgorithmImports import *
class PoliticalUncertainty(QCAlgorithm):
def Initialize(self):
self.set_start_date(1996, 1, 1)
self.set_cash(100_000)
self.symbol: Symbol = self.add_data(QuantpediaFutures, 'CME_GI1', Resolution.DAILY).symbol
self.securities[self.symbol].set_fee_model(CustomFeeModel())
self.securities[self.symbol].set_leverage(2)
self.schedule.on(self.date_rules.month_end(self.symbol), self.time_rules.at(0, 0), self.rebalance)
def rebalance(self) -> None:
if self.time.date() > QuantpediaFutures.get_last_update_date()[self.symbol.value]:
self.liquidate()
return
year = self.time.year
if year % 4 == 0: # every 4th year
if self.time.month == 6:
self.set_holdings(self.symbol, -1)
elif self.time.month == 9:
if self.portfolio.invested:
self.liquidate()
# 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
# Custom fee model
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))
VI. Backtest Performance