fix: backfill symbol_demand for sidebar-added symbols + analyst ratings schema fix

- Add await ctx.cache.subscribe() to addSymbol mutation so symbols
  added via the sidebar get registered in symbol_demand and yfinance
  jobs are queued immediately
- Backfill PEP, WYNN, STZ, CELH into symbol_demand + adapter_queue
- Upgrade yahoo-finance2 3.15.3 -> 3.15.4 and pass validateResult:false
  to quoteSummary() to handle Yahoo schema drift
- Add error detail logging for analyst ratings schema failures
- Update .gitignore with common ignores
This commit is contained in:
Investor Flow Build
2026-07-23 18:02:24 -04:00
parent 5ef2b2f060
commit e262187c3c
204 changed files with 25014 additions and 2934 deletions
+99 -2
View File
@@ -21,7 +21,8 @@ CREATE TABLE IF NOT EXISTS users (
drawdown_tolerance REAL, -- onboarding max-drawdown % (beginner -20, etc.)
convexity_posture TEXT NOT NULL DEFAULT 'off', -- off|covered_income|cash_secured_entry|insurance_sleeve|leaps_conviction
backup_codes_hashed TEXT, -- JSON array of scrypt-hashed backup codes (slice 2)
created_at TEXT NOT NULL
status TEXT NOT NULL DEFAULT 'active', -- active|pending_approval|rejected (user provisioning)
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS sessions (
@@ -112,7 +113,27 @@ CREATE TABLE IF NOT EXISTS institution_filings (
value_usd REAL,
reported_quarter TEXT NOT NULL, -- calendar quarter positions are "as of"
filed_at TEXT NOT NULL,
fetched_at TEXT NOT NULL
fetched_at TEXT NOT NULL,
put_call TEXT -- Put|Call|null for common stock (13F only)
);
-- Idempotency for institution_filings: one row per (filer, symbol, quarter, form).
-- Created in runMigrations (after legacy de-dupe) so it never fails on existing DBs.
-- CREATE UNIQUE INDEX IF NOT EXISTS uq_inst_filings
-- ON institution_filings(filer_cik, symbol, reported_quarter, form);
-- Data-quality lint reports (periodic gap/staleness detection).
CREATE TABLE IF NOT EXISTS data_quality (
symbol TEXT NOT NULL,
kind TEXT NOT NULL, -- 'institution_filings' | 'insider_transactions'
last_checked_at TEXT,
stored_count INTEGER,
discovered_count INTEGER,
missing_count INTEGER,
stale INTEGER NOT NULL DEFAULT 0, -- 1 if latest data is older than threshold
status TEXT NOT NULL, -- ok | gaps_found | stale | error
detail TEXT, -- JSON blob (notes)
PRIMARY KEY (symbol, kind)
);
CREATE TABLE IF NOT EXISTS insider_transactions (
@@ -129,6 +150,7 @@ CREATE TABLE IF NOT EXISTS insider_transactions (
classification TEXT NOT NULL, -- informed_buy|informed_sell|routine
filed_at TEXT NOT NULL,
fetched_at TEXT NOT NULL,
accession TEXT, -- SEC accession number (enables lint diff by filing)
PRIMARY KEY (form4_id, symbol, insider_name, tx_date)
);
@@ -254,6 +276,27 @@ CREATE TABLE IF NOT EXISTS portfolio_holdings (
CREATE UNIQUE INDEX IF NOT EXISTS uq_portfolio_owner_symbol
ON portfolio_holdings(owner_id, symbol);
-- User-owned option legs (long calls/puts, covered calls, CSPs). Not chain cache.
CREATE TABLE IF NOT EXISTS portfolio_option_legs (
id TEXT PRIMARY KEY,
owner_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
underlying TEXT NOT NULL,
right TEXT NOT NULL, -- call | put
side TEXT NOT NULL, -- long | short
strike REAL NOT NULL,
expiry TEXT NOT NULL, -- YYYY-MM-DD
contracts REAL NOT NULL,
premium REAL NOT NULL, -- avg premium per share
multiplier INTEGER NOT NULL DEFAULT 100,
role TEXT NOT NULL, -- long_call | long_put | covered_call | cash_secured_put | other
status TEXT NOT NULL DEFAULT 'open', -- open | closed
acquired_at TEXT NOT NULL,
note TEXT
);
CREATE INDEX IF NOT EXISTS idx_option_legs_owner_status
ON portfolio_option_legs(owner_id, status);
CREATE TABLE IF NOT EXISTS trades (
id TEXT PRIMARY KEY,
owner_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
@@ -387,6 +430,23 @@ CREATE TABLE IF NOT EXISTS rotation_signals (
verdict TEXT -- real|false_alarm (gamma two-stage)
);
-- Daily snapshot of sector RS ranks (Market Outlook history).
CREATE TABLE IF NOT EXISTS rotation_rank_snapshots (
as_of_date TEXT NOT NULL, -- YYYY-MM-DD
symbol TEXT NOT NULL,
name TEXT,
grp TEXT, -- UI group label
rank_1m INTEGER,
rs_1m REAL,
rank_1w INTEGER,
rs_1w REAL,
leadership TEXT, -- leading|lagging|inline|unknown
early_watch INTEGER NOT NULL DEFAULT 0,
strength TEXT, -- none|weak|moderate|strong (market-wide)
PRIMARY KEY (as_of_date, symbol)
);
CREATE INDEX IF NOT EXISTS idx_rotation_rank_date ON rotation_rank_snapshots(as_of_date DESC);
-- ===== Section 1.5 — LLM provenance (ADR-0006). Operator-managed, NOT user-editable. =====
CREATE TABLE IF NOT EXISTS llm_providers (
id TEXT PRIMARY KEY, -- 'local_ollama' | 'local_vllm' | 'zen_glm_5_2' | ...
@@ -441,6 +501,32 @@ CREATE TABLE IF NOT EXISTS reddit_posts (
CREATE INDEX IF NOT EXISTS idx_x_cookie_cashtag ON x_cookie_posts(cashtag, posted_at DESC);
CREATE INDEX IF NOT EXISTS idx_reddit_subreddit ON reddit_posts(subreddit, posted_at DESC);
-- ===== X cookie credentials (operator-managed; never exposed to client) =====
-- Singleton row (id = 'singleton') stores AES-256-GCM encrypted ct0 + auth_token.
-- Health status is tracked here so the adapter can report degraded/failed state.
CREATE TABLE IF NOT EXISTS x_credentials (
id TEXT PRIMARY KEY DEFAULT 'singleton',
ct0_enc TEXT, -- AES-256-GCM ciphertext (or NULL if unset)
auth_token_enc TEXT, -- AES-256-GCM ciphertext (or NULL if unset)
fred_api_key_enc TEXT, -- AES-256-GCM ciphertext (or NULL if unset)
healthy INTEGER NOT NULL DEFAULT 0, -- 1 = healthy, 0 = degraded/failed
last_error TEXT,
updated_at TEXT -- ISO8601; when credentials were last stored or health last reported
);
-- Per-symbol tracked X accounts (admin-managed). The overview page merges cashtag
-- feed with timelines of these handles for a given symbol.
CREATE TABLE IF NOT EXISTS x_accounts (
id TEXT PRIMARY KEY, -- uuid
symbol TEXT NOT NULL,
handle TEXT NOT NULL, -- bare handle (no @)
label TEXT, -- optional human-readable name
created_at TEXT NOT NULL,
UNIQUE(symbol, handle)
);
CREATE INDEX IF NOT EXISTS idx_x_accounts_symbol ON x_accounts(symbol);
-- ===== Indexes (existing) =====
CREATE INDEX IF NOT EXISTS idx_pc_symbol_tf_ts ON price_candles(symbol, timeframe, ts);
CREATE INDEX IF NOT EXISTS idx_quote_obs ON quotes(observed_at);
@@ -550,3 +636,14 @@ CREATE TABLE IF NOT EXISTS theses (
CREATE INDEX IF NOT EXISTS idx_theses_user ON theses(user_id);
CREATE INDEX IF NOT EXISTS idx_theses_symbol ON theses(symbol);
-- Rotation signal state (latest check result for alert dedup)
CREATE TABLE IF NOT EXISTS rotation_state (
id TEXT PRIMARY KEY DEFAULT 'singleton',
signal TEXT NOT NULL DEFAULT 'none',
spread REAL,
inflow_avg REAL,
outflow_avg REAL,
sub_group TEXT, -- the strongest sub-group (e.g. "Financials")
checked_at TEXT
);