Twenty tabs of market analysis in one Streamlit app: technical indicators, news sentiment, fundamentals, backtesting, Monte Carlo, options chains, crypto and portfolio tooling — all running on a data source that needs no API key.
The signals this system produces come from heuristic scoring — a hand-chosen 40% technical / 30% sentiment / 30% fundamental weighting — and a model fitted to past prices. They are not predictions of future returns.
There is no backtested profitability figure, no claimed accuracy, and no live-trading validation anywhere in this project, because none has been produced. The scoring weights and signal thresholds were chosen by hand; they were not fitted, tuned or cross-validated against outcomes.
This disclaimer is not confined to this page. It is pinned permanently in the running app's sidebar, so no tab can be screenshotted without it.
The ML tab reports a cross-validation score on whatever data you load. That is an in-sample fit statistic, not a forward-return accuracy, and reading it as "the model is right X% of the time about the future" would be wrong. The app labels it accordingly.
This project is usually described as a "35-module platform". Counting the files in modules/ gives 34, and three of those are not modules at all. The real figure is 31 importable modules plus app.py.
tabs_new.py, tabs_new2.py and tabs_new3.py cannot be imported. Their own headers say "paste this content at the BOTTOM of app.py"; they reference st without importing Streamlit and call st.tabs() at module scope. That content is already present in app.py from line 1119 — they are superseded working copies.
They were kept on disk rather than deleted, and the test suite now asserts they are fragments, so nobody mistakes them for live modules later. Correcting the count downward was the honest option; the alternative was letting a number stand that the directory does not support.
| # | Module | Role |
|---|---|---|
| 1 | data_fetcher | yfinance OHLCV + company info; demo/live mode selection |
| 2 | technicals | RSI, MACD, Bollinger, MAs, Stochastic, ATR, support/resistance |
| 3 | sentiment | VADER over news headlines; RSS fallback with no key |
| 4 | fundamentals | P/E, revenue growth, margin, ROE scoring |
| 5 | scorer | Weighted final score → signal |
| 6 | ml_model | Random Forest + Gradient Boosting on engineered features |
| 7 | backtester | RSI / MA Crossover / MACD / Buy & Hold, with stops and targets |
| 8 | monte_carlo | Geometric Brownian Motion price-path simulation |
| 9 | risk_calculator | Position sizing, R:R, portfolio risk, scenarios |
| 10 | strategy_builder | User-defined rule strategies, persisted to disk |
| 11 | screener | Multi-criteria universe screening |
| 12 | multiframe | Multi-timeframe agreement analysis |
| 13 | correlation | Return correlation, clustering, diversification score |
| 14 | portfolio | Position tracking and P&L |
| 15 | watchlist | Watchlist persistence and scanning |
| 16 | journal | Trade journal with win/loss statistics |
| 17 | alerts | Price/indicator alert rules and triggers |
| 18 | notifier | Desktop toast + SMTP email delivery |
| 19 | voice_alerts | Offline text-to-speech briefings |
| 20 | live_dashboard | Live quotes, intraday, index summary |
| 21 | market_summary | Market mood, buy/sell lists, daily briefing |
| 22 | heatmap | Sector performance heatmap |
| 23 | earnings | Earnings dates and history |
| 24 | dividends | Dividend history and yield scanning |
| 25 | insider_tracker | Insider transactions and institutional holders |
| 26 | options | Options chain, expiries, summary metrics |
| 27 | crypto | Crypto quotes, scanning, indicators |
| 28 | news_aggregator | Sector/ticker news aggregation |
| 29 | ai_chat | Optional Anthropic-backed chat; local fallback without a key |
| 30 | exporter | Excel / CSV export |
| 31 | theme | Theme configuration and chart palettes |
| — | tabs_new, tabs_new2, tabs_new3 | Paste fragments, not modules — already inlined in app.py |
| Source | Key required | Used for |
|---|---|---|
| yfinance | No | Primary — OHLCV, company info, options, crypto |
| Public RSS | No | News for sentiment (default path) |
| NewsAPI | Optional | Richer news; free tier 100/day |
| Alpha Vantage | Optional | Secondary quotes; free tier 25/day |
| Anthropic | Optional | AI Chat tab only; local fallback without it |
The app is fully functional with no keys and an empty .env. yfinance requires no account, which is why it is the primary source rather than a paid feed.
STOCK_DEMO_MODE=false (default)STOCK_DEMO_MODE=truedata/demo/The snapshot is captured through the same yfinance path the live app uses, by scripts/capture_demo_snapshot.py, and stamped with its capture date. Bundled: AAPL, AMZN, MSFT, NVDA, TSLA — 251 rows each, captured 2026-08-11.
No fabricated prices exist anywhere in this project. There is no code path that returns snapshot data without setting the is_demo flag, and the test suite asserts that a missing snapshot returns an error rather than inventing values.
The most valuable part of this pass had nothing to do with forecasting.
| # | Issue | Fix |
|---|---|---|
| 1 | API keys edited directly into config.py | All keys read from environment / .env; .env.example added |
| 2 | anthropic_key.json and notifier_config.json held a plaintext API key and a Gmail app password, with no ignore file | .gitignore added covering both plus all runtime state |
| 3 | ai_chat.load_api_key preferred the file over the environment | Environment now wins, so .env can override a stale file |
| 4 | pyttsx3 imported by voice_alerts but absent from requirements | Added, along with python-dotenv |
| 5 | No offline mode — unusable without internet | Demo mode over a real recorded snapshot |
| 6 | Nothing on screen distinguished live from stale data | is_demo threaded through; sidebar badge + per-analysis banner |
| 7 | No standing risk disclaimer | Permanent sidebar disclaimer |
| 8 | No tests | tests/test_smoke.py, 30 checks |
| Check | Result |
|---|---|
| All 31 modules import cleanly | Pass |
| Smoke suite, demo mode | 30 / 30 |
| Smoke suite, live mode | 27 / 27 |
| Streamlit serves | HTTP 200 |
/_stcore/health | ok |
| Missing snapshot returns error, not fabricated data | Pass |
| End-to-end on the real snapshot | AAPL, 251 rows → RSI 44.27 (Neutral) → HOLD, score 50.3, risk MEDIUM |
The three extra checks in demo mode are snapshot-specific assertions that cannot run against a live feed.
python -m venv .venv && .venv\Scripts\activate pip install -r requirements.txt copy .env.example .env # optional — every key is optional streamlit run app.py python -m tests.test_smoke # no network, no keys needed
Rendered by the application's own charting code on the bundled demo snapshot — 251 rows of AAPL, captured 2026-08-11. The demo-data label is produced by the app, not added to the screenshot afterwards.
Financial tooling makes overclaiming easy and consequential. The engineering worth showing here is the demo/live split and the credential cleanup, not a made-up accuracy number.