196 lines
7.1 KiB
TypeScript
196 lines
7.1 KiB
TypeScript
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, // 0–20% → 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 },
|
||
},
|
||
};
|