- Add bundled financial modeling pipeline (ratios, KPI dimensions/notes, trend series, standardization) - Introduce company financial bundles storage (Drizzle migration + repo wiring) - Refactor financials page/API/query flow to use surfaceKind + cadence and new response shapes
121 lines
5.0 KiB
TypeScript
121 lines
5.0 KiB
TypeScript
import type {
|
|
FinancialCategory,
|
|
FinancialUnit
|
|
} from '@/lib/types';
|
|
|
|
export type IndustryTemplate =
|
|
| 'internet_platforms'
|
|
| 'software_saas'
|
|
| 'semiconductors_industrial_auto';
|
|
|
|
export type KpiDefinition = {
|
|
key: string;
|
|
label: string;
|
|
category: FinancialCategory;
|
|
unit: FinancialUnit;
|
|
preferredConceptNames?: string[];
|
|
preferredAxisIncludes?: string[];
|
|
preferredMemberIncludes?: string[];
|
|
noteLabelIncludes?: string[];
|
|
};
|
|
|
|
type RegistryBundle = {
|
|
tickerTemplates: Record<string, IndustryTemplate>;
|
|
globalDefinitions: KpiDefinition[];
|
|
industryDefinitions: Record<IndustryTemplate, KpiDefinition[]>;
|
|
tickerDefinitions: Record<string, KpiDefinition[]>;
|
|
};
|
|
|
|
const KPI_REGISTRY: RegistryBundle = {
|
|
tickerTemplates: {
|
|
GOOG: 'internet_platforms',
|
|
META: 'internet_platforms',
|
|
NFLX: 'internet_platforms',
|
|
MSFT: 'software_saas',
|
|
CRM: 'software_saas',
|
|
NOW: 'software_saas',
|
|
NVDA: 'semiconductors_industrial_auto',
|
|
TSLA: 'semiconductors_industrial_auto',
|
|
CAT: 'semiconductors_industrial_auto'
|
|
},
|
|
globalDefinitions: [
|
|
{
|
|
key: 'segment_revenue',
|
|
label: 'Segment Revenue',
|
|
category: 'segment_revenue',
|
|
unit: 'currency',
|
|
preferredConceptNames: ['Revenues', 'RevenueFromContractWithCustomerExcludingAssessedTax', 'SalesRevenueNet']
|
|
},
|
|
{
|
|
key: 'segment_profit',
|
|
label: 'Segment Profit',
|
|
category: 'segment_profit',
|
|
unit: 'currency',
|
|
preferredConceptNames: ['OperatingIncomeLoss', 'SegmentProfitLoss']
|
|
}
|
|
],
|
|
industryDefinitions: {
|
|
internet_platforms: [
|
|
{ key: 'tac', label: 'TAC', category: 'operating_kpi', unit: 'currency', preferredConceptNames: ['TrafficAcquisitionCosts'], noteLabelIncludes: ['traffic acquisition costs', 'tac'] },
|
|
{ key: 'paid_clicks', label: 'Paid Clicks', category: 'operating_kpi', unit: 'count', noteLabelIncludes: ['paid clicks'] },
|
|
{ key: 'cpc', label: 'CPC', category: 'operating_kpi', unit: 'currency', noteLabelIncludes: ['cost per click', 'cpc'] },
|
|
{ key: 'dau', label: 'DAU', category: 'user_metric', unit: 'count', preferredConceptNames: ['DailyActiveUsers'], noteLabelIncludes: ['daily active users', 'dau'] },
|
|
{ key: 'mau', label: 'MAU', category: 'user_metric', unit: 'count', preferredConceptNames: ['MonthlyActiveUsers'], noteLabelIncludes: ['monthly active users', 'mau'] },
|
|
{ key: 'arpu', label: 'ARPU', category: 'operating_kpi', unit: 'currency', noteLabelIncludes: ['average revenue per user', 'arpu'] },
|
|
{ key: 'arpp', label: 'ARPP', category: 'operating_kpi', unit: 'currency', noteLabelIncludes: ['average revenue per paying user', 'arpp'] },
|
|
{ key: 'backlog', label: 'Backlog', category: 'backlog', unit: 'currency', noteLabelIncludes: ['backlog'] }
|
|
],
|
|
software_saas: [
|
|
{ key: 'arr', label: 'ARR', category: 'operating_kpi', unit: 'currency', noteLabelIncludes: ['annual recurring revenue', 'arr'] },
|
|
{ key: 'rpo', label: 'RPO', category: 'backlog', unit: 'currency', preferredConceptNames: ['RemainingPerformanceObligation'], noteLabelIncludes: ['remaining performance obligations', 'rpo'] },
|
|
{ key: 'remaining_performance_obligations', label: 'Remaining Performance Obligations', category: 'backlog', unit: 'currency', preferredConceptNames: ['RemainingPerformanceObligation'], noteLabelIncludes: ['remaining performance obligations'] },
|
|
{ key: 'large_customer_count', label: 'Large Customer Count', category: 'operating_kpi', unit: 'count', noteLabelIncludes: ['customers contributing', 'large customers'] }
|
|
],
|
|
semiconductors_industrial_auto: [
|
|
{ key: 'deliveries', label: 'Deliveries', category: 'operating_kpi', unit: 'count', noteLabelIncludes: ['deliveries'] },
|
|
{ key: 'utilization', label: 'Utilization', category: 'operating_kpi', unit: 'percent', noteLabelIncludes: ['utilization'] },
|
|
{ key: 'backlog', label: 'Backlog', category: 'backlog', unit: 'currency', noteLabelIncludes: ['backlog'] }
|
|
]
|
|
},
|
|
tickerDefinitions: {}
|
|
};
|
|
|
|
export function getTickerIndustryTemplate(ticker: string): IndustryTemplate | null {
|
|
return KPI_REGISTRY.tickerTemplates[ticker.trim().toUpperCase()] ?? null;
|
|
}
|
|
|
|
export function resolveKpiDefinitions(ticker: string) {
|
|
const normalizedTicker = ticker.trim().toUpperCase();
|
|
const template = getTickerIndustryTemplate(normalizedTicker);
|
|
|
|
const definitionsByKey = new Map<string, KpiDefinition>();
|
|
for (const definition of KPI_REGISTRY.globalDefinitions) {
|
|
definitionsByKey.set(definition.key, definition);
|
|
}
|
|
if (template) {
|
|
for (const definition of KPI_REGISTRY.industryDefinitions[template]) {
|
|
definitionsByKey.set(definition.key, definition);
|
|
}
|
|
}
|
|
for (const definition of KPI_REGISTRY.tickerDefinitions[normalizedTicker] ?? []) {
|
|
definitionsByKey.set(definition.key, definition);
|
|
}
|
|
|
|
return {
|
|
template,
|
|
definitions: [...definitionsByKey.values()]
|
|
};
|
|
}
|
|
|
|
export const KPI_CATEGORY_ORDER = [
|
|
'segment_revenue',
|
|
'segment_profit',
|
|
'segment_margin',
|
|
'operating_kpi',
|
|
'geographic_mix',
|
|
'capital_returns',
|
|
'backlog',
|
|
'user_metric',
|
|
'other'
|
|
] as const;
|