Skip to content

Commit

Permalink
only calculate final APR for settlement quotes
Browse files Browse the repository at this point in the history
  • Loading branch information
simontreanor committed Feb 28, 2024
1 parent f7f6871 commit e40ae9d
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 39 deletions.
2 changes: 1 addition & 1 deletion docs/exampleAmortisation.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ let actualPayments = [|

let amortisationSchedule =
actualPayments
|> Amortisation.generate scheduleParameters IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest
|> Amortisation.generate scheduleParameters IntendedPurpose.Statement ApplyNegativeInterest
amortisationSchedule

(*** include-it ***)
Expand Down
13 changes: 6 additions & 7 deletions src/Amortisation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ module Amortisation =
|> fst

/// wraps the amortisation schedule in some statistics, and optionally calculate the final APR (optional because it can be processor-intensive)
let calculateStats sp finalAprOption items =
let calculateStats sp intendedPurpose items =
let finalItem = Array.last items
let principalTotal = items |> Array.sumBy _.PrincipalPortion
let feesTotal = items |> Array.sumBy _.FeesPortion
Expand All @@ -376,15 +376,14 @@ module Amortisation =
let finalPaymentDay = finalItem.OffsetDay
let finalBalanceStatus = finalItem.BalanceStatus
let finalAprSolution =
match finalAprOption with
| CalculateFinalApr when finalBalanceStatus = ClosedBalance ->
match intendedPurpose with
| IntendedPurpose.Quote Settlement when finalBalanceStatus = ClosedBalance ->
items
|> Array.filter(fun asi -> asi.NetEffect > 0L<Cent>)
|> Array.map(fun asi -> { Apr.TransferType = Apr.Payment; Apr.TransferDate = sp.StartDate.AddDays(int asi.OffsetDay); Apr.Amount = asi.NetEffect })
|> Apr.calculate sp.Calculation.AprMethod sp.Principal sp.StartDate
|> ValueSome
| CalculateFinalApr
| DoNotCalculateFinalApr ->
| _ ->
ValueNone
{
ScheduleItems = items
Expand All @@ -401,7 +400,7 @@ module Amortisation =
}

/// generates an amortisation schedule and final statistics
let generate sp intendedPurpose finalAprOption negativeInterestOption originalFinalPaymentDay actualPayments =
let generate sp intendedPurpose negativeInterestOption originalFinalPaymentDay actualPayments =
let payments =
match sp.PaymentSchedule with
| RegularSchedule _ ->
Expand Down Expand Up @@ -432,5 +431,5 @@ module Amortisation =
|> applyPayments asOfDay intendedPurpose sp.FeesAndCharges.LatePaymentGracePeriod latePaymentCharge sp.Calculation.PaymentTimeout actualPayments
|> calculate sp intendedPurpose originalFinalPaymentDay' negativeInterestOption
|> Array.takeWhile(fun si -> originalFinalPaymentDay.IsNone || si.PaymentStatus <> NoLongerRequired) // remove extra items from rescheduling
|> calculateStats sp finalAprOption
|> calculateStats sp intendedPurpose
|> ValueSome
8 changes: 0 additions & 8 deletions src/Calculation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@ module Calculation =
/// intended just for information, e.g. to view the current status of a loan
| Statement

/// whether to calculate the final APR
[<Struct>]
type FinalAprOption =
/// calculate the final APR (can be compute-intensive)
| CalculateFinalApr
/// do not calculate the final APR (not needed)
| DoNotCalculateFinalApr

/// whether to apply interest on a negative balance (i.e. while a refund is owing)
[<Struct>]
type NegativeInterestOption =
Expand Down
4 changes: 2 additions & 2 deletions src/Quotes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ module Quotes =
/// calculates a revised schedule showing the generated payment for the given quote type
let getQuote quoteType sp negativeInterestOption (actualPayments: CustomerPayment array) =
voption {
let! currentAmortisationSchedule = Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr negativeInterestOption ValueNone actualPayments
let! currentAmortisationSchedule = Amortisation.generate sp IntendedPurpose.Statement negativeInterestOption ValueNone actualPayments
let originalFinalPaymentDay = currentAmortisationSchedule.ScheduleItems |> Array.last |> _.OffsetDay
let! revisedAmortisationSchedule = Amortisation.generate sp (IntendedPurpose.Quote quoteType) DoNotCalculateFinalApr negativeInterestOption (ValueSome originalFinalPaymentDay) actualPayments
let! revisedAmortisationSchedule = Amortisation.generate sp (IntendedPurpose.Quote quoteType) negativeInterestOption (ValueSome originalFinalPaymentDay) actualPayments
let! si = revisedAmortisationSchedule.ScheduleItems |> Array.tryFind(_.GeneratedPayment.IsSome) |> toValueOption
let confirmedPayments = si.ActualPayments |> Array.sumBy(function ActualPayment.Confirmed ap -> ap | _ -> 0L<Cent>)
let pendingPayments = si.ActualPayments |> Array.sumBy(function ActualPayment.Pending ap -> ap | _ -> 0L<Cent>)
Expand Down
4 changes: 2 additions & 2 deletions src/Rescheduling.fs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ module Rescheduling =
Interest = { sp.Interest with InitialGracePeriod = 0<DurationDay>; Holidays = rp.InterestHolidays }
}
// create the new amortiation schedule
let! rescheduledSchedule = Amortisation.generate spNew IntendedPurpose.Statement DoNotCalculateFinalApr DoNotApplyNegativeInterest (ValueSome quote.OriginalFinalPaymentDay) [||]
let! rescheduledSchedule = Amortisation.generate spNew IntendedPurpose.Statement DoNotApplyNegativeInterest (ValueSome quote.OriginalFinalPaymentDay) [||]
return quote.RevisedSchedule, rescheduledSchedule
}

