Proposed Sale Tracker
Monitor Form 144 filings to spot planned insider sales before they happen.
In this tutorial
What You'll Build
A command-line Python script that monitors SEC Form 144 filings to spot planned insider sales before they happen. Form 144 is the "Notice of Proposed Sale" that corporate insiders must file with the SEC when they intend to sell restricted or control securities. Unlike Form 4, which reports trades that have already occurred, Form 144 gives you advance notice -- the insider is declaring their intention to sell, but hasn't done it yet.
The script will:
- Fetch recent proposed sale filings from the stockdata.dev API
- Display a formatted table with filing date, ticker, person, title, shares, estimated value, approximate sale date, and exchange
- Sort results by estimated value descending to surface the largest planned sales
- Support filtering by ticker or time range
Prerequisites
- Python 3.7 or newer
- The
requestslibrary (pip install requests) - A stockdata.dev API key (get one free)
The Code
Create a file called proposed_sales.py and paste the following:
import argparse import requests API_KEY = "your_api_key_here" BASE_URL = "https://api.stockdata.dev/v1" def fetch_proposed_sales(ticker=None, days=7): """Fetch proposed sale filings from the API.""" headers = {"X-API-Key": API_KEY} url = f"{BASE_URL}/proposed-sales" params = {"days": days} if ticker: params["ticker"] = ticker.upper() resp = requests.get(url, headers=headers, params=params) resp.raise_for_status() return resp.json()["filings"] def format_value(value): """Format a dollar value with commas.""" if value >= 1_000_000: return f"${value / 1_000_000:.1f}M" if value >= 1_000: return f"${value:,.0f}" return f"${value:,.2f}" def print_sales(filings, title): """Print proposed sales as a formatted table.""" if not filings: print("No proposed sales found.") return # Sort by estimated value descending filings.sort(key=lambda f: f.get("aggregate_market_value", 0), reverse=True) print(f"\n{title}") print("=" * 110) print( f"{'Filed':12}" f"{'Ticker':8}" f"{'Person':22}" f"{'Title':18}" f"{'Shares':>10}" f"{'Est. Value':>14}" f"{'Sale Date':>12}" f"{'Exchange':>10}" ) print("─" * 110) for f in filings: person = f["reporting_person"][:21] title_str = f.get("title", "")[:17] shares = f"{f['shares_to_sell']:>,.0f}" value = format_value(f.get("aggregate_market_value", 0)) sale_date = f.get("approximate_sale_date", "") exchange = f.get("exchange", "") print( f"{f['filing_date']:12}" f"{f['ticker']:8}" f"{person:22}" f"{title_str:18}" f"{shares:>10}" f"{value:>14}" f"{sale_date:>12}" f"{exchange:>10}" ) print(f"\nTotal: {len(filings)} proposed sales") def main(): parser = argparse.ArgumentParser(description="Track proposed insider sales (Form 144)") parser.add_argument("--ticker", help="Filter to a specific company") parser.add_argument("--days", type=int, default=7, help="Look back N days (default: 7)") args = parser.parse_args() filings = fetch_proposed_sales(ticker=args.ticker, days=args.days) if args.ticker: title = f"Proposed Sales for {args.ticker.upper()} (last {args.days} days)" else: title = f"Proposed Insider Sales (last {args.days} days)" print_sales(filings, title) if __name__ == "__main__": main()
Replace "your_api_key_here" with your actual API key. For production scripts, consider using an environment variable instead of hardcoding the key.
How It Works
When a corporate insider plans to sell restricted or control securities, they must file Form 144 with the SEC. This filing is a notice of intent -- it tells the market that a sale is planned but has not yet happened. The insider has up to 90 days from the filing date to complete the sale, and they are not obligated to sell at all. Think of it as a leading indicator: you see the intention before the action.
Form 144 filings contain several useful fields:
- Reporting person and title -- Who is planning to sell, and what is their role at the company (CEO, Director, VP, etc.). Sales by C-suite executives carry more weight than sales by former employees exercising old options.
- Shares to sell -- The number of shares the insider intends to sell. Compare this to the company's average daily volume to gauge the potential market impact.
- Aggregate market value -- The estimated dollar value of the planned sale based on recent market prices. This is the best single number for ranking the significance of a filing.
- Approximate sale date -- When the insider expects to execute the sale. This can be the filing date itself or a future date.
- Broker -- The brokerage firm handling the sale. Useful for tracking patterns in how insiders route their trades.
Form 144 is often filed on the same day the insider plans to sell, so the "advance notice" window can be very short. However, some filings are made days or weeks ahead, giving you time to research the company before the sale happens. The approximate_sale_date field tells you when to expect the trade.
The key difference between Form 144 and Form 4:
- Form 144 -- Filed before the sale. A declaration of intent. The sale may or may not happen.
- Form 4 -- Filed after the trade. A report of what actually occurred. See the Insider Trade Scanner tutorial for working with Form 4 data.
By monitoring both forms, you can see the full lifecycle of an insider sale: the planned sale via Form 144, then the completed trade via Form 4.
Running It
Scan for all recent proposed sales:
python proposed_sales.py
Example output:
Proposed Insider Sales (last 7 days) ============================================================================================================== Filed Ticker Person Title Shares Est. Value Sale Date Exchange ────────────────────────────────────────────────────────────────────────────────────────────────────────────── 2026-02-18 ORCL Ellison Lawrence J Chairman 500,000 $85.2M 2026-02-20 NYSE 2026-02-17 CRM Benioff Marc CEO 200,000 $52.4M 2026-02-19 NASDAQ 2026-02-16 NFLX Sarandos Ted Co-CEO 50,000 $47.1M 2026-02-18 NASDAQ 2026-02-15 NOW McDermott William President 80,000 $38.9M 2026-02-17 NYSE 2026-02-18 SNSE Peyer James Former Director 950 $25,769 2026-02-18 NASDAQ Total: 5 proposed sales
Filter to a specific company:
python proposed_sales.py --ticker ORCL --days 30
Example output:
Proposed Sales for ORCL (last 30 days) ============================================================================================================== Filed Ticker Person Title Shares Est. Value Sale Date Exchange ────────────────────────────────────────────────────────────────────────────────────────────────────────────── 2026-02-18 ORCL Ellison Lawrence J Chairman 500,000 $85.2M 2026-02-20 NYSE 2026-02-03 ORCL Catz Safra A CEO 150,000 $25.6M 2026-02-05 NYSE 2026-01-22 ORCL Henley Jeffrey O EVP 30,000 $5.1M 2026-01-24 NYSE Total: 3 proposed sales
Look further back to spot patterns:
python proposed_sales.py --days 30
Enhancements
Here are some ways to extend the proposed sale tracker:
- Cross-reference with Form 4 -- After a Form 144 is filed, check whether the corresponding Form 4 sale actually happened. Use the Insider Trade Scanner to look up completed trades by the same person and ticker. If the insider filed Form 144 but never sold, that could be a signal they changed their mind about the stock.
- Volume impact analysis -- Compare the planned share count against the stock's average daily volume to estimate market impact. A proposed sale of 500,000 shares in a stock that trades 2 million shares daily is significant; the same sale in a stock that trades 50 million shares daily is noise.
- Save to CSV -- Add a
--csv output.csvflag to export proposed sales for analysis in Excel or Google Sheets. Use Python's built-incsvmodule. - Email alerts -- Run the script on a schedule with a cron job and send an email when new large proposed sales appear. See the SEC Filing Alert Email tutorial for a notification pattern you can adapt.
- Filter by value -- Add a
--min-valueflag to only show proposed sales above a certain dollar threshold. This helps filter out small, routine sales and focus on the ones that matter. - Amendment tracking -- The API response includes an
is_amendmentfield. Amended filings mean the insider changed the terms of a previously filed Form 144. Track amendments to see if insiders are increasing or decreasing their planned sales.
A Form 144 filing does not guarantee a sale will happen. Insiders can change their plans. Always cross-reference with actual Form 4 trade reports and do your own research before making investment decisions.