Skip to content

Commit

Permalink
simplify negative interest
Browse files Browse the repository at this point in the history
  • Loading branch information
simontreanor committed Apr 12, 2024
1 parent 9c5665c commit 8db73fb
Show file tree
Hide file tree
Showing 14 changed files with 16 additions and 102 deletions.
4 changes: 1 addition & 3 deletions docs/exampleAmortisation.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ let scheduleParameters =
AprMethod = Apr.CalculationMethod.UnitedKingdom 3
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
PaymentTimeout = 3<DurationDay> }
}

let actualPayments = [|
Expand Down
1 change: 0 additions & 1 deletion docs/examplePaymentSchedule.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ let scheduleParameters =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}
let schedule = scheduleParameters |> calculate AroundZero
Expand Down
6 changes: 3 additions & 3 deletions src/Amortisation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,12 @@ module Amortisation =

let newInterest =
if si.PrincipalBalance <= 0L<Cent> then
match sp.Calculation.NegativeInterestOption with
| ApplyNegativeInterest ->
match sp.Interest.RateOnNegativeBalance with
| ValueSome _ ->
dailyInterestRates si.OffsetDay ap.AppliedPaymentDay
|> Array.map(fun dr -> { dr with InterestRate = sp.Interest.RateOnNegativeBalance |> ValueOption.defaultValue Interest.Rate.Zero })
|> Interest.calculate (si.PrincipalBalance + si.FeesBalance) ValueNone (ValueSome sp.Calculation.RoundingOptions.InterestRounding)
| DoNotApplyNegativeInterest ->
| ValueNone ->
0m<Cent>
else
dailyInterestRates si.OffsetDay ap.AppliedPaymentDay
Expand Down
8 changes: 0 additions & 8 deletions src/Calculation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,6 @@ module Calculation =
/// intended just for information, e.g. to view the current status of a loan
| Statement

/// whether to apply interest on a negative balance (i.e. while a refund is owing)
[<Struct>]
type NegativeInterestOption =
/// apply negative interest (e.g. statutory interest on outstanding refunds)
| ApplyNegativeInterest
/// do not apply negative interest (e.g. if the customer has volutarily overpaid)
| DoNotApplyNegativeInterest

/// holds the result of a devision, separated into quotient and remainder
[<Struct>]
type DivisionResult = {
Expand Down
2 changes: 0 additions & 2 deletions src/PaymentSchedule.fs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ module PaymentSchedule =
AprMethod: Apr.CalculationMethod
/// which rounding method to use
RoundingOptions: RoundingOptions
/// how to handle interest on negative principal balances
NegativeInterestOption: NegativeInterestOption
/// the minimum payment that can be taken and how to handle it
MinimumPayment: MinimumPayment
/// the duration after which a pending payment is considered a missed payment
Expand Down
10 changes: 7 additions & 3 deletions src/Rescheduling.fs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module Rescheduling =
/// whether the plan is autogenerated or a manual plan provided
PaymentSchedule: CustomerPaymentSchedule
/// how to handle interest on negative principal balances
NegativeInterestOption: NegativeInterestOption
RateOnNegativeBalance: Interest.Rate voption
/// any promotional or introductory offers during which a different interest rate is applicable
PromotionalInterestRates: Interest.PromotionalRate array
/// any period during which charges are not payable
Expand Down Expand Up @@ -70,8 +70,12 @@ module Rescheduling =
ScheduleType = PaymentSchedule.ScheduleType.Rescheduled rp.OriginalFinalPaymentDay
PaymentSchedule = [| oldPaymentSchedule; newPaymentSchedule |] |> Array.concat |> IrregularSchedule
FeesAndCharges = { sp.FeesAndCharges with ChargesHolidays = rp.ChargesHolidays }
Interest = { sp.Interest with InitialGracePeriod = 0<DurationDay>; PromotionalRates = rp.PromotionalInterestRates }
Calculation = { sp.Calculation with NegativeInterestOption = rp.NegativeInterestOption }
Interest =
{ sp.Interest with
InitialGracePeriod = 0<DurationDay>
PromotionalRates = rp.PromotionalInterestRates
RateOnNegativeBalance = rp.RateOnNegativeBalance
}
}
// create the new amortiation schedule
let intendedPurpose =
Expand Down
20 changes: 0 additions & 20 deletions tests/ActualPaymentTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -134,7 +133,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -182,7 +180,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -230,7 +227,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -302,7 +298,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -374,7 +369,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -451,7 +445,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -526,7 +519,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -602,7 +594,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -679,7 +670,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -755,7 +745,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -831,7 +820,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -909,7 +897,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -959,7 +946,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -1032,7 +1018,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -1082,7 +1067,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -1133,7 +1117,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -1185,7 +1168,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -1237,7 +1219,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down Expand Up @@ -1286,7 +1267,6 @@ module ActualPaymentTests =
RoundingOptions = RoundingOptions.recommended
PaymentTimeout = 0<DurationDay>
MinimumPayment = NoMinimumPayment
NegativeInterestOption = ApplyNegativeInterest
}
}

