Hastructure-0.45.0: src/AssetClass/ProjectedCashFlow.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
module AssetClass.ProjectedCashFlow
(ProjectedCashflow(..))
where
import qualified Data.Time as T
import qualified Assumptions as A
import Asset as Ast
import Types
import Lib
import Util
import DateUtil
import InterestRate as IR
import qualified Data.Map as Map
import Data.List
import Data.Ratio
import Data.Maybe
import GHC.Generics
import Data.Aeson hiding (json)
import Language.Haskell.TH
import Data.Aeson.TH
import Data.Aeson.Types
import qualified Cashflow as CF
import AssetClass.AssetBase
import AssetClass.AssetCashflow
import Cashflow (extendTxns,TsRow(..))
import Debug.Trace
import Control.Lens hiding (element,Index)
import Control.Lens.TH
debug = flip trace
projectScheduleFlow :: [CF.TsRow] -> Rate -> Balance -> [CF.TsRow] -> [DefaultRate] -> [PrepaymentRate] -> [Amount] -> [Amount] -> (Int, Rate) -> [CF.TsRow]
projectScheduleFlow trs _ last_bal [] _ _ [] [] (_,_) = trs
projectScheduleFlow trs bal_factor last_bal (flow:flows) (defRate:defRates) (ppyRate:ppyRates) recV lossV (recoveryLag,recoveryRate)
= projectScheduleFlow (trs++[tr]) surviveRate endBal flows defRates ppyRates (tail recVector) (tail lossVector) (recoveryLag,recoveryRate) -- `debug` ("===>C")
where
startBal = last_bal
defAmt = mulBR startBal defRate
ppyAmt = mulBR (startBal - defAmt) ppyRate
afterBal = startBal - defAmt - ppyAmt
surviveRate = (1 - defRate) * (1 - ppyRate) * bal_factor
schedulePrin = mulBR (CF.mflowPrincipal flow) surviveRate --TODO round trip -- `debug` ("Schedule Principal"++(printf "%.2f" (CF.mflowPrincipal flow))++" Rate"++show(_schedule_rate))
scheduleInt = mulBR (CF.mflowInterest flow) surviveRate
newRec = mulBR defAmt recoveryRate
newLoss = mulBR defAmt (1 - recoveryRate)
recVector = replace recV recoveryLag newRec
lossVector = replace lossV recoveryLag newLoss
endBal = max 0 $ afterBal - schedulePrin -- `debug` ("start bal"++ show startBal ++"sch prin"++ show schedulePrin)
tr = CF.MortgageFlow (CF.getDate flow) endBal schedulePrin scheduleInt ppyAmt defAmt (head recVector) (head lossVector) 0.0 Nothing Nothing Nothing--TODO missing ppy-penalty here
projectScheduleFlow trs b_factor lastBal [] _ _ (r:rs) (l:ls) (recovery_lag,recovery_rate)
= projectScheduleFlow (trs++[tr]) b_factor lastBal [] [] [] rs ls (recovery_lag - 1,recovery_rate)
where
remain_length = length rs
lastDate = CF.getDate (last trs)
flowDate = nextDate lastDate Lib.Monthly
tr = CF.MortgageFlow flowDate lastBal 0 0 0 0 r l 0.0 Nothing Nothing Nothing
-- ^ project cashflow with floater rate portion
projFixCfwithAssumption :: (CF.CashFlowFrame, DatePattern) -> ([Rate],[Rate],Rate,Int) -> Date -> Either String CF.CashFlowFrame
projFixCfwithAssumption (cf@(CF.CashFlowFrame (begBal, begDate, accInt) flows), dp)
(ppyRates,defRates,recoveryRate,recoveryLag)
asOfDay
= let
curveDatesLength = recoveryLag + length flows
endDate = CF.getDate (last flows)
extraDates = genSerialDates dp Exc endDate recoveryLag
cfDates = (CF.getDate <$> flows) ++ extraDates
in
do
let txns = projectScheduleFlow [] 1.0 begBal flows defRates ppyRates
(replicate curveDatesLength 0.0)
(replicate curveDatesLength 0.0)
(recoveryLag,recoveryRate) -- `debug` (" begin bal"++ show begBal)
let (futureTxns,historyM) = CF.cutoffTrs asOfDay txns
let cb = (CF.mflowBegBalance . head) futureTxns
return $ CF.CashFlowFrame (cb,asOfDay,Nothing) futureTxns
-- ^ project cashflow with fix rate portion
projIndexCashflows :: ([Date],[Balance],[Principal],Index,Spread) -> DatePattern -> ([Rate],[Rate],Rate,Int) -> Maybe [RateAssumption] -> Either String CF.CashFlowFrame
projIndexCashflows (ds,bals,principals,index,spd) dp pAssump (Just ras) =
do
-- mIndexToApply = A.getRateAssumption ras index
indexRates <- sequenceA $ A.lookupRate0 ras index <$> ds
let rates = (spd +) <$> indexRates
let interestFlow = zipWith (flip mulBIR) rates bals
let flowSize = length bals
let scheduleCf = CF.CashFlowFrame (head bals, head ds, Nothing) $
zipWith12 MortgageFlow
ds
bals
principals
interestFlow
(replicate flowSize 0 )
(replicate flowSize 0 )
(replicate flowSize 0 )
(replicate flowSize 0 )
rates
(replicate flowSize Nothing)
(replicate flowSize Nothing)
(replicate flowSize Nothing)
projFixCfwithAssumption (scheduleCf, dp) pAssump (head ds)
-- ^ project cashflow with fix rate portion and floater rate portion
seperateCashflows :: ProjectedCashflow -> Maybe A.AssetPerfAssumption -> Maybe [RateAssumption] -> Either String (CF.CashFlowFrame, [CF.CashFlowFrame])
seperateCashflows a@(ProjectedFlowMixFloater pflow@(CF.CashFlowFrame (begBal, begDate, accuredInt) flows) dp (fixPct,fixRate) floaterList)
mPassump
mRates
= let
begBal = CF.mflowBegBalance $ head flows
totalBals = begBal: ((view CF.tsRowBalance) <$> flows)
ds = (view CF.tsDate) <$> flows
flowSize = length ds
-- fix rate cashflow
-- fix balance = total balance * fix percent
fixedBals = flip mulBR fixPct <$> totalBals
-- fix principal flow = total principal flow * fix percent
fixedPrincipalFlow = flip mulBR fixPct <$> CF.mflowPrincipal <$> flows
-- fix principal interest = total principal flow * fix rate
fixedInterestFlow = flip mulBIR fixRate <$> fixedBals
fixFlow = zipWith12 MortgageFlow ds fixedBals fixedPrincipalFlow fixedInterestFlow (replicate flowSize 0) (replicate flowSize 0) (replicate flowSize 0) (replicate flowSize 0) (replicate flowSize fixRate) (replicate flowSize Nothing) (replicate flowSize Nothing) (replicate flowSize Nothing)
-- float rate cashflow
-- float balance = total balance - fixed balance
totalFloatBalFlow = zipWith (-) totalBals fixedBals
-- float principal flow = total principal flow - fixed principal flow
floatPrincipalFlow = zipWith (-) (CF.mflowPrincipal <$> flows) fixedPrincipalFlow
rs = (\(a,b,c) -> a) <$> floaterList -- portion of each floater
spds = (\(a,b,c) -> b) <$> floaterList -- spreads
indexes = (\(a,b,c) -> c) <$> floaterList -- indexes
floaterSize = length rs
-- float bal brekdown by index
floatBalsBreakDown = (\r -> flip mulBR r <$> totalFloatBalFlow ) <$> rs
-- float principal flow breakdown by index
floatPrincipalFlowBreakDown = (\r -> flip mulBR r <$> floatPrincipalFlow) <$> rs -- `debug` ("float bal breakdown"++ show floatBalsBreakDown)
recoveryLag = case mPassump of
Nothing -> 0
Just passump -> fromMaybe 0 $ getRecoveryLagFromAssumption passump
curveDatesLength = length flows + recoveryLag
in
do
assumptionInput <- case mPassump of
Just pAssump -> buildAssumptionPpyDefRecRate a (begDate:ds) pAssump
Nothing -> Right (replicate curveDatesLength 0.0, replicate curveDatesLength 0.0, 0.0, 0)
fixedCashFlow <- projFixCfwithAssumption ((CF.CashFlowFrame ( ((flip mulBR) fixPct) begBal
, begDate
, (flip mulBR) fixPct <$> accuredInt)
fixFlow)
, dp) assumptionInput begDate
floatedCashFlow <- sequenceA $ (\x -> projIndexCashflows x dp assumptionInput mRates) <$> zip5
(replicate floaterSize ds)
floatBalsBreakDown
floatPrincipalFlowBreakDown
indexes
spds
return (fixedCashFlow, floatedCashFlow) -- `debug` ("float cf"++ show floatedCashFlow)
instance Ast.Asset ProjectedCashflow where
getCurrentBal (ProjectedFlowFixed cf@(CF.CashFlowFrame (begBal,_,_) _) _) = begBal
getCurrentBal (ProjectedFlowMixFloater cf@(CF.CashFlowFrame (begBal,_,_) _) _ _ _) = begBal
getOriginBal x = getCurrentBal x
getOriginRate x = 0.0
isDefaulted f = error ""
getOriginDate f = error ""
getOriginInfo f = error ""
getCurrentRate f = 0.0
calcCashflow f@(ProjectedFlowFixed cf _) d _ = Right $ cf
calcCashflow f@(ProjectedFlowMixFloater cf _ fxPortion floatPortion) d mRate
= do
(fixedCashFlow, floatedCashFlow) <- seperateCashflows f Nothing mRate -- `debug` ("running fixed cashflow"++show fixedCashFlow)
return $ foldl CF.combine fixedCashFlow floatedCashFlow
projCashflow f@(ProjectedFlowFixed cf dp) asOfDay (pAssump,_,_) mRates
= do
let cfDates = CF.getDatesCashFlowFrame cf
let begDate = view (CF.cfBeginStatus . _2) cf
pRates <- buildAssumptionPpyDefRecRate f (begDate:cfDates) pAssump
p <- projFixCfwithAssumption (cf, dp) pRates asOfDay
return (p, Map.empty)
projCashflow f asOfDay (pAssump, _, _) mRates
= do
(fixedCashFlow, floatedCashFlow) <- seperateCashflows f (Just pAssump) mRates
return $ (foldl CF.combine fixedCashFlow floatedCashFlow, Map.empty)
--(fixedCashFlow, Map.empty)
projCashflow a b c d = Left $ "Failed to match when proj projected flow with assumption >>" ++ show a ++ show b ++ show c ++ show d
getBorrowerNum f = 0
splitWith f rs = [f]
-- instance IR.UseRate ProjectedCashflow where
-- isAdjustbleRate _ = False
-- getIndex _ = Nothing
-- getIndexes _ = Nothing