Skip to content

Commit 2c73e5d

Browse files
committed
prepare to release
1 parent 2cb9453 commit 2c73e5d

File tree

5 files changed

+515
-51
lines changed

5 files changed

+515
-51
lines changed

absbox/local/component.py

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def getStartDate(x:dict) -> tuple:
8888
return (vDate(lastCollected), vDate(pp))
8989
case {"lastCollect":a,"lastPay":b}:
9090
return (a, b)
91-
case ("accrue", m):
91+
case ("accrue", m) | ("accrued", m):
9292
return getStartDate(m)
9393
case _:
9494
raise RuntimeError(f"Failed to get Start Date from {x}")
@@ -97,8 +97,8 @@ def getStartDate(x:dict) -> tuple:
9797
def mkDate(x):
9898
''' make dates component for deal '''
9999
match x:
100-
case ("accrue", m):
101-
return mkDate(m) | {"tag": "AccruedGenericDates"}
100+
# case ("accrue", m) | ("accrued", m):
101+
# return mkDate(m) | {"tag": "AccruedGenericDates"}
102102

103103
case {"cutoff": a, "closing": b, "firstPay": c,"firstCollect": d, "stated": e, "poolFreq": pf, "payFreq": bf}:
104104
cust = x.get("cust", {})
@@ -113,6 +113,19 @@ def mkDate(x):
113113
"CollectionDates":mkDatePattern(pf),
114114
} | custom
115115
return mkTag(("GenericDates",m))
116+
case {"cutoff": a, "closing": b, "firstPay": c, "stated": e, "poolFreq": pf, "payFreq": bf}:
117+
cust = x.get("cust", {})
118+
custom = {f"CustomExeDates {k}":mkDatePattern(v) for k,v in cust.items()}
119+
m = {
120+
"CutoffDate":mkDatePattern(vDate(a)),
121+
"ClosingDate":mkDatePattern(vDate(b)),
122+
"FirstPayDate":mkDatePattern(vDate(c)),
123+
"FirstCollectDate":mkDatePattern(vDate(b)),
124+
"StatedMaturityDate":mkDatePattern(vDate(e)),
125+
"DistributionDates":mkDatePattern(bf),
126+
"CollectionDates":mkDatePattern(pf),
127+
} | custom
128+
return mkTag(("GenericDates",m))
116129
case {"lastCollect": a, "lastPay": b, "nextPay": c,"nextCollect": d, "stated": e, "poolFreq": pf, "payFreq": bf} :
117130
cust = x.get("cust", {})
118131
custom = {f"CustomExeDates {k}":mkDatePattern(v) for k,v in cust.items()}
@@ -167,28 +180,28 @@ def mkDsRate(x):
167180

