{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "![](../images/logo.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Interest Rate Swap " ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [], "source": [ "from rivapy.pricing.interest_rate_swap_pricing import InterestRateSwapPricer\n", "from rivapy.instruments.ir_swap_specification import InterestRateSwapSpecification, IrFixedLegSpecification, IrFloatLegSpecification, InterestRateBasisSwapSpecification\n", "from rivapy.instruments.components import NotionalStructure, ConstNotionalStructure, VariableNotionalStructure, ResettingNotionalStructure\n", "from rivapy.pricing.pricing_data import (\n", " InterestRateSwapFloatLegPricingData_rivapy,\n", " InterestRateSwapLegPricingData_rivapy,\n", " InterestRateSwapPricingData_rivapy,\n", ")\n", "from rivapy.pricing.pricing_request import InterestRateSwapPricingRequest\n", "import datetime as dt\n", "from dateutil.relativedelta import relativedelta\n", "from rivapy.marketdata.curves import DiscountCurve\n", "from rivapy.tools.enums import InterpolationType, ExtrapolationType\n", "import math\n", "from dateutil.relativedelta import relativedelta\n", "\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Definition of an Interest Rate Swap\n", "\n", "The most common type of an interest rate swap is the so called \"plain vanilla\" interest rate swap. Here, a company agrees to pay cashflows equal to a predetermined fixed interest rate on a notional principal during a predetermined number of years; in return, it receives interest payments on the same notional principal at a floating rate during the same number of years.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Valuation of Interest Rate Swaps\n", "\n", "The value of a swap can, for example, be regarded as the difference between two bonds. From the point of view of a fixed-rate payer, the value of the swap can be regarded as a long position in a floating-rate bond and a short position in a fixed-rate bond.\n", "\n", "$$V_{swap} = B_{float} - B_{fix}$$\n", "\n", "Alternatively, a plain vanilla interest rate swap can also be valued making the assumption that forward interest rates are realized. Here, cashflows of the floating leg are calculated assuming that the floating interest rate equals the forward interest rates. The value of the interest rate swap is then the sum of the present value of the net cashflows. This method will be applied in the following.\n", "\n", "For a detailed discussion please refer to Hull, *Options, futures, and other derivatives, 8th Edition,* 2012, p. 148 ff.\n", "\n", "In the following, we will use a slighly modified example taken from Hull, p. 161 to illustrate how a interest rate swap can be valued.\n", "\n", "**Example:**\n", "\n", "*Suppose that a financial institution has agreed to pay 6-month EURIBOR and receive 8% per annum (with semiannual compounding) on a notional principal of €100 million. The swap has a remaining life of 1.5 years. The EURIBOR rates with continuous compunding for 6-month, 12-month, and 18-month maturities are 10%, 10.5%, and 11%, respectively.*\n", "\n", "### Valuation in terms of bond prices\n", "\n", "**Fixed Leg**\n", "\n", "The fixed-rate bond has three payment dates in 6 months, 12 months, and 18 months with a cashflow of €4 million at the first two payment dates and a cashflow of €4 million plus the notional of €100 million at the last payment date. These cashflows have to be discounted using the given EURIBOR rates and subsequently summed up to receive the present value of the fixed leg.\n", "\n", "**Floating Leg**\n", "\n", "The floating leg bond is worth the notional immediately after an interest payment because at this time, the bond is a \"fair deal\" where the borower pays EURIBOR for earch subsequent accrual period. Consequently, immediately before a payment, the bond is worth the notional plus the accrued interest. Also, this cashflow has to be discounted using the given EURIBOR rate." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-05-03T11:41:34.650886Z", "start_time": "2020-05-03T11:41:34.605224Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fixed Leg Value = 95.58716101349117\n", "Floating Leg Value = 100.00000000000001\n", "Interest Rate Swap Value = 4.412838986508845\n" ] } ], "source": [ "#Fixed Leg\n", "time_to_maturity = [0.5, 1.0, 1.5]\n", "cf_fix = [4, 4, 104]\n", "libor_rates = [0.1, 0.105, 0.11]\n", "\n", "df = [math.exp(-lr * tm) for lr, tm in zip(libor_rates, time_to_maturity)]\n", "\n", "pv_cf_fix = [cf * d for cf, d in zip(cf_fix, df)]\n", " \n", "B_fix = sum(pv_cf_fix)\n", "print(f'Fixed Leg Value = {B_fix}')\n", "\n", "\n", "# Floating Leg\n", "floating_rate = 2 * (math.exp(libor_rates[0] / 2) - 1) # libor rate has to be converted from contiuous compounding to semiannual comp.\n", "cf_float = 100 + (100 * floating_rate * 0.5) # 1/2 of converted libor_rate as semiannual payment\n", "pv_cf_float = cf_float * df[0] # discounted with 3-month libor rate\n", "print(f'Floating Leg Value = {pv_cf_float}')\n", "\n", "\n", "# Interest Rate Swap Valuation (from a fixed-rate payer's perspective)\n", "value = (pv_cf_float - B_fix)\n", "print(f'Interest Rate Swap Value = {value}')\n" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.0461656036199884" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pv_cf_float/B_fix" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Valuation in terms of forward rate agreements\n", "\n", "As before, the value of the interest rate swap is given as the difference between the present value of the fixed and the floating leg.\n", "\n", "**Fixed Leg**\n", "\n", "The present value of the fixed leg is given by:\n", "\n", "$$PV_{fixed}(t) = rN\\sum_{i=1}^n\\tau_iD_i$$\n", "\n", "with $t$ as the valuation date, $r$ as the interest rate of the fixed leg, $N$ as the notional principal, $i$ as the $i^{th}$ cashflow, $\\tau_i$ as the accrual period for the $i^{th}$ cashflow and $D_i$ as the discount factor of the $i^{th}$ period.\n", "\n", "\n", "\n", "\n", "**Floating Leg**\n", "\n", "The present value of the float leg is given by:\n", "\n", "$$PV_{floating}(t)=N\\sum_{i=1}^n(F_i+s)\\tau_iD_i$$\n", "\n", "with $F_i$ as the simply compounded forward rate defined as $\\frac{1}{\\tau_i}(\\frac{D_{i-1}}{D_i}-1)$, $s$ as the floating spread and the rest defined as before.\n", "\n", "Note that the discount factors used for the forward rate can be different from the discount factor used to discount the cashflows. Also, a different discount factors from the fixed leg are theoretically possible.\n", "\n", "The following code manually calculates the value of the interest-rate swap. Thereby, the formula for the floating leg assuming that there is no spread $s$ is simplified as follows:\n", "\n", "\\begin{align}\n", "PV_{floating}(t) & =N\\sum_{i=1}^n\\frac{1}{\\tau_i}(\\frac{D_{i-1}}{D_i}-1)\\tau_iD_i \\\\\n", "& =N\\sum_{i=1}^n(\\frac{D_{i-1}}{D_i}-1)D_i \\\\\n", "& =N\\sum_{i=1}^n(D_{i-1}-{D_i}) \\\\\n", "& =N\\cdot[(D_0-D_1)+(D_1-D_2)+ ... +(D_{n-2}-D_{n-1})+(D_{n-1}-D_n)] \\\\\n", "& =N\\cdot(D_0-D_n)\n", "\\end{align}" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "ExecuteTime": { "end_time": "2020-05-03T11:41:34.676710Z", "start_time": "2020-05-03T11:41:34.653879Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "PV fixed leg = 10.797790604699582\n", "PV floating leg = 15.210629591208413\n", "Interest rate swap value = 4.412838986508831\n" ] } ], "source": [ "ttm = [0.5, 1, 1.5]\n", "rates = [0.1, 0.105, 0.11]\n", "yf = [0.25, 0.75, 1.25]\n", "r = 0.08\n", "N = 100\n", "\n", "df = [math.exp(-r * tm) for r, tm in zip(rates, ttm)]\n", " \n", "PV_fix = r * N * ((ttm[0] - 0) * df[0] + (ttm[1] - ttm[0]) * df[1] + (ttm[2] - ttm[1]) * df[2])\n", "print('PV fixed leg =',PV_fix)\n", "\n", "PV_fl = N * (1 - df[2])\n", "print(f'PV floating leg = {PV_fl}')\n", "\n", "print(f'Interest rate swap value = {PV_fl-PV_fix}')\n", "\n", "# PV fixed leg = 10.797790604699582\n", "# PV floating leg = 15.210629591208413\n", "# Interest rate swap value = 4.412838986508831" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following code shows the manual calculation of the example as it is done by Hull:" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "ExecuteTime": { "end_time": "2020-05-03T11:41:34.715601Z", "start_time": "2020-05-03T11:41:34.679859Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Present value fixed leg = 10.797790604699582\n", "Present value floating leg = 15.210629591208429\n", "Interest rate swap value = 4.412838986508847\n" ] } ], "source": [ "ttm = [0.5, 1.0, 1.5]\n", "rates = [0.1, 0.105, 0.11]\n", "N = 100 # Notional\n", "m = 2 # compounding frequency\n", "\n", "ref_date = dt.datetime(2017, 1, 1)\n", "\n", "days_to_maturity = [180, 360, 540]\n", "dates = [ref_date + dt.timedelta(days=d) for d in days_to_maturity]\n", "\n", "# discount factors from constant rate\n", "df = [math.exp(-r * t) for r, t in zip(rates, ttm)]\n", "\n", "# Fixed leg\n", "fix_cf = [4, 4, 4]\n", "pv_fix = [cf * d for cf, d in zip(fix_cf, df)]\n", "print(f'Present value fixed leg = {sum(pv_fix)}')\n", "\n", "# Floating leg\n", "floatrates = [m * (math.exp(rates[0] / m) - 1)]\n", "\n", "for i in range(1, len(rates)):\n", " contrate = ((rates[i] * ttm[i] - rates[i-1] * ttm[i-1]) / (ttm[i] - ttm[i-1])) \n", " floatrates.append(m * (math.exp(contrate / m) - 1))\n", "\n", "float_cf = [fr * N / m for fr in floatrates]\n", "float_pv = [cf * d for cf, d in zip(float_cf, df)]\n", "print(f'Present value floating leg = {sum(float_pv)}')\n", "\n", "# Net Cash-Flow\n", "net_CF = [fl - fx for fl, fx in zip(float_cf, fix_cf)]\n", "pv_net_cf = [cf * d for cf, d in zip(net_CF, df)]\n", "value = sum(pv_net_cf)\n", "print(f'Interest rate swap value = {value}')\n", "\n", "# Present value fixed leg = 10.797790604699582\n", "# Present value floating leg = 15.210629591208427\n", "# Interest rate swap value = 4.412838986508847" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Valuation using the interest rate swap specification\n", "\n", "The pyvacon interest rate swap specification uses the valuation in terms of forward rate agreements. In order to price an interest rate swap, we need to create the necessary market data and setup the specification and pricing data.\n", "\n", "#### Create the necessary market data\n", "\n", "As a first step, we need to create a discount curve containing the discount factors to derive the present values of both, the fixed and the floating leg. Here, we use this discount curve to derive the forward rate discount factors as well. Theoretically, we could also use different discount curves for the fixed and floating legs as well as for the forward rates." ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "ExecuteTime": { "end_time": "2020-05-03T11:41:34.743388Z", "start_time": "2020-05-03T11:41:34.719071Z" } }, "outputs": [], "source": [ "# Discount curve - we use these discount factors to get the present values of both the fixed and floating leg as well as \n", "\n", "object_id = \"TEST_DC\" \n", "refdatedc = dt.datetime(2017, 1, 1)\n", "days_to_maturity = [180, 360, 540]\n", "dates = [refdatedc + dt.timedelta(days=d) for d in days_to_maturity]\n", "# discount factors from constant rate\n", "rates = [0.10, 0.105, 0.11]\n", "df = [math.exp(-r * d / 360) for r, d in zip(rates, days_to_maturity)]\n", "dc = DiscountCurve(id=object_id, refdate=refdatedc, dates=dates, df=df, interpolation=InterpolationType.LINEAR, extrapolation=ExtrapolationType.LINEAR)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [], "source": [ "# #test market data\n", "# ref_date = dt.datetime(2019, 8, 31)\n", "# dates = [dt.datetime(2019, 8, 31), dt.datetime(2020, 8, 31)]\n", "# df = [1.0, 0.9900633419771339]\n", "# dc = DiscountCurve(id=object_id, refdate=refdatedc, dates=dates, df=df, interpolation=InterpolationType.LINEAR, extrapolation=ExtrapolationType.LINEAR)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Setup the specification\n", "\n", "Here, we need to provide the start-, end-, and pay-dates for the different accrual periods. The floating leg additionally needs a reset date vector. Moreover, we need to define a vector of notionals since if needed, each accrual period may have a different notional. However, if the vector if of length 1, the same notional is applied to all periods.\n", "\n", "Having defined the relevant information, we are able to create the fixed- and floating leg. The fixed leg needs to be provided with the fixed rate; the float leg requires information about an eventual spread. Afterwards, the interest rate swap specification can be set." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setting up an interest rate swap\n", "A plain vanilla interest rate swap is a financial contract in which a stream of fixed payments is exchanged for floating payments linked to a reference index. The par rate (r) of a swap is the fixed rate under which the value of the two streams (legs) is equal:\n", "$$ \n", "r \\cdot \\sum_{i=1}^n dcf_{i} \\cdot P(0,t_{i} ) = \\sum_{k=1}^m F_{k} \\cdot dcf_{k} \\cdot P(0,t_{k})\n", "$$ \n", "\n", "where $t_{i}$, $i=1,..,n$ and $t_{k}$, $i=1,..,m$ are the payment structures of the fixed and floating legs, and $P(0,t_{i/k})$ are the corresponding discount factors, $dcf_{i/k}$ is the day count fraction for the period $[t_{(i/k-1)},t_{i/k}]$, and $F_{k}$ is the expected value of underlying reference rate for the period $[t_{(k-1)},t_{k}]$.\n", "\n", "The standard payment frequency of the fixed leg depends on the currency of the swap as well as the tenor of the underlying. In the EUR market swaps are usually quoted with annual fixed payments.\n", "\n", "The payment frequency of the floating leg usually coincides with the tenor of the underlying reference index. In some currencies, however, the floating rate can be compounded and payed out at less frequent intervals (e.g. CAD). \n", "\n", "In the context of pyvacon an IRS can be defined using an InterestRateSwapSpecification." ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "ExecuteTime": { "end_time": "2020-05-03T11:41:34.768148Z", "start_time": "2020-05-03T11:41:34.746471Z" } }, "outputs": [], "source": [ "# # Create the vectors defining the statdates, enddates, paydates and reset dates\n", "days_to_maturity = [0, 180, 360, 540]\n", "dates = [dt.datetime(2017, 1, 1) + dt.timedelta(d) for d in days_to_maturity]\n", "\n", "startdates = dates[:-1]\n", "enddates = dates[1:]\n", "paydates = enddates\n", "resetdates = startdates\n", "refdate = dates[0]\n", "\n", "\n", "fixed_leg = IrFixedLegSpecification(fixed_rate = 0.08, obj_id = 'dummy_fixed_leg', notional = 100.0, \n", " start_dates=startdates, end_dates=enddates, pay_dates=paydates, currency='EUR', day_count_convention='Act360')\n", "\n", "spread = 0.00\n", "\n", "\n", "ns = ConstNotionalStructure(100.0)\n", "\n", "float_leg =IrFloatLegSpecification(obj_id = 'dummy_float_leg', notional = ns, reset_dates=resetdates, start_dates=startdates, end_dates=enddates,\n", " rate_start_dates=startdates, rate_end_dates=enddates, pay_dates=paydates, currency = \"EUR\", \n", " udl_id=\"test_udl_id\", fixing_id=\"test_fixing_id\", day_count_convention=\"Act360\", spread=spread)\n", "\n", "maturity_date = refdate + dt.timedelta(600)\n", "\n", "ir_swap = InterestRateSwapSpecification(obj_id=\"dummy_swap\", notional=ns, issue_date=refdate, maturity_date=maturity_date,\n", " pay_leg=fixed_leg, receive_leg=float_leg,currency='EUR', day_count_convention=\"Act360\",\n", " issuer=\"DBK\", securitization_level=\"COLLATERALIZED\"\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Setup the pricing data\n", "\n", "A product may be priced in two different ways: One may either fill the respective pricing data needed for a special pricer (which inherits from th BasePricingData) and use the respective price method where just the pricing data is given. Another possibility is to use the price-method where the storages are given. In this case, the pricer will fill the needed pricing data according to the underlying and other data as specified in the product specification.\n", "\n", "**Here we show the approach explicitely setting the pricing data.** \n", "\n", "The pricing data needs the following information:\n", "- A discount curve for the pay- and receive leg\n", "- A discount curve for the floating leg (here the receiving leg)\n", "- A pricer\n", "- A pricing request\n", "- A valuation date\n", "- The created interest rate swap specification\n", "- An eventual FX-rate for the pay- and receive leg" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2020-05-03T11:41:34.791290Z", "start_time": "2020-05-03T11:41:34.773760Z" } }, "outputs": [], "source": [ "\n", "pay_leg_pricing_data = InterestRateSwapLegPricingData_rivapy(\n", " spec=ir_swap.get_pay_leg(),\n", " discount_curve=dc,\n", " forward_curve=dc, \n", " fixing_map = None, \n", " fx_rate=1.0,\n", " weight=-1.0\n", ")\n", "\n", "rec_leg_pricing_data = InterestRateSwapFloatLegPricingData_rivapy(\n", " spec=ir_swap.get_receive_leg(),\n", " discount_curve=dc,\n", " forward_curve=dc, \n", " fixing_map = None, \n", " fixing_grace_period=0, \n", " fixing_curve=dc,\n", " fx_rate=1.0,\n", " weight=1.0,\n", ")\n", "\n", "\n", "\n", "pricing_data_all = {}\n", "pricing_data_all['discount_curve_pay_leg'] =dc\n", "pricing_data_all['discount_curve_receive_leg']=dc\n", "pricing_data_all['fixing_curve_pay_leg']=dc\n", "pricing_data_all['fixing_curve_receive_leg']=dc\n", "pricing_data_all['fx_fwd_curve_pay_leg']=dc\n", "pricing_data_all['fx_fwd_curve_receive_leg']=dc\n", "pricing_data_all['pricing_param']={\"fixing_grace_period\": 0}\n", "pricing_data_all['fixing_map']=None\n", "pricing_data_all['fx_pay_leg']=1.0\n", "pricing_data_all['fx_receive_leg']=1.0\n", "\n", "ir_swap_pr = InterestRateSwapPricingRequest()\n", "\n" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [], "source": [ "ir_swap_pricing_data = InterestRateSwapPricingData_rivapy(\n", " spec=ir_swap,\n", " val_date=ref_date,\n", " pricing_request = ir_swap_pr,\n", " pricer=\"InterestRateSwapPricer\",\n", " ccy='EUR',\n", " leg_pricing_data=pricing_data_all # previousl this heald the pricingData objects for each leg...\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Pricing" ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "ExecuteTime": { "end_time": "2020-05-03T11:41:34.967821Z", "start_time": "2020-05-03T11:41:34.795052Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "runtime: 0:00:00.001991\n", "Price: 4.522463649728445\n" ] } ], "source": [ "# tic = dt.datetime.now()\n", "# pr = price(ir_swap_pricing_data)\n", "# print(f'runtime: {dt.datetime.now() - tic}')\n", "# pr.getPrice()\n", "\n", "\n", "tic = dt.datetime.now()\n", "pr = ir_swap_pricing_data.price()\n", "print(f'runtime: {dt.datetime.now() - tic}')\n", "print(f\"Price: {pr}\")\n", "\n", "# runtime: 0:00:00.000938\n", "# 4.412838984470563" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Further Example\n", "#### Create the necessary market data\n", "\n", "This time, we distinguish between the discount curve used to discount the cashflows to present values and a discount curve for the calculation of the forward interest rates which we use as fixing curve." ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "ExecuteTime": { "end_time": "2020-05-03T11:41:35.513670Z", "start_time": "2020-05-03T11:41:34.971396Z" } }, "outputs": [], "source": [ "refdatedc = dt.datetime(2017, 1, 1)\n", "days_to_maturity = [1, 180, 365, 720, 3 * 365, 4 * 365, 10 * 365]\n", "dates = [refdatedc + dt.timedelta(days=d) for d in days_to_maturity]\n", "\n", "# Discount curve\n", "object_id = \"TEST_DC\" \n", "dsc_rate = 0.01\n", "df = [math.exp(-d / 365.0 * dsc_rate) for d in days_to_maturity]\n", "dc = DiscountCurve(id=object_id, refdate=refdatedc, dates=dates, df=df)\n", "\n", "# Fixing curves\n", "object_id = \"TEST_fwd_\"\n", "fwd_rate = {'3m': 0.05, '6m': 0.052}\n", "fwd_df = {t: [math.exp(-d / 365.0 * r) for d in days_to_maturity] for t, r in fwd_rate.items()}\n", "fwd_dc = {t: DiscountCurve(id=object_id + t, refdate=refdatedc, dates=dates, df=df) for t, df in fwd_df.items()}\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Setup the specification\n", "In our example we are using one fixed leg and one floating leg (with just two payment and fixing dates)\n", "The swap needs to get vectors with start, end, and payment dates (floater additionally need a rest date vector). The i-th accrual period is defined by the i-th entry of start and end vectors.\n", "Note that this kind of swap has only floating legs with floating rate period equal to accrual period." ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "ExecuteTime": { "end_time": "2020-05-03T11:41:35.541885Z", "start_time": "2020-05-03T11:41:35.515359Z" } }, "outputs": [], "source": [ "# Create the vectors defining the statdates, enddates, paydates and reset dates\n", "days_to_maturity = [0, 180, 360, 540]\n", "dates = [dt.datetime(2017,1,1) + dt.timedelta(days=d) for d in days_to_maturity]\n", "\n", "startdates = dates[:-1]\n", "enddates = dates[1:]\n", "paydates = enddates\n", "resetdates = startdates\n", "refdate = dates[0]\n", "\n", "# We need a vector of notionals since if needed, each accrual period may have a different notional\n", "# However, if the vector is of length 1 the same notional is applied to all periods\n", "\n", "notionals = ConstNotionalStructure(100.0)\n", "\n", "fixedleg = IrFixedLegSpecification(0.03, \"fixed_leg\", notionals, startdates, enddates, paydates,'EUR', 'Act365Fixed')\n", "spread = 0.00\n", "floatleg = IrFloatLegSpecification(\"float_leg\", notionals, resetdates, startdates, enddates,startdates, enddates, paydates,'EUR', \n", " \"dummy_udl\", \"dummy_fixing_id\", 'Act365Fixed','Act365Fixed', spread)\n", "\n", "ir_swap = InterestRateSwapSpecification('TEST_SWAP', notionals, refdate, paydates[-1], fixedleg, floatleg, \"EUR\",\n", " day_count_convention=\"Act365Fixed\",\n", " issuer=\"DBK\", securitization_level=\"COLLATERALIZED\"\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Setup the pricer object" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [], "source": [ "ir_pricer = InterestRateSwapPricer(\n", " val_date=refdate,\n", " spec=ir_swap,\n", " discount_curve_pay_leg=dc,\n", " discount_curve_receive_leg=dc,\n", " fixing_curve_pay_leg=dc,\n", " fixing_curve_receive_leg=fwd_dc['6m'],\n", " fx_fwd_curve_pay_leg=None,\n", " fx_fwd_curve_receive_leg=None,\n", " pricing_request=[],\n", " pricing_param={\"fixing_grace_period\": 0.0},\n", ")\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Pricing" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "ExecuteTime": { "end_time": "2020-05-03T11:41:35.649831Z", "start_time": "2020-05-03T11:41:35.576171Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "runtime: 0:00:00.007013\n", "3.3213902680066774\n" ] } ], "source": [ "tic = dt.datetime.now()\n", "pr = ir_pricer.price()\n", "print(f'runtime: {dt.datetime.now() - tic}')\n", "print(pr)\n", "\n", "# runtime: 0:00:00.013420\n", "# 3.3213902674791154" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tenor Basis Swap\n", "\n", "There are two types of tenor basis swaps:\n", "- In most currencies, a tenor basis swap consists of two float legs (e.g. 3M-USD-Libor vs 6M-USD-Libor) where the on the leg with the shorter tenor (in this case the 3M leg) there is an additional spread (the \"basis\").\n", "- In EUR, a the \"basis\" is quoted as the difference of swap rates between two vanilla swaps with different float leg tenor (e.g. [swap rate of 6M-Euribor-Swap] - [swap rate of 3M-Euribor-Swap]). This is equivalent to a swap with three legs:\n", " - a floating leg which pays 3M-Euribor\n", " - a floating leg which receives 6M-Euribor\n", " - a fixed leg which pays the basis (usually annually)\n", "\n", "In either case, if the discount curve and one of the forward (projection) curve are known, a strip of tenor basis swap quotes can be used to bootstrap the other forward curve." ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [], "source": [ "# Set up curves\n", "# Discount curve\n", "object_id = \"TEST_DC\"\n", "dsc_rate = 0.01\n", "df = [math.exp(-d / 365.0 * dsc_rate) for d in days_to_maturity]\n", "dc = DiscountCurve(id=object_id, refdate=refdatedc, dates=dates, df=df, interpolation = InterpolationType.LINEAR_LOG, extrapolation=ExtrapolationType.LINEAR_LOG)\n", "\n", "# Fixing curves\n", "object_id = \"TEST_fwd_\"\n", "fwd_rate = {'3m': 0.05, '6m': 0.052}\n", "fwd_df = {t: [math.exp(-d / 365.0 * r) for d in days_to_maturity] for t, r in fwd_rate.items()}\n", "fwd_dc = {t: DiscountCurve(id=object_id + t, refdate=refdatedc, dates=dates, df=df, interpolation = InterpolationType.LINEAR_LOG, extrapolation=ExtrapolationType.LINEAR_LOG) for t, df in fwd_df.items()}" ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [], "source": [ "# set up two payer swaps (receive 3m/6m-Euribor, pay fixed)\n", "refdate = dt.datetime(2017, 1, 1)\n", "days = 720\n", "tenor_in_days = {'3m': 90, '6m': 180, '12m': 360}\n", "spread = 0.002\n", "swap_rate_3m = .03\n", "swap_rate_6m = swap_rate_3m + spread\n", "swap_rates = {'3m': swap_rate_3m, '6m': swap_rate_6m}\n", "days_to_maturity = {t: list(range(0, days + 1, tenor_days)) for t, tenor_days in tenor_in_days.items()}\n", "notionals = ConstNotionalStructure(100.0)\n", "ccy = 'EUR'\n", "swaps = {}\n", "swap_pricer = {}\n", "t_fix = '12m'\n", "dates_fix = [refdate + dt.timedelta(days=d) for d in days_to_maturity[t_fix]]\n", "\n", "for t_fl in ['3m', '6m']:\n", "# set up specification\n", " tenor_days_fl = tenor_in_days[t_fl]\n", " dates_fl = [refdate + dt.timedelta(days=d) for d in days_to_maturity[t_fl]]\n", "\n", "\n", " fixedleg = IrFixedLegSpecification(swap_rates[t_fl], \"fixed_leg\", notionals, dates_fix[:-1], dates_fix[1:], dates_fix[1:],ccy, 'Act365Fixed')\n", "\n", " floatleg = IrFloatLegSpecification(\"float_leg\", notionals, dates_fl[:-1], dates_fl[:-1], dates_fl[1:], dates_fl[:-1], dates_fl[1:], dates_fl[1:],ccy, \n", " \"dummy_udl\", \"dummy_fixing_id\", 'Act365Fixed','Act365Fixed', spread)\n", "\n", " swaps[t_fl] = InterestRateSwapSpecification('TEST_SWAP', notionals, refdate, paydates[-1], fixedleg, floatleg, \"EUR\",\n", " day_count_convention=\"Act365Fixed\",\n", " issuer=\"DBK\", securitization_level=\"COLLATERALIZED\"\n", " )\n", "\n", "\n", " #set up pricing data\n", " swap_pricer[t_fl] = ir_pricer = InterestRateSwapPricer(\n", " val_date=refdate,\n", " spec=swaps[t_fl],\n", " discount_curve_pay_leg=dc,\n", " discount_curve_receive_leg=dc,\n", " fixing_curve_pay_leg=dc,\n", " fixing_curve_receive_leg=fwd_dc[t_fl],\n", " fx_fwd_curve_pay_leg=None,\n", " fx_fwd_curve_receive_leg=None,\n", " pricing_request=[],\n", " pricing_param={\"fixing_grace_period\": 0.0},\n", " )\n", "\n", "\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3m payer swap: 4.373913684160208\n", "6m payer swap: 4.433019638823725\n", "Difference: 0.05910595466351687\n" ] } ], "source": [ "# Price the swaps\n", "price_3m = swap_pricer['3m'].price()\n", "price_6m = swap_pricer['6m'].price()\n", "\n", "price_diff = price_6m - price_3m\n", "print(f\"3m payer swap: {price_3m}\")\n", "print(f\"6m payer swap: {price_6m}\")\n", "print(f\"Difference: {price_diff}\") \n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [], "source": [ "# Set up basis swap\n", "\n", "float_leg_3m = swaps['3m'].get_float_leg()\n", "float_leg_6m = swaps['6m'].get_float_leg()\n", "spread_leg = IrFixedLegSpecification(spread, 'spread_leg',notionals, dates_fix[:-1], dates_fix[1:], dates_fix[1:], ccy,\n", " 'Act365Fixed')\n", "\n", "#basis_swap = InterestRateBasisSwapSpecification('xx', 'dummy_issuer', 'COLLATERALIZED', ccy, dates_fix[-1], float_leg_6m, float_leg_3m, spread_leg)\n", "basis_swap = InterestRateBasisSwapSpecification(\n", " obj_id=\"basis_3m_6m\",\n", " notional=notionals,\n", " issue_date=refdate,\n", " maturity_date=refdate + relativedelta(years=1),\n", " pay_leg=float_leg_3m,\n", " receive_leg=float_leg_6m,\n", " spread_leg=spread_leg,\n", " currency=ccy,\n", " day_count_convention=\"Act365Fixed\",\n", " issuer=\"dummy_issuer\",\n", " securitization_level=\"COLLATERALIZED\",\n", " )\n", "\n" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [], "source": [ "fair_bs_spread = InterestRateSwapPricer.compute_basis_spread(\n", " ref_date=refdate,\n", " discount_curve=dc,\n", " payLegFixingCurve=fwd_dc['3m'],\n", " receiveLegFixingCurve=fwd_dc['6m'],\n", " pay_leg= basis_swap.get_pay_leg(),\n", " receive_leg= basis_swap.get_receive_leg(),\n", " spread_leg= basis_swap.get_spread_leg(),\n", " \n", " pricing_params= {\"fixing_grace_period\": 0.0, \"set_rate\": True,\n", " \"desired_rate\": 1.0},\n", " )" ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3m/6m basis swap fair spread: 0.002304096556289179\n" ] } ], "source": [ "print(f\"3m/6m basis swap fair spread: {fair_bs_spread}\")\n", "\n" ] } ], "metadata": { "kernelspec": { "display_name": "rivapy", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.11" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": { "height": "calc(100% - 180px)", "left": "10px", "top": "150px", "width": "292px" }, "toc_section_display": true, "toc_window_display": true }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }