UEFA Match Day Short Soccer Stock Strategy
Log in to collectAcademic paper
Understanding Investor Sentiment: The Case of Soccer
Gennaro Bernile; Evgeny Lyandres
- University of Miami
- ?University of Miami - Department of Finance
- ILTel Aviv University
- ?Tel-Aviv University
Strategy in a nutshell
The investment universe consists of liquid soccer clubs’ stocks that are publicly traded. The investor then sells short stocks of clubs that play UEFA Championship matches (or other important matches) at the end of the business day before the match. Stocks are held for one day, and the portfolio of stocks is equally weighted (if there are multiple clubs with matches that day).
Economic rationale
The academic paper postulates that an important reason for the stock market’s apparent inefficient response to soccer game results is the systematic bias in investors’ beliefs about the probability distribution of match outcomes. Bookmaker odds are compiled by a small group of experts and do not reflect investors’ subjective beliefs, while equity prices do so.
Backtest performance
Full Python code
from AlgoLib import *
#endregion
class SoccerClubsStocksArbitrage(XXX):
def Initialize(self):
self.SetStartDate(2000, 1, 1)
self.SetCash(100000)
self.tickers = [
'FCPP', # Futebol Clube Do Porto
'SPSO', # Sporting Clube De Portugal
'SLBEN', # Benfica
'LAZI', # Lazio
'ASR', # AS Rome
'AJAX', # AJAX
'JUVE', # Juventus
'MANU', # Manchester United
'BVB', # Dortmund
'CCP', # Celtic
# 'BOLA' # Bali Bintang Sejahtera Tbk PT
]
self.match_dates = {}
for ticker in self.tickers:
security = self.AddData(QuantpediaSoccer, ticker, Resolution.Daily)
security.SetFeeModel(CustomFeeModel())
security.SetLeverage(5)
csv_string_file = self.Download('data.quantpedia.com/backtesting_data/equity/soccer/soccer_matches.csv')
lines = csv_string_file.split('\r\n')
for line in lines:
line_split = line.split(';')
date = datetime.strptime(line_split[0], "%d.%m.%Y").date()
self.match_dates[date] = []
for i in range(1, len(line_split)):
ticker = line_split[i]
self.match_dates[date].append(ticker)
def OnData(self, data):
self.Liquidate()
short = []
# Looking for todays date, because only daily closes are traded.
today = (self.Time - timedelta(days=1)).date()
if today in self.match_dates:
for ticker in self.tickers:
if ticker in self.match_dates[today] and ticker in data:
short.append(ticker)
for ticker in short:
self.SetHoldings(ticker, -1 / len(short))
# Quantpedia data.
# NOTE: IMPORTANT: Data order must be ascending (datewise)
class QuantpediaSoccer(PythonData):
def GetSource(self, config, date, isLiveMode):
return SubscriptionDataSource("data.quantpedia.com/backtesting_data/equity/soccer/{0}.csv".format(config.Symbol.Value), SubscriptionTransportMedium.RemoteFile, FileFormat.Csv)
def Reader(self, config, line, date, isLiveMode):
data = QuantpediaSoccer()
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['price'] = float(split[1])
data.Value = float(split[1])
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"))