A command-line tool to fetch historical stock data for STOXX Europe 600 and S&P 500 companies using Yahoo Finance, with support for incremental updates, resumable fetching, period analysis, and an interactive dashboard.
- Dual Market Support: STOXX Europe 600 (default) and S&P 500
- Fetches 2 years of OHLCV (Open, High, Low, Close, Volume) data
- Incremental updates: only fetches missing days for existing data
- Resumable: interrupted fetches can be resumed from where they left off
- Period Analysis: Identifies stocks at period highs and lows across multiple timeframes
- Interactive Dashboard: Visualize and compare stocks with a modern web UI
- Caches ticker lists locally with company metadata (sector, industry, country)
- Stores data as efficient Parquet files using Polars
- Docker and Docker Compose
- Make (optional, for convenience commands)
# Fetch European stocks (STOXX Europe 600)
make fetch
# Run analysis
make analyze
# Launch dashboard
make dashboard# Fetch/update European stock data
make fetch
# Refresh ticker list from Wikipedia
make refresh
# Reset and re-fetch everything
make reset
# Analyze stocks for period highs/lows
make analyze
# Fetch and analyze in one command
make fetch_and_analyze# Fetch/update S&P 500 stock data
make sp500_fetch
# Refresh ticker list from Wikipedia
make sp500_refresh
# Reset and re-fetch everything
make sp500_reset
# Analyze stocks for period highs/lows
make sp500_analyze
# Fetch and analyze in one command
make sp500_fetch_and_analyzeLaunch the interactive dashboard to visualize stock data:
make dashboardOpen http://localhost:8050 in your browser. The dashboard allows you to:
- Switch between European and S&P 500 markets
- View stocks at period highs or lows
- Select multiple timeframes (1 week to 2 years, plus historical events)
- Compare multiple stocks on the same chart
- Apply moving average smoothing
- View company information (name, sector, country)
# European stocks
docker compose run --rm stockpicker --market europe
docker compose run --rm stockpicker --market europe --analyze
# S&P 500
docker compose run --rm stockpicker --market sp500
docker compose run --rm stockpicker --market sp500 --analyze
# Dashboard
docker compose run --rm --service-ports dashboard| Command | Description |
|---|---|
make fetch |
Fetch/update European stock data |
make refresh |
Fetch with refreshed ticker list from Wikipedia |
make reset |
Reset progress and re-fetch all data |
make analyze |
Find stocks at period highs/lows |
make fetch_and_analyze |
Fetch latest data then run analysis |
| Command | Description |
|---|---|
make sp500_fetch |
Fetch/update S&P 500 stock data |
make sp500_refresh |
Fetch with refreshed ticker list |
make sp500_reset |
Reset progress and re-fetch all data |
make sp500_analyze |
Find stocks at period highs/lows |
make sp500_fetch_and_analyze |
Fetch latest data then run analysis |
| Command | Description |
|---|---|
make dashboard |
Launch interactive dashboard at http://localhost:8050 |
make build |
Build the Docker image |
make clean |
Remove all downloaded data |
make help |
Show all available commands |
Data is stored in market-specific subdirectories:
data/
├── europe/
│ ├── tickers.parquet # STOXX Europe 600 ticker list with metadata
│ ├── analysis.json # Period high/low analysis results
│ ├── fetch_status_europe.json # Progress tracking
│ └── {TICKER}.parquet # OHLCV data (e.g., ASML.AS.parquet)
└── sp500/
├── tickers.parquet # S&P 500 ticker list with metadata
├── analysis.json # Period high/low analysis results
├── fetch_status_sp500.json # Progress tracking
└── {TICKER}.parquet # OHLCV data (e.g., AAPL.parquet)
Each ticker's Parquet file contains:
| Column | Type | Description |
|---|---|---|
| date | date | Trading date |
| open | float | Opening price |
| high | float | Highest price |
| low | float | Lowest price |
| close | float | Closing price |
| volume | int | Trading volume |
| ticker | str | Ticker symbol |
The tickers.parquet file contains:
| Column | Type | Description |
|---|---|---|
| ticker | str | Ticker symbol (with exchange suffix for European stocks) |
| security | str | Company name |
| sector | str | Business sector |
| sub_industry | str | Sub-industry (S&P 500 only) |
| country | str | Country of headquarters |
import polars as pl
# Read European stock data
df = pl.read_parquet("data/europe/ASML.AS.parquet")
# Read S&P 500 stock data
df = pl.read_parquet("data/sp500/AAPL.parquet")
# Read ticker metadata
metadata = pl.read_parquet("data/europe/tickers.parquet")
# Read analysis results
import json
with open("data/europe/analysis.json") as f:
analysis = json.load(f)
# Get stocks at 1-month highs
stocks_at_highs = analysis["periods"]["1_month"]["highest"]
# Returns: [["TICKER", percent_change], ...]European stocks use exchange suffixes for Yahoo Finance compatibility:
| Country | Suffix | Example |
|---|---|---|
| Germany | .DE | SAP.DE |
| France | .PA | MC.PA |
| United Kingdom | .L | SHEL.L |
| Netherlands | .AS | ASML.AS |
| Switzerland | .SW | NESN.SW |
| Spain | .MC | SAN.MC |
| Italy | .MI | ENI.MI |
| Belgium | .BR | ABI.BR |
| Sweden | .ST | VOLV-B.ST |
| Denmark | .CO | NOVO-B.CO |
The analysis covers multiple timeframes:
- Short-term: 1 week, 10 days, 30 days
- Monthly: 1-24 months
- Quarterly: 1-4 quarters
- Yearly: 1-2 years
- Historical Events:
- Since COVID (March 23, 2020)
- Since Ukraine Invasion (February 24, 2022)
- Since Trump Elected (November 5, 2024)
- Since Trump Tariffs (April 2, 2025)