PRAGMA foreign_keys = ON; CREATE TABLE IF NOT EXISTS ipo_master ( ticker TEXT PRIMARY KEY, company_name_en TEXT NOT NULL, company_name_zh TEXT, stock_short_name TEXT, exchange TEXT NOT NULL DEFAULT 'HKEX', board TEXT NOT NULL DEFAULT 'Main Board', status TEXT NOT NULL, listing_date TEXT, application_start_date TEXT, application_end_date TEXT, allotment_results_expected_date TEXT, industry_label TEXT, data_as_of TEXT NOT NULL, notes TEXT ); CREATE TABLE IF NOT EXISTS offering_terms ( ticker TEXT PRIMARY KEY REFERENCES ipo_master(ticker), source_id TEXT NOT NULL, prospectus_date TEXT, offer_price_hkd REAL, board_lot INTEGER, min_subscription_amount_hkd REAL, global_offer_shares INTEGER, hk_offer_shares_initial INTEGER, international_offer_shares_initial INTEGER, public_offer_pct_initial REAL, over_allotment_offer_shares INTEGER, offer_size_adjustment_offer_shares INTEGER, market_cap_hkd_m REAL, gross_proceeds_hkd_m REAL, net_proceeds_hkd_m REAL, issued_shares_upon_listing INTEGER, data_as_of TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS ipo_demand ( demand_id TEXT PRIMARY KEY, ticker TEXT NOT NULL REFERENCES ipo_master(ticker), source_id TEXT NOT NULL, stage_date TEXT NOT NULL, valid_applications INTEGER, successful_applications INTEGER, public_oversubscription_times REAL, international_placees INTEGER, international_oversubscription_times REAL, final_hk_offer_shares INTEGER, final_international_offer_shares INTEGER, data_as_of TEXT NOT NULL, notes TEXT ); CREATE TABLE IF NOT EXISTS ipo_market_heat ( heat_id TEXT PRIMARY KEY, ticker TEXT NOT NULL REFERENCES ipo_master(ticker), source_id TEXT NOT NULL REFERENCES source_refs(source_id), stage TEXT NOT NULL DEFAULT 'T0_5_market_heat', provider TEXT NOT NULL, observed_at TEXT NOT NULL, margin_subscription_multiple REAL, margin_multiple_label TEXT, offer_price_low_hkd REAL, offer_price_high_hkd REAL, board_lot INTEGER, min_subscription_amount_hkd REAL, subscription_deadline TEXT, result_announcement_date TEXT, listing_date TEXT, data_as_of TEXT NOT NULL, notes TEXT, UNIQUE (ticker, provider, observed_at) ); CREATE TABLE IF NOT EXISTS external_ipo_history ( history_id TEXT PRIMARY KEY, ticker TEXT NOT NULL, provider TEXT NOT NULL, source_id TEXT NOT NULL, stock_name TEXT, listing_date TEXT, price_range_low_hkd REAL, price_range_high_hkd REAL, issue_price_hkd REAL, one_lot_capital_hkd REAL, one_hand_win_rate_pct REAL, public_oversubscription_times REAL, total_fundraise_hkd_b REAL, market_cap_at_listing_hkd_b REAL, grey_market_return_pct REAL, first_day_return_pct REAL, sponsor TEXT, source_date TEXT, archived_at TEXT NOT NULL, local_path TEXT NOT NULL, url TEXT NOT NULL, file_sha256 TEXT, notes TEXT, UNIQUE (ticker, provider, listing_date), CHECK (local_path NOT LIKE '/%'), CHECK (local_path NOT LIKE './%'), CHECK (local_path NOT LIKE '%\%') ); CREATE TABLE IF NOT EXISTS price_performance ( performance_id TEXT PRIMARY KEY, ticker TEXT NOT NULL REFERENCES ipo_master(ticker), stage TEXT NOT NULL, source_id TEXT, as_of_date TEXT NOT NULL, open_price_hkd REAL, high_price_hkd REAL, low_price_hkd REAL, close_price_hkd REAL, return_pct REAL, turnover_hkd_m REAL, data_as_of TEXT NOT NULL, notes TEXT, UNIQUE (ticker, stage) ); CREATE TABLE IF NOT EXISTS new_listing_report_entries ( report_entry_id TEXT PRIMARY KEY, ticker TEXT NOT NULL REFERENCES ipo_master(ticker), report_year INTEGER NOT NULL, board TEXT NOT NULL, source_id TEXT NOT NULL, company_name_en TEXT NOT NULL, prospectus_date TEXT, listing_date TEXT NOT NULL, offer_price_hkd REAL, funds_raised_hkd REAL, subscription_ratio_times REAL, market_cap_hkd REAL, outstanding_shares_at_listing INTEGER, listing_method TEXT, industry_label TEXT, place_of_incorporation TEXT, sponsors TEXT, reporting_accountants TEXT, valuers TEXT, data_as_of TEXT NOT NULL, notes TEXT, UNIQUE (ticker, report_year, board) ); CREATE TABLE IF NOT EXISTS source_refs ( source_id TEXT PRIMARY KEY, ticker TEXT NOT NULL REFERENCES ipo_master(ticker), source_type TEXT NOT NULL, title TEXT NOT NULL, path_base TEXT NOT NULL DEFAULT 'repo_root', local_path TEXT NOT NULL, url TEXT NOT NULL, file_sha256 TEXT, source_date TEXT, archived_at TEXT NOT NULL, notes TEXT, CHECK (path_base = 'repo_root'), CHECK (local_path NOT LIKE '/%'), CHECK (local_path NOT LIKE './%'), CHECK (local_path NOT LIKE '%\%') ); CREATE TABLE IF NOT EXISTS listed_share_classes ( share_class_id TEXT PRIMARY KEY, ticker TEXT NOT NULL REFERENCES ipo_master(ticker), share_class_type TEXT NOT NULL, related_ticker TEXT NOT NULL, exchange TEXT NOT NULL, board TEXT, relationship TEXT NOT NULL, company_name TEXT, listed_date TEXT, detection_method TEXT NOT NULL, confidence TEXT NOT NULL, prospectus_source_id TEXT REFERENCES source_refs(source_id), web_source_id TEXT REFERENCES source_refs(source_id), evidence_text TEXT, data_as_of TEXT NOT NULL, notes TEXT, UNIQUE (ticker, share_class_type, related_ticker), CHECK (share_class_type IN ('A_share', 'other_onshore_share')), CHECK (relationship IN ('same_issuer', 'parent', 'subsidiary', 'affiliate', 'comparable')), CHECK (confidence IN ('high', 'medium', 'low')) ); CREATE TABLE IF NOT EXISTS data_gaps ( gap_id TEXT PRIMARY KEY, ticker TEXT NOT NULL REFERENCES ipo_master(ticker), stage TEXT NOT NULL, field_name TEXT NOT NULL, reason TEXT NOT NULL, expected_resolution_date TEXT, created_at TEXT NOT NULL, notes TEXT ); CREATE TABLE IF NOT EXISTS sync_runs ( sync_run_id TEXT PRIMARY KEY, mode TEXT NOT NULL, as_of TEXT NOT NULL, started_at TEXT NOT NULL, finished_at TEXT, status TEXT NOT NULL, notes TEXT, CHECK (status IN ('running', 'complete', 'failed')) ); CREATE TABLE IF NOT EXISTS ticker_sync_state ( ticker TEXT NOT NULL REFERENCES ipo_master(ticker), stage TEXT NOT NULL, status TEXT NOT NULL, required INTEGER NOT NULL DEFAULT 1, due_date TEXT, completed_at TEXT, last_source_id TEXT, data_gap_id TEXT, last_sync_run_id TEXT REFERENCES sync_runs(sync_run_id), updated_at TEXT NOT NULL, notes TEXT, PRIMARY KEY (ticker, stage), CHECK (status IN ('complete', 'pending_not_due', 'pending_due', 'blocked', 'not_applicable')), CHECK (required IN (0, 1)) ); CREATE TABLE IF NOT EXISTS sync_tasks ( task_id TEXT PRIMARY KEY, ticker TEXT NOT NULL REFERENCES ipo_master(ticker), stage TEXT NOT NULL, task_type TEXT NOT NULL, task_status TEXT NOT NULL, due_date TEXT, data_gap_id TEXT, last_sync_run_id TEXT REFERENCES sync_runs(sync_run_id), updated_at TEXT NOT NULL, notes TEXT, CHECK (task_status IN ('open', 'waiting_until_due', 'blocked')) );