Notice
Recent Posts
Recent Comments
Link
반응형
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
Archives
Today
Total
관리 메뉴

To Be Develop

Constructing FixedIncome Portfolios with Duration 본문

study

Constructing FixedIncome Portfolios with Duration

To Be Develop 2024. 11. 27. 22:45
반응형

Overview

Fixed-income portfolios are highly sensitive to interest rate fluctuations, which can significantly impact their value. One of the most effective ways to mitigate this risk is through duration matching, a technique that aligns the interest rate sensitivity of a portfolio with its liabilities. For more precision, convexity matching is used as a complementary approach to account for non-linear interest rate effects.

In this blog, we’ll cover:

  1. The fundamentals of duration and convexity.
  2. How to apply duration and convexity matching to construct robust fixed-income portfolios.
  3. A Python implementation to design and test these strategies.

1. Understanding Duration and Convexity

1.1 What is Duration?

Duration measures the sensitivity of a bond’s price to changes in interest rates.

  • Macaulay Duration: Weighted average time until a bond’s cash flows are received.
  • Modified Duration: Approximates the percentage change in bond price for a 1% change in yield.

[
\text{Modified Duration} = \frac{\text{Macaulay Duration}}{1 + \text{Yield/Compounding Periods}}
]

Interpretation: If a bond has a modified duration of 5, its price will decrease by approximately 5% for a 1% increase in interest rates.


1.2 What is Convexity?

Convexity measures the curvature in the relationship between bond prices and interest rates.

[
\text{Convexity} = \frac{\sum \left( \text{Cash Flow}_t \cdot t \cdot (t + 1) \cdot (1 + \text{Yield})^{-t-2} \right)}{\text{Price}}
]

Interpretation: Convexity accounts for the non-linear effect of interest rate changes. Bonds with higher convexity experience smaller price declines when rates rise.


1.3 Why Use Duration and Convexity Matching?

  • Duration Matching: Ensures that portfolio and liability values respond similarly to small interest rate changes.
  • Convexity Matching: Fine-tunes the portfolio to account for larger or non-linear rate changes.

2. Applying Duration and Convexity Matching

2.1 Goal

Construct a portfolio of bonds such that:

  1. The portfolio’s duration matches the duration of liabilities.
  2. The portfolio’s convexity is as close as possible to that of the liabilities.

2.2 Steps to Construct the Portfolio

Step 1: Calculate Duration and Convexity

For each bond and liability, calculate:

  • Present value of cash flows.
  • Duration and convexity using their respective formulas.

Step 2: Optimize Weights

Use optimization techniques to find the bond weights ( w_i ) that minimize the mismatch:

[
\text{Objective: Minimize } \left| D_\text{portfolio} - D_\text{liabilities} \right| + \lambda \left| C_\text{portfolio} - C_\text{liabilities} \right|
]

Where:

  • ( D ): Duration.
  • ( C ): Convexity.
  • ( \lambda ): Penalty factor for convexity mismatch.

Step 3: Test the Portfolio

Simulate interest rate changes and evaluate the portfolio’s performance compared to liabilities.


3. Python Implementation

3.1 Import Libraries

import numpy as np
import pandas as pd
from scipy.optimize import minimize

3.2 Bond Data Setup

# Example bonds (Face Value, Coupon Rate, Years to Maturity, Yield)
bonds = pd.DataFrame({
'Face Value': [1000, 1000, 1000],
'Coupon Rate': [0.05, 0.06, 0.07],
'Years to Maturity': [5, 7, 10],
'Yield': [0.04, 0.05, 0.06]
})

# Liability (Duration and Convexity to match)
liabilities = {
'Duration': 7.0,
'Convexity': 50.0
}

3.3 Duration and Convexity Calculations

# Calculate bond price
def bond_price(face_value, coupon_rate, years_to_maturity, yield_rate):
cash_flows = [face_value * coupon_rate] * years_to_maturity + [face_value]
discount_factors = [(1 + yield_rate) ** -t for t in range(1, years_to_maturity + 2)]
return sum(cf * df for cf, df in zip(cash_flows, discount_factors))

# Calculate duration
def duration(face_value, coupon_rate, years_to_maturity, yield_rate):
cash_flows = [face_value * coupon_rate] * years_to_maturity + [face_value]
discount_factors = [(1 + yield_rate) ** -t for t in range(1, years_to_maturity + 2)]
weighted_times = [t * cf * df for t, (cf, df) in enumerate(zip(cash_flows, discount_factors), start=1)]
price = bond_price(face_value, coupon_rate, years_to_maturity, yield_rate)
return sum(weighted_times) / price

# Calculate convexity
def convexity(face_value, coupon_rate, years_to_maturity, yield_rate):
cash_flows = [face_value * coupon_rate] * years_to_maturity + [face_value]
discount_factors = [(1 + yield_rate) ** -t for t in range(1, years_to_maturity + 2)]
weighted_terms = [t * (t + 1) * cf * df for t, (cf, df) in enumerate(zip(cash_flows, discount_factors), start=1)]
price = bond_price(face_value, coupon_rate, years_to_maturity, yield_rate)
return sum(weighted_terms) / price / (1 + yield_rate) ** 2

3.4 Optimization

Objective Function

# Portfolio optimization: minimize duration and convexity mismatch
def objective(weights):
portfolio_duration = sum(weights[i] * duration(*bonds.iloc[i]) for i in range(len(bonds)))
portfolio_convexity = sum(weights[i] * convexity(*bonds.iloc[i]) for i in range(len(bonds)))
duration_mismatch = abs(portfolio_duration - liabilities['Duration'])
convexity_mismatch = abs(portfolio_convexity - liabilities['Convexity'])
return duration_mismatch + 0.1 * convexity_mismatch  # Lambda = 0.1

Solve for Optimal Weights

# Constraints: weights sum to 1
constraints = [{'type': 'eq', 'fun': lambda w: sum(w) - 1}]
bounds = [(0, 1) for _ in range(len(bonds))]  # No shorting

# Initial guess
initial_weights = [1 / len(bonds)] * len(bonds)

# Optimization
result = minimize(objective, initial_weights, constraints=constraints, bounds=bounds)
optimal_weights = result.x

print("Optimal Weights:", optimal_weights)

3.5 Test the Portfolio

# Compute portfolio duration and convexity
portfolio_duration = sum(optimal_weights[i] * duration(*bonds.iloc[i]) for i in range(len(bonds)))
portfolio_convexity = sum(optimal_weights[i] * convexity(*bonds.iloc[i]) for i in range(len(bonds)))

print("Portfolio Duration:", portfolio_duration)
print("Portfolio Convexity:", portfolio_convexity)

4. Limitations and Considerations

4.1 Assumptions

  • Constant yield curves and no reinvestment risk.
  • Linear approximation for small interest rate changes.

4.2 Enhancements

  • Incorporate convexity constraints more dynamically.
  • Use stochastic interest rate models (e.g., CIR model).
  • Extend to include credit risk or liquidity considerations.

5. Conclusion

Duration and convexity matching are essential techniques for constructing fixed-income portfolios that minimize interest rate risk. By balancing these metrics, investors can ensure their portfolios remain robust against both small and large changes in rates. With Python, implementing these strategies becomes accessible, enabling precise and data-driven decision-making.


References

반응형