protected override IResult <Response> HandleSync(Request request) { try { var totalPrincipal = request.AssetCost - request.DownPayment; var numberOfPeriods = request.NumberOfYears * request.PeriodsPerYear; var ratePerPeriod = request.AnnualInterestRate == 0 ? 0 : request.AnnualInterestRate / request.PeriodsPerYear; var amortizationRequest = new AmortizationRequest(totalPrincipal, numberOfPeriods, ratePerPeriod); var amortizationResponse = _amortizationCalculator.Calculate(amortizationRequest); var response = new Response( totalPrincipal: totalPrincipal, paymentPerPeriod: amortizationResponse.PaymentPerPeriod + request.EscrowPerPeriod, totalInterestPaid: amortizationResponse.TotalInterestPaid, totalEscrowPaid: request.EscrowPerPeriod * numberOfPeriods, totalPaid: request.AssetCost + amortizationResponse.TotalInterestPaid + request.EscrowPerPeriod * numberOfPeriods, schedule: amortizationResponse.Schedule); return(Ok(response)); } catch (System.AggregateException ex) { if (ex.InnerExceptions.All(e => e.GetType() == typeof(System.OverflowException))) { return(Fail(new LoanOverflowFailure())); } else { throw; } } }
/// <summary> /// Calculate the loan request. /// </summary> /// <param name="request">The request to calculate</param> /// <returns>A completed AmortizationResponse</returns> /// <exception cref="ArgumentNullException">Throws when request is null.</exception> public AmortizationResponse Calculate(AmortizationRequest request) { if (request is null) { throw new ArgumentNullException(nameof(request), "Calculator request cannot be null."); } var ratePerPeriod = request.RatePerPeriod; var numberOfPeriods = request.NumberOfPeriods; var totalPrincipal = request.TotalPrincipal; var schedule = new AmortizationPeriod[numberOfPeriods]; Parallel.For(1, numberOfPeriods + 1, periodNumber => { var principalPayment = _financial.PrincipalPayment(ratePerPeriod, periodNumber, numberOfPeriods, -totalPrincipal); var interestPayment = _financial.InterestPayment(ratePerPeriod, periodNumber, numberOfPeriods, -totalPrincipal); var balanceLeft = ratePerPeriod == 0 ? totalPrincipal - (principalPayment * periodNumber) : _financial.InterestPayment(ratePerPeriod, periodNumber + 1, numberOfPeriods, -totalPrincipal) / ratePerPeriod; schedule[periodNumber - 1] = new AmortizationPeriod(periodNumber, interestPayment, principalPayment, balanceLeft); }); var paymentPerPeriod = _financial.Payment(ratePerPeriod, numberOfPeriods, -totalPrincipal); var totalInterestPaid = schedule.Sum(p => p.InterestPayment); var totalPaid = totalPrincipal + totalInterestPaid; return(new AmortizationResponse(paymentPerPeriod, totalInterestPaid, totalPaid, schedule, request)); }
internal AmortizationResponse(decimal paymentPerPeriod, decimal totalInterestPaid, decimal totalPaid, IReadOnlyList <AmortizationPeriod> schedule, AmortizationRequest request) { PaymentPerPeriod = paymentPerPeriod; TotalInterestPaid = totalInterestPaid; TotalPaid = totalPaid; Schedule = schedule; Request = request; }