diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87e88665f..1cb222fa8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [5.60.0] - 2026-10-02
+
+### Added
+
+- **`get_current_filings()`, `iter_current_filings_pages()` and `get_all_current_filings()` take a `timeout`.** A slow feed page can get 90s without changing the 30s default for every other request, and a timed-out page still gets all 5 attempts where a process-wide 60s timeout allowed one. (GH #1391, bead edgartools-7xtb)
+- **`from edgar import Form4, Schedule13D` now works for the whole ownership family.** Form3, Form4, Form5, Form144, Schedule13D and Schedule13G are top-level names, identical to their subpackage classes and loaded on first use, so `import edgar` time is unchanged (596 ms vs 602 ms). The `filing.obj()` import table in the API docs had four rows that raised ImportError; all thirteen now import, and a test keeps them that way.
+- **A guard that fails when cassette replay stops reaching edgartools' HTTP stack.** A vcrpy/httpx upgrade that breaks interception used to make every cassette test fetch live and still pass. Three fast tests now replay a cassette for a dead address through `get_with_retry` (sync and async); with vcr's httpcore patch removed, all three fail.
+- **A gate that fails a pull request which stages a 6.0 change without updating the upgrade guide.** Deprecations, will-raise warnings and "removed in 6.0" notes must now touch `docs/upgrade/6.0.md` in the same PR; replayed over the 265 commits since the guide existed, it flags the four PRs that shipped staging with no guide entry, including #1201. Adds a PR template with the Definition of Done checklist.
+- **`ReportingPerson.reported()` and `OwnershipComparison.reported_shares_change` tell an unreported ownership figure from a reported 0.** The 5.x fields still hold a placeholder 0 for a figure the filing leaves out and name it in `ReportingPerson.unreported_fields`; `reported()`, `reported_shares_change` and `reported_percent_change` return None for it, as the fields will in 6.0. (GH #1379)
+
+### Fixed
+
+- **Schedule 13 `OwnershipComparison` summed joint filers, while `total_shares` took the largest one.** A fund and its adviser reporting one 10,000,000-share position compared as 20,000,000. The comparison now uses the same max, and the new `aggregation_basis` (`single`, `identical`, `parent_sums_children`, `ambiguous`) says when it may fall short: GAMCO's Nevro 13D is 1,322,950 by max and 2,123,900 by its own Item 5(a). (bead edgartools-qsk4)
+- **BDC portfolios summed above the balance sheet from joint-venture holdings, range facts and subtotals the 6xxb rules missed.** BCSF went from +90.5% to 0.0%, NMFC +24.8% to +0.9%, OBDC +6.4% to 0.0%. FSK's +11.5% was netting: the new `unfunded_commitments_fair_value` (-1,448.1M) is added back, giving +0.3%. DERA `summary_by_company()` gains `total_source` (`filed` or `summed`). (bead edgartools-3vad)
+- **MCP `edgar_fund` silently picked one BDC when a name matched several.** "Golub" returned GOLUB CAPITAL BDC though four Golub BDCs score 99.4-100. A name now resolves only on one match equal to it (ignoring a suffix like Corp or Inc., so "Ares Capital" is still ARES CAPITAL CORP) or a clear top hit; otherwise the tool returns `AMBIGUOUS_BDC` with up to five candidates and their CIKs. (bead edgartools-1c6z)
+- **`Filing.grep(document="EX-10.1")` also searched EX-10.10 through EX-10.19.** In SeeQC's S-1 it returned 378 matches for "agreement" where EX-10.1 holds 133. An exact filename or document type now wins, and a substring match applies only when nothing matches exactly, so `document="EX-10"` still selects every EX-10 exhibit. (bead edgartools-5qqy)
+- **A literal `grep` returned shifted matches after a character whose lowercase is longer.** Positions were found in the lowercased text and applied to the original, so four "İ" before "going concern" returned "g concern dou". Literal matches are now found in the original text, case-insensitively. (bead edgartools-6th2)
+- **`BDCEntity.is_active` would have turned False for BDCs the newest SEC report dropped, though they still file.** Their rows come from the 2025 report, so Ares Capital's activity date stopped at 2025-05-29 and would leave the 18-month window around 2026-11-29. For these 22 BDCs, `is_active` now checks the company's own filings once the report date expires; the new `in_latest_report` flag marks them. (bead edgartools-huul)
+- **MCP `bdc_portfolio` investment records named no borrower.** Every record had only type, fair value, cost and rate because the builder read a `name` field `PortfolioInvestment` does not have. Records now carry `company_name`, `identifier`, principal, PIK rate, spread and percent of net assets, with `None` for a missing figure; MAIN's first record is "MSC Adviser I, LLC", $255.0M. (bead edgartools-rd23)
+- **`NonAccrualResult.num_nonaccrual` reported 0 when a filing gave only a portfolio-level non-accrual figure, or none.** WhiteHorse Finance files $10.6M of non-accrual investments for 2025 with no per-investment detail and showed 0; the count is now `None` unless the filing itemizes, and the new `evidence_level` says which (`investment`, `aggregate`, `none`). (bead edgartools-vblr)
+- **An 8-K with no SIGNATURES heading no longer folds its signature block into the last item.** When the block opens straight with "Pursuant to the requirements of the Securities Exchange Act of 1934 ... caused this report to be signed", that sentence now ends the item. In a 90-document 8-K sample this cleaned the last item of 3 filings and changed nothing else.
+- **Hard-wrapped paragraph text no longer keeps its line breaks** — `HTMLParser` now collapses whitespace runs inside a text node to one space, as a browser does, while `
` and `
` still break lines. Item 7 of Google's FY2004 10-K had about 500 mid-sentence newlines. (#1370) +- **BDC `portfolio_investments()` no longer counts holdings twice.** Company totals, tranche parents and a second schedule's copies of a row were read as holdings, so rows summed 2-48% above the filer's own total on every BDC checked; MAIN's FY2025 10-K summed to $6.30B against a reported $5.52B and now reconciles within 0.2%. Foreign-currency values no longer replace USD ones, and new `reported_total_fair_value` and `reconciliation_gap` show any remaining difference. +- **`TenK` keeps both items of a combined heading such as "Items 1 and 2. Business and Properties".** A TOC link reading "Items 1 and 2" (or "Items 1. and 2.", "Items 7. and 7A.") was dropped, so `tenk.business` was None on Viper, Devon, Freeport and Cheniere. The section is now keyed as the first item, lists both in `Section.covered_items`, and answers for either. Talos no longer shows a 1,311-character "Properties" fragment as Item 2. (GH #1382, #1383) +- **`OwnershipComparison.get_summary()` warnings now point at the caller.** It emitted two `FutureWarning`s attributed to `amendments.py`, so Python showed them once per process wherever it was called from; it now emits one, at the caller's line. A filing with an empty `` now displays its ownership as "not reported" instead of 0, matching the comparison. (GH #1379) +- **`OwnershipComparison.is_liquidating` reported a sale against a Schedule 13D/G filing that gives no share figures.** A 13D/A or 13G/A may omit them and a pre-2025 header-only filing has none, but they were read as 0: an amendment omitting them on the Aadi Bioscience 13D in the test data sold all 4,535,000 shares. The direction flags are now False when the change is unknown, and `shares_change` and `percent_change` warn that they return None in 6.0. (GH #1379) +- **`edgar.ownership.core.is_numeric()` raised `TypeError` for pandas string and nullable numeric columns.** The reported Arnaboldi Form 4 has `UnderlyingShares` of `0 [F1]`; its value calculation now returns `None` instead of raising. Ownership share totals also return `None` for unreadable text rather than raising. (GH #1368, thanks @manantlerio) +- **`EntityFacts.get_total_liabilities()` returned total assets for filers with no standalone liabilities total.** Its last fallback was `LiabilitiesAndStockholdersEquity`, which always equals total assets: NIKE's FY2026 figure came back as $38.41 billion against $23.545 billion of liabilities, and 16 of 51 large US filers were affected. It now returns None there, matching `Financials.get_total_liabilities()`. (GH #1279) +- **`PortfolioInvestment.company_name` returned the instrument (`First Lien`, `Ordinary Shares`) when the filer tagged the company as a member.** On PSEC's FY2026 10-K (`0001287032-26-000269`) 142 of 248 identifiers named an instrument. A member candidate that fills the whole first pipe segment is now the company, not a prefix to strip. (GH #1373) +- **`PortfolioInvestment.company_name` named a schedule heading or sub-sector instead of the company on category-led identifiers.** On CGBD's Q2 2026 10-Q (`0001544206-26-000055`) 348 of 352 positions read `Investment`, `Credit Fund` or a sub-sector such as `Durable`; SLRC and PFX were hit too. Only the axis prefix is stripped now, and leading heading segments are skipped. (GH #1372) +- **`Form4.shares_traded` raised `AttributeError` on a derivative-only filing.** `market_trades` is None when the non-derivative table has no transactions, and the accessor read `.Shares` off it without the None/empty guard its neighbours already had: Vertex's 2020-10-19 Form 4 parsed and returned its derivative transaction but crashed on `shares_traded`, which now reports 0 market trades. (GH #1359) + ## [5.59.1] - 2026-09-26 ### Fixed diff --git a/changelog.d/07lk-12-1-1-ownership-exports.added.md b/changelog.d/07lk-12-1-1-ownership-exports.added.md deleted file mode 100644 index 837da4406..000000000 --- a/changelog.d/07lk-12-1-1-ownership-exports.added.md +++ /dev/null @@ -1 +0,0 @@ -**`from edgar import Form4, Schedule13D` now works for the whole ownership family.** Form3, Form4, Form5, Form144, Schedule13D and Schedule13G are top-level names, identical to their subpackage classes and loaded on first use, so `import edgar` time is unchanged (596 ms vs 602 ms). The `filing.obj()` import table in the API docs had four rows that raised ImportError; all thirteen now import, and a test keeps them that way. diff --git a/changelog.d/07lk-14-upgrade-guide-gate.added.md b/changelog.d/07lk-14-upgrade-guide-gate.added.md deleted file mode 100644 index 5d2722618..000000000 --- a/changelog.d/07lk-14-upgrade-guide-gate.added.md +++ /dev/null @@ -1 +0,0 @@ -**A gate that fails a pull request which stages a 6.0 change without updating the upgrade guide.** Deprecations, will-raise warnings and "removed in 6.0" notes must now touch `docs/upgrade/6.0.md` in the same PR; replayed over the 265 commits since the guide existed, it flags the four PRs that shipped staging with no guide entry, including #1201. Adds a PR template with the Definition of Done checklist. diff --git a/changelog.d/1279-entity-facts.fixed.md b/changelog.d/1279-entity-facts.fixed.md deleted file mode 100644 index 943b206c4..000000000 --- a/changelog.d/1279-entity-facts.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**`EntityFacts.get_total_liabilities()` returned total assets for filers with no standalone liabilities total.** Its last fallback was `LiabilitiesAndStockholdersEquity`, which always equals total assets: NIKE's FY2026 figure came back as $38.41 billion against $23.545 billion of liabilities, and 16 of 51 large US filers were affected. It now returns None there, matching `Financials.get_total_liabilities()`. (GH #1279) diff --git a/changelog.d/1359.fixed.md b/changelog.d/1359.fixed.md deleted file mode 100644 index 36fdec062..000000000 --- a/changelog.d/1359.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**`Form4.shares_traded` raised `AttributeError` on a derivative-only filing.** `market_trades` is None when the non-derivative table has no transactions, and the accessor read `.Shares` off it without the None/empty guard its neighbours already had: Vertex's 2020-10-19 Form 4 parsed and returned its derivative transaction but crashed on `shares_traded`, which now reports 0 market trades. (GH #1359) diff --git a/changelog.d/1368.fixed.md b/changelog.d/1368.fixed.md deleted file mode 100644 index 58a0a1dfa..000000000 --- a/changelog.d/1368.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**`edgar.ownership.core.is_numeric()` raised `TypeError` for pandas string and nullable numeric columns.** The reported Arnaboldi Form 4 has `UnderlyingShares` of `0 [F1]`; its value calculation now returns `None` instead of raising. Ownership share totals also return `None` for unreadable text rather than raising. (GH #1368, thanks @manantlerio) diff --git a/changelog.d/1370.fixed.md b/changelog.d/1370.fixed.md deleted file mode 100644 index be9776a90..000000000 --- a/changelog.d/1370.fixed.md +++ /dev/null @@ -1 +0,0 @@ -- **Hard-wrapped paragraph text no longer keeps its line breaks** — `HTMLParser` now collapses whitespace runs inside a text node to one space, as a browser does, while `
` and `` still break lines. Item 7 of Google's FY2004 10-K had about 500 mid-sentence newlines. (#1370) diff --git a/changelog.d/1372.fixed.md b/changelog.d/1372.fixed.md deleted file mode 100644 index a4dd610b4..000000000 --- a/changelog.d/1372.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**`PortfolioInvestment.company_name` named a schedule heading or sub-sector instead of the company on category-led identifiers.** On CGBD's Q2 2026 10-Q (`0001544206-26-000055`) 348 of 352 positions read `Investment`, `Credit Fund` or a sub-sector such as `Durable`; SLRC and PFX were hit too. Only the axis prefix is stripped now, and leading heading segments are skipped. (GH #1372) diff --git a/changelog.d/1373.fixed.md b/changelog.d/1373.fixed.md deleted file mode 100644 index 6db08602a..000000000 --- a/changelog.d/1373.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**`PortfolioInvestment.company_name` returned the instrument (`First Lien`, `Ordinary Shares`) when the filer tagged the company as a member.** On PSEC's FY2026 10-K (`0001287032-26-000269`) 142 of 248 identifiers named an instrument. A member candidate that fills the whole first pipe segment is now the company, not a prefix to strip. (GH #1373) diff --git a/changelog.d/1379-summary-warning.fixed.md b/changelog.d/1379-summary-warning.fixed.md deleted file mode 100644 index a26aac2ba..000000000 --- a/changelog.d/1379-summary-warning.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**`OwnershipComparison.get_summary()` warnings now point at the caller.** It emitted two `FutureWarning`s attributed to `amendments.py`, so Python showed them once per process wherever it was called from; it now emits one, at the caller's line. A filing with an empty `` now displays its ownership as "not reported" instead of 0, matching the comparison. (GH #1379) diff --git a/changelog.d/1379.added.md b/changelog.d/1379.added.md deleted file mode 100644 index 6b2021648..000000000 --- a/changelog.d/1379.added.md +++ /dev/null @@ -1 +0,0 @@ -**`ReportingPerson.reported()` and `OwnershipComparison.reported_shares_change` tell an unreported ownership figure from a reported 0.** The 5.x fields still hold a placeholder 0 for a figure the filing leaves out and name it in `ReportingPerson.unreported_fields`; `reported()`, `reported_shares_change` and `reported_percent_change` return None for it, as the fields will in 6.0. (GH #1379) diff --git a/changelog.d/1379.fixed.md b/changelog.d/1379.fixed.md deleted file mode 100644 index 81fe26408..000000000 --- a/changelog.d/1379.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**`OwnershipComparison.is_liquidating` reported a sale against a Schedule 13D/G filing that gives no share figures.** A 13D/A or 13G/A may omit them and a pre-2025 header-only filing has none, but they were read as 0: an amendment omitting them on the Aadi Bioscience 13D in the test data sold all 4,535,000 shares. The direction flags are now False when the change is unknown, and `shares_change` and `percent_change` warn that they return None in 6.0. (GH #1379) diff --git a/changelog.d/1382.fixed.md b/changelog.d/1382.fixed.md deleted file mode 100644 index 5bb0f7def..000000000 --- a/changelog.d/1382.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**`TenK` keeps both items of a combined heading such as "Items 1 and 2. Business and Properties".** A TOC link reading "Items 1 and 2" (or "Items 1. and 2.", "Items 7. and 7A.") was dropped, so `tenk.business` was None on Viper, Devon, Freeport and Cheniere. The section is now keyed as the first item, lists both in `Section.covered_items`, and answers for either. Talos no longer shows a 1,311-character "Properties" fragment as Item 2. (GH #1382, #1383) diff --git a/changelog.d/1391.added.md b/changelog.d/1391.added.md deleted file mode 100644 index ac0d23972..000000000 --- a/changelog.d/1391.added.md +++ /dev/null @@ -1 +0,0 @@ -**`get_current_filings()`, `iter_current_filings_pages()` and `get_all_current_filings()` take a `timeout`.** A slow feed page can get 90s without changing the 30s default for every other request, and a timed-out page still gets all 5 attempts where a process-wide 60s timeout allowed one. (GH #1391, bead edgartools-7xtb) diff --git a/changelog.d/1c6z.fixed.md b/changelog.d/1c6z.fixed.md deleted file mode 100644 index c2c679418..000000000 --- a/changelog.d/1c6z.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**MCP `edgar_fund` silently picked one BDC when a name matched several.** "Golub" returned GOLUB CAPITAL BDC though four Golub BDCs score 99.4-100. A name now resolves only on one match equal to it (ignoring a suffix like Corp or Inc., so "Ares Capital" is still ARES CAPITAL CORP) or a clear top hit; otherwise the tool returns `AMBIGUOUS_BDC` with up to five candidates and their CIKs. (bead edgartools-1c6z) diff --git a/changelog.d/243z-vcr-interception-guard.added.md b/changelog.d/243z-vcr-interception-guard.added.md deleted file mode 100644 index 1fa084f4d..000000000 --- a/changelog.d/243z-vcr-interception-guard.added.md +++ /dev/null @@ -1 +0,0 @@ -**A guard that fails when cassette replay stops reaching edgartools' HTTP stack.** A vcrpy/httpx upgrade that breaks interception used to make every cassette test fetch live and still pass. Three fast tests now replay a cassette for a dead address through `get_with_retry` (sync and async); with vcr's httpcore patch removed, all three fail. diff --git a/changelog.d/3vad.fixed.md b/changelog.d/3vad.fixed.md deleted file mode 100644 index 6ecbfca17..000000000 --- a/changelog.d/3vad.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**BDC portfolios summed above the balance sheet from joint-venture holdings, range facts and subtotals the 6xxb rules missed.** BCSF went from +90.5% to 0.0%, NMFC +24.8% to +0.9%, OBDC +6.4% to 0.0%. FSK's +11.5% was netting: the new `unfunded_commitments_fair_value` (-1,448.1M) is added back, giving +0.3%. DERA `summary_by_company()` gains `total_source` (`filed` or `summed`). (bead edgartools-3vad) diff --git a/changelog.d/5qqy.fixed.md b/changelog.d/5qqy.fixed.md deleted file mode 100644 index a544f2fbb..000000000 --- a/changelog.d/5qqy.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**`Filing.grep(document="EX-10.1")` also searched EX-10.10 through EX-10.19.** In SeeQC's S-1 it returned 378 matches for "agreement" where EX-10.1 holds 133. An exact filename or document type now wins, and a substring match applies only when nothing matches exactly, so `document="EX-10"` still selects every EX-10 exhibit. (bead edgartools-5qqy) diff --git a/changelog.d/6th2.fixed.md b/changelog.d/6th2.fixed.md deleted file mode 100644 index f6ae892b0..000000000 --- a/changelog.d/6th2.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**A literal `grep` returned shifted matches after a character whose lowercase is longer.** Positions were found in the lowercased text and applied to the original, so four "İ" before "going concern" returned "g concern dou". Literal matches are now found in the original text, case-insensitively. (bead edgartools-6th2) diff --git a/changelog.d/6xxb.fixed.md b/changelog.d/6xxb.fixed.md deleted file mode 100644 index e4bbb9d4d..000000000 --- a/changelog.d/6xxb.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**BDC `portfolio_investments()` no longer counts holdings twice.** Company totals, tranche parents and a second schedule's copies of a row were read as holdings, so rows summed 2-48% above the filer's own total on every BDC checked; MAIN's FY2025 10-K summed to $6.30B against a reported $5.52B and now reconciles within 0.2%. Foreign-currency values no longer replace USD ones, and new `reported_total_fair_value` and `reconciliation_gap` show any remaining difference. diff --git a/changelog.d/8k-signature-statement.fixed.md b/changelog.d/8k-signature-statement.fixed.md deleted file mode 100644 index 7245b5079..000000000 --- a/changelog.d/8k-signature-statement.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**An 8-K with no SIGNATURES heading no longer folds its signature block into the last item.** When the block opens straight with "Pursuant to the requirements of the Securities Exchange Act of 1934 ... caused this report to be signed", that sentence now ends the item. In a 90-document 8-K sample this cleaned the last item of 3 filings and changed nothing else. diff --git a/changelog.d/huul.fixed.md b/changelog.d/huul.fixed.md deleted file mode 100644 index 81ccf9b0f..000000000 --- a/changelog.d/huul.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**`BDCEntity.is_active` would have turned False for BDCs the newest SEC report dropped, though they still file.** Their rows come from the 2025 report, so Ares Capital's activity date stopped at 2025-05-29 and would leave the 18-month window around 2026-11-29. For these 22 BDCs, `is_active` now checks the company's own filings once the report date expires; the new `in_latest_report` flag marks them. (bead edgartools-huul) diff --git a/changelog.d/qsk4.fixed.md b/changelog.d/qsk4.fixed.md deleted file mode 100644 index 1f3b44d6a..000000000 --- a/changelog.d/qsk4.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**Schedule 13 `OwnershipComparison` summed joint filers, while `total_shares` took the largest one.** A fund and its adviser reporting one 10,000,000-share position compared as 20,000,000. The comparison now uses the same max, and the new `aggregation_basis` (`single`, `identical`, `parent_sums_children`, `ambiguous`) says when it may fall short: GAMCO's Nevro 13D is 1,322,950 by max and 2,123,900 by its own Item 5(a). (bead edgartools-qsk4) diff --git a/changelog.d/rd23.fixed.md b/changelog.d/rd23.fixed.md deleted file mode 100644 index 4ddec8920..000000000 --- a/changelog.d/rd23.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**MCP `bdc_portfolio` investment records named no borrower.** Every record had only type, fair value, cost and rate because the builder read a `name` field `PortfolioInvestment` does not have. Records now carry `company_name`, `identifier`, principal, PIK rate, spread and percent of net assets, with `None` for a missing figure; MAIN's first record is "MSC Adviser I, LLC", $255.0M. (bead edgartools-rd23) diff --git a/changelog.d/vblr.fixed.md b/changelog.d/vblr.fixed.md deleted file mode 100644 index aa22ce338..000000000 --- a/changelog.d/vblr.fixed.md +++ /dev/null @@ -1 +0,0 @@ -**`NonAccrualResult.num_nonaccrual` reported 0 when a filing gave only a portfolio-level non-accrual figure, or none.** WhiteHorse Finance files $10.6M of non-accrual investments for 2025 with no per-investment detail and showed 0; the count is now `None` unless the filing itemizes, and the new `evidence_level` says which (`investment`, `aggregate`, `none`). (bead edgartools-vblr) diff --git a/edgar/__about__.py b/edgar/__about__.py index 4a76da683..b028e56e9 100644 --- a/edgar/__about__.py +++ b/edgar/__about__.py @@ -1,4 +1,4 @@ # SPDX-FileCopyrightText: 2022-present Dwight Gunning # # SPDX-License-Identifier: MIT -__version__ = '5.59.1' +__version__ = '5.60.0'