using ReserveManagementSystemLib.Models; namespace ReserveManagementSystemApi.Helpers { public class LoanCalculationHelper { private decimal _daysInterest; private LoanReserve loanReserve = new LoanReserve(); public LoanCalculationHelper() { } public LoanReserve CalculateBuyDown(CalculationConfig cc) { // Case - Buy down Below Threshold where difference in rates is less than or equal to 0.25 percent if (cc.BuydownRateDifference() <= cc.NoReserveThreshold()) //we already know it's positive { loanReserve.ReserveAmount = 0; loanReserve.ReserveTypeMessage = "No Reserve"; loanReserve.FlatFeePaid = false; return loanReserve; } // Case - This is a regular Buy Down _daysInterest = cc.BuydownRateDifference() / 100 * cc.DaysToFirstPmt() * cc.LoanData().LoanAmount / 365 * (decimal)Math.Pow(1 + (double)cc.LoanData().ContractRate / 100, (double)cc.LoanData().Term / 12); loanReserve.ReserveAmount = -(cc.BuyFinanceCharge - cc.ContractFinanceCharge + _daysInterest); loanReserve.ReserveTypeMessage = "Buy Down Reserve"; loanReserve.FlatFeePaid = false; return loanReserve; } public LoanReserve CalculateNoMarkup(CalculationConfig cc) { // Case - Contract rate is equal to the Buy rate _daysInterest = cc.StdRate() * cc.DaysToFirstPmt() * cc.LoanData().LoanAmount / 365 * (decimal)Math.Pow(1 + (double)cc.LoanData().ContractRate / 100, (double)cc.LoanData().Term / 12); loanReserve.ReserveAmount = (cc.ContractFinanceCharge - cc.BuyFinanceCharge + _daysInterest); loanReserve.ReserveTypeMessage = "No Reserve"; loanReserve.FlatFeePaid = false; return loanReserve; } public LoanReserve CalculateStandardMarkup(CalculationConfig cc) { // Case - Contract rate is greater than Buy rate _daysInterest = (1 - cc.ReserveDiscount()/100) * cc.StdRate() / 100 * cc.DaysToFirstPmt() * cc.LoanData().LoanAmount / 365 * (decimal)Math.Pow(1 + (double)cc.LoanData().ContractRate / 100, (double)cc.LoanData().Term / 12); loanReserve.ReserveAmount = ((1 - cc.ReserveDiscount() / 100) * (cc.ContractFinanceCharge - cc.BuyFinanceCharge) + _daysInterest); if ((double)cc.StdRate() <= 2.5) { loanReserve.ReserveTypeMessage = "Standard Reserve"; } else { loanReserve.ReserveTypeMessage = "Standard Reserve - Exceeds Max Markup"; } return loanReserve; } public LoanReserve AssignFlatFee(LoanReserve loanReserve, CalculationConfig cc) { loanReserve.FlatFeePaid = false; if (loanReserve.ReserveAmount < (cc.FlatFeePercent() / 100 * cc._loanData.LoanAmount * (1 - cc.ReserveDiscount() / 100))) { loanReserve.ReserveAmount = (cc.FlatFeePercent() / 100 * cc._loanData.LoanAmount * (1 - cc.ReserveDiscount() / 100)); loanReserve.ReserveTypeMessage = "Flat Fee Reserve"; loanReserve.FlatFeePaid = true; } return loanReserve; } public decimal FinanceCharge(int term, decimal loanAmount, decimal rate) { if (rate == 0) { return 0; } double paymentRate = (double)rate / (double)1200; decimal rateFactor = (decimal)Math.Pow((1 + paymentRate), (double)term); return (loanAmount * term * rate / 1200 * rateFactor) / (rateFactor - 1) - loanAmount; } internal DateTime GetLastChargebackDate(DateTime loanDate, DateTime firstPmtDate, int numOfChargebackPayments, Int16? numOfChargebackDays) { // in rare cases that date from loan date is most recent, check table values and compare between loan date vs. first payment date int result = DateTime.Compare(loanDate.AddDays((Double)numOfChargebackDays), firstPmtDate.AddMonths(numOfChargebackPayments - 1)); if (result > 0) { return loanDate.AddDays((Double)numOfChargebackDays); //Loan Date is most recent } return firstPmtDate.AddMonths(numOfChargebackPayments - 1); } internal decimal CalculateChargeback(decimal reserve, int term, int remainingMonths) { return reserve * MonthFactor(remainingMonths) / MonthFactor(term); } private decimal MonthFactor(int numberOfMonths) { return numberOfMonths * (numberOfMonths + 1) / 2; } public int GetRemainingMonths(Loan loanData) { if (loanData.PaidLoanDate >= loanData.FinalPmtDate) return 0; var monthsOccurred = (loanData.PaidLoanDate.Value - loanData.FirstPmtDate).TotalDays / (365) * 12 + 1; var remainingMonths = (int)(loanData.Term - Math.Round((decimal)monthsOccurred, 0)); return remainingMonths; } } }