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

module Analytics (calcConvexity,calcDuration,pv,calcWAL,pv2,pv3
      ,fv2,pv21,calcRequiredAmtForIrrAtDate,calcIRR
      ,calcSurvivorFactors)

  where 
import Types
import Lib
import Util
import DateUtil
import Data.Aeson hiding (json)
import Language.Haskell.TH
import Data.Aeson.TH
import Data.Aeson.Types
import GHC.Generics
import Data.Ratio
import Numeric.RootFinding

import Debug.Trace
debug :: c -> String -> c
debug = (String -> c -> c) -> c -> String -> c
forall a b c. (a -> b -> c) -> b -> a -> c
flip String -> c -> c
forall a. String -> a -> a
trace

calcSurvivorFactors :: Date -> [Date] -> Double -> [Double]
calcSurvivorFactors :: Date -> [Date] -> Double -> [Double]
calcSurvivorFactors Date
sd [Date]
ds Double
0 = Int -> Double -> [Double]
forall a. Int -> a -> [a]
replicate ([Date] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Date]
ds) Double
1.0 
calcSurvivorFactors Date
sd [Date]
ds Double
survivalRate = 
  let 
    [Double]
yearFractions::[Double] = [ Integer -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac (Date -> Date -> Integer
daysBetween Date
sd Date
d) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
365.0 | Date
d <- [Date]
ds ]
    factors :: [Double]
factors = [ (Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
survivalRate) Double -> Double -> Double
forall a. Floating a => a -> a -> a
** Double
x | Double
x <- [Double]
yearFractions ]
  in 
    [Double]
factors

-- ^ calculate the Weighted Average Life of cashflow, with unit option to Monthly or Yearly
calcWAL :: TimeHorizion -> Balance -> Date -> [(Balance,Date)] -> Balance 
calcWAL :: TimeHorizion -> Amount -> Date -> [(Amount, Date)] -> Amount
calcWAL TimeHorizion
th Amount
bal Date
d [(Amount, Date)]
ps = 
  let 
    interval :: Integer
interval = case TimeHorizion
th of
                  TimeHorizion
ByYear -> Integer
365
                  TimeHorizion
ByMonth -> Integer
30
    weightedAmts :: [Amount]
weightedAmts = [ Amount -> Rate -> Amount
mulBR Amount
futureAmt ((Date -> Date -> Integer
daysBetween Date
d Date
futureDate) Integer -> Integer -> Rate
forall a. Integral a => a -> a -> Ratio a
% Integer
interval)  | (Amount
futureAmt,Date
futureDate) <- [(Amount, Date)]
ps ]
  in 
    [Amount] -> Amount
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [Amount]
weightedAmts Amount -> Amount -> Amount
forall a. Fractional a => a -> a -> a
/ Amount
bal

calcDuration :: DayCount -> Date -> [(Date,Balance)] -> Ts -> Rate
calcDuration :: DayCount -> Date -> [(Date, Amount)] -> Ts -> Rate
calcDuration DayCount
dc Date
d [(Date, Amount)]
ps Ts
pricingCurve 
  = (((Date, Amount) -> Rate -> Rate)
-> Rate -> [(Date, Amount)] -> Rate
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\(Date
_d,Amount
_b) Rate
acc ->
                    Rate -> Rate -> Rate
forall a. Num a => a -> a -> a
(*) 
                      (Amount -> Amount -> Rate
divideBB (Ts -> Date -> Date -> Amount -> Amount
pv Ts
pricingCurve Date
d Date
_d Amount
_b) Amount
presentValue) 
                      (DayCount -> Date -> Date -> Rate
yearCountFraction DayCount
dc Date
d Date
_d)
                    Rate -> Rate -> Rate
forall a. Num a => a -> a -> a
+ Rate
acc)
                    Rate
0.0000
                    [(Date, Amount)]
ps)
    where 
      presentValue :: Amount
presentValue = [Amount] -> Amount
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [ Ts -> Date -> Date -> Amount -> Amount
pv Ts
pricingCurve Date
d Date
_d Amount
_b | (Date
_d,Amount
_b) <- [(Date, Amount)]
ps ] 

calcConvexity :: DayCount -> Date -> [(Date,Balance)] -> Ts -> Rate
calcConvexity :: DayCount -> Date -> [(Date, Amount)] -> Ts -> Rate
calcConvexity DayCount
dc Date
d [(Date, Amount)]
ps Ts
pricingCurve 
  = Double -> Rate
forall a. Real a => a -> Rate
toRational (Double -> Rate) -> Double -> Rate
forall a b. (a -> b) -> a -> b
$
      Double -> Double -> Double
forall a. Num a => a -> a -> a
(*)
        Double
presentValue' (Double -> Double) -> Double -> Double
forall a b. (a -> b) -> a -> b
$
        (((Double, Rate, Double) -> Double -> Double)
-> Double -> [(Double, Rate, Double)] -> Double
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\(Double
_t,Rate
_c,Double
_f) Double
acc ->
                      (Double
_t Double -> Double -> Double
forall a. Num a => a -> a -> a
* (Double
_t Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
1) Double -> Double -> Double
forall a. Num a => a -> a -> a
* Rate -> Double
forall a. Fractional a => Rate -> a
fromRational Rate
_c) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ ((Double
1.000 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
_f) Double -> Double -> Double
forall a. Floating a => a -> a -> a
** (Double
_tDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
2))
                      )
                      Double
0.0000
                      ([Double] -> [Rate] -> [Double] -> [(Double, Rate, Double)]
forall a b c. [a] -> [b] -> [c] -> [(a, b, c)]
zip3 [Double]
ts [Rate]
payments [Double]
pvFactors)) -- `debug` ("'v"++show presentValue'++"others"++ show (zip3 ts payments pvFactors))
    where 
      [Double]
pvFactors::[Double] = Rate -> Double
forall a. Fractional a => Rate -> a
fromRational (Rate -> Double) -> (Date -> Rate) -> Date -> Double
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ts -> CutoffType -> Date -> Rate
getValByDate Ts
pricingCurve CutoffType
Inc (Date -> Double)
-> ((Date, Amount) -> Date) -> (Date, Amount) -> Double
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Date, Amount) -> Date
forall a b. (a, b) -> a
fst ((Date, Amount) -> Double) -> [(Date, Amount)] -> [Double]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Date, Amount)]
ps
      Double
presentValue'::Double = Double
1 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ (Rate -> Double
forall a. Fractional a => Rate -> a
fromRational (Rate -> Double) -> (Amount -> Rate) -> Amount -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Amount -> Rate
forall a. Real a => a -> Rate
toRational) ([Amount] -> Amount
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [ Ts -> Date -> Date -> Amount -> Amount
pv Ts
pricingCurve Date
d Date
_d Amount
_b | (Date
_d,Amount
_b) <- [(Date, Amount)]
ps ])
      payments :: [Rate]
payments = Amount -> Rate
forall a. Real a => a -> Rate
toRational (Amount -> Rate)
-> ((Date, Amount) -> Amount) -> (Date, Amount) -> Rate
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Date, Amount) -> Amount
forall a b. (a, b) -> b
snd ((Date, Amount) -> Rate) -> [(Date, Amount)] -> [Rate]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Date, Amount)]
ps
      [Double]
ts::[Double] = Rate -> Double
forall a. Fractional a => Rate -> a
fromRational (Rate -> Double) -> (Date -> Rate) -> Date -> Double
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> DayCount -> Date -> Date -> Rate
yearCountFraction DayCount
dc Date
d (Date -> Double)
-> ((Date, Amount) -> Date) -> (Date, Amount) -> Double
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Date, Amount) -> Date
forall a b. (a, b) -> a
fst ((Date, Amount) -> Double) -> [(Date, Amount)] -> [Double]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [(Date, Amount)]
ps

-- ^ calculate present value of input amount in future with given a curve and PV date
pv :: Ts -> Date -> Date -> Amount -> Amount
pv :: Ts -> Date -> Date -> Amount -> Amount
pv Ts
pc Date
today Date
d Amount
amt = 
  Double -> Amount
forall a b. (Real a, Fractional b) => a -> b
realToFrac (Double -> Amount) -> Double -> Amount
forall a b. (a -> b) -> a -> b
$ (Amount -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac Amount
amt) Double -> Double -> Double
forall a. Num a => a -> a -> a
* (Double
1 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
factor) --  `debug` ("DF:"++show factor++" PV AMT"++show amt)
  where
    Double
distance::Double = Integer -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Double) -> Integer -> Double
forall a b. (a -> b) -> a -> b
$ Date -> Date -> Integer
daysBetween Date
today Date
d
    discount_rate :: Double
discount_rate = Rate -> Double
forall a. Fractional a => Rate -> a
fromRational (Rate -> Double) -> Rate -> Double
forall a b. (a -> b) -> a -> b
$ Ts -> CutoffType -> Date -> Rate
getValByDate Ts
pc CutoffType
Exc Date
d -- `debug` ("Get val by ts"++show pc ++">>d"++ show d)
    Double
factor::Double = (Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac Double
discount_rate) Double -> Double -> Double
forall a. Floating a => a -> a -> a
** (Double
distance Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
365) --  `debug` ("discount_rate"++show(discount_rate) ++" dist days=>"++show(distance))

-- ^ calculate present value in the future using constant rate
pv2 :: IRate -> Date -> Date -> Amount -> Amount
pv2 :: IRate -> Date -> Date -> Amount -> Amount
pv2 IRate
discount_rate Date
today Date
d Amount
amt 
  | Date
today Date -> Date -> Bool
forall a. Eq a => a -> a -> Bool
== Date
d = Amount
amt
  | Bool
otherwise 
    = Double -> Amount
forall a b. (Real a, Fractional b) => a -> b
realToFrac (Double -> Amount) -> Double -> Amount
forall a b. (a -> b) -> a -> b
$ (Amount -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac Amount
amt) Double -> Double -> Double
forall a. Num a => a -> a -> a
* (Double
1Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/Double
denominator)  -- `debug` ("pv: cash"++ show amt++" deno"++ show denominator++">> rate"++show discount_rate)
      where
        Double
denominator::Double = (Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ IRate -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac IRate
discount_rate) Double -> Double -> Double
forall a. Floating a => a -> a -> a
** (Double
distance Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
365)
        Double
distance::Double = Integer -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Double) -> Integer -> Double
forall a b. (a -> b) -> a -> b
$ Date -> Date -> Integer
daysBetween Date
today Date
d -- `debug` ("days betwwen"++ show (daysBetween today d)++">>"++ show d ++ ">>today>>"++ show today)

-- ^ calculate present value to specific date given a series of amount with dates
pv21 :: IRate -> Date -> [Date] -> [Amount] -> Balance
pv21 :: IRate -> Date -> [Date] -> [Amount] -> Amount
pv21 IRate
r Date
d [Date]
ds [Amount]
vs = [Amount] -> Amount
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [ IRate -> Date -> Date -> Amount -> Amount
pv2 IRate
r Date
d Date
_d Amount
amt | (Date
_d,Amount
amt) <- [Date] -> [Amount] -> [(Date, Amount)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Date]
ds [Amount]
vs ]

-- ^ using double for ridder's method

pv2' :: Double -> Date -> Date -> Double -> Double
pv2' :: Double -> Date -> Date -> Double -> Double
pv2' Double
r Date
today Date
d Double
amt 
  | Double
amt Double -> Double -> Bool
forall a. Eq a => a -> a -> Bool
== Double
0 = Double
0
  | Date
today Date -> Date -> Bool
forall a. Eq a => a -> a -> Bool
== Date
d = Double
amt
  | Bool
otherwise 
    = Double
amt Double -> Double -> Double
forall a. Num a => a -> a -> a
* (Double
1Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/Double
denominator)  -- `debug` ("pv: cash"++ show amt++" deno"++ show denominator++">> rate"++show discount_rate)
      where
        Double
