European Central Bank
Euro Yield Curve
Introduction
The Euro Area Yield Curve dataset by the European Central Bank (ECB) tracks the yield curve of euro area government bonds. The data covers 6 yield curves at 10 maturities from 3 months to 30 years, starts in September 2004, and is delivered on a daily frequency. This dataset is created by the ECB fitting a Svensson model to the prices of euro area government bonds.
For more information about the Euro Yield Curve dataset, including CLI commands and pricing, see the dataset listing.
About the Provider
The European Central Bank is the central bank of the euro area, established in 1998 and based in Frankfurt. Its main task is to keep prices stable in the countries that use the euro, and it sets the interest rates for the euro area. It also publishes statistics on the euro area economy and its financial markets.
Getting Started
The following snippet demonstrates how to request data from the Euro Area Yield Curve dataset:
self.curve = self.add_data(ECBYieldCurve, ECB.YieldCurves.AAA_SPOT, Resolution.DAILY).symbol self.all_issuers = self.add_data(ECBYieldCurve, ECB.YieldCurves.ALL_ISSUERS_SPOT, Resolution.DAILY).symbol
_curve = AddData<ECBYieldCurve>(ECB.YieldCurves.AaaSpot, Resolution.Daily).Symbol; _allIssuers = AddData<ECBYieldCurve>(ECB.YieldCurves.AllIssuersSpot, Resolution.Daily).Symbol;
Requesting Data
To add Euro Area Yield Curve data to your algorithm, call the AddDataadd_data method. Save a reference to the dataset Symbol so you can access the data later in your algorithm.
class ECBYieldCurveDataAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2018, 1, 1)
self.set_end_date(2021, 3, 31)
self.set_cash(100000)
self.equity = self.add_equity("SPY", Resolution.DAILY).symbol
self.curve = self.add_data(ECBYieldCurve, ECB.YieldCurves.AAA_SPOT, Resolution.DAILY).symbol
self.all_issuers = self.add_data(ECBYieldCurve, ECB.YieldCurves.ALL_ISSUERS_SPOT, Resolution.DAILY).symbol public class ECBYieldCurveDataAlgorithm : QCAlgorithm
{
private Symbol _equity, _curve, _allIssuers;
public override void Initialize()
{
SetStartDate(2018, 1, 1);
SetEndDate(2021, 3, 31);
SetCash(100000);
_equity = AddEquity("SPY", Resolution.Daily).Symbol;
_curve = AddData<ECBYieldCurve>(ECB.YieldCurves.AaaSpot, Resolution.Daily).Symbol;
_allIssuers = AddData<ECBYieldCurve>(ECB.YieldCurves.AllIssuersSpot, Resolution.Daily).Symbol;
}
}
Accessing Data
To get the current Euro Area Yield Curve data, index the current Slice with the dataset Symbol. Slice objects deliver unique events to your algorithm as they happen, but the Slice may not contain data for your dataset at every time step. To avoid issues, check if the Slice contains the data you want before you index it.
def on_data(self, slice: Slice) -> None:
if slice.contains_key(self.curve):
data_point = slice[self.curve]
self.log(f"{self.curve} 10Y rate at {slice.time}: {data_point.ten_year}") public override void OnData(Slice slice)
{
if (slice.ContainsKey(_curve))
{
var dataPoint = slice[_curve];
Log($"{_curve} 10Y rate at {slice.Time}: {dataPoint.TenYear}");
}
}
To iterate through all of the dataset objects in the current Slice, call the Getget method.
def on_data(self, slice: Slice) -> None:
for dataset_symbol, data_point in slice.get(ECBYieldCurve).items():
self.log(f"{dataset_symbol} 10Y rate at {slice.time}: {data_point.ten_year}") public override void OnData(Slice slice)
{
foreach (var kvp in slice.Get<ECBYieldCurve>())
{
var datasetSymbol = kvp.Key;
var dataPoint = kvp.Value;
Log($"{datasetSymbol} 10Y rate at {slice.Time}: {dataPoint.TenYear}");
}
}
Historical Data
To get historical Euro Area Yield Curve data, call the Historyhistory method with the dataset Symbol. If there is no data in the period you request, the history result is empty.
# DataFrame history_df = self.history(self.curve, 100, Resolution.DAILY) # Dataset objects history_bars = self.history[ECBYieldCurve](self.curve, 100, Resolution.DAILY)
var history = History<ECBYieldCurve>(_curve, 100, Resolution.Daily);
For more information about historical data, see History Requests.
Supported Curves
The following table shows the accessor code you need to add each curve to your algorithm:
| Curve | Ticker | Constant |
|---|---|---|
Triple A rated issuers | ||
| Spot rate | AAA_SPOT | ECB.YieldCurves.AaaSpotECB.YieldCurves.AAA_SPOT |
| Par yield | AAA_PAR | ECB.YieldCurves.AaaParECB.YieldCurves.AAA_PAR |
| Instantaneous forward | AAA_FORWARD | ECB.YieldCurves.AaaForwardECB.YieldCurves.AAA_FORWARD |
All euro area government issuers | ||
| Spot rate | ALL_SPOT | ECB.YieldCurves.AllIssuersSpotECB.YieldCurves.ALL_ISSUERS_SPOT |
| Par yield | ALL_PAR | ECB.YieldCurves.AllIssuersParECB.YieldCurves.ALL_ISSUERS_PAR |
| Instantaneous forward | ALL_FORWARD | ECB.YieldCurves.AllIssuersForwardECB.YieldCurves.ALL_ISSUERS_FORWARD |
Example Applications
The Euro Area Yield Curve dataset enables you to monitor euro area bond yields across maturities in your strategies. Examples include the following strategies:
- Reducing European equity exposure when the yield curve inverts
- Trading the spread between the AAA and all issuers curves as a sovereign stress signal
- Comparing the euro area and US yield curves for cross-market positioning
Classic Algorithm Example
The following example algorithm buys SPY when the all issuers 10-year spot rate is at most 60 basis points above the AAA rate. Otherwise, it holds cash.
from AlgorithmImports import *
class ECBYieldCurveExampleAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2018, 1, 1)
self.set_end_date(2021, 3, 31)
self.set_cash(100000)
self.equity = self.add_equity("SPY", Resolution.DAILY).symbol
self.triple_a = self.add_data(ECBYieldCurve, ECB.YieldCurves.AAA_SPOT, Resolution.DAILY).symbol
self.all_issuers = self.add_data(ECBYieldCurve, ECB.YieldCurves.ALL_ISSUERS_SPOT, Resolution.DAILY).symbol
history = self.history[ECBYieldCurve](self.triple_a, timedelta(days=365), Resolution.DAILY)
self.debug(f"Got {len(list(history))} historical curve readings")
self.triple_a_ten_year = None
self.all_issuers_ten_year = None
def on_data(self, slice):
curves = slice.get(ECBYieldCurve)
# Keep the latest 10-year rate of each curve.
if self.triple_a in curves:
self.triple_a_ten_year = curves[self.triple_a].ten_year
if self.all_issuers in curves:
self.all_issuers_ten_year = curves[self.all_issuers].ten_year
if self.triple_a_ten_year is None or self.all_issuers_ten_year is None:
return
# Wait for the SPY bar before trading.
if self.equity not in slice.bars:
return
spread = (self.all_issuers_ten_year - self.triple_a_ten_year) * 100
if spread > 60:
if self.portfolio[self.equity].invested:
self.liquidate(self.equity)
elif not self.portfolio[self.equity].invested:
self.set_holdings(self.equity, 1) public class ECBYieldCurveExampleAlgorithm : QCAlgorithm
{
private Symbol _equity, _tripleA, _allIssuers;
private decimal? _tripleATenYear, _allIssuersTenYear;
public override void Initialize()
{
SetStartDate(2018, 1, 1);
SetEndDate(2021, 3, 31);
SetCash(100000);
_equity = AddEquity("SPY", Resolution.Daily).Symbol;
_tripleA = AddData<ECBYieldCurve>(ECB.YieldCurves.AaaSpot, Resolution.Daily).Symbol;
_allIssuers = AddData<ECBYieldCurve>(ECB.YieldCurves.AllIssuersSpot, Resolution.Daily).Symbol;
var history = History<ECBYieldCurve>(_tripleA, TimeSpan.FromDays(365), Resolution.Daily);
Debug($"Got {history.Count()} historical curve readings");
}
public override void OnData(Slice slice)
{
var curves = slice.Get<ECBYieldCurve>();
// Keep the latest 10-year rate of each curve.
if (curves.ContainsKey(_tripleA))
{
_tripleATenYear = curves[_tripleA].TenYear;
}
if (curves.ContainsKey(_allIssuers))
{
_allIssuersTenYear = curves[_allIssuers].TenYear;
}
if (!_tripleATenYear.HasValue || !_allIssuersTenYear.HasValue)
{
return;
}
// Wait for the SPY bar before trading.
if (!slice.Bars.ContainsKey(_equity))
{
return;
}
var spread = (_allIssuersTenYear.Value - _tripleATenYear.Value) * 100m;
if (spread > 60m)
{
if (Portfolio[_equity].Invested)
{
Liquidate(_equity);
}
}
else if (!Portfolio[_equity].Invested)
{
SetHoldings(_equity, 1);
}
}
}
Framework Algorithm Example
The following example algorithm emits up insights for SPY when the 10-year rate of the AAA spot curve is above its 2-year rate. Otherwise, it emits flat insights.
from AlgorithmImports import *
class ECBYieldCurveFrameworkAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2018, 1, 1)
self.set_end_date(2021, 3, 31)
self.set_cash(100000)
self.universe_settings.resolution = Resolution.DAILY
symbols = [Symbol.create("SPY", SecurityType.EQUITY, Market.USA)]
self.set_universe_selection(ManualUniverseSelectionModel(symbols))
self.add_alpha(ECBCurveSlopeAlphaModel(self))
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
class ECBCurveSlopeAlphaModel(AlphaModel):
"""Emits insights from the slope of the euro area yield curve."""
def __init__(self, algorithm):
self._curve = algorithm.add_data(ECBYieldCurve, ECB.YieldCurves.AAA_SPOT, Resolution.DAILY).symbol
history = algorithm.history[ECBYieldCurve](self._curve, timedelta(days=365), Resolution.DAILY)
algorithm.debug(f"Got {len(list(history))} historical curve readings")
self._slope = None
self._symbols = []
def update(self, algorithm, data):
curves = data.get(ECBYieldCurve)
if self._curve in curves:
data_point = curves[self._curve]
if data_point.ten_year is not None and data_point.two_year is not None:
self._slope = data_point.ten_year - data_point.two_year
if self._slope is None:
return []
# Emit up insights while the curve slopes upward and flat insights otherwise.
direction = InsightDirection.UP if self._slope > 0 else InsightDirection.FLAT
# Emit insights only for the securities that have a bar in this slice.
return [Insight.price(symbol, timedelta(days=30), direction)
for symbol in self._symbols if symbol in data.bars]
def on_securities_changed(self, algorithm, changes):
for security in changes.added_securities:
self._symbols.append(security.symbol)
for security in changes.removed_securities:
if security.symbol in self._symbols:
self._symbols.remove(security.symbol) public class ECBYieldCurveFrameworkAlgorithm : QCAlgorithm
{
public override void Initialize()
{
SetStartDate(2018, 1, 1);
SetEndDate(2021, 3, 31);
SetCash(100000);
UniverseSettings.Resolution = Resolution.Daily;
var symbols = new[] { QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA) };
SetUniverseSelection(new ManualUniverseSelectionModel(symbols));
AddAlpha(new ECBCurveSlopeAlphaModel(this));
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
SetExecution(new ImmediateExecutionModel());
}
}
public class ECBCurveSlopeAlphaModel : AlphaModel
{
private readonly Symbol _curve;
private readonly List<Symbol> _symbols = new();
private decimal? _slope;
public ECBCurveSlopeAlphaModel(QCAlgorithm algorithm)
{
_curve = algorithm.AddData<ECBYieldCurve>(ECB.YieldCurves.AaaSpot, Resolution.Daily).Symbol;
var history = algorithm.History<ECBYieldCurve>(_curve, TimeSpan.FromDays(365), Resolution.Daily);
algorithm.Debug($"Got {history.Count()} historical curve readings");
}
public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
{
var curves = data.Get<ECBYieldCurve>();
if (curves.ContainsKey(_curve))
{
var dataPoint = curves[_curve];
if (dataPoint.TenYear.HasValue && dataPoint.TwoYear.HasValue)
{
_slope = dataPoint.TenYear.Value - dataPoint.TwoYear.Value;
}
}
if (!_slope.HasValue)
{
return new List<Insight>();
}
// Emit up insights while the curve slopes upward and flat insights otherwise.
var direction = _slope > 0m ? InsightDirection.Up : InsightDirection.Flat;
// Emit insights only for the securities that have a bar in this slice.
return _symbols
.Where(symbol => data.Bars.ContainsKey(symbol))
.Select(symbol => Insight.Price(symbol, TimeSpan.FromDays(30), direction))
.ToList();
}
public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
{
foreach (var security in changes.AddedSecurities)
{
_symbols.Add(security.Symbol);
}
foreach (var security in changes.RemovedSecurities)
{
_symbols.Remove(security.Symbol);
}
}
}