Expand Down
15 changes: 3 additions & 12 deletions tests/ActualPaymentTestsExtra.fs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ module ActualPaymentTestsExtra =
RoundingOptions = ro
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}
|> applyPayments
Expand Down Expand Up @@ -257,7 +256,6 @@ module ActualPaymentTestsExtra =
RoundingOptions = ro
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
}
)))))))))))))))
Expand Down Expand Up @@ -349,7 +347,6 @@ module ActualPaymentTestsExtra =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
})
let actual =
Expand Down Expand Up @@ -426,7 +423,6 @@ module ActualPaymentTestsExtra =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
})
let actual =
Expand Down Expand Up @@ -505,7 +501,6 @@ module ActualPaymentTestsExtra =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
})
let originalFinalPaymentDay = paymentDays sp.StartDate sp.PaymentSchedule |> Array.tryLast |> Option.defaultValue 0<OffsetDay>
Expand All @@ -518,8 +513,9 @@ module ActualPaymentTestsExtra =
let rp : RescheduleParameters = {
OriginalFinalPaymentDay = originalFinalPaymentDay'
FeesSettlement = Fees.SettlementRefund.ProRata
PaymentSchedule = RegularFixedSchedule [| { UnitPeriodConfig = UnitPeriod.Config.Weekly(2, Date(2022, 9, 1)); PaymentCount = 155; PaymentAmount = 20_00L<Cent> } |]
NegativeInterestOption = DoNotApplyNegativeInterest
PaymentSchedule = RegularFixedSchedule [|
{ UnitPeriodConfig = UnitPeriod.Config.Weekly(2, Date(2022, 9, 1)); PaymentCount = 155; PaymentAmount = 20_00L<Cent> } |]
RateOnNegativeBalance = ValueNone
PromotionalInterestRates = [||]
ChargesHolidays = [||]
FutureSettlementDay = ValueNone
Expand Down Expand Up @@ -591,7 +587,6 @@ module ActualPaymentTestsExtra =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
})
let actual =
Expand Down Expand Up @@ -668,7 +663,6 @@ module ActualPaymentTestsExtra =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
})
let actual =
Expand Down Expand Up @@ -745,7 +739,6 @@ module ActualPaymentTestsExtra =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
})
let actual =
Expand Down Expand Up @@ -824,7 +817,6 @@ module ActualPaymentTestsExtra =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
})
let originalFinalPaymentDay = paymentDays sp.StartDate sp.PaymentSchedule |> Array.tryLast |> Option.defaultValue 0<OffsetDay>
Expand Down Expand Up @@ -909,7 +901,6 @@ module ActualPaymentTestsExtra =
RoundingOptions = RoundingOptions.recommended
MinimumPayment = DeferOrWriteOff 50L<Cent>
PaymentTimeout = 3<DurationDay>
NegativeInterestOption = ApplyNegativeInterest
}
})
let originalFinalPaymentDay = paymentDays sp.StartDate sp.PaymentSchedule |> Array.tryLast |> Option.defaultValue 0<OffsetDay>
Expand Down
Loading

0 comments on commit 8db73fb

Please sign in to comment.