Insider buying is one of the most watched signals in the market. When executives spend their own money to buy their company’s stock, it often signals confidence. Here’s how to track it systematically.
Why Track Insider Buying?
Insiders—CEOs, CFOs, directors, and major shareholders—have deep knowledge of their companies. They know about:
- Upcoming product launches
- Contract negotiations
- Financial performance trends
- Strategic initiatives
While they can’t trade on material non-public information, their transactions can still provide valuable signals.
The Asymmetry Principle
Insiders sell for many reasons:
- Diversification
- Tax obligations
- Personal expenses
- Pre-planned 10b5-1 plans
But they buy for one reason: They believe the stock will go up.
This asymmetry makes insider purchases far more informative than sales.
Setting Up Insider Tracking
First, install the FinBrain Python SDK:
pip install finbrain-python pandasBasic Insider Data Retrieval
from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
# Get insider transactions for a tickerdf = fb.insider_transactions.ticker("S&P 500", "AAPL", as_dataframe=True)
print(df.head())# insiderTradings relationship transaction shares cost USDValue# date# 2024-01-15 Tim Cook Chief Executive Officer Sale 50000 185.50 9275000
# Filter for purchases onlypurchases = df[df["transaction"].str.contains("Buy", na=False)]print(f"\nTotal purchases: {len(purchases)}")print(purchases[["insiderTradings", "relationship", "USDValue"]])Filtering for Meaningful Signals
Not all insider purchases are equal. Here’s how to filter for the most significant ones.
1. Filter by Transaction Size
Small purchases may be token gestures. Focus on significant positions:
def get_significant_purchases(df, min_value=100000): """Filter for purchases above minimum value""" purchases = df[df["transaction"].str.contains("Buy", na=False)] significant = purchases[purchases["USDValue"] >= min_value] return significant.sort_values("USDValue", ascending=False)
# Get purchases over $100Ksignificant = get_significant_purchases(df, min_value=100000)print(f"Significant purchases: {len(significant)}")2. Focus on C-Suite Executives
CEOs, CFOs, and other top executives have the deepest insight:
C_SUITE = [ "chief executive", "ceo", "chief financial", "cfo", "chief operating", "coo", "president", "chairman"]
def get_c_suite_purchases(df): """Filter for C-suite executive purchases""" purchases = df[df["transaction"].str.contains("Buy", na=False)]
pattern = "|".join(C_SUITE) c_suite = purchases[ purchases["relationship"].str.lower().str.contains(pattern, na=False) ] return c_suite
c_suite_buys = get_c_suite_purchases(df)print(f"C-suite purchases: {len(c_suite_buys)}")3. Detect Cluster Buying
Multiple insiders buying within a short period is a strong signal:
from datetime import timedelta
def detect_cluster_buying(df, window_days=14, min_buyers=2): """Find periods where multiple insiders bought""" purchases = df[df["transaction"].str.contains("Buy", na=False)]
if purchases.empty: return []
clusters = [] for date in purchases.index.unique(): window_end = date + timedelta(days=window_days) window = purchases[(purchases.index >= date) & (purchases.index <= window_end)]
unique_buyers = window["insiderTradings"].nunique() if unique_buyers >= min_buyers: clusters.append({ "start_date": date, "buyers": list(window["insiderTradings"].unique()), "total_value": window["USDValue"].sum() })
return clusters
clusters = detect_cluster_buying(df)for c in clusters: print(f"Cluster on {c['start_date'].strftime('%Y-%m-%d')}: {len(c['buyers'])} buyers, ${c['total_value']:,.0f}")Building a Multi-Stock Scanner
Scan your watchlist for insider activity:
def scan_insider_activity(tickers, market="S&P 500", min_value=50000): """Scan multiple tickers for recent insider buying""" fb = FinBrainClient(api_key="YOUR_API_KEY") results = []
for ticker in tickers: try: df = fb.insider_transactions.ticker(market, ticker, as_dataframe=True)
if df.empty: continue
# Filter for purchases purchases = df[df["transaction"].str.contains("Buy", na=False)] significant = purchases[purchases["USDValue"] >= min_value]
if not significant.empty: results.append({ "ticker": ticker, "purchase_count": len(significant), "total_value": significant["USDValue"].sum(), "unique_buyers": significant["insiderTradings"].nunique(), "latest_date": significant.index.max() })
except Exception as e: continue
# Sort by total value return sorted(results, key=lambda x: x["total_value"], reverse=True)
# Scan tech stockstickers = ["AAPL", "MSFT", "GOOGL", "AMZN", "NVDA", "META", "TSLA"]results = scan_insider_activity(tickers)
print("=== Insider Buying Scanner ===\n")for r in results: print(f"{r['ticker']}: ${r['total_value']:,.0f} from {r['unique_buyers']} buyers")Creating Alerts
Set up a simple alert system for notable insider activity:
def generate_alerts(results, value_threshold=500000, buyer_threshold=2): """Generate alerts for notable insider activity""" alerts = []
for r in results: # High value alert if r["total_value"] >= value_threshold: alerts.append({ "type": "HIGH_VALUE", "ticker": r["ticker"], "message": f"${r['total_value']:,.0f} in insider purchases" })
# Multiple buyers alert if r["unique_buyers"] >= buyer_threshold: alerts.append({ "type": "CLUSTER", "ticker": r["ticker"], "message": f"{r['unique_buyers']} different insiders buying" })
return alerts
alerts = generate_alerts(results)for alert in alerts: print(f"[{alert['type']}] {alert['ticker']}: {alert['message']}")Complete Insider Tracking Script
Here’s a complete script you can run daily:
from finbrain import FinBrainClientfrom datetime import datetime, timedelta
class InsiderTracker: def __init__(self, api_key): self.fb = FinBrainClient(api_key=api_key) self.c_suite_keywords = [ "chief executive", "ceo", "chief financial", "cfo", "chief operating", "coo", "president", "chairman", "director" ]
def is_c_suite(self, relationship): rel_lower = str(relationship).lower() return any(kw in rel_lower for kw in self.c_suite_keywords)
def analyze_ticker(self, market, ticker, days=30, min_value=50000): """Analyze insider activity for a single ticker""" try: df = self.fb.insider_transactions.ticker(market, ticker, as_dataframe=True) except: return None
if df.empty: return None
# Filter recent cutoff = datetime.now() - timedelta(days=days) recent = df[df.index >= cutoff]
if recent.empty: return None
# Purchases only purchases = recent[recent["transaction"].str.contains("Buy", na=False)] significant = purchases[purchases["USDValue"] >= min_value]
if significant.empty: return None
# C-suite purchases c_suite = significant[significant["relationship"].apply(self.is_c_suite)]
return { "ticker": ticker, "total_purchases": len(significant), "total_value": significant["USDValue"].sum(), "c_suite_purchases": len(c_suite), "unique_buyers": significant["insiderTradings"].nunique(), "top_buyer": significant.loc[significant["USDValue"].idxmax(), "insiderTradings"], "top_value": significant["USDValue"].max() }
def scan(self, tickers, market="S&P 500"): """Scan multiple tickers""" results = [] for ticker in tickers: result = self.analyze_ticker(market, ticker) if result: results.append(result) return sorted(results, key=lambda x: x["total_value"], reverse=True)
# Run the trackertracker = InsiderTracker(api_key="YOUR_API_KEY")
watchlist = [ "AAPL", "MSFT", "GOOGL", "AMZN", "NVDA", "META", "TSLA", "JPM", "BAC", "GS", "V", "MA", "JNJ", "PFE", "UNH", "ABBV"]
print(f"Scanning {len(watchlist)} stocks for insider buying...\n")results = tracker.scan(watchlist)
print("=== INSIDER BUYING DETECTED ===\n")for r in results: print(f"{r['ticker']}") print(f" Total: ${r['total_value']:,.0f} from {r['unique_buyers']} buyers") print(f" C-Suite purchases: {r['c_suite_purchases']}") print(f" Top buyer: {r['top_buyer']} (${r['top_value']:,.0f})") print()Key Takeaways
- Focus on purchases, not sales—the asymmetry principle
- Filter by transaction size ($100K+ is meaningful)
- Weight C-suite purchases more heavily
- Cluster buying (multiple insiders) is a strong signal
- Scan regularly and set up alerts for notable activity
Start tracking insider buying with the Insider Transactions Dataset and API Reference.