Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.26.1 #154

Merged
merged 20 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
# Changelog for Hastructure


## 0.26.2
### 2024-03-24
* FIX: patch recoveries for `Mortgage` type cashflow
* ENHANCE: create new endpoint for `OAS`
* NEW: add new asset class `Receivable` which represent a `invoice factored`,`trading receivable`
* NEW: `DefaultAtEnd` assumption, which assumes asset default at last payment

## 0.26.1
### 2024-03-09
* NEW: `fundWith` which will increate the balance of bond and deposit cash to account.
* NEW: `writeOff` which will write off balance of bond via a formula
* NEW: a new predicate `passMaturity` which True if bonds has passed their expected maturity/pay off date.
* NEW: `Not` as composite boolean test.
* NEW: add new `OAS` pricing assumption, which return OAS spread given input scenarios.

## 0.26.0
### 2024-02-27
* NEW: add `NO_FirstN` as type of `Mortgage` which implies no payment for first N period and interest due will be capitalized.
Expand Down
3 changes: 2 additions & 1 deletion Hastructure.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack

name: Hastructure
version: 0.26.0
version: 0.26.5
description: Please see the README on GitHub at <https://github.com/yellowbean/Hastructure#readme>
category: StructuredFinance;Securitisation;Cashflow
homepage: https://github.com/yellowbean/Hastructure#readme
Expand Down Expand Up @@ -37,6 +37,7 @@ library
AssetClass.Loan
AssetClass.MixedAsset
AssetClass.Mortgage
AssetClass.Receivable
Assumptions
Call
Cashflow
Expand Down
84 changes: 57 additions & 27 deletions app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ import qualified DateUtil as DU


import Debug.Trace
import qualified Deal.DealBase as DB
import qualified Types as W
debug = flip Debug.Trace.trace

data Version = Version
Expand All @@ -93,13 +93,14 @@ $(deriveJSON defaultOptions ''Version)
instance ToSchema Version

version1 :: Version
version1 = Version "0.26.0"
version1 = Version "0.26.5"

data PoolType = MPool (P.Pool AB.Mortgage)
| LPool (P.Pool AB.Loan)
| IPool (P.Pool AB.Installment)
| RPool (P.Pool AB.Lease)
| FPool (P.Pool AB.FixedAsset)
| VPool (P.Pool AB.Receivable)
deriving(Show, Generic)

instance ToSchema PoolType
Expand All @@ -110,12 +111,14 @@ instance ToSchema (P.Pool AB.Loan)
instance ToSchema (P.Pool AB.Installment)
instance ToSchema (P.Pool AB.Lease)
instance ToSchema (P.Pool AB.FixedAsset)
instance ToSchema (P.Pool AB.Receivable)

data DealType = MDeal (DB.TestDeal AB.Mortgage)
| LDeal (DB.TestDeal AB.Loan)
| 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)
deriving(Show, Generic)

Expand All @@ -139,13 +142,15 @@ instance ToSchema (DB.UnderlyingDeal AB.Mortgage)
instance ToSchema (DB.UnderlyingDeal AB.Loan)
instance ToSchema (DB.UnderlyingDeal AB.Installment)
instance ToSchema (DB.UnderlyingDeal AB.Lease)
instance ToSchema (DB.UnderlyingDeal AB.Receivable)
instance ToSchema (DB.UnderlyingDeal AB.AssetUnion)
instance ToSchema (DB.UnderlyingDeal AB.FixedAsset)

