CLO IC Test (WIP)

import pandas as pd
pd.set_option('display.max_rows', None)
from absbox import Generic

bonds = (
    ("A1",{"balance":1000
             ,"rate":0.07
             ,"originBalance":1000
             ,"originRate":0.07
             ,"startDate":"2020-01-03"
             ,"rateType":{"Fixed":0.08}
             ,"bondType":{"Sequential":None}})
      ,("B",{"balance":1000
             ,"rate":0.0
             ,"originBalance":1000
             ,"originRate":0.07
             ,"startDate":"2020-01-03"
             ,"rateType":{"Fixed":0.00}
             ,"bondType":{"Sequential":None}
             })
      ,("E",{"balance":500
             ,"rate":0.0
             ,"originBalance":500
             ,"originRate":0.07
             ,"startDate":"2020-01-03"
             ,"rateType":{"Fixed":0.00}
             ,"bondType":{"Equity":None}
             })             
)

bond_OCs = [("A1",("A1",),1.1),("B",("A1","B"),1.05)]

OC_triggers = {f"OC_{tName}":
                  {"condition":(("/",("poolBalance",)
                                    ,("bondBalance",*b))
                                ,"<"
                                ,threshold)
                    ,"effects":None
                    ,"status":False
                    ,"curable":True}
                for (tName,b,threshold) in bond_OCs}

bond_ICs = [("A",("A1",),1.05),("B",("A1","B"),1.03)]

IC_triggers = {f"IC_{tName}":
                  {"condition":["all"
                                  ,[("bondDueInt",*b),">",0]
                                  ,[("/", ("accountBalance","intAcc"),("bondDueInt",*b))
                                    ,"<"
                                    ,threshold]
                              ]
                    ,"effects":None
                    ,"status":False
                    ,"curable":True}
                for (tName,b,threshold) in bond_OCs}

IC_triggerNames = list(IC_triggers.keys())
OC_triggerNames = list(OC_triggers.keys())


CLO_sample = Generic(
    "CLO_sample"
    ,{"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.02
          ,"remainTerm":30
          ,"status":"current"}]]}
    ,(("intAcc",{"balance":0}),("prinAcc",{"balance":0}))
    ,bonds
    ,(("seniorFee",{"type":{"annualPctFee":[("poolBalance",),0.003]},"feeStart":"2021-06-15"})
      ,("subFee",{"type":{"annualPctFee":[("poolBalance",),0.002]},"feeStart":"2021-06-15"})
      ,)
    ,{"amortizing":[
        # intereset waterfall
        # accrue and pay senior fee 
        ["calcAndPayFee","intAcc",['seniorFee']]
        # accrue interest for both bonds (NO payment) 
        ,["calcInt","A1","B"]
        # calculate OC and IC test
        ,['runTriggers',*(IC_triggerNames)]
        # payout interest of A1 
        ,["payInt","intAcc",["A1"]]
        # if all IC test is passing 
        ,["IfElse",[("trigger","InDistribution", "IC_A1"),False],
            # if passing
            [["payInt","intAcc",["B"]]
              ,["calcAndPayFee","intAcc",['subFee']]
              ,["payIntResidual","intAcc","E"]]
            # if failing, redemption senior to satisfy IC
            ,[["payPrin","intAcc",["A1"]
                ,{"limit":{"formula":("bondBalance","A1")}}
                ]
              # update IC test again
              #,['runTriggers',*IC_triggerNames]
              # if IC test is passing
              ,["If",[("trigger","InDistribution", "IC_A1"), False],
                # pay interest of B and residual to E
                ["payInt","intAcc",["B"]],
                ["calcAndPayFee","intAcc",['subFee']],
                ["payIntResidual","intAcc","E"]]
              ]]
        # principal waterfall
          ,["payPrinBySeq","prinAcc",["A1","B"]]
          ,["payPrinResidual","prinAcc",["E"]]
      ]}
    ,[["CollectedInterest","intAcc"]
      ,["CollectedPrincipal","prinAcc"]
      ,["CollectedPrepayment","prinAcc"]
      ,["CollectedRecoveries","prinAcc"]]
    ,None
    ,None
    ,None
    ,{"InDistribution":OC_triggers | IC_triggers}
    ,("PreClosing","Amortizing")
    )
from absbox import API,mkDeal,EnginePath
localAPI = API(EnginePath.DEV,check=False)

r = localAPI.run(CLO_sample
                ,poolAssump = None
                ,runAssump = None
                ,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(
Warning Message from server:Bond E is not paid off
Fee subFee is not paid off
Account intAcc has cash to be distributed
r['triggers']['InWF']['IC_A1'].head(20)
status memo
date
2021-07-26 False <Tag:All:[Right 7.86 > 0.00|Right 1.7417302798...
2021-08-20 True <Tag:All:[Right 3.39 > 0.00|Right 0.9174041297...
2021-09-20 True <Tag:All:[Right 4.05 > 0.00|Right 0.7160493827...
2021-10-20 True <Tag:All:[Right 4.38 > 0.00|Right 0.6392694063...
2021-11-20 True <Tag:All:[Right 4.49 > 0.00|Right 0.5946547884...
2021-12-20 True <Tag:All:[Right 4.22 > 0.00|Right 0.6090047393...
2022-01-20 True <Tag:All:[Right 3.71 > 0.00|Right 0.6576819407...
2022-02-20 True <Tag:All:[Right 2.89 > 0.00|Right 0.8062283737...
2022-03-20 False <Tag:All:[Right 1.63 > 0.00|Right 1.3803680981...
2022-04-20 False <Tag:All:[Right 0.75 > 0.00|Right 2.8 < 1.10]>
2022-05-20 False <Tag:All:[Right 0.3 > 0.00|Right 6.63333333333...
2022-06-20 False <Tag:All:[Right 0.0 > 0.00|Left "Date:2022-06-...
2022-07-20 False <Tag:All:[Right 0.0 > 0.00|Left "Date:2022-07-...
2022-08-20 False <Tag:All:[Right 0.0 > 0.00|Left "Date:2022-08-...
2022-09-20 False <Tag:All:[Right 0.0 > 0.00|Left "Date:2022-09-...
2022-10-20 False <Tag:All:[Right 0.0 > 0.00|Left "Date:2022-10-...
2022-11-20 False <Tag:All:[Right 0.0 > 0.00|Left "Date:2022-11-...
2022-12-20 False <Tag:All:[Right 0.0 > 0.00|Left "Date:2022-12-...
2023-01-20 False <Tag:All:[Right 0.0 > 0.00|Left "Date:2023-01-...
2023-02-20 False <Tag:All:[Right 0.0 > 0.00|Left "Date:2023-02-...
r['triggers']['InWF']['IC_A1'].head(20).loc['2021-07-26'].memo
'<Tag:All:[Right 7.86 > 0.00|Right 1.7417302798982188 < 1.10]>'
r['accounts']['intAcc'].loc["2021-07-26"]
balance change memo
date
2021-07-26 13.69 -0.23 <SeqPayFee:seniorFee>
2021-07-26 5.83 -7.86 <PayInt:A1>
2021-07-26 0.00 -5.83 <PayPrin:A1>
r['accounts']['intAcc'].loc["2021-08-20"]
balance change memo
date
2021-08-20 3.11 -0.07 <SeqPayFee:seniorFee>
2021-08-20 0.00 -3.11 <PayInt:A1>
r['bonds']['A1']
balance interest principal rate cash intDue intOverInt factor memo
date
2021-07-26 707.13 7.86 292.87 0.07 300.73 0.00 0 0.70713 [[<PayInt:A1>, <PayPrin:A1>], <PayPrin:A1>]
2021-08-20 635.07 3.11 72.06 0.07 75.17 0.28 0 0.63507 [<PayInt:A1>, <PayPrin:A1>]
2021-09-20 562.89 2.90 72.18 0.07 75.08 1.15 0 0.56289 [<PayInt:A1>, <PayPrin:A1>]
2021-10-20 490.59 2.80 72.30 0.07 75.10 1.58 0 0.49059 [<PayInt:A1>, <PayPrin:A1>]
2021-11-20 418.17 2.67 72.42 0.07 75.09 1.82 0 0.41817 [<PayInt:A1>, <PayPrin:A1>]
2021-12-20 345.63 2.57 72.54 0.07 75.11 1.66 0 0.34563 [[<PayInt:A1>, <PayInt:A1>], <PayPrin:A1>]
2022-01-20 272.97 2.44 72.66 0.07 75.10 1.27 0 0.27297 [<PayInt:A1>, <PayPrin:A1>]
2022-02-20 200.19 2.33 72.78 0.07 75.11 0.56 0 0.20019 [<PayInt:A1>, <PayPrin:A1>]
2022-03-20 126.67 1.63 73.52 0.07 75.15 0.00 0 0.12667 [[<PayInt:A1>, <PayPrin:A1>], <PayPrin:A1>]
2022-04-20 52.31 0.75 74.36 0.07 75.11 0.00 0 0.05231 [[<PayInt:A1>, <PayPrin:A1>], <PayPrin:A1>]
2022-05-20 0.00 0.30 52.31 0.07 52.61 0.00 0 0.00000 [[<PayInt:A1>, <PayPrin:A1>], <PayPrin:A1>]