二叉樹
QuantLib 可轉換債券定價產生奇怪的 delta
我正在嘗試使用 QuantLib(1.14 版)函式為可轉換債券生成股權增量,但使用重新定價方法或直接從樹(程式碼如下)生成的增量都生成大於 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; } }
下面的程式碼用於在 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);
下面是結果:
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.
你的轉化率是 $ 100/12.1 \approx 8.26 $ ,因此可兌換性是大約 8 只標的股票的一種選擇,並且增量相應地縮放。不過,我不熟悉它的引用方式。您是否期望它是一單位股票的增量?