Bureau of Labor Statistics
US Bureau of Labor Statistics (BLS)
Introduction
The US Bureau of Labor Statistics (BLS) tracks key U.S. macroeconomic indicators across four major surveys: Consumer Price Index (CPI), Current Employment Statistics (CES), Producer Price Index (PPI), and Job Openings and Labor Turnover Survey (JOLTS). The data covers 50 non-seasonally-adjusted series, starting in January 2000, and is delivered on a daily frequency (monthly releases with exact publication timestamps). This dataset is created by analyzing BLS release schedules for accurate publication dates and fetching series data from the Public Data API.
For more information about the US Bureau of Labor Statistics (BLS) dataset, including CLI commands and pricing, see the dataset listing.
About the Provider
The BLS was established in 1884 as a division of the Department of the Interior, with the goal of collecting and disseminating essential economic data about the U.S. labor market and price changes. BLS provides access to employment, inflation, compensation, and productivity data for researchers, policymakers, and financial professionals.
Getting Started
The following snippet demonstrates how to request data from the BLS dataset:
self.add_data(BLSEconomicSurveysCpi, "CPI") self.add_data(BLSEconomicSurveysCes, "CES") self.add_data(BLSEconomicSurveysPpi, "PPI") self.add_data(BLSEconomicSurveysJolts, "JOLTS")
AddData<BLSEconomicSurveysCpi>("CPI");
AddData<BLSEconomicSurveysCes>("CES");
AddData<BLSEconomicSurveysPpi>("PPI");
AddData<BLSEconomicSurveysJolts>("JOLTS");
Requesting Data
To add BLS data to your algorithm, call the AddData method. Save a reference to the dataset Symbol so you can access the data later in your algorithm.
class BLSDataAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2020, 1, 1)
self.set_end_date(2024, 1, 1)
self.set_cash(100000)
self._cpi = self.add_data(BLSEconomicSurveysCpi, "CPI").symbol
self._ces = self.add_data(BLSEconomicSurveysCes, "CES").symbol
self._ppi = self.add_data(BLSEconomicSurveysPpi, "PPI").symbol
self._jolts = self.add_data(BLSEconomicSurveysJolts, "JOLTS").symbol public class BLSDataAlgorithm : QCAlgorithm
{
private Symbol _cpi, _ces, _ppi, _jolts;
public override void Initialize()
{
SetStartDate(2020, 1, 1);
SetEndDate(2024, 1, 1);
SetCash(100000);
_cpi = AddData<BLSEconomicSurveysCpi>("CPI").Symbol;
_ces = AddData<BLSEconomicSurveysCes>("CES").Symbol;
_ppi = AddData<BLSEconomicSurveysPpi>("PPI").Symbol;
_jolts = AddData<BLSEconomicSurveysJolts>("JOLTS").Symbol;
}
}
Accessing Data
To get the current BLS 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, data: Slice) -> None:
if self._cpi in data:
cpi = data[self._cpi]
all_items = cpi.all_items
core = cpi.core_cpi
energy = cpi.energy public override void OnData(Slice data)
{
if (data.ContainsKey(_cpi))
{
var cpi = data.Get<BLSEconomicSurveysCpi>(_cpi);
var allItems = cpi.AllItems;
var core = cpi.CoreCpi;
var energy = cpi.Energy;
}
}
Historical Data
To get historical BLS data, call the History 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._cpi, 100, Resolution.DAILY) # Dataset objects history_bars = self.history[BLSEconomicSurveysCpi](self._cpi, 100, Resolution.DAILY)
var history = History<BLSEconomicSurveysCpi>(_cpi, 100, Resolution.Daily);
For more information about historical data, see History Requests.
Supported Surveys
The BLS database contains over 77 million series from over 60 different surveys, but not all of them are integrated into QuantConnect. The following table show the integrated surveys:
| Ticker | Survey | Release Time (ET) | Start Date |
|---|---|---|---|
| CPI | Consumer Price Index | 08:30 AM | Jan 2000 |
| CES | Current Employment Statistics | 08:30 AM | Jan 2000 |
| PPI | Producer Price Index | 08:30 AM | Jan 2000 |
| JOLTS | Job Openings and Labor Turnover | 10:00 AM | Nov 2007 |
Example Applications
The BLS dataset provides unique alternative data that the US government releases. Example applications of this dataset include the following strategies:
- Monitoring month-over-month changes in nonfarm payrolls and sector employment to identify economic regime shifts.
- Tracking the inflation rate of gasoline to forecast the performance of electric vehicle (EV) manufacturers on the premise that rising gas prices will increase the revenue and stock prices of EV companies.
- Combining CPI, PPI, and CES data to build composite economic health indicators.
Classic Algorithm Example
The following example algorithm rotates to an energy ETF when energy prices are rising faster than core CPI. Otherwise, it holds the market benchmark.
from AlgorithmImports import *
class BLSEconomicSurveysAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2026, 3, 1)
self.set_cash(100000)
# Add some Equities to trade.
self._market = self.add_equity('SPY')
self._energy = self.add_equity('XLE')
# Add the BLS CPI survey data.
self._cpi = self.add_data(BLSEconomicSurveysCpi, "CPI").symbol
# Add a warm-up period so we have the most recent CPI survey on deployment.
self._target = None
self.set_warm_up(timedelta(60))
def on_data(self, data: Slice):
# Check for CPI data.
if data.contains_key(self._cpi):
cpi = data.get(BLSEconomicSurveysCpi, self._cpi)
# Simple signal: if energy CPI is rising faster than core, rotate to the energy ETF.
if cpi.energy is not None and cpi.core_cpi is not None and cpi.energy > cpi.core_cpi * 1.5:
self._target = self._energy
else:
self._target = self._market
# Rebalance whenever the target changes.
if not self.is_warming_up and self._target and not self._target.invested:
self.set_holdings(self._target, 1, True)
public class BLSEconomicSurveysAlgorithm: QCAlgorithm
{
private Equity _market, _energy, _target;
private Symbol _cpi;
public override void Initialize()
{
SetStartDate(2023, 1, 1);
SetEndDate(2026, 3, 1);
SetCash(100000);
// Add some Equities to trade.
_market = AddEquity("SPY");
_energy = AddEquity("XLE");
// Add the BLS CPI survey data.
_cpi = AddData<BLSEconomicSurveysCpi>("CPI").Symbol;
// Add a warm-up period so we have the most recent CPI survey on deployment.
SetWarmUp(TimeSpan.FromDays(60));
}
public override void OnData(Slice data)
{
// Check for CPI data.
if (data.ContainsKey(_cpi))
{
var cpi = data.Get<BLSEconomicSurveysCpi>(_cpi);
// Simple signal: if energy CPI is rising faster than core, rotate to the energy ETF.
if (cpi.Energy.HasValue && cpi.CoreCpi.HasValue &&
cpi.Energy.Value > cpi.CoreCpi.Value * 1.5m)
{
_target = _energy;
}
else
{
_target = _market;
}
}
// Rebalance whenever the target changes.
if (!IsWarmingUp && !_target.Invested)
{
SetHoldings(_target, 1.0m, true);
}
}
}
Framework Algorithm Example
The following example algorithm rotates to energy stocks in the OEF ETF when energy prices are rising faster than core CPI. Otherwise, it holds an equal-weighted portfolio of the ETF's constituents.
from AlgorithmImports import *
class BLSEconomicSurveysAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2026, 3, 1)
self.set_cash(100000)
# Add the framework models.
self.add_universe_selection(ETFConstituentsUniverseSelectionModel('OEF'))
self.add_alpha(CpiAlphaModel(self))
self.set_portfolio_construction(InsightWeightingPortfolioConstructionModel())
class MyAlphaModel(AlphaModel):
def __init__(self, algorithm):
# Set up a member to track the universe.
self._universe = []
# Add the BLS CPI survey data.
self._cpi = algorithm.add_data(BLSEconomicSurveysCpi, "CPI").symbol
def update(self, algorithm: QCAlgorithm, data: Slice) -> List[Insight]:
# Get the latest CPI survey.
if data.contains_key(self._cpi):
cpi = data.get(BLSEconomicSurveysCpi, self._cpi)
# Simple signal: if energy CPI is rising faster than core, rotate to the energy stocks.
target_energy = cpi.energy is not None and cpi.core_cpi is not None and cpi.energy > cpi.core_cpi * 1.5:
securities = []
for security in self._universe:
if target_energy:
if security.fundamentals.asset_classification.morningstar_sector_code == MorningstarSectorCode.ENERGY:
securities.append(security)
else:
securities.append(security)
# Emit monthly insights for the selected stocks.
return [Insight.price(s, timedelta(35), InsightDirection.UP, weight=1/len(securities)) for s in securities]
return []
# As assets enter and leave the universe, update our self._universe member.
def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
for security in changes.added_securities:
self._universe.append(security)
for security in changes.removed_securities:
self._universe.remove(security)
public class BLSEconomicSurveysAlgorithm : QCAlgorithm
{
public override void Initialize()
{
SetStartDate(2023, 1, 1);
SetEndDate(2026, 3, 1);
SetCash(100000);
// Add the framework models.
AddUniverseSelection(new ETFConstituentsUniverseSelectionModel("OEF"));
AddAlpha(new CpiAlphaModel(this));
SetPortfolioConstruction(new InsightWeightingPortfolioConstructionModel());
}
}
public class CpiAlphaModel : AlphaModel
{
private readonly Symbol _cpi;
private readonly List<Security> _universe = new List<Security>();
public CpiAlphaModel(QCAlgorithm algorithm)
{
// Add the BLS CPI survey data.
_cpiSymbol = algorithm.AddData<BLSEconomicSurveysCpi>("CPI").Symbol;
}
public override IEnumerable<Insight> Update(QCAlgorithm algorithm, Slice data)
{
// Get the latest CPI survey.
if (data.ContainsKey(_cpi))
{
var cpi = data.Get<BLSEconomicSurveysCpi>(_cpi);
// Simple signal: if energy CPI is rising faster than core (energy > 1.5 * core)
var targetEnergy = cpi.Energy.HasValue
&& cpi.CoreCpi.HasValue
&& cpi.Energy.Value > cpi.CoreCpi.Value * 1.5;
var securities = new List<Security>();
foreach (var security in _universe)
{
if (targetEnergy)
{
if (security.Fundamentals.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.Energy)
{
securities.Add(security);
}
}
else
{
securities.Add(security);
}
}
if (securities.Count > 0)
{
// Emit monthly insights for the selected stocks.
return securities.Select(s =>
Insight.Price(s, TimeSpan.FromDays(35), InsightDirection.Up, weight: 1.0m / securities.Count)
);
}
}
return Enumerable.Empty<Insight>();
}
// As assets enter and leave the universe, update our _universe member.
public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
{
foreach (var security in changes.AddedSecurities)
{
_universe.Add(security);
}
foreach (var security in changes.RemovedSecurities)
{
_universe.Remove(security);
}
}
}
Data Point Attributes
The BLS database contains over 77 million series from over 60 different surveys, but not all of them are integrated into QuantConnect. The following table show the integrated surveys:
| Ticker | Survey | Release Time (ET) | Start Date |
|---|---|---|---|
| CPI | Consumer Price Index | 08:30 AM | Jan 2000 |
| CES | Current Employment Statistics | 08:30 AM | Jan 2000 |
| PPI | Producer Price Index | 08:30 AM | Jan 2000 |
| JOLTS | Job Openings and Labor Turnover | 10:00 AM | Nov 2007 |