Quant BuffetRelax, Not Over Thinking

US Holiday Effect in EU Markets

Log in to collect

Academic paper

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.

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.

Backtest performance

Annualised return6.52%
Beta0
Sortino ratio-0.451
Win rate59%

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"))