SRT Example
import pandas as pd
pd.set_option('display.max_rows', None)
Deal Structure
waterfall
Distribution Day :
Principal :
Pro Ratato senior (A) and protected tranche (B)Interest : “issuer” will pay interest to tranches
End of Colleciton : allocate pool loss by move fund from collateral cash account to “Issuer”
Closing Day: fund collateral cash account with SRT tranche balance
Clean up: when deal was called, pay all cash from collateral cash account to protected tranche (B)
from absbox import Generic
seniorBalance = 1000
srtTrancheBal = 200
srtRate = 0.08
closingDate = "2021-06-15"
periodPrincipal = ("curPoolCollection", None, "Principal","Prepayment","Recovery")
reinvestRate = 0.00
srt01 = Generic(
"SRT Example"
,{"cutoff":"2021-06-01","closing":closingDate,"firstPay":"2021-07-20"
,"payFreq":["DayOfMonth",20],"poolFreq":"MonthEnd","stated":"2030-01-01"}
,{'assets':[["Mortgage"
,{"originBalance":2200,"originRate":["fix",0.045],"originTerm":24
,"freq":"Monthly","type":"Level","originDate":"2021-06-01"}
,{"currentBalance":2200
,"currentRate":0.08
,"remainTerm":24
,"status":"current"}]],
}
,(("acc01",{"balance":0})
,("srtAcc",{"balance":0.0
,"interest":{"period":"QuarterEnd"
,"rate":reinvestRate
,"lastSettleDate":closingDate}})
,("dummy",{"balance":0.0})
,)
,(("A1",{"balance":seniorBalance
,"rate":0.00
,"originBalance":seniorBalance
,"originRate":0.00
,"startDate":closingDate
,"rateType":{"Fixed":0.00}
,"bondType":{"Sequential":None}})
,("B",{"balance":0.0
,"rate":srtRate
,"originBalance":0
,"originRate":srtRate
,"startDate":closingDate
,"rateType":{"Fixed":srtRate}
,"bondType":{"Sequential":None}
}))
,tuple()
,{"amortizing":[
# pay prorata to senior and SRT tranch
['accrueAndPayInt',"dummy",["A1"],{"support":["facility","originator"]}]
,["calcInt","B"]
,["payInt","dummy",["B"],{"support":["facility","originator"]}]
,["calcBondPrin",["A1","B"],{"formula":periodPrincipal}]
,["payPrinWithDue","acc01",["A1"]]
,["payPrinWithDue","srtAcc",["B"]]
],
"closingDay":[
["fundWith","srtAcc","B",{"formula":("const",srtTrancheBal)}]
],
"endOfCollection":[
# draw loss amount and pay to originator
["liqRepayResidual","srtAcc", "originator", {"formula":("curPoolCollection",None,"Losses")}]
],
"cleanUp":[
["payIntResidual","srtAcc","B"]
]
}
,[["CollectedInterest","acc01"]
,["CollectedPrincipal","acc01"]
,["CollectedPrepayment","acc01"]
,["CollectedRecoveries","acc01"]]
,{"originator":{"type" : "Unlimit", "start": closingDate}}
,None
,None
,None
,("PreClosing","Amortizing")
)
from absbox import API,EnginePath
localAPI = API(EnginePath.DEV, lang='english', check=False)
r = localAPI.run(srt01
,poolAssump=("Pool",("Mortgage", {"CDR":0.01}, None, None, None)
,None
,None)
,runAssump=[("call",("poolFactor",0.10))]
,read=True)
Connecting engine server -> https://absbox.org/api/dev
/home/docs/checkouts/readthedocs.org/user_builds/absbox-doc/envs/stable/lib/python3.11/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'absbox.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
warnings.warn(
✅Connected, local lib:0.52.3, server:0.52.3
/home/docs/checkouts/readthedocs.org/user_builds/absbox-doc/envs/stable/lib/python3.11/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'absbox.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings
warnings.warn(
---------------------------------------------------------------------------
EngineError Traceback (most recent call last)
Cell In[3], line 5
1 from absbox import API,EnginePath
3 localAPI = API(EnginePath.DEV, lang='english', check=False)
----> 5 r = localAPI.run(srt01
6 ,poolAssump=("Pool",("Mortgage", {"CDR":0.01}, None, None, None)
7 ,None
8 ,None)
9 ,runAssump=[("call",("poolFactor",0.10))]
10 ,read=True)
File ~/checkouts/readthedocs.org/user_builds/absbox-doc/envs/stable/lib/python3.11/site-packages/absbox/client.py:358, in API.run(self, deal, poolAssump, runAssump, read, showWarning, rtn, debug)
355 if debug:
356 return req
--> 358 result = self._send_req(req, url)
360 if result is None or 'error' in result or 'Left' in result:
361 leftVal = result.get("Left","")
File ~/checkouts/readthedocs.org/user_builds/absbox-doc/envs/stable/lib/python3.11/site-packages/absbox/client.py:828, in API._send_req(self, _req, _url, timeout, headers)
826 raise AbsboxError("❌ Failed to get response from server")
827 if r.status_code != 200:
--> 828 raise EngineError(r)
829 try:
830 return json.loads(r.text)
EngineError: Error in $.liqProvider.originator.liqCredit: When parsing Types.SupportAvailType expected Object but got String.
View Bond Cashflow
from absbox import readBondsCf
readBondsCf(r['bonds']).head()
| Bond | A1 | B | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Field | balance | interest | principal | rate | cash | balance | interest | principal | rate | cash |
| date | ||||||||||
| 2021-06-15 | NaN | NaN | NaN | NaN | NaN | 200.00 | 0.00 | -200.00 | 0.00 | 0.00 |
| 2021-07-20 | 1000.00 | 0.0 | 0.00 | 0.0 | 0.00 | 200.00 | 1.53 | 0.00 | 0.08 | 1.53 |
| 2021-08-20 | 929.37 | 0.0 | 70.63 | 0.0 | 70.63 | 185.88 | 1.35 | 14.12 | 0.08 | 15.47 |
| 2021-09-20 | 858.33 | 0.0 | 71.04 | 0.0 | 71.04 | 171.67 | 1.26 | 14.21 | 0.08 | 15.47 |
| 2021-10-20 | 786.88 | 0.0 | 71.45 | 0.0 | 71.45 | 157.38 | 1.12 | 14.29 | 0.08 | 15.41 |
Pool Loss metrics
Here, we can identify total pool loss is 23.11
r['pool']['flow'].Loss.sum()
23.11
How much pool loss was cured via SRT traction ? we can filter out transaction in the srtAcc ,the collateral cash account draw out
r['accounts']['srtAcc'].loc[lambda df: df.memo == "<Support:originator>"].head()
| balance | change | memo | |
|---|---|---|---|
| date | |||
| 2021-06-15 | 200.00 | 0.00 | <Support:originator> |
| 2021-06-30 | 200.00 | 0.00 | <Support:originator> |
| 2021-07-31 | 198.19 | -1.81 | <Support:originator> |
| 2021-08-31 | 182.27 | -1.80 | <Support:originator> |
| 2021-09-30 | 166.34 | -1.72 | <Support:originator> |
Summing up all loss cured by SRT account, that’s total cured 18.11
r['accounts']['srtAcc'].loc[lambda df: df.memo == "<Support:originator>"].change.sum()
-18.11
We can tie out 18.11 loss in the SRT tranche (B). There 18.11 oustanding balance at end of projection.
r['bonds']['B'].tail()
| balance | interest | principal | rate | cash | factor | memo | |
|---|---|---|---|---|---|---|---|
| date | |||||||
| 2023-01-20 | 18.11 | 0.12 | 0.0 | 0.08 | 0.12 | None | [<PayInt:B>, <PayPrin:B>] |
| 2023-02-20 | 18.11 | 0.12 | 0.0 | 0.08 | 0.12 | None | [<PayInt:B>, <PayPrin:B>] |
| 2023-03-20 | 18.11 | 0.11 | 0.0 | 0.08 | 0.11 | None | [<PayInt:B>, <PayPrin:B>] |
| 2023-04-20 | 18.11 | 0.12 | 0.0 | 0.08 | 0.12 | None | [<PayInt:B>, <PayPrin:B>] |
| 2023-05-20 | 18.11 | 0.11 | 0.0 | 0.08 | 0.11 | None | [[<PayInt:B>, <PayPrin:B>], <PayYield:B>] |
Return of SRT transaction
Let’s calculate the IRR of protect tranche
r['bonds']['B'].head()
| balance | interest | principal | rate | cash | factor | memo | |
|---|---|---|---|---|---|---|---|
| date | |||||||
| 2021-06-15 | 200.00 | 0.00 | -200.00 | 0.00 | 0.00 | None | <FundWith:B,200.00> |
| 2021-07-20 | 200.00 | 1.53 | 0.00 | 0.08 | 1.53 | None | [<PayInt:B>, <PayPrin:B>] |
| 2021-08-20 | 185.88 | 1.35 | 14.12 | 0.08 | 15.47 | None | [<PayInt:B>, <PayPrin:B>] |
| 2021-09-20 | 171.67 | 1.26 | 14.21 | 0.08 | 15.47 | None | [<PayInt:B>, <PayPrin:B>] |
| 2021-10-20 | 157.38 | 1.12 | 14.29 | 0.08 | 15.41 | None | [<PayInt:B>, <PayPrin:B>] |
Let’s build a simple irr function
from pyxirr import xirr
def calcIRR(df, init):
investDate,investAmount = init
ds = [investDate] + df.index.to_list()
vs = [investAmount] + df.cash.to_list()
return xirr(ds, vs)
calcIRR(r['bonds']['B'], ("2021-06-15",-200))
-0.04323574436198406
Sensitiviy Analysis : Pool Perf vs. IRR
let’s assump how different prepayment behavior would impact on the IRR
build an assumption Map
run with ..runByScenarios()
from lenses import lens
myAssumption = ("Pool",("Mortgage",None,None,None,None)
,None
,None)
myAssumption2 = myAssumption & lens[1][2].set({"CPR":0.01})
myAssumption3 = myAssumption & lens[1][2].set({"CPR":0.02})
rs = localAPI.runByScenarios(srt01
,poolAssump={"CPR-0":myAssumption
,"CPR-1":myAssumption2
,"CPR-2":myAssumption3
}
,read=True)
Warning Message from server: Account acc01 has cash to be distributed LiquidityProvider originator is not paid off Account acc01 has cash to be distributed LiquidityProvider originator is not paid off Account acc01 has cash to be distributed LiquidityProvider originator is not paid off
View pool cashflow from multiple scenarios
from absbox import readMultiFlowsByScenarios
readMultiFlowsByScenarios(rs, (lens['pool']['flow'],["Balance",'Prepayment'])).head()
| Scenario | CPR-0 | CPR-1 | CPR-2 | |||
|---|---|---|---|---|---|---|
| Field | Balance | Prepayment | Balance | Prepayment | Balance | Prepayment |
| Date | ||||||
| 2021-06-15 | 2200.00 | 0 | 2200.00 | 0.00 | 2200.00 | 0.00 |
| 2021-06-30 | 2200.00 | 0 | 2200.00 | 0.00 | 2200.00 | 0.00 |
| 2021-07-31 | 2115.17 | 0 | 2113.43 | 1.81 | 2111.66 | 3.65 |
| 2021-08-31 | 2029.77 | 0 | 2026.37 | 1.80 | 2022.93 | 3.62 |
| 2021-09-30 | 1943.81 | 0 | 1938.90 | 1.72 | 1933.94 | 3.46 |
import toolz as tz
tz.valmap(lambda x: calcIRR(x['bonds']['B'], ("2021-06-15",-200)), rs)
{'CPR-0': 0.0822966584499679,
'CPR-1': 0.08250918429431883,
'CPR-2': 0.08235205608296689}
Whoa, interesting ! CPR=1% will yield most IRR
What’s the protection exposure ?
To what extend the current capital structure will hedge the default risk ?
let’s assump how different default stress would impact on the IRR
build an assumption Map
run with ..runByScenarios()
Sensitiviy Analysis : Pool Perf vs. Exposed Loss
Exposed Loss : the loss not being hedged by SRT tranche
myAssumption = ("Pool",("Mortgage",None,None,None,None)
,None
,None)
myAssumption2 = myAssumption & lens[1][1].set({"CDR":0.01})
myAssumption3 = myAssumption & lens[1][1].set({"CDR":0.02})
rs = localAPI.runByScenarios(srt01
,poolAssump={"CDR-0":myAssumption
,"CDR-1":myAssumption2
,"CDR-2":myAssumption3
}
,read=True)
Warning Message from server: Account acc01 has cash to be distributed LiquidityProvider originator is not paid off Bond B is not paid off Account acc01 has cash to be distributed LiquidityProvider originator is not paid off Bond B is not paid off Account acc01 has cash to be distributed LiquidityProvider originator is not paid off
View pool cashflows
from absbox import readMultiFlowsByScenarios
readMultiFlowsByScenarios(rs, (lens['pool']['flow'],["Balance",'Default'])).head()
| Scenario | CDR-0 | CDR-1 | CDR-2 | |||
|---|---|---|---|---|---|---|
| Field | Balance | Default | Balance | Default | Balance | Default |
| Date | ||||||
| 2021-06-15 | 2200.00 | 0 | 2200.00 | 0.00 | 2200.00 | 0.00 |
| 2021-06-30 | 2200.00 | 0 | 2200.00 | 0.00 | 2200.00 | 0.00 |
| 2021-07-31 | 2115.17 | 0 | 2113.43 | 1.81 | 2111.66 | 3.65 |
| 2021-08-31 | 2029.77 | 0 | 2026.37 | 1.80 | 2022.93 | 3.62 |
| 2021-09-30 | 1943.81 | 0 | 1938.90 | 1.72 | 1933.94 | 3.46 |
By writing a small function to calculate unhendge amount,we are able to tell in all the scenarios, the unhedge amount
def unHedgeAmount(x:dict):
"x is the single run result"
poolLoss = x['pool']['flow'].Loss.sum()
hedgedAmount = x['accounts']['srtAcc'].loc[lambda df: df.memo == "<Support:originator>"].change.sum()
return max(poolLoss - hedgedAmount,0)
tz.valmap(unHedgeAmount, rs)
{'CDR-0': 0.0, 'CDR-1': 41.449999999999996, 'CDR-2': 81.19999999999999}
Others ?
Actuall absbox is flexible enough to perform sensitivity analysis on any two variables :
It could be reinvestment rate v.s IRR on SRT tranche..
It could be capital structure v.s IRR on SRT tranche..
SRT with 3 tranches
The key for 3 tranches is , the srtAcc only move funds to the loss which is the excess amount over the first loss tranche
firstLossTranche = 5
formulaToCurLoss = ("excess", ("cumPoolNetLoss",)
, ("abs",("accountTxnAmount","<Support:originator>","srtAcc"))
, ("originalBondBalance","C"))
seniorBalance = 1000
srtTrancheBal = 200
srtRate = 0.08
closingDate = "2021-06-15"
periodPrincipal = ("curPoolCollection", None, "Principal","Prepayment","Recovery")
reinvestRate = 0.00
srt02 = Generic(
"SRT Example"
,{"cutoff":"2021-06-01","closing":closingDate,"firstPay":"2021-07-20"
,"payFreq":["DayOfMonth",20],"poolFreq":"MonthEnd","stated":"2030-01-01"}
,{'assets':[["Mortgage"
,{"originBalance":2200,"originRate":["fix",0.045],"originTerm":24
,"freq":"Monthly","type":"Level","originDate":"2021-06-01"}
,{"currentBalance":2200
,"currentRate":0.08
,"remainTerm":24
,"status":"current"}]],
}
,(("acc01",{"balance":0})
,("srtAcc",{"balance":0.0
,"interest":{"period":"QuarterEnd"
,"rate":reinvestRate
,"lastSettleDate":closingDate}})
,("dummy",{"balance":0.0})
,)
,(("A1",{"balance":seniorBalance
,"rate":0.00
,"originBalance":seniorBalance
,"originRate":0.00
,"startDate":closingDate
,"rateType":{"Fixed":0.00}
,"bondType":{"Sequential":None}})
,("B",{"balance":0.0
,"rate":srtRate
,"originBalance":0
,"originRate":srtRate
,"startDate":closingDate
,"rateType":{"Fixed":srtRate}
,"bondType":{"Sequential":None}
})
,("C",{"balance":firstLossTranche
,"rate":0.0
,"originBalance":firstLossTranche
,"originRate":0.0
,"startDate":closingDate
,"rateType":{"Fixed":0.0}
,"bondType":{"Sequential":None}
})
)
,tuple()
,{"amortizing":[
# pay prorata to senior and SRT tranch
["calcInt","B"]
,["payInt","dummy",["B"],{"support":["facility","originator"]}]
,["calcBondPrin",["A1","B"],{"formula":periodPrincipal}]
,["payPrinWithDue","acc01",["A1"]]
,["payPrinWithDue","srtAcc",["B"]]
,["liqRepayResidual","acc01", "originator"]
,["If", [('isPaidOff',"A1","B"),True]
,["payPrin","acc01",["C"]]]
],
"closingDay":[["fundWith","srtAcc","B",{"formula":("const",srtTrancheBal)}]],
"endOfCollection":[
# draw loss amount and pay to originator
["liqRepayResidual","srtAcc", "originator", {"formula":formulaToCurLoss}]
],
"cleanUp":[
["payIntResidual","srtAcc","B"]
]
}
,[#["CollectedInterest","acc01"]
["CollectedPrincipal","acc01"]
,["CollectedPrepayment","acc01"]
,["CollectedRecoveries","acc01"]]
,{"originator":{"type" : "Unlimited", "start": closingDate}}
,None
,None
,None
,("PreClosing","Amortizing")
)
from absbox import API,EnginePath
r02 = localAPI.run(srt02
,poolAssump=("Pool",("Mortgage", {"CDR":0.01}, None, None, None)
,None
,None)
,runAssump=[("call",("poolFactor",0.10))
,("inspect"
,("MonthEnd",("excess", ("cumPoolNetLoss",)
, ("abs",("accountTxnAmount","<Support:originator>","srtAcc"))
, ("constant",firstLossTranche))
))
]
,read=True)
Warning Message from server: Bond B is not paid off Bond C is not paid off LiquidityProvider originator is not paid off
readBondsCf(r02['bonds']).head()
| Bond | A1 | B | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Field | balance | interest | principal | rate | cash | balance | interest | principal | rate | cash |
| date | ||||||||||
| 2021-06-15 | NaN | NaN | NaN | NaN | NaN | 200.00 | 0.00 | -200.00 | 0.00 | 0.00 |
| 2021-07-20 | 1000.00 | 0.0 | 0.00 | 0.0 | 0.00 | 200.00 | 1.53 | 0.00 | 0.08 | 1.53 |
| 2021-08-20 | 929.37 | 0.0 | 70.63 | 0.0 | 70.63 | 185.88 | 1.35 | 14.12 | 0.08 | 15.47 |
| 2021-09-20 | 858.33 | 0.0 | 71.04 | 0.0 | 71.04 | 171.67 | 1.26 | 14.21 | 0.08 | 15.47 |
| 2021-10-20 | 786.88 | 0.0 | 71.45 | 0.0 | 71.45 | 157.38 | 1.12 | 14.29 | 0.08 | 15.41 |
Now , the loss from the pool only will be cured from srtAcc account if cumulative loss is greater than 5
Here, at 2021-09-30, the cumulative loss is 5.33 from the pool, the engine will only allocate 0.33
pd.concat([r02['accounts']['srtAcc'],r02['pool']['flow'].CumLoss.to_frame()]).sort_index().loc["2021-09-30"]
| balance | change | memo | CumLoss | |
|---|---|---|---|---|
| 2021-09-30 | 171.67 | 0.00 | <BankInterest:> | NaN |
| 2021-09-30 | 171.34 | -0.33 | <Support:originator> | NaN |
| 2021-09-30 | NaN | NaN | NaN | 5.33 |
How thickness of FirstLossTranche would affect the IRR ?
firstLossTrancheDraft = {"FirstLoss-1": 5,"FirstLoss-2":10,"FirstLoss-3":20}
build deal structures with a map( and lenses !)
dealDrafts = tz.valmap(lambda x: srt02 & lens.bonds[2][1].Fork(lens['balance'],lens['originBalance']).set(x)
,firstLossTrancheDraft)
run differnt deal structures with funciton runStructs()
rm02 = localAPI.runStructs(dealDrafts
,poolAssump=("Pool",("Mortgage", {"CDR":0.01}, None, None, None)
,None
,None)
,nonPoolAssump=[("call",("poolFactor",0.10))
,("inspect"
,("MonthEnd",("excess", ("cumPoolNetLoss",)
, ("abs",("accountTxnAmount","<Support:originator>","srtAcc"))
, ("constant",firstLossTranche))
))
]
,read=True)
tz.valmap(lambda x: calcIRR(x['bonds']['B'], ("2021-06-15",-200)), rm02)
{'FirstLoss-1': -0.008036217135081734,
'FirstLoss-2': 0.020824974743072763,
'FirstLoss-3': 0.08232329854365725}
SRT with trigger on Prorata to Sequential
user won’t have to create a seperate new deal object but only need to create a new one from the old one: srt02
add a trigger which will be fired if pool performance hit
add extra branch of waterfall which will be execute after the trigger has been fired.
srt03 = srt02 & lens.trigger.set(
{"BeforeCollect":
{"defaultTrigger":
{"condition":[("cumPoolDefaultedRate",),">",0.01]
,"effects":("newStatus","Accelerated")
,"status":False
,"curable":False}
}
}
)
acceleratedWaterfall = [
['calcInt', 'B'],
['payInt', 'dummy', ['B'], {'support': ['facility', 'originator']}],
['payPrin','acc01',["A1"]],
['liqRepayResidual', 'acc01', 'originator'],
["If", [('isPaidOff', 'A1'), True],
['payPrin',"srtAcc", ["B"],
{"limit":
{"formula": periodPrincipal}}]],
['If', [('isPaidOff', 'A1', 'B'), True], ['payPrin', 'acc01', ['C']]]
]
srt03 &= lens.waterfall.modify(lambda x: tz.assoc(x,("amortizing", "accelerated"),acceleratedWaterfall ))
r03 = localAPI.run(srt03
,poolAssump=("Pool",("Mortgage", {"CDR":0.02}, None, None, None)
,None
,None)
,runAssump=[("call",("poolFactor",0.05))
,("inspect"
,("MonthEnd",("excess", ("cumPoolNetLoss",)
, ("abs",("accountTxnAmount","<Support:originator>","srtAcc"))
, ("constant",firstLossTranche))
))
]
,read=True)
Warning Message from server: Bond B is not paid off Bond C is not paid off LiquidityProvider originator is not paid off
readBondsCf(r03['bonds']).loc["2021-08-20":"2022-09-20",]
| Bond | A1 | B | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Field | balance | interest | principal | rate | cash | balance | interest | principal | rate | cash |
| date | ||||||||||
| 2021-08-20 | 929.43 | 0.0 | 70.57 | 0.0 | 70.57 | 185.89 | 1.35 | 14.11 | 0.08 | 15.46 |
| 2021-09-20 | 858.51 | 0.0 | 70.92 | 0.0 | 70.92 | 171.71 | 1.26 | 14.18 | 0.08 | 15.44 |
| 2021-10-20 | 787.24 | 0.0 | 71.27 | 0.0 | 71.27 | 157.46 | 1.12 | 14.25 | 0.08 | 15.37 |
| 2021-11-20 | 715.61 | 0.0 | 71.63 | 0.0 | 71.63 | 143.14 | 1.06 | 14.32 | 0.08 | 15.38 |
| 2021-12-20 | 643.62 | 0.0 | 71.99 | 0.0 | 71.99 | 128.75 | 0.94 | 14.39 | 0.08 | 15.33 |
| 2022-01-20 | 571.29 | 0.0 | 72.33 | 0.0 | 72.33 | 114.28 | 0.87 | 14.47 | 0.08 | 15.34 |
| 2022-02-20 | 484.05 | 0.0 | 87.24 | 0.0 | 87.24 | 114.28 | 0.77 | 0.00 | 0.08 | 0.77 |
| 2022-03-20 | 396.37 | 0.0 | 87.68 | 0.0 | 87.68 | 114.28 | 0.70 | 0.00 | 0.08 | 0.70 |
| 2022-04-20 | 308.25 | 0.0 | 88.12 | 0.0 | 88.12 | 114.28 | 0.77 | 0.00 | 0.08 | 0.77 |
| 2022-05-20 | 219.69 | 0.0 | 88.56 | 0.0 | 88.56 | 114.28 | 0.75 | 0.00 | 0.08 | 0.75 |
| 2022-06-20 | 130.68 | 0.0 | 89.01 | 0.0 | 89.01 | 114.28 | 0.77 | 0.00 | 0.08 | 0.77 |
| 2022-07-20 | 41.23 | 0.0 | 89.45 | 0.0 | 89.45 | 114.28 | 0.75 | 0.00 | 0.08 | 0.75 |
| 2022-08-20 | 0.00 | 0.0 | 41.23 | 0.0 | 41.23 | 31.31 | 0.77 | 82.97 | 0.08 | 83.74 |
| 2022-09-20 | NaN | NaN | NaN | NaN | NaN | 31.31 | 0.21 | 0.00 | 0.08 | 0.21 |
SRT with Sequential
Sequential waterfall
sequentialWaterfall = [
['calcInt', 'B'],
['payInt', 'dummy', ['B'], {'support': ['facility', 'originator']}],
['payPrin','acc01',["A1"]],
['liqRepayResidual', 'acc01', 'originator'],
["If", [('isPaidOff', 'A1'), True],
['payPrin',"srtAcc", ["B"],
{"limit":
{"formula": periodPrincipal}}]],
['If', [('isPaidOff', 'A1', 'B'), True], ['payPrin', 'acc01', ['C']]]
]
srt04 = lens.waterfall.modify(lambda x: tz.assoc(x,"amortizing", sequentialWaterfall))(srt03)
r04 = localAPI.run(srt04
,poolAssump=("Pool",("Mortgage", {"CDR":0.02}, None, None, None)
,None
,None)
,runAssump=[("call",("poolFactor",0.05))]
,read=True)
readBondsCf(r04['bonds']).loc["2021-08-20":"2022-09-20",]
Warning Message from server: Bond B is not paid off Bond C is not paid off LiquidityProvider originator is not paid off
| Bond | A1 | B | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Field | balance | interest | principal | rate | cash | balance | interest | principal | rate | cash |
| date | ||||||||||
| 2021-08-20 | 915.31 | 0.0 | 84.69 | 0.0 | 84.69 | 200.00 | 1.35 | 0.00 | 0.08 | 1.35 |
| 2021-09-20 | 830.20 | 0.0 | 85.11 | 0.0 | 85.11 | 200.00 | 1.35 | 0.00 | 0.08 | 1.35 |
| 2021-10-20 | 744.67 | 0.0 | 85.53 | 0.0 | 85.53 | 200.00 | 1.31 | 0.00 | 0.08 | 1.31 |
| 2021-11-20 | 658.71 | 0.0 | 85.96 | 0.0 | 85.96 | 200.00 | 1.35 | 0.00 | 0.08 | 1.35 |
| 2021-12-20 | 572.32 | 0.0 | 86.39 | 0.0 | 86.39 | 200.00 | 1.31 | 0.00 | 0.08 | 1.31 |
| 2022-01-20 | 485.51 | 0.0 | 86.81 | 0.0 | 86.81 | 200.00 | 1.35 | 0.00 | 0.08 | 1.35 |
| 2022-02-20 | 398.27 | 0.0 | 87.24 | 0.0 | 87.24 | 200.00 | 1.35 | 0.00 | 0.08 | 1.35 |
| 2022-03-20 | 310.59 | 0.0 | 87.68 | 0.0 | 87.68 | 200.00 | 1.22 | 0.00 | 0.08 | 1.22 |
| 2022-04-20 | 222.47 | 0.0 | 88.12 | 0.0 | 88.12 | 200.00 | 1.35 | 0.00 | 0.08 | 1.35 |
| 2022-05-20 | 133.91 | 0.0 | 88.56 | 0.0 | 88.56 | 200.00 | 1.31 | 0.00 | 0.08 | 1.31 |
| 2022-06-20 | 44.90 | 0.0 | 89.01 | 0.0 | 89.01 | 200.00 | 1.35 | 0.00 | 0.08 | 1.35 |
| 2022-07-20 | 0.00 | 0.0 | 44.90 | 0.0 | 44.90 | 110.55 | 1.31 | 89.45 | 0.08 | 90.76 |
| 2022-08-20 | NaN | NaN | NaN | NaN | NaN | 31.31 | 0.75 | 79.24 | 0.08 | 79.99 |
| 2022-09-20 | NaN | NaN | NaN | NaN | NaN | 31.31 | 0.21 | 0.00 | 0.08 | 0.21 |