Hedging and Tracking#

Net exposure and basis#

Long \(\$1\) of asset \(i\) and short \(h\) of asset \(j\) yields net exposure

\[ \epsilon_t = r_{i,t} - h\, r_{j,t} \]

Basis risk is the volatility of \(\epsilon_t\).

Basis risk and the optimal hedge#

With volatilities \(\sigma_i, \sigma_j\) and correlation \(\rho_{i,j}\):

\[ \sigma^2_{\epsilon} = \sigma_i^2 + h^2\sigma_j^2 - 2h\sigma_i\sigma_j\rho_{i,j} \]

Minimizing w.r.t. \(h\) gives the optimal hedge ratio

\[ h^{*} = \frac{\sigma_i}{\sigma_j} \rho_{i,j} \]
  • Higher \(\rho\) \(\Rightarrow\) larger \(h^{*}\).

  • If \(\rho<0\), you hedge by going long the hedge asset.

Regression view of hedging#

The optimal hedge ratio is the regression beta from

\[ r_{i,t} = \beta_{i,j} r_{j,t} + \varepsilon_t \]

so \(\beta_{i,j} = h^{*}\) and the minimized basis variance is the residual variance.

Multiple hedging assets and excess returns#

With multiple hedges:

\[ r_{i,t} = \beta_{i,1} r_{1,t} + \cdots + \beta_{i,k} r_{k,t} + \varepsilon_t \]

In excess returns form:

\[ \tilde r_{i,t} = \beta_{i,1} \tilde r_{1,t} + \cdots + \beta_{i,k} \tilde r_{k,t} + \varepsilon_t \]

Adding an intercept isolates the portion of the mean that the hedge cannot replicate.

Market-hedged positions#

If you want exposure to asset \(i\) without market risk \(m\), estimate

\[ \tilde r_{i,t} = \alpha + \beta_{i,m} \tilde r_{m,t} + \varepsilon_t \]

Then take the position \(\tilde r_{i,t} - \beta_{i,m}\tilde r_{m,t}\), which has

\[ \tilde r_{i,t} - \beta_{i,m}\tilde r_{m,t} = \alpha + \varepsilon_t \]

Mean excess return \(=\alpha\), volatility \(=\sigma_{\varepsilon}\).

Tracking vs. hedging#

A tracking portfolio tries to follow a target factor:

\[ \tilde r_{i,t} = \alpha + \beta \tilde r_{j,t} + \varepsilon_t \]
  • \(\varepsilon\) is tracking error.

  • \(R^2\) gauges tracking quality.

  • \(\text{IR}=\alpha/\sigma_{\varepsilon}\) trades extra mean vs extra tracking error.

Mutual funds often track broad factors; hedge funds often hedge them—though in practice, each may keep some exposure.


Looking at the Data#

import pandas as pd
import numpy as np
import seaborn as sns

from cmds.portfolio import performanceMetrics, get_ols_metrics
LOADFILE = '../data/risk_etf_data.xlsx'
info = pd.read_excel(LOADFILE,sheet_name='descriptions').set_index('ticker')
rets = pd.read_excel(LOADFILE,sheet_name='total returns').set_index('Date')
prices = pd.read_excel(LOADFILE,sheet_name='prices').set_index('Date')

FREQ = 252
AS_OF = rets.index.max()
performanceMetrics(rets,FREQ).style.format('{:.1%}',na_rep='-').set_caption(f'Daily performance through {AS_OF:%Y-%m-%d}')
Daily performance through 2026-06-30
  Mean Vol Sharpe Min Max
SPY 15.9% 18.3% 87.1% -10.9% 10.5%
VEA 11.5% 17.4% 66.1% -11.2% 8.9%
UPRO 40.4% 54.8% 73.7% -34.9% 28.0%
GLD 14.1% 16.2% 86.6% -10.3% 6.4%
USO 9.4% 39.2% 24.1% -25.3% 16.7%
FXE 1.3% 7.3% 17.4% -2.1% 2.5%
BTC 65.5% 67.5% 97.0% -37.2% 25.2%
HYG 4.7% 8.3% 57.0% -5.5% 6.5%
IEF 1.5% 6.7% 21.7% -2.5% 2.6%
TIP 2.8% 5.8% 48.9% -2.9% 4.5%
SHV 2.3% 0.3% 851.1% -0.1% 0.1%
get_ols_metrics(rets['SPY'],rets,FREQ).style.format('{:.1%}',na_rep='-')
  alpha SPY r-squared Treynor Ratio Info Ratio
SPY 0.0% 100.0% 100.0% 15.9% -
VEA -1.5% 81.6% 73.4% 14.1% -16.6%
UPRO -7.3% 299.3% 99.8% 13.5% -288.0%
GLD 12.6% 9.4% 1.1% 149.4% 77.8%
USO 0.9% 53.8% 6.3% 17.6% 2.3%
FXE 0.3% 5.9% 2.2% 21.5% 4.5%
BTC 50.1% 96.8% 6.9% 67.7% 76.9%
HYG -0.9% 35.4% 60.7% 13.4% -17.3%
IEF 2.1% -4.2% 1.3% -34.8% 31.8%
TIP 2.5% 2.1% 0.5% 132.8% 43.1%
SHV 2.3% -0.1% 0.3% -2690.0% 857.5%

Considering VEA#

TICK_TARGET = 'VEA'
targ = rets[[TICK_TARGET]]
instruments = rets[['SPY','FXE']]
get_ols_metrics(instruments,targ,FREQ).style.format('{:.1%}',na_rep='-')
  alpha SPY FXE r-squared Info Ratio
VEA -1.7% 77.9% 64.3% 80.4% -22.0%

Considering BTC#

TICK_TARGET = 'BTC'
targ = rets[[TICK_TARGET]]
instruments = rets[['SPY', 'IEF', 'GLD','FXE']]
get_ols_metrics(instruments,targ,FREQ).style.format('{:.1%}',na_rep='-')
  alpha SPY IEF GLD FXE r-squared Info Ratio
BTC 46.7% 91.9% -10.3% 28.2% 30.4% 7.5% 71.9%

Monthly Frequency#

CODE_RESAMPLE = 'ME'
FREQ_M = 12

px = (1+rets).cumprod()
retsM = px.resample(CODE_RESAMPLE).last().pct_change().dropna()
retsM.dropna(inplace=True)
targ = retsM[[TICK_TARGET]]
get_ols_metrics(instruments,targ,FREQ_M).style.format('{:.1%}',na_rep='-')
  alpha SPY IEF GLD FXE r-squared Info Ratio
BTC 82.6% -113.3% -182.2% 422.2% 391.0% 3.6% 104.7%