denominator::Double = (Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
r) Double -> Double -> Double
forall a. Floating a => a -> a -> a
** (Double
distance Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
365)
        Double
distance::Double = Integer -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Double) -> Integer -> Double
forall a b. (a -> b) -> a -> b
$ Date -> Date -> Integer
daysBetween Date
today Date
d -- `debug` ("days betwwen"++ show (daysBetween today d)++">>"++ show d ++ ">>today>>"++ show today)

pv22 :: Double -> Date -> [Date] -> [Double] -> Double
pv22 :: Double -> Date -> [Date] -> [Double] -> Double
pv22 Double
r Date
d [Date]
ds [Double]
vs = [Double] -> Double
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [ Double -> Date -> Date -> Double -> Double
pv2' Double
r Date
d Date
_d Double
amt | (Date
_d,Double
amt) <- [Date] -> [Double] -> [(Date, Double)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Date]
ds [Double]
vs ] 

-- ^ calcualte present value given a series of amount with dates
pv3 :: Ts -> Date -> [Date] -> [Amount] -> Balance 
pv3 :: Ts -> Date -> [Date] -> [Amount] -> Amount
pv3 Ts
pvCurve Date
pricingDate [Date]
ds [Amount]
vs 
  = let 
      rs :: [IRate]
rs = Rate -> IRate
forall a. Fractional a => Rate -> a
fromRational (Rate -> IRate) -> [Rate] -> [IRate]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ts -> CutoffType -> [Date] -> [Rate]
getValByDates Ts
pvCurve CutoffType
Inc [Date]
ds
      pvs :: [Amount]
pvs = [ IRate -> Date -> Date -> Amount -> Amount
pv2 IRate
r Date
pricingDate Date
d Amount
amt | (IRate
r,Date
d,Amount
amt) <- [IRate] -> [Date] -> [Amount] -> [(IRate, Date, Amount)]
forall a b c. [a] -> [b] -> [c] -> [(a, b, c)]
zip3 [IRate]
rs [Date]
ds [Amount]
vs ]
    in 
      [Amount] -> Amount
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [Amount]
pvs

pv3' :: Ts -> Date -> [Date] -> [Amount] -> Balance
pv3' :: Ts -> Date -> [Date] -> [Amount] -> Amount
pv3' Ts
pvCurve Date
pricingDate [Date]
ds [Amount]
vs 
  = let 
      rs :: [Double]
rs = Rate -> Double
forall a. Fractional a => Rate -> a
fromRational (Rate -> Double) -> [Rate] -> [Double]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ts -> CutoffType -> [Date] -> [Rate]
getValByDates Ts
pvCurve CutoffType
Inc [Date]
ds
      vs' :: [Double]
vs' = (Rate -> Double
forall a. Fractional a => Rate -> a
fromRational (Rate -> Double) -> (Amount -> Rate) -> Amount -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Amount -> Rate
forall a. Real a => a -> Rate
toRational) (Amount -> Double) -> [Amount] -> [Double]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Amount]
vs
      pvs :: [Double]
pvs = [ Double -> Date -> Date -> Double -> Double
pv2' Double
r Date
pricingDate Date
d Double
amt | (Double
r,Date
d,Double
amt) <- [Double] -> [Date] -> [Double] -> [(Double, Date, Double)]
forall a b c. [a] -> [b] -> [c] -> [(a, b, c)]
zip3 [Double]
rs [Date]
ds [Double]
vs' ]
    in 
      Rate -> Amount
forall a. Fractional a => Rate -> a
fromRational (Rate -> Amount) -> (Double -> Rate) -> Double -> Amount
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> Rate
forall a. Real a => a -> Rate
toRational (Double -> Amount) -> Double -> Amount
forall a b. (a -> b) -> a -> b
$ (Double -> Double -> Double) -> Double -> [Double] -> Double
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr Double -> Double -> Double
forall a. Num a => a -> a -> a
(+) Double
0 [Double]
pvs


fv2 :: IRate -> Date -> Date -> Amount -> Amount
fv2 :: IRate -> Date -> Date -> Amount -> Amount
fv2 IRate
discount_rate Date
today Date
futureDay Amount
amt 
  = Double -> Amount
forall a b. (Real a, Fractional b) => a -> b
realToFrac (Double -> Amount) -> Double -> Amount
forall a b. (a -> b) -> a -> b
$ Amount -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac Amount
amt Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
factor 
  where
    Double
factor::Double = (Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ IRate -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac IRate
discount_rate) Double -> Double -> Double
forall a. Floating a => a -> a -> a
** (Double
distance Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
365)
    Double
distance::Double = Integer -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Integer -> Double) -> Integer -> Double
forall a b. (a -> b) -> a -> b
$ Date -> Date -> Integer
daysBetween Date
today Date
futureDay


