Metadata-Version: 2.5
Name: investdaytip
Version: 0.14.2
Summary: A tool that suggests 5 stock investment recommendations at the moment you run it.
Project-URL: Homepage, https://github.com/dfdezdom/investdaytip
Project-URL: Issues, https://github.com/dfdezdom/investdaytip/issues
License: MIT License
        
        Copyright (c) 2026 InvestDayTip Contributors
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: etf,finance,fundamental analysis,investing,investment,portfolio,scoring,screening,stock screener,stocks,trading,yfinance
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Python: >=3.10
Requires-Dist: argcomplete>=3.0
Requires-Dist: lxml>=4.9
Requires-Dist: numpy<3.0,>=1.24
Requires-Dist: pandas<3.0,>=2.0
Requires-Dist: rich<15.0,>=13.0
Requires-Dist: yahooquery>=2.4
Requires-Dist: yfinance<0.3,>=0.2.40
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest-mock>=3.10; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# InvestDayTip

<p align="center">
  <img src="logo.svg" alt="InvestDayTip Logo" width="300">
</p>

> A multi-factor analysis tool that suggests long-term **stock & ETF** buy recommendations from US, European, Asian, and Superinvestor-consensus markets, computed live from public market data.

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![PyPI version](https://img.shields.io/pypi/v/investdaytip.svg)](https://pypi.org/project/investdaytip/)
[![CI](https://github.com/dfdezdom/investdaytip/actions/workflows/ci.yml/badge.svg)](https://github.com/dfdezdom/investdaytip/actions/workflows/ci.yml)
[![GitHub stars](https://img.shields.io/github/stars/dfdezdom/investdaytip?style=flat&logo=github)](https://github.com/dfdezdom/investdaytip/stargazers)
[![Last commit](https://img.shields.io/github/last-commit/dfdezdom/investdaytip)](https://github.com/dfdezdom/investdaytip/commits/main)

---

## Features

- 📈 **Multi-factor scoring** — composite 0-100 score per asset
- 🔗 **Multiple data sources** — yfinance (default), yahooquery (batch), Financial Modeling Prep (FMP) or StockFit (US stocks, Starter plan), with automatic fallback
- 🏦 **Stocks & ETFs** — auto-detected and scored with dedicated models
- 🌍 **US, European, Asian & Superinvestor markets** — S&P 500, DAX, CAC 40, FTSE 100, Nikkei 225, Hang Seng, NSE, and superinvestor consensus picks from DataRoma 13F filings
- 💱 **Currency filter** — narrow by native currency (`USD`, `EUR`, `JPY`, …)
- ⚡ **Concurrent fetching** — analyzes ~300 tickers in seconds
- 📊 **Rich CLI output** — price, 1M/1Y change, score breakdown and rationale
- 🧾 **Self-contained HTML export** — interactive report with filters and sortable columns
- 🧪 **Pure scoring functions** — testable without network
- 🧠 **Interactive advisor** — market pulse, portfolio review, and tailored buy recommendations via the `advisor` subcommand
- 🤖 **AI-powered advisor** — chat with an intelligent investment advisor that analyzes markets, reviews portfolios, and recommends buys — powered by [OpenCode](https://opencode.ai) agents
- 📊 **Backtest validation** — historical backtesting with automated before/after comparison script
- 🧾 **StockFit integration (optional)** — point-in-time SEC filing dates for backtests (`--pit-source stockfit`, works offline from a local snapshot), a fundamentals insights section in the HTML report (`--fundamental-insights`), per-ticker `deep-dive` reports and a keyless devil's advocate layer (risk signals). Works on every StockFit plan — and without a key at all (`stockfit-status` shows what yours unlocks)
---

## Installation

### From PyPI (recommended)

```bash
pip install investdaytip
```

### Upgrade

```bash
pip install --upgrade investdaytip
```

Force reinstall if the version doesn't update (e.g. cached old wheel):

```bash
pip install --upgrade --no-cache-dir investdaytip
```

### From source (for development)

```bash
git clone https://github.com/dfdezdom/investdaytip.git
cd investdaytip
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
```

---

## Quick start

```bash
investdaytip
```

That's it. You'll see the top 5 buys scored across 300+ stocks & ETFs.

<p align="center">
  <img src="docs/screenshot-CLI.png" alt="InvestDayTip CLI output" width="90%">
</p>

---

## Usage

### CLI

**Subcommands:**

| Command | What it does | Section |
|---|---|---|
| `investdaytip [flags]` | Top recommendations from the curated universes | below |
| `investdaytip advisor` | Interactive market pulse, portfolio review, buys | [Advisor](#advisor-subcommand) |
| `investdaytip deep-dive` | Per-ticker deep report (score + research + diagnostics) | [Deep-dive](#deep-dive-subcommand) |
| `investdaytip backtest` | Historical validation of the scoring model | [Backtest](#backtest-subcommand) |
| `investdaytip stockfit-status` | Your StockFit plan + which features unlock | [Tiers](#stockfit-integration--tiers) |

```bash
investdaytip                         # Top 5 from full universe (US + EU + Asia, stocks + ETFs)
investdaytip -n 10                   # Top 10
investdaytip -a stocks               # Only stocks
investdaytip -a etfs                 # Only ETFs
investdaytip -r us                   # Only US
investdaytip -r eu                   # Only Europe
investdaytip -r asia                 # Only Asia
investdaytip -r superinvestor        # Only superinvestor consensus tickers
investdaytip -r us eu                # US + Europe
investdaytip -r eu -a stocks         # Only European stocks
investdaytip -r asia -a etfs         # Only Asian ETFs
investdaytip -c USD                  # Only USD-denominated assets
investdaytip -c EUR                  # Only EUR-denominated assets
investdaytip -c USD EUR              # USD + EUR assets
investdaytip -c JPY                  # Only JPY-denominated assets
investdaytip -r eu -c USD            # EU region + USD post-filter
investdaytip -r us eu -c USD EUR     # US + EU, USD + EUR assets
investdaytip -t AAPL MSFT VOO        # Custom ticker list

investdaytip --tickers-file tickers.txt # Custom ticker file (lines, spaces, commas)
investdaytip --tickers-file tickers-files-examples/semiconductors_relevant_tickers.txt
investdaytip --export-html report.html # Export report with interactive filters
investdaytip --export-html             # Uses investDayTip-aaaammdd-hhmm.html
                                       # or investDayTip-<tag>-aaaammdd-hhmm.html
                                       # when --tickers-file is set

investdaytip --superinvestor         # Fetch DataRoma superinvestor data and display column
investdaytip --no-include-technical  # Disable RSI + MACD (enabled by default for quant)
investdaytip --scoring-model classic # Use the classic Graham/Buffett model instead of quant
investdaytip --min-market-cap 1B     # Raise min market cap to $1B
investdaytip --min-market-cap 0      # Disable market-cap filter

investdaytip --data-source yahooquery  # Use yahooquery (Yahoo internal API, batch-friendly)
                                       # Falls back to yfinance automatically per ticker
investdaytip --data-source fmp         # Use Financial Modeling Prep (requires FMP_API_KEY env var)
                                       # Get a free key at https://financialmodelingprep.com/
investdaytip --no-cache                # Bypass SQLite cache, fetch fresh data
investdaytip --cache-clear           # Purge all cached data before running
investdaytip --workers 20            # More parallelism

investdaytip advisor                    # Interactive mode (asks for risk, region, etc.)

investdaytip deep-dive -t AAPL          # Deep report: score + research + risk signals
investdaytip deep-dive -t NVDA --export-html nvda.html

investdaytip backtest -n 10 -r us       # Backtest stock scoring on US market (stocks only)
investdaytip backtest -t AAPL MSFT VOO  # Backtest on custom ticker list
investdaytip backtest --export-html     # Export backtest results to HTML
investdaytip backtest --no-cache        # Bypass cache in backtest
investdaytip backtest --cache-clear     # Purge cache before backtest

investdaytip stockfit-status            # Your StockFit plan and unlocked features

investdaytip --help
./preview.sh                         # Serve generated HTML files on localhost:8000
```

#### Options

| Flag | Description | Default |
|---|---|---|
| `-n, --top N` | Number of recommendations (defaults to ticker count when `-t` is used) | `5` |
| `-t, --tickers ...` | Custom ticker list (overrides universe; quoted space-separated ok) | curated universe |
| `--tickers-file PATH` | Text file with custom tickers (merged with `--tickers` if both are used) | disabled |
| `-a, --asset-class {all,stocks,etfs}` | Asset class filter | `all` |
| `-r, --region {all,us,eu,asia,superinvestor}` `nargs="+"` | Region filter(s) — e.g. `-r us eu` | `all` |
| `-c, --currency {all,USD,EUR,GBP,…}` `nargs="+"` | Currency filter(s); narrows universe to matching regions when no `-r` is given | `all` |
| `-s, --sector TEXT` | Sector/category prefix filter, case-insensitive (e.g. `Financial` matches Financial Services) | disabled |
| `--export-html [PATH]` | Export recommendations to self-contained HTML (`investDayTip-aaaammdd-hhmm.html` if omitted) | disabled |
| `--superinvestor` | Include superinvestor ownership data from DataRoma (adds ~80 HTTP requests, shows column in HTML and CLI) | disabled |
| `--fundamental-insights` | Add a StockFit fundamentals section to the HTML report (margin trends, FCF/NI quality, balance-sheet health; US stocks only). Uses `STOCKFIT_API_KEY` when set and is omitted gracefully without it | disabled |
| `--include-technical` | Include RSI + MACD technical indicators in the scoring. **Default is `True` for `quant` and `False` for `classic`.** Use `--no-include-technical` to force-disable. | model-dependent |
| `--no-include-technical` | Force-disable RSI + MACD technical indicators | disabled |
| `--scoring-model {classic,quant}` | Stock/ETF scoring model | `quant` |
| `--data-source {yfinance,yahooquery,fmp,stockfit}` | Data source. `stockfit` = US stocks only, requires `STOCKFIT_API_KEY` + Starter plan (rankings differ from yfinance — as-filed FY semantics) | `yfinance` |
| `--min-market-cap VALUE` | Minimum market cap (`1B`, `500M`, `0` to disable; see [Market Cap Classification](#market-cap-classification)) | `0` with tickers, `2B` otherwise |
| `--no-cache` | Skip SQLite cache, fetch all data live from Yahoo Finance | disabled |
| `--cache-clear` | Purge the SQLite cache before running | disabled |
| `--workers N` | Parallel fetch threads | `10` |
| `--version` | Show the installed version and exit | n/a |
| `-h, --help` | Show the CLI help message and exit | n/a |

---

## 🤖 OpenCode AI Agent

InvestDayTip includes an **AI-powered investment advisor** that lets you chat with an intelligent market analyst directly from your terminal — no memorizing CLI flags.

### Prerequisites

```bash
# Install via the official installer (macOS / Linux)
curl -fsSL https://opencode.ai/install | bash

# Also available via npm, brew, bun, and more — see https://opencode.ai
```

### What it can do

1. **Market pulse** — Full macro analysis (VIX + yield curve + bond volatility + DXY) in ~30 seconds
2. **Portfolio review** — Score your holdings, identify weaknesses, and get actionable signals
3. **Buy recommendations** — Best picks filtered by region, asset class, and risk profile
4. **Devil's advocate** — Bear case + risk signals for specific tickers (see below)
5. **Full analysis** — All of the above combined into a single comprehensive report

### Quick example

```text
@advisor what's the market pulse?
```

**Expected output:**

```markdown
📊 Market Pulse

Macro Score: 58/100 (Neutral)
- VIX: 14.2 (calm)
- 10Y-2Y Spread: +0.85% (healthy)
- MOVE: 78 (normal)
- DXY: 102.3 (strong)
- Fear & Greed: 67/Greed

Signal: 🟡 HOLD — selective buying
```

### 🩺 Devil's advocate — bear cases from SEC footnotes

Every portfolio review and buy recommendation now ends with a **bear case**
for the analyzed tickers, in two layers:

| Layer | Source | Needs |
|---|---|---|
| 1 — risk signals | `investdaytip deep-dive` (risk bullets, Piotroski checks, Altman zone) | nothing — works keyless |
| 2 — SEC footnotes | StockFit MCP tools: customer/supplier **concentration trends**, debt **maturity walls**, stock-comp **dilution**, **insider summaries** (0 buys vs N sells), governance flags | StockFit MCP connected |

```text
@advisor devil's advocate on NVDA
```

**Expected output:** severity bullets (🔴/🟡/🔵) mixing both layers — e.g.
"Customer One went from 14% → 25% of receivables in two years" next to
"0 insider buys vs 318 sells ($2.18B) in 12 months" — **plus the balanced
view** of what does *not* support the bear case (clean balance sheet, no
hidden leverage). Never fabricated: every bullet comes from a tool call.

**Connecting the StockFit MCP (layer 2):** the server is registered in
`opencode.json` (OAuth 2.1 — no API key in config):

```bash
opencode mcp add stockfit --url https://api.stockfit.io/mcp   # already registered
```

Then sign in once from the OpenCode TUI: run `/mcps`, select `stockfit` and
complete the browser flow. Tools appear as `tools.stockfit.*` (125 of them —
footnotes, governance, insider/ownership summaries, sector-aware metrics).
Without the MCP — or on lower plans — the advisor degrades to layer 1 and
says so explicitly.

### AI Agent vs CLI advisor

| | OpenCode AI Agent | `investdaytip advisor` CLI |
|---|---|---|
| **Interface** | Natural language chat | Interactive menu prompts |
| **Flexibility** | Ad-hoc questions, follow-ups | Predefined flow |
| **Speed** | ~30s for market pulse | ~1–2 min for full analysis |
| **Output** | Clean markdown tables | Rich colored tables (terminal) |
| **Best for** | Quick questions, exploration | Deep structured analysis |

### Full documentation

See [`.opencode/agents/advisor.md`](.opencode/agents/advisor.md) for:
- VIX / macro regime interpretation rules
- Fear & Greed Index signals
- Bubble burst historical indicators (dot-com 2000, railroads 1845)
- Portfolio scoring thresholds and presentation format
- Devil's advocate rules (bear-case layers, StockFit tool map, never-fabricate)
- Execution methods (market pulse, portfolio review, devil's advocate, full analysis)

---

### Advisor subcommand

Interactive market analysis with **multi-indicator macro pulse** (VIX, 10Y-2Y yield curve, MOVE bond volatility, DXY dollar strength), portfolio review, and buy recommendations:

```bash
investdaytip advisor                          # Interactive mode (asks for risk, region, etc.)
investdaytip advisor --risk moderate          # Non-interactive with risk preset
investdaytip advisor --risk aggressive -r us -a stocks    # US stocks, aggressive, non-interactive
investdaytip advisor --risk moderate --portfolio portfolios/portfolio.txt  # Custom portfolio
```

The advisor now displays a **composite macro health score** (0-100):
- 🟢 **≥70** — Macro healthy
- 🟡 **≥45** — Mixed signals
- 🟠 **≥25** — Macro warning
- 🔴 **<25** — Macro danger

See `investdaytip advisor --help` for all options.

#### Market Indicators

The advisor fetches six live indicators to compute the composite **macro health score** (0-100):

| Indicator | Ticker | What it measures | Reference ranges | Score impact |
|---|---|---|---|---|
| **VIX** | `^VIX` | Expected S&P 500 volatility over the next 30 days | ≤15 calm, ≤25 neutral, ≤35 fear, >35 panic | ±20 |
| **VXN** | `^VXN` | Expected Nasdaq 100 volatility (tech-heavy complement to VIX) | (informational only) | — |
| **10Y-2Y Spread** | `^TNX` + `2YY=F` | US Treasury yield curve slope — inverted curve signals recession risk | >1% healthy, 0–1% neutral, <0% inverted | −20 / +5 |
| **MOVE** | `^MOVE` | Bond market volatility (Merrill Lynch Option Volatility Estimate) | <60 calm, 60–100 normal, 100–120 elevated, >120 panic | −15 / +5 |
| **DXY** | `DX-Y.NYB` | US Dollar Index — strength against EUR, JPY, GBP, CAD, SEK, CHF | <95 weak, 95–100 neutral, 100–105 strong, >105 very strong | −10 / +5 |
| **Fear & Greed** | [CNN API](https://production.dataviz.cnn.io/index/fearandgreed/graphdata) | Composite market sentiment from 7 sub-indicators (momentum, breadth, put/call, volatility, junk bonds, safe havens) | 0–100; <25 extreme fear, >75 extreme greed | ±10 (contrarian) |

The score starts at a neutral **50** and each indicator adjusts it up or down based on current readings. The final score determines the macro signal:

- 🟢 **≥70** — Macro healthy → **buy**
- 🟡 **≥45** — Mixed signals → **hold**
- 🟠 **≥25** — Macro warning → **hold**
- 🔴 **<25** — Macro danger → **sell**

All indicators are shown live in the `📈 Market Analysis` table when running `investdaytip advisor`.

### Deep-dive subcommand

Per-ticker deep report — this is where the detail lives: the InvestDayTip
score, StockFit's research summary and keyless health diagnostics.

```bash
investdaytip deep-dive -t AAPL                      # Rich terminal report
investdaytip deep-dive -t "AAPL MSFT" --export-html # Self-contained HTML page
```

| Section | Source | Needs `STOCKFIT_API_KEY`? |
|---|---|---|
| Score + factor breakdown | live data + the scoring model | no |
| Earnings snapshot (margins, ROE/ROIC, FCF, growth, next dates) | StockFit `company/research-summary` | yes (omitted gracefully otherwise) |
| Piotroski F-Score (9 checks) + Altman Z + zone | local computation from the ticker's own statements | no |
| Devil's advocate — risk signals | local heuristics (Altman zone, Piotroski failures, leverage, payout, losses) | no |

Piotroski/Altman are **informative diagnostics — never scored** (they were
validated and rejected as scoring factors).

| Flag | Description | Default |
|---|---|---|
| `-t, --tickers ...` | Ticker(s) to analyse (required) | — |
| `--export-html [PATH]` | Export the report to a self-contained HTML page | disabled |
| `--scoring-model {classic,quant}` | Scoring model for the displayed score | `quant` |
| `--no-cache` | Skip SQLite cache for the underlying fetches | disabled |

### StockFit integration & tiers

InvestDayTip works **on every StockFit plan — and without a key at all**:
features unlock more as the tier rises and degrade gracefully below it
(omitted with the reason, never fabricated). See what your setup unlocks:

```bash
investdaytip stockfit-status
```

| Feature | Needs |
|---|---|
| PIT backtests + snapshot (`--pit-source stockfit`) | keyless (local snapshot) |
| HTML fundamental insights (`--fundamental-insights`) | any key (Free) |
| deep-dive earnings snapshot | Starter |
| Rich devil's advocate footnotes (roadmap) | Pro |

### Backtest subcommand

Historical validation of the stock scoring model:

```bash
investdaytip backtest                          # Backtest US stocks (default)
investdaytip backtest -n 10                    # Top 10 picks per snapshot
investdaytip backtest -r us                    # US stocks
investdaytip backtest -r eu                    # European stocks
investdaytip backtest -r asia                  # Asian stocks
investdaytip backtest -r superinvestor         # Superinvestor consensus stocks
investdaytip backtest -t AAPL MSFT             # Custom ticker list
investdaytip backtest --benchmark SPY          # Custom benchmark
investdaytip backtest --interval-months 6      # Semi-annual snapshots
investdaytip backtest --lag-days 90            # Reporting lag (default: 60)
investdaytip backtest --period 3y              # Shorter price history
investdaytip backtest --export-html            # Export to HTML
investdaytip backtest --pit-source stockfit    # Point-in-time SEC filing dates (StockFit)
investdaytip backtest --no-cache               # Bypass SQLite cache
investdaytip backtest --cache-clear            # Purge cache before run
```

#### Backtest options

| Flag | Description | Default |
|---|---|---|
| `-n, --top N` | Top N picks per snapshot | `10` |
| `-t, --tickers ...` | Custom ticker list | curated US universe |
| `-r, --region ...` | Region filter(s) | `us` |
| `-c, --currency ...` | Currency filter(s) | `all` |
| `--period PERIOD` | yfinance lookback period | `5y` |
| `--interval-months N` | Months between snapshots | `3` |
| `--lag-days N` | Reporting lag in days | `60` |
| `--min-market-cap CAP` | Minimum market cap (`0`, `1B`, …) | `0` with tickers, `2B` otherwise |
| `--benchmark TICKER` | Benchmark for alpha/returns | `SPY` (per region) |
| `--export-html [PATH]` | Export results to HTML | disabled |
| `--scoring-model {classic,quant}` | Scoring model to validate | `quant` |
| `--pit-source {none,stockfit}` | Point-in-time fundamentals (exact SEC filing dates; needs key **or** local snapshot) | `none` |
| `--include-technical` / `--no-include-technical` | Force RSI/MACD in/out | model-dependent |
| `--max-workers N` | Parallel fetch threads | `10` |
| `--no-cache` / `--cache-clear` | Bypass / purge the SQLite cache | disabled |

**Note:** Backtest only supports stocks (no ETFs). It simulates quarterly snapshots
with a configurable reporting lag, scores each stock, and measures forward returns
against a benchmark.

#### Point-in-time fundamentals (StockFit, opt-in)

`--pit-source stockfit` replaces the fixed reporting-lag assumption with each
filing's actual SEC acceptance date (`dateFiled`) — every snapshot only sees
data that was public that day, so slow filers can't leak results back in time.
US stocks only. Validated on the full US universe: Sharpe 0.74→0.85, max
drawdown 24%→11%, 12M win rate 50%→56%.

It works **without an API key** when a local snapshot exists
(`~/.investdaytip/pit/`) — build one while your StockFit key is valid:

```bash
python scripts/pit_snapshot.py          # full US stock universe (196 tickers)
python scripts/pit_snapshot.py --status # inspect saved files (offline)
```

Resolution order, per ticker: live StockFit (when `STOCKFIT_API_KEY` is set)
→ local snapshot → the classic fixed-lag path.

#### Backtest-Driven Scoring Validation

Use the included `scripts/scoring_baseline.py` to validate scoring changes objectively:

```bash
# Save baseline BEFORE your change
python scripts/scoring_baseline.py run \
  --tag "before" -r us -n 10 \
  -t "AAPL MSFT GOOGL META NVDA" \
  --period 2y --interval-months 3 \
  --min-market-cap 0

# Save baseline AFTER your change
python scripts/scoring_baseline.py run \
  --tag "after" -r us -n 10 \
  -t "AAPL MSFT GOOGL META NVDA" \
  --period 2y --interval-months 3 \
  --min-market-cap 0

# Compare two different scoring models on the same universe
python scripts/scoring_baseline.py run --tag classic -r us -n 10 \
  -t "AAPL MSFT GOOGL META NVDA" --period 2y --interval-months 3 \
  --min-market-cap 0 --scoring-model classic

python scripts/scoring_baseline.py run --tag quant -r us -n 10 \
  -t "AAPL MSFT GOOGL META NVDA" --period 2y --interval-months 3 \
  --min-market-cap 0 --scoring-model quant

python scripts/scoring_baseline.py compare baseline-classic.json baseline-quant.json

# Compare side-by-side with decision rules
python scripts/scoring_baseline.py compare baseline-before.json baseline-after.json
```

Decision rules:
- **Ship it**: Alpha ↑ AND Sharpe ↑ AND 12M win rate ↑
- **Consider**: Alpha ↑ OR Sharpe ↑ (mixed, review drawdown)
- **Reject / iterate**: Alpha ↓ AND Sharpe ↓

### When to use technical indicators

The `--include-technical` flag adds RSI-14 and MACD histogram to the Momentum factor (15% weight, blended at 30%). Under the **quant** model this is now enabled by default; under **classic** it remains opt-in.

Backtest validation across four scenarios under the **quant** model shows the flag is generally additive for US screens but mixed for concentrated mega-caps and EU:

| Scenario | Without `--include-technical` | With `--include-technical` | Verdict |
|---|---|---|---|
| **US ($2B+)** (134 tickers, `-n 10`) | Alpha 21.94%, Sharpe 1.49 | Alpha **25.00%**, Sharpe **1.68** | ✅ **Improved** — higher alpha (+3pp), Sharpe (+0.19), and lower drawdown (6.76% → 5.63%) |
| **US (no cap filter)** (134 tickers, `-n 10`) | Alpha 5.47%, Sharpe 0.54 | Alpha **11.53%**, Sharpe **0.85** | ✅ **Improved** — alpha doubles, drawdown halves (34.3% → 16.2%), 12M win rate climbs (53% → 73%) |
| **US mega-caps ($200B+)** (134 tickers, `-n 2`) | Alpha **44.33%**, Sharpe **1.31** | Alpha 42.35%, Sharpe 1.18 | ⚠️ **Mixed** — slightly lower alpha (−2pp) and Sharpe, but drawdown improves sharply (18.8% → 10.4%) |
| **EU ($2B+)** (66 tickers, `-n 10`) | Alpha **7.11%**, Sharpe **1.71** | Alpha 3.93%, Sharpe 0.93 | ⚠️ **Neutral/Mixed** — lower alpha and Sharpe with tech, but 12M win rate climbs (42% → 60%) |

**Guidelines:**
- ✅ **Default behavior** — `quant` enables technical indicators automatically; `classic` keeps them opt-in
- ✅ **Broad US screens** benefit the most — higher alpha, Sharpe, and lower drawdown
- ⚠️ **Concentrated mega-cap lists** — mixed results; alpha and Sharpe may dip but drawdown improves
- ⚠️ **EU screens** — fundamental-only (no-tech) shows stronger risk-adjusted returns; verify on your specific ticker list
- ❌ **Use `--no-include-technical`** if you rely on the `classic` model or want a pure fundamental signal


### Market Cap Classification

The `--min-market-cap` filter controls the size of companies included in the analysis. Standard classification for reference:

| Category | Market Cap | Typical characteristics |
|---|---|---|
| **Mega-cap** | > $200B | Largest global companies (Apple, Microsoft, Google). Highest liquidity, most followed by analysts, technical indicators most reliable. |
| **Large-cap** | $10B – $200B | Established companies with stable fundamentals. Good balance of growth and stability. |
| **Mid-cap** | $2B – $10B | Growing companies, regional leaders. More volatile than large-caps but higher growth potential. |
| **Small-cap** | $300M – $2B | Niche players or regional companies. Higher volatility, less analyst coverage, fundamentals less reliable. |
| **Micro-cap** | < $300M | Early-stage or distressed companies. Very high risk, low liquidity. |

**Why the default is $2B (mid-cap threshold):**

The model is designed for long-term fundamental investing. Below $2B (small/micro-caps), companies tend to have:
- Incomplete or unreliable financial data in yfinance
- Higher volatility that overwhelms fundamental signals
- Lower liquidity, making technical indicators less meaningful

The $2B threshold strikes a balance: it includes mid-caps and above (where fundamentals are reliable) while filtering out the noisiest small/micro-caps. Use `--min-market-cap 0` to include all tickers, or raise it to `10B`/`50B` for a pure large/mega-cap focus.

**Note:** when explicit tickers are provided via `-t` or `--tickers-file`, the filter defaults to `0` (disabled) — the assumption is that you curated your own list.

### HTML export

Generate an interactive report that works offline (single file with inline CSS/JS):

```bash
investdaytip -n 25 -a all -r all --export-html investdaytip-report.html
```

The generated report includes filters for:

- Text search (ticker/name/sector)
- Asset class (`stock` / `etf`)
- Region (`us` / `eu` / `asia`)
- Minimum score
- Minimum 1M return (%)
- Minimum 1Y return (%)

When `--superinvestor` is enabled, an additional **"Superinvestors"** sortable column appears between 1Y and Score showing the number of tracked managers holding each ticker.
When `--include-technical` is enabled, **"RSI"** and **"MACD"** columns appear between 1Y (or Superinvestors) and Score.

It also includes:

- Click-to-sort columns (ascending/descending)
- Full-width responsive layout (uses available browser width)
- Pre-rendered rows + client-side interactivity (works even if JS is restricted)
- Direct platform links in table columns: `Ticker` (Google Finance), `T` (TradingView), `Y` (Yahoo Finance)

Direct platform links (`Ticker` → Google Finance, `T` → TradingView, `Y` → Yahoo Finance) use exchange suffix mapping. See `_normalize_exchange_hint()` and `_exchange_mapping()` in [`src/investdaytip/html_export.py`](src/investdaytip/html_export.py) for the full list of ~20 supported exchanges.

### Programmatic API

```python
from investdaytip import get_recommendations

# Get top 5 Asian stocks
picks = get_recommendations(top_n=5, region="asia", asset_class="stocks")
for s in picks:
    print(f"{s.data.ticker} ({s.asset_type}) — score={s.total:.1f}")
    print(f"  Price: {s.data.current_price} {s.data.currency}")
    print(f"  1M: {s.data.return_1m:.2%}  1Y: {s.data.return_12m:.2%}")
    print(f"  Why: {'; '.join(s.rationale[:3])}")

# Or filter by sector (prefix match, case-insensitive)
picks = get_recommendations(top_n=5, sector="Financial")  # Financial + Financial Services

# Or mix regions and asset classes
picks = get_recommendations(top_n=10, region="all")  # US + EU + Asia stocks & ETFs

# Choose the scoring model explicitly (quant is the default)
picks = get_recommendations(top_n=5, region="us", scoring_model="classic")

# Use FMP data source (requires FMP_API_KEY env var)
picks = get_recommendations(top_n=5, region="us", data_source="fmp")
```

---

## Output

Each recommendation includes:

| Column | Meaning |
|---|---|
| **Type** | `STOCK` or `ETF` |
| **Ticker / Name / Sector** | Identification |
| **Ticker link** | Opens Google Finance in a new tab |
| **T / Y** | Opens TradingView / Yahoo Finance in a new tab |
| **Price** | Current price in native currency |
| **% Today** | Daily change vs previous close |
| **P/E** | Trailing price-to-earnings ratio (stocks; `-` when unavailable) |
| **Yield** | Dividend yield for stocks (TTM from raw dividends, fallback to `dividendYield`) or ETFs (`yield_`) — `-` when unavailable |
| **1M Δ** | % change vs ~22 trading days ago |
| **1Y Δ** | % change vs ~252 trading days ago |
| **RSI** | RSI-14 (when `--include-technical` is enabled) |
| **MACD** | MACD histogram % vs price (when `--include-technical` is enabled) |
| **Superinvestors** | Number of managers holding the stock (HTML only; `-` if not in DataRoma universe) |
| **Score** | Composite 0-100 weighted score |
| **Breakdown** | Four sub-scores (shown in a compact single line) |
| **Why** | Top 3 rationale notes |

---

## Scoring Model

InvestDayTip supports two stock-scoring models, selectable via `--scoring-model {classic,quant}`. The default is **`quant`**, a five-factor model inspired by Seeking Alpha Quant Ratings. The original **`classic`** model (Graham/Buffett + momentum) remains available for backwards compatibility.

Each metric is normalized to **0-100** via piecewise-linear functions over empirically reasonable ranges. Missing data contributes a neutral **50** so a ticker isn't penalized for lacking a metric.

### Stocks — `quant` (default)

| Factor | Weight | Metrics |
|---|---|---|
| **Value** | 25% | trailing P/E, P/B, PEG, FCF yield |
| **Growth** | 20% | earnings growth, revenue growth |
| **Profitability** | 25% | ROE, ROA, profit margin + a YoY-improvement sub-score (Δgross margin + ΔROA vs last fiscal year; neutral when statements are unavailable) |
| **Momentum** | 15% | price vs SMA200, 12-month return, SMA200 slope (plus RSI-14 + MACD histogram by default; disable with `--no-include-technical`) |
| **EPS Revisions** | 15% | average EPS surprise (Reported EPS vs analyst Estimate) over the last four quarters |

The `quant` model uses **disqualifying grades**: if any high-impact factor falls into red-flag territory, the total score is capped at neutral (50). This prevents a strong showing in one area from masking a serious weakness elsewhere.

### Stocks — `classic`

| Pillar | Weight | Metrics |
|---|---|---|
| **Quality** | 35% | ROE, profit margin, earnings & revenue growth |
| **Value** | 25% | trailing P/E, P/B, PEG |
| **Health** | 20% | Debt/Equity, current ratio, free cash flow |
| **Trend** | 20% | price vs SMA200, 12-month return, SMA200 slope (plus RSI-14 + MACD histogram when `--include-technical` is explicitly enabled) |

### ETFs

ETF scoring also switches between two models via `--scoring-model {classic,quant}`. The default is **`quant`**, a momentum-first model.

**`quant`** (default for ETFs when `--scoring-model quant`):

| Factor | Weight | Metrics |
|---|---|---|
| **Momentum** | 65% | 1m (15%), 3m (25%), 6m (30%), 12m (30%) returns |
| **Risk** | 15% | volatility + beta (lower is better) |
| **Cost** | 12% | expense ratio (lower is better) |
| **Liquidity** | 8% | AUM (log scale) |

**`classic`** (used when `--scoring-model classic`):

| Pillar | Weight | Metrics |
|---|---|---|
| **Returns** | 40% | 3y avg, 5y avg, 12m return |
| **RiskAdj** | 25% | Sharpe proxy `(r12 - rf) / σ`, annualized volatility |
| **Size** | 15% | AUM (log scale) |
| **Cost/Yield** | 20% | expense ratio (lower=better), dividend yield |

---

## Universes

When no `-t` is given, InvestDayTip uses curated universes:

- **US stocks** — 121 large-caps across all S&P sectors (`src/investdaytip/universe.py`)
- **US ETFs** — 41 broad-market, factor, sector and bond ETFs (`etf_universe.py`)
- **EU stocks** — 99 large-caps from DAX, CAC, FTSE 100, IBEX, AEX, SMI, FTSE MIB, Nordics (`eu_universe.py`)
- **EU UCITS ETFs** — 35 broad, sector and bond UCITS ETFs (`eu_etf_universe.py`)
- **Asia stocks** — 110 large-caps from Japan, Hong Kong, Singapore, India, South Korea, Taiwan, and Australia (`asia_universe.py`)
- **Asia ETFs** — 17 broad-market, country-specific, and sector ETFs with significant Asian exposure (`asia_etf_universe.py`)
- **Superinvestor stocks** — 101 consensus picks held by ≥2 of ~82 top investors tracked by DataRoma 13F filings (`superinvestor_universe.py`)

Tickers use Yahoo Finance suffixes: 
- **US:** no suffix (AAPL, MSFT)
- **EU:** `.DE` Xetra · `.PA` Paris · `.AS` Amsterdam · `.L` London · `.MC` Madrid · `.MI` Milan · `.SW` Swiss
- **Asia:** `.T` Tokyo · `.HK` Hong Kong · `.SI` Singapore · `.NS` NSE India · `.KS` Korea · `.TW` Taiwan · `.AX` Australia

### Ticker File Examples

The repository includes ready-to-use ticker sets in `tickers-files-examples/` for thematic analyses:

- `artificial_intelligence_relevant_tickers.txt`
- `biotech_relevant_tickers.txt`
- `energy_relevant_tickers.txt`
- `eu_etfs_relevant_tickers.txt`
- `financial_relevant_tickers.txt`
- `health_relevant_tickers.txt`
- `pharma_relevant_tickers.txt`
- `quantum_computing_relevant_tickers.txt`
- `semiconductors_relevant_tickers.txt`
- `space_relevant_tickers.txt`
- `spain_relevant_tickers.txt`
- `technology_relevant_tickers.txt`

Example:

```bash
investdaytip -n 15 --tickers-file tickers-files-examples/artificial_intelligence_relevant_tickers.txt --export-html
```

After export, preview the generated file in a browser by running:

```bash
./preview.sh
```

Then open the generated report from:

```text
http://localhost:8000/<generated-file-name>.html
```

### Shell tab completion

Requires the `argcomplete` package (installed automatically with `pip install investdaytip`):

```bash
# Enable for the current session:
eval "$(register-python-argcomplete investdaytip)"

# Make it permanent (add to ~/.zshrc or ~/.bashrc):
echo 'eval "$(register-python-argcomplete investdaytip)"' >> ~/.zshrc
```

Once enabled, try `investdaytip -<TAB>` or `investdaytip --region <TAB>`.

---

## Data Source

All market data is fetched live from **Yahoo Finance** via the [`yfinance`](https://github.com/ranaroussi/yfinance) library. Fundamentals come from `Ticker.info`, prices and trend metrics from `Ticker.history(period="2y")`.

**Dividend yield normalization:** `yfinance` reports `dividendYield` inconsistently — most US tickers return a decimal (e.g. `0.054` = 5.4%) while some European tickers return an already-multiplied percentage (e.g. `4.05` = 4.05%). InvestDayTip normalizes any value greater than `1.0` by dividing by 100.

**Reliable stock dividend yield:** For stocks, the **Yield** column is computed directly from `Ticker.dividends` over the trailing twelve months divided by the current price, because yfinance's `dividendYield` field can be wildly wrong for some tickers (e.g. AAPL and V). When raw dividend history is unavailable, InvestDayTip falls back to the normalized `dividendYield` from `Ticker.info`.

**Superinvestor data** is scraped from **DataRoma** (https://www.dataroma.com) — 13F filings from ~82 legendary investors. The data is fetched once and cached for 7 days. Use `--superinvestor` to enable this data and display the "Superinvestors" column in both the HTML report and the CLI table (disabled by default to avoid the ~80 HTTP requests).

**Ticker normalization:** When a company has multiple share classes (e.g., Alphabet's GOOGL and GOOG), DataRoma holdings are normalized to a single ticker (GOOGL) to avoid duplicate counting. This ensures the "Superinvestors" column reflects unique manager positions, not duplicated entries.

### Local Cache

InvestDayTip includes an **SQLite cache** (`~/.investdaytip/cache.db`) created automatically on first fetch:

| Data | TTL |
|------|-----|
| Prices & history | 15 minutes |
| Fundamentals (info dict) | 1 day |
| Financial statements (balance sheet, income, cash flow) | 7 days |
| Dividends | 7 days |
| Fear & Greed Index | 1 hour |
| Superinvestor holdings | 7 days |

The cache uses per-thread SQLite connections with a write lock to support concurrent fetches safely. Use `--no-cache` to bypass the cache for a single run, or `--cache-clear` to purge all entries. Both flags work on the main and `backtest` subcommands. Caching is automatically disabled when running tests.

---

## Project Structure

```
preview.sh                # Local static server for generated HTML reports
scripts/
├── scoring_baseline.py    # Backtest before/after comparison tool
├── factor_ic.py           # Factor IC analysis (which metrics predict returns)
├── pit_snapshot.py        # StockFit PIT statement snapshot builder
└── compare_data_sources.py # yfinance vs yahooquery vs FMP field comparison
src/investdaytip/
├── __init__.py            # Public API: get_recommendations
├── main.py                # CLI entry point + rich table rendering
├── advisor.py             # Interactive advisor: market pulse (VIX + macro), portfolio review, buy recs
├── backtest.py            # Historical stock scoring validation (stocks only)
├── dataroma.py            # DataRoma superinvestor holdings scraper
├── html_export.py         # Self-contained HTML report exporter
├── recommender.py         # Concurrent orchestration
├── cache.py               # SQLite caching layer (per-thread connections, WAL mode)
├── data_source.py         # yfinance wrapper + dataclasses (StockData / EtfData)
├── data_source_fmp.py     # FMP wrapper (alternative data source, 6 endpoints/ticker)
├── data_source_yahooquery.py # Yahooquery batch data source
├── data_source_stockfit.py # StockFit client: PIT statements, insights, plan detection, live source
├── financial_health.py    # Piotroski F-Score, Altman Z, YoY-improvement flags
├── deep_dive.py           # Per-ticker deep report (score + research + diagnostics)
├── risk_signals.py        # Keyless devil's advocate layer (bear-case bullets)
├── scoring.py             # Pure scoring functions (score_stock, score_etf)
├── sentiment.py           # CNN Fear & Greed Index fetch
├── universe.py            # US stock universe
├── etf_universe.py        # US ETF universe
├── superinvestor_universe.py  # Superinvestor consensus universe (DataRoma 13F)
├── eu_universe.py         # EU stock universe
├── eu_etf_universe.py     # EU UCITS ETF universe
├── asia_universe.py       # Asia stock universe
└── asia_etf_universe.py   # Asia ETF universe
portfolios/               # Portfolio ticker files
advisor_recommendations/   # Advisor-generated HTML reports (git-ignored)
tests/
├── conftest.py            # Global fixtures (cache off, network guard, temp snapshot dir)
├── test_integration.py    # End-to-end integration tests (sorting, filters, CLI, export)
├── test_advisor.py        # Advisor tests (VIX, macro regime, bubble risk)
├── test_backtest.py       # Backtest engine tests (fetch, snapshots, scoring, cache, interpret)
├── test_recommender.py    # Recommendation orchestration tests
├── test_main.py           # CLI helper tests
├── test_html_export.py    # HTML export tests
├── test_scoring.py        # Stock scoring tests
├── test_scoring_quant.py  # Quant model tests (five factors, improvement sub-score)
├── test_etf_scoring.py    # ETF scoring tests
├── test_financial_health.py # Piotroski / Altman / improvement-flag tests
├── test_sentiment.py      # Fear & Greed sentiment tests
├── test_cache.py          # SQLite cache tests
├── test_universes.py      # Universe integrity tests
├── test_data_source.py    # yfinance data fetching tests
├── test_data_source_fmp.py # FMP data fetching tests
├── test_data_source_yahooquery.py # Yahooquery data source tests
├── test_data_source_stockfit_pit.py # StockFit PIT layer tests (mocked HTTP)
├── test_data_source_stockfit_live.py # StockFit live data source tests (mocked HTTP)
├── test_fundamental_insights.py # StockFit insights section tests
├── test_pit_snapshot.py   # PIT snapshot layer tests
├── test_deep_dive.py      # Deep-dive report tests
├── test_stockfit_plan.py  # StockFit tier detection & capability gating tests
└── test_dataroma.py       # DataRoma scraper tests
tickers-files-examples/
├── semiconductors_relevant_tickers.txt
├── artificial_intelligence_relevant_tickers.txt
├── quantum_computing_relevant_tickers.txt
├── energy_relevant_tickers.txt
├── space_relevant_tickers.txt
├── technology_relevant_tickers.txt
├── spanish_relevant_tickers.txt
├── pharma_relevant_tickers.txt
├── biotech_relevant_tickers.txt
├── health_relevant_tickers.txt
└── financial_relevant_tickers.txt
```

---

## Limitations & Caveats

- **No currency normalization** — prices, market caps and AUM stay in native currency
- **Currency filter** (`-c`) compares against yfinance's reported currency field; tickers with a missing/unknown currency are always kept (a missing field shouldn't silently drop a candidate)
- **`min_market_cap` filter** (`--min-market-cap`) is applied against raw native-currency figures; when active, tickers with a missing market cap are excluded
- Some European tickers change Yahoo symbols over time; if a ticker is delisted in Yahoo it's silently skipped
- ETF expense ratios are sometimes missing in `yfinance` — the scorer falls back to a slightly optimistic default (60) in that case
- **Long-term, fundamental-driven model**: not suitable for short-term/day trading signals

---

## Testing

```bash
pip install -e ".[dev]"   # pytest, ruff, mypy
pytest -q                 # run the suite
ruff check src tests      # lint
mypy                      # type-check
```

**335 tests** across 16 test files. The scoring engine is purely functional
and tested without network calls; an autouse guard in `tests/conftest.py` fails
fast if a test reaches yfinance unmocked. Integration tests mock the full
`recommend()` → `main()` → export pipeline.

---

## Contributing

Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).

---

## Disclaimer

**This is not financial advice.** InvestDayTip is an educational tool that applies a deterministic scoring model to publicly available data. Always do your own research and consult a licensed advisor before making investment decisions.

---

## License

[MIT](LICENSE)
