Precious Metals: Daily Spot Price Snapshot¶
A quick daily view of gold, silver, platinum, and palladium with performance, volatility, and a simple 12-month trend projection.
Data source & cadence: Alpha Vantage (daily), using TIME_SERIES_DAILY with symbolUSD and an FX_DAILY fallback, refreshed each morning with the AV_API_KEY secret. Timestamps below show the latest run (UTC).
Precious Metal Spot Price Comparison (5 Years)¶
This notebook compares precious metal spot prices over the last five years and builds a simple forecast using interactive charts.
Where to get spot price data¶
Common sources for spot price data include:
- LBMA (London Bullion Market Association): Official daily gold and silver price benchmarks. Useful for authoritative spot pricing.
- Metals-API: Paid/free tiers with JSON API access for multiple metals (gold, silver, platinum, palladium).
- Alpha Vantage: Free tier provides precious metals data with API keys and rate limits.
- Quandl/Nasdaq Data Link: Offers LBMA and other datasets (free and paid).
- Yahoo Finance: Convenient access for analysis (e.g.,
XAUUSD=X,XAGUSD=X,XPTUSD=X,XPDUSD=X). While not an official benchmark, it's easy to use for exploratory analysis.
This notebook uses Yahoo Finance spot proxies for convenience.
# If needed, install dependencies
!pip install -q yfinance pandas plotly statsmodels
import os
import time
import pandas as pd
import requests
import plotly.express as px
import plotly.graph_objects as go
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from datetime import datetime, timezone
from IPython.display import Markdown, display
import yfinance as yf
Data download¶
Download the last five years of daily closes for each metal.
tickers = {
"Gold (XAUUSD)": "GC=F",
"Silver (XAGUSD)": "SI=F",
# "Platinum (XPTUSD)": "PL=F",
# "Palladium (XPDUSD)": "PA=F",
}
price_frames = []
skipped = []
for label, yahoo_symbol in tickers.items():
try:
ticker = yf.Ticker(yahoo_symbol)
# Download data for the last 5 years using Ticker.history
# auto_adjust=True is the default for history(), so no need to specify.
# progress=False is not a valid argument for history()
data = ticker.history(period="5y", interval="1d")
if not data.empty:
# Use 'Close' as auto_adjust handles splits/dividends
series = data['Close'].rename(label)
price_frames.append(series)
else:
skipped.append(f"{label} (No data returned from Yahoo Finance for symbol {yahoo_symbol})")
except Exception as e:
skipped.append(f"{label} (Error fetching data from Yahoo Finance: {type(e).__name__} - {e})")
time.sleep(1) # Be nice to Yahoo Finance
if not price_frames:
raise ValueError("No price data returned from Yahoo Finance for any metal. Skipped: " + ", ".join(skipped))
prices = pd.concat(price_frames, axis=1).sort_index().ffill().dropna(how="all")
if prices.empty:
raise ValueError("Price data empty after cleaning; check Yahoo Finance availability.")
prices.index.name = "Date"
prices.tail()
normalized = prices / prices.iloc[0] * 100
normalized.head()
pct_change = prices.pct_change().dropna() * 100
pct_change.head()
Spot price history¶
See how absolute prices have moved over time.
fig = px.line(
prices.reset_index(),
x="Date",
y=prices.columns,
title="Spot Price Comparison (Last 5 Years)",
labels={"value": "USD per troy ounce", "Date": "Date"},
)
fig.update_layout(legend_title_text="Metal")
fig.show()