From 9a3881a6a36a0fa27fb10b45c1811a3d7becbc48 Mon Sep 17 00:00:00 2001 From: piper0124 <55830785+piper0124@users.noreply.github.com> Date: Thu, 22 May 2025 10:36:50 +0800 Subject: [PATCH 01/12] Include the computation of interest rate swap duration Calculation of the Macaulay Duration and Modified_Duration in an Interest Rate Swap Based on Bond Math: The Theory Behind the Formulas, Second Edition by Donald J. Smith --- financepy/products/rates/ibor_swap.py | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/financepy/products/rates/ibor_swap.py b/financepy/products/rates/ibor_swap.py index 596d1978..df2799f0 100644 --- a/financepy/products/rates/ibor_swap.py +++ b/financepy/products/rates/ibor_swap.py @@ -392,6 +392,55 @@ def print_payments(self): ########################################################################### + def buyer_side_macaulay_duration(self): + """Calculation of the Payer's Macaulay Duration in an Interest Rate Swap + Based on Bond Math: The Theory Behind the Formulas, Second Edition by + Donald J. Smith + """ + # Number of fixed leg payments + payment_period = len(self.fixed_leg.payments) + + # Get coupon frequency + coupon_frequency = self.fixed_leg.freq_type.value + + # Get swap rate + swap_rate_val = self.swap_rate() + + y = swap_rate_val / coupon_frequency + N = payment_period + c = swap_rate_val / coupon_frequency + md1 = (1 + y) / y + md2 = 1 + y + (N * (c - y)) + md3 = c * ((1 + y) ** N - 1) + y + mac_duration = 1 - (md1 - md2 / md3) + return mac_duration + + def receiver_side_macaulay_duration(self): + """Calculation of the Receiver's Macaulay Duration in an Interest Rate Swap + Based on Bond Math: The Theory Behind the Formulas, Second Edition by + Donald J. Smith + """ + return self.buyer_side_macaulay_duration()*(-1) + + def buyer_side_modified_duration(self): + """Computation of the Modified Duration for the Fixed-Rate + Payer's Perspective in Interest Rate Swap + """ + # Get coupon frequency + coupon_frequency = self.fixed_leg.freq_type.value + + # Get swap rate + swap_rate_val = self.swap_rate() + + return self.buyer_side_macaulay_duration()/(1+swap_rate_val/coupon_frequency) + + def receiver_side_modified_duration(self): + """Computation of the Modified Duration for the Fixed-Rate + Receiver's Perspective in Interest Rate Swap + """ + return self.buyer_side_modified_duration()*(-1) + + def __repr__(self): s = label_to_string("OBJECT TYPE", type(self).__name__) From 1c94087f8c63f95648f3ca8b6286e96beb713f18 Mon Sep 17 00:00:00 2001 From: piper0124 <55830785+piper0124@users.noreply.github.com> Date: Thu, 22 May 2025 10:47:20 +0800 Subject: [PATCH 02/12] Update ibor_swap.py --- financepy/products/rates/ibor_swap.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/financepy/products/rates/ibor_swap.py b/financepy/products/rates/ibor_swap.py index df2799f0..1916190f 100644 --- a/financepy/products/rates/ibor_swap.py +++ b/financepy/products/rates/ibor_swap.py @@ -415,6 +415,8 @@ def buyer_side_macaulay_duration(self): mac_duration = 1 - (md1 - md2 / md3) return mac_duration + ########################################################################### + def receiver_side_macaulay_duration(self): """Calculation of the Receiver's Macaulay Duration in an Interest Rate Swap Based on Bond Math: The Theory Behind the Formulas, Second Edition by @@ -422,6 +424,8 @@ def receiver_side_macaulay_duration(self): """ return self.buyer_side_macaulay_duration()*(-1) + ########################################################################### + def buyer_side_modified_duration(self): """Computation of the Modified Duration for the Fixed-Rate Payer's Perspective in Interest Rate Swap @@ -434,12 +438,15 @@ def buyer_side_modified_duration(self): return self.buyer_side_macaulay_duration()/(1+swap_rate_val/coupon_frequency) + ########################################################################### + def receiver_side_modified_duration(self): """Computation of the Modified Duration for the Fixed-Rate Receiver's Perspective in Interest Rate Swap """ return self.buyer_side_modified_duration()*(-1) + ########################################################################### def __repr__(self): From 8ece84760a789c4a764e9c2aa9caa2571bc0bb71 Mon Sep 17 00:00:00 2001 From: piper0124 <55830785+piper0124@users.noreply.github.com> Date: Fri, 23 May 2025 09:36:54 +0800 Subject: [PATCH 03/12] fix duration method fix duration method --- financepy/products/rates/ibor_swap.py | 28 +- ..._ReplicationgBONDMATHDurationExample.ipynb | 587 ++++++++++++++++++ ...eplicationgBONDMATHDurationExample3M.ipynb | 477 ++++++++++++++ 3 files changed, 1079 insertions(+), 13 deletions(-) create mode 100644 notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb create mode 100644 notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb diff --git a/financepy/products/rates/ibor_swap.py b/financepy/products/rates/ibor_swap.py index 1916190f..8a4f26eb 100644 --- a/financepy/products/rates/ibor_swap.py +++ b/financepy/products/rates/ibor_swap.py @@ -392,22 +392,24 @@ def print_payments(self): ########################################################################### - def buyer_side_macaulay_duration(self): + def payer_side_macaulay_duration(self, value_dt, discount_curve, payment_periods: float): """Calculation of the Payer's Macaulay Duration in an Interest Rate Swap Based on Bond Math: The Theory Behind the Formulas, Second Edition by Donald J. Smith """ - # Number of fixed leg payments - payment_period = len(self.fixed_leg.payments) - # Get coupon frequency coupon_frequency = self.fixed_leg.freq_type.value # Get swap rate - swap_rate_val = self.swap_rate() + swap_rate_val = self.swap_rate(value_dt,discount_curve) y = swap_rate_val / coupon_frequency - N = payment_period + # payment_periods is the number of periods to maturity as of the beginning of the period; + # for instance, If the start date of the interest rate swap is 2024-01-01, + # the end date is 2025-12-31, the valuation date is 2024-03-31, + # and the frequency type is QUARTERLY, + # then the number of payment periods is 8; it is independent of the valuation date. + N = payment_periods c = swap_rate_val / coupon_frequency md1 = (1 + y) / y md2 = 1 + y + (N * (c - y)) @@ -417,16 +419,16 @@ def buyer_side_macaulay_duration(self): ########################################################################### - def receiver_side_macaulay_duration(self): + def receiver_side_macaulay_duration(self, value_dt, discount_curve,payment_periods: float): """Calculation of the Receiver's Macaulay Duration in an Interest Rate Swap Based on Bond Math: The Theory Behind the Formulas, Second Edition by Donald J. Smith """ - return self.buyer_side_macaulay_duration()*(-1) + return self.payer_side_macaulay_duration(value_dt, discount_curve,payment_periods)*(-1) ########################################################################### - def buyer_side_modified_duration(self): + def payer_side_modified_duration(self, value_dt, discount_curve,payment_periods: float): """Computation of the Modified Duration for the Fixed-Rate Payer's Perspective in Interest Rate Swap """ @@ -434,17 +436,17 @@ def buyer_side_modified_duration(self): coupon_frequency = self.fixed_leg.freq_type.value # Get swap rate - swap_rate_val = self.swap_rate() + swap_rate_val = self.swap_rate(value_dt,discount_curve) - return self.buyer_side_macaulay_duration()/(1+swap_rate_val/coupon_frequency) + return self.payer_side_macaulay_duration(value_dt, discount_curve,payment_periods)/(1+swap_rate_val/coupon_frequency) ########################################################################### - def receiver_side_modified_duration(self): + def receiver_side_modified_duration(self, value_dt, discount_curve,payment_periods: float): """Computation of the Modified Duration for the Fixed-Rate Receiver's Perspective in Interest Rate Swap """ - return self.buyer_side_modified_duration()*(-1) + return self.payer_side_modified_duration(value_dt, discount_curve,payment_periods)*(-1) ########################################################################### diff --git a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb new file mode 100644 index 00000000..916ef5a9 --- /dev/null +++ b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb @@ -0,0 +1,587 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "initial_id", + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2025-05-23T01:25:52.331390Z", + "start_time": "2025-05-23T01:25:52.328042Z" + } + }, + "source": [ + "from financepy.utils import *\n", + "from financepy.products.rates import *\n", + "from financepy.market.curves import *\n", + "import datetime as dt\n", + "\n", + "from notebooks.products.rates.FINIBORSWAP_ReplicationgBONDMATHDurationExample3M import payment_period" + ], + "outputs": [], + "execution_count": 22 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:53.315940Z", + "start_time": "2025-05-23T01:25:53.311144Z" + } + }, + "cell_type": "code", + "source": [ + "swap_cal_type = CalendarTypes.TARGET\n", + "bd_type = BusDayAdjustTypes.FOLLOWING\n", + "dg_type = DateGenRuleTypes.BACKWARD\n", + "\n", + "\n", + "fixed_cpn = 0.034\n", + "fixed_freq_type = FrequencyTypes.QUARTERLY\n", + "fixed_dc_type = DayCountTypes.THIRTY_360_BOND\n", + "\n", + "float_spread = 0.0\n", + "float_freq_type = FrequencyTypes.QUARTERLY\n", + "float_dc_type = DayCountTypes.THIRTY_360_BOND\n", + "\n", + "swap_type = SwapTypes.PAY\n", + "notional = 60000000\n", + "\n", + "start_dt = Date(1, 1, 2024)\n", + "maturity_dt = start_dt.add_tenor('2Y')\n", + "\n", + "swap = IborSwap(start_dt,\n", + " maturity_dt,\n", + " swap_type,\n", + " fixed_cpn,\n", + " fixed_freq_type,\n", + " fixed_dc_type,\n", + " notional,\n", + " float_spread,\n", + " float_freq_type,\n", + " float_dc_type,\n", + " swap_cal_type,\n", + " bd_type,\n", + " dg_type)\n" + ], + "id": "1ebf43316efee835", + "outputs": [], + "execution_count": 23 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:55.512229Z", + "start_time": "2025-05-23T01:25:55.509220Z" + } + }, + "cell_type": "code", + "source": [ + "value_date= dt.datetime(2023,12,31)\n", + "value_dt = from_datetime(value_date)\n", + "\n", + "settle_dt = value_dt.add_weekdays(0)\n", + "# settle_dt=value_dt" + ], + "id": "8e46f142f3cba5d7", + "outputs": [], + "execution_count": 24 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:57.156649Z", + "start_time": "2025-05-23T01:25:57.152272Z" + } + }, + "cell_type": "code", + "source": [ + "yield_curve=[i/100 for i in [0.5,1.0407,1.5829,2.1271,2.4506,2.7756,3.1025,3.4316]]\n", + "zero_dts=value_dt.add_years([0.25,0.5,0.75,1.0,1.25,1.5,1.75,2.0])\n", + "\n", + "zero_curve = DiscountCurveZeros(value_dt=value_dt,\n", + " zero_dts=zero_dts,\n", + " zero_rates=yield_curve,\n", + " freq_type=FrequencyTypes.QUARTERLY,\n", + " dc_type=DayCountTypes.THIRTY_360_BOND,\n", + " interp_type=InterpTypes.FINCUBIC_ZERO_RATES\n", + " )" + ], + "id": "290b7a06932da41d", + "outputs": [], + "execution_count": 25 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:58.324864Z", + "start_time": "2025-05-23T01:25:58.320708Z" + } + }, + "cell_type": "code", + "source": "swap.swap_rate(value_dt, zero_curve)", + "id": "7bb1613d5e865838", + "outputs": [ + { + "data": { + "text/plain": [ + "0.03404344249149676" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 26 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:59.653219Z", + "start_time": "2025-05-23T01:25:59.647340Z" + } + }, + "cell_type": "code", + "source": "swap.value(settle_dt, zero_curve)", + "id": "e2fabe76f4bbdf2d", + "outputs": [ + { + "data": { + "text/plain": [ + "5070.7014320408925" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 27 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:33:14.904831Z", + "start_time": "2025-05-23T01:33:14.899446Z" + } + }, + "cell_type": "code", + "source": [ + "payment_periods=8\n", + "swap.payer_side_macaulay_duration(value_dt, zero_curve,payment_periods)" + ], + "id": "3f988f26e6662203", + "outputs": [ + { + "data": { + "text/plain": [ + "-6.767653343606554" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 40 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:27:02.073420Z", + "start_time": "2025-05-23T01:27:02.060189Z" + } + }, + "cell_type": "code", + "source": "swap.receiver_side_macaulay_duration(value_dt, zero_curve,payment_periods)", + "id": "96d568f7a71861fe", + "outputs": [ + { + "ename": "TypeError", + "evalue": "IborSwap.receiver_side_macaulay_duration() takes 3 positional arguments but 4 were given", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[1;32mIn[36], line 1\u001B[0m\n\u001B[1;32m----> 1\u001B[0m \u001B[43mswap\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mreceiver_side_macaulay_duration\u001B[49m\u001B[43m(\u001B[49m\u001B[43mvalue_dt\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mzero_curve\u001B[49m\u001B[43m,\u001B[49m\u001B[43mpayment_periods\u001B[49m\u001B[43m)\u001B[49m\n", + "\u001B[1;31mTypeError\u001B[0m: IborSwap.receiver_side_macaulay_duration() takes 3 positional arguments but 4 were given" + ] + } + ], + "execution_count": 36 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:27:08.799220Z", + "start_time": "2025-05-23T01:27:08.788672Z" + } + }, + "cell_type": "code", + "source": "swap.payer_side_modified_duration(value_dt, zero_curve,payment_periods)", + "id": "ec2187c7336c1bc3", + "outputs": [ + { + "ename": "TypeError", + "evalue": "IborSwap.payer_side_modified_duration() takes 3 positional arguments but 4 were given", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[1;32mIn[37], line 1\u001B[0m\n\u001B[1;32m----> 1\u001B[0m \u001B[43mswap\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mpayer_side_modified_duration\u001B[49m\u001B[43m(\u001B[49m\u001B[43mvalue_dt\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mzero_curve\u001B[49m\u001B[43m,\u001B[49m\u001B[43mpayment_periods\u001B[49m\u001B[43m)\u001B[49m\n", + "\u001B[1;31mTypeError\u001B[0m: IborSwap.payer_side_modified_duration() takes 3 positional arguments but 4 were given" + ] + } + ], + "execution_count": 37 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:27:12.992002Z", + "start_time": "2025-05-23T01:27:12.980577Z" + } + }, + "cell_type": "code", + "source": "swap.receiver_side_modified_duration(value_dt, zero_curve,payment_periods)", + "id": "2c1b845afaf5e18f", + "outputs": [ + { + "ename": "TypeError", + "evalue": "IborSwap.receiver_side_modified_duration() takes 3 positional arguments but 4 were given", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[1;32mIn[38], line 1\u001B[0m\n\u001B[1;32m----> 1\u001B[0m \u001B[43mswap\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mreceiver_side_modified_duration\u001B[49m\u001B[43m(\u001B[49m\u001B[43mvalue_dt\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mzero_curve\u001B[49m\u001B[43m,\u001B[49m\u001B[43mpayment_periods\u001B[49m\u001B[43m)\u001B[49m\n", + "\u001B[1;31mTypeError\u001B[0m: IborSwap.receiver_side_modified_duration() takes 3 positional arguments but 4 were given" + ] + } + ], + "execution_count": 38 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:42.381647100Z", + "start_time": "2025-05-22T06:57:55.610805Z" + } + }, + "cell_type": "code", + "source": "swap.print_payments()", + "id": "2af7c95b07730dda", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "START DATE: 01-JAN-2024\n", + "MATURITY DATE: 02-JAN-2026\n", + "COUPON (%): 3.4000000000000004\n", + "FREQUENCY: FrequencyTypes.QUARTERLY\n", + "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", + "\n", + "PAYMENTS SCHEDULE:\n", + "+---------+-------------+-------------+-------------+------+----------+------+-----------+\n", + "| PAY_NUM | PAY_dt | ACCR_START | ACCR_END | DAYS | YEARFRAC | RATE | PMNT |\n", + "+---------+-------------+-------------+-------------+------+----------+------+-----------+\n", + "| 1 | 02-APR-2024 | 01-JAN-2024 | 02-APR-2024 | 91 | 0.2528 | 3.4 | 515666.67 |\n", + "| 2 | 01-JUL-2024 | 02-APR-2024 | 01-JUL-2024 | 89 | 0.2472 | 3.4 | 504333.33 |\n", + "| 3 | 01-OCT-2024 | 01-JUL-2024 | 01-OCT-2024 | 90 | 0.25 | 3.4 | 510000.0 |\n", + "| 4 | 02-JAN-2025 | 01-OCT-2024 | 02-JAN-2025 | 91 | 0.2528 | 3.4 | 515666.67 |\n", + "| 5 | 01-APR-2025 | 02-JAN-2025 | 01-APR-2025 | 89 | 0.2472 | 3.4 | 504333.33 |\n", + "| 6 | 01-JUL-2025 | 01-APR-2025 | 01-JUL-2025 | 90 | 0.25 | 3.4 | 510000.0 |\n", + "| 7 | 01-OCT-2025 | 01-JUL-2025 | 01-OCT-2025 | 90 | 0.25 | 3.4 | 510000.0 |\n", + "| 8 | 02-JAN-2026 | 01-OCT-2025 | 02-JAN-2026 | 91 | 0.2528 | 3.4 | 515666.67 |\n", + "+---------+-------------+-------------+-------------+------+----------+------+-----------+\n", + "START DATE: 01-JAN-2024\n", + "MATURITY DATE: 02-JAN-2026\n", + "SPREAD (bp): 0.0\n", + "FREQUENCY: FrequencyTypes.QUARTERLY\n", + "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", + "\n", + "PAYMENTS SCHEDULE:\n", + "+---------+-------------+-------------+-------------+------+----------+\n", + "| PAY_NUM | PAY_dt | ACCR_START | ACCR_END | DAYS | YEARFRAC |\n", + "+---------+-------------+-------------+-------------+------+----------+\n", + "| 1 | 02-APR-2024 | 01-JAN-2024 | 02-APR-2024 | 91 | 0.2528 |\n", + "| 2 | 01-JUL-2024 | 02-APR-2024 | 01-JUL-2024 | 89 | 0.2472 |\n", + "| 3 | 01-OCT-2024 | 01-JUL-2024 | 01-OCT-2024 | 90 | 0.25 |\n", + "| 4 | 02-JAN-2025 | 01-OCT-2024 | 02-JAN-2025 | 91 | 0.2528 |\n", + "| 5 | 01-APR-2025 | 02-JAN-2025 | 01-APR-2025 | 89 | 0.2472 |\n", + "| 6 | 01-JUL-2025 | 01-APR-2025 | 01-JUL-2025 | 90 | 0.25 |\n", + "| 7 | 01-OCT-2025 | 01-JUL-2025 | 01-OCT-2025 | 90 | 0.25 |\n", + "| 8 | 02-JAN-2026 | 01-OCT-2025 | 02-JAN-2026 | 91 | 0.2528 |\n", + "+---------+-------------+-------------+-------------+------+----------+\n" + ] + } + ], + "execution_count": 65 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:42.389019500Z", + "start_time": "2025-05-22T07:19:36.402004Z" + } + }, + "cell_type": "code", + "source": "pd.Series(swap.fixed_leg.payment_dts)", + "id": "52df44f5a67bc377", + "outputs": [ + { + "data": { + "text/plain": [ + "0 02-APR-2024\n", + "1 01-JUL-2024\n", + "2 01-OCT-2024\n", + "3 02-JAN-2025\n", + "4 01-APR-2025\n", + "5 01-JUL-2025\n", + "6 01-OCT-2025\n", + "7 02-JAN-2026\n", + "dtype: object" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 70 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "# 3months later", + "id": "39513cfdc5d7ef1" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:42.392700200Z", + "start_time": "2025-05-22T08:57:46.274653Z" + } + }, + "cell_type": "code", + "source": [ + "yield_curve_3m=[i/100 for i in [0.75,1.1853,1.6822,2.1474,2.4371,2.7390,3.0198]]\n", + "zero_dts_3m=value_dt.add_years([0.25,0.5,0.75,1.0,1.25,1.5,1.75])\n", + "\n", + "zero_curve_3m = DiscountCurveZeros(value_dt=value_dt.add_months(3),\n", + " zero_dts=zero_dts_3m,\n", + " zero_rates=yield_curve_3m,\n", + " freq_type=FrequencyTypes.QUARTERLY,\n", + " dc_type=DayCountTypes.THIRTY_360_BOND,\n", + " interp_type=InterpTypes.FINCUBIC_ZERO_RATES\n", + " )\n", + "\n" + ], + "id": "8acc84107dae665e", + "outputs": [], + "execution_count": 93 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:42.392700200Z", + "start_time": "2025-05-22T08:58:16.963888Z" + } + }, + "cell_type": "code", + "source": [ + "fixed_cpn_3m=0.034\n", + "swap_3m = IborSwap(start_dt.add_months(3),\n", + " maturity_dt,\n", + " swap_type,\n", + " fixed_cpn_3m,\n", + " fixed_freq_type,\n", + " fixed_dc_type,\n", + " notional,\n", + " float_spread,\n", + " float_freq_type,\n", + " float_dc_type,\n", + " swap_cal_type,\n", + " bd_type,\n", + " dg_type)\n", + "\n" + ], + "id": "5892081fb637c810", + "outputs": [], + "execution_count": 99 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:42.392700200Z", + "start_time": "2025-05-22T08:58:18.380055Z" + } + }, + "cell_type": "code", + "source": "swap_3m.value(value_dt.add_months(3), zero_curve_3m)", + "id": "3aa3b8731a5cc344", + "outputs": [ + { + "data": { + "text/plain": [ + "-1103746.1176017257" + ] + }, + "execution_count": 100, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 100 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:42.392700200Z", + "start_time": "2025-05-22T08:58:11.381600Z" + } + }, + "cell_type": "code", + "source": "swap_3m.swap_rate(value_dt.add_months(3), zero_curve_3m)", + "id": "5d8fe704908ed541", + "outputs": [ + { + "data": { + "text/plain": [ + "0.023244839642912206" + ] + }, + "execution_count": 98, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 98 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:42.394185900Z", + "start_time": "2025-05-22T08:15:54.437385Z" + } + }, + "cell_type": "code", + "source": "swap_3m.payer_side_macaulay_duration(value_dt.add_months(3), zero_curve_3m)", + "id": "f9c796b4218d3df7", + "outputs": [ + { + "data": { + "text/plain": [ + "-5.879831278293125" + ] + }, + "execution_count": 76, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 76 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:42.394185900Z", + "start_time": "2025-05-22T08:15:55.564419Z" + } + }, + "cell_type": "code", + "source": "start_dt.add_months(3)", + "id": "68a6d3e4cc14ee91", + "outputs": [ + { + "data": { + "text/plain": [ + "01-APR-2024" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 77 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:25:42.394185900Z", + "start_time": "2025-05-22T08:19:10.564739Z" + } + }, + "cell_type": "code", + "source": "swap_3m.print_payments()", + "id": "68097534a5e9ba5e", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "START DATE: 01-APR-2024\n", + "MATURITY DATE: 02-JAN-2026\n", + "COUPON (%): 3.0\n", + "FREQUENCY: FrequencyTypes.QUARTERLY\n", + "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", + "\n", + "PAYMENTS SCHEDULE:\n", + "+---------+-------------+-------------+-------------+------+----------+------+----------+\n", + "| PAY_NUM | PAY_dt | ACCR_START | ACCR_END | DAYS | YEARFRAC | RATE | PMNT |\n", + "+---------+-------------+-------------+-------------+------+----------+------+----------+\n", + "| 1 | 01-JUL-2024 | 01-APR-2024 | 01-JUL-2024 | 90 | 0.25 | 3.0 | 450000.0 |\n", + "| 2 | 01-OCT-2024 | 01-JUL-2024 | 01-OCT-2024 | 90 | 0.25 | 3.0 | 450000.0 |\n", + "| 3 | 02-JAN-2025 | 01-OCT-2024 | 02-JAN-2025 | 91 | 0.2528 | 3.0 | 455000.0 |\n", + "| 4 | 01-APR-2025 | 02-JAN-2025 | 01-APR-2025 | 89 | 0.2472 | 3.0 | 445000.0 |\n", + "| 5 | 01-JUL-2025 | 01-APR-2025 | 01-JUL-2025 | 90 | 0.25 | 3.0 | 450000.0 |\n", + "| 6 | 01-OCT-2025 | 01-JUL-2025 | 01-OCT-2025 | 90 | 0.25 | 3.0 | 450000.0 |\n", + "| 7 | 02-JAN-2026 | 01-OCT-2025 | 02-JAN-2026 | 91 | 0.2528 | 3.0 | 455000.0 |\n", + "+---------+-------------+-------------+-------------+------+----------+------+----------+\n", + "START DATE: 01-APR-2024\n", + "MATURITY DATE: 02-JAN-2026\n", + "SPREAD (bp): 0.0\n", + "FREQUENCY: FrequencyTypes.QUARTERLY\n", + "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", + "\n", + "PAYMENTS SCHEDULE:\n", + "+---------+-------------+-------------+-------------+------+----------+\n", + "| PAY_NUM | PAY_dt | ACCR_START | ACCR_END | DAYS | YEARFRAC |\n", + "+---------+-------------+-------------+-------------+------+----------+\n", + "| 1 | 01-JUL-2024 | 01-APR-2024 | 01-JUL-2024 | 90 | 0.25 |\n", + "| 2 | 01-OCT-2024 | 01-JUL-2024 | 01-OCT-2024 | 90 | 0.25 |\n", + "| 3 | 02-JAN-2025 | 01-OCT-2024 | 02-JAN-2025 | 91 | 0.2528 |\n", + "| 4 | 01-APR-2025 | 02-JAN-2025 | 01-APR-2025 | 89 | 0.2472 |\n", + "| 5 | 01-JUL-2025 | 01-APR-2025 | 01-JUL-2025 | 90 | 0.25 |\n", + "| 6 | 01-OCT-2025 | 01-JUL-2025 | 01-OCT-2025 | 90 | 0.25 |\n", + "| 7 | 02-JAN-2026 | 01-OCT-2025 | 02-JAN-2026 | 91 | 0.2528 |\n", + "+---------+-------------+-------------+-------------+------+----------+\n" + ] + } + ], + "execution_count": 83 + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb new file mode 100644 index 00000000..603c18e5 --- /dev/null +++ b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb @@ -0,0 +1,477 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "initial_id", + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2025-05-23T01:13:12.026880Z", + "start_time": "2025-05-23T01:13:12.023386Z" + } + }, + "source": [ + "from financepy.utils import *\n", + "from financepy.products.rates import *\n", + "from financepy.market.curves import *\n", + "import datetime as dt" + ], + "outputs": [], + "execution_count": 92 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:13:13.020649Z", + "start_time": "2025-05-23T01:13:13.015544Z" + } + }, + "cell_type": "code", + "source": [ + "swap_cal_type = CalendarTypes.TARGET\n", + "bd_type = BusDayAdjustTypes.FOLLOWING\n", + "dg_type = DateGenRuleTypes.BACKWARD\n", + "\n", + "\n", + "fixed_cpn = 0.034\n", + "fixed_freq_type = FrequencyTypes.QUARTERLY\n", + "fixed_dc_type = DayCountTypes.THIRTY_360_BOND\n", + "\n", + "float_spread = 0.0\n", + "float_freq_type = FrequencyTypes.QUARTERLY\n", + "float_dc_type = DayCountTypes.THIRTY_360_BOND\n", + "\n", + "swap_type = SwapTypes.PAY\n", + "notional = 60000000\n", + "\n", + "start_dt = Date(1, 4, 2024)\n", + "maturity_dt = start_dt.add_years(1.75)\n", + "\n", + "swap = IborSwap(start_dt,\n", + " maturity_dt,\n", + " swap_type,\n", + " fixed_cpn,\n", + " fixed_freq_type,\n", + " fixed_dc_type,\n", + " notional,\n", + " float_spread,\n", + " float_freq_type,\n", + " float_dc_type,\n", + " swap_cal_type,\n", + " bd_type,\n", + " dg_type)\n" + ], + "id": "1ebf43316efee835", + "outputs": [], + "execution_count": 93 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:13:15.489337Z", + "start_time": "2025-05-23T01:13:15.485019Z" + } + }, + "cell_type": "code", + "source": [ + "value_date= dt.datetime(2024,3,31)\n", + "value_dt = from_datetime(value_date)\n", + "\n", + "settle_dt = value_dt.add_weekdays(0)\n", + "# settle_dt=value_dt\n", + "settle_dt" + ], + "id": "8e46f142f3cba5d7", + "outputs": [ + { + "data": { + "text/plain": [ + "31-MAR-2024" + ] + }, + "execution_count": 94, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 94 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:13:18.003794Z", + "start_time": "2025-05-23T01:13:17.998703Z" + } + }, + "cell_type": "code", + "source": [ + "# 书中曲线\n", + "# yield_curve=[i/100 for i in [0.75,1.1853,1.6822,2.1474,2.4371,2.7390,3.0198]]\n", + "# zero_dts=value_dt.add_years([0.25,0.5,0.75,1.0,1.25,1.5,1.75])\n", + "\n", + "\n", + "yield_curve=[i/100 for i in [0.75,1.1853,1.6822,2.1474,2.4371,2.7390,3.0198]]\n", + "zero_dts=value_dt.add_years([0.25,0.5,0.75,1.0,1.25,1.5,1.75])\n", + "\n", + "zero_curve = DiscountCurveZeros(value_dt=value_dt,\n", + " zero_dts=zero_dts,\n", + " zero_rates=yield_curve,\n", + " freq_type=FrequencyTypes.QUARTERLY,\n", + " dc_type=DayCountTypes.THIRTY_360_BOND,\n", + " interp_type=InterpTypes.FINCUBIC_ZERO_RATES\n", + " )" + ], + "id": "290b7a06932da41d", + "outputs": [], + "execution_count": 95 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:13:19.311195Z", + "start_time": "2025-05-23T01:13:19.307399Z" + } + }, + "cell_type": "code", + "source": "swap.swap_rate(value_dt, zero_curve)", + "id": "7bb1613d5e865838", + "outputs": [ + { + "data": { + "text/plain": [ + "0.030065619962567392" + ] + }, + "execution_count": 96, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 96 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:13:29.846190Z", + "start_time": "2025-05-23T01:13:29.841877Z" + } + }, + "cell_type": "code", + "source": [ + "# def payer_side_macaulay_duration(value_dt, discount_curve):\n", + "# \"\"\"Calculation of the Payer's Macaulay Duration in an Interest Rate Swap\n", + "# Based on Bond Math: The Theory Behind the Formulas, Second Edition by\n", + "# Donald J. Smith\n", + "# \"\"\"\n", + "# # Number of fixed leg payments\n", + "# payment_period = len(swap.fixed_leg.payments)\n", + "#\n", + "# # Get coupon frequency\n", + "# coupon_frequency = self.fixed_leg.freq_type.value\n", + "#\n", + "# # Get swap rate\n", + "# swap_rate_val = self.swap_rate(value_dt, discount_curve)\n", + "#\n", + "# y = swap_rate_val / coupon_frequency\n", + "# N = payment_period\n", + "# c = swap_rate_val / coupon_frequency\n", + "# md1 = (1 + y) / y\n", + "# md2 = 1 + y + (N * (c - y))\n", + "# md3 = c * ((1 + y) ** N - 1) + y\n", + "# mac_duration = 1 - (md1 - md2 / md3)\n", + "# return mac_duration\n", + "\n", + "\n", + "payment_period = len(swap.fixed_leg.payments) + 1\n", + "coupon_frequency = swap.fixed_leg.freq_type.value\n", + "swap_rate_val = swap.swap_rate(value_dt, zero_curve)\n", + "y = swap_rate_val / coupon_frequency\n", + "N = payment_period\n", + "c = swap_rate_val / coupon_frequency\n", + "md1 = (1 + y) / y\n", + "md2 = 1 + y + (N * (c - y))\n", + "md3 = c * ((1 + y) ** N - 1) + y\n", + "mac_duration = 1 - (md1 - md2 / md3)\n", + "\n", + "delta_mv=-(-mac_duration/4*notional*(-0.004))" + ], + "id": "65a401442f9bf084", + "outputs": [], + "execution_count": 98 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:13:32.846466Z", + "start_time": "2025-05-23T01:13:32.841730Z" + } + }, + "cell_type": "code", + "source": "swap.value(settle_dt, zero_curve)", + "id": "e2fabe76f4bbdf2d", + "outputs": [ + { + "data": { + "text/plain": [ + "-404094.769553849" + ] + }, + "execution_count": 99, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 99 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T01:23:14.567789Z", + "start_time": "2025-05-23T01:23:14.556465Z" + } + }, + "cell_type": "code", + "source": "swap.payer_side_macaulay_duration(value_dt,zero_curve,8)", + "id": "3f988f26e6662203", + "outputs": [ + { + "ename": "TypeError", + "evalue": "IborSwap.payer_side_macaulay_duration() takes 3 positional arguments but 4 were given", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[1;32mIn[107], line 1\u001B[0m\n\u001B[1;32m----> 1\u001B[0m \u001B[43mswap\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mpayer_side_macaulay_duration\u001B[49m\u001B[43m(\u001B[49m\u001B[43mvalue_dt\u001B[49m\u001B[43m,\u001B[49m\u001B[43mzero_curve\u001B[49m\u001B[43m,\u001B[49m\u001B[38;5;241;43m8\u001B[39;49m\u001B[43m)\u001B[49m\n", + "\u001B[1;31mTypeError\u001B[0m: IborSwap.payer_side_macaulay_duration() takes 3 positional arguments but 4 were given" + ] + } + ], + "execution_count": 107 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T00:45:15.232484600Z", + "start_time": "2025-05-23T00:39:29.952636Z" + } + }, + "cell_type": "code", + "source": "swap.receiver_side_macaulay_duration(value_dt, zero_curve)", + "id": "96d568f7a71861fe", + "outputs": [ + { + "data": { + "text/plain": [ + "5.84526657803498" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 55 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T00:45:15.232484600Z", + "start_time": "2025-05-23T00:39:30.019485Z" + } + }, + "cell_type": "code", + "source": "swap.payer_side_modified_duration(value_dt, zero_curve)", + "id": "ec2187c7336c1bc3", + "outputs": [ + { + "data": { + "text/plain": [ + "-5.801658959676467" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 56 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T00:45:15.238495100Z", + "start_time": "2025-05-23T00:39:30.068245Z" + } + }, + "cell_type": "code", + "source": "swap.receiver_side_modified_duration(value_dt, zero_curve)", + "id": "2c1b845afaf5e18f", + "outputs": [ + { + "data": { + "text/plain": [ + "5.801658959676467" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 57 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T00:45:15.239505100Z", + "start_time": "2025-05-23T00:39:30.125510Z" + } + }, + "cell_type": "code", + "source": "swap.print_payments()", + "id": "2af7c95b07730dda", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "START DATE: 01-APR-2024\n", + "MATURITY DATE: 02-JAN-2026\n", + "COUPON (%): 3.4000000000000004\n", + "FREQUENCY: FrequencyTypes.QUARTERLY\n", + "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", + "\n", + "PAYMENTS SCHEDULE:\n", + "+---------+-------------+-------------+-------------+------+----------+------+-----------+\n", + "| PAY_NUM | PAY_dt | ACCR_START | ACCR_END | DAYS | YEARFRAC | RATE | PMNT |\n", + "+---------+-------------+-------------+-------------+------+----------+------+-----------+\n", + "| 1 | 01-JUL-2024 | 01-APR-2024 | 01-JUL-2024 | 90 | 0.25 | 3.4 | 510000.0 |\n", + "| 2 | 01-OCT-2024 | 01-JUL-2024 | 01-OCT-2024 | 90 | 0.25 | 3.4 | 510000.0 |\n", + "| 3 | 02-JAN-2025 | 01-OCT-2024 | 02-JAN-2025 | 91 | 0.2528 | 3.4 | 515666.67 |\n", + "| 4 | 01-APR-2025 | 02-JAN-2025 | 01-APR-2025 | 89 | 0.2472 | 3.4 | 504333.33 |\n", + "| 5 | 01-JUL-2025 | 01-APR-2025 | 01-JUL-2025 | 90 | 0.25 | 3.4 | 510000.0 |\n", + "| 6 | 01-OCT-2025 | 01-JUL-2025 | 01-OCT-2025 | 90 | 0.25 | 3.4 | 510000.0 |\n", + "| 7 | 02-JAN-2026 | 01-OCT-2025 | 02-JAN-2026 | 91 | 0.2528 | 3.4 | 515666.67 |\n", + "+---------+-------------+-------------+-------------+------+----------+------+-----------+\n", + "START DATE: 01-APR-2024\n", + "MATURITY DATE: 02-JAN-2026\n", + "SPREAD (bp): 0.0\n", + "FREQUENCY: FrequencyTypes.QUARTERLY\n", + "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", + "\n", + "PAYMENTS SCHEDULE:\n", + "+---------+-------------+-------------+-------------+------+----------+\n", + "| PAY_NUM | PAY_dt | ACCR_START | ACCR_END | DAYS | YEARFRAC |\n", + "+---------+-------------+-------------+-------------+------+----------+\n", + "| 1 | 01-JUL-2024 | 01-APR-2024 | 01-JUL-2024 | 90 | 0.25 |\n", + "| 2 | 01-OCT-2024 | 01-JUL-2024 | 01-OCT-2024 | 90 | 0.25 |\n", + "| 3 | 02-JAN-2025 | 01-OCT-2024 | 02-JAN-2025 | 91 | 0.2528 |\n", + "| 4 | 01-APR-2025 | 02-JAN-2025 | 01-APR-2025 | 89 | 0.2472 |\n", + "| 5 | 01-JUL-2025 | 01-APR-2025 | 01-JUL-2025 | 90 | 0.25 |\n", + "| 6 | 01-OCT-2025 | 01-JUL-2025 | 01-OCT-2025 | 90 | 0.25 |\n", + "| 7 | 02-JAN-2026 | 01-OCT-2025 | 02-JAN-2026 | 91 | 0.2528 |\n", + "+---------+-------------+-------------+-------------+------+----------+\n" + ] + } + ], + "execution_count": 58 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T00:45:15.239505100Z", + "start_time": "2025-05-23T00:39:30.159010Z" + } + }, + "cell_type": "code", + "source": "swap.print_float_leg_pv()", + "id": "1c960526153c2223", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "START DATE: 01-APR-2024\n", + "MATURITY DATE: 02-JAN-2026\n", + "SPREAD (BPS): 0.0\n", + "FREQUENCY: FrequencyTypes.QUARTERLY\n", + "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", + "\n", + "PAYMENTS VALUATION:\n", + "+---------+-------------+----------+--------+-----------+--------+-----------+------------+\n", + "| PAY_NUM | PAY_dt | NOTIONAL | IBOR | PMNT | DF | PV | CUM_PV |\n", + "+---------+-------------+----------+--------+-----------+--------+-----------+------------+\n", + "| 1 | 01-JUL-2024 | 60000000 | 0.7529 | 112935.42 | 0.9981 | 112722.26 | 112722.26 |\n", + "| 2 | 01-OCT-2024 | 60000000 | 1.6379 | 245686.53 | 0.994 | 244222.77 | 356945.03 |\n", + "| 3 | 02-JAN-2025 | 60000000 | 2.7131 | 411480.71 | 0.9873 | 406243.16 | 763188.19 |\n", + "| 4 | 01-APR-2025 | 60000000 | 3.5062 | 520087.02 | 0.9788 | 509054.52 | 1272242.71 |\n", + "| 5 | 01-JUL-2025 | 60000000 | 3.588 | 538192.75 | 0.9701 | 522093.07 | 1794335.78 |\n", + "| 6 | 01-OCT-2025 | 60000000 | 4.293 | 643955.16 | 0.9598 | 618058.3 | 2412394.08 |\n", + "| 7 | 02-JAN-2026 | 60000000 | 4.6963 | 712268.76 | 0.9485 | 675604.45 | 3087998.53 |\n", + "+---------+-------------+----------+--------+-----------+--------+-----------+------------+\n" + ] + } + ], + "execution_count": 59 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T00:45:15.240494900Z", + "start_time": "2025-05-23T00:39:30.197673Z" + } + }, + "cell_type": "code", + "source": "swap.print_fixed_leg_pv()", + "id": "5037651021a91d38", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "START DATE: 01-APR-2024\n", + "MATURITY DATE: 02-JAN-2026\n", + "COUPON (%): 3.4000000000000004\n", + "FREQUENCY: FrequencyTypes.QUARTERLY\n", + "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", + "\n", + "PAYMENTS VALUATION:\n", + "+---------+-------------+----------+------+-----------+--------+-----------+------------+\n", + "| PAY_NUM | PAY_dt | NOTIONAL | RATE | PMNT | DF | PV | CUM_PV |\n", + "+---------+-------------+----------+------+-----------+--------+-----------+------------+\n", + "| 1 | 01-JUL-2024 | 60000000 | 3.4 | 510000.0 | 0.9981 | 509037.4 | 509037.4 |\n", + "| 2 | 01-OCT-2024 | 60000000 | 3.4 | 510000.0 | 0.994 | 506961.51 | 1015998.91 |\n", + "| 3 | 02-JAN-2025 | 60000000 | 3.4 | 515666.67 | 0.9873 | 509102.98 | 1525101.88 |\n", + "| 4 | 01-APR-2025 | 60000000 | 3.4 | 504333.33 | 0.9788 | 493635.02 | 2018736.9 |\n", + "| 5 | 01-JUL-2025 | 60000000 | 3.4 | 510000.0 | 0.9701 | 494743.69 | 2513480.58 |\n", + "| 6 | 01-OCT-2025 | 60000000 | 3.4 | 510000.0 | 0.9598 | 489490.19 | 3002970.77 |\n", + "| 7 | 02-JAN-2026 | 60000000 | 3.4 | 515666.67 | 0.9485 | 489122.52 | 3492093.3 |\n", + "+---------+-------------+----------+------+-----------+--------+-----------+------------+\n" + ] + } + ], + "execution_count": 60 + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From b6dd01e13422eb1dec037453c8dfd58c45d9ef46 Mon Sep 17 00:00:00 2001 From: piper0124 <55830785+piper0124@users.noreply.github.com> Date: Fri, 23 May 2025 10:27:10 +0800 Subject: [PATCH 04/12] Using Modified Duration to Estimate Gains or Losses --- financepy/products/rates/ibor_swap.py | 12 ++ ..._ReplicationgBONDMATHDurationExample.ipynb | 134 ++++++++-------- ...eplicationgBONDMATHDurationExample3M.ipynb | 150 ++++++++++-------- 3 files changed, 164 insertions(+), 132 deletions(-) diff --git a/financepy/products/rates/ibor_swap.py b/financepy/products/rates/ibor_swap.py index 8a4f26eb..ffcf2b01 100644 --- a/financepy/products/rates/ibor_swap.py +++ b/financepy/products/rates/ibor_swap.py @@ -450,6 +450,18 @@ def receiver_side_modified_duration(self, value_dt, discount_curve,payment_perio ########################################################################### + def payer_side_profits(self, + value_dt, + discount_curve,payment_periods: float, + swap_rate_changes: float ): + """Computation of the Profits for the Fixed-Rate Payer's Perspective in Interest Rate Swap + """ + # Get coupon frequency + coupon_frequency = self.fixed_leg.freq_type.value + annualized_modi_dur = self.payer_side_modified_duration(value_dt, discount_curve,payment_periods)/coupon_frequency + return (-1)*(annualized_modi_dur*self.fixed_leg.notional*swap_rate_changes) + + def __repr__(self): s = label_to_string("OBJECT TYPE", type(self).__name__) diff --git a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb index 916ef5a9..724c6aa6 100644 --- a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb +++ b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-05-23T01:25:52.331390Z", - "start_time": "2025-05-23T01:25:52.328042Z" + "end_time": "2025-05-23T01:37:56.175867Z", + "start_time": "2025-05-23T01:37:56.171771Z" } }, "source": [ @@ -15,17 +15,16 @@ "from financepy.products.rates import *\n", "from financepy.market.curves import *\n", "import datetime as dt\n", - "\n", - "from notebooks.products.rates.FINIBORSWAP_ReplicationgBONDMATHDurationExample3M import payment_period" + "\n" ], "outputs": [], - "execution_count": 22 + "execution_count": 2 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:53.315940Z", - "start_time": "2025-05-23T01:25:53.311144Z" + "end_time": "2025-05-23T01:37:57.664887Z", + "start_time": "2025-05-23T01:37:57.637136Z" } }, "cell_type": "code", @@ -65,13 +64,13 @@ ], "id": "1ebf43316efee835", "outputs": [], - "execution_count": 23 + "execution_count": 3 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:55.512229Z", - "start_time": "2025-05-23T01:25:55.509220Z" + "end_time": "2025-05-23T01:38:00.243014Z", + "start_time": "2025-05-23T01:38:00.238262Z" } }, "cell_type": "code", @@ -84,13 +83,13 @@ ], "id": "8e46f142f3cba5d7", "outputs": [], - "execution_count": 24 + "execution_count": 4 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:57.156649Z", - "start_time": "2025-05-23T01:25:57.152272Z" + "end_time": "2025-05-23T01:38:01.679542Z", + "start_time": "2025-05-23T01:38:01.673799Z" } }, "cell_type": "code", @@ -108,13 +107,13 @@ ], "id": "290b7a06932da41d", "outputs": [], - "execution_count": 25 + "execution_count": 5 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:58.324864Z", - "start_time": "2025-05-23T01:25:58.320708Z" + "end_time": "2025-05-23T01:38:03.960837Z", + "start_time": "2025-05-23T01:38:03.952526Z" } }, "cell_type": "code", @@ -127,18 +126,18 @@ "0.03404344249149676" ] }, - "execution_count": 26, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 26 + "execution_count": 6 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:59.653219Z", - "start_time": "2025-05-23T01:25:59.647340Z" + "end_time": "2025-05-23T01:38:05.697242Z", + "start_time": "2025-05-23T01:38:05.690197Z" } }, "cell_type": "code", @@ -151,18 +150,18 @@ "5070.7014320408925" ] }, - "execution_count": 27, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 27 + "execution_count": 7 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:33:14.904831Z", - "start_time": "2025-05-23T01:33:14.899446Z" + "end_time": "2025-05-23T01:38:07.096647Z", + "start_time": "2025-05-23T01:38:07.090478Z" } }, "cell_type": "code", @@ -178,18 +177,18 @@ "-6.767653343606554" ] }, - "execution_count": 40, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 40 + "execution_count": 8 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:27:02.073420Z", - "start_time": "2025-05-23T01:27:02.060189Z" + "end_time": "2025-05-23T01:38:09.247259Z", + "start_time": "2025-05-23T01:38:09.242767Z" } }, "cell_type": "code", @@ -197,24 +196,23 @@ "id": "96d568f7a71861fe", "outputs": [ { - "ename": "TypeError", - "evalue": "IborSwap.receiver_side_macaulay_duration() takes 3 positional arguments but 4 were given", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[1;32mIn[36], line 1\u001B[0m\n\u001B[1;32m----> 1\u001B[0m \u001B[43mswap\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mreceiver_side_macaulay_duration\u001B[49m\u001B[43m(\u001B[49m\u001B[43mvalue_dt\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mzero_curve\u001B[49m\u001B[43m,\u001B[49m\u001B[43mpayment_periods\u001B[49m\u001B[43m)\u001B[49m\n", - "\u001B[1;31mTypeError\u001B[0m: IborSwap.receiver_side_macaulay_duration() takes 3 positional arguments but 4 were given" - ] + "data": { + "text/plain": [ + "6.767653343606554" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" } ], - "execution_count": 36 + "execution_count": 9 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:27:08.799220Z", - "start_time": "2025-05-23T01:27:08.788672Z" + "end_time": "2025-05-23T01:38:11.105175Z", + "start_time": "2025-05-23T01:38:11.098720Z" } }, "cell_type": "code", @@ -222,24 +220,23 @@ "id": "ec2187c7336c1bc3", "outputs": [ { - "ename": "TypeError", - "evalue": "IborSwap.payer_side_modified_duration() takes 3 positional arguments but 4 were given", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[1;32mIn[37], line 1\u001B[0m\n\u001B[1;32m----> 1\u001B[0m \u001B[43mswap\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mpayer_side_modified_duration\u001B[49m\u001B[43m(\u001B[49m\u001B[43mvalue_dt\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mzero_curve\u001B[49m\u001B[43m,\u001B[49m\u001B[43mpayment_periods\u001B[49m\u001B[43m)\u001B[49m\n", - "\u001B[1;31mTypeError\u001B[0m: IborSwap.payer_side_modified_duration() takes 3 positional arguments but 4 were given" - ] + "data": { + "text/plain": [ + "-6.710540865595371" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" } ], - "execution_count": 37 + "execution_count": 10 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:27:12.992002Z", - "start_time": "2025-05-23T01:27:12.980577Z" + "end_time": "2025-05-23T01:38:12.408437Z", + "start_time": "2025-05-23T01:38:12.401251Z" } }, "cell_type": "code", @@ -247,24 +244,23 @@ "id": "2c1b845afaf5e18f", "outputs": [ { - "ename": "TypeError", - "evalue": "IborSwap.receiver_side_modified_duration() takes 3 positional arguments but 4 were given", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[1;32mIn[38], line 1\u001B[0m\n\u001B[1;32m----> 1\u001B[0m \u001B[43mswap\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mreceiver_side_modified_duration\u001B[49m\u001B[43m(\u001B[49m\u001B[43mvalue_dt\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mzero_curve\u001B[49m\u001B[43m,\u001B[49m\u001B[43mpayment_periods\u001B[49m\u001B[43m)\u001B[49m\n", - "\u001B[1;31mTypeError\u001B[0m: IborSwap.receiver_side_modified_duration() takes 3 positional arguments but 4 were given" - ] + "data": { + "text/plain": [ + "6.710540865595371" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" } ], - "execution_count": 38 + "execution_count": 11 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:42.381647100Z", - "start_time": "2025-05-22T06:57:55.610805Z" + "end_time": "2025-05-23T01:38:15.309142Z", + "start_time": "2025-05-23T01:38:15.301795Z" } }, "cell_type": "code", @@ -316,13 +312,13 @@ ] } ], - "execution_count": 65 + "execution_count": 12 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:42.389019500Z", - "start_time": "2025-05-22T07:19:36.402004Z" + "end_time": "2025-05-23T01:38:16.922391Z", + "start_time": "2025-05-23T01:38:16.915012Z" } }, "cell_type": "code", @@ -343,12 +339,12 @@ "dtype: object" ] }, - "execution_count": 70, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 70 + "execution_count": 13 }, { "metadata": {}, diff --git a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb index 603c18e5..d377e7bb 100644 --- a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb +++ b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-05-23T01:13:12.026880Z", - "start_time": "2025-05-23T01:13:12.023386Z" + "end_time": "2025-05-23T02:25:34.603675Z", + "start_time": "2025-05-23T02:25:34.599467Z" } }, "source": [ @@ -17,13 +17,13 @@ "import datetime as dt" ], "outputs": [], - "execution_count": 92 + "execution_count": 16 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:13:13.020649Z", - "start_time": "2025-05-23T01:13:13.015544Z" + "end_time": "2025-05-23T02:25:34.630084Z", + "start_time": "2025-05-23T02:25:34.621619Z" } }, "cell_type": "code", @@ -63,13 +63,13 @@ ], "id": "1ebf43316efee835", "outputs": [], - "execution_count": 93 + "execution_count": 17 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:13:15.489337Z", - "start_time": "2025-05-23T01:13:15.485019Z" + "end_time": "2025-05-23T02:25:34.640874Z", + "start_time": "2025-05-23T02:25:34.636086Z" } }, "cell_type": "code", @@ -89,18 +89,18 @@ "31-MAR-2024" ] }, - "execution_count": 94, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 94 + "execution_count": 18 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:13:18.003794Z", - "start_time": "2025-05-23T01:13:17.998703Z" + "end_time": "2025-05-23T02:25:34.675868Z", + "start_time": "2025-05-23T02:25:34.663679Z" } }, "cell_type": "code", @@ -123,13 +123,13 @@ ], "id": "290b7a06932da41d", "outputs": [], - "execution_count": 95 + "execution_count": 19 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:13:19.311195Z", - "start_time": "2025-05-23T01:13:19.307399Z" + "end_time": "2025-05-23T02:25:34.693421Z", + "start_time": "2025-05-23T02:25:34.686347Z" } }, "cell_type": "code", @@ -142,18 +142,18 @@ "0.030065619962567392" ] }, - "execution_count": 96, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 96 + "execution_count": 20 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:13:29.846190Z", - "start_time": "2025-05-23T01:13:29.841877Z" + "end_time": "2025-05-23T02:25:34.722494Z", + "start_time": "2025-05-23T02:25:34.715862Z" } }, "cell_type": "code", @@ -197,13 +197,13 @@ ], "id": "65a401442f9bf084", "outputs": [], - "execution_count": 98 + "execution_count": 21 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:13:32.846466Z", - "start_time": "2025-05-23T01:13:32.841730Z" + "end_time": "2025-05-23T02:25:34.740382Z", + "start_time": "2025-05-23T02:25:34.734050Z" } }, "cell_type": "code", @@ -216,18 +216,18 @@ "-404094.769553849" ] }, - "execution_count": 99, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 99 + "execution_count": 22 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:23:14.567789Z", - "start_time": "2025-05-23T01:23:14.556465Z" + "end_time": "2025-05-23T02:25:34.769581Z", + "start_time": "2025-05-23T02:25:34.763504Z" } }, "cell_type": "code", @@ -235,96 +235,95 @@ "id": "3f988f26e6662203", "outputs": [ { - "ename": "TypeError", - "evalue": "IborSwap.payer_side_macaulay_duration() takes 3 positional arguments but 4 were given", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mTypeError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[1;32mIn[107], line 1\u001B[0m\n\u001B[1;32m----> 1\u001B[0m \u001B[43mswap\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mpayer_side_macaulay_duration\u001B[49m\u001B[43m(\u001B[49m\u001B[43mvalue_dt\u001B[49m\u001B[43m,\u001B[49m\u001B[43mzero_curve\u001B[49m\u001B[43m,\u001B[49m\u001B[38;5;241;43m8\u001B[39;49m\u001B[43m)\u001B[49m\n", - "\u001B[1;31mTypeError\u001B[0m: IborSwap.payer_side_macaulay_duration() takes 3 positional arguments but 4 were given" - ] + "data": { + "text/plain": [ + "-6.794198629548404" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" } ], - "execution_count": 107 + "execution_count": 23 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T00:45:15.232484600Z", - "start_time": "2025-05-23T00:39:29.952636Z" + "end_time": "2025-05-23T02:25:34.803424Z", + "start_time": "2025-05-23T02:25:34.798626Z" } }, "cell_type": "code", - "source": "swap.receiver_side_macaulay_duration(value_dt, zero_curve)", + "source": "swap.receiver_side_macaulay_duration(value_dt, zero_curve,8)", "id": "96d568f7a71861fe", "outputs": [ { "data": { "text/plain": [ - "5.84526657803498" + "6.794198629548404" ] }, - "execution_count": 55, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 55 + "execution_count": 24 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T00:45:15.232484600Z", - "start_time": "2025-05-23T00:39:30.019485Z" + "end_time": "2025-05-23T02:25:34.836332Z", + "start_time": "2025-05-23T02:25:34.831799Z" } }, "cell_type": "code", - "source": "swap.payer_side_modified_duration(value_dt, zero_curve)", + "source": "swap.payer_side_modified_duration(value_dt, zero_curve,8)", "id": "ec2187c7336c1bc3", "outputs": [ { "data": { "text/plain": [ - "-5.801658959676467" + "-6.743511664816526" ] }, - "execution_count": 56, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 56 + "execution_count": 25 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T00:45:15.238495100Z", - "start_time": "2025-05-23T00:39:30.068245Z" + "end_time": "2025-05-23T02:25:34.864470Z", + "start_time": "2025-05-23T02:25:34.860289Z" } }, "cell_type": "code", - "source": "swap.receiver_side_modified_duration(value_dt, zero_curve)", + "source": "swap.receiver_side_modified_duration(value_dt, zero_curve,8)", "id": "2c1b845afaf5e18f", "outputs": [ { "data": { "text/plain": [ - "5.801658959676467" + "6.743511664816526" ] }, - "execution_count": 57, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 57 + "execution_count": 26 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T00:45:15.239505100Z", - "start_time": "2025-05-23T00:39:30.125510Z" + "end_time": "2025-05-23T02:25:34.895364Z", + "start_time": "2025-05-23T02:25:34.888381Z" } }, "cell_type": "code", @@ -374,13 +373,13 @@ ] } ], - "execution_count": 58 + "execution_count": 27 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T00:45:15.239505100Z", - "start_time": "2025-05-23T00:39:30.159010Z" + "end_time": "2025-05-23T02:25:34.923991Z", + "start_time": "2025-05-23T02:25:34.918594Z" } }, "cell_type": "code", @@ -412,13 +411,13 @@ ] } ], - "execution_count": 59 + "execution_count": 28 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T00:45:15.240494900Z", - "start_time": "2025-05-23T00:39:30.197673Z" + "end_time": "2025-05-23T02:25:35.017712Z", + "start_time": "2025-05-23T02:25:35.013394Z" } }, "cell_type": "code", @@ -450,7 +449,32 @@ ] } ], - "execution_count": 60 + "execution_count": 29 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T02:25:35.127867Z", + "start_time": "2025-05-23T02:25:35.115423Z" + } + }, + "cell_type": "code", + "source": "swap.payer_side_profits(value_dt, zero_curve,8,-0.004)", + "id": "5ef5f29ad5dfad00", + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'IborSwap' object has no attribute 'payer_side_profits'", + "output_type": "error", + "traceback": [ + "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[1;31mAttributeError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[1;32mIn[30], line 1\u001B[0m\n\u001B[1;32m----> 1\u001B[0m \u001B[43mswap\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mpayer_side_profits\u001B[49m(value_dt, zero_curve,\u001B[38;5;241m8\u001B[39m,\u001B[38;5;241m-\u001B[39m\u001B[38;5;241m0.004\u001B[39m)\n", + "\u001B[1;31mAttributeError\u001B[0m: 'IborSwap' object has no attribute 'payer_side_profits'" + ] + } + ], + "execution_count": 30 } ], "metadata": { From 9275b9322393931cedb501e03d6ecda5b09eb1c6 Mon Sep 17 00:00:00 2001 From: piper0124 <55830785+piper0124@users.noreply.github.com> Date: Fri, 23 May 2025 10:44:16 +0800 Subject: [PATCH 05/12] add BPV for payer and receiver --- financepy/products/rates/ibor_swap.py | 33 ++++ ...eplicationgBONDMATHDurationExample3M.ipynb | 165 ++++++------------ 2 files changed, 90 insertions(+), 108 deletions(-) diff --git a/financepy/products/rates/ibor_swap.py b/financepy/products/rates/ibor_swap.py index ffcf2b01..c65910e1 100644 --- a/financepy/products/rates/ibor_swap.py +++ b/financepy/products/rates/ibor_swap.py @@ -458,9 +458,42 @@ def payer_side_profits(self, """ # Get coupon frequency coupon_frequency = self.fixed_leg.freq_type.value + annualized_modi_dur = self.payer_side_modified_duration(value_dt, discount_curve,payment_periods)/coupon_frequency return (-1)*(annualized_modi_dur*self.fixed_leg.notional*swap_rate_changes) + ########################################################################### + + def receiver_side_profits(self, + value_dt, + discount_curve,payment_periods: float, + swap_rate_changes: float): + """Computation of the Profits for the Fixed-Rate Receiver's Perspective in Interest Rate Swap + """ + return self.payer_side_profits(value_dt, discount_curve,payment_periods,swap_rate_changes)*(-1) + + ########################################################################### + + def payer_side_BPV(self, value_dt, discount_curve,payment_periods: float,): + """ + calculate the basis‐point‐value (BPV) for the payer_side of the swap, + which is swap's modified duration times the notional principal, + times one basis point (0.0001) + """ + bp=0.0001 + return self.payer_side_modified_duration(value_dt, discount_curve,payment_periods)*self.fixed_leg.notional*bp + + ########################################################################### + + def receiver_side_BPV(self, value_dt, discount_curve,payment_periods: float,): + """ + calculate the basis‐point‐value (BPV) for receiver_side of the swap, + which is swap's modified duration times the notional principal, + times one basis point (0.0001) + """ + return self.payer_side_BPV(value_dt, discount_curve,payment_periods)*(-1) + + ########################################################################### def __repr__(self): diff --git a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb index d377e7bb..0b7a1adf 100644 --- a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb +++ b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.603675Z", - "start_time": "2025-05-23T02:25:34.599467Z" + "end_time": "2025-05-23T02:31:22.631355Z", + "start_time": "2025-05-23T02:31:22.625849Z" } }, "source": [ @@ -17,13 +17,13 @@ "import datetime as dt" ], "outputs": [], - "execution_count": 16 + "execution_count": 2 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.630084Z", - "start_time": "2025-05-23T02:25:34.621619Z" + "end_time": "2025-05-23T02:31:22.679560Z", + "start_time": "2025-05-23T02:31:22.657680Z" } }, "cell_type": "code", @@ -63,13 +63,13 @@ ], "id": "1ebf43316efee835", "outputs": [], - "execution_count": 17 + "execution_count": 3 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.640874Z", - "start_time": "2025-05-23T02:25:34.636086Z" + "end_time": "2025-05-23T02:31:22.694667Z", + "start_time": "2025-05-23T02:31:22.687165Z" } }, "cell_type": "code", @@ -89,18 +89,18 @@ "31-MAR-2024" ] }, - "execution_count": 18, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 18 + "execution_count": 4 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.675868Z", - "start_time": "2025-05-23T02:25:34.663679Z" + "end_time": "2025-05-23T02:31:22.727011Z", + "start_time": "2025-05-23T02:31:22.719602Z" } }, "cell_type": "code", @@ -123,13 +123,13 @@ ], "id": "290b7a06932da41d", "outputs": [], - "execution_count": 19 + "execution_count": 5 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.693421Z", - "start_time": "2025-05-23T02:25:34.686347Z" + "end_time": "2025-05-23T02:31:22.746919Z", + "start_time": "2025-05-23T02:31:22.739589Z" } }, "cell_type": "code", @@ -142,68 +142,18 @@ "0.030065619962567392" ] }, - "execution_count": 20, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 20 + "execution_count": 6 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.722494Z", - "start_time": "2025-05-23T02:25:34.715862Z" - } - }, - "cell_type": "code", - "source": [ - "# def payer_side_macaulay_duration(value_dt, discount_curve):\n", - "# \"\"\"Calculation of the Payer's Macaulay Duration in an Interest Rate Swap\n", - "# Based on Bond Math: The Theory Behind the Formulas, Second Edition by\n", - "# Donald J. Smith\n", - "# \"\"\"\n", - "# # Number of fixed leg payments\n", - "# payment_period = len(swap.fixed_leg.payments)\n", - "#\n", - "# # Get coupon frequency\n", - "# coupon_frequency = self.fixed_leg.freq_type.value\n", - "#\n", - "# # Get swap rate\n", - "# swap_rate_val = self.swap_rate(value_dt, discount_curve)\n", - "#\n", - "# y = swap_rate_val / coupon_frequency\n", - "# N = payment_period\n", - "# c = swap_rate_val / coupon_frequency\n", - "# md1 = (1 + y) / y\n", - "# md2 = 1 + y + (N * (c - y))\n", - "# md3 = c * ((1 + y) ** N - 1) + y\n", - "# mac_duration = 1 - (md1 - md2 / md3)\n", - "# return mac_duration\n", - "\n", - "\n", - "payment_period = len(swap.fixed_leg.payments) + 1\n", - "coupon_frequency = swap.fixed_leg.freq_type.value\n", - "swap_rate_val = swap.swap_rate(value_dt, zero_curve)\n", - "y = swap_rate_val / coupon_frequency\n", - "N = payment_period\n", - "c = swap_rate_val / coupon_frequency\n", - "md1 = (1 + y) / y\n", - "md2 = 1 + y + (N * (c - y))\n", - "md3 = c * ((1 + y) ** N - 1) + y\n", - "mac_duration = 1 - (md1 - md2 / md3)\n", - "\n", - "delta_mv=-(-mac_duration/4*notional*(-0.004))" - ], - "id": "65a401442f9bf084", - "outputs": [], - "execution_count": 21 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.740382Z", - "start_time": "2025-05-23T02:25:34.734050Z" + "end_time": "2025-05-23T02:31:22.765913Z", + "start_time": "2025-05-23T02:31:22.759349Z" } }, "cell_type": "code", @@ -216,18 +166,18 @@ "-404094.769553849" ] }, - "execution_count": 22, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 22 + "execution_count": 7 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.769581Z", - "start_time": "2025-05-23T02:25:34.763504Z" + "end_time": "2025-05-23T02:31:22.784960Z", + "start_time": "2025-05-23T02:31:22.779808Z" } }, "cell_type": "code", @@ -240,18 +190,18 @@ "-6.794198629548404" ] }, - "execution_count": 23, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 23 + "execution_count": 8 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.803424Z", - "start_time": "2025-05-23T02:25:34.798626Z" + "end_time": "2025-05-23T02:31:22.817161Z", + "start_time": "2025-05-23T02:31:22.812093Z" } }, "cell_type": "code", @@ -264,18 +214,18 @@ "6.794198629548404" ] }, - "execution_count": 24, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 24 + "execution_count": 9 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.836332Z", - "start_time": "2025-05-23T02:25:34.831799Z" + "end_time": "2025-05-23T02:31:22.858301Z", + "start_time": "2025-05-23T02:31:22.853154Z" } }, "cell_type": "code", @@ -288,18 +238,18 @@ "-6.743511664816526" ] }, - "execution_count": 25, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 25 + "execution_count": 10 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.864470Z", - "start_time": "2025-05-23T02:25:34.860289Z" + "end_time": "2025-05-23T02:31:22.894478Z", + "start_time": "2025-05-23T02:31:22.889858Z" } }, "cell_type": "code", @@ -312,18 +262,18 @@ "6.743511664816526" ] }, - "execution_count": 26, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 26 + "execution_count": 11 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.895364Z", - "start_time": "2025-05-23T02:25:34.888381Z" + "end_time": "2025-05-23T02:31:22.942622Z", + "start_time": "2025-05-23T02:31:22.937046Z" } }, "cell_type": "code", @@ -373,13 +323,13 @@ ] } ], - "execution_count": 27 + "execution_count": 12 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:34.923991Z", - "start_time": "2025-05-23T02:25:34.918594Z" + "end_time": "2025-05-23T02:31:22.977499Z", + "start_time": "2025-05-23T02:31:22.971711Z" } }, "cell_type": "code", @@ -411,13 +361,13 @@ ] } ], - "execution_count": 28 + "execution_count": 13 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:35.017712Z", - "start_time": "2025-05-23T02:25:35.013394Z" + "end_time": "2025-05-23T02:31:23.015658Z", + "start_time": "2025-05-23T02:31:23.010172Z" } }, "cell_type": "code", @@ -449,13 +399,13 @@ ] } ], - "execution_count": 29 + "execution_count": 14 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:25:35.127867Z", - "start_time": "2025-05-23T02:25:35.115423Z" + "end_time": "2025-05-23T02:31:23.043840Z", + "start_time": "2025-05-23T02:31:23.039172Z" } }, "cell_type": "code", @@ -463,18 +413,17 @@ "id": "5ef5f29ad5dfad00", "outputs": [ { - "ename": "AttributeError", - "evalue": "'IborSwap' object has no attribute 'payer_side_profits'", - "output_type": "error", - "traceback": [ - "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[1;31mAttributeError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[1;32mIn[30], line 1\u001B[0m\n\u001B[1;32m----> 1\u001B[0m \u001B[43mswap\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mpayer_side_profits\u001B[49m(value_dt, zero_curve,\u001B[38;5;241m8\u001B[39m,\u001B[38;5;241m-\u001B[39m\u001B[38;5;241m0.004\u001B[39m)\n", - "\u001B[1;31mAttributeError\u001B[0m: 'IborSwap' object has no attribute 'payer_side_profits'" - ] + "data": { + "text/plain": [ + "-404610.6998889916" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" } ], - "execution_count": 30 + "execution_count": 15 } ], "metadata": { From 0cfcc73a4fd697069c528d570076b28dd1dc53e5 Mon Sep 17 00:00:00 2001 From: piper0124 <55830785+piper0124@users.noreply.github.com> Date: Fri, 23 May 2025 10:55:41 +0800 Subject: [PATCH 06/12] modified BPV calculation --- financepy/products/rates/ibor_swap.py | 3 +- ...eplicationgBONDMATHDurationExample3M.ipynb | 182 +++++++++++++----- 2 files changed, 132 insertions(+), 53 deletions(-) diff --git a/financepy/products/rates/ibor_swap.py b/financepy/products/rates/ibor_swap.py index c65910e1..e3247417 100644 --- a/financepy/products/rates/ibor_swap.py +++ b/financepy/products/rates/ibor_swap.py @@ -481,7 +481,8 @@ def payer_side_BPV(self, value_dt, discount_curve,payment_periods: float,): times one basis point (0.0001) """ bp=0.0001 - return self.payer_side_modified_duration(value_dt, discount_curve,payment_periods)*self.fixed_leg.notional*bp + modi_dur = self.payer_side_modified_duration(value_dt, discount_curve,payment_periods) + return modi_dur*self.fixed_leg.notional*bp ########################################################################### diff --git a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb index 0b7a1adf..e2759851 100644 --- a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb +++ b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb @@ -6,24 +6,26 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-05-23T02:31:22.631355Z", - "start_time": "2025-05-23T02:31:22.625849Z" + "end_time": "2025-05-23T02:54:25.031837Z", + "start_time": "2025-05-23T02:54:25.027815Z" } }, "source": [ "from financepy.utils import *\n", "from financepy.products.rates import *\n", "from financepy.market.curves import *\n", - "import datetime as dt" + "import datetime as dt\n", + "\n", + "from notebooks.products.rates.FINIBORSWAP_ReplicationgBONDMATHDurationExample import payment_periods" ], "outputs": [], - "execution_count": 2 + "execution_count": 34 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:22.679560Z", - "start_time": "2025-05-23T02:31:22.657680Z" + "end_time": "2025-05-23T02:54:25.043454Z", + "start_time": "2025-05-23T02:54:25.039940Z" } }, "cell_type": "code", @@ -63,13 +65,13 @@ ], "id": "1ebf43316efee835", "outputs": [], - "execution_count": 3 + "execution_count": 35 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:22.694667Z", - "start_time": "2025-05-23T02:31:22.687165Z" + "end_time": "2025-05-23T02:54:25.056980Z", + "start_time": "2025-05-23T02:54:25.052895Z" } }, "cell_type": "code", @@ -89,18 +91,18 @@ "31-MAR-2024" ] }, - "execution_count": 4, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 4 + "execution_count": 36 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:22.727011Z", - "start_time": "2025-05-23T02:31:22.719602Z" + "end_time": "2025-05-23T02:54:25.078736Z", + "start_time": "2025-05-23T02:54:25.073700Z" } }, "cell_type": "code", @@ -123,13 +125,13 @@ ], "id": "290b7a06932da41d", "outputs": [], - "execution_count": 5 + "execution_count": 37 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:22.746919Z", - "start_time": "2025-05-23T02:31:22.739589Z" + "end_time": "2025-05-23T02:54:25.092695Z", + "start_time": "2025-05-23T02:54:25.087190Z" } }, "cell_type": "code", @@ -142,18 +144,18 @@ "0.030065619962567392" ] }, - "execution_count": 6, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 6 + "execution_count": 38 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:22.765913Z", - "start_time": "2025-05-23T02:31:22.759349Z" + "end_time": "2025-05-23T02:54:25.120452Z", + "start_time": "2025-05-23T02:54:25.116135Z" } }, "cell_type": "code", @@ -166,18 +168,18 @@ "-404094.769553849" ] }, - "execution_count": 7, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 7 + "execution_count": 39 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:22.784960Z", - "start_time": "2025-05-23T02:31:22.779808Z" + "end_time": "2025-05-23T02:54:25.148481Z", + "start_time": "2025-05-23T02:54:25.143313Z" } }, "cell_type": "code", @@ -190,18 +192,18 @@ "-6.794198629548404" ] }, - "execution_count": 8, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 8 + "execution_count": 40 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:22.817161Z", - "start_time": "2025-05-23T02:31:22.812093Z" + "end_time": "2025-05-23T02:54:25.169741Z", + "start_time": "2025-05-23T02:54:25.164469Z" } }, "cell_type": "code", @@ -214,18 +216,18 @@ "6.794198629548404" ] }, - "execution_count": 9, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 9 + "execution_count": 41 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:22.858301Z", - "start_time": "2025-05-23T02:31:22.853154Z" + "end_time": "2025-05-23T02:54:25.199176Z", + "start_time": "2025-05-23T02:54:25.193898Z" } }, "cell_type": "code", @@ -238,18 +240,18 @@ "-6.743511664816526" ] }, - "execution_count": 10, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 10 + "execution_count": 42 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:22.894478Z", - "start_time": "2025-05-23T02:31:22.889858Z" + "end_time": "2025-05-23T02:54:25.228763Z", + "start_time": "2025-05-23T02:54:25.224367Z" } }, "cell_type": "code", @@ -262,18 +264,18 @@ "6.743511664816526" ] }, - "execution_count": 11, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 11 + "execution_count": 43 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:22.942622Z", - "start_time": "2025-05-23T02:31:22.937046Z" + "end_time": "2025-05-23T02:54:25.264697Z", + "start_time": "2025-05-23T02:54:25.260233Z" } }, "cell_type": "code", @@ -323,13 +325,13 @@ ] } ], - "execution_count": 12 + "execution_count": 44 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:22.977499Z", - "start_time": "2025-05-23T02:31:22.971711Z" + "end_time": "2025-05-23T02:54:25.286790Z", + "start_time": "2025-05-23T02:54:25.282897Z" } }, "cell_type": "code", @@ -361,13 +363,13 @@ ] } ], - "execution_count": 13 + "execution_count": 45 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:23.015658Z", - "start_time": "2025-05-23T02:31:23.010172Z" + "end_time": "2025-05-23T02:54:25.315781Z", + "start_time": "2025-05-23T02:54:25.311063Z" } }, "cell_type": "code", @@ -399,17 +401,21 @@ ] } ], - "execution_count": 14 + "execution_count": 46 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:31:23.043840Z", - "start_time": "2025-05-23T02:31:23.039172Z" + "end_time": "2025-05-23T02:54:25.345988Z", + "start_time": "2025-05-23T02:54:25.341966Z" } }, "cell_type": "code", - "source": "swap.payer_side_profits(value_dt, zero_curve,8,-0.004)", + "source": [ + "payment_periods=8\n", + "swap_rate_changes=-0.004\n", + "swap.payer_side_profits(value_dt, zero_curve,payment_periods,swap_rate_changes)" + ], "id": "5ef5f29ad5dfad00", "outputs": [ { @@ -418,12 +424,84 @@ "-404610.6998889916" ] }, - "execution_count": 15, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 47 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T02:54:25.433818Z", + "start_time": "2025-05-23T02:54:25.427828Z" + } + }, + "cell_type": "code", + "source": "swap.receiver_side_profits(value_dt, zero_curve,payment_periods,swap_rate_changes)", + "id": "11109a6f2da7b7dc", + "outputs": [ + { + "data": { + "text/plain": [ + "404610.6998889916" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 48 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T02:54:25.518531Z", + "start_time": "2025-05-23T02:54:25.513863Z" + } + }, + "cell_type": "code", + "source": "swap.payer_side_BPV(value_dt, zero_curve,payment_periods)", + "id": "a4c532a9a90b445c", + "outputs": [ + { + "data": { + "text/plain": [ + "-40461.06998889916" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 49 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T02:54:25.578843Z", + "start_time": "2025-05-23T02:54:25.574712Z" + } + }, + "cell_type": "code", + "source": "swap.receiver_side_BPV(value_dt, zero_curve,payment_periods)", + "id": "8f8630e90883e3b5", + "outputs": [ + { + "data": { + "text/plain": [ + "40461.06998889916" + ] + }, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 15 + "execution_count": 50 } ], "metadata": { From 59558be0ea5fe1b29e168666d67bce4ba0da815f Mon Sep 17 00:00:00 2001 From: piper0124 <55830785+piper0124@users.noreply.github.com> Date: Fri, 23 May 2025 11:07:28 +0800 Subject: [PATCH 07/12] modified BPV calculation --- financepy/products/rates/ibor_swap.py | 5 +- ...eplicationgBONDMATHDurationExample3M.ipynb | 141 ++++++++---------- 2 files changed, 69 insertions(+), 77 deletions(-) diff --git a/financepy/products/rates/ibor_swap.py b/financepy/products/rates/ibor_swap.py index e3247417..c57566c8 100644 --- a/financepy/products/rates/ibor_swap.py +++ b/financepy/products/rates/ibor_swap.py @@ -481,8 +481,11 @@ def payer_side_BPV(self, value_dt, discount_curve,payment_periods: float,): times one basis point (0.0001) """ bp=0.0001 + # Get coupon frequency + coupon_frequency = self.fixed_leg.freq_type.value modi_dur = self.payer_side_modified_duration(value_dt, discount_curve,payment_periods) - return modi_dur*self.fixed_leg.notional*bp + annualized_modi_dur= modi_dur/coupon_frequency + return annualized_modi_dur*self.fixed_leg.notional*bp ########################################################################### diff --git a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb index e2759851..ca5a8b00 100644 --- a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb +++ b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb @@ -6,26 +6,24 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.031837Z", - "start_time": "2025-05-23T02:54:25.027815Z" + "end_time": "2025-05-23T03:01:38.516595Z", + "start_time": "2025-05-23T03:01:38.512089Z" } }, "source": [ "from financepy.utils import *\n", "from financepy.products.rates import *\n", "from financepy.market.curves import *\n", - "import datetime as dt\n", - "\n", - "from notebooks.products.rates.FINIBORSWAP_ReplicationgBONDMATHDurationExample import payment_periods" + "import datetime as dt\n" ], "outputs": [], - "execution_count": 34 + "execution_count": 22 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.043454Z", - "start_time": "2025-05-23T02:54:25.039940Z" + "end_time": "2025-05-23T03:01:38.528444Z", + "start_time": "2025-05-23T03:01:38.524597Z" } }, "cell_type": "code", @@ -65,13 +63,13 @@ ], "id": "1ebf43316efee835", "outputs": [], - "execution_count": 35 + "execution_count": 23 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.056980Z", - "start_time": "2025-05-23T02:54:25.052895Z" + "end_time": "2025-05-23T03:01:38.542275Z", + "start_time": "2025-05-23T03:01:38.537531Z" } }, "cell_type": "code", @@ -91,18 +89,18 @@ "31-MAR-2024" ] }, - "execution_count": 36, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 36 + "execution_count": 24 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.078736Z", - "start_time": "2025-05-23T02:54:25.073700Z" + "end_time": "2025-05-23T03:01:38.569014Z", + "start_time": "2025-05-23T03:01:38.564129Z" } }, "cell_type": "code", @@ -125,13 +123,13 @@ ], "id": "290b7a06932da41d", "outputs": [], - "execution_count": 37 + "execution_count": 25 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.092695Z", - "start_time": "2025-05-23T02:54:25.087190Z" + "end_time": "2025-05-23T03:01:38.585724Z", + "start_time": "2025-05-23T03:01:38.579955Z" } }, "cell_type": "code", @@ -144,18 +142,18 @@ "0.030065619962567392" ] }, - "execution_count": 38, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 38 + "execution_count": 26 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.120452Z", - "start_time": "2025-05-23T02:54:25.116135Z" + "end_time": "2025-05-23T03:01:38.610747Z", + "start_time": "2025-05-23T03:01:38.606021Z" } }, "cell_type": "code", @@ -168,18 +166,18 @@ "-404094.769553849" ] }, - "execution_count": 39, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 39 + "execution_count": 27 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.148481Z", - "start_time": "2025-05-23T02:54:25.143313Z" + "end_time": "2025-05-23T03:01:38.645126Z", + "start_time": "2025-05-23T03:01:38.640384Z" } }, "cell_type": "code", @@ -192,18 +190,18 @@ "-6.794198629548404" ] }, - "execution_count": 40, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 40 + "execution_count": 28 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.169741Z", - "start_time": "2025-05-23T02:54:25.164469Z" + "end_time": "2025-05-23T03:01:38.686342Z", + "start_time": "2025-05-23T03:01:38.681347Z" } }, "cell_type": "code", @@ -216,18 +214,18 @@ "6.794198629548404" ] }, - "execution_count": 41, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 41 + "execution_count": 29 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.199176Z", - "start_time": "2025-05-23T02:54:25.193898Z" + "end_time": "2025-05-23T03:01:38.722907Z", + "start_time": "2025-05-23T03:01:38.717909Z" } }, "cell_type": "code", @@ -240,18 +238,18 @@ "-6.743511664816526" ] }, - "execution_count": 42, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 42 + "execution_count": 30 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.228763Z", - "start_time": "2025-05-23T02:54:25.224367Z" + "end_time": "2025-05-23T03:01:38.756278Z", + "start_time": "2025-05-23T03:01:38.751727Z" } }, "cell_type": "code", @@ -264,18 +262,18 @@ "6.743511664816526" ] }, - "execution_count": 43, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 43 + "execution_count": 31 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.264697Z", - "start_time": "2025-05-23T02:54:25.260233Z" + "end_time": "2025-05-23T03:01:38.790007Z", + "start_time": "2025-05-23T03:01:38.786002Z" } }, "cell_type": "code", @@ -325,13 +323,13 @@ ] } ], - "execution_count": 44 + "execution_count": 32 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.286790Z", - "start_time": "2025-05-23T02:54:25.282897Z" + "end_time": "2025-05-23T03:01:38.821049Z", + "start_time": "2025-05-23T03:01:38.816897Z" } }, "cell_type": "code", @@ -363,13 +361,13 @@ ] } ], - "execution_count": 45 + "execution_count": 33 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.315781Z", - "start_time": "2025-05-23T02:54:25.311063Z" + "end_time": "2025-05-23T03:01:38.907291Z", + "start_time": "2025-05-23T03:01:38.903054Z" } }, "cell_type": "code", @@ -401,13 +399,13 @@ ] } ], - "execution_count": 46 + "execution_count": 34 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.345988Z", - "start_time": "2025-05-23T02:54:25.341966Z" + "end_time": "2025-05-23T03:01:39.014637Z", + "start_time": "2025-05-23T03:01:39.009932Z" } }, "cell_type": "code", @@ -424,18 +422,18 @@ "-404610.6998889916" ] }, - "execution_count": 47, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 47 + "execution_count": 35 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.433818Z", - "start_time": "2025-05-23T02:54:25.427828Z" + "end_time": "2025-05-23T03:01:39.056730Z", + "start_time": "2025-05-23T03:01:39.050609Z" } }, "cell_type": "code", @@ -448,18 +446,18 @@ "404610.6998889916" ] }, - "execution_count": 48, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 48 + "execution_count": 36 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.518531Z", - "start_time": "2025-05-23T02:54:25.513863Z" + "end_time": "2025-05-23T03:01:39.100675Z", + "start_time": "2025-05-23T03:01:39.095809Z" } }, "cell_type": "code", @@ -472,36 +470,27 @@ "-40461.06998889916" ] }, - "execution_count": 49, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 49 + "execution_count": 37 }, { "metadata": { + "jupyter": { + "is_executing": true + }, "ExecuteTime": { - "end_time": "2025-05-23T02:54:25.578843Z", - "start_time": "2025-05-23T02:54:25.574712Z" + "start_time": "2025-05-23T03:01:55.128551Z" } }, "cell_type": "code", "source": "swap.receiver_side_BPV(value_dt, zero_curve,payment_periods)", "id": "8f8630e90883e3b5", - "outputs": [ - { - "data": { - "text/plain": [ - "40461.06998889916" - ] - }, - "execution_count": 50, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 50 + "outputs": [], + "execution_count": null } ], "metadata": { From 7b3572d287ea6946111e7bf4025e37b58511a531 Mon Sep 17 00:00:00 2001 From: piper0124 <55830785+piper0124@users.noreply.github.com> Date: Fri, 23 May 2025 14:53:44 +0800 Subject: [PATCH 08/12] Calculate IRS Duration & Metrics vs. Bond Math Case --- ..._ReplicationgBONDMATHDurationExample.ipynb | 669 +++++++++++------- ...eplicationgBONDMATHDurationExample3M.ipynb | 272 +++---- 2 files changed, 505 insertions(+), 436 deletions(-) diff --git a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb index 724c6aa6..f42cf92e 100644 --- a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb +++ b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-05-23T01:37:56.175867Z", - "start_time": "2025-05-23T01:37:56.171771Z" + "end_time": "2025-05-23T06:49:48.974729Z", + "start_time": "2025-05-23T06:49:48.971287Z" } }, "source": [ @@ -15,16 +15,57 @@ "from financepy.products.rates import *\n", "from financepy.market.curves import *\n", "import datetime as dt\n", - "\n" + "import numpy as np" ], "outputs": [], - "execution_count": 2 + "execution_count": 101 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:37:57.664887Z", - "start_time": "2025-05-23T01:37:57.637136Z" + "end_time": "2025-05-23T06:49:49.013677Z", + "start_time": "2025-05-23T06:49:49.009267Z" + } + }, + "cell_type": "code", + "source": [ + " \"\"\"\n", + "Calculating the Duration and Other Interest Rate Sensitivity Metrics in an Interest Rate Swap\n", + "Based on Bond Math: The Theory Behind the Formulas, Second Edition by Donald J. Smith\n", + " \"\"\"" + ], + "id": "1771696c9beba73c", + "outputs": [ + { + "data": { + "text/plain": [ + "'\\nCalculating the Duration and Other Interest Rate Sensitivity Metrics in an Interest Rate Swap\\nBased on Bond Math: The Theory Behind the Formulas, Second Edition by Donald J. Smith\\n'" + ] + }, + "execution_count": 102, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 102 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "# 1.Interest rate swaps at the beginning", + "id": "c710af2d079e815d" + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "## Define the swap", + "id": "e606fc8bae71cc9e" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-23T06:49:49.022425Z", + "start_time": "2025-05-23T06:49:49.018680Z" } }, "cell_type": "code", @@ -33,7 +74,6 @@ "bd_type = BusDayAdjustTypes.FOLLOWING\n", "dg_type = DateGenRuleTypes.BACKWARD\n", "\n", - "\n", "fixed_cpn = 0.034\n", "fixed_freq_type = FrequencyTypes.QUARTERLY\n", "fixed_dc_type = DayCountTypes.THIRTY_360_BOND\n", @@ -64,38 +104,42 @@ ], "id": "1ebf43316efee835", "outputs": [], - "execution_count": 3 + "execution_count": 103 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:38:00.243014Z", - "start_time": "2025-05-23T01:38:00.238262Z" + "end_time": "2025-05-23T06:49:49.040866Z", + "start_time": "2025-05-23T06:49:49.037306Z" } }, "cell_type": "code", "source": [ - "value_date= dt.datetime(2023,12,31)\n", + "value_date = dt.datetime(2023, 12, 31)\n", "value_dt = from_datetime(value_date)\n", - "\n", - "settle_dt = value_dt.add_weekdays(0)\n", - "# settle_dt=value_dt" + "settle_dt = value_dt.add_weekdays(0)\n" ], "id": "8e46f142f3cba5d7", "outputs": [], - "execution_count": 4 + "execution_count": 104 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "## Define the yield curve", + "id": "9e298d9eeefb972a" }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:38:01.679542Z", - "start_time": "2025-05-23T01:38:01.673799Z" + "end_time": "2025-05-23T06:49:49.057895Z", + "start_time": "2025-05-23T06:49:49.053646Z" } }, "cell_type": "code", "source": [ - "yield_curve=[i/100 for i in [0.5,1.0407,1.5829,2.1271,2.4506,2.7756,3.1025,3.4316]]\n", - "zero_dts=value_dt.add_years([0.25,0.5,0.75,1.0,1.25,1.5,1.75,2.0])\n", + "yield_curve = [i / 100 for i in [0.5, 1.0407, 1.5829, 2.1271, 2.4506, 2.7756, 3.1025, 3.4316]]\n", + "zero_dts = value_dt.add_years([0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0])\n", "\n", "zero_curve = DiscountCurveZeros(value_dt=value_dt,\n", " zero_dts=zero_dts,\n", @@ -107,17 +151,26 @@ ], "id": "290b7a06932da41d", "outputs": [], - "execution_count": 5 + "execution_count": 105 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "## Calculate the swap rate and sensitivity indicators", + "id": "de6b6c0c33f6f4a6" }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:38:03.960837Z", - "start_time": "2025-05-23T01:38:03.952526Z" + "end_time": "2025-05-23T06:49:49.073393Z", + "start_time": "2025-05-23T06:49:49.068617Z" } }, "cell_type": "code", - "source": "swap.swap_rate(value_dt, zero_curve)", + "source": [ + "swap_rate = swap.swap_rate(value_dt, zero_curve)\n", + "swap_rate" + ], "id": "7bb1613d5e865838", "outputs": [ { @@ -126,18 +179,24 @@ "0.03404344249149676" ] }, - "execution_count": 6, + "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 6 + "execution_count": 106 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "", + "id": "6ead1b5ec3065f1e" }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:38:05.697242Z", - "start_time": "2025-05-23T01:38:05.690197Z" + "end_time": "2025-05-23T06:49:49.111806Z", + "start_time": "2025-05-23T06:49:49.108411Z" } }, "cell_type": "code", @@ -150,24 +209,25 @@ "5070.7014320408925" ] }, - "execution_count": 7, + "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 7 + "execution_count": 107 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:38:07.096647Z", - "start_time": "2025-05-23T01:38:07.090478Z" + "end_time": "2025-05-23T06:49:49.152954Z", + "start_time": "2025-05-23T06:49:49.148624Z" } }, "cell_type": "code", "source": [ - "payment_periods=8\n", - "swap.payer_side_macaulay_duration(value_dt, zero_curve,payment_periods)" + "payment_periods = 8\n", + "payer_side_macaulay_duration = swap.payer_side_macaulay_duration(value_dt, zero_curve, payment_periods)\n", + "payer_side_macaulay_duration" ], "id": "3f988f26e6662203", "outputs": [ @@ -177,22 +237,25 @@ "-6.767653343606554" ] }, - "execution_count": 8, + "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 8 + "execution_count": 108 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:38:09.247259Z", - "start_time": "2025-05-23T01:38:09.242767Z" + "end_time": "2025-05-23T06:49:49.197909Z", + "start_time": "2025-05-23T06:49:49.192534Z" } }, "cell_type": "code", - "source": "swap.receiver_side_macaulay_duration(value_dt, zero_curve,payment_periods)", + "source": [ + "receiver_side_macaulay_duration = swap.receiver_side_macaulay_duration(value_dt, zero_curve, payment_periods)\n", + "receiver_side_macaulay_duration" + ], "id": "96d568f7a71861fe", "outputs": [ { @@ -201,22 +264,25 @@ "6.767653343606554" ] }, - "execution_count": 9, + "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 9 + "execution_count": 109 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:38:11.105175Z", - "start_time": "2025-05-23T01:38:11.098720Z" + "end_time": "2025-05-23T06:49:49.243489Z", + "start_time": "2025-05-23T06:49:49.239303Z" } }, "cell_type": "code", - "source": "swap.payer_side_modified_duration(value_dt, zero_curve,payment_periods)", + "source": [ + "payer_side_modified_duration = swap.payer_side_modified_duration(value_dt, zero_curve, payment_periods)\n", + "payer_side_modified_duration" + ], "id": "ec2187c7336c1bc3", "outputs": [ { @@ -225,22 +291,25 @@ "-6.710540865595371" ] }, - "execution_count": 10, + "execution_count": 110, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 10 + "execution_count": 110 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:38:12.408437Z", - "start_time": "2025-05-23T01:38:12.401251Z" + "end_time": "2025-05-23T06:49:49.288001Z", + "start_time": "2025-05-23T06:49:49.283397Z" } }, "cell_type": "code", - "source": "swap.receiver_side_modified_duration(value_dt, zero_curve,payment_periods)", + "source": [ + "receiver_side_modified_duration = swap.receiver_side_modified_duration(value_dt, zero_curve, payment_periods)\n", + "receiver_side_modified_duration" + ], "id": "2c1b845afaf5e18f", "outputs": [ { @@ -249,314 +318,428 @@ "6.710540865595371" ] }, - "execution_count": 11, + "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 11 + "execution_count": 111 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:38:15.309142Z", - "start_time": "2025-05-23T01:38:15.301795Z" + "end_time": "2025-05-23T06:49:49.340211Z", + "start_time": "2025-05-23T06:49:49.332412Z" } }, "cell_type": "code", - "source": "swap.print_payments()", - "id": "2af7c95b07730dda", + "source": [ + "payer_side_BPV = swap.payer_side_BPV(value_dt, zero_curve, payment_periods)\n", + "payer_side_BPV" + ], + "id": "602fd89b88bd58f0", "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "START DATE: 01-JAN-2024\n", - "MATURITY DATE: 02-JAN-2026\n", - "COUPON (%): 3.4000000000000004\n", - "FREQUENCY: FrequencyTypes.QUARTERLY\n", - "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", - "\n", - "PAYMENTS SCHEDULE:\n", - "+---------+-------------+-------------+-------------+------+----------+------+-----------+\n", - "| PAY_NUM | PAY_dt | ACCR_START | ACCR_END | DAYS | YEARFRAC | RATE | PMNT |\n", - "+---------+-------------+-------------+-------------+------+----------+------+-----------+\n", - "| 1 | 02-APR-2024 | 01-JAN-2024 | 02-APR-2024 | 91 | 0.2528 | 3.4 | 515666.67 |\n", - "| 2 | 01-JUL-2024 | 02-APR-2024 | 01-JUL-2024 | 89 | 0.2472 | 3.4 | 504333.33 |\n", - "| 3 | 01-OCT-2024 | 01-JUL-2024 | 01-OCT-2024 | 90 | 0.25 | 3.4 | 510000.0 |\n", - "| 4 | 02-JAN-2025 | 01-OCT-2024 | 02-JAN-2025 | 91 | 0.2528 | 3.4 | 515666.67 |\n", - "| 5 | 01-APR-2025 | 02-JAN-2025 | 01-APR-2025 | 89 | 0.2472 | 3.4 | 504333.33 |\n", - "| 6 | 01-JUL-2025 | 01-APR-2025 | 01-JUL-2025 | 90 | 0.25 | 3.4 | 510000.0 |\n", - "| 7 | 01-OCT-2025 | 01-JUL-2025 | 01-OCT-2025 | 90 | 0.25 | 3.4 | 510000.0 |\n", - "| 8 | 02-JAN-2026 | 01-OCT-2025 | 02-JAN-2026 | 91 | 0.2528 | 3.4 | 515666.67 |\n", - "+---------+-------------+-------------+-------------+------+----------+------+-----------+\n", - "START DATE: 01-JAN-2024\n", - "MATURITY DATE: 02-JAN-2026\n", - "SPREAD (bp): 0.0\n", - "FREQUENCY: FrequencyTypes.QUARTERLY\n", - "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", - "\n", - "PAYMENTS SCHEDULE:\n", - "+---------+-------------+-------------+-------------+------+----------+\n", - "| PAY_NUM | PAY_dt | ACCR_START | ACCR_END | DAYS | YEARFRAC |\n", - "+---------+-------------+-------------+-------------+------+----------+\n", - "| 1 | 02-APR-2024 | 01-JAN-2024 | 02-APR-2024 | 91 | 0.2528 |\n", - "| 2 | 01-JUL-2024 | 02-APR-2024 | 01-JUL-2024 | 89 | 0.2472 |\n", - "| 3 | 01-OCT-2024 | 01-JUL-2024 | 01-OCT-2024 | 90 | 0.25 |\n", - "| 4 | 02-JAN-2025 | 01-OCT-2024 | 02-JAN-2025 | 91 | 0.2528 |\n", - "| 5 | 01-APR-2025 | 02-JAN-2025 | 01-APR-2025 | 89 | 0.2472 |\n", - "| 6 | 01-JUL-2025 | 01-APR-2025 | 01-JUL-2025 | 90 | 0.25 |\n", - "| 7 | 01-OCT-2025 | 01-JUL-2025 | 01-OCT-2025 | 90 | 0.25 |\n", - "| 8 | 02-JAN-2026 | 01-OCT-2025 | 02-JAN-2026 | 91 | 0.2528 |\n", - "+---------+-------------+-------------+-------------+------+----------+\n" - ] + "data": { + "text/plain": [ + "-10065.811298393059" + ] + }, + "execution_count": 112, + "metadata": {}, + "output_type": "execute_result" } ], - "execution_count": 12 + "execution_count": 112 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:38:16.922391Z", - "start_time": "2025-05-23T01:38:16.915012Z" + "end_time": "2025-05-23T06:49:49.366870Z", + "start_time": "2025-05-23T06:49:49.360337Z" } }, "cell_type": "code", - "source": "pd.Series(swap.fixed_leg.payment_dts)", - "id": "52df44f5a67bc377", + "source": [ + "receiver_side_BPV = swap.receiver_side_BPV(value_dt, zero_curve, payment_periods)\n", + "receiver_side_BPV" + ], + "id": "b868dc3219fa867", "outputs": [ { "data": { "text/plain": [ - "0 02-APR-2024\n", - "1 01-JUL-2024\n", - "2 01-OCT-2024\n", - "3 02-JAN-2025\n", - "4 01-APR-2025\n", - "5 01-JUL-2025\n", - "6 01-OCT-2025\n", - "7 02-JAN-2026\n", - "dtype: object" + "10065.811298393059" ] }, - "execution_count": 13, + "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 13 - }, - { - "metadata": {}, - "cell_type": "markdown", - "source": "# 3months later", - "id": "39513cfdc5d7ef1" + "execution_count": 113 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:42.392700200Z", - "start_time": "2025-05-22T08:57:46.274653Z" + "end_time": "2025-05-23T06:49:49.412316Z", + "start_time": "2025-05-23T06:49:49.405136Z" } }, "cell_type": "code", "source": [ - "yield_curve_3m=[i/100 for i in [0.75,1.1853,1.6822,2.1474,2.4371,2.7390,3.0198]]\n", - "zero_dts_3m=value_dt.add_years([0.25,0.5,0.75,1.0,1.25,1.5,1.75])\n", + "index = ['swap_rate',\n", + " 'payer_side_macaulay_duration',\n", + " 'payer_side_modified_duration',\n", + " 'payer_side_BPV',\n", + " 'receiver_side_macaulay_duration',\n", + " 'receiver_side_modified_duration',\n", + " 'receiver_side_BPV']\n", + "data_financepy = [swap_rate,\n", + " payer_side_macaulay_duration, payer_side_modified_duration, payer_side_BPV,\n", + " receiver_side_macaulay_duration, receiver_side_modified_duration, receiver_side_BPV]\n", + "data_bondmath = [0.034,\n", + " np.nan, np.nan, np.nan,\n", + " np.nan, np.nan, np.nan]\n", + "data = {\n", + " 'financepy': data_financepy,\n", + " 'Bond Math by Donald J. Smith': data_bondmath\n", + "}\n", "\n", - "zero_curve_3m = DiscountCurveZeros(value_dt=value_dt.add_months(3),\n", - " zero_dts=zero_dts_3m,\n", - " zero_rates=yield_curve_3m,\n", - " freq_type=FrequencyTypes.QUARTERLY,\n", - " dc_type=DayCountTypes.THIRTY_360_BOND,\n", - " interp_type=InterpTypes.FINCUBIC_ZERO_RATES\n", - " )\n", - "\n" + "df = pd.DataFrame(data, index=index)\n", + "df" ], - "id": "8acc84107dae665e", - "outputs": [], - "execution_count": 93 + "id": "952ec880bd1140f2", + "outputs": [ + { + "data": { + "text/plain": [ + " financepy Bond Math by Donald J. Smith\n", + "swap_rate 0.034043 0.034\n", + "payer_side_macaulay_duration -6.767653 NaN\n", + "payer_side_modified_duration -6.710541 NaN\n", + "payer_side_BPV -10065.811298 NaN\n", + "receiver_side_macaulay_duration 6.767653 NaN\n", + "receiver_side_modified_duration 6.710541 NaN\n", + "receiver_side_BPV 10065.811298 NaN" + ], + "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", + "
financepyBond Math by Donald J. Smith
swap_rate0.0340430.034
payer_side_macaulay_duration-6.767653NaN
payer_side_modified_duration-6.710541NaN
payer_side_BPV-10065.811298NaN
receiver_side_macaulay_duration6.767653NaN
receiver_side_modified_duration6.710541NaN
receiver_side_BPV10065.811298NaN
\n", + "
" + ] + }, + "execution_count": 114, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 114 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "# 2.Calculation of indicators after 3 months", + "id": "39513cfdc5d7ef1" + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "## update the swap", + "id": "91abc51ca7618b13" }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:42.392700200Z", - "start_time": "2025-05-22T08:58:16.963888Z" + "end_time": "2025-05-23T06:49:49.459858Z", + "start_time": "2025-05-23T06:49:49.455501Z" } }, "cell_type": "code", "source": [ - "fixed_cpn_3m=0.034\n", - "swap_3m = IborSwap(start_dt.add_months(3),\n", - " maturity_dt,\n", - " swap_type,\n", - " fixed_cpn_3m,\n", - " fixed_freq_type,\n", - " fixed_dc_type,\n", - " notional,\n", - " float_spread,\n", - " float_freq_type,\n", - " float_dc_type,\n", - " swap_cal_type,\n", - " bd_type,\n", - " dg_type)\n", + "fixed_cpn_3m = 0.034\n", + "start_dt_3m = Date(1, 4, 2024)\n", + "maturity_dt_3m = start_dt_3m.add_years(1.75)\n", + "\n", + "swap_3m = IborSwap(start_dt_3m,\n", + " maturity_dt_3m,\n", + " swap_type,\n", + " fixed_cpn_3m,\n", + " fixed_freq_type,\n", + " fixed_dc_type,\n", + " notional,\n", + " float_spread,\n", + " float_freq_type,\n", + " float_dc_type,\n", + " swap_cal_type,\n", + " bd_type,\n", + " dg_type)\n", "\n" ], "id": "5892081fb637c810", "outputs": [], - "execution_count": 99 + "execution_count": 115 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:42.392700200Z", - "start_time": "2025-05-22T08:58:18.380055Z" + "end_time": "2025-05-23T06:49:49.556137Z", + "start_time": "2025-05-23T06:49:49.552270Z" } }, "cell_type": "code", - "source": "swap_3m.value(value_dt.add_months(3), zero_curve_3m)", - "id": "3aa3b8731a5cc344", + "source": [ + "value_date_3m = dt.datetime(2024, 3, 31)\n", + "value_dt_3m = from_datetime(value_date_3m)\n", + "\n", + "settle_dt_3m = value_dt_3m.add_weekdays(0)\n", + "settle_dt_3m" + ], + "id": "9093bbf0197b619b", "outputs": [ { "data": { "text/plain": [ - "-1103746.1176017257" + "31-MAR-2024" ] }, - "execution_count": 100, + "execution_count": 116, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 100 + "execution_count": 116 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "## update the yield curve", + "id": "28ea6f9df37837d0" }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:42.392700200Z", - "start_time": "2025-05-22T08:58:11.381600Z" + "end_time": "2025-05-23T06:49:49.640192Z", + "start_time": "2025-05-23T06:49:49.634830Z" } }, "cell_type": "code", - "source": "swap_3m.swap_rate(value_dt.add_months(3), zero_curve_3m)", - "id": "5d8fe704908ed541", - "outputs": [ - { - "data": { - "text/plain": [ - "0.023244839642912206" - ] - }, - "execution_count": 98, - "metadata": {}, - "output_type": "execute_result" - } + "source": [ + "yield_curve_3m = [i / 100 for i in [0.75, 1.1853, 1.6822, 2.1474, 2.4371, 2.7390, 3.0198]]\n", + "zero_dts_3m = value_dt_3m.add_years([0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75])\n", + "\n", + "zero_curve_3m = DiscountCurveZeros(value_dt_3m,\n", + " zero_dts=zero_dts_3m,\n", + " zero_rates=yield_curve_3m,\n", + " freq_type=FrequencyTypes.QUARTERLY,\n", + " dc_type=DayCountTypes.THIRTY_360_BOND,\n", + " interp_type=InterpTypes.FINCUBIC_ZERO_RATES\n", + " )\n", + "\n" ], - "execution_count": 98 + "id": "8acc84107dae665e", + "outputs": [], + "execution_count": 117 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:42.394185900Z", - "start_time": "2025-05-22T08:15:54.437385Z" + "end_time": "2025-05-23T06:49:49.701074Z", + "start_time": "2025-05-23T06:49:49.696661Z" } }, "cell_type": "code", - "source": "swap_3m.payer_side_macaulay_duration(value_dt.add_months(3), zero_curve_3m)", - "id": "f9c796b4218d3df7", + "source": "swap_3m.value(value_dt_3m, zero_curve_3m)", + "id": "3aa3b8731a5cc344", "outputs": [ { "data": { "text/plain": [ - "-5.879831278293125" + "-404094.769553849" ] }, - "execution_count": 76, + "execution_count": 118, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 76 + "execution_count": 118 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T01:25:42.394185900Z", - "start_time": "2025-05-22T08:15:55.564419Z" + "end_time": "2025-05-23T06:49:49.753341Z", + "start_time": "2025-05-23T06:49:49.741421Z" } }, "cell_type": "code", - "source": "start_dt.add_months(3)", - "id": "68a6d3e4cc14ee91", + "source": [ + "data_financepy_3m = [swap_3m.swap_rate(value_dt_3m, zero_curve_3m),\n", + " swap_3m.payer_side_macaulay_duration(value_dt_3m, zero_curve_3m, payment_periods),\n", + " swap_3m.payer_side_modified_duration(value_dt_3m, zero_curve_3m, payment_periods),\n", + " swap_3m.payer_side_BPV(value_dt_3m, zero_curve_3m, payment_periods),\n", + " swap_3m.receiver_side_macaulay_duration(value_dt_3m, zero_curve_3m, payment_periods),\n", + " swap_3m.receiver_side_modified_duration(value_dt_3m, zero_curve_3m, payment_periods),\n", + " swap_3m.receiver_side_BPV(value_dt_3m, zero_curve_3m, payment_periods)]\n", + "\n", + "data_bondmath_3m = [0.030,\n", + " -6.769,\n", + " -6.7186,\n", + " -10077.9,\n", + " 6.769,\n", + " 6.7186,\n", + " 10077.9]\n", + "\n", + "data_3m = {\n", + " 'financepy': data_financepy_3m,\n", + " 'Bond Math by Donald J. Smith': data_bondmath_3m\n", + "}\n", + "\n", + "df3m = pd.DataFrame(data_3m, index=index)\n", + "df3m" + ], + "id": "9e64b678f01aa539", "outputs": [ { "data": { "text/plain": [ - "01-APR-2024" + " financepy Bond Math by Donald J. Smith\n", + "swap_rate 0.030066 0.0300\n", + "payer_side_macaulay_duration -6.794199 -6.7690\n", + "payer_side_modified_duration -6.743512 -6.7186\n", + "payer_side_BPV -10115.267497 -10077.9000\n", + "receiver_side_macaulay_duration 6.794199 6.7690\n", + "receiver_side_modified_duration 6.743512 6.7186\n", + "receiver_side_BPV 10115.267497 10077.9000" + ], + "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", + "
financepyBond Math by Donald J. Smith
swap_rate0.0300660.0300
payer_side_macaulay_duration-6.794199-6.7690
payer_side_modified_duration-6.743512-6.7186
payer_side_BPV-10115.267497-10077.9000
receiver_side_macaulay_duration6.7941996.7690
receiver_side_modified_duration6.7435126.7186
receiver_side_BPV10115.26749710077.9000
\n", + "
" ] }, - "execution_count": 77, + "execution_count": 119, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 77 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-23T01:25:42.394185900Z", - "start_time": "2025-05-22T08:19:10.564739Z" - } - }, - "cell_type": "code", - "source": "swap_3m.print_payments()", - "id": "68097534a5e9ba5e", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "START DATE: 01-APR-2024\n", - "MATURITY DATE: 02-JAN-2026\n", - "COUPON (%): 3.0\n", - "FREQUENCY: FrequencyTypes.QUARTERLY\n", - "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", - "\n", - "PAYMENTS SCHEDULE:\n", - "+---------+-------------+-------------+-------------+------+----------+------+----------+\n", - "| PAY_NUM | PAY_dt | ACCR_START | ACCR_END | DAYS | YEARFRAC | RATE | PMNT |\n", - "+---------+-------------+-------------+-------------+------+----------+------+----------+\n", - "| 1 | 01-JUL-2024 | 01-APR-2024 | 01-JUL-2024 | 90 | 0.25 | 3.0 | 450000.0 |\n", - "| 2 | 01-OCT-2024 | 01-JUL-2024 | 01-OCT-2024 | 90 | 0.25 | 3.0 | 450000.0 |\n", - "| 3 | 02-JAN-2025 | 01-OCT-2024 | 02-JAN-2025 | 91 | 0.2528 | 3.0 | 455000.0 |\n", - "| 4 | 01-APR-2025 | 02-JAN-2025 | 01-APR-2025 | 89 | 0.2472 | 3.0 | 445000.0 |\n", - "| 5 | 01-JUL-2025 | 01-APR-2025 | 01-JUL-2025 | 90 | 0.25 | 3.0 | 450000.0 |\n", - "| 6 | 01-OCT-2025 | 01-JUL-2025 | 01-OCT-2025 | 90 | 0.25 | 3.0 | 450000.0 |\n", - "| 7 | 02-JAN-2026 | 01-OCT-2025 | 02-JAN-2026 | 91 | 0.2528 | 3.0 | 455000.0 |\n", - "+---------+-------------+-------------+-------------+------+----------+------+----------+\n", - "START DATE: 01-APR-2024\n", - "MATURITY DATE: 02-JAN-2026\n", - "SPREAD (bp): 0.0\n", - "FREQUENCY: FrequencyTypes.QUARTERLY\n", - "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", - "\n", - "PAYMENTS SCHEDULE:\n", - "+---------+-------------+-------------+-------------+------+----------+\n", - "| PAY_NUM | PAY_dt | ACCR_START | ACCR_END | DAYS | YEARFRAC |\n", - "+---------+-------------+-------------+-------------+------+----------+\n", - "| 1 | 01-JUL-2024 | 01-APR-2024 | 01-JUL-2024 | 90 | 0.25 |\n", - "| 2 | 01-OCT-2024 | 01-JUL-2024 | 01-OCT-2024 | 90 | 0.25 |\n", - "| 3 | 02-JAN-2025 | 01-OCT-2024 | 02-JAN-2025 | 91 | 0.2528 |\n", - "| 4 | 01-APR-2025 | 02-JAN-2025 | 01-APR-2025 | 89 | 0.2472 |\n", - "| 5 | 01-JUL-2025 | 01-APR-2025 | 01-JUL-2025 | 90 | 0.25 |\n", - "| 6 | 01-OCT-2025 | 01-JUL-2025 | 01-OCT-2025 | 90 | 0.25 |\n", - "| 7 | 02-JAN-2026 | 01-OCT-2025 | 02-JAN-2026 | 91 | 0.2528 |\n", - "+---------+-------------+-------------+-------------+------+----------+\n" - ] - } - ], - "execution_count": 83 + "execution_count": 119 } ], "metadata": { diff --git a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb index ca5a8b00..5c6d719b 100644 --- a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb +++ b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample3M.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.516595Z", - "start_time": "2025-05-23T03:01:38.512089Z" + "end_time": "2025-05-23T03:11:32.198988Z", + "start_time": "2025-05-23T03:11:30.509140Z" } }, "source": [ @@ -16,14 +16,27 @@ "from financepy.market.curves import *\n", "import datetime as dt\n" ], - "outputs": [], - "execution_count": 22 + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "####################################################################\n", + "# FINANCEPY BETA Version 0.370 - This build: 28 Oct 2024 at 20:29 #\n", + "# This software is distributed FREE AND WITHOUT ANY WARRANTY #\n", + "# Report bugs as issues at https://github.com/domokane/FinancePy #\n", + "####################################################################\n", + "\n" + ] + } + ], + "execution_count": 1 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.528444Z", - "start_time": "2025-05-23T03:01:38.524597Z" + "end_time": "2025-05-23T03:11:32.234873Z", + "start_time": "2025-05-23T03:11:32.208415Z" } }, "cell_type": "code", @@ -63,13 +76,13 @@ ], "id": "1ebf43316efee835", "outputs": [], - "execution_count": 23 + "execution_count": 2 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.542275Z", - "start_time": "2025-05-23T03:01:38.537531Z" + "end_time": "2025-05-23T03:11:32.413949Z", + "start_time": "2025-05-23T03:11:32.408884Z" } }, "cell_type": "code", @@ -89,26 +102,22 @@ "31-MAR-2024" ] }, - "execution_count": 24, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 24 + "execution_count": 3 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.569014Z", - "start_time": "2025-05-23T03:01:38.564129Z" + "end_time": "2025-05-23T03:11:32.447267Z", + "start_time": "2025-05-23T03:11:32.442210Z" } }, "cell_type": "code", "source": [ - "# 书中曲线\n", - "# yield_curve=[i/100 for i in [0.75,1.1853,1.6822,2.1474,2.4371,2.7390,3.0198]]\n", - "# zero_dts=value_dt.add_years([0.25,0.5,0.75,1.0,1.25,1.5,1.75])\n", - "\n", "\n", "yield_curve=[i/100 for i in [0.75,1.1853,1.6822,2.1474,2.4371,2.7390,3.0198]]\n", "zero_dts=value_dt.add_years([0.25,0.5,0.75,1.0,1.25,1.5,1.75])\n", @@ -123,13 +132,13 @@ ], "id": "290b7a06932da41d", "outputs": [], - "execution_count": 25 + "execution_count": 4 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.585724Z", - "start_time": "2025-05-23T03:01:38.579955Z" + "end_time": "2025-05-23T03:11:32.461542Z", + "start_time": "2025-05-23T03:11:32.456727Z" } }, "cell_type": "code", @@ -142,18 +151,18 @@ "0.030065619962567392" ] }, - "execution_count": 26, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 26 + "execution_count": 5 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.610747Z", - "start_time": "2025-05-23T03:01:38.606021Z" + "end_time": "2025-05-23T03:11:32.501044Z", + "start_time": "2025-05-23T03:11:32.497430Z" } }, "cell_type": "code", @@ -166,18 +175,18 @@ "-404094.769553849" ] }, - "execution_count": 27, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 27 + "execution_count": 6 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.645126Z", - "start_time": "2025-05-23T03:01:38.640384Z" + "end_time": "2025-05-23T03:11:32.548673Z", + "start_time": "2025-05-23T03:11:32.543824Z" } }, "cell_type": "code", @@ -190,18 +199,18 @@ "-6.794198629548404" ] }, - "execution_count": 28, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 28 + "execution_count": 7 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.686342Z", - "start_time": "2025-05-23T03:01:38.681347Z" + "end_time": "2025-05-23T03:11:32.591829Z", + "start_time": "2025-05-23T03:11:32.587918Z" } }, "cell_type": "code", @@ -214,18 +223,18 @@ "6.794198629548404" ] }, - "execution_count": 29, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 29 + "execution_count": 8 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.722907Z", - "start_time": "2025-05-23T03:01:38.717909Z" + "end_time": "2025-05-23T03:11:32.651777Z", + "start_time": "2025-05-23T03:11:32.646875Z" } }, "cell_type": "code", @@ -238,18 +247,18 @@ "-6.743511664816526" ] }, - "execution_count": 30, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 30 + "execution_count": 9 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.756278Z", - "start_time": "2025-05-23T03:01:38.751727Z" + "end_time": "2025-05-23T03:11:32.715295Z", + "start_time": "2025-05-23T03:11:32.711182Z" } }, "cell_type": "code", @@ -262,150 +271,18 @@ "6.743511664816526" ] }, - "execution_count": 31, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 31 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.790007Z", - "start_time": "2025-05-23T03:01:38.786002Z" - } - }, - "cell_type": "code", - "source": "swap.print_payments()", - "id": "2af7c95b07730dda", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "START DATE: 01-APR-2024\n", - "MATURITY DATE: 02-JAN-2026\n", - "COUPON (%): 3.4000000000000004\n", - "FREQUENCY: FrequencyTypes.QUARTERLY\n", - "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", - "\n", - "PAYMENTS SCHEDULE:\n", - "+---------+-------------+-------------+-------------+------+----------+------+-----------+\n", - "| PAY_NUM | PAY_dt | ACCR_START | ACCR_END | DAYS | YEARFRAC | RATE | PMNT |\n", - "+---------+-------------+-------------+-------------+------+----------+------+-----------+\n", - "| 1 | 01-JUL-2024 | 01-APR-2024 | 01-JUL-2024 | 90 | 0.25 | 3.4 | 510000.0 |\n", - "| 2 | 01-OCT-2024 | 01-JUL-2024 | 01-OCT-2024 | 90 | 0.25 | 3.4 | 510000.0 |\n", - "| 3 | 02-JAN-2025 | 01-OCT-2024 | 02-JAN-2025 | 91 | 0.2528 | 3.4 | 515666.67 |\n", - "| 4 | 01-APR-2025 | 02-JAN-2025 | 01-APR-2025 | 89 | 0.2472 | 3.4 | 504333.33 |\n", - "| 5 | 01-JUL-2025 | 01-APR-2025 | 01-JUL-2025 | 90 | 0.25 | 3.4 | 510000.0 |\n", - "| 6 | 01-OCT-2025 | 01-JUL-2025 | 01-OCT-2025 | 90 | 0.25 | 3.4 | 510000.0 |\n", - "| 7 | 02-JAN-2026 | 01-OCT-2025 | 02-JAN-2026 | 91 | 0.2528 | 3.4 | 515666.67 |\n", - "+---------+-------------+-------------+-------------+------+----------+------+-----------+\n", - "START DATE: 01-APR-2024\n", - "MATURITY DATE: 02-JAN-2026\n", - "SPREAD (bp): 0.0\n", - "FREQUENCY: FrequencyTypes.QUARTERLY\n", - "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", - "\n", - "PAYMENTS SCHEDULE:\n", - "+---------+-------------+-------------+-------------+------+----------+\n", - "| PAY_NUM | PAY_dt | ACCR_START | ACCR_END | DAYS | YEARFRAC |\n", - "+---------+-------------+-------------+-------------+------+----------+\n", - "| 1 | 01-JUL-2024 | 01-APR-2024 | 01-JUL-2024 | 90 | 0.25 |\n", - "| 2 | 01-OCT-2024 | 01-JUL-2024 | 01-OCT-2024 | 90 | 0.25 |\n", - "| 3 | 02-JAN-2025 | 01-OCT-2024 | 02-JAN-2025 | 91 | 0.2528 |\n", - "| 4 | 01-APR-2025 | 02-JAN-2025 | 01-APR-2025 | 89 | 0.2472 |\n", - "| 5 | 01-JUL-2025 | 01-APR-2025 | 01-JUL-2025 | 90 | 0.25 |\n", - "| 6 | 01-OCT-2025 | 01-JUL-2025 | 01-OCT-2025 | 90 | 0.25 |\n", - "| 7 | 02-JAN-2026 | 01-OCT-2025 | 02-JAN-2026 | 91 | 0.2528 |\n", - "+---------+-------------+-------------+-------------+------+----------+\n" - ] - } - ], - "execution_count": 32 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.821049Z", - "start_time": "2025-05-23T03:01:38.816897Z" - } - }, - "cell_type": "code", - "source": "swap.print_float_leg_pv()", - "id": "1c960526153c2223", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "START DATE: 01-APR-2024\n", - "MATURITY DATE: 02-JAN-2026\n", - "SPREAD (BPS): 0.0\n", - "FREQUENCY: FrequencyTypes.QUARTERLY\n", - "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", - "\n", - "PAYMENTS VALUATION:\n", - "+---------+-------------+----------+--------+-----------+--------+-----------+------------+\n", - "| PAY_NUM | PAY_dt | NOTIONAL | IBOR | PMNT | DF | PV | CUM_PV |\n", - "+---------+-------------+----------+--------+-----------+--------+-----------+------------+\n", - "| 1 | 01-JUL-2024 | 60000000 | 0.7529 | 112935.42 | 0.9981 | 112722.26 | 112722.26 |\n", - "| 2 | 01-OCT-2024 | 60000000 | 1.6379 | 245686.53 | 0.994 | 244222.77 | 356945.03 |\n", - "| 3 | 02-JAN-2025 | 60000000 | 2.7131 | 411480.71 | 0.9873 | 406243.16 | 763188.19 |\n", - "| 4 | 01-APR-2025 | 60000000 | 3.5062 | 520087.02 | 0.9788 | 509054.52 | 1272242.71 |\n", - "| 5 | 01-JUL-2025 | 60000000 | 3.588 | 538192.75 | 0.9701 | 522093.07 | 1794335.78 |\n", - "| 6 | 01-OCT-2025 | 60000000 | 4.293 | 643955.16 | 0.9598 | 618058.3 | 2412394.08 |\n", - "| 7 | 02-JAN-2026 | 60000000 | 4.6963 | 712268.76 | 0.9485 | 675604.45 | 3087998.53 |\n", - "+---------+-------------+----------+--------+-----------+--------+-----------+------------+\n" - ] - } - ], - "execution_count": 33 + "execution_count": 10 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T03:01:38.907291Z", - "start_time": "2025-05-23T03:01:38.903054Z" - } - }, - "cell_type": "code", - "source": "swap.print_fixed_leg_pv()", - "id": "5037651021a91d38", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "START DATE: 01-APR-2024\n", - "MATURITY DATE: 02-JAN-2026\n", - "COUPON (%): 3.4000000000000004\n", - "FREQUENCY: FrequencyTypes.QUARTERLY\n", - "DAY COUNT: DayCountTypes.THIRTY_360_BOND\n", - "\n", - "PAYMENTS VALUATION:\n", - "+---------+-------------+----------+------+-----------+--------+-----------+------------+\n", - "| PAY_NUM | PAY_dt | NOTIONAL | RATE | PMNT | DF | PV | CUM_PV |\n", - "+---------+-------------+----------+------+-----------+--------+-----------+------------+\n", - "| 1 | 01-JUL-2024 | 60000000 | 3.4 | 510000.0 | 0.9981 | 509037.4 | 509037.4 |\n", - "| 2 | 01-OCT-2024 | 60000000 | 3.4 | 510000.0 | 0.994 | 506961.51 | 1015998.91 |\n", - "| 3 | 02-JAN-2025 | 60000000 | 3.4 | 515666.67 | 0.9873 | 509102.98 | 1525101.88 |\n", - "| 4 | 01-APR-2025 | 60000000 | 3.4 | 504333.33 | 0.9788 | 493635.02 | 2018736.9 |\n", - "| 5 | 01-JUL-2025 | 60000000 | 3.4 | 510000.0 | 0.9701 | 494743.69 | 2513480.58 |\n", - "| 6 | 01-OCT-2025 | 60000000 | 3.4 | 510000.0 | 0.9598 | 489490.19 | 3002970.77 |\n", - "| 7 | 02-JAN-2026 | 60000000 | 3.4 | 515666.67 | 0.9485 | 489122.52 | 3492093.3 |\n", - "+---------+-------------+----------+------+-----------+--------+-----------+------------+\n" - ] - } - ], - "execution_count": 34 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-23T03:01:39.014637Z", - "start_time": "2025-05-23T03:01:39.009932Z" + "end_time": "2025-05-23T03:11:32.914101Z", + "start_time": "2025-05-23T03:11:32.909828Z" } }, "cell_type": "code", @@ -422,18 +299,18 @@ "-404610.6998889916" ] }, - "execution_count": 35, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 35 + "execution_count": 14 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T03:01:39.056730Z", - "start_time": "2025-05-23T03:01:39.050609Z" + "end_time": "2025-05-23T03:11:32.963176Z", + "start_time": "2025-05-23T03:11:32.958757Z" } }, "cell_type": "code", @@ -446,18 +323,18 @@ "404610.6998889916" ] }, - "execution_count": 36, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 36 + "execution_count": 15 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T03:01:39.100675Z", - "start_time": "2025-05-23T03:01:39.095809Z" + "end_time": "2025-05-23T03:11:33.018125Z", + "start_time": "2025-05-23T03:11:33.013277Z" } }, "cell_type": "code", @@ -467,30 +344,39 @@ { "data": { "text/plain": [ - "-40461.06998889916" + "-10115.26749722479" ] }, - "execution_count": 37, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 37 + "execution_count": 16 }, { "metadata": { - "jupyter": { - "is_executing": true - }, "ExecuteTime": { - "start_time": "2025-05-23T03:01:55.128551Z" + "end_time": "2025-05-23T03:11:33.062580Z", + "start_time": "2025-05-23T03:11:33.058102Z" } }, "cell_type": "code", "source": "swap.receiver_side_BPV(value_dt, zero_curve,payment_periods)", "id": "8f8630e90883e3b5", - "outputs": [], - "execution_count": null + "outputs": [ + { + "data": { + "text/plain": [ + "10115.26749722479" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 17 } ], "metadata": { From 07c7560a9a6264f94b8bfa28571d5cd4b516fa1b Mon Sep 17 00:00:00 2001 From: piper0124 <55830785+piper0124@users.noreply.github.com> Date: Fri, 23 May 2025 15:16:51 +0800 Subject: [PATCH 09/12] Update FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb --- ..._ReplicationgBONDMATHDurationExample.ipynb | 155 ++++++++++-------- 1 file changed, 84 insertions(+), 71 deletions(-) diff --git a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb index f42cf92e..1fee0073 100644 --- a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb +++ b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-05-23T06:49:48.974729Z", - "start_time": "2025-05-23T06:49:48.971287Z" + "end_time": "2025-05-23T07:06:02.211270Z", + "start_time": "2025-05-23T07:06:00.531998Z" } }, "source": [ @@ -17,14 +17,27 @@ "import datetime as dt\n", "import numpy as np" ], - "outputs": [], - "execution_count": 101 + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "####################################################################\n", + "# FINANCEPY BETA Version 0.370 - This build: 28 Oct 2024 at 20:29 #\n", + "# This software is distributed FREE AND WITHOUT ANY WARRANTY #\n", + "# Report bugs as issues at https://github.com/domokane/FinancePy #\n", + "####################################################################\n", + "\n" + ] + } + ], + "execution_count": 1 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.013677Z", - "start_time": "2025-05-23T06:49:49.009267Z" + "end_time": "2025-05-23T07:06:02.229480Z", + "start_time": "2025-05-23T07:06:02.224272Z" } }, "cell_type": "code", @@ -42,12 +55,12 @@ "'\\nCalculating the Duration and Other Interest Rate Sensitivity Metrics in an Interest Rate Swap\\nBased on Bond Math: The Theory Behind the Formulas, Second Edition by Donald J. Smith\\n'" ] }, - "execution_count": 102, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 102 + "execution_count": 2 }, { "metadata": {}, @@ -64,8 +77,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.022425Z", - "start_time": "2025-05-23T06:49:49.018680Z" + "end_time": "2025-05-23T07:06:02.422259Z", + "start_time": "2025-05-23T07:06:02.399642Z" } }, "cell_type": "code", @@ -104,13 +117,13 @@ ], "id": "1ebf43316efee835", "outputs": [], - "execution_count": 103 + "execution_count": 3 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.040866Z", - "start_time": "2025-05-23T06:49:49.037306Z" + "end_time": "2025-05-23T07:06:02.435325Z", + "start_time": "2025-05-23T07:06:02.431814Z" } }, "cell_type": "code", @@ -121,7 +134,7 @@ ], "id": "8e46f142f3cba5d7", "outputs": [], - "execution_count": 104 + "execution_count": 4 }, { "metadata": {}, @@ -132,8 +145,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.057895Z", - "start_time": "2025-05-23T06:49:49.053646Z" + "end_time": "2025-05-23T07:06:02.451098Z", + "start_time": "2025-05-23T07:06:02.444694Z" } }, "cell_type": "code", @@ -151,7 +164,7 @@ ], "id": "290b7a06932da41d", "outputs": [], - "execution_count": 105 + "execution_count": 5 }, { "metadata": {}, @@ -162,8 +175,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.073393Z", - "start_time": "2025-05-23T06:49:49.068617Z" + "end_time": "2025-05-23T07:06:02.466457Z", + "start_time": "2025-05-23T07:06:02.460826Z" } }, "cell_type": "code", @@ -179,12 +192,12 @@ "0.03404344249149676" ] }, - "execution_count": 106, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 106 + "execution_count": 6 }, { "metadata": {}, @@ -195,8 +208,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.111806Z", - "start_time": "2025-05-23T06:49:49.108411Z" + "end_time": "2025-05-23T07:06:02.510127Z", + "start_time": "2025-05-23T07:06:02.504897Z" } }, "cell_type": "code", @@ -209,18 +222,18 @@ "5070.7014320408925" ] }, - "execution_count": 107, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 107 + "execution_count": 7 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.152954Z", - "start_time": "2025-05-23T06:49:49.148624Z" + "end_time": "2025-05-23T07:06:02.538230Z", + "start_time": "2025-05-23T07:06:02.532904Z" } }, "cell_type": "code", @@ -237,18 +250,18 @@ "-6.767653343606554" ] }, - "execution_count": 108, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 108 + "execution_count": 8 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.197909Z", - "start_time": "2025-05-23T06:49:49.192534Z" + "end_time": "2025-05-23T07:06:02.577877Z", + "start_time": "2025-05-23T07:06:02.572337Z" } }, "cell_type": "code", @@ -264,18 +277,18 @@ "6.767653343606554" ] }, - "execution_count": 109, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 109 + "execution_count": 9 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.243489Z", - "start_time": "2025-05-23T06:49:49.239303Z" + "end_time": "2025-05-23T07:06:02.618947Z", + "start_time": "2025-05-23T07:06:02.613773Z" } }, "cell_type": "code", @@ -291,18 +304,18 @@ "-6.710540865595371" ] }, - "execution_count": 110, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 110 + "execution_count": 10 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.288001Z", - "start_time": "2025-05-23T06:49:49.283397Z" + "end_time": "2025-05-23T07:06:02.657887Z", + "start_time": "2025-05-23T07:06:02.652511Z" } }, "cell_type": "code", @@ -318,18 +331,18 @@ "6.710540865595371" ] }, - "execution_count": 111, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 111 + "execution_count": 11 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.340211Z", - "start_time": "2025-05-23T06:49:49.332412Z" + "end_time": "2025-05-23T07:06:02.688544Z", + "start_time": "2025-05-23T07:06:02.683201Z" } }, "cell_type": "code", @@ -345,18 +358,18 @@ "-10065.811298393059" ] }, - "execution_count": 112, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 112 + "execution_count": 12 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.366870Z", - "start_time": "2025-05-23T06:49:49.360337Z" + "end_time": "2025-05-23T07:06:02.730798Z", + "start_time": "2025-05-23T07:06:02.725738Z" } }, "cell_type": "code", @@ -372,18 +385,18 @@ "10065.811298393059" ] }, - "execution_count": 113, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 113 + "execution_count": 13 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.412316Z", - "start_time": "2025-05-23T06:49:49.405136Z" + "end_time": "2025-05-23T07:06:02.778884Z", + "start_time": "2025-05-23T07:06:02.769818Z" } }, "cell_type": "code", @@ -487,12 +500,12 @@ "" ] }, - "execution_count": 114, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 114 + "execution_count": 14 }, { "metadata": {}, @@ -509,8 +522,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.459858Z", - "start_time": "2025-05-23T06:49:49.455501Z" + "end_time": "2025-05-23T07:06:02.844522Z", + "start_time": "2025-05-23T07:06:02.840897Z" } }, "cell_type": "code", @@ -536,13 +549,13 @@ ], "id": "5892081fb637c810", "outputs": [], - "execution_count": 115 + "execution_count": 15 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.556137Z", - "start_time": "2025-05-23T06:49:49.552270Z" + "end_time": "2025-05-23T07:06:02.897137Z", + "start_time": "2025-05-23T07:06:02.892722Z" } }, "cell_type": "code", @@ -561,12 +574,12 @@ "31-MAR-2024" ] }, - "execution_count": 116, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 116 + "execution_count": 16 }, { "metadata": {}, @@ -577,8 +590,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.640192Z", - "start_time": "2025-05-23T06:49:49.634830Z" + "end_time": "2025-05-23T07:06:03.045244Z", + "start_time": "2025-05-23T07:06:03.040708Z" } }, "cell_type": "code", @@ -597,13 +610,13 @@ ], "id": "8acc84107dae665e", "outputs": [], - "execution_count": 117 + "execution_count": 17 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.701074Z", - "start_time": "2025-05-23T06:49:49.696661Z" + "end_time": "2025-05-23T07:06:03.139582Z", + "start_time": "2025-05-23T07:06:03.133116Z" } }, "cell_type": "code", @@ -616,18 +629,18 @@ "-404094.769553849" ] }, - "execution_count": 118, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 118 + "execution_count": 18 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T06:49:49.753341Z", - "start_time": "2025-05-23T06:49:49.741421Z" + "end_time": "2025-05-23T07:06:03.193803Z", + "start_time": "2025-05-23T07:06:03.181239Z" } }, "cell_type": "code", @@ -734,12 +747,12 @@ "" ] }, - "execution_count": 119, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 119 + "execution_count": 19 } ], "metadata": { From bbf5e8d5df9d488b4f5a5644c5ea9e7c38a79435 Mon Sep 17 00:00:00 2001 From: piper0124 <55830785+piper0124@users.noreply.github.com> Date: Mon, 26 May 2025 09:11:43 +0800 Subject: [PATCH 10/12] Use swap.Type to determine which indicators to output. --- financepy/products/rates/ibor_swap.py | 99 +++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/financepy/products/rates/ibor_swap.py b/financepy/products/rates/ibor_swap.py index c57566c8..56c9c557 100644 --- a/financepy/products/rates/ibor_swap.py +++ b/financepy/products/rates/ibor_swap.py @@ -417,6 +417,44 @@ def payer_side_macaulay_duration(self, value_dt, discount_curve, payment_periods mac_duration = 1 - (md1 - md2 / md3) return mac_duration + + def macaulay_duration(self, value_dt, discount_curve, swap_type, payment_periods: float): + """Calculation of the Payer's Macaulay Duration in an Interest Rate Swap + Based on Bond Math: The Theory Behind the Formulas, Second Edition by + Donald J. Smith + """ + # Get coupon frequency + coupon_frequency = self.fixed_leg.freq_type.value + + # Get swap rate + swap_rate_val = self.swap_rate(value_dt,discount_curve) + + y = swap_rate_val / coupon_frequency + # payment_periods is the number of periods to maturity as of the beginning of the period; + # for instance, If the start date of the interest rate swap is 2024-01-01, + # the end date is 2025-12-31, the valuation date is 2024-03-31, + # and the frequency type is QUARTERLY, + # then the number of payment periods is 8; it is independent of the valuation date. + N = payment_periods + c = swap_rate_val / coupon_frequency + md1 = (1 + y) / y + md2 = 1 + y + (N * (c - y)) + md3 = c * ((1 + y) ** N - 1) + y + + """ + The party who pays the fixed rate(ie,SwapTypes.PAY) is known as the payer + and the party who receives the fixed rate is known as the receiver. + The fixed‐rate payer and floating‐rate receiver, + sometimes is said to be the “buyer” of the swap, or is “long” the swap. + """ + if swap_type == SwapTypes.PAY: + mac_duration = 1 - (md1 - md2 / md3) + + elif swap_type == SwapTypes.RECEIVE: + mac_duration = (1 - (md1 - md2 / md3))*(-1) + + return mac_duration + ########################################################################### def receiver_side_macaulay_duration(self, value_dt, discount_curve,payment_periods: float): @@ -440,6 +478,27 @@ def payer_side_modified_duration(self, value_dt, discount_curve,payment_periods: return self.payer_side_macaulay_duration(value_dt, discount_curve,payment_periods)/(1+swap_rate_val/coupon_frequency) + def modified_duration(self, value_dt, discount_curve, swap_type, payment_periods: float): + """Computation of the Modified Duration for the Fixed-Rate + Payer's Perspective in Interest Rate Swap + """ + # Get coupon frequency + coupon_frequency = self.fixed_leg.freq_type.value + + # Get swap rate + swap_rate_val = self.swap_rate(value_dt, discount_curve) + + if swap_type == SwapTypes.PAY: + modified_duration = (self.macaulay_duration(value_dt, discount_curve, swap_type, payment_periods) / + (1 + swap_rate_val / coupon_frequency)) + + elif swap_type == SwapTypes.RECEIVE: + modified_duration = (self.macaulay_duration(value_dt, discount_curve, swap_type, payment_periods) / + (1 + swap_rate_val / coupon_frequency)) + + return modified_duration + + ########################################################################### def receiver_side_modified_duration(self, value_dt, discount_curve,payment_periods: float): @@ -472,6 +531,25 @@ def receiver_side_profits(self, """ return self.payer_side_profits(value_dt, discount_curve,payment_periods,swap_rate_changes)*(-1) + + def change_in_market_value(self, + value_dt, discount_curve, swap_type, payment_periods: float, + swap_rate_changes: float): + """Computation of the Profits for the Fixed-Rate Payer's Perspective in Interest Rate Swap + """ + # Get coupon frequency + coupon_frequency = self.fixed_leg.freq_type.value + + annualized_modi_dur = self.modified_duration(value_dt, discount_curve,swap_type,payment_periods)/coupon_frequency + + if swap_type == SwapTypes.PAY: + delta_mv=(-1)*(annualized_modi_dur*self.fixed_leg.notional*swap_rate_changes) + + elif swap_type == SwapTypes.RECEIVE: + delta_mv=(annualized_modi_dur*self.fixed_leg.notional*swap_rate_changes) + + return delta_mv + ########################################################################### def payer_side_BPV(self, value_dt, discount_curve,payment_periods: float,): @@ -499,6 +577,27 @@ def receiver_side_BPV(self, value_dt, discount_curve,payment_periods: float,): ########################################################################### + def basis_point_value (self,value_dt, discount_curve,swap_type, payment_periods: float): + """ + calculate the basis‐point‐value (BPV) for the payer_side of the swap, + which is swap's modified duration times the notional principal, + times one basis point (0.0001) + """ + bp=0.0001 + # Get coupon frequency + coupon_frequency = self.fixed_leg.freq_type.value + modi_dur = self.modified_duration(value_dt, discount_curve, swap_type, payment_periods) + annualized_modi_dur= modi_dur/coupon_frequency + + if swap_type == SwapTypes.PAY: + BPV=annualized_modi_dur*self.fixed_leg.notional*bp + + elif swap_type == SwapTypes.RECEIVE: + BPV=annualized_modi_dur*self.fixed_leg.notional*bp*(-1) + + return BPV + + def __repr__(self): s = label_to_string("OBJECT TYPE", type(self).__name__) From ff0750a4e1138ac3e7808ca363c091a071dc53bc Mon Sep 17 00:00:00 2001 From: piper0124 <55830785+piper0124@users.noreply.github.com> Date: Mon, 26 May 2025 09:40:21 +0800 Subject: [PATCH 11/12] modified BVP --- financepy/products/rates/ibor_swap.py | 2 +- ..._ReplicationgBONDMATHDurationExample.ipynb | 224 +++++++++++------- 2 files changed, 141 insertions(+), 85 deletions(-) diff --git a/financepy/products/rates/ibor_swap.py b/financepy/products/rates/ibor_swap.py index 56c9c557..e01b27ee 100644 --- a/financepy/products/rates/ibor_swap.py +++ b/financepy/products/rates/ibor_swap.py @@ -593,7 +593,7 @@ def basis_point_value (self,value_dt, discount_curve,swap_type, payment_periods: BPV=annualized_modi_dur*self.fixed_leg.notional*bp elif swap_type == SwapTypes.RECEIVE: - BPV=annualized_modi_dur*self.fixed_leg.notional*bp*(-1) + BPV=annualized_modi_dur*self.fixed_leg.notional*bp return BPV diff --git a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb index 1fee0073..c0f5d1b9 100644 --- a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb +++ b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.211270Z", - "start_time": "2025-05-23T07:06:00.531998Z" + "end_time": "2025-05-26T01:38:51.362248Z", + "start_time": "2025-05-26T01:38:51.358838Z" } }, "source": [ @@ -17,27 +17,14 @@ "import datetime as dt\n", "import numpy as np" ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "####################################################################\n", - "# FINANCEPY BETA Version 0.370 - This build: 28 Oct 2024 at 20:29 #\n", - "# This software is distributed FREE AND WITHOUT ANY WARRANTY #\n", - "# Report bugs as issues at https://github.com/domokane/FinancePy #\n", - "####################################################################\n", - "\n" - ] - } - ], - "execution_count": 1 + "outputs": [], + "execution_count": 53 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.229480Z", - "start_time": "2025-05-23T07:06:02.224272Z" + "end_time": "2025-05-26T01:38:51.410238Z", + "start_time": "2025-05-26T01:38:51.406404Z" } }, "cell_type": "code", @@ -55,12 +42,12 @@ "'\\nCalculating the Duration and Other Interest Rate Sensitivity Metrics in an Interest Rate Swap\\nBased on Bond Math: The Theory Behind the Formulas, Second Edition by Donald J. Smith\\n'" ] }, - "execution_count": 2, + "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 2 + "execution_count": 54 }, { "metadata": {}, @@ -77,8 +64,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.422259Z", - "start_time": "2025-05-23T07:06:02.399642Z" + "end_time": "2025-05-26T01:38:51.453736Z", + "start_time": "2025-05-26T01:38:51.448754Z" } }, "cell_type": "code", @@ -117,13 +104,13 @@ ], "id": "1ebf43316efee835", "outputs": [], - "execution_count": 3 + "execution_count": 55 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.435325Z", - "start_time": "2025-05-23T07:06:02.431814Z" + "end_time": "2025-05-26T01:38:51.479888Z", + "start_time": "2025-05-26T01:38:51.476727Z" } }, "cell_type": "code", @@ -134,7 +121,7 @@ ], "id": "8e46f142f3cba5d7", "outputs": [], - "execution_count": 4 + "execution_count": 56 }, { "metadata": {}, @@ -145,8 +132,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.451098Z", - "start_time": "2025-05-23T07:06:02.444694Z" + "end_time": "2025-05-26T01:38:51.489195Z", + "start_time": "2025-05-26T01:38:51.484891Z" } }, "cell_type": "code", @@ -164,7 +151,7 @@ ], "id": "290b7a06932da41d", "outputs": [], - "execution_count": 5 + "execution_count": 57 }, { "metadata": {}, @@ -175,8 +162,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.466457Z", - "start_time": "2025-05-23T07:06:02.460826Z" + "end_time": "2025-05-26T01:38:51.510004Z", + "start_time": "2025-05-26T01:38:51.503319Z" } }, "cell_type": "code", @@ -192,12 +179,12 @@ "0.03404344249149676" ] }, - "execution_count": 6, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 6 + "execution_count": 58 }, { "metadata": {}, @@ -208,8 +195,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.510127Z", - "start_time": "2025-05-23T07:06:02.504897Z" + "end_time": "2025-05-26T01:38:51.544573Z", + "start_time": "2025-05-26T01:38:51.538275Z" } }, "cell_type": "code", @@ -222,18 +209,18 @@ "5070.7014320408925" ] }, - "execution_count": 7, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 7 + "execution_count": 59 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.538230Z", - "start_time": "2025-05-23T07:06:02.532904Z" + "end_time": "2025-05-26T01:38:51.589597Z", + "start_time": "2025-05-26T01:38:51.584447Z" } }, "cell_type": "code", @@ -250,18 +237,18 @@ "-6.767653343606554" ] }, - "execution_count": 8, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 8 + "execution_count": 60 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.577877Z", - "start_time": "2025-05-23T07:06:02.572337Z" + "end_time": "2025-05-26T01:38:51.643031Z", + "start_time": "2025-05-26T01:38:51.637701Z" } }, "cell_type": "code", @@ -277,18 +264,18 @@ "6.767653343606554" ] }, - "execution_count": 9, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 9 + "execution_count": 61 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.618947Z", - "start_time": "2025-05-23T07:06:02.613773Z" + "end_time": "2025-05-26T01:38:51.675717Z", + "start_time": "2025-05-26T01:38:51.669055Z" } }, "cell_type": "code", @@ -304,18 +291,18 @@ "-6.710540865595371" ] }, - "execution_count": 10, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 10 + "execution_count": 62 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.657887Z", - "start_time": "2025-05-23T07:06:02.652511Z" + "end_time": "2025-05-26T01:38:51.714007Z", + "start_time": "2025-05-26T01:38:51.709011Z" } }, "cell_type": "code", @@ -331,18 +318,18 @@ "6.710540865595371" ] }, - "execution_count": 11, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 11 + "execution_count": 63 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.688544Z", - "start_time": "2025-05-23T07:06:02.683201Z" + "end_time": "2025-05-26T01:38:51.740994Z", + "start_time": "2025-05-26T01:38:51.735899Z" } }, "cell_type": "code", @@ -358,18 +345,18 @@ "-10065.811298393059" ] }, - "execution_count": 12, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 12 + "execution_count": 64 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.730798Z", - "start_time": "2025-05-23T07:06:02.725738Z" + "end_time": "2025-05-26T01:38:51.782002Z", + "start_time": "2025-05-26T01:38:51.775583Z" } }, "cell_type": "code", @@ -385,18 +372,18 @@ "10065.811298393059" ] }, - "execution_count": 13, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 13 + "execution_count": 65 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.778884Z", - "start_time": "2025-05-23T07:06:02.769818Z" + "end_time": "2025-05-26T01:38:51.811959Z", + "start_time": "2025-05-26T01:38:51.805127Z" } }, "cell_type": "code", @@ -500,12 +487,68 @@ "" ] }, - "execution_count": 14, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 14 + "execution_count": 66 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-26T01:38:51.857020Z", + "start_time": "2025-05-26T01:38:51.849735Z" + } + }, + "cell_type": "code", + "source": [ + "print(swap.macaulay_duration(value_dt, zero_curve, SwapTypes.PAY,payment_periods),\n", + " swap.modified_duration(value_dt, zero_curve, SwapTypes.PAY,payment_periods),\n", + " swap.basis_point_value(value_dt, zero_curve, SwapTypes.PAY,payment_periods))\n" + ], + "id": "7f15cd3f76dfa4c1", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-6.767653343606554 -6.710540865595371 -10065.811298393059\n" + ] + } + ], + "execution_count": 67 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "", + "id": "61aa4eb194b3d1d0" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-26T01:38:52.000991Z", + "start_time": "2025-05-26T01:38:51.995187Z" + } + }, + "cell_type": "code", + "source": [ + "print(swap.macaulay_duration(value_dt, zero_curve, SwapTypes.RECEIVE,payment_periods),\n", + " swap.modified_duration(value_dt, zero_curve, SwapTypes.RECEIVE,payment_periods),\n", + " swap.basis_point_value(value_dt, zero_curve, SwapTypes.RECEIVE,payment_periods))" + ], + "id": "a5c75d93d6565468", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6.767653343606554 6.710540865595371 -10065.811298393059\n" + ] + } + ], + "execution_count": 68 }, { "metadata": {}, @@ -522,8 +565,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.844522Z", - "start_time": "2025-05-23T07:06:02.840897Z" + "end_time": "2025-05-26T01:38:52.113722Z", + "start_time": "2025-05-26T01:38:52.109152Z" } }, "cell_type": "code", @@ -549,13 +592,13 @@ ], "id": "5892081fb637c810", "outputs": [], - "execution_count": 15 + "execution_count": 69 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:02.897137Z", - "start_time": "2025-05-23T07:06:02.892722Z" + "end_time": "2025-05-26T01:38:52.183795Z", + "start_time": "2025-05-26T01:38:52.179481Z" } }, "cell_type": "code", @@ -574,12 +617,12 @@ "31-MAR-2024" ] }, - "execution_count": 16, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 16 + "execution_count": 70 }, { "metadata": {}, @@ -590,8 +633,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:03.045244Z", - "start_time": "2025-05-23T07:06:03.040708Z" + "end_time": "2025-05-26T01:38:52.215532Z", + "start_time": "2025-05-26T01:38:52.211001Z" } }, "cell_type": "code", @@ -610,13 +653,13 @@ ], "id": "8acc84107dae665e", "outputs": [], - "execution_count": 17 + "execution_count": 71 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:03.139582Z", - "start_time": "2025-05-23T07:06:03.133116Z" + "end_time": "2025-05-26T01:38:52.258023Z", + "start_time": "2025-05-26T01:38:52.253684Z" } }, "cell_type": "code", @@ -629,18 +672,18 @@ "-404094.769553849" ] }, - "execution_count": 18, + "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 18 + "execution_count": 72 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-23T07:06:03.193803Z", - "start_time": "2025-05-23T07:06:03.181239Z" + "end_time": "2025-05-26T01:38:52.303046Z", + "start_time": "2025-05-26T01:38:52.290708Z" } }, "cell_type": "code", @@ -747,12 +790,25 @@ "" ] }, - "execution_count": 19, + "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 19 + "execution_count": 73 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-05-26T01:38:52.350298Z", + "start_time": "2025-05-26T01:38:52.347838Z" + } + }, + "cell_type": "code", + "source": "", + "id": "25eb3b1fb6b97bde", + "outputs": [], + "execution_count": null } ], "metadata": { From a3efa4995e708dbd6acec4846f23df202165d7ad Mon Sep 17 00:00:00 2001 From: piper0124 <55830785+piper0124@users.noreply.github.com> Date: Mon, 26 May 2025 13:32:07 +0800 Subject: [PATCH 12/12] Upgraded the computation of multiple metrics 1. Use the class member fixed_leg_type(SwapTypes.PAY or SwapTypes.RECEIVE) to determine whether a counterparty is a buyer or seller, and calculate the Macaulay duration, modified duration, basis point value (BPV), and present value change of the interest rate swap (IRS). 2. Updated the notebook: FINIBORSWAP_ReplicationBONDMATHDurationExample.ipynb. --- financepy/products/rates/ibor_swap.py | 148 +----- ..._ReplicationgBONDMATHDurationExample.ipynb | 473 ++++++------------ 2 files changed, 159 insertions(+), 462 deletions(-) diff --git a/financepy/products/rates/ibor_swap.py b/financepy/products/rates/ibor_swap.py index e01b27ee..efc770ba 100644 --- a/financepy/products/rates/ibor_swap.py +++ b/financepy/products/rates/ibor_swap.py @@ -392,34 +392,8 @@ def print_payments(self): ########################################################################### - def payer_side_macaulay_duration(self, value_dt, discount_curve, payment_periods: float): - """Calculation of the Payer's Macaulay Duration in an Interest Rate Swap - Based on Bond Math: The Theory Behind the Formulas, Second Edition by - Donald J. Smith - """ - # Get coupon frequency - coupon_frequency = self.fixed_leg.freq_type.value - - # Get swap rate - swap_rate_val = self.swap_rate(value_dt,discount_curve) - - y = swap_rate_val / coupon_frequency - # payment_periods is the number of periods to maturity as of the beginning of the period; - # for instance, If the start date of the interest rate swap is 2024-01-01, - # the end date is 2025-12-31, the valuation date is 2024-03-31, - # and the frequency type is QUARTERLY, - # then the number of payment periods is 8; it is independent of the valuation date. - N = payment_periods - c = swap_rate_val / coupon_frequency - md1 = (1 + y) / y - md2 = 1 + y + (N * (c - y)) - md3 = c * ((1 + y) ** N - 1) + y - mac_duration = 1 - (md1 - md2 / md3) - return mac_duration - - def macaulay_duration(self, value_dt, discount_curve, swap_type, payment_periods: float): - """Calculation of the Payer's Macaulay Duration in an Interest Rate Swap + """Calculation of the Macaulay Duration in an Interest Rate Swap Based on Bond Math: The Theory Behind the Formulas, Second Edition by Donald J. Smith """ @@ -427,7 +401,7 @@ def macaulay_duration(self, value_dt, discount_curve, swap_type, payment_periods coupon_frequency = self.fixed_leg.freq_type.value # Get swap rate - swap_rate_val = self.swap_rate(value_dt,discount_curve) + swap_rate_val = self.swap_rate(value_dt, discount_curve) y = swap_rate_val / coupon_frequency # payment_periods is the number of periods to maturity as of the beginning of the period; @@ -447,37 +421,16 @@ def macaulay_duration(self, value_dt, discount_curve, swap_type, payment_periods The fixed‐rate payer and floating‐rate receiver, sometimes is said to be the “buyer” of the swap, or is “long” the swap. """ - if swap_type == SwapTypes.PAY: + if swap_type == SwapTypes.PAY: mac_duration = 1 - (md1 - md2 / md3) - elif swap_type == SwapTypes.RECEIVE: - mac_duration = (1 - (md1 - md2 / md3))*(-1) + elif swap_type == SwapTypes.RECEIVE: + mac_duration = (1 - (md1 - md2 / md3)) * (-1) return mac_duration ########################################################################### - def receiver_side_macaulay_duration(self, value_dt, discount_curve,payment_periods: float): - """Calculation of the Receiver's Macaulay Duration in an Interest Rate Swap - Based on Bond Math: The Theory Behind the Formulas, Second Edition by - Donald J. Smith - """ - return self.payer_side_macaulay_duration(value_dt, discount_curve,payment_periods)*(-1) - - ########################################################################### - - def payer_side_modified_duration(self, value_dt, discount_curve,payment_periods: float): - """Computation of the Modified Duration for the Fixed-Rate - Payer's Perspective in Interest Rate Swap - """ - # Get coupon frequency - coupon_frequency = self.fixed_leg.freq_type.value - - # Get swap rate - swap_rate_val = self.swap_rate(value_dt,discount_curve) - - return self.payer_side_macaulay_duration(value_dt, discount_curve,payment_periods)/(1+swap_rate_val/coupon_frequency) - def modified_duration(self, value_dt, discount_curve, swap_type, payment_periods: float): """Computation of the Modified Duration for the Fixed-Rate Payer's Perspective in Interest Rate Swap @@ -488,50 +441,13 @@ def modified_duration(self, value_dt, discount_curve, swap_type, payment_periods # Get swap rate swap_rate_val = self.swap_rate(value_dt, discount_curve) - if swap_type == SwapTypes.PAY: - modified_duration = (self.macaulay_duration(value_dt, discount_curve, swap_type, payment_periods) / - (1 + swap_rate_val / coupon_frequency)) - - elif swap_type == SwapTypes.RECEIVE: - modified_duration = (self.macaulay_duration(value_dt, discount_curve, swap_type, payment_periods) / - (1 + swap_rate_val / coupon_frequency)) + modified_duration = (self.macaulay_duration(value_dt, discount_curve, swap_type, payment_periods) / + (1 + swap_rate_val / coupon_frequency)) return modified_duration - ########################################################################### - def receiver_side_modified_duration(self, value_dt, discount_curve,payment_periods: float): - """Computation of the Modified Duration for the Fixed-Rate - Receiver's Perspective in Interest Rate Swap - """ - return self.payer_side_modified_duration(value_dt, discount_curve,payment_periods)*(-1) - - ########################################################################### - - def payer_side_profits(self, - value_dt, - discount_curve,payment_periods: float, - swap_rate_changes: float ): - """Computation of the Profits for the Fixed-Rate Payer's Perspective in Interest Rate Swap - """ - # Get coupon frequency - coupon_frequency = self.fixed_leg.freq_type.value - - annualized_modi_dur = self.payer_side_modified_duration(value_dt, discount_curve,payment_periods)/coupon_frequency - return (-1)*(annualized_modi_dur*self.fixed_leg.notional*swap_rate_changes) - - ########################################################################### - - def receiver_side_profits(self, - value_dt, - discount_curve,payment_periods: float, - swap_rate_changes: float): - """Computation of the Profits for the Fixed-Rate Receiver's Perspective in Interest Rate Swap - """ - return self.payer_side_profits(value_dt, discount_curve,payment_periods,swap_rate_changes)*(-1) - - def change_in_market_value(self, value_dt, discount_curve, swap_type, payment_periods: float, swap_rate_changes: float): @@ -540,63 +456,31 @@ def change_in_market_value(self, # Get coupon frequency coupon_frequency = self.fixed_leg.freq_type.value - annualized_modi_dur = self.modified_duration(value_dt, discount_curve,swap_type,payment_periods)/coupon_frequency + annualized_modi_dur = self.modified_duration(value_dt, discount_curve, swap_type, + payment_periods) / coupon_frequency - if swap_type == SwapTypes.PAY: - delta_mv=(-1)*(annualized_modi_dur*self.fixed_leg.notional*swap_rate_changes) - - elif swap_type == SwapTypes.RECEIVE: - delta_mv=(annualized_modi_dur*self.fixed_leg.notional*swap_rate_changes) + delta_mv = (-1) * (annualized_modi_dur * self.fixed_leg.notional * swap_rate_changes) return delta_mv ########################################################################### - def payer_side_BPV(self, value_dt, discount_curve,payment_periods: float,): + def basis_point_value(self, value_dt, discount_curve, swap_type, payment_periods: float): """ - calculate the basis‐point‐value (BPV) for the payer_side of the swap, + calculate the basis‐point‐value (BPV) of the swap, which is swap's modified duration times the notional principal, times one basis point (0.0001) """ - bp=0.0001 - # Get coupon frequency - coupon_frequency = self.fixed_leg.freq_type.value - modi_dur = self.payer_side_modified_duration(value_dt, discount_curve,payment_periods) - annualized_modi_dur= modi_dur/coupon_frequency - return annualized_modi_dur*self.fixed_leg.notional*bp - - ########################################################################### - - def receiver_side_BPV(self, value_dt, discount_curve,payment_periods: float,): - """ - calculate the basis‐point‐value (BPV) for receiver_side of the swap, - which is swap's modified duration times the notional principal, - times one basis point (0.0001) - """ - return self.payer_side_BPV(value_dt, discount_curve,payment_periods)*(-1) - - ########################################################################### - - def basis_point_value (self,value_dt, discount_curve,swap_type, payment_periods: float): - """ - calculate the basis‐point‐value (BPV) for the payer_side of the swap, - which is swap's modified duration times the notional principal, - times one basis point (0.0001) - """ - bp=0.0001 + bp = 0.0001 # Get coupon frequency coupon_frequency = self.fixed_leg.freq_type.value modi_dur = self.modified_duration(value_dt, discount_curve, swap_type, payment_periods) - annualized_modi_dur= modi_dur/coupon_frequency - - if swap_type == SwapTypes.PAY: - BPV=annualized_modi_dur*self.fixed_leg.notional*bp - - elif swap_type == SwapTypes.RECEIVE: - BPV=annualized_modi_dur*self.fixed_leg.notional*bp + annualized_modi_dur = modi_dur / coupon_frequency + BPV = annualized_modi_dur * self.fixed_leg.notional * bp return BPV + ########################################################################### def __repr__(self): diff --git a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb index c0f5d1b9..34c69cf8 100644 --- a/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb +++ b/notebooks/products/rates/FINIBORSWAP_ReplicationgBONDMATHDurationExample.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.362248Z", - "start_time": "2025-05-26T01:38:51.358838Z" + "end_time": "2025-05-26T05:31:31.795273Z", + "start_time": "2025-05-26T05:31:31.791904Z" } }, "source": [ @@ -18,36 +18,24 @@ "import numpy as np" ], "outputs": [], - "execution_count": 53 + "execution_count": 15 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.410238Z", - "start_time": "2025-05-26T01:38:51.406404Z" + "end_time": "2025-05-26T05:31:31.835720Z", + "start_time": "2025-05-26T05:31:31.831900Z" } }, "cell_type": "code", "source": [ - " \"\"\"\n", - "Calculating the Duration and Other Interest Rate Sensitivity Metrics in an Interest Rate Swap\n", - "Based on Bond Math: The Theory Behind the Formulas, Second Edition by Donald J. Smith\n", - " \"\"\"" + "# Calculating the macaulay_duration, modified_duration, basis‐point‐value and change_in_value_of_IRS\n", + "# in an Interest Rate Swap\n", + "# Based on Bond Math: The Theory Behind the Formulas, Second Edition by Donald J. Smith\n" ], "id": "1771696c9beba73c", - "outputs": [ - { - "data": { - "text/plain": [ - "'\\nCalculating the Duration and Other Interest Rate Sensitivity Metrics in an Interest Rate Swap\\nBased on Bond Math: The Theory Behind the Formulas, Second Edition by Donald J. Smith\\n'" - ] - }, - "execution_count": 54, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 54 + "outputs": [], + "execution_count": 16 }, { "metadata": {}, @@ -64,8 +52,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.453736Z", - "start_time": "2025-05-26T01:38:51.448754Z" + "end_time": "2025-05-26T05:31:31.847045Z", + "start_time": "2025-05-26T05:31:31.842610Z" } }, "cell_type": "code", @@ -104,13 +92,13 @@ ], "id": "1ebf43316efee835", "outputs": [], - "execution_count": 55 + "execution_count": 17 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.479888Z", - "start_time": "2025-05-26T01:38:51.476727Z" + "end_time": "2025-05-26T05:31:31.862908Z", + "start_time": "2025-05-26T05:31:31.858964Z" } }, "cell_type": "code", @@ -121,7 +109,7 @@ ], "id": "8e46f142f3cba5d7", "outputs": [], - "execution_count": 56 + "execution_count": 18 }, { "metadata": {}, @@ -132,8 +120,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.489195Z", - "start_time": "2025-05-26T01:38:51.484891Z" + "end_time": "2025-05-26T05:31:31.880211Z", + "start_time": "2025-05-26T05:31:31.875398Z" } }, "cell_type": "code", @@ -151,7 +139,7 @@ ], "id": "290b7a06932da41d", "outputs": [], - "execution_count": 57 + "execution_count": 19 }, { "metadata": {}, @@ -162,8 +150,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.510004Z", - "start_time": "2025-05-26T01:38:51.503319Z" + "end_time": "2025-05-26T05:31:31.898089Z", + "start_time": "2025-05-26T05:31:31.892320Z" } }, "cell_type": "code", @@ -179,12 +167,12 @@ "0.03404344249149676" ] }, - "execution_count": 58, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 58 + "execution_count": 20 }, { "metadata": {}, @@ -195,8 +183,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.544573Z", - "start_time": "2025-05-26T01:38:51.538275Z" + "end_time": "2025-05-26T05:31:31.945567Z", + "start_time": "2025-05-26T05:31:31.941273Z" } }, "cell_type": "code", @@ -209,198 +197,52 @@ "5070.7014320408925" ] }, - "execution_count": 59, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 59 + "execution_count": 21 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.589597Z", - "start_time": "2025-05-26T01:38:51.584447Z" + "end_time": "2025-05-26T05:31:31.990346Z", + "start_time": "2025-05-26T05:31:31.986969Z" } }, "cell_type": "code", - "source": [ - "payment_periods = 8\n", - "payer_side_macaulay_duration = swap.payer_side_macaulay_duration(value_dt, zero_curve, payment_periods)\n", - "payer_side_macaulay_duration" - ], + "source": "payment_periods = 8\n", "id": "3f988f26e6662203", - "outputs": [ - { - "data": { - "text/plain": [ - "-6.767653343606554" - ] - }, - "execution_count": 60, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 60 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.643031Z", - "start_time": "2025-05-26T01:38:51.637701Z" - } - }, - "cell_type": "code", - "source": [ - "receiver_side_macaulay_duration = swap.receiver_side_macaulay_duration(value_dt, zero_curve, payment_periods)\n", - "receiver_side_macaulay_duration" - ], - "id": "96d568f7a71861fe", - "outputs": [ - { - "data": { - "text/plain": [ - "6.767653343606554" - ] - }, - "execution_count": 61, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 61 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.675717Z", - "start_time": "2025-05-26T01:38:51.669055Z" - } - }, - "cell_type": "code", - "source": [ - "payer_side_modified_duration = swap.payer_side_modified_duration(value_dt, zero_curve, payment_periods)\n", - "payer_side_modified_duration" - ], - "id": "ec2187c7336c1bc3", - "outputs": [ - { - "data": { - "text/plain": [ - "-6.710540865595371" - ] - }, - "execution_count": 62, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 62 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.714007Z", - "start_time": "2025-05-26T01:38:51.709011Z" - } - }, - "cell_type": "code", - "source": [ - "receiver_side_modified_duration = swap.receiver_side_modified_duration(value_dt, zero_curve, payment_periods)\n", - "receiver_side_modified_duration" - ], - "id": "2c1b845afaf5e18f", - "outputs": [ - { - "data": { - "text/plain": [ - "6.710540865595371" - ] - }, - "execution_count": 63, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 63 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.740994Z", - "start_time": "2025-05-26T01:38:51.735899Z" - } - }, - "cell_type": "code", - "source": [ - "payer_side_BPV = swap.payer_side_BPV(value_dt, zero_curve, payment_periods)\n", - "payer_side_BPV" - ], - "id": "602fd89b88bd58f0", - "outputs": [ - { - "data": { - "text/plain": [ - "-10065.811298393059" - ] - }, - "execution_count": 64, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 64 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.782002Z", - "start_time": "2025-05-26T01:38:51.775583Z" - } - }, - "cell_type": "code", - "source": [ - "receiver_side_BPV = swap.receiver_side_BPV(value_dt, zero_curve, payment_periods)\n", - "receiver_side_BPV" - ], - "id": "b868dc3219fa867", - "outputs": [ - { - "data": { - "text/plain": [ - "10065.811298393059" - ] - }, - "execution_count": 65, - "metadata": {}, - "output_type": "execute_result" - } - ], - "execution_count": 65 + "outputs": [], + "execution_count": 22 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.811959Z", - "start_time": "2025-05-26T01:38:51.805127Z" + "end_time": "2025-05-26T05:31:32.021487Z", + "start_time": "2025-05-26T05:31:32.014011Z" } }, "cell_type": "code", "source": [ "index = ['swap_rate',\n", - " 'payer_side_macaulay_duration',\n", - " 'payer_side_modified_duration',\n", - " 'payer_side_BPV',\n", - " 'receiver_side_macaulay_duration',\n", - " 'receiver_side_modified_duration',\n", - " 'receiver_side_BPV']\n", + " 'payer_macaulay_duration',\n", + " 'payer_modified_duration',\n", + " 'payer_BPV',\n", + " 'change_in_value_of_payer',\n", + " 'receiver_macaulay_duration',\n", + " 'receiver_modified_duration',\n", + " 'receiver_BPV',\n", + " 'change_in_value_of_receiver', ]\n", + "\n", "data_financepy = [swap_rate,\n", - " payer_side_macaulay_duration, payer_side_modified_duration, payer_side_BPV,\n", - " receiver_side_macaulay_duration, receiver_side_modified_duration, receiver_side_BPV]\n", + " np.nan, np.nan, np.nan, np.nan,\n", + " np.nan, np.nan, np.nan, np.nan]\n", + "\n", "data_bondmath = [0.034,\n", - " np.nan, np.nan, np.nan,\n", - " np.nan, np.nan, np.nan]\n", + " np.nan, np.nan, np.nan, np.nan,\n", + " np.nan, np.nan, np.nan, np.nan, ]\n", "data = {\n", " 'financepy': data_financepy,\n", " 'Bond Math by Donald J. Smith': data_bondmath\n", @@ -414,14 +256,16 @@ { "data": { "text/plain": [ - " financepy Bond Math by Donald J. Smith\n", - "swap_rate 0.034043 0.034\n", - "payer_side_macaulay_duration -6.767653 NaN\n", - "payer_side_modified_duration -6.710541 NaN\n", - "payer_side_BPV -10065.811298 NaN\n", - "receiver_side_macaulay_duration 6.767653 NaN\n", - "receiver_side_modified_duration 6.710541 NaN\n", - "receiver_side_BPV 10065.811298 NaN" + " financepy Bond Math by Donald J. Smith\n", + "swap_rate 0.034043 0.034\n", + "payer_macaulay_duration NaN NaN\n", + "payer_modified_duration NaN NaN\n", + "payer_BPV NaN NaN\n", + "change_in_value_of_payer NaN NaN\n", + "receiver_macaulay_duration NaN NaN\n", + "receiver_modified_duration NaN NaN\n", + "receiver_BPV NaN NaN\n", + "change_in_value_of_receiver NaN NaN" ], "text/html": [ "
\n", @@ -453,33 +297,43 @@ " 0.034\n", " \n", " \n", - " payer_side_macaulay_duration\n", - " -6.767653\n", + " payer_macaulay_duration\n", + " NaN\n", " NaN\n", " \n", " \n", - " payer_side_modified_duration\n", - " -6.710541\n", + " payer_modified_duration\n", + " NaN\n", " NaN\n", " \n", " \n", - " payer_side_BPV\n", - " -10065.811298\n", + " payer_BPV\n", + " NaN\n", " NaN\n", " \n", " \n", - " receiver_side_macaulay_duration\n", - " 6.767653\n", + " change_in_value_of_payer\n", + " NaN\n", " NaN\n", " \n", " \n", - " receiver_side_modified_duration\n", - " 6.710541\n", + " receiver_macaulay_duration\n", + " NaN\n", " NaN\n", " \n", " \n", - " receiver_side_BPV\n", - " 10065.811298\n", + " receiver_modified_duration\n", + " NaN\n", + " NaN\n", + " \n", + " \n", + " receiver_BPV\n", + " NaN\n", + " NaN\n", + " \n", + " \n", + " change_in_value_of_receiver\n", + " NaN\n", " NaN\n", " \n", " \n", @@ -487,37 +341,12 @@ "
" ] }, - "execution_count": 66, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 66 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-26T01:38:51.857020Z", - "start_time": "2025-05-26T01:38:51.849735Z" - } - }, - "cell_type": "code", - "source": [ - "print(swap.macaulay_duration(value_dt, zero_curve, SwapTypes.PAY,payment_periods),\n", - " swap.modified_duration(value_dt, zero_curve, SwapTypes.PAY,payment_periods),\n", - " swap.basis_point_value(value_dt, zero_curve, SwapTypes.PAY,payment_periods))\n" - ], - "id": "7f15cd3f76dfa4c1", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "-6.767653343606554 -6.710540865595371 -10065.811298393059\n" - ] - } - ], - "execution_count": 67 + "execution_count": 23 }, { "metadata": {}, @@ -525,31 +354,6 @@ "source": "", "id": "61aa4eb194b3d1d0" }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-26T01:38:52.000991Z", - "start_time": "2025-05-26T01:38:51.995187Z" - } - }, - "cell_type": "code", - "source": [ - "print(swap.macaulay_duration(value_dt, zero_curve, SwapTypes.RECEIVE,payment_periods),\n", - " swap.modified_duration(value_dt, zero_curve, SwapTypes.RECEIVE,payment_periods),\n", - " swap.basis_point_value(value_dt, zero_curve, SwapTypes.RECEIVE,payment_periods))" - ], - "id": "a5c75d93d6565468", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "6.767653343606554 6.710540865595371 -10065.811298393059\n" - ] - } - ], - "execution_count": 68 - }, { "metadata": {}, "cell_type": "markdown", @@ -565,8 +369,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:52.113722Z", - "start_time": "2025-05-26T01:38:52.109152Z" + "end_time": "2025-05-26T05:31:32.072889Z", + "start_time": "2025-05-26T05:31:32.068405Z" } }, "cell_type": "code", @@ -592,13 +396,13 @@ ], "id": "5892081fb637c810", "outputs": [], - "execution_count": 69 + "execution_count": 24 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:52.183795Z", - "start_time": "2025-05-26T01:38:52.179481Z" + "end_time": "2025-05-26T05:31:32.129906Z", + "start_time": "2025-05-26T05:31:32.125936Z" } }, "cell_type": "code", @@ -617,12 +421,12 @@ "31-MAR-2024" ] }, - "execution_count": 70, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 70 + "execution_count": 25 }, { "metadata": {}, @@ -633,8 +437,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:52.215532Z", - "start_time": "2025-05-26T01:38:52.211001Z" + "end_time": "2025-05-26T05:31:32.243507Z", + "start_time": "2025-05-26T05:31:32.239142Z" } }, "cell_type": "code", @@ -653,13 +457,13 @@ ], "id": "8acc84107dae665e", "outputs": [], - "execution_count": 71 + "execution_count": 26 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:52.258023Z", - "start_time": "2025-05-26T01:38:52.253684Z" + "end_time": "2025-05-26T05:31:32.335358Z", + "start_time": "2025-05-26T05:31:32.329091Z" } }, "cell_type": "code", @@ -672,37 +476,47 @@ "-404094.769553849" ] }, - "execution_count": 72, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 72 + "execution_count": 27 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-05-26T01:38:52.303046Z", - "start_time": "2025-05-26T01:38:52.290708Z" + "end_time": "2025-05-26T05:31:32.392498Z", + "start_time": "2025-05-26T05:31:32.379861Z" } }, "cell_type": "code", "source": [ + "# a -40 basis point change in the swap fi xed rate\n", + "swap_rate_changes = -0.004\n", + "\n", "data_financepy_3m = [swap_3m.swap_rate(value_dt_3m, zero_curve_3m),\n", - " swap_3m.payer_side_macaulay_duration(value_dt_3m, zero_curve_3m, payment_periods),\n", - " swap_3m.payer_side_modified_duration(value_dt_3m, zero_curve_3m, payment_periods),\n", - " swap_3m.payer_side_BPV(value_dt_3m, zero_curve_3m, payment_periods),\n", - " swap_3m.receiver_side_macaulay_duration(value_dt_3m, zero_curve_3m, payment_periods),\n", - " swap_3m.receiver_side_modified_duration(value_dt_3m, zero_curve_3m, payment_periods),\n", - " swap_3m.receiver_side_BPV(value_dt_3m, zero_curve_3m, payment_periods)]\n", + " swap_3m.macaulay_duration(value_dt_3m, zero_curve_3m, swap_type, payment_periods),\n", + " swap_3m.modified_duration(value_dt_3m, zero_curve_3m, swap_type, payment_periods),\n", + " swap_3m.basis_point_value(value_dt_3m, zero_curve_3m, swap_type, payment_periods),\n", + " swap_3m.change_in_market_value(value_dt_3m, zero_curve_3m, swap_type, payment_periods,\n", + " swap_rate_changes),\n", + " swap_3m.macaulay_duration(value_dt_3m, zero_curve_3m, SwapTypes.RECEIVE, payment_periods),\n", + " swap_3m.modified_duration(value_dt_3m, zero_curve_3m, SwapTypes.RECEIVE, payment_periods),\n", + " swap_3m.basis_point_value(value_dt_3m, zero_curve_3m, SwapTypes.RECEIVE, payment_periods),\n", + " swap_3m.change_in_market_value(value_dt_3m, zero_curve_3m, SwapTypes.RECEIVE,\n", + " payment_periods, swap_rate_changes)\n", + " ]\n", "\n", "data_bondmath_3m = [0.030,\n", " -6.769,\n", " -6.7186,\n", " -10077.9,\n", + " -403116,\n", " 6.769,\n", " 6.7186,\n", - " 10077.9]\n", + " 10077.9,\n", + " 403116]\n", "\n", "data_3m = {\n", " 'financepy': data_financepy_3m,\n", @@ -717,14 +531,16 @@ { "data": { "text/plain": [ - " financepy Bond Math by Donald J. Smith\n", - "swap_rate 0.030066 0.0300\n", - "payer_side_macaulay_duration -6.794199 -6.7690\n", - "payer_side_modified_duration -6.743512 -6.7186\n", - "payer_side_BPV -10115.267497 -10077.9000\n", - "receiver_side_macaulay_duration 6.794199 6.7690\n", - "receiver_side_modified_duration 6.743512 6.7186\n", - "receiver_side_BPV 10115.267497 10077.9000" + " financepy Bond Math by Donald J. Smith\n", + "swap_rate 0.030066 0.0300\n", + "payer_macaulay_duration -6.794199 -6.7690\n", + "payer_modified_duration -6.743512 -6.7186\n", + "payer_BPV -10115.267497 -10077.9000\n", + "change_in_value_of_payer -404610.699889 -403116.0000\n", + "receiver_macaulay_duration 6.794199 6.7690\n", + "receiver_modified_duration 6.743512 6.7186\n", + "receiver_BPV 10115.267497 10077.9000\n", + "change_in_value_of_receiver 404610.699889 403116.0000" ], "text/html": [ "
\n", @@ -756,59 +572,56 @@ " 0.0300\n", " \n", " \n", - " payer_side_macaulay_duration\n", + " payer_macaulay_duration\n", " -6.794199\n", " -6.7690\n", " \n", " \n", - " payer_side_modified_duration\n", + " payer_modified_duration\n", " -6.743512\n", " -6.7186\n", " \n", " \n", - " payer_side_BPV\n", + " payer_BPV\n", " -10115.267497\n", " -10077.9000\n", " \n", " \n", - " receiver_side_macaulay_duration\n", + " change_in_value_of_payer\n", + " -404610.699889\n", + " -403116.0000\n", + " \n", + " \n", + " receiver_macaulay_duration\n", " 6.794199\n", " 6.7690\n", " \n", " \n", - " receiver_side_modified_duration\n", + " receiver_modified_duration\n", " 6.743512\n", " 6.7186\n", " \n", " \n", - " receiver_side_BPV\n", + " receiver_BPV\n", " 10115.267497\n", " 10077.9000\n", " \n", + " \n", + " change_in_value_of_receiver\n", + " 404610.699889\n", + " 403116.0000\n", + " \n", " \n", "\n", "
" ] }, - "execution_count": 73, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 73 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-05-26T01:38:52.350298Z", - "start_time": "2025-05-26T01:38:52.347838Z" - } - }, - "cell_type": "code", - "source": "", - "id": "25eb3b1fb6b97bde", - "outputs": [], - "execution_count": null + "execution_count": 28 } ], "metadata": {