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");

Data Summary

The following table describes the dataset properties:

PropertyValue
Start DateJanuary 2000 (CPI/CES/PPI), November 2007 (JOLTS)
Data DensitySparse
Survey Coverage50 non-seasonally-adjusted series across 4 surveys
ResolutionDaily
TimezoneNew York

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.

Remove Subscriptions

To remove your subscription to BLS data, call the RemoveSecurity method.

self.RemoveSecurity(self._cpi)
RemoveSecurity(_cpi);

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:

TickerSurveyRelease Time (ET)Start Date
CPIConsumer Price Index08:30 AMJan 2000
CESCurrent Employment Statistics08:30 AMJan 2000
PPIProducer Price Index08:30 AMJan 2000
JOLTSJob Openings and Labor Turnover10:00 AMNov 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:

TickerSurveyRelease Time (ET)Start Date
CPIConsumer Price Index08:30 AMJan 2000
CESCurrent Employment Statistics08:30 AMJan 2000
PPIProducer Price Index08:30 AMJan 2000
JOLTSJob Openings and Labor Turnover10:00 AMNov 2007

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: