ECB Interest Rates
ECB Key Interest Rates and Reference Rates
This interactive visualization displays the evolution of the European Central Bank’s key interest rates from 1999 to the present day.
The visualization shows:
- Main Refinancing Operations (MRO) Rate: The rate at which the ECB lends to banks in regular operations.
- Deposit Facility Rate (DFR): The rate banks receive for depositing money with the ECB overnight.
- Marginal Lending Facility Rate (MLF): The rate at which banks can borrow from the ECB overnight.
- Euro Short-Term Rate (€STR): A reference rate based on euro area banks’ overnight borrowing.
- EONIA: Euro Overnight Index Average (discontinued in January 2022).
Understanding ECB Rates
The three main policy rates (MRO, DFR, and MLF) form an interest rate corridor:
- The MRO rate is the central policy rate used for the ECB’s regular refinancing operations
- The MLF rate forms the ceiling of the corridor
- The DFR rate forms the floor of the corridor
Reference rates like €STR and the former EONIA reflect actual money market transactions and are influenced by these policy rates.
Generating Your Own Visualization
This visualization is created using Python with the following libraries:
ecbdata
: For fetching data directly from the ECB Statistical Data Warehousepandas
: For data manipulation and analysisplotly
: For creating interactive visualizations
You can generate this visualization yourself using the provided Python script. The script and all necessary files are available in the static/python/ecb_rates/
directory of this website.
Step 1: Clone the Repository
git clone https://github.com/yourusername/yourrepository.git
cd yourrepository
Step 2: Install Dependencies
cd static/python/ecb_rates
pip install -r requirements.txt
Step 3: Run the Script
python generate_visualization.py
This will:
- Fetch the latest ECB rates data from the ECB Statistical Data Warehouse
- Generate an interactive HTML visualization
- Save it to
static/html/ecb_rates.html
Optional Arguments
You can customize the script with the following arguments:
--start-date
: Start date in YYYY-MM format (default: 1999-01)--output
: Output HTML file path (default: static/html/ecb_rates.html)
Example with custom parameters:
python generate_visualization.py --start-date 2010-01 --output my_custom_ecb_rates.html
Python Code
The complete code for the visualization generator is shown below:
#!/usr/bin/env python3
"""
ECB Key Interest Rates Visualization Generator
This script fetches ECB key interest rates and reference rates data
and generates an interactive visualization using Plotly.
"""
from ecbdata import ecbdata
import pandas as pd
import plotly.graph_objects as go
import os
import argparse
from datetime import datetime
# Series keys for ECB rates
SERIES_KEYS = {
'MRO (Fixed Rate)': 'FM.D.U2.EUR.4F.KR.MRR_FR.LEV', # Main Refinancing Operations Rate (Fixed Rate)
'MRO (Variable Rate)': 'FM.D.U2.EUR.4F.KR.MRR_MBR.LEV', # Main Refinancing Operations Rate (Variable Rate)
'DFR': 'FM.D.U2.EUR.4F.KR.DFR.LEV', # Deposit Facility Rate
'MLF': 'FM.D.U2.EUR.4F.KR.MLFR.LEV', # Marginal Lending Facility Rate
'EONIA': 'EON.D.EONIA_TO.RATE', # EONIA Rate (discontinued)
'€STR': 'EST.B.EU000A2X2A25.WT' # Euro Short-Term Rate
}
def fetch_ecb_rates(start_date='1999-01'):
"""
Fetches ECB key interest rates and reference rates at daily frequency.
Combines MRO Fixed and Variable Rates into a single 'MRO' series.
Args:
start_date (str): Start date in YYYY-MM format
Returns:
pandas.DataFrame: DataFrame with rates data
"""
rates_data = {}
# Fetch each rate series
for rate_name, series_key in SERIES_KEYS.items():
try:
df = ecbdata.get_series(series_key, start=start_date)
# Convert TIME_PERIOD to datetime and set as index (if present)
if 'TIME_PERIOD' in df.columns:
df['TIME_PERIOD'] = pd.to_datetime(df['TIME_PERIOD'])
df = df.set_index('TIME_PERIOD')
else:
df.index = pd.to_datetime(df.index)
rates_data[rate_name] = df['OBS_VALUE']
except Exception as e:
print(f"Error fetching {rate_name}: {e}")
# Combine all rates into a single DataFrame
combined_rates = pd.DataFrame(rates_data)
# Combine the two MRO series (Fixed & Variable) by taking their mean
if 'MRO (Fixed Rate)' in combined_rates.columns and 'MRO (Variable Rate)' in combined_rates.columns:
combined_rates['MRO'] = combined_rates[['MRO (Fixed Rate)', 'MRO (Variable Rate)']].mean(axis=1)
combined_rates.drop(columns=['MRO (Fixed Rate)', 'MRO (Variable Rate)'], inplace=True)
return combined_rates
def create_interactive_plot(rates_df=None, output_path='static/html/ecb_rates.html'):
"""
Creates an interactive plot of the ECB rates and saves it as HTML.
Args:
rates_df (pandas.DataFrame, optional): DataFrame with rates data
output_path (str): Path where to save the HTML file
"""
# If no data provided, fetch from ECB
if rates_df is None:
try:
rates_df = fetch_ecb_rates()
except Exception as e:
print(f"Error fetching ECB rates: {e}")
return
fig = go.Figure()
# Plot policy rates with solid lines
policy_rates = ['MRO', 'DFR', 'MLF']
for rate in policy_rates:
if rate in rates_df.columns:
fig.add_trace(
go.Scatter(
x=rates_df.index,
y=rates_df[rate],
name=rate,
line=dict(width=2),
hovertemplate="%{x|%Y-%m-%d}<br>" +
f"{rate}: %{{y:.3f}}%<br>" +
"<extra></extra>"
)
)
# Plot reference rates with dashed lines
reference_rates = {'€STR': 'rgba(240,128,128,0.8)', 'EONIA': 'rgba(176,196,222,0.8)'}
for rate, color in reference_rates.items():
if rate in rates_df.columns:
fig.add_trace(
go.Scatter(
x=rates_df.index,
y=rates_df[rate],
name=rate,
line=dict(width=2, dash='dash', color=color),
hovertemplate="%{x|%Y-%m-%d}<br>" +
f"{rate}: %{{y:.3f}}%<br>" +
"<extra></extra>"
)
)
# Update layout
fig.update_layout(
title='ECB Key Interest Rates and Reference Rates',
xaxis_title='Date',
yaxis_title='Rate (%)',
hovermode='x unified',
plot_bgcolor='white',
xaxis=dict(
showgrid=True,
gridwidth=1,
gridcolor='rgba(128,128,128,0.2)',
),
yaxis=dict(
showgrid=True,
gridwidth=1,
gridcolor='rgba(128,128,128,0.2)',
),
legend=dict(
yanchor="top",
y=0.99,
xanchor="left",
x=0.01
),
margin=dict(l=50, r=50, t=50, b=50)
)
# Get the latest rates and last available values
latest_rates = rates_df.iloc[-1]
# For €STR and EONIA, get last available (non-NaN) observations and their dates
estr_series = rates_df['€STR'].dropna() if '€STR' in rates_df.columns else pd.Series()
eonia_series = rates_df['EONIA'].dropna() if 'EONIA' in rates_df.columns else pd.Series()
# Create info section
rates_info = "<div style='font-family: Arial, sans-serif; padding: 10px; background-color: #f8f9fa; border-radius: 5px; margin-bottom: 20px;'>"
rates_info += "<h3>Latest Available Rates:</h3>"
rates_info += "<ul>"
# Add policy rates
for rate_name in ['MRO', 'DFR', 'MLF']:
if rate_name in latest_rates.index:
value = latest_rates.get(rate_name, None)
if pd.notna(value):
rates_info += f"<li><strong>{rate_name}:</strong> {value:.3f}%</li>"
# Add €STR and EONIA information
if not estr_series.empty:
last_estr = estr_series.iloc[-1]
last_estr_date = estr_series.index[-1]
rates_info += f"<li><strong>€STR:</strong> {last_estr:.3f}% (as of {last_estr_date.strftime('%Y-%m-%d')})</li>"
if not eonia_series.empty:
last_eonia = eonia_series.iloc[-1]
last_eonia_date = eonia_series.index[-1]
rates_info += f"<li><strong>EONIA:</strong> {last_eonia:.3f}% (discontinued on {last_eonia_date.strftime('%Y-%m-%d')})</li>"
rates_info += "</ul>"
# Add last updated timestamp
rates_info += f"<p><em>Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</em></p>"
rates_info += "</div>"
# Save both the info and the figure to an HTML file
html_content = rates_info + fig.to_html(full_html=False, include_plotlyjs='cdn')
# Ensure the directory exists
os.makedirs(os.path.dirname(output_path), exist_ok=True)
# Write the HTML file
with open(output_path, 'w', encoding='utf-8') as f:
f.write(html_content)
print(f"ECB rates visualization has been saved to {output_path}")
return output_path
def main():
"""Main function to parse arguments and generate the visualization."""
parser = argparse.ArgumentParser(description='Generate ECB rates visualization')
parser.add_argument('--start-date', type=str, default='1999-01',
help='Start date in YYYY-MM format (default: 1999-01)')
parser.add_argument('--output', type=str, default='static/html/ecb_rates.html',
help='Output HTML file path (default: static/html/ecb_rates.html)')
args = parser.parse_args()
try:
rates = fetch_ecb_rates(start_date=args.start_date)
create_interactive_plot(rates, output_path=args.output)
except Exception as e:
print(f"Error generating visualization: {e}")
return 1
return 0
if __name__ == "__main__":
exit(main())
## Setting Up Automatic Updates
For a website that always displays the latest ECB rates, you can set up automatic updates using GitHub Actions. Here's a simple workflow example:
```yaml
name: Update ECB Rates Visualization
on:
schedule:
# Run every Monday at 9:00 AM
- cron: '0 9 * * 1'
workflow_dispatch: # Allow manual triggers
jobs:
update-visualization:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
cd static/python/ecb_rates
pip install -r requirements.txt
- name: Generate visualization
run: |
cd static/python/ecb_rates
python generate_visualization.py
- name: Commit and push changes
run: |
git config --global user.name 'GitHub Actions'
git config --global user.email 'actions@github.com'
git add static/html/ecb_rates.html
git diff --staged --quiet || (git commit -m "Update ECB rates visualization" && git push)
How to Use the Visualization
- Hover: See exact values at specific dates
- Zoom: Click and drag to zoom in on a specific time period
- Pan: After zooming, click and drag to pan across different periods
- Reset: Double-click to reset the view
- Legend: Click on items in the legend to hide/show specific rates