packages feed

Hastructure-0.45.0: src/AssetClass/AssetCashflow.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}

module AssetClass.AssetCashflow
  (applyHaircut,patchPrepayPenaltyFlow,getRecoveryLag,decreaseBorrowerNum
  ,patchLossRecovery,getRecoveryLagFromAssumption)
  where

import qualified Data.Time as T
import qualified Cashflow as CF 
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 AssetClass.AssetBase

import Debug.Trace
import qualified Assumptions as A 
import GHC.Float.RealFracMethods (truncateFloatInteger)
import Cashflow (mflowDefault)
debug = flip trace

-- This module is a collection of common cashflow functions to project cashflow for different asset types.

-- ^ apply haircut to pool cashflow, reduce cash via a percentage
applyHaircut :: Maybe A.ExtraStress -> CF.CashFlowFrame -> CF.CashFlowFrame
applyHaircut Nothing cf = cf 
applyHaircut (Just A.ExtraStress{A.poolHairCut = Nothing}) cf = cf
applyHaircut (Just A.ExtraStress{A.poolHairCut = Just haircuts}) (CF.CashFlowFrame st txns)
  = CF.CashFlowFrame st $ 
      (\txn -> foldr 
                 (\fn acc -> fn acc ) 
                 txn 
                 (applyHaircutTxn <$> haircuts) ) <$> txns
    where
      applyHaircutTxn (CollectedInterest,r) 
                      (CF.MortgageDelinqFlow d bal prin interest ppy delinq def recovery loss irate mbn mppn mst) 
        = CF.MortgageDelinqFlow d bal prin (mulBR interest (1-r)) ppy delinq def recovery loss irate mbn mppn mst
      applyHaircutTxn (CollectedPrincipal,r)
                      (CF.MortgageDelinqFlow d bal prin interest ppy delinq def recovery loss irate mbn mppn mst) 
        = CF.MortgageDelinqFlow d bal (mulBR prin (1-r)) interest ppy delinq def recovery loss irate mbn mppn mst
      applyHaircutTxn (CollectedRecoveries,r)
                      (CF.MortgageDelinqFlow d bal prin interest ppy delinq def recovery loss irate mbn mppn mst) 
        = CF.MortgageDelinqFlow d bal prin interest ppy delinq def (mulBR recovery (1-r)) loss irate mbn mppn mst
      applyHaircutTxn (CollectedPrepayment,r)
                      (CF.MortgageDelinqFlow d bal prin interest ppy delinq def recovery loss irate mbn mppn mst) 
        = CF.MortgageDelinqFlow d bal prin interest (mulBR ppy (1-r)) delinq def recovery loss irate mbn mppn mst
      applyHaircutTxn (CollectedPrepaymentPenalty,r)
                      (CF.MortgageDelinqFlow d bal prin interest ppy delinq def recovery loss irate mbn mppn mst) 
        = CF.MortgageDelinqFlow d bal prin interest ppy delinq def recovery loss irate mbn ((\x -> mulBR x (1-r) ) <$> mppn) mst
      
      applyHaircutTxn (CollectedInterest,r) 
                      (CF.MortgageFlow d bal prin interest ppy def recovery loss irate mbn mppn mst) 
        = CF.MortgageFlow d bal prin (mulBR interest (1-r)) ppy def recovery loss irate mbn mppn mst
      applyHaircutTxn (CollectedPrincipal,r)
                      (CF.MortgageFlow d bal prin interest ppy def recovery loss irate mbn mppn mst) 
        = CF.MortgageFlow d bal (mulBR prin (1-r)) interest ppy def recovery loss irate mbn mppn mst
      applyHaircutTxn (CollectedRecoveries,r)
                      (CF.MortgageFlow d bal prin interest ppy def recovery loss irate mbn mppn mst) 
        = CF.MortgageFlow d bal prin interest ppy def (mulBR recovery (1-r)) loss irate mbn mppn mst
      applyHaircutTxn (CollectedPrepayment,r)
                      (CF.MortgageFlow d bal prin interest ppy def recovery loss irate mbn mppn mst) 
        = CF.MortgageFlow d bal prin interest (mulBR ppy (1-r)) def recovery loss irate mbn mppn mst
      applyHaircutTxn (CollectedPrepaymentPenalty,r)
                      (CF.MortgageFlow d bal prin interest ppy def recovery loss irate mbn mppn mst)
        = CF.MortgageFlow d bal prin interest ppy def recovery loss irate mbn ((\x -> mulBR x (1-r) ) <$> mppn) mst
      
      applyHaircutTxn _ _ = error "Not implemented"
   
