European Central Bank

Policy Rates

Introduction

The Euro Area Policy Rates dataset by the European Central Bank (ECB) tracks the three key ECB interest rates and the euro short-term rate. The data covers the euro area, starts in January 1999, and is delivered on a daily frequency. This dataset is created by processing the rates the ECB publishes on its Data Portal. The euro short-term rate starts in October 2019.

For more information about the Policy Rates 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 Policy Rates dataset:

self.rates = self.add_data(ECBPolicyRates, ECB.PolicyRates.EURO_AREA, Resolution.DAILY).symbol
_rates = AddData<ECBPolicyRates>(ECB.PolicyRates.EuroArea, Resolution.Daily).Symbol;

Data Summary

The following table describes the dataset properties:

PropertyValue
Start DateJanuary 1999
Asset Coverage1 Euro Area Series
Data DensityRegular
ResolutionDaily*
TimezoneBerlin

* One row per weekday, including TARGET holidays.

Requesting Data

To add Euro Area Policy Rates 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 ECBPolicyRatesExampleAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.set_start_date(2020, 1, 1)
        self.set_end_date(2021, 1, 1)
        self._rates = self.add_data(ECBPolicyRates, ECB.PolicyRates.EURO_AREA).symbol
public class ECBPolicyRatesExampleAlgorithm : QCAlgorithm
{
    private Symbol _rates;

    public override void Initialize()
    {
        SetStartDate(2020, 1, 1);
        SetEndDate(2021, 1, 1);
        _rates = AddData<ECBPolicyRates>(ECB.PolicyRates.EuroArea).Symbol;
    }
}

Accessing Data

To get the current Euro Area Policy Rates 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._rates):
        data_point = slice[self._rates]
        self.log(f"{self._rates} deposit facility rate at {slice.time}: {data_point.deposit_facility_rate}")
public override void OnData(Slice slice)
{
    if (slice.ContainsKey(_rates))
    {
        var dataPoint = slice[_rates];
        Log($"{_rates} deposit facility rate at {slice.Time}: {dataPoint.DepositFacilityRate}");
    }
}

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(ECBPolicyRates).items():
        self.log(f"{dataset_symbol} deposit facility rate at {slice.time}: {data_point.deposit_facility_rate}")
public override void OnData(Slice slice)
{
    foreach (var kvp in slice.Get<ECBPolicyRates>())
    {
        var datasetSymbol = kvp.Key;
        var dataPoint = kvp.Value;
        Log($"{datasetSymbol} deposit facility rate at {slice.Time}: {dataPoint.DepositFacilityRate}");
    }
}

Historical Data

To get historical Euro Area Policy Rates 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._rates, 100, Resolution.DAILY)

# Dataset objects
history_bars = self.history[ECBPolicyRates](self._rates, 100, Resolution.DAILY)
var history = History<ECBPolicyRates>(_rates, 100, Resolution.Daily);

For more information about historical data, see History Requests.

Remove Subscriptions

To remove your subscription to Euro Area Policy Rates data, call the RemoveSecurityremove_security method.

self.remove_security(self._rates)
RemoveSecurity(_rates);

Supported Rates

The following table shows the rates in the Euro Area Policy Rates dataset:

RatePropertyCoverage
Main refinancing operationsMainRefinancingRatemain_refinancing_rateJanuary 1999 onward
Deposit facilityDepositFacilityRatedeposit_facility_rateJanuary 1999 onward
Marginal lending facilityMarginalLendingRatemarginal_lending_rateJanuary 1999 onward
Euro short-term rateEuroShortTermRateeuro_short_term_rateOctober 2019 onward

Example Applications

The Euro Area Policy Rates dataset enables you to follow ECB monetary policy in your strategies. Examples include the following strategies:

  • Trading the gap between the euro short-term rate and the deposit facility rate
  • Using the euro funding cost in carry and financing calculations
  • Comparing ECB and Federal Reserve rates for currency positioning

Classic Algorithm Example

The following example algorithm buys SPY when the euro short-term rate is at least five basis points below the deposit facility rate. Otherwise, it holds cash.

from AlgorithmImports import *


class ECBPolicyRatesAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2019, 1, 1)
        self.set_end_date(2021, 3, 31)
        self.set_cash(100000)

        self.equity = self.add_equity("SPY", Resolution.DAILY).symbol
        self.rates = self.add_data(ECBPolicyRates, ECB.PolicyRates.EURO_AREA, Resolution.DAILY).symbol

        self.spread = None

    def on_data(self, slice: Slice) -> None:
        rates = slice.get(ECBPolicyRates)
        if self.rates in rates:
            point = rates[self.rates]

            if point.euro_short_term_rate is not None and point.deposit_facility_rate is not None:
                self.log(f"{point.end_time} corridor: deposit {point.deposit_facility_rate}, "
                         f"main {point.main_refinancing_rate}, lending {point.marginal_lending_rate}, "
                         f"overnight {point.euro_short_term_rate}")

                # Measure how far the overnight rate trades below the deposit facility rate.
                self.spread = point.euro_short_term_rate - point.deposit_facility_rate

        if self.spread is None:
            return

        # Wait for the SPY bar before trading.
        if self.equity not in slice.bars:
            return

        if self.spread > -0.05:
            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 ECBPolicyRatesAlgorithm : QCAlgorithm
{
    private Symbol _equity;
    private Symbol _rates;

    private decimal? _spread;

    public override void Initialize()
    {
        SetStartDate(2019, 1, 1);
        SetEndDate(2021, 3, 31);
        SetCash(100000);

        _equity = AddEquity("SPY", Resolution.Daily).Symbol;
        _rates = AddData<ECBPolicyRates>(ECB.PolicyRates.EuroArea, Resolution.Daily).Symbol;
    }

    public override void OnData(Slice slice)
    {
        var rates = slice.Get<ECBPolicyRates>();
        if (rates.ContainsKey(_rates))
        {
            var point = rates[_rates];

            if (point.EuroShortTermRate.HasValue && point.DepositFacilityRate.HasValue)
            {
                Log($"{point.EndTime} corridor: deposit {point.DepositFacilityRate}, "
                    + $"main {point.MainRefinancingRate}, lending {point.MarginalLendingRate}, "
                    + $"overnight {point.EuroShortTermRate}");

                // Measure how far the overnight rate trades below the deposit facility rate.
                _spread = point.EuroShortTermRate.Value - point.DepositFacilityRate.Value;
            }
        }

        if (!_spread.HasValue)
        {
            return;
        }

        // Wait for the SPY bar before trading.
        if (!slice.Bars.ContainsKey(_equity))
        {
            return;
        }

        if (_spread > -0.05m)
        {
            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 euro short-term rate is at least five basis points below the deposit facility rate. Otherwise, it emits flat insights.

from AlgorithmImports import *

class ECBPolicyRatesFrameworkAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2019, 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(ECBCorridorAlphaModel(self))

        self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
        self.set_execution(ImmediateExecutionModel())


class ECBCorridorAlphaModel(AlphaModel):
    """Emits insights from where the overnight rate sits against the corridor floor."""

    def __init__(self, algorithm):
        self._rates = algorithm.add_data(ECBPolicyRates, ECB.PolicyRates.EURO_AREA, Resolution.DAILY).symbol

        history = algorithm.history[ECBPolicyRates](self._rates, timedelta(days=365), Resolution.DAILY)
        algorithm.debug(f"Got {len(list(history))} historical rate readings")

        self._spread = None
        self._symbols = []

    def update(self, algorithm, data):
        rates = data.get(ECBPolicyRates)
        if self._rates in rates:
            data_point = rates[self._rates]
            if data_point.euro_short_term_rate is not None and data_point.deposit_facility_rate is not None:
                # Measure how far the overnight rate trades below the deposit facility rate.
                self._spread = data_point.euro_short_term_rate - data_point.deposit_facility_rate

        if self._spread is None:
            return []

        # Emit up insights while the gap is wide and flat insights once it closes.
        direction = InsightDirection.UP if self._spread <= -0.05 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 ECBPolicyRatesFrameworkAlgorithm : QCAlgorithm
{
    public override void Initialize()
    {
        SetStartDate(2019, 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 ECBCorridorAlphaModel(this));

        SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
        SetExecution(new ImmediateExecutionModel());
    }
}

public class ECBCorridorAlphaModel : AlphaModel
{
    private readonly Symbol _rates;
    private readonly List<Symbol> _symbols = new();

    private decimal? _spread;

    public ECBCorridorAlphaModel(QCAlgorithm algorithm)
    {
        _rates = algorithm.AddData<ECBPolicyRates>(ECB.PolicyRates.EuroArea, Resolution.Daily).Symbol;

        var history = algorithm.History<ECBPolicyRates>(_rates, TimeSpan.FromDays(365), Resolution.Daily);
        algorithm.Debug($"Got {history.Count()} historical rate readings");
    }

    public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
    {
        var rates = data.Get<ECBPolicyRates>();
        if (rates.ContainsKey(_rates))
        {
            var dataPoint = rates[_rates];
            if (dataPoint.EuroShortTermRate.HasValue && dataPoint.DepositFacilityRate.HasValue)
            {
                // Measure how far the overnight rate trades below the deposit facility rate.
                _spread = dataPoint.EuroShortTermRate.Value - dataPoint.DepositFacilityRate.Value;
            }
        }

        if (!_spread.HasValue)
        {
            return Enumerable.Empty<Insight>();
        }

        // Emit up insights while the gap is wide and flat insights once it closes.
        var direction = _spread <= -0.05m ? 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));
    }

    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 Policy Rates dataset provides ECBPolicyRates 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: