Skip to content

Commit de75b12

Browse files
committed
commit on new account interest setup
1 parent 8cb9388 commit de75b12

File tree

2 files changed

+80
-78
lines changed

2 files changed

+80
-78
lines changed

absbox/local/component.py

Lines changed: 79 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import logging
1515
import toolz as tz
1616
from lenses import lens
17-
1817
import pandas as pd
1918

2019
numVal = Or(float,int)
@@ -48,7 +47,7 @@ def mkDatePattern(x):
4847
return mkTag(("EveryNMonth", [vDate(d), vInt(n)]))
4948
case ["Weekday", n] if n >= 0 and n <= 6:
5049
return mkTag(("Weekday", vInt(n)))
51-
case ["All", *_dps] | ["AllDatePattern", *_dps] | ["+", *_dps]:
50+
case ["all", *_dps] | ["All", *_dps] | ["AllDatePattern", *_dps] | ["+", *_dps]:
5251
return mkTag(("AllDatePattern", lmap(mkDatePattern, _dps)))
5352
case [">", _d, dp] | ["After", _d, dp] | ["之后", _d, dp]:
5453
return mkTag(("StartsAt", ["Exc", vDate(_d), mkDatePattern(dp) ]))
@@ -450,12 +449,14 @@ def queryType(y):
450449

451450
def mkAccInt(x):
452451
match x:
453-
case {"周期": _dp, "利率": idx, "利差": spd, "最近结息日": lsd} \
454-
| {"period": _dp, "index": idx, "spread": spd, "lastSettleDate": lsd}:
455-
return mkTag(("InvestmentAccount", [idx, spd, lsd, mkDatePattern(_dp)]))
452+
case {"周期": _dp, "重置周期":_dp2, "参考利率": idx, "利差": spd, "最近结息日": lsd,"利率": r} \
453+
| {"period": _dp,"reset": _dp2, "index": idx, "spread": spd, "lastSettleDate": lsd
454+
,"rate": r}:
455+
return mkTag(("InvestmentAccount", [idx, spd, mkDatePattern(_dp)
456+
,mkDatePattern(_dp2), lsd, r]))
456457
case {"周期": _dp, "利率": br, "最近结息日": lsd} \
457458
| {"period": _dp, "rate": br, "lastSettleDate": lsd}:
458-
return mkTag(("BankAccount", [br, lsd, mkDatePattern(_dp)]))
459+
return mkTag(("BankAccount", [br, mkDatePattern(_dp), lsd]))
459460
case None:
460461
return None
461462
case _:
@@ -517,7 +518,10 @@ def mkAcc(an, x=None):
517518
return {"accBalance": vNum(b), "accName": vStr(an), "accType": mkAccType(t), "accInterest": mkAccInt(i), "accStmt": mkAccTxn(tx)}
518519

