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;
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.
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:
| Economy | Geo code | Constant |
|---|---|---|
Aggregates | ||
| Euro area (21 countries) | EA21 | Eurostat.Economies.EuroAreaEurostat.Economies.EURO_AREA |
| Euro area (20 countries) | EA20 | Eurostat.Economies.EuroArea20Eurostat.Economies.EURO_AREA_20 |
| Euro area (19 countries) | EA19 | Eurostat.Economies.EuroArea19Eurostat.Economies.EURO_AREA_19 |
| Euro area (12 countries) | EA12 | Eurostat.Economies.EuroArea12Eurostat.Economies.EURO_AREA_12 |
| Euro area, chain linked | EA | Eurostat.Economies.EuroAreaChainedEurostat.Economies.EURO_AREA_CHAINED |
| European Union | EU27_2020 | Eurostat.Economies.EuropeanUnionEurostat.Economies.EUROPEAN_UNION |
| European Union, chain linked | EU | Eurostat.Economies.EuropeanUnionChainedEurostat.Economies.EUROPEAN_UNION_CHAINED |
| European Economic Area | EEA | Eurostat.Economies.EuropeanEconomicAreaEurostat.Economies.EUROPEAN_ECONOMIC_AREA |
Countries | ||
| Albania | AL | Eurostat.Economies.AlbaniaEurostat.Economies.ALBANIA |
| Austria | AT | Eurostat.Economies.AustriaEurostat.Economies.AUSTRIA |
| Belgium | BE | Eurostat.Economies.BelgiumEurostat.Economies.BELGIUM |
| Bosnia and Herzegovina | BA | Eurostat.Economies.BosniaAndHerzegovinaEurostat.Economies.BOSNIA_AND_HERZEGOVINA |
| Bulgaria | BG | Eurostat.Economies.BulgariaEurostat.Economies.BULGARIA |
| Croatia | HR | Eurostat.Economies.CroatiaEurostat.Economies.CROATIA |
| Cyprus | CY | Eurostat.Economies.CyprusEurostat.Economies.CYPRUS |
| Czechia | CZ | Eurostat.Economies.CzechiaEurostat.Economies.CZECHIA |
| Denmark | DK | Eurostat.Economies.DenmarkEurostat.Economies.DENMARK |
| Estonia | EE | Eurostat.Economies.EstoniaEurostat.Economies.ESTONIA |
| Finland | FI | Eurostat.Economies.FinlandEurostat.Economies.FINLAND |
| France | FR | Eurostat.Economies.FranceEurostat.Economies.FRANCE |
| Georgia | GE | Eurostat.Economies.GeorgiaEurostat.Economies.GEORGIA |
| Germany | DE | Eurostat.Economies.GermanyEurostat.Economies.GERMANY |
| Greece | EL | Eurostat.Economies.GreeceEurostat.Economies.GREECE |
| Hungary | HU | Eurostat.Economies.HungaryEurostat.Economies.HUNGARY |
| Iceland | IS | Eurostat.Economies.IcelandEurostat.Economies.ICELAND |
| Ireland | IE | Eurostat.Economies.IrelandEurostat.Economies.IRELAND |
| Italy | IT | Eurostat.Economies.ItalyEurostat.Economies.ITALY |
| Japan | JP | Eurostat.Economies.JapanEurostat.Economies.JAPAN |
| Kosovo | XK | Eurostat.Economies.KosovoEurostat.Economies.KOSOVO |
| Latvia | LV | Eurostat.Economies.LatviaEurostat.Economies.LATVIA |
| Lithuania | LT | Eurostat.Economies.LithuaniaEurostat.Economies.LITHUANIA |
| Luxembourg | LU | Eurostat.Economies.LuxembourgEurostat.Economies.LUXEMBOURG |
| Malta | MT | Eurostat.Economies.MaltaEurostat.Economies.MALTA |
| Moldova | MD | Eurostat.Economies.MoldovaEurostat.Economies.MOLDOVA |
| Montenegro | ME | Eurostat.Economies.MontenegroEurostat.Economies.MONTENEGRO |
| Netherlands | NL | Eurostat.Economies.NetherlandsEurostat.Economies.NETHERLANDS |
| North Macedonia | MK | Eurostat.Economies.NorthMacedoniaEurostat.Economies.NORTH_MACEDONIA |
| Norway | NO | Eurostat.Economies.NorwayEurostat.Economies.NORWAY |
| Poland | PL | Eurostat.Economies.PolandEurostat.Economies.POLAND |
| Portugal | PT | Eurostat.Economies.PortugalEurostat.Economies.PORTUGAL |
| Romania | RO | Eurostat.Economies.RomaniaEurostat.Economies.ROMANIA |
| Serbia | RS | Eurostat.Economies.SerbiaEurostat.Economies.SERBIA |
| Slovakia | SK | Eurostat.Economies.SlovakiaEurostat.Economies.SLOVAKIA |
| Slovenia | SI | Eurostat.Economies.SloveniaEurostat.Economies.SLOVENIA |
| Spain | ES | Eurostat.Economies.SpainEurostat.Economies.SPAIN |
| Sweden | SE | Eurostat.Economies.SwedenEurostat.Economies.SWEDEN |
| Switzerland | CH | Eurostat.Economies.SwitzerlandEurostat.Economies.SWITZERLAND |
| Turkiye | TR | Eurostat.Economies.TurkiyeEurostat.Economies.TURKIYE |
| Ukraine | UA | Eurostat.Economies.UkraineEurostat.Economies.UKRAINE |
| United Kingdom | UK | Eurostat.Economies.UnitedKingdomEurostat.Economies.UNITED_KINGDOM |
| United States | US | Eurostat.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: