0
$\begingroup$

I am trying to generate equity delta for convertible bond using QuantLib(version 1.14) functions, but the deltas generated either using a repricing approach or by directly obtaining from the tree(code as below) all generates deltas bigger than 1.

#include <ql/qldefines.hpp>
#ifdef BOOST_MSVC
#  include <ql/auto_link.hpp>
#endif
#include <ql/experimental/convertiblebonds/convertiblebond.hpp>
#include <ql/experimental/convertiblebonds/binomialconvertibleengine.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/daycounters/thirty360.hpp>
#include <ql/utilities/dataformatters.hpp>

#include <boost/timer.hpp>
#include <iostream>
#include <iomanip>

#define LENGTH(a) (sizeof(a)/sizeof(a[0]))

using namespace QuantLib;

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {

    Integer sessionId() { return 0; }

}
#endif
template<typename Method>
auto calculate(double underlying)
{
    auto analysis_date = Date(8, May, 2020);
    Real spreadRate = 0.0125;
    Spread dividendYield = 0.0;
    Rate riskFreeRate = 0.03;
    Volatility volatility = 0.3436553822850044;
    Integer settlementDays = 0;
    Integer length = 3;
    Real redemption = 100.0;
    Real conversionRatio = 100 / 12.1; // at the money
    // set up dates/schedules
    Calendar calendar = TARGET();
    Date today = calendar.adjust(analysis_date);
    Settings::instance().evaluationDate() = today;
    Date settlementDate = calendar.advance(today, settlementDays, Days);
    Date exerciseDate = calendar.advance(settlementDate, length, Years);
    Date issueDate = calendar.advance(exerciseDate, -length, Years);
    BusinessDayConvention convention = ModifiedFollowing;
    Frequency frequency = Annual;
    Schedule schedule(issueDate, exerciseDate, Period(frequency), calendar, convention, convention, DateGeneration::Backward, false);
    DividendSchedule dividends;
    CallabilitySchedule callability;
    std::vector<Real> coupons(1, 0.05);
    DayCounter bondDayCount = Thirty360();

    for (Date d = today + 6 * Months; d < exerciseDate; d += 6 * Months)
    {
        dividends.push_back(boost::shared_ptr<Dividend>(new FixedDividend(1, d)));
    }
    DayCounter dayCounter = Actual365Fixed();
    boost::shared_ptr<Exercise> exercise(new EuropeanExercise(exerciseDate));
    boost::shared_ptr<Exercise> amExercise(new AmericanExercise(settlementDate, exerciseDate));
    Handle<Quote> underlyingH(boost::shared_ptr<Quote>(new SimpleQuote(underlying)));
    Handle<YieldTermStructure> flatTermStructure(boost::shared_ptr<YieldTermStructure>(new FlatForward(settlementDate, riskFreeRate, dayCounter)));
    Handle<YieldTermStructure> flatDividendTS(boost::shared_ptr<YieldTermStructure>(new FlatForward(settlementDate, dividendYield, dayCounter)));
    Handle<BlackVolTermStructure> flatVolTS(boost::shared_ptr<BlackVolTermStructure>(new BlackConstantVol(settlementDate, calendar, volatility, dayCounter)));
    boost::shared_ptr<BlackScholesMertonProcess> stochasticProcess(new BlackScholesMertonProcess(underlyingH, flatDividendTS, flatTermStructure, flatVolTS));
    Size timeSteps = 801;
    Handle<Quote> creditSpread(boost::shared_ptr<Quote>(new SimpleQuote(spreadRate)));
    boost::shared_ptr<Quote> rate(new SimpleQuote(riskFreeRate));
    Handle<YieldTermStructure> discountCurve(boost::shared_ptr<YieldTermStructure>(new FlatForward(today, Handle<Quote>(rate), dayCounter)));
    ConvertibleFixedCouponBond americanBond(amExercise, conversionRatio, dividends, callability, creditSpread, issueDate, settlementDays, coupons, bondDayCount,
            schedule, redemption);
    americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>(new BinomialConvertibleEngine<Method>(stochasticProcess, timeSteps)));
    Real npv = americanBond.NPV();
    Real delta = americanBond.delta();
    return std::make_pair(npv, delta);
}

template<typename Method> void calc_sensitivity()
{
    auto spot=10.34;
    auto [npv, delta]= calculate<Method>(spot);
    auto [npv2, delta2]=calculate<Method>(spot*1.01);
    delta2 = (npv2 - npv) / (spot*0.01);
    // write column headings
    Size widths[] =
    { 14, 14, 14, 14 };
    Size totalWidth = widths[0] + widths[1] + widths[2] + widths[3];
    std::string rule(totalWidth, '-'), dblrule(totalWidth, '=');
    std::cout << typeid(Method).name() << std::endl;
    std::cout << dblrule << std::endl;
    std::cout << std::setw(widths[0]) << std::left << "PV0" << std::setw(widths[1]) << std::left << "PV1" << std::setw(widths[2]) << std::left
            << "Tree Delta" << std::setw(widths[3]) << std::left << "Iterative Delta" << std::endl;
    std::cout << rule << std::endl;
    std::cout << std::setw(widths[0]) << std::left << npv << std::fixed << std::setw(widths[1]) << std::left << npv2 << std::setw(widths[2]) << std::left
            << delta << std::setw(widths[2]) << std::left << delta2 << std::endl;

    std::cout << dblrule << std::endl;
}

int main(int, char*[])
{

    try
    {
        boost::timer timer;
        std::cout << std::endl;

        calc_sensitivity<JarrowRudd>();
        calc_sensitivity<CoxRossRubinstein>();
        calc_sensitivity<AdditiveEQPBinomialTree>();

        double seconds = timer.elapsed();
        Integer hours = int(seconds / 3600);
        seconds -= hours * 3600;
        Integer minutes = int(seconds / 60);
        seconds -= minutes * 60;
        std::cout << " \nRun completed in ";
        if (hours > 0)
            std::cout << hours << " h ";
        if (hours > 0 || minutes > 0)
            std::cout << minutes << " m ";
        std::cout << std::fixed << std::setprecision(0) << seconds << " s\n" << std::endl;

        return 0;
    } catch (std::exception &e)
    {
        std::cerr << e.what() << std::endl;
        return 1;
    } catch (...)
    {
        std::cerr << "unknown error" << std::endl;
        return 1;
    }

}

below code for generating the delta in binomialconvertibleengine.hpp:

convertible.initialize(lattice, maturity);
convertible.rollback(time_grid[1]);
auto value_up = convertible.values()[1];
auto value_down = convertible.values()[0];
auto s_up = tree->underlying(1, 1);
auto s_down = tree->underlying(1, 0);
auto delta = (value_up - value_down) / (s_up - s_down);

below is the result:

N8QuantLib10JarrowRuddE
========================================================
PV0           PV1           Tree Delta    Iterative Delta
--------------------------------------------------------
104.455       104.677186    1.985473      2.148005      
========================================================
N8QuantLib17CoxRossRubinsteinE
========================================================
PV0           PV1           Tree Delta    Iterative Delta
--------------------------------------------------------
104.454988    104.673906    1.984574      2.117198      
========================================================
N8QuantLib23AdditiveEQPBinomialTreeE
========================================================
PV0           PV1           Tree Delta    Iterative Delta
--------------------------------------------------------
104.480229    104.704433    1.996642      2.168310      
========================================================

Remark: Should have divided by the conversion ratio, the updated result is as below:
========================================================
PV0           PV1           Tree Delta    Iterative Delta
--------------------------------------------------------
110.041       110.544284    0.530003      0.588715      
========================================================
now the deltas looks nice.
$\endgroup$
1
  • $\begingroup$ Just curious, but 2.1683/8.26 is not 0.588715 though? $\endgroup$
    – zsljulius
    Commented Apr 14, 2021 at 2:17

1 Answer 1

3
$\begingroup$

You have a conversion ratio of $100/12.1 \approx 8.26$, so the convertibility is an option on about 8 underlying stocks and the delta scales accordingly. I'm not familiar about the way it's quoted, though. Did you expect it to be the delta for one unit of stock?

$\endgroup$
2
  • $\begingroup$ The stock is trading at 12.1, and the bond has a face value of 100, and so the conversion ratio is around 8.26... $\endgroup$
    – Michael
    Commented Jun 7, 2020 at 19:55
  • $\begingroup$ you are correct, I should have divided the result by the conversion ratio to get the delta of one unit of stock. BTW, I just bought your book... $\endgroup$
    – Michael
    Commented Jun 8, 2020 at 4:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.