Skip to content

Commit

Permalink
remove deps of pyxirr
Browse files Browse the repository at this point in the history
  • Loading branch information
yellowbean committed Mar 6, 2024
1 parent 2b0480a commit 5ecaa57
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 50 deletions.
32 changes: 3 additions & 29 deletions absbox/local/analytics.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pandas as pd
from pyxirr import xirr, xnpv
import finance_calculator as fc
from absbox.local.base import china_bondflow_fields_s, english_bondflow_fields_s
from absbox.validation import vStr
import numpy as np
Expand Down Expand Up @@ -39,7 +40,8 @@ def extract_cash_col(_cols):
dates = [invest_date]+dates
amounts = [invest_amount]+amounts

return xirr(np.array(dates), np.array(amounts))
#return xirr(np.array(dates), np.array(amounts))
return fc.get_xirr([(d, a) for d, a in zip(dates, amounts)])/100


def sum_fields_to_field(df: pd.DataFrame, cols: list, col: str):
Expand All @@ -50,34 +52,6 @@ def sum_fields_to_field(df: pd.DataFrame, cols: list, col: str):
return df.assign(col=df[cols].sum(axis=1))


def npv(_flow: pd.DataFrame, **p):
flow = _flow.copy()
cols = flow.columns.to_list()
idx_name = flow.index.name
init_date, _init_amt = p['init']
init_amt = _init_amt if _init_amt != 0.00 else 0.0001

def _pv(_af):
af = flow[_af]
return xnpv(p['rate']
, [init_date] + flow.index.to_list()
, [-1 * init_amt] + af.to_list())
match (cols, idx_name):
case (china_rental_flow, "日期"):
return _pv("租金")
case (english_rental_flow, "Date"):
return _pv("Rental")
case (english_mortgage_flow_fields, "Date"):
sum_fields_to_field(flow, ["Principal", "Interest", "Prepayment", "Recovery"], "Cash")
return _pv("Cash")
case (china_bondflow_fields, "日期"):
return _pv("本息合计")
case (english_bondflow_fields, "Date"):
return _pv("cash")
case _:
raise RuntimeError("Failed to match", cols, idx_name)


def flow_by_scenario(rs, flowpath, node="col", rtn_df=True, ax=1, rnd=2):
"pull flows from multiple scenario"
r = None
Expand Down
10 changes: 5 additions & 5 deletions absbox/local/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@


# Bond
china_bondflow_fields = ["日期", "余额", "利息", "本金", "执行利率", "本息合计", "备注"]
china_bondflow_fields_s = ["余额", "利息", "本金", "执行利率", "本息合计", "备注"]
china_bond_cashflow = ["本金","利息","本息合计"]
english_bondflow_fields = ["date", "balance", "interest", "principal", "rate", "cash", "memo"]
english_bondflow_fields_s = ["balance", "interest", "principal", "rate", "cash", "memo"]
china_bondflow_fields_s = ["余额", "利息", "本金", "执行利率", "本息合计", "本金系数", "备注"]
china_bondflow_fields = ["日期"] + china_bondflow_fields_s
china_bond_cashflow = ["本金", "利息", "本息合计"]
english_bondflow_fields_s = ["balance", "interest", "principal", "rate", "cash", "factor", "memo"]
english_bondflow_fields = ["date"] + english_bondflow_fields_s

# Cumulative
china_cumStats = ["累计还款", "累计早偿", "累计拖欠", "累计违约", "累计回收", "累计损失"]
Expand Down
9 changes: 7 additions & 2 deletions absbox/local/china.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from urllib.request import unquote
from functools import reduce
from pyspecter import query,S
import toolz as tz

