Header menu logo FSharp.Finance.Personal

Amortisation FAQ

How is interest calculated?

Initial calculations

When setting up a schedule, interest is calculated based on a simple schedule that assumes that all payments will be made on time and in full.

Running calculations

While a schedule is active (i.e. it has started and the principal balance is non-zero), the interest is calculated based on the scheduled and actual payments made. The day of any scheduled or actual payment constitutes an event in the amortisation schedule, and simple interest is calculated based on the principal (+ fees) balance and the number of days since the last event.

For full details, see Amortisation Calculations.

Basis of calculations for the following questions

The basic schedule we will use here is a loan of £1000 advanced on 24 April 2025, paid back over 4 months starting one month after the advance date. The loan has a daily interest rate of 0.798% and a cap of 0.8% per day as well as a cap of 100% of the principal amount. The examples may use either the simple-interest method or the add-on-interest method.


Show/hide parameters
let parameters = {
    EvaluationDate = Date(2025, 4, 24) // the date that we're evaluating the schedule
    StartDate = Date(2025, 4, 24)
    Principal = 1000_00L<Cent>
    ScheduleConfig = AutoGenerateSchedule {
        UnitPeriodConfig = Monthly(1, 2025, 5, 24)
        ScheduleLength = PaymentCount 4
    }
    PaymentConfig = {
        LevelPaymentOption = LowerFinalPayment
        ScheduledPaymentOption = AsScheduled
        Rounding = RoundUp
        Minimum = DeferOrWriteOff 50L<Cent>
        Timeout = 3<DurationDay>
    }
    FeeConfig = None
    ChargeConfig = None
    InterestConfig = {
        Method = Interest.Method.Simple
        StandardRate = Interest.Rate.Daily (Percent 0.798m)
        Cap = {
            TotalAmount = Amount.Percentage (Percent 100m, Restriction.NoLimit)
            DailyAmount = Amount.Percentage (Percent 0.8m, Restriction.NoLimit)
        }
        InitialGracePeriod = 3<DurationDay>
        PromotionalRates = [||]
        RateOnNegativeBalance = Interest.Rate.Zero
        Rounding = RoundDown
        AprMethod = Apr.CalculationMethod.UnitedKingdom 3
    }
}

What happens if a customer were to not make their repayment on time?

Let's take a look at the amortisation schedules to illustrate this. First we will look at the simple-interest method and then the add-on-interest method.


Note: As a general principle, for payments that are not yet due it is assumed that they will be paid on time and in full. This is to provide for a more realistic projection of the schedule.

Simple Interest

Here's the schedule prior to any actual payments being made (looking at it from day 0). The main thing to note is that the principal balance at the end of the schedule is zero, meaning it is fully amortised.

Show/hide code
let amortisation0 =
    Amortisation.generate
        parameters //the parameters defined above
        SettlementDay.NoSettlement // no settlement quotation requested
        false // don't clip unrequired payments from the end of the schedule
        Map.empty // no actual payments made
Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 information only open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 0.0000 0.00 1,000.00 0.00
30 2025-05-24 n/a original 417.72 1 417.72 n/a n/a 417.72 not yet due open 239.4000 239.4000 n/a 178.32 0.00 239.40 0.00 0.00 821.68 0.00 0.0000 0.00 821.68 0.00
61 2025-06-24 n/a original 417.72 2 417.72 n/a n/a 417.72 not yet due open 203.2672 203.2672 n/a 214.46 0.00 203.26 0.00 0.00 607.22 0.00 0.0000 0.00 607.22 0.00
91 2025-07-24 n/a original 417.72 3 417.72 n/a n/a 417.72 not yet due open 145.3685 145.3685 n/a 272.36 0.00 145.36 0.00 0.00 334.86 0.00 0.0000 0.00 334.86 0.00
122 2025-08-24 n/a original 417.69 4 417.69 n/a n/a 417.69 not yet due closed 82.8377 82.8377 n/a 334.86 0.00 82.83 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

Now, let's assume that it's day 35 and no payments have been made, so the payment due on day 30 has been missed. The schedule would look like this:

Show/hide code
let amortisation1 =
    Amortisation.generate
        { parameters with
            EvaluationDate = Date(2025, 5, 29) // evaluate the schedule on day 35
        }
        SettlementDay.NoSettlement // no settlement quotation requested
        false // don't clip unrequired payments from the end of the schedule
        Map.empty // no actual payments made
Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 none scheduled open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 0.0000 0.00 1,000.00 0.00
30 2025-05-24 n/a original 417.72 1 417.72 n/a n/a 0.00 missed payment open 239.4000 239.4000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 239.4000 0.00 1,239.40 0.00
35 2025-05-29 n/a n/a 1 0.00 n/a n/a 0.00 information only open 39.9000 39.9000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 279.3000 0.00 1,279.30 0.00
61 2025-06-24 n/a original 417.72 2 417.72 n/a n/a 417.72 not yet due open 207.4800 207.4800 n/a 0.00 0.00 417.72 0.00 0.00 1,000.00 0.00 69.0600 0.00 1,069.06 0.00
91 2025-07-24 n/a original 417.72 3 417.72 n/a n/a 417.72 not yet due open 239.4000 239.4000 n/a 109.26 0.00 308.46 0.00 0.00 890.74 0.00 0.0000 0.00 890.74 0.00
122 2025-08-24 n/a original 417.69 4 417.69 n/a n/a 417.69 not yet due open 220.3513 220.3513 n/a 197.34 0.00 220.35 0.00 0.00 693.40 0.00 0.0000 0.00 693.40 0.00

Note: You can see that a new event at day 35 has been created, as we are observing the schedule on that day. This capitalises the interest that has accrued so far so that we can see the principal balance on that day.

As the payment due on day 30 has been missed, the interest that has accrued is not paid off and is added to the interest balance. This means that any subsequent payments would need to clear the interest balance before the principal balance is reduced, and this means that the principal balance remains higher for longer. Looking at the principal balance at the end of the schedule shows that this has not-insubstantial consequences, as there is still £693.40 outstanding.

Add-On Interest

Here's the schedule prior to any actual payments being made (looking at it from day 0). This time the interest is added up-front, with an initial interest balance of £815.56. The interest is paid off before the principal, so more interest is accrued in total than under the simple-interest method. The principal at the end of the schedule is zero, meaning it is fully amortised.

Show/hide code
let amortisation2 =
    Amortisation.generate
        { parameters with
            InterestConfig.Method = Interest.Method.AddOn // use the add-on interest method
        }
        SettlementDay.NoSettlement // no settlement quotation requested
        false // don't clip unrequired payments from the end of the schedule
        Map.empty // no actual payments made
Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 information only open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 816.5600 0.00 1,000.00 0.00
30 2025-05-24 n/a original 454.15 1 454.15 n/a n/a 454.15 not yet due open 239.4000 0.0000 n/a 0.00 0.00 454.15 0.00 0.00 1,000.00 0.00 362.4100 0.00 1,239.40 0.00
61 2025-06-24 n/a original 454.15 2 454.15 n/a n/a 454.15 not yet due open 247.3800 0.0000 n/a 91.74 0.00 362.41 0.00 0.00 908.26 0.00 0.0000 0.00 908.26 0.00
91 2025-07-24 n/a original 454.15 3 454.15 n/a n/a 454.15 not yet due open 217.4374 0.0000 n/a 454.15 0.00 0.00 0.00 0.00 454.11 0.00 0.0000 0.00 454.11 0.00
122 2025-08-24 n/a original 454.11 4 454.11 n/a n/a 454.11 not yet due closed 112.3377 0.0000 n/a 454.11 0.00 0.00 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

Let's again assume that it's day 35 and no payments have been made, so the payment due on day 30 has been missed. The schedule would look like this:

Show/hide code
let amortisation3 =
    Amortisation.generate
        { parameters with
            EvaluationDate = Date(2025, 5, 27) // evaluate the schedule on day 35
            InterestConfig.Method = Interest.Method.AddOn // use the add-on interest method
        }
        SettlementDay.NoSettlement // no settlement quotation requested
        false // don't clip unrequired payments from the end of the schedule
        Map.empty // no actual payments made
Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 none scheduled open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 816.5600 0.00 1,000.00 0.00
30 2025-05-24 n/a original 454.15 1 454.15 n/a n/a 454.15 payment due open 239.4000 0.0000 n/a 0.00 0.00 454.15 0.00 0.00 1,000.00 0.00 362.4100 0.00 1,239.40 0.00
33 2025-05-27 n/a n/a 1 0.00 n/a n/a 0.00 information only open 23.9400 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 362.4100 0.00 1,263.34 0.00
61 2025-06-24 n/a original 454.15 2 454.15 n/a n/a 454.15 not yet due open 223.4400 0.0000 n/a 91.74 0.00 362.41 0.00 0.00 908.26 0.00 0.0000 0.00 908.26 0.00
91 2025-07-24 n/a original 454.15 3 454.15 n/a n/a 454.15 not yet due open 217.4374 0.0000 n/a 454.15 0.00 0.00 0.00 0.00 454.11 0.00 0.0000 0.00 454.11 0.00
122 2025-08-24 n/a original 454.11 4 454.11 n/a n/a 454.11 not yet due closed 112.3377 0.0000 n/a 454.11 0.00 0.00 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

As the payment due on day 30 has been missed, the interest balance stays higher for longer, meaning that the principal balance remains higher for longer. This means that more interest is accrued in total than the initial interest balance. To correct for this, new interest of £134.30 is added to the final schedule item. The principal balance at the end of the schedule is therefore £588.45.


Why is the final settlement amount lower for the add-on method?

You may well wonder, given that the add-on-interest method results in a higher amount of interest accruing than simple-interest method, why the final settlement amount (i.e. the final principal balance) is lower for the add-on method.

One interesting feature of the add-on method is that in the early days of the schedule, missed payments have no effect on the principal balance. Looking at the original add-on schedule, we see that the principal balance remains at £1000 until day 30. Any extra interest is only accrued if the principal balance remains outstanding at this level for longer than this. Looking at the schedule on day 35, we see that the principal balance remains at £1000 until day 61, meaning that extra interest is accrued, but only for 31 days out of 60 compared to the simple-interest method.


Why is the new interest only added at the very end of the schedule?

During the schedule, actual-payment amounts and timings may vary, some of which may well have an effect on the total interest accrued. If we were to make constant adjustments to the interest balance, this would become difficult to track. If subsequent payments were paid earlier than due this could even lead to a situation where interest was overpaid and a refund might be required. By making the interest adjustment at the end, we can avoid this situation.

How is the additional interest calculated?

Simple Interest

Given that the interest rate is usually fixed for the duration of the schedule, the interest accrued is purely a function of the principal balance and how many days it is outstanding:

graph LR A["$$\text{interest} = \text{principal balance} \times \text{daily interest rate} \times \text{days}$$"]

Therefore any variations will affect the interest accrued:

Add-On Interest

Though the interest is calculated up-front and added to the schedule as an initial interest balance, adjustments will need to be made if the payment schedule is not adhered to. At the end of the schedule, the total simple interest (calculated as stated in the Simple Interest section above) is compared to the initial interest balance and a correction is made to the final schedule item if necessary.

Is the additional interest calculated on the outstanding loan principal?

Indirectly, yes. At the end of the schedule, the total simple interest (calculated as stated in the Simple Interest section above) is compared to the initial interest balance and a correction is made to the final schedule item if necessary.

Where is the additional interest added?

For simple-interest, the interest is automatically adjusted during the schedule. For add-on interest, the adjustment is made at the end of the schedule.

When is the customer expected to pay off this additional interest?

If additional interest has accrued, the effect of this will be that the schedule is not fully amortised, i.e. the final principal balance is not zero. Deciding how to handle this is outside the scope of the library, and depends on business rules. There are a number of ways, including:

Is there a set maximum amount of days that are used as a limit for additional interest to be charged?

No, caps on interest charges are not based on the number of days, but on the interest caps set in the loan parameters. Both daily and total caps can be set, and these are defined as either a simple amount or a percentage of the principal amount.

What happens when a customer settles earlier than the agreed term?

First we will look at the simple-interest method and then the add-on-interest method.

Simple Interest

As a reminder, here's the schedule prior to any actual payments being made (looking at it from day 0).

Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 information only open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 0.0000 0.00 1,000.00 0.00
30 2025-05-24 n/a original 417.72 1 417.72 n/a n/a 417.72 not yet due open 239.4000 239.4000 n/a 178.32 0.00 239.40 0.00 0.00 821.68 0.00 0.0000 0.00 821.68 0.00
61 2025-06-24 n/a original 417.72 2 417.72 n/a n/a 417.72 not yet due open 203.2672 203.2672 n/a 214.46 0.00 203.26 0.00 0.00 607.22 0.00 0.0000 0.00 607.22 0.00
91 2025-07-24 n/a original 417.72 3 417.72 n/a n/a 417.72 not yet due open 145.3685 145.3685 n/a 272.36 0.00 145.36 0.00 0.00 334.86 0.00 0.0000 0.00 334.86 0.00
122 2025-08-24 n/a original 417.69 4 417.69 n/a n/a 417.69 not yet due closed 82.8377 82.8377 n/a 334.86 0.00 82.83 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

Now, let's assume that the first two payments have been made on time, and the customer decides to repay in full on day 70. The schedule would look like this:

Show/hide code
let amortisation4 =
    Amortisation.generate
        { parameters with
            EvaluationDate = Date(2025, 7, 3) // evaluate the schedule on day 70
        }
        SettlementDay.SettlementOnEvaluationDay // settlement quotation requested on day 70
        false // don't clip unrequired payments from the end of the schedule
        (Map [
            30<OffsetDay>, [| ActualPayment.quickConfirmed 417_72L<Cent> |]
            61<OffsetDay>, [| ActualPayment.quickConfirmed 417_72L<Cent> |]
        ]) // actual payments made on days 30 and 61
Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 none scheduled open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 0.0000 0.00 1,000.00 0.00
30 2025-05-24 n/a original 417.72 1 417.72 confirmed 417.72 n/a 417.72 payment made open 239.4000 239.4000 n/a 178.32 0.00 239.40 0.00 0.00 821.68 0.00 0.0000 0.00 821.68 0.00
61 2025-06-24 n/a original 417.72 2 417.72 confirmed 417.72 n/a 417.72 payment made open 203.2672 203.2672 n/a 214.46 0.00 203.26 0.00 0.00 607.22 0.00 0.0000 0.00 607.22 0.00
70 2025-07-03 n/a n/a 2 0.00 n/a 650.83 650.83 generated closed 43.6105 43.6105 n/a 607.22 0.00 43.61 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00
91 2025-07-24 n/a original 417.72 3 0.00 n/a n/a 0.00 no longer required closed 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00
122 2025-08-24 n/a original 417.69 4 0.00 n/a n/a 0.00 no longer required closed 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

Note: You can see that a new event at day 70 has been created, as we are observing the schedule on that day, and have requested a settlement quotation on that day too.

The settlement quotation is £650.83, which is the sum required to pay off all outstanding balances, including any interest accrued up to that day. This fully amortises the schedule, and the remaining two payments on days 91 and 122 are no longer required. The total of the two payments is £835.41, meaning that the customer has saved £184.58 in interest.

Add-On Interest