519520
case {"余额": b} | {"balance": b}:
520-
return mkAcc(vStr(an), x | {"计息": x.get("计息", None), "interest": x.get("interest", None), "记录": x.get("记录", None), "txn": x.get("txn", None), "类型": x.get("类型", None), "type": x.get("type", None)})
521+
extraFields = [ (_, None) for _ in ["计息","interest","类型","type","记录","txn"] ]
522+
extraInfo = subMap(x, extraFields)
523+
# extraInfo = {"计息": x.get("计息", None), "interest": x.get("interest", None), "记录": x.get("记录", None), "txn": x.get("txn", None), "类型": x.get("类型", None), "type": x.get("type", None)}
524+
return mkAcc(vStr(an), x | extraInfo)
521525
case None:
522526
return mkAcc(vStr(an), {"balance": 0})
523527
case _:
@@ -604,6 +608,7 @@ def mkBnd(bn, x:dict):
604608
lastAccrueDate = getValWithKs(x, ["计提日", "lastAccrueDate"])
605609
lastIntPayDate = getValWithKs(x, ["付息日", "lastIntPayDate"])
606610
dueInt = getValWithKs(x, ["应付利息", "dueInt"], defaultReturn=0)
611+
duePrin = getValWithKs(x, ["应付本金", "duePrin"], defaultReturn=0)
607612
dueIntOverInt = getValWithKs(x, ["拖欠利息", "dueIntOverInt"], defaultReturn=0)
608613
mSt = earlyReturnNone(mkStepUp, getValWithKs(x, ["调息", "stepUp"], defaultReturn=None))
609614
match x:
@@ -612,14 +617,14 @@ def mkBnd(bn, x:dict):
612617
return {"bndName": vStr(bn), "bndBalance": bndBalance, "bndRate": bndRate
613618
, "bndOriginInfo": {"originBalance": originBalance, "originDate": originDate, "originRate": originRate} | {"maturityDate": md}
614619
, "bndInterestInfo": mkBondRate(bndInterestInfo), "bndType": mkBondType(bndType)
615-
, "bndDuePrin": 0, "bndDueInt": dueInt, "bndDueIntOverInt":dueIntOverInt, "bndDueIntDate": lastAccrueDate, "bndStepUp": mSt
620+
, "bndDuePrin": duePrin, "bndDueInt": dueInt, "bndDueIntOverInt":dueIntOverInt, "bndDueIntDate": lastAccrueDate, "bndStepUp": mSt
616621
, "bndLastIntPayDate": lastIntPayDate}
617622
case {"初始余额": originBalance, "初始利率": originRate, "起息日": originDate, "利率": bndInterestInfo, "债券类型": bndType} | \
618623
{"originBalance": originBalance, "originRate": originRate, "startDate": originDate, "rateType": bndInterestInfo, "bondType": bndType}:
619624
return {"bndName": vStr(bn), "bndBalance": originBalance, "bndRate": originRate
620625
, "bndOriginInfo": {"originBalance": originBalance, "originDate": originDate, "originRate": originRate} | {"maturityDate": md}
621626
, "bndInterestInfo": mkBondRate(bndInterestInfo), "bndType": mkBondType(bndType)
622-
, "bndDuePrin": 0, "bndDueInt": dueInt, "bndDueIntOverInt":dueIntOverInt, "bndDueIntDate": lastAccrueDate, "bndStepUp": mSt
627+
, "bndDuePrin": duePrin, "bndDueInt": dueInt, "bndDueIntOverInt":dueIntOverInt, "bndDueIntDate": lastAccrueDate, "bndStepUp": mSt
623628
, "bndLastIntPayDate": lastIntPayDate}
624629
case _:
625630
raise RuntimeError(f"Failed to match bond:{bn},{x}:mkBnd")
@@ -643,14 +648,6 @@ def mkLiqMethod(x):
643648
raise RuntimeError(f"Failed to match {x}:mkLiqMethod")
644649

645650

646-
def mkPDA(x):
647-
match x:
648-
case {"公式": ds} | {"formula": ds}:
649-
return mkTag(("DS", mkDs(ds)))
650-
case _:
651-
raise RuntimeError(f"Failed to match {x}:mkPDA")
652-
653-
654651
def mkAccountCapType(x):
655652
match x:
656653
case {"余额百分比": pct} | {"balPct": pct}:
@@ -1106,18 +1103,6 @@ def mkThreshold(x):
11061103
raise RuntimeError(f"Failed to match :{x}:mkThreshold")
11071104

11081105

1109-
def _rateTypeDs(x):
1110-
h = x[0]
1111-
if h in set(["资产池累积违约率"
1112-
, "cumPoolDefaultedRate"
1113-
, "债券系数"
1114-
, "bondFactor"
1115-
, "资产池系数"
1116-
, "poolFactor"]):
1117-
return True
1118-
return False
1119-
1120-
11211106
def mkTrigger(x: dict):
11221107
match x:
11231108
case {"condition":p, "effects":e, "status":st, "curable":c} | {"条件":p, "效果":e, "状态":st, "重置":c}:
@@ -1137,13 +1122,13 @@ def mkTriggerEffect(x):
11371122
return mkTag(("DealStatusTo", mkStatus(s)))
11381123
case ("动作", *actions) | ("actions", *actions):
11391124
return mkTag(("RunActions", lmap(mkAction, actions)))
1140-
case ["计提费用", *fn] | ["accrueFees", *fn]:
1125+
case ["计提费用", *fn] | ["accrueFees", *fn] | ("accrueFees", *fn):
11411126
return mkTag(("DoAccrueFee", vList(fn, str)))
11421127
case ["新增事件", trg] | ["newTrigger", trg]: # not implementd in Hastructure
11431128
return mkTag(("AddTrigger", mkTrigger(trg)))
1144-
case ["新储备目标", accName, newReserve] | ["newReserveBalance", accName, newReserve]:
1129+
case ["新储备目标", accName, newReserve] | ["newReserveBalance", accName, newReserve] | ("newReserveBalance", accName, newReserve):
11451130
return mkTag(("ChangeReserveBalance", [vStr(accName), mkAccType(newReserve)]))
1146-
case ["结果", *efs] | ["Effects", *efs]:
1131+
case ["结果", *efs] | ["Effects", *efs] | ("Effects", *efs):
11471132
return mkTag(("TriggerEffects", [mkTriggerEffect(e) for e in efs]))
11481133
case None:
11491134
return mkTag(("DoNothing"))
@@ -2065,51 +2050,72 @@ def uplift_ds(df:pd.DataFrame) -> pd.DataFrame:
20652050
r['waterfall'] = pd.DataFrame(data = [ [c['contents'][0],readTagMap(c['contents'][1])] for c in waterfall_logs ]
20662051
,columns = ["Date","Waterfall Location"])
20672052

2068-
20692053
# build financial reports
2070-
def mapItem(z):
2071-
match z:
2072-
case {"tag":"Item","contents":[accName,accBal]}:
2073-
return {accName:accBal}
2074-
case {"tag":"ParentItem","contents":[accName,subItems]}:
2075-
items = map(mapItem, subItems)
2076-
return {accName : items}
2077-
2078-
def buildBalanceSheet(bsData):
2079-
bsRptDate = bsData.pop("reportDate")
2080-
bs = mapListValBy(bsData, mapItem)
2081-
return mapValsBy(bs, uplift_m_list) | {"reportDate":bsRptDate}
2082-
2083-
def buildBsType(yname, y:dict)-> pd.DataFrame:
2084-
mi = pd.MultiIndex.from_product([[yname],y.keys()])
2085-
d = y.values()
2086-
return pd.DataFrame(d, index=mi).T
2087-
2088-
def buildBS(bs):
2089-
bs_df = pd.concat([ buildBsType(k,v) for k,v in bs.items() if k!="reportDate"],axis=1)
2090-
bs_df['reportDate'] = bs['reportDate']
2091-
return bs_df.set_index("reportDate")
2092-
2093-
def buildCashReport(cashData):
2094-
sd = cashData.pop('startDate')
2095-
ed = cashData.pop('endDate')
2096-
net = cashData.pop('net')
2097-
cashList = mapListValBy(cashData, mapItem)
2098-
cashMap = {k:uplift_m_list(v) for k,v in cashList.items() }
2099-
cashMap = pd.concat([ buildBsType(k,v) for k,v in cashMap.items() ],axis=1)
2100-
cashMap['startDate'] = sd
2101-
cashMap['endDate'] = ed
2102-
cashMap['Net'] = net
2103-
return cashMap.set_index(["startDate","endDate"])
2104-
2105-
balanceSheetIdx = 2
2106-
cashReportIdx = 3
2054+
def buildTree(x:dict):
2055+
def lookParent(x:dict):
2056+
match x:
2057+
case {'tag': 'ParentItem', 'contents': [itemName, []]}:
2058+
return {itemName:0}
2059+
case {'tag':'ParentItem','contents':[itemName, v]}:
2060+
subBranchNum = len(v)
2061+
subVals_ = [ lookParent(_) for _ in v ]
2062+
subVals = reduce(lambda d1, d2: d1 | d2, subVals_)
2063+
return {itemName: subVals}
2064+
case {'tag':'Item', 'contents':[itemName,itemValue]}:
2065+
return {itemName:itemValue}
2066+
case _:
2067+
return {}
2068+
2069+
match x:
2070+
case {'tag': 'ParentItem', 'contents': [itemName, []]}:
2071+
return {itemName: 0}
2072+
case {'tag':'ParentItem','contents':[itemName, v]}:
2073+
subVals = reduce(lambda d1, d2: d1 | d2, [ lookParent(_) for _ in v ])
2074+
return {itemName: subVals}
2075+
case {'tag':'Item', 'contents':[itemName,itemValue]}:
2076+
return {itemName: itemValue}
2077+
case _:
2078+
return {}
2079+
2080+
def buildBalanceSheet(bsData, rptDate):
2081+
comp = ["asset","liability","equity"]
2082+
x = reduce(lambda d1, d2: d1 | d2, [ bsData[_] & lens.modify(buildTree) for _ in comp ])
2083+
x = pd.json_normalize(x | {"date":rptDate})
2084+
x.columns = pd.MultiIndex.from_tuples([tuple(col.split(".")) for col in x.columns])
2085+
x.set_index("date")
2086+
return x[["Asset","Liability","Net Asset"]]
2087+
2088+
def buildCashReport(cashData, begDate, endDate, missingFields):
2089+
comp = ["inflow","outflow","net"]
2090+
x = reduce(lambda d1, d2: d1 | d2, [ cashData[_] & lens.modify(buildTree) for _ in comp ])
2091+
x = {k: {} if (v==0 and k!="Net Cash") else v for k,v in x.items() }
2092+
x = {k: missingFields[k] | v if k!="Net Cash" else v for k,v in x.items() }
2093+
x = pd.json_normalize(x | {"startDate":begDate,"endDate":endDate})
2094+
x.columns = pd.MultiIndex.from_tuples([tuple(col.split(".")) for col in x.columns])
2095+
x.set_index(["startDate","endDate"])
2096+
return x[["startDate","endDate","Inflow","Outflow","Net Cash"]]
2097+
2098+
def patchMissingField(_rpts):
2099+
comp = ["inflow","outflow","net"]
2100+
x = [ reduce(lambda d1, d2: d1 | d2, [ rpt[_] & lens.modify(buildTree) for _ in comp ])
2101+
for rpt in _rpts ]
2102+
x = [ tz.valmap(lambda v: set(v.keys()) if isinstance(v,dict) else set([]),_) for _ in x ]
2103+
x = tz.merge_with(lambda vs: reduce(lambda x,y: x|y,vs), *x)
2104+
x = tz.valmap(lambda d: {_:0 for _ in d} ,x)
2105+
return x
2106+
2107+
[ beginDateIdx, endDateIdx, balanceSheetIdx, cashReportIdx ] = [ 0, 1, 2, 3 ]
21072108
rpts = [ _['contents'] for _ in (filter_by_tags(x, ["FinancialReport"])) ]
2109+
21082110
if rpts:
21092111
r['report'] = {}
2110-
r['report']['balanceSheet'] = pd.concat([buildBS(buildBalanceSheet(rpt[balanceSheetIdx])) for rpt in rpts])
2111-
r['report']['cash'] = pd.concat([buildCashReport(rpt[cashReportIdx]) for rpt in rpts])[["inflow","outflow","Net"]]
2112-
2112+
r['report']['balanceSheet'] = pd.concat([(buildBalanceSheet(rpt[balanceSheetIdx],rpt[endDateIdx])) for rpt in rpts])
2113+
2114+
cashReportRaw = [ rpt[cashReportIdx] for rpt in rpts ]
2115+
cfAllFieldsMap = tz.dissoc(patchMissingField(cashReportRaw), "Net Cash")
2116+
cfRpts = [buildCashReport(rpt[cashReportIdx],rpt[beginDateIdx],rpt[endDateIdx],cfAllFieldsMap)
2117+
for rpt in rpts]
2118+
r['report']['cash'] = pd.concat(cfRpts)
21132119

21142120
return r
21152121

absbox/local/util.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def readTagStr(x: str) -> str:
4242
x = json.loads(x.replace("'", "\"").replace("True", "true").replace("False", "false").replace("None","null"))
4343
return readTagMap(x)
4444

45+
4546
def readTagMap(x:dict) -> str:
4647
match x:
4748
case {"tag": _t, "contents": None}:
@@ -57,7 +58,6 @@ def readTagMap(x:dict) -> str:
5758
case _ :
5859
return f"<{x}>"
5960

60-
6161
def readTag(x: dict):
6262
return f"<{x['tag']}:{','.join(x['contents'])}>"
6363

@@ -137,10 +137,6 @@ def normDate(x: str):
137137
return f"{x[:4]}-{x[4:6]}-{x[6:8]}"
138138

139139

140-
def daysBetween(sd, ed):
141-
return (ed - sd).days
142-
143-
144140
def guess_locale(x):
145141
''' Guess local from deal map'''
146142
accs = x['accounts']

0 commit comments

Comments
 (0)