Analytics ============== .. autosummary:: :toctree: generate Setup a API ---------------- here is a list of available servers at `absbox.org `_ .. code-block:: python from absbox import API,EnginePath localAPI = API("https://absbox.org/api/latest") # setting default language localAPI = API("https://absbox.org/api/latest",lang='english') # since version 0.26.7 # https://absbox.org/api/dev localAPI = API(EnginePath.DEV,check=False) # https://absbox.org/api/latest localAPI = API(EnginePath.PROD,check=False) # http://localhost:8081 localAPI = API(EnginePath.LOCAL,check=False) .. note:: User can pull the docker image to run his/her own in-house environment .. note:: the remote engine exposes RESTful Service , ``absbox`` send deal models and cashflow projection assumptions to that server. The engine code was hosted at `Hastructure `_ Asset Performance Assumption ---------------------------------------- Assumpitions are required to set when running stressed scenario as well as getting specific outputs other than cashflow. There are two type of assumptions: * Assumptions for performance of asset * Assumptions for running a deal .. graphviz:: :name: sphinx.ext.graphviz :caption: pool assumption :alt: pool assumption :align: center digraph { rankdir = LR "Asset Performance" -> "Current" "Current" -> "Mortgage/Installment/Loan" "Mortgage/Installment/Loan" -> "Prepayment" "Mortgage/Installment/Loan" -> "Default" "Mortgage/Installment/Loan" -> "Recovery" "Receivable" -> "Recovery" [label="Not implemented", "color"="red"] "Receivable" -> "Default" "Current" -> "Lease" "Lease" -> "Rental Increase" "Lease" -> "Rental Renew" "Current" -> "FixedAsset" "Current" -> "Receivable" "FixedAsset" -> "Production Rate" "FixedAsset" -> "Utilization Rate" "Asset Performance" -> "Delinquent" [label="Not implemented","color"="red"] "Asset Performance" -> "Defaulted" "Prepayment" ["color"="green"] "Default" ["color"="green"] "Recovery" ["color"="green"] "Rental Renew" ["color"="green"] "Rental Increase" ["color"="green"] "Production Rate" ["color"="green"] "Utilization Rate" ["color"="green"] } .. seealso:: The assumption of asset start from point of time of `Asset` :ref:`How assumption was applied on asset ?` Mortgage ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Here is sample which used to set ``Pool`` level assumption on ``Mortgage`` asset class. .. code-block:: python r = localAPI.run(deal ,poolAssump = ("Pool",("Mortgage",,,,) , ,) ,runAssump = None ,read=True) .. note:: * ``Pool`` ,means the assumption will be applied to ``all`` the assets in the pool * ``Mortgage`` ,means the it's assumption applied to ``Mortgage`` asset class Performing """""""""""" * default assumption for performing asset * ``{"CDR":0.01}`` means 1% in annualized of current balance will be defaulted at the end of each period * ``{"CDR":[0.01,0.02,0.04]}`` means a vector of CDR will be applied since the asset snapshot date (determined by ``remain terms``) * ``{"CDRPadding":[0.01,0.02,0.04]}`` same with above but the CDR 4% will be applied to rest of periods of the asset * ``{"ByAmount":(2000,[0.25,0.25,0.50])}`` apply a custom default amount vector. * ``{"DefaultAtEndByRate":(0.05,0.10)}``, will apply 5% as CDR for all periods except last period. The last period will use default CDR 10% (which start from begining day). .. versionadded:: 0.42.2 * ``{"ByTerm":[ [vec1],[vec2]...]``, input list of vectors, asset will use vector with same origin term length * prepayment assumption for performing asset * ``{"CPR":0.01}`` means 1% in annualized of current balance will be prepay at the end of each period * ``{"CPR":[0.01,0.02,0.04]}`` means a vector of CPR will be applied since the asset snapshot date (determined by ``remain terms``) * ``{"CPRPadding":[0.01,0.02,0.04]}`` same with above but the CPR 4% will be applied to rest of periods of the asset .. versionadded:: 0.42.2 * ``{"PSA": 1.0}`` 100% of PSA Speed. * ``{"ByTerm":[ [vec1],[vec2]...]``, input list of vectors, asset will use vector with same origin term length * recovery assumption for performing asset * ``{"Rate":0.7,"Lag":18}`` means 70% of current balance will be recovered at 18 periods after defaulted date * ``{"Rate":0.45,"Timing":[0.3,0.3,0.4]}``, recovery rate is 45% of current balance, and the recovery will be distributed to 3 periods after defaulted date with 30%,30% and 40% respectively Non-Performing """""""""""""""""""" * assumption to project cashflow of asset in ``delinquent`` status .. warning:: is not implemented yet ,it only serves as a place holder *reserve for future use* : always use ``None`` * assumption to project cashflow of asset in ``defaulted`` status .. code-block:: python ("Defaulted":[0.5,4,[0.5,0.2,0.3]]) which says: * the recovery percentage is 50% of current balance * the recovery starts at 4 periods after defaulted date * the recovery distribution is 50%,20% and 30% Summary """""""""""""""" .. graphviz:: :name: sphinx.ext.graphviz :caption: mortgage-assumption :alt: mortgage-assumption :align: center digraph { rankdir = LR Mortgage -> Performing Mortgage -> Delinquent Mortgage -> Defaulted Performing -> "Default Assumption" Performing -> "Prepayment Assumption" Performing -> "Recovery Assumption" "Default Assumption" -> "{'CDR':x}" "Default Assumption" -> "{'CDR':[x...]}" "Default Assumption" -> "{'CDRPadding':[x...]}" "Default Assumption" -> "{'ByAmount':(, [x...])}" "Default Assumption" -> "{'DefaultAtEndByRate':(x,y)}" "Default Assumption" -> "{'byTerm':....}" "Prepayment Assumption" -> "{'CPR':x}" "Prepayment Assumption" -> "{'CPR':[x...]}" "Prepayment Assumption" -> "{'CPRPadding':[x...]}" "Prepayment Assumption" -> "{'byTerm':....}" "Recovery Assumption" -> "{'Rate':x,'Lag':y}" "Defaulted" -> "Defaulted Assumption" "Defaulted Assumption" -> "{'Defaulted':[x,y,[z...]]}" } Loan ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python r = localAPI.run(deal ,poolAssump = ("Pool",("Loan",,,,) , ,) ,runAssump = None ,read=True) * Default * : ``{"CDR":<%>}``, can be a vector or constant value * : ``{"CDRPadding":<%>}``, can be a vector or constant value, with last element till end of the asset .. versionadded:: 0.42.2 * ``{"ByTerm":[ [vec1],[vec2]...]``, input list of vectors, asset will use vector with same origin term length * Prepayment * : ``{"CPR":<%>}``, can be a vector or constant value * : ``{"CPRPadding":<%>}``, can be a vector or constant value , with last element till end of the asset .. versionadded:: 0.42.2 * ``{"ByTerm":[ [vec1],[vec2]...]``, input list of vectors, asset will use vector with same origin term length Summary """""""""""""""" .. graphviz:: :name: sphinx.ext.graphviz :caption: loan-assumption :alt: loan-assumption :align: center digraph { rankdir = LR Loan -> Performing Loan -> Delinquent Loan -> Defaulted Performing -> "Default Assumption" Performing -> "Prepayment Assumption" Performing -> "Recovery Assumption" "Prepayment Assumption" -> "{'CPR':x}" "Prepayment Assumption" -> "{'CPR':[x...]}" "Prepayment Assumption" -> "{'CPRPadding':[x...]}" "Prepayment Assumption" -> "{'byTerm':....}" "Default Assumption" -> "{'CDR':x}" "Default Assumption" -> "{'CDR':[x...]}" "Default Assumption" -> "{'CDRPadding':[x...]}" "Default Assumption" -> "{'DefaultAtEndByRate':(x,y)}" "Default Assumption" -> "{'byTerm':....}" "Recovery Assumption" -> "{'Rate':x,'Lag':y}" } Installment ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python r = localAPI.run(deal ,poolAssump = ("Pool",("Installment",,,,) , ,) ,runAssump = None ,read=True) * Default * : ``{"CDR":<%>}`` .. versionadded:: 0.42.2 * ``{"ByTerm":[ [vec1],[vec2]...]``, input list of vectors, asset will use vector with same origin term length * Prepayment * : ``{"CPR":<%>}`` .. versionadded:: 0.42.2 * ``{"ByTerm":[ [vec1],[vec2]...]``, input list of vectors, asset will use vector with same origin term length Summary """""""""""""""" .. graphviz:: :name: sphinx.ext.graphviz :caption: installment-assumption :alt: installment-assumption :align: center digraph { rankdir = LR Installment -> Performing Installment -> Delinquent Installment -> Defaulted Performing -> "Default Assumption" Performing -> "Prepayment Assumption" Performing -> "Recovery Assumption" "Prepayment Assumption" -> "{'CPR':x}" "Prepayment Assumption" -> "{'byTerm':....}" "Default Assumption" -> "{'CDR':x}" "Default Assumption" -> "{'DefaultAtEndByRate':(x,y)}" "Default Assumption" -> "{'byTerm':....}" "Recovery Assumption" -> "{'Rate':x,'Lag':y}" } Receivable ^^^^^^^^^^^^^^^^^^^^^ user can set assumption on receivable asset class: * Default * assume default at last period ( 0 cash received ) * a CDR way ,which is a percentage of current balance remains. .. versionadded:: 0.27.3 * Recovery * aussming a reocvery rate, with a distribution of recoverys by day offsets from defaulted day .. code-block:: python # apply on asset level r = localAPI.run(test01 ,runAssump=[] ,poolAssump = ("ByIndex" ,([0],(("Receivable", {"CDR":0.12}, None, None) ,None,None)) ,([1],(("Receivable", "DefaultAtEnd", None, None) ,None,None)) ) ,read=True) receivableAssump = ("Pool" ,("Receivable", {"CDR":0.01}, None, None) ,None ,None) receivableAssump = ("Pool",("Receivable" ,"DefaultAtEnd" ,{"Rate":0.5,"ByDays":[(10,0.5),(20,0.5)]} ,None) ,None ,None) # apply on pool level r = localAPI.run(test01 ,runAssump=[] ,poolAssump = receivableAssump ,read=True) Summary """""""""""""""" .. graphviz:: :name: sphinx.ext.graphviz :caption: receivable-assumption :alt: receivable-assumption :align: center digraph { rankdir = LR Receivable -> Performing Receivable -> Delinquent Receivable -> Defaulted Performing -> "Default Assumption" Performing -> "Recovery Assumption" "Default Assumption" -> "'DefaultAtEnd'" "Default Assumption" -> "{'CDR':x}" "Recovery Assumption" -> "{'Rate':0.5,'ByDays':[(10,0.5),(20,0.5)]}" } Extra Stress ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Supported Asset Class: * :ref:`Mortgage` * :ref:`Loan` * :ref:`Installment` * :ref:`Receivable` .. versionadded:: 0.29.9 user can specify a time series stress curve on prepay or default curve syntax: ``("StressByCurve",[,])`` * ```` : a list of [date,rate] pairs * ```` : the assumption to apply when the curve is active .. code-block:: python # stress default curve defAssump = {"CDR":0.017} stressCurve = [["2020-10-01",1.0],["2023-03-01",4.0]] stressDef = {"StressByCurve":[stressCurve,defAssump]} p = localAPI.runPool(myPool,poolAssump=("Pool",("Mortgage",stressDef ,None,None,None) ,None ,None) ,read=True) # stress prepay curve ppyAssump = {"CPR":0.017} stressCurve = [["2020-10-01",1.0],["2023-03-01",4.0]] stressPpy = {"StressByCurve":[stressCurve,ppyAssump]} p = localAPI.runPool(myPool,poolAssump=("Pool",("Mortgage",None ,stressPpy,None,None) ,None ,None) ,read=True) Lease ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *Lease* is an asset type that model contractual cash inflow from leasing out equipments or houses. It is differente from other asset type *Loan* or *Mortgage* in the performance assumption. * No-Prepayment There is little economic motivation to prepay the rental in advance * Revovling by default The *Lease* shall base on some physical entity, like the *Room/Hotel* or *CellPhone* , which can be used to generate a series of *Lease contracts* .. code-block:: python r = localAPI.run(deal ,poolAssump = ("Pool",("Lease", ,, , ) , , ) ,runAssump = None ,read=True) Notes: * ```` -> optional, assumption on gap days between new lease and old lease * ```` -> assumption on gap days between new lease and old lease * ```` -> describe the rental increase/decrease over time * ```` * ``("byDate", "2026-09-20")``-> the date when lease projection ends * ``("byExtTimes",1)``-> how many times lease will roll over Lease Rental """""""""""""""" `Rental` is being used to factor in the future rental upside and downside risk. In this example: it assume the market rental rate is dropping by 30% each year. When a new lease contract was created, the new rental rate depends on the two attributes from `last lease`. ``('byAnnualRate', -0.3)`` * `rental rate` from `last lease` * `originate date` from `last lease` syntax: * ``('byAnnualRate', )`` : the rental will increase/decrease by a fixed rate in annual * ``('byRateCurve', )`` : the rental will increase/decrease by a curve, which is a list of [date,rate] pairs * ``('byRateVec', -0.1,-0.3,-0.2)`` : the rental will increase/decrease by a vector of rates, which is applied to each new projected lease. Lease Default """""""""""""""" User can pass it as ``None`` or default assumptions as below: * ``('byContinuation', )`` * ``('byTermination', )`` .. note:: `byContinuate` vs `byTermination` There are two type of asset being leased out, categorized by how default behaviors affecting cashflow. Like, office lending. The default behavior of first lease won't affect second lease. But for phone leasing, once the default happends, the phone will be lost and can't be lease any more. In the phone case, the default of first lease will affect cashflow of leases afterwards. * `byContinuation` : for the phone lease case. * `byTermination` : for the office/hotel room lease case. Lease Gap """""""""""""" `Gap` was to model the blank period between `last lease` and `new lease`. In such period, there isn't any cash flow in. It varies because to model different type of asset. Like, commerial office , on average , has longer gap days than a smart phone. * ``('days', x)`` : the number of days between old lease and new lease * ``('byCurve', c)`` : the number of days between old lease and new lease depends on a curve Lease End """""""""""""" Describle the end type of lease projection,either by a ``Date`` or a ``Extend Time`` * ``("byDate", "2026-09-20")`` : the date when lease projection ends * ``("byExtTimes", 1)`` : how many times lease will roll over for 1 time .. versionadded:: 0.46.1 * ``("earlierOf", "2026-09-20", 3)`` : the lease projection ends at the earlier of the date or extend time * ``("laterOf", "2026-09-20", 3)`` : the lease projection ends at the later of the date or extend time Summary """""""""""""""" .. graphviz:: :name: sphinx.ext.graphviz :caption: lease-assumption :alt: lease-assumption :align: center digraph { rankdir = LR Lease -> Performing Lease -> Delinquent Lease -> Defaulted Performing -> "Lease Gap" Performing -> "Rental Curve" Performing -> "Default Assumption" Performing -> "End Type" "Default Assumption" -> "By Continuation" "By Continuation" -> "('byContinuation', x)" "Default Assumption" -> "By Termination" "By Termination" -> "('byTermination', x)" "Lease Gap" -> "('days', x)" "Lease Gap" -> "('byCurve', curve)" "Rental Curve" -> "('byAnnualRate', x)" "Rental Curve" -> "('byRateCurve', x)" "End Type" -> "end by date" "End Type" -> "end by extend time" } Fixed Asset ^^^^^^^^^^^^^^^^^ syntax ("Fixed",,) .. code-block:: python myAssump = ("Pool" ,("Fixed",[["2022-01-01",0.1]] ,[["2022-01-01",400] ,["2024-09-01",300]]) ,None ,None) p = localAPI.runAsset("2021-04-01" ,assets ,poolAssump=myAssump ,read=True) Summary """""""""" .. graphviz:: :name: sphinx.ext.graphviz :caption: fixedAsset-assumption :alt: fixedAsset-assumption :align: center digraph { rankdir = LR "Fixed Asset" -> Performing "Fixed Asset" -> Delinquent "Fixed Asset" -> Defaulted Performing -> "Utilization Rate" Performing -> "Production Rate" "Utilization Rate" -> "[(d,vs)...]" "Production Rate" -> "[(d,vs)..]" } How to setup assumption for assets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Pool assumption can be applied via multiple ways: * By Pool Level * By Asset Index * By Obligor .. graphviz:: :name: sphinx.ext.graphviz :caption: asset-assumption :alt: asset-assumption :align: center digraph { rankdir = LR "Asset Assumption" -> "Whole Pool" "Asset Assumption" -> "By Pool Name" "Asset Assumption" -> "By Pool Id" "Asset Assumption" -> "By Deal Name" "Whole Pool" -> "('Pool',)" "By Pool Name" -> "('ByName',)" "By Pool Id" -> "('ByPoolId',)" "By Deal Name" -> "('ByDealName',)" "Asset Assumption" -> "By Pool Index" "By Pool Index" -> "('ByIndex',(index,)...)" "Asset Assumption" -> "By Obligor" "By Obligor" -> "obligor Id" "By Obligor" -> "obligor Tag" "By Obligor" -> "obligor Default" "obligor Tag" -> "TagEq" "obligor Tag" -> "TagAny" "obligor Tag" -> "TagSubset" "obligor Tag" -> "TagSuperset" } By Pool Level """""""""""""""""""""""" The assump will be applied to *ALL* assets in the pool .. code-block:: python # For Loan type asset ("Pool",("Loan",,,,) , ,) ("Pool",("Mortgage",,,,) , ,) ("Pool",("Installment",,,,) , ,) # others Asset Level By Index """""""""""""""""""""""" The assumption will be applied to assets by their index position in the pool .. code-block:: python #syntax ("ByIndex" ,([..],(,,)) ,([..],(,,)) ,.... ) i.e .. code-block:: python myAsset1 = ["Mortgage" ,{"originBalance": 12000.0 ,"originRate": ["fix",0.045] ,"originTerm": 120 ,"freq": "monthly" ,"type": "level" ,"originDate": "2021-02-01"} ,{"currentBalance": 10000.0 ,"currentRate": 0.075 ,"remainTerm": 80 ,"status": "current"}] myAsset2 = ["Mortgage" ,{"originBalance": 12000.0 ,"originRate": ["fix",0.045] ,"originTerm": 120 ,"freq": "monthly" ,"type": "level" ,"originDate": "2021-02-01"} ,{"currentBalance": 10000.0 ,"currentRate": 0.075 ,"remainTerm": 80 ,"status": "current"}] myPool = {'assets':[myAsset1,myAsset2], 'cutoffDate':"2022-03-01"} Asset01Assump = (("Mortgage" ,{"CDR":0.01} ,{"CPR":0.1}, None, None) ,None ,None) Asset02Assump = (("Mortgage" ,{"CDR":0.2} ,None, None, None) ,None ,None) AssetLevelAssumption = ("ByIndex" ,([0],Asset01Assump) ,([1],Asset02Assump)) r = localAPI.runPool(myPool ,poolAssump=AssetLevelAssumption ,read=True) # asset cashflow r[0] By Obligor """""""""""""""""""""""" User can apply assumption on assets with specific obligor tags/id with optional default clause. .. versionadded:: 0.29.1 User supply a list of rules to match assets, each rule will match a set of assets and apply the same assumption. `Sequence` is important, earlier rule has higher priority, the assets not match any of above rules will be test agaist the rules next. * By ID: hit when obligor id is in the list * By Tag: * ``TagEq`` hit when asset tags equals to tags in the assumption * ``TagSubset`` hit when asset tags is a subset of the list * ``TagSuperset`` hit when asset tags is a superset of the list * ``TagAny`` hit when asset tags has any intersetion with tags in assumption * ``("not", "")`` hit when negate the above rules * By Default : default asset performance if assets are not hit by any of above rules before .. code-block:: python #syntax ("ByObligor",("ByTag",,,) ,("ById",,) ,("ByDefault",)) .. seealso:: Example :ref:`Apply Assumption with Obligor info` By Obligor Fields """""""""""""""""""""""" .. versionadded:: 0.29.2 Syntax is similar to ``By Obligor``, but the match rule is based on asset fields. Field Match Rule: * ("not", ) : negate the rule * (, "in",