50 lines
1.7 KiB
TypeScript
50 lines
1.7 KiB
TypeScript
import { ScoringRules } from '../config/ScoringConfig.js';
|
|
import { MarketRegime } from '../market/MarketRegime.js';
|
|
import { SCORE_MODE } from '../config/constants.js';
|
|
import type { AssetType, MarketContext } from '../types.js';
|
|
|
|
interface RuleSet {
|
|
gates: Record<string, number>;
|
|
weights: Record<string, number>;
|
|
thresholds: Record<string, number>;
|
|
}
|
|
|
|
export const RuleMerger = {
|
|
getRulesForAsset(
|
|
type: AssetType,
|
|
metrics: { sector?: string },
|
|
marketContext: Partial<MarketContext> = {},
|
|
mode: string = SCORE_MODE.FUNDAMENTAL,
|
|
): RuleSet {
|
|
const base = ScoringRules[type as keyof typeof ScoringRules];
|
|
if (!base) throw new Error(`No rules configured for asset type: ${type}`);
|
|
|
|
// Deep clone to avoid mutating the source config
|
|
const rules: RuleSet & { SECTOR_OVERRIDE?: unknown } = JSON.parse(JSON.stringify(base));
|
|
|
|
if (type === 'STOCK' && metrics.sector) {
|
|
const stockBase = ScoringRules.STOCK;
|
|
const override =
|
|
stockBase.SECTOR_OVERRIDE?.[
|
|
metrics.sector.toUpperCase() as keyof typeof stockBase.SECTOR_OVERRIDE
|
|
];
|
|
if (override) {
|
|
rules.gates = { ...rules.gates, ...override.gates };
|
|
rules.weights = { ...rules.weights, ...override.weights };
|
|
rules.thresholds = { ...rules.thresholds, ...override.thresholds };
|
|
}
|
|
}
|
|
delete rules.SECTOR_OVERRIDE;
|
|
|
|
if (mode === SCORE_MODE.INFLATED) {
|
|
const { gates, thresholds } = new MarketRegime(
|
|
marketContext as MarketContext,
|
|
).getInflatedOverrides(type, metrics.sector);
|
|
rules.gates = { ...rules.gates, ...gates };
|
|
rules.thresholds = { ...rules.thresholds, ...thresholds };
|
|
}
|
|
|
|
return rules;
|
|
},
|
|
};
|