Class: SecApi::XbrlData
- Inherits:
-
Dry::Struct
- Object
- Dry::Struct
- SecApi::XbrlData
- Includes:
- DeepFreezable
- Defined in:
- lib/sec_api/objects/xbrl_data.rb
Overview
Taxonomy Transparency - Element Names Are NOT Normalized This gem returns element names exactly as provided by sec-api.io, without any normalization between US GAAP and IFRS taxonomies. This design decision ensures:
-
Accuracy: You see exactly what the company reported
-
**No data loss:** Taxonomy-specific nuances are preserved
-
Predictability: The gem never modifies financial data
Users are responsible for knowing which elements to access based on the filing’s taxonomy. Use #element_names to discover available elements in any filing.
US GAAP Taxonomy Common Elements US domestic filings (10-K, 10-Q) use US GAAP taxonomy with verbose element names:
**Income Statement:**
-
RevenueFromContractWithCustomerExcludingAssessedTax (revenue)
-
CostOfGoodsAndServicesSold (cost of goods sold)
-
NetIncomeLoss (net income)
-
GrossProfit (gross profit)
-
OperatingIncomeLoss (operating income)
**Balance Sheet:**
-
Assets (total assets)
-
Liabilities (total liabilities)
-
StockholdersEquity (shareholders’ equity)
-
CashAndCashEquivalentsAtCarryingValue (cash)
-
AccountsReceivableNetCurrent (accounts receivable)
**Cash Flow Statement:**
-
NetCashProvidedByUsedInOperatingActivities (operating cash flow)
-
NetCashProvidedByUsedInInvestingActivities (investing cash flow)
-
NetCashProvidedByUsedInFinancingActivities (financing cash flow)
IFRS Taxonomy Common Elements Foreign issuer filings (20-F, 40-F) often use IFRS taxonomy with simpler element names:
**Income Statement:**
-
Revenue (revenue)
-
CostOfSales (cost of sales)
-
ProfitLoss (net income/profit)
-
GrossProfit (gross profit)
**Balance Sheet:**
-
Assets (total assets - same as US GAAP)
-
Liabilities (total liabilities - same as US GAAP)
-
Equity (shareholders’ equity - NOT StockholdersEquity)
**Cash Flow Statement:**
-
CashFlowsFromUsedInOperatingActivities (operating cash flow)
-
CashFlowsFromUsedInInvestingActivities (investing cash flow)
-
CashFlowsFromUsedInFinancingActivities (financing cash flow)
Note: Element names are NOT normalized between taxonomies. Users working with international filings should use #element_names to discover available elements.
Immutable value object representing XBRL financial data extracted from SEC filings.
Heuristic Validation Strategy (Architecture ADR-5): Rather than validating against full XBRL taxonomies (US GAAP, IFRS), we use heuristic checks that verify data integrity without bundling 100MB+ schema files:
-
Structure validation: At least one statement section must be present
-
Type validation: Dry::Struct enforces correct types via coercion
-
Fact validation: Each Fact object validates period and value structure
-
Deep freeze: Immutability enforced at construction time
Why not full schema validation?
-
sec-api.io already validates against taxonomies - we trust their parsing
-
Schema files are huge and change with each taxonomy release
-
Our heuristics catch the failures that matter: malformed responses, missing data
-
Full validation would add latency and complexity without practical benefit
This class uses Dry::Struct for type safety and immutability, ensuring thread-safe access to financial data. All nested structures are deeply frozen to prevent modification.
The structure mirrors the sec-api.io XBRL-to-JSON response format:
-
statements_of_income: Income statement elements (e.g., Revenue, NetIncome)
-
balance_sheets: Balance sheet elements (e.g., Assets, Liabilities)
-
statements_of_cash_flows: Cash flow statement elements
-
cover_page: Document and entity information (DEI taxonomy)
Constant Summary collapse
- StatementHash =
Statement hash type: element_name => Array of Fact objects
Types::Hash.map(Types::String, Types::Array.of(Fact)).optional
Class Method Summary collapse
-
.from_api(data) ⇒ XbrlData
Parses sec-api.io XBRL-to-JSON response into an XbrlData object.
Instance Method Summary collapse
-
#element_names ⇒ Array<String>
Returns all unique element names across all financial statements.
-
#initialize(attributes) ⇒ XbrlData
constructor
Override constructor to ensure deep immutability.
-
#taxonomy_hint ⇒ Symbol
Attempts to detect the taxonomy used in this XBRL filing based on element names.
-
#valid? ⇒ Boolean
Checks if this XbrlData object has valid structure.
Constructor Details
#initialize(attributes) ⇒ XbrlData
Override constructor to ensure deep immutability
264 265 266 267 268 269 270 271 |
# File 'lib/sec_api/objects/xbrl_data.rb', line 264 def initialize(attributes) super deep_freeze(statements_of_income) if statements_of_income deep_freeze(balance_sheets) if balance_sheets deep_freeze(statements_of_cash_flows) if statements_of_cash_flows deep_freeze(cover_page) if cover_page freeze end |
Class Method Details
.from_api(data) ⇒ XbrlData
Parses sec-api.io XBRL-to-JSON response into an XbrlData object.
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/sec_api/objects/xbrl_data.rb', line 291 def self.from_api(data) statements_of_income = parse_statement_section(data, :StatementsOfIncome, "StatementsOfIncome") balance_sheets = parse_statement_section(data, :BalanceSheets, "BalanceSheets") statements_of_cash_flows = parse_statement_section(data, :StatementsOfCashFlows, "StatementsOfCashFlows") cover_page = parse_statement_section(data, :CoverPage, "CoverPage") validate_has_statements!(statements_of_income, balance_sheets, statements_of_cash_flows, cover_page, data) new( statements_of_income: statements_of_income, balance_sheets: balance_sheets, statements_of_cash_flows: statements_of_cash_flows, cover_page: cover_page ) end |
Instance Method Details
#element_names ⇒ Array<String>
Use this method to understand what data is available before accessing specific elements. This is especially important for international filings where element names differ from US GAAP conventions.
Returns all unique element names across all financial statements.
This method is essential for discovering what XBRL elements are available in a filing. Element names vary by taxonomy (US GAAP vs IFRS) and by company. The gem does NOT normalize element names between taxonomies.
189 190 191 192 193 194 195 196 |
# File 'lib/sec_api/objects/xbrl_data.rb', line 189 def element_names names = [] names.concat(statements_of_income.keys) if statements_of_income names.concat(balance_sheets.keys) if balance_sheets names.concat(statements_of_cash_flows.keys) if statements_of_cash_flows names.concat(cover_page.keys) if cover_page names.uniq.sort end |
#taxonomy_hint ⇒ Symbol
This is a best-effort heuristic and may not be 100% accurate. Some filings may use mixed element naming conventions or custom elements that don’t clearly indicate either taxonomy. Always verify with #element_names when uncertain. For authoritative taxonomy information, refer to the filing’s original XBRL instance document.
Attempts to detect the taxonomy used in this XBRL filing based on element names.
This method uses heuristics to guess whether the filing uses US GAAP or IFRS taxonomy. Detection is based on characteristic element name patterns:
-
**US GAAP indicators:** Verbose element names like “StockholdersEquity”, “RevenueFromContractWithCustomerExcludingAssessedTax”, “NetCashProvidedByUsedInOperatingActivities”
-
**IFRS indicators:** Simpler element names like “Equity”, “Revenue”, “ProfitLoss”, “CashFlowsFromUsedInOperatingActivities”
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/sec_api/objects/xbrl_data.rb', line 231 def taxonomy_hint names = element_names # US GAAP indicators - verbose, specific naming patterns us_gaap_patterns = [ /StockholdersEquity/, /RevenueFromContractWithCustomer/, /CostOfGoodsAndServicesSold/, /NetCashProvidedByUsedIn/, /NetIncomeLoss/, /CommonStockSharesOutstanding/, /OperatingLeaseLiability/, /PropertyPlantAndEquipmentNet/ ] # IFRS indicators - simpler, shorter naming patterns ifrs_patterns = [ /\AEquity\z/, # Exact match for "Equity" (vs "StockholdersEquity") /\ARevenue\z/, # Exact match for "Revenue" /\AProfitLoss\z/, /\ACostOfSales\z/, /CashFlowsFromUsedIn/ # Substring match: IFRS variants like "CashFlowsFromUsedInOperatingActivities" ] us_gaap_score = us_gaap_patterns.count { |pattern| names.any? { |name| name.match?(pattern) } } ifrs_score = ifrs_patterns.count { |pattern| names.any? { |name| name.match?(pattern) } } return :us_gaap if us_gaap_score > ifrs_score && us_gaap_score > 0 return :ifrs if ifrs_score > us_gaap_score && ifrs_score > 0 :unknown end |
#valid? ⇒ Boolean
Checks if this XbrlData object has valid structure.
Returns true if at least one financial statement section is present. This method is useful for defensive programming when XbrlData objects are created via the constructor directly (bypassing from_api validation).
Note: Objects created via from_api are guaranteed valid, as validation happens at construction time and raises ValidationError on failure.
159 160 161 |
# File 'lib/sec_api/objects/xbrl_data.rb', line 159 def valid? [statements_of_income, balance_sheets, statements_of_cash_flows, cover_page].any? end |