diff --git a/ChangeLog.md b/ChangeLog.md index 458cb163..75d7775e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,7 +1,26 @@ # Changelog for Hastructure -## 0.28.3 -### TODO +## 0.28.15 +### 2024-7-31 +* FIX: enable compound formula on `weighted average` formula. + + +## 0.28.14 +### 2024-07-06 +* FIX: enable `annualized rate fee type` with formula `bondbalance` on `bondGroup` + +## 0.28.13 +### 2024-06-30 +* NEW: new assumption `issue bond` which allow funding by issuing new bonds during cashflow projection. +* NEW: new asset class `projectScheduleFlow` which can be divided projected cashflow with fix portion and float portions. The interest from the float portion will be affected by interest rate assumption. +* ENHANCE: enable formula `bondRate`/`bondWaRate` on `bondGroup` +* FIX: `formula` will return `inf` if a `divide` with zero instead of just throw exception +* FIX: `financial reports` was failing because it can't access to `interest due` on bond group. +* FIX: enable formula query on `bond groups` + + +## 0.28.8 +### 2024-06 * FIX: `limit` on `payFee` was not working with `duePct` * ENHANCE: expose `transaction statement` for `triggers` diff --git a/Hastructure.cabal b/Hastructure.cabal index 9a9b3206..ad3077db 100644 --- a/Hastructure.cabal +++ b/Hastructure.cabal @@ -1,6 +1,6 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.2. +-- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack @@ -37,6 +37,7 @@ library AssetClass.Loan AssetClass.MixedAsset AssetClass.Mortgage + AssetClass.ProjectedCashFlow AssetClass.Receivable Assumptions Call @@ -63,6 +64,7 @@ library Triggers Types Util + Validation Waterfall other-modules: Paths_Hastructure @@ -76,6 +78,7 @@ library , containers , hashable , lens + , numeric-limits , openapi3 , regex-base , regex-pcre-builtin @@ -115,6 +118,7 @@ executable Hastructure-exe , lens , lucid , mtl + , numeric-limits , openapi3 , regex-base , regex-pcre-builtin @@ -173,6 +177,7 @@ test-suite Hastructure-test , containers , hashable , lens + , numeric-limits , openapi3 , regex-base , regex-pcre-builtin diff --git a/app/Main.hs b/app/Main.hs index 05e7d5dd..b6c1a3bc 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -67,6 +67,7 @@ import qualified AssetClass.Installment import qualified AssetClass.Mortgage import qualified AssetClass.Loan import qualified AssetClass.Lease +import qualified AssetClass.ProjectedCashFlow import qualified AssetClass.MixedAsset as MA import qualified AssetClass.AssetBase as AB import qualified Assumptions as AP @@ -102,7 +103,7 @@ $(deriveJSON defaultOptions ''Version) instance ToSchema Version version1 :: Version -version1 = Version "0.28.6" +version1 = Version "0.28.15" data DealType = MDeal (DB.TestDeal AB.Mortgage) @@ -110,8 +111,9 @@ data DealType = MDeal (DB.TestDeal AB.Mortgage) | IDeal (DB.TestDeal AB.Installment) | RDeal (DB.TestDeal AB.Lease) | FDeal (DB.TestDeal AB.FixedAsset) - | VDeal (DB.TestDeal AB.Receivable) - | UDeal (DB.TestDeal AB.AssetUnion) + | VDeal (DB.TestDeal AB.Receivable) + | PDeal (DB.TestDeal AB.ProjectedCashflow) + | UDeal (DB.TestDeal AB.AssetUnion) deriving(Show, Generic) instance ToSchema CF.CashFlowFrame @@ -122,6 +124,7 @@ instance ToSchema AB.LeaseStepUp instance ToSchema AB.Lease instance ToSchema AB.FixedAsset instance ToSchema AB.Receivable +instance ToSchema AB.ProjectedCashflow instance ToSchema CutoffFields instance ToSchema (P.Pool AB.Mortgage) instance ToSchema (P.Pool AB.Loan) @@ -130,12 +133,13 @@ instance ToSchema (P.Pool AB.Lease) instance ToSchema (P.Pool AB.FixedAsset) instance ToSchema (P.Pool AB.Receivable) instance ToSchema (P.Pool AB.AssetUnion) +instance ToSchema (P.Pool AB.ProjectedCashflow) instance ToSchema AB.AssetUnion instance ToSchema PoolId instance ToSchema DealStatus instance ToSchema DateType -instance ToSchema DateDesp -instance ToSchema ActionOnDate +instance ToSchema DB.DateDesp +instance ToSchema DB.ActionOnDate instance ToSchema DealStats instance ToSchema Cmp instance ToSchema PricingMethod @@ -181,7 +185,7 @@ instance ToSchema CE.LiqDrawType instance ToSchema CustomDataType instance ToSchema TRG.Trigger instance ToSchema TRG.TriggerEffect -instance ToSchema OverrideType +instance ToSchema DB.OverrideType instance ToSchema Types.BalanceSheetReport instance ToSchema Types.CashflowReport instance ToSchema Types.BookItem @@ -191,6 +195,7 @@ instance ToSchema AB.AssociateExp instance ToSchema AB.AssociateIncome instance ToSchema RV.RevolvingPool instance ToSchema (TsPoint [AB.AssetUnion]) +instance ToSchema (TsPoint AP.IssueBondEvent) instance ToSchema AP.NonPerfAssumption instance ToSchema AP.BondPricingInput instance ToSchema AP.RevolvingAssumption @@ -214,6 +219,7 @@ instance ToSchema (DB.TestDeal AB.Loan) instance ToSchema (DB.TestDeal AB.Installment) instance ToSchema (DB.TestDeal AB.Lease) instance ToSchema (DB.TestDeal AB.Receivable) +instance ToSchema (DB.TestDeal AB.ProjectedCashflow) instance ToSchema (DB.TestDeal AB.AssetUnion) instance ToSchema (DB.TestDeal AB.FixedAsset) @@ -223,6 +229,7 @@ instance ToSchema (DB.PoolType AB.Installment) instance ToSchema (DB.PoolType AB.Lease) instance ToSchema (DB.PoolType AB.FixedAsset) instance ToSchema (DB.PoolType AB.Receivable) +instance ToSchema (DB.PoolType AB.ProjectedCashflow) instance ToSchema (DB.PoolType AB.AssetUnion) instance ToSchema (DB.UnderlyingDeal AB.Mortgage) @@ -231,6 +238,7 @@ instance ToSchema (DB.UnderlyingDeal AB.Installment) instance ToSchema (DB.UnderlyingDeal AB.Lease) instance ToSchema (DB.UnderlyingDeal AB.FixedAsset) instance ToSchema (DB.UnderlyingDeal AB.Receivable) +instance ToSchema (DB.UnderlyingDeal AB.ProjectedCashflow) instance ToSchema (DB.UnderlyingDeal AB.AssetUnion) instance ToSchema ResultComponent @@ -268,6 +276,11 @@ wrapRun (VDeal d) mAssump mNonPerfAssump = let (_d,_pflow,_rs,_p) = D.runDeal d D.DealPoolFlowPricing mAssump mNonPerfAssump in (VDeal _d,_pflow,_rs,_p) +wrapRun (PDeal d) mAssump mNonPerfAssump = let + (_d,_pflow,_rs,_p) = D.runDeal d D.DealPoolFlowPricing mAssump mNonPerfAssump + in + (PDeal _d,_pflow,_rs,_p) + wrapRun x _ _ = error $ "RunDeal Failed ,due to unsupport deal type "++ show x @@ -277,6 +290,7 @@ data PoolTypeWrap = LPool (DB.PoolType AB.Loan) | RPool (DB.PoolType AB.Lease) | FPool (DB.PoolType AB.FixedAsset) | VPool (DB.PoolType AB.Receivable) + | PPool (DB.PoolType AB.ProjectedCashflow) | UPool (DB.PoolType AB.AssetUnion) deriving(Show, Generic) @@ -293,6 +307,7 @@ wrapRunPoolType (IPool pt) assump mRates = D.runPoolType pt assump $ Just (AP.No wrapRunPoolType (RPool pt) assump mRates = D.runPoolType pt assump $ Just (AP.NonPerfAssumption{AP.interest = mRates}) wrapRunPoolType (FPool pt) assump mRates = D.runPoolType pt assump $ Just (AP.NonPerfAssumption{AP.interest = mRates}) wrapRunPoolType (VPool pt) assump mRates = D.runPoolType pt assump $ Just (AP.NonPerfAssumption{AP.interest = mRates}) +wrapRunPoolType (PPool pt) assump mRates = D.runPoolType pt assump $ Just (AP.NonPerfAssumption{AP.interest = mRates}) wrapRunPoolType (UPool pt) assump mRates = D.runPoolType pt assump $ Just (AP.NonPerfAssumption{AP.interest = mRates}) wrapRunPoolType x _ _ = error $ "RunPool Failed ,due to unsupport pool type "++ show x diff --git a/package.yaml b/package.yaml index 7623cb65..73e7ca2d 100644 --- a/package.yaml +++ b/package.yaml @@ -42,6 +42,7 @@ dependencies: - swagger2 - split - tabular +- numeric-limits library: source-dirs: diff --git a/src/Accounts.hs b/src/Accounts.hs index f477ee92..d959b2d5 100644 --- a/src/Accounts.hs +++ b/src/Accounts.hs @@ -126,7 +126,7 @@ deposit :: Amount -> Date -> TxnComment -> Account -> Account deposit amount d source acc@(Account bal _ _ _ maybeStmt) = acc {accBalance = newBal, accStmt = newStmt} where - newBal = bal + amount + newBal = bal + amount -- `debug` ("Date:"++show d++ "deposit"++show amount++"from"++show bal) newStmt = appendStmt maybeStmt (AccTxn d newBal amount source) -- | draw cash from account with a comment diff --git a/src/Asset.hs b/src/Asset.hs index e33b8688..6b282646 100644 --- a/src/Asset.hs +++ b/src/Asset.hs @@ -77,7 +77,7 @@ class (Show a,IR.UseRate a) => Asset a where splitWith :: a -> [Rate] -> [a] -- | ! Change the origination date of an asset updateOriginDate :: a -> Date -> a - -- | get Last Interest Payment date + -- | Get Last Interest Payment date getLastInterestPaymentDate :: a -> Maybe Date -- | Calculate Accrued Interest calcAccruedInterest :: a -> Date -> Balance @@ -96,12 +96,6 @@ class (Show a,IR.UseRate a) => Asset a where - - - - - - -- | apply ExtraStress on prepayment/default rates applyExtraStress :: Maybe A.ExtraStress -> [Date] -> [Rate] -> [Rate] -> ([Rate],[Rate]) applyExtraStress Nothing _ ppy def = (ppy,def) diff --git a/src/AssetClass/AssetBase.hs b/src/AssetClass/AssetBase.hs index e8b93289..2a3f94ad 100644 --- a/src/AssetClass/AssetBase.hs +++ b/src/AssetClass/AssetBase.hs @@ -9,6 +9,7 @@ module AssetClass.AssetBase ,LeaseStepUp(..),AccrualPeriod(..),PrepayPenaltyType(..) ,AmortPlan(..),Loan(..),Mortgage(..),AssetUnion(..),MixedAsset(..),FixedAsset(..) ,AmortRule(..),Capacity(..),AssociateExp(..),AssociateIncome(..),ReceivableFeeType(..),Receivable(..) + ,ProjectedCashflow(..) ,calcAssetPrinInt, calcPmt ) where @@ -171,6 +172,16 @@ data Mortgage = Mortgage OriginalInfo Balance IRate RemainTerms (Maybe BorrowerN | ScheduleMortgageFlow Date [CF.TsRow] DatePattern deriving (Show,Generic,Eq,Ord) + +type FixRatePortion = (Rate, IRate) +type FloatRatePortion = (Rate, Spread, Index) + + +data ProjectedCashflow = ProjectedFlowFixed CF.CashFlowFrame DatePattern + | ProjectedFlowMixFloater CF.CashFlowFrame DatePattern FixRatePortion [FloatRatePortion] + deriving (Show,Generic,Eq,Ord) + + data Receivable = Invoice OriginalInfo Status | DUMMY4 deriving (Show,Generic,Eq,Ord) @@ -212,6 +223,7 @@ data AssetUnion = MO Mortgage | LS Lease | FA FixedAsset | RE Receivable + | PF ProjectedCashflow deriving (Show, Generic,Ord,Eq) instance IR.UseRate AssetUnion where @@ -221,6 +233,7 @@ instance IR.UseRate AssetUnion where getIndex (LS ma) = IR.getIndex ma getIndex (FA ma) = IR.getIndex ma getIndex (RE ma) = IR.getIndex ma + getIndex (PF ma) = IR.getIndex ma instance IR.UseRate Mortgage where @@ -247,36 +260,31 @@ instance IR.UseRate FixedAsset where instance IR.UseRate Receivable where getIndex _ = Nothing +instance IR.UseRate ProjectedCashflow where + getIndex (ProjectedFlowFixed cf _) = Nothing + + getIndex (ProjectedFlowMixFloater cf _ _ (f:fs)) = Just $ (\(a,b,c) -> c) f + getIndexes (ProjectedFlowMixFloater cf _ _ fs ) + = Just $ (\(a,b,c) -> c) <$> fs $(concat <$> traverse (deriveJSON defaultOptions) [''OriginalInfo, ''FixedAsset, ''AmortPlan, ''PrepayPenaltyType , ''Capacity, ''AmortRule, ''ReceivableFeeType]) --- $(deriveJSON defaultOptions ''AmortRule) --- $(deriveJSON defaultOptions ''Capacity) $(deriveJSON defaultOptions ''AssociateExp) $(deriveJSON defaultOptions ''AssociateIncome) --- $(deriveJSON defaultOptions ''FixedAsset) $(deriveJSON defaultOptions ''Status) --- $(deriveJSON defaultOptions ''AmortPlan) --- $(deriveJSON defaultOptions ''ReceivableFeeType) --- $(deriveJSON defaultOptions ''OriginalInfo) $(deriveJSON defaultOptions ''Installment) $(deriveJSON defaultOptions ''LeaseStepUp) $(deriveJSON defaultOptions ''Mortgage) $(deriveJSON defaultOptions ''Loan) $(deriveJSON defaultOptions ''Lease) $(deriveJSON defaultOptions ''Receivable) +$(deriveJSON defaultOptions ''ProjectedCashflow) $(deriveJSON defaultOptions ''AssetUnion) --- $(deriveJSON defaultOptions ''PrepayPenaltyType) - - --- instance ToSchema TsPoint --- instance ToSchema (Ratio Integer) instance ToSchema Capacity instance ToSchema AmortRule --- instance ToSchema (Ratio Integer) instance ToSchema (Ratio Integer) where declareNamedSchema _ = NamedSchema Nothing <$> declareSchema (Proxy :: Proxy Double) @@ -295,7 +303,6 @@ instance ToSchema Direction instance ToSchema AmortPlan instance ToSchema DatePattern instance ToSchema IR.RateType --- instance ToSchema (IR.RoundingBy IR.RateType) instance ToSchema CF.TsRow instance ToSchema Period instance ToSchema IR.ARM diff --git a/src/AssetClass/MixedAsset.hs b/src/AssetClass/MixedAsset.hs index 70748389..c1dfd60a 100644 --- a/src/AssetClass/MixedAsset.hs +++ b/src/AssetClass/MixedAsset.hs @@ -34,6 +34,7 @@ import AssetClass.Installment import AssetClass.Receivable import AssetClass.AssetCashflow import AssetClass.FixedAsset +import AssetClass.ProjectedCashFlow import Debug.Trace import Assumptions (AssetDefaultAssumption(DefaultCDR)) @@ -165,6 +166,7 @@ calcAssetUnion (ACM.IL ast) d mRates = P.calcCashflow ast d mRates calcAssetUnion (ACM.LS ast) d mRates = P.calcCashflow ast d mRates calcAssetUnion (ACM.FA ast) d mRates = P.calcCashflow ast d mRates calcAssetUnion (ACM.RE ast) d mRates = P.calcCashflow ast d mRates +calcAssetUnion (ACM.PF ast) d mRates = P.calcCashflow ast d mRates calcAssetUnion x _ _ = error ("Failed to match proj AssetUnion"++ show x) projAssetUnion :: ACM.AssetUnion -> Date -> A.AssetPerf -> Maybe [RateAssumption] -> (CF.CashFlowFrame, Map.Map CutoffFields Balance) @@ -174,6 +176,7 @@ projAssetUnion (ACM.IL ast) d assumps mRates = P.projCashflow ast d assumps mRat projAssetUnion (ACM.LS ast) d assumps mRates = P.projCashflow ast d assumps mRates projAssetUnion (ACM.FA ast) d assumps mRates = P.projCashflow ast d assumps mRates projAssetUnion (ACM.RE ast) d assumps mRates = P.projCashflow ast d assumps mRates +projAssetUnion (ACM.PF ast) d assumps mRates = P.projCashflow ast d assumps mRates projAssetUnion x _ _ _ = error ("Failed to match proj AssetUnion"++ show x) projAssetUnionList :: [ACM.AssetUnion] -> Date -> A.ApplyAssumptionType -> Maybe [RateAssumption] -> (CF.CashFlowFrame, Map.Map CutoffFields Balance) diff --git a/src/AssetClass/ProjectedCashFlow.hs b/src/AssetClass/ProjectedCashFlow.hs new file mode 100644 index 00000000..26be0ee0 --- /dev/null +++ b/src/AssetClass/ProjectedCashFlow.hs @@ -0,0 +1,202 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE DeriveGeneric #-} + +module AssetClass.ProjectedCashFlow + (ProjectedCashflow(..)) + where + +import qualified Data.Time as T +import qualified Assumptions as A +import Asset as Ast +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 qualified Cashflow as CF + +import AssetClass.AssetBase +import AssetClass.AssetCashflow + +import Cashflow (extendTxns,TsRow(..),mflowBalance) + +import Debug.Trace + +debug = flip trace + + +projectScheduleFlow :: [CF.TsRow] -> Rate -> Balance -> [CF.TsRow] -> [DefaultRate] -> [PrepaymentRate] -> [Amount] -> [Amount] -> (Int, Rate) -> [CF.TsRow] +projectScheduleFlow trs _ last_bal [] _ _ [] [] (_,_) = trs +projectScheduleFlow trs bal_factor last_bal (flow:flows) (defRate:defRates) (ppyRate:ppyRates) recV lossV (recoveryLag,recoveryRate) + = projectScheduleFlow (trs++[tr]) surviveRate endBal flows defRates ppyRates (tail recVector) (tail lossVector) (recoveryLag,recoveryRate) -- `debug` ("===>C") + where + startBal = last_bal + defAmt = mulBR startBal defRate + ppyAmt = mulBR (startBal - defAmt) ppyRate + afterBal = startBal - defAmt - ppyAmt + + surviveRate = (1 - defRate) * (1 - ppyRate) * bal_factor + schedulePrin = mulBR (CF.mflowPrincipal flow) surviveRate --TODO round trip -- `debug` ("Schedule Principal"++(printf "%.2f" (CF.mflowPrincipal flow))++" Rate"++show(_schedule_rate)) + scheduleInt = mulBR (CF.mflowInterest flow) surviveRate + + newRec = mulBR defAmt recoveryRate + newLoss = mulBR defAmt (1 - recoveryRate) + + recVector = replace recV recoveryLag newRec + lossVector = replace lossV recoveryLag newLoss + + endBal = max 0 $ afterBal - schedulePrin -- `debug` ("start bal"++ show startBal ++"sch prin"++ show schedulePrin) + + tr = CF.MortgageFlow (CF.getDate flow) endBal schedulePrin scheduleInt ppyAmt defAmt (head recVector) (head lossVector) 0.0 Nothing Nothing Nothing--TODO missing ppy-penalty here + +projectScheduleFlow trs b_factor lastBal [] _ _ (r:rs) (l:ls) (recovery_lag,recovery_rate) + = projectScheduleFlow (trs++[tr]) b_factor lastBal [] [] [] rs ls (recovery_lag - 1,recovery_rate) + where + remain_length = length rs + lastDate = CF.getDate (last trs) + flowDate = nextDate lastDate Lib.Monthly + tr = CF.MortgageFlow flowDate lastBal 0 0 0 0 r l 0.0 Nothing Nothing Nothing + + + +projFixCfwithAssumption :: (CF.CashFlowFrame, DatePattern) -> Maybe A.AssetPerfAssumption -> Date -> CF.CashFlowFrame +projFixCfwithAssumption (cf@(CF.CashFlowFrame (begBal, begDate, accInt) flows), dp) + mPassump + asOfDay + = CF.CashFlowFrame (cb,asOfDay,Nothing) futureTxns + where + curveDatesLength = recoveryLag + length flows + endDate = CF.getDate (last flows) + (ppyRates,defRates,recoveryRate,recoveryLag) = case mPassump of + Just pAssump -> buildAssumptionPpyDefRecRate (begDate:cfDates) pAssump + Nothing -> (replicate curveDatesLength 0.0, replicate curveDatesLength 0.0, 0.0, 0) + extraDates = genSerialDates dp Exc endDate recoveryLag + cfDates = (CF.getDate <$> flows) ++ extraDates + + txns = projectScheduleFlow [] 1.0 begBal flows defRates ppyRates + (replicate curveDatesLength 0.0) + (replicate curveDatesLength 0.0) + (recoveryLag,recoveryRate) -- `debug` (" begin bal"++ show begBal) + + (futureTxns,historyM) = CF.cutoffTrs asOfDay txns + + cb = (CF.mflowBegBalance . head) futureTxns + +projIndexCashflows :: ([Date],[Balance],[Principal],Index,Spread) -> DatePattern -> Maybe A.AssetPerfAssumption -> Maybe [RateAssumption] -> CF.CashFlowFrame +projIndexCashflows (ds,bals,principals,index,spd) dp mPassump (Just ras) = + let + mIndexToApply = A.getRateAssumption ras index + indexRates = A.lookupRate0 ras index <$> ds + + rates = (spd +) <$> indexRates + interestFlow = zipWith (flip mulBIR) rates bals + flowSize = length bals + scheduleCf = CF.CashFlowFrame (head bals, head ds, Nothing) $ + zipWith12 MortgageFlow + ds + bals + principals + interestFlow + (replicate flowSize 0 ) + (replicate flowSize 0 ) + (replicate flowSize 0 ) + (replicate flowSize 0 ) + rates + (replicate flowSize Nothing) + (replicate flowSize Nothing) + (replicate flowSize Nothing) + in + projFixCfwithAssumption (scheduleCf, dp) mPassump (head ds) + +-- ^ project cashflow with fix rate portion and floater rate portion +seperateCashflows :: ProjectedCashflow -> Maybe A.AssetPerfAssumption -> Maybe [RateAssumption] -> (CF.CashFlowFrame, [CF.CashFlowFrame]) +seperateCashflows (ProjectedFlowMixFloater pflow@(CF.CashFlowFrame (begBal, begDate, accuredInt) flows) dp (fixPct,fixRate) floaterList) + mPassump + mRates + = let + begBal = CF.mflowBegBalance $ head flows + totalBals = begBal: (CF.mflowBalance <$> flows) + ds = CF.mflowDate <$> flows + flowSize = length ds + -- fix rate cashflow + fixedBals = flip mulBR fixPct <$> totalBals + fixedPrincipalFlow = flip mulBR fixPct <$> CF.mflowPrincipal <$> flows + fixedInterestFlow = flip mulBIR fixRate <$> fixedBals + fixFlow = zipWith12 MortgageFlow ds fixedBals fixedPrincipalFlow fixedInterestFlow (replicate flowSize 0) (replicate flowSize 0) (replicate flowSize 0) (replicate flowSize 0) (replicate flowSize fixRate) (replicate flowSize Nothing) (replicate flowSize Nothing) (replicate flowSize Nothing) + fixedCashFlow = projFixCfwithAssumption ((CF.CashFlowFrame ( ((flip mulBR) fixPct) begBal + , begDate + , (flip mulBR) fixPct <$> accuredInt) + fixFlow) + , dp) mPassump begDate + -- float rate cashflow + totalFloatBalFlow = zipWith (-) totalBals fixedBals + floatPrincipalFlow = zipWith (-) (CF.mflowPrincipal <$> flows) fixedPrincipalFlow + + floaterSize = length rs + rs = (\(a,b,c) -> a) <$> floaterList -- portion of each floater + spds = (\(a,b,c) -> b) <$> floaterList -- spreads + indexes = (\(a,b,c) -> c) <$> floaterList -- indexes + + floatBalsBreakDown = (\r -> flip mulBR r <$> totalFloatBalFlow ) <$> rs + floatPrincipalFlowBreakDown = (\r -> flip mulBR r <$> floatPrincipalFlow) <$> rs -- `debug` ("float bal breakdown"++ show floatBalsBreakDown) + floatedCashFlow = (\x -> projIndexCashflows x dp mPassump mRates) <$> zip5 + (replicate floaterSize ds) + floatBalsBreakDown + floatPrincipalFlowBreakDown + indexes + spds + in + (fixedCashFlow, floatedCashFlow) -- `debug` ("float cf"++ show floatedCashFlow) + + + +instance Ast.Asset ProjectedCashflow where + + getCurrentBal (ProjectedFlowFixed cf@(CF.CashFlowFrame (begBal,_,_) _) _) = begBal + getCurrentBal (ProjectedFlowMixFloater cf@(CF.CashFlowFrame (begBal,_,_) _) _ _ _) = begBal + + getOriginBal x = getCurrentBal x + getOriginRate x = 0.0 + + isDefaulted f = error "" + getOriginDate f = error "" + getOriginInfo f = error "" + + calcCashflow f@(ProjectedFlowFixed cf _) d _ = cf + + calcCashflow f@(ProjectedFlowMixFloater cf _ fxPortion floatPortion) d mRate + = let + (fixedCashFlow, floatedCashFlow) = seperateCashflows f Nothing mRate -- `debug` ("running fixed cashflow"++show fixedCashFlow) + in + foldl CF.combine fixedCashFlow floatedCashFlow +-- projFixCfwithAssumption :: (CF.CashFlowFrame, DatePattern) -> A.AssetPerfAssumption -> Date -> CF.CashFlowFrame + projCashflow f@(ProjectedFlowFixed cf dp) asOfDay (pAssump,_,_) mRates + = (projFixCfwithAssumption (cf, dp) (Just pAssump) asOfDay,Map.empty) + + projCashflow f asOfDay (pAssump, _, _) mRates + = let + (fixedCashFlow, floatedCashFlow) = seperateCashflows f (Just pAssump) mRates + in + (foldl CF.combine fixedCashFlow floatedCashFlow, Map.empty) + --(fixedCashFlow, Map.empty) + + getBorrowerNum f = 0 + + splitWith f rs = [f] + +-- instance IR.UseRate ProjectedCashflow where +-- isAdjustbleRate _ = False +-- getIndex _ = Nothing +-- getIndexes _ = Nothing \ No newline at end of file diff --git a/src/Assumptions.hs b/src/Assumptions.hs index ebc809f6..6bc680c6 100644 --- a/src/Assumptions.hs +++ b/src/Assumptions.hs @@ -14,11 +14,12 @@ module Assumptions (BondPricingInput(..) ,NonPerfAssumption(..),AssetPerf ,AssetDelinquencyAssumption(..) ,AssetDelinqPerfAssumption(..),AssetDefaultedPerfAssumption(..) - ,getCDR,calcResetDates,AssumpReceipes) + ,getCDR,calcResetDates,AssumpReceipes,IssueBondEvent) where import Call as C import Lib (Ts(..),TsPoint(..),toDate,mkRateTs) +import Liability (Bond) import Util import DateUtil import qualified Data.Map as Map @@ -64,18 +65,21 @@ data ApplyAssumptionType = PoolLevel AssetPerf -- ^ assumption for a named deal deriving (Show, Generic) +type IssueBondEvent = (String,AccName,Bond) -- bond group name, account name, bond + data NonPerfAssumption = NonPerfAssumption { - stopRunBy :: Maybe Date -- ^ optional stop day,which will stop cashflow projection - ,projectedExpense :: Maybe [(FeeName,Ts)] -- ^ optional expense projection - ,callWhen :: Maybe [C.CallOption] -- ^ optional call options set, once any of these were satisfied, then clean up waterfall is triggered - ,revolving :: Maybe RevolvingAssumption -- ^ optional revolving assumption with revoving assets - ,interest :: Maybe [RateAssumption] -- ^ optional interest rates assumptions - ,inspectOn :: Maybe [(DatePattern,DealStats)] -- ^ optional tuple list to inspect variables during waterfall run - ,buildFinancialReport :: Maybe DatePattern -- ^ optional dates to build financial reports - ,pricing :: Maybe BondPricingInput -- ^ optional bond pricing input( discount curve etc) - ,fireTrigger :: Maybe [(Date,DealCycle,String)] -- ^ optional fire a trigger + stopRunBy :: Maybe Date -- ^ optional stop day,which will stop cashflow projection + ,projectedExpense :: Maybe [(FeeName,Ts)] -- ^ optional expense projection + ,callWhen :: Maybe [C.CallOption] -- ^ optional call options set, once any of these were satisfied, then clean up waterfall is triggered + ,revolving :: Maybe RevolvingAssumption -- ^ optional revolving assumption with revoving assets + ,interest :: Maybe [RateAssumption] -- ^ optional interest rates assumptions + ,inspectOn :: Maybe [(DatePattern,DealStats)] -- ^ optional tuple list to inspect variables during waterfall run + ,buildFinancialReport :: Maybe DatePattern -- ^ optional dates to build financial reports + ,pricing :: Maybe BondPricingInput -- ^ optional bond pricing input( discount curve etc) + ,fireTrigger :: Maybe [(Date,DealCycle,String)] -- ^ optional fire a trigger ,makeWholeWhen :: Maybe (Date,Spread,Table Float Spread) -} deriving (Show,Generic) + ,issueBondSchedule :: Maybe [TsPoint IssueBondEvent] +} deriving (Show, Generic) data AssumptionInput = Single ApplyAssumptionType NonPerfAssumption -- ^ one assumption request | Multiple (Map.Map String ApplyAssumptionType) NonPerfAssumption -- ^ multiple assumption request in a single request @@ -225,6 +229,8 @@ calcResetDates (r:rs) bs $(deriveJSON defaultOptions ''BondPricingInput) +-- $(deriveJSON defaultOptions ''IssueBondEvent) + $(concat <$> traverse (deriveJSON defaultOptions) [''ApplyAssumptionType, ''AssetPerfAssumption , ''AssetDefaultedPerfAssumption, ''AssetDelinqPerfAssumption, ''NonPerfAssumption, ''AssetDefaultAssumption , ''AssetPrepayAssumption, ''RecoveryAssumption, ''ExtraStress diff --git a/src/Cashflow.hs b/src/Cashflow.hs index 75e0fb61..3590e1cd 100644 --- a/src/Cashflow.hs +++ b/src/Cashflow.hs @@ -28,6 +28,7 @@ import Lib (weightedBy,toDate,getIntervalFactors,daysBetween,paySeqLiabilitiesAm import Util (mulBR,mulBInt,mulIR,lastOf) import DateUtil ( splitByDate ) import Types +--import Deal.DealType import qualified Data.Map as Map import qualified Data.Time as T import qualified Data.List as L @@ -146,7 +147,7 @@ scaleTsRow r (MortgageDelinqFlow d b p i prep delinq def rec los rat mbn pp st) rat mbn pp - ((splitStats r) <$> st) + (splitStats r <$> st) scaleTsRow r (LoanFlow d b p i prep def rec los rat st) = LoanFlow d (fromRational r * b) (fromRational r * p) (fromRational r * i) (fromRational r * prep) (fromRational r * def) (fromRational r * rec) (fromRational r * los) rat ((splitStats r) <$> st) scaleTsRow r (LeaseFlow d b rental) = LeaseFlow d (fromRational r * b) (fromRational r * rental) @@ -161,6 +162,7 @@ type BeginStatus = (BeginBalance, BeginDate, AccuredInterest) data CashFlowFrame = CashFlowFrame BeginStatus [TsRow] | MultiCashFlowFrame (Map.Map String [CashFlowFrame]) +-- | CashFlowFrameIndex BeginStatus [TsRow] IR.Index deriving (Eq,Generic,Ord) instance Show CashFlowFrame where @@ -844,6 +846,8 @@ setPrepaymentPenalty _ _ = error "prepay pental only applies to MortgageFlow" setPrepaymentPenaltyFlow :: [Balance] -> [TsRow] -> [TsRow] setPrepaymentPenaltyFlow bals trs = [ setPrepaymentPenalty bal tr | (bal,tr) <- zip bals trs] + +-- ^ split single cashflow record by a rate splitTs :: Rate -> TsRow -> TsRow splitTs r (MortgageDelinqFlow d bal p i ppy delinq def recovery loss rate mB mPPN mStat) = MortgageDelinqFlow d (mulBR bal r) (mulBR p r) (mulBR i r) (mulBR ppy r) @@ -960,7 +964,7 @@ patchCumulative (cPrin,cPrepay,cDelinq,cDefault,cRecovery,cLoss) where newSt = (0,0,0,0,0,0) -patchCumulative a b c = error ("faile to patch cumulative stats for "++show a ++">>"++show b++">>"++show c) +patchCumulative a b c = error ("failed to patch cumulative stats for "++show a ++">>"++show b++">>"++show c) diff --git a/src/CreditEnhancement.hs b/src/CreditEnhancement.hs index 3c2bdc9f..d33fa915 100644 --- a/src/CreditEnhancement.hs +++ b/src/CreditEnhancement.hs @@ -151,7 +151,7 @@ accrueLiqProvider d liq@(LiqFacility _ _ curBal mCredit mRateType mPRateType rat let bals = weightAvgBalanceByDates [lastAccDate,d] $ getTxns mStmt in - sum $ flip mulBIR r <$> bals `debug` ("Gettnig bal of liq"++ show bals) + sum $ flip mulBIR r <$> bals -- `debug` ("Gettnig bal of liq"++ show bals) accureFee = case prate of Nothing -> 0 Just r -> diff --git a/src/Deal.hs b/src/Deal.hs index 03a3648e..a79492d8 100644 --- a/src/Deal.hs +++ b/src/Deal.hs @@ -11,6 +11,7 @@ module Deal (run,runPool,getInits,runDeal,ExpectReturn(..) ,calcTargetAmount,updateLiqProvider ,projAssetUnion,priceAssetUnion ,removePoolCf,setFutureCF,runPoolType,PoolType + ,ActionOnDate(..),DateDesp(..),OverrideType(..) ) where import qualified Accounts as A @@ -77,7 +78,7 @@ import qualified Hedge as HE debug = flip trace - +-- ^ update bond interest rate from rate assumption setBondNewRate :: Ast.Asset a => TestDeal a -> Date -> [RateAssumption] -> L.Bond -> L.Bond setBondNewRate t d ras b@(L.Bond _ _ _ ii (Just (L.PassDateSpread _ spd)) bal currentRate _ dueInt _ (Just dueIntDate) _ _ _) = b { L.bndRate = currentRate + spd, L.bndDueInt = dueInt + accrueInt, L.bndDueIntDate = Just d} @@ -237,6 +238,7 @@ testCall t d opt = C.Pre pre -> testPre d t pre _ -> error ("failed to find call options"++ show opt) +-- ^ if any of the call options are satisfied testCalls :: Ast.Asset a => TestDeal a -> Date -> [C.CallOption] -> Bool testCalls t d [] = False testCalls t d opts = any (testCall t d) opts @@ -287,9 +289,7 @@ runTriggers (t@TestDeal{status=oldStatus, triggers = Just trgM},rc) d dcycle = (\trg -> (not (trgStatus trg) || trgStatus trg && trgCurable trg)) trgsMap -- test triggers - triggeredTrgs = Map.map - (\trg -> testTrigger t d trg) - trgsToTest + triggeredTrgs = Map.map (testTrigger t d) trgsToTest -- extract trigger effects to run, if the trigger is true triggeredEffects = [ trgEffects _trg | _trg <- Map.elems triggeredTrgs, (trgStatus _trg) ] @@ -526,7 +526,30 @@ run t@TestDeal{accounts=accMap,fees=feeMap,triggers=mTrgMap,bonds=bndMap,status= bondPricingResult in run t {bonds = depositBondFlow, status = Ended } poolFlowMap (Just []) rates calls rAssump $ log++[EndRun (Just d) "MakeWhole call"] - + IssueBond d bGroupName accName bnd -> + let + newBndName = L.bndName bnd + + newBonds = case Map.lookup bGroupName bndMap of + Nothing -> bndMap + Just (L.Bond {}) -> bndMap + Just (L.BondGroup bndGrpMap) -> let + bndOInfo = (L.bndOriginInfo bnd) {L.originDate = d} + bndToInsert = bnd {L.bndOriginInfo = bndOInfo, + L.bndDueIntDate = Just d, + L.bndLastIntPay = Just d, + L.bndLastPrinPay = Just d} + in + Map.insert bGroupName + (L.BondGroup (Map.insert newBndName bndToInsert bndGrpMap)) + bndMap + + issuanceProceeds = L.bndBalance bnd + newAcc = Map.adjust (A.deposit issuanceProceeds d (IssuanceProceeds newBndName)) + accName + accMap + in + run t{bonds = newBonds, accounts = newAcc} poolFlowMap (Just ads) rates calls rAssump log _ -> error $ "Failed to match action on Date"++ show ad where cleanUpActions = Map.findWithDefault [] W.CleanUp (waterfall t) -- `debug` ("Running AD"++show(ad)) @@ -784,7 +807,7 @@ runPoolType (ResecDeal dm) mAssumps mNonPerfAssump Map.mapWithKey (\(DealBondFlow dn bn sd pct) (uDeal, mAssump) -> let (poolAssump,dealAssump) = case mAssump of - Nothing -> (Nothing, AP.NonPerfAssumption Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing) + Nothing -> (Nothing, AP.NonPerfAssumption Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing) Just (_poolAssump, _dealAssump) -> (Just _poolAssump, _dealAssump) (dealRunned, _, _, _) = runDeal uDeal DealPoolFlowPricing poolAssump dealAssump bondFlow = cutBy Inc Future sd $ concat $ Map.elems $ Map.map Stmt.getTxns $ getBondStmtByName dealRunned (Just [bn]) -- `debug` ("Bondflow from underlying runned"++ show (getBondStmtByName dealRunned (Just [bn]))) @@ -877,11 +900,18 @@ getInits t@TestDeal{fees=feeMap,pool=thePool,status=status,bonds=bndMap} mAssump Just AP.NonPerfAssumption{AP.makeWholeWhen = Just (_d,_s,_t)} -> [MakeWhole _d _s _t] _ -> [] + -- issue bonds in the future + bondIssuePlan = case mNonPerfAssump of + Just AP.NonPerfAssumption{AP.issueBondSchedule = Just bndPlan} + -> [ IssueBond _d bGroupName accName b | TsPoint _d (bGroupName,accName,b) <- bndPlan] + _ -> [] + allActionDates = let __actionDates = let a = concat [bActionDates,pActionDates,iAccIntDates,makeWholeDate ,feeAccrueDates,liqResetDates,mannualTrigger,concat rateCapSettleDates - ,concat irSwapRateDates,inspectDates, bndRateResets,financialRptDates] + ,concat irSwapRateDates,inspectDates, bndRateResets,financialRptDates + ,bondIssuePlan] in case dates t of PreClosingDates {} -> sortBy sortActionOnDate $ DealClosed closingDate:a -- `debug` ("add a closing date"++show closingDate) @@ -1003,4 +1033,4 @@ depositPoolFlow rules d pFlowMap amap -- = foldr (\pflowM acc -> depositPoolInflow rules d pflowM acc) amap $ pFlowMap `debug` ("Deposit p fd"++ show (Map.elems pFlowMap)) = foldr (\rule acc -> depositInflow d rule pFlowMap acc) amap rules -$(deriveJSON defaultOptions ''ExpectReturn) +$(deriveJSON defaultOptions ''ExpectReturn) \ No newline at end of file diff --git a/src/Deal/DealAction.hs b/src/Deal/DealAction.hs index 924fdeb8..34cef1a5 100644 --- a/src/Deal/DealAction.hs +++ b/src/Deal/DealAction.hs @@ -64,6 +64,9 @@ import Debug.Trace import Cashflow (CashFlowFrame(CashFlowFrame)) import Control.Lens hiding (element) import Control.Lens.TH +import GHC.Real (infinity) +import Deal.DealQuery (patchDatesToStats) +import Data.OpenApi (HasPatch(patch)) debug = flip trace @@ -83,16 +86,12 @@ testTrigger :: Ast.Asset a => TestDeal a -> Date -> Trigger -> Trigger testTrigger t d trigger@Trigger{trgStatus=st,trgCurable=curable,trgCondition=cond,trgStmt = tStmt} | not curable && st = trigger | otherwise = let - newSt = testPre d t cond - newTxn = TrgTxn d newSt Stmt.Empty + (memo, newSt) = testPre2 d t cond + newTxn = TrgTxn d newSt (Stmt.Tag memo) in trigger { trgStatus = newSt , trgStmt = Stmt.appendStmt tStmt newTxn} --- updateTrigger :: Ast.Asset a => TestDeal a -> Date -> Trigger -> Trigger --- updateTrigger t d trigger@Trigger{ trgStatus=st,trgCurable=cure,trgCondition=cond} --- | testTrigger t d trigger = trigger {trgStatus = True} --- | otherwise = trigger pricingAssets :: PricingMethod -> [ACM.AssetUnion] -> Date -> Amount pricingAssets (BalanceFactor currentfactor defaultfactor) assets d = 0 @@ -186,24 +185,13 @@ calcDueFee t calcDay f@(F.Fee fn (F.AnnualRateFee feeBase r) fs fd Nothing fa lp -- ^ annualized % fee base on pool balance/amount calcDueFee t@TestDeal{pool = pool} calcDay f@(F.Fee fn (F.AnnualRateFee feeBase _r) fs fd (Just _fdDay) fa lpd _) - = f{ F.feeDue=fd+newDue, F.feeDueDate = Just calcDay } -- `debug` ("Fee DUE new Due "++show calcDay ++show baseBal ++show(newDue)) + = f{ F.feeDue=fd+newDue, F.feeDueDate = Just calcDay } -- `debug` ("Fee DUE new Due "++show newDue++"oldDue"++show fd) where accrueStart = _fdDay - baseBal = case feeBase of - CurrentPoolBalance mPns -> - let - txnsByPool = getAllCollectedTxns t mPns - waBalByPool = Map.map (CF.mflowWeightAverageBalance accrueStart calcDay <$>) txnsByPool - in - sum $ fromMaybe 0 <$> Map.elems waBalByPool - OriginalPoolBalance mPns -> mulBR - (Map.findWithDefault 0.0 IssuanceBalance (getIssuanceStatsConsol t mPns)) - (yearCountFraction DC_ACT_365F accrueStart calcDay) - OriginalBondBalance -> mulBR (queryDeal t OriginalBondBalance) (yearCountFraction DC_ACT_365F accrueStart calcDay) - CurrentBondBalance -> Map.foldr (\v a-> a + L.weightAverageBalance accrueStart calcDay v ) 0.0 (bonds t) - CurrentBondBalanceOf bns -> Map.foldr (\v a-> a + L.weightAverageBalance accrueStart calcDay v ) 0.0 (getBondsByName t (Just bns)) - r = toRational $ queryDealRate t _r - newDue = mulBR baseBal r + patchedDs = patchDatesToStats t accrueStart calcDay feeBase + baseBal = queryDeal t patchedDs + r = toRational $ queryDealRate t _r -- `debug` ("Base "++ show calcDay ++">>"++ show baseBal++"From ds"++show patchedDs++"Fee Name"++fn) + newDue = mulBR baseBal r -- `debug` ("Fee Name"++fn ++"Date"++ show [accrueStart, calcDay] ++ "base bal"++ show baseBal++"new rate"++show r) -- ^ % fee base on pool balance/amount calcDueFee t calcDay f@(F.Fee fn (F.PctFee (PoolCurCollection its mPns) r ) fs fd fdDay fa lpd _) @@ -343,6 +331,7 @@ calcDueInt t calc_date mBal mRate b@(L.Bond bn bt bo bi _ bond_bal bond_rate _ i calcDuePrin :: Ast.Asset a => TestDeal a -> T.Day -> L.Bond -> L.Bond +calcDuePrin t calc_date b@(L.BondGroup bMap) = L.BondGroup $ Map.map (calcDuePrin t calc_date) bMap calcDuePrin t calc_date b@(L.Bond _ L.Sequential _ _ _ bondBal _ _ _ _ _ _ _ _) = b {L.bndDuePrin = bondBal } @@ -392,6 +381,7 @@ priceAssetUnion (ACM.LO m) d pm aps = Ast.priceAsset m d pm aps priceAssetUnion (ACM.IL m) d pm aps = Ast.priceAsset m d pm aps priceAssetUnion (ACM.LS m) d pm aps = Ast.priceAsset m d pm aps priceAssetUnion (ACM.RE m) d pm aps = Ast.priceAsset m d pm aps +priceAssetUnion (ACM.PF m) d pm aps = Ast.priceAsset m d pm aps priceAssetUnionList :: [ACM.AssetUnion] -> Date -> PricingMethod -> AP.ApplyAssumptionType -> Maybe [RateAssumption] -> [PriceResult] priceAssetUnionList assetList d pm (AP.PoolLevel assetPerf) mRates @@ -451,7 +441,7 @@ evalExtraSupportBalance d t (W.WithCondition pre s) | testPre d t pre = evalExtraSupportBalance d t s | otherwise = [0] evalExtraSupportBalance d t@TestDeal{accounts=accMap} (W.SupportAccount an _) = [A.accBalance $ accMap Map.! an] -evalExtraSupportBalance d t@TestDeal{liqProvider=Just liqMap} (W.SupportLiqFacility liqName) = [ fromMaybe 1e100 (CE.liqCredit (liqMap Map.! liqName))] -- `debug` ("Returning"++ show [ fromMaybe 1e100 (CE.liqCredit (liqMap Map.! liqName))]) +evalExtraSupportBalance d t@TestDeal{liqProvider=Just liqMap} (W.SupportLiqFacility liqName) = [ fromMaybe (fromRational (toRational infinity)) (CE.liqCredit (liqMap Map.! liqName))] -- `debug` ("Returning"++ show [ fromMaybe 1e100 (CE.liqCredit (liqMap Map.! liqName))]) evalExtraSupportBalance d t (W.MultiSupport supports) = concat $ evalExtraSupportBalance d t <$> supports @@ -620,6 +610,11 @@ performActionWrap d (t, rc, logs) (W.WatchVal ms dss) performActionWrap d (t, rc, logs) (W.ActionWithPre p actions) | testPre d t p = foldl (performActionWrap d) (t,rc,logs) actions | otherwise = (t, rc, logs) + -- where + -- trgsToRun = preHasTrigger p + -- trgsEffects = [] + -- (newT, newRc) = runEffects (t, rc) d trgsEffects + performActionWrap d (t, rc, logs) (W.ActionWithPre2 p actionsTrue actionsFalse) | testPre d t p = foldl (performActionWrap d) (t,rc,logs) actionsTrue @@ -721,7 +716,7 @@ performAction d t@TestDeal{fees=feeMap, accounts=accMap} (W.PayFeeBySeq mLimit a feesPaid = map (\(f,amt) -> F.payFee d amt f) feesAmountToBePaid -- update primary account map - accPaidOut = min actualPaidOut availAccBal + accPaidOut = min actualPaidOut availAccBal dealAfterAcc = t {accounts = Map.adjust (A.draw accPaidOut d (SeqPayFee fns)) an accMap ,fees = Map.fromList (zip fns feesPaid) <> feeMap} @@ -754,12 +749,12 @@ performAction d t@TestDeal{fees=feeMap, accounts=accMap} (W.PayFee mLimit an fns Just (DueCapAmt amt) -> min amt feeTotalDueAmt Just (DuePct pct) -> mulBR feeTotalDueAmt pct -- total actual pay out - actualPaidOut = min amtAvailable dueAmtAfterCap + actualPaidOut = min amtAvailable dueAmtAfterCap feesAmountToBePaid = zip feesToPay $ prorataFactors feeDueAmts actualPaidOut feesPaid = map (\(f,amt) -> F.payFee d amt f) feesAmountToBePaid -- update primary account map - accPaidOut = min actualPaidOut availAccBal + accPaidOut = min actualPaidOut availAccBal -- `debug` ("Actual paid out"++ show actualPaidOut++" acc bal"++ show availAccBal ++">>"++ show (snd <$> feesAmountToBePaid)++">>"++ show fns) dealAfterAcc = t {accounts = Map.adjust (A.draw accPaidOut d (SeqPayFee fns)) an accMap ,fees = Map.fromList (zip fns feesPaid) <> feeMap} @@ -783,7 +778,7 @@ performAction d t@TestDeal{bonds=bndMap, accounts=accMap, liqProvider=liqMap} (W Just (DueCapAmt amt) -> min amt (accBal + supportAvail) _ -> error $ "Not support for limit when pay int by seq" ++ show mLimit bndsList = (Map.!) bndMap <$> bnds - dueAmts = L.bndDueIntOverInt<$> bndsList + dueAmts = L.bndDueIntOverInt <$> bndsList actualPaids = paySeqLiabilitiesAmt amtAvailable dueAmts -- update bond paid bondsPaid = uncurry (L.payInt d) <$> zip actualPaids bndsList @@ -980,7 +975,7 @@ performAction d t@TestDeal{bonds=bndMap,accounts=accMap} (W.PayPrinGroup mLimit bndsWithDueMap = Map.map (calcDuePrin t d) bndsToPay bndsDueAmtsMap = Map.map (\x -> (x, L.bndDuePrin x)) bndsWithDueMap totalDueAmount = sum $ snd <$> Map.elems bndsDueAmtsMap -- `debug` (">date"++show d++" due amt"++show bndsDueAmtsMap) - payAmount = min totalDueAmount amtAvailable -- `debug` (">date total dueAmt"++ show totalDueAmount) + payAmount = min totalDueAmount amtAvailable -- `debug` (">date total available"++ show amtAvailable) -- actualPaids = paySeqLiabilitiesAmt payAmount bndsDueAmts payOutPlan = allocAmtToBonds by payAmount (Map.elems bndsDueAmtsMap) -- `debug` (">date"++ show payAmount) @@ -1002,7 +997,7 @@ performAction d t@TestDeal{bonds=bndMap,accounts=accMap} (W.PayPrinGroup mLimit performAction d t@TestDeal{bonds=bndMap} (W.AccrueAndPayIntGroup mLimit an bndName by mSupport) = let - dAfterAcc = performAction d t (W.AccrueIntGroup [bndName]) + dAfterAcc = performAction d t (W.AccrueIntGroup [bndName])-- `debug` ("Acc due int grp"++ show (getDueInt (bndMap Map.! bndName))) in performAction d dAfterAcc (W.PayIntGroup mLimit an bndName by mSupport) @@ -1349,10 +1344,14 @@ performAction d t@TestDeal{rateSwap = Just rtSwap, accounts = accsMap } (W.SwapS performAction d t2 (W.SwapPay accName sName) -performAction d t@TestDeal{ triggers = Just trgM } (W.RunTrigger loc tName) +performAction d t@TestDeal{ triggers = Just trgM } (W.RunTrigger loc tNames) = t { triggers = Just (Map.insert loc newMap trgM) } where -- newMap = Map.adjust (updateTrigger t d) tName (trgM Map.! loc) - newMap = Map.adjust (testTrigger t d) tName (trgM Map.! loc) + triggerM = trgM Map.! loc + newMap = foldr + (Map.adjust (testTrigger t d)) + triggerM + tNames performAction d t action = error $ "failed to match action>>"++show action++">>Deal"++show (name t) diff --git a/src/Deal/DealBase.hs b/src/Deal/DealBase.hs index eb4d8511..52b835fc 100644 --- a/src/Deal/DealBase.hs +++ b/src/Deal/DealBase.hs @@ -9,7 +9,10 @@ module Deal.DealBase (TestDeal(..),SPV(..),dealBonds,dealFees,dealAccounts,dealPool,PoolType(..),getIssuanceStats ,getAllAsset,getAllAssetList,getAllCollectedFrame,getLatestCollectFrame,getAllCollectedTxns ,getIssuanceStatsConsol,getAllCollectedTxnsList,dealScheduledCashflow - ,getPoolIds,getBondByName, UnderlyingDeal(..),dealCashflow, uDealFutureTxn,viewDealAllBonds) + ,getPoolIds,getBondByName, UnderlyingDeal(..),dealCashflow, uDealFutureTxn,viewDealAllBonds,DateDesp(..),ActionOnDate(..),OverrideType(..) + ,sortActionOnDate,viewDealAllBonds,dealBondGroups + ,viewDealBondsByNames + ) where import qualified Accounts as A import qualified Ledger as LD @@ -60,6 +63,93 @@ debug = flip trace -- import Data.Text (unpack) -- import Control.Monad.IO.Class (liftIO) + + + +data ActionOnDate = EarnAccInt Date AccName -- ^ sweep bank account interest + | ChangeDealStatusTo Date DealStatus -- ^ change deal status + | AccrueFee Date FeeName -- ^ accure fee + | ResetLiqProvider Date String -- ^ reset credit for liquidity provider + | ResetLiqProviderRate Date String -- ^ accure interest/premium amount for liquidity provider + | PoolCollection Date String -- ^ collect pool cashflow and deposit to accounts + | RunWaterfall Date String -- ^ execute waterfall + | DealClosed Date -- ^ actions to perform at the deal closing day, and enter a new deal status + | FireTrigger Date DealCycle String -- ^ fire a trigger + | InspectDS Date DealStats -- ^ inspect formula + | ResetIRSwapRate Date String -- ^ reset interest rate swap dates + | AccrueCapRate Date String -- ^ reset interest rate cap dates + | ResetBondRate Date String -- ^ reset bond interest rate per bond's interest rate info + | ResetSrtRate Date String + | AccrueSrt Date String + | MakeWhole Date Spread (Table Float Spread) + | IssueBond Date String AccName L.Bond + | BuildReport StartDate EndDate -- ^ build cashflow report between dates and balance report at end date + | StopRunFlag Date -- ^ stop the run with a message + | HitStatedMaturity Date -- ^ hit the stated maturity date + deriving (Show,Generic,Read) + +instance Ord ActionOnDate where + compare a1 a2 = compare (getDate a1) (getDate a2) + +instance Eq ActionOnDate where + a1 == a2 = getDate a1 == getDate a2 + + +instance TimeSeries ActionOnDate where + getDate (RunWaterfall d _) = d + getDate (ResetLiqProvider d _) = d + getDate (PoolCollection d _) = d + getDate (EarnAccInt d _) = d + getDate (AccrueFee d _) = d + getDate (DealClosed d) = d + getDate (FireTrigger d _ _) = d + getDate (ChangeDealStatusTo d _ ) = d + getDate (InspectDS d _ ) = d + getDate (ResetIRSwapRate d _ ) = d + getDate (AccrueCapRate d _ ) = d + getDate (ResetBondRate d _ ) = d + getDate (MakeWhole d _ _) = d + getDate (BuildReport sd ed) = ed + getDate (IssueBond d _ _ _) = d + + +sortActionOnDate :: ActionOnDate -> ActionOnDate -> Ordering +sortActionOnDate a1 a2 + | d1 == d2 = case (a1,a2) of + (BuildReport sd1 ed1 ,_) -> GT -- build report should be executed last + (_ , BuildReport sd1 ed1) -> LT -- build report should be executed last + (ResetIRSwapRate _ _ ,_) -> LT -- reset interest swap should be first + (_ , ResetIRSwapRate _ _) -> GT -- reset interest swap should be first + (ResetBondRate {} ,_) -> LT -- reset bond rate should be first + (_ , ResetBondRate {}) -> GT -- reset bond rate should be first + (EarnAccInt {} ,_) -> LT -- earn should be first + (_ , EarnAccInt {}) -> GT -- earn should be first + (ResetLiqProvider {} ,_) -> LT -- reset liq be first + (_ , ResetLiqProvider {}) -> GT -- reset liq be first + (PoolCollection {}, RunWaterfall {}) -> LT -- pool collection should be executed before waterfall + (RunWaterfall {}, PoolCollection {}) -> GT -- pool collection should be executed before waterfall + (_,_) -> EQ + | otherwise = compare d1 d2 + where + d1 = getDate a1 + d2 = getDate a2 + + +data OverrideType = CustomActionOnDates [ActionOnDate] + deriving (Show,Generic,Ord,Eq) + + +data DateDesp = FixInterval (Map.Map DateType Date) Period Period + -- cutoff pool closing bond payment dates + | CustomDates Date [ActionOnDate] Date [ActionOnDate] + | PatternInterval (Map.Map DateType (Date, DatePattern, Date)) + -- cutoff closing mRevolving end-date dp1-pc dp2-bond-pay + | PreClosingDates Date Date (Maybe Date) Date DateVector DateVector + -- (last collect,last pay), mRevolving end-date dp1-pool-pay dp2-bond-pay + | CurrentDates (Date,Date) (Maybe Date) Date DateVector DateVector + deriving (Show,Eq, Generic,Ord) + + class SPV a where getBondsByName :: a -> Maybe [String] -> Map.Map String L.Bond getBondBegBal :: a -> String -> Balance @@ -227,6 +317,9 @@ instance SPV (TestDeal a) where ResecDeal _ -> True _ -> False + + + viewDealAllBonds :: TestDeal a -> [L.Bond] viewDealAllBonds d = let @@ -236,12 +329,41 @@ viewDealAllBonds d = in concat $ view <$> bs +viewDealBondsByNames :: Ast.Asset a => TestDeal a -> [BondName] -> [L.Bond] +viewDealBondsByNames _ [] = [] +viewDealBondsByNames t@TestDeal{bonds= bndMap } bndNames + = let + -- bonds and bond groups + bnds = filter (\b -> L.bndName b `elem` bndNames) $ viewDealAllBonds t + -- bndsFromGrp = $ Map.filter (\L.BondGroup {} -> True) bndMap + bndsFromGrp = Map.foldrWithKey + (\k (L.BondGroup bMap) acc -> + if k `elem` bndNames + then + acc ++ Map.elems bMap + else + acc) + [] + (view dealBondGroups t ) + in + bnds ++ bndsFromGrp + dealBonds :: Ast.Asset a => Lens' (TestDeal a) (Map.Map BondName L.Bond) dealBonds = lens getter setter where getter d = bonds d setter d newBndMap = d {bonds = newBndMap} +dealBondGroups :: Ast.Asset a => Lens' (TestDeal a) (Map.Map BondName L.Bond) +dealBondGroups = lens getter setter + where + getter d = Map.filter + (\case + (L.Bond {}) -> False + (L.BondGroup {}) -> True) + (bonds d) + setter d newBndMap = d {bonds = newBndMap} + dealAccounts :: Ast.Asset a => Lens' (TestDeal a) (Map.Map AccountName A.Account) dealAccounts = lens getter setter where @@ -385,7 +507,7 @@ getAllCollectedTxnsList t mPns data UnderBond b = UnderBond BondName Rate (TestDeal b) -$(concat <$> traverse (deriveJSON defaultOptions) [''TestDeal, ''UnderlyingDeal, ''PoolType]) +$(concat <$> traverse (deriveJSON defaultOptions) [''TestDeal, ''UnderlyingDeal, ''PoolType, ''DateDesp, ''ActionOnDate, ''OverrideType]) -- $(deriveJSON defaultOptions ''UnderlyingDeal) -- $(deriveJSON defaultOptions ''PoolType) -- $(deriveJSON defaultOptions ''TestDeal) diff --git a/src/Deal/DealDate.hs b/src/Deal/DealDate.hs index 20608c16..f8fa70d2 100644 --- a/src/Deal/DealDate.hs +++ b/src/Deal/DealDate.hs @@ -7,6 +7,7 @@ module Deal.DealDate (DealDates,getClosingDate,getFirstPayDate) where import qualified Data.Map as Map +import Deal.DealBase import Types import Lib diff --git a/src/Deal/DealQuery.hs b/src/Deal/DealQuery.hs index 48a34769..54afcdea 100644 --- a/src/Deal/DealQuery.hs +++ b/src/Deal/DealQuery.hs @@ -5,7 +5,7 @@ {-# LANGUAGE ScopedTypeVariables #-} module Deal.DealQuery (queryDealBool,queryDeal,queryDealInt,queryDealRate - ,patchDateToStats, testPre, calcTargetAmount) + ,patchDateToStats,patchDatesToStats,testPre, calcTargetAmount, testPre2) where import Deal.DealBase @@ -14,6 +14,9 @@ import qualified Asset as P import Data.List import Data.Fixed import Data.Maybe +import Data.Text (replace, pack, unpack) +import Numeric.Limits +import GHC.Real import qualified Data.Map as Map import qualified Data.Set as S import qualified Liability as L @@ -29,10 +32,10 @@ import qualified Analytics as A import Stmt import Util import DateUtil -import Lib import Control.Lens hiding (element) import Control.Lens.TH import Debug.Trace +import Lib import Cashflow (CashFlowFrame(CashFlowFrame)) import qualified Cashflow as P import qualified Util as CF @@ -90,6 +93,27 @@ patchDateToStats d t Round ds rb -> Round (patchDateToStats d ds) rb _ -> t -- `debug` ("Failed to patch date to stats"++show t) +patchDatesToStats :: P.Asset a => TestDeal a -> Date -> Date -> DealStats -> DealStats +patchDatesToStats t d1 d2 ds + = case ds of + CurrentBondBalanceOf bns -> WeightedAvgCurrentBondBalance d1 d2 bns + OriginalBondBalanceOf bns -> WeightedAvgOriginalBondBalance d1 d2 bns + CurrentPoolBalance mPns -> WeightedAvgCurrentPoolBalance d1 d2 mPns + OriginalPoolBalance mPns -> WeightedAvgOriginalPoolBalance d1 d2 mPns + CurrentBondBalance -> WeightedAvgCurrentBondBalance d1 d2 (Map.keys $ bonds t) + OriginalBondBalance -> WeightedAvgOriginalBondBalance d1 d2 (Map.keys $ bonds t) + Excess dss -> Excess $ [ patchDatesToStats t d1 d2 ds | ds <- dss ] + Abs ds -> Abs $ patchDatesToStats t d1 d2 ds + Avg dss -> Avg $ [ patchDatesToStats t d1 d2 ds | ds <- dss ] + Divide ds1 ds2 -> Divide (patchDatesToStats t d1 d2 ds1) (patchDatesToStats t d1 d2 ds2) + FloorAndCap f c s -> FloorAndCap (patchDatesToStats t d1 d2 f) (patchDatesToStats t d1 d2 c) (patchDatesToStats t d1 d2 s) + Multiply dss -> Multiply $ [ patchDatesToStats t d1 d2 ds | ds <- dss ] + FloorWith ds f -> FloorWith (patchDatesToStats t d1 d2 ds) (patchDatesToStats t d1 d2 f) + CapWith ds c -> CapWith (patchDatesToStats t d1 d2 ds) (patchDatesToStats t d1 d2 c) + Round ds rb -> Round (patchDatesToStats t d1 d2 ds) rb + Sum dss -> Sum $ [ patchDatesToStats t d1 d2 ds | ds <- dss ] + x -> x + queryDealRate :: P.Asset a => TestDeal a -> DealStats -> Micro queryDealRate t s = @@ -122,14 +146,27 @@ queryDealRate t s = cumuPoolDefBal / originPoolBal -- `debug` (show idx ++" cumulative p def rate"++show cumuPoolDefBal++">>"++show originPoolBal) - BondRate bn -> toRational $ L.bndRate $ bonds t Map.! bn + BondRate bn -> case Map.lookup bn (bonds t) of + Just b@(L.Bond {}) -> toRational $ L.bndRate b + Just b@(L.BondGroup bSubMap) -> + let + bnds = Map.elems bSubMap + rates = toRational <$> L.bndRate <$> bnds + bals = L.getCurBalance <$> bnds + in + weightedBy bals rates + Nothing -> + case viewDealBondsByNames t [bn] of + [b] -> toRational $ L.bndRate b + _ -> error ("Failed to find bond by name"++bn) BondWaRate bns -> let rs = toRational <$> (\bn -> queryDealRate t (BondRate bn)) <$> bns - ws = toRational <$> (\bn -> queryDeal t (CurrentBondBalanceOf [bn])) <$> bns + ws = (\bn -> queryDeal t (CurrentBondBalanceOf [bn])) <$> bns in - toRational $ sum (zipWith (+) ws rs) / sum ws + -- toRational $ safeDivide $ sum (zipWith (*) ws rs) $ sum ws + weightedBy ws rs PoolWaRate mPns -> let @@ -211,11 +248,11 @@ queryDeal t@TestDeal{accounts=accMap, bonds=bndMap, fees=feeMap, ledgers=ledgerM OriginalBondBalance -> Map.foldr (\x acc -> getOriginBalance x + acc) 0.0 bndMap - BondDuePrin bnds -> sum $ L.bndDuePrin <$> ((bndMap Map.!) <$> bnds) --TODO Failed if bond group + BondDuePrin bnds -> sum $ L.bndDuePrin <$> viewDealBondsByNames t bnds - OriginalBondBalanceOf bnds -> sum $ getOriginBalance . (bndMap Map.!) <$> bnds + OriginalBondBalanceOf bnds -> sum $ getOriginBalance <$> viewDealBondsByNames t bnds - CurrentBondBalanceOf bns -> sum $ getCurBalance . (bndMap Map.!) <$> bns -- `debug` ("Current bond balance of"++show (sum $ L.bndBalance . (bndMap Map.!) <$> bns)) + CurrentBondBalanceOf bns -> sum $ getCurBalance <$> viewDealBondsByNames t bns CurrentPoolBalance mPns -> foldl (\acc x -> acc + P.getCurrentBal x) 0.0 (getAllAssetList t) --TODO TOBE FIX: mPns is not used @@ -237,27 +274,6 @@ queryDeal t@TestDeal{accounts=accMap, bonds=bndMap, fees=feeMap, ledgers=ledgerM Nothing -> error "No issuance balance found in the pool, pls specify it in the pool stats map `issuanceStat`" UnderlyingBondBalance mBndNames -> 0 - -- let - -- bBals = case pt of - -- ResecDeal uBndMap -> - -- case mBndNames of - -- Just bndNames -> sum $ Map.elems - -- $ Map.filterWithKey (\(UnderlyingBond (k,pct,_)) v -> S.member k (S.fromList bndNames)) bondsBals - -- Nothing -> sum $ Map.elems bondsBals - -- where - -- bondsBals = Map.mapWithKey - -- (\(UnderlyingBond (bn,pct,_)) (UnderlyingDeal uDeal _ _ _ ) -> - -- let - -- bals = queryDeal uDeal (CurrentBondBalanceOf [bn]) - -- pctBals = flip mulBR pct <$> [bals] - -- in - -- sum pctBals) - -- uBndMap - -- _ -> error "Failed to find underlying bond balance in the deal" - -- where - -- pt = view dealPool t - -- in - -- bBals AllAccBalance -> sum $ map A.accBalance $ Map.elems accMap @@ -396,15 +412,9 @@ queryDeal t@TestDeal{accounts=accMap, bonds=bndMap, fees=feeMap, ledgers=ledgerM in sum pvs -- `debug` ("pvs"++ show pvs) - -- OriginalBondBalanceOf bns -> sum $ L.originBalance . L.bndOriginInfo <$> (bndMap Map.!) <$> bns - -- IsPaidOff bns -> all isPaidOff <$> (theBondGrp Map.!) <$> bns - - - BondsIntPaidAt d bns -> let - bSubMap = getBondsByName t (Just bns) -- Map.filterWithKey (\bn b -> S.member bn bnSet) (bonds t) - stmts = map L.bndStmt $ Map.elems bSubMap + stmts = map L.bndStmt $ viewDealBondsByNames t bns ex s = case s of Nothing -> 0 Just (Statement txns) @@ -418,8 +428,7 @@ queryDeal t@TestDeal{accounts=accMap, bonds=bndMap, fees=feeMap, ledgers=ledgerM BondsPrinPaidAt d bns -> let - bSubMap = getBondsByName t (Just bns) -- Map.filterWithKey (\bn b -> S.member bn bnSet) (bonds t) - stmts = map L.bndStmt $ Map.elems bSubMap + stmts = map L.bndStmt $ viewDealBondsByNames t bns ex s = case s of Nothing -> 0 Just (Statement txns) @@ -445,7 +454,8 @@ queryDeal t@TestDeal{accounts=accMap, bonds=bndMap, fees=feeMap, ledgers=ledgerM BondTxnAmtBy d bns mCmt -> let - bnds = (bndMap Map.!) <$> bns -- Map.elems $ getBondByName t (Just bns) + -- bnds = (bndMap Map.!) <$> bns -- Map.elems $ getBondByName t (Just bns) + bnds = viewDealBondsByNames t bns in case mCmt of Just cmt -> sum [ queryTxnAmtAsOf bnd d cmt | bnd <- bnds ] @@ -497,11 +507,11 @@ queryDeal t@TestDeal{accounts=accMap, bonds=bndMap, fees=feeMap, ledgers=ledgerM sum $ map ex stmts CurrentDueBondInt bns -> - sum $ L.bndDueInt <$> (bndMap Map.!) <$> bns -- `debug` ("bond due int" ++ show ((bndMap Map.!) <$> bns )) + sum $ L.bndDueInt <$> viewDealBondsByNames t bns CurrentDueBondIntOverInt bns -> - sum $ L.bndDueIntOverInt <$> (bndMap Map.!) <$> bns -- `debug` ("bond due int" ++ show ((bndMap Map.!) <$> bns )) - + sum $ L.bndDueIntOverInt <$> viewDealBondsByNames t bns + CurrentDueBondIntTotal bns -> sum (queryDeal t <$> [CurrentDueBondInt bns,CurrentDueBondIntOverInt bns]) CurrentDueFee fns -> sum $ F.feeDue <$> (feeMap Map.!) <$> fns @@ -530,6 +540,31 @@ queryDeal t@TestDeal{accounts=accMap, bonds=bndMap, fees=feeMap, ledgers=ledgerM Nothing -> error $ "No "++ rsName ++" Found in rate swap map with key"++ show (Map.keys rm) Just rc -> H.rcNetCash rc + WeightedAvgCurrentBondBalance d1 d2 bns -> + Map.foldr (\v a-> a + (L.weightAverageBalance d1 d2 v)) -- `debug` (" Avg Bal for bond"++ show (L.weightAverageBalance d1 d2 v)) ) + 0.0 + (getBondsByName t (Just bns)) + + WeightedAvgCurrentPoolBalance d1 d2 mPns -> + let + txnsByPool = getAllCollectedTxns t mPns + waBalByPool = Map.map (CF.mflowWeightAverageBalance d1 d2 <$>) txnsByPool + in + sum $ fromMaybe 0 <$> Map.elems waBalByPool + + WeightedAvgOriginalBondBalance d1 d2 bns -> + let + bnds = viewDealBondsByNames t bns + oBals = getOriginBalance <$> bnds + bgDates = L.originDate . L.bndOriginInfo <$> bnds -- `debug` ("bals"++show oBals++">>"++ show d1++"-"++show d2) + in + sum $ (\(b,sd) -> mulBR b (yearCountFraction DC_ACT_365F (max d1 sd) d2)) <$> (zip oBals bgDates) -- `debug` ("bgDates"++show bgDates) + + WeightedAvgOriginalPoolBalance d1 d2 mPns -> + mulBR + (Map.findWithDefault 0.0 IssuanceBalance (getIssuanceStatsConsol t mPns)) + (yearCountFraction DC_ACT_365F d1 d2) + Sum _s -> sum $ map (queryDeal t) _s Subtract (ds:dss) -> @@ -546,7 +581,8 @@ queryDeal t@TestDeal{accounts=accMap, bonds=bndMap, fees=feeMap, ledgers=ledgerM Min ss -> minimum' [ queryDeal t s | s <- ss ] Divide ds1 ds2 -> if (queryDeal t ds2) == 0 then - error $ show (ds2) ++" is zero" + -- (fromRational . toRational) GHC.Real.infinity `debug` ("Hit zero") + (fromRational . toRational) Numeric.Limits.infinity -- `debug` ("Hit zero") else queryDeal t ds1 / queryDeal t ds2 @@ -562,10 +598,10 @@ queryDeal t@TestDeal{accounts=accMap, bonds=bndMap, fees=feeMap, ledgers=ledgerM FloorAndCap floor cap s -> max (queryDeal t floor) $ min (queryDeal t cap) (queryDeal t s) Factor s f -> mulBR (queryDeal t s) f - Multiply ss -> foldl1 (*) (queryDeal t <$> ss) + Multiply ss -> product (queryDeal t <$> ss) FloorWith s floor -> max (queryDeal t s) (queryDeal t floor) FloorWithZero s -> max (queryDeal t s) 0 - Excess (s1:ss) -> max 0 $ queryDeal t s1 - queryDeal t (Sum ss) + Excess (s1:ss) -> max 0 $ queryDeal t s1 - queryDeal t (Sum ss) -- `debug` ("Excess"++show (queryDeal t s1)++"ss"++show ( queryDeal t (Sum ss))) CapWith s cap -> min (queryDeal t s) (queryDeal t cap) Abs s -> abs $ queryDeal t s Round ds rb -> roundingBy rb (queryDeal t ds) @@ -594,6 +630,8 @@ queryDealBool t@TestDeal{triggers= trgs,bonds = bndMap} ds d = _ -> False IsPaidOff bns -> all isPaidOff $ (bndMap Map.!) <$> bns + + IsOutstanding bns -> all (not . isPaidOff) $ (bndMap Map.!) <$> bns TestRate ds cmp r -> let testRate = queryDealRate t ds @@ -620,6 +658,7 @@ queryDealBool t@TestDeal{triggers= trgs,bonds = bndMap} ds d = _ -> error ("Failed to query bool type formula"++ show ds) +-- ^ test a condition with a deal and a date testPre :: P.Asset a => Date -> TestDeal a -> Pre -> Bool testPre d t p = case p of @@ -649,3 +688,42 @@ testPre d t p = LE -> (<=) E -> (==) ps = patchDateToStats d + +replaceToInf :: String -> String +replaceToInf x = unpack $ Data.Text.replace nInf "-inf" $ Data.Text.replace inf "inf" c + where + c = pack x + inf = pack "179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216.00" + nInf = pack "-179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216.00" + + +-- ^ convert a condition to string in a deal context +preToStr :: P.Asset a => TestDeal a -> Date -> Pre -> String +preToStr t d p = + case p of + (IfZero ds) -> "0 == " ++ show (queryDeal t (ps ds)) + (If cmp ds bal) -> show (queryDeal t (ps ds)) ++" "++ show cmp ++" " ++show bal -- `debug` (">>> left"++ show (queryDeal t (ps ds))) + (IfRate cmp ds r) -> show (queryDealRate t (ps ds)) ++" "++ show cmp ++" " ++show r + (IfInt cmp ds r) -> show (queryDealInt t (ps ds) d) ++" "++ show cmp ++" " ++show r + (IfCurve cmp ds ts) -> show (queryDeal t (ps ds)) ++" "++ show cmp ++" " ++show (fromRational (getValByDate ts Inc d)) + (IfDate cmp _d) -> show d ++" "++ show cmp ++" " ++show _d + (IfBool ds b) -> show (queryDealBool t ds d) ++" == "++ show b + (If2 cmp ds1 ds2) -> show (queryDeal t (ps ds1)) ++" "++ show cmp ++" " ++show (queryDeal t (ps ds2)) + (IfRate2 cmp ds1 ds2) -> show (queryDealRate t (ps ds1)) ++" "++ show cmp ++" " ++show (queryDealRate t (ps ds2)) + (IfInt2 cmp ds1 ds2) -> show (queryDealInt t (ps ds1) d) ++" "++ show cmp ++" " ++show (queryDealInt t (ps ds2) d) + (IfDealStatus st) -> show (status t) ++" == "++ show st + (Always b) -> show b + (IfNot _p) -> "Not "++ preToStr t d _p + (Types.All pds) -> "All:"++ intercalate "|" (map (preToStr t d) pds) + (Types.Any pds) -> "Any:"++ intercalate "|" (map (preToStr t d) pds) + _ -> "Failed to read condition"++ show p + + where + ps = patchDateToStats d + +testPre2 :: P.Asset a => Date -> TestDeal a -> Pre -> (String, Bool) +testPre2 d t p = + let + r = testPre d t p + in + ( replaceToInf (preToStr t d p), r) diff --git a/src/Deal/DealValidation.hs b/src/Deal/DealValidation.hs index 65bb617d..b55541cf 100644 --- a/src/Deal/DealValidation.hs +++ b/src/Deal/DealValidation.hs @@ -25,6 +25,8 @@ import qualified Asset as P import qualified Assumptions as AP import qualified InterestRate as IR +import Control.Lens hiding (element) +import Control.Lens.TH import Data.Maybe import qualified Assumptions as A @@ -161,7 +163,7 @@ validateAction ((W.ActionWithPre2 p subActionList1 subActionList2):as) rs accKey validateAction (action:as) rs accKeys bndKeys feeKeys liqProviderKeys rateSwapKeys rcKeys ledgerKeys = validateAction as rs accKeys bndKeys feeKeys liqProviderKeys rateSwapKeys rcKeys ledgerKeys -extractRequiredRates :: (P.Asset a,IR.UseRate a) => TestDeal a -> Set.Set Index +extractRequiredRates :: (P.Asset a,IR.UseRate a) => TestDeal a -> Set.Set Types.Index extractRequiredRates t@TestDeal{accounts = accM ,fees = feeM ,bonds = bondM @@ -207,8 +209,20 @@ validateAggRule rules validPids = osPid = Set.elems $ Set.difference (Set.fromList (concat (getPids <$> rules))) (Set.fromList validPids) +validateFee :: F.Fee -> [ResultComponent] +-- validateFee (F.Fee fn (F.AnnualRateFee (CurrentBondBalanceOf _) _) _ _ _ _ _ _) = [] +-- validateFee (F.Fee fn (F.AnnualRateFee (OriginalBondBalanceOf _) _) _ _ _ _ _ _) = [] +-- validateFee (F.Fee fn (F.AnnualRateFee (CurrentPoolBalance _) _) _ _ _ _ _ _) = [] +-- validateFee (F.Fee fn (F.AnnualRateFee (OriginalPoolBalance _) _) _ _ _ _ _ _) = [] +-- validateFee (F.Fee fn (F.AnnualRateFee CurrentBondBalance _) _ _ _ _ _ _) = [] +-- validateFee (F.Fee fn (F.AnnualRateFee OriginalBondBalance _) _ _ _ _ _ _) = [] +-- validateFee (F.Fee fn (F.AnnualRateFee ds _) _ _ _ _ _ _ ) +-- = [ErrorMsg ("Fee Name "++fn++" has an unsupported base "++show ds)] +validateFee _ = [] + + validateReq :: (IR.UseRate a,P.Asset a) => TestDeal a -> AP.NonPerfAssumption -> (Bool,[ResultComponent]) -validateReq t assump@A.NonPerfAssumption{A.interest = intM} +validateReq t@TestDeal{accounts = accMap, fees = feeMap} assump@A.NonPerfAssumption{A.interest = intM, A.issueBondSchedule = mIssuePlan} = let ratesRequired = extractRequiredRates t ratesSupplied = case intM of @@ -219,9 +233,29 @@ validateReq t assump@A.NonPerfAssumption{A.interest = intM} [] else [ErrorMsg ("Failed to find index "++show missingIndex++"in assumption rates"++ show ratesSupplied)] + -- fee validation + feeErrors = concatMap validateFee $ Map.elems feeMap + -- issue plan validation + issuePlanError = case mIssuePlan of + Nothing -> [] + Just issueBndEventlist + -> let + bgNamesInAssump = Set.fromList $ [ bgName | TsPoint d (bgName,_,_) <- issueBndEventlist ] + bgNamesInDeal = Map.keysSet $ view dealBondGroups t + bgNameErrors = [ ErrorMsg ("issueBond:Missing Bond Group Name in Deal:"++ missingBgName ) | missingBgName <- Set.elems (Set.difference bgNamesInAssump bgNamesInDeal)] + + newBndNames = Set.fromList $ [ L.bndName bnd | TsPoint d (_,_,bnd) <- issueBndEventlist ] + existingBndNames = Set.fromList $ L.bndName <$> viewDealAllBonds t + bndNameErrors = [ ErrorMsg ("issueBond:Existing Bond Name in Deal:"++ existsBndName ) | existsBndName <- Set.elems (Set.intersection newBndNames existingBndNames)] + + acNamesInAssump = Set.fromList $ [ acName | TsPoint d (_,acName,_) <- issueBndEventlist ] + existingAccNames = Map.keysSet accMap + accNameErrors = [ ErrorMsg ("issueBond:Missing Account Name in Deal:"++ missingAccName ) | missingAccName <- Set.elems (Set.difference acNamesInAssump existingAccNames)] + in + bgNameErrors ++ accNameErrors ++ bndNameErrors (dealWarnings,dealErrors) = validatePreRun t - finalErrors = missingIndexError ++ dealErrors + finalErrors = missingIndexError ++ dealErrors ++ issuePlanError ++ feeErrors finalWarnings = dealWarnings in (null finalErrors,finalErrors++finalWarnings) diff --git a/src/InterestRate.hs b/src/InterestRate.hs index 8d14dc98..a67dec79 100644 --- a/src/InterestRate.hs +++ b/src/InterestRate.hs @@ -31,7 +31,8 @@ import Types IRate, Dates, Date, - Balance, DealStats ) + Balance ) +-- import Deal.DealType import Util import Lib diff --git a/src/Liability.hs b/src/Liability.hs index e2aa34d8..cb05cb2c 100644 --- a/src/Liability.hs +++ b/src/Liability.hs @@ -11,7 +11,8 @@ module Liability ,priceBond,PriceResult(..),pv,InterestInfo(..),RateReset(..) ,weightAverageBalance,calcZspread,payYield,scaleBond,totalDueInt ,buildRateResetDates,isAdjustble,StepUp(..),isStepUp,getDayCountFromInfo - ,calcWalBond,patchBondFactor,fundWith,writeOff,InterestOverInterestType(..)) + ,calcWalBond,patchBondFactor,fundWith,writeOff,InterestOverInterestType(..) + ,getCurBalance) where import Language.Haskell.TH @@ -27,6 +28,7 @@ import Util import DateUtil import Types hiding (BondGroup) import Analytics + import Data.Ratio import Data.Maybe @@ -43,33 +45,15 @@ import qualified Data.Map as Map import Debug.Trace import InterestRate (UseRate(getIndexes)) import Control.Lens hiding (Index) +import Control.Lens.TH import Language.Haskell.TH.Lens (_BytesPrimL) import Stmt (getTxnAmt) +import Data.Char (GeneralCategory(NotAssigned)) +import qualified Stmt as L -- import Deal.DealBase (UnderlyingDeal(futureCf)) debug = flip trace -type RateReset = DatePattern - -data InterestOverInterestType = OverCurrRateBy Rational -- ^ inflat ioi rate by pct over current rate - | OverFixSpread Spread -- ^ inflat ioi rate by fix spread - deriving (Show, Eq, Generic, Ord) - - ---------------------------- start Rate, index, spread, reset dates, daycount, floor, cap -data InterestInfo = Floater IRate Index Spread RateReset DayCount (Maybe Floor) (Maybe Cap) - | Fix IRate DayCount -- ^ fixed rate - | InterestByYield IRate - | RefRate IRate DealStats Float RateReset -- ^ interest rate depends to a formula - | CapRate InterestInfo IRate -- ^ cap rate - | FloorRate InterestInfo IRate -- ^ floor rate - | WithIoI InterestInfo InterestOverInterestType -- ^ Interest Over Interest(normal on left,IoI on right) - deriving (Show, Eq, Generic, Ord) - -data StepUp = PassDateSpread Date Spread -- ^ add a spread on a date and effective afterwards - | PassDateLadderSpread Date Spread RateReset -- ^ add a spread on the date pattern - deriving (Show, Eq, Generic, Ord) - -- | test if a bond may changes its interest rate isAdjustble :: InterestInfo -> Bool isAdjustble Floater {} = True @@ -104,14 +88,34 @@ getDayCountFromInfo (FloorRate info _) = getDayCountFromInfo info getDayCountFromInfo (WithIoI info _) = getDayCountFromInfo info getDayCountFromInfo _ = Nothing +type RateReset = DatePattern +type PlannedAmorSchedule = Ts + +data InterestOverInterestType = OverCurrRateBy Rational -- ^ inflat ioi rate by pct over current rate + | OverFixSpread Spread -- ^ inflat ioi rate by fix spread + deriving (Show, Eq, Generic, Ord, Read) + + +--------------------------- start Rate, index, spread, reset dates, daycount, floor, cap +data InterestInfo = Floater IRate Index Spread RateReset DayCount (Maybe Floor) (Maybe Cap) + | Fix IRate DayCount -- ^ fixed rate + | InterestByYield IRate + | RefRate IRate DealStats Float RateReset -- ^ interest rate depends to a formula + | CapRate InterestInfo IRate -- ^ cap rate + | FloorRate InterestInfo IRate -- ^ floor rate + | WithIoI InterestInfo InterestOverInterestType -- ^ Interest Over Interest(normal on left,IoI on right) + deriving (Show, Eq, Generic, Ord, Read) + +data StepUp = PassDateSpread Date Spread -- ^ add a spread on a date and effective afterwards + | PassDateLadderSpread Date Spread RateReset -- ^ add a spread on the date pattern + deriving (Show, Eq, Generic, Ord, Read) + data OriginalInfo = OriginalInfo { originBalance::Balance -- ^ issuance balance ,originDate::Date -- ^ issuance date ,originRate::Rate -- ^ issuance rate of the bond ,maturityDate :: Maybe Date -- ^ optional maturity date -} deriving (Show, Eq, Generic, Ord) - -type PlannedAmorSchedule = Ts +} deriving (Show, Eq, Generic, Ord, Read) data BondType = Sequential -- ^ Pass through type tranche | PAC PlannedAmorSchedule -- ^ bond with schedule amortization @@ -119,11 +123,11 @@ data BondType = Sequential -- ^ Pass through typ | Lockout Date -- ^ No principal due till date | Z -- ^ Z tranche | Equity -- ^ Equity type tranche - deriving (Show, Eq, Generic, Ord) + deriving (Show, Eq, Generic, Ord, Read) data Bond = Bond { bndName :: String - ,bndType :: BondType -- ^ bond type ,which describle the how principal due was calculated + ,bndType :: BondType -- ^ bond type ,which describe the how principal due was calculated ,bndOriginInfo :: OriginalInfo -- ^ fact data on origination ,bndInterestInfo :: InterestInfo -- ^ interest info which used to update interest rate ,bndStepUp :: Maybe StepUp -- ^ step up which update interest rate @@ -139,7 +143,7 @@ data Bond = Bond { ,bndStmt :: Maybe S.Statement -- ^ transaction history } | BondGroup (Map.Map String Bond) -- ^ bond group - deriving (Show, Eq, Generic, Ord) + deriving (Show, Eq, Generic, Ord, Read) consolStmt :: Bond -> Bond consolStmt (BondGroup bMap) = BondGroup $ Map.map consolStmt bMap @@ -330,18 +334,36 @@ backoutDueIntByYield d b@(Bond _ _ (OriginalInfo obal odate _ _) (InterestByYiel Nothing -> [] +-- weightAverageBalance :: Date -> Date -> Bond -> Balance +weightAverageBalance sd ed b@(Bond _ _ (OriginalInfo ob bd _ _ ) _ _ currentBalance _ _ _ _ _ _ _ Nothing) + = mulBR currentBalance (yearCountFraction DC_ACT_365F (max bd sd) ed) +weightAverageBalance sd ed b@(Bond _ _ (OriginalInfo ob bd _ _ ) _ _ currentBalance _ _ _ _ _ _ _ (Just stmt)) + = L.weightAvgBalance' + (max bd sd) + ed + (view S.statementTxns stmt) + -- TO BE Deprecate, it was implemented in Cashflow Frame -weightAverageBalance :: Date -> Date -> Bond -> Balance -weightAverageBalance sd ed b@(Bond _ _ _ _ _ currentBalance _ _ _ _ _ _ _ stmt) - = sum $ zipWith mulBR _bals _dfs -- `debug` ("dfs"++show(sd)++show(ed)++show(_ds)++show(_bals)++show(_dfs)) -- `debug` (">> stmt"++show(sliceStmt (bndStmt _b) sd ed)) - where - _dfs = getIntervalFactors $ [sd]++ _ds ++ [ed] - _bals = currentBalance : map S.getTxnBegBalance txns -- `debug` ("txn"++show(txns)) - _ds = S.getDates txns -- `debug` ("Slice"++show((sliceStmt (bndStmt _b) sd ed))) - _b = consolStmt b - txns = case S.sliceStmt sd ed <$> bndStmt _b of - Nothing -> [] - Just (S.Statement _txns) -> _txns-- map getTxnBalance _txns +-- weightAverageBalance :: Date -> Date -> Bond -> Balance +-- weightAverageBalance sd ed b@(Bond _ _ (OriginalInfo ob bd _ _ ) _ _ currentBalance _ _ _ _ _ _ _ stmt) +-- = sum $ zipWith mulBR (_bals txns) _dfs `debug` ("date"++ show sd ++"ed"++ show ed++"Bals"++ show _bals) +-- where +-- allTxns = S.getTxns $ bndStmt (consolStmt b) +-- _ds = S.getDates allTxns +-- begBals = currentBalance:(S.getTxnBalance <$> allTxns) -- from current balance to the end +-- balCurve = zipBalTs _ds begBals +-- +-- _dfs = getIntervalFactors $ [max bd sd]++ _ds ++ [ed] +-- +-- txns = sliceBy IE sd ed $ S.getTxns $ bndStmt b + + -- _bals = currentBalance : map S.getTxnBegBalance txns -- `debug` ("txn"++show(txns)) + -- _b = consolStmt b + -- txns = case S.sliceStmt sd ed <$> bndStmt _b of + -- Nothing -> [] + -- Just (S.Statement _txns) -> _txns-- map getTxnBalance _txns +weightAverageBalance sd ed bg@(BondGroup bMap) + = sum $ weightAverageBalance sd ed <$> Map.elems bMap -- `debug` (">>>"++ show (weightAverageBalance sd ed <$> Map.elems bMap)) calcZspread :: (Rational,Date) -> Int -> (Float, (Rational,Rational),Rational) -> Bond -> Ts -> Spread calcZspread _ _ _ b@Bond{bndStmt = Nothing} _ = error "No Cashflow for bond" @@ -455,6 +477,9 @@ instance Liable Bond where getOriginBalance b@Bond{ bndOriginInfo = bo } = originBalance bo getOriginBalance (BondGroup bMap) = sum $ getOriginBalance <$> Map.elems bMap + getDueInt b@Bond{bndDueInt=di} = di + getDueInt (BondGroup bMap) = sum $ getDueInt <$> Map.elems bMap + instance IR.UseRate Bond where isAdjustbleRate :: Bond -> Bool isAdjustbleRate Bond{bndInterestInfo = iinfo} = isAdjustble iinfo diff --git a/src/Lib.hs b/src/Lib.hs index aa05ec7f..d1b85735 100644 --- a/src/Lib.hs +++ b/src/Lib.hs @@ -14,8 +14,8 @@ module Lib ,getValOnByDate,sumValTs,subTsBetweenDates,splitTsByDate ,paySeqLiabilitiesAmt,getIntervalDays,getIntervalFactors ,zipWith8,zipWith9,zipWith10,zipWith11,zipWith12 - ,weightedBy, mkTs, DealStatus(..) - ,mkRateTs,Pre(..) + ,weightedBy, mkTs + ,mkRateTs ) where import qualified Data.Time as T @@ -28,15 +28,21 @@ import Data.Aeson.TH import Data.Aeson.Types import Data.Aeson hiding (json) import Text.Regex.TDFA -import Data.Fixed +import Data.Fixed (Fixed(..), HasResolution,Centi, resolution) +import Data.Ratio import Types import Control.Lens import Data.List.Lens import Control.Lens.TH +-- import Deal.DealType + import Debug.Trace debug = flip trace + + + annualRateToPeriodRate :: Period -> Float -> Float annualRateToPeriodRate p annualRate = 1 - (1 - annualRate ) ** n @@ -64,6 +70,7 @@ getIntervalDays :: [Date] -> [Int] getIntervalDays ds = zipWith daysBetweenI (init ds) (tail ds) -- = map (\(x,y)-> (fromIntegral (T.diffDays y x))) $ zip (init ds) (tail ds) +-- get fractional years from a set of dates getIntervalFactors :: [Date] -> [Rate] getIntervalFactors ds = (\x -> toRational x / 365) <$> getIntervalDays ds -- `debug` ("Interval Days"++show(ds)) diff --git a/src/Reports.hs b/src/Reports.hs index 2490294b..fa7fc212 100644 --- a/src/Reports.hs +++ b/src/Reports.hs @@ -88,7 +88,7 @@ buildBalanceSheet t@TestDeal{ pool = pool, bonds = bndMap , fees = feeMap , liqP --tranches bndM = [ Item bndName bndBal | (bndName,bndBal) <- Map.toList $ Map.map L.getCurBalance (bonds t) ] - bndAccPayable = [ Item ("Accured Int:"++bndName) bndAccBal | (bndName,bndAccBal) <- Map.toList (Map.map (L.bndDueInt . (calcDueInt t d Nothing Nothing)) bndMap)] + bndAccPayable = [ Item ("Accured Int:"++bndName) bndAccBal | (bndName,bndAccBal) <- Map.toList (Map.map (L.totalDueInt . (calcDueInt t d Nothing Nothing)) bndMap)] feeToPay = [ Item ("Fee Due:"++feeName) feeDueBal | (feeName,feeDueBal) <- Map.toList (Map.map (F.feeDue . (calcDueFee t d)) feeMap)] liqProviderToPay = [ Item ("Liquidity Provider:"++liqName) liqBal | (liqName,liqBal) <- Map.toList (Map.map (CE.liqBalance . (CE.accrueLiqProvider d)) (fromMaybe Map.empty liqMap))] swapToPay = [ Item ("Swap:"++rsName) (negate rsNet) | (rsName,rsNet) <- Map.toList (Map.map (HE.rsNetCash . (HE.accrueIRS d)) (fromMaybe Map.empty rsMap)) diff --git a/src/Stmt.hs b/src/Stmt.hs index c041a219..baee7752 100644 --- a/src/Stmt.hs +++ b/src/Stmt.hs @@ -9,9 +9,9 @@ module Stmt ,getTxns,getTxnComment,getTxnAmt,toDate,getTxnPrincipal,getTxnAsOf,getTxnBalance ,appendStmt,combineTxn,sliceStmt,getTxnBegBalance,getDate,getDates ,TxnComment(..),QueryByComment(..) - ,weightAvgBalanceByDates,weightAvgBalance, sumTxn, consolTxn + ,weightAvgBalanceByDates,weightAvgBalance,weightAvgBalance',sumTxn, consolTxn ,getFlow,FlowDirection(..), aggByTxnComment,scaleByFactor - ,scaleTxn,isEmptyTxn + ,scaleTxn,isEmptyTxn, statementTxns, viewBalanceAsOf ) where @@ -97,7 +97,7 @@ getTxnAmt (ExpTxn _ _ t _ _ ) = t getTxnAmt (SupportTxn _ _ t _ _ _ _) = t getTxnAmt (IrsTxn _ _ t _ _ _ _ ) = t getTxnAmt (EntryTxn _ _ t _) = t -getTxnAmt (TrgTxn {} ) = 0.0 +getTxnAmt TrgTxn {} = 0.0 getTxnAsOf :: [Txn] -> Date -> Maybe Txn getTxnAsOf txns d = find (\x -> getDate x <= d) $ reverse txns @@ -125,12 +125,25 @@ sliceStmt :: Date -> Date -> Statement -> Statement sliceStmt sd ed (Statement txns) = Statement $ sliceBy II sd ed txns +viewBalanceAsOf :: Date -> [Txn] -> Balance +viewBalanceAsOf d [] = 0.0 +viewBalanceAsOf d txns + | d < begDate = getTxnBegBalance fstTxn -- `debug` (" get first txn") + | d > endDate = getTxnBalance lstTxn -- `debug` (" get last txn") + | otherwise = getTxnBalance $ fromJust $ getTxnAsOf txns d -- `debug` ("Found txn>>>>>"++show d++show (getTxnAsOf txns d)) + where + fstTxn = head txns + lstTxn = last txns + begDate = getDate fstTxn + endDate = getDate lstTxn + weightAvgBalanceByDates :: [Date] -> [Txn] -> [Balance] weightAvgBalanceByDates ds txns - = map (\(_sd,_ed) -> weightAvgBalance _sd _ed txns) intervals -- `debug` ("interval"++ show intervals++ show txns) + = (\(_sd,_ed) -> weightAvgBalance _sd _ed txns) <$> intervals -- `debug` ("interval"++ show intervals++ show txns) where intervals = zip (init ds) (tail ds) +-- ^ Txn must be full transactions weightAvgBalance :: Date -> Date -> [Txn] -> Balance -- txn has to be between sd & ed weightAvgBalance sd ed txns = sum $ zipWith mulBR bals dsFactor -- `debug` ("WavgBalace "++show bals++show dsFactor) @@ -140,17 +153,28 @@ weightAvgBalance sd ed txns ds = [sd] ++ map getDate _txns ++ [ed] dsFactor = getIntervalFactors ds -- `debug` ("DS>>>"++show ds) +weightAvgBalance' :: Date -> Date -> [Txn] -> Balance +weightAvgBalance' sd ed [] = 0.0 +weightAvgBalance' sd ed (_txn:_txns) + = let + -- txns = sliceBy EE sd ed txns + txns = reverse $ foldl consolTxn [_txn] _txns + viewDs = sort $ [sd,ed] ++ (getDate <$> (sliceBy EE sd ed txns)) + balances = flip viewBalanceAsOf txns <$> viewDs -- `debug` ("get bal snapshot"++ show viewDs++ ">>>"++show txns) + factors = getIntervalFactors viewDs + in + sum $ zipWith mulBR balances factors --`debug` ("In weight avg bal: Factors"++show factors++"Balances"++show balances ++ "interval "++ show (sd,ed)) data Statement = Statement [Txn] - deriving (Show, Generic, Eq, Ord) + deriving (Show, Generic, Eq, Ord, Read) appendStmt :: Maybe Statement -> Txn -> Maybe Statement appendStmt (Just stmt@(Statement txns)) txn = Just $ Statement (txns++[txn]) appendStmt Nothing txn = Just $ Statement [txn] -statmentTxns :: Lens' Statement [Txn] -statmentTxns = lens getter setter +statementTxns :: Lens' Statement [Txn] +statementTxns = lens getter setter where getter (Statement txns) = txns setter (Statement _) txns = Statement txns @@ -202,6 +226,7 @@ getFlow comment = SwapOutSettle -> Outflow PurchaseAsset -> Outflow SupportDraw -> Noneflow + IssuanceProceeds _ -> Inflow TxnComments cmts -> let directionList = getFlow <$> cmts diff --git a/src/Triggers.hs b/src/Triggers.hs index 1634b4e4..ce8e37c9 100644 --- a/src/Triggers.hs +++ b/src/Triggers.hs @@ -10,7 +10,7 @@ module Triggers( import qualified Data.Text as T import qualified Stmt as S import Text.Read (readMaybe) -import Lib ( Pre, DealStatus ) +import Lib import Types import Accounts (ReserveAmount) import Waterfall (Action) diff --git a/src/Types.hs b/src/Types.hs index 6d1fef85..999bcce3 100644 --- a/src/Types.hs +++ b/src/Types.hs @@ -6,25 +6,28 @@ module Types - (DayCount(..),DateType(..),OverrideType(..),CutoffFields(..) - ,ActionOnDate(..),DealStatus(..),DatePattern(..) + (DayCount(..),DateType(..) + ,DatePattern(..) ,BondName,BondNames,FeeName,FeeNames,AccName,AccNames,AccountName - ,Pre(..),Ts(..),TsPoint(..),PoolSource(..) - ,DateDesp(..),Period(..), Threshold(..) - ,RangeType(..),CutoffType(..),CustomDataType(..) - ,Balance,DealStats(..),Index(..) - ,DealCycle(..),Cmp(..),TimeHorizion(..) + ,Ts(..),TsPoint(..),PoolSource(..) + ,Period(..), Threshold(..) + ,RangeType(..),CutoffType(..),DealStatus(..) + ,Balance,Index(..) + ,Cmp(..),TimeHorizion(..) ,Date,Dates,TimeSeries(..),IRate,Amount,Rate,StartDate,EndDate,Lag ,Spread,Floor,Cap,Interest,Principal,Cash,Default,Loss,Rental,PrepaymentPenalty - ,ResultComponent(..),SplitType(..),BookItem(..),BookItems,BalanceSheetReport(..),CashflowReport(..) + ,SplitType(..),BookItem(..),BookItems,BalanceSheetReport(..),CashflowReport(..) ,Floater,CeName,RateAssumption(..) ,PrepaymentRate,DefaultRate,RecoveryRate,RemainTerms,Recovery,Prepayment ,Table(..),lookupTable,Direction(..),epocDate,BorrowerNum - ,PricingMethod(..),sortActionOnDate,PriceResult(..),IRR,Limit(..) + ,Txn(..),TxnComment(..) ,RoundingBy(..),DateDirection(..) - ,TxnComment(..),BookDirection(..),DealStatType(..),getDealStatType + ,BookDirection(..),IRR(..),DealCycle(..),Limit(..),Pre(..) ,Liable(..),CumPrepay,CumDefault,CumDelinq,CumPrincipal,CumLoss,CumRecovery,PoolId(..) - ,DealName,lookupIntervalTable,getPriceValue,Txn(..),preToStr + ,DealName,lookupIntervalTable,CutoffFields(..),PriceResult(..) + ,DueInt,DuePremium, DueIoI,DateVector,DealStats(..) + ,PricingMethod(..),CustomDataType(..),ResultComponent(..),DealStatType(..) + ,getDealStatType,getPriceValue,preHasTrigger ) where @@ -43,12 +46,14 @@ import Language.Haskell.TH import Text.Read (readMaybe) + import Data.Aeson hiding (json) import Data.Aeson.TH import Data.Aeson.Types import Data.Fixed import Data.Ix + import Data.List (intercalate, findIndex) -- import Cashflow (CashFlowFrame) @@ -104,6 +109,17 @@ type RemainTerms = Int type BorrowerNum = Int type Lag = Int + +type Valuation = Centi +type PerFace = Micro +type WAL = Centi +type Duration = Balance +type Convexity = Micro +type Yield = Micro +type AccruedInterest = Centi +type IRR = Rational +data YieldResult = Yiel + data Index = LPR5Y | LPR1Y | LIBOR1M @@ -132,7 +148,7 @@ data Index = LPR5Y | BBSW | IRPH -- The IRPH (Índice de Referencia de Préstamos Hipotecarios) is a reference index used in Spain to fix the interest rate of mortgage loans | SONIA - deriving (Show,Eq,Generic,Ord) + deriving (Show,Eq,Generic,Ord,Read) type Floater = (Index,Spread) @@ -150,7 +166,7 @@ data DayCount = DC_30E_360 -- ^ ISMA European 30S/360 Special German Eurob | DC_30_360_ISDA -- ^ IDSA | DC_30_360_German -- ^ Gernman | DC_30_360_US -- ^ 30/360 US Municipal , Bond basis - deriving (Show,Eq,Generic,Ord) + deriving (Show,Eq,Generic,Ord,Read) data DateType = ClosingDate -- ^ deal closing day @@ -179,7 +195,7 @@ data DatePattern = MonthEnd | Exclude DatePattern [DatePattern] | OffsetBy DatePattern Int -- | DayOfWeek Int -- T.DayOfWeek - deriving (Show, Eq, Generic, Ord) + deriving (Show, Eq, Generic, Ord, Read) data Period = Daily @@ -193,23 +209,6 @@ data Period = Daily type DateVector = (Date, DatePattern) -data DealCycle = EndCollection -- ^ | collection period collection action , waterfall action - | EndCollectionWF -- ^ | collection period collection action , waterfall action - | BeginDistributionWF -- ^ | collection period collection action , waterfall action - | EndDistributionWF -- ^ | collection period collection action , waterfall action - | InWF -- ^ | collection period collection action , waterfall action - deriving (Show, Ord, Eq, Read, Generic) - - -data DealStatus = DealAccelerated (Maybe Date) -- ^ Deal is accelerated status with optinal accerlerated date - | DealDefaulted (Maybe Date) -- ^ Deal is defaulted status with optinal default date - | Amortizing -- ^ Deal is amortizing - | Revolving -- ^ Deal is revolving - | RampUp -- ^ Deal is being ramping up - | Ended -- ^ Deal is marked as closed - | PreClosing DealStatus -- ^ Deal is not closed - | Called -- ^ Deal is called - deriving (Show,Ord,Eq,Read, Generic) data RoundingBy a = RoundCeil a @@ -335,18 +334,6 @@ data BookDirection = Credit | Debit deriving (Show,Ord, Eq,Read, Generic) -data Limit = DuePct Rate -- ^ up to % of total amount due - | DueCapAmt Balance -- ^ up to $ amount - | KeepBalAmt DealStats -- ^ pay till a certain amount remains in an account - | DS DealStats -- ^ transfer with limit described by a `DealStats` - | ClearLedger String -- ^ when transfer, clear the ledger by transfer amount - | BookLedger String -- ^ when transfer, book the ledger by the transfer amount - | RemainBalPct Rate -- ^ pay till remain balance equals to a percentage of `stats` - | TillTarget -- ^ transfer amount which make target account up reach reserve balanace - | TillSource -- ^ transfer amount out till source account down back to reserve balance - | Multiple Limit Float -- ^ factor of a limit - deriving (Show,Ord,Eq,Read,Generic) - type DueInt = Balance type DuePremium = Balance @@ -354,16 +341,68 @@ type DueIoI = Balance -data Txn = BondTxn Date Balance Interest Principal IRate Cash DueInt DueIoI (Maybe Float) TxnComment -- ^ bond transaction record for interest and principal - | AccTxn Date Balance Amount TxnComment -- ^ account transaction record - | ExpTxn Date Balance Amount Balance TxnComment -- ^ expense transaction record - | SupportTxn Date (Maybe Balance) Amount Balance DueInt DuePremium TxnComment -- ^ liquidity provider transaction record - | IrsTxn Date Balance Amount IRate IRate Balance TxnComment -- ^ interest swap transaction record - | EntryTxn Date Balance Amount TxnComment -- ^ ledger book entry - | TrgTxn Date Bool TxnComment - deriving (Show, Generic, Eq) + +data DealCycle = EndCollection -- ^ | collection period collection action , waterfall action + | EndCollectionWF -- ^ | collection period collection action , waterfall action + | BeginDistributionWF -- ^ | collection period collection action , waterfall action + | EndDistributionWF -- ^ | collection period collection action , waterfall action + | InWF -- ^ | collection period collection action , waterfall action + deriving (Show, Ord, Eq, Read, Generic) + + +data DealStatus = DealAccelerated (Maybe Date) -- ^ Deal is accelerated status with optinal accerlerated date + | DealDefaulted (Maybe Date) -- ^ Deal is defaulted status with optinal default date + | Amortizing -- ^ Deal is amortizing + | Revolving -- ^ Deal is revolving + | RampUp -- ^ Deal is being ramping up + | Ended -- ^ Deal is marked as closed + | PreClosing DealStatus -- ^ Deal is not closed + | Called -- ^ Deal is called + deriving (Show,Ord,Eq,Read, Generic) + + +data PricingMethod = BalanceFactor Rate Rate -- ^ [balance] to be multiply with rate1 and rate2 if status of asset is "performing" or "defaulted" + | BalanceFactor2 Rate Rate Rate -- ^ [balance] by performing/delinq/default factor + | DefaultedBalance Rate -- ^ [balance] only liquidate defaulted balance + | PV IRate IRate -- ^ discount factor, recovery pct on default + | PVCurve Ts -- ^ [CF] Pricing cashflow with a Curve + | PvRate Rate -- ^ [CF] Pricing cashflow with a constant rate + | PvByRef DealStats -- ^ [CF] Pricing cashflow with a ref rate + | Custom Rate -- ^ custom amount + deriving (Show, Eq ,Generic, Read,Ord) + + +data Pre = IfZero DealStats + | If Cmp DealStats Balance + | IfRate Cmp DealStats Micro + | IfInt Cmp DealStats Int + | IfCurve Cmp DealStats Ts + | IfRateCurve Cmp DealStats Ts + | IfIntCurve Cmp DealStats Ts + | IfDate Cmp Date + | IfBool DealStats Bool + -- compare deal + | If2 Cmp DealStats DealStats + | IfRate2 Cmp DealStats DealStats + | IfInt2 Cmp DealStats DealStats + -- | IfRateCurve DealStats Cmp Ts + | IfDealStatus DealStatus + | Always Bool + | IfNot Pre + | Any [Pre] + | All [Pre] -- ^ + deriving (Show,Generic,Eq,Ord) + + + +data Table a b = ThresholdTable [(a,b)] + deriving (Show,Eq,Ord,Read,Generic) +data ActionType = ActionResetRate -- ^ reset interest rate from curve + | ActionAccrue -- ^ accrue liablity + deriving (Show,Eq,Ord,Read,Generic) + data TxnComment = PayInt [BondName] | PayYield BondName | PayPrin [BondName] @@ -390,27 +429,19 @@ data TxnComment = PayInt [BondName] | SwapInSettle | SwapOutSettle | PurchaseAsset + | IssuanceProceeds String | TxnDirection BookDirection | TxnComments [TxnComment] deriving (Eq, Show, Ord ,Read, Generic) - - -type Valuation = Centi -type PerFace = Micro -type WAL = Centi -type Duration = Balance -type Convexity = Micro -type Yield = Micro -type AccruedInterest = Centi -type IRR = Rational -data YieldResult = Yield - -data PriceResult = PriceResult Valuation PerFace WAL Duration Convexity AccruedInterest [Txn] - | AssetPrice Valuation WAL Duration Convexity AccruedInterest - | OASResult PriceResult [Valuation] Spread - | ZSpread Spread - deriving (Show, Eq, Generic) +data Txn = BondTxn Date Balance Interest Principal IRate Cash DueInt DueIoI (Maybe Float) TxnComment -- ^ bond transaction record for interest and principal + | AccTxn Date Balance Amount TxnComment -- ^ account transaction record + | ExpTxn Date Balance Amount Balance TxnComment -- ^ expense transaction record + | SupportTxn Date (Maybe Balance) Amount Balance DueInt DuePremium TxnComment -- ^ liquidity provider transaction record + | IrsTxn Date Balance Amount IRate IRate Balance TxnComment -- ^ interest swap transaction record + | EntryTxn Date Balance Amount TxnComment -- ^ ledger book entry + | TrgTxn Date Bool TxnComment + deriving (Show, Generic, Eq, Read) data DealStats = CurrentBondBalance @@ -462,6 +493,7 @@ data DealStats = CurrentBondBalance | CurrentBondBalanceOf [BondName] | IsMostSenior BondName [BondName] | IsPaidOff [BondName] + | IsOutstanding [BondName] | BondIntPaidAt Date BondName | BondsIntPaidAt Date [BondName] | BondPrinPaidAt Date BondName @@ -499,6 +531,13 @@ data DealStats = CurrentBondBalance | TestNot DealStats | PoolWaRate (Maybe [PoolId]) | BondRate BondName + -- weighted average balancer over period + | WeightedAvgCurrentPoolBalance Date Date (Maybe [PoolId]) + | WeightedAvgCurrentBondBalance Date Date [BondName] + | WeightedAvgOriginalPoolBalance Date Date (Maybe [PoolId]) + | WeightedAvgOriginalBondBalance Date Date [BondName] + + -- | Factor DealStats Rational | Multiply [DealStats] | Max [DealStats] @@ -509,6 +548,7 @@ data DealStats = CurrentBondBalance | Excess [DealStats] | Avg [DealStats] | Divide DealStats DealStats + | DivideRatio DealStats DealStats | Constant Rational | FloorAndCap DealStats DealStats DealStats | CustomData String Date @@ -519,90 +559,61 @@ data DealStats = CurrentBondBalance | Round DealStats (RoundingBy Balance) deriving (Show,Eq,Ord,Read,Generic) +preHasTrigger :: Pre -> [(DealCycle,String)] +preHasTrigger (IfBool (TriggersStatus dc tName) _) = [(dc,tName)] +preHasTrigger (Any ps) = concat $ preHasTrigger <$> ps +preHasTrigger (All ps) = concat $ preHasTrigger <$> ps +preHasTrigger _ = [] -data PricingMethod = BalanceFactor Rate Rate -- ^ [balance] to be multiply with rate1 and rate2 if status of asset is "performing" or "defaulted" - | BalanceFactor2 Rate Rate Rate -- ^ [balance] by performing/delinq/default factor - | DefaultedBalance Rate -- ^ [balance] only liquidate defaulted balance - | PV IRate IRate -- ^ discount factor, recovery pct on default - | PVCurve Ts -- ^ [CF] Pricing cashflow with a Curve - | PvRate Rate -- ^ [CF] Pricing cashflow with a constant rate - | PvByRef DealStats -- ^ [CF] Pricing cashflow with a ref rate - | Custom Rate -- ^ custom amount - deriving (Show, Eq ,Generic, Read,Ord) +data Limit = DuePct Rate -- ^ up to % of total amount due + | DueCapAmt Balance -- ^ up to $ amount + | KeepBalAmt DealStats -- ^ pay till a certain amount remains in an account + | DS DealStats -- ^ transfer with limit described by a `DealStats` + | ClearLedger String -- ^ when transfer, clear the ledger by transfer amount + | BookLedger String -- ^ when transfer, book the ledger by the transfer amount + | RemainBalPct Rate -- ^ pay till remain balance equals to a percentage of `stats` + | TillTarget -- ^ transfer amount which make target account up reach reserve balanace + | TillSource -- ^ transfer amount out till source account down back to reserve balance + | Multiple Limit Float -- ^ factor of a limit + deriving (Show,Ord,Eq,Read, Generic) -data Table a b = ThresholdTable [(a,b)] - deriving (Show,Eq,Ord,Read,Generic) +type BookItems = [BookItem] +data BookItem = Item String Balance + | ParentItem String BookItems + deriving (Show,Read,Generic) -data ActionType = ActionResetRate -- ^ reset interest rate from curve - | ActionAccrue -- ^ accrue liablity - deriving (Show,Eq,Ord,Read,Generic) -data ActionOnDate = EarnAccInt Date AccName -- ^ sweep bank account interest - | ChangeDealStatusTo Date DealStatus -- ^ change deal status - | AccrueFee Date FeeName -- ^ accure fee - | ResetLiqProvider Date String -- ^ reset credit for liquidity provider - | ResetLiqProviderRate Date String -- ^ accure interest/premium amount for liquidity provider - | PoolCollection Date String -- ^ collect pool cashflow and deposit to accounts - | RunWaterfall Date String -- ^ execute waterfall - | DealClosed Date -- ^ actions to perform at the deal closing day, and enter a new deal status - | FireTrigger Date DealCycle String -- ^ fire a trigger - | InspectDS Date DealStats -- ^ inspect formula - | ResetIRSwapRate Date String -- ^ reset interest rate swap dates - | AccrueCapRate Date String -- ^ reset interest rate cap dates - | ResetBondRate Date String -- ^ reset bond interest rate per bond's interest rate info - | ResetSrtRate Date String - | AccrueSrt Date String - | MakeWhole Date Spread (Table Float Spread) - | BuildReport StartDate EndDate -- ^ build cashflow report between dates and balance report at end date - | StopRunFlag Date -- ^ stop the run with a message - | HitStatedMaturity Date -- ^ hit the stated maturity date - deriving (Show,Generic,Read) - - -data DateDesp = FixInterval (Map.Map DateType Date) Period Period - -- cutoff pool closing bond payment dates - | CustomDates Date [ActionOnDate] Date [ActionOnDate] - | PatternInterval (Map.Map DateType (Date, DatePattern, Date)) - -- cutoff closing mRevolving end-date dp1-pc dp2-bond-pay - | PreClosingDates Date Date (Maybe Date) Date DateVector DateVector - -- (last collect,last pay), mRevolving end-date dp1-pool-pay dp2-bond-pay - | CurrentDates (Date,Date) (Maybe Date) Date DateVector DateVector - deriving (Show,Eq, Generic,Ord) - - -sortActionOnDate :: ActionOnDate -> ActionOnDate -> Ordering -sortActionOnDate a1 a2 - | d1 == d2 = case (a1,a2) of - (BuildReport sd1 ed1 ,_) -> GT -- build report should be executed last - (_ , BuildReport sd1 ed1) -> LT -- build report should be executed last - (ResetIRSwapRate _ _ ,_) -> LT -- reset interest swap should be first - (_ , ResetIRSwapRate _ _) -> GT -- reset interest swap should be first - (ResetBondRate {} ,_) -> LT -- reset bond rate should be first - (_ , ResetBondRate {}) -> GT -- reset bond rate should be first - (EarnAccInt {} ,_) -> LT -- earn should be first - (_ , EarnAccInt {}) -> GT -- earn should be first - (ResetLiqProvider {} ,_) -> LT -- reset liq be first - (_ , ResetLiqProvider {}) -> GT -- reset liq be first - (PoolCollection {}, RunWaterfall {}) -> LT -- pool collection should be executed before waterfall - (RunWaterfall {}, PoolCollection {}) -> GT -- pool collection should be executed before waterfall - (_,_) -> EQ - | otherwise = compare d1 d2 - where - d1 = getDate a1 - d2 = getDate a2 +data BalanceSheetReport = BalanceSheetReport { + asset :: BookItems + ,liability :: BookItems + ,equity :: BookItems + ,reportDate :: Date} -- ^ snapshot date of the balance sheet + deriving (Show,Read,Generic) + +data CashflowReport = CashflowReport { + inflow :: BookItems + ,outflow :: BookItems + ,net :: Balance + ,startDate :: Date + ,endDate :: Date } + deriving (Show,Read,Generic) -opts :: JSONKeyOptions -opts = defaultJSONKeyOptions -- { keyModifier = toLower } -data OverrideType = CustomActionOnDates [ActionOnDate] - deriving (Show,Generic,Ord,Eq) +data Threshold = Below + | EqBelow + | Above + | EqAbove + deriving (Show,Eq,Ord,Read,Generic) + + +data SplitType = EqToLeft -- if equal, the element belongs to left + | EqToRight -- if equal, the element belongs to right + | EqToLeftKeepOne + | EqToLeftKeepOnes + deriving (Show, Eq, Generic) -data CustomDataType = CustomConstant Rational - | CustomCurve Ts - | CustomDS DealStats - deriving (Show,Ord,Eq,Read,Generic) data CutoffFields = IssuanceBalance -- ^ pool issuance balance | HistoryRecoveries -- ^ cumulative recoveries @@ -617,128 +628,33 @@ data CutoffFields = IssuanceBalance -- ^ pool issuance balance | AccruedInterest -- ^ accrued interest at closing deriving (Show,Ord,Eq,Read,Generic) +data PriceResult = PriceResult Valuation PerFace WAL Duration Convexity AccruedInterest [Txn] + | AssetPrice Valuation WAL Duration Convexity AccruedInterest + | OASResult PriceResult [Valuation] Spread + | ZSpread Spread + deriving (Show, Eq, Generic) -data DealStatType = RtnBalance - | RtnRate - | RtnBool - | RtnInt - deriving (Show,Eq,Ord,Read,Generic) -getDealStatType :: DealStats -> DealStatType -getDealStatType (CumulativePoolDefaultedRateTill _ _) = RtnRate -getDealStatType (CumulativePoolDefaultedRate _) = RtnRate -getDealStatType (CumulativeNetLossRatio _) = RtnRate -getDealStatType BondFactor = RtnRate -getDealStatType (PoolFactor _) = RtnRate -getDealStatType (FutureCurrentBondFactor _) = RtnRate -getDealStatType (FutureCurrentPoolFactor _ _) = RtnRate -getDealStatType (BondWaRate _) = RtnRate -getDealStatType (PoolWaRate _) = RtnRate -getDealStatType (BondRate _) = RtnRate -getDealStatType (CurrentPoolBorrowerNum _) = RtnInt -getDealStatType (MonthsTillMaturity _) = RtnInt -getDealStatType ProjCollectPeriodNum = RtnInt +getPriceValue :: PriceResult -> Balance +getPriceValue (AssetPrice v _ _ _ _ ) = v +getPriceValue (PriceResult v _ _ _ _ _ _) = v +getPriceValue x = error $ "failed to match with type when geting price value" ++ show x -getDealStatType (IsMostSenior _ _) = RtnBool -getDealStatType (TriggersStatus _ _)= RtnBool -getDealStatType (IsDealStatus _)= RtnBool -getDealStatType TestRate {} = RtnBool -getDealStatType (TestAny _ _) = RtnBool -getDealStatType (TestAll _ _) = RtnBool -getDealStatType (Avg dss) = getDealStatType (head dss) -getDealStatType (Max dss) = getDealStatType (head dss) -getDealStatType (Min dss) = getDealStatType (head dss) -getDealStatType (Divide ds1 ds2) = getDealStatType ds1 -getDealStatType _ = RtnBalance +getValuation :: PriceResult -> PerFace +getValuation (PriceResult _ val _ _ _ _ _) = val +getValuation (OASResult pr _ _) = getValuation pr +getValuation pr = error $ "not support for pricing result"++ show pr -dealStatType _ = RtnBalance +class Liable lb where - -data Pre = IfZero DealStats - | If Cmp DealStats Balance - | IfRate Cmp DealStats Micro - | IfInt Cmp DealStats Int - | IfCurve Cmp DealStats Ts - | IfRateCurve Cmp DealStats Ts - | IfIntCurve Cmp DealStats Ts - | IfDate Cmp Date - | IfBool DealStats Bool - -- compare deal - | If2 Cmp DealStats DealStats - | IfRate2 Cmp DealStats DealStats - | IfInt2 Cmp DealStats DealStats - -- | IfRateCurve DealStats Cmp Ts - | IfDealStatus DealStatus - | Always Bool - | IfNot Pre - | Any [Pre] - | All [Pre] -- ^ - deriving (Show,Generic,Eq,Ord) - -preToStr :: Pre -> String -preToStr p = show p - - -type BookItems = [BookItem] - -data BookItem = Item String Balance - | ParentItem String BookItems - deriving (Show,Read,Generic) - - -data BalanceSheetReport = BalanceSheetReport { - asset :: BookItems - ,liability :: BookItems - ,equity :: BookItems - ,reportDate :: Date} -- ^ snapshot date of the balance sheet - deriving (Show,Read,Generic) - -data CashflowReport = CashflowReport { - inflow :: BookItems - ,outflow :: BookItems - ,net :: Balance - ,startDate :: Date - ,endDate :: Date } - deriving (Show,Read,Generic) - -data ResultComponent = CallAt Date -- ^ the date when deal called - | DealStatusChangeTo Date DealStatus DealStatus -- ^ record when status changed - | BondOutstanding String Balance Balance -- ^ when deal ends,calculate oustanding principal balance - | BondOutstandingInt String Balance Balance -- ^ when deal ends,calculate oustanding interest due - | InspectBal Date DealStats Balance -- ^ A bal value from inspection - | InspectInt Date DealStats Int -- ^ A int value from inspection - | InspectRate Date DealStats Micro -- ^ A rate value from inspection - | InspectBool Date DealStats Bool -- ^ A bool value from inspection - | FinancialReport StartDate EndDate BalanceSheetReport CashflowReport - | InspectWaterfall Date (Maybe String) [DealStats] [String] - | ErrorMsg String - | WarningMsg String - | EndRun (Maybe Date) String -- ^ end of run with a message - -- | SnapshotCashflow Date String CashFlowFrame - deriving (Show, Generic) - -data Threshold = Below - | EqBelow - | Above - | EqAbove - deriving (Show,Eq,Ord,Read,Generic) - - -data SplitType = EqToLeft -- if equal, the element belongs to left - | EqToRight -- if equal, the element belongs to right - | EqToLeftKeepOne - | EqToLeftKeepOnes - deriving (Show, Eq, Generic) - -class Liable lb where - - -- must implement - isPaidOff :: lb -> Bool - getCurBalance :: lb -> Balance - getOriginBalance :: lb -> Balance + -- must implement + isPaidOff :: lb -> Bool + getCurBalance :: lb -> Balance + getOriginBalance :: lb -> Balance + getDueInt :: lb -> Balance -- optional implement -- getTotalDue :: [lb] -> Balance @@ -761,7 +677,7 @@ lookupIntervalTable :: Ord a => Table a b -> Direction -> (a -> Bool) -> Maybe ( lookupIntervalTable (ThresholdTable rows) direction lkUpFunc = case findIndex lkUpFunc rs of Nothing -> Nothing - Just i -> if (succ i) == length rows then + Just i -> if succ i == length rows then Nothing else Just $ (rows!!i, rows!!(i+1)) -- `debug` ("Find index"++ show i) @@ -781,48 +697,88 @@ lookupIntervalTable (ThresholdTable rows) direction lkUpFunc -data RateAssumption = RateCurve Index Ts --om a:message^ a rate curve ,which value of rates depends on time +data RateAssumption = RateCurve Index Ts -- ^ a rate curve ,which value of rates depends on time | RateFlat Index IRate -- ^ a rate constant deriving (Show, Generic) -getPriceValue :: PriceResult -> Balance -getPriceValue (AssetPrice v _ _ _ _ ) = v -getPriceValue (PriceResult v _ _ _ _ _ _) = v -getPriceValue x = error $ "failed to match with type when geting price value" ++ show x - - -getValuation :: PriceResult -> PerFace -getValuation (PriceResult _ val _ _ _ _ _) = val -getValuation (OASResult pr _ _) = getValuation pr -getValuation pr = error $ "not support for pricing result"++ show pr - data TimeHorizion = ByMonth | ByYear | ByQuarter +instance TimeSeries (TsPoint a) where + getDate (TsPoint d a) = d + +instance Ord a => Ord (TsPoint a) where + compare (TsPoint d1 tv1) (TsPoint d2 tv2) = compare d1 d2 + -instance Ord ActionOnDate where - compare a1 a2 = compare (getDate a1) (getDate a2) -instance Eq ActionOnDate where - a1 == a2 = getDate a1 == getDate a2 +instance Show PoolId where + show (PoolName n) = n + show PoolConsol = "PoolConsol" + show (DealBondFlow dn bn sd r) = "BondFlow:"++dn++":"++bn++":"++show sd++":"++show r + +instance (Read PoolId) where + readsPrec d "PoolConsol" = [(PoolConsol,"")] + readsPrec d rStr = + let + pn = Data.List.Split.splitOn ":" rStr + in + case pn of + [dn,bn,sd,r] -> + let + sd' = TF.parseTimeOrError True TF.defaultTimeLocale "%Y-%m-%d" sd + r' = read r::Rate + in + [(DealBondFlow dn bn sd' r',"")] + ["PoolName",pn] -> [(PoolName pn,"")] + _ -> error $ "Invalid PoolId: "++ show pn -instance TimeSeries ActionOnDate where - getDate (RunWaterfall d _) = d - getDate (ResetLiqProvider d _) = d - getDate (PoolCollection d _) = d - getDate (EarnAccInt d _) = d - getDate (AccrueFee d _) = d - getDate (DealClosed d) = d - getDate (FireTrigger d _ _) = d - getDate (ChangeDealStatusTo d _ ) = d - getDate (InspectDS d _ ) = d - getDate (ResetIRSwapRate d _ ) = d - getDate (AccrueCapRate d _ ) = d - getDate (ResetBondRate d _ ) = d - getDate (MakeWhole d _ _) = d - getDate (BuildReport sd ed) = ed +-- instance ToJSON PoolId +-- instance FromJSON PoolId + + + + +$(deriveJSON defaultOptions ''TsPoint) +$(deriveJSON defaultOptions ''Ts) +$(deriveJSON defaultOptions ''Cmp) +$(deriveJSON defaultOptions ''PoolSource) +$(deriveJSON defaultOptions ''RoundingBy) +$(deriveJSON defaultOptions ''PoolId) + +instance ToJSONKey PoolId where + toJSONKey :: ToJSONKeyFunction PoolId + toJSONKey = toJSONKeyText (T.pack . show) + +instance FromJSONKey PoolId where + fromJSONKey = FromJSONKeyTextParser $ \t -> case readMaybe (T.unpack t) of + Just k -> pure k + Nothing -> fail ("Invalid key: " ++ show t++">>"++ show (T.unpack t)) + + + + + + + + +data ResultComponent = CallAt Date -- ^ the date when deal called + | DealStatusChangeTo Date DealStatus DealStatus -- ^ record when status changed + | BondOutstanding String Balance Balance -- ^ when deal ends,calculate oustanding principal balance + | BondOutstandingInt String Balance Balance -- ^ when deal ends,calculate oustanding interest due + | InspectBal Date DealStats Balance -- ^ A bal value from inspection + | InspectInt Date DealStats Int -- ^ A int value from inspection + | InspectRate Date DealStats Micro -- ^ A rate value from inspection + | InspectBool Date DealStats Bool -- ^ A bool value from inspection + | FinancialReport StartDate EndDate BalanceSheetReport CashflowReport + | InspectWaterfall Date (Maybe String) [DealStats] [String] + | ErrorMsg String + | WarningMsg String + | EndRun (Maybe Date) String -- ^ end of run with a message + -- | SnapshotCashflow Date String CashFlowFrame + deriving (Show, Generic) @@ -855,6 +811,8 @@ instance ToJSON TxnComment where toJSON (TxnDirection dr) = String $ T.pack $ "" toJSON SupportDraw = String $ T.pack $ "" toJSON (FundWith b bal) = String $ T.pack $ "" + toJSON (IssuanceProceeds nb) = String $ T.pack $ "" + toJSON (Tag cmt) = String $ T.pack $ "" instance FromJSON TxnComment where parseJSON = withText "Empty" parseTxn @@ -908,7 +866,10 @@ parseTxn t = case tagName of "FundWith" -> let sv = T.splitOn (T.pack ",") $ T.pack contents in - return $ FundWith (T.unpack (head sv)) (read (T.unpack (sv!!1))::Balance) + return $ FundWith (T.unpack (head sv)) (read (T.unpack (sv!!1))::Balance) +-- toJSON (IssuanceProceeds nb) = String $ T.pack $ "" + "IssuanceProceeds" -> return $ IssuanceProceeds contents + "Tag" -> return $ Tag contents where pat = "<(\\S+):(\\S+)>"::String sr = (T.unpack t =~ pat)::[[String]] @@ -916,104 +877,128 @@ parseTxn t = case tagName of contents = head sr!!2::String +data DealStatType = RtnBalance + | RtnRate + | RtnBool + | RtnInt + deriving (Show,Eq,Ord,Read,Generic) -instance TimeSeries (TsPoint a) where - getDate (TsPoint d a) = d +getDealStatType :: DealStats -> DealStatType +getDealStatType (CumulativePoolDefaultedRateTill _ _) = RtnRate +getDealStatType (CumulativePoolDefaultedRate _) = RtnRate +getDealStatType (CumulativeNetLossRatio _) = RtnRate +getDealStatType BondFactor = RtnRate +getDealStatType (PoolFactor _) = RtnRate +getDealStatType (FutureCurrentBondFactor _) = RtnRate +getDealStatType (FutureCurrentPoolFactor _ _) = RtnRate +getDealStatType (BondWaRate _) = RtnRate +getDealStatType (PoolWaRate _) = RtnRate +getDealStatType (BondRate _) = RtnRate -instance Ord a => Ord (TsPoint a) where - compare (TsPoint d1 tv1) (TsPoint d2 tv2) = compare d1 d2 +getDealStatType (CurrentPoolBorrowerNum _) = RtnInt +getDealStatType (MonthsTillMaturity _) = RtnInt +getDealStatType ProjCollectPeriodNum = RtnInt +getDealStatType (IsMostSenior _ _) = RtnBool +getDealStatType (TriggersStatus _ _)= RtnBool +getDealStatType (IsDealStatus _)= RtnBool +getDealStatType TestRate {} = RtnBool +getDealStatType (TestAny _ _) = RtnBool +getDealStatType (TestAll _ _) = RtnBool +getDealStatType (Avg dss) = getDealStatType (head dss) +getDealStatType (Max dss) = getDealStatType (head dss) +getDealStatType (Min dss) = getDealStatType (head dss) +getDealStatType (Divide ds1 ds2) = getDealStatType ds1 +getDealStatType _ = RtnBalance -instance Show PoolId where - show (PoolName n) = n - show PoolConsol = "PoolConsol" - show (DealBondFlow dn bn sd r) = "BondFlow:"++dn++":"++bn++":"++show sd++":"++show r +dealStatType _ = RtnBalance -instance (Read PoolId) where - readsPrec d "PoolConsol" = [(PoolConsol,"")] - readsPrec d rStr = - let - pn = Data.List.Split.splitOn ":" rStr - in - case pn of - [dn,bn,sd,r] -> - let - sd' = TF.parseTimeOrError True TF.defaultTimeLocale "%Y-%m-%d" sd - r' = read r::Rate - in - [(DealBondFlow dn bn sd' r',"")] - ["PoolName",pn] -> [(PoolName pn,"")] - _ -> error $ "Invalid PoolId: "++ show pn +data CustomDataType = CustomConstant Rational + | CustomCurve Ts + | CustomDS DealStats + deriving (Show,Ord,Eq,Read,Generic) --- instance ToJSON PoolId --- instance FromJSON PoolId +$(deriveJSON defaultOptions ''DealStatus) +$(concat <$> traverse (deriveJSON defaultOptions) [''DealStats, ''PricingMethod, ''DealCycle, ''DateType, ''Period, + ''DatePattern, ''Table, ''BalanceSheetReport, ''BookItem, ''CashflowReport, ''Txn] ) -$(deriveJSON defaultOptions ''TsPoint) -$(deriveJSON defaultOptions ''Ts) -$(deriveJSON defaultOptions ''Cmp) -$(deriveJSON defaultOptions ''PoolSource) -$(deriveJSON defaultOptions ''RoundingBy) -$(deriveJSON defaultOptions ''PoolId) -instance ToJSONKey PoolId where - toJSONKey :: ToJSONKeyFunction PoolId +instance ToJSONKey DateType where + toJSONKey = genericToJSONKey defaultJSONKeyOptions + +instance FromJSONKey DateType where + fromJSONKey = genericFromJSONKey defaultJSONKeyOptions + +$(deriveJSON defaultOptions ''Pre) + + +$(deriveJSON defaultOptions ''CustomDataType) +$(deriveJSON defaultOptions ''ResultComponent) + +$(deriveJSON defaultOptions ''PriceResult) +$(deriveJSON defaultOptions ''CutoffFields) + + + + +instance ToJSONKey DealCycle where toJSONKey = toJSONKeyText (T.pack . show) -instance FromJSONKey PoolId where +instance FromJSONKey DealCycle where fromJSONKey = FromJSONKeyTextParser $ \t -> case readMaybe (T.unpack t) of Just k -> pure k - Nothing -> fail ("Invalid key: " ++ show t++">>"++ show (T.unpack t)) - + Nothing -> fail ("Invalid key: " ++ show t) -$(deriveJSON defaultOptions ''DealStatus) +instance ToJSONKey CutoffFields where + toJSONKey = toJSONKeyText (Text.pack . show) +instance FromJSONKey CutoffFields where + fromJSONKey = FromJSONKeyTextParser $ \t -> case readMaybe (Text.unpack t) of + Just k -> pure k + Nothing -> fail ("Invalid key: " ++ show t) -$(concat <$> traverse (deriveJSON defaultOptions) [''DealStats, ''PricingMethod, ''DealCycle, ''DateDesp, ''DateType, ''Period, ''ActionOnDate - ,''DatePattern, ''Table, ''BalanceSheetReport, ''BookItem, ''CashflowReport] ) -instance ToJSONKey DateType where - toJSONKey = genericToJSONKey defaultJSONKeyOptions +opts :: JSONKeyOptions +opts = defaultJSONKeyOptions -- { keyModifier = toLower } -instance FromJSONKey DateType where - fromJSONKey = genericFromJSONKey defaultJSONKeyOptions $(deriveJSON defaultOptions ''Index) -$(deriveJSON defaultOptions ''Pre) $(deriveJSON defaultOptions ''DayCount) -- $(deriveJSON defaultOptions ''Table) -- $(deriveJSON defaultOptions ''ActionOnDate) -$(deriveJSON defaultOptions ''OverrideType) -- $(deriveJSON defaultOptions ''DatePattern) -- $(deriveJSON defaultOptions ''DateType) $(deriveJSON defaultOptions ''Threshold) +instance ToJSONKey Threshold where + toJSONKey = genericToJSONKey opts +instance FromJSONKey Threshold where + fromJSONKey = genericFromJSONKey opts -- $(deriveJSON defaultOptions ''DateDesp) -- $(deriveJSON defaultOptions ''Period) -$(deriveJSON defaultOptions ''CustomDataType) -$(deriveJSON defaultOptions ''ResultComponent) -- $(deriveJSON defaultOptions ''CashflowReport) -- $(deriveJSON defaultOptions ''BookItem) -- $(deriveJSON defaultOptions ''BalanceSheetReport) -- $(deriveJSON defaultOptions ''DealCycle) -$(deriveJSON defaultOptions ''Txn) -$(deriveJSON defaultOptions ''PriceResult) -$(deriveJSON defaultOptions ''Limit) -$(deriveJSON defaultOptions ''CutoffFields) + $(deriveJSON defaultOptions ''RateAssumption) $(deriveJSON defaultOptions ''BookDirection) $(deriveJSON defaultOptions ''Direction) +-- $(deriveJSON defaultOptions ''DealStats) +-- $(deriveJSON defaultOptions ''Limit) +-- $(deriveJSON defaultOptions ''TxnComment) +-- $(deriveJSON defaultOptions ''Txn) + +$(concat <$> traverse (deriveJSON defaultOptions) [''Limit] ) -- $(deriveJSON defaultOptions ''DateType) -- $(deriveJSON defaultOptions ''Threshold) -instance ToJSONKey Threshold where - toJSONKey = genericToJSONKey opts -instance FromJSONKey Threshold where - fromJSONKey = genericFromJSONKey opts + --instance FromJSON Threshold --instance ToJSON Threshold @@ -1022,26 +1007,11 @@ instance FromJSONKey Threshold where -- instance ToJSON CutoffFields -- instance FromJSON CutoffFields -instance ToJSONKey CutoffFields where - toJSONKey = toJSONKeyText (Text.pack . show) - -instance FromJSONKey CutoffFields where - fromJSONKey = FromJSONKeyTextParser $ \t -> case readMaybe (Text.unpack t) of - Just k -> pure k - Nothing -> fail ("Invalid key: " ++ show t) - -- instance ToJSON DealCycle -- instance FromJSON DealCycle -instance ToJSONKey DealCycle where - toJSONKey = toJSONKeyText (T.pack . show) - -instance FromJSONKey DealCycle where - fromJSONKey = FromJSONKeyTextParser $ \t -> case readMaybe (T.unpack t) of - Just k -> pure k - Nothing -> fail ("Invalid key: " ++ show t) -- instance FromJSON DateType diff --git a/src/Util.hs b/src/Util.hs index b28c52ef..06d730b7 100644 --- a/src/Util.hs +++ b/src/Util.hs @@ -9,8 +9,9 @@ module Util ,replace,paddingDefault, capWith, getTsDates ,shiftTsByAmt,calcWeightBalanceByDates ,maximum',minimum',roundingBy,roundingByM - ,floorWith,slice,toPeriodRateByInterval, dropLastN + ,floorWith,slice,toPeriodRateByInterval, dropLastN, zipBalTs ,lastOf,findBox + ,safeDivide -- for debug ,zyj ) @@ -27,6 +28,7 @@ import Lib import Types import DateUtil +import Numeric.Limits (infinity) import Text.Printf import Control.Exception @@ -184,6 +186,9 @@ getTsSize ts = length (getTsVals ts) zipTs :: [Date] -> [Rational] -> Ts zipTs ds rs = FloatCurve [ TsPoint d r | (d,r) <- zip ds rs ] +zipBalTs :: [Date] -> [Balance] -> Ts +zipBalTs ds rs = BalanceCurve [ TsPoint d r | (d,r) <- zip ds rs ] + multiplyTs :: CutoffType -> Ts -> Ts -> Ts multiplyTs ct (FloatCurve ts1) ts2 = FloatCurve [(TsPoint d (v * (getValByDate ts2 ct d))) | (TsPoint d v) <- ts1 ] @@ -305,6 +310,9 @@ findBox (Exc,Exc) x ((l,h):xs) | otherwise = findBox (Exc,Exc) x xs +safeDivide :: RealFloat a => a -> a -> a +safeDivide _ 0 = infinity +safeDivide x y = x / y ----- DEBUG/PRINT diff --git a/src/Validation.hs b/src/Validation.hs new file mode 100644 index 00000000..c773d93e --- /dev/null +++ b/src/Validation.hs @@ -0,0 +1,42 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE TemplateHaskell #-} + +module Validation () + where + +import Deal.DealBase +import Types +import qualified Data.Map as Map +import qualified Data.Set as Set +import Data.Maybe + +import qualified Waterfall as W +import qualified CreditEnhancement as CE +import qualified Liability as L +import qualified Accounts as A +import qualified Expense as F +import qualified Asset as P +import qualified Assumptions as AP +import qualified InterestRate as IR + +import Control.Lens hiding (element) +import Control.Lens.TH + +import Data.Maybe +import qualified Assumptions as A + + +import Debug.Trace +debug = flip trace + + + +-- valAssetRunReq :: (IR.UseRate a,P.Asset a) => TestDeal a -> AP.NonPerfAssumption -> (Bool,[ResultComponent]) +-- valAssetRunReq t@TestDeal{accounts = accMap} assump@A.NonPerfAssumption{A.interest = intM, A.issueBondSchedule = mIssuePlan} +-- = let \ No newline at end of file diff --git a/src/Waterfall.hs b/src/Waterfall.hs index c1e69c7c..db755908 100644 --- a/src/Waterfall.hs +++ b/src/Waterfall.hs @@ -118,10 +118,10 @@ data Action = Transfer (Maybe Limit) AccountName AccountName (Maybe TxnComment) -- Record booking | BookBy BookType -- ^ book an ledger with book types -- Pre - | ActionWithPre L.Pre [Action] -- ^ execute actions if
 is true 