-- ^ apply a penalty cashflow
patchPrepayPenaltyFlow :: (Int,Maybe PrepayPenaltyType) -> CF.CashFlowFrame -> CF.CashFlowFrame
patchPrepayPenaltyFlow (ot,mPpyPen) mflow@(CF.CashFlowFrame st trs) 
  = let 
      --(startDate,endDate) = CF.getDateRangeCashFlowFrame mflow
      prepaymentFlow = CF.mflowPrepayment <$> trs
      flowSize = CF.sizeCashFlowFrame mflow
    in 
      case mPpyPen of 
        Nothing -> mflow
        Just (ByTerm cutoff rate0 rate1) -> 
          let 
            rs = lastN flowSize $ replicate cutoff rate0 ++ replicate (ot-cutoff) rate1
          in 
            CF.CashFlowFrame st $ CF.setPrepaymentPenaltyFlow (zipWith mulBR prepaymentFlow rs) trs
        Just (FixAmount amt mCutoff) -> 
          let 
            projFlow = case mCutoff of 
                         Nothing -> replicate flowSize amt
                         Just cutoff -> lastN flowSize $ replicate cutoff amt ++ replicate (ot-cutoff) 0 
            actFlow = [ if ppy > 0 then 
                          f
                        else
                          0
                        | (f,ppy) <- zip projFlow prepaymentFlow]
          in 
            CF.CashFlowFrame st $ CF.setPrepaymentPenaltyFlow actFlow trs
        Just (FixPct r mCutoff) ->
          let 
            rs = case mCutoff of 
                   Nothing -> replicate flowSize r
                   Just cutoff -> lastN flowSize $ replicate cutoff r ++ replicate (ot-cutoff) 0
          in
            CF.CashFlowFrame st $ CF.setPrepaymentPenaltyFlow (zipWith mulBR prepaymentFlow rs) trs
        Just (Sliding sr changeRate) -> 
          let 
            rs = lastN flowSize $ paddingDefault 0 (0:[sr,(sr-changeRate)..0]) ot
          in
            CF.CashFlowFrame st $ CF.setPrepaymentPenaltyFlow (zipWith mulBR prepaymentFlow rs) trs
        Just (StepDown ps) ->
          let 
            rs = lastN flowSize $ paddingDefault 0 (concat [ replicate n r | (n,r) <- ps]) ot
          in 
            CF.CashFlowFrame st $ CF.setPrepaymentPenaltyFlow (zipWith mulBR prepaymentFlow rs) trs

getRecoveryLag :: A.RecoveryAssumption -> Int
getRecoveryLag (A.Recovery (_,lag)) = lag 
getRecoveryLag (A.RecoveryTiming (_,rs)) = length rs

getRecoveryLagFromAssumption :: A.AssetPerfAssumption -> Maybe Int
getRecoveryLagFromAssumption (A.MortgageAssump _ _ (Just ra) _) = Just $ getRecoveryLag ra
getRecoveryLagFromAssumption (A.MortgageDeqAssump _ _ (Just ra) _) = Just $ getRecoveryLag ra
getRecoveryLagFromAssumption (A.LoanAssump _ _ (Just ra) _) = Just $ getRecoveryLag ra
getRecoveryLagFromAssumption (A.InstallmentAssump _ _ (Just ra) _) = Just $ getRecoveryLag ra
getRecoveryLagFromAssumption (A.ReceivableAssump _ (Just ra) _) = Just $ getRecoveryLag ra
getRecoveryLagFromAssumption _ = Nothing


decreaseBorrowerNum :: Balance -> Balance -> Maybe BorrowerNum -> Maybe Int
decreaseBorrowerNum bb 0 mBn = Nothing
decreaseBorrowerNum bb eb mBn 
  = case mBn of
      Nothing -> Nothing::(Maybe BorrowerNum)
      Just 0  -> Nothing::(Maybe BorrowerNum)
      Just bn -> Just $ round $ fromRational $ mulIR bn downRate::(Maybe BorrowerNum)
    where 
      downRate = if eb == 0 then 
                   0.0
                 else
                   divideBB eb bb

-- | given a list of future cashflows and patch recovery & loss
patchLossRecovery :: [CF.TsRow] -> Maybe A.RecoveryAssumption -> [CF.TsRow]
patchLossRecovery trs Nothing 
  = CF.dropTailEmptyTxns $ [ CF.tsSetRecovery 0 (CF.tsSetLoss d r) | (d,r) <- zip defaultVec trs ] -- `debug` ("Hit Nothign on recovery"++ show defaultVec)
    where 
      defaultVec = mflowDefault <$> trs

-- ^ make sure trs has empty rows with length=lag. as it drop extended rows
patchLossRecovery trs (Just (A.Recovery (rr,lag)))
  = CF.dropTailEmptyTxns $ [ CF.tsSetRecovery recovery (CF.tsSetLoss loss r) | (r,recovery,loss) <- zip3 trs recoveryAfterLag lossVecAfterLag]
    where 
      defaultVec = mflowDefault <$> trs
      recoveriesVec = (`mulBR` rr) <$> defaultVec -- `debug` ("Default Vec"++ show defaultVec)
      recoveryAfterLag = replicate lag 0.0 ++ recoveriesVec --  `debug` ("recovery"++ show recoveriesVec)
      lossVec = (`mulBR` (1-rr)) <$> defaultVec  --  `debug` ("Rec after lag"++ show recoveryAfterLag)
      lossVecAfterLag = replicate lag 0.0 ++ lossVec  -- drop last lag elements

patchLossRecovery trs (Just (A.RecoveryTiming (rr,recoveryTimingDistribution)))
  = CF.dropTailEmptyTxns $ [ CF.tsSetRecovery recVal (CF.tsSetLoss loss r) | (recVal,loss,r) <- zip3 sumRecovery sumLoss trs ]
    where
      cfLength = length trs -- cashflow length
      rLength = length recoveryTimingDistribution  -- recovery length
      defaultVec = mflowDefault <$> trs  -- default balance of each row

      rs = (rr *) <$> recoveryTimingDistribution 

      recoveriesVec = [ mulBR defaultVal <$> rs  | defaultVal <- defaultVec ] 
      
      offsets = [0..(length defaultVec - rLength)]
      
      paddedRecoveries = [ paddingDefault 0 (replicate prePadding 0 ++ recVal) cfLength 
                          | (prePadding,recVal) <- zip offsets recoveriesVec ]

      sumRecovery = sum <$> transpose paddedRecoveries
      lossVec = [ mulBR defaultVal (1-rr) | defaultVal <- defaultVec ]
      sumLoss = replicate (pred rLength) 0.0 ++ lossVec