From 2a8c07404d8c23a254a2fbca8b96f54c52697b34 Mon Sep 17 00:00:00 2001 From: Shawn Zhang Date: Sun, 26 Nov 2023 23:51:07 +0800 Subject: [PATCH] 0.23.4 compatible release (#126) * expose bond pay shortcut * expose bond pay int shortcut * add conditional support * add Do Nothing on trigger effects * expose PayFeeBySeq * fix irr * add support draw * update wkflow --------- Co-authored-by: yellowbean --- .github/workflows/docker-image.yml | 13 +- app/Main.hs | 2 +- src/CreditEnhancement.hs | 1 - src/Deal.hs | 1 + src/Deal/DealAction.hs | 227 +++++++++++++++++---------- src/Liability.hs | 11 +- src/Stmt.hs | 6 +- src/Triggers.hs | 1 + src/Types.hs | 2 + src/Waterfall.hs | 7 +- swagger.json | 243 ++++++++++++++++++++++++++++- 11 files changed, 410 insertions(+), 104 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 1db1491b..f80785f5 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,16 +1,15 @@ name: Docker Image CI on: - push: - tags: - - 'v*' + pull_request: + types: + - closed jobs: - - build: + if_merged: + if: github.event.pull_request.merged runs-on: ubuntu-latest - steps: - name: Set up QEMU @@ -42,4 +41,4 @@ jobs: push: true tags: ${{ secrets.DOCKER_HUB_USERNAME }}/hastructure:dev cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/hastructure:buildcache - cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/hastructure:buildcache,mode=max + cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/hastructure:buildcache,mode=max diff --git a/app/Main.hs b/app/Main.hs index 210cfdc0..dd34faab 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -93,7 +93,7 @@ $(deriveJSON defaultOptions ''Version) instance ToSchema Version version1 :: Version -version1 = Version "0.23.3" +version1 = Version "0.23.4" data PoolType = MPool (P.Pool AB.Mortgage) | LPool (P.Pool AB.Loan) diff --git a/src/CreditEnhancement.hs b/src/CreditEnhancement.hs index 5a34d076..fcac25f6 100644 --- a/src/CreditEnhancement.hs +++ b/src/CreditEnhancement.hs @@ -13,7 +13,6 @@ module CreditEnhancement import qualified Data.Text as T import qualified Data.Time as Time import qualified Data.Map as Map -import qualified InterestRate as IR import GHC.Generics import Language.Haskell.TH import Data.Aeson hiding (json) diff --git a/src/Deal.hs b/src/Deal.hs index a162ca99..3aa9616b 100644 --- a/src/Deal.hs +++ b/src/Deal.hs @@ -223,6 +223,7 @@ runEffects t@TestDeal{accounts = accMap, fees = feeMap } d te DoAccrueFee fns -> t {fees = foldr (Map.adjust (calcDueFee t d)) feeMap fns} ChangeReserveBalance accName rAmt -> t {accounts = Map.adjust (A.updateReserveBalance rAmt) accName accMap } + DoNothing -> t _ -> error $ "Failed to match trigger effects: "++show te -- ^ test trigger and add a log if deal status changed diff --git a/src/Deal/DealAction.hs b/src/Deal/DealAction.hs index b32e9e4e..9cd062dc 100644 --- a/src/Deal/DealAction.hs +++ b/src/Deal/DealAction.hs @@ -237,7 +237,7 @@ calcDueInt t calc_date b@(L.Bond bn L.Z bo bi _ bond_bal bond_rate _ _ _ lstIntP calcDueInt t calc_date b@(L.Bond bn L.Equity bo (L.InterestByYield y) _ bond_bal _ _ int_due _ lstIntPay _ mStmt) = b {L.bndDueInt = newDue } -- `debug` ("Yield Due Int >>"++ show bn++">> new due"++ show newDue++">> old due"++ show int_due ) where - newDue = L.backoutDueIntByYield calc_date b + newDue = L.backoutDueIntByYield calc_date b calcDueInt t calc_date b@(L.Bond bn bt bo bi _ bond_bal bond_rate _ int_due (Just int_due_date) lstIntPay _ _ ) | calc_date == int_due_date = b @@ -355,18 +355,24 @@ updateOriginDate2 d (ACM.MO m) = ACM.MO $ updateOriginDate m (P.calcAlignDate m updateOriginDate2 d (ACM.IL m) = ACM.IL $ updateOriginDate m (P.calcAlignDate m d) updateOriginDate2 d (ACM.LS m) = ACM.LS $ updateOriginDate m (P.calcAlignDate m d) -evalExtraSupportBalance :: Date -> TestDeal a -> W.ExtraSupport -> [Balance] +-- ^ get available supports in balance +evalExtraSupportBalance :: P.Asset a => Date -> TestDeal a -> W.ExtraSupport -> [Balance] +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 0 (CE.liqCredit (liqMap Map.! liqName))] evalExtraSupportBalance d t (W.MultiSupport supports) = concat $ evalExtraSupportBalance d t <$> supports + +-- ^ draw support from a deal , return updated deal,and remaining oustanding amount drawExtraSupport :: Date -> Amount -> W.ExtraSupport -> TestDeal a -> (TestDeal a,Amount) drawExtraSupport d amt (W.SupportAccount an (Just (W.ByAccountDraw ln))) t@TestDeal{accounts=accMap, ledgers= Just ledgerMap} = let drawAmt = min (A.accBalance (accMap Map.! an)) amt oustandingAmt = amt - drawAmt in - (t {accounts = Map.adjust (A.draw drawAmt d Types.Empty) an accMap + (t {accounts = Map.adjust (A.draw drawAmt d Types.SupportDraw) an accMap ,ledgers = Just $ Map.adjust (LD.entryLog drawAmt d (TxnDirection Debit)) ln ledgerMap} , oustandingAmt) @@ -375,7 +381,7 @@ drawExtraSupport d amt (W.SupportAccount an Nothing) t@TestDeal{accounts=accMap} drawAmt = min (A.accBalance (accMap Map.! an)) amt oustandingAmt = amt - drawAmt in - (t {accounts = Map.adjust (A.draw drawAmt d Types.Empty) an accMap } + (t {accounts = Map.adjust (A.draw drawAmt d Types.SupportDraw) an accMap } , oustandingAmt) drawExtraSupport d amt (W.SupportLiqFacility liqName) t@TestDeal{liqProvider= Just liqMap} @@ -519,12 +525,12 @@ performAction d t@TestDeal{ledgers= Just ledgerM} (W.BookBy (W.PDL ds ledgersLis (zip ledgerNames amtBookedToLedgers) performAction d t@TestDeal{accounts=accMap, ledgers = Just ledgerM} (W.Transfer (Just (ClearLedger ln)) an1 an2 mComment) = - t {accounts = accMapAfterDeposit, ledgers = Just newLedgerM} -- `debug` ("ABCD "++show(d)) + t {accounts = accMapAfterDeposit, ledgers = Just newLedgerM} where sourceAcc = accMap Map.! an1 - targetAcc = accMap Map.! an2 -- `debug` ("Target>>"++an2) - targetAmt = queryDeal t (LedgerBalance [ln]) -- assuming (debit -> positvie) - transferAmt = min (A.accBalance sourceAcc) targetAmt -- `debug` ("Clear PDL"++show d++ show targetAmt) + targetAcc = accMap Map.! an2 + targetAmt = queryDeal t (LedgerBalance [ln]) + transferAmt = min (A.accBalance sourceAcc) targetAmt accMapAfterDraw = Map.adjust (A.draw transferAmt d (TransferBy an1 an2 (ClearLedger ln))) an1 accMap -- `debug` (">>PDL >>Ledger bal"++show d ++ show targetAmt) accMapAfterDeposit = Map.adjust (A.deposit transferAmt d (TransferBy an1 an2 (ClearLedger ln))) an2 accMapAfterDraw @@ -550,64 +556,127 @@ performAction d t@TestDeal{accounts=accMap} (W.Transfer (Just limit) an1 an2 mCo accMapAfterDraw = Map.adjust (A.draw transferAmt d (TransferBy an1 an2 limit)) an1 accMap accMapAfterDeposit = Map.adjust (A.deposit transferAmt d (TransferBy an1 an2 limit)) an2 accMapAfterDraw +performAction d t@TestDeal{fees=feeMap, accounts=accMap} (W.PayFeeBySeq mLimit an fns mSupport) = + let + availAccBal = A.accBalance (accMap Map.! an) + supportAvail = case mSupport of + Just support -> sum ( evalExtraSupportBalance d t support) + Nothing -> 0 + amtAvailable = case mLimit of + Nothing -> availAccBal + supportAvail + Just (DS ds) -> min (availAccBal + supportAvail) $ queryDeal t (patchDateToStats d ds) + Just (DueCapAmt amt) -> min amt $ availAccBal + supportAvail + feesToPay = map (feeMap Map.!) fns + feeDueAmts = map F.feeDue feesToPay + actualPaidOut = min amtAvailable $ sum feeDueAmts + + feesAmountToBePaid = zip feesToPay $ paySeqLiabilitiesAmt actualPaidOut feeDueAmts + + feesPaid = map (\(f,amt) -> F.payFee d amt f) feesAmountToBePaid + -- update primary account map + accPaidOut = min actualPaidOut availAccBal + dealAfterAcc = t {accounts = Map.adjust (A.draw accPaidOut d (SeqPayFee fns)) an accMap + ,fees = Map.fromList (zip fns feesPaid) <> feeMap} + + supportPaidOut = actualPaidOut - accPaidOut + in + case mSupport of + Just support -> fst $ drawExtraSupport d supportPaidOut support dealAfterAcc + Nothing -> dealAfterAcc -performAction d t@TestDeal{fees=feeMap, accounts =accMap} (W.PayFee mLimit an fns mSupport) = +performAction d t@TestDeal{fees=feeMap, accounts=accMap} (W.PayFee mLimit an fns mSupport) = case mSupport of Just support -> fst $ drawExtraSupport d supportPaidOut support dealAfterAcc Nothing -> dealAfterAcc where - feesToPay = map (feeMap Map.!) fns - feeDueAmts = case mLimit of - Nothing -> map F.feeDue feesToPay - Just (DuePct pct) -> map (\x -> mulBR (F.feeDue x) pct ) feesToPay - Just (DueCapAmt amt) -> prorataFactors (F.feeDue <$> feesToPay) amt - availAccBal = A.accBalance (accMap Map.! an) - availBal = case mSupport of - Nothing -> availAccBal - Just support -> availAccBal + sum (evalExtraSupportBalance d t support) + supportAvail = case mSupport of + Just support -> sum ( evalExtraSupportBalance d t support) + Nothing -> 0 + amtAvailable = case mLimit of + Nothing -> availAccBal + supportAvail + Just (DS ds) -> min (availAccBal + supportAvail) $ queryDeal t (patchDateToStats d ds) + Just (DueCapAmt amt) -> min amt $ availAccBal + supportAvail + feesToPay = map (feeMap Map.!) fns + feeDueAmts = map F.feeDue feesToPay + -- Just (DuePct pct) -> map (\x -> mulBR (F.feeDue x) pct ) feesToPay + -- Just (DueCapAmt amt) -> prorataFactors (F.feeDue <$> feesToPay) amt -- total actual pay out - actualPaidOut = min availBal $ sum feeDueAmts -- `debug` ("Fee Due Amounts"++show(feeDueAmts)) + actualPaidOut = min amtAvailable $ sum feeDueAmts -- `debug` ("Fee Due Amounts"++show(feeDueAmts)) feesAmountToBePaid = zip feesToPay $ prorataFactors feeDueAmts actualPaidOut feesPaid = map (\(f,amt) -> F.payFee d amt f) feesAmountToBePaid - -- fee map updated - feeMapUpdated = Map.union (Map.fromList $ zip fns feesPaid) feeMap -- update primary account map accPaidOut = min actualPaidOut availAccBal dealAfterAcc = t {accounts = Map.adjust (A.draw accPaidOut d (SeqPayFee fns)) an accMap - ,fees = feeMapUpdated} + ,fees = Map.fromList (zip fns feesPaid) <> feeMap} + + supportPaidOut = sum feeDueAmts - accPaidOut + +performAction d t (W.AccrueAndPayIntBySeq mLimit an bnds mSupport) + = let + dealWithBondDue = performAction d t (W.CalcBondInt bnds) + in + performAction d dealWithBondDue (W.PayIntBySeq mLimit an bnds mSupport) + +performAction d t@TestDeal{bonds=bndMap, accounts=accMap, liqProvider=liqMap} (W.PayIntBySeq mLimit an bnds mSupport) + = let + accBal = A.accBalance $ accMap Map.! an + supportAvail = case mSupport of + Just support -> sum ( evalExtraSupportBalance d t support) + Nothing -> 0 + amtAvailable = case mLimit of + Nothing -> accBal + supportAvail + Just (DS ds) -> min (accBal + supportAvail) $ queryDeal t (patchDateToStats d ds) + 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.bndDueInt <$> bndsList + actualPaids = paySeqLiabilitiesAmt amtAvailable dueAmts + -- update bond paid + bondsPaid = uncurry (L.payInt d) <$> zip actualPaids bndsList + -- update account + accPay = min (sum actualPaids) accBal + -- update liq Provider + supportPay = sum actualPaids - accPay + dAfterSupport = case mSupport of + Nothing -> t + Just s -> fst $ drawExtraSupport d supportPay s t + in + dAfterSupport { bonds = Map.fromList (zip bnds bondsPaid) <> bndMap + , accounts = Map.adjust (A.draw accPay d (PayInt bnds)) an accMap } - supportPaidOut = actualPaidOut - accPaidOut performAction d t@TestDeal{bonds=bndMap,accounts=accMap} (W.PayInt mLimit an bnds mSupport) = case mSupport of Just support -> fst $ drawExtraSupport d supportPaidOut support dealAfterAcc Nothing -> dealAfterAcc where - acc = accMap Map.! an - availAccBal = case mLimit of - Nothing -> A.accBalance acc - Just (DS ds) -> min (A.accBalance acc) $ queryDeal t (patchDateToStats d ds) - bndsToPay = map (bndMap Map.!) bnds + accBal = A.accBalance $ accMap Map.! an + supportAvail = case mSupport of + Just support -> sum ( evalExtraSupportBalance d t support) + Nothing -> 0 + + availBal = case mLimit of + Nothing -> accBal + supportAvail + Just (DS ds) -> min (accBal + supportAvail) $ queryDeal t (patchDateToStats d ds) + Just (DueCapAmt amt) -> min (accBal + supportAvail) amt + _ -> error ("Not support limit type for pay int" <> show mLimit) + bndsToPay = map (bndMap Map.!) bnds bndsDueAmts = map L.bndDueInt bndsToPay bndsNames = map L.bndName bndsToPay - availBal = case mSupport of - Nothing -> availAccBal - Just support -> availAccBal + sum ( evalExtraSupportBalance d t support) actualPaidOut = min availBal $ sum bndsDueAmts -- `debug` ("due mats"++ show bndsDueAmts ++">>"++ show availBal) - bndsAmountToBePaid = zip bndsToPay $ prorataFactors bndsDueAmts availBal -- `debug` ("prorata"++ show (prorataFactors bndsDueAmts availBal) ) + bndsAmountToBePaid = zip bndsToPay $ prorataFactors bndsDueAmts actualPaidOut -- bond map updated bndsPaid = map (\(l,amt) -> L.payInt d amt l) bndsAmountToBePaid - bndMapUpdated = Map.union (Map.fromList $ zip bndsNames bndsPaid) bndMap -- primary account paid out - accPaidOut = min actualPaidOut availAccBal + accPaidOut = min actualPaidOut accBal dealAfterAcc = t {accounts = Map.adjust (A.draw actualPaidOut d (PayInt bnds)) an accMap - ,bonds = bndMapUpdated} + ,bonds = Map.fromList (zip bndsNames bndsPaid) <> bndMap } supportPaidOut = actualPaidOut - accPaidOut @@ -624,16 +693,14 @@ performAction d t (W.CalcAndPayFee mLimit ans fees mSupport) = performAction d dealWithFeeDue (W.PayFee mLimit ans fees mSupport) performAction d t@TestDeal{bonds=bndMap,accounts=accMap} (W.PayIntResidual mLimit an bndName) = - t {accounts = accMapAfterPay, bonds = bndMapAfterPay} + t {accounts = Map.adjust (A.draw limitAmt d (PayYield bndName)) an accMap + , bonds = Map.adjust (L.payYield d limitAmt) bndName bndMap} where availBal = A.accBalance $ accMap Map.! an limitAmt = case mLimit of Nothing -> availBal - Just (DS ds) -> queryDeal t ds - accMapAfterPay = Map.adjust (A.draw limitAmt d (PayYield bndName)) an accMap - bndMapAfterPay = Map.adjust (L.payYield d limitAmt) bndName bndMap - - + Just (DS ds) -> min availBal $ queryDeal t (patchDateToStats d ds) + Just (DueCapAmt amt) -> min availBal amt performAction d t@TestDeal{fees=feeMap,accounts=accMap} (W.PayFeeResidual mlimit an feeName) = t {accounts = accMapAfterPay, fees = feeMapAfterPay} @@ -646,60 +713,49 @@ performAction d t@TestDeal{fees=feeMap,accounts=accMap} (W.PayFeeResidual mlimit accMapAfterPay = Map.adjust (A.draw paidOutAmt d (PayFeeYield feeName)) an accMap feeMapAfterPay = Map.adjust (F.payResidualFee d paidOutAmt) feeName feeMap --- ^ pay bond till its balance as pct of total balance ---performAction d t@TestDeal{bonds=bndMap,accounts=accMap} (W.PayPrin (Just (RemainBalPct pct)) an [bndName])= --Need to replace with formula --- t {accounts = accMapAfterPay, bonds = bndMapAfterPay} --- where --- availBal = A.accBalance $ accMap Map.! an --- targetBnd = bndMap Map.! bndName --- targetBndBal = L.bndBalance targetBnd --- --- otherBndBal = queryDeal t CurrentBondBalance - targetBndBal --- --- _pct = fromRational pct --- dueAmount = (1/(1-_pct)) * (targetBndBal * (1-_pct) - (_pct * otherBndBal)) --- actAmount = min availBal $ max dueAmount 0 --- --- accMapAfterPay = Map.adjust --- (A.draw actAmount d (PayPrin [bndName])) --- an --- accMap --- bndMapAfterPay = Map.adjust (L.payPrin d actAmount) bndName bndMap performAction d t@TestDeal{bonds=bndMap,accounts=accMap} (W.PayPrinBySeq mLimit an bnds mSupport)= - t {accounts = accMapAfterPay, bonds =bndsUpdated} + dAfterSupport { bonds = Map.fromList (zip bndsToPayNames bondsPaid) <> bndMap + , accounts = Map.adjust (A.draw accPay d (PayPrin bnds)) an accMap } where - availBal = A.accBalance $ accMap Map.! an - bndsToPay = filter (not . L.isPaidOff) $ map (bndMap Map.!) bnds -- list of bond obj available + accBal = A.accBalance $ accMap Map.! an + supportAvail = case mSupport of + Just support -> sum ( evalExtraSupportBalance d t support) + Nothing -> 0 + amtAvailable = case mLimit of + Nothing -> accBal + supportAvail + Just (DS ds) -> min (accBal + supportAvail) $ queryDeal t (patchDateToStats d ds) + Just (DueCapAmt amt) -> min amt (accBal + supportAvail) + _ -> error $ "Not support for limit when pay prin by seq" ++ show mLimit + bndsList = (Map.!) bndMap <$> bnds + + bndsToPay = filter (not . L.isPaidOff) bndsList bndsToPayNames = L.bndName <$> bndsToPay - bndsWithDue = map (calcDuePrin t d) bndsToPay -- - bndsDueAmts = map L.bndDuePrin bndsWithDue - limitCap = case mLimit of - Nothing -> availBal - Just (DS ds) -> queryDeal t $ patchDateToStats d ds - _ -> error "not implement in payPrinBySeq " + bndsWithDue = calcDuePrin t d <$> bndsToPay + bndsDueAmts = L.bndDuePrin <$> bndsWithDue - payAmount = min (sum bndsDueAmts) $ min availBal $ limitCap - - bndsAmountToBePaid = zip bndsToPay $ paySeqLiabilitiesAmt payAmount bndsDueAmts - - bndsPaid = map (\(b,amt) -> L.payPrin d amt b) bndsAmountToBePaid - bndsUpdated = Map.union (Map.fromList $ zip bndsToPayNames bndsPaid) bndMap - - accMapAfterPay = Map.adjust - (A.draw payAmount d (TxnComments [PayPrin bndsToPayNames])) - an - accMap - -performAction d t@TestDeal{bonds=bndMap,accounts=accMap} (W.PayPrin (Just (DS ds)) an bnds Nothing)= --Need to replace with formula - t {accounts = accMapAfterPay, bonds =bndsUpdated} + payAmount = min (sum bndsDueAmts) amtAvailable + + actualPaids = paySeqLiabilitiesAmt payAmount bndsDueAmts + -- update bond paid + bondsPaid = uncurry (L.payPrin d) <$> zip actualPaids bndsToPay + -- update account + accPay = min (sum actualPaids) accBal + -- update liq Provider + supportPay = sum actualPaids - accPay + dAfterSupport = case mSupport of + Nothing -> t + Just s -> fst $ drawExtraSupport d supportPay s t + + +performAction d t@TestDeal{bonds=bndMap,accounts=accMap} (W.PayPrin (Just (DS ds)) an bnds Nothing) = + t {accounts = accMapAfterPay, bonds = bndsUpdated} where availBal = A.accBalance $ accMap Map.! an bndsToPay = filter (not . L.isPaidOff) $ map (bndMap Map.!) bnds bndsToPayNames = L.bndName <$> bndsToPay - bndsWithDue = map (calcDuePrin t d) bndsToPay -- - bndsDueAmts = map L.bndDuePrin bndsWithDue + bndsDueAmts = L.bndDuePrin . calcDuePrin t d <$> bndsToPay payAmount = min (sum bndsDueAmts) $ min availBal $ queryDeal t $ patchDateToStats d ds -- `debug` ("Query with "++show (patchedDs)) bndsAmountToBePaid = zip bndsToPay $ prorataFactors bndsDueAmts payAmount -- (bond, amt-allocated) @@ -788,7 +844,6 @@ performAction d t@TestDeal{fees=feeMap,liqProvider = Just _liqProvider} (W.LiqSu transferAmt = case CE.liqCredit $ _liqProvider Map.! pName of Nothing -> _transferAmt Just _availBal -> min _transferAmt _availBal - -- transferAmt = min _transferAmt $ CE.liqBalance $ _liqProvider Map.! pName newFeeMap = Map.adjust (F.payFee d transferAmt) fn feeMap newLiqMap = Map.adjust (CE.draw transferAmt d ) pName _liqProvider diff --git a/src/Liability.hs b/src/Liability.hs index 49675836..0231d8ef 100644 --- a/src/Liability.hs +++ b/src/Liability.hs @@ -123,10 +123,10 @@ data Bond = Bond { } deriving (Show, Eq, Generic) consolStmt :: Bond -> Bond +consolStmt b@Bond{bndName = bn, bndStmt = Nothing} = b consolStmt b@Bond{bndName = bn, bndStmt = Just (S.Statement (txn:txns))} = b {bndStmt = Just (S.Statement (reverse (foldl S.consolTxn [txn] txns)))} -consolStmt b@Bond{bndName = bn, bndStmt = Nothing} = b payInt :: Date -> Amount -> Bond -> Bond payInt d 0 bnd@(Bond bn bt oi iinfo _ 0 r 0 0 dueIntDate lpayInt lpayPrin stmt) = bnd @@ -239,14 +239,15 @@ calcBondYield d cost b@(Bond _ _ _ _ _ _ _ _ _ _ _ _ (Just (S.Statement txns))) where cashflows = [ TsPoint (S.getDate txn) (S.getTxnAmt txn) | txn <- txns ] +-- ^ backout interest due for a Yield Maintainace type bond backoutDueIntByYield :: Date -> Bond -> Balance backoutDueIntByYield d b@(Bond _ _ (OriginalInfo obal odate _ _) (InterestByYield y) _ currentBalance _ _ _ _ _ _ stmt) - = proj_fv - fvs - currentBalance -- `debug` ("Date"++ show d ++"FV->"++show proj_fv++">>"++show fvs++">>cb"++show currentBalance) + = projFv - fvs - currentBalance -- `debug` ("Date"++ show d ++"FV->"++show projFv++">>"++show fvs++">>cb"++show currentBalance) where - proj_fv = fv2 y odate d obal - fvs = sum $ [ fv2 y d (fst cf) (snd cf) | cf <- cashflows ] + projFv = fv2 y odate d obal + fvs = sum $ [ fv2 y cfDate d cfAmt | (cfDate,cfAmt) <- cashflows ] -- `debug` (show d ++ ":CFS"++ show cashflows) cashflows = case stmt of - Just (S.Statement txns) -> [ ((S.getDate txn),(S.getTxnAmt txn)) | txn <- txns ] + Just (S.Statement txns) -> [ ((S.getDate txn),(S.getTxnAmt txn)) | txn <- txns ] -- `debug` (show d ++":TXNS"++ show txns) Nothing -> [] diff --git a/src/Stmt.hs b/src/Stmt.hs index 747170fd..092e7c69 100644 --- a/src/Stmt.hs +++ b/src/Stmt.hs @@ -2,6 +2,7 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE InstanceSigs #-} module Stmt (Statement(..),Txn(..) @@ -179,6 +180,7 @@ getFlow comment = SwapInSettle -> Inflow SwapOutSettle -> Outflow PurchaseAsset -> Outflow + SupportDraw -> Noneflow TxnComments cmts -> let directionList = getFlow <$> cmts @@ -193,12 +195,12 @@ getFlow comment = _ -> error ("Missing in GetFlow >> "++ show comment) instance Ord Txn where + compare :: Txn -> Txn -> Ordering compare (BondTxn d1 _ _ _ _ _ _ ) (BondTxn d2 _ _ _ _ _ _ ) = compare d1 d2 compare (AccTxn d1 _ _ _ ) (AccTxn d2 _ _ _ ) = compare d1 d2 instance Eq Txn where - (BondTxn d1 _ _ _ _ _ _ ) == (BondTxn d2 _ _ _ _ _ _ ) - = d1 == d2 + (BondTxn d1 _ _ _ _ _ _ ) == (BondTxn d2 _ _ _ _ _ _ ) = d1 == d2 instance TimeSeries Txn where getDate (BondTxn t _ _ _ _ _ _ ) = t diff --git a/src/Triggers.hs b/src/Triggers.hs index ccbd735e..51822bc2 100644 --- a/src/Triggers.hs +++ b/src/Triggers.hs @@ -33,6 +33,7 @@ data TriggerEffect = DealStatusTo DealStatus -- ^ change deal | IssueBonds [L.Bond] AccountName -- ^ issue new bonds and deposit proceeds to account | BuyAsset AccountName PricingMethod -- ^ buy asset from the assumption using funds from account | TriggerEffects [TriggerEffect] -- ^ a combination of effects above + | DoNothing -- ^ do nothing deriving (Show, Eq, Generic) data Trigger = Trigger { diff --git a/src/Types.hs b/src/Types.hs index 6ccae322..25d844d8 100644 --- a/src/Types.hs +++ b/src/Types.hs @@ -308,6 +308,7 @@ data TxnComment = PayInt [BondName] | LiquidationRepay | LiquidationSupportInt Balance Balance | BankInt + | SupportDraw | Empty | Tag String | UsingDS DealStats @@ -344,6 +345,7 @@ instance ToJSON TxnComment where toJSON SwapOutSettle = String $ T.pack $ "" toJSON PurchaseAsset = String $ T.pack $ "" toJSON (TxnDirection dr) = String $ T.pack $ "" + toJSON SupportDraw = String $ T.pack $ "" instance FromJSON TxnComment where parseJSON = withText "Empty" parseTxn diff --git a/src/Waterfall.hs b/src/Waterfall.hs index d9c683b8..37bd4b46 100644 --- a/src/Waterfall.hs +++ b/src/Waterfall.hs @@ -59,25 +59,30 @@ data BookType = PDL DealStats [(LedgerName,DealStats)] -- Reverse PDL Debit refe data ExtraSupport = SupportAccount AccountName (Maybe BookType) -- ^ if there is deficit, draw another account to pay the shortfall | SupportLiqFacility LiquidityProviderName -- ^ if there is deficit, draw facility's available credit to pay the shortfall | MultiSupport [ExtraSupport] -- ^ if there is deficit, draw multiple supports (by sequence in the list) to pay the shortfall + | WithCondition Pre ExtraSupport -- ^ support only available if Pre is true deriving (Show,Generic) data Action = Transfer (Maybe Limit) AccountName AccountName (Maybe TxnComment) -- Fee | CalcFee [FeeName] -- ^ calculate fee due amount in the fee names | PayFee (Maybe Limit) AccountName [FeeName] (Maybe ExtraSupport) -- ^ pay fee with cash from account with optional limit or extra support + | PayFeeBySeq (Maybe Limit) AccountName [FeeName] (Maybe ExtraSupport) -- ^ pay fee with cash from account with optional limit or extra support | CalcAndPayFee (Maybe Limit) AccountName [FeeName] (Maybe ExtraSupport) -- ^ combination of CalcFee and PayFee | PayFeeResidual (Maybe Limit) AccountName FeeName -- ^ pay fee regardless fee due amount -- Bond - Interest | CalcBondInt [BondName] -- ^ calculate interest due amount in the bond names | PayInt (Maybe Limit) AccountName [BondName] (Maybe ExtraSupport) -- ^ pay interest with cash from the account with optional limit or extra support + | PayIntBySeq (Maybe Limit) AccountName [BondName] (Maybe ExtraSupport) -- ^ with sequence | AccrueAndPayInt (Maybe Limit) AccountName [BondName] (Maybe ExtraSupport) -- ^ combination of CalcInt and PayInt + | AccrueAndPayIntBySeq (Maybe Limit) AccountName [BondName] (Maybe ExtraSupport) -- ^ with sequence | PayIntResidual (Maybe Limit) AccountName BondName -- ^ pay interest to bond regardless interest due -- | PayTillYield AccountName [BondName] -- Bond - Principal | PayPrin (Maybe Limit) AccountName [BondName] (Maybe ExtraSupport) -- ^ pay principal to bond via pro-rata | PayPrinBySeq (Maybe Limit) AccountName [BondName] (Maybe ExtraSupport) -- ^ pay principal to bond via sequence | PayPrinResidual AccountName [BondName] -- ^ pay principal regardless predefined balance schedule - -- | PayPrinBy Limit AccountName BondName + | PayIntPrinBySeq (Maybe Limit) AccountName [BondName] (Maybe ExtraSupport) -- ^ pay int & prin to bonds sequentially + | AccrueAndPayIntPrinBySeq (Maybe Limit) AccountName [BondName] (Maybe ExtraSupport) -- Pool/Asset change | BuyAsset (Maybe Limit) PricingMethod AccountName -- ^ buy asset from revolving assumptions using funds from account | LiquidatePool PricingMethod AccountName -- ^ sell all assets and deposit proceeds to account diff --git a/swagger.json b/swagger.json index 4b2c44a5..24f7e402 100644 --- a/swagger.json +++ b/swagger.json @@ -1895,6 +1895,43 @@ "contents" ] }, + { + "type": "object", + "properties": { + "contents": { + "items": [ + { + "$ref": "#/components/schemas/Limit" + }, + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "$ref": "#/components/schemas/ExtraSupport" + } + ], + "maxItems": 4, + "type": "array", + "minItems": 4 + }, + "tag": { + "type": "string", + "enum": [ + "PayFeeBySeq" + ] + } + }, + "required": [ + "tag", + "contents" + ] + }, { "type": "object", "properties": { @@ -2021,6 +2058,43 @@ "contents" ] }, + { + "type": "object", + "properties": { + "contents": { + "items": [ + { + "$ref": "#/components/schemas/Limit" + }, + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "$ref": "#/components/schemas/ExtraSupport" + } + ], + "maxItems": 4, + "type": "array", + "minItems": 4 + }, + "tag": { + "type": "string", + "enum": [ + "PayIntBySeq" + ] + } + }, + "required": [ + "tag", + "contents" + ] + }, { "type": "object", "properties": { @@ -2058,6 +2132,43 @@ "contents" ] }, + { + "type": "object", + "properties": { + "contents": { + "items": [ + { + "$ref": "#/components/schemas/Limit" + }, + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "$ref": "#/components/schemas/ExtraSupport" + } + ], + "maxItems": 4, + "type": "array", + "minItems": 4 + }, + "tag": { + "type": "string", + "enum": [ + "AccrueAndPayIntBySeq" + ] + } + }, + "required": [ + "tag", + "contents" + ] + }, { "type": "object", "properties": { @@ -2194,6 +2305,80 @@ "contents" ] }, + { + "type": "object", + "properties": { + "contents": { + "items": [ + { + "$ref": "#/components/schemas/Limit" + }, + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "$ref": "#/components/schemas/ExtraSupport" + } + ], + "maxItems": 4, + "type": "array", + "minItems": 4 + }, + "tag": { + "type": "string", + "enum": [ + "PayIntPrinBySeq" + ] + } + }, + "required": [ + "tag", + "contents" + ] + }, + { + "type": "object", + "properties": { + "contents": { + "items": [ + { + "$ref": "#/components/schemas/Limit" + }, + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "$ref": "#/components/schemas/ExtraSupport" + } + ], + "maxItems": 4, + "type": "array", + "minItems": 4 + }, + "tag": { + "type": "string", + "enum": [ + "AccrueAndPayIntPrinBySeq" + ] + } + }, + "required": [ + "tag", + "contents" + ] + }, { "type": "object", "properties": { @@ -3700,6 +3885,20 @@ "tag" ] }, + { + "type": "object", + "properties": { + "tag": { + "type": "string", + "enum": [ + "SupportDraw" + ] + } + }, + "required": [ + "tag" + ] + }, { "type": "object", "properties": { @@ -4817,6 +5016,20 @@ "tag", "contents" ] + }, + { + "type": "object", + "properties": { + "tag": { + "type": "string", + "enum": [ + "DoNothing" + ] + } + }, + "required": [ + "tag" + ] } ], "type": "object" @@ -9018,6 +9231,34 @@ "tag", "contents" ] + }, + { + "type": "object", + "properties": { + "contents": { + "items": [ + { + "$ref": "#/components/schemas/Pre" + }, + { + "$ref": "#/components/schemas/ExtraSupport" + } + ], + "maxItems": 2, + "type": "array", + "minItems": 2 + }, + "tag": { + "type": "string", + "enum": [ + "WithCondition" + ] + } + }, + "required": [ + "tag", + "contents" + ] } ], "type": "object" @@ -12770,7 +13011,7 @@ } }, "info": { - "version": "0.23.2", + "version": "0.23.3", "title": "Hastructure API", "license": { "name": "BSD 3"