Fix EBITDA margin auto-correction and TypeScript compilation error

- Added auto-correction logic for EBITDA margins when difference >15pp
- Fixed missing closing brace in revenue validation block
- Enhanced margin validation to catch cases like 95% -> 22.3%
This commit is contained in:
admin
2025-11-10 15:53:17 -05:00
parent 59e0938b72
commit ecd4b13115
2 changed files with 119 additions and 54 deletions

View File

@@ -2166,8 +2166,11 @@ IMPORTANT: Replace all placeholder text with actual information from the CIM doc
const calculatedMargin = (ebitdaValue / revValue) * 100;
const marginDiff = Math.abs(calculatedMargin - marginValue);
// If margin difference is > 5 percentage points, there may be an issue
if (marginDiff > 5 && revValue > 0) {
// If margin difference is > 15 percentage points, this is a critical error
// Examples: 95% when should be 22%, or 15% when should be 75%
if (marginDiff > 15 && revValue > 0) {
validationIssues.push(`CRITICAL: EBITDA margin mismatch for ${period}: stated ${marginValue}% vs calculated ${calculatedMargin.toFixed(1)}% (diff: ${marginDiff.toFixed(1)}pp) - likely column misalignment`);
} else if (marginDiff > 5 && revValue > 0) {
validationIssues.push(`EBITDA margin mismatch for ${period}: stated ${marginValue}% vs calculated ${calculatedMargin.toFixed(1)}%`);
}
@@ -2175,6 +2178,11 @@ IMPORTANT: Replace all placeholder text with actual information from the CIM doc
if (marginValue < 0 || marginValue > 60) {
validationIssues.push(`EBITDA margin for ${period} is outside typical range (${marginValue}%)`);
}
// Additional check: If calculated margin is reasonable but stated margin is way off, flag it
if (calculatedMargin >= 0 && calculatedMargin <= 60 && marginDiff > 15) {
validationIssues.push(`Consider using calculated margin (${calculatedMargin.toFixed(1)}%) instead of stated margin (${marginValue}%) for ${period}`);
}
}
}
});

View File

@@ -559,7 +559,21 @@ Focus on finding these specific fields in the document. Extract exact values, nu
periodData.revenue = 'Not specified in CIM';
}
// Check 2: Detect unusual growth patterns (suggests misaligned columns)
// Check 2: Revenue should generally increase or be stable (FY-1/LTM shouldn't be much lower than FY-2/FY-3)
// Exception: If this is FY-3 and others are higher, that's normal
if (period !== 'fy3' && currentValue < minOtherValue * 0.5 && currentValue < avgOtherValue * 0.6) {
logger.warn('Revenue value suspiciously low compared to other periods - possible column misalignment', {
period,
value: periodData.revenue,
numericValue: currentValue,
avgOtherPeriods: avgOtherValue,
minOtherPeriods: minOtherValue,
reason: `Revenue for ${period} ($${(currentValue / 1000000).toFixed(1)}M) is <50% of minimum other period ($${(minOtherValue / 1000000).toFixed(1)}M) - may indicate column misalignment`
});
// Don't reject automatically, but flag for review - this often indicates wrong column
}
// Check 3: Detect unusual growth patterns (suggests misaligned columns)
// Find adjacent periods to check growth
const periodOrder = ['fy3', 'fy2', 'fy1', 'ltm'];
const currentIndex = periodOrder.indexOf(period);
@@ -620,8 +634,63 @@ Focus on finding these specific fields in the document. Extract exact values, nu
const marginMatch = marginStr.match(/(-?\d+(?:\.\d+)?)/);
if (marginMatch) {
const marginValue = parseFloat(marginMatch[1]);
// First, try to calculate margin from revenue and EBITDA to validate
const revValue = extractNumericValue(periodData.revenue || '');
const ebitdaValue = extractNumericValue(periodData.ebitda || '');
if (revValue !== null && ebitdaValue !== null && revValue > 0) {
const calculatedMargin = (ebitdaValue / revValue) * 100;
const marginDiff = Math.abs(calculatedMargin - marginValue);
// If margin difference is > 15 percentage points, auto-correct it
// This catches cases like 95% when it should be 22%, or 15% when it should be 75%
if (marginDiff > 15) {
logger.warn('EBITDA margin mismatch detected - auto-correcting', {
period,
statedMargin: `${marginValue}%`,
calculatedMargin: `${calculatedMargin.toFixed(1)}%`,
difference: `${marginDiff.toFixed(1)}pp`,
revenue: periodData.revenue,
ebitda: periodData.ebitda,
action: 'Auto-correcting margin to calculated value',
reason: `Stated margin (${marginValue}%) differs significantly from calculated margin (${calculatedMargin.toFixed(1)}%) - likely extraction error`
});
// Auto-correct: Use calculated margin instead of stated margin
periodData.ebitdaMargin = `${calculatedMargin.toFixed(1)}%`;
} else if (marginDiff > 10) {
// If difference is 10-15pp, log warning but don't auto-correct (might be legitimate)
logger.warn('EBITDA margin mismatch detected', {
period,
statedMargin: `${marginValue}%`,
calculatedMargin: `${calculatedMargin.toFixed(1)}%`,
difference: `${marginDiff.toFixed(1)}pp`,
revenue: periodData.revenue,
ebitda: periodData.ebitda,
reason: `Stated margin (${marginValue}%) differs from calculated margin (${calculatedMargin.toFixed(1)}%) - may indicate data extraction error`
});
} else {
// Margin matches calculated value, but check if it's in reasonable range
// Reject margins outside reasonable range (-10% to 60%)
// Negative margins are possible but should be within reason
if (marginValue < -10 || marginValue > 60) {
logger.warn('EBITDA margin outside reasonable range - using calculated value', {
period,
value: marginStr,
numericValue: marginValue,
calculatedMargin: `${calculatedMargin.toFixed(1)}%`,
reason: `Stated margin (${marginValue}%) outside reasonable range (-10% to 60%), but calculated margin (${calculatedMargin.toFixed(1)}%) is valid - using calculated`
});
// Use calculated margin if it's in reasonable range
if (calculatedMargin >= -10 && calculatedMargin <= 60) {
periodData.ebitdaMargin = `${calculatedMargin.toFixed(1)}%`;
} else {
periodData.ebitdaMargin = 'Not specified in CIM';
}
}
}
} else {
// Can't calculate margin, so just check if stated margin is in reasonable range
if (marginValue < -10 || marginValue > 60) {
logger.warn('Rejecting invalid EBITDA margin', {
period,
@@ -630,30 +699,18 @@ Focus on finding these specific fields in the document. Extract exact values, nu
reason: `Margin (${marginValue}%) outside reasonable range (-10% to 60%)`
});
periodData.ebitdaMargin = 'Not specified in CIM';
} else {
// Cross-validate: Check margin consistency with revenue and EBITDA
const revValue = extractNumericValue(periodData.revenue || '');
const ebitdaValue = extractNumericValue(periodData.ebitda || '');
if (revValue !== null && ebitdaValue !== null && revValue > 0) {
const calculatedMargin = (ebitdaValue / revValue) * 100;
const marginDiff = Math.abs(calculatedMargin - marginValue);
// If margin difference is > 10 percentage points, flag it
if (marginDiff > 10) {
logger.warn('EBITDA margin mismatch detected', {
period,
statedMargin: `${marginValue}%`,
calculatedMargin: `${calculatedMargin.toFixed(1)}%`,
difference: `${marginDiff.toFixed(1)}pp`,
revenue: periodData.revenue,
ebitda: periodData.ebitda,
reason: `Stated margin (${marginValue}%) differs significantly from calculated margin (${calculatedMargin.toFixed(1)}%) - may indicate data extraction error`
});
// Don't reject - just log as warning
}
}
// Check margin consistency across periods (margins should be relatively stable)
const otherMargins = otherPeriods
if (periodData.ebitdaMargin && periodData.ebitdaMargin !== 'Not specified in CIM') {
// Re-extract margin value after potential auto-correction
const finalMarginMatch = periodData.ebitdaMargin.match(/(-?\d+(?:\.\d+)?)/);
const finalMarginValue = finalMarginMatch ? parseFloat(finalMarginMatch[1]) : marginValue;
// Get other periods for cross-period validation
const otherPeriodsForMargin = periods.filter(p => p !== period && financials[p]?.ebitdaMargin);
const otherMargins = otherPeriodsForMargin
.map(p => {
const margin = financials[p]?.ebitdaMargin;
if (!margin || margin === 'Not specified in CIM') return null;
@@ -664,15 +721,15 @@ Focus on finding these specific fields in the document. Extract exact values, nu
if (otherMargins.length > 0) {
const avgOtherMargin = otherMargins.reduce((a, b) => a + b, 0) / otherMargins.length;
const marginDiff = Math.abs(marginValue - avgOtherMargin);
const marginDiff = Math.abs(finalMarginValue - avgOtherMargin);
// Flag if margin differs by > 20 percentage points from average
if (marginDiff > 20) {
logger.warn('EBITDA margin inconsistency across periods', {
period,
margin: `${marginValue}%`,
margin: `${finalMarginValue}%`,
avgOtherPeriods: `${avgOtherMargin.toFixed(1)}%`,
difference: `${marginDiff.toFixed(1)}pp`,
reason: `Margin for ${period} (${marginValue}%) differs significantly from average of other periods (${avgOtherMargin.toFixed(1)}%) - may indicate extraction error`
reason: `Margin for ${period} (${finalMarginValue}%) differs significantly from average of other periods (${avgOtherMargin.toFixed(1)}%) - may indicate extraction error`
});
// Don't reject - just log as warning
}