StockAnalysers
Loading market data...
Your portfolio & reports sync across all devices
By continuing you agree to our Privacy Policy.
-- Run this in your Supabase SQL Editor
-- User profiles (email linked to portfolio)
CREATE TABLE public.profiles (
user_id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
email TEXT, full_name TEXT, avatar_url TEXT,
updated_at TIMESTAMPTZ DEFAULT NOW()
);
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "own_profile" ON public.profiles
FOR ALL TO authenticated USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id);
-- Auto-upsert profile on every sign-in
CREATE OR REPLACE FUNCTION public.handle_user_upsert()
RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER AS $$
BEGIN
INSERT INTO public.profiles (user_id, email, full_name, avatar_url)
VALUES (NEW.id, NEW.email,
COALESCE(NEW.raw_user_meta_data->>'full_name', NEW.raw_user_meta_data->>'name'),
NEW.raw_user_meta_data->>'avatar_url')
ON CONFLICT (user_id) DO UPDATE SET
email=EXCLUDED.email, full_name=EXCLUDED.full_name,
avatar_url=EXCLUDED.avatar_url, updated_at=NOW();
RETURN NEW;
END; $$;
CREATE OR REPLACE TRIGGER on_auth_user_created
AFTER INSERT OR UPDATE ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_user_upsert();
-- Reports table
CREATE TABLE public.reports (
id TEXT PRIMARY KEY, user_id UUID NOT NULL DEFAULT auth.uid(),
report_ts TIMESTAMPTZ NOT NULL, tickers TEXT[] DEFAULT '{}',
title TEXT DEFAULT '', description TEXT DEFAULT '',
is_live BOOLEAN DEFAULT TRUE, snapshot JSONB DEFAULT '[]',
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX ON public.reports (user_id, report_ts DESC);
ALTER TABLE public.reports ENABLE ROW LEVEL SECURITY;
CREATE POLICY "own_reports" ON public.reports
FOR ALL TO authenticated USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id);
-- Transactions table
CREATE TABLE public.transactions (
id TEXT PRIMARY KEY, user_id UUID NOT NULL DEFAULT auth.uid(),
ticker TEXT NOT NULL, company_name TEXT DEFAULT '',
volume NUMERIC(18,6) NOT NULL, price NUMERIC(18,4) NOT NULL,
transaction_type TEXT NOT NULL CHECK (transaction_type IN ('buy','sell')),
transaction_date DATE NOT NULL, notes TEXT DEFAULT '',
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX ON public.transactions (user_id, transaction_date DESC);
ALTER TABLE public.transactions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "own_transactions" ON public.transactions
FOR ALL TO authenticated USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id);
-- The GARP Screener table used to be created here. Removed 18 Sep 2026: nothing
-- reads or writes it any more. Worth recording why it died, because the cause was
-- this very block — it declares name/pe/peg, while the live table was created from
-- a later schema using company_name/pe_ratio/peg_ratio. db.js was written against
-- these names, so every upsert failed with 42703 into a swallowed catch and the
-- table never held a row. Its FOR ALL TO authenticated policy was also the same
-- shape dropped from every other table in this schema.
-- ── Financial Data Store — run in Supabase SQL Editor ──
-- Auto-update timestamp helper (skip if already exists)
CREATE OR REPLACE FUNCTION public.touch_updated_at()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$;
-- 1. Company Profiles
CREATE TABLE public.company_profiles (
symbol TEXT PRIMARY KEY,
name TEXT NOT NULL DEFAULT '',
sector TEXT DEFAULT '',
industry TEXT DEFAULT '',
exchange TEXT DEFAULT '',
country TEXT DEFAULT 'US',
currency TEXT DEFAULT 'USD',
description TEXT DEFAULT '',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TRIGGER trg_cp_updated BEFORE UPDATE ON public.company_profiles
FOR EACH ROW EXECUTE FUNCTION public.touch_updated_at();
ALTER TABLE public.company_profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "cp_read_all" ON public.company_profiles FOR SELECT USING (true);
CREATE POLICY "cp_auth_write" ON public.company_profiles
FOR ALL TO authenticated USING (true) WITH CHECK (true);
-- 2. Latest Financial Snapshot (one row per company, upserted on refresh)
CREATE TABLE public.company_financials (
symbol TEXT PRIMARY KEY REFERENCES public.company_profiles(symbol) ON DELETE CASCADE,
-- Market data
price NUMERIC DEFAULT 0,
market_cap NUMERIC DEFAULT 0,
shares_outstanding NUMERIC DEFAULT 0,
week_52_high NUMERIC DEFAULT 0,
week_52_low NUMERIC DEFAULT 0,
beta NUMERIC DEFAULT 0,
-- Valuation multiples
pe_ratio NUMERIC DEFAULT 0,
forward_pe NUMERIC DEFAULT 0,
peg_ratio NUMERIC DEFAULT 0,
ps_ratio NUMERIC DEFAULT 0,
pb_ratio NUMERIC DEFAULT 0,
ev_ebitda NUMERIC DEFAULT 0,
fcf_yield NUMERIC DEFAULT 0,
-- Income (TTM)
revenue_ttm NUMERIC DEFAULT 0,
gross_profit_ttm NUMERIC DEFAULT 0,
ebitda_ttm NUMERIC DEFAULT 0,
net_income_ttm NUMERIC DEFAULT 0,
eps_ttm NUMERIC DEFAULT 0,
-- Growth rates (as decimals: 0.15 = 15%)
revenue_growth_yoy NUMERIC DEFAULT 0,
eps_growth_yoy NUMERIC DEFAULT 0,
eps_growth_3y_cagr NUMERIC DEFAULT 0,
-- Margins (as decimals: 0.65 = 65%)
gross_margin NUMERIC DEFAULT 0,
ebitda_margin NUMERIC DEFAULT 0,
net_margin NUMERIC DEFAULT 0,
operating_margin NUMERIC DEFAULT 0,
-- Efficiency
roe NUMERIC DEFAULT 0,
roa NUMERIC DEFAULT 0,
roic NUMERIC DEFAULT 0,
-- Balance sheet
cash NUMERIC DEFAULT 0,
total_debt NUMERIC DEFAULT 0,
debt_to_equity NUMERIC DEFAULT 0,
current_ratio NUMERIC DEFAULT 0,
quick_ratio NUMERIC DEFAULT 0,
interest_coverage NUMERIC DEFAULT 0,
-- Cash flow
fcf_ttm NUMERIC DEFAULT 0,
operating_cf_ttm NUMERIC DEFAULT 0,
capex_ttm NUMERIC DEFAULT 0,
-- Dividend
dividend_yield NUMERIC DEFAULT 0,
payout_ratio NUMERIC DEFAULT 0,
-- Analyst consensus
analyst_target NUMERIC DEFAULT 0,
analyst_rec TEXT DEFAULT '',
num_analysts INTEGER DEFAULT 0,
-- Metadata
data_source TEXT DEFAULT 'api',
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TRIGGER trg_cf_updated BEFORE UPDATE ON public.company_financials
FOR EACH ROW EXECUTE FUNCTION public.touch_updated_at();
CREATE INDEX ON public.company_financials (peg_ratio);
CREATE INDEX ON public.company_financials (eps_growth_yoy DESC);
ALTER TABLE public.company_financials ENABLE ROW LEVEL SECURITY;
CREATE POLICY "cf_read_all" ON public.company_financials FOR SELECT USING (true);
CREATE POLICY "cf_auth_write" ON public.company_financials
FOR ALL TO authenticated USING (true) WITH CHECK (true);
-- 3. Annual Income History (multi-year per company)
CREATE TABLE public.income_history (
id BIGSERIAL PRIMARY KEY,
symbol TEXT NOT NULL REFERENCES public.company_profiles(symbol) ON DELETE CASCADE,
fiscal_year DATE NOT NULL,
revenue NUMERIC DEFAULT 0,
gross_profit NUMERIC DEFAULT 0,
ebitda NUMERIC DEFAULT 0,
net_income NUMERIC DEFAULT 0,
eps_diluted NUMERIC DEFAULT 0,
shares_diluted NUMERIC DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE (symbol, fiscal_year)
);
CREATE INDEX ON public.income_history (symbol, fiscal_year DESC);
ALTER TABLE public.income_history ENABLE ROW LEVEL SECURITY;
CREATE POLICY "ih_read_all" ON public.income_history FOR SELECT USING (true);
CREATE POLICY "ih_auth_write" ON public.income_history
FOR ALL TO authenticated USING (true) WITH CHECK (true);
-- 4. GARP Assessments (scored snapshots, one per company per day)
CREATE TABLE public.garp_assessments (
id BIGSERIAL PRIMARY KEY,
symbol TEXT NOT NULL REFERENCES public.company_profiles(symbol) ON DELETE CASCADE,
assessed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
classification TEXT DEFAULT '',
-- Five GARP pillar scores (0–10 each)
garp_valuation_score NUMERIC DEFAULT 0,
balance_sheet_score NUMERIC DEFAULT 0,
insider_alignment_score NUMERIC DEFAULT 0,
cash_flow_score NUMERIC DEFAULT 0,
competitive_adv_score NUMERIC DEFAULT 0,
-- Auto-computed composites
total_score NUMERIC GENERATED ALWAYS AS (
garp_valuation_score + balance_sheet_score + insider_alignment_score +
cash_flow_score + competitive_adv_score
) STORED,
score_100 NUMERIC GENERATED ALWAYS AS (
(garp_valuation_score + balance_sheet_score + insider_alignment_score +
cash_flow_score + competitive_adv_score) * 2
) STORED,
-- Key inputs captured at assessment time
peg_snap NUMERIC DEFAULT 0,
eps_growth_snap NUMERIC DEFAULT 0,
gross_margin_snap NUMERIC DEFAULT 0,
roe_snap NUMERIC DEFAULT 0,
debt_equity_snap NUMERIC DEFAULT 0
);
CREATE UNIQUE INDEX ON public.garp_assessments (symbol, (assessed_at::DATE));
CREATE INDEX ON public.garp_assessments (symbol, assessed_at DESC);
CREATE INDEX ON public.garp_assessments (score_100 DESC);
ALTER TABLE public.garp_assessments ENABLE ROW LEVEL SECURITY;
CREATE POLICY "ga_read_all" ON public.garp_assessments FOR SELECT USING (true);
CREATE POLICY "ga_auth_write" ON public.garp_assessments
FOR ALL TO authenticated USING (true) WITH CHECK (true);
Track your holdings and transactions
Exit levels for each holding, taken from that ticker's AI research forecast and measured against your own average cost. Educational only — not financial advice.
Your previously generated StockAnalysers analyses
AI-ranked · Updated in real-time · Educational only
| Company | StockAnalysers Score ⓘ | Price ⓘ | Target ⓘ | P/E ⓘ | Entry Price ⓘ | Price versus Entry ⓘ | Advice ⓘ | Actions |
|---|---|---|---|---|---|---|---|---|
| Click "AI Research" to start analysis… | ||||||||
Tap any row for full analysis · AI-generated · Educational only — not financial advice
StockAnalysers Analysis |
Enter your email and we'll send a reset link
Check your inbox
If that email is registered, you'll receive a reset link shortly. The link expires in 1 hour.
Latest congressional stock disclosures · last 12 months
Open-market insider buys & sales · filed in the last 30 days
Choose a strong new password for your account
Enter your current password then choose a new one