As a reminder, here's the schedule prior to any actual payments being made (looking at it from day 0). This time the interest is added up-front, with an initial interest balance of £815.56.

Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 information only open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 816.5600 0.00 1,000.00 0.00
30 2025-05-24 n/a original 454.15 1 454.15 n/a n/a 454.15 not yet due open 239.4000 0.0000 n/a 0.00 0.00 454.15 0.00 0.00 1,000.00 0.00 362.4100 0.00 1,239.40 0.00
61 2025-06-24 n/a original 454.15 2 454.15 n/a n/a 454.15 not yet due open 247.3800 0.0000 n/a 91.74 0.00 362.41 0.00 0.00 908.26 0.00 0.0000 0.00 908.26 0.00
91 2025-07-24 n/a original 454.15 3 454.15 n/a n/a 454.15 not yet due open 217.4374 0.0000 n/a 454.15 0.00 0.00 0.00 0.00 454.11 0.00 0.0000 0.00 454.11 0.00
122 2025-08-24 n/a original 454.11 4 454.11 n/a n/a 454.11 not yet due closed 112.3377 0.0000 n/a 454.11 0.00 0.00 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

Let's assume again that the first two payments have been made on time, and the customer decides to repay in full on day 70. The schedule would look like this:

Show/hide code
let amortisation5 =
    Amortisation.generate
        { parameters with
            EvaluationDate = Date(2025, 7, 3) // evaluate the schedule on day 70
            InterestConfig.Method = Interest.Method.AddOn // use the add-on interest method
        }
        SettlementDay.SettlementOnEvaluationDay // settlement quotation requested on day 70
        false // don't clip unrequired payments from the end of the schedule
        (Map [
            30<OffsetDay>, [| ActualPayment.quickConfirmed 454_15L<Cent> |]
            61<OffsetDay>, [| ActualPayment.quickConfirmed 454_15L<Cent> |]
        ]) // actual payments made on days 30 and 61
Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 none scheduled open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 816.5600 0.00 1,000.00 0.00
30 2025-05-24 n/a original 454.15 1 454.15 confirmed 454.15 n/a 454.15 payment made open 239.4000 0.0000 n/a 0.00 0.00 454.15 0.00 0.00 1,000.00 0.00 362.4100 0.00 785.25 0.00
61 2025-06-24 n/a original 454.15 2 454.15 confirmed 454.15 n/a 454.15 payment made open 247.3800 0.0000 n/a 91.74 0.00 362.41 0.00 0.00 908.26 0.00 0.0000 0.00 578.48 0.00
70 2025-07-03 n/a n/a 2 0.00 n/a 643.71 643.71 generated closed 65.2312 -264.5488 n/a 908.26 0.00 -264.55 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00
91 2025-07-24 n/a original 454.15 3 0.00 n/a n/a 0.00 no longer required closed 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00
122 2025-08-24 n/a original 454.11 4 0.00 n/a n/a 0.00 no longer required closed 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

The settlement quotation is £643.71, which is the sum required to pay off all outstanding balances, including any interest accrued up to that day. As this is an early settlement, and the interest for the full schedule was charged up-front, an interest rebate of £264.55 has been calculated. The principal balance of £908.26, minus the interest rebate of £264.55, leaves a final settlement payment of £643.71 to pay. This would fully amortise the schedule, and the remaining two payments on days 91 and 122 are no longer required. In contrast to the simple-interest method, where you have to add up the unrequired payments and deduct the settlement figure to calculate the saved interest, in the add-on-interest method the interest rebate is explicitly calculated.

Is overcharged interest refunded?

As shown above, if a customer settles early and has overpaid interest, this is rebated as part of the settlement quotation. It would be termed a rebate rather than a refund, as the settlement quotation is generally a positive amount and so is a net flow from the customer to the lender.

The exception to this is if the customer has somehow managed to overpay, i.e. to pay an amount greater than the settlement figure. In this case, the lender would need to refund the customer the difference between the settlement figure and the amount paid. This is illustrated in the example below:

Show/hide code
let amortisation6 =
    Amortisation.generate
        { parameters with
            EvaluationDate = Date(2025, 4, 29) // evaluate the schedule on day 5
            InterestConfig.Method = Interest.Method.AddOn // use the add-on interest method
        }
        SettlementDay.NoSettlement // no settlement quotation requested
        true // clip unrequired payments from the end of the schedule
        (Map [
            5<OffsetDay>, [| ActualPayment.quickConfirmed 1050_00L<Cent> |]
        ]) // single overpayment made on day 5
Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 none scheduled open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 816.5600 0.00 1,000.00 0.00
5 2025-04-29 n/a n/a 0 0.00 confirmed 1,050.00 n/a 1,050.00 extra payment refund due 39.9000 -776.6600 n/a 1,010.10 0.00 39.90 0.00 0.00 -10.10 0.00 0.0000 0.00 -10.10 0.00

Here, the customer has paid £1050.00 on day 5, which is greater than the settlement figure of £1039.90. The lender would need to refund the customer the difference of £10.10. This is a rare case, as the lender would normally expect to receive the settlement figure and not more than this.

Can customers make partial payments? Can the customer make a payment of any amount towards the amount owed?

Yes, this is possible, though it may increase the total interest accrued. The schedule will automatically calculate the balances, but if the schedule is not fully amortised, the customer will need to pay off the remaining balance at some point in the future.

What is the minimum partial payment a customer can make?

There is no minimum partial payment, but payment processors may have a minimum amount that they will accept.

How does refinancing work?

There are two types of refinancing:

Note: both of these options may be limited by regulatory requirements, particularly in relation to responsible lending. There may be limits on the number of times a loan can be refinanced, and the amount of interest that can be charged. This is outside the scope of this library.

Let's take a look at these two options in more detail.

For both of these scenarios, we will take the same example as above, where a customer has taken a loan of £1000 advanced on 24 April 2025, paid back over 4 months starting one month after the advance date. The loan has a daily interest rate of 0.798% and a cap of 0.8% per day as well as a cap of 100% of the principal amount. We'll use the add-on interest method, though the rescheduling/rollover functions work identically for both methods, just the interest amounts are different. The customer already paid the first two payments on time, but missed the remaining two payments. The customer has requested refinancing on day 152.

Show/hide code

let refinanceExampleParameters =
    { parameters with
        EvaluationDate = Date(2025, 9, 23) // evaluate the schedule on day 152
        InterestConfig.Method = Interest.Method.AddOn // use the add-on interest method
    }
let actualPayments =
    Map [
        30<OffsetDay>, [| ActualPayment.quickConfirmed 454_15L<Cent> |]
        61<OffsetDay>, [| ActualPayment.quickConfirmed 454_15L<Cent> |]
    ] // actual payments made on days 30 and 61
let refinanceExampleSchedule =
    Amortisation.generate
        refinanceExampleParameters
        SettlementDay.SettlementOnEvaluationDay // settlement quotation requested on day 152
        false // don't clip unrequired payments from the end of the schedule
        actualPayments

Here is the status of the schedule on day 152 prior to any refinancing, where a settlement quotation has been requested so the interest is capitalised:

Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 none scheduled open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 816.5600 0.00 1,000.00 0.00
30 2025-05-24 n/a original 454.15 1 454.15 confirmed 454.15 n/a 454.15 payment made open 239.4000 0.0000 n/a 0.00 0.00 454.15 0.00 0.00 1,000.00 0.00 362.4100 0.00 785.25 0.00
61 2025-06-24 n/a original 454.15 2 454.15 confirmed 454.15 n/a 454.15 payment made open 247.3800 0.0000 n/a 91.74 0.00 362.41 0.00 0.00 908.26 0.00 0.0000 0.00 578.48 0.00
91 2025-07-24 n/a original 454.15 3 454.15 n/a n/a 0.00 missed payment open 217.4374 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 908.26 0.00 0.0000 0.00 795.91 0.00
122 2025-08-24 n/a original 454.11 4 454.11 n/a n/a 0.00 paid later in full open 224.6854 112.3428 n/a 0.00 0.00 0.00 0.00 0.00 908.26 0.00 112.3428 0.00 1,020.60 0.00
152 2025-09-23 n/a n/a 4 0.00 n/a 1,091.70 1,091.70 generated closed 71.0972 71.0972 n/a 908.26 0.00 183.44 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

