Buy DAX index (via ETF, CFD, fund or futures) at European close one day before each US holiday day. Sell on European close during US holiday day. Hold cash otherwise.

I. STRATEGY IN A NUTSHELL

The strategy exploits informational advantages in the options market to forecast stock returns. By analyzing option-based indicators—such as volatility skew, implied-realized volatility spread, and changes in skew—it identifies stocks likely to outperform or underperform. The portfolio goes long on stocks with bullish option signals and short on those with bearish ones, rebalanced weekly for consistency.

II. ECONOMIC RATIONALE

Options markets often reflect private information earlier than equities because of their lower transaction costs, higher leverage, and relaxed short-selling limits. When equity markets lag in incorporating this information, option-implied signals can predict future stock movements.

III. SOURCE PAPER

The Effect of US Holidays on the European Markets: When the Cat’s Away [Click to Open PDF]

Casado, Muga, Santamaria,

<Abstract>

This paper presents evidence of the existence of a return effect on European stock markets coinciding with NYSE holidays, which is particularly marked after positive closing returns on the NYSE the previous day. The effect is large enough to be exploited by trading index futures. This anomaly can not be explained by seasonal effects, such as the day of the week effect, the January effect or the pre-holiday effect, nor is it consistent with behavioral finance models that predict positive correlation between trading volume and returns. However, examination of factors such as information volume or investor mix provides a reasonable explanation.

IV. BACKTEST PERFORMANCE

Annualised Return6.52%
VolatilityN/A
Beta0
Sharpe RatioN/A
Sortino Ratio-0.451
Maximum DrawdownN/A
Win Rate59%

V. FULL PYTHON CODE

from AlgorithmImports import *
class USHolidayEUMarkets(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2012, 1, 1)
        self.SetCash(100000)           
        
        data = self.AddData(QuantpediaFutures, "EUREX_FDAX1", Resolution.Daily)
        data.SetFeeModel(CustomFeeModel())
        self.symbol = data.Symbol
        
    def OnData(self, data):
        if self.securities[self.symbol].get_last_data() and self.time.date() > QuantpediaFutures.get_last_update_date()[self.symbol]:
            self.liquidate()
            return
        calendar1 = self.TradingCalendar.GetDaysByType(TradingDayType.PublicHoliday, self.Time, self.Time)
        calendar2 = self.TradingCalendar.GetDaysByType(TradingDayType.Weekend, self.Time, self.Time + timedelta(days=2))
        
        holidays = [i.Date for i in calendar1]
        weekends = [i.Date for i in calendar2]
        
        # subtract weekends in all holidays
        public_holidays = list(set(holidays) - set(weekends))
        if data.contains_key(self.symbol) and data[self.symbol]:
            if not self.Portfolio.Invested and len(public_holidays) > 0:
                self.SetHoldings(self.symbol, 1)
            if self.Portfolio.Invested and len(public_holidays) == 0:
                self.Liquidate()
# Quantpedia data.
# NOTE: IMPORTANT: Data order must be ascending (datewise)
class QuantpediaFutures(PythonData):
    _last_update_date:Dict[Symbol, datetime.date] = {}
    @staticmethod
    def get_last_update_date() -> Dict[Symbol, datetime.date]:
       return QuantpediaFutures._last_update_date
    def GetSource(self, config, date, isLiveMode):
        return SubscriptionDataSource("data.quantpedia.com/backtesting_data/futures/{0}.csv".format(config.Symbol.Value), SubscriptionTransportMedium.RemoteFile, FileFormat.Csv)
    def Reader(self, config, line, date, isLiveMode):
        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])
        if config.Symbol not in QuantpediaFutures._last_update_date:
            QuantpediaFutures._last_update_date[config.Symbol] = datetime(1,1,1).date()
        if data.Time.date() > QuantpediaFutures._last_update_date[config.Symbol]:
            QuantpediaFutures._last_update_date[config.Symbol] = 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"))

Leave a Reply

Discover more from Quant Buffet

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

Continue reading