# Quantlib - exercise probability function?

I am using Quantlib to obtain the option value embedded in a convertible bond. I create an american option as follows:

strike_price = redemption / conversion_ratio
option_type = ql.Option.Call
payoff = ql.PlainVanillaPayoff(option_type, strike_price)
settlement = calculation_date

am_exercise = ql.AmericanExercise(settlement, maturity_date)
american_option = ql.VanillaOption(payoff, am_exercise)

flat_vol_ts = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(calculation_date, calendar, volatility, day_count))
bsm_process = ql.BlackScholesMertonProcess(spot_price_handle,
dividend_ts_handle,
yield_ts_handle,
volatility_ts_handle)

binomial_engine = ql.BinomialVanillaEngine(bsm_process, "crr", time_steps)
american_option.setPricingEngine(binomial_engine)

option_position1 = round(american_option.NPV(),4)
delta_position1 =  round(american_option.delta(),4)
gamma_position1 = round(american_option.gamma(),4)


I want to obtain the exercise probability as this is a measure of how equity or debt like a convertible bond is. (e.g. >60% exercise probability is labelled as equity-like). Is there function within quantlib that will provide me the exercise probability (exercise probability is not the same as the delta)?

Edit 1: Approach of obtaining the equity or debtness of the convertible bond:

Edit 2: I have tried to incorporate a dual delta in code. I calculate the dual delta by retrieving two seperate option values with a slightly different strike price. However, first results show a huge difference between the delta and the dual delta, delta being 2-3x as high, so I must be doing something wrong. Does my code as it currently is makes sense to manually calculate the dual delta?

    strike_price_up = strike_price + 0.0001
strike_price_down = strike_price - 0.0001
payoff_up = ql.PlainVanillaPayoff(option_type, strike_price_up)
payoff_down = ql.PlainVanillaPayoff(option_type, strike_price_down)

american_option_up = ql.VanillaOption(payoff_up, am_exercise)

flat_vol_ts = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(calculation_date, calendar, volatility, day_count))
bsm_process = ql.BlackScholesMertonProcess(spot_price_handle,
dividend_ts_handle,
yield_ts_handle,
flat_vol_ts)

binomial_engine = ql.BinomialVanillaEngine(bsm_process, "crr", time_steps)
american_option_up.setPricingEngine(binomial_engine)
dd_u = american_option_up.NPV()

american_option_down = ql.VanillaOption(payoff_down, am_exercise)

flat_vol_ts = ql.BlackVolTermStructureHandle(ql.BlackConstantVol(calculation_date, calendar, volatility, day_count))
bsm_process = ql.BlackScholesMertonProcess(spot_price_handle,
dividend_ts_handle,
yield_ts_handle,
flat_vol_ts)

binomial_engine = ql.BinomialVanillaEngine(bsm_process, "crr", time_steps)
american_option_down.setPricingEngine(binomial_engine)
dd_d = american_option_down.NPV()

dualdelta = (dd_d - dd_u)/(2*0.0001)
dualdelta_position1 = round(dualdelta,4)


Edit 3: I believe the correct formula should be: dualdelta = (dd_u - dd_d)/(2*0.0001). This returns a negative dual delta..?

• The delta is $\frac{dC}{dS}$, the one you are looking for is $\frac{dC}{dK}$ sometimes called dual_delta, but I do not see a dual_delta attribute in american_option class. You might have to approximate numerically by perturbing K (the strike), i.e. take $\frac{\Delta C}{\Delta K}$ – noob2 Mar 15 at 16:24
• Thank you. As my english is not the best, would you care to explain what you mean by 'pertubing K'. I would like to add that my original reference to obtaining the 'equity/debt-like' characterstic of a convertible bond is based on a paper by C.M. L.R.J. Rogalski and J.K. Seward (1996). I added a picture of their approach in the main post. – Leon Mar 15 at 16:53
• @noob2 See my two edits. – Leon Mar 15 at 17:42
• @noob2 I do not see any reason why to not go for the American Option, especially since a convertible bond can usually be called/converted before the maturity date (maturity date = expiration date). Or am I seeing this wrong? To come back to my dual delta code, I am fairly certain I am making a mistake here. – Leon Mar 15 at 18:10
• Yes, I am sorry, the Dual Delta of a Call is negative. My mistake. For the probability of exercise you need to change the sign so it becomes positive. $p=-\frac{dC}{dK}$ – noob2 Mar 15 at 19:42

OK, here is what I think. (But you should ask for advice from others in this forum or elsewhere).

You computed $$\frac{dC}{dK}$$ (the dual delta) by a discrete approximation. The result is negative and this is correct (it is negative for a Call and Positive for a Put). In the case of a European Call it is given by the formula $$-e^{-r T}N(d_2)$$. (See here for source).

In the article you cited they are using $$N(d_2)$$ as the probability of exercise, so it is a different value. We are discounting the probability to the present time, while the article is using the probability itself (without time discounting). Since these convertible bonds are quite long term (eg. 10 years) it makes a difference. (In my work I usually deal with options of about 1 year, so I have not noticed or thought about this problem before. But from now on I will).

What is the solution? After computing the Dual Delta I would (1) change the sign to positive (2) Multiply by $$e^{rT}$$ to find the future value, i.e. to remove the discount factor $$e^{-rT}$$ mentioned earlier. So the probability you want is

$$p=-e^{rT}\frac{dC}{dK}$$

(With an interest rate of 2.8% the exp(rT) factor for 10 years is about 1.323).

• Thank you! As you have suggested, I multiplied the dual delta with $e^{rT}$. See here how I applied it quick and dirty in my code. However, similar to what you also concluded, the multiplication factor is around 1.3. This would still result in a exercise probability that is quite low. I am still unsure if we are not calculating the same as in the article. The following paper by P. Verwijmeren et al., 2011 finds that only 6 out of 629 CB are <60%. – Leon Mar 16 at 11:11