Screener MCP: Screens US SEC filers by fundamentals, holders and insider data, then checks criteria in words against filings via the Jaspers screener server.
| 1 | import type { ReactElement } from 'react' |
| 2 | import { filterText, type Field, type Screen } from './screener' |
| 3 | |
| 4 | // The funnel: how many companies there are, what each filter passes on its own, and how many pass |
| 5 | // all of them. The server counts every filter alone, so the line shows which one does the cutting. |
| 6 | |
| 7 | interface Props { |
| 8 | screen: Screen | null |
| 9 | fields: Map<string, Field> |
| 10 | } |
| 11 | |
| 12 | export function Funnel({ screen, fields }: Props): ReactElement | null { |
| 13 | if (!screen) return null |
| 14 | const n = (value: number): string => value.toLocaleString('en-US') |
| 15 | return ( |
| 16 | <div className="sc-bar sc-funnel"> |
| 17 | <span> |
| 18 | <b>{n(screen.universe)}</b> {screen.tickers.length > 0 ? 'on the list' : 'companies'} |
| 19 | </span> |
| 20 | {screen.applied.map((filter, index) => ( |
| 21 | <span key={index} className="sc-step"> |
| 22 | <span className="sc-arrow">→</span> {filterText(filter, fields.get(filter.field))} <b>{n(filter.matches)}</b> |
| 23 | </span> |
| 24 | ))} |
| 25 | <span className="sc-step"> |
| 26 | <span className="sc-arrow">→</span> <b>{n(screen.count)}</b> pass{screen.applied.length > 1 ? ' all' : ''} |
| 27 | {screen.countBefore !== null && screen.countBefore !== screen.count && <> (was {n(screen.countBefore)})</>} |
| 28 | </span> |
| 29 | {screen.unknownTickers.length > 0 && <span className="sc-error">No company for {screen.unknownTickers.join(', ')}</span>} |
| 30 | </div> |
| 31 | ) |
| 32 | } |