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;

Data Summary

The following table describes the dataset properties:

PropertyValue
Start DateSeptember 2004
Asset Coverage6 Yield Curves
Data DensitySparse
ResolutionDaily*
TimezoneBerlin

* Published every TARGET business day, about 255 days a year.

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.

Remove Subscriptions

To remove your subscription to Euro Area Yield Curve data, call the RemoveSecurityremove_security method.

self.remove_security(self.curve)
RemoveSecurity(_curve);

Supported Curves

The following table shows the accessor code you need to add each curve to your algorithm:

CurveTickerConstant
Triple A rated issuers
Spot rateAAA_SPOTECB.YieldCurves.AaaSpotECB.YieldCurves.AAA_SPOT
Par yieldAAA_PARECB.YieldCurves.AaaParECB.YieldCurves.AAA_PAR
Instantaneous forwardAAA_FORWARDECB.YieldCurves.AaaForwardECB.YieldCurves.AAA_FORWARD
All euro area government issuers
Spot rateALL_SPOTECB.YieldCurves.AllIssuersSpotECB.YieldCurves.ALL_ISSUERS_SPOT
Par yieldALL_PARECB.YieldCurves.AllIssuersParECB.YieldCurves.ALL_ISSUERS_PAR
Instantaneous forwardALL_FORWARDECB.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);
        }
    }
}

Data Point Attributes

The Euro Area Yield Curve dataset provides ECBYieldCurve objects, which have the following attributes:

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: