Expand financials surfaces with ratios, KPIs, and cadence support
- 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
This commit is contained in:
120
lib/server/financials/kpi-registry.ts
Normal file
120
lib/server/financials/kpi-registry.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user