Closes #012 - Implement Sloan Ratio Accrual Radar
This commit is contained in:
18
DEV_LOG.md
18
DEV_LOG.md
@@ -89,5 +89,23 @@ This document tracks all modifications, npm packages, active compilation states,
|
|||||||
* **Active Bugs**: None.
|
* **Active Bugs**: None.
|
||||||
* **Type Checker Status**: Verified clean compilation (`npx tsc --noEmit` returns exit code 0).
|
* **Type Checker Status**: Verified clean compilation (`npx tsc --noEmit` returns exit code 0).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [2026-06-13] - Sloan Ratio Accrual Radar (Phase 4.9 / Ebene 4) (#ISSUE-012)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
* **Sloan Accrual Radar Solver**: Implemented the Richard Sloan Accrual formula `Accruals = NetIncome - (CFO + CFI)` and `SloanRatio = Accruals / TotalAssets * 100` on the server-side.
|
||||||
|
* **Enhanced Mock & API Interception**: Updated `MOCK_TECH_AI_DATA` and the free Yahoo Finance `/api/finance` & `/api/scanner` endpoints to parse quarterly net income, CFO, CFI, and Total Assets, and return Sloan metrics. Supported `DEV_MODE` offline bypass returning simulated Sloan ratios with `isShieldActive: true`.
|
||||||
|
* **Sloan UI Integration (Module 6)**: Mounted the "Sloan Earnings Quality" metric card at the top of [AiSpecialSilo.tsx](file:///c:/Users/jannr/.gemini/antigravity/scratch/investment-sandbox/components/modules/tech/AiSpecialSilo.tsx) and added a dedicated detailed ledger panel for all active tickers.
|
||||||
|
* **Sloan UI Integration (Module 2)**: Updated the Search Mask in [ScannerDemo.tsx](file:///c:/Users/jannr/.gemini/antigravity/scratch/investment-sandbox/components/modules/scanner/ScannerDemo.tsx) to output the current Sloan Ratio and a green/rose indicator badge alongside the GJR-GARCH forecast.
|
||||||
|
* **Math Handbook details**: Expanded [TechMathModal.tsx](file:///c:/Users/jannr/.gemini/antigravity/scratch/investment-sandbox/components/modules/tech/TechMathModal.tsx) to explain the Sloan Accounting Anomaly with block LaTeX equations.
|
||||||
|
|
||||||
|
### Modified
|
||||||
|
* **`QUANT_ROADMAP.md`**: Updated Section 4.I detailing the Sloan Ratio mathematical formulations and regime classifications.
|
||||||
|
|
||||||
|
### Active Bugs / Compile Status
|
||||||
|
* **Active Bugs**: None.
|
||||||
|
* **Type Checker Status**: Verified clean compilation (`npx tsc --noEmit` returns exit code 0).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -113,12 +113,12 @@ When evaluating an individual equity ticker, the terminal computes three quantit
|
|||||||
Measures the proportion of earnings backed by non-cash accruals. A high ratio indicates that earnings are driven by accounting accruals rather than real operating cash flows.
|
Measures the proportion of earnings backed by non-cash accruals. A high ratio indicates that earnings are driven by accounting accruals rather than real operating cash flows.
|
||||||
|
|
||||||
#### Mathematical Formulation:
|
#### Mathematical Formulation:
|
||||||
$$\text{Sloan Ratio} = \frac{\text{Net Income} - \text{Operating Cash Flow} - \text{Investing Cash Flow}}{\text{Total Assets}}$$
|
$$\text{Accruals} = \text{Net Income} - (\text{Cash Flow From Operations} + \text{Cash Flow From Investing})$$
|
||||||
|
$$\text{Sloan Ratio} = \frac{\text{Accruals}}{\text{Total Assets}} \times 100$$
|
||||||
|
|
||||||
#### Alert Thresholds:
|
#### Regime Classifications & Thresholds:
|
||||||
* **Stable Accruals**: $\le 5\%$ (Green)
|
* **Safe Regime (High Earnings Quality)**: $-10\% \le \text{Sloan Ratio} \le 10\%$
|
||||||
* **Elevated Accruals**: $5\% < \text{Sloan Ratio} \le 10\%$ (Amber)
|
* **Anomaly Regime (Aggressive Accrual Expansion)**: $\text{Sloan Ratio} > 10\%$ or $\text{Sloan Ratio} < -10\%$ (Signals earnings manipulation or aggressive capitalization risk)
|
||||||
* **Toxic Accruals (Manipulative Risk)**: $> 10\%$ (Flashing Neon Rose-Red)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,61 @@ function calculateRSI14(prices: number[]): number {
|
|||||||
return 100 - 100 / (1 + rs);
|
return 100 - 100 / (1 + rs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSimulatedSloan(ticker: string): { sloanRatio: number; sloanRegime: 'SAFE' | 'ANOMALY' } {
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < ticker.length; i++) {
|
||||||
|
hash = ticker.charCodeAt(i) + ((hash << 5) - hash);
|
||||||
|
}
|
||||||
|
const sloanRatio = parseFloat(((hash % 170) / 10).toFixed(2)); // range: 0.0 to 17.0
|
||||||
|
const sloanRegime = (sloanRatio > 10 || sloanRatio < -10) ? ('ANOMALY' as const) : ('SAFE' as const);
|
||||||
|
return { sloanRatio, sloanRegime };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchFmpSloanRatio(ticker: string, apiKey: string): Promise<{ sloanRatio: number; sloanRegime: 'SAFE' | 'ANOMALY' }> {
|
||||||
|
if (ticker.includes('-USD') || ticker.includes('BTC') || ticker.includes('ETH')) {
|
||||||
|
return getSimulatedSloan(ticker);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const incUrl = `https://financialmodelingprep.com/api/v3/income-statement/${ticker}?period=quarter&limit=1&apikey=${apiKey}`;
|
||||||
|
const balUrl = `https://financialmodelingprep.com/api/v3/balance-sheet-statement/${ticker}?period=quarter&limit=1&apikey=${apiKey}`;
|
||||||
|
const cfUrl = `https://financialmodelingprep.com/api/v3/cash-flow-statement/${ticker}?period=quarter&limit=1&apikey=${apiKey}`;
|
||||||
|
|
||||||
|
const [incRes, balRes, cfRes] = await Promise.all([
|
||||||
|
fetch(incUrl, { signal: AbortSignal.timeout(3000) }),
|
||||||
|
fetch(balUrl, { signal: AbortSignal.timeout(3000) }),
|
||||||
|
fetch(cfUrl, { signal: AbortSignal.timeout(3000) })
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (incRes.ok && balRes.ok && cfRes.ok) {
|
||||||
|
const incData = await incRes.json();
|
||||||
|
const balData = await balRes.json();
|
||||||
|
const cfData = await cfRes.json();
|
||||||
|
|
||||||
|
const inc = incData?.[0] || {};
|
||||||
|
const bal = balData?.[0] || {};
|
||||||
|
const cf = cfData?.[0] || {};
|
||||||
|
|
||||||
|
const netIncome = inc.netIncome || 0;
|
||||||
|
const cfo = cf.netCashProvidedByOperatingActivities || cf.operatingCashFlow || 0;
|
||||||
|
const cfi = cf.netCashUsedForInvestingActivites || cf.netCashUsedForInvestingActivities || cf.investingCashFlow || 0;
|
||||||
|
const totalAssets = bal.totalAssets || 0;
|
||||||
|
|
||||||
|
const accruals = netIncome - (cfo + cfi);
|
||||||
|
const sloanRatio = totalAssets > 0 ? (accruals / totalAssets) * 100 : 0;
|
||||||
|
const sloanRegime = (sloanRatio > 10 || sloanRatio < -10) ? ('ANOMALY' as const) : ('SAFE' as const);
|
||||||
|
|
||||||
|
return {
|
||||||
|
sloanRatio: Number(sloanRatio.toFixed(2)),
|
||||||
|
sloanRegime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`Error fetching FMP Sloan data for ${ticker}:`, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSimulatedSloan(ticker);
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch fundamental data from FMP with safe fallback
|
// Fetch fundamental data from FMP with safe fallback
|
||||||
async function fetchFMPFundamentalData(ticker: string, apiKey: string) {
|
async function fetchFMPFundamentalData(ticker: string, apiKey: string) {
|
||||||
if (ticker.includes('-USD') || ticker.includes('BTC') || ticker.includes('ETH')) {
|
if (ticker.includes('-USD') || ticker.includes('BTC') || ticker.includes('ETH')) {
|
||||||
@@ -180,7 +235,43 @@ async function fetchFMPFundamentalData(ticker: string, apiKey: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMockFundamentals(ticker: string): {
|
||||||
|
marketCap: number;
|
||||||
|
trailingPE: number;
|
||||||
|
forwardPE: number;
|
||||||
|
peg: number;
|
||||||
|
priceToBook: number;
|
||||||
|
dividendYield: number;
|
||||||
|
} {
|
||||||
|
if (MOCK_FUNDAMENTALS[ticker]) {
|
||||||
|
return MOCK_FUNDAMENTALS[ticker];
|
||||||
|
}
|
||||||
|
if (ticker.endsWith('-USD')) {
|
||||||
|
return { marketCap: 5e9, trailingPE: 0, forwardPE: 0, peg: 0, priceToBook: 0, dividendYield: 0 };
|
||||||
|
}
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < ticker.length; i++) {
|
||||||
|
hash = ticker.charCodeAt(i) + ((hash << 5) - hash);
|
||||||
|
}
|
||||||
|
const seedCap = Math.abs(hash % 18) + 1;
|
||||||
|
const marketCap = seedCap * 100 * 1000000;
|
||||||
|
const trailingPE = 10 + Math.abs(hash % 30);
|
||||||
|
const forwardPE = trailingPE * 0.82;
|
||||||
|
const peg = 0.5 + (Math.abs(hash % 15) / 10);
|
||||||
|
const priceToBook = 1.0 + (Math.abs(hash % 40) / 10);
|
||||||
|
const dividendYield = (Math.abs(hash % 6) / 2) / 100;
|
||||||
|
return {
|
||||||
|
marketCap,
|
||||||
|
trailingPE,
|
||||||
|
forwardPE,
|
||||||
|
peg,
|
||||||
|
priceToBook,
|
||||||
|
dividendYield
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
|
const isDevMode = process.env.DEV_MODE === 'true';
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const tickerQuery = searchParams.get('ticker');
|
const tickerQuery = searchParams.get('ticker');
|
||||||
const tickersQuery = searchParams.get('tickers');
|
const tickersQuery = searchParams.get('tickers');
|
||||||
@@ -330,20 +421,35 @@ export async function GET(request: Request) {
|
|||||||
sortedResults.map(async (res) => {
|
sortedResults.map(async (res) => {
|
||||||
// Pull live data if in top 15, otherwise load direct mock fallback
|
// Pull live data if in top 15, otherwise load direct mock fallback
|
||||||
if (top15Tickers.has(res.ticker)) {
|
if (top15Tickers.has(res.ticker)) {
|
||||||
const fundamentals = await fetchFMPFundamentalData(res.ticker, fmpApiKey);
|
const fundamentals = isDevMode
|
||||||
return { ...res, ...fundamentals };
|
? { ...getMockFundamentals(res.ticker), dividendYield: getMockFundamentals(res.ticker).dividendYield * 100 }
|
||||||
|
: await fetchFMPFundamentalData(res.ticker, fmpApiKey);
|
||||||
|
|
||||||
|
const sloan = isDevMode
|
||||||
|
? getSimulatedSloan(res.ticker)
|
||||||
|
: await fetchFmpSloanRatio(res.ticker, fmpApiKey);
|
||||||
|
|
||||||
|
return { ...res, ...fundamentals, ...sloan };
|
||||||
} else {
|
} else {
|
||||||
const mock = MOCK_FUNDAMENTALS[res.ticker] || { marketCap: 0, trailingPE: 0, forwardPE: 0, peg: 0, priceToBook: 0, dividendYield: 0 };
|
const mock = MOCK_FUNDAMENTALS[res.ticker] || { marketCap: 0, trailingPE: 0, forwardPE: 0, peg: 0, priceToBook: 0, dividendYield: 0 };
|
||||||
|
const sloan = getSimulatedSloan(res.ticker);
|
||||||
return {
|
return {
|
||||||
...res,
|
...res,
|
||||||
...mock,
|
...mock,
|
||||||
dividendYield: mock.dividendYield * 100
|
dividendYield: mock.dividendYield * 100,
|
||||||
|
...sloan
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const response = NextResponse.json({ results });
|
const response = NextResponse.json({
|
||||||
|
results,
|
||||||
|
isShieldActive: isDevMode
|
||||||
|
});
|
||||||
response.headers.set('Cache-Control', 'no-store, max-age=0, must-revalidate');
|
response.headers.set('Cache-Control', 'no-store, max-age=0, must-revalidate');
|
||||||
|
if (isDevMode) {
|
||||||
|
response.headers.set('X-Shield-Active', 'true');
|
||||||
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,6 +300,61 @@ function generateSimulatedChart(ticker: string, mode: string): TickerDetails {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSimulatedSloan(ticker: string): { sloanRatio: number; sloanRegime: 'SAFE' | 'ANOMALY' } {
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < ticker.length; i++) {
|
||||||
|
hash = ticker.charCodeAt(i) + ((hash << 5) - hash);
|
||||||
|
}
|
||||||
|
const sloanRatio = parseFloat(((hash % 170) / 10).toFixed(2)); // range: 0.0 to 17.0
|
||||||
|
const sloanRegime = (sloanRatio > 10 || sloanRatio < -10) ? ('ANOMALY' as const) : ('SAFE' as const);
|
||||||
|
return { sloanRatio, sloanRegime };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchFmpSloanRatio(ticker: string, apiKey: string): Promise<{ sloanRatio: number; sloanRegime: 'SAFE' | 'ANOMALY' }> {
|
||||||
|
if (ticker.includes('-USD') || ticker.includes('BTC') || ticker.includes('ETH')) {
|
||||||
|
return getSimulatedSloan(ticker);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const incUrl = `https://financialmodelingprep.com/api/v3/income-statement/${ticker}?period=quarter&limit=1&apikey=${apiKey}`;
|
||||||
|
const balUrl = `https://financialmodelingprep.com/api/v3/balance-sheet-statement/${ticker}?period=quarter&limit=1&apikey=${apiKey}`;
|
||||||
|
const cfUrl = `https://financialmodelingprep.com/api/v3/cash-flow-statement/${ticker}?period=quarter&limit=1&apikey=${apiKey}`;
|
||||||
|
|
||||||
|
const [incRes, balRes, cfRes] = await Promise.all([
|
||||||
|
fetch(incUrl, { signal: AbortSignal.timeout(2000) }),
|
||||||
|
fetch(balUrl, { signal: AbortSignal.timeout(2000) }),
|
||||||
|
fetch(cfUrl, { signal: AbortSignal.timeout(2000) })
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (incRes.ok && balRes.ok && cfRes.ok) {
|
||||||
|
const incData = await incRes.json();
|
||||||
|
const balData = await balRes.json();
|
||||||
|
const cfData = await cfRes.json();
|
||||||
|
|
||||||
|
const inc = incData?.[0] || {};
|
||||||
|
const bal = balData?.[0] || {};
|
||||||
|
const cf = cfData?.[0] || {};
|
||||||
|
|
||||||
|
const netIncome = inc.netIncome || 0;
|
||||||
|
const cfo = cf.netCashProvidedByOperatingActivities || cf.operatingCashFlow || 0;
|
||||||
|
const cfi = cf.netCashUsedForInvestingActivites || cf.netCashUsedForInvestingActivities || cf.investingCashFlow || 0;
|
||||||
|
const totalAssets = bal.totalAssets || 0;
|
||||||
|
|
||||||
|
const accruals = netIncome - (cfo + cfi);
|
||||||
|
const sloanRatio = totalAssets > 0 ? (accruals / totalAssets) * 100 : 0;
|
||||||
|
const sloanRegime = (sloanRatio > 10 || sloanRatio < -10) ? ('ANOMALY' as const) : ('SAFE' as const);
|
||||||
|
|
||||||
|
return {
|
||||||
|
sloanRatio: Number(sloanRatio.toFixed(2)),
|
||||||
|
sloanRegime
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`Error fetching FMP Sloan data for ${ticker}:`, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSimulatedSloan(ticker);
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchFMPFundamentalData(ticker: string, apiKey: string) {
|
async function fetchFMPFundamentalData(ticker: string, apiKey: string) {
|
||||||
try {
|
try {
|
||||||
const profileUrl = `https://financialmodelingprep.com/stable/profile?symbol=${ticker}&apikey=${apiKey}`;
|
const profileUrl = `https://financialmodelingprep.com/stable/profile?symbol=${ticker}&apikey=${apiKey}`;
|
||||||
@@ -426,10 +481,15 @@ export async function GET(request: Request) {
|
|||||||
? getMockFundamentals(item.ticker)
|
? getMockFundamentals(item.ticker)
|
||||||
: await fetchFMPFundamentalData(item.ticker, fmpApiKey);
|
: await fetchFMPFundamentalData(item.ticker, fmpApiKey);
|
||||||
|
|
||||||
|
const sloan = useMock
|
||||||
|
? getSimulatedSloan(item.ticker)
|
||||||
|
: await fetchFmpSloanRatio(item.ticker, fmpApiKey);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
...fund,
|
...fund,
|
||||||
dividendYield: fund.dividendYield ? Number((fund.dividendYield * (useMock ? 100 : 1)).toFixed(2)) : 0
|
dividendYield: fund.dividendYield ? Number((fund.dividendYield * (useMock ? 100 : 1)).toFixed(2)) : 0,
|
||||||
|
...sloan
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ interface QuarterData {
|
|||||||
totalDebt: number;
|
totalDebt: number;
|
||||||
equity: number;
|
equity: number;
|
||||||
depreciation: number;
|
depreciation: number;
|
||||||
|
netIncome: number;
|
||||||
|
cfo: number;
|
||||||
|
cfi: number;
|
||||||
|
totalAssets: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CompanyData {
|
interface CompanyData {
|
||||||
@@ -31,66 +35,66 @@ const MOCK_TECH_AI_DATA: CompanyData[] = [
|
|||||||
{
|
{
|
||||||
ticker: 'NVDA',
|
ticker: 'NVDA',
|
||||||
quarters: [
|
quarters: [
|
||||||
{ quarter: 'Q3-24', date: '2024-10-27', revenue: 35082, segmentRevenue: 29010, capex: 291, inventory: 5122, cogs: 8720, purchaseObligations: 0, totalDebt: 8460, equity: 48930, depreciation: 342 },
|
{ quarter: 'Q3-24', date: '2024-10-27', revenue: 35082, segmentRevenue: 29010, capex: 291, inventory: 5122, cogs: 8720, purchaseObligations: 0, totalDebt: 8460, equity: 48930, depreciation: 342, netIncome: 19309, cfo: 16800, cfi: -500, totalAssets: 85000 },
|
||||||
{ quarter: 'Q4-24', date: '2025-01-26', revenue: 37500, segmentRevenue: 31200, capex: 320, inventory: 5400, cogs: 9100, purchaseObligations: 0, totalDebt: 8460, equity: 52000, depreciation: 350 },
|
{ quarter: 'Q4-24', date: '2025-01-26', revenue: 37500, segmentRevenue: 31200, capex: 320, inventory: 5400, cogs: 9100, purchaseObligations: 0, totalDebt: 8460, equity: 52000, depreciation: 350, netIncome: 20500, cfo: 18000, cfi: -550, totalAssets: 90000 },
|
||||||
{ quarter: 'Q1-25', date: '2025-04-27', revenue: 39200, segmentRevenue: 33100, capex: 350, inventory: 5850, cogs: 9500, purchaseObligations: 0, totalDebt: 8500, equity: 55200, depreciation: 360 },
|
{ quarter: 'Q1-25', date: '2025-04-27', revenue: 39200, segmentRevenue: 33100, capex: 350, inventory: 5850, cogs: 9500, purchaseObligations: 0, totalDebt: 8500, equity: 55200, depreciation: 360, netIncome: 21500, cfo: 19000, cfi: -600, totalAssets: 95000 },
|
||||||
{ quarter: 'Q2-25', date: '2025-07-27', revenue: 41500, segmentRevenue: 35400, capex: 380, inventory: 6300, cogs: 10100, purchaseObligations: 0, totalDebt: 8520, equity: 58800, depreciation: 375 },
|
{ quarter: 'Q2-25', date: '2025-07-27', revenue: 41500, segmentRevenue: 35400, capex: 380, inventory: 6300, cogs: 10100, purchaseObligations: 0, totalDebt: 8520, equity: 58800, depreciation: 375, netIncome: 22800, cfo: 20200, cfi: -650, totalAssets: 100000 },
|
||||||
{ quarter: 'Q3-25', date: '2025-10-26', revenue: 44000, segmentRevenue: 37800, capex: 410, inventory: 6800, cogs: 10700, purchaseObligations: 0, totalDebt: 8550, equity: 62500, depreciation: 390 },
|
{ quarter: 'Q3-25', date: '2025-10-26', revenue: 44000, segmentRevenue: 37800, capex: 410, inventory: 6800, cogs: 10700, purchaseObligations: 0, totalDebt: 8550, equity: 62500, depreciation: 390, netIncome: 24200, cfo: 21500, cfi: -700, totalAssets: 105000 },
|
||||||
{ quarter: 'Q4-25', date: '2026-01-25', revenue: 46200, segmentRevenue: 39800, capex: 440, inventory: 7200, cogs: 11200, purchaseObligations: 0, totalDebt: 8600, equity: 66300, depreciation: 410 },
|
{ quarter: 'Q4-25', date: '2026-01-25', revenue: 46200, segmentRevenue: 39800, capex: 440, inventory: 7200, cogs: 11200, purchaseObligations: 0, totalDebt: 8600, equity: 66300, depreciation: 410, netIncome: 25400, cfo: 22600, cfi: -750, totalAssets: 110000 },
|
||||||
{ quarter: 'Q1-26', date: '2026-04-26', revenue: 47800, segmentRevenue: 41200, capex: 470, inventory: 7800, cogs: 11600, purchaseObligations: 0, totalDebt: 8650, equity: 70200, depreciation: 430 },
|
{ quarter: 'Q1-26', date: '2026-04-26', revenue: 47800, segmentRevenue: 41200, capex: 470, inventory: 7800, cogs: 11600, purchaseObligations: 0, totalDebt: 8650, equity: 70200, depreciation: 430, netIncome: 26300, cfo: 23500, cfi: -800, totalAssets: 115000 },
|
||||||
{ quarter: 'Q2-26', date: '2026-07-26', revenue: 49500, segmentRevenue: 42500, capex: 500, inventory: 8500, cogs: 12200, purchaseObligations: 0, totalDebt: 8700, equity: 74500, depreciation: 450 }
|
{ quarter: 'Q2-26', date: '2026-07-26', revenue: 49500, segmentRevenue: 42500, capex: 500, inventory: 8500, cogs: 12200, purchaseObligations: 0, totalDebt: 8700, equity: 74500, depreciation: 450, netIncome: 27200, cfo: 24300, cfi: -850, totalAssets: 120000 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ticker: 'MSFT',
|
ticker: 'MSFT',
|
||||||
quarters: [
|
quarters: [
|
||||||
{ quarter: 'Q3-24', date: '2024-09-30', revenue: 65585, segmentRevenue: 24092, capex: 14920, inventory: 1120, cogs: 21720, purchaseObligations: 23100, totalDebt: 77800, equity: 228900, depreciation: 4610 },
|
{ quarter: 'Q3-24', date: '2024-09-30', revenue: 65585, segmentRevenue: 24092, capex: 14920, inventory: 1120, cogs: 21720, purchaseObligations: 23100, totalDebt: 77800, equity: 228900, depreciation: 4610, netIncome: 24700, cfo: 29000, cfi: -16000, totalAssets: 485000 },
|
||||||
{ quarter: 'Q4-24', date: '2024-12-31', revenue: 68200, segmentRevenue: 25900, capex: 16100, inventory: 1150, cogs: 22400, purchaseObligations: 25500, totalDebt: 78200, equity: 234000, depreciation: 4800 },
|
{ quarter: 'Q4-24', date: '2024-12-31', revenue: 68200, segmentRevenue: 25900, capex: 16100, inventory: 1150, cogs: 22400, purchaseObligations: 25500, totalDebt: 78200, equity: 234000, depreciation: 4800, netIncome: 25800, cfo: 30000, cfi: -17500, totalAssets: 498000 },
|
||||||
{ quarter: 'Q1-25', date: '2025-03-31', revenue: 70800, segmentRevenue: 27400, capex: 17500, inventory: 1190, cogs: 23150, purchaseObligations: 28200, totalDebt: 79000, equity: 240500, depreciation: 5050 },
|
{ quarter: 'Q1-25', date: '2025-03-31', revenue: 70800, segmentRevenue: 27400, capex: 17500, inventory: 1190, cogs: 23150, purchaseObligations: 28200, totalDebt: 79000, equity: 240500, depreciation: 5050, netIncome: 26800, cfo: 31500, cfi: -19000, totalAssets: 512000 },
|
||||||
{ quarter: 'Q2-25', date: '2025-06-30', revenue: 72900, segmentRevenue: 28800, capex: 19000, inventory: 1210, cogs: 23800, purchaseObligations: 31000, totalDebt: 79200, equity: 247000, depreciation: 5300 },
|
{ quarter: 'Q2-25', date: '2025-06-30', revenue: 72900, segmentRevenue: 28800, capex: 19000, inventory: 1210, cogs: 23800, purchaseObligations: 31000, totalDebt: 79200, equity: 247000, depreciation: 5300, netIncome: 27600, cfo: 32500, cfi: -20500, totalAssets: 525000 },
|
||||||
{ quarter: 'Q3-25', date: '2025-09-30', revenue: 75500, segmentRevenue: 30100, capex: 20500, inventory: 1240, cogs: 24500, purchaseObligations: 34100, totalDebt: 80500, equity: 254200, depreciation: 5600 },
|
{ quarter: 'Q3-25', date: '2025-09-30', revenue: 75500, segmentRevenue: 30100, capex: 20500, inventory: 1240, cogs: 24500, purchaseObligations: 34100, totalDebt: 80500, equity: 254200, depreciation: 5600, netIncome: 28700, cfo: 34000, cfi: -22000, totalAssets: 540000 },
|
||||||
{ quarter: 'Q4-25', date: '2025-12-31', revenue: 77800, segmentRevenue: 31400, capex: 22100, inventory: 1260, cogs: 25100, purchaseObligations: 37200, totalDebt: 81000, equity: 261800, depreciation: 5900 },
|
{ quarter: 'Q4-25', date: '2025-12-31', revenue: 77800, segmentRevenue: 31400, capex: 22100, inventory: 1260, cogs: 25100, purchaseObligations: 37200, totalDebt: 81000, equity: 261800, depreciation: 5900, netIncome: 29500, cfo: 35000, cfi: -23800, totalAssets: 554000 },
|
||||||
{ quarter: 'Q1-26', date: '2026-03-31', revenue: 80200, segmentRevenue: 32500, capex: 23800, inventory: 1290, cogs: 25800, purchaseObligations: 39500, totalDebt: 82000, equity: 269500, depreciation: 6200 },
|
{ quarter: 'Q1-26', date: '2026-03-31', revenue: 80200, segmentRevenue: 32500, capex: 23800, inventory: 1290, cogs: 25800, purchaseObligations: 39500, totalDebt: 82000, equity: 269500, depreciation: 6200, netIncome: 30400, cfo: 36200, cfi: -25500, totalAssets: 568000 },
|
||||||
{ quarter: 'Q2-26', date: '2026-06-30', revenue: 82500, segmentRevenue: 33600, capex: 25500, inventory: 1320, cogs: 26400, purchaseObligations: 38200, totalDebt: 83500, equity: 278000, depreciation: 6500 }
|
{ quarter: 'Q2-26', date: '2026-06-30', revenue: 82500, segmentRevenue: 33600, capex: 25500, inventory: 1320, cogs: 26400, purchaseObligations: 38200, totalDebt: 83500, equity: 278000, depreciation: 6500, netIncome: 31300, cfo: 37200, cfi: -27000, totalAssets: 585000 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ticker: 'GOOGL',
|
ticker: 'GOOGL',
|
||||||
quarters: [
|
quarters: [
|
||||||
{ quarter: 'Q3-24', date: '2024-09-30', revenue: 88268, segmentRevenue: 11353, capex: 12980, inventory: 980, cogs: 32610, purchaseObligations: 14800, totalDebt: 28100, equity: 285400, depreciation: 3280 },
|
{ quarter: 'Q3-24', date: '2024-09-30', revenue: 88268, segmentRevenue: 11353, capex: 12980, inventory: 980, cogs: 32610, purchaseObligations: 14800, totalDebt: 28100, equity: 285400, depreciation: 3280, netIncome: 26300, cfo: 28800, cfi: -14000, totalAssets: 410000 },
|
||||||
{ quarter: 'Q4-24', date: '2024-12-31', revenue: 91400, segmentRevenue: 12100, capex: 13800, inventory: 1010, cogs: 33450, purchaseObligations: 16200, totalDebt: 28200, equity: 292000, depreciation: 3400 },
|
{ quarter: 'Q4-24', date: '2024-12-31', revenue: 91400, segmentRevenue: 12100, capex: 13800, inventory: 1010, cogs: 33450, purchaseObligations: 16200, totalDebt: 28200, equity: 292000, depreciation: 3400, netIncome: 27200, cfo: 29800, cfi: -15000, totalAssets: 422000 },
|
||||||
{ quarter: 'Q1-25', date: '2025-03-31', revenue: 94250, segmentRevenue: 12850, capex: 14900, inventory: 1040, cogs: 34300, purchaseObligations: 17900, totalDebt: 28500, equity: 299000, depreciation: 3550 },
|
{ quarter: 'Q1-25', date: '2025-03-31', revenue: 94250, segmentRevenue: 12850, capex: 14900, inventory: 1040, cogs: 34300, purchaseObligations: 17900, totalDebt: 28500, equity: 299000, depreciation: 3550, netIncome: 28100, cfo: 30800, cfi: -16000, totalAssets: 435000 },
|
||||||
{ quarter: 'Q2-25', date: '2025-06-30', revenue: 96800, segmentRevenue: 13500, capex: 15900, inventory: 1060, cogs: 35100, purchaseObligations: 19500, totalDebt: 28600, equity: 306500, depreciation: 3700 },
|
{ quarter: 'Q2-25', date: '2025-06-30', revenue: 96800, segmentRevenue: 13500, capex: 15900, inventory: 1060, cogs: 35100, purchaseObligations: 19500, totalDebt: 28600, equity: 306500, depreciation: 3700, netIncome: 28900, cfo: 31800, cfi: -17000, totalAssets: 448000 },
|
||||||
{ quarter: 'Q3-25', date: '2025-09-30', revenue: 99600, segmentRevenue: 14150, capex: 17000, inventory: 1080, cogs: 36000, purchaseObligations: 21400, totalDebt: 28800, equity: 314800, depreciation: 3900 },
|
{ quarter: 'Q3-25', date: '2025-09-30', revenue: 99600, segmentRevenue: 14150, capex: 17000, inventory: 1080, cogs: 36000, purchaseObligations: 21400, totalDebt: 28800, equity: 314800, depreciation: 3900, netIncome: 29700, cfo: 32800, cfi: -18200, totalAssets: 462000 },
|
||||||
{ quarter: 'Q4-25', date: '2025-12-31', revenue: 102400, segmentRevenue: 14800, capex: 18200, inventory: 1110, cogs: 36800, purchaseObligations: 23500, totalDebt: 29000, equity: 323500, depreciation: 4100 },
|
{ quarter: 'Q4-25', date: '2025-12-31', revenue: 102400, segmentRevenue: 14800, capex: 18200, inventory: 1110, cogs: 36800, purchaseObligations: 23500, totalDebt: 29000, equity: 323500, depreciation: 4100, netIncome: 30600, cfo: 33800, cfi: -19500, totalAssets: 475000 },
|
||||||
{ quarter: 'Q1-26', date: '2026-03-31', revenue: 105200, segmentRevenue: 15350, capex: 19500, inventory: 1130, cogs: 37700, purchaseObligations: 25200, totalDebt: 29200, equity: 332000, depreciation: 4300 },
|
{ quarter: 'Q1-26', date: '2026-03-31', revenue: 105200, segmentRevenue: 15350, capex: 19500, inventory: 1130, cogs: 37700, purchaseObligations: 25200, totalDebt: 29200, equity: 332000, depreciation: 4300, netIncome: 31400, cfo: 34800, cfi: -20800, totalAssets: 488000 },
|
||||||
{ quarter: 'Q2-26', date: '2026-06-30', revenue: 108100, segmentRevenue: 15900, capex: 21000, inventory: 1160, cogs: 38600, purchaseObligations: 24100, totalDebt: 29500, equity: 341000, depreciation: 4500 }
|
{ quarter: 'Q2-26', date: '2026-06-30', revenue: 108100, segmentRevenue: 15900, capex: 21000, inventory: 1160, cogs: 38600, purchaseObligations: 24100, totalDebt: 29500, equity: 341000, depreciation: 4500, netIncome: 32300, cfo: 35800, cfi: -22200, totalAssets: 502000 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ticker: 'META',
|
ticker: 'META',
|
||||||
quarters: [
|
quarters: [
|
||||||
{ quarter: 'Q3-24', date: '2024-09-30', revenue: 40589, segmentRevenue: 39860, capex: 9210, inventory: 290, cogs: 7310, purchaseObligations: 9800, totalDebt: 17800, equity: 142100, depreciation: 2790 },
|
{ quarter: 'Q3-24', date: '2024-09-30', revenue: 40589, segmentRevenue: 39860, capex: 9210, inventory: 290, cogs: 7310, purchaseObligations: 9800, totalDebt: 17800, equity: 142100, depreciation: 2790, netIncome: 15680, cfo: 17200, cfi: -9800, totalAssets: 265000 },
|
||||||
{ quarter: 'Q4-24', date: '2024-12-31', revenue: 42100, segmentRevenue: 41350, capex: 10100, inventory: 300, cogs: 7550, purchaseObligations: 11200, totalDebt: 17900, equity: 146000, depreciation: 2900 },
|
{ quarter: 'Q4-24', date: '2024-12-31', revenue: 42100, segmentRevenue: 41350, capex: 10100, inventory: 300, cogs: 7550, purchaseObligations: 11200, totalDebt: 17900, equity: 146000, depreciation: 2900, netIncome: 16200, cfo: 17850, cfi: -10700, totalAssets: 272000 },
|
||||||
{ quarter: 'Q1-25', date: '2025-03-31', revenue: 43500, segmentRevenue: 42700, capex: 11100, inventory: 310, cogs: 7800, purchaseObligations: 12800, totalDebt: 18100, equity: 150200, depreciation: 3050 },
|
{ quarter: 'Q1-25', date: '2025-03-31', revenue: 43500, segmentRevenue: 42700, capex: 11100, inventory: 310, cogs: 7800, purchaseObligations: 12800, totalDebt: 18100, equity: 150200, depreciation: 3050, netIncome: 16800, cfo: 18500, cfi: -11800, totalAssets: 280000 },
|
||||||
{ quarter: 'Q2-25', date: '2025-06-30', revenue: 44900, segmentRevenue: 44050, capex: 12200, inventory: 315, cogs: 8050, purchaseObligations: 14500, totalDebt: 18200, equity: 154800, depreciation: 3200 },
|
{ quarter: 'Q2-25', date: '2025-06-30', revenue: 44900, segmentRevenue: 44050, capex: 12200, inventory: 315, cogs: 8050, purchaseObligations: 1450, totalDebt: 18200, equity: 154800, depreciation: 3200, netIncome: 17400, cfo: 19100, cfi: -13000, totalAssets: 288000 },
|
||||||
{ quarter: 'Q3-25', date: '2025-09-30', revenue: 46600, segmentRevenue: 45700, capex: 13400, inventory: 320, cogs: 8350, purchaseObligations: 16400, totalDebt: 18400, equity: 159500, depreciation: 3380 },
|
{ quarter: 'Q3-25', date: '2025-09-30', revenue: 46600, segmentRevenue: 45700, capex: 13400, inventory: 320, cogs: 8350, purchaseObligations: 16400, totalDebt: 18400, equity: 159500, depreciation: 3380, netIncome: 18050, cfo: 19800, cfi: -14200, totalAssets: 298000 },
|
||||||
{ quarter: 'Q4-25', date: '2025-12-31', revenue: 48200, segmentRevenue: 47250, capex: 14700, inventory: 330, cogs: 8600, purchaseObligations: 18500, totalDebt: 18500, equity: 164200, depreciation: 3550 },
|
{ quarter: 'Q4-25', date: '2025-12-31', revenue: 48200, segmentRevenue: 47250, capex: 14700, inventory: 330, cogs: 8600, purchaseObligations: 18500, totalDebt: 18500, equity: 164200, depreciation: 3550, netIncome: 18700, cfo: 20500, cfi: -15500, totalAssets: 308000 },
|
||||||
{ quarter: 'Q1-26', date: '2026-03-31', revenue: 49700, segmentRevenue: 48700, capex: 16100, inventory: 340, cogs: 8850, purchaseObligations: 20200, totalDebt: 18700, equity: 169500, depreciation: 3750 },
|
{ quarter: 'Q1-26', date: '2026-03-31', revenue: 49700, segmentRevenue: 48700, capex: 16100, inventory: 340, cogs: 8850, purchaseObligations: 20200, totalDebt: 18700, equity: 169500, depreciation: 3750, netIncome: 19300, cfo: 21200, cfi: -16800, totalAssets: 318000 },
|
||||||
{ quarter: 'Q2-26', date: '2026-06-30', revenue: 51200, segmentRevenue: 50150, capex: 17500, inventory: 350, cogs: 9100, purchaseObligations: 19100, totalDebt: 18900, equity: 175000, depreciation: 3950 }
|
{ quarter: 'Q2-26', date: '2026-06-30', revenue: 51200, segmentRevenue: 50150, capex: 17500, inventory: 350, cogs: 9100, purchaseObligations: 19100, totalDebt: 18900, equity: 175000, depreciation: 3950, netIncome: 19900, cfo: 21850, cfi: -18200, totalAssets: 330000 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ticker: 'AMD',
|
ticker: 'AMD',
|
||||||
quarters: [
|
quarters: [
|
||||||
{ quarter: 'Q3-24', date: '2024-09-28', revenue: 6819, segmentRevenue: 3549, capex: 148, inventory: 4490, cogs: 3410, purchaseObligations: 1450, totalDebt: 2480, equity: 55810, depreciation: 142 },
|
{ quarter: 'Q3-24', date: '2024-09-28', revenue: 6819, segmentRevenue: 3549, capex: 148, inventory: 4490, cogs: 3410, purchaseObligations: 1450, totalDebt: 2480, equity: 55810, depreciation: 142, netIncome: 771, cfo: 1200, cfi: -300, totalAssets: 68000 },
|
||||||
{ quarter: 'Q4-24', date: '2024-12-28', revenue: 7100, segmentRevenue: 3750, capex: 160, inventory: 4620, cogs: 3520, purchaseObligations: 1600, totalDebt: 2480, equity: 56200, depreciation: 148 },
|
{ quarter: 'Q4-24', date: '2024-12-28', revenue: 7100, segmentRevenue: 3750, capex: 160, inventory: 4620, cogs: 3520, purchaseObligations: 1600, totalDebt: 2480, equity: 56200, depreciation: 148, netIncome: 820, cfo: 1250, cfi: -320, totalAssets: 69200 },
|
||||||
{ quarter: 'Q1-25', date: '2025-03-29', revenue: 7380, segmentRevenue: 3980, capex: 175, inventory: 4750, cogs: 3640, purchaseObligations: 1820, totalDebt: 2500, equity: 56650, depreciation: 155 },
|
{ quarter: 'Q1-25', date: '2025-03-29', revenue: 7380, segmentRevenue: 3980, capex: 175, inventory: 4750, cogs: 3640, purchaseObligations: 1820, totalDebt: 2500, equity: 56650, depreciation: 155, netIncome: 860, cfo: 1300, cfi: -350, totalAssets: 70500 },
|
||||||
{ quarter: 'Q2-25', date: '2025-06-28', revenue: 7650, segmentRevenue: 4200, capex: 190, inventory: 4830, cogs: 3750, purchaseObligations: 2050, totalDebt: 2510, equity: 57100, depreciation: 162 },
|
{ quarter: 'Q2-25', date: '2025-06-28', revenue: 7650, segmentRevenue: 4200, capex: 190, inventory: 4830, cogs: 3750, purchaseObligations: 2050, totalDebt: 2510, equity: 57100, depreciation: 162, netIncome: 900, cfo: 1360, cfi: -380, totalAssets: 71800 },
|
||||||
{ quarter: 'Q3-25', date: '2025-09-27', revenue: 7950, segmentRevenue: 4450, capex: 210, inventory: 4920, cogs: 3880, purchaseObligations: 2300, totalDebt: 2520, equity: 57600, depreciation: 170 },
|
{ quarter: 'Q3-25', date: '2025-09-27', revenue: 7950, segmentRevenue: 4450, capex: 210, inventory: 4920, cogs: 3880, purchaseObligations: 2300, totalDebt: 2520, equity: 57600, depreciation: 170, netIncome: 950, cfo: 1420, cfi: -410, totalAssets: 73200 },
|
||||||
{ quarter: 'Q4-25', date: '2025-12-27', revenue: 8250, segmentRevenue: 4700, capex: 230, inventory: 5010, cogs: 4010, purchaseObligations: 2550, totalDebt: 2540, equity: 58150, depreciation: 178 },
|
{ quarter: 'Q4-25', date: '2025-12-27', revenue: 8250, segmentRevenue: 4700, capex: 230, inventory: 5010, cogs: 4010, purchaseObligations: 2550, totalDebt: 2540, equity: 58150, depreciation: 178, netIncome: 1000, cfo: 1480, cfi: -440, totalAssets: 74600 },
|
||||||
{ quarter: 'Q1-26', date: '2026-03-28', revenue: 8520, segmentRevenue: 4920, capex: 250, inventory: 5120, cogs: 4120, purchaseObligations: 2780, totalDebt: 2550, equity: 58700, depreciation: 185 },
|
{ quarter: 'Q1-26', date: '2026-03-28', revenue: 8520, segmentRevenue: 4920, capex: 250, inventory: 5120, cogs: 4120, purchaseObligations: 2780, totalDebt: 2550, equity: 58700, depreciation: 185, netIncome: 1050, cfo: 1540, cfi: -475, totalAssets: 76000 },
|
||||||
{ quarter: 'Q2-26', date: '2026-06-27', revenue: 8800, segmentRevenue: 5150, capex: 270, inventory: 5250, cogs: 4250, purchaseObligations: 2620, totalDebt: 2570, equity: 59300, depreciation: 192 }
|
{ quarter: 'Q2-26', date: '2026-06-27', revenue: 8800, segmentRevenue: 5150, capex: 270, inventory: 5250, cogs: 4250, purchaseObligations: 2620, totalDebt: 2570, equity: 59300, depreciation: 192, netIncome: 3500, cfo: 500, cfi: -100, totalAssets: 25000 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -203,6 +207,11 @@ export async function GET() {
|
|||||||
const eqVal = bal.totalStockholdersEquity || bal.equity || comp.quarters[i]?.equity || 1000;
|
const eqVal = bal.totalStockholdersEquity || bal.equity || comp.quarters[i]?.equity || 1000;
|
||||||
const depVal = inc.depreciationAndAmortization || inc.depreciation || comp.quarters[i]?.depreciation || 100;
|
const depVal = inc.depreciationAndAmortization || inc.depreciation || comp.quarters[i]?.depreciation || 100;
|
||||||
|
|
||||||
|
const netIncVal = inc.netIncome || comp.quarters[i]?.netIncome || 0;
|
||||||
|
const cfoVal = cf.netCashProvidedByOperatingActivities || cf.operatingCashFlow || comp.quarters[i]?.cfo || 0;
|
||||||
|
const cfiVal = cf.netCashUsedForInvestingActivites || cf.netCashUsedForInvestingActivities || cf.investingCashFlow || comp.quarters[i]?.cfi || 0;
|
||||||
|
const assetsVal = bal.totalAssets || comp.quarters[i]?.totalAssets || 1000;
|
||||||
|
|
||||||
// Segment Revenue Parsing
|
// Segment Revenue Parsing
|
||||||
let segRev = 0;
|
let segRev = 0;
|
||||||
if (comp.ticker === 'MSFT') {
|
if (comp.ticker === 'MSFT') {
|
||||||
@@ -244,7 +253,11 @@ export async function GET() {
|
|||||||
purchaseObligations: poVal,
|
purchaseObligations: poVal,
|
||||||
totalDebt: Math.round(totDebt / 1000000) || comp.quarters[i]?.totalDebt || 0,
|
totalDebt: Math.round(totDebt / 1000000) || comp.quarters[i]?.totalDebt || 0,
|
||||||
equity: Math.round(eqVal / 1000000) || comp.quarters[i]?.equity || 0,
|
equity: Math.round(eqVal / 1000000) || comp.quarters[i]?.equity || 0,
|
||||||
depreciation: Math.round(depVal / 1000000) || comp.quarters[i]?.depreciation || 0
|
depreciation: Math.round(depVal / 1000000) || comp.quarters[i]?.depreciation || 0,
|
||||||
|
netIncome: Math.round(netIncVal / 1000000) || comp.quarters[i]?.netIncome || 0,
|
||||||
|
cfo: Math.round(cfoVal / 1000000) || comp.quarters[i]?.cfo || 0,
|
||||||
|
cfi: Math.round(cfiVal / 1000000) || comp.quarters[i]?.cfi || 0,
|
||||||
|
totalAssets: Math.round(assetsVal / 1000000) || comp.quarters[i]?.totalAssets || 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,6 +435,34 @@ export async function GET() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 4. Sloan Accrual Radar
|
||||||
|
const sloanRadar: Record<string, {
|
||||||
|
currentSloan: number;
|
||||||
|
currentRegime: 'SAFE' | 'ANOMALY';
|
||||||
|
data: { quarter: string; accruals: number; sloanRatio: number; regime: 'SAFE' | 'ANOMALY' }[];
|
||||||
|
}> = {};
|
||||||
|
|
||||||
|
companyData.forEach(comp => {
|
||||||
|
const qData = comp.quarters.map(q => {
|
||||||
|
const accruals = q.netIncome - (q.cfo + q.cfi);
|
||||||
|
const sloanRatio = q.totalAssets > 0 ? (accruals / q.totalAssets) * 100 : 0;
|
||||||
|
const regime = (sloanRatio > 10 || sloanRatio < -10) ? ('ANOMALY' as const) : ('SAFE' as const);
|
||||||
|
return {
|
||||||
|
quarter: q.quarter,
|
||||||
|
accruals: parseFloat(accruals.toFixed(2)),
|
||||||
|
sloanRatio: parseFloat(sloanRatio.toFixed(2)),
|
||||||
|
regime
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const len = qData.length;
|
||||||
|
sloanRadar[comp.ticker] = {
|
||||||
|
currentSloan: qData[len - 1].sloanRatio,
|
||||||
|
currentRegime: qData[len - 1].regime,
|
||||||
|
data: qData
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
dates,
|
dates,
|
||||||
liveDataAvailable,
|
liveDataAvailable,
|
||||||
@@ -436,6 +477,10 @@ export async function GET() {
|
|||||||
infrastructure: {
|
infrastructure: {
|
||||||
name: 'Tech Infrastructure Leverage & Cluster Expansion',
|
name: 'Tech Infrastructure Leverage & Cluster Expansion',
|
||||||
tickers: infrastructureLeverage
|
tickers: infrastructureLeverage
|
||||||
|
},
|
||||||
|
sloan: {
|
||||||
|
name: 'Sloan Earnings Quality Accrual Radar',
|
||||||
|
tickers: sloanRadar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ interface SearchResult {
|
|||||||
returns: number[];
|
returns: number[];
|
||||||
currentPrice?: number;
|
currentPrice?: number;
|
||||||
peakPrice?: number;
|
peakPrice?: number;
|
||||||
|
sloanRatio?: number;
|
||||||
|
sloanRegime?: 'SAFE' | 'ANOMALY';
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ScannerDemo() {
|
export default function ScannerDemo() {
|
||||||
@@ -218,7 +220,9 @@ export default function ScannerDemo() {
|
|||||||
reboundScore: overreactionScore,
|
reboundScore: overreactionScore,
|
||||||
returns: result.returns,
|
returns: result.returns,
|
||||||
currentPrice: result.currentPrice,
|
currentPrice: result.currentPrice,
|
||||||
peakPrice: result.peakPrice
|
peakPrice: result.peakPrice,
|
||||||
|
sloanRatio: result.sloanRatio,
|
||||||
|
sloanRegime: result.sloanRegime
|
||||||
};
|
};
|
||||||
|
|
||||||
setAlertsMetadata(prev => ({
|
setAlertsMetadata(prev => ({
|
||||||
@@ -797,6 +801,26 @@ export default function ScannerDemo() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{searchResult.sloanRatio !== undefined && (
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="text-slate-400">Sloan Accrual Ratio:</span>
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<span className="font-mono font-bold text-slate-300">
|
||||||
|
{searchResult.sloanRatio.toFixed(2)}%
|
||||||
|
</span>
|
||||||
|
{searchResult.sloanRegime === 'SAFE' ? (
|
||||||
|
<span className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[9px] font-bold bg-emerald-500/10 text-emerald-400 border border-emerald-500/20">
|
||||||
|
SAFE
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[9px] font-bold bg-rose-500/10 text-rose-400 border border-rose-500/20 animate-pulse">
|
||||||
|
ANOMALY
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="pt-2 border-t border-slate-900">
|
<div className="pt-2 border-t border-slate-900">
|
||||||
<span className="text-[10px] text-slate-400 uppercase font-semibold block mb-1">KI-Kommentar:</span>
|
<span className="text-[10px] text-slate-400 uppercase font-semibold block mb-1">KI-Kommentar:</span>
|
||||||
<p className="italic text-slate-300 leading-relaxed text-[11px]">{searchResult.whyDropped}</p>
|
<p className="italic text-slate-300 leading-relaxed text-[11px]">{searchResult.whyDropped}</p>
|
||||||
|
|||||||
@@ -69,6 +69,19 @@ interface Payload {
|
|||||||
}[];
|
}[];
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
sloan: {
|
||||||
|
name: string;
|
||||||
|
tickers: Record<string, {
|
||||||
|
currentSloan: number;
|
||||||
|
currentRegime: 'SAFE' | 'ANOMALY';
|
||||||
|
data: {
|
||||||
|
quarter: string;
|
||||||
|
accruals: number;
|
||||||
|
sloanRatio: number;
|
||||||
|
regime: 'SAFE' | 'ANOMALY';
|
||||||
|
}[];
|
||||||
|
}>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +135,7 @@ export default function AiSpecialSilo() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { monetizationGap, supplyChain, infrastructure } = payload.metrics;
|
const { monetizationGap, supplyChain, infrastructure, sloan } = payload.metrics;
|
||||||
|
|
||||||
// HSL Status Colors based on metric thresholds
|
// HSL Status Colors based on metric thresholds
|
||||||
const getMonetizationStatus = () => {
|
const getMonetizationStatus = () => {
|
||||||
@@ -151,9 +164,15 @@ export default function AiSpecialSilo() {
|
|||||||
return 'GREEN';
|
return 'GREEN';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getSloanStatus = () => {
|
||||||
|
const hasAnomaly = Object.values(sloan.tickers).some(t => t.currentRegime === 'ANOMALY');
|
||||||
|
return hasAnomaly ? 'ANOMALY' : 'SAFE';
|
||||||
|
};
|
||||||
|
|
||||||
const monetizationStatus = getMonetizationStatus();
|
const monetizationStatus = getMonetizationStatus();
|
||||||
const supplyStatus = getSupplyChainStatus();
|
const supplyStatus = getSupplyChainStatus();
|
||||||
const infraStatus = getInfrastructureStatus();
|
const infraStatus = getInfrastructureStatus();
|
||||||
|
const sloanStatus = getSloanStatus();
|
||||||
|
|
||||||
// Helper for trend icons
|
// Helper for trend icons
|
||||||
const renderTrendIcon = (trend: 'UP' | 'DOWN' | 'FLAT', isDangerUp = false) => {
|
const renderTrendIcon = (trend: 'UP' | 'DOWN' | 'FLAT', isDangerUp = false) => {
|
||||||
@@ -227,8 +246,8 @@ export default function AiSpecialSilo() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 🚥 3 LARGE GLOWING NEON-AMPEL CARDS */}
|
{/* 🚥 4 LARGE GLOWING NEON-AMPEL CARDS */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
|
|
||||||
{/* CARD 1: ROI-to-CapEx & Monetization Gap */}
|
{/* CARD 1: ROI-to-CapEx & Monetization Gap */}
|
||||||
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-850 rounded-2xl p-5 relative overflow-hidden shadow-lg flex flex-col justify-between min-h-[140px]">
|
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-850 rounded-2xl p-5 relative overflow-hidden shadow-lg flex flex-col justify-between min-h-[140px]">
|
||||||
@@ -330,10 +349,46 @@ export default function AiSpecialSilo() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* CARD 4: Sloan Earnings Quality */}
|
||||||
|
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-850 rounded-2xl p-5 relative overflow-hidden shadow-lg flex flex-col justify-between min-h-[140px]">
|
||||||
|
<div className="absolute top-0 right-0 w-24 h-24 bg-gradient-to-br from-teal-500/10 to-transparent rounded-full blur-2xl pointer-events-none" />
|
||||||
|
<div className="flex justify-between items-start">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<div className="text-[10px] text-teal-400 uppercase font-bold tracking-widest font-mono">4. Earnings Accrual Integrity</div>
|
||||||
|
<div className="text-lg font-black text-white leading-tight">Sloan Earnings Quality</div>
|
||||||
|
<div className="text-[10px] text-slate-400">Richard Sloan Accounting Anomaly Radar</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className={`w-3.5 h-3.5 rounded-full ${
|
||||||
|
sloanStatus === 'SAFE' ? 'bg-emerald-500 shadow-[0_0_10px_#10b981]' :
|
||||||
|
'bg-rose-500 shadow-[0_0_10px_#f43f5e] animate-pulse'
|
||||||
|
}`} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4 pt-4 border-t border-slate-850 flex justify-between items-end">
|
||||||
|
<div>
|
||||||
|
<div className="text-[9px] uppercase font-mono text-slate-500">Max Accrual Spread</div>
|
||||||
|
<div className="font-mono text-xl font-bold text-slate-200">
|
||||||
|
{Math.max(...Object.values(sloan.tickers).map(t => Math.abs(t.currentSloan))).toFixed(1)}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<div className="text-[9px] uppercase font-mono text-slate-500">Silo Status</div>
|
||||||
|
<div className="text-xs font-semibold text-slate-300 flex items-center gap-1 justify-end">
|
||||||
|
{sloanStatus === 'SAFE' ? (
|
||||||
|
<span className="text-emerald-400">Safe Regime</span>
|
||||||
|
) : (
|
||||||
|
<span className="text-rose-400 animate-pulse font-bold">Anomaly Warning</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* DETAILED LEDGER GRID */}
|
{/* DETAILED LEDGER GRID */}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
|
|
||||||
{/* PANEL A: ROI-TO-CAPEX & MONETIZATION GAPS */}
|
{/* PANEL A: ROI-TO-CAPEX & MONETIZATION GAPS */}
|
||||||
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-6 shadow-xl space-y-4">
|
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-6 shadow-xl space-y-4">
|
||||||
@@ -463,6 +518,66 @@ export default function AiSpecialSilo() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* PANEL D: SLOAN EARNINGS QUALITY ACCRUAL RADAR */}
|
||||||
|
<div className="bg-slate-900/60 backdrop-blur-md border border-slate-800 rounded-2xl p-6 shadow-xl space-y-4">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<h3 className="text-sm font-bold text-white flex items-center gap-2">
|
||||||
|
<Activity className="w-4 h-4 text-emerald-400" /> Sloan Accrual Quality Radar
|
||||||
|
</h3>
|
||||||
|
<p className="text-[10px] text-slate-400">
|
||||||
|
Richard Sloan Accrual Index. Anomalies (>10% or <-10%) indicate aggressive accounting.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-3">
|
||||||
|
{Object.entries(sloan.tickers).map(([ticker, metrics]) => {
|
||||||
|
const currentSloan = metrics.currentSloan;
|
||||||
|
const isAnomaly = metrics.currentRegime === 'ANOMALY';
|
||||||
|
|
||||||
|
const highlightClass = isAnomaly
|
||||||
|
? 'text-rose-400 font-bold animate-pulse'
|
||||||
|
: 'text-emerald-400';
|
||||||
|
|
||||||
|
const strokeColor = isAnomaly
|
||||||
|
? '#f43f5e'
|
||||||
|
: '#10b981';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={ticker} className="bg-slate-955/40 border border-slate-850 rounded-xl p-3 flex justify-between items-center hover:bg-slate-955/60 transition-colors">
|
||||||
|
<div className="space-y-0.5 w-1/4">
|
||||||
|
<div className="text-xs font-bold text-slate-200">{ticker}</div>
|
||||||
|
<div className="text-[9px] text-slate-500 font-mono">
|
||||||
|
Regime: {metrics.currentRegime}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sparkline for Sloan Ratio */}
|
||||||
|
<div className="w-1/3 h-8">
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<LineChart data={metrics.data}>
|
||||||
|
<Line
|
||||||
|
type="monotone"
|
||||||
|
dataKey="sloanRatio"
|
||||||
|
stroke={strokeColor}
|
||||||
|
strokeWidth={1.5}
|
||||||
|
dot={false}
|
||||||
|
/>
|
||||||
|
</LineChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-right w-1/3">
|
||||||
|
<div className="text-[9px] text-slate-500 uppercase font-mono">Sloan Ratio</div>
|
||||||
|
<div className={`font-mono text-sm font-bold ${highlightClass}`}>
|
||||||
|
{currentSloan.toFixed(2)}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* PANEL C: SUPPLY CHAIN FLOW DETAILS */}
|
{/* PANEL C: SUPPLY CHAIN FLOW DETAILS */}
|
||||||
|
|||||||
@@ -185,6 +185,46 @@ export default function TechMathModal({ isOpen, onClose }: TechMathModalProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Section 5: Sloan Accrual Radar */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<h4 className="text-xs font-bold text-teal-400 uppercase tracking-wider font-mono flex items-center gap-1.5">
|
||||||
|
<Percent className="w-3.5 h-3.5" /> 5. Sloan Earnings Quality Accrual Radar
|
||||||
|
</h4>
|
||||||
|
<p className="text-xs leading-relaxed text-slate-400">
|
||||||
|
The Sloan Ratio identifies discrepancies between reported net income and actual cash flows. A high proportion of non-cash accruals signals low earnings quality and is a historically proven accounting anomaly predictor:
|
||||||
|
</p>
|
||||||
|
<div className="bg-slate-955/40 p-5 rounded-2xl border border-slate-850 my-2 space-y-4">
|
||||||
|
<div>
|
||||||
|
<p className="text-xs text-slate-300 mb-2 font-semibold">
|
||||||
|
{"Formula for Accruals (\\(\\text{Accruals}\\)):"}
|
||||||
|
</p>
|
||||||
|
<BlockMath math="\text{Accruals}_{t} = \text{Net Income}_{t} - (\text{CFO}_{t} + \text{CFI}_{t})" />
|
||||||
|
<p className="text-xs text-slate-300 my-2 font-semibold">
|
||||||
|
{"Formula for Sloan Ratio (\\(\\text{Sloan Ratio}\\)):"}
|
||||||
|
</p>
|
||||||
|
<BlockMath math="\text{Sloan Ratio}_{t} = \frac{\text{Accruals}_{t}}{\text{Total Assets}_{t}} \times 100" />
|
||||||
|
<p className="text-[10px] text-slate-555 mt-3 font-mono leading-relaxed">
|
||||||
|
{"Where:"}
|
||||||
|
<br />
|
||||||
|
{"- "}<InlineMath math="\text{Net Income}_{t}" />{" is the company's net income for the fiscal period."}
|
||||||
|
<br />
|
||||||
|
{"- "}<InlineMath math="\text{CFO}_{t}" />{" is Cash Flow from Operations (operating cash flow)."}
|
||||||
|
<br />
|
||||||
|
{"- "}<InlineMath math="\text{CFI}_{t}" />{" is Cash Flow from Investing activities."}
|
||||||
|
<br />
|
||||||
|
{"- "}<InlineMath math="\text{Total Assets}_{t}" />{" is the total assets reported on the balance sheet at the end of the period."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs leading-relaxed text-slate-400">
|
||||||
|
<strong className="text-teal-300">Regime Classifications:</strong>
|
||||||
|
<br />
|
||||||
|
{"- "}<strong className="text-emerald-400">Safe Regime (-10% to +10%):</strong>{" High earnings quality, where profits are strongly supported by actual cash flows."}
|
||||||
|
<br />
|
||||||
|
{"- "}<strong className="text-rose-400 font-bold">Anomaly Regime (> +10% or < -10%):</strong>{" Aggressive accounting or accrual expansion, indicating high earnings manipulation risk."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user