feat(sec): capture the Form 144 filer CIK and 10b5-1 plan adoption date - #4411
Merged
Conversation
A Form 144 says an affiliate intends to sell. Whether they actually sold is only knowable by joining the notice to that person's Form 4 executions, and nothing could do that: the parser stored the seller as free text and dropped the identity, so matching fell back to names and resolved barely half the corpus. EDGAR does carry the identity, in headerData/filerInfo/filer/filerCredentials, and it is the same CIK the person's Forms 3/4/5 are filed under. Storing it verbatim makes the join exact against InsiderOwner.OwnerCik, which keeps the Form 4 value in the same zero-padded shape. A test pins that padding, because trimming it would make every join miss silently rather than fail loudly. Also captures the Rule 10b5-1 plan adoption date from noticeSignature. The 10b5-1 flag exists only on InsiderTransaction, so an executed notice can borrow it from the matching transaction while a never-executed notice cannot. The notice's own plan date is the only pre-arranged-versus-discretionary signal available for the cohort where it matters most. Both columns are nullable and backfilled by Form144FilerCikBackfillWorker, which re-reads each notice's XML from EDGAR. Selection is driven by FilerCik being null, so the run drains, terminates and resumes; documents EDGAR will not serve are parked after three attempts so one bad filing cannot stall the backlog. It shares the worker process's SEC rate limiter and starts on a longer stagger than the insider reprocess so the two do not split the request budget. Migration is additive only: two nullable columns and one index.
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.
Problem
A Form 144 announces that an affiliate intends to sell. Whether the sale actually happened is only knowable by joining the notice to that person's Form 4 executions, and nothing in the codebase could do that.
The parser stored the seller as free text and dropped the identity entirely, with the model comment asserting that "Form 144 XML carries the seller's name but not a CIK". That is not correct. The CIK is in the submission header, and it is the same CIK the person's Forms 3/4/5 are filed under.
Falling back to name matching resolves about 55% of the corpus, and the unresolved remainder is not missing at random, so any analysis built on it is biased before it starts.
What changed
Form144Filinggains two nullable columns:FilerCik, fromheaderData/filerInfo/filer/filerCredentials/cik. Stored verbatim, because EDGAR zero-pads to ten characters on both Form 4 and Form 144, andInsiderOwner.OwnerCikkeeps the Form 4 value in exactly that shape. The two therefore compare without normalisation. A test pins the padding: trimming it would make every join miss silently rather than fail loudly.PlanAdoptionDate, fromnoticeSignature/planAdoptionDates. The 10b5-1 flag exists only onInsiderTransaction, so an executed notice can borrow it from the matching transaction while a never-executed notice has nothing to borrow from. The notice's own plan date is the only pre-arranged-versus-discretionary signal available for that cohort.Form144FilerCikBackfillWorkerfills history by re-reading each notice's XML. Selection is driven byFilerCik IS NULL, so the run drains, terminates and resumes. Documents EDGAR will not serve are parked after three attempts so one bad filing cannot stall the backlog, and a batch that makes no progress at all ends the cycle rather than looping on the same rows. It runs in the worker process to share the single SEC rate limiter, on a longer stagger than the insider reprocess so the two do not split the request budget.Verification
The identity claim was checked against EDGAR directly before writing any code:
form.idx, once under the issuer CIK and once under the natural person's. Across 17 quarters, 122,720 of 122,723 distinct accessions carry exactly two rows.filerCredentials/cikinside the notice XML in 20 of 20 cases, across three different filing agents.Tests: 10 new, covering agent-filed and self-filed shapes, the zero-padding invariant, missing header data, joint filings with several
<filer>elements, plan-date capture including the several-dates and none cases, agreement between the processor and the backfill parse paths, and malformed XML. All 57 Form 144 tests pass.Migration
AddForm144FilerCikAndPlanAdoptionDateis additive only, two nullable columns and one index, so it is invisible to running code and safe under a migrate-before-rollout deploy.Not in this PR
FilerCikis deliberately not exposed on any DTO, MCP tool or portal surface. It is an internal identity key, and putting it on the public wire is a separate decision.