Eurostat
Surveys
Introduction
The Business and Consumer Surveys dataset by Eurostat tracks the five monthly confidence surveys the European Commission runs across households, industry, construction, retail and services. The data covers 35 European economies, starts in January 1980, and is delivered on a monthly frequency. This dataset is created by processing Eurostat's public dissemination API.
The surveys are the earliest read of the European economy available. They measure expectations rather than outcomes, so they are released before the month they describe has even finished, ahead of every hard indicator. European data moves the European Central Bank, and through it euro area rates, the euro itself and European equity indices, so a signal that leads the measured statistics by a month leads the market reaction with it. The price expectation questions, carried in four of the five surveys, are the earliest inflation signal on the platform.
For more information about the Surveys 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. The confidence surveys themselves are collected by the European Commission's Directorate-General for Economic and Financial Affairs, which runs the joint harmonised programme the member states report into.
QuantConnect processes and caches these surveys so they reach your algorithm on their publication date rather than at the close of the month they measure, 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 Business and Consumer Surveys dataset:
self.consumer = self.add_data(EurostatConsumerSurvey, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol self.industry = self.add_data(EurostatIndustrySurvey, Eurostat.Economies.EURO_AREA, Resolution.DAILY).symbol
_consumer = AddData<EurostatConsumerSurvey>(Eurostat.Economies.Germany, Resolution.Daily).Symbol; _industry = AddData<EurostatIndustrySurvey>(Eurostat.Economies.EuroArea, Resolution.Daily).Symbol;
Requesting Data
To add Business and Consumer Surveys 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 EurostatSurveysDataAlgorithm(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.consumer = self.add_data(EurostatConsumerSurvey, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol
self.industry = self.add_data(EurostatIndustrySurvey, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol
self.services = self.add_data(EurostatServicesSurvey, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol public class EurostatSurveysDataAlgorithm : QCAlgorithm
{
private Symbol _equity, _consumer, _industry, _services;
public override void Initialize()
{
SetStartDate(2018, 1, 1);
SetEndDate(2021, 3, 31);
SetCash(100000);
_equity = AddEquity("SPY", Resolution.Daily).Symbol;
_consumer = AddData<EurostatConsumerSurvey>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
_industry = AddData<EurostatIndustrySurvey>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
_services = AddData<EurostatServicesSurvey>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
}
}
Subscribe to as many surveys as you need for the same economy. The surveys describe economies and are not tradeable, so add a separate tradeable security if you want to place orders.
Accessing Data
To get the current Business and Consumer Surveys 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 surveys are monthly, 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(EurostatConsumerSurvey)
if self.consumer in data:
data_point = data[self.consumer]
if data_point.consumer_confidence is not None:
self.log(f"{self.consumer} consumer confidence at {slice.time}: {data_point.consumer_confidence}") public override void OnData(Slice slice)
{
var data = slice.Get<EurostatConsumerSurvey>();
if (data.ContainsKey(_consumer))
{
var dataPoint = data[_consumer];
if (dataPoint.ConsumerConfidence.HasValue)
{
Log($"{_consumer} consumer confidence at {slice.Time}: {dataPoint.ConsumerConfidence}");
}
}
}
To iterate through all of the economies subscribed to a survey in the current Slice, call the Getget method.
def on_data(self, slice: Slice) -> None:
for dataset_symbol, data_point in slice.get(EurostatIndustrySurvey).items():
self.log(f"{dataset_symbol} industrial confidence at {slice.time}: {data_point.industrial_confidence}") public override void OnData(Slice slice)
{
foreach (var kvp in slice.Get<EurostatIndustrySurvey>())
{
var datasetSymbol = kvp.Key;
var dataPoint = kvp.Value;
Log($"{datasetSymbol} industrial confidence at {slice.Time}: {dataPoint.IndustrialConfidence}");
}
}
The Value property of each class is that survey's own confidence indicator.
Historical Data
To get historical Business and Consumer Surveys 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 surveys are monthly, 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 surveys published inside them. Five years is 1,825 days, which returns 60 points.
# DataFrames consumer_history_df = self.history(self.consumer, timedelta(days=1825), Resolution.DAILY) industry_history_df = self.history(self.industry, timedelta(days=1825), Resolution.DAILY) history_df = self.history([self.consumer, self.industry], timedelta(days=1825), Resolution.DAILY) # Dataset objects consumer_history_bars = self.history[EurostatConsumerSurvey](self.consumer, timedelta(days=1825), Resolution.DAILY) industry_history_bars = self.history[EurostatIndustrySurvey](self.industry, timedelta(days=1825), Resolution.DAILY)
// Dataset objects
var consumerHistory = History<EurostatConsumerSurvey>(_consumer, TimeSpan.FromDays(1825), Resolution.Daily);
var industryHistory = History<EurostatIndustrySurvey>(_industry, TimeSpan.FromDays(1825), Resolution.Daily);
// Slice objects
var history = History(new[] {_consumer, _industry}, 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 surveys cover the following 35 economies:
| Economy | Geo code | Constant |
|---|---|---|
Aggregates | ||
| Euro area | EA21 | Eurostat.Economies.EuroAreaEurostat.Economies.EURO_AREA |
| Euro area (20 countries) | EA20 | Eurostat.Economies.EuroArea20Eurostat.Economies.EURO_AREA_20 |
| European Union | EU27_2020 | Eurostat.Economies.EuropeanUnionEurostat.Economies.EUROPEAN_UNION |
Countries | ||
| Albania | AL | Eurostat.Economies.AlbaniaEurostat.Economies.ALBANIA |
| Austria | AT | Eurostat.Economies.AustriaEurostat.Economies.AUSTRIA |
| Belgium | BE | Eurostat.Economies.BelgiumEurostat.Economies.BELGIUM |
| 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 |
| Germany | DE | Eurostat.Economies.GermanyEurostat.Economies.GERMANY |
| Greece | EL | Eurostat.Economies.GreeceEurostat.Economies.GREECE |
| Hungary | HU | Eurostat.Economies.HungaryEurostat.Economies.HUNGARY |
| Ireland | IE | Eurostat.Economies.IrelandEurostat.Economies.IRELAND |
| Italy | IT | Eurostat.Economies.ItalyEurostat.Economies.ITALY |
| 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 |
| Montenegro | ME | Eurostat.Economies.MontenegroEurostat.Economies.MONTENEGRO |
| Netherlands | NL | Eurostat.Economies.NetherlandsEurostat.Economies.NETHERLANDS |
| North Macedonia | MK | Eurostat.Economies.NorthMacedoniaEurostat.Economies.NORTH_MACEDONIA |
| 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 |
| Turkiye | TR | Eurostat.Economies.TurkiyeEurostat.Economies.TURKIYE |
EA21 is the current euro area composition and EA20 the previous one, and the surveys stop publishing EA20 in December 2025. The five surveys share a release date, so one round delivers the whole economy at once.
Example Applications
The Business and Consumer Surveys dataset lets you read the European economy before it shows up in any measured statistic. Examples include the following strategies:
- Timing exposure to European equities or the euro on the direction of consumer confidence, the earliest reading of the five.
- Reading price expectations across industry, construction, retail and services as an inflation signal that leads both producer and consumer prices.
- Watching industrial order books, and export order books in particular, as a lead on industrial production and on external demand.
- Comparing confidence across economies, for example German industry against Spanish, to rotate country exposure.
- Combining the five surveys into a breadth measure of how much of the economy is improving at once, rather than reading any single sector.
Classic Algorithm Example
The following example algorithm reads German consumer confidence as a risk-on signal and holds SPY while households are turning less pessimistic, stepping aside when they are not. It also logs what manufacturers expect to charge, which is pipeline inflation months before a price index measures it.
from AlgorithmImports import *
class EurostatSurveysExampleAlgorithm(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.consumer = self.add_data(EurostatConsumerSurvey, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol
self.industry = self.add_data(EurostatIndustrySurvey, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol
self.previous_confidence = None
def on_data(self, slice):
industry = slice.get(EurostatIndustrySurvey)
if self.industry in industry:
data_point = industry[self.industry]
self.debug(f"Industrial confidence: {data_point.industrial_confidence}, selling price expectations: {data_point.selling_price_expectation}")
consumer = slice.get(EurostatConsumerSurvey)
if self.consumer not in consumer:
return
confidence = consumer[self.consumer].consumer_confidence
if confidence is None:
return
# Households turning less pessimistic is the risk-on read.
if self.previous_confidence is not None:
if confidence > self.previous_confidence:
self.set_holdings(self.equity, 1)
else:
self.liquidate(self.equity)
self.previous_confidence = confidence public class EurostatSurveysExampleAlgorithm : QCAlgorithm
{
private Symbol _equity, _consumer, _industry;
private decimal? _previousConfidence;
public override void Initialize()
{
SetStartDate(2018, 1, 1);
SetEndDate(2021, 3, 31);
SetCash(100000);
_equity = AddEquity("SPY", Resolution.Daily).Symbol;
_consumer = AddData<EurostatConsumerSurvey>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
_industry = AddData<EurostatIndustrySurvey>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
}
public override void OnData(Slice slice)
{
var industry = slice.Get<EurostatIndustrySurvey>();
if (industry.ContainsKey(_industry))
{
var dataPoint = industry[_industry];
Debug($"Industrial confidence: {dataPoint.IndustrialConfidence}, selling price expectations: {dataPoint.SellingPriceExpectation}");
}
var consumer = slice.Get<EurostatConsumerSurvey>();
if (!consumer.ContainsKey(_consumer))
{
return;
}
var confidence = consumer[_consumer].ConsumerConfidence;
if (!confidence.HasValue)
{
return;
}
// Households turning less pessimistic is the risk-on read.
if (_previousConfidence.HasValue)
{
if (confidence > _previousConfidence)
{
SetHoldings(_equity, 1);
}
else
{
Liquidate(_equity);
}
}
_previousConfidence = confidence;
}
}
Framework Algorithm Example
The following example algorithm trades a manually selected universe from the confidence surveys. An alpha model subscribes to the German consumer and industry surveys, then emits an up insight only when households and manufacturers are turning more confident in the same round, which is a breadth reading rather than a single sector one.
from AlgorithmImports import *
class EurostatSurveysFrameworkAlgorithm(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(EurostatConfidenceAlphaModel(self))
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
class EurostatConfidenceAlphaModel(AlphaModel):
"""Emits insights from the direction of European confidence, read across households and industry."""
def __init__(self, algorithm):
self._consumer = algorithm.add_data(EurostatConsumerSurvey, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol
self._industry = algorithm.add_data(EurostatIndustrySurvey, Eurostat.Economies.GERMANY, Resolution.DAILY).symbol
history = algorithm.history(EurostatConsumerSurvey, self._consumer, timedelta(days=1825), Resolution.DAILY)
algorithm.debug(f"Got {len(history)} historical consumer survey rows")
self._previous_consumer = None
self._previous_industry = None
self._symbols = []
def update(self, algorithm, data):
insights = []
consumer = data.get(EurostatConsumerSurvey)
if self._consumer not in consumer:
return insights
confidence = consumer[self._consumer].consumer_confidence
if confidence is None:
return insights
# The industry survey shares its release date with the consumer one, so the confirmation
# is read from the same round.
industry_point = algorithm.securities[self._industry].cache.get_data(EurostatIndustrySurvey)
industrial = industry_point.industrial_confidence if industry_point else None
if self._previous_consumer is not None:
improving = confidence > self._previous_consumer
if improving and industrial is not None and self._previous_industry is not None:
improving = industrial > self._previous_industry
direction = InsightDirection.UP if improving else InsightDirection.FLAT
insights = [Insight.price(symbol, timedelta(days=30), direction) for symbol in self._symbols]
self._previous_consumer = confidence
if industrial is not None:
self._previous_industry = industrial
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 EurostatSurveysFrameworkAlgorithm : 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 EurostatConfidenceAlphaModel(this));
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
SetExecution(new ImmediateExecutionModel());
}
}
public class EurostatConfidenceAlphaModel : AlphaModel
{
private readonly Symbol _consumer;
private readonly Symbol _industry;
private readonly List<Symbol> _symbols = new();
private decimal? _previousConsumer;
private decimal? _previousIndustry;
public EurostatConfidenceAlphaModel(QCAlgorithm algorithm)
{
_consumer = algorithm.AddData<EurostatConsumerSurvey>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
_industry = algorithm.AddData<EurostatIndustrySurvey>(Eurostat.Economies.Germany, Resolution.Daily).Symbol;
var history = algorithm.History<EurostatConsumerSurvey>(_consumer, TimeSpan.FromDays(1825), Resolution.Daily);
algorithm.Debug($"Got {history.Count()} historical consumer survey rows");
}
public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
{
var insights = new List<Insight>();
var consumer = data.Get<EurostatConsumerSurvey>();
if (!consumer.ContainsKey(_consumer))
{
return insights;
}
var confidence = consumer[_consumer].ConsumerConfidence;
if (!confidence.HasValue)
{
return insights;
}
// The industry survey shares its release date with the consumer one, so the confirmation
// is read from the same round.
var industryPoint = algorithm.Securities[_industry].Cache.GetData<EurostatIndustrySurvey>();
var industrial = industryPoint?.IndustrialConfidence;
if (_previousConsumer.HasValue)
{
var improving = confidence > _previousConsumer;
if (improving && industrial.HasValue && _previousIndustry.HasValue)
{
improving = industrial > _previousIndustry;
}
var direction = improving ? InsightDirection.Up : InsightDirection.Flat;
insights = _symbols.Select(symbol => Insight.Price(symbol, TimeSpan.FromDays(30), direction)).ToList();
}
_previousConsumer = confidence;
if (industrial.HasValue)
{
_previousIndustry = industrial;
}
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 Business and Consumer Surveys dataset provides EurostatConsumerSurvey, EurostatIndustrySurvey, EurostatConstructionSurvey, EurostatRetailSurvey and EurostatServicesSurvey objects. Every property is nullable: Eurostat publishes a given question for some economies and not others, so a column can be empty for an entire economy.
EurostatConsumerSurvey
EurostatConsumerSurvey objects have the following attributes:
EurostatIndustrySurvey
EurostatIndustrySurvey objects have the following attributes:
EurostatConstructionSurvey
EurostatConstructionSurvey objects have the following attributes:
EurostatRetailSurvey
EurostatRetailSurvey objects have the following attributes:
EurostatServicesSurvey
EurostatServicesSurvey objects have the following attributes: