A Python/Jupyter analysis of WTI and Brent crude oil prices over a decade, covering price trends, volatility, return correlation, drawdowns, and seasonality. Data is pulled live from Yahoo Finance via yfinance.
| Chart | Description |
|---|---|
oil_price_dashboard.png |
Price history, Brent–WTI spread, and 90-day annualised volatility |
oil_return_correlation.png |
Daily return distributions, scatter plot, and rolling 90-day correlation |
oil_seasonality.png |
Average price by month and by year |
oil_drawdown.png |
Cumulative indexed returns and drawdown (base = 2015-01-01) |
.
oil-price-analysis/
├── images/
│ ├── oil_price_dashboard.png
│ ├── oil_return_correlation.png
│ ├── oil_seasonality.png
│ └── oil_drawdown.png
├── notebooks/
│ └── oil_pipeline.ipynb
├── README.md
└── requirements.txt
1. Clone the repository
git clone https://github.com/sandwidinarcisse/oil-price-analysis.git
cd oil-price-analysis2. Create a virtual environment (recommended)
python -m venv .venv
source .venv/bin/activate # macOS / Linux
.venv\Scripts\activate # Windows3. Install dependencies
pip install -r requirements.txt4. Launch the notebook
jupyter notebook oil_prices.ipynbRun all cells — the notebook fetches data from Yahoo Finance, processes it, and saves the four charts to disk.
- Source: Yahoo Finance via
yfinance— tickersBZ=F(Brent) andCL=F(WTI) - Period: 2015-01-01 → 2026-04-30 (daily frequency)
- DatetimeIndex enforced and sorted
- Weekends / public holidays forward-filled (up to 3 days)
- Rows where both prices are missing are dropped
- Extreme outliers clipped at ±5 × IQR from the median
| Feature | Description |
|---|---|
Spread |
Brent Price − WTI Price |
Brent_WTI_Ratio |
Brent / WTI |
*_Return |
Daily percentage return |
*_MA30d / *_MA90d |
30-day and 90-day rolling mean |
*_Vol30d / *_Vol90d |
Annualised rolling volatility (σ × √252) |
Year / Month |
Calendar fields for seasonality |
| Date | Event |
|---|---|
| 2016-01-20 | Oil price crash low |
| 2018-10-03 | 4-year peak |
| 2020-04-20 | WTI goes negative |
| 2022-03-08 | Ukraine war price spike |
| 2023-06-04 | OPEC+ production cuts |
| 2026-02-28 | Iran war |
- Brent–WTI spread is structurally positive but narrow — Brent trades at a modest premium to WTI in most periods, typically in the low single digits ($/bbl). The spread only blew out dramatically in April 2020 when WTI futures went negative, creating an anomalous spike visible in the spread panel.
- WTI went negative on 20 April 2020 (–$37/bbl due to futures contract mechanics and storage constraints), causing a WTI drawdown of approximately –140% from its 2015 base — far exceeding Brent's worst drawdown of ~–70%. This is the largest spread divergence on record, not a typical COVID demand shock.
- Rolling 90-day correlation averages 0.919, not the commonly cited figure of 0.97. The two benchmarks move tightly together under normal conditions, but the correlation collapsed to ~0.35 in April 2020 — the single sharpest decorrelation in the dataset — precisely when WTI's contract mechanics decoupled from physical Brent pricing.
- WTI annualised volatility briefly exceeded 500% in April–May 2020, compared to Brent's ~100% peak over the same window, confirming the 2020 event was a WTI-specific structural dislocation rather than a symmetric commodity shock.
- 2022 was the cycle peak for both benchmarks, driven by the Ukraine war supply shock (annotated March 2022), with Brent reaching ~$128/bbl. Both benchmarks have trended lower since, settling in the $60–$80 range through 2024–2025.
- A new geopolitical event — "Iran war" — is annotated in early 2026, associated with a sharp price spike visible in the dashboard, bringing Brent back above $100/bbl briefly before retracing. This is the most recent macro shock in the dataset.
| Package | Purpose |
|---|---|
pandas |
Data manipulation |
numpy |
Numerical computations |
matplotlib |
All visualisations |
yfinance |
Yahoo Finance data download |
requests |
HTTP (yfinance dependency) |
Python 3.10+ recommended (developed on 3.12.6).
MIT — free to use, modify, and distribute with attribution.