Files
2026FIFAWorldCup/platform/backend/db_init_timescaledb.sql

154 lines
4.6 KiB
SQL

CREATE EXTENSION IF NOT EXISTS timescaledb;
CREATE TABLE IF NOT EXISTS venues (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
city TEXT NOT NULL,
country TEXT NOT NULL,
altitude_meters INTEGER,
timezone TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS teams (
id TEXT PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
fifa_rank INTEGER,
current_elo_rating DOUBLE PRECISION,
group_name TEXT
);
CREATE TABLE IF NOT EXISTS bookmakers (
id TEXT PRIMARY KEY,
name TEXT NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS matches (
id TEXT PRIMARY KEY,
home_team_id TEXT NOT NULL REFERENCES teams (id),
away_team_id TEXT NOT NULL REFERENCES teams (id),
venue_id TEXT NOT NULL REFERENCES venues (id),
match_time_utc TIMESTAMPTZ NOT NULL,
status TEXT NOT NULL DEFAULT 'pre-match',
home_xg DOUBLE PRECISION,
away_xg DOUBLE PRECISION
);
CREATE TABLE IF NOT EXISTS odds_history (
id BIGSERIAL PRIMARY KEY,
match_id TEXT NOT NULL REFERENCES matches (id),
bookmaker_id TEXT NOT NULL REFERENCES bookmakers (id),
market_type TEXT NOT NULL,
selection TEXT NOT NULL,
decimal_odds DOUBLE PRECISION NOT NULL,
implied_probability DOUBLE PRECISION NOT NULL,
recorded_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE IF NOT EXISTS smart_money_flow (
id BIGSERIAL PRIMARY KEY,
match_id TEXT NOT NULL REFERENCES matches (id),
market_type TEXT NOT NULL,
selection TEXT NOT NULL,
ticket_pct DOUBLE PRECISION NOT NULL,
handle_pct DOUBLE PRECISION NOT NULL,
sharp_indicator BOOLEAN NOT NULL,
recorded_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_matches_time_status ON matches (match_time_utc, status);
CREATE INDEX IF NOT EXISTS idx_odds_match_market_recorded_at
ON odds_history (match_id, market_type, recorded_at DESC);
CREATE INDEX IF NOT EXISTS idx_money_flow_match_recorded_at
ON smart_money_flow (match_id, market_type, recorded_at DESC);
CREATE TABLE IF NOT EXISTS value_bet_recommendations (
id TEXT PRIMARY KEY,
match_id TEXT NOT NULL REFERENCES matches (id),
market_type TEXT NOT NULL,
selection TEXT NOT NULL,
stake DOUBLE PRECISION NOT NULL,
recommended_odds DOUBLE PRECISION NOT NULL,
closing_odds DOUBLE PRECISION,
is_win BOOLEAN NOT NULL DEFAULT FALSE,
settled_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
clv_ratio DOUBLE PRECISION,
pnl DOUBLE PRECISION NOT NULL DEFAULT 0,
note TEXT
);
CREATE INDEX IF NOT EXISTS idx_value_bet_recommendations_match_time
ON value_bet_recommendations (match_id, settled_at DESC);
SELECT create_hypertable(
'odds_history',
'recorded_at',
chunk_time_interval => INTERVAL '1 hour',
if_not_exists => TRUE
);
CREATE INDEX IF NOT EXISTS idx_odds_history_time_gist
ON odds_history USING GIST (recorded_at);
-- Stage 33: Affiliate Marketing
CREATE TABLE IF NOT EXISTS affiliate_bookmakers (
id TEXT PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
tracking_url TEXT NOT NULL,
commission_rate DOUBLE PRECISION NOT NULL DEFAULT 0.0
);
CREATE TABLE IF NOT EXISTS affiliate_clicks (
id BIGSERIAL PRIMARY KEY,
bookmaker_id TEXT NOT NULL REFERENCES affiliate_bookmakers (id),
user_ip_hash TEXT NOT NULL,
user_agent TEXT,
referrer TEXT,
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
converted BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE INDEX IF NOT EXISTS idx_affiliate_clicks_timestamp
ON affiliate_clicks (timestamp DESC);
SELECT create_hypertable(
'affiliate_clicks',
'timestamp',
chunk_time_interval => INTERVAL '1 day',
if_not_exists => TRUE
);
-- Note: TimescaleDB continuous aggregates require a bit more setup in modern versions.
-- A simple materialized view for daily conversion rates:
CREATE MATERIALIZED VIEW IF NOT EXISTS affiliate_daily_conversions
WITH (timescaledb.continuous) AS
SELECT
time_bucket('1 day', timestamp) AS bucket,
bookmaker_id,
COUNT(*) AS total_clicks,
COUNT(*) FILTER (WHERE converted = TRUE) AS total_conversions
FROM affiliate_clicks
GROUP BY bucket, bookmaker_id
WITH NO DATA;
-- Stage 35: Social Trading (Copy Bets & Leaderboard)
CREATE TABLE IF NOT EXISTS user_profiles (
id TEXT PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
clv_score DOUBLE PRECISION NOT NULL DEFAULT 0.0,
roi_30d DOUBLE PRECISION NOT NULL DEFAULT 0.0,
sharp_rating INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS copy_bets (
id BIGSERIAL PRIMARY KEY,
follower_id TEXT NOT NULL REFERENCES user_profiles(id),
leader_id TEXT NOT NULL REFERENCES user_profiles(id),
recommendation_id TEXT NOT NULL REFERENCES value_bet_recommendations(id),
follower_stake DOUBLE PRECISION NOT NULL,
copied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_copy_bets_leader_time
ON copy_bets (leader_id, copied_at DESC);