from absbox.local.util import *
from absbox.local.component import *
Expand Down Expand Up @@ -94,8 +95,12 @@ def read(resp):
for k, x in deal_content[comp_name].items():
ir = None
if x[comp_v[0]]:
ir = [_['contents'] for _ in x[comp_v[0]]]
output[comp_name][k] = pd.DataFrame(ir, columns=comp_v[1]).set_index("日期")
ir = list(tz.pluck('contents', x[comp_v[0]]))
if comp_v[0]=='bndStmt' and len(ir[0])==7: #backward compatibility: bond factor
legacy_bond_stmt_col = comp_v[1][:6]+[comp_v[1][-1]]
output[comp_name][k] = pd.DataFrame(ir, columns=legacy_bond_stmt_col).set_index("日期")
else:
output[comp_name][k] = pd.DataFrame(ir, columns=comp_v[1]).set_index("日期")
output[comp_name] = collections.OrderedDict(sorted(output[comp_name].items()))
# aggregate fees
output['fees'] = {f: v.groupby('日期').agg({"余额": "min", "支付": "sum", "剩余支付": "min"})
Expand Down
12 changes: 9 additions & 3 deletions absbox/local/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
from absbox.local.component import *
from absbox.local.base import *
import pandas as pd
import numpy as np
import collections
from absbox.validation import vStr,vDate,vNum,vList,vBool,vFloat,vInt
import toolz as tz


@dataclass
Expand Down Expand Up @@ -87,8 +89,12 @@ def read(resp):
for k, x in deal_content[comp_name].items():
ir = None
if x[comp_v[0]]:
ir = [_['contents'] for _ in x[comp_v[0]]]
output[comp_name][k] = pd.DataFrame(ir, columns=comp_v[1]).set_index("date")
ir = list(tz.pluck('contents', x[comp_v[0]]))
if comp_v[0]=='bndStmt' and len(ir[0])==7: #backward compatibility: bond factor
legacy_bond_stmt_col = comp_v[1][:6]+[comp_v[1][-1]]
output[comp_name][k] = pd.DataFrame(ir, columns=legacy_bond_stmt_col).set_index("date")
else:
output[comp_name][k] = pd.DataFrame(ir, columns=comp_v[1]).set_index("date")
output[comp_name] = collections.OrderedDict(sorted(output[comp_name].items()))
# aggregate fees
output['fees'] = {f: v.groupby('date').agg({"balance": "min", "payment": "sum", "due": "min"})
Expand All @@ -106,7 +112,7 @@ def read(resp):
output['pool']['flow'] = readPoolCf(deal_content['pool']['contents']['futureCf']['contents'])
elif deal_content['pool']['tag']=='MultiPool':
poolMap = deal_content['pool']['contents']
output['pool']['flow'] = {k: readPoolCf(v['futureCf']['contents']) for (k,v) in poolMap.items() }
output['pool']['flow'] = tz.valmap(lambda v: readPoolCf(v['futureCf']['contents']), poolMap)
elif deal_content['pool']['tag']=='ResecDeal':
poolMap = deal_content['pool']['contents']
output['pool']['flow'] = {tz.get([1,2,4],k.split(":")): readPoolCf(v['futureCf']['contents']) for (k,v) in poolMap.items() }
Expand Down
7 changes: 0 additions & 7 deletions absbox/local/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,4 @@ def plot_oc(bsReport: pd.DataFrame, bnds: list):
assert isinstance(bsReport, pd.DataFrame), "input report is not a dataframe"
assert isinstance(bnds, list), "input bond name is not a list"

return None


def plot_bs2(r, **kw):
dealStatusFlag = []
triggerStatusFlag = []

return None
3 changes: 2 additions & 1 deletion absbox/local/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def aggCFby(_df, interval, cols):
return df.groupby([dummy_col])[cols].sum().rename_axis(idx)


def update_deal(d, i, c):
def update_deal(d, i, c): #Deprecated ,to be replace with Lens
"A patch function to update a deal data component list in immuntable way"
_d = d.copy()
_d.pop(i)
Expand Down Expand Up @@ -326,6 +326,7 @@ def _read_asset_pricing(xs, lang) -> pd.DataFrame:


def mergeStrWithDict(s: str, m: dict) -> str:
''' merge a map to a string'''
t = json.loads(s)
return json.dumps(t | m)

Expand Down
6 changes: 3 additions & 3 deletions absbox/tests/benchmark/china/test22.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
,read=True)
aggCFby(p,"M",["租金"]).plot.bar(rot=45,ylabel="租金合计")


from absbox.local.util import npv
npv(p,rate=0.07,init=("2021-01-04",0))
#npv was removed since 0.26.x
#from absbox.local.util import npv
#npv(p,rate=0.07,init=("2021-01-04",0))


deal_data = ["租金类ABS案例"
Expand Down

0 comments on commit 5ecaa57

Please sign in to comment.