Struggling with Modeling Convertible Bond using Python

I am trying to price Convertible bond with the following data:

1. price = 5.11
2. coupon = 0.0575
3. frequency = semi-annual
4. risk free rate = 0.02347
5. conversion Ratio = 3.8095
6. Conversion Price = 26.25
7. volatility = 0.64
8. principal = 100
9. dividend yield = 0.0 10 time = 2.5 yrs
11. value = 0.0
12. N=5 #Number of time steps
13. rates = [0.0107, 0.0136, 0.0145, 0.0202, 0.02347] #term structure
14. call schedule = [103, 101.9, 101.9, 100.85, 100.85, 100]

The problem I am currently facing is to build callability schedule and term structure as I don't have the respective dates. I have taken some sample dates to finish the code and come up with a working solution. However, it will be great if I can have a working python code which doesn't use QuantLib library. Below is the code I have developed so far:

import QuantLib as ql

calculation_date = ql.Date(9,1,2004)
ql.Settings.instance().evaluationDate = calculation_date

redemption = 100.00
face_amount = 100.0
spot_price = 5.11
conversion_price = 26.25
conversion_ratio = 3.8095  # BBG quotes 38.4615; had to scale by a factor of 10

issue_date = ql.Date(15,3,2002)
maturity_date = ql.Date(15,9,2004)

settlement_days = 2
calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond)
coupon = 0.0575
frequency = ql.Semiannual
tenor = ql.Period(frequency)

day_count = ql.Thirty360()

call_dates = [ql.Date(15,9,2002)]
call_price = 103
put_dates = [ql.Date(15,3,2003), ql.Date(15,9,2003), ql.Date(15,3,2004)]
put_price = 100.0

# assumptions
dividend_yield = 0.0
risk_free_rate = 0.02347
volatility = 0.64

callability_schedule = ql.CallabilitySchedule()

for call_date in call_dates:
# callability_price  = ql.CallabilityPrice(call_price,
#                                          ql.CallabilityPrice.Clean)
# call_price = [103, 101.9, 101.9, 100.85, 100.85, 100]
callability_price  = ql.CallabilityPrice(call_price,
ql.CallabilityPrice.Clean)
callability_schedule.append(ql.Callability(callability_price,
ql.Callability.Call,
call_date)
)

for put_date in put_dates:
puttability_price = ql.CallabilityPrice(put_price,
ql.CallabilityPrice.Clean)
callability_schedule.append(ql.Callability(puttability_price,
ql.Callability.Put,
put_date))

dividend_schedule = ql.DividendSchedule() # No dividends
dividend_amount = dividend_yield*spot_price
next_dividend_date = ql.Date(15,9,2002)
dividend_amount = spot_price*dividend_yield
for i in range(5):
dividend_schedule.append(
ql.FixedDividend(dividend_amount, date)
)

schedule = ql.Schedule(issue_date, maturity_date, tenor,
calendar, accrual_convention, accrual_convention,
ql.DateGeneration.Backward, False)

exercise = ql.AmericanExercise(calculation_date, maturity_date)

convertible_bond = ql.ConvertibleFixedCouponBond(exercise,
conversion_ratio,
dividend_schedule,
callability_schedule,
issue_date,
settlement_days,
[coupon],
day_count,
schedule,
redemption)

spot_price_handle = ql.QuoteHandle(ql.SimpleQuote(spot_price))
yield_ts_handle = ql.YieldTermStructureHandle(
ql.FlatForward(calculation_date, risk_free_rate, day_count)
)

#Create the Yield Curve
dates = [ql.Date(15,3,2002),ql.Date(15,9,2002),ql.Date(15,3,2003),ql.Date(15,9,2003),ql.Date(15,3,2004), ql.Date(15,9,2004)]

rates = [0.0107,0.0136,0.0145,0.0202,0.02347, 0.0245]

ts = ql.ForwardCurve(dates, rates, day_count)
ts_handle = ql.YieldTermStructureHandle(ts)

dividend_ts_handle = ql.YieldTermStructureHandle(
ql.FlatForward(calculation_date, dividend_yield, day_count)
)
volatility_ts_handle = ql.BlackVolTermStructureHandle(
ql.BlackConstantVol(calculation_date, calendar,volatility, day_count)
)

bsm_process = ql.BlackScholesMertonProcess(spot_price_handle,
dividend_ts_handle,
ts_handle,
volatility_ts_handle)

time_steps = 5
engine = ql.BinomialConvertibleEngine(bsm_process, "crr", time_steps)

convertible_bond.setPricingEngine(engine)
print ("NPV ", convertible_bond.NPV())


• I actually have a question in terms of your code about credit_spread_handle. You considered credit_spread_handle when you construct the ConvertibleFixedCouponBond() but you did not consider credit_spread_handle when you construct the discounting term strucure ts_handle. Should we consider credit spread in both construction convertible bond and yield curve? Aug 14, 2021 at 20:15

I have recently released a Python financial library called FinancePy. It has a convertible bond model implementation. It is still in beta so may have some bugs but you are welcome to try it out. It also uses Numba so it is fast and you can look through to the actual Python code. The github is at

https://github.com/domokane/FinancePy

Here is an example notebook that prices a convertible bond.

https://github.com/domokane/FinancePy-Examples/blob/master/notebooks/BOND_CONVERTIBLE_ComparisonWithQLExample.ipynb

I am happy to be contacted directly if you find any issues. See my email at the github repository readme.

• I have ran the code from FinancePy and it is working on my system. However, you have used the dates to create the term structure and that's the issue. I don't have the dates and need a work around to price the bond without dates. Sep 25, 2020 at 10:38
• If you don't have the dates, what term structure information do you have ? What are the terms of the rates you have provided above. And is it a 5 year bond ?
– Dom
Sep 25, 2020 at 11:11
• It is a 2.5 year bond. I have mentioned the inputs in the query above. Sep 25, 2020 at 12:51
• Thank you very much, Prof. O'kane, for sharing your very interesting-looking library! Sep 27, 2020 at 13:02
• OK. I deleted my post because of your rejection of the question. I am not familiar with the other libraries apart from QL and asking a question that made my library one of the answers might be seen as biased. Better if someone else does it.
– Dom
Sep 27, 2020 at 13:28