Skip to content
All library documents

Bond DV01: Theoretical Sensitivity, Bump-and-Reprice, and Regression

Article Quant Q&A · Author: Sane

Summary

The document defines bond DV01 as the negative derivative of price with respect to yield and derives it by differentiating the discounted-cash-flow formula. It compares this theoretical sensitivity with finite-difference bump-and-reprice calculations used in practice, noting that the methods are practically similar. Duration is presented as a local linear approximation to the bond price–yield curve, with DV01 expressing the sensitivity in dollar terms. The response also relates this derivative-based approach to option delta.

The document evaluates a proposed regression of observed price on yield as an alternative estimate. In a modeled example, a regression slope resembles duration, but a single linear fit can misstate sensitivity across a convex price–yield curve, especially for longer maturities. Adding curvature terms could improve the fit, though the response argues that this resembles using duration with convexity and introduces estimation error. A second answer notes that empirical sensitivities may be useful when a bond’s yield moves with a benchmark curve and spread. These comments depend on the bond, yield definition, and rate risk being measured.

Key ideas

  • Theoretical DV01 follows from differentiating the present value of a bond’s cash flows with respect to yield.
  • Bump-and-reprice finite differences are a practical way to calculate bond sensitivity.
  • Duration gives a local linear approximation, while convexity makes a broad linear regression less reliable.
  • Adding curvature terms can capture nonlinear price–yield behavior but brings estimation error.
  • Empirical sensitivity can help relate a bond’s yield movements to changes in a benchmark curve.

Tags

Full text
# Is the "$\textit{theoretical}$" $DV01$ of a bond an accurate estimate?


# Is the "$\textit{theoretical}$" $DV01$ of a bond an accurate estimate?












Dollar duration $DV01$ is defined as negative of the price of the bond wrt yield:

$$DV01 = - \frac{\partial P}{\partial y}.$$

As we know that $P = \sum_{t=1}^{n} \frac{CF_{t}}{(1+y)^{t}}$, then

$$DV01 = - \sum_{t=1}^{n} t \frac{CF_{t}}{(1+y)^{t+1}}.$$

Now, as cash-flow stream ($\{CF\}_{t}$) is predetermined, then using current yield curve, one can compute $DV01$. I call this formula as a theoretical value for $DV01$, as it does not capture empirical relationship between price and yield of the bond.

Questions: 1) Do practitioners use the above theoretical formula to compute $DV01$? 2) More importantly, isn't empirically estimated $DV01$ through regression ($P_{t} = \alpha +\beta y_{t} + \epsilon_t$) describe price-yield relationship better than the theoretical formula above?

P.S. The formulation of the question is in the context of $DV01$, but it is relevant for all variations of duration.

## Answer by AKdemy (score 5, accepted)

https://quant.stackexchange.com/a/79205

Question 1: Not that often, usually finite difference is use to compute it (bump and reprice). See for example this complete step by step explanation of Bloomberg's DV01 computation in SWPM. Practically, its the same though.

Question 2: No, because DV01 is an exact linear approximation to the price yield function already. That is similar to the Greek Delta in option pricing, where no one uses some sort of regression to estimate the change of the option value with respect to a change in the price of the underlying either.

TL;DR

You correctly define the price of a bond as the discounted cashflows. Insofar, I am a bit puzzled by your question. As you showed, yield and price have a direct relationship. Demand and supply changes will change price, and hence yield accordingly. A simple example with code for US T-bills can be found here.

(Modified) duration, is a mathematical derivative (rate of change) of price and measures the percentage rate of change of price with respect to yield. DV01 is the same thing measured in absolute (dollar) terms.

Let's look at the bond price formula on Wikipedia

\begin{matrix} \left(\frac{C}{1+i}+\frac{C}{(1+i)^2}+ ... +\frac{C}{(1+i)^N}\right) + \frac{M}{(1+i)^N} \end{matrix} \begin{matrix} \left(\sum_{n=1}^N\frac{C}{(1+i)^n}\right) + \frac{M}{(1+i)^N} \end{matrix} \begin{matrix} C\left(\frac{1-(1+i)^{-N}}{i}\right)+M(1+i)^{-N} \end{matrix}

