Jackson Pipe
All projects

Investing Stats · Financial data

Investing Stats

I built a searchable history of SEC insider transactions, with company filters, filing details, and CSV exports.

Investing Stats interface with transaction filters, a buy/sell summary, export links, and a table of filed transactions.
Transaction explorer · September 9, 2026 snapshot. View full-size image.

From filings to transactions

SEC Form 4 filings report changes in company insiders’ ownership. The source is a collection of XML documents; the app turns those documents into records you can search by company, date, transaction type, or value.

I built the importer, storage and query layers, and Next.js interface. The browser, API, and exports all read from the same SQLite database, so a transaction gets the same interpretation in each view.

Making imports repeatable

The importer discovers filings, finds their XML, and extracts transactions. It then adds issuer information where available and saves the records to SQLite.

Transaction IDs prevent retries from inserting duplicate rows. Historical backfills track completed days so an interrupted run can pick up the remaining dates. Concurrent import requests also share the same in-flight work within a server process.

  1. Discover & parse: filing feeds and indexes, ownership XML.
  2. Persist: transactions, issuer information, import progress.
  3. Query: filtered views, company histories, CSV / feeds.
Scheduled imports write to SQLite; the interface and exports read from it.

When a refresh fails

A failed SEC fetch shouldn’t prevent someone from searching records already in the database. I separated refreshes from reads: scheduled imports update the history, while normal page requests use the stored data. Refresh errors appear in the health endpoint.

For deployments that refresh on read, failed attempts trigger a backoff period. That keeps every new visitor from starting another slow request during an upstream outage. An empty database still needs a successful first import before it can serve results.

Parsing missing values

XML numbers can arrive as plain values, nested values, or text inside an object. I added parsing helpers and tests for those shapes, including empty and malformed fields. Missing values stay missing instead of silently becoming zero.

I also checked how issuer data affects older transactions. A current share count can make a historical comparison misleading, so the app suppresses that comparison when the dates don’t line up. Query tests cover parameter binding and search wildcards.

Current scope

The app supports transaction search, company histories, analytics queries, and exports. It runs with SQLite on persistent storage, with no separate database service.

Data freshness depends on the imports, and issuer details aren’t always available. A remaining interface improvement is to make older or incomplete data easier to spot.

Project notes · September 2026

Next projectBrawl Stats →Email me about this project