fix(sec): keep the market-cap fallback through an amendment merge
Found in review. _merge_amendments rebuilds a period from _MERGED_FIELDS + _CARRIED_FIELDS alone, so a column in neither list is absent from the merged row, not just stale — and callers read it with getattr(..., None), which silently yields None. weighted_avg_diluted_shares was never added when the market-cap fallback landed (_SNAPSHOT_COLS in the importer was updated, its counterpart in the derivation was not). The failure needed both of this branch's fixes at once: a multi-class issuer with a partial amendment on its latest period (META with a Part-III-only 10-K/A) would silently lose market cap and FCF yield again. Adds the field, a regression test for that case, and a guard test asserting the merge/carry lists cover every SnapshotRow field, so the next column added fails loudly rather than losing data quietly. Confirmed the guard catches the original bug. Also from review: - Expose pe_caveat in the valuation payload, so a P/E suppressed by split contamination says why instead of looking like missing data (the caveat was set but never read). - no_xbrl_filings now names both causes; the old text advised pinning a CIK override, which is wrong for a genuine new registrant that simply has not filed yet and clears itself. - Document that fiscalYearEnd is the issuer's current calendar, so a fiscal- year-end change degrades old periods (fallback/newest-wins), not current ones. - Parser-level tests for _select_weighted_avg_shares (shortest-span-wins and concept priority), which only had derivation-level coverage. 823 unit tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -463,3 +463,34 @@ def test_diluted_still_wins_over_basic_when_both_present():
|
||||
datetime(2026, 5, 1, 10, tzinfo=UTC), "10-Q")}
|
||||
res = parse_snapshots(companyfacts, filings, {"X"}, fiscal_year_end="1231")
|
||||
assert res.rows[0].diluted_eps == 1.36
|
||||
|
||||
|
||||
def test_weighted_average_shares_prefers_the_shortest_span():
|
||||
# A 10-Q carries both the quarter's average and the YTD one. The shorter
|
||||
# window sits closer to the current count, which is what market cap wants.
|
||||
companyfacts = {
|
||||
"cik": 1326801,
|
||||
"facts": {"us-gaap": {"WeightedAverageNumberOfDilutedSharesOutstanding": {
|
||||
"units": {"shares": [
|
||||
_dur("2026-01-01", "2026-09-30", 2_600_000_000, "X", fp="Q3"), # YTD
|
||||
_dur("2026-07-01", "2026-09-30", 2_564_000_000, "X", fp="Q3"), # quarter
|
||||
]}
|
||||
}}},
|
||||
}
|
||||
filings = {"X": FilingMeta(date(2026, 9, 30), date(2026, 11, 1),
|
||||
datetime(2026, 11, 1, 10, tzinfo=UTC), "10-Q")}
|
||||
res = parse_snapshots(companyfacts, filings, {"X"}, fiscal_year_end="1231")
|
||||
assert res.rows[0].weighted_avg_diluted_shares == 2_564_000_000
|
||||
|
||||
|
||||
def test_weighted_average_shares_falls_back_to_the_basic_and_diluted_concept():
|
||||
companyfacts = {
|
||||
"cik": 1326801,
|
||||
"facts": {"us-gaap": {"WeightedAverageNumberOfSharesOutstandingBasicAndDiluted": {
|
||||
"units": {"shares": [_dur("2026-07-01", "2026-09-30", 500_000, "X", fp="Q3")]}
|
||||
}}},
|
||||
}
|
||||
filings = {"X": FilingMeta(date(2026, 9, 30), date(2026, 11, 1),
|
||||
datetime(2026, 11, 1, 10, tzinfo=UTC), "10-Q")}
|
||||
res = parse_snapshots(companyfacts, filings, {"X"}, fiscal_year_end="1231")
|
||||
assert res.rows[0].weighted_avg_diluted_shares == 500_000
|
||||
|
||||
Reference in New Issue
Block a user