The strategy focuses on high-dividend yield stocks in Western Europe, with a stock price above EUR 1.00 and market cap over EUR 50 million, taking long positions before the ex-dividend date.

I. STRATEGY IN A NUTSHELL

The strategy targets publicly-listed Western European companies with stock prices > €1 and market capitalization > €50 million, focusing on high-dividend stocks (top quartile by dividend yield). It goes long five days before the ex-dividend date and adjusts positions on the ex-dividend date. The strategy is particularly effective for high-yield stocks due to stronger pre-dividend price pressure.

II. ECONOMIC RATIONALE

Behavioral biases drive the dividend month premium. Investors and arbitrageurs buying stocks to capture dividends push prices up before the ex-dividend date. Limited arbitrage and heightened demand—especially during periods of rare or cut dividends, like COVID—amplify price increases. The result is abnormal pre-dividend returns that decay after the ex-dividend date, making the anomaly exploitable.

III. SOURCE PAPERIII. SOURCE PAPER

Chasing dividends during the COVID-19 pandemic [Click to Open PDF]

Nicolas Eugster, University of Queensland – Business School; Romain Ducret, University of Fribourg (Switzerland) – Faculty of Management, Economics and Social Sciences; Dušan Isakov, University of Fribourg (Switzerland) – Faculty of Management, Economics and Social Sciences; Jean-Philippe Weisskopf, EHL Hospitality Business School

<Abstract>

This paper investigates the impact of the COVID-19 pandemic on the trading behavior of investors around ex-dividend dates in Europe. The sudden decrease in the number of companies paying dividends reduced the opportunities to capture dividends. The firms that have maintained dividend payments during the pandemic thus attracted more interest than before. This led to a doubling in the magnitude of stock return patterns usually observed around exdividend days. Our evidence indicates that dividend-seeking investors are likely to be the main driver of the price patterns observed around ex-dividend dates.

IV. BACKTEST PERFORMANCE

Annualised Return1.8%
VolatilityN/A
Beta0.262
Sharpe RatioN/A
Sortino Ratio0.506
Maximum DrawdownN/A
Win Rate58%

V. FULL PYTHON CODE

from AlgorithmImports import *
#endregion
class TheExDividendDateInTheEuropeanMarket(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2000, 1, 1)
        self.SetCash(100000)
        
        self.leverage:int = 5
        tickers = []
        self.symbols = []
        self.dividends_data = {}
        
        self.invested_data = {}
    
        self.first_date = None
        # Data source: https://www.nasdaq.com/market-activity/dividends
        dividend_data:str = self.Download('data.quantpedia.com/backtesting_data/economic/dividend_dates.json')
        dividend_data_json:Dict[str] = json.loads(dividend_data)
            
        for obj in dividend_data_json:
            date:datetime.date = datetime.strptime(obj['date'], "%Y-%m-%d").date()
            
            self.dividends_data[date] = []
            
            for stock_data in obj['stocks']:
                if ' ' in stock_data['ticker']:
                    continue
                ticker:str = stock_data['ticker']
                if ticker not in tickers:
                    security = self.AddEquity(ticker, Resolution.Daily)
                    security.SetFeeModel(CustomFeeModel())
                    security.SetLeverage(self.leverage)
                    
                    tickers.append(ticker)
                    
                    self.symbols.append(security.Symbol)
                    
                self.dividends_data[date].append(ticker)
        self.long = []
        self.long_size = 80
    def OnData(self, data):
        current_date = self.Time.date()
        date_to_lookup = (self.Time + timedelta(days=5)).date()
        if current_date in self.invested_data:
            self.LiquidateChosenSymbols(self.invested_data[current_date])
            del self.invested_data[current_date]
        # Open new trades.
        if date_to_lookup in self.dividends_data:
            for symbol in self.symbols:
                if symbol.Value in self.dividends_data[date_to_lookup]:
                    if symbol in data and data[symbol]:
                        if self.Add(symbol):
                            if date_to_lookup not in self.invested_data:
                                self.invested_data[date_to_lookup] = []
                                
                            self.invested_data[date_to_lookup].append(symbol)
    
    def Add(self, symbol):
        if len(self.long) < self.long_size:
            if self.Securities[symbol].Price != 0 and self.Securities[symbol].IsTradable:
                self.SetHoldings(symbol, 1 / self.long_size)
                self.long.append(symbol)
                
                return True # Stock successfully invested
        else:
            self.Log("There's not place for additional trade.")
            return False # Unable to invest into stock
            
    def LiquidateChosenSymbols(self, symbols):
        for symbol in symbols:
            if symbol in self.long:
                self.long.remove(symbol)
                self.Liquidate(symbol)
# 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