Skip to content

Commit f4ae58a

Browse files
committed
add analytics
1 parent dcd1e3e commit f4ae58a

File tree

2 files changed

+97
-56
lines changed

2 files changed

+97
-56
lines changed

absbox/client.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -246,19 +246,22 @@ def build_run_deal_req(self, run_type, deal, perfAssump=None, nonPerfAssump=[])
246246
_perfAssump = mkAssumpType(perfAssump)
247247
_nonPerfAssump = mkNonPerfAssumps({}, nonPerfAssump)
248248
r = mkTag(("FirstLossReq", [(_deal, _perfAssump, _nonPerfAssump), bn]))
249-
case ("MaxSpreadToFaceReq", (bn,bns)) :
249+
case ("MaxSpreadToFaceReq", bn):
250250
_deal = deal.json if hasattr(deal, "json") else deal
251251
_perfAssump = mkAssumpType(perfAssump)
252252
_nonPerfAssump = mkNonPerfAssumps({}, nonPerfAssump)
253-
r = mkTag(("MaxSpreadToFaceReq", [(_deal, _perfAssump, _nonPerfAssump)
254-
, (bn,bns)]))
253+
r = mkTag(("MaxSpreadToFaceReq", [(_deal, _perfAssump, _nonPerfAssump), bn]))
255254
case _:
256255
raise RuntimeError(f"Failed to match run type:{run_type}")
256+
257+
assert r is not None, f"Failed to build request for run type:{run_type}"
257258
try:
258259
return json.dumps(r, ensure_ascii=False)
259260
except TypeError as e:
260261
raise AbsboxError(f"❌Failed to convert request to json:{e}")
261262

263+
264+
262265
def build_pool_req(self, pool, poolAssump, rateAssumps, isMultiScenario=False) -> str:
263266
"""build pool run request: (single run, multi-scenario run)
264267
@@ -612,7 +615,7 @@ def runByCombo(self,
612615
if len(dealMap) == 0:
613616
raise AbsboxError(f"❌ No deals found in dealMap,at least one deal is required")
614617

615-
if "^" in " ".join(tz.concatv(dealMap.keys(),poolAssump.keys(),runAssump.keys())):
618+
if "^" in " ".join(tz.concatv([str(_) for _ in dealMap.keys()],[str(_) for _ in poolAssump.keys()],[str(_) for _ in runAssump.keys()])):
616619
raise AbsboxError(f"❌ Deal name should not contain '^' ")
617620

618621
req = self.build_run_deal_req("CS", dealMap, poolAssump, runAssump)
@@ -694,8 +697,8 @@ def runRootFinder(self, p, read=True, debug=False) -> dict:
694697
url = f"{self.url}/{Endpoints.RunRootFinder.value}"
695698
req = None
696699
match p:
697-
case ("maxSpreadToFace",deal,poolAssump,runAssump,(bn,bns),adj):
698-
req = self.build_run_deal_req(("MaxSpreadToFaceReq",(bn,bns)), deal, poolAssump, runAssump)
700+
case ("maxSpreadToFace",deal,poolAssump,runAssump,bn):
701+
req = self.build_run_deal_req(("MaxSpreadToFaceReq",bn), deal, poolAssump, runAssump)
699702
case ("firstLoss",deal,poolAssump,runAssump,bn):
700703
req = self.build_run_deal_req(("FirstLoss",bn), deal, poolAssump, runAssump)
701704
if debug:
@@ -797,6 +800,7 @@ def _send_req(self, _req, _url: str, timeout=10, headers={})-> dict | None:
797800
:return: response in dict
798801
:rtype: dict | None
799802
"""
803+
assert _req is not None, f"❌request body is None, please check your request"
800804
try:
801805
hdrs = self.hdrs | headers
802806
r = None

docs/source/analytics.rst

