package profitability import ( "math" ) // ProfitabilityMetrics contains IRR and NPV calculations for the project. type ProfitabilityMetrics struct { NPV float64 `json:"npv"` // Net Present Value IRR float64 `json:"irr"` // Internal Rate of Return (as percentage) PaybackPeriod float64 `json:"payback_period"` // Payback period in years NPVBreakEven float64 `json:"npv_break_even"` // NPV at break-even discount rate } // CalculateProfitability computes IRR, NPV, and payback period for cash flows. func CalculateProfitability(cashFlows []float64, discountRate float64) ProfitabilityMetrics { // Calculate NPV npv := calculateNPV(cashFlows, discountRate) // Calculate IRR (find rate where NPV = 0) irr := calculateIRR(cashFlows) // Calculate payback period paybackPeriod := calculatePaybackPeriod(cashFlows) // Calculate NPV at break-even (NPV = 0) npvBreakEven := 0.0 // This would be the IRR rate itself return ProfitabilityMetrics{ NPV: npv, IRR: irr * 100, // Convert to percentage PaybackPeriod: paybackPeriod, NPVBreakEven: npvBreakEven, } } // calculateNPV computes Net Present Value for a series of cash flows. func calculateNPV(cashFlows []float64, discountRate float64) float64 { npv := 0.0 for i, cf := range cashFlows { if i == 0 { // Year 0 cash flow is not discounted npv += cf } else { npv += cf / math.Pow(1+discountRate, float64(i)) } } return npv } // calculateIRR finds the Internal Rate of Return using numerical approximation. // This uses a simple iterative approach to find the rate where NPV = 0. func calculateIRR(cashFlows []float64) float64 { // Simple IRR calculation using Newton-Raphson approximation // Start with initial guess of 10% rate := 0.10 // Maximum iterations maxIter := 100 tolerance := 0.0001 for i := 0; i < maxIter; i++ { npv := calculateNPV(cashFlows, rate) npvDerivative := calculateNPVDerivative(cashFlows, rate) if math.Abs(npvDerivative) < tolerance { break // Avoid division by zero } newRate := rate - npv/npvDerivative if math.Abs(newRate-rate) < tolerance { return newRate } rate = newRate // Bound the rate between -50% and 100% to prevent extreme values if rate < -0.5 { rate = -0.5 } else if rate > 1.0 { rate = 1.0 } } return rate } // calculateNPVDerivative calculates the derivative of NPV with respect to discount rate. // Used in Newton-Raphson method for IRR calculation. func calculateNPVDerivative(cashFlows []float64, discountRate float64) float64 { derivative := 0.0 for i, cf := range cashFlows { if i > 0 { derivative -= float64(i) * cf / math.Pow(1+discountRate, float64(i+1)) } } return derivative } // calculatePaybackPeriod calculates how many years it takes to recover initial investment. func calculatePaybackPeriod(cashFlows []float64) float64 { cumulative := 0.0 for i, cf := range cashFlows { cumulative += cf if cumulative >= 0 { if i == 0 { return 0 // Already profitable in year 0 } // Linear interpolation for partial year previousCumulative := cumulative - cf if previousCumulative < 0 { fraction := math.Abs(previousCumulative) / cf return float64(i-1) + fraction } return float64(i) } } // If never reaches break-even, return a large number return 999.0 }