Skip to content

Commit f583755

Browse files
committed
uppdate docs
1 parent 5e0cb1c commit f583755

File tree

7 files changed

+160
-33
lines changed

7 files changed

+160
-33
lines changed

absbox/local/component.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,12 +2117,17 @@ def mkLiqProvider(n: str, x: dict):
21172117
,("creditCalc","liqCreditCalc")
21182118
]
21192119
,opt_key=True)
2120-
2120+
def mkCredit(y):
2121+
match y:
2122+
case None:
2123+
return "Unlimit"
2124+
case amt:
2125+
return mkTag(("ByAvailAmount", vNum(amt)))
21212126
r = {
21222127
"liqName": vStr(n),
21232128
"liqType": mkLiqProviderType(x_transformed["liqType"]),
21242129
"liqBalance":(x_transformed.get("liqBalance",0)),
2125-
"liqCredit":(x_transformed.get("liqCredit",None)),
2130+
"liqCredit":mkCredit(x_transformed.get("liqCredit",None)),
21262131
"liqCreditCalc":(x_transformed.get("liqCreditCalc",None)),
21272132
"liqRateType": mkRateType(x_transformed.get("liqRateType",None)),
21282133
"liqPremiumRateType": mkRateType(x_transformed.get("liqPremiumRateType",None)),

absbox/tests/regression/deals.py

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
"cutoff": "2021-03-01",
1010
"closing": "2021-04-15",
1111
"firstPay": "2021-07-26",
12-
"firstCollect": "2021-04-28",
1312
"payFreq": ["DayOfMonth", 20],
1413
"poolFreq": "MonthEnd",
1514
"stated": "2030-01-01",
@@ -990,9 +989,53 @@
990989
for _ in range(30) ]
991990

992991
fixPct = (1.00,0.07)
993-
# floatPcts = [(0.50, 0.05, 0.02, "LIBOR1M")]
994992
floatPcts = []
995993
projCf = ["ProjectedByFactor", cf, "MonthEnd", fixPct, floatPcts]
996994

997995
test06 = test01 & lens.name.set("TEST06 - ProjectByFactor")\
998996
& lens.pool['assets'].set([projCf])
997+
998+
### current deal
999+
1000+
currentDates = {"collect":["2021-04-01","2021-06-01"]
1001+
,"pay":["2021-04-26","2021-07-15"]
1002+
,"stated":"2030-01-01"
1003+
,"poolFreq":"MonthEnd"
1004+
,"payFreq":["DayOfMonth",20]
1005+
}
1006+
1007+
test07 = test01 & lens.name.set("TEST07 - CurrentDeal")\
1008+
& lens.status.set("amortizing")\
1009+
& lens.pool.modify(lambda x: tz.assoc(x
1010+
, "issuanceStat"
1011+
,{"IssuanceBalance":1800}))\
1012+
& lens.dates.set(currentDates)
1013+
1014+
test08 = test01 & lens.name.set("TEST08 - CurrentDeal&CustomeDate")\
1015+
& lens.status.set("amortizing")\
1016+
& lens.pool.modify(lambda x: tz.assoc(x
1017+
, "issuanceStat"
1018+
,{"IssuanceBalance":1800}))\
1019+
& lens.dates.set({"collect":["2021-04-01","2021-06-01"]
1020+
,"pay":["2021-04-26","2021-07-15"]
1021+
,"stated":"2030-01-01"
1022+
,"poolFreq":["CustomDate","2021-07-01","2021-08-01","2021-09-01","2021-10-01"]
1023+
,"payFreq":["CustomDate","2021-08-15","2021-09-15","2021-10-15"]
1024+
})
1025+
1026+
test09 = test01 & lens.name.set("TEST09 - PreClosingDeal&CustomeDate")\
1027+
& lens.status.set(("PreClosing","amortizing"))\
1028+
& lens.pool.modify(lambda x: tz.assoc(x
1029+
, "issuanceStat"
1030+
,{"IssuanceBalance":1800}))\
1031+
& lens.dates.set({
1032+
"cutoff": "2021-03-01",
1033+
"closing": "2021-04-15",
1034+
"firstPay": "2021-07-26",
1035+
"payFreq": ["CustomDate","2021-08-15","2021-09-15","2021-10-15"],
1036+
"poolFreq": ["CustomDate","2021-07-01","2021-08-01","2021-09-01","2021-10-01"],
1037+
"stated": "2030-01-01",
1038+
})\
1039+
& lens.pool.modify(lambda x: tz.assoc(x
1040+
, "issuanceStat"
1041+
,{"IssuanceBalance":1800}))

absbox/tests/regression/test_main.py

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ def listCloseTo(a,b,r=2):
3030
assert len(a) == len(b), f"Length not match {len(a)} {len(b)}"
3131
assert all([ closeTo(x,y,r) for x,y in zip(a,b)]), f"List not match {a},{b}"
3232

33+
def allEq(xs, x):
34+
return all([ _ == x for _ in xs])
35+
3336
def eqDataFrame(a,b, msg=""):
3437
""" Compare two DataFrame """
3538
assert a.shape == b.shape, f"Shape not match {a.shape} {b.shape}"
@@ -64,8 +67,9 @@ def setup_api():
6467

6568
@pytest.mark.pool
6669
def test_01(setup_api):
67-
r = setup_api.run(test01 , read=True , runAssump = [])
70+
r = setup_api.run(test01 , read=True , runAssump = [("stop","2021-06-15")])
6871
assert r['pool']['flow'].keys() == {"PoolConsol"}
72+
#assert r['pool']['flow']['PoolConsol']['Principal'].sum() == 2200.0
6973
assert r['pool']['flow']['PoolConsol'].to_records()[0][0]== '2021-04-15'
7074

7175

@@ -433,6 +437,7 @@ def test_trigger_chgBondRate(setup_api):
433437
}
434438
})
435439
r = setup_api.run(withTrigger , read=True ,runAssump = [("interest",("SOFR1Y",0.04))])
440+
print(r['bonds']['A1'].rate.to_list())
436441
assert r['bonds']['A1'].rate.to_list() == [0.07, 0.12, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02]
437442

438443
@pytest.mark.pool
@@ -614,15 +619,41 @@ def test_support_account(setup_api):
614619
assert r['accounts']['acc02'].loc['2024-01-20'].change.item() == -1.96
615620
assert r['accounts']['acc03'].loc['2024-01-20'].change.item() == -0.73
616621

