Skip to content

Line-item tags mapped to balance-sheet total concepts — standard_concept == 'Assets' returns 3 rows on a bank filing #914

Description

@DerMayer1

Line-item tags are mapped to balance-sheet total concepts, so a standardized statement returns several rows claiming to be the total. Affects standard_concept on any statement dataframe.

Version 5.43.0.

Repro

from edgar import Company

df = Company('CFG').get_financials().balance_sheet().to_dataframe()
df[df['standard_concept'] == 'Assets'][['concept', 'label', '2025-12-31']]

Citizens Financial Group, FY2025 — three rows:

concept label 2025-12-31
us-gaap_DebtSecuritiesHeldToMaturityExcludingAccruedInterestAfterAllowanceForCreditLoss Debt securities held to maturity 7,933,000,000
us-gaap_BankOwnedLifeInsurance Bank-owned life insurance 3,441,000,000
us-gaap_Assets TOTAL ASSETS 226,351,000,000

Selecting on the standardized concept and taking the first row reports total assets as $7.9B instead of $226.4B.

No network needed:

from edgar.xbrl.standardization.core import MappingStore
MappingStore().get_standard_concept('us-gaap_BankOwnedLifeInsurance')   # 'Total Assets'
MappingStore().get_standard_concept('ifrs-full_IntangibleAssetsAndGoodwill')  # 'Total Non-Current Assets'

edgar/xbrl/standardization/reverse_index.py. 53 index entries sit in section: 'Totals' with is_total: True. Most are line items:

Total Assets            Assets, AssetsNet, ifrs:Assets
                        + AccruedInvestmentIncomeReceivable, BankOwnedLifeInsurance,
                          CashCashEquivalentsAndFederalFundsSold, FederalFundsSold,
                          FederalHomeLoanBankStock, InvestmentOwnedAtFairValue,
                          DebtSecuritiesHeldToMaturityExcludingAccruedInterest...

Total Liabilities       Liabilities, ifrs:Liabilities
                        + AdvancesFromFederalHomeLoanBanks, DueToRelatedPartiesCurrentAndNoncurrent,
                          ManagementFeePayable, OtherBorrowings, SecuredDebt,
                          SecuritiesSoldUnderAgreementsToRepurchase, SubordinatedDebt,
                          UnearnedPremiums, WarrantLiability,
                          SharesSubjectToMandatoryRedemptionSettlementTermsFairValueOfShares

Total Current Liabilities   LiabilitiesCurrent, CurrentLiabilities
                        + CurrentBorrowingsAndCurrentPortionOfNoncurrentBorrowings,
                          CurrentDerivativeFinancialLiabilities, CurrentPortionOfLongtermBorrowings,
                          CurrentProvisions, CurrentTaxLiabilitiesCurrent,
                          LiabilitiesIncludedInDisposalGroupsClassifiedAsHeldForSale,
                          OtherCurrentFinancialLiabilities, TemporaryEquityLiquidationPreference,
                          TradeAndOtherCurrentPayablesToRelatedParties

Total Current Assets    AssetsCurrent, CurrentAssets
                        + CurrentDerivativeFinancialAssets, CurrentPrepaymentsAndOtherCurrentAssets,
                          CurrentTaxAssetsCurrent, CurrentTradeReceivables, OtherCurrentReceivables,
                          CurrentAssetsOtherThanAssetsOrDisposalGroups...

Total Non-Current Assets    AssetsNoncurrent, NoncurrentAssets, ifrs:NoncurrentAssets
                        + IntangibleAssetsAndGoodwill, NoncurrentDerivativeFinancialAssets

The concentration is in bank and insurer line items (federal funds sold, FHLB stock, BOLI, unearned premiums, subordinated debt), which is why it doesn't show on a typical industrial filer. us-gaap_Goodwill maps correctly to Goodwill; the mis-mapped set is specific.

Separately, _normalize_tag (reverse_index.py:188) strips ifrs-full_ / ifrs-full: before lookup, so IFRS tags resolve against the GAAP index by bare name. On VEON's 20-F, ifrs-full_IntangibleAssetsAndGoodwill and ifrs-full_CurrentProvisions are labeled Total Non-Current Assets and Total Current Liabilities. VEON has 24 of 111 rows standardized, Klarna 16 of 81, so IFRS coverage is thin to begin with, the wrong mappings are the part that's actively misleading rather than just missing.

Random sample of 60 tickers from get_cik_tickers(), checking Assets == Liabilities + Equity per rendered period. The identity holds everywhere (0 mismatches over 98 periods, plus 49 large caps) because it reads raw concepts, the defect is only in the standardization layer.

Is gaap_taxonomy_mapping.csv the right place to fix this, or the index generation? The section: 'Totals' bucket looks like it's doing double duty, "this tag belongs to the totals region of the balance sheet" vs "this tag is the total", and is_total: True on a component is what makes it selectable as one. If the intended fix is in the source CSV I can't regenerate the index from the repo, since data/xbrl-mappings/ isn't checked in.

Happy to send a PR once you've said which layer it belongs in. A cheap guard in the meantime would be refusing is_total: True for a tag whose standard_tags is a *Total concept it isn't the canonical tag for, but that's a patch on the output.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions