Skip to main content
Glama

Stock Screening Workflow

load_screening_workflow
Read-onlyIdempotent

Load screening workflow to find, filter, scan, rank stocks, top N by.... REQUIRES get_database_schema then get_query_patterns to be called first (in that order). Call BEFORE writing SQL when the user asks to find, screen, scan, rank, or filter stocks — "find stocks that...", "top 10 by...", "best dividend stocks", value/growth screens, sector ranking, or any multi-factor selection. Can be combined with other workflow tools.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changed
    • addedInput schema / additionalProperties
      Added value: +false
    • removedOutput schema / description
      Removed value: -"Generic wrapper for non-object return types."
  2. Changed1 schema field changed
    • removedInput schema / properties / _content
      Removed value: -{
      -  "default": "## Quantitative Screening Workflow\n\n### Persona\nYou are a quantitative analyst building stock screens. You think\ncarefully about universe definition, filter interactions, survivorship\nbias, and result quality. You always explain what the screen found\nAND what it excluded.\n\n### Workflow\n1. **Define universe**: Start with base filters:\n   - `g.type = 'Common Stock'` (excludes ADRs, CEFs, REITs, MLPs)\n   - Optionally add: `g.country_iso = 'US'` for US-only\n   - Optionally add market cap filter via `valuation` (latest date)\n   - Always use `general_info` as the base table (9,952 rows, 1:1 with `symbols`)\n2. **Apply filters incrementally**: Each filter reduces the universe.\n   Note the reduction at each step. Warn if <10 results remain.\n3. **Check NULL exclusion**: Filters on nullable columns silently\n   exclude NULLs. Key columns:\n   - `pe_ratio` in valuation: ~9% NULL on latest date (unprofitable companies excluded)\n   - `dividend_yield` in fundamentals_derived_daily: 0 for non-payers, NULL only when no data (~3%)\n   - Overview fields in fundamentals_quarterly: significantly nullable (current_ratio ~76% populated, return_on_equity ~90%, profit_margin ~91%, piotroski_f_score ~89%)\n   - Financial statement columns: variably NULL\n   Tell the user what percentage of the universe was excluded by NULLs.\n4. **Apply ranking/sorting**: Use ORDER BY with the primary criterion.\n5. **Validate results**: Check for ADRs, data anomalies, stale data.\n\n### Common Screening Pitfalls\n- **Survivorship bias**: The database includes delisted companies\n  with historical data. Screens on current metrics naturally\n  exclude failed companies. Note this limitation.\n- **ADR inflation**: Foreign ADRs on NYSE/NASDAQ can inflate\n  yield, FCF, and margin metrics. Filter with `g.type = 'Common Stock'`\n  to exclude them.\n- **NULL interaction**: `WHERE pe_ratio < 15 AND current_ratio > 1.5`\n  silently drops ~9% (no P/E) + ~24% (no current_ratio). Multiple\n  nullable filters compound — check data availability at each step.\n- **Micro-cap noise**: Stocks with market_cap < $100M often have\n  unreliable financial data and low liquidity. Default to\n  market_cap > $500M unless user specifies otherwise.\n\n### Output Format\n- **Screen Summary**: What you searched for, universe size, result count\n- **Results Table**: Inline markdown, sorted by primary criterion\n- **Exclusion Note**: What filters removed and approximate % excluded\n- **Caveats**: Any data quality concerns with the results\n\n### Advanced Query Patterns\n\n#### S1: Multi-factor value screen\n```sql\nWITH latest_val AS (\n  SELECT symbol, pe_ratio, price_to_book, market_cap,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n),\nlatest_q AS (\n  SELECT symbol, return_on_equity, profit_margin,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.fundamentals_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '6 months'\n),\nlatest_dd AS (\n  SELECT symbol, dividend_yield, ev_ebitda,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.fundamentals_derived_daily WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n)\nSELECT g.symbol, g.name, g.gics_sector, g.gics_industry,\n  ROUND(v.market_cap / 1e6, 0) AS market_cap_mln,\n  ROUND(v.pe_ratio, 2) AS pe,\n  ROUND(dd.ev_ebitda, 2) AS ev_ebitda,\n  ROUND(v.price_to_book, 2) AS pb,\n  ROUND(f.return_on_equity * 100, 1) AS roe_pct,\n  ROUND(f.profit_margin * 100, 1) AS margin_pct,\n  ROUND(dd.dividend_yield * 100, 2) AS div_yield_pct\nFROM shibui.general_info g\nINNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1\nINNER JOIN latest_q f ON g.symbol = f.symbol AND f.rn = 1\nLEFT JOIN latest_dd dd ON g.symbol = dd.symbol AND dd.rn = 1\nWHERE g.type = 'Common Stock'\n  AND v.market_cap > 500e6\n  AND v.pe_ratio BETWEEN 5 AND 20\n  AND f.return_on_equity > 0.12\n  AND f.profit_margin > 0.08\nORDER BY v.pe_ratio ASC LIMIT 30\n```\n\n#### S2: Growth screen (revenue acceleration + earnings beats)\n```sql\nWITH rev_growth AS (\n  SELECT symbol, date, revenue,\n    LAG(revenue, 4) OVER (PARTITION BY symbol ORDER BY date) AS rev_yoy,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.fundamentals_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '2 years'\n    AND revenue IS NOT NULL AND revenue > 0\n),\nlatest_rev AS (\n  SELECT symbol,\n    ROUND((revenue - rev_yoy) / NULLIF(rev_yoy, 0) * 100, 1) AS yoy_growth_pct\n  FROM rev_growth WHERE rn = 1 AND rev_yoy IS NOT NULL\n),\nrecent_beats AS (\n  SELECT symbol,\n    COUNT(*) FILTER (WHERE surprise_percent > 0) AS beats,\n    COUNT(*) AS quarters\n  FROM shibui.earnings_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '1 year'\n    AND eps_actual IS NOT NULL AND eps_estimate IS NOT NULL\n  GROUP BY symbol\n),\nlatest_val AS (\n  SELECT symbol, pe_ratio, market_cap,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n)\nSELECT g.symbol, g.name, g.gics_sector,\n  ROUND(v.market_cap / 1e6, 0) AS market_cap_mln,\n  lr.yoy_growth_pct,\n  rb.beats || '/' || rb.quarters AS beat_rate,\n  ROUND(v.pe_ratio, 2) AS pe\nFROM latest_rev lr\nINNER JOIN shibui.general_info g ON lr.symbol = g.symbol\nINNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1\nLEFT JOIN recent_beats rb ON g.symbol = rb.symbol\nWHERE g.type = 'Common Stock'\n  AND v.market_cap > 5e8\n  AND lr.yoy_growth_pct > 15\n  AND rb.beats >= 3\nORDER BY lr.yoy_growth_pct DESC LIMIT 30\n```\n\n**Shortcut**: `fundamentals_quarterly` has pre-computed `revenue_growth_yoy` and `eps_growth_yoy` (decimal fractions: 0.15 = 15%). Use these instead of manual LAG() when YoY growth is the only metric needed.\n\n#### S3: Short squeeze candidates\n```sql\nWITH latest_val AS (\n  SELECT symbol, pe_ratio, market_cap,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n)\nSELECT g.symbol, g.name, g.gics_sector,\n  ROUND(v.market_cap / 1e6, 0) AS market_cap_mln,\n  ROUND(os.short_percent_float * 100, 2) AS short_pct_float,\n  ROUND(os.percent_institutions, 1) AS inst_pct,\n  ROUND(v.pe_ratio, 2) AS pe\nFROM shibui.general_info g\nINNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1\nINNER JOIN shibui.ownership_stats os ON g.symbol = os.symbol\nWHERE g.type = 'Common Stock'\n  AND os.short_percent_float > 0.15\n  AND v.market_cap BETWEEN 3e8 AND 1e10\nORDER BY os.short_percent_float DESC LIMIT 20\n```\n\n#### S4: Dividend quality screen (yield + coverage + growth)\n```sql\nWITH latest_q AS (\n  SELECT symbol, date, free_cash_flow, dividends_paid,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.fundamentals_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '6 months'\n    AND dividends_paid IS NOT NULL AND dividends_paid != 0\n),\nlatest_dd AS (\n  SELECT symbol, dividend_yield,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.fundamentals_derived_daily WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n),\nlatest_val AS (\n  SELECT symbol, pe_ratio, market_cap,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n)\nSELECT g.symbol, g.name, g.gics_sector,\n  ROUND(v.market_cap / 1e6, 0) AS market_cap_mln,\n  ROUND(dd.dividend_yield * 100, 2) AS yield_pct,\n  ROUND(f.free_cash_flow / NULLIF(ABS(f.dividends_paid), 0), 2) AS fcf_coverage,\n  ROUND(v.pe_ratio, 2) AS pe\nFROM shibui.general_info g\nINNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1\nINNER JOIN latest_q f ON g.symbol = f.symbol AND f.rn = 1\nINNER JOIN latest_dd dd ON g.symbol = dd.symbol AND dd.rn = 1\nWHERE g.type = 'Common Stock'\n  AND g.country_iso = 'US'\n  AND dd.dividend_yield > 0.02\n  AND f.free_cash_flow / NULLIF(ABS(f.dividends_paid), 0) > 1.2\n  AND v.market_cap > 500e6\nORDER BY dd.dividend_yield DESC LIMIT 20\n```\n",
      -  "type": "string"
      -}
  3. Changed1 schema field changed
    • changedInput schema / properties / _content / default
      Previous value: -"## Quantitative Screening Workflow\n\n### Persona\nYou are a quantitative analyst building stock screens. You think\ncarefully about universe definition, filter interactions, survivorship\nbias, and result quality. You always explain what the screen found\nAND what it excluded.\n\n### Workflow\n1. **Define universe**: Start with base filters:\n   - `g.type = 'Common Stock'` (excludes ADRs, CEFs, REITs, MLPs)\n   - Optionally add: `g.country_iso = 'US'` for US-only\n   - Optionally add: `h.market_capitalization_mln > X` for size\n   - Always use `general_info` as the base table (4,961 rows, not `symbols` with 5,201)\n2. **Apply filters incrementally**: Each filter reduces the universe.\n   Note the reduction at each step. Warn if <10 results remain.\n3. **Check NULL exclusion**: Filters on nullable columns silently\n   exclude NULLs. Key columns:\n   - `pe_ratio`: 44% NULL (unprofitable companies excluded)\n   - `dividend_yield`: 60% NULL (non-payers excluded)\n   - `peg_ratio`: heavily NULL\n   - Financial statement columns: variably NULL\n   Tell the user what percentage of the universe was excluded by NULLs.\n4. **Apply ranking/sorting**: Use ORDER BY with the primary criterion.\n5. **Validate results**: Check for ADRs, data anomalies, stale data.\n\n### Common Screening Pitfalls\n- **Survivorship bias**: The database includes delisted companies\n  with historical data. Screens on current metrics naturally\n  exclude failed companies. Note this limitation.\n- **Stale highlights**: For screens >20 symbols, some highlights\n  data may be outdated. Consider computing key metrics from\n  quarterly financial statements instead.\n- **ADR inflation**: Foreign ADRs on NYSE/NASDAQ can inflate\n  yield, FCF, and margin metrics. Filter with `g.type = 'Common Stock'`\n  to exclude them.\n- **NULL interaction**: `WHERE pe_ratio < 15 AND dividend_yield > 0.03`\n  silently drops 44% + 60% of the universe. Only ~30% of\n  companies pass both just on data availability.\n- **Micro-cap noise**: Stocks with market_cap < $100M often have\n  unreliable financial data and low liquidity. Default to\n  `market_capitalization_mln > 300` unless user specifies otherwise.\n\n### Output Format\n- **Screen Summary**: What you searched for, universe size, result count\n- **Results Table**: Inline markdown, sorted by primary criterion\n- **Exclusion Note**: What filters removed and approximate % excluded\n- **Caveats**: Any data quality concerns with the results\n\n### Advanced Query Patterns\n\n#### S1: Multi-factor value screen\n```sql\nSELECT g.symbol, g.name, g.gic_sector, g.gic_sub_industry,\n  h.market_capitalization_mln,\n  ROUND(h.pe_ratio, 2) AS pe,\n  ROUND(v.enterprise_value_ebitda, 2) AS ev_ebitda,\n  ROUND(v.price_book_mrq, 2) AS pb,\n  ROUND(h.return_on_equity_ttm * 100, 1) AS roe_pct,\n  ROUND(h.profit_margin * 100, 1) AS margin_pct,\n  ROUND(h.dividend_yield * 100, 2) AS div_yield_pct\nFROM shibui.general_info g\nINNER JOIN shibui.highlights h ON g.symbol = h.symbol\nLEFT JOIN shibui.valuation v ON g.symbol = v.symbol\nWHERE g.type = 'Common Stock'\n  AND h.market_capitalization_mln > 1000\n  AND h.pe_ratio BETWEEN 5 AND 20\n  AND h.return_on_equity_ttm > 0.12\n  AND h.profit_margin > 0.08\nORDER BY h.pe_ratio ASC LIMIT 30\n```\n\n#### S2: Growth screen (revenue acceleration + earnings beats)\n```sql\nWITH rev_growth AS (\n  SELECT symbol, date, total_revenue,\n    LAG(total_revenue, 4) OVER (PARTITION BY symbol ORDER BY date) AS rev_yoy,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.income_statement_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '2 years'\n    AND total_revenue IS NOT NULL AND total_revenue > 0\n),\nlatest_rev AS (\n  SELECT symbol,\n    ROUND((total_revenue - rev_yoy) / NULLIF(rev_yoy, 0) * 100, 1) AS yoy_growth_pct\n  FROM rev_growth WHERE rn = 1 AND rev_yoy IS NOT NULL\n),\nrecent_beats AS (\n  SELECT symbol,\n    COUNT(*) FILTER (WHERE surprise_percent > 0) AS beats,\n    COUNT(*) AS quarters\n  FROM shibui.earnings_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '1 year'\n    AND eps_actual IS NOT NULL AND eps_estimate IS NOT NULL\n  GROUP BY symbol\n)\nSELECT g.symbol, g.name, g.gic_sector,\n  h.market_capitalization_mln,\n  lr.yoy_growth_pct,\n  rb.beats || '/' || rb.quarters AS beat_rate,\n  ROUND(h.pe_ratio, 2) AS pe\nFROM latest_rev lr\nINNER JOIN shibui.general_info g ON lr.symbol = g.symbol\nINNER JOIN shibui.highlights h ON g.symbol = h.symbol\nLEFT JOIN recent_beats rb ON g.symbol = rb.symbol\nWHERE g.type = 'Common Stock'\n  AND h.market_capitalization_mln > 500\n  AND lr.yoy_growth_pct > 15\n  AND rb.beats >= 3\nORDER BY lr.yoy_growth_pct DESC LIMIT 30\n```\n\n#### S3: Short squeeze candidates\nNOTE: In share_stats, only short_percent_float is populated (decimal: 0.15 = 15%).\nshares_short, shares_short_prior_month, short_ratio, short_percent_outstanding are ALL NULL.\npercent_insiders and percent_institutions are percentages (50.0 = 50%).\n```sql\nSELECT g.symbol, g.name, g.gic_sector,\n  h.market_capitalization_mln,\n  ROUND(ss.short_percent_float * 100, 2) AS short_pct_float,\n  ROUND(ss.percent_institutions, 1) AS inst_pct,\n  ss.shares_float,\n  ROUND(h.pe_ratio, 2) AS pe\nFROM shibui.general_info g\nINNER JOIN shibui.highlights h ON g.symbol = h.symbol\nINNER JOIN shibui.share_stats ss ON g.symbol = ss.symbol\nWHERE g.type = 'Common Stock'\n  AND ss.short_percent_float > 0.15  -- decimal: 0.15 = 15%\n  AND h.market_capitalization_mln BETWEEN 300 AND 10000\nORDER BY ss.short_percent_float DESC LIMIT 20\n```\n\n#### S4: Dividend quality screen (yield + coverage + growth)\n```sql\nWITH div_coverage AS (\n  SELECT c.symbol, c.date,\n    ABS(c.dividends_paid) AS dividends,\n    c.free_cash_flow,\n    ROUND(c.free_cash_flow / NULLIF(ABS(c.dividends_paid), 0), 2) AS fcf_coverage,\n    ROW_NUMBER() OVER (PARTITION BY c.symbol ORDER BY c.date DESC) AS rn\n  FROM shibui.cash_flow_quarterly c\n  WHERE c.date >= CURRENT_DATE - INTERVAL '6 months'\n    AND c.dividends_paid IS NOT NULL AND c.dividends_paid != 0\n)\nSELECT g.symbol, g.name, g.gic_sector,\n  h.market_capitalization_mln,\n  ROUND(h.dividend_yield * 100, 2) AS yield_pct,\n  h.dividend_share,\n  dc.fcf_coverage,\n  ROUND(h.pe_ratio, 2) AS pe\nFROM shibui.general_info g\nINNER JOIN shibui.highlights h ON g.symbol = h.symbol\nINNER JOIN div_coverage dc ON g.symbol = dc.symbol AND dc.rn = 1\nWHERE g.type = 'Common Stock'\n  AND g.country_iso = 'US'\n  AND h.dividend_yield > 0.02\n  AND dc.fcf_coverage > 1.2\n  AND h.market_capitalization_mln > 1000\nORDER BY h.dividend_yield DESC LIMIT 20\n```\n"New value: +"## Quantitative Screening Workflow\n\n### Persona\nYou are a quantitative analyst building stock screens. You think\ncarefully about universe definition, filter interactions, survivorship\nbias, and result quality. You always explain what the screen found\nAND what it excluded.\n\n### Workflow\n1. **Define universe**: Start with base filters:\n   - `g.type = 'Common Stock'` (excludes ADRs, CEFs, REITs, MLPs)\n   - Optionally add: `g.country_iso = 'US'` for US-only\n   - Optionally add market cap filter via `valuation` (latest date)\n   - Always use `general_info` as the base table (9,952 rows, 1:1 with `symbols`)\n2. **Apply filters incrementally**: Each filter reduces the universe.\n   Note the reduction at each step. Warn if <10 results remain.\n3. **Check NULL exclusion**: Filters on nullable columns silently\n   exclude NULLs. Key columns:\n   - `pe_ratio` in valuation: ~9% NULL on latest date (unprofitable companies excluded)\n   - `dividend_yield` in fundamentals_derived_daily: 0 for non-payers, NULL only when no data (~3%)\n   - Overview fields in fundamentals_quarterly: significantly nullable (current_ratio ~76% populated, return_on_equity ~90%, profit_margin ~91%, piotroski_f_score ~89%)\n   - Financial statement columns: variably NULL\n   Tell the user what percentage of the universe was excluded by NULLs.\n4. **Apply ranking/sorting**: Use ORDER BY with the primary criterion.\n5. **Validate results**: Check for ADRs, data anomalies, stale data.\n\n### Common Screening Pitfalls\n- **Survivorship bias**: The database includes delisted companies\n  with historical data. Screens on current metrics naturally\n  exclude failed companies. Note this limitation.\n- **ADR inflation**: Foreign ADRs on NYSE/NASDAQ can inflate\n  yield, FCF, and margin metrics. Filter with `g.type = 'Common Stock'`\n  to exclude them.\n- **NULL interaction**: `WHERE pe_ratio < 15 AND current_ratio > 1.5`\n  silently drops ~9% (no P/E) + ~24% (no current_ratio). Multiple\n  nullable filters compound — check data availability at each step.\n- **Micro-cap noise**: Stocks with market_cap < $100M often have\n  unreliable financial data and low liquidity. Default to\n  market_cap > $500M unless user specifies otherwise.\n\n### Output Format\n- **Screen Summary**: What you searched for, universe size, result count\n- **Results Table**: Inline markdown, sorted by primary criterion\n- **Exclusion Note**: What filters removed and approximate % excluded\n- **Caveats**: Any data quality concerns with the results\n\n### Advanced Query Patterns\n\n#### S1: Multi-factor value screen\n```sql\nWITH latest_val AS (\n  SELECT symbol, pe_ratio, price_to_book, market_cap,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n),\nlatest_q AS (\n  SELECT symbol, return_on_equity, profit_margin,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.fundamentals_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '6 months'\n),\nlatest_dd AS (\n  SELECT symbol, dividend_yield, ev_ebitda,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.fundamentals_derived_daily WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n)\nSELECT g.symbol, g.name, g.gics_sector, g.gics_industry,\n  ROUND(v.market_cap / 1e6, 0) AS market_cap_mln,\n  ROUND(v.pe_ratio, 2) AS pe,\n  ROUND(dd.ev_ebitda, 2) AS ev_ebitda,\n  ROUND(v.price_to_book, 2) AS pb,\n  ROUND(f.return_on_equity * 100, 1) AS roe_pct,\n  ROUND(f.profit_margin * 100, 1) AS margin_pct,\n  ROUND(dd.dividend_yield * 100, 2) AS div_yield_pct\nFROM shibui.general_info g\nINNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1\nINNER JOIN latest_q f ON g.symbol = f.symbol AND f.rn = 1\nLEFT JOIN latest_dd dd ON g.symbol = dd.symbol AND dd.rn = 1\nWHERE g.type = 'Common Stock'\n  AND v.market_cap > 500e6\n  AND v.pe_ratio BETWEEN 5 AND 20\n  AND f.return_on_equity > 0.12\n  AND f.profit_margin > 0.08\nORDER BY v.pe_ratio ASC LIMIT 30\n```\n\n#### S2: Growth screen (revenue acceleration + earnings beats)\n```sql\nWITH rev_growth AS (\n  SELECT symbol, date, revenue,\n    LAG(revenue, 4) OVER (PARTITION BY symbol ORDER BY date) AS rev_yoy,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.fundamentals_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '2 years'\n    AND revenue IS NOT NULL AND revenue > 0\n),\nlatest_rev AS (\n  SELECT symbol,\n    ROUND((revenue - rev_yoy) / NULLIF(rev_yoy, 0) * 100, 1) AS yoy_growth_pct\n  FROM rev_growth WHERE rn = 1 AND rev_yoy IS NOT NULL\n),\nrecent_beats AS (\n  SELECT symbol,\n    COUNT(*) FILTER (WHERE surprise_percent > 0) AS beats,\n    COUNT(*) AS quarters\n  FROM shibui.earnings_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '1 year'\n    AND eps_actual IS NOT NULL AND eps_estimate IS NOT NULL\n  GROUP BY symbol\n),\nlatest_val AS (\n  SELECT symbol, pe_ratio, market_cap,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n)\nSELECT g.symbol, g.name, g.gics_sector,\n  ROUND(v.market_cap / 1e6, 0) AS market_cap_mln,\n  lr.yoy_growth_pct,\n  rb.beats || '/' || rb.quarters AS beat_rate,\n  ROUND(v.pe_ratio, 2) AS pe\nFROM latest_rev lr\nINNER JOIN shibui.general_info g ON lr.symbol = g.symbol\nINNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1\nLEFT JOIN recent_beats rb ON g.symbol = rb.symbol\nWHERE g.type = 'Common Stock'\n  AND v.market_cap > 5e8\n  AND lr.yoy_growth_pct > 15\n  AND rb.beats >= 3\nORDER BY lr.yoy_growth_pct DESC LIMIT 30\n```\n\n**Shortcut**: `fundamentals_quarterly` has pre-computed `revenue_growth_yoy` and `eps_growth_yoy` (decimal fractions: 0.15 = 15%). Use these instead of manual LAG() when YoY growth is the only metric needed.\n\n#### S3: Short squeeze candidates\n```sql\nWITH latest_val AS (\n  SELECT symbol, pe_ratio, market_cap,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n)\nSELECT g.symbol, g.name, g.gics_sector,\n  ROUND(v.market_cap / 1e6, 0) AS market_cap_mln,\n  ROUND(os.short_percent_float * 100, 2) AS short_pct_float,\n  ROUND(os.percent_institutions, 1) AS inst_pct,\n  ROUND(v.pe_ratio, 2) AS pe\nFROM shibui.general_info g\nINNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1\nINNER JOIN shibui.ownership_stats os ON g.symbol = os.symbol\nWHERE g.type = 'Common Stock'\n  AND os.short_percent_float > 0.15\n  AND v.market_cap BETWEEN 3e8 AND 1e10\nORDER BY os.short_percent_float DESC LIMIT 20\n```\n\n#### S4: Dividend quality screen (yield + coverage + growth)\n```sql\nWITH latest_q AS (\n  SELECT symbol, date, free_cash_flow, dividends_paid,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.fundamentals_quarterly\n  WHERE date >= CURRENT_DATE - INTERVAL '6 months'\n    AND dividends_paid IS NOT NULL AND dividends_paid != 0\n),\nlatest_dd AS (\n  SELECT symbol, dividend_yield,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.fundamentals_derived_daily WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n),\nlatest_val AS (\n  SELECT symbol, pe_ratio, market_cap,\n    ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY date DESC) AS rn\n  FROM shibui.valuation WHERE date >= CURRENT_DATE - INTERVAL '7 days'\n)\nSELECT g.symbol, g.name, g.gics_sector,\n  ROUND(v.market_cap / 1e6, 0) AS market_cap_mln,\n  ROUND(dd.dividend_yield * 100, 2) AS yield_pct,\n  ROUND(f.free_cash_flow / NULLIF(ABS(f.dividends_paid), 0), 2) AS fcf_coverage,\n  ROUND(v.pe_ratio, 2) AS pe\nFROM shibui.general_info g\nINNER JOIN latest_val v ON g.symbol = v.symbol AND v.rn = 1\nINNER JOIN latest_q f ON g.symbol = f.symbol AND f.rn = 1\nINNER JOIN latest_dd dd ON g.symbol = dd.symbol AND dd.rn = 1\nWHERE g.type = 'Common Stock'\n  AND g.country_iso = 'US'\n  AND dd.dividend_yield > 0.02\n  AND f.free_cash_flow / NULLIF(ABS(f.dividends_paid), 0) > 1.2\n  AND v.market_cap > 500e6\nORDER BY dd.dividend_yield DESC LIMIT 20\n```\n"
  4. Changed1 schema field changed
    • addedOutput schema / description
      Added value: +"Generic wrapper for non-object return types."
  5. Added

TDQS

Score is being calculated.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources