Hastructure-0.45.0: test/UT/UtilTest.hs
module UT.UtilTest(daycountTests1,daycountTests2,daycountTests3,daycountTests4
,tsTest,ts2Test,ts3Test,dateVectorPatternTest,paddingTest,dateSliceTest
,capTest,roundingTest,sliceTest,splitTsTest,tableTest,lastOftest,paySeqTest
,scaleListTest)--,daycountTests3,daycountTests4)
where
import Test.Tasty
import Test.Tasty.HUnit
import qualified Data.Time as T
import qualified Cashflow as CF
import qualified Liability as L
import Util
import DateUtil
import Lib
import Types
import Stmt
import Data.Fixed
import qualified Data.DList as DL
import Data.Ratio ((%))
import Debug.Trace
debug = flip trace
daycountTests1 =
let
d1 = T.fromGregorian 2007 12 28
d2 = T.fromGregorian 2008 2 28
in
testGroup "Day Count Tests 1"
[
testCase "Act/Act" $
assertEqual "should be close to 0.1694288494678 "
((4 % 365) + (58 % 366)) $
yearCountFraction DC_ACT_ACT d1 d2
,testCase "Act/365F" $
assertEqual ""
(62 % 365) $
yearCountFraction DC_ACT_365F d1 d2
,testCase "Act/360" $
assertEqual ""
(62 % 360) $
yearCountFraction DC_ACT_360 d1 d2
,testCase "Act/365A" $
assertEqual ""
(62 % 365) $
yearCountFraction DC_ACT_365A d1 d2
,testCase "Act/365L" $
assertEqual ""
(62 % 366) $
yearCountFraction DC_ACT_365L d1 d2
,testCase "NL/365" $
assertEqual ""
(62 % 365) $
yearCountFraction DC_NL_365 d1 d2
,testCase "30 360 ISDA" $
assertEqual ""
(60 % 360) $
yearCountFraction DC_30_360_ISDA d1 d2
,testCase "30E 360" $
assertEqual ""
(60 % 360) $
yearCountFraction DC_30E_360 d1 d2
,testCase "30Ep 360" $
assertEqual ""
(60 % 360) $
yearCountFraction DC_30Ep_360 d1 d2
,testCase "30 360 German" $
assertEqual ""
(60 % 360) $
yearCountFraction DC_30_360_German d1 d2
,testCase "30 360 US" $
assertEqual ""
(60 % 360) $
yearCountFraction DC_30_360_US d1 d2
]
daycountTests2 =
let
d1 = T.fromGregorian 2007 12 28
d2 = T.fromGregorian 2008 2 29
in
testGroup "Day Count Tests 2"
[
testCase "Act/Act" $
assertEqual ""
((4 % 365) + (59 % 366)) $
yearCountFraction DC_ACT_ACT d1 d2
,testCase "Act/365F" $
assertEqual ""
(63 % 365) $
yearCountFraction DC_ACT_365F d1 d2
,testCase "Act/360" $
assertEqual ""
(63 % 360) $
yearCountFraction DC_ACT_360 d1 d2
,testCase "Act/365A" $
assertEqual ""
(63 % 366) $
yearCountFraction DC_ACT_365A d1 d2
,testCase "Act/365L" $
assertEqual ""
(63 % 366) $
yearCountFraction DC_ACT_365L d1 d2
,testCase "NL/365" $
assertEqual ""
(62 % 365) $
yearCountFraction DC_NL_365 d1 d2
,testCase "30 360 ISDA" $
assertEqual ""
(61 % 360) $
yearCountFraction DC_30_360_ISDA d1 d2
,testCase "30E 360" $
assertEqual ""
(61 % 360) $
yearCountFraction DC_30E_360 d1 d2
,testCase "30Ep 360" $
assertEqual ""
(61 % 360) $
yearCountFraction DC_30Ep_360 d1 d2
,testCase "30 360 German" $
assertEqual ""
(62 % 360) $
yearCountFraction DC_30_360_German d1 d2
,testCase "30 360 US" $
assertEqual ""
(61 % 360) $
yearCountFraction DC_30_360_US d1 d2
]
daycountTests3 =
let
d1 = T.fromGregorian 2007 10 31
d2 = T.fromGregorian 2008 11 30
in
testGroup "Day Count Tests 3"
[
testCase "Act/Act" $
assertEqual "should be close to 0.1694288494678 "
((62 % 365) + (334 % 366)) $
yearCountFraction DC_ACT_ACT d1 d2
,testCase "Act/365F" $
assertEqual ""
(396 % 365) $
yearCountFraction DC_ACT_365F d1 d2
,testCase "Act/360" $
assertEqual ""
(396 % 360) $
yearCountFraction DC_ACT_360 d1 d2
,testCase "Act/365A" $
assertEqual ""
(396 % 366) $
yearCountFraction DC_ACT_365A d1 d2
,testCase "Act/365L" $
assertEqual ""
(396 % 366) $
yearCountFraction DC_ACT_365L d1 d2
,testCase "NL/365" $
assertEqual ""
(395 % 365) $
yearCountFraction DC_NL_365 d1 d2
,testCase "30 360 ISDA" $
assertEqual ""
(390 % 360) $
yearCountFraction DC_30_360_ISDA d1 d2
,testCase "30E 360" $
assertEqual ""
(390 % 360) $
yearCountFraction DC_30E_360 d1 d2
,testCase "30Ep 360" $
assertEqual ""
(390 % 360) $
yearCountFraction DC_30Ep_360 d1 d2
,testCase "30 360 German" $
assertEqual ""
(390 % 360) $
yearCountFraction DC_30_360_German d1 d2
,testCase "30 360 US" $
assertEqual ""
(390 % 360) $
yearCountFraction DC_30_360_US d1 d2
]
daycountTests4 =
let
d1 = T.fromGregorian 2008 2 1
d2 = T.fromGregorian 2009 5 31
in
testGroup "Day Count Tests 4"
[
testCase "Act/Act" $
assertEqual ""
((335 % 366) + (150 % 365)) $
yearCountFraction DC_ACT_ACT d1 d2
,testCase "Act/365F" $
assertEqual ""
(485 % 365) $
yearCountFraction DC_ACT_365F d1 d2
,testCase "Act/360" $
assertEqual ""
(485 % 360) $
yearCountFraction DC_ACT_360 d1 d2
,testCase "Act/365A" $
assertEqual ""
(485 % 366) $
yearCountFraction DC_ACT_365A d1 d2
,testCase "Act/365L" $
assertEqual ""
(485 % 365) $
yearCountFraction DC_ACT_365L d1 d2
,testCase "NL/365" $
assertEqual ""
(484 % 365) $
yearCountFraction DC_NL_365 d1 d2
,testCase "30 360 ISDA" $
assertEqual ""
(480 % 360) $
yearCountFraction DC_30_360_ISDA d1 d2
,testCase "30E 360" $
assertEqual ""
(479 % 360) $
yearCountFraction DC_30E_360 d1 d2
,testCase "30Ep 360" $
assertEqual ""
(480 % 360) $
yearCountFraction DC_30Ep_360 d1 d2
,testCase "30 360 German" $
assertEqual ""
(479 % 360) $
yearCountFraction DC_30_360_German d1 d2
,testCase "30 360 US" $
assertEqual ""
(480 % 360) $
yearCountFraction DC_30_360_US d1 d2
]
tsTest =
let
d1 = T.fromGregorian 2007 12 28
d2 = T.fromGregorian 2008 2 28
dpairs = [(toDate "20061201",100)
,(toDate "20070201",80)
,(toDate "20080201",60)]
ed = (toDate "20090101")
testTs = mkTs [(toDate "20061201",100)
,(toDate "20070201",80)
,(toDate "20080201",60)]
tps = [TsPoint _d _v | (_d,_v) <- dpairs ]
rateCurve = FloatCurve [TsPoint (toDate "20061201") 0.03
,TsPoint (toDate "20070201") 0.05
,TsPoint (toDate "20080201") 0.07]
factorCurve = FloatCurve [TsPoint (toDate "20061201") 1.0
,TsPoint (toDate "20070201") 0.8
,TsPoint (toDate "20080201") 0.6]
in
testGroup "Test Trigger Factor Curve"
[
testCase "" $
assertEqual "left most"
[0,100,80,60] $
getValByDates testTs Exc [(toDate "20061201")
,(toDate "20061211")
,(toDate "20070301")
,(toDate "20081201")]
,testCase "FactorCurveClosed" $
assertEqual "leftNotFound as 1"
1.0 $
getValByDate
(FactorCurveClosed tps ed)
Exc
(toDate "20060601")
,testCase "FactorCurveClosed" $
assertEqual "in middle"
80 $
getValByDate
(FactorCurveClosed tps ed)
Exc
(toDate "20070202")
,testCase "FactorCurveClosed" $
assertEqual "right after last dps"
60 $
getValByDate
(FactorCurveClosed tps ed)
Exc
(toDate "20081221")
,testCase "FactorCurveClosed" $
assertEqual "After end date"
1.0 $
getValByDate
(FactorCurveClosed tps ed)
Exc
(toDate "20090601")
,testCase "Multiply Ts" $
assertEqual " multiplyTs 1 "
(mkTs [(toDate "20061201", 0.03) ,(toDate "20070201", 0.04),(toDate "20080201", 0.042)])
(multiplyTs Inc rateCurve factorCurve)
]
ts2Test =
let
testThresholdCurve = ThresholdCurve [(TsPoint (toDate "20220101") (1 % 100))
,(TsPoint (toDate "20220201") (2 % 100))
,(TsPoint (toDate "20220301") (3 % 100))]
in
testGroup "Test Trigger Threshold Curve"
[
testCase "" $
assertEqual "left most"
(1 % 100) $
getValByDate testThresholdCurve Inc (toDate "20211201")
,testCase "" $
assertEqual "on first-ts"
(1 % 100) $
getValByDate testThresholdCurve Inc (toDate "20220101")
,testCase "" $
assertEqual "after first-ts"
(2 % 100) $
getValByDate testThresholdCurve Inc (toDate "20220110")
,testCase "" $
assertEqual "Right most"
(3 % 100) $
getValByDate testThresholdCurve Inc (toDate "20220310")
]
-- ^ date pattern test
dateVectorPatternTest =
let
a = 1
in
testGroup "Test on Date Vector generation"
[ testCase "" $
assertEqual "LeapYear&Month End"
[(toDate "20240229"), (toDate "20240331")]
(genSerialDates MonthEnd Inc (toDate "20240215") 2)
, testCase "Week Day Test" $
assertEqual "week day 2"
[(toDate "20240514"), (toDate "20240521")]
(genSerialDates (Weekday 2) Inc (toDate "20240509") 2)
, testCase "Week Day Test" $
assertEqual "week day 0"
[(toDate "20240512"), (toDate "20240519")]
(genSerialDates (Weekday 0) Inc (toDate "20240509") 2)
, testCase "" $
assertEqual "till test"
[(toDate "20220131"),(toDate "20220228")]
(genSerialDatesTill (toDate "20220101") MonthEnd (toDate "20220315"))
, testCase "First Date in Pattern" $
assertEqual ""
[(toDate "20220630"),(toDate "20220731"),(toDate "20220831")]
(genSerialDatesTill2 IE (toDate "20220630") MonthEnd (toDate "20220901"))
, testCase "Custom Dates" $
assertEqual "exatct same length"
[(toDate "20230202"),(toDate "20230909")]
(genSerialDatesTill2 EE (toDate "20200630") (CustomDate [toDate "20230202", toDate "20230909"] ) (toDate "20230910"))
, testCase "Custom Dates" $
assertEqual "Include both ends"
[(toDate "20200630"), (toDate "20230202"),(toDate "20230909"), (toDate "20230910")]
(genSerialDatesTill2 II (toDate "20200630") (CustomDate [toDate "20230202", toDate "20230909"] ) (toDate "20230910"))
, testCase "Mutiple Date Pattern" $
assertEqual ""
[(toDate "20230909"), (toDate "20230919")]
(genSerialDatesTill2 EE
(toDate "20200630")
(AllDatePattern
[(CustomDate [toDate "20230909"]),(CustomDate [toDate "20230919"])])
(toDate "20230930"))
, testCase "" $
assertEqual "Generate Dates by N Month"
(toDates ["20220201","20220401","20220601"])
(genSerialDatesTill (toDate "20220101") (EveryNMonth (toDate "20220201") 2) (toDate "20220715"))
, testCase "exclude dates" $
assertEqual "Exclude Dates "
[(toDate "20230909")]
(genSerialDatesTill2 EE (toDate "20200630")
(Exclude
(CustomDate [toDate "20230202", toDate "20230909"] )
[(CustomDate [toDate "20230202"] )])
(toDate "20230910"))
, testCase "offset by dates" $
assertEqual "offset by dates"
[(toDate "20230204"),(toDate "20230911")]
(genSerialDatesTill2 EE
(toDate "20200630")
(OffsetBy
(CustomDate [toDate "20230202", toDate "20230909"]) 2)
(toDate "20230910"))
, testCase "No Inclusive or Exclusive" $
assertEqual "1"
(toDates ["20230831","20230930","20231031"])
(genSerialDatesTill2 NO_IE (toDate "20230810") MonthEnd (toDate "20231031") )
, testCase "No Inclusive or Exclusive" $
assertEqual "2"
(toDates ["20230831","20230930"])
(genSerialDatesTill2 NO_IE (toDate "20230810") MonthEnd (toDate "20231030") )
, testCase "No Inclusive or Exclusive" $
assertEqual "3"
(toDates ["20230831","20230930","20231031"])
(genSerialDatesTill2 NO_IE (toDate "20230831") MonthEnd (toDate "20231031") )
, testCase "No Inclusive or Exclusive" $
assertEqual "4"
(toDates ["20230831","20230930"])
(genSerialDatesTill2 NO_IE (toDate "20230831") MonthEnd (toDate "20231030") )
, testCase "starts " $
assertEqual " inc "
(toDates ["20230930"])
(genSerialDatesTill2 NO_IE (toDate "20230831") (StartsAt Inc (toDate "20230901") MonthEnd) (toDate "20231030") )
, testCase "starts " $
assertEqual " inc "
(toDates ["20230831", "20230930"])
(genSerialDatesTill2 NO_IE (toDate "20230831") (StartsAt Inc (toDate "20230831") MonthEnd) (toDate "20231030") )
, testCase "ends " $
assertEqual " inc "
(toDates ["20230831"])
(genSerialDatesTill2 NO_IE (toDate "20230831") (EndsAt Inc (toDate "20230901") MonthEnd) (toDate "20231030") )
, testCase "ends " $
assertEqual " inc "
(toDates ["20230831"])
(genSerialDatesTill2 NO_IE (toDate "20230831") (EndsAt Inc (toDate "20230831") MonthEnd) (toDate "20231030") )
, testCase "ends " $
assertEqual " exc "
([])
(genSerialDatesTill2 NO_IE (toDate "20230831") (EndsAt Exc (toDate "20230831") MonthEnd) (toDate "20231030") )
]
paddingTest =
let
a = [1,2,3]
in
testGroup "padding"
[ testCase "" $
assertEqual "padding+"
[1,2,3,0]
(paddingDefault 0 a 4)
,testCase "" $
assertEqual "padding-"
[1,2]
(paddingDefault 0 a 2)
]
dateSliceTest =
let
ds = [(toDate "20230101"),(toDate "20230201"),(toDate "20230301")]
in
testGroup "slice dates"
[ testCase "sliceAfterkeepPrevious" $
assertEqual ""
ds
(sliceDates (SliceAfterKeepPrevious (toDate "20230115")) ds)
, testCase "sliceAfterkeepPrevious" $
assertEqual ""
(tail ds)
(sliceDates (SliceAfterKeepPrevious (toDate "20230215")) ds)
]
capTest =
let
a = [1,2,3,4]
in
testGroup "Cap Test"
[ testCase "Cap with 2" $
assertEqual "Cap with 2"
[1,2,2,2]
(capWith 2 a)
,testCase "No Cap" $
assertEqual "No Cap"
[1,2,3,4]
(capWith 5 a)
]
ts3Test =
let
ts1 = IRateCurve [TsPoint (toDate "20230301") 0.03,TsPoint (toDate "20230301") 0.05]
in
testGroup "curve update test"
[ testCase "shift curve" $
assertEqual "Shift RateCurve by 0.01"
(IRateCurve [TsPoint (toDate "20230301") 0.04,TsPoint (toDate "20230301") 0.06]) $
(shiftTsByAmt ts1 0.01)
]
roundingTest =
testGroup "rounding by test"
[ testCase "Rounding on Rate" $
assertEqual "Rounding Floor"
0.02
(roundingBy (RoundFloor 0.00125) 0.02124)
, testCase "Rounding on Rate" $
assertEqual "Rounding Ceiling"
0.02
(roundingBy (RoundCeil 0.00125) 0.01876)
, testCase "Rounding on Rate" $
assertEqual "Rounding Equal"
0.02
(roundingBy (RoundCeil 0.00125) 0.02)
, testCase "Rounding on Balance" $
assertEqual "Rounding Ceiling"
100
(roundingBy (RoundCeil 5) 96)
, testCase "Rounding on Balance" $
assertEqual "Rounding Floor"
100
(roundingBy (RoundFloor 5) 104)
, testCase "Rounding on Balance" $
assertEqual "Rounding Equal"
100
(roundingBy (RoundFloor 5) 100)
]
sliceTest =
testGroup "sliceTest"
[ testCase "slice 1" $
assertEqual ""
[1]
(slice 0 1 [1,2,3])
, testCase "slice 2" $
assertEqual ""
[]
(slice 0 0 [1,2,3])
, testCase "slice 2" $
assertEqual ""
[1,2,3]
(slice 0 3 [1,2,3])
, testCase "slice 3" $
assertEqual ""
[1,2,3]
(slice 0 4 [1,2,3])
]
splitTsTest =
let
cashflow = [CF.CashFlow (toDate "20230901") 10, CF.CashFlow (toDate "20231001") 10,CF.CashFlow (toDate "20231101") 10]
in
testGroup "split times series test"
[ testCase "split before earliest" $
assertEqual ""
([],cashflow)
(splitBy (toDate "20230801") Inc cashflow )
,testCase "split after latest" $
assertEqual ""
(cashflow,[])
(splitBy (toDate "20231201") Inc cashflow )
,testCase "split in middle,inclusive" $
assertEqual ""
([CF.CashFlow (toDate "20230901") 10, CF.CashFlow (toDate "20231001") 10],[CF.CashFlow (toDate "20231101") 10])
(splitBy (toDate "20231001") Inc cashflow )
,testCase "split in middle,exclusive" $
assertEqual ""
([CF.CashFlow (toDate "20230901") 10],[CF.CashFlow (toDate "20231001") 10,CF.CashFlow (toDate "20231101") 10])
(splitBy (toDate "20231001") Exc cashflow )
]
tableTest =
let
tbl = ThresholdTable [(5,100),(10,200),(15,300),(20,400)]
in
testGroup "lookup table down"
[
testCase "down & inclusive" $
assertEqual ""
[Nothing,Just 100,Just 100]
[lookupTable tbl Down (3 >=),lookupTable tbl Down (5 >=),lookupTable tbl Down (12 >=)]
,testCase "down & exclusive" $
assertEqual ""
[Nothing,Nothing,Just 100]
[lookupTable tbl Down (3 >),lookupTable tbl Down (5 >),lookupTable tbl Down (6 >)]
,testCase "up & inclusive" $
assertEqual ""
[Nothing,Just 100,Just 100]
[lookupTable tbl Up (3 >=),lookupTable tbl Up (5 >=),lookupTable tbl Up (6 >=)]
,testCase "up & exclusive" $
assertEqual ""
[Just 400, Just 300, Just 200, Nothing]
[lookupTable tbl Up (20 >=),lookupTable tbl Up (16 >=),lookupTable tbl Up (11 >=),lookupTable tbl Up (3 >=) ]
,testCase "table interval" $
assertEqual " Up first "
(Just ((5,100),(10,200)))
(lookupIntervalTable tbl Down (5 >=))
,testCase "table interval 1" $
assertEqual " Up second "
(Just ((15,300),(20,400)))
(lookupIntervalTable tbl Down (\x -> x >= 12))
,testCase "table interval 2" $
assertEqual " Up last"
Nothing
(lookupIntervalTable tbl Down (>= 20))
]
lastOftest =
let
a = [1,2,3,4,5]
b = [[1],[3],[],[5],[],[]]
in
testGroup "test on last of on list" [
testCase "on non empty" $
assertEqual "should be [5]"
(Just [5])
(Util.lastOf b (not . null))
]
paySeqTest =
let
d1 = toDate "20200101"
bnd1 = L.Bond "A" L.Sequential (L.OriginalInfo 100 d1 0.06 Nothing) (L.Fix 0.05 DC_ACT_365F) Nothing 100 0.08 0 0 0 Nothing Nothing Nothing Nothing
bnd2 = L.Bond "B" L.Sequential (L.OriginalInfo 100 d1 0.06 Nothing) (L.Fix 0.05 DC_ACT_365F) Nothing 100 0.08 0 0 0 Nothing Nothing Nothing Nothing
writeAmt1 = 100
in
testGroup "write off on bond" [
testCase "write off on bond 1" $
assertEqual "only 1st bond is written off by 70"
(Right ([bnd1 {L.bndBalance = 30,L.bndStmt = Just (Statement (DL.fromList ([BondTxn d1 30.00 0.00 0.00 0.000000 0.00 0.00 0.00 Nothing (WriteOff "A" 70.00)])))}
, bnd2],0))
(paySeqM d1 70 L.bndBalance (L.writeOff d1) (Right []) [bnd1,bnd2])
,testCase "write off on bond 2" $
assertEqual "2st bond is written off by 70"
(Right ([bnd1 {L.bndBalance = 0,L.bndStmt = Just (Statement (DL.fromList ([BondTxn d1 0.00 0.00 0.00 0.000000 0.00 0.00 0.00 Nothing (WriteOff "A" 100.00)])))}
, bnd2{L.bndBalance = 70,L.bndStmt = Just (Statement (DL.fromList ([BondTxn d1 70.00 0.00 0.00 0.000000 0.00 0.00 0.00 Nothing (WriteOff "B" 30.00)])))}
],0))
(paySeqM d1 130 L.bndBalance (L.writeOff d1) (Right []) [bnd1,bnd2])
,testCase "write off on all bonds " $
assertEqual "over write off"
(Right ([bnd1 {L.bndBalance = 0,L.bndStmt = Just (Statement (DL.fromList ([BondTxn d1 0.00 0.00 0.00 0.000000 0.00 0.00 0.00 Nothing (WriteOff "A" 100.00)])))}
, bnd2{L.bndBalance = 0,L.bndStmt = Just (Statement (DL.fromList ([BondTxn d1 0.00 0.00 0.00 0.000000 0.00 0.00 0.00 Nothing (WriteOff "B" 100.00)])))}
],30))
(paySeqM d1 230 L.bndBalance (L.writeOff d1) (Right []) [bnd1,bnd2])
]
scaleListTest =
let
a = 1
in
testGroup "scale list test"
[ testCase "" $
assertEqual "scale list"
[50.0, 37.5, 25.0]
$ scaleByFstElement 50 [200.0,150.0,100]
, testCase "" $
assertEqual "scale list"
[]
$ scaleByFstElement 50 []
]