Files
market_screener/server/config/ScoringConfig.ts
T
2026-06-05 22:11:11 -04:00

196 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { ScoringRulesShape } from '../types';
// ── Credit rating scale (S&P convention) ─────────────────────────────────
// Bond.ts converts letter ratings to these numbers; BondScorer uses them for gate checks.
// Investment grade = BBB (7) and above.
export const CREDIT_RATING_SCALE: Record<string, number> = {
AAA: 10,
AA: 9,
A: 8,
BBB: 7,
BB: 6,
B: 5,
CCC: 4,
CC: 3,
C: 2,
D: 1,
};
// ── Scoring rule shape ────────────────────────────────────────────────────
// Structural shapes (GateSet/WeightSet/ThresholdSet/RuleBlock/StockRules/
// ScoringRulesShape) live in server/types/asset.model.ts.
// ─────────────────────────────────────────────────────────────────────────────
// Fundamental baseline — Graham / value-investing style.
// MarketRegime.ts overrides the valuation gates for INFLATED-mode analysis.
// Sector overrides are structural — they apply in both modes.
// ─────────────────────────────────────────────────────────────────────────────
export const ScoringRules: ScoringRulesShape = {
STOCK: {
gates: {
maxDebtToEquity: 1.5, // Graham ceiling; most distress starts above 2x
minQuickRatio: 0.8, // below 0.8 signals real liquidity stress in non-tech
maxPERatio: 15, // Graham's actual rule: never pay more than 15x trailing earnings
maxPegGate: 1.0, // PEG > 1.0 means you're paying full price for growth (Lynch standard)
},
weights: {
margin: 2, // net profit margin
opMargin: 2, // operating margin (pricing power)
roe: 3, // return on equity — Buffett's primary quality metric
peg: 2, // valuation relative to growth
revenue: 2, // revenue growth
fcf: 3, // FCF is the most manipulation-resistant quality signal
analyst: 2, // Wall Street consensus (1=Strong Buy … 5=Strong Sell, inverted in scorer)
dcf: 2, // DCF margin of safety: positive = undervalued vs intrinsic value
},
thresholds: {
marginHigh: 15, // 15% net margin is genuinely excellent across most sectors
marginMed: 8, // 8% is the realistic mid-tier for industrials/retail
opMarginHigh: 20,
opMarginMed: 10,
roeHigh: 15, // sustainable 15% ROE is Buffett-quality; 20% is rare/fleeting
roeMed: 10, // 10% is the cost-of-equity floor for most businesses
pegHigh: 0.75, // PEG < 0.75 is genuinely cheap relative to growth
pegMed: 1.0,
revHigh: 10, // 10% organic revenue growth is strong for mature cos
revMed: 5,
fcfHigh: 5,
fcfMed: 2,
// Analyst consensus thresholds (Yahoo recommendationMean scale: 1=Strong Buy, 5=Strong Sell)
analystBuy: 2.0, // ≤ 2.0 → consensus is Buy or better
analystHold: 3.0, // ≤ 3.0 → consensus is Hold or better
// DCF margin-of-safety thresholds (% undervaluation vs intrinsic value)
dcfUndervalued: 20, // ≥ 20% margin of safety → undervalued
dcfFairValue: 0, // 020% → fairly valued; negative → overvalued
},
SECTOR_OVERRIDE: {
TECHNOLOGY: {
gates: { maxDebtToEquity: 2.0, minQuickRatio: 0.8, maxPERatio: 35, maxPegGate: 1.5 },
weights: { margin: 1, opMargin: 3, roe: 3, peg: 3, revenue: 4, fcf: 3 },
thresholds: { marginHigh: 25, opMarginHigh: 25, roeHigh: 20, pegHigh: 1.0, revHigh: 20 },
},
REIT: {
gates: { maxDebtToEquity: 6.0, minQuickRatio: 0.1, maxPERatio: 9999, maxPegGate: 9999 },
weights: { margin: 0, opMargin: 0, roe: 0, peg: 0, revenue: 0, fcf: 0, yield: 5, pFFO: 3 },
thresholds: { minYield: 4.5, maxPFFO: 20 },
},
FINANCIAL: {
gates: {
maxDebtToEquity: 9999,
minQuickRatio: 0.1,
maxPERatio: 9999,
maxPegGate: 9999,
maxPriceToBook: 1.5,
},
weights: { margin: 0, opMargin: 0, peg: 0, roe: 5, revenue: 1, fcf: 1, priceToBook: 3 },
thresholds: { roeHigh: 15, roeMed: 12, revHigh: 10, revMed: 5 },
},
ENERGY: {
gates: { maxDebtToEquity: 1.5, minQuickRatio: 0.6, maxPERatio: 15, maxPegGate: 1.5 },
weights: { margin: 0, opMargin: 3, roe: 2, peg: 1, revenue: 2, fcf: 4, yield: 3 },
thresholds: {
opMarginHigh: 20,
opMarginMed: 10,
roeHigh: 15,
roeMed: 8,
fcfHigh: 8,
fcfMed: 4,
},
},
HEALTHCARE: {
gates: { maxDebtToEquity: 1.5, minQuickRatio: 1.0, maxPERatio: 25, maxPegGate: 1.5 },
weights: { margin: 1, opMargin: 2, roe: 2, peg: 2, revenue: 4, fcf: 3 },
thresholds: {
marginHigh: 20,
marginMed: 10,
roeHigh: 20,
roeMed: 12,
revHigh: 15,
revMed: 8,
fcfHigh: 8,
fcfMed: 3,
},
},
COMMUNICATION: {
gates: { maxDebtToEquity: 2.0, minQuickRatio: 0.8, maxPERatio: 25, maxPegGate: 1.5 },
weights: { margin: 2, opMargin: 3, roe: 2, peg: 2, revenue: 3, fcf: 4 },
thresholds: {
marginHigh: 25,
marginMed: 12,
opMarginHigh: 30,
opMarginMed: 15,
roeHigh: 20,
roeMed: 12,
pegHigh: 1.0,
pegMed: 1.5,
revHigh: 15,
revMed: 5,
fcfHigh: 8,
fcfMed: 3,
},
},
CONSUMER_STAPLES: {
gates: { maxDebtToEquity: 1.5, minQuickRatio: 0.5, maxPERatio: 22, maxPegGate: 2.0 },
weights: { margin: 3, opMargin: 3, roe: 3, peg: 1, revenue: 1, fcf: 3 },
thresholds: {
marginHigh: 12,
marginMed: 7,
opMarginHigh: 18,
opMarginMed: 10,
roeHigh: 20,
roeMed: 12,
pegHigh: 1.5,
pegMed: 2.0,
revHigh: 5,
revMed: 2,
fcfHigh: 5,
fcfMed: 2,
},
},
CONSUMER_DISCRETIONARY: {
gates: { maxDebtToEquity: 2.0, minQuickRatio: 0.5, maxPERatio: 25, maxPegGate: 1.5 },
weights: { margin: 2, opMargin: 2, roe: 2, peg: 2, revenue: 4, fcf: 3 },
thresholds: {
marginHigh: 10,
marginMed: 5,
opMarginHigh: 15,
opMarginMed: 8,
roeHigh: 20,
roeMed: 12,
pegHigh: 1.0,
pegMed: 1.5,
revHigh: 12,
revMed: 5,
fcfHigh: 5,
fcfMed: 2,
},
},
},
},
ETF: {
gates: { maxExpenseRatio: 0.2 },
weights: { yield: 2, lowCost: 4, fiveYearReturn: 2 },
thresholds: {
minYield: 1.5,
maxExpense: 0.05,
minVolume: 1_000_000,
minFiveYearReturn: 8.0,
},
},
BOND: {
gates: { minCreditRating: 7 }, // BBB = investment-grade floor
weights: { yieldSpread: 3, duration: 2 },
thresholds: { minSpread: 1.5, maxDuration: 7 },
},
};