turash/models/profitability/profitability.go
Damir Mukimov 4a2fda96cd
Initial commit: Repository setup with .gitignore, golangci-lint v2.6.0, and code quality checks
- Initialize git repository
- Add comprehensive .gitignore for Go projects
- Install golangci-lint v2.6.0 (latest v2) globally
- Configure .golangci.yml with appropriate linters and formatters
- Fix all formatting issues (gofmt)
- Fix all errcheck issues (unchecked errors)
- Adjust complexity threshold for validation functions
- All checks passing: build, test, vet, lint
2025-11-01 07:36:22 +01:00

124 lines
3.3 KiB
Go

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
}