Professional trading terminal implementing Stan Weinstein's Stage Analysis methodology with real-time market data, AI-powered analysis, and realtime alerts.
┌─────────────────────────────────────────────────┐
│ React + Vite + TS + Tailwind → Vercel │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ Supabase │
│ • Auth (email/password) │
│ • Postgres (profiles, analyses, alerts) │
│ • Realtime (live alert push) │
│ • Edge Functions (Deno) │
│ • pg_cron (alert check every 5 min) │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ External APIs │
│ • Finnhub (live prices, SMA, volume) │
│ • Gemini 2.5 (Weinstein analysis) │
└─────────────────────────────────────────────────┘
The original Google AI Studio export called Gemini directly from the browser and asked it to "find the live price via Google Search". That's the root cause of stale prices — Google Search indexes quotes with 5–60 min lag.
This rebuild:
- Fetches the real price from Finnhub (institutional-grade feed).
- Computes SMA30 weekly and volume ratio in code — not by asking an LLM.
- Classifies the Weinstein stage with deterministic rules first.
- Uses Gemini only for the narrative layer (strategy, wording) — anchored to the real numbers.
- Node.js 20+
- npm
- A GitHub account
- A Supabase account (free tier works)
- A Vercel account (free tier works)
- A Finnhub API key (free tier: 60 req/min)
- A Gemini API key (free tier available)
- Supabase CLI:
npm install -g supabase
cd /Users/juantxu/Projects/weinstein-analyst
npm install
cp .env.example .env
# Edit .env and fill in VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY
npm run dev- Go to https://supabase.com/dashboard → New project.
- Note your project ref (the
xxxxinxxxx.supabase.co) and database password.
cd /Users/juantxu/Projects/weinstein-analyst
supabase login
supabase link --project-ref YOUR_PROJECT_REFsupabase db pushThis creates all tables, RLS policies, realtime publications, and the 5-minute cron job.
supabase secrets set GEMINI_API_KEY=AIza...
supabase secrets set FINNHUB_API_KEY=cq...
supabase secrets set CRON_SECRET=$(openssl rand -hex 32)supabase functions deploy get-live-price
supabase functions deploy analyze-market
supabase functions deploy analyze-operation
supabase functions deploy chat
supabase functions deploy check-alerts --no-verify-jwtOpen Supabase Dashboard → SQL Editor and run:
alter database postgres set "app.supabase_url" to 'https://YOUR_PROJECT_REF.supabase.co';
alter database postgres set "app.cron_secret" to 'THE_SECRET_YOU_GENERATED_ABOVE';Supabase Dashboard → Project Settings → API:
Project URL→ paste intoVITE_SUPABASE_URLanon publickey → paste intoVITE_SUPABASE_ANON_KEY
cd /Users/juantxu/Projects/weinstein-analyst
git init
git add .
git commit -m "Initial professional rebuild"
gh repo create weinstein-analyst --public --source=. --push- Go to https://vercel.com/new → import
weinstein-analyst. - Framework preset: Vite (auto-detected).
- Add environment variables:
VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEY
- Deploy.
Every git push now triggers a Vercel rebuild automatically.
weinstein-analyst/
├── src/
│ ├── components/ # UI (preserved from original)
│ ├── contexts/ # AuthContext (Supabase auth)
│ ├── hooks/ # useAnalyses, useAlerts (+ realtime)
│ ├── lib/
│ │ ├── supabase.ts # browser client
│ │ └── api.ts # typed wrapper for Edge Functions
│ ├── services/
│ │ └── geminiService.ts # back-compat shim → api.ts
│ ├── App.tsx
│ ├── main.tsx
│ └── types.ts
├── supabase/
│ ├── config.toml
│ ├── migrations/
│ │ └── 0001_initial_schema.sql
│ └── functions/
│ ├── _shared/
│ │ ├── cors.ts
│ │ ├── finnhub.ts # Finnhub client + SMA/volume math
│ │ ├── gemini.ts # lightweight REST client
│ │ └── weinstein.ts # deterministic stage classifier
│ ├── get-live-price/
│ ├── analyze-market/
│ ├── analyze-operation/
│ ├── chat/
│ └── check-alerts/ # invoked by pg_cron
├── .env.example
├── vercel.json
└── package.json
User types AAPL → clicks ANALYZE →
- Browser calls
supabase.functions.invoke('analyze-market', {...}). - Edge Function calls Finnhub
/quote→ gets live price + timestamp (UNIX seconds, not a rumor). - Edge Function calls Finnhub
/stock/candle?resolution=W→ gets weekly candles. - Edge Function computes SMA30-weekly and volume ratio in TypeScript.
weinstein.tsclassifies the stage using rules (not the LLM).- Gemini is called with a "technical anchor" block that forces it to use the real numbers — it only writes the narrative (strategy, wording, support/resistance phrasing).
- The Edge Function overwrites
currentPriceandpriceTimestampin Gemini's response with the Finnhub values, so the LLM can't invent numbers.
- User creates an alert → row inserted in
alertstable. pg_cronfires every 5 min → callscheck-alertsEdge Function.- Edge Function skips if the US market is closed (unless
CHECK_ALERTS_ALWAYS=1). - Groups alerts by ticker (1 Finnhub call per ticker, not per alert).
- Evaluates each condition deterministically (no LLM).
- On trigger: updates
alerts.status = 'triggered'+ insertsalert_eventsrow. - Frontend is subscribed via Supabase Realtime → receives websocket push → shows browser Notification.
# Full local stack (runs Postgres + Edge runtime + Studio)
supabase start
# Test an Edge Function locally
supabase functions serve analyze-market --env-file supabase/.env.local
# Regenerate TS types from the DB schema
npm run supabase:types- Finnhub free tier has no crypto real-time feed for all pairs —
BTCUSDworks viaBINANCE:BTCUSDT, etc. - Candles require a paid Finnhub plan for some exchanges; for free stocks on US exchanges (NYSE, NASDAQ) it works.
- The cron runs every 5 min (SQL-defined); if you need sub-minute alerts, move to a push-based WebSocket subscription (Finnhub supports it).
- Supabase Storage for chart uploads (currently images are base64-inlined)
- Price chart embedded in
AnalysisDisplayusing Finnhub candles + Recharts - Stripe integration for Pro tier (unlimited alerts)
- Email notifications via Supabase → Resend
- Mobile PWA manifest
Built with Stan Weinstein's methodology. No financial advice — DYOR.