2026-05-30 11:28:59 -04:00
|
|
|
-- TimescaleDB initialization script
|
|
|
|
|
-- This script creates the necessary extensions and hypertables
|
|
|
|
|
|
|
|
|
|
-- Enable TimescaleDB extension
|
|
|
|
|
CREATE EXTENSION IF NOT EXISTS timescaledb;
|
|
|
|
|
|
|
|
|
|
-- Create stock profiles table
|
|
|
|
|
CREATE TABLE IF NOT EXISTS stock_profiles (
|
|
|
|
|
ticker VARCHAR(20) PRIMARY KEY,
|
|
|
|
|
name VARCHAR(255) NOT NULL,
|
|
|
|
|
exchange VARCHAR(50),
|
|
|
|
|
sector VARCHAR(100),
|
|
|
|
|
industry VARCHAR(100),
|
|
|
|
|
market_cap NUMERIC(20, 2),
|
|
|
|
|
description TEXT,
|
|
|
|
|
website VARCHAR(500),
|
|
|
|
|
ceo VARCHAR(255),
|
|
|
|
|
employees INTEGER,
|
|
|
|
|
pe_ratio NUMERIC(10, 2),
|
|
|
|
|
eps NUMERIC(10, 2),
|
|
|
|
|
dividend_yield NUMERIC(8, 4),
|
|
|
|
|
beta NUMERIC(8, 4),
|
|
|
|
|
last_updated TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Create users table
|
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
|
email VARCHAR(255) UNIQUE NOT NULL,
|
|
|
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
|
|
|
name VARCHAR(255),
|
|
|
|
|
timezone VARCHAR(50) DEFAULT 'UTC',
|
|
|
|
|
settings JSONB DEFAULT '{}',
|
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Create prices table (hypertable for TimescaleDB)
|
|
|
|
|
CREATE TABLE IF NOT EXISTS prices (
|
|
|
|
|
ticker VARCHAR(20) NOT NULL,
|
|
|
|
|
date DATE NOT NULL,
|
|
|
|
|
open NUMERIC(12, 2),
|
|
|
|
|
high NUMERIC(12, 2),
|
|
|
|
|
low NUMERIC(12, 2),
|
|
|
|
|
close NUMERIC(12, 2),
|
|
|
|
|
volume BIGINT,
|
|
|
|
|
adjusted_close NUMERIC(12, 2),
|
|
|
|
|
PRIMARY KEY (ticker, date)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
SELECT create_hypertable('prices', 'date', if_not_exists => TRUE);
|
|
|
|
|
|
|
|
|
|
-- Create watchlists table
|
|
|
|
|
CREATE TABLE IF NOT EXISTS watchlists (
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
|
|
|
name VARCHAR(255) NOT NULL,
|
|
|
|
|
description TEXT,
|
|
|
|
|
is_default BOOLEAN DEFAULT FALSE,
|
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Create watchlist items table
|
|
|
|
|
CREATE TABLE IF NOT EXISTS watchlist_items (
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
|
watchlist_id UUID NOT NULL REFERENCES watchlists(id) ON DELETE CASCADE,
|
|
|
|
|
ticker VARCHAR(20) NOT NULL,
|
|
|
|
|
type VARCHAR(20) DEFAULT 'stock',
|
|
|
|
|
custom_notes TEXT,
|
|
|
|
|
added_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
|
price_at_addition NUMERIC(12, 2),
|
|
|
|
|
UNIQUE (watchlist_id, ticker)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Create strategies table
|
|
|
|
|
CREATE TABLE IF NOT EXISTS strategies (
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
|
|
|
name VARCHAR(255) NOT NULL,
|
|
|
|
|
description TEXT,
|
|
|
|
|
type VARCHAR(50) NOT NULL,
|
|
|
|
|
conditions JSONB NOT NULL,
|
|
|
|
|
backtest_results JSONB,
|
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Create screeners table
|
|
|
|
|
CREATE TABLE IF NOT EXISTS screeners (
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
|
|
|
name VARCHAR(255) NOT NULL,
|
|
|
|
|
description TEXT,
|
|
|
|
|
filters JSONB,
|
|
|
|
|
last_run_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
|
results_count INTEGER DEFAULT 0,
|
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
|
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Create screener runs table
|
|
|
|
|
CREATE TABLE IF NOT EXISTS screener_runs (
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
|
screener_id UUID NOT NULL REFERENCES screeners(id) ON DELETE CASCADE,
|
|
|
|
|
results JSONB NOT NULL,
|
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Create alerts table
|
|
|
|
|
CREATE TABLE IF NOT EXISTS alerts (
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
|
watchlist_id UUID NOT NULL REFERENCES watchlists(id) ON DELETE CASCADE,
|
|
|
|
|
type VARCHAR(50) NOT NULL,
|
|
|
|
|
trigger_type VARCHAR(50),
|
|
|
|
|
message TEXT NOT NULL,
|
|
|
|
|
severity VARCHAR(20) DEFAULT 'info',
|
|
|
|
|
status VARCHAR(20) DEFAULT 'active',
|
|
|
|
|
ticker VARCHAR(20),
|
|
|
|
|
triggered_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
|
resolved_at TIMESTAMP WITH TIME ZONE,
|
|
|
|
|
metadata JSONB,
|
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Create sector rotations table
|
|
|
|
|
CREATE TABLE IF NOT EXISTS sector_rotations (
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
|
detection_date DATE NOT NULL,
|
|
|
|
|
sector_ticker VARCHAR(20) NOT NULL,
|
|
|
|
|
sector_name VARCHAR(100) NOT NULL,
|
|
|
|
|
rank_now INTEGER,
|
|
|
|
|
rank_previous INTEGER,
|
|
|
|
|
rank_change INTEGER,
|
|
|
|
|
momentum_20d NUMERIC(10, 4),
|
|
|
|
|
momentum_50d NUMERIC(10, 4),
|
|
|
|
|
momentum_200d NUMERIC(10, 4),
|
|
|
|
|
relative_strength NUMERIC(10, 4),
|
|
|
|
|
rotation_signal VARCHAR(20),
|
|
|
|
|
macro_context TEXT,
|
|
|
|
|
analysis_summary TEXT,
|
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Create SEC filings table
|
|
|
|
|
CREATE TABLE IF NOT EXISTS sec_filings (
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
|
ticker VARCHAR(20) NOT NULL,
|
|
|
|
|
cik VARCHAR(20),
|
|
|
|
|
form_type VARCHAR(20),
|
|
|
|
|
filing_date DATE,
|
|
|
|
|
report_date DATE,
|
|
|
|
|
accession_number VARCHAR(50),
|
|
|
|
|
url TEXT,
|
|
|
|
|
content_summary TEXT,
|
|
|
|
|
key_metrics JSONB,
|
|
|
|
|
sentiment_score NUMERIC(5, 2),
|
|
|
|
|
tags TEXT[],
|
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Create insider trades table
|
|
|
|
|
CREATE TABLE IF NOT EXISTS insider_trades (
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
|
ticker VARCHAR(20) NOT NULL,
|
|
|
|
|
insider_name VARCHAR(255) NOT NULL,
|
|
|
|
|
insider_title VARCHAR(255),
|
|
|
|
|
transaction_date DATE NOT NULL,
|
|
|
|
|
transaction_type VARCHAR(20),
|
|
|
|
|
shares INTEGER,
|
|
|
|
|
price_per_share NUMERIC(12, 2),
|
|
|
|
|
total_value NUMERIC(15, 2),
|
|
|
|
|
shares_owned_after INTEGER,
|
|
|
|
|
filing_date DATE,
|
|
|
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Create peer groups table
|
|
|
|
|
CREATE TABLE IF NOT EXISTS peer_groups (
|
|
|
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
|
ticker VARCHAR(20) NOT NULL,
|
|
|
|
|
peer_ticker VARCHAR(20) NOT NULL,
|
|
|
|
|
similarity_score NUMERIC(5, 2),
|
|
|
|
|
UNIQUE (ticker, peer_ticker)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
-- Create indexes for performance
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_prices_ticker_date ON prices (ticker, date DESC);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_watchlists_user_id ON watchlists (user_id);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_strategies_user_id ON strategies (user_id);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_screeners_user_id ON screeners (user_id);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_alerts_watchlist_id ON alerts (watchlist_id);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sec_filings_ticker ON sec_filings (ticker);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_insider_trades_ticker ON insider_trades (ticker);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sector_rotations_date ON sector_rotations (detection_date DESC);
|
2026-06-06 22:01:40 -04:00
|
|
|
|
|
|
|
|
-- Financials table (income statement / balance sheet / cash flow snapshots)
|
|
|
|
|
CREATE TABLE IF NOT EXISTS financials (
|
|
|
|
|
ticker VARCHAR(20) NOT NULL,
|
|
|
|
|
filing_date DATE NOT NULL,
|
|
|
|
|
period VARCHAR(10) NOT NULL,
|
|
|
|
|
period_end DATE,
|
|
|
|
|
revenue NUMERIC(18,2),
|
|
|
|
|
cost_of_revenue NUMERIC(18,2),
|
|
|
|
|
gross_profit NUMERIC(18,2),
|
|
|
|
|
operating_expense NUMERIC(18,2),
|
|
|
|
|
operating_income NUMERIC(18,2),
|
|
|
|
|
net_income NUMERIC(18,2),
|
|
|
|
|
eps_basic NUMERIC(12,4),
|
|
|
|
|
eps_diluted NUMERIC(12,4),
|
|
|
|
|
total_assets NUMERIC(20,2),
|
|
|
|
|
total_liabilities NUMERIC(20,2),
|
|
|
|
|
total_equity NUMERIC(20,2),
|
|
|
|
|
operating_cashflow NUMERIC(18,2),
|
|
|
|
|
free_cashflow NUMERIC(18,2),
|
|
|
|
|
debt_to_equity NUMERIC(8,4),
|
|
|
|
|
roe NUMERIC(8,4),
|
|
|
|
|
roa NUMERIC(8,4),
|
|
|
|
|
created_at TIMESTAMPTZ DEFAULT now(),
|
|
|
|
|
PRIMARY KEY (ticker, filing_date, period)
|
|
|
|
|
);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_financials_ticker ON financials (ticker);
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_financials_period ON financials (period);
|