Skip to content

Commit d933ccc

Browse files
committed
update regression for revolving deal
1 parent a973cd0 commit d933ccc

File tree

5 files changed

+161
-9
lines changed

5 files changed

+161
-9
lines changed

absbox/local/component.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1227,7 +1227,10 @@ def mkMod(y: dict) -> tuple:
12271227
case ["购买资产", liq, source] | ["buyAsset", liq, source]:
12281228
return mkTag(("BuyAsset", [None, mkLiqMethod(liq), vStr(source), None]))
12291229
### Revolving buy with all cash and source/target pool
1230-
case ["购买资产2", liq, source, _limit, sPool, mPn] | ["buyAsset2", liq, source, _limit, sPool, mPn ]:
1230+
case ["购买资产2", liq, source, _limit, sPool, mPn] \
1231+
| ["buyAsset2", liq, source, _limit, sPool, mPn ] \
1232+
| ["buyAssetFromTo", liq, source, _limit, sPool, mPn ] \
1233+
:
12311234
return mkTag(("BuyAssetFrom", [mkLimit(_limit), mkLiqMethod(liq), vStr(source), sPool, mkPid(mPn)]))
12321235
## Trigger
12331236
case ["更新事件", trgName] | ["runTriggers", *trgName] | ["runTrigger", *trgName]:

absbox/local/generic.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,12 @@ def read(resp):
138138
output['pool'] = {}
139139

140140
outstanding_pool_flow = {k:{"flow": readPoolCf(aggFlow['contents'])
141-
,"breakdown": [ readPoolCf(_['contents']) for _ in breakdownFlows]}
142-
for k,(aggFlow,breakdownFlows) in resp[4].items()}
141+
,"breakdown": [ readPoolCf(_['contents']) for _ in breakdownFlows]}
142+
for k,(aggFlow,breakdownFlows) in resp[4].items()}
143143
output['pool_outstanding'] = {"flow": { k:v['flow'] for k,v in outstanding_pool_flow.items() }
144144
,"breakdown": { k:v['breakdown'] for k,v in outstanding_pool_flow.items() } }
145145

146146
poolMap = deal_content['pool']['contents']
147-
148147

149148
if deal_content['pool']['tag']=='MultiPool':
150149
output['pool']['flow'] = tz.valmap(lambda v: readPoolCf(v['futureCf'][0]['contents']) if (not v['futureCf'] is None) else pd.DataFrame(), poolMap)

absbox/tests/regression/deals.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,107 @@
341341
("PreClosing", "Amortizing"),
342342
)
343343

344+
test05 = Generic(
345+
"TEST05- preclosing - multi-assets- revolving",
346+
{
347+
"cutoff": "2021-03-01",
348+
"closing": "2021-04-15",
349+
"firstPay": "2021-07-26",
350+
"firstCollect": "2021-04-28",
351+
"payFreq": ["DayOfMonth", 20],
352+
"poolFreq": "MonthEnd",
353+
"stated": "2030-01-01",
354+
},
355+
{
356+
"assets": [
357+
[
358+
"Mortgage",
359+
{
360+
"originBalance": 1400,
361+
"originRate": ["fix", 0.045],
362+
"originTerm": 30,
363+
"freq": "Monthly",
364+
"type": "Level",
365+
"originDate": "2021-02-01",
366+
},
367+
{
368+
"currentBalance": 1400,
369+
"currentRate": 0.08,
370+
"remainTerm": 30,
371+
"status": "current",
372+
},
373+
],
374+
[
375+
"Mortgage",
376+
{
377+
"originBalance": 800,
378+
"originRate": ["fix", 0.045],
379+
"originTerm": 36,
380+
"freq": "Monthly",
381+
"type": "Level",
382+
"originDate": "2021-02-01",
383+
},
384+
{
385+
"currentBalance": 800,
386+
"currentRate": 0.05,
387+
"remainTerm": 36,
388+
"status": "current",
389+
},
390+
]
391+
]
392+
},
393+
(("acc01", {"balance": 0}),),
394+
(
395+
(
396+
"A1",
397+
{
398+
"balance": 1000,
399+
"rate": 0.07,
400+
"originBalance": 1000,
401+
"originRate": 0.07,
402+
"startDate": "2020-01-03",
403+
"rateType": {"Fixed": 0.08},
404+
"bondType": {"Sequential": None},
405+
},
406+
),
407+
(
408+
"B",
409+
{
410+
"balance": 1000,
411+
"rate": 0.0,
412+
"originBalance": 1000,
413+
"originRate": 0.07,
414+
"startDate": "2020-01-03",
415+
"rateType": {"Fixed": 0.00},
416+
"bondType": {"Equity": None},
417+
},
418+
),
419+
),
420+
tuple(),
421+
{
422+
"default": [
423+
["if", ["date", "<", "2021-09-30"]
424+
, ["buyAsset",["Current|Defaulted", 1.0, 0] , "acc01"]
425+
],
426+
["accrueAndPayInt", "acc01", ["A1"]],
427+
["payPrin", "acc01", ["A1"]],
428+
["payPrin", "acc01", ["B"]],
429+
["payIntResidual", "acc01", "B"],
430+
]
431+
},
432+
[
433+
["CollectedInterest", "acc01"],
434+
["CollectedPrincipal", "acc01"],
435+
["CollectedPrepayment", "acc01"],
436+
["CollectedRecoveries", "acc01"],
437+
],
438+
None,
439+
None,
440+
None,
441+
None,
442+
("PreClosing", "Amortizing"),
443+
)
444+
344445

345446
Irr01 = Generic(
346447
"IRR Case",

absbox/tests/regression/test_main.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pytest
55
import re, math, json
66
from pathlib import Path
7+
from collections import Counter
78

89
from .deals import *
910
from .assets import *
@@ -18,6 +19,10 @@ def closeTo(a,b,r=2):
1819
def filterTxn(rs, f, rg):
1920
return [ r for r in rs if re.match(rg, r[f])]
2021

22+
def filterRowsByStr(df, f, rg):
23+
""" Filter rows by regex on column f """
24+
return filterTxn(df.to_dict('records'), f, rg)
25+
2126
def listCloseTo(a,b,r=2):
2227
assert len(a) == len(b), f"Length not match {len(a)} {len(b)}"
2328
assert all([ closeTo(x,y,r) for x,y in zip(a,b)]), f"List not match {a},{b}"
@@ -282,9 +287,6 @@ def test_rootfind_stressdef(setup_api):
282287
)
283288
assert r[1][1]['PoolLevel'][0]['MortgageAssump'][0] == {'DefaultCDR': 0.07603587859615266}
284289

285-
286-
287-
288290
@pytest.mark.bond
289291
def test_pac_01(setup_api):
290292
r = setup_api.run(pac01 , read=True , runAssump = [])
@@ -409,3 +411,51 @@ def test_collect_pool_loanlevel_cashflow(setup_api):
409411
# combined = pd.concat([rWithOsPoolFlow['pool']['flow']['PoolConsol']
410412
# ,rWithOsPoolFlow['pool_outstanding']['flow']['PoolConsol']])
411413
# eqDataFrame(complete['pool']['flow']['PoolConsol'], combined)
414+
415+
@pytest.mark.report
416+
def test_reports(setup_api):
417+
""" Test reports on variuos asset class """
418+
pairs = {"mortgage01": (test01,None)}
419+
r = {}
420+
for k, (vd,vp) in pairs.items():
421+
r[k] = setup_api.run(vd, poolAssump=vp, read=True, runAssump =[("report", {"dates":"MonthEnd"})])
422+
423+
@pytest.mark.revolving
424+
def test_revolving_01(setup_api):
425+
""" Test revolving pool with collection """
426+
revol_asset = ["Mortgage",{
427+
"originBalance": 1400,
428+
"originRate": ["fix", 0.045],
429+
"originTerm": 30,
430+
"freq": "Monthly",
431+
"type": "Level",
432+
"originDate": "2021-02-01",},
433+
{"currentBalance": 1400,
434+
"currentRate": 0.08,
435+
"remainTerm": 30,
436+
"status": "current",}, ]
437+
r = setup_api.run(test05, read=True, runAssump =[("revolving"
438+
,["constant",revol_asset]
439+
,("Pool",("Mortgage",None,None,None,None)
440+
,None
441+
,None))]
442+
,rtn=["AssetLevelFlow"])
443+
revolvingBuyTxn = filterTxn(r['accounts']['acc01'].to_dict(orient="records"),"memo",r".*PurchaseAsset.*")
444+
assert len(revolvingBuyTxn) == 3, "Should have 3 purchase asset txn"
445+
446+
buyAmts = [ _['change'] for _ in revolvingBuyTxn]
447+
assert buyAmts == [-302.44,-75.61,-86.76], "Buy amount should be same with revolving asset"
448+
449+
buy_asset1_flow = r['pool']['breakdown']['PoolConsol'][2]
450+
buy_asset2_flow = r['pool']['breakdown']['PoolConsol'][3]
451+
buy_asset3_flow = r['pool']['breakdown']['PoolConsol'][4]
452+
assert buy_asset1_flow.Principal.sum().round(2) == 302.44
453+
assert buy_asset2_flow.Principal.sum().round(2) == 75.61
454+
assert buy_asset3_flow.Principal.sum().round(2) == 86.76
455+
456+
#breakdown cashflow should tieout with aggregated pool cashflow
457+
totalPrins = r['pool']['breakdown']['PoolConsol'] & lens.Each().Principal.collect() & lens.Each().modify(lambda x: x.sum())
458+
assert r['pool']['flow']['PoolConsol'].Principal.sum().round(2) == sum(totalPrins).round(2), "Breakdown cashflow should tieout with aggregated pool cashflow"
459+
460+
461+

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ minversion = "7.0"
5858
addopts = "-ra -q --color=yes --import-mode=importlib"
5959
markers = [
6060
"pool","account","bond","fee","interest","collect","asset","performance",
61-
"trigger",
62-
"TB","analytics","dontrun"
61+
"trigger","report","revolving","TB","analytics","dontrun"
6362
]
6463

6564

0 commit comments

Comments
 (0)