-            | ActionWithPre2 L.Pre [Action] [Action]  -- ^ execute action1 if 
 is true ,else execute action2 
+            | ActionWithPre Pre [Action]            -- ^ execute actions if 
 is true 
+            | ActionWithPre2 Pre [Action] [Action]  -- ^ execute action1 if 
 is true ,else execute action2 
             -- Trigger
-            | RunTrigger DealCycle String        -- ^ update the trigger status during the waterfall execution
+            | RunTrigger DealCycle [String]        -- ^ update the trigger status during the waterfall execution
             -- Debug
             | WatchVal (Maybe String) [DealStats]     -- ^ inspect vals during the waterfall execution
             deriving (Show,Generic,Eq,Ord)
diff --git a/stack.yaml.lock b/stack.yaml.lock
new file mode 100644
index 00000000..db78c23b
--- /dev/null
+++ b/stack.yaml.lock
@@ -0,0 +1,12 @@
+# This file was autogenerated by Stack.
+# You should not edit this file by hand.
+# For more information, please see the documentation at:
+#   https://docs.haskellstack.org/en/stable/lock_files
+
+packages: []
+snapshots:
+- completed:
+    sha256: 629fdd46125079fa6d355106005b2ab88bd39332169dd416fda06d5c0aaa63e2
+    size: 713332
+    url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/17.yaml
+  original: lts-22.17
diff --git a/swagger.json b/swagger.json
index 94f83f94..9161affd 100644
--- a/swagger.json
+++ b/swagger.json
@@ -1530,7 +1530,10 @@
                                         "$ref": "#/components/schemas/DealCycle"
                                     },
                                     {
-                                        "type": "string"
+                                        "items": {
+                                            "type": "string"
+                                        },
+                                        "type": "array"
                                     }
                                 ],
                                 "maxItems": 2,