Rescheduling

Here, the customer has agreed to pay £50 per week from 1 October 2025.

Show/hide code
let rescheduleParameters : RescheduleParameters = {
    FeeSettlementRebate = Fee.SettlementRebate.Zero // no fees, so irrelevant
    PaymentSchedule =
        FixedSchedules [|
            {
                UnitPeriodConfig = Weekly(1, Date(2025, 10, 1)) // weekly payments starting on 1 October 2025
                PaymentCount = 100 // more than enough payments to cover the schedule (this will be automatically curtailed)
                PaymentValue = 50_00L<Cent> // £50 per week
                ScheduleType = ScheduleType.Rescheduled 152<OffsetDay> // indicate that rescheduling was requested on day 152
            }
        |]
    RateOnNegativeBalance = Interest.Rate.Zero // no negative balance, so irrelevant
    PromotionalInterestRates = [||] // no promotional rates
    SettlementDay = SettlementDay.NoSettlement //no settlement requested, just generate a statement
}
let rescheduleSchedules = reschedule refinanceExampleParameters rescheduleParameters actualPayments

The rescheduled amortisation is as follows:

Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 none scheduled open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 0.0000 0.00 1,000.00 0.00
30 2025-05-24 n/a original 454.15 1 454.15 confirmed 454.15 n/a 454.15 payment made open 239.4000 239.4000 n/a 214.75 0.00 239.40 0.00 0.00 785.25 0.00 0.0000 0.00 785.25 0.00
61 2025-06-24 n/a original 454.15 2 454.15 confirmed 454.15 n/a 454.15 payment made open 194.2551 194.2551 n/a 259.90 0.00 194.25 0.00 0.00 525.35 0.00 0.0000 0.00 525.35 0.00
91 2025-07-24 n/a original 454.15 3 454.15 n/a n/a 0.00 missed payment open 125.7688 125.7688 n/a 0.00 0.00 0.00 0.00 0.00 525.35 0.00 125.7688 0.00 651.11 0.00
122 2025-08-24 n/a original 454.11 4 454.11 n/a n/a 0.00 missed payment open 129.9611 129.9611 n/a 0.00 0.00 0.00 0.00 0.00 525.35 0.00 255.7299 0.00 781.07 0.00
152 2025-09-23 n/a n/a 4 0.00 n/a n/a 0.00 information only open 125.7688 125.7688 n/a 0.00 0.00 0.00 0.00 0.00 525.35 0.00 381.4987 0.00 906.84 0.00
160 2025-10-01 n/a rescheduled 50.00 5 50.00 n/a n/a 50.00 not yet due open 33.5383 33.5383 n/a 0.00 0.00 50.00 0.00 0.00 525.35 0.00 365.0370 0.00 890.38 0.00
167 2025-10-08 n/a rescheduled 50.00 6 50.00 n/a n/a 50.00 not yet due open 29.3461 29.3461 n/a 0.00 0.00 50.00 0.00 0.00 525.35 0.00 344.3831 0.00 869.73 0.00
174 2025-10-15 n/a rescheduled 50.00 7 50.00 n/a n/a 50.00 not yet due open 29.3461 29.3461 n/a 0.00 0.00 50.00 0.00 0.00 525.35 0.00 323.7291 0.00 849.07 0.00
181 2025-10-22 n/a rescheduled 50.00 8 50.00 n/a n/a 50.00 not yet due open 29.3461 29.3461 n/a 0.00 0.00 50.00 0.00 0.00 525.35 0.00 303.0752 0.00 828.42 0.00
188 2025-10-29 n/a rescheduled 50.00 9 50.00 n/a n/a 50.00 not yet due open 29.3461 29.3461 n/a 0.00 0.00 50.00 0.00 0.00 525.35 0.00 282.4212 0.00 807.77 0.00
195 2025-11-05 n/a rescheduled 50.00 10 50.00 n/a n/a 50.00 not yet due open 29.3461 29.3461 n/a 0.00 0.00 50.00 0.00 0.00 525.35 0.00 261.7673 0.00 787.11 0.00
202 2025-11-12 n/a rescheduled 50.00 11 50.00 n/a n/a 50.00 not yet due open 4.5776 4.5776 n/a 0.00 0.00 50.00 0.00 0.00 525.35 0.00 216.3449 0.00 741.69 0.00
209 2025-11-19 n/a rescheduled 50.00 12 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 0.00 0.00 50.00 0.00 0.00 525.35 0.00 166.3449 0.00 691.69 0.00
216 2025-11-26 n/a rescheduled 50.00 13 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 0.00 0.00 50.00 0.00 0.00 525.35 0.00 116.3449 0.00 641.69 0.00
223 2025-12-03 n/a rescheduled 50.00 14 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 0.00 0.00 50.00 0.00 0.00 525.35 0.00 66.3449 0.00 591.69 0.00
230 2025-12-10 n/a rescheduled 50.00 15 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 0.00 0.00 50.00 0.00 0.00 525.35 0.00 16.3449 0.00 541.69 0.00
237 2025-12-17 n/a rescheduled 50.00 16 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 33.66 0.00 16.34 0.00 0.00 491.69 0.00 0.0000 0.00 491.69 0.00
244 2025-12-24 n/a rescheduled 50.00 17 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 50.00 0.00 0.00 0.00 0.00 441.69 0.00 0.0000 0.00 441.69 0.00
251 2025-12-31 n/a rescheduled 50.00 18 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 50.00 0.00 0.00 0.00 0.00 391.69 0.00 0.0000 0.00 391.69 0.00
258 2026-01-07 n/a rescheduled 50.00 19 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 50.00 0.00 0.00 0.00 0.00 341.69 0.00 0.0000 0.00 341.69 0.00
265 2026-01-14 n/a rescheduled 50.00 20 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 50.00 0.00 0.00 0.00 0.00 291.69 0.00 0.0000 0.00 291.69 0.00
272 2026-01-21 n/a rescheduled 50.00 21 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 50.00 0.00 0.00 0.00 0.00 241.69 0.00 0.0000 0.00 241.69 0.00
279 2026-01-28 n/a rescheduled 50.00 22 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 50.00 0.00 0.00 0.00 0.00 191.69 0.00 0.0000 0.00 191.69 0.00
286 2026-02-04 n/a rescheduled 50.00 23 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 50.00 0.00 0.00 0.00 0.00 141.69 0.00 0.0000 0.00 141.69 0.00
293 2026-02-11 n/a rescheduled 50.00 24 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 50.00 0.00 0.00 0.00 0.00 91.69 0.00 0.0000 0.00 91.69 0.00
300 2026-02-18 n/a rescheduled 50.00 25 50.00 n/a n/a 50.00 not yet due open 0.0000 0.0000 n/a 50.00 0.00 0.00 0.00 0.00 41.69 0.00 0.0000 0.00 41.69 0.00
307 2026-02-25 n/a rescheduled 50.00 26 41.69 n/a n/a 41.69 not yet due closed 0.0000 0.0000 n/a 41.69 0.00 0.00 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

There are some interesting points to note:

  • The first rescheduled payments, from day 160 to day 237, are paying off the interest balance, and only then does the principal balance start to be paid off.
  • On day 202, the new interest drops significantly and falls to zero on all subsequent days. This is because the 100% interest cap has been reached.

Rollover

Here, the customer has agreed to roll over the loan to a new 8-month loan, starting on 1 October 2025.

Show/hide code
let originalFinalPaymentDay = // get the final payment day from the original schedule
    refinanceExampleSchedule.AmortisationSchedule.ScheduleItems
    |> Map.filter (fun _ si -> ScheduledPayment.isSome si.ScheduledPayment)
    |> Map.maxKeyValue
    |> fst
let rolloverParameters : RolloverParameters = {
    OriginalFinalPaymentDay = originalFinalPaymentDay
    PaymentSchedule =
        AutoGenerateSchedule {
            UnitPeriodConfig = Monthly(1, 2025, 10, 1) // monthly payments starting on 1 October 2025
            ScheduleLength = PaymentCount 8 // 8 payments
        }
    InterestConfig = refinanceExampleParameters.InterestConfig // use the same interest config as the original schedule
    PaymentConfig = refinanceExampleParameters.PaymentConfig // use the same payment config as the original schedule
    FeeHandling = Fee.FeeHandling.CarryOverAsIs // no fees, so irrelevant
}
let rolloverSchedules = rollOver refinanceExampleParameters rolloverParameters actualPayments

The old loan would be closed as per the settlement quote above. The new rolled-over loan amortisation schedule is as follows:

Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-09-23 1,091.70 n/a 0 0.00 n/a n/a 0.00 information only open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,091.70 0.00 1,091.7000 0.00 1,091.70 0.00
8 2025-10-01 n/a original 272.93 1 272.93 n/a n/a 272.93 not yet due open 69.6941 0.0000 n/a 0.00 0.00 272.93 0.00 0.00 1,091.70 0.00 818.7700 0.00 1,161.39 0.00
39 2025-11-01 n/a original 272.93 2 272.93 n/a n/a 272.93 not yet due open 270.0647 0.0000 n/a 0.00 0.00 272.93 0.00 0.00 1,091.70 0.00 545.8400 0.00 1,431.45 0.00
69 2025-12-01 n/a original 272.93 3 272.93 n/a n/a 272.93 not yet due open 261.3530 0.0000 n/a 0.00 0.00 272.93 0.00 0.00 1,091.70 0.00 272.9100 0.00 1,364.61 0.00
100 2026-01-01 n/a original 272.93 4 272.93 n/a n/a 272.93 not yet due open 270.0647 0.0000 n/a 0.02 0.00 272.91 0.00 0.00 1,091.68 0.00 0.0000 0.00 1,091.68 0.00
131 2026-02-01 n/a original 272.93 5 272.93 n/a n/a 272.93 not yet due open 220.5234 0.0000 n/a 272.93 0.00 0.00 0.00 0.00 818.75 0.00 0.0000 0.00 818.75 0.00
159 2026-03-01 n/a original 272.93 6 272.93 n/a n/a 272.93 not yet due open 0.0000 0.0000 n/a 272.93 0.00 0.00 0.00 0.00 545.82 0.00 0.0000 0.00 545.82 0.00
190 2026-04-01 n/a original 272.93 7 272.93 n/a n/a 272.93 not yet due open 0.0000 0.0000 n/a 272.93 0.00 0.00 0.00 0.00 272.89 0.00 0.0000 0.00 272.89 0.00
220 2026-05-01 n/a original 272.89 8 272.89 n/a n/a 272.89 not yet due closed 0.0000 0.0000 n/a 272.89 0.00 0.00 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

There are some interesting points to note:

  • The settlement figure is £1091.70, which is greater than the original loan amount due to the capitalised interest.
  • By comparison, the settlement figure on day 152 when rescheduling (rather than rolling over) is only £908.64, because the interest is not capitalised.
  • On day 131 of the new loan, you can see that the simple interest hits the 100% total cap, and interest is no longer accrued from that point on.

What happens to the payment amounts?

Payment amounts can be:

What happens to the loan term?

Depending on the payment schedule, the loan term will either be amortised over a set number of payments, or the number of payments will be determined by how long it takes to amortise the schedule based on the payment amount.

If forbearance is offered, how is this calculated?

The schedule can be partially or fully paid off by using write-off payments.

Let's take our simple-interest loan, where the customer has already made the first two payments on time.

Single-payment write-off

Show/hide code
let amortisation7 =
    Amortisation.generate
        { parameters with
            EvaluationDate = Date(2025, 7, 3) // evaluate the schedule on day 70
        }
        SettlementDay.NoSettlement // no settlement quotation requested
        false // don't clip unrequired payments from the end of the schedule
        (Map [
            30<OffsetDay>, [| ActualPayment.quickConfirmed 417_72L<Cent> |]
            61<OffsetDay>, [| ActualPayment.quickConfirmed 417_72L<Cent> |]
            91<OffsetDay>, [| ActualPayment.quickWriteOff 417_72L<Cent> |]
        ]) // actual payments made on days 30 and 61, and a single-payment write-off on day 91
Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 none scheduled open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 0.0000 0.00 1,000.00 0.00
30 2025-05-24 n/a original 417.72 1 417.72 confirmed 417.72 n/a 417.72 payment made open 239.4000 239.4000 n/a 178.32 0.00 239.40 0.00 0.00 821.68 0.00 0.0000 0.00 821.68 0.00
61 2025-06-24 n/a original 417.72 2 417.72 confirmed 417.72 n/a 417.72 payment made open 203.2672 203.2672 n/a 214.46 0.00 203.26 0.00 0.00 607.22 0.00 0.0000 0.00 607.22 0.00
70 2025-07-03 n/a n/a 2 0.00 n/a n/a 0.00 information only open 43.6105 43.6105 n/a 0.00 0.00 0.00 0.00 0.00 607.22 0.00 43.6105 0.00 650.83 0.00
91 2025-07-24 n/a original 417.72 3 417.72 write-off 417.72 n/a 417.72 not yet due open 101.7579 101.7579 n/a 272.36 0.00 145.36 0.00 0.00 334.86 0.00 0.0000 0.00 334.86 0.00
122 2025-08-24 n/a original 417.69 4 417.69 n/a n/a 417.69 not yet due closed 82.8377 82.8377 n/a 334.86 0.00 82.83 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

You can see that the single-payment write-off has no effect on the remainder of the schedule, and the remaining payment is still due on day 122.

Full write-off

Show/hide code
// first, run the amortisation with the existing actual payments to get the settlement figure
let amortisation8 =
    Amortisation.generate
        { parameters with
            EvaluationDate = Date(2025, 7, 3) // evaluate the schedule on day 70
        }
        (SettlementDay.SettlementOn 91<OffsetDay>) // settlement quotation requested on day 91
        false // don't clip unrequired payments from the end of the schedule
        (Map [
            30<OffsetDay>, [| ActualPayment.quickConfirmed 417_72L<Cent> |]
            61<OffsetDay>, [| ActualPayment.quickConfirmed 417_72L<Cent> |]
        ]) // actual payments made on days 30 and 61, and a single-payment write-off on day 91
// get the generated settlement figure
let settlementFigure = amortisation8.AmortisationSchedule.FinalStats.SettlementFigure
// use the settlement figure as the full write-off amount
let fullWriteOffAmount = settlementFigure |> ValueOption.map snd |> ValueOption.defaultValue 0L<Cent>
// run the amortisation again with the full write-off payment
let amortisation8' =
    Amortisation.generate
        { parameters with
            EvaluationDate = Date(2025, 7, 3) // evaluate the schedule on day 70
        }
        (SettlementDay.SettlementOn 91<OffsetDay>) // settlement quotation requested on day 91
        false // don't clip unrequired payments from the end of the schedule
        (Map [
            30<OffsetDay>, [| ActualPayment.quickConfirmed 417_72L<Cent> |]
            61<OffsetDay>, [| ActualPayment.quickConfirmed 417_72L<Cent> |]
            91<OffsetDay>, [| ActualPayment.quickWriteOff fullWriteOffAmount |]
        ]) // actual payments made on days 30 and 61, and a full write-off on day 91
Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 none scheduled open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 0.0000 0.00 1,000.00 0.00
30 2025-05-24 n/a original 417.72 1 417.72 confirmed 417.72 n/a 417.72 payment made open 239.4000 239.4000 n/a 178.32 0.00 239.40 0.00 0.00 821.68 0.00 0.0000 0.00 821.68 0.00
61 2025-06-24 n/a original 417.72 2 417.72 confirmed 417.72 n/a 417.72 payment made open 203.2672 203.2672 n/a 214.46 0.00 203.26 0.00 0.00 607.22 0.00 0.0000 0.00 607.22 0.00
91 2025-07-24 n/a original 417.72 3 417.72 write-off 752.58 752.58 1,170.30 generated closed 145.3685 145.3685 n/a 607.22 0.00 145.36 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00
122 2025-08-24 n/a original 417.69 4 0.00 n/a n/a 0.00 no longer required closed 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

