Automate issuer overlay creation from ticker searches

This commit is contained in:
2026-03-19 20:44:58 -04:00
parent 17de3dd72d
commit 391d6d34ce
79 changed files with 4746 additions and 695 deletions

View File

@@ -9,7 +9,7 @@ import {
} from '@/lib/server/repos/company-financial-bundles';
import type { FilingTaxonomySnapshotRecord } from '@/lib/server/repos/filing-taxonomy';
export function computeSourceSignature(snapshots: FilingTaxonomySnapshotRecord[]) {
function computeSourceSignature(snapshots: FilingTaxonomySnapshotRecord[]) {
return snapshots
.map((snapshot) => `${snapshot.id}:${snapshot.updated_at}`)
.sort((left, right) => left.localeCompare(right))

View File

@@ -37,7 +37,7 @@ export function periodSorter(left: FinancialStatementPeriod, right: FinancialSta
return left.id.localeCompare(right.id);
}
export function isInstantPeriod(period: FinancialStatementPeriod) {
function isInstantPeriod(period: FinancialStatementPeriod) {
return period.periodStart === null;
}
@@ -218,7 +218,7 @@ function selectPrimaryPeriodFromSnapshot(
candidates.map((period) => [period.id, coverageScore(coverageRows, period.id)])
);
if (statement === 'balance') {
if (statement === 'balance' || statement === 'equity') {
return [...candidates].sort((left, right) => compareBalancePeriods(left, right, rowCoverage))[0] ?? null;
}
@@ -238,6 +238,10 @@ export function surfaceToStatementKind(surfaceKind: FinancialSurfaceKind): Finan
return 'balance';
case 'cash_flow_statement':
return 'cash_flow';
case 'equity_statement':
return 'equity';
case 'disclosures':
return 'disclosure';
default:
return null;
}
@@ -417,7 +421,7 @@ export function buildLtmFaithfulRows(
const sourceValues = slice.map((period) => sourceRow.values[period.id] ?? null);
const sourceUnits = slice.map((period) => sourceRow.units[period.id] ?? null).filter((unit): unit is string => unit !== null);
row.values[ltmPeriod.id] = statement === 'balance'
row.values[ltmPeriod.id] = statement === 'balance' || statement === 'equity'
? sourceValues[sourceValues.length - 1] ?? null
: aggregateValues(sourceValues);
row.units[ltmPeriod.id] = sourceUnits[sourceUnits.length - 1] ?? null;

View File

@@ -3,7 +3,7 @@ import type {
FinancialUnit
} from '@/lib/types';
export type IndustryTemplate =
type IndustryTemplate =
| 'internet_platforms'
| 'software_saas'
| 'semiconductors_industrial_auto';
@@ -80,7 +80,7 @@ const KPI_REGISTRY: RegistryBundle = {
tickerDefinitions: {}
};
export function getTickerIndustryTemplate(ticker: string): IndustryTemplate | null {
function getTickerIndustryTemplate(ticker: string): IndustryTemplate | null {
return KPI_REGISTRY.tickerTemplates[ticker.trim().toUpperCase()] ?? null;
}

View File

@@ -101,7 +101,7 @@ export function buildDimensionBreakdown(
return map.size > 0 ? Object.fromEntries(map.entries()) : null;
}
export function cloneStandardizedRows(rows: StandardizedFinancialRow[]) {
function cloneStandardizedRows(rows: StandardizedFinancialRow[]) {
return rows.map((row) => ({
...row,
values: { ...row.values },
@@ -116,7 +116,7 @@ export function buildLtmStandardizedRows(
quarterlyRows: StandardizedFinancialRow[],
quarterlyPeriods: FinancialStatementPeriod[],
ltmPeriods: FinancialStatementPeriod[],
statement: Extract<FinancialStatementKind, 'income' | 'balance' | 'cash_flow'>
statement: Extract<FinancialStatementKind, 'income' | 'balance' | 'cash_flow' | 'equity' | 'disclosure'>
) {
const sortedQuarterlyPeriods = [...quarterlyPeriods].sort((left, right) => {
return Date.parse(left.periodEnd ?? left.filingDate) - Date.parse(right.periodEnd ?? right.filingDate);
@@ -141,7 +141,7 @@ export function buildLtmStandardizedRows(
const slice = sortedQuarterlyPeriods.slice(anchorIndex - 3, anchorIndex + 1);
const sourceValues = slice.map((period) => source.values[period.id] ?? null);
row.values[ltmPeriod.id] = statement === 'balance'
row.values[ltmPeriod.id] = statement === 'balance' || statement === 'equity'
? sourceValues[sourceValues.length - 1] ?? null
: sumValues(sourceValues);
row.resolvedSourceRowKeys[ltmPeriod.id] = source.formulaKey ? null : source.resolvedSourceRowKeys[slice[slice.length - 1]?.id ?? ''] ?? null;

View File

@@ -64,6 +64,12 @@ export function buildTrendSeries(input: {
return (input.statementRows ?? [])
.filter((row) => row.key === 'operating_cash_flow' || row.key === 'free_cash_flow' || row.key === 'capital_expenditures')
.map(toTrendSeriesRow);
case 'equity_statement':
return (input.statementRows ?? [])
.filter((row) => row.key === 'total_equity' || row.key === 'retained_earnings' || row.key === 'common_stock_and_apic')
.map(toTrendSeriesRow);
case 'disclosures':
return [];
case 'ratios':
return (input.ratioRows ?? [])
.filter((row) => row.category === 'margins')