@@ -2048,6 +2051,41 @@
                         "title": "MakeWhole",
                         "type": "object"
                     },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
+                                    {
+                                        "$ref": "#/components/schemas/Day"
+                                    },
+                                    {
+                                        "type": "string"
+                                    },
+                                    {
+                                        "type": "string"
+                                    },
+                                    {
+                                        "$ref": "#/components/schemas/Bond"
+                                    }
+                                ],
+                                "maxItems": 4,
+                                "minItems": 4,
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "IssueBond"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "IssueBond",
+                        "type": "object"
+                    },
                     {
                         "properties": {
                             "contents": {
@@ -3232,6 +3270,25 @@
                         ],
                         "title": "RE",
                         "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "$ref": "#/components/schemas/ProjectedCashflow"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "PF"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "PF",
+                        "type": "object"
                     }
                 ]
             },
@@ -6215,6 +6272,28 @@
                         "title": "IsPaidOff",
                         "type": "object"
                     },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": {
+                                    "type": "string"
+                                },
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "IsOutstanding"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "IsOutstanding",
+                        "type": "object"
+                    },
                     {
                         "properties": {
                             "contents": {
@@ -7199,6 +7278,146 @@
                         "title": "BondRate",
                         "type": "object"
                     },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
+                                    {
+                                        "$ref": "#/components/schemas/Day"
+                                    },
+                                    {
+                                        "$ref": "#/components/schemas/Day"
+                                    },
+                                    {
+                                        "items": {
+                                            "$ref": "#/components/schemas/PoolId"
+                                        },
+                                        "type": "array"
+                                    }
+                                ],
+                                "maxItems": 3,
+                                "minItems": 3,
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "WeightedAvgCurrentPoolBalance"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "WeightedAvgCurrentPoolBalance",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
+                                    {
+                                        "$ref": "#/components/schemas/Day"
+                                    },
+                                    {
+                                        "$ref": "#/components/schemas/Day"
+                                    },
+                                    {
+                                        "items": {
+                                            "type": "string"
+                                        },
+                                        "type": "array"
+                                    }
+                                ],
+                                "maxItems": 3,
+                                "minItems": 3,
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "WeightedAvgCurrentBondBalance"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "WeightedAvgCurrentBondBalance",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
+                                    {
+                                        "$ref": "#/components/schemas/Day"
+                                    },
+                                    {
+                                        "$ref": "#/components/schemas/Day"
+                                    },
+                                    {
+                                        "items": {
+                                            "$ref": "#/components/schemas/PoolId"
+                                        },
+                                        "type": "array"
+                                    }
+                                ],
+                                "maxItems": 3,
+                                "minItems": 3,
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "WeightedAvgOriginalPoolBalance"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "WeightedAvgOriginalPoolBalance",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
+                                    {
+                                        "$ref": "#/components/schemas/Day"
+                                    },
+                                    {
+                                        "$ref": "#/components/schemas/Day"
+                                    },
+                                    {
+                                        "items": {
+                                            "type": "string"
+                                        },
+                                        "type": "array"
+                                    }
+                                ],
+                                "maxItems": 3,
+                                "minItems": 3,
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "WeightedAvgOriginalBondBalance"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "WeightedAvgOriginalBondBalance",
+                        "type": "object"
+                    },
                     {
                         "properties": {
                             "contents": {
@@ -7434,6 +7653,35 @@
                         "title": "Divide",
                         "type": "object"
                     },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
+                                    {
+                                        "$ref": "#/components/schemas/DealStats"
+                                    },
+                                    {
+                                        "$ref": "#/components/schemas/DealStats"
+                                    }
+                                ],
+                                "maxItems": 2,
+                                "minItems": 2,
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "DivideRatio"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "DivideRatio",
+                        "type": "object"
+                    },
                     {
                         "properties": {
                             "contents": {
@@ -7894,6 +8142,25 @@
                         "title": "VDeal",
                         "type": "object"
                     },
+                    {
+                        "properties": {
+                            "contents": {
+                                "$ref": "#/components/schemas/TestDeal_ProjectedCashflow"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "PDeal"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "PDeal",
+                        "type": "object"
+                    },
                     {
                         "properties": {
                             "contents": {
@@ -8487,19 +8754,34 @@
                                         "type": "number"
                                     },
                                     {
-                                        "$ref": "#/components/schemas/Day"
+                                        "$ref": "#/components/schemas/Index"
+                                    },
+                                    {
+                                        "multipleOf": 1.0e-6,
+                                        "type": "number"
                                     },
                                     {
                                         "$ref": "#/components/schemas/DatePattern"
-                                    }
-                                ],
-                                "maxItems": 3,
-                                "minItems": 3,
+                                    },
+                                    {
+                                        "$ref": "#/components/schemas/DayCount"
+                                    },
+                                    {
+                                        "multipleOf": 1.0e-6,
+                                        "type": "number"
+                                    },
+                                    {
+                                        "multipleOf": 1.0e-6,
+                                        "type": "number"
+                                    }
+                                ],
+                                "maxItems": 7,
+                                "minItems": 7,
                                 "type": "array"
                             },
                             "tag": {
                                 "enum": [
-                                    "BankAccount"
+                                    "Floater"
                                 ],
                                 "type": "string"
                             }
@@ -8508,7 +8790,7 @@
                             "tag",
                             "contents"
                         ],
-                        "title": "BankAccount",
+                        "title": "Floater",
                         "type": "object"
                     },
                     {
@@ -8516,14 +8798,65 @@
                             "contents": {
                                 "items": [
                                     {
-                                        "$ref": "#/components/schemas/Index"
+                                        "multipleOf": 1.0e-6,
+                                        "type": "number"
                                     },
+                                    {
+                                        "$ref": "#/components/schemas/DayCount"
+                                    }
+                                ],
+                                "maxItems": 2,
+                                "minItems": 2,
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "Fix"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "Fix",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "multipleOf": 1.0e-6,
+                                "type": "number"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "InterestByYield"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "InterestByYield",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
                                     {
                                         "multipleOf": 1.0e-6,
                                         "type": "number"
                                     },
                                     {
-                                        "$ref": "#/components/schemas/Day"
+                                        "$ref": "#/components/schemas/DealStats"
+                                    },
+                                    {
+                                        "format": "float",
+                                        "type": "number"
                                     },
                                     {
                                         "$ref": "#/components/schemas/DatePattern"
@@ -8535,7 +8868,7 @@
                             },
                             "tag": {
                                 "enum": [
-                                    "InvestmentAccount"
+                                    "RefRate"
                                 ],
                                 "type": "string"
                             }
@@ -8544,7 +8877,140 @@
                             "tag",
                             "contents"
                         ],
-                        "title": "InvestmentAccount",
+                        "title": "RefRate",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
+                                    {
+                                        "$ref": "#/components/schemas/InterestInfo"
+                                    },
+                                    {
+                                        "multipleOf": 1.0e-6,
+                                        "type": "number"
+                                    }
+                                ],
+                                "maxItems": 2,
+                                "minItems": 2,
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "CapRate"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "CapRate",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
+                                    {
+                                        "$ref": "#/components/schemas/InterestInfo"
+                                    },
+                                    {
+                                        "multipleOf": 1.0e-6,
+                                        "type": "number"
+                                    }
+                                ],
+                                "maxItems": 2,
+                                "minItems": 2,
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "FloorRate"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "FloorRate",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
+                                    {
+                                        "$ref": "#/components/schemas/InterestInfo"
+                                    },
+                                    {
+                                        "$ref": "#/components/schemas/InterestOverInterestType"
+                                    }
+                                ],
+                                "maxItems": 2,
+                                "minItems": 2,
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "WithIoI"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "WithIoI",
+                        "type": "object"
+                    }
+                ]
+            },
+            "InterestOverInterestType": {
+                "oneOf": [
+                    {
+                        "properties": {
+                            "contents": {
+                                "format": "double",
+                                "type": "number"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "OverCurrRateBy"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "OverCurrRateBy",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "multipleOf": 1.0e-6,
+                                "type": "number"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "OverFixSpread"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "OverFixSpread",
                         "type": "object"
                     }
                 ]
@@ -9522,6 +9988,12 @@
                         },
                         "type": "array"
                     },
+                    "issueBondSchedule": {
+                        "items": {
+                            "$ref": "#/components/schemas/TsPoint_([Char],[Char],Bond)"
+                        },
+                        "type": "array"
+                    },
                     "makeWholeWhen": {
                         "items": [
                             {
@@ -9825,6 +10297,25 @@
                         "title": "VPool",
                         "type": "object"
                     },
+                    {
+                        "properties": {
+                            "contents": {
+                                "$ref": "#/components/schemas/PoolType_ProjectedCashflow"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "PPool"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "PPool",
+                        "type": "object"
+                    },
                     {
                         "properties": {
                             "contents": {
@@ -10248,6 +10739,73 @@
                     }
                 ]
             },
+            "PoolType_ProjectedCashflow": {
+                "oneOf": [
+                    {
+                        "properties": {
+                            "contents": {
+                                "$ref": "#/components/schemas/Pool_ProjectedCashflow"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "SoloPool"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "SoloPool",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "additionalProperties": {
+                                    "$ref": "#/components/schemas/Pool_ProjectedCashflow"
+                                },
+                                "type": "object"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "MultiPool"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "MultiPool",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "additionalProperties": {
+                                    "$ref": "#/components/schemas/UnderlyingDeal_ProjectedCashflow"
+                                },
+                                "type": "object"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "ResecDeal"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "ResecDeal",
+                        "type": "object"
+                    }
+                ]
+            },
             "PoolType_Receivable": {
                 "oneOf": [
                     {
@@ -10519,6 +11077,40 @@
                 ],
                 "type": "object"
             },
+            "Pool_ProjectedCashflow": {
+                "properties": {
+                    "asOfDate": {
+                        "$ref": "#/components/schemas/Day"
+                    },
+                    "assets": {
+                        "items": {
+                            "$ref": "#/components/schemas/ProjectedCashflow"
+                        },
+                        "type": "array"
+                    },
+                    "extendPeriods": {
+                        "$ref": "#/components/schemas/DatePattern"
+                    },
+                    "futureCf": {
+                        "$ref": "#/components/schemas/CashFlowFrame"
+                    },
+                    "futureScheduleCf": {
+                        "$ref": "#/components/schemas/CashFlowFrame"
+                    },
+                    "issuanceStat": {
+                        "additionalProperties": {
+                            "multipleOf": 1.0e-2,
+                            "type": "number"
+                        },
+                        "type": "object"
+                    }
+                },
+                "required": [
+                    "assets",
+                    "asOfDate"
+                ],
+                "type": "object"
+            },
             "Pool_Receivable": {
                 "properties": {
                     "asOfDate": {
@@ -11521,25 +12113,123 @@
                                 "enum": [
                                     "PvByRef"
                                 ],
-                                "type": "string"
-                            }
-                        },
-                        "required": [
-                            "tag",
-                            "contents"
-                        ],
-                        "title": "PvByRef",
-                        "type": "object"
-                    },
-                    {
-                        "properties": {
-                            "contents": {
-                                "format": "double",
-                                "type": "number"
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "PvByRef",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "format": "double",
+                                "type": "number"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "Custom"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "Custom",
+                        "type": "object"
+                    }
+                ]
+            },
+            "ProjectedCashflow": {
+                "oneOf": [
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
+                                    {
+                                        "$ref": "#/components/schemas/CashFlowFrame"
+                                    },
+                                    {
+                                        "$ref": "#/components/schemas/DatePattern"
+                                    }
+                                ],
+                                "maxItems": 2,
+                                "minItems": 2,
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "ProjectedFlowFixed"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "ProjectedFlowFixed",
+                        "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
+                                    {
+                                        "$ref": "#/components/schemas/CashFlowFrame"
+                                    },
+                                    {
+                                        "$ref": "#/components/schemas/DatePattern"
+                                    },
+                                    {
+                                        "items": [
+                                            {
+                                                "format": "double",
+                                                "type": "number"
+                                            },
+                                            {
+                                                "multipleOf": 1.0e-6,
+                                                "type": "number"
+                                            }
+                                        ],
+                                        "maxItems": 2,
+                                        "minItems": 2,
+                                        "type": "array"
+                                    },
+                                    {
+                                        "items": {
+                                            "items": [
+                                                {
+                                                    "format": "double",
+                                                    "type": "number"
+                                                },
+                                                {
+                                                    "multipleOf": 1.0e-6,
+                                                    "type": "number"
+                                                },
+                                                {
+                                                    "$ref": "#/components/schemas/Index"
+                                                }
+                                            ],
+                                            "maxItems": 3,
+                                            "minItems": 3,
+                                            "type": "array"
+                                        },
+                                        "type": "array"
+                                    }
+                                ],
+                                "maxItems": 4,
+                                "minItems": 4,
+                                "type": "array"
                             },
                             "tag": {
                                 "enum": [
-                                    "Custom"
+                                    "ProjectedFlowMixFloater"
                                 ],
                                 "type": "string"
                             }
@@ -11548,7 +12238,7 @@
                             "tag",
                             "contents"
                         ],
-                        "title": "Custom",
+                        "title": "ProjectedFlowMixFloater",
                         "type": "object"
                     }
                 ]
@@ -13067,10 +13757,13 @@
                     },
                     {
                         "$ref": "#/components/schemas/DatePattern"
+                    },
+                    {
+                        "$ref": "#/components/schemas/Day"
                     }
                 ],
-                "maxItems": 2,
-                "minItems": 2,
+                "maxItems": 3,
+                "minItems": 3,
                 "type": "array"
             },
             "RunDealReq": {
@@ -14146,6 +14839,124 @@
                 ],
                 "type": "object"
             },
+            "TestDeal_ProjectedCashflow": {
+                "properties": {
+                    "accounts": {
+                        "additionalProperties": {
+                            "$ref": "#/components/schemas/Account"
+                        },
+                        "type": "object"
+                    },
+                    "bonds": {
+                        "additionalProperties": {
+                            "$ref": "#/components/schemas/Bond"
+                        },
+                        "type": "object"
+                    },
+                    "call": {
+                        "items": {
+                            "$ref": "#/components/schemas/CallOption"
+                        },
+                        "type": "array"
+                    },
+                    "collects": {
+                        "items": {
+                            "$ref": "#/components/schemas/CollectionRule"
+                        },
+                        "type": "array"
+                    },
+                    "currencySwap": {
+                        "additionalProperties": {
+                            "$ref": "#/components/schemas/CurrencySwap"
+                        },
+                        "type": "object"
+                    },
+                    "custom": {
+                        "additionalProperties": {
+                            "$ref": "#/components/schemas/CustomDataType"
+                        },
+                        "type": "object"
+                    },
+                    "dates": {
+                        "$ref": "#/components/schemas/DateDesp"
+                    },
+                    "fees": {
+                        "additionalProperties": {
+                            "$ref": "#/components/schemas/Fee"
+                        },
+                        "type": "object"
+                    },
+                    "ledgers": {
+                        "additionalProperties": {
+                            "$ref": "#/components/schemas/Ledger"
+                        },
+                        "type": "object"
+                    },
+                    "liqProvider": {
+                        "additionalProperties": {
+                            "$ref": "#/components/schemas/LiqFacility"
+                        },
+                        "type": "object"
+                    },
+                    "name": {
+                        "type": "string"
+                    },
+                    "overrides": {
+                        "items": {
+                            "$ref": "#/components/schemas/OverrideType"
+                        },
+                        "type": "array"
+                    },
+                    "pool": {
+                        "$ref": "#/components/schemas/PoolType_ProjectedCashflow"
+                    },
+                    "rateCap": {
+                        "additionalProperties": {
+                            "$ref": "#/components/schemas/RateCap"
+                        },
+                        "type": "object"
+                    },
+                    "rateSwap": {
+                        "additionalProperties": {
+                            "$ref": "#/components/schemas/RateSwap"
+                        },
+                        "type": "object"
+                    },
+                    "status": {
+                        "$ref": "#/components/schemas/DealStatus"
+                    },
+                    "triggers": {
+                        "additionalProperties": {
+                            "additionalProperties": {
+                                "$ref": "#/components/schemas/Trigger"
+                            },
+                            "type": "object"
+                        },
+                        "type": "object"
+                    },
+                    "waterfall": {
+                        "additionalProperties": {
+                            "items": {
+                                "$ref": "#/components/schemas/Action"
+                            },
+                            "type": "array"
+                        },
+                        "type": "object"
+                    }
+                },
+                "required": [
+                    "name",
+                    "status",
+                    "dates",
+                    "accounts",
+                    "fees",
+                    "bonds",
+                    "pool",
+                    "waterfall",
+                    "collects"
+                ],
+                "type": "object"
+            },
             "TestDeal_Receivable": {
                 "properties": {
                     "accounts": {
@@ -14277,6 +15088,9 @@
                     },
                     "trgStatus": {
                         "type": "boolean"
+                    },
+                    "trgStmt": {
+                        "$ref": "#/components/schemas/Statement"
                     }
                 },
                 "required": [
@@ -14722,6 +15536,32 @@
                 "minItems": 2,
                 "type": "array"
             },
+            "TsPoint_([Char],[Char],Bond)": {
+                "items": [
+                    {
+                        "$ref": "#/components/schemas/Day"
+                    },
+                    {
+                        "items": [
+                            {
+                                "type": "string"
+                            },
+                            {
+                                "type": "string"
+                            },
+                            {
+                                "$ref": "#/components/schemas/Bond"
+                            }
+                        ],
+                        "maxItems": 3,
+                        "minItems": 3,
+                        "type": "array"
+                    }
+                ],
+                "maxItems": 2,
+                "minItems": 2,
+                "type": "array"
+            },
             "TsPoint_Bool": {
                 "items": [
                     {
@@ -15552,6 +16392,38 @@
                         ],
                         "title": "EntryTxn",
                         "type": "object"
+                    },
+                    {
+                        "properties": {
+                            "contents": {
+                                "items": [
+                                    {
+                                        "$ref": "#/components/schemas/Day"
+                                    },
+                                    {
+                                        "type": "boolean"
+                                    },
+                                    {
+                                        "$ref": "#/components/schemas/TxnComment"
+                                    }
+                                ],
+                                "maxItems": 3,
+                                "minItems": 3,
+                                "type": "array"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "TrgTxn"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "TrgTxn",
+                        "type": "object"
                     }
                 ]
             },
@@ -16093,6 +16965,25 @@
                         "title": "PurchaseAsset",
                         "type": "object"
                     },
+                    {
+                        "properties": {
+                            "contents": {
+                                "type": "string"
+                            },
+                            "tag": {
+                                "enum": [
+                                    "IssuanceProceeds"
+                                ],
+                                "type": "string"
+                            }
+                        },
+                        "required": [
+                            "tag",
+                            "contents"
+                        ],
+                        "title": "IssuanceProceeds",
+                        "type": "object"
+                    },
                     {
                         "properties": {
                             "contents": {
@@ -16280,6 +17171,30 @@
                 ],
                 "type": "object"
             },
+            "UnderlyingDeal_ProjectedCashflow": {
+                "properties": {
+                    "deal": {
+                        "$ref": "#/components/schemas/TestDeal_ProjectedCashflow"
+                    },
+                    "futureCf": {
+                        "$ref": "#/components/schemas/CashFlowFrame"
+                    },
+                    "futureScheduleCf": {
+                        "$ref": "#/components/schemas/CashFlowFrame"
+                    },
+                    "issuanceStat": {
+                        "additionalProperties": {
+                            "multipleOf": 1.0e-2,
+                            "type": "number"
+                        },
+                        "type": "object"
+                    }
+                },
+                "required": [
+                    "deal"
+                ],
+                "type": "object"
+            },
             "UnderlyingDeal_Receivable": {
                 "properties": {
                     "deal": {
@@ -16323,7 +17238,7 @@
             "name": "BSD 3"
         },
         "title": "Hastructure API",
-        "version": "0.28.2"
+        "version": "0.28.14"
     },
     "openapi": "3.0.0",
     "paths": {
diff --git a/test/DealTest/DealTest.hs b/test/DealTest/DealTest.hs
index 8a5e5e93..a4de6443 100644
--- a/test/DealTest/DealTest.hs
+++ b/test/DealTest/DealTest.hs
@@ -115,7 +115,7 @@ baseCase = D.TestDeal {
 
 baseTests = 
   let 
-   (dealAfterRun,poolCf,_,_) = DR.runDeal baseCase DealPoolFlowPricing Nothing (AP.NonPerfAssumption Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing)
+   (dealAfterRun,poolCf,_,_) = DR.runDeal baseCase DealPoolFlowPricing Nothing (AP.NonPerfAssumption Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing)
   in 
    testGroup "Base Deal Test" 
    [ testCase "Dates pattern" $
diff --git a/test/DealTest/MultiPoolDealTest.hs b/test/DealTest/MultiPoolDealTest.hs
index c0d04b07..cabcf019 100644
--- a/test/DealTest/MultiPoolDealTest.hs
+++ b/test/DealTest/MultiPoolDealTest.hs
@@ -93,7 +93,7 @@ baseCase = D.TestDeal {
 
 baseTests = 
   let 
-   (dealAfterRun,poolCf,_,_) = DR.runDeal baseCase DealPoolFlowPricing Nothing (AP.NonPerfAssumption Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing)
+   (dealAfterRun,poolCf,_,_) = DR.runDeal baseCase DealPoolFlowPricing Nothing (AP.NonPerfAssumption Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing)
   in 
    testGroup "Base Deal Test" 
    [ testCase "Dates pattern" $
diff --git a/test/UT/StmtTest.hs b/test/UT/StmtTest.hs
index 379616db..88bd0491 100644
--- a/test/UT/StmtTest.hs
+++ b/test/UT/StmtTest.hs
@@ -27,16 +27,81 @@ txnTest =
            weightAvgBalance 
              (toDate "20221101") 
              (toDate "20221201")
-             [(AccTxn (toDate "20221115") 100 20 Empty)
-             ,(AccTxn (toDate "20221125") 90 (negate 10) Empty)
+             [AccTxn (toDate "20221115") 100 20 Empty
+             ,AccTxn (toDate "20221125") 90 (negate 10) Empty
              ]
       ,testCase "Weight Average Balance " $ 
            assertEqual "Weight Average Balacne by dates"
            [ 7.26, 5.64 ] $
            weightAvgBalanceByDates 
-             [(toDate "20221101"),(toDate "20221201"),(toDate "20221225")]
-             [(AccTxn (toDate "20221115") 100 20 Empty)
-             ,(AccTxn (toDate "20221125") 90 (negate 10) Empty)
-             ,(AccTxn (toDate "20221215") 80 (negate 10) Empty)]
+             [toDate "20221101",toDate "20221201",toDate "20221225"]
+             [AccTxn (toDate "20221115") 100 20 Empty
+             ,AccTxn (toDate "20221125") 90 (negate 10) Empty
+             ,AccTxn (toDate "20221215") 80 (negate 10) Empty]
+      , let
+          testTxns = [AccTxn (toDate "20221115") 100 20 Empty
+                      ,AccTxn (toDate "20221125") 90 (negate 10) Empty
+                      ,AccTxn (toDate "20221215") 80 (negate 10) Empty]
+        in
+        testCase "Get Txn As Of" $ 
+           assertEqual "Get Txn Asof 1"
+            Nothing $
+            getTxnAsOf testTxns (toDate "20221101")
+      , let
+          testTxns = [AccTxn (toDate "20221115") 100 20 Empty
+                      ,AccTxn (toDate "20221125") 90 (negate 10) Empty
+                      ,AccTxn (toDate "20221215") 80 (negate 10) Empty]
+        in
+        testCase "Get Txn As Of" $
+           assertEqual "Get Txn Asof 2"
+            [(Just (AccTxn (toDate "20221115") 100 20 Empty)) 
+            , (Just (AccTxn (toDate "20221215") 80 (negate 10) Empty))
+            , (Just (AccTxn (toDate "20221115") 100 20 Empty))
+            ] $
+            [(getTxnAsOf testTxns (toDate "20221115"))
+             ,(getTxnAsOf testTxns (toDate "20221216"))
+             ,(getTxnAsOf testTxns (toDate "20221120"))
+            ]
+      , let 
+          testTxns = [AccTxn (toDate "20221115") 100 20 Empty
+                      ,AccTxn (toDate "20221125") 90 (negate 10) Empty
+                      ,AccTxn (toDate "20221215") 80 (negate 10) Empty]
+        in 
+          testCase "Test View balance as of " $
+            assertEqual "View balance as of 1"
+            [80,100,100,80] $
+            [viewBalanceAsOf (toDate "20221114") testTxns,
+            viewBalanceAsOf (toDate "20221115") testTxns,
+            viewBalanceAsOf (toDate "20221116") testTxns,
+            viewBalanceAsOf (toDate "20221225") testTxns]
+
+      ,testCase "weight Average Balance 0 ' " $ 
+            assertEqual "Weight Average Balacne '"
+            0.27 $
+            weightAvgBalance' (toDate "20221115") (toDate "20221116") 
+              [BondTxn (toDate "20221115") 100 20 10 0.02 30 0 0 Nothing Empty ]
+      ,testCase "weight Average Balance 1" $ 
+            assertEqual "Weight Average Balacne '"
+            8.21 $
+            weightAvgBalance' (toDate "20221115") (toDate "20221215") 
+              [BondTxn (toDate "20221115") 100 20 10 0.02 30 0 0 Nothing Empty
+              ,BondTxn (toDate "20221215") 50 50 10 0.02 30 0 0 Nothing Empty]
+      ,testCase "weight Average Balance 2" $ 
+            assertEqual "Weight Average Balacne '"
+            14.74 $
+            weightAvgBalance' (toDate "20221101") (toDate "20230101") 
+              [BondTxn (toDate "20221115") 100 20 10 0.02 30 0 0 Nothing Empty
+              ,BondTxn (toDate "20221215") 50 50 10 0.02 30 0 0 Nothing Empty]
+      ,testCase "weight Average Balance 3" $ 
+            assertEqual "Weight Average Balacne '"
+            12.03 $
+            weightAvgBalance' (toDate "20221110") (toDate "20230101")
+              [(BondTxn (toDate "20221115") 100 20 10 0.02 30 0 0 Nothing Empty)
+              ,(BondTxn (toDate "20221215") 50 50 10 0.02 30 0 0 Nothing Empty)]
+      ,testCase "weight Average Balance 4" $ 
+            assertEqual "Weight Average Balacne '"
+            8.86 $
+            weightAvgBalance'  (toDate "20220101") (toDate "20220201")
+              [(BondTxn (toDate "20220115") 100 20 10 0.02 30 0 0 Nothing Empty) ]
     ]