You can see that a settlement payment of £752.58 was determined on day 91, and this is used as the write-off amount. This amount is less than the remaining two payments (£417.72 + £417.69 = £835.41) as this is effectively an early settlement and so less interest is accrued. The remaining payment on day 122 is no longer required as the schedule is fully amortised.

Can interest be frozen for a period of time?

Yes, the library supports promotional interest rates, essentially ranges of dates during which a different interest rate is applied. To freeze interest, these can be used with the interest rate set to zero. This is illustrated in the example below:

Show/hide code
// first, run the amortisation with the existing actual payments to get the settlement figure
let amortisation9 =
    Amortisation.generate
        { parameters with
            InterestConfig.PromotionalRates = [|
                { // promotional rate to freeze interest for a month
                    DateRange = { Start = Date(2025, 6, 25); End = Date(2025, 7, 24) }
                    Rate = Interest.Rate.Zero // zero interest rate
                }
            |]
        }
        SettlementDay.NoSettlement // no settlement quotation requested
        false // don't clip unrequired payments from the end of the schedule
        Map.empty // no actual payments made
Day Datestamp Advances Scheduled payment Window Payment due Actual payments Generated payment Net effect Payment status Balance status Simple interest New interest New charges Principal portion Fee portion Interest portion Charges portion Fee rebate Principal balance Fee balance Interest balance Charges balance Settlement figure Fee rebate if settled
0 2025-04-24 1,000.00 n/a 0 0.00 n/a n/a 0.00 information only open 0.0000 0.0000 n/a 0.00 0.00 0.00 0.00 0.00 1,000.00 0.00 0.0000 0.00 1,000.00 0.00
30 2025-05-24 n/a original 381.82 1 381.82 n/a n/a 381.82 not yet due open 239.4000 239.4000 n/a 142.42 0.00 239.40 0.00 0.00 857.58 0.00 0.0000 0.00 857.58 0.00
61 2025-06-24 n/a original 381.82 2 381.82 n/a n/a 381.82 not yet due open 212.1481 212.1481 n/a 169.68 0.00 212.14 0.00 0.00 687.90 0.00 0.0000 0.00 687.90 0.00
91 2025-07-24 n/a original 381.82 3 381.82 n/a n/a 381.82 not yet due open 0.0000 0.0000 n/a 381.82 0.00 0.00 0.00 0.00 306.08 0.00 0.0000 0.00 306.08 0.00
122 2025-08-24 n/a original 381.79 4 381.79 n/a n/a 381.79 not yet due closed 75.7181 75.7181 n/a 306.08 0.00 75.71 0.00 0.00 0.00 0.00 0.0000 0.00 0.00 0.00

You can see that the interest accrued over the month up to the payment on day 91 is zero. The scheduled payments are lowered to account for this as we've set this up from the start of the loan.

It would be equally possible to set this up after a schedule has started, but depending on the actual promotional rates applied, this might end up requiring a rebate of existing interest paid or even a refund to the customer if it meant that existing payments exceeded the revised settlement figure. The library would automatically calculate this, but it would be up to the lender to decide how to handle it.

When does accrued interest start?

Accrued interest starts from the first day of the schedule, i.e. the advance date.

The parameters used in these examples have a 3-day initial grace period, meaning that if the customer pays back the principal amount within 3 days of the advance date, no interest is charged. If they wanted to settle the schedule after this date, interest would be charged from the advance date.

If there were no initial grace period, a customer could pay back the principal amount on the advance date and no interest would be charged. If they wanted to settle the schedule after this date, interest would be charged from the advance date. So if they wanted to settle the schedule on day 1, one day's interest would be charged.

Do we allow up until 11:59pm for payment to be made on due date of the scheduled repayment?

The library does not have a concept of time, so all dates are treated as whole days. It is up to the lender to decide how to handle this, e.g. deciding on what day to record any payment as having been made.

The parameters used in these examples have a 3-day payment timeout, allowing us to mark payments as pending for up to 3 days after the scheduled payment date. This means that if a payment is made on day 30, it will be marked as pending until day 33. If the payment is not made by then, it will be marked as missed.

Can a customer delay one payment and keep all subsequent payments the same?

This kind of scenario could be handled in a number of ways:

Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Finance
namespace FSharp.Finance.Personal
module Calculation from FSharp.Finance.Personal
<summary> convenience functions and options to help with calculations </summary>
module DateDay from FSharp.Finance.Personal
<summary> a .NET Framework polyfill equivalent to the DateOnly structure in .NET Core </summary>
module Scheduling from FSharp.Finance.Personal
<summary> functions for generating a regular payment schedule, with payment amounts, interest and APR </summary>
module UnitPeriod from FSharp.Finance.Personal
<summary> an unambiguous way to represent regular date intervals and generate schedules based on them note: unit-period definitions are based on US federal legislation but the definitions are universally applicable </summary>
val parameters: Parameters
Multiple items
[<Struct>] type Date = new: year: int * month: int * day: int -> Date val Year: int val Month: int val Day: int member AddDays: i: int -> Date member AddMonths: i: int -> Date member AddYears: i: int -> Date member ToDateTime: unit -> DateTime static member (-) : d1: Date * d2: Date -> TimeSpan static member DaysInMonth: year: int * month: int -> int ...
<summary> the date at the customer's location - ensure any time-zone conversion is performed before using this - as all calculations are date-only with no time component, summer time or other such time artefacts </summary>

--------------------
Date ()
new: year: int * month: int * day: int -> Date
Multiple items
module Cent from FSharp.Finance.Personal.Calculation
<summary> utility functions for base currency unit values </summary>

--------------------
[<Measure>] type Cent
<summary> the base unit of a currency (cent, penny, øre etc.) </summary>
Multiple items
module ScheduleConfig from FSharp.Finance.Personal.Scheduling
<summary> whether a payment plan is generated according to a regular schedule or is an irregular array of payments </summary>

--------------------
[<Struct>] type ScheduleConfig = | AutoGenerateSchedule of AutoGenerateSchedule: AutoGenerateSchedule | FixedSchedules of FixedSchedules: FixedSchedule array | CustomSchedule of CustomSchedule: Map<int<OffsetDay>,ScheduledPayment>
<summary> whether a payment plan is generated according to a regular schedule or is an irregular array of payments </summary>
Multiple items
union case ScheduleConfig.AutoGenerateSchedule: AutoGenerateSchedule: AutoGenerateSchedule -> ScheduleConfig
<summary> a schedule based on a unit-period config with a specific number of payments with an auto-calculated amount, optionally limited to a maximum duration </summary>

--------------------
[<Struct>] type AutoGenerateSchedule = { UnitPeriodConfig: Config ScheduleLength: ScheduleLength }
<summary> a regular schedule based on a unit-period config with a specific number of payments with an auto-calculated amount </summary>
union case Config.Monthly: MonthMultiple: int * Year: int * Month: int * Day: int -> Config
<summary> (multi-)monthly: every n months starting on the date given by year, month and day, which tracks month-end (see config) </summary>
[<Struct>] type ScheduleLength = | PaymentCount of Payments: int | MaxDuration of Days: int<DurationDay> member Html: string with get
<summary> defines the length of a payment schedule, either by the number of payments or by the maximum duration </summary>
union case ScheduleLength.PaymentCount: Payments: int -> ScheduleLength
Multiple items
module PaymentConfig from FSharp.Finance.Personal.Scheduling
<summary> how to treat scheduled payments </summary>