617-
618-
619-
# @pytest.mark.analytics
620-
# def test_rootfinder_by_formula(setup_api):
621-
# poolPerf = ("Pool",("Mortgage",{"CDR":0.002},{"CPR":0.001},{"Rate":0.1,"Lag":18},None)
622-
# ,None
623-
# ,None)
624-
# r = setup_api.runRootFinder(test01, poolPerf ,[]
625-
# ,("stressPrepayment",("byFormula", ("bondTxnAmt", "<PayInt:B>","B") , 500))
626-
# )
627-
# assert r[1][1]['PoolLevel'][0]['MortgageAssump'][1] == {'PrepaymentCPR': 0.38642105474696914 }
628-
622+
@pytest.mark.dates
623+
def test_dates_preClosing(setup_api):
624+
preclosingDeal = test01
625+
pDates = preclosingDeal.dates
626+
bondNames = preclosingDeal.bonds & lens.Each()[0].collect()
627+
r = setup_api.run(preclosingDeal,read=True)
628+
629+
# test for preclosing date
630+
assert r['pool']['flow']['PoolConsol'].index[0] == pDates['closing']
631+
assert allEq([ r['bonds'][b].index[0] for b in bondNames ],pDates['firstPay'])
632+
633+
@pytest.mark.dates
634+
def test_dates_current(setup_api):
635+
currentDeal = test07
636+
pDates = currentDeal.dates
637+
bondNames = currentDeal.bonds & lens.Each()[0].collect()
638+
r = setup_api.run(currentDeal,read=True)
639+
# test for current date
640+
assert r['pool']['flow']['PoolConsol'].index[0] == pDates['collect'][1]
641+
assert allEq([ r['bonds'][b].index[0] for b in bondNames ],pDates['pay'][1])
642+
643+
@pytest.mark.dates
644+
def test_dates_custom(setup_api):
645+
customDeal = test08
646+
pDates = customDeal.dates
647+
bondNames = customDeal.bonds & lens.Each()[0].collect()
648+
r = setup_api.run(customDeal,read=True)
649+
# test for custom date
650+
assert r['pool']['flow']['PoolConsol'].index.to_list() == [pDates['collect'][1]]+pDates['poolFreq'][1:]
651+
assert allEq([ r['bonds'][b].index.to_list() for b in bondNames ],[pDates['pay'][1]]+pDates['payFreq'][1:])
652+
653+
customDeal = test09
654+
pDates = customDeal.dates
655+
bondNames = customDeal.bonds & lens.Each()[0].collect()
656+
r = setup_api.run(customDeal,read=True)
657+
# test for custom date
658+
assert r['pool']['flow']['PoolConsol'].index.to_list() == [pDates['closing']]+pDates['poolFreq'][1:]
659+
assert allEq([ r['bonds'][b].index.to_list() for b in bondNames ],[pDates['firstPay']]+pDates['payFreq'][1:])

docs/source/conf.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
copyright = '2025, Xiaoyu Zhang'
1010
author = 'Xiaoyu Zhang'
1111

12-
release = "0.50.2"
13-
release = "0.50.2"
12+
release = "0.51.6"
1413

1514
# -- General configuration
1615

@@ -25,8 +24,6 @@
2524
'sphinxemoji.sphinxemoji',
2625
'sphinx.ext.graphviz',
2726
'sphinx_changelog',
28-
#'autoapi.extension'
29-
#'nbsphinx'
3027
'myst_nb',
3128
'sphinxcontrib.googleanalytics'
3229
]
@@ -80,4 +77,4 @@
8077
epub_show_urls = 'footnote'
8178

8279

83-
googleanalytics_id = 'G-C0JWMTTLRN'
80+
googleanalytics_id = 'G-C0JWMTTLRN'

docs/source/modeling.rst

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -755,20 +755,41 @@ Custom Defined Dates(for payment holiday)
755755
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
756756
User are free to feed in a series of custom defined pool collection date / bond payment dates to accommodate holidays etc.
757757

758+
For current deal
759+
758760
.. code-block:: python
759761
760-
{"poolCollection":["2023-01-31","2023-02-28"...]
761-
,"distirbution":["2023-02-01","2023-03-01"...]
762-
,"cutoff":"2022-11-21"
763-
,"closing":"2023-01-01"}
762+
{"collect":["2021-04-01","2021-06-01"]
763+
,"pay":["2021-04-26","2021-07-15"]
764+
,"stated":"2030-01-01"
765+
,"poolFreq":["CustomDate","2021-07-01","2021-08-01","2021-09-01","2021-10-01"]
766+
,"payFreq":["CustomDate","2021-08-15","2021-09-15","2021-10-15"]
767+
}
768+
769+
For preClosing deal
770+
771+
.. code-block:: python
772+
773+
{
774+
"cutoff": "2021-03-01",
775+
"closing": "2021-04-15",
776+
"firstPay": "2021-07-26",
777+
"payFreq": ["CustomDate","2021-08-15","2021-09-15","2021-10-15"],
778+
"poolFreq": ["CustomDate","2021-07-01","2021-08-01","2021-09-01","2021-10-01"],
779+
"stated": "2030-01-01",
780+
}
781+
764782
765783
766784
Custom Dates for waterfall
767785
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
786+
.. versionadded:: 0.41.3
768787

769788
In examples below , the engine will pick a waterfall named ``custA`` on every first day of the month and ``custB`` on every quarter end.
770789

771-
.. versionadded:: 0.41.3
790+
Make sure to include ``custA`` ``custB`` in the :ref:`Waterfall` section of the deal.
791+
792+
772793

773794
.. code-block:: python
774795
@@ -784,6 +805,25 @@ In examples below , the engine will pick a waterfall named ``custA`` on every fi
784805
.. note::
785806
User can generate a series of dates on his/her own and nudge the dates before/afterwards base on the holidays which are generated by `python-holidays <https://python-holidays.readthedocs.io/en/latest/>`_
786807

808+
Automatically Accrue When Waterfall Begins
809+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
810+
.. versionadded:: 0.51.6
811+
812+
By default, fees/expenses will not accrue at begin of the waterfall.
813+
814+
User have to mannually accrue them in waterfall with ``calcFee, accrueAndPay, calcInt``
815+
816+
To save the trouble ,all `fees` `bonds` will be auto accrued if prefix with a "accrue"
817+
818+
.. code-block:: python
819+
820+
,("accrue"
821+
,{"cutoff":"2021-03-01","closing":"2021-06-15"
822+
,"firstPay":"2021-07-26","firstCollect":"2021-06-30"
823+
,"payFreq":["DayOfMonth",20],"poolFreq":"MonthEnd"
824+
,"stated":"2030-01-01"}
825+
)
826+
787827
Deal Status
788828
----------------
789829

@@ -835,6 +875,7 @@ Changing Deal Status
835875

836876
* ``Trigger`` -> :ref:`Effects/Consequence of a trigger`
837877
* auto enter new status if it is a ``PreClosing`` status
878+
* via waterfall action :ref:`Change Deal Status`
838879

839880
.. seealso::
840881

@@ -2520,7 +2561,10 @@ It was modeled as a map, with key as identifier to distinguish different type of
25202561
25212562
* Waterfall which being executed only once :
25222563
2523-
* ``"cleanUp"`` -> will be exectued *once* when deal is being clean up call
2564+
* ``"cleanUp"`` ->
2565+
2566+
* will be exectued *once* when deal is being clean up call
2567+
* will be exectued *once* when deal running till end
25242568
* ``"closingDay"`` -> will be exectued *once* at the `Day of Closing` if deal status is `PreClosing` ( only valid for deals in `PreClosing` status)
25252569
25262570
@@ -4152,8 +4196,8 @@ Mannual fire a trigger
41524196
:language: python
41534197
:emphasize-lines: 72,78
41544198
4155-
Waterfall
4156-
--------------
4199+
Waterfall Examples
4200+
---------------------
41574201
41584202
Limit Principal Payment
41594203
^^^^^^^^^^^^^^^^^^^^^^^^^^^

justfile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ update-version version:
1212
sed -i "s/^release = .*/release = \"{{version}}\"/g" docs/source/conf.py
1313
sed -i "s/^version = .*/version = \"{{version}}\"/g" docs/source/conf.py
1414

15-
tag version:
15+
tag env version:
1616
echo "Tagging"
1717
git add pyproject.toml setup.cfg
1818
git commit -m "bump version to-> < {{version}} >"
@@ -30,4 +30,8 @@ push-tag:
3030

3131
push-code:
3232
echo "Pushing Code"
33-
git push origin HEAD
33+
git push origin HEAD
34+
35+
live-doc:
36+
echo "Live Doc"
37+
sphinx-autobuild docs/source docs/build/html --open-browser --watch docs/source

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ 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","report","revolving","TB","analytics","dontrun","waterfall"
61+
"trigger","report","revolving","TB","analytics","dontrun","waterfall",
62+
"dates",
63+
6264
]
6365

6466

@@ -75,6 +77,7 @@ test = [
7577
"deepdiff",
7678
"ipykernel",
7779
"pytest-notebook",
80+
"marimo",
7881
]
7982

8083
[tool.towncrier]

0 commit comments

Comments
 (0)