Eurostat

Macro Indicators

Introduction

The Macroeconomic Indicators dataset by Eurostat tracks the headline statistics the European Union's statistical office publishes for the euro area and the individual European economies: harmonised consumer prices, industrial production, producer prices, retail trade, unemployment and gross domestic product. The data covers 51 economies, starts in January 1975, and is delivered on a monthly and quarterly frequency. This dataset is created by processing Eurostat's public dissemination API.

European macro data moves the European Central Bank, and through it euro area rates, the euro itself and European equity indices. The HICP is the release the ECB targets, and its euro area flash lands on the last working day of the month it measures, as it has since Eurostat launched the flash in 2001. Each indicator carries its breakdown alongside the headline, so an algorithm can separate energy from core inflation, or capital goods from consumer goods in production, rather than reacting to a single number.

For more information about the Macro Indicators dataset, including CLI commands and pricing, see the dataset listing.

About the Provider

Eurostat is the statistical office of the European Union, based in Luxembourg and operating since 1953. Its role is to produce statistics that are comparable across member states, which is what makes a single European reading possible rather than 27 national ones computed differently. Eurostat publishes through a free dissemination API that requires no key and no registration.

QuantConnect processes and caches these indicators so they reach your algorithm on each indicator's publication date rather than at the close of the period it measures, which is what keeps a backtest from reading a figure before it existed.

Getting Started

The following snippet demonstrates how to request data from the Macroeconomic Indicators dataset:

self.prices = self.add_data(EurostatConsumerPrices, Eurostat.Economies.EURO_AREA, Resolution.DAILY).symbol
self.production = self.add_data(EurostatIndustrialProduction, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol
_prices = AddData<EurostatConsumerPrices>(Eurostat.Economies.EuroArea, Resolution.Daily).Symbol;
_production = AddData<EurostatIndustrialProduction>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;

Data Summary

The following table describes the dataset properties:

PropertyValue
Start DateJanuary 1975
Data Points75,327
Asset Coverage51 economies
Data DensitySparse
ResolutionDaily*
TimezoneBrussels

* Monthly and quarterly data we fetch daily.

Requesting Data

To add Macroeconomic Indicators data to your algorithm, call the AddDataadd_data method. The dataset is unlinked, so instead of a security Symbol you pass the geo code of the economy you want. Save a reference to the dataset Symbol so you can access the data later in your algorithm.

class EurostatIndicatorsDataAlgorithm(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.prices = self.add_data(EurostatConsumerPrices, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol
        self.production = self.add_data(EurostatIndustrialProduction, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol
        self.gdp = self.add_data(EurostatGrossDomesticProduct, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol
public class EurostatIndicatorsDataAlgorithm : QCAlgorithm
{
    private Symbol _equity, _prices, _production, _gdp;

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

        _equity = AddEquity("SPY", Resolution.Daily).Symbol;

        _prices = AddData<EurostatConsumerPrices>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
        _production = AddData<EurostatIndustrialProduction>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
        _gdp = AddData<EurostatGrossDomesticProduct>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
    }
}

Each indicator arrives on its own release schedule, so an algorithm subscribed to several reacts to whichever one moves rather than waiting for a combined print.

Accessing Data

To get the current Macroeconomic Indicators 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. The indicators are monthly and quarterly, so most days carry no new data. To avoid issues, check if the Slice contains the data you want before you index it, and check the property you want against nullNone before you act on it.

def on_data(self, slice: Slice) -> None:
    data = slice.get(EurostatConsumerPrices)
    if self.prices in data:
        data_point = data[self.prices]
        if data_point.all_items is not None:
            self.log(f"{self.prices} HICP at {slice.time}: {data_point.all_items}, core {data_point.core}")
public override void OnData(Slice slice)
{
    var data = slice.Get<EurostatConsumerPrices>();
    if (data.ContainsKey(_prices))
    {
        var dataPoint = data[_prices];
        if (dataPoint.AllItems.HasValue)
        {
            Log($"{_prices} HICP at {slice.Time}: {dataPoint.AllItems}, core {dataPoint.Core}");
        }
    }
}

To iterate through all of the economies subscribed to an indicator in the current Slice, call the Getget method.

def on_data(self, slice: Slice) -> None:
    for dataset_symbol, data_point in slice.get(EurostatIndustrialProduction).items():
        self.log(f"{dataset_symbol} industrial production at {slice.time}: {data_point.total_industry}")
public override void OnData(Slice slice)
{
    foreach (var kvp in slice.Get<EurostatIndustrialProduction>())
    {
        var datasetSymbol = kvp.Key;
        var dataPoint = kvp.Value;
        Log($"{datasetSymbol} industrial production at {slice.Time}: {dataPoint.TotalIndustry}");
    }
}

The Value property of each class is that indicator's headline reading, falling back to zero when Eurostat has not published it. Read the typed property if you need to tell a missing reading from a real one, since zero is not a possible index level.

Historical Data

To get historical Macroeconomic Indicators data, call the Historyhistory method with the dataset Symbol. If there is no data in the period you request, the history result is empty. The indicators are monthly and quarterly, so ask for a period rather than a bar count: a count is read as daily bars, and 60 of those return only the two releases published inside them. Five years is 1,825 days.

# DataFrames
prices_history_df = self.history(self.prices, timedelta(days=1825), Resolution.DAILY)
production_history_df = self.history(self.production, timedelta(days=1825), Resolution.DAILY)
history_df = self.history([self.prices, self.production], timedelta(days=1825), Resolution.DAILY)

# Dataset objects
prices_history_bars = self.history[EurostatConsumerPrices](self.prices, timedelta(days=1825), Resolution.DAILY)
production_history_bars = self.history[EurostatIndustrialProduction](self.production, timedelta(days=1825), Resolution.DAILY)
// Dataset objects
var pricesHistory = History<EurostatConsumerPrices>(_prices, TimeSpan.FromDays(1825), Resolution.Daily);
var productionHistory = History<EurostatIndustrialProduction>(_production, TimeSpan.FromDays(1825), Resolution.Daily);

// Slice objects
var history = History(new[] {_prices, _production}, TimeSpan.FromDays(1825), Resolution.Daily);

For more information about historical data, see History Requests.

Remove Subscriptions

To remove your subscription to a Macroeconomic Indicators class, call the RemoveSecurityremove_security method.

self.remove_security(self.prices)
RemoveSecurity(_prices);

Supported Economies

The ticker of a subscription is the Eurostat geo code of the economy. The Eurostat.Economies helper gives each one a readable name that resolves to its code. The indicators cover the following 51 economies:

EconomyGeo codeConstant
Aggregates
Euro area (21 countries)EA21Eurostat.Economies.EuroAreaEurostat.Economies.EURO_AREA
Euro area (20 countries)EA20Eurostat.Economies.EuroArea20Eurostat.Economies.EURO_AREA_20
Euro area (19 countries)EA19Eurostat.Economies.EuroArea19Eurostat.Economies.EURO_AREA_19
Euro area (12 countries)EA12Eurostat.Economies.EuroArea12Eurostat.Economies.EURO_AREA_12
Euro area, chain linkedEAEurostat.Economies.EuroAreaChainedEurostat.Economies.EURO_AREA_CHAINED
European UnionEU27_2020Eurostat.Economies.EuropeanUnionEurostat.Economies.EUROPEAN_UNION
European Union, chain linkedEUEurostat.Economies.EuropeanUnionChainedEurostat.Economies.EUROPEAN_UNION_CHAINED
European Economic AreaEEAEurostat.Economies.EuropeanEconomicAreaEurostat.Economies.EUROPEAN_ECONOMIC_AREA
Countries
AlbaniaALEurostat.Economies.AlbaniaEurostat.Economies.ALBANIA
AustriaATEurostat.Economies.AustriaEurostat.Economies.AUSTRIA
BelgiumBEEurostat.Economies.BelgiumEurostat.Economies.BELGIUM
Bosnia and HerzegovinaBAEurostat.Economies.BosniaAndHerzegovinaEurostat.Economies.BOSNIA_AND_HERZEGOVINA
BulgariaBGEurostat.Economies.BulgariaEurostat.Economies.BULGARIA
CroatiaHREurostat.Economies.CroatiaEurostat.Economies.CROATIA
CyprusCYEurostat.Economies.CyprusEurostat.Economies.CYPRUS
CzechiaCZEurostat.Economies.CzechiaEurostat.Economies.CZECHIA
DenmarkDKEurostat.Economies.DenmarkEurostat.Economies.DENMARK
EstoniaEEEurostat.Economies.EstoniaEurostat.Economies.ESTONIA
FinlandFIEurostat.Economies.FinlandEurostat.Economies.FINLAND
FranceFREurostat.Economies.FranceEurostat.Economies.FRANCE
GeorgiaGEEurostat.Economies.GeorgiaEurostat.Economies.GEORGIA
GermanyDEEurostat.Economies.GermanyEurostat.Economies.GERMANY
GreeceELEurostat.Economies.GreeceEurostat.Economies.GREECE
HungaryHUEurostat.Economies.HungaryEurostat.Economies.HUNGARY
IcelandISEurostat.Economies.IcelandEurostat.Economies.ICELAND
IrelandIEEurostat.Economies.IrelandEurostat.Economies.IRELAND
ItalyITEurostat.Economies.ItalyEurostat.Economies.ITALY
JapanJPEurostat.Economies.JapanEurostat.Economies.JAPAN
KosovoXKEurostat.Economies.KosovoEurostat.Economies.KOSOVO
LatviaLVEurostat.Economies.LatviaEurostat.Economies.LATVIA
LithuaniaLTEurostat.Economies.LithuaniaEurostat.Economies.LITHUANIA
LuxembourgLUEurostat.Economies.LuxembourgEurostat.Economies.LUXEMBOURG
MaltaMTEurostat.Economies.MaltaEurostat.Economies.MALTA
MoldovaMDEurostat.Economies.MoldovaEurostat.Economies.MOLDOVA
MontenegroMEEurostat.Economies.MontenegroEurostat.Economies.MONTENEGRO
NetherlandsNLEurostat.Economies.NetherlandsEurostat.Economies.NETHERLANDS
North MacedoniaMKEurostat.Economies.NorthMacedoniaEurostat.Economies.NORTH_MACEDONIA
NorwayNOEurostat.Economies.NorwayEurostat.Economies.NORWAY
PolandPLEurostat.Economies.PolandEurostat.Economies.POLAND
PortugalPTEurostat.Economies.PortugalEurostat.Economies.PORTUGAL
RomaniaROEurostat.Economies.RomaniaEurostat.Economies.ROMANIA
SerbiaRSEurostat.Economies.SerbiaEurostat.Economies.SERBIA
SlovakiaSKEurostat.Economies.SlovakiaEurostat.Economies.SLOVAKIA
SloveniaSIEurostat.Economies.SloveniaEurostat.Economies.SLOVENIA
SpainESEurostat.Economies.SpainEurostat.Economies.SPAIN
SwedenSEEurostat.Economies.SwedenEurostat.Economies.SWEDEN
SwitzerlandCHEurostat.Economies.SwitzerlandEurostat.Economies.SWITZERLAND
TurkiyeTREurostat.Economies.TurkiyeEurostat.Economies.TURKIYE
UkraineUAEurostat.Economies.UkraineEurostat.Economies.UKRAINE
United KingdomUKEurostat.Economies.UnitedKingdomEurostat.Economies.UNITED_KINGDOM
United StatesUSEurostat.Economies.UnitedStatesEurostat.Economies.UNITED_STATES

Japan and the United States are carried because Eurostat publishes them alongside the European economies for comparison. Coverage varies by indicator: consumer prices reach 46 economies and retail trade 35, and industrial production, retail trade and the labour market are published per country with no euro area aggregate.

Example Applications

The Macroeconomic Indicators dataset lets you read the European economy as it is measured. Examples include the following strategies:

  • Anticipating European Central Bank decisions from the HICP release, whose euro area flash lands the same day the period closes.
  • Separating energy from core inflation to tell an external price shock apart from broad inflation.
  • Comparing the industrial cycle across economies, for example German production against Spanish, to rotate country exposure.
  • Combining producer prices with consumer prices to watch cost pressure move down the chain into inflation.
  • Using the unemployment rate and the GDP components as a regime filter that scales risk up or down.

Classic Algorithm Example

The following example algorithm reads German industrial production as a growth signal and holds SPY while output is rising, stepping aside when it contracts. It also logs the HICP, so the position can be read against what inflation was doing at the time.

from AlgorithmImports import *

class EurostatIndicatorsExampleAlgorithm(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.prices = self.add_data(EurostatConsumerPrices, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol
        self.production = self.add_data(EurostatIndustrialProduction, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol

        self.previous_production = None

    def on_data(self, slice):
        prices = slice.get(EurostatConsumerPrices)
        if self.prices in prices:
            data_point = prices[self.prices]
            self.debug(f"HICP all items: {data_point.all_items}, core: {data_point.core}, energy: {data_point.energy}")

        production = slice.get(EurostatIndustrialProduction)
        if self.production not in production:
            return

        total_industry = production[self.production].total_industry
        if total_industry is None:
            return

        # Rising output is the risk-on read; a contracting month steps aside.
        if self.previous_production is not None:
            if total_industry > self.previous_production:
                self.set_holdings(self.equity, 1)
            else:
                self.liquidate(self.equity)

        self.previous_production = total_industry
public class EurostatIndicatorsExampleAlgorithm : QCAlgorithm
{
    private Symbol _equity, _prices, _production;
    private decimal? _previousProduction;

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

        _equity = AddEquity("SPY", Resolution.Daily).Symbol;

        _prices = AddData<EurostatConsumerPrices>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
        _production = AddData<EurostatIndustrialProduction>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
    }

    public override void OnData(Slice slice)
    {
        var prices = slice.Get<EurostatConsumerPrices>();
        if (prices.ContainsKey(_prices))
        {
            var dataPoint = prices[_prices];
            Debug($"HICP all items: {dataPoint.AllItems}, core: {dataPoint.Core}, energy: {dataPoint.Energy}");
        }

        var production = slice.Get<EurostatIndustrialProduction>();
        if (!production.ContainsKey(_production))
        {
            return;
        }

        var totalIndustry = production[_production].TotalIndustry;
        if (!totalIndustry.HasValue)
        {
            return;
        }

        // Rising output is the risk-on read; a contracting month steps aside.
        if (_previousProduction.HasValue)
        {
            if (totalIndustry > _previousProduction)
            {
                SetHoldings(_equity, 1);
            }
            else
            {
                Liquidate(_equity);
            }
        }

        _previousProduction = totalIndustry;
    }
}

Framework Algorithm Example

The following example algorithm trades a manually selected universe from the macro cycle. An alpha model subscribes to German industrial production and the unemployment rate, then emits an up insight only when output is rising and unemployment is not, which is a two-sided reading of the same expansion.

from AlgorithmImports import *

class EurostatIndicatorsFrameworkAlgorithm(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(EurostatMacroCycleAlphaModel(self))

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


class EurostatMacroCycleAlphaModel(AlphaModel):
    """Emits insights from the European macro cycle, read across output and unemployment."""

    def __init__(self, algorithm):
        self._production = algorithm.add_data(EurostatIndustrialProduction, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol
        self._labour = algorithm.add_data(EurostatLabourMarket, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol

        history = algorithm.history(EurostatIndustrialProduction, self._production, timedelta(days=1825), Resolution.DAILY)
        algorithm.debug(f"Got {len(history)} historical industrial production rows")

        self._previous_production = None
        self._previous_unemployment = None
        self._symbols = []

    def update(self, algorithm, data):
        insights = []

        production = data.get(EurostatIndustrialProduction)
        if self._production not in production:
            return insights

        total_industry = production[self._production].total_industry
        if total_industry is None:
            return insights

        # The labour market publishes on its own schedule, so the last reading is read from the
        # cache rather than from this slice.
        labour_point = algorithm.securities[self._labour].cache.get_data(EurostatLabourMarket)
        unemployment = labour_point.unemployment_rate if labour_point else None

        if self._previous_production is not None:
            expanding = total_industry > self._previous_production
            if expanding and unemployment is not None and self._previous_unemployment is not None:
                expanding = unemployment <= self._previous_unemployment

            direction = InsightDirection.UP if expanding else InsightDirection.FLAT
            insights = [Insight.price(symbol, timedelta(days=30), direction) for symbol in self._symbols]

        self._previous_production = total_industry
        if unemployment is not None:
            self._previous_unemployment = unemployment

        return insights

    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 EurostatIndicatorsFrameworkAlgorithm : 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 EurostatMacroCycleAlphaModel(this));

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

public class EurostatMacroCycleAlphaModel : AlphaModel
{
    private readonly Symbol _production;
    private readonly Symbol _labour;
    private readonly List<Symbol> _symbols = new();

    private decimal? _previousProduction;
    private decimal? _previousUnemployment;

    public EurostatMacroCycleAlphaModel(QCAlgorithm algorithm)
    {
        _production = algorithm.AddData<EurostatIndustrialProduction>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
        _labour = algorithm.AddData<EurostatLabourMarket>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;

        var history = algorithm.History<EurostatIndustrialProduction>(_production, TimeSpan.FromDays(1825), Resolution.Daily);
        algorithm.Debug($"Got {history.Count()} historical industrial production rows");
    }

    public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
    {
        var insights = new List<Insight>();

        var production = data.Get<EurostatIndustrialProduction>();
        if (!production.ContainsKey(_production))
        {
            return insights;
        }

        var totalIndustry = production[_production].TotalIndustry;
        if (!totalIndustry.HasValue)
        {
            return insights;
        }

        // The labour market publishes on its own schedule, so the last reading is read from the
        // cache rather than from this slice.
        var labourPoint = algorithm.Securities[_labour].Cache.GetData<EurostatLabourMarket>();
        var unemployment = labourPoint?.UnemploymentRate;

        if (_previousProduction.HasValue)
        {
            var expanding = totalIndustry > _previousProduction;
            if (expanding && unemployment.HasValue && _previousUnemployment.HasValue)
            {
                expanding = unemployment <= _previousUnemployment;
            }

            var direction = expanding ? InsightDirection.Up : InsightDirection.Flat;
            insights = _symbols.Select(symbol => Insight.Price(symbol, TimeSpan.FromDays(30), direction)).ToList();
        }

        _previousProduction = totalIndustry;
        if (unemployment.HasValue)
        {
            _previousUnemployment = unemployment;
        }

        return insights;
    }

    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 Macroeconomic Indicators dataset provides EurostatConsumerPrices, EurostatIndustrialProduction, EurostatProducerPrices, EurostatRetailTrade, EurostatLabourMarket and EurostatGrossDomesticProduct objects. Every property is nullable: Eurostat publishes a given breakdown for some economies and not others, so a column can be empty for an entire economy.

EurostatConsumerPrices

EurostatConsumerPrices objects have the following attributes:

EurostatIndustrialProduction

EurostatIndustrialProduction objects have the following attributes:

EurostatProducerPrices

EurostatProducerPrices objects have the following attributes:

EurostatRetailTrade

EurostatRetailTrade objects have the following attributes:

EurostatLabourMarket

EurostatLabourMarket objects have the following attributes:

EurostatGrossDomesticProduct

EurostatGrossDomesticProduct objects 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: