diff --git a/packages/shared/src/screening/strategies.test.ts b/packages/shared/src/screening/strategies.test.ts index 6f0c0c1f..07acfabb 100644 --- a/packages/shared/src/screening/strategies.test.ts +++ b/packages/shared/src/screening/strategies.test.ts @@ -256,6 +256,28 @@ describe('fundamental rules', () => { expect(metrics.revenueGrowth).toBe(12) }) + it('never reads the oldest period when the newest one carries no number', () => { + // Longbridge-style multi-period account, newest first. The newest period + // ships only a yoy (value unknown to the provider), so the latest usable + // number is Q4 2026 = 12 — the oldest 30 must never be reported as latest. + const report = financialReport({}) + report.statements.IS!.indicators[0].accounts = [ + { + field: 'ROE', + name: 'ROE', + values: [ + { fpEnd: NOW_SECONDS - 86_400, period: 'Q1 2027', year: 2027, value: Number.NaN, yoy: '20' }, + { fpEnd: NOW_SECONDS - 90 * 86_400, period: 'Q4 2026', year: 2026, value: 12 }, + { fpEnd: NOW_SECONDS - 180 * 86_400, period: 'Q3 2026', year: 2026, value: 30 }, + ], + }, + ] + const metrics = extractFinancialMetrics(makeContext({ financials: report }).data) + expect(metrics.roe).toBe(12) + // 12% is below the bar; the stale 30% must not produce a candidate. + expect(getScreeningStrategy('high-roe')!.compute(makeContext({ financials: report }))).toBeNull() + }) + it('high-dividend requires yield above the bar and cites payment history', () => { const rule = getScreeningStrategy('high-dividend')! const result = rule.compute( diff --git a/packages/shared/src/screening/strategies.ts b/packages/shared/src/screening/strategies.ts index 189030d5..dd4578a3 100644 --- a/packages/shared/src/screening/strategies.ts +++ b/packages/shared/src/screening/strategies.ts @@ -184,7 +184,10 @@ function statementAccounts(report: FinancialReport | undefined, kind: 'IS' | 'BS function latestAccountValue(accounts: ReportAccount[], field: string): number | undefined { const account = accounts.find((entry) => entry.field === field) if (!account) return undefined - return account.values[0]?.value ?? account.values[account.values.length - 1]?.value + // Values are newest-first: the latest *reported* number is the first one with + // a usable value. Never fall back to the array tail — that is the oldest + // period and would report stale financials as the latest figures. + return account.values.find((entry) => entry.value !== undefined)?.value } function latestAccountYoy(accounts: ReportAccount[], field: string): number | undefined {