Original paper
Abstract
We examine the commodity futures pricing role of active attention to weather, disease, geopolitical or economic threats or “hazard fear” as proxied by the volume of internet searches by 149 query terms. A long-short portfolio strategy that sorts the cross-section of commodity futures contracts according to a hazard fear signal captures a significant premium. This commodity hazard fear premium reflects compensation for extant fundamental, tail, volatility and liquidity risks factors, but it is not subsumed by them. Exposure to hazard-fear is strongly priced in the cross-section of commodity portfolios. The hazard fear premium exacerbates during periods of adverse sentiment or pessimism in financial markets.
Keywords:Â Commodity futures; Fear; Attention; Hazards; Internet searches; Sentiment; Long-short portfolios
Trading rules
Here's a rephrased version of the trading rules:
- Investment universe: 28 commodity futures.
- Accumulate weekly data on Google Search Volume Index (GSVI) for a set of 149 terms (refer to the table 1 of the paper)
- Determine the weekly logarithmic variation in GSVI for every term, and then normalize it by its own standard deviation
- Conduct a retrospective regression analysis to determine the correlation between the frequency of keyword searches and the extra returns from commodities
- Calculate hazard-fear CFEAR characteristic as the sum of betas for retained keywords (retain those with the largest t-stat)
- Multiply by 1 if the beta’s t-stat absolute value is larger than the two-sided 10% critical value from a normal N(0,1) distribution
- Multiply by 0 if otherwise
- Normalize the CFEAR metric by deducting the mean across the set and then dividing by the set's standard deviation.
- Categorize commodities into five groups using the adjusted CFEAR value.
- Short the highest group and buy in the lowest group.
- Use an equally-weighted strategy and rebalance weekly.
Python code
Backtrader
import backtrader as bt
import numpy as np
import pandas as pd
from scipy import stats
from sklearn.linear_model import LinearRegression
class HazardFear(bt.Strategy):
params = (
('rebalance_days', 5),
)
def __init__(self):
self.counter = 0
def next(self):
self.counter += 1
if self.counter % self.params.rebalance_days == 0:
self.rebalance()
def rebalance(self):
# Gather weekly GSVI data and calculate log change
gsvi_data = self.get_gsvi_data() # Implement function to fetch GSVI data
log_changes = np.log(gsvi_data / gsvi_data.shift(1))
log_changes_std = log_changes.std()
# Perform backward-looking regression
excess_returns = self.get_excess_returns() # Implement function to fetch excess returns data
betas = []
t_stats = []
for keyword in log_changes_std.index:
X = log_changes[keyword] / log_changes_std[keyword]
y = excess_returns
reg = LinearRegression().fit(X.values.reshape(-1, 1), y)
beta = reg.coef_[0]
betas.append(beta)
t_stat = (beta - 0) / stats.sem(X)
t_stats.append(t_stat)
# Calculate hazard-fear CFEAR characteristic
retained_betas = [beta if abs(t_stat) > stats.norm.ppf(0.95) else 0
for beta, t_stat in zip(betas, t_stats)]
c_fear = np.sum(retained_betas)
# Standardize CFEAR
c_fear_std = (c_fear - c_fear.mean()) / c_fear.std()
# Sort commodities into quintiles based on standardized CFEAR
sorted_commodities = c_fear_std.sort_values()
# Determine long and short positions
short_commodities = sorted_commodities.head(len(sorted_commodities) // 5)
long_commodities = sorted_commodities.tail(len(sorted_commodities) // 5)
# Execute trades
for data in self.getdatanames():
size = 0
if data in long_commodities.index:
size = self.broker.getvalue() / len(long_commodities)
elif data in short_commodities.index:
size = -self.broker.getvalue() / len(short_commodities)
self.order_target_value(data=data, target=size)
if __name__ == '__main__':
cerebro = bt.Cerebro()
cerebro.addstrategy(HazardFear)
# Add data feeds for 28 commodity futures
# Implement the logic to fetch and add data feeds
cerebro.broker.setcash(100000.0)
cerebro.run()
Please note that you will need to implement the functions get_gsvi_data()
and get_excess_returns()
to fetch the required data. Additionally, you’ll need to add data feeds for the 28 commodity futures to the Backtrader Cerebro engine.