Hastructure-0.45.0: src/AssetClass/Loan.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
module AssetClass.Loan
(projectLoanFlow,updateOriginDate)
where
import qualified Data.Time as T
import qualified Cashflow as CF -- (Cashflow,Amount,Interests,Principals)
import qualified Assumptions as A
import InterestRate
import Asset
import Lib
import Util
import DateUtil
import Types
import qualified Data.Map as Map
import Data.List
import Data.Maybe
import Data.Aeson hiding (json)
import Language.Haskell.TH
import Data.Aeson.TH
import Data.Aeson.Types
import GHC.Generics
import AssetClass.AssetBase
import AssetClass.AssetCashflow
import Debug.Trace
import Assumptions (AssetDefaultAssumption(DefaultCDR))
import qualified Asset as A
import Control.Lens hiding (element)
import Control.Lens.TH
debug = flip trace
-- instance Asset Loan where
projectLoanFlow :: ((Balance,Int,IRate), Balance, Date, AmortPlan, DayCount, IRate, Rational) -> (Dates, [DefaultRate],[PrepaymentRate],[IRate],[Int]) -> ([CF.TsRow],Rational)
projectLoanFlow ((originBal,ot,or), startBal, lastPayDate, pt, dc,startRate, begFactor) (cfDates,defRates,ppyRates,rateVector,remainTerms) =
let
initRow = CF.LoanFlow lastPayDate startBal 0.0 0.0 0.0 0.0 0.0 0.0 startRate Nothing
in
foldl
(\(acc,factor) (pDate, ppyRate, defRate, intRate, rt)
-> let
begBal = view CF.tsRowBalance (last acc)
lastPaidDate = getDate (last acc)
newDefault = mulBR begBal defRate
newPrepay = mulBR (begBal - newDefault) ppyRate
intBal = begBal - newDefault - newPrepay
newFactor = factor * (1-defRate) * (1- ppyRate )
newInt = case (rt,pt) of
(0,F_P) -> 0
(_,F_P) -> mulBR (mulBIR originBal or) newFactor
_ -> calcInt intBal lastPaidDate pDate intRate dc
newPrin = case (rt,pt) of
(1,I_P) -> intBal
(1,F_P) -> intBal
(0,F_P) -> 0
(_,F_P) -> mulBR (divideBI intBal rt) newFactor
(_,I_P) -> 0
(0,ScheduleRepayment cf _) -> intBal
(_,ScheduleRepayment cf _) ->
let
projAmt = fromRational $ getValByDate cf Inc pDate
in
if rt == 1 then
intBal
else
mulBR projAmt newFactor
_ -> error $ "failed to match Loan Project newPrin"++ show (rt,pt)
endBal = intBal - newPrin
in
(acc ++ [CF.LoanFlow pDate endBal newPrin newInt newPrepay newDefault 0.0 0.0 intRate Nothing]
,newFactor))
([initRow],begFactor)
(zip5 cfDates ppyRates defRates rateVector remainTerms)
instance Asset Loan where
calcCashflow pl@(PersonalLoan (LoanOriginalInfo ob or ot p sd ptype _) bal rate term _ ) asOfDay mRates
= fst <$>
projCashflow pl
asOfDay
(A.LoanAssump Nothing Nothing Nothing Nothing
,A.DummyDelinqAssump
,A.DummyDefaultAssump)
mRates
getCurrentBal pl@(PersonalLoan (LoanOriginalInfo ob or ot p sd ptype _) _bal _rate _term _ )
= _bal
getOriginRate pl@(PersonalLoan (LoanOriginalInfo ob or ot p sd ptype _) _bal _rate _term _ )
= case or of
Fix _ _r -> _r
Floater _ _ _ _r _ _ _ _ -> _r
getCurrentRate pl@(PersonalLoan (LoanOriginalInfo ob or ot p sd ptype _) _bal _rate _term _ )
= _rate
getOriginBal pl@(PersonalLoan (LoanOriginalInfo ob _ _ _ _ _ _) _ _ _ _ ) = ob
isDefaulted pl@(PersonalLoan _ _ _ _ (Defaulted _)) = True
isDefaulted PersonalLoan {} = False
getOriginInfo (PersonalLoan oi cb cr rt st) = oi
getOriginDate (PersonalLoan (LoanOriginalInfo ob or ot p sd I_P _) cb cr rt st ) = sd
resetToOrig m@(PersonalLoan (LoanOriginalInfo ob or ot p sd I_P obr) cb cr rt st )
= PersonalLoan (LoanOriginalInfo ob or ot p sd I_P obr) ob (getOriginRate m) ot st
getRemainTerms (PersonalLoan (LoanOriginalInfo ob or ot p sd I_P _) cb cr rt st ) = rt
updateOriginDate (PersonalLoan (LoanOriginalInfo ob or ot p sd I_P obr) cb cr rt st ) nd
= PersonalLoan (LoanOriginalInfo ob or ot p nd I_P obr) cb cr rt st
getPaymentDates pl@(PersonalLoan (LoanOriginalInfo ob _ ot p sd (ScheduleRepayment ts mDp) _) _bal _rate _term _ ) extra
= let
pdays = getTsDates ts
extraDates = genSerialDates (fromMaybe MonthEnd mDp) Inc (last pdays) extra
in
pdays ++ extraDates
getPaymentDates pl@(PersonalLoan (LoanOriginalInfo ob _ ot p sd _ _) _bal _rate _term _ ) extra
= genDates sd p (ot+extra)
-- ^ Project cashflow for loans with prepayment/default/loss and interest rate assumptions
projCashflow pl@(PersonalLoan (LoanOriginalInfo ob or ot p sd prinPayType _) cb cr rt Current)
asOfDay
(A.LoanAssump defaultAssump prepayAssump recoveryAssump ams,_,_)
mRate
= let
recoveryLag = maybe 0 getRecoveryLag recoveryAssump
lastPayDate:cfDates = lastN (rt + recoveryLag + 1) $ sd:getPaymentDates pl recoveryLag
in
do
rateVector <- A.projRates cr or mRate cfDates
ppyRates <- A.buildPrepayRates pl (lastPayDate:cfDates) prepayAssump
defRates <- A.buildDefaultRates pl (lastPayDate:cfDates) defaultAssump
let dc = getDayCount or
let remainTerms = reverse $ replicate recoveryLag 0 ++ [0..rt] -- `debug` ("rateVector"++show rateVector)
let initFactor = case prinPayType of
ScheduleRepayment ts _ ->
let
scheduleBals = scanl (-) ob $ fromRational <$> getTsVals ts
in
divideBB cb (scheduleBals!!(ot - rt))
_ -> 1.0
let (txns,_) = projectLoanFlow ((ob,ot,getOriginRate pl), cb,lastPayDate,prinPayType,dc,cr,initFactor) (cfDates,defRates,ppyRates,rateVector,remainTerms) -- `debug` (" rateVector"++show rateVector)
let (futureTxns,historyM) = CF.cutoffTrs asOfDay (patchLossRecovery txns recoveryAssump)
let begBal = CF.buildBegBal futureTxns
return $ (applyHaircut ams (CF.CashFlowFrame (begBal,asOfDay,Nothing) futureTxns), historyM)
-- ^ Project cashflow for defautled loans
projCashflow m@(PersonalLoan (LoanOriginalInfo ob or ot p sd prinPayType _) cb cr rt (Defaulted (Just defaultedDate)))
asOfDay
(_,_,A.DefaultedRecovery rr lag timing)
_
= let
(cf_dates1,cf_dates2) = splitAt (pred lag) $ genDates defaultedDate p (lag+ length timing)
beforeRecoveryTxn = [ CF.LoanFlow d cb 0 0 0 0 0 0 cr Nothing| d <- cf_dates1 ]
recoveries = calcRecoveriesFromDefault cb rr timing
_txns = [ CF.LoanFlow d 0 0 0 0 0 r 0 cr Nothing | (d,r) <- zip cf_dates2 recoveries ]
(_, txns) = splitByDate (beforeRecoveryTxn++_txns) asOfDay EqToRight -- `debug` ("AS OF Date"++show asOfDay)
(futureTxns,historyM) = CF.cutoffTrs asOfDay txns
begBal = CF.buildBegBal futureTxns
in
Right $ (CF.CashFlowFrame (begBal,asOfDay,Nothing) futureTxns, historyM)
projCashflow m@(PersonalLoan (LoanOriginalInfo ob or ot p sd prinPayType _) cb cr rt (Defaulted Nothing)) asOfDay assumps _
= Right $ (CF.CashFlowFrame (cb,asOfDay,Nothing) [CF.LoanFlow asOfDay 0 0 0 0 0 0 0 cr Nothing],Map.empty)
projCashflow a b c d = Left $ "failed to match projCashflow for Loan "++show a++show b++show c++show d
splitWith l@(PersonalLoan (LoanOriginalInfo ob or ot p sd prinPayType obr) cb cr rt st) rs
= [ PersonalLoan (LoanOriginalInfo (mulBR ob ratio) or ot p sd prinPayType obr) (mulBR cb ratio) cr rt st | ratio <- rs ]