diff --git a/src/advisor.mjs b/src/advisor.mjs index a5c1583..7e09f9b 100644 --- a/src/advisor.mjs +++ b/src/advisor.mjs @@ -279,8 +279,18 @@ function compact(v) { const n = Number(v); if (!Number.isFinite(n)) return null; const units = [[1e12, "T"], [1e9, "B"], [1e6, "M"], [1e3, "K"]]; - for (const [size, suffix] of units) { - if (Math.abs(n) >= size) return `${(n / size).toFixed(2).replace(/\.?0+$/, "")}${suffix}`; + for (let i = 0; i < units.length; i++) { + const [size, suffix] = units[i]; + if (Math.abs(n) < size) continue; + // Rounding to 2 decimals can push a value just under the next boundary up to + // a full thousand of this unit (999,999,999 → "1000M"); carry it to the next + // unit up (→ "1B") so the number never reads as an un-carried thousand. + const scaled = (n / size).toFixed(2); + if (Math.abs(Number(scaled)) >= 1000 && i > 0) { + const [upSize, upSuffix] = units[i - 1]; + return `${(n / upSize).toFixed(2).replace(/\.?0+$/, "")}${upSuffix}`; + } + return `${scaled.replace(/\.?0+$/, "")}${suffix}`; } return String(n); } diff --git a/test/advisor.test.mjs b/test/advisor.test.mjs index a710817..413f206 100644 --- a/test/advisor.test.mjs +++ b/test/advisor.test.mjs @@ -185,6 +185,19 @@ test("a report missing its optional sections still renders", () => { assert.match(renderAdvisor("report", bare, { columns: 88 }), /AAA/); }); +test("fundamentals just under a magnitude boundary carry to the next unit", () => { + // 999,999,999 rounds to 1000.00 of a million; it must read "1B", not "1000M". + const near = { + ticker: "AAA", lastPrice: null, disclaimer: "d", + facts: { source: "sec", marketCap: 999999999, revenue: 999999, freeCashFlow: 999999999999 }, + }; + const out = renderAdvisor("report", near, { columns: 88 }); + assert.match(out, /cap 1B\b/); + assert.match(out, /rev 1M\b/); + assert.match(out, /fcf 1T\b/); + assert.doesNotMatch(out, /1000M|1000K|1000B/); +}); + test("empty result sets say so instead of rendering an empty table", () => { assert.match(renderAdvisor("signals", { ticker: "AAA", signals: [] }), /no signals indexed/); assert.match(renderAdvisor("search", { query: "zzz", results: [] }), /nothing indexed matches/);