fix(sec): correct Form 4 date filtering and per-row footnote attribution - #7642
Open
mateosandoval10 wants to merge 1 commit into
Open
Conversation
Two independent correctness bugs in the SEC Form 4 path behind
`equity.ownership.insider_trading`.
1. `get_form_4_urls` discarded every filing when only one date bound was
supplied. `(not start_date or not item.filing_date)` short-circuits to
True whenever `start_date` is None, so the loop skipped all items.
`SecInsiderTradingFetcher.transform_query` only backfills defaults when
both bounds are missing, so a request with just `start_date` (or just
`end_date`) reached the helper with one bound set to None and returned
no URLs, surfacing as "No Form 4 data was returned for {symbol}".
2. `parse_form_4_data` rebound the `footnotes` id-to-text map to a joined
string while iterating transactions, so after the first row carrying a
footnote every later row in the same filing inherited that row's text.
Footnote resolution now goes through `resolve_footnotes`, which reads
the map without mutating it. Building the map also tolerates an empty
`<footnote id="F1"/>` element, which previously raised KeyError.
Adds unit tests covering both bounds, each bound alone, neither bound, and
per-row footnote resolution. No network access required.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request the OpenBB Platform
Description
Two independent correctness bugs in the SEC Form 4 path behind
equity.ownership.insider_trading. Both are inopenbb_platform/providers/sec/openbb_sec/utils/form4.py. No linked issue — found while reading the parser.1. A one-sided date range returns no data
get_form_4_urlsfiltered with:(not start_date or not item.filing_date)short-circuits toTruewheneverstart_dateisNone, so the loopcontinues on every filing and returns an empty list. Theend_dateguard below it has the same shape.This is normally hidden because
SecInsiderTradingFetcher.transform_querybackfills a 120-day window — but only when both bounds are missing:So supplying exactly one bound leaves the other
None, no URLs come back, andget_form_4raisesNo Form 4 data was returned for {symbol}:The guards now test the bound first, so an absent bound simply doesn't filter. The both-bounds path is unchanged.
2. Footnote text leaks from one transaction to the next
parse_form_4_databuildsfootnotesas an id → text map, then rebinds that same name to a joined string inside the per-transaction loop:After the first row that carries footnote references,
footnotesis no longer a dict. Theisinstance(footnotes, str)branches downstream then hand that same string to every remaining row in the filing. A three-transaction Form 4 whose rows reference F1+F2, F3 and F4 returns:Form 4 footnotes carry the qualification that makes a transaction readable — whether a sale was under a Rule 10b5-1 plan, whether shares were withheld for taxes, whether a position is held indirectly through a trust — so attaching the wrong one to a row is misleading rather than merely cosmetic.
Footnote resolution now goes through a small
resolve_footnoteshelper that reads the map without mutating it, replacing four near-duplicate inline blocks. Building the map also tolerates an empty<footnote id="F1"/>element, which previously raisedKeyError: '#text'.Notes for the reviewer
developand survive verbatim intofeature/v5-sec([V5]openbb-sec: Refactor for V5 #7525), so this isn't superseded by the V5 refactor.get_form_4_urlsfiltersstart_dateagainstfiling_datebutend_dateagainstreport_date, and the derivative-table branch never resolves footnotes at all. Both look like separate decisions rather than defects — happy to follow up if you'd like either changed.How has this been tested?
New
openbb_platform/providers/sec/tests/test_form4.py, 12 tests, no network access:get_form_4_urlsacross both bounds, start-only, end-only and neither, plus ISO-string bounds.resolve_footnotesfor a single reference, multiple references, an unknown id, a missing reference and an absent footnote map — asserting the map is not mutated.parse_form_4_dataend to end on a three-transaction ownership document, asserting each row keeps its own footnote, and on a document with an empty footnote element.Full provider suite,
pytest openbb_platform/providers/sec/tests/ -q: 233 passed, 2 failed. The two failures (test_sec_sic_search_fetcher,test_sec_equity_ftd_fetcher) reproduce identically on a clean checkout ofdevelopand are unrelated to this change.ruff checkandblack --checkclean on both files.Checklist
feature/feature-nameorhotfix/hotfix-name.