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.

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 Return6.4%
VolatilityN/A
Beta-0.007
Sharpe Ratio-0.47
Sortino Ratio-0.174
Maximum DrawdownN/A
Win Rate38%

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

Leave a Reply

Discover more from Quant Buffet

Subscribe now to keep reading and get access to the full archive.

Continue reading