From 226ea9156afb82d14f41a687a9a71b5262a698db Mon Sep 17 00:00:00 2001 From: Xiaoyu Date: Wed, 20 Dec 2023 08:22:36 +0800 Subject: [PATCH] expose new permunation --- absbox/deal.py | 30 +++++++++++++++++++++--------- absbox/local/base.py | 17 ++++++++++++++++- absbox/local/component.py | 18 +++++++++++++----- absbox/local/util.py | 35 ++++++++--------------------------- 4 files changed, 58 insertions(+), 42 deletions(-) diff --git a/absbox/deal.py b/absbox/deal.py index fc76445..16e93c0 100644 --- a/absbox/deal.py +++ b/absbox/deal.py @@ -1,11 +1,9 @@ from absbox.local.generic import * from absbox.local.component import * from absbox.local.util import * -from pyspecter import * -import rich from rich.console import Console - - +from lenses import lens +from itertools import permutations, product console = Console() @@ -25,11 +23,6 @@ def mkDeal(x:dict,preCheck=True): , defaultReturn={}).items()) pool = getValWithKs(x, ['pool', "collateral", "资产池"]) - # _assets = [mkAsset(x) for x in getValWithKs(_pool - # , ['assets', 'loans', 'mortgages', "清单", "资产清单"])] - # _asOfDate = getValWithKs(_pool, ["asOfDate", "asofDate", "封包日"]) - # _issuanceStat = readIssuance(_pool) - # _futureCf = mkCf(getValWithKs(_pool,["归集表","现金流归集表","projected","cashflow"],defaultReturn=[])) bonds = list((bn,bo) for bn,bo in getValWithKs(x @@ -82,3 +75,22 @@ def mkDeal(x:dict,preCheck=True): return deal +def mkDealsBy(d, m: dict)->list: + "Input a deal, permunations, lenses ,and return a list of deals with variety" + #return {k: dataclasses.replace(d, **v) for k, v in m.items()} + + +def setDealsBy(d, *receipes: list): + "input a deal object, a list of tweaks( path, values) ,return an updated deals" + for (p, v) in receipes: + d &= p.set(v) + return d + + +def prodDealsBy(d, *receipes) -> dict: + inflated = [[(p, _) for _ in vs] for (p, vs) in receipes] + #print("inflated",inflated) + permus = product(*inflated) + #return permus + #names = [str(_) for _ in permus] + return {str(v): setDealsBy(d, *v) for v in permus} \ No newline at end of file diff --git a/absbox/local/base.py b/absbox/local/base.py index 9ffe0cd..6dd0a84 100644 --- a/absbox/local/base.py +++ b/absbox/local/base.py @@ -1,3 +1,5 @@ +from enum import Enum + # Bond china_bondflow_fields = ["日期", "余额", "利息", "本金", "执行利率", "本息合计", "备注"] @@ -123,4 +125,17 @@ ,'called':"清仓回购" ,'ramp':"RampUp"}} -cfIndexMap = {'cn':"日期",'en':"Date","english":"Date","chinese":"日期"} \ No newline at end of file +cfIndexMap = {'cn':"日期",'en':"Date","english":"Date","chinese":"日期"} + +class DC(Enum): # TODO need to check with HS code + DC_30E_360 = "DC_30E_360" + DC_30Ep_360 = "DC_30Ep_360" + DC_ACT_360 = "DC_ACT_360" + DC_ACT_365A = "DC_ACT_365A" + DC_ACT_365L = "DC_ACT_365L" + DC_NL_365 = "DC_NL_365" + DC_ACT_365F = "DC_ACT_365F" + DC_ACT_ACT = "DC_ACT_ACT" + DC_30_360_ISDA = "DC_30_360_ISDA" + DC_30_360_German = "DC_30_360_German" + DC_30_360_US = "DC_30_360_US" diff --git a/absbox/local/component.py b/absbox/local/component.py index ed526b5..b30e54d 100644 --- a/absbox/local/component.py +++ b/absbox/local/component.py @@ -1,4 +1,4 @@ -from absbox.local.util import mkTag, DC, mkTs, guess_locale, readTagStr, subMap, subMap2, renameKs, ensure100 +from absbox.local.util import mkTag, mkTs, guess_locale, readTagStr, subMap, subMap2, renameKs, ensure100 from absbox.local.util import mapListValBy, uplift_m_list, mapValsBy, allList, getValWithKs, applyFnToKey,flat from absbox.local.util import earlyReturnNone, mkFloatTs, mkRateTs, mkRatioTs, mkTbl, mapNone, guess_pool_flow_header @@ -11,6 +11,7 @@ import pandas as pd from pyspecter import query, S + def mkLiq(x): ''' make pricing method ''' match x: @@ -25,8 +26,9 @@ def mkLiq(x): case _: raise RuntimeError(f"Failed to match {x} in Liquidation Method") + def mkDatePattern(x): - ''' make date pattern ''' + ''' make date pattern, to describle a series of dates''' match x: case ["DayOfMonth", _d] | ["每月", _d] | ("每月", _d): return mkTag(("DayOfMonth", _d)) @@ -51,6 +53,7 @@ def mkDatePattern(x): case _: raise RuntimeError(f"Failed to match {x}") + def getStartDate(x): match x: case {"封包日": a, "起息日": b, "首次兑付日": c, "法定到期日": d, "收款频率": pf, "付款频率": bf} | \ @@ -65,6 +68,7 @@ def getStartDate(x): case _: raise RuntimeError(f"Failed to get Start Date from {x}") + def mkDate(x): ''' make date component for deal ''' match x: @@ -87,12 +91,14 @@ def mkDate(x): case _: raise RuntimeError(f"Failed to match:{x} in Dates") + def mkDsRate(x): if isinstance(x,float): return mkDs(("constant", x)) else: return mkDs(x) + def mkFeeType(x): match x: case {"年化费率": [base, rate]} | {"annualPctFee": [base, rate]}: @@ -135,6 +141,7 @@ def mkDateVector(x): case _: raise RuntimeError(f"not match found: {x}") + def mkPoolSource(x): match x: case "利息" | "Interest" | "利息回款" | "CollectedInterest" : @@ -158,6 +165,7 @@ def mkPoolSource(x): case _ : raise RuntimeError(f"not match found: {x} :make Pool Source") + @functools.lru_cache(maxsize=128) def mkDs(x): "Making Deal Stats" @@ -340,9 +348,11 @@ def mkDs(x): case _: raise RuntimeError(f"Failed to match DS/Formula: {x}") + def mkCurve(tag, xs): return mkTag((tag, xs)) + def mkPre(p): def queryType(y): match y: @@ -519,8 +529,6 @@ def mkBnd(bn, x): , "bndInterestInfo": mkBondRate(bndInterestInfo), "bndType": mkBondType(bndType) , "bndDuePrin": 0, "bndDueInt": dueInt, "bndDueIntDate": lastAccrueDate, "bndStepUp": mSt , "bndLastIntPayDate": lastIntPayDate} - - case _: raise RuntimeError(f"Failed to match bond:{bn},{x}:mkBnd") @@ -790,7 +798,7 @@ def mkMod(y:dict)->tuple: case ["特殊计提利息", (mbal, mrate), bndName] | ["calcIntBy", (mbal, mrate), bndName]: return mkTag(("CalcBondInt", [[bndName] , earlyReturnNone(mkDs, mbal) - , earlyReturnNone(mkDsRate,mrate)])) + , earlyReturnNone(mkDsRate, mrate)])) case ["计提利息", *bndNames] | ["calcInt", *bndNames]: return mkTag(("CalcBondInt", [bndNames, None, None])) case ["计提支付费用", source, target, m] | ["calcAndPayFee", source, target, m]: diff --git a/absbox/local/util.py b/absbox/local/util.py index 23fee42..350e310 100644 --- a/absbox/local/util.py +++ b/absbox/local/util.py @@ -4,10 +4,10 @@ import itertools,re from enum import Enum import numpy as np -import dataclasses from functools import reduce from absbox.local.base import * -from pyspecter import query,S +from pyspecter import query, S +from datetime import datetime import rich from rich.console import Console @@ -133,24 +133,6 @@ def update_deal(d, i, c): return _d -def mkDealsBy(d, m: dict)->dict: - return {k: dataclasses.replace(d, **v) for k, v in m.items()} - - -class DC(Enum): # TODO need to check with HS code - DC_30E_360 = "DC_30E_360" - DC_30Ep_360 = "DC_30Ep_360" - DC_ACT_360 = "DC_ACT_360" - DC_ACT_365A = "DC_ACT_365A" - DC_ACT_365L = "DC_ACT_365L" - DC_NL_365 = "DC_NL_365" - DC_ACT_365F = "DC_ACT_365F" - DC_ACT_ACT = "DC_ACT_ACT" - DC_30_360_ISDA = "DC_30_360_ISDA" - DC_30_360_German = "DC_30_360_German" - DC_30_360_US = "DC_30_360_US" - - def str2date(x: str): return datetime.strptime(x, '%Y-%m-%d').date() @@ -160,7 +142,7 @@ def normDate(x: str): return f"{x[:4]}-{x[4:6]}-{x[6:8]}" -def daysBetween(sd,ed): +def daysBetween(sd, ed): return (ed - sd).days @@ -172,9 +154,9 @@ def guess_locale(x): acc_cols = set(list(accs.values())[0].columns.to_list()) locale = None if acc_cols == set(["余额", "变动额", "备注"]): - locale="cn" + locale = "cn" if acc_cols == set(["balance", "change", "memo"]): - locale="en" + locale = "en" return locale @@ -187,7 +169,7 @@ def guess_pool_locale(x): raise RuntimeError("Failed to match {x} in guess pool locale") -def renameKs(m:dict, mapping, opt_key=False): +def renameKs(m: dict, mapping, opt_key=False): ''' rename keys in a map with from a mapping tuple passed in `opt_key` = True, allow skipping mapping tuple not exist in the map @@ -206,7 +188,6 @@ def subMap(m: dict, ks: list): def subMap2(m: dict, ks: list): - #fieldNames = [ fName for (fName, fTargetName, fDefaultValue) in ks] _m = {k: m.get(k, defaultVal) for (k, _, defaultVal) in ks} _mapping = [_[:2] for _ in ks] return renameKs(_m, _mapping) @@ -237,7 +218,7 @@ def applyFnToKey(m: dict, f, k, applyNone=False): return m -def renameKs2(m:dict, kmapping): +def renameKs2(m: dict, kmapping): ''' Given a map, rename ks from a key-mapping ''' assert isinstance(m, dict), "M is not a map" assert isinstance(kmapping, dict), f"Mapping is not a map: {kmapping}" @@ -246,7 +227,7 @@ def renameKs2(m:dict, kmapping): def ensure100(xs, msg=""): - assert sum(xs)==1.0, f"Doesn't not sum up 100%: {msg}" + assert sum(xs) == 1.0, f"Doesn't not sum up 100%: {msg}" def guess_pool_flow_header(x, l):