Regulatory Lens
A unified regulatory intelligence engine: from raw SEC Form ADV and 13F bulk data ingestion, to a SQLite relational store, a local FastMCP tool server, and fine-tuned LLM models.
Investment adviser intelligence lives in separate SEC data universes that never talk to each other. This project brings them together—building a unified ingestion pipeline, a SQLite relational store, a FastMCP tool server, and local AI models trained directly on the dataset.
The Core Fragmentation of SEC Filings
For allocators, compliance teams, and analysts, researching an investment adviser means navigating a fragmented regulatory landscape. The SEC publishes three distinct datasets, each running on separate schedules and in separate formats:
- Form ADV Part 1: Structured registration data (Item 5 assets/clients, Item 9 custody flags, and Item 11 disciplinary yes/no flags).
- Form ADV Part 2A (Brochures): Plain-English narrative PDFs covering fees, conflicts of interest, and detailed disciplinary disclosures (Item 9).
- Form 13F: Quarterly tab-separated (TSV) filings disclosing long US equity holdings for managers managing over $100 million.
Stitching these together manually takes substantial time. Furthermore, none of this data is structured for AI agents (like Claude or Cursor) to query directly. We resolved this by building a unified data engine and local AI Studio.
1. Methodology: Relational Entity Resolution
At the core of the system is a unified relational model built on SQLite (for local-first speed) and Postgres (for production). The primary link between these filings is the Central Registration Depository (CRD) number.
┌────────────────────────────────────────────────────────┐
│ Firms │
├────────────────────────────────────────────────────────┤
│ crd_number (PK) │ legal_name │
│ sec_number │ business_name │
│ total_aum │ office_state │
│ client_types (JSON) │ advisory_activities (JSON) │
└────────────────────────┬───────────────────────────────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Disclosures │ │Private Funds │ │ Firm CIKs │
├──────────────┤ ├──────────────┤ ├──────────────┤
│ crd_number FK│ │crd_number FK │ │crd_number PK │
│ question_item│ │fund_name │ │cik │
│ answer (bool)│ │gross_assets │ └──────┬───────┘
└──────────────┘ └──────────────┘ │
▼
┌──────────────────────┐
│ 13F Submissions │
├──────────────────────┤
│ accession_number (PK)│
│ cik (FK) │
│ crd_number (FK) │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ 13F Holdings │
├──────────────────────┤
│ accession_number (FK)│
│ issuer_name │
│ value_usd │
└──────────────────────┘
Resolving Entity Identifiers
- FilingID to CRD Mapping: Every ADV filing contains a unique
FilingIDmapping to a firm’scrd_number. Across 788,478 historical filings, we determine the latest (maximum)FilingIDfor each of the 49,574 distinct advisers and load only conformed latest records to prevent historical duplication. - CRD-to-CIK Cross-Walk: CIKs (Central Index Keys) are required for 13F filings, while CRDs are used for ADV. By extracting
FilingID -> CRDfrom base filings andFilingID -> CIKfrom specialized CIK files (IA_1D3_CIKandERA_1D3_CIK), we map 5,554 adviser CRDs to their conformed CIKs.
2. Data Collection & Ingestion Pipeline
To ingest the SEC’s bulk data sets politely and robustly, we implemented a custom Python pipeline:
- Rate-Limiting & Compliance: All network crawlers declare a structured User-Agent header (declaring organization name and contact email) to comply with the SEC fair-use policy, and enforce rate limits.
- WAL-Mode Streaming: A single quarter of Form 13F holdings contains over 3.4 million records. To load these under low-memory constraints, the parser streams TSV datasets in 10,000-row chunks using SQLite’s Write-Ahead Logging (WAL) and transactional batch inserts.
- Header Normalization: The SEC’s Form ADV column headers drift slightly between yearly versions (e.g.
5F(2)(c)vs.5F2c). The parser implements a fuzzy match alias dictionary inpipeline/column_map.pyto resolve headers dynamically.
| Dataset | Source Format | Local Records | Raw Size |
|---|---|---|---|
| Form ADV Part 1 | Bulk CSV (Monthly Zip) | 49,574 firms | 1.15 GB |
| ADV Part 2A | Narrative PDFs (ZIP) | 12,000+ files | 563 MB |
| Form 13F | TSV (DERA Dataset) | 6.75M holdings | 700 MB |
3. Building Local AI Training & Multi-Adapter Routing
To automate the review of narrative disciplinary disclosures, we trained a specialized model and integrated it into the workspace:
Instruction-Tuning Dataset
We compiled a dataset of 250 high-quality instruction-tuning pairs by extracting narrative text from firm_brochures.item_9 (covering SEC, state, and civil court disciplinary events) and generating structured summary targets covering:
- What the case/event is.
- Who or what entities were mentioned.
- What penalties were imposed.
QLoRA Fine-Tuning
Using PEFT (Parameter-Efficient Fine-Tuning) and BitsAndBytes, we loaded a base TinyLlama-1.1B chat model in 4-bit NF4 quantization to minimize memory overhead. The specialized adapter was trained locally on our GPU across 3 epochs, outputting LoRA weights to models/tinyllama-adv-drp-qlora/ with a VRAM footprint of just 4.3 GB.
Dynamic PEFT Multi-Adapter Router
To avoid loading multiple large models into GPU memory, we implemented a dynamic router in the FastAPI backend (dashboard/app.py). Using PEFT’s adapter switching capability, we host two specialized adapters on top of a single quantized base model instance:
adv_lora: General Form ADV regulatory questions.drp_lora: Specialized brochure disciplinary summaries (Item 9 DRPs).
During inference, requests are routed to the target adapter on-the-fly, allowing instant model toggling with zero switching latency and zero additional VRAM cost.
4. Interactive Features & Workspaces
We built two high-fidelity workspaces that connect directly to your local FastAPI backend server:
A. The Search & Portfolio Dashboard (app.html)
A glassmorphic, visual interface that displays:
- Advanced Filters: Filter by registration status (SEC Registered IA or Exempt Reporting Adviser), office state, and sort by AUM.
- Firm Profile Cards: Show CCO details, employee count, website, and conformed CIK.
- AUM Breakdown: Interactive progress bar separating discretionary and non-discretionary assets.
- 13F Portfolio Breakdown: A Chart.js doughnut chart visualizing portfolio concentration, paired with a sortable holdings table showing security names, CUSIPs, share counts, and dollar values.
- ADV-AI Disclosures Panel: A summarization widget that runs the locally hosted QLoRA
drp_loraadapter model to summarize narrative disciplinary information.
B. The AI Studio Telemetry Dashboard (training.html)
An operational control center that displays:
- System Telemetry: Real-time monitoring of CPU load, RAM, GPU temperature, and VRAM occupancy.
- Loss Curve: Interactive Chart.js line plot tracking QLoRA training steps and loss.
- Process Controller: Buttons to start, pause, or resume training in the background safely.
- Model Playground: Interactive chat interface to run prompt tests, with sliders for max tokens, temperature, and top-p.
5. Relational Model Context Protocol (MCP) Server
To make this dataset available to external AI clients (like Cursor, Claude Desktop, or custom scripts), the engine exposes the database through FastMCP:
search_advisers: Full-text and filter searches across 56K+ registered investment advisers.get_adviser: Fetches full adviser details including client types and advisory activities.get_disclosures: Lists all positive Item 11 disclosures.get_brochure: Retrieves narrative Item 4 through 15 sections from conformed brochures.get_13f_holdings: Returns a list of equity holdings for a specific quarter.query_sql: A read-only SQL escape hatch for ad-hoc custom queries.
Interactive Workspaces
1. Adviser & Portfolio Lookup
Use the live panel below to search adviser profiles, inspect client type breakdowns, view active advisory activities, and browse their latest Form 13F stock portfolios:
2. AI Studio: Model Training Telemetry
If your local backend server is running (ml_env\Scripts\python.exe dashboard/app.py --port 8000), the panel below will stream GPU/VRAM telemetry, render Chart.js loss curves, display the standard output training log stream, and let you test the QLoRA adapter models in the Playground: