JSPM

  • Created
  • Published
  • Downloads 195
  • Score
    100M100P100Q87067F
  • License ISC

Fetch and parse SEC earnings reports and other filings. Useful for financial analysis.

Package Exports

  • sec-edgar-api
  • sec-edgar-api/build/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (sec-edgar-api) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

sec-edgar-api

Fetch and parse earnings reports and other documents filed with the SEC using the EDGAR API. This package is focused on the earnings reports for stock analysis.

Report Interface

Reports are all returned as a uniform interface:

interface ReportTranslated {
    dateReport: string
    dateFiled: string
    fiscalPeriod: FiscalPeriod
    fiscalYear: number
    form: string
    isTTM: boolean

    assetTotal: number | null
    assetCurrent: number | null
    assetCurrentCashEquivalents: number | null
    assetCurrentInvestments: number | null
    assetCurrentAccountsReceivable: number | null
    assetCurrentInventory: number | null

    assetNonCurrent: number | null
    assetNonCurrentPPENet: number | null
    assetNonCurrentPPEGross: number | null
    assetNonCurrentInvestments: number | null
    assetNonCurrentGoodwill: number | null
    assetNonCurrentIntangibleLessGoodwill: number | null

    liabilityTotal: number | null
    liabilityCurrent: number | null
    liabilityCurrentAccountsPayable: number | null
    liabilityCurrentDebt: number | null
    liabilityNonCurrent: number | null
    liabilityNonCurrentDebt: number | null

    equityTotal: number | null
    equityRetainedEarnings: number | null
    equityStockPreferred: number | null

    sharesOutstanding: number | null
    sharesOutstandingDiluted: number | null

    eps: number | null
    epsDiluted: number | null

    ebit: number | null
    ebitda: number | null

    profitGross: number | null

    revenueTotal: number | null
    revenueCost: number | null
    revenueOperating: number | null

    expenseTotal: number | null
    expenseOperating: number | null
    expenseResearchDevelopment: number | null
    expenseInterest: number | null
    expenseDepreciation: number | null
    expenseTax: number | null

    expenseDepreciationAccumulated: number | null
    expenseStockCompensation: number | null
    expenseNonCashOther: number | null

    incomeOperating: number | null
    incomeNet: number | null

    cashFlowFree: number | null
    cashFlowDividendsPaid: number | null
    cashFlowDividendsPaidPreferred: number | null

    cashFlowCapex: number | null
    cashFlowOperating: number | null
    cashFlowDeferredTax: number | null

    cashFlowWorkingCapitalNonCash: number | null
}

Usage

import package contents

import { factFileReader, reportParser, secEdgarApi } from 'sec-edgar-api'

You can fetch reports individually directly from the SEC website, (throttled to 10 requests per second)

// returns array of ReportWrapper (which implements ReportTranslated)
const reports = await secEdgarApi.getReports({ symbol: 'AAPL' })

or download all data from the SEC website and read directly from the files so you don't have to worry about rate limiting.

const downloadDirectory = './downloads/companyfacts'

// Download companyfacts directory from sec.gov (over 15GB)
await secEdgarApi.downloadCompanyFactsDirectory({
    outputDirname: downloadDirectory,
    onDownloadComplete: () => process.stdout.write('\n'),
    onChunk: ({ percentComplete, stage }) => {
        // Write progress bar to console
        const countBarsComplete = Math.ceil(percentComplete * 30)
        const barStr = `${'='.repeat(countBarsComplete)}${' '.repeat(30 - countBarsComplete)}`
        const percentStr = `${(Math.round(percentComplete * 10000) / 100).toFixed(2)}%`
        const statusStr = stage === 'download' ? 'Downloading...' : 'Unzipping...'

        process.stdout.write(`\r<${barStr}> ${percentStr} ${statusStr}`)
    },
})

// read companyfacts directory
const companyFacts = factFileReader.readFactFile({
    companyFactsDirname: downloadDirectory,
    symbol: 'AAPL',
})

// parse reports (same return value as secEdgarApi.getReports())
const reports = reportParser.parseReports(companyFacts)

Resolvers

WARNING Still in testing. Values may not be accurate for all companies since the properties provided in the reports differ.

The main problem with the edgar API is that the property names and data provided are not uniform. You have to deal with companies omitting important data in some filings, or using different property keys for the same data point.

Resolvers attempt to get information from each report and output a uniform interface. The resolvers will calculate missing data if there is other data that can be used to derive from.

Resolver Formula used to derive values
resolveAssetCurrent assetTotal - assetNonCurrent = assetCurrent
resolveAssetNonCurrentPpeGross assetNonCurrentPPENet + expenseDepreciationAccumulated = assetNonCurrentPpeGross
resolveCashFlowCapex Q1 + Q2 + Q3 + Q4 = FY (if FY known, divides evenly between missing quarters)
resolveCashFlowFree cashFlowOperating - cashFlowCapex = cashFlowFree
resolveCashFlowOperating incomeNet + expenseDepreciation - changeInWorkingCapitalNonCash = cashFlowOperating
resolveCashFlowWorkingCapitalNonCash (assetCurrent - assetCurrentCashEquivalents) - (liabilityCurrent - liabilityCurrentDebt) = cashFlowWorkingCapitalNonCash
resolveEbit expenseDepreciation + ebitda = ebit
resolveExpenseDepreciation (expenseDepreciationFY / assetNonCurrentPpeGrossFY) x assetNonCurrentPpeGross = expenseDepreciation
resolveExpenseOperating revenueTotal - incomeOperating - revenueCost = expenseOperating
resolveExpenseTotal revenueTotal - incomeNet = expenseTotal
resolveFiscalYearCumulativeProperties Q1 + Q2 + Q3 + Q4 = FY (for quarterly properties that add to annual)
resolveQ4FiscalYearMatchingProperties Q4 = FY (for non-cumulative properties such as sharesOutstanding)
resolveRevenueTotal revenueCost + profitGross = revenueTotal

Contributing

Getting all the properties in a uniform interface accurately is proving to be very difficult due to the differences in all the reports. Please contribute if you know how to improve this.

Files for mapping & resolving properties:

  • Mapping properties: src/util/key-translations.ts
  • Resolving properties: src/services/ReportParser.ts (add resolvers to the resolvers/ directory, import to /resolver/index.ts, and add to ReportParser.resolveAll)

These are the scripts I used to get keys commonly used across reports, which you can use to add to key-translations.ts

import { readAllCompanyFactFiles, getPropertyUsageCounts } from './scripts/script-helpers'

const companyFactsList = readAllCompanyFactFiles(path.resolve('./downloads/companyfacts'), 10)
const propertyUsageCounts = getPropertyUsageCounts(companyFactsList)

fs.writeFileSync('./downloads/property-usage-counts.json', JSON.stringify(propertyUsageCounts, null, 2))

Resources