The strategy trades liquid bond futures based on the last 30 minutes of trading. Long positions are taken with positive returns, short positions with negative returns, and positions are held until market close.

I. STRATEGY IN A NUTSHELL

The strategy trades the 16 most liquid bond futures from developed markets (North America, Europe, Asia/Australia). Futures with positive returns in the last 30 minutes of trading are taken long, while those with negative returns are taken short. Positions are held until the end of the trading day, with contracts evaluated individually and equally weighted. The nearest-to-delivery, most liquid contract is used, rolling over to the next contract when its volume exceeds the current one. The portfolio is rebalanced daily, capturing intraday momentum efficiently.

II. ECONOMIC RATIONALE

Early-day price movements predict subsequent returns due to transaction costs, liquidity patterns, and hedging behavior. Traders complete hedges at the close to avoid overnight risk, while late informed trading and infrequent rebalancing generate intraday momentum. Peak trading hours differ across asset classes, making opening and closing periods ideal for executing the strategy.

III. SOURCE PAPER

Low-Risk Effect: Hedging demand and market intraday momentum[Click to Open PDF]

Guido Baltussen, Zhi Da,Sten Lammers,Martin Martens.Erasmus School of Economics, Erasmus University Rotterdam, Burgemeester Oudlaan 50, Rotterdam 3000 DR, Netherlands.Robeco Asset Management, Weena 850, Rotterdam 3014 DA, Netherlands.University of Notre Dame, 239 Mendoza College of Business, Notre Dame, IN 46556, USA.Erasmus School of Economics, Erasmus University Rotterdam, Burgemeester Oudlaan 50, Rotterdam 3000 DR, Netherlands.Robeco Asset Management, Weena 850, Rotterdam 3014 DA, Netherlands.

<Abstract>

Hedging short gamma exposure requires trading in Hedging short gamma exposure requires trading in the direction of price movements, thereby creating price momentum. Using intraday returns on over 60 futures on equities, bonds, commodities, and currencies between 1974 and 2020, we document strong “market intraday momentum” everywhere. The return during the last 30 minutes before the market close is positively predicted by the return during the rest of the day (from previous market close to the last 30 minutes). The predictive power is economically and statistically highly significant, and reverts over the next days. We provide novel evidence that links market intraday momentum to the gamma hedging demand from market participants such as market makers of options and leveraged ETFs.

IV. BACKTEST PERFORMANCE

Annualised Return2.16%
Volatility1.33%
Beta0
Sharpe Ratio1.62
Sortino RatioN/A
Maximum DrawdownN/A
Win Rate6%

V. FULL PYTHON CODE

from datetime import time
EXPIRY_MIN_DAYS = 90
EXPIRY_LIQUIDATE_DAYS = 5
class IntradayMomentuminFixedIncome(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2008, 1, 1)
        self.SetCash(100000)
        self.symbols = [
                        Futures.Financials.Y30TreasuryBond,
                        Futures.Financials.Y10TreasuryNote,
                        Futures.Financials.Y5TreasuryNote,
                        Futures.Financials.Y2TreasuryNote,
                        ]
                    
        self.close_price = {}
        self.active_future = {}
        
        for symbol in self.symbols:
            future = self.AddFuture(symbol)
            future.SetFilter(EXPIRY_LIQUIDATE_DAYS, EXPIRY_MIN_DAYS)
            self.active_future[symbol] = None
        
        self.Schedule.On(self.DateRules.EveryDay(future.Symbol), self.TimeRules.BeforeMarketClose(future.Symbol, 1), self.Close)
        self.Schedule.On(self.DateRules.EveryDay(future.Symbol), self.TimeRules.BeforeMarketClose(future.Symbol, 30), self.Purchase)
    
    def OnData(self, slice):
        for chain in slice.FutureChains:
            contracts = [contract for contract in chain.Value]
            
            sym = chain.Value.Symbol.Value
            if len(contracts) == 0:
                self.active_future[sym] = None
                continue
            
            contract = sorted(contracts, key=lambda k : k.OpenInterest, reverse=True)[0]
            if sym in self.active_future and self.active_future[sym] is not None and contract.Symbol == self.active_future[sym].Symbol:
                continue
            
            self.active_future[sym] = contract
            self.Securities[contract.Symbol].SetFeeModel(CustomFeeModel(self))
            
    def Purchase(self):
        if len(self.close_price) == 0: return
        
        performance_until_purchase = {}
        for symbol, close_price in self.close_price.items():
            if symbol in self.active_future:
                current_price = (self.active_future[symbol].AskPrice + self.active_future[symbol].BidPrice) / 2 if self.active_future[symbol].LastPrice == 0 else self.active_future[symbol].LastPrice
                sym = self.active_future[symbol].Symbol
                
                if current_price != 0 and close_price != 0:
                    performance_until_purchase[sym] = current_price / close_price - 1
                
        self.close_price.clear()
        
        if len(performance_until_purchase) == 0: return
        
        long = [x[0] for x in performance_until_purchase.items() if x[1] > 0]
        short = [x[0] for x in performance_until_purchase.items() if x[1] < 0]
        
        long_count = len(long)
        short_count = len(short)
        
        for symbol in long:
            self.SetHoldings(symbol, 0.1 / long_count)
        for symbol in short:
            self.SetHoldings(symbol, -0.1 / short_count)
        
    def Close(self):
        self.Liquidate()
        
        for symbol, contract in self.active_future.items():
            if contract:
                self.close_price[symbol] = (contract.AskPrice + contract.BidPrice) / 2 if contract.LastPrice == 0 else contract.LastPrice
# Custom fee model.
class CustomFeeModel(FeeModel):
    def GetOrderFee(self, parameters):
        fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0
        return OrderFee(CashAmount(fee, "USD"))

Leave a Reply

Discover more from Quant Buffet

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

Continue reading