A real-time financial analytics platform for retail investors.
Live charts · Federal Reserve macro data · AI-powered assistant · Portfolio risk analytics · News sentiment
Tradeski integrates five distinct data pipelines — live equity prices, Federal Reserve macroeconomic data, financial news with sentiment scoring, a fundamental stock screener, and an AI assistant grounded in real-time context — into a single coherent interface, with quantitative portfolio analytics and a 90-day correlation heatmap layered on top.
Every quantitative indicator is implemented from first principles without TA-Lib or pandas. The AI assistant (Ski) receives live macro, portfolio, and news data on every request — not generic training knowledge. The backend is a persistent WebSocket server deployed on Render; the frontend is a static site on Vercel.
| Backend | Python 3.12 · Flask · Flask-SocketIO · gevent |
| Frontend | Vanilla JS · Plotly.js · Socket.IO |
| Data | yfinance · FRED API · NewsAPI · Anthropic Claude |
| Tests | 45 tests across 9 files — CI on every push |
| Deployment | Render (backend) · Vercel (frontend) · tradeski.dev |
- Features
- Architecture
- Tech Stack
- Quantitative Indicators
- Ski — AI Financial Assistant
- Production Hardening
- API Reference
- Getting Started
- Running Tests
- Deployment
- Background
Live equity prices broadcast over WebSocket (Socket.IO / gevent) with automatic chart extension, a scrolling ticker tape across 11 symbols, and color-coded flash animations on every price update.
| Capability | Detail |
|---|---|
| Chart types | Candlestick (OHLC) and line chart |
| Timeframes | 1D (5-min bars) · 5D (15-min) · 1M · 3M · 6M · 1Y (daily) |
| Overlays | Bollinger Bands · SMA 20/50 · EMA 20 — each independently toggleable |
| Subpanels | RSI and MACD as separate panels below the main chart |
| Click-to-inspect | Click any candle to populate the data strip with exact OHLC, change %, and range |
| Compare mode | Normalize up to 5 symbols to a common 0% baseline for relative performance |
A live ribbon beneath the header pulls seven Federal Reserve series from the FRED API, refreshed hourly. Each indicator displays its current value, trend direction (↑ / ↓), and date of last observation.
| FRED Series | Metric |
|---|---|
| CPIAUCSL | Consumer Price Index (YoY %) |
| FEDFUNDS | Effective Federal Funds Rate |
| GDP | Real GDP (Chained 2017 $B) |
| UNRATE | Unemployment Rate |
| DGS10 | 10-Year Treasury Yield |
| T10Y2Y | Yield Curve Spread (10Y − 2Y) |
| BAMLH0A0HYM2 | High-Yield Credit Spread (OAS) |
Recent headlines per symbol are fetched from NewsAPI and scored with VADER sentiment analysis. The standard VADER lexicon is augmented with ~30 financial-domain terms ("beats" → +2.0, "bankruptcy" → −3.0, "downgraded" → −1.8) to correct systematic under-scoring of financial jargon. Each article shows a BULLISH / BEARISH / NEUTRAL chip with its numeric score, and an aggregate sentiment badge summarizes the overall tone.
Add holdings by symbol, share count, and average cost basis. See unrealized P&L in real time, persisted in SQLite via UPSERT. The risk metrics panel computes three statistics from one year of daily returns weighted by current market value:
| Metric | Method |
|---|---|
| Sharpe Ratio | Annualized excess return / annualized volatility · risk-free rate: 4.5% |
| Portfolio Beta | Covariance with SPY / variance of SPY over 252 trading days |
| Annualized Volatility | Rolling standard deviation of weighted daily returns × √252 |
A full-screen modal shows a pairwise 90-day return correlation matrix for all 11 tracked symbols, rendered as a Plotly heatmap with a diverging red-to-green colorscale. Cached server-side for one hour.
Rule-based alerts — price above/below threshold, RSI overbought (>70) / oversold (<30), volume spike, volatility spike — with cooldown tracking, optional Pushover push notifications, and SQLite persistence across restarts.
Fundamental data for 28 curated stocks across five sectors, fetched in parallel via an 8-worker thread pool and cached per symbol. Filterable by P/E ratio, market cap tier, sector, and 52-week performance. Clicking a result loads that symbol on the chart.
Tradeski/
│
├── tracker/ # Core Python backend module
│ ├── analyzer.py # 10 quantitative indicators, implemented from scratch
│ ├── database.py # SQLite persistence — prices, alerts, portfolio
│ ├── price_fetcher.py # yfinance: live prices, OHLC history, screener data
│ ├── fred.py # FRED API client — 7 macro series with trend detection
│ ├── news.py # NewsAPI + VADER sentiment with financial lexicon
│ ├── alerts.py # Rule-based alert evaluation engine
│ ├── notifier.py # Pushover push notification handler
│ ├── scheduler.py # APScheduler background job manager
│ ├── daily_summary.py # Daily price/volume summary generator
│ ├── pruning.py # Automatic 30-day data retention cleanup
│ ├── config.py # Environment variable configuration
│ ├── logger.py # Rotating file + console logger
│ └── main.py # Real-time tracker entry point
│
├── Plotly dashboard/ # Flask + Socket.IO application server
│ ├── app.py # REST API, WebSocket emitters, Ski /chat endpoint
│ └── cache.py # TTL in-memory cache (SimpleCache)
│
├── frontend/ # Static UI — served via Vercel
│ ├── landing.html # Landing page at tradeski.dev/
│ ├── index.html # Dashboard at tradeski.dev/app
│ ├── 404.html # Custom terminal-style 404 page
│ ├── favicon.svg # SVG favicon
│ ├── styles.css # Terminal dark theme — CSS custom properties, mobile-responsive
│ └── dashboard.js # WebSocket, Plotly, Ski, screener, health indicator
│
├── tests/ # pytest suite — 45 tests across 9 files
│ ├── test_analyzer.py # Indicator shape, value range, arithmetic correctness
│ ├── test_analyzer_basic.py # Edge cases: empty input, flat series, single-element
│ ├── test_database.py # SQLite round-trips for prices, alerts, portfolio
│ ├── test_fred.py # FRED client with mocked HTTP — trend detection
│ ├── test_news.py # VADER: financial lexicon augmentation, aggregation
│ ├── test_portfolio.py # CRUD operations, UPSERT behavior
│ ├── test_screener.py # Data normalization, ETF fallback, edge cases
│ ├── test_notifier.py # Pushover mock: correct POST structure
│ └── test_price_fetcher.py # Live yfinance smoke test
│
├── .github/workflows/ci.yml # GitHub Actions: lint → test on every push
├── render.yaml # Render deployment config (gunicorn + gevent worker)
├── vercel.json # Vercel routing config
├── wsgi.py # WSGI entry point with gevent monkey-patching
├── requirements.txt # Python dependencies
├── setup.cfg # Flake8 configuration
└── .env.example # All required environment variables documented
┌──────────────────────────────────────────────────────────────────┐
│ External Data Sources │
│ yfinance · FRED API · NewsAPI · Anthropic Claude │
└────────┬──────────┬────────────┬──────────────────┬─────────────┘
│ │ │ │
▼ ▼ ▼ ▼
price_fetcher fred.py news.py /chat endpoint
│ │ │ (Haiku 4.5)
▼ │ │ │
database.py │ │ │
(SQLite) │ │ │
│ │ │ │
▼ ▼ ▼ │
analyzer.py macro VADER Ski panel
(indicators) snapshot scores (frontend)
│ │ │
└──────────┴────────────┘
│
▼
app.py (Flask)
┌──────────────────┐
│ REST Endpoints │ WebSocket
│ /health │──────────────► price_update
│ /stats │──────────────► alert_triggered
│ /price_history │
│ /macro │
│ /news │
│ /portfolio │
│ /screener │
│ /chat │
└────────┬─────────┘
│ JSON / WS
▼
dashboard.js
(Plotly · Socket.IO)
| Layer | Technology |
|---|---|
| Language | Python 3.12 |
| Web Framework | Flask + Flask-SocketIO |
| Real-Time Transport | WebSocket (Socket.IO / gevent) |
| Database | SQLite (sqlite3 stdlib) |
| Market Data | yfinance (Yahoo Finance) |
| Macro Data | FRED API (St. Louis Federal Reserve) |
| News & Sentiment | NewsAPI + VADER with financial lexicon |
| AI Assistant | Anthropic Claude Haiku 4.5 |
| Parallel Fetching | concurrent.futures.ThreadPoolExecutor |
| Scheduler | APScheduler |
| Notifications | Pushover API (optional) |
| Frontend Charting | Plotly.js |
| Frontend Runtime | Vanilla JavaScript (ES2020) |
| Fonts | Space Grotesk · JetBrains Mono |
| CI/CD | GitHub Actions |
| Linting | Flake8 |
| Testing | pytest (45 tests) |
| Backend Hosting | Render |
| Frontend Hosting | Vercel |
All ten indicators live in tracker/analyzer.py and are implemented without TA-Lib or pandas — a deliberate choice that requires genuine engagement with the underlying mathematics.
| Indicator | Implementation detail |
|---|---|
| SMA (20, 50) | Each window computed independently to avoid floating-point drift |
| EMA (20) | Standard k = 2/(n+1) multiplier |
| RSI (14) | Wilder smoothing — industry-standard formulation, less volatile than SMA-based RSI |
| MACD | 12/26 EMA divergence with 9-period signal line and histogram |
| Bollinger Bands | ±2σ bands around SMA-20 |
| Z-Score | Rolling 20-period deviation from mean |
| Volatility | Rolling standard deviation proxy for realized vol |
| ATR (14) | True range smoothed via Wilder's method — matches Wilder (1978) spec |
| Stochastic | %K/%D oscillator with configurable lookback |
| Linear Regression | Least-squares next-value prediction via numpy.linalg.lstsq |
RSI — Wilder Smoothing: Initial up/down averages use an arithmetic mean over the seed period. Subsequent values: avg = (prev × (period − 1) + current) / period. Output range: [0, 100].
ATR — True Range: max(High − Low, |High − PrevClose|, |Low − PrevClose|), then Wilder-smoothed to match the original specification.
Ski is powered by Anthropic Claude Haiku 4.5 and grounded in three live data sources injected on every request. Most AI financial tools answer questions in a vacuum — Ski answers questions about your situation.
| Context block | Source | Content |
|---|---|---|
| System prompt | Static | Financial knowledge: equities, macro, sector rotation, indicator signals |
| Macro context | FRED API (cached 1h) | CPI, Fed Funds Rate, GDP, unemployment, yield curve, credit spreads |
| Portfolio context | SQLite (live) | Holdings, share counts, avg cost, current P&L |
| News context | NewsAPI + VADER (cached 30m) | Recent headlines for the viewed symbol with sentiment scores |
Ski can answer "Is now a good time to add to my NVDA position?" with awareness of the user's actual cost basis, the current macro environment, and recent news sentiment — not boilerplate.
Rate limiting: 20 messages/hour · 50 messages/day per IP via Flask-Limiter.
| Measure | Implementation |
|---|---|
| Rate limiting | Flask-Limiter: 10 req/min per IP globally · 20/hr + 50/day on /chat |
| Input sanitization | Strips HTML tags via regex · 500-character hard limit on chat messages |
| Symbol sanitization | Ticker restricted to [A-Z0-9.] — no injection vectors |
| CORS | Restricted to tradeski.dev and www.tradeski.dev via ALLOWED_ORIGINS env var |
| Secrets | All API keys read from environment variables exclusively — never committed |
| Measure | Implementation |
|---|---|
| TTL caching | FRED 1hr · prices 5min · news 30min · screener 10min · correlation 1hr |
| Health endpoint | GET /health returns live API service status — configured vs missing |
| Graceful AI errors | RateLimitError → 429 · 529 overloaded → 503 with retry guidance |
| Frontend error UX | Distinct messages for 429, 503, and network failures |
| Measure | Implementation |
|---|---|
| Landing page | / serves a dark marketing page · dashboard lives at /app |
| Custom 404 | Terminal-style 404 page with path display and navigation |
| Mobile responsive | Portrait: slide-in drawer · Landscape: full desktop layout |
| API health indicator | Green / yellow / red dot in the header reflects live /health status |
| Analytics | Vercel Analytics via /_vercel/insights/script.js |
Returns live status for all three external API integrations.
{
"status": "ok",
"services": {
"fred": "configured",
"news": "configured",
"anthropic": "configured"
}
}OHLC snapshot + real 52-week range from yfinance. Cached 5 minutes.
{
"symbol": "AAPL",
"open": 189.42, "high": 191.05, "low": 188.30, "close": 190.67,
"high_52w": 199.62, "low_52w": 143.90, "change_pct": 0.66
}Full OHLC + indicator payload. All arrays are index-aligned to timestamps. Valid tf: 1D 5D 1M 3M 6M 1Y.
{
"timestamps": ["2024-01-02T00:00:00", "..."],
"open": [], "high": [], "low": [], "close": [],
"sma20": [], "sma50": [], "ema20": [],
"rsi": [], "macd": [], "signal": [], "histogram": [],
"upper_band": [], "lower_band": [],
"zscore": [], "volatility": []
}Current FRED macro snapshot. Cached 1 hour. Requires FRED_API_KEY.
{
"CPIAUCSL": { "label": "CPI", "value": 3.2, "unit": "%", "trend": "down", "date": "2024-10-01" },
"FEDFUNDS": { "label": "Fed Funds Rate", "value": 5.33, "unit": "%", "trend": "neutral", "date": "..." }
}VADER-scored headlines + aggregate sentiment. Cached 30 minutes. Requires NEWS_API_KEY.
{
"articles": [{
"title": "Apple beats Q4 earnings expectations",
"source": "Reuters",
"published_at": "2024-11-01T12:00:00Z",
"sentiment": 0.612,
"sentiment_label": "bullish"
}],
"aggregate": { "score": 0.341, "label": "bullish", "count": 10 }
}List holdings with live P&L, add/update a position (UPSERT on symbol), or remove one.
POST body: { "symbol": "AAPL", "shares": 10, "avg_cost": 175.00 }
Sharpe ratio, beta vs. S&P 500, and annualized volatility from 1-year daily returns.
{ "sharpe": 1.24, "beta": 0.91, "volatility": 18.4 }Pairwise 90-day return correlation matrix for all tracked symbols. Cached 1 hour.
{
"symbols": ["AAPL", "MSFT", "NVDA", "..."],
"matrix": [[1.0, 0.874, 0.761], ["..."]]
}Fundamental data for the full 28-stock universe. Parallel fetch, cached 10 min per symbol.
List, create, and delete rule-based price and indicator alerts.
Ski chatbot. Body: { "message": "...", "history": [...], "symbol": "AAPL" }.
Injects live macro, portfolio, and news context. Requires ANTHROPIC_API_KEY.
| Event | Direction | Payload |
|---|---|---|
price_update |
Server → Client | { symbol, price, change_pct, timestamp } |
alert_triggered |
Server → Client | { symbol, message, timestamp } |
- Python 3.12+
- pip
git clone https://github.com/Danny-397/Tradeski.git
cd Tradeski
pip install -r requirements.txtcp .env.example .envOpen .env and fill in the three required keys:
| Variable | Where to get it | Required? |
|---|---|---|
ANTHROPIC_API_KEY |
console.anthropic.com | Yes — powers Ski |
FRED_API_KEY |
fred.stlouisfed.org/docs/api/api_key.html | No — macro ribbon disabled without it |
NEWS_API_KEY |
newsapi.org/register | No — news panel disabled without it |
# Terminal 1 — backend
cd "Plotly dashboard"
python app.py
# → http://localhost:5000
# Terminal 2 — frontend
python -m http.server 8080 --directory frontend
# → http://localhost:8080python -m pytest -v45 tests across 9 files:
| File | What it covers |
|---|---|
test_analyzer.py |
Indicator shape, value range, arithmetic correctness |
test_analyzer_basic.py |
Edge cases: empty input, flat series, single-element arrays |
test_database.py |
SQLite round-trips for prices, alerts, portfolio |
test_fred.py |
FRED client with mocked HTTP — trend detection, missing series |
test_news.py |
VADER: financial lexicon augmentation, aggregation |
test_portfolio.py |
CRUD operations, UPSERT behavior, case-insensitive symbols |
test_screener.py |
Data normalization, ETF sector fallback, edge cases |
test_notifier.py |
Pushover mock: correct POST structure, skip without credentials |
test_price_fetcher.py |
Live yfinance smoke test |
flake8 . # lintingGitHub Actions runs lint and the full test suite on every push and pull request to main.
- Connect the repo to Render and select
render.yamlfor configuration. - Set all environment variables in the Render dashboard → Environment tab.
render.yamlconfigures a gunicorn + gevent WebSocket worker — do not change the worker class.- Render provisions a public HTTPS URL with persistent WebSocket connections.
- Connect the repo to Vercel. Vercel reads
vercel.jsonautomatically. - Update
CFG.APIandCFG.WSinfrontend/dashboard.jsto your Render URL. - Push — Vercel deploys in under 30 seconds.
The tools professional traders use daily — live indicators, macro dashboards, portfolio analytics, AI-assisted research — are locked behind expensive terminals or scattered across a dozen websites. Bloomberg Terminal costs $25,000/year. Retail investors end up making decisions on stale, fragmented information.
The goal was to build a platform that integrates all of those data sources into a single coherent interface — and to build it properly. That meant implementing every quantitative indicator from first principles rather than importing a library. It meant wiring a real macroeconomic data pipeline directly from the Federal Reserve. It meant building an AI assistant that knows what the user is actually looking at, not one answering questions in a vacuum.
Writing Wilder's smoothing from scratch requires understanding why it diverges from a simple moving average and what that difference means at the edges of a time series. Writing the VADER financial lexicon requires reading enough headlines to know that "beats" and "surges" are systematically under-scored by a general-purpose sentiment model. These are not problems that get solved by importing a package.
Tradeski spans six engineering disciplines in one codebase:
- Data engineering — real-time polling, SQLite schema design, TTL caching, 30-day retention pruning
- Backend architecture — REST API design, WebSocket streaming, background scheduling, parallel HTTP fetching
- Quantitative analysis — ten financial indicators from first principles; Sharpe ratio, beta, and volatility from raw daily return vectors
- Statistical computing — pairwise correlation matrix over 90-day return series; portfolio variance weighted by live market value
- AI integration — context injection pipeline, grounding an LLM in live structured data
- Frontend engineering — real-time state management, multi-library charting, normalized multi-stock overlays, terminal-grade UI
MIT © 2026 Dan Lichtenberger. See LICENSE.