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

@@ -151,11 +151,28 @@ function latestMetrics(snapshots: FilingTaxonomySnapshotRecord[]) {
}
function defaultDisplayModes(surfaceKind: FinancialSurfaceKind): FinancialDisplayMode[] {
if (surfaceKind === 'equity_statement') {
return ['faithful'];
}
return isStatementSurface(surfaceKind)
? ['standardized', 'faithful']
: ['standardized'];
}
function defaultDisplayMode(
surfaceKind: FinancialSurfaceKind,
displayModes = defaultDisplayModes(surfaceKind)
): FinancialDisplayMode {
if (surfaceKind === 'equity_statement') {
return 'faithful';
}
return displayModes.includes('standardized')
? 'standardized'
: displayModes[0] ?? 'standardized';
}
function rekeyRowsByFilingId<T extends {
values: Record<string, number | null>;
resolvedSourceRowKeys?: Record<string, string | null>;
@@ -311,6 +328,10 @@ function emptyNormalizationMetadata(): NormalizationMetadata {
kpiRowCount: 0,
unmappedRowCount: 0,
materialUnmappedRowCount: 0,
residualPrimaryCount: 0,
residualDisclosureCount: 0,
unsupportedConceptCount: 0,
issuerOverlayMatchCount: 0,
warnings: []
};
}
@@ -348,6 +369,22 @@ function buildNormalizationMetadata(
(sum, snapshot) => sum + (snapshot.normalization_summary?.materialUnmappedRowCount ?? 0),
0
),
residualPrimaryCount: snapshots.reduce(
(sum, snapshot) => sum + (snapshot.normalization_summary?.residualPrimaryCount ?? 0),
0
),
residualDisclosureCount: snapshots.reduce(
(sum, snapshot) => sum + (snapshot.normalization_summary?.residualDisclosureCount ?? 0),
0
),
unsupportedConceptCount: snapshots.reduce(
(sum, snapshot) => sum + (snapshot.normalization_summary?.unsupportedConceptCount ?? 0),
0
),
issuerOverlayMatchCount: snapshots.reduce(
(sum, snapshot) => sum + (snapshot.normalization_summary?.issuerOverlayMatchCount ?? 0),
0
),
warnings: [...new Set(snapshots.flatMap((snapshot) => snapshot.normalization_summary?.warnings ?? []))]
.sort((left, right) => left.localeCompare(right))
};
@@ -568,7 +605,7 @@ function buildLtmDetailRows(input: {
detailRows: SurfaceDetailMap;
quarterlyPeriods: FinancialStatementPeriod[];
ltmPeriods: FinancialStatementPeriod[];
statement: Extract<FinancialStatementKind, 'income' | 'balance' | 'cash_flow'>;
statement: Extract<FinancialStatementKind, 'income' | 'balance' | 'cash_flow' | 'equity' | 'disclosure'>;
}) {
const sortedQuarterlyPeriods = [...input.quarterlyPeriods].sort(periodSorter);
@@ -586,7 +623,7 @@ function buildLtmDetailRows(input: {
const slice = sortedQuarterlyPeriods.slice(anchorIndex - 3, anchorIndex + 1);
const sourceValues = slice.map((period) => row.values[period.id] ?? null);
values[ltmPeriod.id] = input.statement === 'balance'
values[ltmPeriod.id] = input.statement === 'balance' || input.statement === 'equity'
? sourceValues[sourceValues.length - 1] ?? null
: sourceValues.some((value) => value === null)
? null
@@ -606,7 +643,7 @@ function buildLtmDetailRows(input: {
}
function buildQuarterlyStatementSurfaceRows(input: {
statement: Extract<FinancialStatementKind, 'income' | 'balance' | 'cash_flow'>;
statement: Extract<FinancialStatementKind, 'income' | 'balance' | 'cash_flow' | 'equity' | 'disclosure'>;
sourcePeriods: FinancialStatementPeriod[];
selectedPeriodIds: Set<string>;
faithfulRows: TaxonomyStatementRow[];
@@ -682,6 +719,8 @@ function buildEmptyResponse(input: {
nextCursor: string | null;
coverageFacts: number;
}) {
const displayModes = defaultDisplayModes(input.surfaceKind);
return {
company: {
ticker: input.ticker,
@@ -690,8 +729,8 @@ function buildEmptyResponse(input: {
},
surfaceKind: input.surfaceKind,
cadence: input.cadence,
displayModes: defaultDisplayModes(input.surfaceKind),
defaultDisplayMode: 'standardized',
displayModes,
defaultDisplayMode: defaultDisplayMode(input.surfaceKind, displayModes),
periods: [],
statementRows: isStatementSurface(input.surfaceKind)
? { faithful: [], standardized: [] }
@@ -728,7 +767,10 @@ function buildEmptyResponse(input: {
}
async function buildStatementSurfaceBundle(input: {
surfaceKind: Extract<FinancialSurfaceKind, 'income_statement' | 'balance_sheet' | 'cash_flow_statement'>;
surfaceKind: Extract<
FinancialSurfaceKind,
'income_statement' | 'balance_sheet' | 'cash_flow_statement' | 'equity_statement' | 'disclosures'
>;
cadence: FinancialCadence;
sourcePeriods: FinancialStatementPeriod[];
targetPeriods: FinancialStatementPeriod[];
@@ -752,7 +794,14 @@ async function buildStatementSurfaceBundle(input: {
}
const statement = surfaceToStatementKind(input.surfaceKind);
if (!statement || (statement !== 'income' && statement !== 'balance' && statement !== 'cash_flow')) {
if (
!statement ||
(statement !== 'income' &&
statement !== 'balance' &&
statement !== 'cash_flow' &&
statement !== 'equity' &&
statement !== 'disclosure')
) {
return {
rows: [],
detailRows: {},
@@ -810,6 +859,16 @@ async function buildStatementSurfaceBundle(input: {
return payload;
}
async function buildDisclosureSurfaceBundle(input: Omit<
Parameters<typeof buildStatementSurfaceBundle>[0],
'surfaceKind'
>) {
return await buildStatementSurfaceBundle({
...input,
surfaceKind: 'disclosures'
});
}
async function buildRatioSurfaceBundle(input: {
ticker: string;
cadence: FinancialCadence;
@@ -1039,18 +1098,36 @@ export async function getCompanyFinancials(input: GetCompanyFinancialsInput): Pr
const factsForStatement = allFacts.facts.filter((fact) => fact.statement === statement);
const factsForStandardization = allFacts.facts;
const standardizedPayload = await buildStatementSurfaceBundle({
surfaceKind: input.surfaceKind as Extract<FinancialSurfaceKind, 'income_statement' | 'balance_sheet' | 'cash_flow_statement'>,
cadence: input.cadence,
sourcePeriods: selection.periods,
targetPeriods: periods,
selectedPeriodIds: selection.selectedPeriodIds,
faithfulRows: baseFaithfulRows,
facts: factsForStandardization,
snapshots: selection.snapshots
});
const standardizedPayload = input.surfaceKind === 'disclosures'
? await buildDisclosureSurfaceBundle({
cadence: input.cadence,
sourcePeriods: selection.periods,
targetPeriods: periods,
selectedPeriodIds: selection.selectedPeriodIds,
faithfulRows: baseFaithfulRows,
facts: factsForStandardization,
snapshots: selection.snapshots
})
: await buildStatementSurfaceBundle({
surfaceKind: input.surfaceKind as Extract<
FinancialSurfaceKind,
'income_statement' | 'balance_sheet' | 'cash_flow_statement' | 'equity_statement'
>,
cadence: input.cadence,
sourcePeriods: selection.periods,
targetPeriods: periods,
selectedPeriodIds: selection.selectedPeriodIds,
faithfulRows: baseFaithfulRows,
facts: factsForStandardization,
snapshots: selection.snapshots
});
const standardizedRows = standardizedPayload.rows;
const statementDisplayModes: FinancialDisplayMode[] = input.surfaceKind === 'equity_statement'
? (standardizedRows.length > 0
? ['faithful', 'standardized']
: ['faithful'])
: defaultDisplayModes(input.surfaceKind);
const rawFacts = input.includeFacts
? await listTaxonomyFactsByTicker({
@@ -1075,8 +1152,8 @@ export async function getCompanyFinancials(input: GetCompanyFinancialsInput): Pr
},
surfaceKind: input.surfaceKind,
cadence: input.cadence,
displayModes: defaultDisplayModes(input.surfaceKind),
defaultDisplayMode: 'standardized',
displayModes: statementDisplayModes,
defaultDisplayMode: defaultDisplayMode(input.surfaceKind, statementDisplayModes),
periods,
statementRows: {
faithful: faithfulRows,
@@ -1187,7 +1264,7 @@ export async function getCompanyFinancials(input: GetCompanyFinancialsInput): Pr
surfaceKind: input.surfaceKind,
cadence: input.cadence,
displayModes: defaultDisplayModes(input.surfaceKind),
defaultDisplayMode: 'standardized',
defaultDisplayMode: defaultDisplayMode(input.surfaceKind),
periods: basePeriods,
statementRows: null,
statementDetails: null,
@@ -1253,7 +1330,7 @@ export async function getCompanyFinancials(input: GetCompanyFinancialsInput): Pr
surfaceKind: input.surfaceKind,
cadence: input.cadence,
displayModes: defaultDisplayModes(input.surfaceKind),
defaultDisplayMode: 'standardized',
defaultDisplayMode: defaultDisplayMode(input.surfaceKind),
periods: basePeriods,
statementRows: null,
statementDetails: null,
@@ -1287,11 +1364,11 @@ export async function getCompanyFinancials(input: GetCompanyFinancialsInput): Pr
};
}
export async function getCompanyFinancialTaxonomy(input: GetCompanyFinancialsInput) {
async function getCompanyFinancialTaxonomy(input: GetCompanyFinancialsInput) {
return await getCompanyFinancials(input);
}
export const __financialTaxonomyInternals = {
const __financialTaxonomyInternals = {
buildRows,
buildDimensionBreakdown,
buildNormalizationMetadata,