US Equity

Corporate Fundamentals

Introduction

Corporate fundamental data contains all the information on the underlying company of an Equity asset and the information in their financial statements. Since corporate data contains information not found in price and alternative data, adding corporate data to your trading strategies provides you with more information so you can make more informed trading decisions. Corporate fundamental data is available through the US Fundamental Data from Morningstar.

Direct Access

To get fundamental data for Equities in your algorithm, use the Fundamentalsfundamentals property of the Equity objects. The fundamental data represent the company's fundamentals for the current algorithm time.

var fundamentals = Securities[_symbol].Fundamentals;
fundamentals = self.securities[self._symbol].fundamentals

To get fundamental data for Equities, regardless of whether or not you have subscribed to them in your algorithm, call the Fundamentalsfundamentals method. If you pass one Symbol, the method returns a Fundamental object. If you pass a list of Symbol objects, the method returns a list of Fundamental objects. The fundamental data represents the corporate fundamentals for the current algorithm time.

// Single asset 
var ibm = QuantConnect.Symbol.Create("IBM", SecurityType.Equity, Market.USA);
var ibmFundamental = Fundamentals(ibm);

// Multiple assets
var nb = QuantConnect.Symbol.Create("NB", SecurityType.Equity, Market.USA);
var fundamentals = Fundamentals(new List<Symbol>{ nb, ibm }).ToList();
# Single asset
ibm = QuantConnect.symbol.create("IBM", SecurityType.EQUITY, Market.USA)
ibm_fundamental = self.fundamentals(ibm)

# Multiple assets
nb = QuantConnect.symbol.create("NB", SecurityType.EQUITY, Market.USA)
fundamentals = self.fundamentals([ nb, ibm ])

Data Availability

Some assets don't have fundamentals (for example, ETFs) and the Morningstar dataset doesn't provide fundamentals for all US Equities. To check if fundamental data is available for an asset, use the HasFundamentalDatahas_fundamental_data property.

var hasFundamentalData = Securities[_symbol].Fundamentals.HasFundamentalData;
has_fundamental_data = self.securities[self._symbol].fundamentals.has_fundamental_data

Object References

If you save a reference to the Fundamentalsfundamentals object or its properties, you can access the fundamental properties as they change over time.

_fundamentals = Securities[_symbol].Fundamentals;
var earningRatios = _fundamentals.EarningRatios;
self._fundamentals = self.securities[self._symbol].fundamentals
earning_ratios = self.fundamentals.earning_ratios

Universe Selection

You can access fundamental data in the selection function of your fundamental universe.

public class MyUniverseAlgorithm : QCAlgorithm {
    public override void Initialize() 
    {
        UniverseSettings.Asynchronous = true;
        AddUniverse(FundamentalFilterFunction);
    }
        
    private IEnumerable<Symbol> FundamentalFilterFunction(IEnumerable<Fundamental> fundamental) 
    {
         return (from f in fundamental
                where f.HasFundamentalData
                select f.Symbol);
    }
}
class MyUniverseAlgorithm(QCAlgorithm):
    def initialize(self) -> None:
        self.universe_settings.asynchronous = True
        self.add_universe(self._fundamental_function)
    
    def _fundamental_function(self, fundamental: List[Fundamental]) -> List[Symbol]:
        return [c.symbol for c in fundamental if c.has_fundamental_data]

Historical Data

To get historical fundamental data, call the Historyhistory method. The return type depends on how you call the method.

var ibm = QuantConnect.Symbol.Create("IBM", SecurityType.Equity, Market.USA);

// Fundamental objects
var fundamentalHistory = History<Fundamental>(ibm, TimeSpan.FromDays(30));

// Fundamentals objects for all US Equities (including delisted companies)
var fundamentalsHistory = History<Fundamentals>(TimeSpan.FromDays(30));

