import { test } from 'node:test'; import assert from 'node:assert/strict'; import { MarketRegime } from '../src/market/MarketRegime.js'; import { SECTOR, ASSET_TYPE } from '../src/config/constants.js'; const regime = (benchmarks, extra = {}) => new MarketRegime({ benchmarks, ...extra }); test('stock inflated P/E = marketPE × 1.5', () => { const { gates } = regime({ marketPE: 24 }).getInflatedOverrides(ASSET_TYPE.STOCK, SECTOR.GENERAL); assert.equal(gates.maxPERatio, Math.round(24 * 1.5)); // 36 }); test('tech inflated P/E = techPE × 1.3', () => { const { gates } = regime({ techPE: 40 }).getInflatedOverrides( ASSET_TYPE.STOCK, SECTOR.TECHNOLOGY, ); assert.equal(gates.maxPERatio, Math.round(40 * 1.3)); // 52 }); test('REIT inflated minYield = reitYield × 0.85 in NORMAL rate regime', () => { const { thresholds } = regime({ reitYield: 4.0 }, { rateRegime: 'NORMAL' }).getInflatedOverrides( ASSET_TYPE.STOCK, SECTOR.REIT, ); assert.equal(thresholds.minYield, +(4.0 * 0.85).toFixed(2)); // 3.40 }); test('REIT inflated minYield = reitYield × 0.95 in HIGH rate regime', () => { const { thresholds } = regime({ reitYield: 4.0 }, { rateRegime: 'HIGH' }).getInflatedOverrides( ASSET_TYPE.STOCK, SECTOR.REIT, ); assert.equal(thresholds.minYield, +(4.0 * 0.95).toFixed(2)); // 3.80 }); test('bond inflated minSpread = igSpread × 0.80 in NORMAL rate regime', () => { const { thresholds } = regime({ igSpread: 1.5 }, { rateRegime: 'NORMAL' }).getInflatedOverrides( ASSET_TYPE.BOND, SECTOR.GENERAL, ); assert.equal(thresholds.minSpread, +(1.5 * 0.8).toFixed(2)); // 1.20 }); test('bond inflated minSpread = igSpread × 0.90 in HIGH rate regime', () => { const { thresholds } = regime({ igSpread: 1.5 }, { rateRegime: 'HIGH' }).getInflatedOverrides( ASSET_TYPE.BOND, SECTOR.GENERAL, ); assert.equal(thresholds.minSpread, +(1.5 * 0.9).toFixed(2)); // 1.35 }); test('GENERAL stock P/E multiplier compresses to 1.2× in HIGH rate regime', () => { const { gates } = regime({ marketPE: 25 }, { rateRegime: 'HIGH' }).getInflatedOverrides( ASSET_TYPE.STOCK, SECTOR.GENERAL, ); assert.equal(gates.maxPERatio, Math.round(25 * 1.2)); // 30 }); test('ETF inflated loosens expense gate to 0.75', () => { const { gates } = regime({}).getInflatedOverrides(ASSET_TYPE.ETF); assert.equal(gates.maxExpenseRatio, 0.75); }); test('falls back to defaults when benchmarks missing', () => { const { gates } = new MarketRegime({}).getInflatedOverrides(ASSET_TYPE.STOCK, SECTOR.GENERAL); assert.equal(gates.maxPERatio, Math.round(22 * 1.5)); // default marketPE = 22 });