170 lines
5.4 KiB
Markdown
170 lines
5.4 KiB
Markdown
# GLD ETF vs Gold Futures Basis: Implementation Guide
|
||
|
||
## Executive Summary
|
||
|
||
GLD (SPDR Gold Shares ETF) does **not** track gold at a simple 10:1 ratio. Two mechanical factors affect the conversion between GLD and physical gold/futures.
|
||
|
||
## Key Findings for Dashboard Implementation
|
||
|
||
### 1. Expense Ratio Decay (Permanent, Predictable)
|
||
|
||
| Metric | Launch (2004) | Current (2026) |
|
||
|--------|----------------|-----------------|
|
||
| Gold per share | 0.1000 oz | ~0.0919 oz |
|
||
| Effective ratio | 10:1 | **10.9:1** |
|
||
| Cumulative decay | — | 8.1% |
|
||
|
||
**Formula:**
|
||
```
|
||
ounces_per_share = 0.10 * e^(-0.004 * years_since_2004)
|
||
```
|
||
|
||
**Dashboard Implementation:**
|
||
```python
|
||
# Current GLD backing (as of 2026)
|
||
GLD_OUNCES_PER_SHARE = 0.0919
|
||
|
||
def gld_price_to_gold_spot(gld_price: float) -> float:
|
||
"""Convert GLD price to implied gold spot price."""
|
||
return gld_price / GLD_OUNCES_PER_SHARE
|
||
|
||
def gold_spot_to_gld_price(gold_spot: float) -> float:
|
||
"""Convert gold spot price to GLD equivalent."""
|
||
return gold_spot * GLD_OUNCES_PER_SHARE
|
||
|
||
# Example: At $4,600/oz gold
|
||
# GLD ≈ $4,600 × 0.0919 ≈ $423 (NOT $460)
|
||
```
|
||
|
||
### 2. Futures-Spot Basis (Variable, Market-Dependent)
|
||
|
||
| Market State | Futures vs. Spot | GLD vs. GC=F÷10 |
|
||
|--------------|------------------|-----------------|
|
||
| **Contango** (normal) | Futures > Spot by $10-15/oz | GLD appears at "discount" |
|
||
| **Backwardation** (stress) | Spot > Futures | GLD appears at "premium" |
|
||
|
||
**During stress events:**
|
||
- March 2020 COVID: COMEX futures $50-70 above London spot
|
||
- GLD holders benefited (physical-backed)
|
||
- Futures traders suffered negative roll yield
|
||
|
||
### 3. After-Hours Pricing Gap
|
||
|
||
| Instrument | Trading Hours |
|
||
|------------|---------------|
|
||
| GLD | US market hours (9:30 AM - 4 PM ET) |
|
||
| GC=F (futures) | 23 hours/day, 6 days/week |
|
||
|
||
**Implication:** GLD opens with gaps after weekend/overnight gold moves. Dashboard should show "last regular session close" vs. "current futures indication."
|
||
|
||
## Dashboard Recommendations
|
||
|
||
### Data Display
|
||
|
||
```python
|
||
class GoldPriceDisplay:
|
||
"""Recommended price display for vault-dash."""
|
||
|
||
def __init__(self, gld_price: float, gold_futures_price: float):
|
||
self.gld_price = gld_price
|
||
self.gold_futures_price = gold_futures_price
|
||
self.ounces_per_share = 0.0919 # Current GLD backing
|
||
|
||
@property
|
||
def implied_spot_from_gld(self) -> float:
|
||
"""Gold spot implied from GLD price."""
|
||
return self.gld_price / self.ounces_per_share
|
||
|
||
@property
|
||
def gld_fair_value(self) -> float:
|
||
"""What GLD 'should' trade at based on futures."""
|
||
# Adjust for contango (~$10/oz typically)
|
||
spot_estimate = self.gold_futures_price - 10
|
||
return spot_estimate * self.ounces_per_share
|
||
|
||
@property
|
||
def basis_bps(self) -> float:
|
||
"""Basis between GLD and fair value in basis points."""
|
||
diff_bps = (self.gld_price / self.gld_fair_value - 1) * 10000
|
||
return diff_bps
|
||
```
|
||
|
||
### Warning Thresholds
|
||
|
||
```python
|
||
# Raise warnings when basis exceeds normal bounds
|
||
BASIS_WARNING_THRESHOLD = 50 # 50 bps = 0.5%
|
||
|
||
if abs(basis_bps) > BASIS_WARNING_THRESHOLD:
|
||
# GLD trading at unusual premium/discount
|
||
# Possible causes: after-hours gap, physical stress, AP arb failure
|
||
show_warning(f"GLD basis elevated: {basis_bps:.0f} bps")
|
||
```
|
||
|
||
### Options Pricing
|
||
|
||
For Lombard hedge calculations:
|
||
|
||
```python
|
||
def calculate_hedge_strikes(
|
||
portfolio_gold_ounces: float,
|
||
margin_call_price: float,
|
||
current_gold_spot: float
|
||
) -> dict:
|
||
"""
|
||
Calculate appropriate GLD option strikes for hedge.
|
||
|
||
IMPORTANT: Use GLD price directly, not converted from futures.
|
||
"""
|
||
# Convert gold price thresholds to GLD strikes
|
||
gld_current = current_gold_spot * GLD_OUNCES_PER_SHARE
|
||
gld_margin_call = margin_call_price * GLD_OUNCES_PER_SHARE
|
||
|
||
# Recommended strikes
|
||
atm_strike = round(gld_current) # e.g., $423
|
||
otm_10_strike = round(gld_current * 0.90) # 10% OTM: ~$381
|
||
otm_5_strike = round(gld_current * 0.95) # 5% OTM: ~$402
|
||
|
||
return {
|
||
"current_gld": gld_current,
|
||
"margin_call_gld": gld_margin_call,
|
||
"atm_strike": atm_strike,
|
||
"otm_5pct_strike": otm_5_strike,
|
||
"otm_10pct_strike": otm_10_strike,
|
||
"contracts_needed": math.ceil(portfolio_gold_ounces / (100 * GLD_OUNCES_PER_SHARE))
|
||
}
|
||
```
|
||
|
||
## Data Sources
|
||
|
||
| Data Point | Source | Notes |
|
||
|------------|--------|-------|
|
||
| GLD NAV/ounce | spdrgoldshares.com | Daily updated |
|
||
| GLD price | Market data API | Real-time |
|
||
| GC=F price | CME/futures API | Extended hours |
|
||
| Contango estimate | Futures curve | Calculate from term structure |
|
||
|
||
## Key Takeaways for Vault-Dash
|
||
|
||
1. **Never use a fixed 10:1 ratio** — always use current GLD backing (~0.092 oz/share)
|
||
|
||
2. **Display both measures:**
|
||
- GLD implied spot = GLD ÷ 0.0919
|
||
- GC=F adjusted = GC=F ÷ 10 (naive) for comparison
|
||
|
||
3. **Show basis indicator:**
|
||
- Green: basis within ±25 bps (normal)
|
||
- Yellow: basis ±25-50 bps (elevated)
|
||
- Red: basis > 50 bps (unusual — possible stress)
|
||
|
||
4. **For hedging:**
|
||
- Use GLD's actual price for strike selection
|
||
- Contract count = gold ounces ÷ (100 × 0.0919)
|
||
- Don't convert from GC=F to GLD — use GLD directly
|
||
|
||
## References
|
||
|
||
- SEC GLD Registration Statement (2004)
|
||
- SPDR Gold Shares: GLDM methodology updates
|
||
- CME Gold Futures specifications
|
||
- Research paper: "Optimal Hedging Strategies for Gold-Backed Lombard Loans" |