Synthetic Lending Rates Predict Subsequent Market Return
Log in to collectAcademic paper
Synthetic Lending Rates Predict Subsequent Market Return
Matus Padysak
- SKComenius University Bratislava
- ?Comenius University - Faculty of Mathematics, Physics and Informatics
- ?Quantpedia.com
Strategy in a nutshell
Universe: SPY ETF. Use CBOE borrow intensity data (45-day maturity) to measure synthetic shorting costs. Daily, calculate the change in aggregate borrow intensity:
Positive change → buy SPY at 15:59
Negative change → short SPY at 15:59
Positions are closed the next day at 15:58.
Economic rationale
Changes in borrow intensity reflect shorting demand and market sentiment:
Increase in intensity → lower shorting fees → positive subsequent returns
Decrease in intensity → higher shorting fees → negative subsequent returns
The signal is short-lived, with partial reversal over two days. Correlations with VIX suggest borrow intensity also proxies fear/sentiment, and intraday negative correlation indicates a short-term reversal effect linked to liquidity provision.
Backtest performance
Full Python code
from AlgorithmImports import *
#endregion
class SyntheticLendingRatesPredictSubsequentMarketReturn(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2016, 1, 1)
self.SetCash(100000)
self.spy_symbol:Symbol = self.AddEquity('SPY', Resolution.Minute).Symbol
self.lending_data_symbol:Symbol = self.AddData(
QuantpediaLendingRates,
'lending_rate',
Resolution.Minute).Symbol
self.last_lending_mean = None
def OnData(self, data: Slice):
curr_time:datetime.datetime = self.Time
# liquidate at 15:58
if curr_time.hour == 15 and curr_time.minute == 58:
self.Liquidate(self.spy_symbol)
# lending rate data came in
if self.lending_data_symbol in data and data[self.lending_data_symbol]:
curr_lending_mean:float = data[self.lending_data_symbol].Value
if self.last_lending_mean:
# calculate daily change in lending rate
diff:float = curr_lending_mean - self.last_lending_mean
if diff > 0:
self.SetHoldings(self.spy_symbol, 1)
else:
self.SetHoldings(self.spy_symbol, -1)
self.last_lending_mean = curr_lending_mean
# Quantpedia data.
# NOTE: IMPORTANT: Data order must be ascending (datewise)
class QuantpediaLendingRates(PythonData):
def GetSource(self, config, date, isLiveMode):
return SubscriptionDataSource("data.quantpedia.com/backtesting_data/options/lending_rates_day_close_matur_45_days.csv".format(config.Symbol.Value), SubscriptionTransportMedium.RemoteFile, FileFormat.Csv)
def Reader(self, config, line, date, isLiveMode):
data:QuantpediaLendingRates = QuantpediaLendingRates()
data.Symbol = config.Symbol
if not line[0].isdigit(): return None
split:list = line.split(';')
datetime_str:str = split[0] + ', 15:59'
data.Time = datetime.strptime(datetime_str, "%Y-%m-%d, %H:%M")
valid_values:list = list(filter(lambda value: value != '', split[1:]))
valid_values:list = list(map(lambda str_value: float(str_value), valid_values))
data.Value = np.mean(valid_values)
return data