```
C  = coupon payment (periodic interest payment)
N = number of payments 
i = market interest rate, or required yield, or observed / appropriate yield to maturity
M = value at maturity, usually equals face value (Nominalwert)
P = market price of bond
```

Put in Python this looks like this

```
M = 1000 # nominal value
coupon = 0.05 # pct per period
c = coupon*M
i = 0.1 # YTM
N = 3 # periods

p = c*((1 - (1+i)**-N)/i)  + M/(1+i)**N
```

yielding a price of ~875.66 in this example. Now, YTM is usally derived from a market quoted price. For this, you need some solver. It's relatively straighforward to use Newton Raphson in this example.

```
# Finding the yield of a coupon bearing bond 
def newtonRaphson(m,p,t,c,r,f,epsilon=0.00001):        
    def fx(r):
        cum = 0
        red = m*(1+(r/100/f))**(-t*f)
        for i in range(1,t*f+1):
            cum += c/f*(1+(r/100/f))**(-i)
        cum += red    
        return p-cum

    def fxdx(r):
        cum = 0
        red = t*m*(1+(r/100/f))**-(t*f+1)
        for i in range(1,t*f+1):
            T = i/f
            cum += t*c/f*(1+(r/100/f))**-(i+1)
        cum += red
        return cum
    
    x_n = r
    i = 1
    while abs(fx(x_n)) > epsilon:
        x_next = x_n/100 - fx(x_n)/fxdx(x_n)
        x_n = x_next*100
        print('Guess {}: {}'.format(i, round(x_n,5)))
        i += 1
        if i > 10:
            print('No solution found.')
            break
    ytm = round(x_n,6)
    return ytm
```

Not surprisingly, this matches the yield we used to compute the price before.

Now that we established how YTM and prices are related to each other we an look at this in a more generic way. I often find charts intuitive.

```
def bond_price(N,i,M,c,f):
    return ((M*c/f*(1-(1+i/f)**(-f*N)))/(i/f)) + M*(1+(i/f))**(-f*N)

freq = 1
bond_price(N,i,M, coupon,freq)

freq = 2 # payments per year (semi annual)
N = 20 # maturity in years 
int_changes = np.arange(start=0.001, stop=0.14, step=0.001)  
M = 1000 # Nominal
c= 0.05 # 5% coupon

[plt.plot(int_changes, bond_price(i ,int_changes,M,c, freq), label = f'Bond with {i} year(s) maturity') for i in np.arange(start=1, stop=32, step=10)]
plt.xlabel("YTM")
plt.ylabel("Bond price")
plt.title('Bond prices as a function of interest rates')
plt.axvline(x=0.05, color='black', linestyle='-.', linewidth = 0.4)
plt.axvline(x=0.02, color='black', linestyle='-.', linewidth = 0.4)
plt.axhline(y=1688, color='c', linestyle='-', linewidth = 0.4)

plt.legend(prop={'size': 10})
plt.show()
```

Duration is just a linear approximation to this curve, similar to how delta works for options (see here). Implementing the formula in Wikipedia you compute it as such.

```
M = 100 
c = 0.07
freq = 1
N = 5
r = 0.07  
year = [i for i in range(1,N+1)]
Cashflow = [round(M * c, 3) if i < N else 100 +  round(M * c, 3) for i in range(1,N+1)]
dcf_t = [f"1/((1+{r})^{i})" for  i in range(1,N+1)]
dcf = [round(1/((1+r)**i), 5) for  i in range(1,N+1)]
cf_weighted = [i*round(M * c, 3) * round(1/((1+r)**i), 5) if i < N else  i*(100 + round(M * c, 3)) * round(1/((1+r)**i), 5)for  i in range(1,N+1) ]
cf_disc = [round(M * c, 3) * round(1/((1+r)**i), 5) if i < N else  (100 + round(M * c, 3)) * round(1/((1+r)**i), 5)for  i in range(1,N+1) ]

df = pd.DataFrame({"Year" : year, "Cashflow" : Cashflow, "Discount Formula" : dcf_t,  "Discount Factor" : dcf,  "NPV Weighted CF" :  cf_weighted, "NPV CF" :  cf_disc})
df
```

