{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE InstanceSigs #-}
module AssetClass.FixedAsset
()
where
import qualified Data.Time as T
import Data.Ratio
import Data.Aeson hiding (json)
import Language.Haskell.TH
import Data.Maybe
import Data.List
import Data.Aeson.TH
import qualified Data.Map as Map
import Data.Aeson.Types
import GHC.Generics
import qualified Assumptions as A
import Types hiding (startDate)
import Lib
import Util
import DateUtil
import qualified Cashflow as CF
import AssetClass.AssetBase
import Debug.Trace
import AssetClass.AssetCashflow
import qualified Asset as Ast
import Asset (Asset(projCashflow))
import Assumptions (AssetDelinqPerfAssumption(DummyDelinqAssump))
debug :: c -> [Char] -> c
debug = ([Char] -> c -> c) -> c -> [Char] -> c
forall a b c. (a -> b -> c) -> b -> a -> c
flip [Char] -> c -> c
forall a. [Char] -> a -> a
trace
calcAmortAmt ::FixedAsset -> Either String [Balance]
calcAmortAmt :: FixedAsset -> Either [Char] [Balance]
calcAmortAmt fa :: FixedAsset
fa@(FixedAsset fai :: OriginalInfo
fai@FixedAssetInfo{originBalance :: OriginalInfo -> Balance
originBalance=Balance
ob, accRule :: OriginalInfo -> AmortRule
accRule=AmortRule
ar, originTerm :: OriginalInfo -> Int
originTerm=Int
ot
,residualBalance :: OriginalInfo -> Balance
residualBalance=Balance
rb ,capacity :: OriginalInfo -> Capacity
capacity=Capacity
cap} Balance
b Int
rt)
= case AmortRule
ar of
AmortRule
StraightLine -> [Balance] -> Either [Char] [Balance]
forall a b. b -> Either a b
Right ([Balance] -> Either [Char] [Balance])
-> [Balance] -> Either [Char] [Balance]
forall a b. (a -> b) -> a -> b
$ Int -> Balance -> [Balance]
forall a. Int -> a -> [a]
replicate Int
ot (Balance -> [Balance]) -> Balance -> [Balance]
forall a b. (a -> b) -> a -> b
$ Balance -> Int -> Balance
divideBI (Balance
bBalance -> Balance -> Balance
forall a. Num a => a -> a -> a
-Balance
rb) Int
rt
AmortRule
DecliningBalance ->
let
amortizeRate :: Balance
amortizeRate = Ratio Int -> Balance
forall a b. (Real a, Fractional b) => a -> b
realToFrac (Ratio Int -> Balance) -> Ratio Int -> Balance
forall a b. (a -> b) -> a -> b
$ Int
2 Int -> Int -> Ratio Int
forall a. Integral a => a -> a -> Ratio a
% Int
ot
futureBals' :: [Balance]
futureBals' = Balance -> [Balance] -> [Balance]
forall a. Fractional a => a -> [a] -> [a]
scaleByFstElement Balance
b ([Balance] -> [Balance]) -> [Balance] -> [Balance]
forall a b. (a -> b) -> a -> b
$ Int -> [Balance] -> [Balance]
forall a. Int -> [a] -> [a]
lastN (Int -> Int
forall a. Enum a => a -> a
succ Int
rt) ([Balance] -> [Balance]) -> [Balance] -> [Balance]
forall a b. (a -> b) -> a -> b
$ (Balance -> Balance -> Balance)
-> Balance -> [Balance] -> [Balance]
forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl (\Balance
acc Balance
r -> Balance
acc Balance -> Balance -> Balance
forall a. Num a => a -> a -> a
* (Balance
1 Balance -> Balance -> Balance
forall a. Num a => a -> a -> a
- Balance
r)) Balance
ob (Int -> Balance -> [Balance]
forall a. Int -> a -> [a]
replicate Int
ot Balance
amortizeRate)
futureBals'' :: [Balance]
futureBals'' = (Balance -> (Balance, Balance, Int) -> Balance)
-> Balance -> [(Balance, Balance, Int)] -> [Balance]
forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl
(\Balance
acc (Balance
bal',Balance
amt',Int
rt') ->
(Balance
acc Balance -> Balance -> Balance
forall a. Num a => a -> a -> a
- (Balance -> Balance -> Balance
forall a. Ord a => a -> a -> a
max Balance
amt' (Balance -> Int -> Balance
divideBI (Balance
acc Balance -> Balance -> Balance
forall a. Num a => a -> a -> a
- Balance
rb) (Int
rt Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
rt'))))
)
([Balance] -> Balance
forall a. HasCallStack => [a] -> a
head [Balance]
futureBals')
([Balance] -> [Balance] -> [Int] -> [(Balance, Balance, Int)]
forall a b c. [a] -> [b] -> [c] -> [(a, b, c)]
zip3 [Balance]
futureBals' ([Balance] -> [Balance]
forall a. Num a => [a] -> [a]
diffNum [Balance]
futureBals') [Int
0..Int -> Int
forall a. Enum a => a -> a
succ Int
rt])
in
[Balance] -> Either [Char] [Balance]
forall a b. b -> Either a b
Right ([Balance] -> [Balance]
forall a. Num a => [a] -> [a]
diffNum [Balance]
futureBals'')
AmortRule
_ -> [Char] -> Either [Char] [Balance]
forall a b. a -> Either a b
Left ([Char]
"Not implemented for depreciation rule"[Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++AmortRule -> [Char]
forall a. Show a => a -> [Char]
show AmortRule
ar)
calcAmortBals ::FixedAsset -> Either String [Balance]
calcAmortBals :: FixedAsset -> Either [Char] [Balance]
calcAmortBals fa :: FixedAsset
fa@(FixedAsset fai :: OriginalInfo
fai@FixedAssetInfo{originBalance :: OriginalInfo -> Balance
originBalance=Balance
ob, accRule :: OriginalInfo -> AmortRule
accRule=AmortRule
ar, originTerm :: OriginalInfo -> Int
originTerm=Int
ot
,residualBalance :: OriginalInfo -> Balance
residualBalance=Balance
rb ,capacity :: OriginalInfo -> Capacity
capacity=Capacity
cap} Balance
b Int
rt)
= do
[Balance]
bals <- FixedAsset -> Either [Char] [Balance]
calcAmortAmt FixedAsset
fa
[Balance] -> Either [Char] [Balance]
forall a. a -> Either [Char] a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Balance] -> Either [Char] [Balance])
-> [Balance] -> Either [Char] [Balance]
forall a b. (a -> b) -> a -> b
$ (Balance -> Balance -> Balance)
-> Balance -> [Balance] -> [Balance]
forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl (-) Balance
ob [Balance]
bals
instance Ast.Asset FixedAsset where
calcCashflow :: FixedAsset
-> Date -> Maybe [RateAssumption] -> Either [Char] CashFlowFrame
calcCashflow fa :: FixedAsset
fa@(FixedAsset {}) Date
asOfDay Maybe [RateAssumption]
_ =
(CashFlowFrame, Map CutoffFields Balance) -> CashFlowFrame
forall a b. (a, b) -> a
fst ((CashFlowFrame, Map CutoffFields Balance) -> CashFlowFrame)
-> Either [Char] (CashFlowFrame, Map CutoffFields Balance)
-> Either [Char] CashFlowFrame
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FixedAsset
-> Date
-> AssetPerf
-> Maybe [RateAssumption]
-> Either [Char] (CashFlowFrame, Map CutoffFields Balance)
forall a.
Asset a =>
a
-> Date
-> AssetPerf
-> Maybe [RateAssumption]
-> Either [Char] (CashFlowFrame, Map CutoffFields Balance)
projCashflow FixedAsset
fa Date
asOfDay (Ts -> Ts -> Maybe Int -> AssetPerfAssumption
A.FixedAssetAssump ([(Date, Rate)] -> Ts
mkTs []) ([(Date, Rate)] -> Ts
mkTs []) Maybe Int
forall a. Maybe a
Nothing, AssetDelinqPerfAssumption
A.DummyDelinqAssump, AssetDefaultedPerfAssumption
A.DummyDefaultAssump) Maybe [RateAssumption]
forall a. Maybe a
Nothing
getCurrentBal :: FixedAsset -> Balance
getCurrentBal fa :: FixedAsset
fa@(FixedAsset fai :: OriginalInfo
fai@FixedAssetInfo{originBalance :: OriginalInfo -> Balance
originBalance=Balance
ob, accRule :: OriginalInfo -> AmortRule
accRule=AmortRule
ar, originTerm :: OriginalInfo -> Int
originTerm=Int
ot
,residualBalance :: OriginalInfo -> Balance
residualBalance=Balance
rb ,capacity :: OriginalInfo -> Capacity
capacity=Capacity
cap} Balance
curBal Int
rt)
= Balance
curBal
resetToOrig :: FixedAsset -> FixedAsset
resetToOrig fa :: FixedAsset
fa@(FixedAsset fai :: OriginalInfo
fai@FixedAssetInfo{originBalance :: OriginalInfo -> Balance
originBalance=Balance
ob, accRule :: OriginalInfo -> AmortRule
accRule=AmortRule
ar, originTerm :: OriginalInfo -> Int
originTerm=Int
ot
,residualBalance :: OriginalInfo -> Balance
residualBalance=Balance
rb ,capacity :: OriginalInfo -> Capacity
capacity=Capacity
cap} Balance
b Int
rt)
= OriginalInfo -> Balance -> Int -> FixedAsset
FixedAsset OriginalInfo
fai Balance
b Int
ot
getPaymentDates :: FixedAsset -> Int -> [Date]
getPaymentDates
(FixedAsset fo :: OriginalInfo
fo@FixedAssetInfo{startDate :: OriginalInfo -> Date
startDate=Date
sd ,period :: OriginalInfo -> Period
period=Period
p,originTerm :: OriginalInfo -> Int
originTerm=Int
ot} Balance
_ Int
rt)
Int
extra
= Date -> Period -> Int -> [Date]
genDates Date
sd Period
p (Int
otInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
extra)
projCashflow :: FixedAsset
-> Date
-> AssetPerf
-> Maybe [RateAssumption]
-> Either [Char] (CashFlowFrame, Map CutoffFields Balance)
projCashflow fa :: FixedAsset
fa@(FixedAsset fai :: OriginalInfo
fai@FixedAssetInfo{originBalance :: OriginalInfo -> Balance
originBalance=Balance
ob, accRule :: OriginalInfo -> AmortRule
accRule=AmortRule
ar, originTerm :: OriginalInfo -> Int
originTerm=Int
ot
,residualBalance :: OriginalInfo -> Balance
residualBalance=Balance
rb ,capacity :: OriginalInfo -> Capacity
capacity=Capacity
cap} Balance
curBalance Int
rt)
Date
asOfDay
(A.FixedAssetAssump Ts
uCurve Ts
pCurve Maybe Int
mExtPeriods,AssetDelinqPerfAssumption
_,AssetDefaultedPerfAssumption
_)
Maybe [RateAssumption]
_
= let
extPeriods :: Int
extPeriods = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
0 Maybe Int
mExtPeriods
cfLength :: Int
cfLength = Int
rt Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
extPeriods
pdates :: [Date]
pdates = Int -> [Date] -> [Date]
forall a. Int -> [a] -> [a]
lastN Int
cfLength ([Date] -> [Date]) -> [Date] -> [Date]
forall a b. (a -> b) -> a -> b
$ FixedAsset -> Int -> [Date]
forall a. Asset a => a -> Int -> [Date]
Ast.getPaymentDates FixedAsset
fa Int
extPeriods
capacityCaps :: [Balance]
capacityCaps = case Capacity
cap of
FixedCapacity Balance
b -> Int -> Balance -> [Balance]
forall a. Int -> a -> [a]
replicate Int
cfLength Balance
b
CapacityByTerm [(Int, Balance)]
tbl -> Int -> [Balance] -> [Balance]
forall a. Int -> [a] -> [a]
lastN Int
cfLength ([Balance] -> [Balance]) -> [Balance] -> [Balance]
forall a b. (a -> b) -> a -> b
$ [[Balance]] -> [Balance]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ Int -> Balance -> [Balance]
forall a. Int -> a -> [a]
replicate Int
i Balance
b | (Int
i,Balance
b) <- [(Int, Balance)]
tbl ] [Balance] -> [Balance] -> [Balance]
forall a. [a] -> [a] -> [a]
++ (Int -> Balance -> [Balance]
forall a. Int -> a -> [a]
replicate Int
extPeriods ((Int, Balance) -> Balance
forall a b. (a, b) -> b
snd ([(Int, Balance)] -> (Int, Balance)
forall a. HasCallStack => [a] -> a
last [(Int, Balance)]
tbl)))
utilsVec :: [Rate]
utilsVec = Ts -> CutoffType -> [Date] -> [Rate]
getValByDates Ts
uCurve CutoffType
Inc [Date]
pdates
units :: [Balance]
units = [ Balance -> Rate -> Balance
mulBR Balance
c Rate
u | (Rate
u,Balance
c) <- [Rate] -> [Balance] -> [(Rate, Balance)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Rate]
utilsVec [Balance]
capacityCaps]
prices :: [Rate]
prices = Ts -> CutoffType -> [Date] -> [Rate]
getValByDates Ts
pCurve CutoffType
Inc [Date]
pdates
cash :: [Balance]
cash = [ Balance -> Rate -> Balance
mulBR Balance
u Rate
p | (Rate
p,Balance
u) <- [Rate] -> [Balance] -> [(Rate, Balance)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Rate]
prices [Balance]
units]
in
do
[Balance]
scheduleAmt <- FixedAsset -> Either [Char] [Balance]
calcAmortAmt FixedAsset
fa
let amortizedBals :: [Balance]
amortizedBals = Int -> [Balance] -> [Balance]
forall a. Int -> [a] -> [a]
lastN Int
cfLength ([Balance] -> [Balance]) -> [Balance] -> [Balance]
forall a b. (a -> b) -> a -> b
$ [Balance]
scheduleAmt [Balance] -> [Balance] -> [Balance]
forall a. [a] -> [a] -> [a]
++ Int -> Balance -> [Balance]
forall a. Int -> a -> [a]
replicate Int
extPeriods Balance
0
let scheduleBals :: [Balance]
scheduleBals = [Balance] -> [Balance]
forall a. HasCallStack => [a] -> [a]
tail ([Balance] -> [Balance]) -> [Balance] -> [Balance]
forall a b. (a -> b) -> a -> b
$ (Balance -> Balance -> Balance)
-> Balance -> [Balance] -> [Balance]
forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl (-) Balance
curBalance ([Balance]
amortizedBals [Balance] -> [Balance] -> [Balance]
forall a. [a] -> [a] -> [a]
++ [Balance
0])
let cumuDep :: Balance
cumuDep = Balance
ob Balance -> Balance -> Balance
forall a. Num a => a -> a -> a
- Balance
curBalance
let cumuDepreciation :: [Balance]
cumuDepreciation = [Balance] -> [Balance]
forall a. HasCallStack => [a] -> [a]
tail ([Balance] -> [Balance]) -> [Balance] -> [Balance]
forall a b. (a -> b) -> a -> b
$ (Balance -> Balance -> Balance)
-> Balance -> [Balance] -> [Balance]
forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl Balance -> Balance -> Balance
forall a. Num a => a -> a -> a
(+) Balance
cumuDep [Balance]
amortizedBals
let txns :: [TsRow]
txns = (Date
-> Balance -> Balance -> Balance -> Balance -> Balance -> TsRow)
-> [Date]
-> [Balance]
-> [Balance]
-> [Balance]
-> [Balance]
-> [Balance]
-> [TsRow]
forall a b c d e f g.
(a -> b -> c -> d -> e -> f -> g)
-> [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g]
zipWith6 Date
-> Balance -> Balance -> Balance -> Balance -> Balance -> TsRow
CF.FixedFlow [Date]
pdates [Balance]
scheduleBals [Balance]
amortizedBals [Balance]
cumuDepreciation [Balance]
units [Balance]
cash
let futureTxns :: [TsRow]
futureTxns = CutoffType -> DateDirection -> Date -> [TsRow] -> [TsRow]
forall ts.
TimeSeries ts =>
CutoffType -> DateDirection -> Date -> [ts] -> [ts]
cutBy CutoffType
Inc DateDirection
Future Date
asOfDay [TsRow]
txns
let begBal :: Balance
begBal = [TsRow] -> Balance
CF.buildBegBal [TsRow]
futureTxns
(CashFlowFrame, Map CutoffFields Balance)
-> Either [Char] (CashFlowFrame, Map CutoffFields Balance)
forall a. a -> Either [Char] a
forall (m :: * -> *) a. Monad m => a -> m a
return ((CashFlowFrame, Map CutoffFields Balance)
-> Either [Char] (CashFlowFrame, Map CutoffFields Balance))
-> (CashFlowFrame, Map CutoffFields Balance)
-> Either [Char] (CashFlowFrame, Map CutoffFields Balance)
forall a b. (a -> b) -> a -> b
$ (BeginStatus -> [TsRow] -> CashFlowFrame
CF.CashFlowFrame (Balance
begBal,Date
asOfDay,Maybe Balance
forall a. Maybe a
Nothing) ([TsRow] -> CashFlowFrame) -> [TsRow] -> CashFlowFrame
forall a b. (a -> b) -> a -> b
$ [TsRow]
futureTxns, Map CutoffFields Balance
forall k a. Map k a
Map.empty)