instance ToSchema (DB.TestDeal AB.Mortgage)
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.AssetUnion)
instance ToSchema (DB.TestDeal AB.FixedAsset)
instance ToSchema (DB.PoolType AB.AssetUnion)
Expand All @@ -154,6 +159,7 @@ instance ToSchema (DB.PoolType AB.Mortgage)
instance ToSchema (DB.PoolType AB.Loan)
instance ToSchema (DB.PoolType AB.Installment)
instance ToSchema (DB.PoolType AB.Lease)
instance ToSchema (DB.PoolType AB.Receivable)
instance ToSchema (P.Pool AB.AssetUnion)
instance ToSchema HE.RateCap
instance ToSchema AB.LeaseStepUp
Expand All @@ -176,6 +182,8 @@ instance ToSchema Pre
instance ToSchema W.ActionWhen
instance ToSchema W.ExtraSupport
instance ToSchema W.Action
instance ToSchema BookDirection
instance ToSchema Direction
instance ToSchema W.BookType
instance ToSchema W.CollectionRule
instance ToSchema Limit
Expand Down Expand Up @@ -207,7 +215,7 @@ instance ToSchema Types.CashflowReport
instance ToSchema Types.BookItem
instance ToSchema Stmt.Statement
instance ToSchema Stmt.Txn
instance ToSchema Stmt.Direction
-- instance ToSchema Stmt.Direction
instance ToSchema Stmt.TxnComment
instance ToSchema CF.TsRow
instance ToSchema (TsPoint Balance)
Expand All @@ -223,11 +231,14 @@ instance ToSchema AB.OriginalInfo
instance ToSchema IR.RateType
instance ToSchema AB.AmortPlan
instance ToSchema AB.AssetUnion
instance ToSchema AB.Receivable
instance ToSchema CutoffFields
instance ToSchema PricingMethod
instance ToSchema RV.RevolvingPool
instance ToSchema (TsPoint [AB.AssetUnion])
instance ToSchema (RoundingBy IRate)
instance ToSchema (RoundingBy Rate)
instance ToSchema (RoundingBy Integer)
instance ToSchema (RoundingBy Balance)
instance ToSchema AP.NonPerfAssumption
instance ToSchema AP.RevolvingAssumption
Expand All @@ -244,6 +255,7 @@ instance ToSchema AP.LeaseAssetGapAssump
instance ToSchema AP.LeaseAssetRentAssump
instance ToSchema (Table Balance Balance)
instance ToSchema (Table Float Spread)
instance ToSchema AB.ReceivableFeeType

instance ToSchema (Ratio Integer) where
declareNamedSchema _ = NamedSchema Nothing <$> declareSchema (Proxy :: Proxy Double)
Expand Down Expand Up @@ -278,13 +290,18 @@ wrapRun (UDeal d) mAssump mNonPerfAssump = let
(_d,_pflow,_rs,_p) = D.runDeal d D.DealPoolFlowPricing mAssump mNonPerfAssump
in
(UDeal _d,_pflow,_rs,_p)
wrapRun (VDeal d) mAssump mNonPerfAssump = let
(_d,_pflow,_rs,_p) = D.runDeal d D.DealPoolFlowPricing mAssump mNonPerfAssump
in
(VDeal _d,_pflow,_rs,_p)
wrapRun x _ _ = error $ "RunDeal Failed ,due to unsupport deal type "++ show x

wrapRunPool :: PoolType -> Maybe AP.ApplyAssumptionType -> Maybe [RateAssumption] -> (CF.CashFlowFrame, Map CutoffFields Balance)
wrapRunPool (MPool p) assump mRates = P.aggPool Nothing $ D.runPool p assump mRates
wrapRunPool (LPool p) assump mRates = P.aggPool Nothing $ D.runPool p assump mRates
wrapRunPool (IPool p) assump mRates = P.aggPool Nothing $ D.runPool p assump mRates
wrapRunPool (RPool p) assump mRates = P.aggPool Nothing $ D.runPool p assump mRates
wrapRunPool (VPool p) assump mRates = P.aggPool Nothing $ D.runPool p assump mRates
wrapRunPool x _ _ = error $ "RunPool Failed ,due to unsupport pool type "++ show x

data RunAssetReq = RunAssetReq Date [AB.AssetUnion] AP.ApplyAssumptionType (Maybe [RateAssumption]) (Maybe PricingMethod)
Expand All @@ -310,8 +327,14 @@ type ScenarioName = String
data RunDealReq = SingleRunReq DealType (Maybe AP.ApplyAssumptionType) AP.NonPerfAssumption
| MultiScenarioRunReq DealType (Map.Map ScenarioName AP.ApplyAssumptionType) AP.NonPerfAssumption
| MultiDealRunReq (Map.Map ScenarioName DealType) (Maybe AP.ApplyAssumptionType) AP.NonPerfAssumption
| MultiScenarioAndCurveRunReq DealType (Map.Map ScenarioName AP.ApplyAssumptionType) AP.NonPerfAssumption
deriving(Show, Generic)

data RunSimDealReq = OASReq DealType (Map.Map ScenarioName AP.ApplyAssumptionType) AP.NonPerfAssumption
deriving(Show, Generic)



instance ToSchema RunDealReq
data RunPoolReq = SingleRunPoolReq PoolType (Maybe AP.ApplyAssumptionType) (Maybe [RateAssumption])
| MultiScenarioRunPoolReq PoolType (Map.Map ScenarioName AP.ApplyAssumptionType) (Maybe [RateAssumption])
Expand All @@ -334,6 +357,7 @@ type EngineAPI = "version" :> Get '[JSON] Version
:<|> "runPoolByScenarios" :> ReqBody '[JSON] RunPoolReq :> Post '[JSON] (Map.Map ScenarioName (CF.CashFlowFrame,Map.Map CutoffFields Balance))
:<|> "runDeal" :> ReqBody '[JSON] RunDealReq :> Post '[JSON] RunResp
:<|> "runDealByScenarios" :> ReqBody '[JSON] RunDealReq :> Post '[JSON] (Map.Map ScenarioName RunResp)
-- :<|> "runDealOAS" :> ReqBody '[JSON] RunDealReq :> Post '[JSON] (Map.Map ScenarioName [PriceResult])
:<|> "runMultiDeals" :> ReqBody '[JSON] RunDealReq :> Post '[JSON] (Map.Map ScenarioName RunResp)
:<|> "runDate" :> ReqBody '[JSON] RunDateReq :> Post '[JSON] [Date]

Expand All @@ -353,6 +377,8 @@ engineSwagger = toOpenApi engineAPI
& info.description ?~ "Hastructure is a white-label friendly Cashflow & Analytics Engine for MBS/ABS and REITs"
& info.license ?~ "BSD 3"



myServer :: Server API
myServer = return engineSwagger
:<|> showVersion
Expand All @@ -361,6 +387,7 @@ myServer = return engineSwagger
:<|> runPoolScenarios
:<|> runDeal
:<|> runDealScenarios
-- :<|> runDealOAS
:<|> runMultiDeals
:<|> runDate
-- :<|> error "not implemented"
Expand All @@ -370,6 +397,32 @@ myServer = return engineSwagger
runPool (SingleRunPoolReq pt passumption mRates) = return $ wrapRunPool pt passumption mRates
runPoolScenarios (MultiScenarioRunPoolReq pt mAssumps mRates) = return $ Map.map (\assump -> wrapRunPool pt (Just assump) mRates) mAssumps
runDeal (SingleRunReq dt assump nonPerfAssump) = return $ wrapRun dt assump nonPerfAssump
-- runDealScenarios (MultiScenarioRunReq dt mAssumps [email protected]{AP.pricing=Just bondPricingInput})
-- = case bondPricingInput of
-- AP.OASInput d bName price spreads curves ->
-- let
-- priceCurves = Map.map (\curve ->
-- (\spd -> U.shiftTsByAmt curve (toRational spd)) <$> spreads) curves -- curve as key , shifted curve list as value
-- pricingScenarios = Map.map (\curves -> Just . AP.DiscountCurve d <$> curves) priceCurves -- curve as key, list of discount curves as value
-- nonPerfAssumpEachScenario = Map.map (\discountCurves ->
-- (\pvCurve -> nonPerfAssump { AP.pricing = pvCurve}) <$> discountCurves) pricingScenarios -- curve as key, list of nonperfassump as value
-- runResultEachScenario = Map.intersectionWith
-- (\pAssump dAssumps ->
-- let
-- runResults::[RunResp] = wrapRun dt (Just pAssump) <$> dAssumps
-- mPricingResults::([Maybe (Map String PriceResult)]) = (\(_a,_b,_c,_d) -> _d) <$> runResults
-- -- bondPrices = (maybe 0 (\y -> getPriceValue . (Map.! y bName))) <$> mPricingResults
-- in
-- [] -- bondPrices
-- )
-- mAssumps
-- nonPerfAssumpEachScenario --- curve as key, list of bond prices as value
-- -- avgPricesPerSpreads = (\xs -> sum xs / length xs) <$> transpose $ Map.elems runResultEachScenario -- average PV list
-- -- spreadPriceTable = ThresholdTable $ zip avgPricesPerSpreads spreads

-- in
-- return $ Map.map (\singleAssump -> wrapRun dt (Just singleAssump) nonPerfAssump) mAssumps
-- _ -> return $ Map.map (\singleAssump -> wrapRun dt (Just singleAssump) nonPerfAssump) mAssumps
runDealScenarios (MultiScenarioRunReq dt mAssumps nonPerfAssump)
= return $ Map.map (\singleAssump -> wrapRun dt (Just singleAssump) nonPerfAssump) mAssumps
runMultiDeals (MultiDealRunReq mDts assump nonPerfAssump)
Expand All @@ -386,29 +439,6 @@ data Config = Config { port :: Int}

instance FromJSON Config

-- data Ctyp a
--
-- {-
-- if you are using GHC 8.6 and above you can make use of deriving Via
-- for creating the Accept Instance
--
-- >> data Ctyp a
-- >> deriving Accept via JSON
-- -}
--
-- instance Accept (Ctyp JSON) where
-- contentType _ = contentType (Proxy @JSON)
--
-- instance HasErrorBody (Ctyp JSON) '[] where
-- encodeError = undefined -- write your custom implementation
--
-- -- | Example Middleware with a different 'HasErrorBody' instance for JSON
-- errorMwJson :: Application -> Application
-- errorMwJson = errorMw @(Ctyp JSON) @'[]

--instance HasErrorBody (Ctyp JSON) '["error","warning","resp"] where
-- encodeError = error

main :: IO ()
main =
do
Expand All @@ -418,8 +448,8 @@ main =
let (Config _p) = case mc of
Left exp -> Config 8081
Right c -> c
print ("Engine start with version:"++ _version version1++";running at Port:"++ show _p)
run _p
-- $ errorMwJson @JSON @'["error","warning","status"]
$ errorMwDefJson
$ serve (Proxy :: Proxy API) myServer

Expand Down
2 changes: 1 addition & 1 deletion package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Hastructure
version: 0.26.0
version: 0.26.5
github: "yellowbean/Hastructure"
license: BSD3
author: "Xiaoyu"
Expand Down
4 changes: 2 additions & 2 deletions src/Asset.hs
Original file line number Diff line number Diff line change
Expand Up @@ -333,10 +333,10 @@ aggPool mStat xs
,Map.findWithDefault 0 HistoryRecoveries m
,Map.findWithDefault 0 HistoryLoss m)
-- (CumPrincipal,CumPrepay,CumDelinq,CumDefault,CumRecovery,CumLoss)
txns = CF.patchCumulative cumulativeStatAtCutoff [] _txns
txns = CF.patchCumulative cumulativeStatAtCutoff [] _txns
in
case Map.lookup AccruedInterest =<< mStat of
Nothing -> (CF.CashFlowFrame txns, stats)
Nothing -> (CF.CashFlowFrame txns, stats)
Just accruedIntAmt -> (CF.CashFlowFrame (CF.clawbackInt accruedIntAmt txns), stats)

$(deriveJSON defaultOptions ''Pool)
30 changes: 29 additions & 1 deletion src/AssetClass/AssetBase.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
(Installment(..),Lease(..),OriginalInfo(..),Status(..)
,LeaseStepUp(..),AccrualPeriod(..),PrepayPenaltyType(..)
,AmortPlan(..),Loan(..),Mortgage(..),AssetUnion(..),MixedAsset(..),FixedAsset(..)
,AmortRule(..),Capacity(..),AssociateExp(..),AssociateIncome(..)
,AmortRule(..),Capacity(..),AssociateExp(..),AssociateIncome(..),ReceivableFeeType(..),Receivable(..)
,calcAssetPrinInt, calcPmt
)
where
Expand Down Expand Up @@ -49,6 +49,7 @@

type InterestAmount = Amount
type PrincipalAmount = Amount

calcAssetPrinInt :: AmortPlan -> Balance -> IRate -> Int -> Int -> (InterestAmount, PrincipalAmount)
calcAssetPrinInt pt bal rate ot rt =
let
Expand Down Expand Up @@ -97,6 +98,14 @@
| SumYearsDigit -- ^ Not implemented
deriving (Show,Generic,Eq,Ord)

data ReceivableFeeType = FixedFee Balance -- ^ a flat fee amount
| FixedRateFee Rate -- ^ a percentage fee against balance for once
| FactorFee Rate Int Direction -- ^ a percentage fee against balance for each period (N days)
| AdvanceFee Rate -- ^ annualized rate for discount fee based on advance amount
| CompoundFee [ReceivableFeeType] -- ^ compound fee
deriving (Show,Generic,Eq,Ord)