--------------------
type PaymentConfig = { LevelPaymentOption: LevelPaymentOption ScheduledPaymentOption: ScheduledPaymentOption Rounding: Rounding Minimum: MinimumPayment Timeout: int<DurationDay> }
<summary> how to treat scheduled payments </summary>
Multiple items
module LevelPaymentOption from FSharp.Finance.Personal.Scheduling
<summary> when calculating the level payments, whether the final payment should be lower or higher than the level payment </summary>

--------------------
[<Struct>] type LevelPaymentOption = | LowerFinalPayment | SimilarFinalPayment | HigherFinalPayment member Html: string with get
<summary> when calculating the level payments, whether the final payment should be lower or higher than the level payment </summary>
union case LevelPaymentOption.LowerFinalPayment: LevelPaymentOption
<summary> the final payment must be lower than the level payment </summary>
[<Struct>] type ScheduledPaymentOption = | AsScheduled | AddChargesAndInterest member Html: string with get
<summary> whether to stick to scheduled payment amounts or add charges and interest to them </summary>
union case ScheduledPaymentOption.AsScheduled: ScheduledPaymentOption
<summary> keep to the scheduled payment amounts even if this results in an open balance </summary>
Multiple items
module Rounding from FSharp.Finance.Personal.Calculation
<summary> the type of rounding, specifying midpoint-rounding where necessary </summary>

--------------------
[<Struct>] type Rounding = | NoRounding | RoundUp | RoundDown | RoundWith of MidpointRounding member Html: string with get
<summary> the type of rounding, specifying midpoint-rounding where necessary </summary>
union case Rounding.RoundUp: Rounding
<summary> round up to the specified precision (= ceiling) </summary>
union case MinimumPayment.DeferOrWriteOff: DeferOrWriteOff: int64<Cent> -> MinimumPayment
<summary> add the payment due to the next payment or close the balance if the final payment </summary>
[<Measure>] type DurationDay
<summary> a duration of a number of days </summary>
union case Option.None: Option<'T>
module Interest from FSharp.Finance.Personal
<summary> methods for calculating interest and unambiguously expressing interest rates, as well as enforcing regulatory caps on interest chargeable </summary>
[<Struct>] type Method = | Simple | AddOn member Html: string with get
<summary> the method used to calculate the interest </summary>
union case Interest.Method.Simple: Interest.Method
<summary> simple interest method, where interest is based on the principal balance and the number of days outstanding </summary>
Multiple items
module Rate from FSharp.Finance.Personal.Interest

--------------------
[<Struct>] type Rate = | Zero | Annual of Annual: Percent | Daily of Daily: Percent member Html: string with get
<summary> the interest rate expressed as either an annual or a daily rate </summary>
union case Interest.Rate.Daily: Daily: Percent -> Interest.Rate
<summary> the daily interest rate, or the annual interest rate divided by 365 </summary>
Multiple items
union case Percent.Percent: decimal -> Percent

--------------------
module Percent from FSharp.Finance.Personal.Calculation
<summary> utility functions for percent values </summary>

--------------------
[<Struct>] type Percent = | Percent of decimal member Html: string with get
<summary> a percentage, e.g. 42%, as opposed to its decimal representation 0.42m </summary>
Multiple items
module Amount from FSharp.Finance.Personal.Calculation
<summary> an amount specified either as a simple amount or as a percentage of another amount, optionally restricted to lower and/or upper limits </summary>

--------------------
[<Struct>] type Amount = | Percentage of Percentage: Percent * Restriction: Restriction | Simple of Simple: int64<Cent> | Unlimited member Html: string with get
<summary> an amount specified either as a simple amount or as a percentage of another amount, optionally restricted to lower and/or upper limits </summary>
union case Amount.Percentage: Percentage: Percent * Restriction: Restriction -> Amount
<summary> a percentage of the principal, optionally restricted </summary>
Multiple items
module Restriction from FSharp.Finance.Personal.Calculation
<summary> the type of restriction placed on a possible value </summary>

--------------------
[<Struct>] type Restriction = | NoLimit | LowerLimit of LowerLimit: int64<Cent> | UpperLimit of UpperLimit: int64<Cent> | WithinRange of MinValue: int64<Cent> * MaxValue: int64<Cent> member Html: string with get
<summary> the type of restriction placed on a possible value </summary>
union case Restriction.NoLimit: Restriction
<summary> does not constrain values at all </summary>
union case Interest.Rate.Zero: Interest.Rate
<summary> a zero rate </summary>
union case Rounding.RoundDown: Rounding
<summary> round down to the specified precision (= floor) </summary>
module Apr from FSharp.Finance.Personal
<summary> calculating the APR according to various country-specific regulations </summary>
[<Struct>] type CalculationMethod = | UnitedKingdom of UkPrecision: int | UsActuarial of UsPrecision: int | UnitedStatesRule member Html: string with get
<summary> the calculation method used to determine the APR </summary>
union case Apr.CalculationMethod.UnitedKingdom: UkPrecision: int -> Apr.CalculationMethod
<summary> calculates the APR according to UK FCA rules to the stated decimal precision (note that this is two places more than the percent precision) </summary>
val amortisation0: Amortisation.GenerationResult
module Amortisation from FSharp.Finance.Personal
<summary> calculating the principal balance over time, taking into account the effects of charges, interest and fee </summary>
val generate: sp: Parameters -> settlementDay: SettlementDay -> trimEnd: bool -> actualPayments: Map<int<OffsetDay>,ActualPayment array> -> Amortisation.GenerationResult
<summary> generates an amortisation schedule and final statistics </summary>
[<Struct>] type SettlementDay = | SettlementOn of SettlementDay: int<OffsetDay> | SettlementOnEvaluationDay | NoSettlement
<summary> the intended day on which to quote a settlement </summary>
union case SettlementDay.NoSettlement: SettlementDay
<summary> no settlement figure is required </summary>
Multiple items
module Map from FSharp.Finance.Personal.Calculation
<summary> functions for working with maps </summary>

--------------------
module Map from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> = interface IReadOnlyDictionary<'Key,'Value> interface IReadOnlyCollection<KeyValuePair<'Key,'Value>> interface IEnumerable interface IStructuralEquatable interface IComparable interface IEnumerable<KeyValuePair<'Key,'Value>> interface ICollection<KeyValuePair<'Key,'Value>> interface IDictionary<'Key,'Value> new: elements: ('Key * 'Value) seq -> Map<'Key,'Value> member Add: key: 'Key * value: 'Value -> Map<'Key,'Value> ...

--------------------
new: elements: ('Key * 'Value) seq -> Map<'Key,'Value>
val empty<'Key,'T (requires comparison)> : Map<'Key,'T> (requires comparison)
Multiple items
module Schedule from FSharp.Finance.Personal.Amortisation
<summary> a schedule showing the amortisation, itemising the effects of payments and calculating balances for each item, and producing some final statistics resulting from the calculations </summary>

--------------------
[<Struct>] type Schedule = { ScheduleItems: Map<int<OffsetDay>,ScheduleItem> FinalStats: FinalStats }
<summary> a schedule showing the amortisation, itemising the effects of payments and calculating balances for each item, and producing some final statistics resulting from the calculations </summary>
val toHtmlTable: schedule: Amortisation.Schedule -> string
<summary> formats the schedule items as an HTML table (stats can be rendered separately) </summary>
val html: string
val amortisation1: Amortisation.GenerationResult
val amortisation2: Amortisation.GenerationResult
union case Interest.Method.AddOn: Interest.Method
<summary> add-on interest method, where the interest accrued over the loan is added to the initial balance and the interest is paid off before the principal balance </summary>
val amortisation3: Amortisation.GenerationResult
val amortisation4: Amortisation.GenerationResult
union case SettlementDay.SettlementOnEvaluationDay: SettlementDay
<summary> quote a settlement figure on the evaluation day </summary>
Multiple items
module OffsetDay from FSharp.Finance.Personal.DateDay
<summary> functions for converting offset days to and from dates </summary>

