Skip to content

Commit 42208a6

Browse files
authored
0.30.x (#216)
* fix revolve buy when building balance; avoid duplicate run waterfall in call * "expose combo sensitivity endpoint" * expose pricing for bond groups * expose single clear ledger function * expose writeoffBySeq * expose which pool to liquidate * add new assumption curve with padding last value to rest * expose extra Stress on ppy/def curve * expose transferMultiple
1 parent 10ece4f commit 42208a6

32 files changed

+1445
-330
lines changed

.github/workflows/docker-image-dev-by-tag.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
ls -la .
4040
rm -rf /opt/ghc
4141
rm -rf /opt/hostedtoolcache
42-
42+
4343
-
4444
name: Build and push
4545
uses: docker/build-push-action@v3
@@ -48,4 +48,4 @@ jobs:
4848
push: true
4949
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/hastructure:dev, ${{ steps.meta.outputs.tags }}
5050
cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/hastructure:buildcache
51-
cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/hastructure:buildcache,mode=max
51+
cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/hastructure:buildcache,mode=max

Hastructure.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ test-suite Hastructure-test
160160
UT.BondTest
161161
UT.CashflowTest
162162
UT.DealTest
163+
UT.DealTest2
163164
UT.ExpTest
164165
UT.InterestRateTest
165166
UT.LibTest

app/Main.hs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import qualified Data.ByteString.Char8 as BS
4444
import Lucid hiding (type_)
4545
import Network.Wai
4646
import Network.Wai.Handler.Warp
47+
import Network.Wai.Middleware.Cors
4748
import qualified Data.Aeson.Parser
4849
import Language.Haskell.TH
4950

@@ -102,7 +103,7 @@ $(deriveJSON defaultOptions ''Version)
102103
instance ToSchema Version
103104

104105
version1 :: Version
105-
version1 = Version "0.29.5"
106+
version1 = Version "0.29.17"
106107

107108

108109

@@ -343,9 +344,10 @@ wrapRunAsset (RunAssetReq d assets (Just (AP.PoolLevel assumps)) mRates (Just pm
343344
type ScenarioName = String
344345

345346
data RunDealReq = SingleRunReq DealType (Maybe AP.ApplyAssumptionType) AP.NonPerfAssumption
346-
| MultiScenarioRunReq DealType (Map.Map ScenarioName AP.ApplyAssumptionType) AP.NonPerfAssumption
347-
| MultiDealRunReq (Map.Map ScenarioName DealType) (Maybe AP.ApplyAssumptionType) AP.NonPerfAssumption
348-
| MultiRunAssumpReq DealType (Maybe AP.ApplyAssumptionType) (Map.Map ScenarioName AP.NonPerfAssumption)
347+
| MultiScenarioRunReq DealType (Map.Map ScenarioName AP.ApplyAssumptionType) AP.NonPerfAssumption --- multi pool perf
348+
| MultiDealRunReq (Map.Map ScenarioName DealType) (Maybe AP.ApplyAssumptionType) AP.NonPerfAssumption -- multi deal struct
349+
| MultiRunAssumpReq DealType (Maybe AP.ApplyAssumptionType) (Map.Map ScenarioName AP.NonPerfAssumption) -- multi run assump
350+
| MultiComboReq (Map.Map ScenarioName DealType) (Map.Map ScenarioName (Maybe AP.ApplyAssumptionType)) (Map.Map ScenarioName AP.NonPerfAssumption)
349351
deriving(Show, Generic)
350352

351353
data RunSimDealReq = OASReq DealType (Map.Map ScenarioName AP.ApplyAssumptionType) AP.NonPerfAssumption
@@ -382,10 +384,9 @@ type EngineAPI = "version" :> Get '[JSON] Version
382384
:<|> "runDealByScenarios" :> ReqBody '[JSON] RunDealReq :> Post '[JSON] (Map.Map ScenarioName RunResp)
383385
:<|> "runMultiDeals" :> ReqBody '[JSON] RunDealReq :> Post '[JSON] (Map.Map ScenarioName RunResp)
384386
:<|> "runDealByRunScenarios" :> ReqBody '[JSON] RunDealReq :> Post '[JSON] (Map.Map ScenarioName RunResp)
387+
:<|> "runByCombo" :> ReqBody '[JSON] RunDealReq :> Post '[JSON] (Map.Map String RunResp)
385388
:<|> "runDate" :> ReqBody '[JSON] RunDateReq :> Post '[JSON] [Date]
386389

387-
-- instance NFData [Date]
388-
389390

390391
engineAPI :: Proxy EngineAPI
391392
engineAPI = Proxy
@@ -433,7 +434,19 @@ runDate (RunDateReq sd dp md) = return $
433434

434435
runDealByRunScenarios :: RunDealReq -> Handler (Map.Map ScenarioName RunResp)
435436
runDealByRunScenarios (MultiRunAssumpReq dt mAssump nonPerfAssumpMap)
436-
= return $ Map.map (\singleAssump -> wrapRun dt mAssump singleAssump) nonPerfAssumpMap
437+
= return $ Map.map (wrapRun dt mAssump) nonPerfAssumpMap
438+
439+
440+
runDealByCombo :: RunDealReq -> Handler (Map.Map String RunResp)
441+
runDealByCombo (MultiComboReq dMap assumpMap nonPerfAssumpMap)
442+
= let
443+
dList = Map.toList dMap
444+
aList = Map.toList assumpMap
445+
nList = Map.toList nonPerfAssumpMap
446+
r = [ (intercalate "^" [dk,ak,nk], wrapRun d a n) | (dk,d) <- dList, (ak,a) <- aList, (nk,n) <- nList ]
447+
rMap = Map.fromList r
448+
in
449+
return rMap -- `debug` ("RunDealByCombo->"++ show rMap)
437450

438451

439452
myServer :: ServerT API Handler
@@ -446,6 +459,7 @@ myServer = return engineSwagger
446459
:<|> runDealScenarios
447460
:<|> runMultiDeals
448461
:<|> runDealByRunScenarios
462+
:<|> runDealByCombo
449463
:<|> runDate
450464
-- :<|> error "not implemented"
451465

@@ -459,7 +473,9 @@ data Config = Config { port :: Int}
459473
instance FromJSON Config
460474

461475
app :: Application
462-
app = serve (Proxy :: Proxy API) myServer
476+
-- app = serve (Proxy :: Proxy API) myServer
477+
app = simpleCors $ serve (Proxy :: Proxy API) myServer
478+
463479

464480

465481
main :: IO ()

src/Asset.hs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,16 @@ buildPrepayRates ds mPa =
167167
Util.toPeriodRateByInterval
168168
(paddingDefault 0.0 vs (pred size))
169169
(getIntervalDays ds)
170+
Just (A.PrepaymentVecPadding vs) -> zipWith
171+
Util.toPeriodRateByInterval
172+
(paddingDefault (last vs) vs (pred size))
173+
(getIntervalDays ds)
174+
Just (A.PrepayStressByTs ts x) ->
175+
let
176+
rs = buildPrepayRates ds (Just x)
177+
in
178+
getTsVals $ multiplyTs Exc (zipTs (tail ds) rs) ts
179+
170180
_ -> error ("failed to find prepayment type"++ show mPa)
171181
where
172182
size = length ds
@@ -175,18 +185,30 @@ buildDefaultRates :: [Date] -> Maybe A.AssetDefaultAssumption -> [Rate]
175185
buildDefaultRates ds Nothing = replicate (pred (length ds)) 0.0
176186
buildDefaultRates ds mDa =
177187
case mDa of
178-
Just (A.DefaultConstant r) -> replicate size r
188+
Just (A.DefaultConstant r) -> replicate size r
179189
Just (A.DefaultCDR r) -> Util.toPeriodRateByInterval r <$> getIntervalDays ds
180190
Just (A.DefaultVec vs) -> zipWith
181191
Util.toPeriodRateByInterval
182192
(paddingDefault 0.0 vs (pred size))
183193
(getIntervalDays ds)
194+
Just (A.DefaultVecPadding vs) -> zipWith
195+
Util.toPeriodRateByInterval
196+
(paddingDefault (last vs) vs (pred size))
197+
(getIntervalDays ds)
184198
Just (A.DefaultAtEndByRate r rAtEnd)
185199
-> case size of
186200
0 -> []
187201
1 -> []
188202
_ -> (Util.toPeriodRateByInterval r <$> getIntervalDays (init ds)) ++ (Util.toPeriodRateByInterval rAtEnd <$> getIntervalDays [head ds,last ds])
189203

204+
Just (A.DefaultStressByTs ts x) ->
205+
let
206+
rs = buildDefaultRates ds (Just x)
207+
r = getTsVals $ multiplyTs Inc (zipTs (tail ds) rs) ts
208+
in
209+
r -- `debug` ("Default Stress"++ show [ (fromRational x)::Float | x <- r] )
210+
211+
190212
_ -> error ("failed to find prepayment type"++ show mDa)
191213
where
192214
size = length ds

src/AssetClass/FixedAsset.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ instance Ast.Asset FixedAsset where
102102
cumuDepreciation = tail $ scanl (+) cumuDep amortizedBals
103103

104104
txns = zipWith6 CF.FixedFlow pdates scheduleBals amortizedBals cumuDepreciation units cash
105+
futureTxns = cutBy Inc Future asOfDay txns
106+
begBal = CF.buildBegBal futureTxns
105107
in
106-
(CF.CashFlowFrame (head scheduleBals,asOfDay,Nothing) $ cutBy Inc Future asOfDay txns, Map.empty)
108+
(CF.CashFlowFrame (begBal,asOfDay,Nothing) $ futureTxns, Map.empty)
107109

src/AssetClass/Installment.hs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ projectInstallmentFlow (startBal, lastPaidDate, (originRepay,originInt), startRa
7272

7373
instance Asset Installment where
7474
calcCashflow inst@(Installment (LoanOriginalInfo ob or ot p sd ptype _) cb rt st) asOfDay _
75-
= CF.CashFlowFrame (cb,asOfDay,Nothing) flows
75+
= CF.CashFlowFrame (begBal,asOfDay,Nothing) flows
7676
where
7777
lastPayDate:cf_dates = lastN (rt+1) $ sd:getPaymentDates inst 0
7878
opmt = divideBI ob ot
@@ -99,6 +99,7 @@ instance Asset Installment where
9999
(replicate _rt orate) (replicate _rt Nothing)
100100

101101
flows = cutBy Inc Future asOfDay _flows
102+
begBal = CF.buildBegBal flows
102103

103104

104105
getCurrentBal (Installment _ b _ _ ) = b
@@ -130,7 +131,7 @@ instance Asset Installment where
130131
asOfDay
131132
pAssump@(A.InstallmentAssump defaultAssump prepayAssump recoveryAssump ams,_,_)
132133
mRates
133-
= (applyHaircut ams (CF.CashFlowFrame (cb,asOfDay,Nothing) futureTxns), historyM)
134+
= (applyHaircut ams (CF.CashFlowFrame (begBal,asOfDay,Nothing) futureTxns), historyM)
134135
where
135136
recoveryLag = maybe 0 getRecoveryLag recoveryAssump
136137
lastPayDate:cfDates = lastN (rt + recoveryLag +1) $ sd:getPaymentDates inst recoveryLag
@@ -149,6 +150,7 @@ instance Asset Installment where
149150
defRates = Ast.buildDefaultRates (lastPayDate:cfDates) defaultAssump
150151
(txns,_) = projectInstallmentFlow (cb,lastPayDate,(opmt,ofee),orate,currentFactor,pt,ot) (cfDates,defRates,ppyRates,remainTerms)
151152
(futureTxns,historyM) = CF.cutoffTrs asOfDay (patchLossRecovery txns recoveryAssump)
153+
begBal = CF.buildBegBal futureTxns
152154

153155

154156
-- ^ project with defaulted at a date
@@ -162,8 +164,10 @@ instance Asset Installment where
162164
recoveries = calcRecoveriesFromDefault cb rr timing
163165
bals = scanl (-) cb recoveries
164166
_txns = [ CF.LoanFlow d b 0 0 0 0 r 0 cr Nothing | (b,d,r) <- zip3 bals cf_dates2 recoveries ]
167+
futureTxns = cutBy Inc Future asOfDay $ beforeRecoveryTxn++_txns
168+
begBal = CF.buildBegBal futureTxns
165169
in
166-
(CF.CashFlowFrame (cb,asOfDay,Nothing)$ cutBy Inc Future asOfDay (beforeRecoveryTxn++_txns),Map.empty)
170+
(CF.CashFlowFrame (begBal,asOfDay,Nothing) futureTxns ,Map.empty)
167171
where
168172
cr = getOriginRate inst
169173

src/AssetClass/Lease.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ instance Asset Lease where
211211
= fst . patchBalance $ RegularLease (LeaseInfo sd ot dp dr ob) bal ot st
212212

213213
projCashflow l asOfDay ((AP.LeaseAssump gapAssump rentAssump ed exStress),_,_) mRates
214-
= (CF.CashFlowFrame (0,asOfDay,Nothing) allTxns, Map.empty)
214+
= (CF.CashFlowFrame (begBal,asOfDay,Nothing) allTxns, Map.empty)
215215
where
216216
currentCf = calcCashflow l asOfDay mRates
217217
-- (rc,rcCurve,mgTbl,gapDays,ed) = extractAssump (A.LeaseAssump gapAssump rentAssump) -- (0.0,mkTs [],([(0.0,0)],0),0,epocDate)-- `debug` ("7")
@@ -232,6 +232,8 @@ instance Asset Lease where
232232
[]
233233
newCfs = [ calcCashflow l asOfDay mRates | l <- newLeases ] -- `debug` ("new leases"++ show newLeases )
234234
allTxns = view CF.cashflowTxn currentCf ++ (concat $ (view CF.cashflowTxn) <$> newCfs)
235+
begBal = CF.buildBegBal allTxns
236+
235237

236238
getCurrentBal l = case l of
237239
StepUpLease _ _ bal _ _ -> bal

src/AssetClass/Loan.hs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ instance Asset Loan where
127127
asOfDay
128128
(A.LoanAssump defaultAssump prepayAssump recoveryAssump ams,_,_)
129129
mRate
130-
= (applyHaircut ams (CF.CashFlowFrame (cb,asOfDay,Nothing) futureTxns), historyM)
130+
= (applyHaircut ams (CF.CashFlowFrame (begBal,asOfDay,Nothing) futureTxns), historyM)
131131
where
132132
recoveryLag = maybe 0 getRecoveryLag recoveryAssump
133133
lastPayDate:cfDates = lastN (rt + recoveryLag + 1) $ sd:getPaymentDates pl recoveryLag
@@ -143,8 +143,9 @@ instance Asset Loan where
143143
in
144144
divideBB cb (scheduleBals!!(ot - rt))
145145
_ -> 1.0
146-
(txns,_) = projectLoanFlow ((ob,ot,getOriginRate pl), cb,lastPayDate,prinPayType,dc,cr,initFactor) (cfDates,defRates,ppyRates,rateVector,remainTerms) `debug` (" rateVector"++show rateVector)
146+
(txns,_) = projectLoanFlow ((ob,ot,getOriginRate pl), cb,lastPayDate,prinPayType,dc,cr,initFactor) (cfDates,defRates,ppyRates,rateVector,remainTerms) -- `debug` (" rateVector"++show rateVector)
147147
(futureTxns,historyM) = CF.cutoffTrs asOfDay (patchLossRecovery txns recoveryAssump)
148+
begBal = CF.buildBegBal futureTxns
148149

149150
-- ^ Project cashflow for defautled loans
150151
projCashflow m@(PersonalLoan (LoanOriginalInfo ob or ot p sd prinPayType _) cb cr rt (Defaulted (Just defaultedDate)))
@@ -158,8 +159,9 @@ instance Asset Loan where
158159
_txns = [ CF.LoanFlow d 0 0 0 0 0 r 0 cr Nothing | (d,r) <- zip cf_dates2 recoveries ]
159160
(_, txns) = splitByDate (beforeRecoveryTxn++_txns) asOfDay EqToRight -- `debug` ("AS OF Date"++show asOfDay)
160161
(futureTxns,historyM) = CF.cutoffTrs asOfDay txns
162+
begBal = CF.buildBegBal futureTxns
161163
in
162-
(CF.CashFlowFrame (cb,asOfDay,Nothing) futureTxns, historyM)
164+
(CF.CashFlowFrame (begBal,asOfDay,Nothing) futureTxns, historyM)
163165

164166
projCashflow m@(PersonalLoan (LoanOriginalInfo ob or ot p sd prinPayType _) cb cr rt (Defaulted Nothing)) asOfDay assumps _
165167
= (CF.CashFlowFrame (cb,asOfDay,Nothing) [CF.LoanFlow asOfDay 0 0 0 0 0 0 0 cr Nothing],Map.empty)

src/AssetClass/MixedAsset.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ projAssetUnion (ACM.PF ast) d assumps mRates = P.projCashflow ast d assumps mRat
217217
projAssetUnion x _ _ _ = error ("Failed to match proj AssetUnion"++ show x)
218218

219219
projAssetUnionList :: [ACM.AssetUnion] -> Date -> A.ApplyAssumptionType -> Maybe [RateAssumption] -> (CF.CashFlowFrame, Map.Map CutoffFields Balance)
220+
projAssetUnionList [] d (A.PoolLevel assetPerf) mRate = (CF.CashFlowFrame (0,d,Nothing) [], Map.empty)
220221
projAssetUnionList assets d (A.PoolLevel assetPerf) mRate =
221222
let
222223
results = [ projAssetUnion asset d assetPerf mRate | asset <- assets ]

0 commit comments

Comments
 (0)