How I built a fully autonomous investment advisory system using open-source Python tools — for $0/month.
Most retail investors operate on vibes. They concentrate in a handful of tickers they believe in, never quantify whether their instincts are working, and react emotionally when prices move.
I wanted something different: a system that applies the same analytical rigour used by quantitative hedge funds — at zero cost. Here is the full architecture and the key things I learned building it.
Stack The Full Architecture
Layer 1: Data → yfinance (free) + Broker API Layer 2: Backtest → quantstats tearsheets vs SPY Layer 3: Optimise → Riskfolio-Lib (4 methods) Layer 4: Dip signals → 5-signal watchlist engine Layer 5: Rules alerts → concentration, stop-loss, drift Layer 6: Delivery → Telegram daily digest Layer 7: Cloud → GitHub Actions (free tier) Layer 8: Memory → advice_journal.csv
1 Start With Honest Measurement
The first script I wrote wasn’t an alert engine — it was a performance analyser. Using quantstats and yfinance, I generated a tearsheet comparing my portfolio against SPY (S&P 500).
import quantstats as qs # Build portfolio returns from your weights + yfinance price data qs.reports.html(portfolio_returns, benchmark='SPY', output='tearsheet.html')
Key metrics: Sharpe ratio, Max Drawdown, Calmar ratio, rolling beta vs benchmark.
2 Mathematical Portfolio Optimisation
I used Riskfolio-Lib to compute optimal weights across four methods. Same tickers, very different risk profiles:
| Method | CAGR | Sharpe | Max Drawdown |
|---|---|---|---|
| Max Sharpe (Markowitz) | 26.35% | 2.23 | −10.21% |
| Risk Parity | 17.26% | 1.63 | −10.66% |
| Min Risk | 9.13% | 1.22 | −7.76% |
| Unoptimised original | 46.77% | 1.15 | −40.44% |
The optimizer runs monthly on GitHub Actions and commits fresh target weights automatically.
3 The 5-Signal Dip-Buy Engine
For every stock on the watchlist, five signals compute independently. Score = number of signals fired:
- Price < 50-day MA by a configurable threshold (e.g. −5%)
- Price < 200-day MA — deeper correction signal
- RSI(14) < 40 — technically oversold
- Price > 15% below 6-month high — significant drawdown
- Price < your average cost basis — natural averaging-down trigger
3–5 ★ STRONG BUY
2 ★ BUY
1 ★ MILD DIP
Multi-signal filtering removes the false positives you get from single-indicator systems.
4 Semiconductor Stack — Full AI Coverage
If you’re bullish on AI, own the whole supply chain — not just one layer:
| Layer | Tickers | Thesis |
|---|---|---|
| Chip designers | NVDA, AMD | AI training & inference |
| AI networking | AVGO | Data centre infrastructure glue |
| Foundry | TSM | Manufactures ~90% of advanced chips |
| Equipment | ASML | Only EUV lithography machine maker on Earth |
| Diversified | SMH ETF | Top 25 semiconductor names in one ticker |
One sector. Multiple moats. Much lower single-stock risk than going all-in on one name.
5 Daily Rules Engine
A separate rules engine checks the live portfolio every morning:
RULES = {
"max_single_position_pct": 15.0, # alert if any stock > 15%
"max_sector_pct": 40.0, # alert if any sector > 40%
"stop_loss_pct": -30.0, # alert if any position < -30%
"min_position_value": 200.0, # alert if position < $200 (dead weight)
"drift_threshold_pct": 5.0, # alert if drift from target > 5%
}
Every morning the digest lands in Telegram:
Portfolio Advisory — 2026-05-14
BUY-THE-DIP ALERTS
*** MSFT $408 — Below 200d MA, RSI 38, -20% off 6m high
Suggest +$500 (current 14% → cap 15%)
** META $603 — Below 200d MA, -18% off high
Critical Alerts
[CONCENTRATION] TSLA is 62.1% — REDUCE
Rebalance Suggestions
AAPL: 3.2% vs target 12.0% — ADD 8.8%
Logged 21 alerts to advice_journal.csv
6 The Advice Journal — Most Underrated Feature
Every alert is logged to an append-only CSV with the price at alert time:
date, ticker, alert_type, score, price_now, suggested, notes, your_action, price_30d, return_30d
After 30 days, one command backfills outcomes:
python journal.py --backfill # fetches price 30 days after each alert python journal.py --summary # win rate, avg return by alert type
7 Cloud Deployment — Free
Two GitHub Actions workflows power everything:
- advisor.yml — weekdays 13:30 UTC. Syncs live portfolio, runs signals, sends Telegram digest, commits updated journal. Runtime: ~40 seconds.
- optimize.yml — 1st of every month 12:00 UTC. Re-runs Riskfolio-Lib, commits new target weights. The daily bot picks them up automatically.
GitHub free tier = 2,000 compute minutes/month. This system uses ~110.
# .github/workflows/advisor.yml (simplified)
on:
schedule:
- cron: '30 13 * * 1-5' # weekdays at market open
permissions:
contents: write # needed to commit the journal back
# Steps: checkout → install deps → rebuild .env from secrets
# → sync portfolio → run advisor → commit journal [skip ci]
8 Key Design Principles
| Principle | Why it matters |
|---|---|
| Advisory-only, no auto-trading | Human error-correction loop is preserved |
| Read-only API keys | Worst-case breach = data exposure, not account drain |
| [skip ci] on bot commits | Prevents journal commit re-triggering the workflow |
| Separate daily / monthly workflows | Daily: 15s install. Monthly: 3-min heavy deps |
| Cache instrument catalogs | Avoids re-fetching 15,000+ broker symbols every day |
| Measure first, build second | Tearsheets before alert rules — you need the honest baseline |
9 DCA + Dip = No Forced Selling
Instead of rebalancing by selling winners:
- Keep your conviction positions
- Let their weight dilute naturally as you add to others on dips
- Only add when dip signals fire — not on emotion
- Set a position cap per stock in your watchlist config (e.g. max 12%)
Result: the portfolio diversifies over time without a single forced sell decision.
Tools All Free, All Open Source
quantstats
Riskfolio-Lib
pandas
numpy
requests
python-dotenv
Telegram Bot API
GitHub Actions
Files The Codebase
portfolio_analysis.py → tearsheet generator (quantstats) portfolio_optimize.py → 4-method Riskfolio optimizer [broker]_sync.py → live portfolio sync via broker API advisor.py → rules engine + Telegram delivery watchlist.py → 5-signal dip detector journal.py → advice logger + 30-day backfiller .github/workflows/ advisor.yml → daily weekday cloud run optimize.yml → monthly target-weights refresh
★ The One-Line Takeaway
“Build the measurement system first. Act on the data second. Build more features third.”
Most people skip straight to step three.
Stack: Python · quantstats · Riskfolio-Lib · yfinance · pandas · Telegram Bot API · GitHub Actions