data OriginalInfo = MortgageOriginalInfo { originBalance :: Balance
,originRate :: IR.RateType
,originTerm :: Int
Expand All @@ -122,6 +131,12 @@
,accRule :: AmortRule
,capacity :: Capacity
}
| ReceivableInfo { startDate :: Date
,originBalance :: Balance
,originAdvance :: Balance
,dueDate :: Date
,feeType :: Maybe ReceivableFeeType
}
deriving (Show,Generic,Ord,Eq)


Expand Down Expand Up @@ -152,6 +167,11 @@
| ScheduleMortgageFlow Date [CF.TsRow] DatePattern
deriving (Show,Generic,Eq,Ord)

data Receivable = Invoice OriginalInfo Status
| DUMMY4
deriving (Show,Generic,Eq,Ord)


data MixedAsset = MixedPool (Map.Map String [AssetUnion])
| DUMMY2
deriving (Show,Generic,Eq,Ord)
Expand Down Expand Up @@ -180,49 +200,57 @@
| IL Installment
| LS Lease
| FA FixedAsset
| RE Receivable
deriving (Show, Generic,Ord,Eq)

instance IR.UseRate AssetUnion where

Check warning on line 206 in src/AssetClass/AssetBase.hs

View workflow job for this annotation

GitHub Actions / build

• No explicit implementation for

Check warning on line 206 in src/AssetClass/AssetBase.hs

View workflow job for this annotation

GitHub Actions / build

• No explicit implementation for
getIndex (MO ma) = IR.getIndex ma
getIndex (LO ma) = IR.getIndex ma
getIndex (IL ma) = IR.getIndex ma
getIndex (LS ma) = IR.getIndex ma
getIndex (FA ma) = IR.getIndex ma
getIndex (RE ma) = IR.getIndex ma


instance IR.UseRate Mortgage where

Check warning on line 215 in src/AssetClass/AssetBase.hs

View workflow job for this annotation

GitHub Actions / build

• No explicit implementation for

Check warning on line 215 in src/AssetClass/AssetBase.hs

View workflow job for this annotation

GitHub Actions / build

• No explicit implementation for
getIndex (Mortgage oi@MortgageOriginalInfo{ originRate = IR.Floater _ idx _ _ _ _ _ _ } _ _ _ _ _) = Just idx
getIndex Mortgage {} = Nothing
getIndex (AdjustRateMortgage oi@MortgageOriginalInfo{ originRate = IR.Floater _ idx _ _ _ _ _ _ } _ _ _ _ _ _) = Just idx
getIndex AdjustRateMortgage {} = Nothing

instance IR.UseRate Loan where

Check warning on line 221 in src/AssetClass/AssetBase.hs

View workflow job for this annotation

GitHub Actions / build

• No explicit implementation for

Check warning on line 221 in src/AssetClass/AssetBase.hs

View workflow job for this annotation

GitHub Actions / build

• No explicit implementation for
getIndex (PersonalLoan oi@LoanOriginalInfo{originRate = IR.Floater _ idx _ _ _ _ _ _ } _ _ _ _) = Just idx
getIndex PersonalLoan {} = Nothing

instance IR.UseRate Installment where

Check warning on line 225 in src/AssetClass/AssetBase.hs

View workflow job for this annotation

GitHub Actions / build

• No explicit implementation for

Check warning on line 225 in src/AssetClass/AssetBase.hs

View workflow job for this annotation

GitHub Actions / build

• No explicit implementation for
getIndex (Installment oi@LoanOriginalInfo{originRate = IR.Floater _ idx _ _ _ _ _ _ } _ _ _) = Just idx
getIndex Installment {} = Nothing

instance IR.UseRate Lease where

Check warning on line 229 in src/AssetClass/AssetBase.hs

View workflow job for this annotation

GitHub Actions / build

• No explicit implementation for

Check warning on line 229 in src/AssetClass/AssetBase.hs

View workflow job for this annotation

GitHub Actions / build

• No explicit implementation for
getIndex :: Lease -> Maybe Index
getIndex _ = Nothing

instance IR.UseRate FixedAsset where
getIndex _ = Nothing

instance IR.UseRate Receivable where
getIndex _ = Nothing


$(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 ''AssetUnion)
$(deriveJSON defaultOptions ''PrepayPenaltyType)
Loading
Loading