calcPvFromIRR :: Double -> [Date] -> [Amount] -> Date -> Double -> Double
calcPvFromIRR :: Double -> [Date] -> [Amount] -> Date -> Double -> Double
calcPvFromIRR Double
irr [] [Amount]
_ Date
d Double
amt = Double
0
calcPvFromIRR Double
irr [Date]
ds [Amount]
vs Date
d Double
amt = 
  let 
    begDate :: Date
begDate = [Date] -> Date
forall a. HasCallStack => [a] -> a
head [Date]
ds
    vs' :: [Double]
vs' = Rate -> Double
forall a. Fractional a => Rate -> a
fromRational (Rate -> Double) -> (Amount -> Rate) -> Amount -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Amount -> Rate
forall a. Real a => a -> Rate
toRational (Amount -> Double) -> [Amount] -> [Double]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Amount]
vs
    pv :: Double
pv = Double -> Date -> [Date] -> [Double] -> Double
pv22 Double
irr Date
begDate ([Date]
ds[Date] -> [Date] -> [Date]
forall a. [a] -> [a] -> [a]
++[Date
d]) ([Double]
vs'[Double] -> [Double] -> [Double]
forall a. [a] -> [a] -> [a]
++[Double
amt])
  in 
    (Rate -> Double
forall a. Fractional a => Rate -> a
fromRational (Rate -> Double) -> (Double -> Rate) -> Double -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> Rate
forall a. Real a => a -> Rate
toRational) Double
pv

-- ^ calculate IRR of a series of cashflow
calcRequiredAmtForIrrAtDate :: Double -> [Date] -> [Amount] -> Date -> Maybe Amount
calcRequiredAmtForIrrAtDate :: Double -> [Date] -> [Amount] -> Date -> Maybe Amount
calcRequiredAmtForIrrAtDate Double
irr [] [Amount]
_ Date
d = Maybe Amount
forall a. Maybe a
Nothing 
calcRequiredAmtForIrrAtDate Double
irr [Date]
ds [Amount]
vs Date
d = 
  let 
    itertimes :: Int
itertimes = Int
500
    def :: RiddersParam
def = RiddersParam { riddersMaxIter :: Int
riddersMaxIter = Int
itertimes, riddersTol :: Tolerance
riddersTol = Double -> Tolerance
RelTol Double
0.00000001}
  in 
    case RiddersParam
-> (Double, Double) -> (Double -> Double) -> Root Double
ridders RiddersParam
def (Double
0.0001,Double
100000000000000) (Double -> [Date] -> [Amount] -> Date -> Double -> Double
calcPvFromIRR Double
irr [Date]
ds [Amount]
vs Date
d) of
      Root Double
finalAmt -> Amount -> Maybe Amount
forall a. a -> Maybe a
Just (Rate -> Amount
forall a. Fractional a => Rate -> a
fromRational (Double -> Rate
forall a. Real a => a -> Rate
toRational Double
finalAmt))
      Root Double
_ -> Maybe Amount
forall a. Maybe a
Nothing

