Files
investor-flow/app/src/components/FundHoldingsStrip.tsx
T
Investor Flow Build ac94acf9e3
CI / Test & Type-Check (push) Canceled after 0s
feat: dealer flow, mirror portfolio (M21), options convexity, FINRA short interest, alert producers, vendor gate
Snapshot of in-progress module work across multiple slices:

- Dealer Flow: dealerExposureEngine, dealerMapService, dealerMapExplain,
  dealerMapIntegrity, dealerMapReplay, dealerStudyEngine, hanStyleLevels
- Mirror Portfolio (M21): fundRepository, captureIngest, mirrorAlertProducers,
  fund holdings strip, live book, position capture ingest
- Options: BSM, NormalizedOptionSurface types, OptionsChainRouter,
  ConvexityGate, option legs panel
- Alert producers: vixLevel, rotation, thesis, unlock, portfolioRisk,
  mirror (fund_capture, fund_13f, mirror_diff)
- FINRA short interest adapter + queue integration
- SEC company tickers adapter + ingest (symbol search index seed)
- Vendor gate (rate-limit-first data plane, ADR-0009)
- CUSIP registry, reverse 13F refresh, stock float service
- LRU cache, portfolio backtest engine
- Frontend: dealer-flow, funds, journal, lab, monitor, plan, portfolio,
  reports, screener, strategies, theses, guided-start, exits, more pages
- Volume profile, workspace profile, visibility-aware poll
- ADRs 0010 (mirror math not advice), 0011 (symbol search index)
- VENDOR_INTEGRATIONS.md, END_USER_TEST.md
- .gitignore: exclude DBs, .DS_Store, local config, agent scratch
2026-08-10 13:36:26 -04:00

76 lines
3.0 KiB
TypeScript

"use client";
// Two-tier "who holds this" strip (M22 Symbol Search Index, ADR-0011).
//
// Tier 1 — tracked funds (M21 `fund_position_records`): each fund that holds the
// symbol, with its weight in the fund's disclosed book and the record source/as-of.
// Tier 2 — the count of distinct 13F filers that reported the symbol (M4 cache).
//
// Facts only (ADR-0010 / ADR-0007): never "you should follow this fund".
import { useEffect, useState } from "react";
import { api, type FundHoldersResult } from "@/lib/trpc";
import { Badge } from "@/components/ui";
export function FundHoldingsStrip({ symbol }: { symbol: string | null }) {
const [data, setData] = useState<FundHoldersResult | null>(null);
const [showInstitutions, setShowInstitutions] = useState(false);
const [error, setError] = useState(false);
useEffect(() => {
let cancelled = false;
setData(null);
setShowInstitutions(false);
setError(false);
if (!symbol) return;
api.symbols
.holders({ symbol })
.then((d) => { if (!cancelled) setData(d); })
.catch(() => { if (!cancelled) setError(true); });
return () => { cancelled = true; };
}, [symbol]);
if (error) return null; // silent — strip is auxiliary
if (!data) return null;
if (data.tracked.length === 0 && data.institutionCount === 0) return null;
return (
<section className="rounded-xl border border-line-strong bg-surface-raised p-4">
<h3 className="text-sm font-semibold text-fg mb-2">Who holds {symbol}</h3>
{data.tracked.length > 0 && (
<ul className="space-y-1.5">
{data.tracked.map((t) => (
<li key={t.fundId} className="flex items-center gap-2 text-sm">
<span className="font-medium text-fg">{t.fundName}</span>
{t.weightPct != null && (
<span className="text-xs text-fg-muted tnum">{t.weightPct.toFixed(1)}% of book</span>
)}
<Badge tone={t.source === "13f" ? "neutral" : "warn"}>
{t.source === "13f" ? "SEC 13F" : t.source}
</Badge>
<span className="text-[10px] text-fg-faint">as of {t.asOf}</span>
</li>
))}
</ul>
)}
{data.institutionCount > 0 && (
<div className="mt-2 border-t border-line pt-2">
<button
onClick={() => setShowInstitutions((v) => !v)}
className="text-xs text-fg-muted hover:text-fg transition-colors flex items-center gap-1"
>
<span>{data.institutionCount} other institution{data.institutionCount === 1 ? "" : "s"} report holding this (13F)</span>
<span className="text-fg-faint">{showInstitutions ? "▾" : "▸"}</span>
</button>
{showInstitutions && (
<p className="mt-1 text-[10px] text-fg-faint">
From the shared 13F filings cache (M4 institutional module). This is every
distinct filer that reported the symbol, passive and active alike.
</p>
)}
</div>
)}
</section>
);
}