How to Detect Insider Cluster Buys: A Signal-Building Framework
Cluster buys — multiple distinct insiders purchasing the same stock within a rolling window — are one of the strongest documented insider signals. Here's how to define, detect, and rank them.
A cluster buy is a coordinated, non-coordinated event: three or more distinct insiders — with no legal ability to trade in concert — independently choosing to buy their own company's stock in a short window. It is the closest thing public markets offer to a group of people who know the business best simultaneously voting with their wallets.
Why clusters outperform single insider buys
Single insider buys are noisy. A director topping up a small position tells you very little. But when the CEO, CFO, and two independent directors all buy inside a two-week window, the base rate of that event happening for non-fundamental reasons collapses. The academic literature (Jeng, Metrick, Zeckhauser 2003; Cohen et al. 2012) finds cluster-buy portfolios generate meaningfully stronger post-event drift than single-insider portfolios, particularly in small- and mid-caps where insider information asymmetry is highest.
A working definition of a cluster
There is no regulatory definition of a cluster — you have to build one. A defensible baseline:
- At least 3 distinct insiders (unique CIKs), not 3 filings from the same person.
- Transactions with code P (open-market purchase) only.
- Trade dates within a rolling 14-day window.
- Combined dollar value above a floor (e.g. $250K) to filter symbolic buys.
- At least one filer with an executive or director role, not solely 10% owners.
A minimal detection algorithm
The naive approach — scan every ticker every day and count insiders — works but wastes compute. A better shape: index Form 4 purchases by (ticker, insider_cik, trade_date), then evaluate any ticker touched by a new filing in the last 14 days.
from collections import defaultdict
from datetime import timedelta
def find_clusters(trades, window_days=14, min_insiders=3, min_value=250_000):
by_ticker = defaultdict(list)
for t in trades:
if t["txn_code"] == "P":
by_ticker[t["ticker"]].append(t)
clusters = []
for ticker, ts in by_ticker.items():
ts.sort(key=lambda x: x["txn_date"])
for i, anchor in enumerate(ts):
window_end = anchor["txn_date"] + timedelta(days=window_days)
window = [x for x in ts[i:] if x["txn_date"] <= window_end]
insiders = {x["insider_cik"] for x in window}
value = sum(x["value_usd"] for x in window)
if len(insiders) >= min_insiders and value >= min_value:
clusters.append({"ticker": ticker, "insiders": len(insiders), "value": value, "start": anchor["txn_date"]})
break
return clustersRanking clusters once you have them
Detection is half the problem. Ranking is the other half. Signal quality varies enormously between clusters: five directors buying $50K each is qualitatively different from a CEO and CFO buying $2M each. A useful ranking model weights on:
- Insider role — CEO / CFO trades carry more information than independent directors.
- Absolute dollar value, log-scaled to prevent one large trade from dominating.
- Insider concentration — percent of the insider's existing holdings the trade represents.
- Temporal density — a 3-day cluster is stronger than a 14-day cluster.
- Historical trading pattern — routine buyers get discounted, opportunistic buyers get amplified.
The NexusForm4 API exposes a pre-computed conviction score via /signals/cluster-buys that folds these dimensions together, so you can rank by score rather than reimplement the whole model.
Common pitfalls
- Counting 10b5-1 pre-planned trades — filter to true discretionary buys.
- Double-counting amended filings (Form 4/A) — dedupe by accession number lineage.
- Ignoring share class differences — SEC filings sometimes report multiple classes for one issuer.
- Forgetting look-ahead bias in backtests — use filing_date, not trade_date, for entry.