168181
def mkFeeType(x):
169182
match x:
170-
case {"年化费率": [base, rate]} | {"annualPctFee": [base, rate]}:
183+
case {"年化费率": [base, rate]} | {"annualPctFee": [base, rate]} | ("annualPctFee", base, rate):
171184
return mkTag(("AnnualRateFee", [mkDs(base), mkDsRate(rate)]))
172-
case {"百分比费率": [base, _rate]} | {"pctFee": [base, _rate]}:
185+
case {"百分比费率": [base, _rate]} | {"pctFee": [base, _rate]} | ("pctFee", base, _rate):
173186
rate = mkDsRate(_rate)
174187
return mkTag(("PctFee", [mkDs(base), rate]))
175-
case {"固定费用": amt} | {"fixFee": amt}:
188+
case {"固定费用": amt} | {"fixFee": amt} | ("fixFee", amt):
176189
return mkTag(("FixFee", vNum(amt)))
177-
case {"周期费用": [p, amt]} | {"recurFee": [p, amt]}:
190+
case {"周期费用": [p, amt]} | {"recurFee": [p, amt]} | ("recurFee", p, amt):
178191
return mkTag(("RecurFee", [mkDatePattern(p), vNum(amt)]))
179-
case {"自定义": fflow} | {"customFee": fflow}:
192+
case {"自定义": fflow} | {"customFee": fflow} | ("customFee", fflow):
180193
return mkTag(("FeeFlow", mkTs("BalanceCurve", fflow)))
181-
case {"计数费用": [p, s, amt]} | {"numFee": [p, s, amt]}:
194+
case {"计数费用": [p, s, amt]} | {"numFee": [p, s, amt]} | ("numFee", p, s, amt):
182195
return mkTag(("NumFee", [mkDatePattern(p), mkDs(s), amt]))
183-
case {"差额费用": [ds1, ds2]} | {"targetBalanceFee": [ds1, ds2]}:
196+
case {"差额费用": [ds1, ds2]} | {"targetBalanceFee": [ds1, ds2]} | ("targetBalanceFee", ds1, ds2):
184197
return mkTag(("TargetBalanceFee", [mkDs(ds1), mkDs(ds2)]))
185-
case {"回款期间费用": amt} | {"byPeriod": amt}:
198+
case {"回款期间费用": amt} | {"byPeriod": amt} | ("byPeriod", amt):
186199
return mkTag(("ByCollectPeriod", amt))
187-
case {"分段费用": [dp, ds, tbl]} | {"byTable": [dp, ds, tbl]}:
200+
case {"分段费用": [dp, ds, tbl]} | {"byTable": [dp, ds, tbl]} | ("byTable", dp, ds, tbl):
188201
return mkTag(("AmtByTbl", [mkDatePattern(dp), mkDs(ds), tbl]))
189-
case {"flowByBondPeriod": curve}:
202+
case {"flowByBondPeriod": curve} | ("flowByBondPeriod", curve):
190203
return mkTag(("FeeFlowByBondPeriod", mkTag(("CurrentVal", curve))))
191-
case {"flowByPoolPeriod": curve}:
204+
case {"flowByPoolPeriod": curve} | ("flowByPoolPeriod", curve):
192205
return mkTag(("FeeFlowByPoolPeriod", mkTag(("CurrentVal", curve))))
193206
case _:
194207
raise RuntimeError(f"Failed to match on fee type:{x}")
@@ -245,6 +258,8 @@ def mkDs(x):
245258
return mkTag(("CurrentBondBalanceOf", vList(bnds, str)))
246259
case ("债券应付本金", *bnds) | ("bondDuePrin", *bnds):
247260
return mkTag(("BondDuePrin", vList(bnds, str)))
261+
case ("待偿债券数量",) | ("activeBondNum",):
262+
return mkTag("ActiveBondNum")
248263
case ("初始债券余额",*bnds) | ("originalBondBalance",*bnds):
249264
if bnds:
250265
return mkTag(("OriginalBondBalanceOf", vList(bnds, str)))
@@ -307,6 +322,8 @@ def mkDs(x):
307322
if pNames:
308323
return mkTag(("PoolCurCollectionStats", [idx, lmap(mkPoolSource,i), lmap(mkPid,pNames)]))
309324
return mkTag(("PoolCollectionStats", [idx, lmap(mkPoolSource,i), None]))
325+
case ("poolIssuanceBalance", *pNames):
326+
return mkTag(("PoolIssuanceBalance", lmap(mkPid,pNames)))
310327
case ("计划资产池估值", pricingMethod, *pNames) | ("schedulePoolValuation", pricingMethod, *pNames):
311328
if pNames:
312329
return mkTag(("PoolScheduleCfPv", [mkLiqMethod(pricingMethod), lmap(mkPid,pNames)]))

absbox/tests/regression/deals.py

Lines changed: 205 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import toolz as tz
33
from lenses import lens
44

5+
def insert_functional(lst, index, element):
6+
return lst[:index] + [element] + lst[index:]
7+
58

69
test01 = Generic(
710
"TEST01- preclosing",
@@ -43,7 +46,7 @@
4346
"rate": 0.07,
4447
"originBalance": 1000,
4548
"originRate": 0.07,
46-
"startDate": "2020-01-03",
49+
"startDate": "2021-04-15",
4750
"rateType": {"Fixed": 0.08},
4851
"bondType": {"Sequential": None},
4952
},
@@ -55,7 +58,7 @@
5558
"rate": 0.0,
5659
"originBalance": 1000,
5760
"originRate": 0.07,
58-
"startDate": "2020-01-03",
61+
"startDate": "2021-04-15",
5962
"rateType": {"Fixed": 0.00},
6063
"bondType": {"Equity": None},
6164
},
@@ -696,7 +699,7 @@
696699
"default": [
697700
["accrueAndPayInt", "acc01", ["A1", "A2"]],
698701
["payPrinBySeq", "acc01", ["A1", "A2"]] ,
699-
["payPrin", "acc01", ["A1"]],
702+
["payPrin", "acc01", ["B"]],
700703
["payIntResidual", "acc01", "B"],
701704
],
702705
},
@@ -1038,4 +1041,202 @@
10381041
})\
10391042
& lens.pool.modify(lambda x: tz.assoc(x
10401043
, "issuanceStat"
1041-
,{"IssuanceBalance":1800}))
1044+
,{"IssuanceBalance":1800}))
1045+
1046+
1047+
accruedDeal = test01 & lens.name.set("TEST10 - AccruedDeal")\
1048+
& lens.waterfall['default'][0][0].set('payInt')\
1049+
& lens.dates.modify(lambda x : ("accrued", x) )
1050+
1051+
test01Fee = test01 & lens.name.set("TEST11 - With Fix Fee")\
1052+
& lens.fees.modify(lambda x: x + (("issuance_fee"
1053+
,{"type":{"fixFee":400}
1054+
,"feeStart":"2021-04-15"})
1055+
,))\
1056+
& lens.waterfall['default'].modify(lambda xs: [["calcAndPayFee","acc01",["issuance_fee"]]]+xs)
1057+
1058+
test02Fee = test01 & lens.name.set("TEST12 - With Recur Fee")\
1059+
& lens.fees.modify(lambda x: x + (("test_fee"
1060+
,{"type":("recurFee","QuarterEnd",20)
1061+
,"feeStart":"2021-04-15"})
1062+
,))\
1063+
& lens.waterfall['default'].modify(lambda xs: [["calcAndPayFee","acc01",["test_fee"]]]+xs)
1064+
1065+
test03Fee = test02Fee & lens.name.set("TEST13 - With pct fee")\
1066+
& lens.fees.modify(lambda x: x + (("test_fee"
1067+
,{"type":("pctFee",("bondBalance","A1"),0.01)
1068+
,"feeStart":"2021-04-15"})
1069+
,))
1070+
1071+
test04Fee = test02Fee & lens.name.set("TEST14 - With annual pct fee")\
1072+
& lens.fees.modify(lambda x: x + (("test_fee"
1073+
,{"type":("annualPctFee",("poolBalance","A1"),0.02)
1074+
,"feeStart":"2021-04-15"})
1075+
,))
1076+
1077+
1078+
1079+
test05Fee = test02Fee & lens.name.set("TEST15 - With custom fee")\
1080+
& lens.fees.set((("test_fee"
1081+
,{"type":("customFee",[["2021-08-01",100]
1082+
,["2021-12-20",50]])
1083+
,"feeStart":"2021-04-15"})
1084+
,))
1085+
1086+
test06Fee = test02Fee & lens.name.set("TEST16 - flow by bond period")\
1087+
& lens.fees.set((("test_fee"
1088+
,{"type":("flowByBondPeriod",[ [1,50],[2,80],[3,85] ])
1089+
,"feeStart":"2021-07-26"})
1090+
,))
1091+
1092+
test07Fee = test02Fee & lens.name.set("TEST17 - With count type fee")\
1093+
& lens.fees.set((("test_fee"
1094+
,{"type":("numFee",["DayOfMonth",20],("activeBondNum",), 8)
1095+
,"feeStart":"2021-07-26"})
1096+
,))
1097+
1098+
1099+
test08Fee = test02Fee & lens.name.set("TEST18 - With target fee")\
1100+
& lens.fees.set((("test_fee"
1101+
,{"type":("targetBalanceFee"
1102+
, ("*"
1103+
, ("cumPoolCollection", None, "Interest")
1104+
, 0.03)
1105+
, ("feeTxnAmt",None,"test_fee"))
1106+
,"feeStart":"2021-04-15"})
1107+
,))\
1108+
& lens.waterfall['default'][0].set(["calcAndPayFee","acc01",["test_fee"]])
1109+
1110+
1111+
test09Fee = test02Fee & lens.name.set("TEST19 - With pool period fee")\
1112+
& lens.fees.set((("test_fee"
1113+
,{"type":("byPeriod",15)
1114+
,"feeStart":"2021-04-15"})
1115+
,))
1116+
1117+
test10Fee = test02Fee & lens.name.set("TEST20 - With custom fee")\
1118+
& lens.fees.set((("test_fee"
1119+
,{"type":("byTable"
1120+
,"MonthEnd"
1121+
,("*",("poolBalance",),0.02)
1122+
,[(0,5),(15,10),(30,15)]
1123+
),
1124+
"feeStart":"2021-04-15"
1125+
})
1126+
,))\
1127+
& lens.waterfall['default'][0].set(["payFee","acc01",["test_fee"]])
1128+
1129+
1130+
test11Fee = test01 & lens.name.set("TEST21 - With two fees")\
1131+
& lens.fees.modify(lambda x: x + (("fee1",{"type":{"fixFee":400},"feeStart":"2021-04-15"})
1132+
,("fee2",{"type":{"fixFee":400},"feeStart":"2021-04-15"})))\
1133+
& lens.waterfall['default'].modify(lambda xs: [["calcFee","fee1","fee2"],["payFeeBySeq","acc01",["fee1","fee2"]]]+xs)
1134+
1135+
1136+
test2Bonds = Generic(
1137+
"TEST01- PreClosing-Two Bonds",
1138+
{
1139+
"cutoff": "2021-03-01",
1140+
"closing": "2021-04-15",
1141+
"firstPay": "2021-07-26",
1142+
"payFreq": ["DayOfMonth", 20],
1143+
"poolFreq": "MonthEnd",
1144+
"stated": "2030-01-01",
1145+
},
1146+
{
1147+
"assets": [
1148+
[
1149+
"Mortgage",
1150+
{
1151+
"originBalance": 2200,
1152+
"originRate": ["fix", 0.045],
1153+
"originTerm": 30,
1154+
"freq": "Monthly",
1155+
"type": "Level",
1156+
"originDate": "2021-02-01",
1157+
},
1158+
{
1159+
"currentBalance": 2200,
1160+
"currentRate": 0.08,
1161+
"remainTerm": 30,
1162+
"status": "current",
1163+
},
1164+
]
1165+
]
1166+
},
1167+
(("acc01", {"balance": 0}),),
1168+
(
1169+
(
1170+
"A1",
1171+
{
1172+
"balance": 400,
1173+
"rate": 0.07,
1174+
"originBalance": 400,
1175+
"originRate": 0.07,
1176+
"startDate": "2021-04-15",
1177+
"rateType": {"Fixed": 0.07},
1178+
"bondType": {"Sequential": None},
1179+
},
1180+
),
1181+
(
1182+
"A2",
1183+
{
1184+
"balance": 600,
1185+
"rate": 0.08,
1186+
"originBalance": 600,
1187+
"originRate": 0.08,
1188+
"startDate": "2021-04-15",
1189+
"rateType": {"Fixed": 0.08},
1190+
"bondType": {"Sequential": None},
1191+
},
1192+
),
1193+
(
1194+
"B",
1195+
{
1196+
"balance": 1000,
1197+
"rate": 0.0,
1198+
"originBalance": 1000,
1199+
"originRate": 0.07,
1200+
"startDate": "2021-04-15",
1201+
"rateType": {"Fixed": 0.00},
1202+
"bondType": {"Equity": None},
1203+
},
1204+
),
1205+
),
1206+
(("issuance_fee"
1207+
,{"type":{"fixFee":10}
1208+
,"feeStart":"2021-04-15"})
1209+
,),
1210+
{
1211+
"default": [
1212+
["payFee", "acc01", ["issuance_fee"]],
1213+
["accrueAndPayInt", "acc01", ["A1","A2"]],
1214+
["payPrin", "acc01", ["A1","A2"]],
1215+
["payPrin", "acc01", ["B"]],
1216+
["payIntResidual", "acc01", "B"],
1217+
]
1218+
},
1219+
[
1220+
["CollectedInterest", "acc01"],
1221+
["CollectedPrincipal", "acc01"],
1222+
["CollectedPrepayment", "acc01"],
1223+
["CollectedRecoveries", "acc01"],
1224+
],
1225+
None,
1226+
None,
1227+
None,
1228+
None,
1229+
("PreClosing", "Amortizing"),
1230+
)
1231+
1232+
test2BondsIntBySeq = test2Bonds & lens.name.set("TEST01- PreClosing-Two Bonds")\
1233+
& lens.fees[0][1]['type']['fixFee'].set(305)\
1234+
& lens.waterfall['default'][1][0].set("accrueAndPayIntBySeq")
1235+
1236+
1237+
threeAccounts = test01 & lens.name.set("TEST03- PreClosing- Three Accounts")\
1238+
& lens.accounts.modify(lambda x: x+(('acc02', {'balance': 50}),('acc03', {'balance': 30})))\
1239+
& lens.waterfall['default'].modify(lambda xs: insert_functional(xs, 0, ["transferM"
1240+
,[("acc02",{"formula": ("const",10)})
1241+
,("acc03",{"formula": ("const",10)})]
1242+
,"acc01"]) )

0 commit comments

Comments
 (0)