Using a free online bond calculator or WolframAlpha provides identical values.

Computing MD via finite difference (similar to how BBG computes DV01 and the delta example linked above) works like this. $$ Modified Duration = \frac{P^{-} - P^{+}}{2*P_0*dY}$$

where $dY$ is difference in the interest rate / YTM (frequently 100bp) $P^{-}$ Price of bond if rates down $P^{+}$ Price of bond if rates up $P_0$ Market value of bond

Adding duration to the chart above looks like this

```
int_changes = np.arange(start=0.001, stop=0.14, step=0.001)  
plt.plot(int_changes, bond_price(N ,int_changes,M,c, freq), label = f'Bond with {N} year(s) maturity') 
plt.plot(int_changes,  [bond_price(N,r,M, c,freq) - duration/(1+r)*i*100 + duration/(1+r)*r*100 for i in int_changes], label = f'Duration approximation', linestyle='dashdot')
plt.xlabel("YTM")
plt.ylabel("Bond price")
plt.title('Bond prices as a function of interest rates')
plt.axvline(x=r, color='black', linestyle='-.', linewidth = 0.4)
plt.axhline(y=bond_price(N,r,M, c,freq), color='c', linestyle='-', linewidth = 0.4,  label = f'Market price for YTM = {r}' )
plt.legend(prop={'size': 10})
plt.show()
```

Question 2:

So what about your linear regression idea? You are essentially proposing to use a linear functional form for a convex function. Our example already computed a few observation. Therefore, we can quickly use statsmodel to write the following Python code which implements your idea with our example from above. As you can see, the slope is actually quite similar to the duration we just computed (in absolute values).

```
x = int_changes
y = bond_price(N ,int_changes,M,c, freq)
model = sm.OLS(y, sm.add_constant(x))
results = model.fit()
results.summary()
```

Charting this with a scatter plot also shows the similarity (just add `plt.axline(xy1=(0, b), slope=m, label=f'$y = {b:+.1f} {m:.1f}x $', color='r'`) to the chart.

The more pronounced convexity (e.g. the longer maturity), the worse your idea gets.

As you can see, for a 30 year bond, your linear regression logic is misstating the change in the current price of the bond be quite a bit, whereas DVO1 (duration) is still an exact linear approximation at the point of interest (the current market price).

Of course, you can change the functional form and make the OLS regression fit the curve, but that will really just provide a very similar result to using duration, and adding convexity (or using Delta and adding Gamma in the options example I linked), albeit in a more convoluted way, and adding estimation errors.

## Answer by Rylan (score 2)

https://quant.stackexchange.com/a/79199

A few thoughts:

- At time $t$, in this setup, price and yield to maturity (which I assume we're talking about as there's no time "attached" to $y$) are "equivalent" in the sense that if we know the yield, we can figure out the price exactly, and vice versa. (semi-mathematically, there is a bijection between price and yield for a given bond at time $t$). In that sense, using the derivative or some other Taylor expansion to estimate change in price is appropriate.

- The impact of a rate (being general here, not necessarily a YTM!) change can be very different over different times to maturity. Even for YTM, you expect a big impact on price if bonds are maturing in a long time, and a small impact if maturity is soon. If your training set contains one bond observed at 30 years, 30 years - 1 day, ... 29 years, it's probably fine, but if you observe a bond with one year to maturity ,364 days, ... 2 days, then your yield ~ price impact will look very different in your observations

- IIRC, empirical DV01 is most useful for, say, a corporate bond, which we can imagine is priced on a government yield curve + a spread. I'm no expert in this part, but you could try estimating the corporate yield change as a function of the government yield change, and then say (for example) that if the government curve rises 1bp then the corporate curve rises $\beta$bp, so as long as beta is fairly small then you could estimate change in bond price for a change in government yield by using "theoretical DV01" $\times \beta$

As always with fixed income, the devil is in the details! these are just a few thoughts

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.