Lines changed: 87 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ Performing
124124
* ``{"ByAmount":(2000,[500,500,1000])}`` apply a custom default amount vector.
125125
* ``{"DefaultAtEndByRate":(0.05,0.10)}``, will apply 5% as CDR for all periods except last period. The last period will use default CDR 10% (which start from begining day).
126126
.. versionadded:: 0.42.2
127+
127128
* ``{"ByTerm":[ [vec1],[vec2]...]``, input list of vectors, asset will use vector with same origin term length
128129
* <Prepayment Assumption>
129130

@@ -1382,52 +1383,6 @@ Pass a map to ``poolAssump`` to run multiple scenarios.
13821383
.. seealso::
13831384
For details on sensitivity run pls refer to :ref:`Sensitivity Analysis`
13841385
1385-
First Loss Run
1386-
""""""""""""""""""""
1387-
1388-
.. versionadded:: 0.42.2
1389-
1390-
User can input with an assumption with one more field ("Bond Name") compare to single run:
1391-
1392-
* deal object
1393-
* pool performance assumption
1394-
* deal run assumption
1395-
* bond name
1396-
1397-
.. versionchanged:: 0.42.6
1398-
Using endpoint of ``runRootFinder()``
1399-
1400-
syntax
1401-
``("firstLoss",<deal>,<poolAssump>,<runAssump>,<bondName>)``
1402-
1403-
1404-
The engine will stress on the default assumption till the bond incur a 0.01 loss.
1405-
Then engine return a tuple
1406-
1407-
* The factor it used and the stressed assumption.
1408-
* The assumption was applied to make the bond incur first 0.01 loss.
1409-
1410-
.. warning::
1411-
The iteration begins with stress 500x on the default assumption. Make sure the default assumption is not zero
1412-
1413-
.. code-block:: python
1414-
1415-
r0 = localAPI.runRootFinder(
1416-
("firstLoss", test01
1417-
,("Pool",("Mortgage",{"CDRPadding":[0.01,0.02]},{"CPR":0.02},{"Rate":0.1,"Lag":5},None)
1418-
,None
1419-
,None)
1420-
,[]
1421-
,"A1"
1422-
)
1423-
)
1424-
# stress factor
1425-
r0['FirstLossResult'][0]
1426-
# stressed scenario
1427-
r0['FirstLossResult'][1]
1428-
1429-
.. seealso::
1430-
For details on first loss run pls refer to :ref:`First Loss Example`
14311386
14321387
14331388
Running a pool of assets
@@ -2134,14 +2089,96 @@ In combination of above three.
21342089
,"B":[("call",("poolBalance",500))]}
21352090
,read=True)
21362091
2092+
Root Finder
2093+
----------------------
2094+
2095+
.. warning::
2096+
2097+
This a collection of advance analytics which involves CPU intenstive task. Pls don't abuse these functions in public server.
2098+
2099+
2100+
First Loss Run
2101+
^^^^^^^^^^^^^^^^^^^^
2102+
2103+
.. versionadded:: 0.42.2
2104+
2105+
User can input with an assumption with one more field ("Bond Name") compare to single run:
2106+
2107+
* deal object
2108+
* pool performance assumption
2109+
* deal run assumption
2110+
* bond name
2111+
2112+
.. versionchanged:: 0.42.6
2113+
2114+
Using endpoint of ``runRootFinder()``
2115+
2116+
syntax
2117+
``("firstLoss",<deal>,<poolAssump>,<runAssump>,<bondName>)``
2118+
2119+
2120+
The engine will stress on the default assumption till the bond incur a 0.01 loss.
2121+
Then engine return a tuple
2122+
2123+
* The factor it used and the stressed assumption.
2124+
* The assumption was applied to make the bond incur first 0.01 loss.
2125+
2126+
.. warning::
2127+
The iteration begins with stress 500x on the default assumption. Make sure the default assumption is not zero
2128+
2129+
.. code-block:: python
2130+
2131+
r0 = localAPI.runRootFinder(
2132+
("firstLoss", test01
2133+
,("Pool",("Mortgage",{"CDRPadding":[0.01,0.02]},{"CPR":0.02},{"Rate":0.1,"Lag":5},None)
2134+
,None
2135+
,None)
2136+
,[]
2137+
,"A1"
2138+
)
2139+
)
2140+
# stress factor
2141+
r0['FirstLossResult'][0]
2142+
# stressed scenario
2143+
r0['FirstLossResult'][1]
2144+
2145+
.. seealso::
2146+
For details on first loss run pls refer to :ref:`First Loss Example`
2147+
2148+
2149+
2150+
Spread Breakeven
2151+
^^^^^^^^^^^^^^^^^^^^
2152+
.. versionadded:: 0.45.3
2153+
2154+
It will tune up the spread/interest rate of a bond gradually till `` pricing of bond equals to originBalance ``
2155+
2156+
* The pricing curve shall be passed in the runAssump.
2157+
* The bond init rate/original rate should be 0.0
2158+
2159+
syntax
2160+
``("maxSpreadToFace",<deal>,<poolAssump>,<runAssump>,<bondName>)``
2161+
2162+
.. code-block:: python
2163+
2164+
localAPI.runRootFinder(
2165+
("maxSpreadToFace"
2166+
, SLYF2501
2167+
,p
2168+
,newRassump
2169+
,"A"
2170+
)
2171+
,read=True)
2172+
2173+
21372174
21382175
Retriving Results
2139-
^^^^^^^^^^^^^^^^^^^^^
2176+
---------------------
21402177
21412178
The result returned from sensitivity run is just a map, with key as identifer for each scenario, the value is the same as single run.
21422179
21432180
Plain Python Keys
2144-
""""""""""""""""""
2181+
^^^^^^^^^^^^^^^^^^
21452182
21462183
To access same component from different sceanrio :
21472184
@@ -2157,15 +2194,15 @@ To access same component from different sceanrio :
21572194
21582195
21592196
Built-in Comparision functions
2160-
""""""""""""""""""""""""""""""""""""""
2197+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21612198
21622199
.. seealso::
21632200
There are couple built-in functions will help user to get result in easier way :ref:`Read Multiple Result Map`
21642201
21652202
21662203
21672204
Compare two results
2168-
""""""""""""""""""""""""
2205+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21692206
21702207
.. versionadded:: 0.43.1
21712208

0 commit comments

Comments
 (0)