--------------------
[<Measure>] type OffsetDay
<summary> the offset of a date from the start date, in days </summary>
Multiple items
module ActualPayment from FSharp.Finance.Personal.Scheduling
<summary> an actual payment made by the customer, optionally including metadata such as bank references etc. </summary>

--------------------
type ActualPayment = { ActualPaymentStatus: ActualPaymentStatus Metadata: Map<string,obj> } member Html: string with get
<summary> an actual payment made by the customer, optionally including metadata such as bank references etc. </summary>
val quickConfirmed: amount: int64<Cent> -> ActualPayment
<summary> a quick convenient method to create a confirmed actual payment </summary>
val amortisation5: Amortisation.GenerationResult
val amortisation6: Amortisation.GenerationResult
val refinanceExampleParameters: Parameters
val actualPayments: Map<int<OffsetDay>,ActualPayment array>
val refinanceExampleSchedule: Amortisation.GenerationResult
module Rescheduling from FSharp.Finance.Personal
<summary> functions for rescheduling payments after an original schedule failed to amortise </summary>
val rescheduleParameters: RescheduleParameters
type RescheduleParameters = { FeeSettlementRebate: SettlementRebate PaymentSchedule: ScheduleConfig RateOnNegativeBalance: Rate PromotionalInterestRates: PromotionalRate array SettlementDay: SettlementDay }
<summary> the parameters used for setting up additional items for an existing schedule or new items for a new schedule </summary>
module Fee from FSharp.Finance.Personal
<summary> a product fee &gt; NOTE: differences between fee and charge: &gt; - a fee is an up-front amount paid under agreed terms for receiving an advance &gt; - a fee is added to the principal balance and therefore accrues interest </summary>
[<Struct>] type SettlementRebate = | Zero | ProRata | ProRataRescheduled of OriginalFinalPaymentDay: int<OffsetDay> | Balance member Html: string with get
<summary> how the fee is treated in the event of an early settlement </summary>
union case Fee.SettlementRebate.Zero: Fee.SettlementRebate
<summary> fee is due in full with no discount or rebate </summary>
union case ScheduleConfig.FixedSchedules: FixedSchedules: FixedSchedule array -> ScheduleConfig
<summary> a schedule based on one or more unit-period configs each with a specific number of payments of a specified amount and type </summary>
union case Config.Weekly: WeekMultiple: int * WeekStartDate: Date -> Config
<summary> (multi-)weekly: every n weeks starting on the given date </summary>
[<Struct>] type ScheduleType = | Original | Rescheduled of RescheduleDay: int<OffsetDay> member Html: string with get
<summary> the type of the schedule; for scheduled payments, this affects how any payment due is calculated </summary>
union case ScheduleType.Rescheduled: RescheduleDay: int<OffsetDay> -> ScheduleType
<summary> a new schedule created after the original schedule, indicating the day it was created </summary>
val rescheduleSchedules: {| NewSchedules: Amortisation.GenerationResult; OldSchedules: Amortisation.GenerationResult |}
val reschedule: sp: Parameters -> rp: RescheduleParameters -> actualPayments: Map<int<OffsetDay>,ActualPayment array> -> {| NewSchedules: Amortisation.GenerationResult; OldSchedules: Amortisation.GenerationResult |}
<summary> take an existing schedule and reschedule the remaining payments e.g. to allow the customer more time to pay </summary>
anonymous record field NewSchedules: Amortisation.GenerationResult
Amortisation.GenerationResult.AmortisationSchedule: Amortisation.Schedule
val originalFinalPaymentDay: int<OffsetDay>
Amortisation.Schedule.ScheduleItems: Map<int<OffsetDay>,Amortisation.ScheduleItem>
<summary> a list of amortisation items, showing the events and calculations for a particular offset day </summary>
val filter: predicate: ('Key -> 'T -> bool) -> table: Map<'Key,'T> -> Map<'Key,'T> (requires comparison)
val si: Amortisation.ScheduleItem
Multiple items
module ScheduledPayment from FSharp.Finance.Personal.Scheduling

--------------------
type ScheduledPayment = { Original: int64<Cent> voption Rescheduled: RescheduledPayment voption PreviousRescheduled: RescheduledPayment array Adjustment: int64<Cent> Metadata: Map<string,obj> } member Html: string with get
<summary> any original or rescheduled payment, affecting how any payment due is calculated </summary>
val isSome: sp: ScheduledPayment -> bool
<summary> whether the payment has either an original or a rescheduled value </summary>
Amortisation.ScheduleItem.ScheduledPayment: ScheduledPayment
<summary> any payment scheduled on the current day </summary>
val maxKeyValue: table: Map<'Key,'T> -> 'Key * 'T (requires comparison)
val fst: tuple: ('T1 * 'T2) -> 'T1
val rolloverParameters: RolloverParameters
type RolloverParameters = { OriginalFinalPaymentDay: int<OffsetDay> PaymentSchedule: ScheduleConfig InterestConfig: Config PaymentConfig: PaymentConfig FeeHandling: FeeHandling }
<summary> parameters for creating a rolled-over schedule </summary>
Parameters.InterestConfig: Interest.Config
<summary> options relating to interest </summary>
Parameters.PaymentConfig: PaymentConfig
<summary> options relating to scheduled payments </summary>
[<Struct>] type FeeHandling = | CapitaliseAsPrincipal | CarryOverAsIs | WriteOffFeeBalance
<summary> how to handle any fee when rescheduling or rolling over </summary>
union case Fee.FeeHandling.CarryOverAsIs: Fee.FeeHandling
<summary> carry any outstanding fee balance over as an initial fee balance, maintaining the original final payment day if pro-rated </summary>
val rolloverSchedules: {| NewSchedules: Amortisation.GenerationResult; OldSchedules: Amortisation.GenerationResult |}
val rollOver: sp: Parameters -> rp: RolloverParameters -> actualPayments: Map<int<OffsetDay>,ActualPayment array> -> {| NewSchedules: Amortisation.GenerationResult; OldSchedules: Amortisation.GenerationResult |}
<summary> take an existing schedule and settle it, then use the result to create a new schedule to pay it off under different terms </summary>
val amortisation7: Amortisation.GenerationResult
val quickWriteOff: amount: int64<Cent> -> ActualPayment
<summary> a quick convenient method to create a written off actual payment </summary>
val amortisation8: Amortisation.GenerationResult
union case SettlementDay.SettlementOn: SettlementDay: int<OffsetDay> -> SettlementDay
<summary> quote a settlement figure on the specified day </summary>
val settlementFigure: (int<OffsetDay> * int64<Cent>) voption
Amortisation.Schedule.FinalStats: Amortisation.FinalStats
<summary> final stats resulting from the calculations </summary>
Amortisation.FinalStats.SettlementFigure: (int<OffsetDay> * int64<Cent>) voption
<summary> the generated settlement figure from the schedule </summary>
val fullWriteOffAmount: int64<Cent>
Multiple items
module ValueOption from Microsoft.FSharp.Core

--------------------
[<Struct>] type ValueOption<'T> = | ValueNone | ValueSome of 'T static member Some: value: 'T -> 'T voption static member op_Implicit: value: 'T -> 'T voption member IsNone: bool with get member IsSome: bool with get member Value: 'T with get static member None: 'T voption with get
val map: mapping: ('T -> 'U) -> voption: 'T voption -> 'U voption
val snd: tuple: ('T1 * 'T2) -> 'T2
val defaultValue: value: 'T -> voption: 'T voption -> 'T
val amortisation8': Amortisation.GenerationResult
val amortisation9: Amortisation.GenerationResult
[<Struct>] type DateRange = { Start: Date End: Date } member Html: string with get
<summary> a holiday, i.e. a period when no interest and/or charges are accrued </summary>

Type something to start searching.