{ "cells": [ { "cell_type": "markdown", "id": "06126620-2fc4-4c57-a175-9697a2939d47", "metadata": {}, "source": [ "# Project cashflow with a pool of assets with Absbox\n", "\n", "## Quick Start:\n", "\n", "### Step 1: Connect to Engine\n", "\n", "*Again, user can connect to public server as well as his/her own server*\n", "\n", "\n", "It's fairly easy that it just need a one-line command to pull a docker image or download executable from github directly." ] }, { "cell_type": "code", "execution_count": 1, "id": "e3dedb34-e33f-449b-9ebd-b87371613c2f", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Connecting engine server -> https://absbox.org/api/dev\n",
       "
\n" ], "text/plain": [ "Connecting engine server -> \u001b[4;94mhttps://absbox.org/api/dev\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stderr", "output_type": "stream", "text": [ "/home/xiaoyu/repo/AbsBox/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'absbox.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", " warnings.warn(\n" ] }, { "data": { "text/html": [ "
✅Connected, local lib:0.46.5, server:0.50.1\n",
       "
\n" ], "text/plain": [ "✅Connected, local li\u001b[1;92mb:0\u001b[0m.\u001b[1;36m46.5\u001b[0m, server:\u001b[1;36m0.50\u001b[0m.\u001b[1;36m1\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from absbox import API,EnginePath\n", "\n", "localAPI = API(EnginePath.DEV,lang=\"english\",check=False)" ] }, { "cell_type": "markdown", "id": "7c59e611-ed9b-4024-b8ed-48215f3faafa", "metadata": {}, "source": [ "### Step 2: Prepare asset data\n", "\n", "Asset data is just plain Python data structures: `list` and `map`, as long as they are assembled in a correct way. They shall be consumed by `absbox` without question. \n", "\n", "> why using Python's basic type(list/map) ? Because , user may save the data in different storage place , maybe Redis, maybe mongodb, maybe just 1.44MB disk, or just pandas dataframe. The commonality is all data sources with variuos formats are able to be converted into Python's basic strcutres.\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "efdb5176-7613-41a7-9db5-8557cc96eba1", "metadata": {}, "outputs": [], "source": [ "ast1 = [\"Mortgage\"\n", " ,{\"originBalance\": 12000.0\n", " ,\"originRate\": [\"fix\",0.045]\n", " ,\"originTerm\": 120\n", " ,\"freq\": \"monthly\"\n", " ,\"type\": \"level\"\n", " ,\"originDate\": \"2021-02-01\"}\n", " ,{\"currentBalance\": 10000.0\n", " ,\"currentRate\": 0.075\n", " ,\"remainTerm\": 80\n", " ,\"status\": \"current\"}]\n", "\n", "ast2 = [\"Mortgage\"\n", " ,{\"originBalance\": 12000.0\n", " ,\"originRate\": [\"fix\",0.045]\n", " ,\"originTerm\": 120\n", " ,\"freq\": \"monthly\"\n", " ,\"type\": \"level\"\n", " ,\"originDate\": \"2021-02-01\"}\n", " ,{\"currentBalance\": 10000.0\n", " ,\"currentRate\": 0.075\n", " ,\"remainTerm\": 80\n", " ,\"status\": \"current\"}] " ] }, { "cell_type": "markdown", "id": "b883e5b4-9f7f-4efb-bd37-6d7635117f38", "metadata": {}, "source": [ "### Step 3: Construct Pool\n", "\n", "Just build a map with fields:\n", " * `assets` : a list of assets\n", " * `cutoffDate` : all cashflow before this date will be truncated " ] }, { "cell_type": "code", "execution_count": 3, "id": "424f1f5b-e8d8-4b61-b2b2-c822bd6cc948", "metadata": {}, "outputs": [], "source": [ "myPool = {'assets':[ ast1, ast2 ],\n", " 'cutoffDate':\"2022-03-01\"}" ] }, { "cell_type": "markdown", "id": "f5a174db-0879-4552-a41a-2f223e25d313", "metadata": {}, "source": [ "### Step 4: Run with assumption\n", "\n", "#### 4.1 Performance assumption\n", "The pool performance assumption should be setup base on the asset type in the fields `assets`. The argument should be passed into field `poolAssump`.\n", "\n", "> Pls noted that, given a specific asset class, there are multiply way to stress instead of one.\n", "\n", "#### 4.2 Interest rate assumption\n", "In case there are assets with a floater setup , user need to pass interest rate curve assumption to `rateAssump`" ] }, { "cell_type": "code", "execution_count": 4, "id": "ad972689-f090-4910-b357-a29df3e80dac", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/xiaoyu/repo/AbsBox/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'absbox.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", " warnings.warn(\n" ] } ], "source": [ "r = localAPI.runPool(myPool\n", " ,poolAssump=(\"Pool\",(\"Mortgage\",{\"CDR\":0.01},None,None,None)\n", " ,None\n", " ,None)\n", " ,rateAssump=[(\"SOFR6M\", 0.03),(\"SOFR1M\",[[\"2021-01-01\",0.025]\n", " ,[\"2022-08-01\",0.029]])]\n", " ,read=True)" ] }, { "cell_type": "markdown", "id": "e9a3d1dd-f616-442b-8836-6a101e4fb252", "metadata": {}, "source": [ "#### 4.3 Cashflow Result\n", "\n", "if it's a single pool , the default key is `PoolConsol` ( Pool Consolidated ), it's a `Dataframe` if `read` is True" ] }, { "cell_type": "code", "execution_count": 5, "id": "06fa7feb-7901-4baa-bbed-3b14a5d1aee0", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
BalancePrincipalInterestPrepaymentDefaultRecoveryLossWACBorrowerNumPrepayPenaltyCumPrincipalCumPrepayCumDelinqCumDefaultCumRecoveryCumLoss
Date
2024-06-0120000.000.000.0000.0000.000.075NoneNone0.00000.0000.00
2024-07-0119790.20193.30124.88016.50016.500.075NoneNone193.300016.50016.50
2024-08-0119579.00194.32123.58016.88016.880.075NoneNone387.620033.38033.38
2024-09-0119366.92195.38122.26016.70016.700.075NoneNone583.000050.08050.08
2024-10-0119154.50196.44120.94015.98015.980.075NoneNone779.440066.06066.06
\n", "
" ], "text/plain": [ " Balance Principal Interest Prepayment Default Recovery \\\n", "Date \n", "2024-06-01 20000.00 0.00 0.00 0 0.00 0 \n", "2024-07-01 19790.20 193.30 124.88 0 16.50 0 \n", "2024-08-01 19579.00 194.32 123.58 0 16.88 0 \n", "2024-09-01 19366.92 195.38 122.26 0 16.70 0 \n", "2024-10-01 19154.50 196.44 120.94 0 15.98 0 \n", "\n", " Loss WAC BorrowerNum PrepayPenalty CumPrincipal CumPrepay \\\n", "Date \n", "2024-06-01 0.00 0.075 None None 0.00 0 \n", "2024-07-01 16.50 0.075 None None 193.30 0 \n", "2024-08-01 16.88 0.075 None None 387.62 0 \n", "2024-09-01 16.70 0.075 None None 583.00 0 \n", "2024-10-01 15.98 0.075 None None 779.44 0 \n", "\n", " CumDelinq CumDefault CumRecovery CumLoss \n", "Date \n", "2024-06-01 0 0.00 0 0.00 \n", "2024-07-01 0 16.50 0 16.50 \n", "2024-08-01 0 33.38 0 33.38 \n", "2024-09-01 0 50.08 0 50.08 \n", "2024-10-01 0 66.06 0 66.06 " ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "r['PoolConsol']['flow'].head()" ] }, { "cell_type": "markdown", "id": "d08e5c25-0987-4eaa-a983-4af460fd3d9a", "metadata": {}, "source": [ "## Let's rock with unicorn" ] }, { "cell_type": "markdown", "id": "ef50d423-adc0-452a-8865-68a6f53b6ed4", "metadata": {}, "source": [ "### Run with mulitiple scenarios\n", "\n", "Before you write code like this ( a loop just run with different pool performance input ):" ] }, { "cell_type": "code", "execution_count": 6, "id": "0fa388df-54ec-4d98-b327-d453aa65a8f6", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/xiaoyu/repo/AbsBox/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'absbox.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", " warnings.warn(\n", "/home/xiaoyu/repo/AbsBox/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'absbox.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", " warnings.warn(\n", "/home/xiaoyu/repo/AbsBox/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'absbox.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", " warnings.warn(\n" ] } ], "source": [ "scenarioDefaults = [0.01,0.02,0.03]\n", "rs = []\n", "for d in scenarioDefaults:\n", " r = localAPI.runPool(myPool\n", " ,poolAssump=(\"Pool\",(\"Mortgage\",{\"CDR\":d},None,None,None)\n", " ,None\n", " ,None)\n", " ,rateAssump=[(\"SOFR6M\", 0.03),(\"SOFR1M\",[[\"2021-01-01\",0.025]\n", " ,[\"2022-08-01\",0.029]])]\n", " ,read=True)\n", " rs.append(r)" ] }, { "cell_type": "markdown", "id": "59ce495f-a139-4227-ba80-b1068033ac21", "metadata": {}, "source": [ "let try with a new funciton `runPoolByScenarios()` from the api" ] }, { "cell_type": "code", "execution_count": 7, "id": "b97ffa4e-0197-4886-9ac1-e8ba20567bf2", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/xiaoyu/repo/AbsBox/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'absbox.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", " warnings.warn(\n" ] } ], "source": [ "multiScenario = {\n", " \"Stress01\":(\"Pool\",(\"Mortgage\",{\"CDR\":0.01},None,None,None)\n", " ,None\n", " ,None)\n", " ,\"Stress02\":(\"Pool\",(\"Mortgage\",{\"CDR\":0.05},None,None,None)\n", " ,None\n", " ,None)\n", "}\n", "\n", "rs = localAPI.runPoolByScenarios(myPool\n", " ,poolAssump = multiScenario\n", " ,read=True)" ] }, { "cell_type": "markdown", "id": "b115dd30-6857-4d01-810c-55933b41f348", "metadata": {}, "source": [ "Now the `rs` is the map with keys `\"Stress01\",\"Stress02\"`, with values from corresponding performance input." ] }, { "cell_type": "code", "execution_count": 8, "id": "9ebd5ecb-4eab-4be8-8daa-f955d71bee86", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
BalancePrincipalInterestPrepaymentDefaultRecoveryLossWACBorrowerNumPrepayPenaltyCumPrincipalCumPrepayCumDelinqCumDefaultCumRecoveryCumLoss
Date
2024-06-0120000.000.000.0000.0000.000.075NoneNone0.00000.0000.00
2024-07-0119790.20193.30124.88016.50016.500.075NoneNone193.300016.50016.50
2024-08-0119579.00194.32123.58016.88016.880.075NoneNone387.620033.38033.38
2024-09-0119366.92195.38122.26016.70016.700.075NoneNone583.000050.08050.08
2024-10-0119154.50196.44120.94015.98015.980.075NoneNone779.440066.06066.06
\n", "
" ], "text/plain": [ " Balance Principal Interest Prepayment Default Recovery \\\n", "Date \n", "2024-06-01 20000.00 0.00 0.00 0 0.00 0 \n", "2024-07-01 19790.20 193.30 124.88 0 16.50 0 \n", "2024-08-01 19579.00 194.32 123.58 0 16.88 0 \n", "2024-09-01 19366.92 195.38 122.26 0 16.70 0 \n", "2024-10-01 19154.50 196.44 120.94 0 15.98 0 \n", "\n", " Loss WAC BorrowerNum PrepayPenalty CumPrincipal CumPrepay \\\n", "Date \n", "2024-06-01 0.00 0.075 None None 0.00 0 \n", "2024-07-01 16.50 0.075 None None 193.30 0 \n", "2024-08-01 16.88 0.075 None None 387.62 0 \n", "2024-09-01 16.70 0.075 None None 583.00 0 \n", "2024-10-01 15.98 0.075 None None 779.44 0 \n", "\n", " CumDelinq CumDefault CumRecovery CumLoss \n", "Date \n", "2024-06-01 0 0.00 0 0.00 \n", "2024-07-01 0 16.50 0 16.50 \n", "2024-08-01 0 33.38 0 33.38 \n", "2024-09-01 0 50.08 0 50.08 \n", "2024-10-01 0 66.06 0 66.06 " ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rs['Stress01']['PoolConsol']['flow'].head()" ] }, { "cell_type": "markdown", "id": "adfe1543-c583-4a1f-880d-728c973ffb6c", "metadata": {}, "source": [ "#### Make code more concise\n", "\n", "Let's get back to why `absbox` is obsessed with native python structures. Here is the example how to make code more concise and robust.\n", "\n", "Here, we can factor out the data structure `(\"Pool\", (\"Mortgage\"..))` by focus on the essense of performance input `[(\"stress01\",0.01,0.02),(\"stress02\",0.03,0.04)]` " ] }, { "cell_type": "code", "execution_count": 9, "id": "c70fa245-3af5-46cb-bf0a-d50b7fa644dd", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/xiaoyu/repo/AbsBox/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'absbox.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", " warnings.warn(\n" ] } ], "source": [ "perfAssumpPairs = [(\"stress01\",0.01,0.02),(\"stress02\",0.03,0.04)]\n", "\n", "multiScenario = {stressName: (\"Pool\",(\"Mortgage\",{\"CDR\":defaultRate},{\"CPR\":prepayRate},None,None)\n", " ,None\n", " ,None)\n", " for stressName,defaultRate,prepayRate in perfAssumpPairs}\n", "\n", "rs = localAPI.runPoolByScenarios(myPool\n", " ,poolAssump = multiScenario\n", " ,read=True)" ] }, { "cell_type": "markdown", "id": "5905b154-c37f-4c99-90e1-f8a23db15236", "metadata": {}, "source": [ "### Finer assumption on pool\n", "\n", "Given a same asset class, `absbox` provides a mulitple stress type. like `Life default amount`, `Default rate by curve`. `absbox` also extend its stressing capabilities to a finer granularity.\n", "\n", "#### Set Assumption by Index\n", "\n", "Let's rewind a little bit about pool model syntax , the `assets` field accepts a `List` . The question will be asked \" how to specifiy elements in a list ? \" ,`Index` ! Now it will make sense that why there is a `Pool` in the `(\"Pool\",(\"Mortgage\"`, because that will be applied to whole pool instead of assets of the pool.\n", "\n", "Now the first asset `0` will use the `CDR=0%` and second asset will use `CDR=1%`" ] }, { "cell_type": "code", "execution_count": 10, "id": "e5dc59c3-32df-434f-b590-9240d2dc097e", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/xiaoyu/repo/AbsBox/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'absbox.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", " warnings.warn(\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
BalancePrincipalInterestPrepaymentDefaultRecoveryLossWACBorrowerNumPrepayPenaltyCumPrincipalCumPrepayCumDelinqCumDefaultCumRecoveryCumLoss
Date
2024-06-0120000.000.000.0000.0000.000.075NoneNone0.00000.0000.00
2024-07-0119798.38193.37124.9408.2508.250.075NoneNone193.37008.2508.25
2024-08-0119595.45194.49123.6808.4408.440.075NoneNone387.860016.69016.69
2024-09-0119391.47195.63122.4108.3508.350.075NoneNone583.490025.04025.04
2024-10-0119186.71196.77121.1407.9907.990.075NoneNone780.260033.03033.03
\n", "
" ], "text/plain": [ " Balance Principal Interest Prepayment Default Recovery \\\n", "Date \n", "2024-06-01 20000.00 0.00 0.00 0 0.00 0 \n", "2024-07-01 19798.38 193.37 124.94 0 8.25 0 \n", "2024-08-01 19595.45 194.49 123.68 0 8.44 0 \n", "2024-09-01 19391.47 195.63 122.41 0 8.35 0 \n", "2024-10-01 19186.71 196.77 121.14 0 7.99 0 \n", "\n", " Loss WAC BorrowerNum PrepayPenalty CumPrincipal CumPrepay \\\n", "Date \n", "2024-06-01 0.00 0.075 None None 0.00 0 \n", "2024-07-01 8.25 0.075 None None 193.37 0 \n", "2024-08-01 8.44 0.075 None None 387.86 0 \n", "2024-09-01 8.35 0.075 None None 583.49 0 \n", "2024-10-01 7.99 0.075 None None 780.26 0 \n", "\n", " CumDelinq CumDefault CumRecovery CumLoss \n", "Date \n", "2024-06-01 0 0.00 0 0.00 \n", "2024-07-01 0 8.25 0 8.25 \n", "2024-08-01 0 16.69 0 16.69 \n", "2024-09-01 0 25.04 0 25.04 \n", "2024-10-01 0 33.03 0 33.03 " ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "perfByIndex = (\"ByIndex\"\n", " ,([0,],((\"Mortgage\",{\"CDR\":0.0},None,None,None)\n", " ,None\n", " ,None))\n", " ,([1,],((\"Mortgage\",{\"CDR\":0.01},None,None,None)\n", " ,None\n", " ,None))\n", " )\n", "\n", "r = localAPI.runPool(myPool\n", " ,poolAssump=perfByIndex\n", " ,read=True)\n", "\n", "r['PoolConsol']['flow'].head()" ] }, { "cell_type": "markdown", "id": "d9dbde31-15d4-4911-b60e-d06e3d947e1a", "metadata": {}, "source": [ "#### Set Assumption by `Tag`\n", "\n", "But setting assumption is `precise` while a little bit lack-off-flexibility, for example, with the amortizing of the pool, new pool data came in , then the number of assets will be changed, as a result of that , indexing will be off the track.\n", "\n", "Also, the setting assumption base on index doesn't carry much business sense, like judging personality by driver license id, there is no \"correlation\" about that.\n", "\n", "That's why `absbox` supports tagging on assets, then user can applpy pool performancing by `tags`. User can define *ANY* tag on the assets via field `Obligor` tag.\n", "\n", "##### Model asset with `Tag`" ] }, { "cell_type": "code", "execution_count": 11, "id": "3cfa0288-d92a-482f-98eb-ed374b2d233f", "metadata": {}, "outputs": [], "source": [ "ob1 = {\n", " \"id\":\"A1\",\n", " \"tag\":[\"LowRisk\",\"HighPrepay\",\"NewYork\"]\n", "} \n", "\n", "ob2 = {\n", " \"id\":\"A2\",\n", " \"tag\":[\"LowRisk\",\"LowPrepay\",\"LA\"]\n", "} \n", "\n", "ast3 = [\"Mortgage\"\n", " ,{\"originBalance\": 12000.0\n", " ,\"originRate\": [\"fix\",0.045]\n", " ,\"originTerm\": 120\n", " ,\"freq\": \"monthly\"\n", " ,\"type\": \"level\"\n", " ,\"originDate\": \"2021-02-01\"\n", " ,\"obligor\":ob1}\n", " ,{\"currentBalance\": 15000.0\n", " ,\"currentRate\": 0.075\n", " ,\"remainTerm\": 80\n", " ,\"status\": \"current\"}]\n", "\n", "ast4 = [\"Mortgage\"\n", " ,{\"originBalance\": 12000.0\n", " ,\"originRate\": [\"fix\",0.045]\n", " ,\"originTerm\": 120\n", " ,\"freq\": \"monthly\"\n", " ,\"type\": \"even\"\n", " ,\"originDate\": \"2021-02-01\"\n", " ,\"obligor\":ob2}\n", " ,{\"currentBalance\": 5000.0\n", " ,\"currentRate\": 0.075\n", " ,\"remainTerm\": 80\n", " ,\"status\": \"current\"}] " ] }, { "cell_type": "markdown", "id": "756f2777-cb5a-4710-8714-99afb74eae5e", "metadata": {}, "source": [ "#### `Tag` Operation\n", "\n", "There are 5 types of matching rule for `tag`:\n", "\n", "* `TagEq` -> extact match\n", "* `TagSubset` -> Asset's tags are subset or equal to input tag\n", "* `TagSuperset` -> Asset's tags are superset or equal to input tag\n", "* `TagAny`\n", "* `(\"not\", )`\n", "\n", "* `ByDefault` will catch all the asset not matched\n", "* `ById` will match by obligor id ( string equal operation )" ] }, { "cell_type": "code", "execution_count": 12, "id": "81127343-0d7d-4fb2-9eb2-63ff4555f831", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/xiaoyu/repo/AbsBox/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'absbox.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", " warnings.warn(\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
BalancePrincipalInterestPrepaymentDefaultRecoveryLossWACBorrowerNumPrepayPenaltyCumPrincipalCumPrepayCumDelinqCumDefaultCumRecoveryCumLoss
Date
2024-06-0120000.000.000.0000000.075NoneNone0.0000000
2024-07-0119792.42207.58125.0000000.075NoneNone207.5800000
2024-08-0119583.93208.49123.6900000.075NoneNone416.0700000
2024-09-0119374.53209.40122.3900000.075NoneNone625.4700000
2024-10-0119164.21210.32121.0800000.075NoneNone835.7900000
\n", "
" ], "text/plain": [ " Balance Principal Interest Prepayment Default Recovery \\\n", "Date \n", "2024-06-01 20000.00 0.00 0.00 0 0 0 \n", "2024-07-01 19792.42 207.58 125.00 0 0 0 \n", "2024-08-01 19583.93 208.49 123.69 0 0 0 \n", "2024-09-01 19374.53 209.40 122.39 0 0 0 \n", "2024-10-01 19164.21 210.32 121.08 0 0 0 \n", "\n", " Loss WAC BorrowerNum PrepayPenalty CumPrincipal CumPrepay \\\n", "Date \n", "2024-06-01 0 0.075 None None 0.00 0 \n", "2024-07-01 0 0.075 None None 207.58 0 \n", "2024-08-01 0 0.075 None None 416.07 0 \n", "2024-09-01 0 0.075 None None 625.47 0 \n", "2024-10-01 0 0.075 None None 835.79 0 \n", "\n", " CumDelinq CumDefault CumRecovery CumLoss \n", "Date \n", "2024-06-01 0 0 0 0 \n", "2024-07-01 0 0 0 0 \n", "2024-08-01 0 0 0 0 \n", "2024-09-01 0 0 0 0 \n", "2024-10-01 0 0 0 0 " ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ppyAssump = ((\"Mortgage\",None ,{\"CPR\":0.1}, None, None)\n", " ,None\n", " ,None)\n", "defAssump = ((\"Mortgage\",{\"CDR\":0.2} ,None, None, None)\n", " ,None\n", " ,None)\n", "\n", "r = localAPI.runPool(myPool | {\"assets\":[ast3,ast4]}\n", " ,poolAssump=(\"ByObligor\"\n", " #,(\"ById\",[\"A1\"],ppyAssump)\n", " #,(\"ByTag\",[\"LowRisk\",\"LowPrepay\",\"LA\"],\"TagEq\",defAssump)\n", " #,(\"ByTag\",[\"LowRisk\",\"LowPrepay\",\"LA\"],\"TagSubset\",defAssump)\n", " #,(\"ByTag\",[\"LowRisk\",\"LowPrepay\"],\"TagSuperset\",defAssump)\n", " #,(\"ByTag\",[\"LowRisk\"],\"TagAny\",defAssump)\n", " ,(\"ByTag\",[\"LowRisk\"],(\"not\",\"TagAny\"),defAssump)\n", " #,(\"ByDefault\",)\n", " )\n", " ,read=True)\n", "\n", "r['PoolConsol']['flow'].head()" ] }, { "cell_type": "markdown", "id": "fe8dce8c-d419-4ec6-ad42-b7a3192391a7", "metadata": {}, "source": [ "#### Set Assumption By Field Value\n", "\n", "Other than `Tag based` way to set assumption , user can also setup `selector` to apply assumption to asset by testing the numeric field value .\n", "\n", "* `(\"not\" , )` => negate the matching rule\n", "* `(, \"cmp\", , )` => only for numeric field value, hit when asset field value compare with value by cmp operator\n", "* `(, \"range\", , , )` => only for numeric field value, hit when asset field value in the range\n", "\n", "Lets' start with patching extra numeric field value `FICO` and `status`( in string)" ] }, { "cell_type": "code", "execution_count": 13, "id": "ca48d444-5e94-40a5-ade1-825dcd417cbb", "metadata": {}, "outputs": [], "source": [ "ob1 = {\n", " \"id\":\"A1\",\n", " \"tag\":[\"LowRisk\",\"HighPrepay\",\"NewYork\"],\n", " \"fields\":{\"fico\":500,\"status\":\"current\"}\n", "} \n", "\n", "ob2 = {\n", " \"id\":\"A2\",\n", " \"tag\":[\"LowRisk\",\"LowPrepay\",\"LA\"],\n", " \"fields\":{\"fico\":600,\"status\":\"defaulted\"}\n", "}\n", "\n", "ast5 = [\"Mortgage\"\n", " ,{\"originBalance\": 12000.0\n", " ,\"originRate\": [\"fix\",0.045]\n", " ,\"originTerm\": 120\n", " ,\"freq\": \"monthly\"\n", " ,\"type\": \"level\"\n", " ,\"originDate\": \"2021-02-01\"\n", " ,\"obligor\":ob1}\n", " ,{\"currentBalance\": 15000.0\n", " ,\"currentRate\": 0.075\n", " ,\"remainTerm\": 80\n", " ,\"status\": \"current\"}]\n", "\n", "ast6 = [\"Mortgage\"\n", " ,{\"originBalance\": 12000.0\n", " ,\"originRate\": [\"fix\",0.045]\n", " ,\"originTerm\": 120\n", " ,\"freq\": \"monthly\"\n", " ,\"type\": \"even\"\n", " ,\"originDate\": \"2021-02-01\"\n", " ,\"obligor\":ob2}\n", " ,{\"currentBalance\": 5000.0\n", " ,\"currentRate\": 0.075\n", " ,\"remainTerm\": 80\n", " ,\"status\": \"current\"}] " ] }, { "cell_type": "markdown", "id": "bd66d634-92e5-4ea7-9364-b8cbc7090c9c", "metadata": {}, "source": [ "User can always combine the `ByTag` and `ByField` rules together !" ] }, { "cell_type": "code", "execution_count": 14, "id": "3449459d-2adc-433d-969c-cb49aec390d0", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/xiaoyu/repo/AbsBox/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host 'absbox.org'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", " warnings.warn(\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
BalancePrincipalInterestPrepaymentDefaultRecoveryLossWACBorrowerNumPrepayPenaltyCumPrincipalCumPrepayCumDelinqCumDefaultCumRecoveryCumLoss
Date
2024-06-0120000.000.000.000.000.0000.000.075NoneNone0.000.0000.0000.00
2024-07-0119574.62205.19123.62129.3390.86090.860.075NoneNone205.19129.33090.86090.86
2024-08-0119148.78203.65120.94131.1991.00091.000.075NoneNone408.84260.520181.860181.86
2024-09-0118729.74202.13118.32128.7488.17088.170.075NoneNone610.97389.260270.030270.03
2024-10-0118324.10200.71115.77122.2682.67082.670.075NoneNone811.68511.520352.700352.70
\n", "
" ], "text/plain": [ " Balance Principal Interest Prepayment Default Recovery \\\n", "Date \n", "2024-06-01 20000.00 0.00 0.00 0.00 0.00 0 \n", "2024-07-01 19574.62 205.19 123.62 129.33 90.86 0 \n", "2024-08-01 19148.78 203.65 120.94 131.19 91.00 0 \n", "2024-09-01 18729.74 202.13 118.32 128.74 88.17 0 \n", "2024-10-01 18324.10 200.71 115.77 122.26 82.67 0 \n", "\n", " Loss WAC BorrowerNum PrepayPenalty CumPrincipal CumPrepay \\\n", "Date \n", "2024-06-01 0.00 0.075 None None 0.00 0.00 \n", "2024-07-01 90.86 0.075 None None 205.19 129.33 \n", "2024-08-01 91.00 0.075 None None 408.84 260.52 \n", "2024-09-01 88.17 0.075 None None 610.97 389.26 \n", "2024-10-01 82.67 0.075 None None 811.68 511.52 \n", "\n", " CumDelinq CumDefault CumRecovery CumLoss \n", "Date \n", "2024-06-01 0 0.00 0 0.00 \n", "2024-07-01 0 90.86 0 90.86 \n", "2024-08-01 0 181.86 0 181.86 \n", "2024-09-01 0 270.03 0 270.03 \n", "2024-10-01 0 352.70 0 352.70 " ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "r = localAPI.runPool(myPool | {\"assets\":[ast5,ast6]}\n", " ,poolAssump=(\"ByObligor\"\n", " ,(\"ById\",[\"A1\"],ppyAssump)\n", " #,(\"ByTag\",[\"LowRisk\",\"LowPrepay\",\"LA\"],\"TagEq\",defAssump)\n", " #,(\"ByTag\",[\"LowRisk\",\"LowPrepay\",\"LA\"],\"TagSubset\",defAssump)\n", " #,(\"ByTag\",[\"LowRisk\",\"LowPrepay\"],\"TagSuperset\",defAssump)\n", " #,(\"ByTag\",[\"LowRisk\"],\"TagAny\",defAssump)\n", " #,(\"ByField\",[(\"status\",\"in\",[\"defaulted\"])],defAssump)\n", " #,(\"ByField\",[(\"fico\",\"cmp\",\"G\",550),],defAssump)\n", " #,(\"ByField\",[(\"fico\",\"cmp\",\"G\",550),(\"status\",\"in\",[\"defaulted\"])],defAssump)\n", " ,(\"ByField\",[(\"fico\",\"range\",\"IE\",500,600)],defAssump)\n", " #,(\"ByDefault\",)\n", " )\n", " ,read=True)\n", "\n", "r['PoolConsol']['flow'].head()" ] }, { "cell_type": "markdown", "id": "f67f1eb0-1b19-42fd-aa9d-eb965f769afd", "metadata": {}, "source": [ "### Conclusion\n", "\n", "`Absbox` ships extream flexible way for user to apply different guranuality\n", "\n", "* User can set assumption on pool level , which applies to all assets\n", "* set assumption by Index machine friendly but not to human\n", "* set by Tag/Field ,user first model the tag/field value by any field name, the engine will apply pool assumptions with the tag/field matching rule.\n", "\n", "Happy hacking let me know anything I can help with , cheers\n", "\n", "Xiaoyu, xiaoyu@asset-backed.org" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.3" } }, "nbformat": 4, "nbformat_minor": 5 }