Private
Public Access
1
0

Fix loan interest calculation to use daily accrual instead of rate/12

The amortization schedule and payoff calculator were using rate/12 for
monthly interest, which assumes 30.42 days per period and overestimates
interest. Switch to daily accrual (rate/365 * actual days) to match how
lenders actually compute interest between payment dates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Catherine Renelle
2026-02-14 16:52:50 -05:00
parent 08a0bcda3d
commit b7ef9e36ce
+13 -8
View File
@@ -113,11 +113,12 @@ public class LoanService : ILoanService
// Build new schedule with extra payments // Build new schedule with extra payments
var balance = Math.Abs(account.Balance); var balance = Math.Abs(account.Balance);
var monthlyRate = l.InterestRate / 100 / 12; var dailyRate = l.InterestRate / 100m / 365m;
var extraMonthly = request.ExtraMonthlyPayment ?? 0; var extraMonthly = request.ExtraMonthlyPayment ?? 0;
var basePayment = l.MonthlyPayment - (l.EscrowAmount ?? 0); var basePayment = l.MonthlyPayment - (l.EscrowAmount ?? 0);
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
var paymentDate = new DateTime(now.Year, now.Month, l.OriginationDate.Day, 0, 0, 0, DateTimeKind.Utc); var startDate = new DateTime(now.Year, now.Month, l.OriginationDate.Day, 0, 0, 0, DateTimeKind.Utc);
var previousDate = startDate;
var newEntries = new List<AmortizationEntryResponse>(); var newEntries = new List<AmortizationEntryResponse>();
int paymentNum = 0; int paymentNum = 0;
@@ -132,14 +133,16 @@ public class LoanService : ILoanService
while (balance > 0 && paymentNum < 600) while (balance > 0 && paymentNum < 600)
{ {
paymentNum++; paymentNum++;
paymentDate = paymentDate.AddMonths(1); var paymentDate = startDate.AddMonths(paymentNum);
var days = (paymentDate - previousDate).Days;
var interest = balance * monthlyRate; var interest = balance * dailyRate * days;
var principal = Math.Min(basePayment + extraMonthly - interest, balance); var principal = Math.Min(basePayment + extraMonthly - interest, balance);
if (principal < 0) principal = 0; if (principal < 0) principal = 0;
balance -= principal; balance -= principal;
if (balance < 0.01m) balance = 0; if (balance < 0.01m) balance = 0;
previousDate = paymentDate;
newEntries.Add(new AmortizationEntryResponse( newEntries.Add(new AmortizationEntryResponse(
paymentNum, paymentDate, principal + interest + (l.EscrowAmount ?? 0), paymentNum, paymentDate, principal + interest + (l.EscrowAmount ?? 0),
@@ -180,18 +183,20 @@ public class LoanService : ILoanService
{ {
var entries = new List<AmortizationEntry>(); var entries = new List<AmortizationEntry>();
var balance = loan.OriginalBalance; var balance = loan.OriginalBalance;
var monthlyRate = loan.InterestRate / 100 / 12; var dailyRate = loan.InterestRate / 100m / 365m;
var payment = loan.MonthlyPayment - (loan.EscrowAmount ?? 0); var payment = loan.MonthlyPayment - (loan.EscrowAmount ?? 0);
var extra = loan.ExtraPayment ?? 0; var extra = loan.ExtraPayment ?? 0;
var paymentDate = loan.OriginationDate; var previousDate = loan.OriginationDate;
for (int i = 1; i <= loan.TermMonths && balance > 0; i++) for (int i = 1; i <= loan.TermMonths && balance > 0; i++)
{ {
paymentDate = paymentDate.AddMonths(1); var paymentDate = loan.OriginationDate.AddMonths(i);
var interest = balance * monthlyRate; var days = (paymentDate - previousDate).Days;
var interest = balance * dailyRate * days;
var principal = Math.Min(payment + extra - interest, balance); var principal = Math.Min(payment + extra - interest, balance);
balance -= principal; balance -= principal;
if (balance < 0.01m) balance = 0; if (balance < 0.01m) balance = 0;
previousDate = paymentDate;
entries.Add(new AmortizationEntry entries.Add(new AmortizationEntry
{ {