DEV Community

Cristian Lungu
Cristian Lungu

Posted on

I built an insider-conviction screener for my watchlist in ~40 lines of Python (SEC Form 4)

Corporate insiders — CEOs, CFOs, directors — have to report every time they buy or sell their own company's stock on SEC Form 4, within two business days. Insider buying on the open market is one of the few market signals that's both public and hard to fake: people rarely put their own cash into their own stock unless they mean it.

The problem: Form 4 is filed as XML, thousands of times a week. Reading it by hand is hopeless, and parsing the XML yourself is a weekend gone.

So I built a small screener that ranks my watchlist by insider conviction. Here's the whole thing.

Disclosure: I use my own API for this (SEC Intel). The Form 4 parsing + scoring is the tedious part it removes; the screener logic below is yours to keep.

The idea

  1. For each ticker on my watchlist, pull the parsed Form 4 summary.
  2. Read the insider-conviction score (0–100) — it weighs how many insiders bought, whether the C-suite is involved, and buys vs. sells.
  3. Rank so the real buying floats to the top.

The code

import requests

HOST = "sec-intel-filings-financials-insider-trades.p.rapidapi.com"
HEADERS = {"X-RapidAPI-Key": "YOUR_KEY", "X-RapidAPI-Host": HOST}

WATCHLIST = ["NVDA", "AAPL", "MSFT", "TSLA", "AMD", "META", "AMZN", "XOM"]

def get(path, **params):
    r = requests.get(f"https://{HOST}{path}", params=params, headers=HEADERS, timeout=30)
    r.raise_for_status()
    return r.json()

rows = []
for ticker in WATCHLIST:
    summary = get("/insider", query=ticker)["summary"]
    rows.append((summary["conviction_score"], summary["signal"], ticker, summary["net_usd"]))

# rank: strongest insider conviction first
for score, signal, ticker, net in sorted(rows, reverse=True):
    print(f"{ticker:6} conviction {score:>3}  {signal:8}  net ${net:,.0f}")
Enter fullscreen mode Exit fullscreen mode

Live output when I ran it:

TSLA   conviction  70  bullish   net $879,654,993
XOM    conviction   0  neutral   net $0
AAPL   conviction   0  bearish   net $-111,739,341
MSFT   conviction   0  bearish   net $-2,843,905
META   conviction   0  bearish   net $-10,063,533
AMZN   conviction   0  bearish   net $-51,643,529
AMD    conviction   0  bearish   net $-167,076,160
NVDA   conviction   0  bearish   net $-410,446,401
Enter fullscreen mode Exit fullscreen mode

One insider put ~$880M into TSLA on the open market while the rest of mega-cap tech shows insiders selling. That's the whole point: the buy is the rare signal, and it floats straight to the top. No XML, no CIK lookups, no Form 4 field decoding.

Make it an alert

Wrap it in a loop and ping yourself when conviction crosses your threshold — Slack, Telegram, email, whatever:

def scan_and_alert(threshold=50):
    for ticker in WATCHLIST:
        s = get("/insider", query=ticker)["summary"]
        if s["conviction_score"] >= threshold:
            notify(f"Insider buying: {ticker} ({s['conviction_score']}/100, {s['signal']})")
Enter fullscreen mode Exit fullscreen mode

Bonus: want the whole market instead of a watchlist? GET /insider-signals returns the newest open-market insider buys across all filers (Form 4 code P), sorted by size.

Feed it to an agent instead

The same JSON drops straight into a prompt or a tool call — or connect the API as an MCP server (Claude Desktop, Cursor, VS Code) and just ask "any strong insider buying on my watchlist today?". The data comes back structured, so the model doesn't guess.

Notes on using this responsibly

Insider buying is a signal, not a crystal ball — people buy for many reasons, and this isn't investment advice. But as a filter to surface where informed people put their own money, a conviction-ranked screen beats scrolling filings by hand.

Try it

What would you screen for? Drop your watchlist + threshold in the comments.

Top comments (0)