{-# 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


-- life time schedule amortization amount list
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)
          -- straigh lines
          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)