Automated ETF analysis and portfolio optimization tool. Fetches price history, forecasts future prices, scores stocks, optimizes portfolio allocation, and delivers results via Telegram.
yfinance ──► GARCH/Prophet forecast ──► Analyzer (score + metadata) ──► GA/CVXPY optimizer ──► Telegram
- Downloader — fetches up to 5 years of daily price history and ticker metadata from Yahoo Finance
- Predictor — forecasts prices 90 days forward using either GARCH(1,1) or Facebook Prophet
- Analyzer — scores each ETF (growth direction, volatility, profitability, sector weights)
- Optimizer — allocates a fixed budget across the scored ETFs using a genetic algorithm or CVXPY
- Notifier — sends forecast charts and a text recommendation to a Telegram chat
# 1. Install dependencies
poetry install
# 2. Create .env with required credentials (see Configuration table below)
cp .env.example .env # then fill in values
# 3. Run the full pipeline
python main.pyAll settings are loaded from environment variables or a .env file.
| Variable | Required | Default | Description |
|---|---|---|---|
TELEGRAM_TO |
yes | — | Telegram chat ID to send results to |
TELEGRAM_TOKEN |
yes | — | Telegram bot token |
GET_AND_INCREMENT_COUNTER_URL |
yes | — | URL of the counter service (used by stock picker) |
APP_SCRIPT_ID |
yes | — | Google Apps Script ID for statistics tracking |
PREDICTER |
no | garch |
Forecast backend: garch or prophet |
OPTIMIZER |
no | ga |
Allocation backend: ga (genetic algorithm) or cvxpy |
Override via GA__<FIELD> env var (double underscore delimiter).
| Variable | Default | Description |
|---|---|---|
GA__POPULATION |
120 |
Population size |
GA__GENERATIONS |
350 |
Number of generations |
GA__TOURNAMENT_SIZE |
5 |
Tournament selection size |
GA__MUTATION_RATE |
0.55 |
Per-individual mutation probability |
GA__CROSSOVER_RATE |
0.35 |
Per-individual crossover probability |
GA__MAX_SECTOR_CONCENTRATION |
0.40 |
Max portfolio weight in one sector |
| Variable | Default | Description |
|---|---|---|
CVXPY__COMPANY_MAX_EXPOSURE |
0.10 |
Max weight per individual holding |
CVXPY__RISK_GAMMA |
0.01 |
Risk aversion coefficient |
# Use GARCH(1,1) forecasting (default — faster, no external model training)
PREDICTER=garch python main.py
# Use Facebook Prophet (slower, captures seasonality)
PREDICTER=prophet python main.py
# Optimize with genetic algorithm (default)
OPTIMIZER=ga python main.py
# Optimize with CVXPY (mean-variance, deterministic)
OPTIMIZER=cvxpy python main.py# Run tests
poetry run pytest
# Lint
poetry run ruff check .
# Type check
poetry run mypy src tests
# Install pre-commit hooks (run once after cloning)
poetry run pre-commit install
# Run hooks manually against all files
poetry run pre-commit run --all-files