stonks
Daily financial news + market data analysis. Fetches crypto/equity/futures prices (yfinance-equivalent + Finnhub) and market news (RSS), asks Claude for an investment-advice briefing, and posts it to Discord — on a systemd user timer.
See PLAN.md for the full design, cost analysis, and rationale.
Build
go build -o stonks ./cmd/stonks
Configure
mkdir -p ~/.config/stonks
cp config/config.yaml ~/.config/stonks/config.yaml
cp config/secrets.env.example ~/.config/stonks/secrets.env
chmod 600 ~/.config/stonks/secrets.env
$EDITOR ~/.config/stonks/secrets.env # fill in FINNHUB_API_KEY, DISCORD_WEBHOOK_URL, (ANTHROPIC_API_KEY if using api mode)
If analyzer.mode: subscription (the default), install Claude Code and authenticate once:
claude login
Multi-agent mode (optional)
analyzer.advisors in config.yaml lists additional CLI-based AI analysts (Gemini, Codex) that get the same fetched data and produce an independent take; their output is appended to Claude's prompt as extra context (Claude still writes the one final report — see PLAN.md § "Claude integration"). Each needs to be installed and logged in separately:
gemini # first run walks through auth
codex login
A failing or unauthenticated advisor is logged as a warning and simply excluded — it never fails the run. Remove an entry (or the whole advisors: list) to disable multi-agent mode.
Archiving runs (optional)
Set archive.enabled: true in config.yaml to save each run's fetched data, full prompt (including any advisor section), raw and parsed analysis to archive.dir (default ~/.local/state/stonks/runs), one timestamped subdirectory per run: fetched.json, prompt.txt, analysis_raw.md, report.md, recommendations.json. Useful for debugging prompt or parsing issues without reproducing a live run. Off by default — these accumulate over time with no automatic cleanup.
Install as a user-scope systemd service
mkdir -p ~/.local/bin ~/.config/systemd/user
go build -o ~/.local/bin/stonks ./cmd/stonks
cp systemd/stonks.service systemd/stonks.timer ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now stonks.timer
loginctl enable-linger "$USER" # keep the timer firing while logged out
Run it once manually to verify before waiting for the timer:
systemctl --user start stonks.service
journalctl --user -u stonks.service -f
Adding a new fetcher / output
Implement the fetch.Fetcher / output.Output interface in a new file under internal/fetch / internal/output, self-register it in an init() (see internal/fetch/rss.go for an example), add the name to config.yaml, and rebuild. See PLAN.md § "Modularity in Go" for why this is compile-time rather than a runtime plugin system.