Hedging Factor in Cryptocurrencies
Log in to collectAcademic paper
Predictability of Crypto Returns: A Habit-Based Explanation of the Risk Premium
Kwamie Dunbar; Johnson Owusu-Amoako
- Worcester Polytechnic Institute
- Fayetteville State University
Strategy in a nutshell
Allocate weekly between Bitcoin and risk-free T-bills using a hedging factor from commercial traders’ net short Bitcoin futures positions. Expected excess returns are forecast via regression, adjusted for risk aversion and volatility.
Economic rationale
Commercial traders’ futures positions contain predictive information for Bitcoin returns. This hedging factor outperforms uncertainty measures and traditional risk factors, enabling risk-averse investors to dynamically adjust exposure for superior risk-adjusted performance.
Backtest performance
Annualised return4.68%
Beta0.124
Sortino ratio0.48
Win rate44%
Full Python code
from AlgorithmImports import *
import numpy as np
from data_tools import CommitmentsOfTraders, CryptoCotPair
from typing import Tuple, List
import statsmodels.api as sm
#endregion
class HedgingFactorinCryptocurrencies(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1)
self.SetCash(1000000)
self.leverage:int = 5
# subscribe crypto and COT data
data:Crypto = self.AddCrypto('BTCUSD', Resolution.Minute, Market.Bitfinex)
data.SetLeverage(self.leverage)
crypto_symbol:Symbol = data.Symbol
cot_symbol:Symbol = self.AddData(CommitmentsOfTraders, 'QBT', Resolution.Daily).Symbol
data:Equity = self.AddEquity('BIL', Resolution.Minute)
data.SetLeverage(self.leverage)
self.t_bill:Symbol = data.Symbol
self.risk_aversion:float = 3.
self.allocation_limits:List[float] = [-0.5, 1.5]
# weekly hedging factor data
hedging_factor_w_period:int = 8
price_w_period:int = hedging_factor_w_period + 1
self.SetWarmup(max(price_w_period, hedging_factor_w_period) * 7, Resolution.Daily)
max_missing_crypto_days:int = 5
max_missing_cot_days:int = 7
self.crypto_cot_pair:CryptoCotPair = CryptoCotPair(crypto_symbol, \
cot_symbol, \
price_w_period, \
hedging_factor_w_period, \
max_missing_crypto_days, \
max_missing_cot_days
)
def OnData(self, data:Slice) -> None:
# COT data is present in the algo
if self.crypto_cot_pair._cot_symbol in data and data[self.crypto_cot_pair._cot_symbol] and \
self.crypto_cot_pair._crypto_symbol in data and data[self.crypto_cot_pair._crypto_symbol]:
# update weekly COT data
cot_data = data[self.crypto_cot_pair._cot_symbol]
comm_hedgers_interest:float = cot_data.GetProperty("COMMERCIAL_HEDGER_LONG") + cot_data.GetProperty("COMMERCIAL_HEDGER_SHORT")
if comm_hedgers_interest != 0:
hedging_factor_value:float = cot_data.GetProperty("COMMERCIAL_HEDGER_SHORT") / comm_hedgers_interest
price:float = data[self.crypto_cot_pair._crypto_symbol].Value
self.crypto_cot_pair.update_data(price, hedging_factor_value)
if self.crypto_cot_pair.is_ready():
x:Tuple[np.ndarray, np.ndarray] = self.crypto_cot_pair.get_regression_data()
model = self.multiple_linear_regression(x[1][:-1], x[0][1:])
forecast_return:float = model.predict([1, x[1][-1]])[0]
forecast_variance:float = np.std(x[0]) ** 2 # * np.sqrt(52)
w_t:float = (1. / self.risk_aversion) * (forecast_return / forecast_variance)
w_t = min(max(w_t, self.allocation_limits[0]), self.allocation_limits[1])
t_bill_w:float = 1. - w_t
self.SetHoldings(self.crypto_cot_pair._crypto_symbol, w_t)
self.SetHoldings(self.t_bill, t_bill_w)
else:
# COT data is still comming in
if not self.crypto_cot_pair.cot_updated(self):
self.crypto_cot_pair.reset_data()
self.Liquidate()
def multiple_linear_regression(self, x:np.ndarray, y:np.ndarray):
x = np.array(x).T
x = sm.add_constant(x)
result = sm.OLS(endog=y, exog=x).fit()
return result