Skip to content

Commit

Permalink
expose new permunation
Browse files Browse the repository at this point in the history
  • Loading branch information
yellowbean committed Dec 20, 2023
1 parent 2aac1fb commit 226ea91
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 42 deletions.
30 changes: 21 additions & 9 deletions absbox/deal.py
Original file line number Diff line number Diff line change
@@ -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()

Expand All @@ -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
Expand Down Expand Up @@ -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}
17 changes: 16 additions & 1 deletion absbox/local/base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from enum import Enum


# Bond
china_bondflow_fields = ["日期", "余额", "利息", "本金", "执行利率", "本息合计", "备注"]
Expand Down Expand Up @@ -123,4 +125,17 @@
,'called':"清仓回购"
,'ramp':"RampUp"}}

cfIndexMap = {'cn':"日期",'en':"Date","english":"Date","chinese":"日期"}
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"
18 changes: 13 additions & 5 deletions absbox/local/component.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -11,6 +11,7 @@
import pandas as pd
from pyspecter import query, S


def mkLiq(x):
''' make pricing method '''
match x:
Expand All @@ -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))
Expand All @@ -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} | \
Expand All @@ -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:
Expand All @@ -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]}:
Expand Down Expand Up @@ -135,6 +141,7 @@ def mkDateVector(x):
case _:
raise RuntimeError(f"not match found: {x}")


def mkPoolSource(x):
match x:
case "利息" | "Interest" | "利息回款" | "CollectedInterest" :
Expand All @@ -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"
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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")

Expand Down Expand Up @@ -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]:
Expand Down
35 changes: 8 additions & 27 deletions absbox/local/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()

Expand All @@ -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


Expand All @@ -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


Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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}"
Expand All @@ -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):
Expand Down

1 comment on commit 226ea91

@yellowbean
Copy link
Owner Author

@yellowbean yellowbean commented on 226ea91 Dec 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def setDealsBy(d, *receipes: list, init=lens.Json(), common=None):
    "input a deal object, a list of tweaks( path, values) ,return an updated deal"
    if common:
        receipes = [ (common & _[0],_[1]) for _ in receipes ]
    for (p, v) in receipes:
        d &= (init & p).set(v)
    return d


def prodDealsBy(d, *receipes, **kwargs) -> dict:
    inflated = [[(p, _) for _ in vs] for (p, vs) in receipes]
    permus = product(*inflated)
    return {str(v): setDealsBy(d, *v, **kwargs) for v in permus}

TODO: how to make Keys more readable to user ?

Please sign in to comment.