Expand Down Expand Up @@ -116,7 +116,7 @@ module Rescheduling =
Calculation = rp.Calculation |> ValueOption.defaultValue sp.Calculation
}
// create the new amortiation schedule
let! rescheduledSchedule = Amortisation.generate spNew IntendedPurpose.Statement DoNotCalculateFinalApr DoNotApplyNegativeInterest (ValueSome quote.OriginalFinalPaymentDay) [||]
let! rescheduledSchedule = Amortisation.generate spNew IntendedPurpose.Statement DoNotApplyNegativeInterest (ValueSome quote.OriginalFinalPaymentDay) [||]
return quote.RevisedSchedule, rescheduledSchedule
}

Expand Down
38 changes: 19 additions & 19 deletions tests/ActualPaymentTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter (_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest001.md")

Expand Down Expand Up @@ -128,7 +128,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest002.md")

Expand Down Expand Up @@ -173,7 +173,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest003.md")

Expand Down Expand Up @@ -218,7 +218,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest004.md")

Expand Down Expand Up @@ -285,7 +285,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest005.md")

Expand Down Expand Up @@ -357,7 +357,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest006.md")

Expand Down Expand Up @@ -426,7 +426,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest007.md")

Expand Down Expand Up @@ -498,7 +498,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest008.md")

Expand Down Expand Up @@ -570,7 +570,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest009.md")

Expand Down Expand Up @@ -641,7 +641,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest010.md")

Expand Down Expand Up @@ -712,7 +712,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest011.md")

Expand Down Expand Up @@ -785,7 +785,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest012.md")

Expand Down Expand Up @@ -854,7 +854,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest013.md")

Expand Down Expand Up @@ -924,7 +924,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest014.md")

Expand Down Expand Up @@ -971,7 +971,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest015.md")

Expand Down Expand Up @@ -1019,7 +1019,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest016.md")

Expand Down Expand Up @@ -1068,7 +1068,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest017.md")

Expand Down Expand Up @@ -1117,7 +1117,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest018.md")

Expand Down Expand Up @@ -1166,7 +1166,7 @@ module ActualPaymentTests =

let schedule =
actualPayments
|> Amortisation.generate sp IntendedPurpose.Statement DoNotCalculateFinalApr ApplyNegativeInterest ValueNone
|> Amortisation.generate sp IntendedPurpose.Statement ApplyNegativeInterest ValueNone

schedule |> ValueOption.iter(_.ScheduleItems >> Formatting.outputListToHtml "out/ActualPaymentTest019.md")

Expand Down

0 comments on commit e40ae9d

Please sign in to comment.