Master Trust Example
Deal need to have a BondGroup to be qualifed as Master Trust deal.
In the run assumption, the new issue bond will be inserted into that BondGroup by BondGroup Name
In this example, the bond group name is A, which has a syntax as:
(<Bond Group Name>, <Map: bondName, bond dict> )
from absbox import Generic,API,EnginePath,readBondsCf
test01 = Generic(
"TEST01"
,{"cutoff":"2021-03-01","closing":"2021-06-15","firstPay":"2021-07-26"
,"payFreq":["DayOfMonth",20],"poolFreq":"MonthEnd","stated":"2030-01-01"}
,{'assets':[["Mortgage"
,{"originBalance":2200,"originRate":["fix",0.045],"originTerm":30
,"freq":"Monthly","type":"Level","originDate":"2021-02-01"}
,{"currentBalance":2200
,"currentRate":0.08
,"remainTerm":20
,"status":"current"}]]}
,(("acc01",{"balance":0}),)
,(("A",{"A-1":
{"balance":400
,"rate":0.09
,"originBalance":400
,"originRate":0.07
,"startDate":"2021-06-15"
,"rateType":{"Fixed":0.08}
,"bondType":{"Sequential":None}
,"maturityDate":"2025-01-01"}
,"A-2":
{"balance":600
,"rate":0.08
,"originBalance":600
,"originRate":0.07
,"startDate":"2021-06-15"
,"rateType":{"Fixed":0.08}
,"bondType":{"Sequential":None}
,"maturityDate":"2026-01-01"}
})
,("B",{"balance":1000
,"rate":0.0
,"originBalance":1000
,"originRate":0.07
,"startDate":"2020-01-03"
,"rateType":{"Fixed":0.00}
,"bondType":{"Equity":None}
}))
,(("trusteeFee",{"type":{"fixFee":30},"feeStart":"2021-06-15"}),)
,{"amortizing":[
["payFee","acc01",['trusteeFee']]
,["accrueAndPayIntByGroup","acc01","A","byStartDate"]
,["payPrinByGroup","acc01","A","byStartDate"]
,["payPrin","acc01",["B"]]
,["payIntResidual","acc01","B"]
]}
,[["CollectedInterest","acc01"]
,["CollectedPrincipal","acc01"]
,["CollectedPrepayment","acc01"]
,["CollectedRecoveries","acc01"]]
,None
,None
,None
,None
,("PreClosing","Amortizing")
)
localAPI = API(EnginePath.LOCAL,check=False)
Connecting engine server -> http://localhost:8081
✅Connected, local lib:0.28.7, server:0.28.21
Issuance by ganrantee
User can specify a series of bond issuance events. All these events are going to be executed.
("issueBond", ("date1","bondGroupName", "accountName", <bondObject1>)
, ("date2","bondGroupName", "accountName", <bondObject2>)
, ...
)
fundingPlan = [("2022-04-02","A","acc01"
,{"balance":600
,"rate":0.08
,"name":"A-3"
,"originBalance":600
,"originRate":0.07
,"startDate":"2022-04-02"
,"rateType":{"Fixed":0.08}
,"bondType":{"Sequential":None}
,"maturityDate":"2026-01-01"}
)]
r = localAPI.run(test01
,runAssump = [
("issueBond",*fundingPlan)
]
,read=True)
Warning Message from server:
readBondsCf(r['bonds']).loc["2022-02-20":"2022-06-20"]
| BondGroup | A | B | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bond | A-1 | A-2 | A-3 | - | ||||||||||||||||
| Field | balance | interest | principal | rate | cash | balance | interest | principal | rate | cash | balance | interest | principal | rate | cash | balance | interest | principal | rate | cash |
| date | ||||||||||||||||||||
| 2022-02-20 | 369.57 | 24.60 | 30.43 | 0.09 | 55.03 | 600.00 | 32.83 | 0.00 | 0.08 | 32.83 | NaN | NaN | NaN | NaN | NaN | 1000.0 | 0.0 | 0.0 | 0 | 0.0 |
| 2022-03-20 | 257.94 | 2.55 | 111.63 | 0.09 | 114.18 | 600.00 | 3.68 | 0.00 | 0.08 | 3.68 | NaN | NaN | NaN | NaN | NaN | 1000.0 | 0.0 | 0.0 | 0 | 0.0 |
| 2022-04-20 | 0.00 | 1.97 | 257.94 | 0.09 | 259.91 | 148.19 | 4.07 | 451.81 | 0.08 | 455.88 | 600.00 | 2.07 | 0.00 | 0.07 | 2.07 | 1000.0 | 0.0 | 0.0 | 0 | 0.0 |
| 2022-05-20 | NaN | NaN | NaN | NaN | NaN | 34.75 | 0.97 | 113.44 | 0.08 | 114.41 | 600.00 | 3.45 | 0.00 | 0.07 | 3.45 | 1000.0 | 0.0 | 0.0 | 0 | 0.0 |
| 2022-06-20 | NaN | NaN | NaN | NaN | NaN | 0.00 | 0.23 | 34.75 | 0.08 | 34.98 | 520.69 | 3.56 | 79.31 | 0.07 | 82.87 | 1000.0 | 0.0 | 0.0 | 0 | 0.0 |
r['accounts']['acc01'].loc["2022-04-01":"2022-04-28"]
| balance | change | memo | |
|---|---|---|---|
| date | |||
| 2022-04-02 | 717.86 | 600.00 | <IssuanceProceeds:A-3> |
| 2022-04-20 | 717.86 | 0.00 | <SeqPayFee:trusteeFee> |
| 2022-04-20 | 709.75 | -8.11 | <PayInt:A> |
| 2022-04-20 | 0.00 | -709.75 | <PayPrin:A> |
| 2022-04-20 | 0.00 | 0.00 | <PayPrin:B> |
| 2022-04-20 | 0.00 | 0.00 | <PayYield:B> |
r['bonds']['A']['A-3']
| balance | interest | principal | rate | cash | intDue | intOverInt | factor | memo | |
|---|---|---|---|---|---|---|---|---|---|
| date | |||||||||
| 2022-04-20 | 600.00 | 2.07 | 0.00 | 0.07 | 2.07 | 0 | 0 | 1.000000 | [<PayInt:A-3>, <PayPrin:A-3>] |
| 2022-05-20 | 600.00 | 3.45 | 0.00 | 0.07 | 3.45 | 0 | 0 | 1.000000 | [<PayInt:A-3>, <PayPrin:A-3>] |
| 2022-06-20 | 520.69 | 3.56 | 79.31 | 0.07 | 82.87 | 0 | 0 | 0.867817 | [<PayInt:A-3>, <PayPrin:A-3>] |
| 2022-07-20 | 405.82 | 2.99 | 114.87 | 0.07 | 117.86 | 0 | 0 | 0.676367 | [<PayInt:A-3>, <PayPrin:A-3>] |
| 2022-08-20 | 290.38 | 2.41 | 115.44 | 0.07 | 117.85 | 0 | 0 | 0.483967 | [<PayInt:A-3>, <PayPrin:A-3>] |
| 2022-09-20 | 174.25 | 1.72 | 116.13 | 0.07 | 117.85 | 0 | 0 | 0.290417 | [<PayInt:A-3>, <PayPrin:A-3>] |
| 2022-10-20 | 57.39 | 1.00 | 116.86 | 0.07 | 117.86 | 0 | 0 | 0.095650 | [<PayInt:A-3>, <PayPrin:A-3>] |
| 2022-11-20 | 0.00 | 0.34 | 57.39 | 0.07 | 57.73 | 0 | 0 | 0.000000 | [<PayInt:A-3>, <PayPrin:A-3>] |
Issue new bonds by predicate
| new after Hastructure: 0.29.x
User can supply a series of bond issuance event which has a prefix of Condition . Then engine will execute the issuance of bonds if the Condition evaluates to True
syntax
("date1", <Pre> ,"bondGroupName", "accountName", <bondObject1>, None, None)
bondToBeIssue = {"balance":600
,"rate":0.08
,"name":"A-3"
,"originBalance":600
,"originRate":0.07
,"startDate":"2022-04-02"
,"rateType":{"Fixed":0.08}
,"bondType":{"Sequential":None}
,"maturityDate":"2026-01-01"}
bondToBeIssue2 = bondToBeIssue | {"name":"A-4"}
bondToNotIssue = bondToBeIssue | {"name":"A-0"}
fundingPlan = [("2022-04-02","A","acc01",bondToBeIssue)
,("2022-07-19",[("bondBalance","A"),"<",600],"A","acc01",bondToBeIssue2,None,None)
,("2022-07-19",[("bondBalance","A"),"<",600],"A","acc01",bondToNotIssue,None,None)
]
r = localAPI.run(test01
,runAssump = [
("issueBond",*fundingPlan)
]
,read=True)
Warning Message from server: Failed to issue to bond groupA:If L (CurrentBondBalanceOf ["A"]) 600.00
In this example, the first bond bondToBeIssue2 will be issued, becasue the condition (bondBalance, "A") < 600 evaluates to True. But the bond bondNotIssue won’t be issued as the condition (bondBalance, "A") < 600 won’t be True as, the bond group balance just get increased by 600 by issuing the bond A-4
readBondsCf(r['bonds'])['A'].head()
| Bond | A-1 | A-2 | A-3 | A-4 | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Field | balance | interest | principal | rate | cash | balance | interest | principal | rate | cash | balance | interest | principal | rate | cash | balance | interest | principal | rate | cash |
| date | ||||||||||||||||||||
| 2021-07-26 | 400.0 | 0.0 | 0.0 | 0.09 | 0.0 | 600.0 | 0.0 | 0.0 | 0.08 | 0.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 2021-08-20 | 400.0 | 0.0 | 0.0 | 0.09 | 0.0 | 600.0 | 0.0 | 0.0 | 0.08 | 0.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 2021-09-20 | 400.0 | 0.0 | 0.0 | 0.09 | 0.0 | 600.0 | 0.0 | 0.0 | 0.08 | 0.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 2021-10-20 | 400.0 | 0.0 | 0.0 | 0.09 | 0.0 | 600.0 | 0.0 | 0.0 | 0.08 | 0.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 2021-11-20 | 400.0 | 0.0 | 0.0 | 0.09 | 0.0 | 600.0 | 0.0 | 0.0 | 0.08 | 0.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Issue new bonds by overriding Balance
What if the size of issuance depends on a Formula instead of a hard code amount ?
User can override the bond balance by supplying a Formula in the tuple
syntax
("date1", <Pre> ,"bondGroupName", "accountName", <bondObject1>, <Formula for bond balance>, None)
bondToBeIssue = {"balance":600
,"rate":0.08
,"name":"A-3"
,"originBalance":600
,"originRate":0.07
,"startDate":"2022-04-02"
,"rateType":{"Fixed":0.08}
,"bondType":{"Sequential":None}
,"maturityDate":"2026-01-01"}
sizeOfBondBalance = ("excess", ("poolBalance",), ("bondBalance","A"))
fundingPlan = [
("2022-07-18",[("bondBalance","A"),"<",600],"A","acc01"
, bondToBeIssue
, sizeOfBondBalance,None)
]
r = localAPI.run(test01
,runAssump = [
("issueBond",*fundingPlan)
,("inspect"#,[["CustomDate","2022-07-18"],sizeOfBondBalance]
,[["CustomDate","2022-07-18"],("poolBalance",)]
,[["CustomDate","2022-07-18"],("bondBalance","A")]
)
]
,read=True)
Warning Message from server:
readBondsCf(r['bonds'])['A'].head()
| Bond | A-1 | A-2 | A-3 | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Field | balance | interest | principal | rate | cash | balance | interest | principal | rate | cash | balance | interest | principal | rate | cash |
| date | |||||||||||||||
| 2021-07-26 | 400.0 | 0.0 | 0.0 | 0.09 | 0.0 | 600.0 | 0.0 | 0.0 | 0.08 | 0.0 | NaN | NaN | NaN | NaN | NaN |
| 2021-08-20 | 400.0 | 0.0 | 0.0 | 0.09 | 0.0 | 600.0 | 0.0 | 0.0 | 0.08 | 0.0 | NaN | NaN | NaN | NaN | NaN |
| 2021-09-20 | 400.0 | 0.0 | 0.0 | 0.09 | 0.0 | 600.0 | 0.0 | 0.0 | 0.08 | 0.0 | NaN | NaN | NaN | NaN | NaN |
| 2021-10-20 | 400.0 | 0.0 | 0.0 | 0.09 | 0.0 | 600.0 | 0.0 | 0.0 | 0.08 | 0.0 | NaN | NaN | NaN | NaN | NaN |
| 2021-11-20 | 400.0 | 0.0 | 0.0 | 0.09 | 0.0 | 600.0 | 0.0 | 0.0 | 0.08 | 0.0 | NaN | NaN | NaN | NaN | NaN |
from absbox import unifyTs
unifyTs(r['result']['inspect'].values())
| <CurrentBondBalanceOf:A> | <CurrentPoolBalance> | |
|---|---|---|
| Date | ||
| 2021-03-01 | 1000.00 | 0.00 |
| 2022-07-18 | 519.75 | 1570.39 |
Now , it should issue with balance of 1050.64
1570.39 - 519.75
1050.64
We can inspect on that day, we had the bond proceed with value of 1050.64
r['accounts']['acc01'].loc["2022-07-18"]
balance 1168.5
change 1050.64
memo <IssuanceProceeds:A-3>
Name: 2022-07-18, dtype: object
r['bonds']['A']["A-3"].loc["2022-07-20"]
balance 405.7
interest 0.4
principal 644.94
rate 0.07
cash 645.34
intDue 0
intOverInt 0
factor 0.386146
memo [<PayInt:A-3>, <PayPrin:A-3>]
Name: 2022-07-20, dtype: object
644.94 + 405.70
1050.64
Issue new bonds by overriding Rate
Other than overriding the balance, we can override the rate as well
Here, we have using a formula to describle the bond issuance rate.
bondToBeIssue = {"balance":600
,"rate":0.08
,"name":"A-3"
,"originBalance":600
,"originRate":0.07
,"startDate":"2022-04-02"
,"rateType":{"Fixed":0.08}
,"bondType":{"Sequential":None}
,"maturityDate":"2026-01-01"}
newBondRate = ("*", ("poolWaRate",), ("const",1.1))
fundingPlan = [
("2022-07-18",[("bondBalance","A"),"<",600],"A","acc01"
, bondToBeIssue
, ("const",300) , newBondRate)
]
r = localAPI.run(test01
,runAssump = [
("issueBond",*fundingPlan)
,("inspect"#,[["CustomDate","2022-07-18"],sizeOfBondBalance]
,[["CustomDate","2022-07-18"],("poolBalance",)]
,[["CustomDate","2022-07-18"],("bondBalance","A")]
)
]
,read=True)
Warning Message from server:
r['bonds']['A']["A-3"]
| balance | interest | principal | rate | cash | intDue | intOverInt | factor | memo | |
|---|---|---|---|---|---|---|---|---|---|
| date | |||||||||
| 2022-07-20 | 300.00 | 0.14 | 0.00 | 0.088 | 0.14 | 0 | 0 | 1.000000 | [<PayInt:A-3>, <PayPrin:A-3>] |
| 2022-08-20 | 290.54 | 2.24 | 9.46 | 0.088 | 11.70 | 0 | 0 | 0.968467 | [<PayInt:A-3>, <PayPrin:A-3>] |
| 2022-09-20 | 174.86 | 2.17 | 115.68 | 0.088 | 117.85 | 0 | 0 | 0.582867 | [<PayInt:A-3>, <PayPrin:A-3>] |
| 2022-10-20 | 58.26 | 1.26 | 116.60 | 0.088 | 117.86 | 0 | 0 | 0.194200 | [<PayInt:A-3>, <PayPrin:A-3>] |
| 2022-11-20 | 0.00 | 0.43 | 58.26 | 0.088 | 58.69 | 0 | 0 | 0.000000 | [<PayInt:A-3>, <PayPrin:A-3>] |
Here, we set the new issue bond rate as 110% of pool weighted average rate
r['pool']['flow'].head()
| Balance | Principal | Interest | Prepayment | Default | Recovery | Loss | WAC | BorrowerNum | PrepayPenalty | CumPrincipal | CumPrepay | CumDelinq | CumDefault | CumRecovery | CumLoss | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Date | ||||||||||||||||
| 2021-06-15 | 2200.0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0.08 | None | None | 0.0 | 0 | 0 | 0 | 0 | 0 |
| 2021-06-30 | 2200.0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0.08 | None | None | 0.0 | 0 | 0 | 0 | 0 | 0 |
| 2021-07-31 | 2200.0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0.08 | None | None | 0.0 | 0 | 0 | 0 | 0 | 0 |
| 2021-08-31 | 2200.0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0.08 | None | None | 0.0 | 0 | 0 | 0 | 0 | 0 |
| 2021-09-30 | 2200.0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0.08 | None | None | 0.0 | 0 | 0 | 0 | 0 | 0 |