// Collection of Fundamental objects for all US Equities (including delisted companies)
var collectionHistory = History(_universe, 30, Resolution.Daily);
foreach (var fundamental in collectionHistory)
{
    // Cast to Fundamental is required
    var highestMarketCap = fundamental.OfType<Fundamental>().OrderByDescending(x => x.MarketCap).Take(5);
}
ibm = Symbol.create("IBM", SecurityType.EQUITY, Market.USA)

# DataFrame of fundamental data for a given asset
df_history = self.history(Fundamental, ibm, timedelta(30), flatten=True)

# Fundamental objects
fundamental_history = self.history[Fundamental](ibm, timedelta(30))

# Fundamentals objects for all US Equities (including delisted companies)
fundamentals_history = self.history[Fundamentals](timedelta(30))

# DataFrame of fundamental data for universe constituents
df_history = self.history(self._universe, 30, Resolution.DAILY, flatten=True)

# Series of fundamental data for universe constituents
series_history = self.history(self._universe, 30, Resolution.DAILY)
for (universe_symbol, time), fundamental in series_history.items():
    highest_market_cap = sorted(fundamental, key=lambda x: x.market_cap)[-5:]

For more information about historical fundamental data, see Equity Fundamental Data.

Data Updates

The US Fundamental dataset only provides the originally-reported figures. If there was a mistake in reporting a figure, the data value isn't fixed later. In live trading, new fundamental data is available to your algorithms at approximately 6/7 AM Eastern Time (ET) each day. The majority of the corporate data update occurs once per month, but the financial ratios update daily. If there is no new data for a period of time, the previous data is filled forward.

Properties

The US Fundamentals dataset provides Fundamental objects. To filter Fundamental objects, you can use the MorningstarSectorCode, MorningstarIndustryGroupCode, and MorningstarIndustryCode enumeration values.

Fundamentals Attributes

Fundamentals objects have the following attributes:

MorningstarSectorCode Enumeration

Sectors are large super categories of data. To access the sector of an Equity, use the MorningstarSectorCodemorningstar_sector_code property.

filtered = fundamental.Where(x => x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.Technology);
filtered = [x for x in fundamental if x.asset_classification.morningstar_sector_code == MorningstarSectorCode.TECHNOLOGY]

The MorningstarSectorCode enumeration has the following members:

MorningstarIndustryGroupCode Enumeration

Industry groups are clusters of related industries which tie together. To access the industry group of an Equity, use the MorningstarIndustryGroupCodemorningstar_industry_group_code property.

filtered = fundamental.Where(x => x.AssetClassification.MorningstarIndustryGroupCode == MorningstarIndustryGroupCode.ApplicationSoftware);
filtered = [x for x in fundamental if x.asset_classification.morningstar_industry_group_code == MorningstarIndustryGroupCode.APPLICATION_SOFTWARE]

The MorningstarIndustryGroupCode enumeration has the following members:

MorningstarIndustryCode Enumeration

Industries are the finest level of classification available and are the individual industries according to the Morningstar classification system. To access the industry group of an Equity, use the MorningstarIndustryCodemorningstar_industry_code property:

filtered = fundamental.Where(x => x.AssetClassification.MorningstarIndustryCode == MorningstarIndustryCode.SoftwareApplication);
filtered = [x for x in fundamental if x.asset_classification.morningstar_industry_code == MorningstarIndustryCode.SOFTWARE_APPLICATION]

The MorningstarIndustryCode enumeration has the following members:

Exchange Id Values

Exchange Id is mapped to represent the exchange that lists the Equity. To access the exchange Id of an Equity, use the PrimaryExchangeIDprimary_exchange_id property.

filtered = fundamental.Where(x => x.CompanyReference.PrimaryExchangeID == "NAS");
filtered = [x for x in fundamental if x.company_reference.primary_exchange_id == "NAS"]

The exchanges are represented by the following string values:

String RepresentationExchange
NYSNew York Stock Exchange (NYSE)
NASNASDAQ
ASEAmerican Stock Exchange (AMEX)
TSETokyo Stock Exchange
AMSAmsterdam Internet Exchange
SGOSantiago Stock Exchange
XMADMadrid Stock Exchange
ASXAustralian Securities Exchange
BVMFB3 (stock exchange)
LONLondon Stock Exchange
TKSIstanbul Stock Exchange Settlement and Custody Bank
SHGShanghai Exchange
LIMLima Stock Exchange
FRAFrankfurt Stock Exchange
JSEJohannesburg Stock Exchange
MILMilan Stock Exchange
TAETel Aviv Stock Exchange
STOStockholm Stock Exchange
ETRDeutsche Boerse Xetra Core
PARParis Stock Exchange
BUEBuenos Aires Stock Exchange
KRXKorea Exchange
SWXSIX Swiss Exchange
PINXPink Sheets (OTC)
CSECanadian Securities Exchange
PHSPhilippine Stock Exchange
MEXMexican Stock Exchange
TAITaiwan Stock Exchange
IDXIndonesia Stock Exchange
OSLOslo Stock Exchange
BOGColombia Stock Exchange
NSENational Stock Exchange of India
HELNasdaq Helsinki
MISXMoscow Exchange
HKGHong Kong Stock Exchange
ISTIstanbul Stock Exchange
BOMBombay Stock Exchange
TSXToronto Stock Exchange
BRUBrussels Stock Exchange
BATSBATS Global Markets
ARCXNYSE Arca
GREYGrey Market (OTC)
DUSDusseldorf Stock Exchange
BERBerlin Stock Exchange
ROCOTaipei Exchange
CNQCanadian Trading and Quotation System Inc.
BSPBangko Sentral ng Pilipinas
NEOENEO Exchange

Examples

The following examples demonstrate some common practices for US Equity corporate fundamentals.

Example 1: Capturing the Overreaction to Financial Reports

The following algorithm is based on the hypothesis that investors overreact to new financial report releases. It buys AAPL stock on every new 10Q or 8K filing and then liquidates the position after seven days.

public class USEquityCorporateFundamentalsExampleAlgorithm : QCAlgorithm
{
    private Equity _aapl;
    // Add a member to track the last financial statement filing date.
    private DateTime? _lastFilingDate = null;
    private DateTime _exitTime;

    public override void Initialize()
    {
        // Add Equity data.
        _aapl = AddEquity("AAPL");
    }

    public override void OnData(Slice slice)
    {
        // Get the most recent filing date.
        var lastFilingDate = _aapl.Fundamentals.FinancialStatements.FileDate.Value;

        // When new financial statements are released, buy AAPL.
        if (_lastFilingDate.HasValue && lastFilingDate != _lastFilingDate.Value)
        {
            SetHoldings(_aapl.Symbol, 1);
            _exitTime = Time.AddDays(7);
        }

        // Liquidate the position after 1 week.
        if (_aapl.Holdings.Invested && Time >= _exitTime)
        {
            Liquidate();
        }
    
        // Update the last filing date to identify future changes.
        _lastFilingDate = lastFilingDate;
    }
}
class USEquityCorporateFundamentalsExampleAlgorithm(QCAlgorithm):
        
    def initialize(self) -> None:
        # Add Equity data.
        self._aapl = self.add_equity("AAPL")
        # Add a member to track the last financial statement filing date.
        self._last_filing_date = None

    def on_data(self, slice: Slice) -> None:
        # Get the most recent filing date.
        last_filing_date = self._aapl.fundamentals.financial_statements.file_date.value
        
        # When new financial statements are released, buy AAPL.
        if self._last_filing_date and last_filing_date != self._last_filing_date:
            self.set_holdings(self._aapl.symbol, 1)
            self._exit_time = self.time + timedelta(7)

        # Liquidate the position after 1 week.
        if self._aapl.holdings.invested and self.time >= self._exit_time:
            self.liquidate()
        
        # Update the last filing date to identify future changes.
        self._last_filing_date = last_filing_date

