← All work
Side project  Write-up

InvestHub

A practice space for learning how the stock market works. Look up companies, compare them side by side, build a portfolio with pretend money, and ask an AI to explain what you're looking at.

903
companies in the bundled browse list
6
pages: explore, stock, compare, portfolio, account, home
5
logo URLs tried in order
0
AI keys required to use it
Role · Solo Stack · Python · Flask · Firebase Auth · yfinance · Cohere · vanilla JS
Public repo
Jun 14, 2026
Commits
1cleaned-up export
Code
4.0k lines994 Python, 3.0k HTML
Files
12
01 · Timeline

One export commit

InvestHub went public as a single cleaned-up commit, so GitHub only shows the publish date.

  1. Jun 14, 2026
    Published

    Firebase session login, stock search and detail pages, side-by-side comparison, the practice portfolio, an options pricing helper, optional AI explanations, and a bundled list of companies to browse.

    GitHub12 files · +4,970 lines7f0277b ↗
02 · What it does

A practice sandbox for learning the market

Look things up

Search any ticker for its quote, company profile, and price history, or browse a bundled list of 903 companies by sector.

Compare

Put companies next to each other on valuation (trailing and forward P/E), risk (beta), dividend yield, sector, and market cap.

Practice

Build a portfolio of simulated holdings and review your strategy. A helper looks up option mark prices from Yahoo's options chains.

Ask why

With a Cohere key configured, you can ask for a plain-English explanation of a stock, a comparison, or your portfolio. Without one, everything else still works and the AI buttons tell you it isn't set up.

03 · How it's built

One Flask app, data fetched on demand

Flask serves both the pages and the JSON endpoints behind them. Firebase handles identity through session cookies, and market data comes from Yahoo Finance when you ask for it. A batch endpoint fetches several tickers at once and computes the day's change and percent change from the previous close, so a page showing several stocks loads them in one request.

Logos needed their own fallback logic. No single free source has every company, so the app builds an ordered list of candidates from the company's website and ticker and lets the browser fall through them until one loads.

app.py
def make_logos(ticker, site=""):
    t = (ticker or "").upper()
    dom = get_dom(site)
    urls = []
    if dom:
        urls.append(f"https://logo.clearbit.com/{dom}")
    urls.append(f"https://storage.googleapis.com/iex/api/logos/{t}.png")
    if dom:
        urls.append(f"https://api.faviconkit.com/{dom}/128")
        urls.append(f"https://www.google.com/s2/favicons?domain={dom}&sz=128")
    urls.append(f"https://www.google.com/s2/favicons?domain={t}.com&sz=128")
    return list(dict.fromkeys(urls))   # dedupe, keep order

Condensed from app.py; the original dedupes with an explicit loop.

04 · What I'd do next

Making the portfolio real

Read the code.

Runs locally with a Firebase project; the Cohere key is optional.

More work