I Built a Tool That Scores Congressional Trades for Suspicious Timing — and Found Some Interesting Patterns
Politicians Trade Suspiciously Well. Let's Quantify It.
Members of Congress are required to disclose stock trades but face minimal enforcement on insider trading rules. Academic research has repeatedly shown that congressional portfolios outperform the market. The question isn't whether they trade well — it's whether we can score how suspicious each trade is.
I built a pipeline that cross-references congressional trading disclosures with four other data sources through one API, then scores each trade on a 100-point suspicion scale. The results are... interesting.
The Thesis
If a politician buys a stock 3 days before a blowout earnings report, while sitting on the oversight committee for that sector, and while the company is actively lobbying Congress — that's probably not a coincidence. Each of those signals alone might be noise. But when they stack up, the pattern becomes hard to ignore.
The scoring model uses five dimensions:
| Component | Max Points | What it measures |
|---|---|---|
| Earnings Proximity | 30 | How close the trade is to an earnings announcement |
| SEC Filing Proximity | 25 | How close to a material filing (8-K scores highest) |
| Committee Relevance | 20 | Whether the politician sits on a relevant oversight committee |
| Lobbying Activity | 15 | Whether the company actively lobbies Congress |
| News Proximity | 10 | Whether material news broke near the trade date |
Ratings: HIGHLY SUSPICIOUS (70+), SUSPICIOUS (50+), NOTABLE (30+), LOW (15+), UNREMARKABLE (under 15).
Step 1: Pull Congressional Trades
Start with a ticker. Who in Congress has been trading NVIDIA?
curl "https://api.gettrawl.com/api/congress-trading/search?ticker=NVDA"Step 2: Check Earnings Timing
Now check when earnings were announced. A trade within 3 days of earnings scores 30 points. Within a week, 20 points.
curl "https://api.gettrawl.com/api/earnings/search?ticker=NVDA"Step 3: Cross-Reference Lobbying
If the company is actively lobbying Congress while a legislator trades the stock, that adds 10-15 points to the suspicion score.
curl "https://api.gettrawl.com/api/lobbying/search?client_name=NVIDIA"The Scoring Algorithm
The suspicion score is the sum of five components, each computed independently:
Earnings Proximity (0-30 points): The most heavily weighted signal. A trade within 3 days before earnings scores the maximum 30. The logic: if you buy right before a beat, you either got lucky or you knew something. Trades 4-7 days out score 20, and 8-14 days score 10. Post-earnings trades within 7 days score 5 (possible leak of next-quarter guidance).
SEC Filing Proximity (0-25 points): 8-K filings are event-driven disclosures — mergers, leadership changes, material events. A trade within 5 days of an 8-K scores 25 if it came before the filing, 20 if after. Other filing types within 5 days score 10.
Committee Relevance (0-20 points): If the politician sits on a committee with oversight of the traded company's sector — Financial Services, Commerce, Armed Services, Intelligence — that's 20 points. This is the "they regulate what they trade" signal.
Lobbying Activity (0-15 points): Companies with 5+ lobbying records score 15 points. 1-4 records score 10. The thesis: if a company is spending money to influence policy, and a legislator is trading that stock, the information asymmetry is highest.
News Proximity (0-10 points): Material news within 3 days of a trade scores 10 points. Within 7 days, 5 points. This captures "did they trade around market-moving events?"
The Full Pipeline
from trawl import TrawlClient
client = TrawlClient()
def score_trade(trade, earnings, filings, lobbying, news):
"""Score a single congressional trade on 0-100 suspicion scale."""
score = 0
trade_date = trade.transaction_date
# Earnings proximity (0-30)
for event in earnings:
days = (event.date - trade_date).days
if 0 < days <= 3:
score += 30; break
elif 0 < days <= 7:
score += 20; break
elif 0 < days <= 14:
score += 10; break
# SEC filing proximity (0-25)
for filing in filings:
days = abs((filing.date - trade_date).days)
if "8-K" in filing.form_type and days <= 5:
score += 25; break
elif days <= 5:
score += 10; break
# Lobbying activity (0-15)
if len(lobbying) >= 5:
score += 15
elif len(lobbying) >= 1:
score += 10
return score
# Run it
ticker = "NVDA"
trades = client.congress_trading.search(ticker=ticker)
earnings = client.earnings.search(ticker=ticker)
filings = client.filings.search(ticker=ticker)
lobbying = client.lobbying.search(client_name="NVIDIA")
news = client.news.search(q=ticker)
for trade in trades.results:
suspicion = score_trade(
trade, earnings.results, filings.results,
lobbying.results, news.results
)
label = (
"HIGHLY SUSPICIOUS" if suspicion >= 70 else
"SUSPICIOUS" if suspicion >= 50 else
"NOTABLE" if suspicion >= 30 else
"LOW" if suspicion >= 15 else
"UNREMARKABLE"
)
print(f"{trade.politician}: {suspicion}/100 ({label})")
print(f" {trade.type} {ticker} on {trade.transaction_date}")
print(f" Amount: {trade.amount}")
Why This Matters
The public data is all there — on Senate.gov, on EDGAR, on the LDA database. But nobody has time to manually cross-reference five databases for every trade. This pipeline runs in under a minute and surfaces patterns that would take a researcher days to find.
The scoring is deliberately transparent and deterministic. No black-box ML — just proximity math that anyone can audit. The point isn't to accuse anyone of insider trading. It's to surface the trades that deserve a closer look.
Non-Code Options
You don't need Python to run this analysis. Trawl's MCP server works inside Claude Desktop:
"Cross-reference NVDA congressional trades with earnings dates, SEC filings, and lobbying activity. Score each trade for suspicious timing."
Claude pulls from all five sources, runs the proximity checks, and ranks the trades. You review the output, not the code.
Source Code
The full implementation with Rich terminal output, CLI argument parsing, and comprehensive scoring is available on GitHub:
github.com/trawlhq/examples/tree/master/congress-frontrunner