AI-powered market analysis that delivers pre-market reports and trade ideas every trading day — via Telegram, web dashboard, CLI, or GitHub Actions.
- Stock Assistant
- Table of Contents
- What It Does
- Start Here
- Option A — GitHub Actions (default, no local setup)
- Option B — Web Dashboard & API Server
- Option C — Telegram Bot (interactive)
- Option D — CLI / Cron
- Environment Setup (local only)
- API Reference
- Telegram Bot Commands
- Strategies
- Switching LLM Providers
- Project Structure
- API Keys
- Disclaimer
- Fetches live prices (indices, commodities, crypto) via yfinance
- Pulls recent financial headlines from NewsAPI
- Feeds both into an LLM (Gemini, Claude, GPT-4o, Llama) to generate analysis and trade ideas
- Screens your watchlist for setups using configurable strategies
- Delivers to your Telegram, a web dashboard, a CLI, or GitHub Actions artifacts
- The project now has a canonical deterministic pipeline:
signals -> candidate merge -> signal synthesis -> portfolio decisions -> execution proposal - Canonical portfolio state is stored separately from messaging preferences:
- preferences live in SQLite in
core/preferences.py - portfolio profile / snapshot live in SQLite in
core/portfolio_store.py
- preferences live in SQLite in
- The pipeline is available through:
POST /api/pipeline/runPOST /api/proposals/buildGET /api/proposals/latest- structured report generation
- watchlist push / Telegram watchlist summaries
- Telegram
/reportand/ideas
- Presentation is increasingly renderer-driven:
- canonical markdown renderers now live in
app/renderers/proposal_renderer.py - report markdown, market stock ideas, strategy reports, and Telegram proposal text now reuse those renderers
- canonical markdown renderers now live in
- Legacy compatibility paths still exist, but are now explicitly marked:
build_screen_response()/format_screen_response()remain for older score-first consumersbuild_personalized_report()remains as a compatibility wrapper, but now delegates to canonical pipeline helpersbuild_structured_watchlist_report()remains as a migration fallback for symbols that do not yet map cleanly into canonical proposal output
- Remaining work is focused on:
- AI reliability for structured outputs
- continuing to retire legacy score-only presentation helpers
- expanding regression coverage
Choose a run option first.
- If you want the easiest setup, use Option A: GitHub Actions. You can configure notifications and report settings entirely in the GitHub web UI.
- If you want a browser dashboard or interactive bot, use Option B/C and then do Environment Setup.
- If you want terminal or cron usage, use Option D and then do Environment Setup.
Recommended for most users. Fork the repo, add your API keys in the GitHub UI, and get a daily Telegram report every trading day at 8:30 AM New York time — no terminal, no server, no code changes needed.
Click Fork at the top of this page to create your own copy.
In your forked repo: Settings → Secrets and variables → Actions → Secrets → New repository secret
Add these secrets:
| Secret name | Where to get it | Required? |
|---|---|---|
GEMINI_API_KEY |
aistudio.google.com — free | One LLM key required |
ANTHROPIC_API_KEY |
console.anthropic.com | (or) |
OPENAI_API_KEY |
platform.openai.com | (or) |
GROQ_API_KEY |
console.groq.com — free | (or) |
NEWS_API_KEY |
newsapi.org/register — free | Yes |
TELEGRAM_BOT_TOKEN |
Message @BotFather on Telegram → /newbot |
For Telegram delivery |
TELEGRAM_CHAT_ID |
Message @userinfobot on Telegram to find yours |
For Telegram delivery |
In your forked repo: Settings → Secrets and variables → Actions → Variables → New repository variable
| Variable name | Example value | Description |
|---|---|---|
WATCHLIST |
AAPL,NVDA,^GSPC,GC=F |
Tickers to track (default: ^GSPC,^IXIC,BTC-USD,GC=F,CL=F) |
STRATEGIES |
breakout,pullback |
Screening strategies (default: breakout) |
REPORT_LANGUAGE |
zh |
en or zh (default: en) |
LLM_MODEL |
gemini/gemini-2.0-flash |
Override the LLM model. If omitted, the model is auto-selected from whichever API key is set (Gemini → gemini/gemini-2.0-flash, Anthropic → claude-haiku-4-5-20251001, OpenAI → gpt-4o-mini, Groq → groq/llama-3.3-70b-versatile). |
LLM_REQUEST_DELAY_S |
4 |
Seconds to wait between LLM calls. Set to 4 when using a free-tier provider (Gemini, Groq) to stay within the ~15 RPM limit. Default: 0 (no delay). |
Go to the Actions tab of your fork. If prompted, click "I understand my workflows, enable them".
Run the workflow once from GitHub to confirm your secrets and settings are correct.
Go to Actions → Stock Report → Run workflow. You can override any settings for that one run:
| Input | Description |
|---|---|
watchlist |
Comma-separated tickers, e.g. AAPL,NVDA,^GSPC |
strategies |
e.g. breakout,pullback,commodity_macro |
language |
en or zh |
sections |
watchlist / watchlist,market / watchlist,market,commodity |
send_telegram |
true to send to Telegram, false to skip (default: false) |
After that first manual run, the workflow (.github/workflows/stock-report.yml) will keep running automatically based on the repo settings:
- Every trading day (Mon–Fri) at 8:30 AM New York time
- Uses your repository secrets and variables by default
- Saves the report as a downloadable artifact in the Actions run
- Sends the report to Telegram only if
TELEGRAM_BOT_TOKENandTELEGRAM_CHAT_IDare set andsend_telegramistrue
- Checks out your repo
- Installs Python 3.11 + dependencies
- Runs the report script with all env vars injected
- Writes the report to the GitHub Actions Job Summary (visible on the run page)
- Uploads
.md+.jsonreport files as a downloadable artifact (kept 30 days) - Sends the report to your Telegram chat (optional — skipped if tokens are not set)
Edit .github/workflows/stock-report.yml in the GitHub web editor (click the pencil icon). Change the cron line:
schedule:
- cron: "30 13 * * 1-5" # Mon–Fri 8:30 AM ET (13:30 UTC)
# - cron: "0 14 * * 1" # Weekly — every Monday 9:00 AM ET
# - cron: "0 13 * * 1-5" # Mon–Fri 8:00 AM ETNote on daylight saving time: GitHub Actions cron runs in UTC.
13:30 UTC= 8:30 AM EST (Nov–Mar) and 9:30 AM EDT (Mar–Nov). The report always arrives before or at the opening bell either way.
A local server with a browser UI and full REST API. Also activates scheduled Telegram pushes and the interactive Telegram bot.
Requires: Local setup first.
cd stock-assistant
python3 -m venv venv
source venv/bin/activate # macOS / Linux
# venv\Scripts\activate # Windows
pip install -r requirements.txt
python main.py| URL | Description |
|---|---|
http://localhost:8000 |
Web dashboard |
http://localhost:8000/docs |
Interactive API explorer (Swagger UI) |
The server also starts:
- APScheduler — runs per-user scheduled Telegram pushes
- Telegram bot — starts polling if
TELEGRAM_BOT_TOKENis set
A conversational bot you can message any time to get on-demand reports and manage your watchlist.
Requires: The web server running (Option B). The bot starts automatically.
Setup:
- Message
@BotFather→/newbot→ copy the token intoTELEGRAM_BOT_TOKENin.env - Message
@userinfoboton Telegram to get your chat ID → setTELEGRAM_CHAT_ID - Start the server:
python main.py - Find your bot on Telegram and send
/start
How scheduled pushes work:
Set up your schedule with a few commands:
/schedule daily → every day at your push time
/schedule twice → twice a day (push time + 8 h)
/schedule weekly → every Monday at your push time
/pushtime 08:30 → set push time (24h)
/timezone America/New_York
Each scheduled push sends messages in order:
- Watchlist summary — trade ideas from your tickers (always included)
- US market overview — indices, gold, oil, Bitcoin (add with
/sections watchlist,market) - Commodity screen — top commodity setups (add with
/sections watchlist,market,commodity)
See all bot commands in the Telegram Bot Commands section.
Generate a report from the terminal — no server required. Useful for scheduled tasks or quick spot-checks.
Requires: Local setup first.
cd stock-assistantMinimal — print report to stdout:
python scripts/run_report.pyCustom watchlist and Telegram notification:
python scripts/run_report.py \
--watchlist "AAPL,NVDA,^GSPC,GC=F" \
--strategies "breakout,pullback" \
--language en \
--sections "watchlist,market" \
--send-telegramAll flags (each has a matching env var as fallback):
| Flag | Env var | Default | Description |
|---|---|---|---|
--watchlist |
WATCHLIST |
^GSPC,^IXIC,BTC-USD |
Comma-separated tickers |
--strategies |
STRATEGIES |
breakout |
Comma-separated strategy IDs |
--language |
REPORT_LANGUAGE |
en |
en or zh |
--sections |
REPORT_SECTIONS |
watchlist,market |
watchlist, market, commodity |
--schedule |
— | weekly |
LLM prompt context label |
--send-telegram |
SEND_TELEGRAM=true |
off | Send to Telegram |
--chat-id |
TELEGRAM_CHAT_ID |
— | Override Telegram chat ID |
Reports are saved to reports/github-actions/<report_id>.md and .json.
macOS / Linux cron example (every weekday at 8:30 AM local time):
30 8 * * 1-5 cd /path/to/stock-assistant && venv/bin/python scripts/run_report.py --send-telegramRequired for Options B, C, and D.
cd stock-assistant
python3 -m venv venv
source venv/bin/activate # macOS / Linux
# venv\Scripts\activate # Windows
pip install -r requirements.txtcp .env.example .envEdit .env:
# LLM_MODEL is optional — if omitted, the model is auto-selected from whichever key is set below.
# GEMINI_API_KEY=your_key_here # → gemini/gemini-2.0-flash (free tier)
# ANTHROPIC_API_KEY=your_key_here # → claude-haiku-4-5-20251001
# OPENAI_API_KEY=your_key_here # → gpt-4o-mini
# GROQ_API_KEY=your_key_here # → groq/llama-3.3-70b-versatile (free tier)
# To override the auto-selected model:
# LLM_MODEL=gemini/gemini-2.0-flash
# Free-tier rate limit guard (set to 4 for Gemini/Groq free tiers — ~15 RPM limit)
# LLM_REQUEST_DELAY_S=4
# News headlines
NEWS_API_KEY=your_key_here
# Telegram — optional. Remove or leave blank to skip Telegram delivery.
# TELEGRAM_BOT_TOKEN=your_bot_token
# TELEGRAM_CHAT_ID=your_chat_idpython -m core.market_data # prints a live market price snapshot
python -m core.news # prints recent headlines
python -m core.ai_analysis # prints an LLM-generated summaryAll endpoints available on the running local server.
| Method | Path | Description |
|---|---|---|
GET |
/ |
Web dashboard |
GET |
/api/market |
Raw market snapshot (no LLM) |
GET |
/api/summary |
AI-generated global market summary |
GET |
/api/analyze/{ticker} |
AI analysis for one ticker. Optional: ?strategy=emotion_cycle |
GET |
/api/strategies |
List strategies. Optional: ?capability=analysis|screen|backtest |
GET |
/api/strategies/{id} |
Full strategy config + documentation |
GET |
/api/screen |
Legacy-compatible screener view. Params: strategy, asset_type, top_n, tickers |
GET |
/api/candidates/latest |
Recommended canonical replacement for saved-profile/watchlist candidate views |
GET |
/api/profile/{profile_id} |
Get the canonical portfolio user profile |
PUT |
/api/profile/{profile_id} |
Update the canonical portfolio user profile |
GET |
/api/portfolio/{profile_id} |
Get the canonical portfolio snapshot |
PUT |
/api/portfolio/{profile_id} |
Update the canonical portfolio snapshot |
POST |
/api/signals/run |
Run the v2 signal pipeline and return merged candidates |
POST |
/api/pipeline/run |
Canonical deterministic pipeline output |
POST |
/api/proposals/build |
Canonical proposal output for a request payload |
GET |
/api/proposals/latest |
Canonical proposal output from saved profile preferences |
GET |
/api/backtest |
Daily backtest. Params: ticker, strategy, start_date, end_date, cost params |
GET |
/api/report/{profile_id} |
Generate + save a full report for a profile |
GET |
/api/reports/{profile_id} |
List saved reports |
GET |
/api/reports/{profile_id}/{report_id} |
Load a saved report |
GET |
/api/reports/{profile_id}/{report_id}/download |
Download as ?format=md or ?format=json |
GET |
/api/preferences/{profile_id} |
Get profile preferences |
PUT |
/api/preferences/{profile_id} |
Update profile preferences |
Interactive docs: http://localhost:8000/docs
/api/screen note:
- This endpoint is kept for backward compatibility with older score-first consumers.
- It is no longer the recommended entry point for new product work.
- Prefer
/api/candidates/latestas the first replacement for watchlist/profile-driven UI. - Prefer
/api/pipeline/runwhen the client needs a custom ticker universe and full pipeline output. - Prefer
/api/proposals/buildor/api/proposals/latestwhen only proposal output is needed.
| Command | Description |
|---|---|
/start |
Welcome message and command reference |
/report |
Full AI dashboard for your watchlist |
/fullreport |
Full saved markdown report for your watchlist |
/summary |
Global market overview (indices, commodities, crypto) |
/analyze TICKER |
Deep-dive on one ticker, e.g. /analyze AAPL |
/ideas [strategy] [asset_type] [top_n] |
Ranked trade ideas, e.g. /ideas commodity_macro commodity 5 |
| Command | Description |
|---|---|
/watch TICKER |
Add a ticker, e.g. /watch NVDA |
/unwatch TICKER |
Remove a ticker |
/watchlist |
Show watchlist and all current settings |
| Command | Description |
|---|---|
/schedule daily|twice|weekly|off |
Push frequency |
/timezone ZONE |
IANA timezone, e.g. /timezone America/New_York |
/pushtime HH:MM |
Push time in 24h local time, e.g. /pushtime 08:30 |
/sections watchlist,market,commodity |
Which sections are included in scheduled pushes |
/strategy breakout,pullback |
Default strategies used for screening |
/pushmode simple|full |
Scheduled push detail level |
/reportmode summary|ideas|summary+ideas |
What watchlist reports contain |
/language en|zh |
Report language |
/risk conservative|moderate|aggressive |
Portfolio risk profile used by the canonical proposal flow |
/maxpos N |
Max single-position limit, e.g. 5 for 5% |
/horizon swing|day|longterm |
Preferred holding horizon |
| ID | Asset types | Capabilities | Description |
|---|---|---|---|
breakout |
stock | analysis, screen, backtest | Price breaks above recent range with volume |
pullback |
stock | analysis, screen, backtest | Buy the dip in an uptrend |
commodity_macro |
commodity | analysis, screen, backtest | Trend + macro filter for commodities |
trend_following |
stock, commodity | analysis, screen, backtest | Multi-timeframe trend alignment |
mean_reversion |
stock, commodity | analysis, screen, backtest | Oversold bounce in a range |
donchian_breakout |
stock, commodity | analysis, screen, backtest | Donchian channel breakout |
emotion_cycle |
stock, commodity | analysis only | Sentiment and fear/greed positioning |
Strategy definitions live in strategies/ — each has a .yaml (metadata) and .md (LLM prompt documentation). You can add your own by following the same format.
LLM_MODEL is optional. If you omit it, the model is auto-selected from whichever API key is present in the environment:
| API key set | Auto-selected model |
|---|---|
GEMINI_API_KEY |
gemini/gemini-2.0-flash (free tier) |
ANTHROPIC_API_KEY |
claude-haiku-4-5-20251001 |
OPENAI_API_KEY |
gpt-4o-mini |
GROQ_API_KEY |
groq/llama-3.3-70b-versatile (free tier) |
To override, set LLM_MODEL explicitly:
LLM_MODEL=gemini/gemini-2.0-flash # Google Gemini (free)
LLM_MODEL=anthropic/claude-sonnet-4-6 # Anthropic Claude
LLM_MODEL=gpt-4o # OpenAI GPT-4o
LLM_MODEL=groq/llama-3.3-70b-versatile # Groq Llama (free, fast)All routing is handled by LiteLLM — no code changes needed.
Free-tier rate limits: Gemini and Groq free tiers allow ~15 RPM. Each report generation makes 3–5 LLM calls. Set LLM_REQUEST_DELAY_S=4 in .env to add a 4-second gap between calls and avoid hitting the limit.
stock-assistant/
├── main.py # FastAPI app + APScheduler + Telegram bot startup
├── requirements.txt
├── .env.example
├── scripts/
│ ├── run_report.py # Standalone CLI — used by GitHub Actions and cron
│ └── validate_v2_pipeline.py # Validation helper for the v2 canonical pipeline
├── app/
│ ├── renderers/
│ │ └── proposal_renderer.py # Canonical markdown renderers for proposals/reports
│ └── services/
│ ├── research_service.py # Market summaries, analysis, and legacy-compatible APIs
│ ├── report_service.py # Report composition, persistence, push messages
│ ├── signal_service.py # Strategy signal execution
│ ├── candidate_merge_service.py # Multi-strategy candidate aggregation
│ ├── signal_synthesis.py # Per-candidate synthesis layer
│ ├── portfolio_engine.py # Portfolio-aware decision engine
│ ├── execution_planner.py # Proposal/execution plan generation
│ └── proposal_service.py # Canonical proposal and latest-candidate helpers
├── core/
│ ├── market_data.py # yfinance price fetching
│ ├── news.py # NewsAPI with 1-hour cache
│ ├── ai_analysis.py # LiteLLM prompt building + LLM calls
│ ├── strategy_registry.py # Loads YAML + MD strategy definitions
│ ├── scheduler.py # APScheduler per-user push jobs
│ ├── preferences.py # Per-user preference store (SQLite + legacy JSON migration)
│ └── portfolio_store.py # Canonical portfolio profile/snapshot store (SQLite)
├── domain/schemas/ # Pydantic schemas for signals, proposals, reports, portfolio
├── strategies/ # Strategy definitions (YAML + Markdown)
├── backtesting/engine.py # Deterministic daily backtest engine
├── channels/telegram/bot.py # Telegram command handlers
├── data/preferences.db # Persisted user preferences
├── data/portfolio.db # Persisted portfolio profiles and snapshots
├── reports/<profile_id>/ # Saved reports (auto-created)
├── tests/ # Regression coverage for the v2 pipeline and API/bot surfaces
└── web/index.html # Dashboard frontend
.github/
└── workflows/
└── stock-report.yml # GitHub Actions: scheduled + manual delivery
| Service | URL | Notes |
|---|---|---|
| Google Gemini | aistudio.google.com | Free, instant |
| Anthropic Claude | console.anthropic.com | Pay-as-you-go |
| OpenAI | platform.openai.com | Pay-as-you-go |
| Groq | console.groq.com | Free tier, fast |
| NewsAPI | newsapi.org/register | Free, 100 req/day |
| Telegram Bot | Message @BotFather on Telegram |
Free, instant |
All AI-generated analysis is for informational purposes only. This is not financial advice. Always do your own research before making any investment decisions.