sec-edgar-toolkit
Version:
Open source toolkit to facilitate working with the SEC EDGAR database
377 lines • 15 kB
JavaScript
"use strict";
/**
* XBRL instance class providing comprehensive financial data analysis
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.XBRLInstance = void 0;
class XBRLInstance {
constructor(filing, api) {
// Cache for XBRL data
this._facts = null;
this._usGaapFacts = null;
this._deiFacts = null;
this._api = api || filing.api;
this.filing = filing;
this.cik = filing.cik;
}
/**
* Get all XBRL facts for the company
*/
async getFacts() {
if (!this._facts) {
this._facts = await this._api.xbrl.getCompanyFacts(this.cik);
}
return this._facts;
}
/**
* Get US-GAAP facts
*/
async getUsGaap() {
if (!this._usGaapFacts) {
const facts = await this.getFacts();
this._usGaapFacts = facts?.facts?.['us-gaap'] || {};
}
return this._usGaapFacts;
}
/**
* Get DEI (Document Entity Information) facts
*/
async getDei() {
if (!this._deiFacts) {
const facts = await this.getFacts();
this._deiFacts = facts?.facts?.dei || {};
}
return this._deiFacts;
}
/**
* Query XBRL facts with filtering
*/
async query(options = {}) {
const { concept, taxonomy = 'us-gaap', unit, period } = options;
const results = [];
// Get facts for the specified taxonomy
let taxonomyFacts;
if (taxonomy === 'us-gaap') {
taxonomyFacts = await this.getUsGaap();
}
else if (taxonomy === 'dei') {
taxonomyFacts = await this.getDei();
}
else {
const facts = await this.getFacts();
taxonomyFacts = facts?.facts?.[taxonomy] || {};
}
// If concept is specified, filter to that concept
if (concept) {
if (concept in taxonomyFacts) {
const conceptData = taxonomyFacts[concept];
results.push(...this.processConceptData(concept, conceptData, unit, period));
}
}
else {
// Query all concepts
for (const [conceptName, conceptData] of Object.entries(taxonomyFacts)) {
results.push(...this.processConceptData(conceptName, conceptData, unit, period));
}
}
return results;
}
/**
* Process concept data and apply filters
*/
processConceptData(conceptName, conceptData, unitFilter, periodFilter) {
const results = [];
const units = conceptData.units || {};
for (const [unit, unitData] of Object.entries(units)) {
// Apply unit filter
if (unitFilter && unit !== unitFilter) {
continue;
}
if (Array.isArray(unitData)) {
for (const fact of unitData) {
// Apply period filter
if (periodFilter) {
// Check if the filter matches the end date or period fields
const matchesEndDate = fact.end === periodFilter;
const matchesInstant = fact.instant === periodFilter;
const factPeriod = fact.fy || fact.fp || fact.frame || '';
const matchesPeriod = String(factPeriod).includes(periodFilter);
if (!matchesEndDate && !matchesInstant && !matchesPeriod) {
continue;
}
}
// Create standardized fact record
const factRecord = {
concept: conceptName,
taxonomy: 'us-gaap',
value: fact.val,
unit: unit,
period: fact.frame || `FY${fact.fy || ''}${fact.fp || ''}`,
fiscal_year: fact.fy,
fiscal_period: fact.fp,
start_date: fact.start,
end_date: fact.end,
filed: fact.filed,
accession_number: fact.accn,
form: fact.form,
};
results.push(factRecord);
}
}
}
return results;
}
/**
* Find and extract a specific financial statement
*/
async findStatement(statementType, period) {
switch (statementType) {
case 'balance_sheet':
return await this.extractBalanceSheet(period);
case 'income_statement':
return await this.extractIncomeStatement(period);
case 'cash_flow':
return await this.extractCashFlowStatement(period);
default:
console.warn(`Unknown statement type: ${statementType}`);
return null;
}
}
/**
* Extract balance sheet data
*/
async extractBalanceSheet(period) {
const concepts = [
'Assets', 'AssetsCurrent', 'AssetsNoncurrent',
'Liabilities', 'LiabilitiesCurrent', 'LiabilitiesNoncurrent',
'StockholdersEquity', 'RetainedEarningsAccumulatedDeficit'
];
const statementData = {};
for (const concept of concepts) {
const facts = await this.query({ concept, unit: 'USD', period });
if (facts.length > 0) {
// Get most recent fact
const latestFact = facts.reduce((prev, current) => (current.filed || '') > (prev.filed || '') ? current : prev);
statementData[concept] = latestFact;
}
}
return {
statement_type: 'balance_sheet',
period,
data: statementData,
};
}
/**
* Extract income statement data
*/
async extractIncomeStatement(period) {
const concepts = [
'Revenues', 'RevenueFromContractWithCustomerExcludingAssessedTax',
'CostOfRevenue', 'GrossProfit',
'OperatingIncomeLoss', 'IncomeLossFromContinuingOperationsBeforeIncomeTaxesExtraordinaryItemsNoncontrollingInterest',
'NetIncomeLoss', 'EarningsPerShareBasic', 'EarningsPerShareDiluted'
];
const statementData = {};
for (const concept of concepts) {
const facts = await this.query({ concept, unit: 'USD', period });
if (facts.length > 0) {
// Get most recent fact
const latestFact = facts.reduce((prev, current) => (current.filed || '') > (prev.filed || '') ? current : prev);
statementData[concept] = latestFact;
}
}
return {
statement_type: 'income_statement',
period,
data: statementData,
};
}
/**
* Extract cash flow statement data
*/
async extractCashFlowStatement(period) {
const concepts = [
'NetCashProvidedByUsedInOperatingActivities',
'NetCashProvidedByUsedInInvestingActivities',
'NetCashProvidedByUsedInFinancingActivities',
'CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalents'
];
const statementData = {};
for (const concept of concepts) {
const facts = await this.query({ concept, unit: 'USD', period });
if (facts.length > 0) {
// Get most recent fact
const latestFact = facts.reduce((prev, current) => (current.filed || '') > (prev.filed || '') ? current : prev);
statementData[concept] = latestFact;
}
}
return {
statement_type: 'cash_flow',
period,
data: statementData,
};
}
/**
* Get structured balance sheet
*/
async getBalanceSheet(period) {
const statement = await this.extractBalanceSheet(period);
const data = statement.data;
// Helper to convert XbrlFact to BalanceSheetItem
const toBalanceSheetItem = (fact) => ({
label: fact.concept,
value: typeof fact.value === 'number' ? fact.value : parseFloat(fact.value),
units: fact.unit,
period: fact.period,
filed: new Date(fact.filed || '')
});
const assets = {
current_assets: data.AssetsCurrent ? [toBalanceSheetItem(data.AssetsCurrent)] : [],
non_current_assets: data.AssetsNoncurrent ? [toBalanceSheetItem(data.AssetsNoncurrent)] : [],
total_assets: data.Assets ? toBalanceSheetItem(data.Assets) : undefined
};
const liabilities = {
current_liabilities: data.LiabilitiesCurrent ? [toBalanceSheetItem(data.LiabilitiesCurrent)] : [],
non_current_liabilities: data.LiabilitiesNoncurrent ? [toBalanceSheetItem(data.LiabilitiesNoncurrent)] : [],
total_liabilities: data.Liabilities ? toBalanceSheetItem(data.Liabilities) : undefined
};
const equity = {
total_equity: data.StockholdersEquity ? toBalanceSheetItem(data.StockholdersEquity) : undefined,
retained_earnings: data.RetainedEarningsAccumulatedDeficit ?
toBalanceSheetItem(data.RetainedEarningsAccumulatedDeficit) : undefined
};
return { assets, liabilities, equity };
}
/**
* Get structured income statement
*/
async getIncomeStatement(period) {
const statement = await this.extractIncomeStatement(period);
const data = statement.data;
// Helper to convert XbrlFact to IncomeStatementItem
const toIncomeStatementItem = (fact) => ({
label: fact.concept,
value: typeof fact.value === 'number' ? fact.value : parseFloat(fact.value),
units: fact.unit,
period: fact.period,
filed: new Date(fact.filed || '')
});
return {
revenue: data.Revenues || data.RevenueFromContractWithCustomerExcludingAssessedTax ?
toIncomeStatementItem(data.Revenues || data.RevenueFromContractWithCustomerExcludingAssessedTax) : undefined,
gross_profit: data.GrossProfit ? toIncomeStatementItem(data.GrossProfit) : undefined,
operating_income: data.OperatingIncomeLoss ? toIncomeStatementItem(data.OperatingIncomeLoss) : undefined,
net_income: data.NetIncomeLoss ? toIncomeStatementItem(data.NetIncomeLoss) : undefined,
earnings_per_share: data.EarningsPerShareBasic ? toIncomeStatementItem(data.EarningsPerShareBasic) : undefined,
operating_expenses: []
};
}
/**
* Get structured cash flow statement
*/
async getCashFlowStatement(period) {
const statement = await this.extractCashFlowStatement(period);
const data = statement.data;
// Helper to convert XbrlFact to CashFlowItem
const toCashFlowItem = (fact) => ({
label: fact.concept,
value: typeof fact.value === 'number' ? fact.value : parseFloat(fact.value),
units: fact.unit,
period: fact.period,
filed: new Date(fact.filed || '')
});
return {
operating_activities: data.NetCashProvidedByUsedInOperatingActivities ?
[toCashFlowItem(data.NetCashProvidedByUsedInOperatingActivities)] : [],
investing_activities: data.NetCashProvidedByUsedInInvestingActivities ?
[toCashFlowItem(data.NetCashProvidedByUsedInInvestingActivities)] : [],
financing_activities: data.NetCashProvidedByUsedInFinancingActivities ?
[toCashFlowItem(data.NetCashProvidedByUsedInFinancingActivities)] : [],
net_cash_flow: data.CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalents ?
toCashFlowItem(data.CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalents) : undefined
};
}
/**
* Get a single value for a specific concept
*/
async getConceptValue(concept, taxonomy = 'us-gaap', unit = 'USD', period) {
const facts = await this.query({ concept, taxonomy, unit, period });
if (facts.length > 0) {
// Return the most recent value
const latestFact = facts.reduce((prev, current) => (current.filed || '') > (prev.filed || '') ? current : prev);
return typeof latestFact.value === 'number' ?
latestFact.value : parseFloat(latestFact.value);
}
return null;
}
/**
* Get all facts for a specific concept
*/
async getFactsByConcept(concept, taxonomy = 'us-gaap') {
const facts = await this.getFacts();
const taxonomyFacts = facts?.facts?.[taxonomy];
if (!taxonomyFacts || !taxonomyFacts[concept]) {
return [];
}
const conceptData = taxonomyFacts[concept];
const results = [];
// Process all units
for (const [unit, values] of Object.entries(conceptData.units || {})) {
if (Array.isArray(values)) {
for (const value of values) {
results.push({
concept,
taxonomy,
value: value.val,
unit,
period: value.end || value.instant || '',
fiscal_year: value.fy,
fiscal_period: value.fp,
start_date: value.start,
end_date: value.end,
filed: value.filed,
accession_number: value.accn,
form: value.form,
});
}
}
}
return results;
}
/**
* List all available concepts in a taxonomy
*/
async listConcepts(taxonomy = 'us-gaap') {
let taxonomyFacts;
if (taxonomy === 'us-gaap') {
taxonomyFacts = await this.getUsGaap();
}
else if (taxonomy === 'dei') {
taxonomyFacts = await this.getDei();
}
else {
const facts = await this.getFacts();
taxonomyFacts = facts?.facts?.[taxonomy] || {};
}
return Object.keys(taxonomyFacts);
}
/**
* Convert XBRL data to plain object
*/
async toObject(options = {}) {
const facts = await this.query(options);
return {
metadata: {
cik: this.cik,
filing_date: this.filing.filingDate,
form_type: this.filing.formType,
},
facts: facts,
};
}
toString() {
return `XBRL instance for ${this.filing.formType} filing (CIK: ${this.cik})`;
}
}
exports.XBRLInstance = XBRLInstance;
//# sourceMappingURL=xbrl.js.map