Example 2: Diversifying Across Sectors

The follow algorithm aims to reduce concentration risk in any one sector. It selects the 500 largest Equities at the start of each month and then forms a portfolio that gives equal weight to each sector and equal weight to the assets within each sector.

public class USEquityCorporateFundamentalsExampleAlgorithm : QCAlgorithm
{
    private Universe _universe;
    
    public override void Initialize()
    {
        SetCash(10_000_000);
        // Define the universe settings.
        var spy = QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA);
        UniverseSettings.Resolution = Resolution.Daily;
        UniverseSettings.Schedule.On(DateRules.MonthStart(spy));
        // Add a universe of the 500 largest Equities.
        _universe = AddUniverse(coarse => coarse
            .OrderByDescending(x => x.MarketCap)
            .Take(500)
            .Select(x => x.Symbol)
        );
        // Create a Scheduled Event to rebalance the portfolio each month.
        Schedule.On(DateRules.MonthStart(spy, 1), TimeRules.Midnight, Rebalance);
    }

    private void Rebalance()
    {
        // Wait until there are assets in the universe.
        if (_universe.Selected == null)
        {
            return;
        }
        // Group the universe constituents by sector.
        var symbolsBySector = new Dictionary<int, List<Symbol>>();
        foreach (var symbol in _universe.Selected)
        {
            var equity = Securities[symbol];
            if (equity.Price == 0)
            {
                continue;
            }
            var sectorCode = equity.Fundamentals.AssetClassification.MorningstarSectorCode;
            if (!symbolsBySector.ContainsKey(sectorCode))
            {
                symbolsBySector[sectorCode] = new List<Symbol>();
            }
            symbolsBySector[sectorCode].Add(symbol);
        }

        // Create targets to invest equally in each sector and invest equally in the assets within each sector.
        var targets = new List<PortfolioTarget>();
        var sectorCount = symbolsBySector.Count;
        foreach (var symbols in symbolsBySector.Values)
        {
            var weight = 1m / sectorCount / symbols.Count;
            targets.AddRange(symbols.Select(symbol => new PortfolioTarget(symbol, weight)));
        }

        // Place the trades.
        SetHoldings(targets, true);
    }
}
class USEquityCorporateFundamentalsExampleAlgorithm(QCAlgorithm):
        
    def initialize(self) -> None:
        self.set_cash(10_000_000)
        # Define the universe settings.
        spy = Symbol.create('SPY', SecurityType.EQUITY, Market.USA)
        self.universe_settings.resolution = Resolution.DAILY
        self.universe_settings.schedule.on(self.date_rules.month_start(spy))
        # Add a universe of the 500 largest Equities.
        self._universe = self.add_universe(
            lambda fundamental: [
                f.symbol for f in sorted(fundamental, key=lambda f: f.market_cap)[-500:]
            ]
        )
        # Create a Scheduled Event to rebalance the portfolio each month.
        self.schedule.on(self.date_rules.month_start(spy, 1), self.time_rules.midnight, self._rebalance)

    def _rebalance(self) -> None:
        # Wait until there are asset in the universe.
        if not self._universe.selected:
            return

        # Group the universe constituents by sector.
        symbols_by_sector = {}
        for symbol in self._universe.selected:
            equity = self.securities[symbol]
            if not equity.price:
                continue
            sector_code = equity.fundamentals.asset_classification.morningstar_sector_code
            if sector_code not in symbols_by_sector:
                symbols_by_sector[sector_code] = []
            symbols_by_sector[sector_code].append(symbol)
        
        # Create targets to invest equally in each sector and invest equally in the assets within each sector.
        targets = []
        sector_count = len(symbols_by_sector)
        for symbols in symbols_by_sector.values():
            weight = 1 / sector_count / len(symbols)
            targets.extend([PortfolioTarget(symbol, weight) for symbol in symbols])

        # Place the trades.
        self.set_holdings(targets, True)

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: