# ADR 014: Finviz Smart Money Scanner — Phase 1b Bottom-Up Signal Layer ## Status Accepted ## Context The macro scanner pipeline produced top-down qualitative analysis (geopolitical events, market movers, sector rotation) but selected stocks entirely from macro reasoning. There was no bottom-up quantitative signal layer to cross-validate candidates. Adding institutional footprint detection (insider buying, unusual volume, breakout accumulation) via `finvizfinance` creates a "Golden Overlap" — stocks confirmed by both top-down macro themes and bottom-up institutional signals carry higher conviction. Key constraints considered during design: 1. `run_tool_loop()` has `MAX_TOOL_ROUNDS=5`. The market_movers_scanner already uses ~4 rounds. Adding Finviz tools to it would silently truncate at round 5. 2. `finvizfinance` is a web scraper, not an official API — it can be blocked or rate-limited at any time. 3. LLMs can hallucinate string parameter values when calling parameterized tools. 4. Sector rotation context is available from sector_scanner output and should inform smart money interpretation. ## Decisions ### 1. Separate Phase 1b Node (not bolted onto market_movers_scanner) A dedicated `smart_money_scanner` node avoids the `MAX_TOOL_ROUNDS=5` truncation risk entirely. It runs sequentially after `sector_scanner` (not in the Phase 1a parallel fan-out), giving it access to `sector_performance_report` in state. This context lets the LLM cross-reference institutional footprints against leading/lagging sectors. Final topology: ``` Phase 1a (parallel): START → geopolitical_scanner START → market_movers_scanner START → sector_scanner Phase 1b (sequential): sector_scanner → smart_money_scanner Phase 2: geopolitical_scanner, market_movers_scanner, smart_money_scanner → industry_deep_dive Phase 3: industry_deep_dive → macro_synthesis → END ``` ### 2. Three Zero-Parameter Tools (not one parameterized tool) Original proposal: `get_smart_money_anomalies(scan_type: str)` with values like `"insider_buying"`. Problem: LLMs hallucinate string parameter values. The LLM might call `get_smart_money_anomalies("insider_buys")` or `get_smart_money_anomalies("volume_spike")` — strings that have no corresponding filter set. Solution: Three separate zero-parameter tools: - `get_insider_buying_stocks()` — hardcoded insider purchase filters - `get_unusual_volume_stocks()` — hardcoded volume anomaly filters - `get_breakout_accumulation_stocks()` — hardcoded 52-week high + volume filters With zero parameters, there is nothing to hallucinate. The LLM selects tools by name from its schema — unambiguous. All three share a `_run_finviz_screen(filters_dict, label)` private helper to keep the implementation DRY. ### 3. Graceful Degradation (never raise) `finvizfinance` wraps a web scraper that can fail at any time (rate limiting, Finviz HTML changes, network errors). `_run_finviz_screen()` catches all exceptions and returns a string starting with `"Smart money scan unavailable (Finviz error): "`. The pipeline never hard-fails due to Finviz unavailability. `macro_synthesis` is instructed to note the absence and proceed on remaining reports. ### 4. `breakout_accumulation` over `oversold_bounces` Original proposal included an `oversold_bounces` scan (RSI < 30). This was rejected: RSI < 30 bounces are retail contrarian signals, not smart money signals. Institutions don't systematically buy at RSI < 30. Replaced with `breakout_accumulation` (52-week highs on 2x+ volume) — the O'Neil CAN SLIM institutional accumulation pattern, where institutional buying drives price to new highs on above-average volume. ### 5. Golden Overlap in macro_synthesis `macro_synthesis` now receives `smart_money_report` alongside the 4 existing reports. The system prompt includes explicit Golden Overlap instructions: if a smart money ticker fits the top-down macro narrative (e.g., an energy stock with heavy insider buying during a supply shock), assign it `"high"` conviction. If no smart money tickers align, proceed on remaining reports. The JSON output schema is unchanged. ## Consequences - **Pro**: Dual evidence layer — top-down macro + bottom-up institutional signals improve conviction quality - **Pro**: Zero hallucination risk — no string parameters in any Finviz tool - **Pro**: Pipeline never fails due to Finviz — graceful degradation preserves all other outputs - **Pro**: Sector context injection — smart money interpretation is informed by rotation context from sector_scanner - **Con**: `finvizfinance` is a web scraper — brittle to Finviz HTML changes; requires periodic maintenance - **Con**: Finviz screener results lag real-time institutional data (data is end-of-day); not suitable for intraday signals - **Con**: Adds ~620 tokens to scanner pipeline token budget (quick_llm tier, acceptable) ## Source Files - `tradingagents/agents/scanners/smart_money_scanner.py` (new) - `tradingagents/agents/utils/scanner_tools.py` (3 new tools + `_run_finviz_screen` helper) - `tradingagents/agents/utils/scanner_states.py` (`smart_money_report` field) - `tradingagents/graph/scanner_setup.py` (Phase 1b topology) - `tradingagents/graph/scanner_graph.py` (agent instantiation) - `tradingagents/agents/scanners/macro_synthesis.py` (Golden Overlap prompt) - `pyproject.toml` (`finvizfinance>=0.14.0`) - `tests/unit/test_scanner_mocked.py` (6 new tests for Finviz tools)