A production-deployed stock monitoring app with live market data, interactive charts, and per-user watchlists. Built with Flask and Plotly Dash, backed by the Massive.com market API and PostgreSQL.
A full-stack web app taken end to end β designed, built, deployed, and running live with a public demo. It shows real third-party API integration, response caching, authentication, schema migrations, and a modular Flask architecture, all wired together and shipped to production.
| Β | Β |
|---|---|
| URL | stockwatch-cqzs.onrender.com |
| Demo login | demo@stockwatch.dev / Demo123! |
β±οΈ Hosted on Renderβs free tier β the first visit after idle may take ~30s to cold-start, then responds normally. The demo account is shared for evaluation and can be reset safely.
| Stock dashboard |
|---|
![]() |
| Watchlist management |
|---|
![]() |
/health endpoint for monitoring.render.yaml and app.yaml blueprints provision the database and web service automatically.| Feature | Description |
|---|---|
| π Authentication | Registration and login via Flask-Login with hashed passwords |
| π Live market data | Current prices and company details from the Massive.com REST API |
| π Watchlist management | Create and delete multiple watchlists; add or remove tickers |
| π Interactive charts | Line charts with volume overlays β Today shows intraday session bars; 5DβMAX use daily history |
| π’ Company fundamentals | Logo, market cap, exchange, website, description, and day-over-day price change |
| β‘ Response caching | 5-minute price cache and 24-hour company-details cache |
| ποΈ Database migrations | Schema versioning with Flask-Migrate / Alembic |
| Layer | Technologies |
|---|---|
| Backend | Flask, SQLAlchemy, Flask-Login, Gunicorn |
| Frontend | Plotly Dash, Dash Bootstrap Components |
| Database | PostgreSQL (production) Β· SQLite (development) |
| API | Massive.com |
| Deployment | Render, Docker Compose |
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β Browser βββββββΊβ Flask App βββββββΊβ PostgreSQL β
β β β + Dash UI β β β
ββββββββββββββββ ββββββββ¬ββββββββ ββββββββββββββββ
β
βΌ
ββββββββββββββββ
βMassive.com β
β REST API β
ββββββββββββββββ
/dash/ for interactive charts without a separate JS build step or frontend toolchain to maintain.StockWatch/
βββ app/
β βββ blueprints/ # auth Β· main Β· stock Β· user route handlers
β βββ services/ # stock_services Β· user_services (business logic)
β βββ utils/ # cache_manager Β· cache_monitor
β βββ models.py # User, Watchlist, Stock ORM models
β βββ extensions.py # db, migrate, login, cache instances
β βββ templates/ # Jinja2 HTML templates
βββ frontend/
β βββ dashboard.py # Plotly Dash interactive dashboard
βββ migrations/ # Alembic database migrations
βββ tests/ # pytest test suite
βββ config.py # App configuration
βββ wsgi.py # WSGI entry point
βββ docker-compose.yml # Local PostgreSQL via Docker
βββ Pipfile # Pipenv dependencies
βββ requirements.txt # pip dependencies
The quickest way to run StockWatch locally is with SQLite β no database server, no Docker, no ports to configure. You only need Python and a free API key. (Want production parity with PostgreSQL? See Run against PostgreSQL below.)
pip install pipenvgit clone https://github.com/epeltz33/StockWatch.git
cd StockWatch
pipenv install
Create a .env file in the project root. For the SQLite quickstart, leave DATABASE_URL out β the app falls back to a local SQLite file (app.db):
SECRET_KEY=any-random-string
POLYGON_API_KEY=your_massive_api_key
pipenv run flask db upgrade
This creates app.db with all tables. FLASK_APP is already set in .flaskenv, so no extra flags are needed.
pipenv run flask seed-demo-user
Creates the demo account (demo@stockwatch.dev / Demo123!) with a pre-populated watchlist, so you can log in and see data right away.
pipenv run flask run --port 8080
The app is available at http://localhost:8080.
For a production-style server, use Gunicorn:
pipenv run gunicorn wsgi:app --bind 0.0.0.0:8080
For parity with production, run PostgreSQL locally with the bundled Docker setup. The container is published on host port 15433 (mapped to its internal 5432) so it wonβt clash with an existing Postgres:
docker compose up -d # start
docker compose down # stop
Point DATABASE_URL at it in your .env and re-run migrations:
SECRET_KEY=any-random-string
POLYGON_API_KEY=your_massive_api_key
DATABASE_URL=postgresql://stockwatch_user:stockwatch_password@localhost:15433/stockwatch
pipenv run flask db upgrade
pipenv run flask run --port 8080
StockWatch ships with a render.yaml blueprint for one-click deployment.
StockWatch repo. Render detects render.yaml and provisions:
stockwatch-db, ~$7/mo)stockwatch, free tier with cold starts)POLYGON_API_KEY to your Massive.com key. SECRET_KEY and DATABASE_URL are generated automatically.flask seed-demo-user.https://your-app.onrender.com/health β {"status": "healthy"}demo@stockwatch.dev / Demo123!Note: Migrations run in the start command, not the build command β Renderβs internal database hostname is only reachable at runtime.
Use app.yaml instead:
DATABASE_URL, SECRET_KEY, and POLYGON_API_KEY as encrypted env vars.pipenv run pytest
Released under the MIT License.
Questions or feedback? Reach out at erpeltz@gmail.com.