-- ^ calc IRR from a cashflow 
calcIRR :: [Date] -> [Amount] -> Either String Rate
calcIRR :: [Date] -> [Amount] -> Either String Rate
calcIRR  [Date]
_ [] = String -> Either String Rate
forall a b. a -> Either a b
Left String
"No cashflow amount"
calcIRR [] [Amount]
_ = String -> Either String Rate
forall a b. a -> Either a b
Left String
"No cashflow date"
calcIRR [Date]
ds [Amount]
vs
  | (Amount -> Bool) -> [Amount] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Amount -> Amount -> Bool
forall a. Ord a => a -> a -> Bool
>= Amount
0) [Amount]
vs = String -> Either String Rate
forall a b. a -> Either a b
Left (String -> Either String Rate) -> String -> Either String Rate
forall a b. (a -> b) -> a -> b
$ String
"All cashflow can't be all positive:"String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Amount] -> String
forall a. Show a => a -> String
show [Amount]
vs
  | (Amount -> Bool) -> [Amount] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Amount -> Amount -> Bool
forall a. Ord a => a -> a -> Bool
<= Amount
0) [Amount]
vs = String -> Either String Rate
forall a b. a -> Either a b
Left (String -> Either String Rate) -> String -> Either String Rate
forall a b. (a -> b) -> a -> b
$ String
"All cashflow can't be all negative:"String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Amount] -> String
forall a. Show a => a -> String
show [Amount]
vs
  | (Amount -> Bool) -> [Amount] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Amount -> Amount -> Bool
forall a. Eq a => a -> a -> Bool
== Amount
0) [Amount]
vs = String -> Either String Rate
forall a b. a -> Either a b
Left String
"All cashflow can't be all zeros"
  | Bool
otherwise = 
    let 
      itertimes :: Int
itertimes = Int
1000
      def :: RiddersParam
def = RiddersParam { riddersMaxIter :: Int
riddersMaxIter = Int
itertimes, riddersTol :: Tolerance
riddersTol = Double -> Tolerance
RelTol Double
0.000001}
      beginDate :: Date
beginDate = [Date] -> Date
forall a. HasCallStack => [a] -> a
head [Date]
ds
      vs' :: [Double]
vs' = Rate -> Double
forall a. Fractional a => Rate -> a
fromRational (Rate -> Double) -> (Amount -> Rate) -> Amount -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Amount -> Rate
forall a. Real a => a -> Rate
toRational (Amount -> Double) -> [Amount] -> [Double]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Amount]
vs
      sumOfPv :: Double -> Double
sumOfPv Double
irr = Double -> Date -> [Date] -> [Double] -> Double
pv22 Double
irr Date
beginDate [Date]
ds [Double]
vs'
    in 
      case RiddersParam
-> (Double, Double) -> (Double -> Double) -> Root Double
ridders RiddersParam
def (-Double
1,Double
1000) Double -> Double
sumOfPv of
        Root Double
irrRate -> Rate -> Either String Rate
forall a b. b -> Either a b
Right (Rate -> Either String Rate) -> Rate -> Either String Rate
forall a b. (a -> b) -> a -> b
$ Double -> Rate
forall a. Real a => a -> Rate
toRational Double
irrRate
        Root Double
NotBracketed -> String -> Either String Rate
forall a b. a -> Either a b
Left (String -> Either String Rate) -> String -> Either String Rate
forall a b. (a -> b) -> a -> b
$ String
"IRR: not bracketed" String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Double] -> String
forall a. Show a => a -> String
show [Double]
vs' String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" and dates"String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Date] -> String
forall a. Show a => a -> String
show [Date]
ds
        Root Double
SearchFailed -> String -> Either String Rate
forall a b. a -> Either a b
Left (String -> Either String Rate) -> String -> Either String Rate
forall a b. (a -> b) -> a -> b
$ String
"IRR: search failed:  can't be calculated with input "String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Amount] -> String
forall a. Show a => a -> String
show [Amount]
vsString -> String -> String
forall a. [a] -> [a] -> [a]
++String
" and dates"String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Date] -> String
forall a. Show a => a -> String
show [Date]
ds