feat(frontend): A4 — FundamentalsPanel v1 (quarter tape + earnings + peers)
Reshapes FundamentalsPanel to consume the additive API v1, within the app's
existing dark-glass language.
- types.ts updated to the exact v1 shape (metrics/earnings/valuation/reads +
legacy fields preserved).
- The quarter tape is the single distinctive device: per-metric 4-cell tape
(revenue/EPS growth, operating + FCF margin, share count) with the latest cell
toned by the deterministic read; color is always paired with the read text.
- Restrained peer strips for Net debt/EBITDA, P/E, FCF yield: value + a
polarity-aware percentile bar with a median marker + the read; hidden ("peers
n/a") when industry is null (< 5 peers).
- Earnings: next date/session/countdown + last-N beat/miss arrows (▲/▼/·) with
text aria-labels; explicit "no date" state.
- Explicit n/a, insufficient-peer, and no-earnings states; header shows the
deterministic sentence. Removed the hard-coded "FMP" source label.
Frontend tsc -b passes; backend suite 778 passed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -703,8 +703,74 @@ export interface SentimentResponse {
|
||||
}
|
||||
|
||||
// Fundamentals
|
||||
export interface MetricIndustry {
|
||||
label: string;
|
||||
median: number;
|
||||
favorable_percentile: number; // 0-100, polarity-aware (higher = more favorable)
|
||||
peer_count: number;
|
||||
}
|
||||
|
||||
export interface MetricHistoryPoint {
|
||||
period_end: string | null; // YYYY-MM-DD
|
||||
value: number | null;
|
||||
}
|
||||
|
||||
export type MetricKey =
|
||||
| 'revenue_growth_yoy'
|
||||
| 'eps_growth_yoy'
|
||||
| 'operating_margin'
|
||||
| 'fcf_margin'
|
||||
| 'net_debt'
|
||||
| 'net_debt_to_ebitda'
|
||||
| 'share_count_change_yoy';
|
||||
|
||||
export interface MetricItem {
|
||||
key: MetricKey;
|
||||
value: number | null;
|
||||
history: MetricHistoryPoint[];
|
||||
industry: MetricIndustry | null;
|
||||
period_end: string | null;
|
||||
filed_date: string | null;
|
||||
source: string; // 'sec' | 'legacy_api'
|
||||
}
|
||||
|
||||
export interface EarningsNext {
|
||||
date: string;
|
||||
session: string; // bmo | amc | unknown
|
||||
days_until: number;
|
||||
}
|
||||
|
||||
export interface EarningsRecent {
|
||||
announce_date: string;
|
||||
period_end: string | null;
|
||||
eps_estimate: number | null;
|
||||
eps_actual: number | null;
|
||||
surprise_pct: number | null;
|
||||
}
|
||||
|
||||
export interface EarningsObject {
|
||||
next: EarningsNext | null;
|
||||
recent: EarningsRecent[];
|
||||
}
|
||||
|
||||
export interface Valuation {
|
||||
pe: number | null;
|
||||
fcf_yield: number | null;
|
||||
market_cap_est: number | null;
|
||||
pe_industry: MetricIndustry | null;
|
||||
fcf_yield_industry: MetricIndustry | null;
|
||||
price_date: string | null;
|
||||
}
|
||||
|
||||
export interface FundamentalsReads {
|
||||
header: string | null;
|
||||
// fixed map over every metric key plus 'pe' and 'fcf_yield'; null when unavailable
|
||||
by_key: Record<string, string | null>;
|
||||
}
|
||||
|
||||
export interface FundamentalResponse {
|
||||
symbol: string;
|
||||
// legacy fields (unchanged)
|
||||
pe_ratio: number | null;
|
||||
revenue_growth: number | null;
|
||||
earnings_surprise: number | null;
|
||||
@@ -712,6 +778,11 @@ export interface FundamentalResponse {
|
||||
next_earnings_date: string | null;
|
||||
fetched_at: string | null;
|
||||
unavailable_fields: Record<string, string>;
|
||||
// additive v1 (SEC/Dolt-derived) — present, with null/empty when unavailable
|
||||
earnings: EarningsObject | null;
|
||||
metrics: MetricItem[] | null;
|
||||
valuation: Valuation | null;
|
||||
reads: FundamentalsReads | null;
|
||||
}
|
||||
|
||||
// Indicators
|
||||
|
||||
Reference in New Issue
Block a user