diff --git a/ql/experimental/callablebonds/blackcallablebondengine.cpp b/ql/experimental/callablebonds/blackcallablebondengine.cpp index 1364a1182da..8db134ea4bc 100644 --- a/ql/experimental/callablebonds/blackcallablebondengine.cpp +++ b/ql/experimental/callablebonds/blackcallablebondengine.cpp @@ -107,7 +107,7 @@ namespace QuantLib { Duration::Modified, false, exerciseDate); - Real cashStrike = arguments_.callabilityPrices[0]; + Real cashStrike = arguments_.callabilityPrices[0] * arguments_.faceAmount / 100.0; dayCounter = volatility_->dayCounter(); Date referenceDate = volatility_->referenceDate(); Time exerciseTime = dayCounter.yearFraction(referenceDate, @@ -145,7 +145,7 @@ namespace QuantLib { Real fwdCashPrice = (value - spotIncome())/ discountCurve_->discount(exerciseDate); - Real cashStrike = arguments_.callabilityPrices[0]; + Real cashStrike = arguments_.callabilityPrices[0] * arguments_.faceAmount / 100.0; Option::Type type = (arguments_.putCallSchedule[0]->type() == Callability::Call ? Option::Call : Option::Put); diff --git a/ql/experimental/callablebonds/callablebond.cpp b/ql/experimental/callablebonds/callablebond.cpp index 15111dc5efb..abe24dbeb89 100644 --- a/ql/experimental/callablebonds/callablebond.cpp +++ b/ql/experimental/callablebonds/callablebond.cpp @@ -28,15 +28,17 @@ namespace QuantLib { CallableBond::CallableBond(Natural settlementDays, - const Schedule& schedule, + const Date& maturityDate, + const Calendar& calendar, DayCounter paymentDayCounter, + Real faceAmount, const Date& issueDate, CallabilitySchedule putCallSchedule) - : Bond(settlementDays, schedule.calendar(), issueDate), + : Bond(settlementDays, calendar, issueDate), paymentDayCounter_(std::move(paymentDayCounter)), - putCallSchedule_(std::move(putCallSchedule)) { + putCallSchedule_(std::move(putCallSchedule)), faceAmount_(faceAmount) { - maturityDate_ = schedule.dates().back(); + maturityDate_ = maturityDate; if (!putCallSchedule_.empty()) { Date finalOptionDate = Date::minDate(); @@ -68,28 +70,72 @@ namespace QuantLib { } + class CallableBond::ImpliedVolHelper { + public: + ImpliedVolHelper(const CallableBond& bond, + const Handle& discountCurve, + Real targetValue, + bool matchNPV); + Real operator()(Volatility x) const; + private: + ext::shared_ptr engine_; + Real targetValue_; + bool matchNPV_; + ext::shared_ptr vol_; + const CallableBond::results* results_; + }; + CallableBond::ImpliedVolHelper::ImpliedVolHelper( const CallableBond& bond, - Real targetValue) - : targetValue_(targetValue) { + const Handle& discountCurve, + Real targetValue, + bool matchNPV) + : targetValue_(targetValue), matchNPV_(matchNPV) { vol_ = ext::make_shared(0.0); - bond.blackVolQuote_.linkTo(vol_); - - QL_REQUIRE(bond.blackEngine_, - "Must set blackEngine_ to use impliedVolatility"); + engine_ = ext::make_shared(Handle(vol_), + discountCurve); - engine_ = bond.blackEngine_; bond.setupArguments(engine_->getArguments()); results_ = - dynamic_cast(engine_->getResults()); + dynamic_cast(engine_->getResults()); } - Real CallableBond::ImpliedVolHelper::operator()(Volatility x) const { vol_->setValue(x); engine_->calculate(); // get the Black NPV based on vol x - return results_->value-targetValue_; + Real value = matchNPV_ ? results_->value : results_->settlementValue; + return value - targetValue_; + } + + + Volatility CallableBond::impliedVolatility( + const Bond::Price& targetPrice, + const Handle& discountCurve, + Real accuracy, + Size maxEvaluations, + Volatility minVol, + Volatility maxVol) const { + QL_REQUIRE(!isExpired(), "instrument expired"); + + Real dirtyTargetPrice; + switch (targetPrice.type()) { + case Bond::Price::Dirty: + dirtyTargetPrice = targetPrice.amount(); + break; + case Bond::Price::Clean: + dirtyTargetPrice = targetPrice.amount() + accruedAmount(); + break; + default: + QL_FAIL("unknown price type"); + } + + Real targetValue = dirtyTargetPrice * faceAmount_ / 100.0; + Volatility guess = 0.5 * (minVol + maxVol); + ImpliedVolHelper f(*this, discountCurve, targetValue, false); + Brent solver; + solver.setMaxEvaluations(maxEvaluations); + return solver.solve(f, accuracy, guess, minVol, maxVol); } Volatility CallableBond::impliedVolatility( @@ -99,16 +145,15 @@ namespace QuantLib { Size maxEvaluations, Volatility minVol, Volatility maxVol) const { - calculate(); QL_REQUIRE(!isExpired(), "instrument expired"); - Volatility guess = 0.5*(minVol + maxVol); - blackDiscountCurve_.linkTo(*discountCurve, false); - ImpliedVolHelper f(*this,targetValue); + Volatility guess = 0.5 * (minVol + maxVol); + ImpliedVolHelper f(*this, discountCurve, targetValue, true); Brent solver; solver.setMaxEvaluations(maxEvaluations); return solver.solve(f, accuracy, guess, minVol, maxVol); } + namespace { template @@ -220,6 +265,15 @@ namespace QuantLib { } + class CallableBond::NPVSpreadHelper { + public: + explicit NPVSpreadHelper(CallableBond& bond); + Real operator()(Spread x) const; + private: + CallableBond& bond_; + const Instrument::results* results_; + }; + CallableBond::NPVSpreadHelper::NPVSpreadHelper(CallableBond& bond): bond_(bond), results_(dynamic_cast(bond.engine_->getResults())) @@ -227,15 +281,15 @@ namespace QuantLib { bond.setupArguments(bond.engine_->getArguments()); } - Real CallableBond::NPVSpreadHelper::operator()(Real x) const - { - auto* args = dynamic_cast(bond_.engine_->getArguments()); - // Pops the original value when function finishes - RestoreVal restorer(args->spread); - args->spread=x; - bond_.engine_->calculate(); - return results_->value; - } + Real CallableBond::NPVSpreadHelper::operator()(Real x) const + { + auto* args = dynamic_cast(bond_.engine_->getArguments()); + // Pops the original value when function finishes + RestoreVal restorer(args->spread); + args->spread=x; + bond_.engine_->calculate(); + return results_->value; + } Spread CallableBond::OAS(Real cleanPrice, const Handle& engineTS, @@ -361,79 +415,9 @@ namespace QuantLib { } - CallableFixedRateBond::CallableFixedRateBond( - Natural settlementDays, - Real faceAmount, - const Schedule& schedule, - const std::vector& coupons, - const DayCounter& accrualDayCounter, - BusinessDayConvention paymentConvention, - Real redemption, - const Date& issueDate, - const CallabilitySchedule& putCallSchedule, - const Period& exCouponPeriod, - const Calendar& exCouponCalendar, - BusinessDayConvention exCouponConvention, - bool exCouponEndOfMonth) - : CallableBond(settlementDays, schedule, accrualDayCounter, - issueDate, putCallSchedule) { - - frequency_ = schedule.tenor().frequency(); - - bool isZeroCouponBond = (coupons.size() == 1 && close(coupons[0], 0.0)); - - if (!isZeroCouponBond) { - cashflows_ = - FixedRateLeg(schedule) - .withNotionals(faceAmount) - .withCouponRates(coupons, accrualDayCounter) - .withPaymentAdjustment(paymentConvention) - .withExCouponPeriod(exCouponPeriod, - exCouponCalendar, - exCouponConvention, - exCouponEndOfMonth); - - addRedemptionsToCashflows(std::vector(1, redemption)); - } else { - Date redemptionDate = calendar_.adjust(maturityDate_, - paymentConvention); - setSingleRedemption(faceAmount, redemption, redemptionDate); - } - - // used for impliedVolatility() calculation - ext::shared_ptr dummyVolQuote(new SimpleQuote(0.)); - blackVolQuote_.linkTo(dummyVolQuote); - blackEngine_ = ext::shared_ptr( - new BlackCallableFixedRateBondEngine(blackVolQuote_, - blackDiscountCurve_)); - } - - - Real CallableFixedRateBond::accrued(Date settlement) const { + void CallableBond::setupArguments(PricingEngine::arguments* args) const { - if (settlement == Date()) settlement = settlementDate(); - - const bool IncludeToday = false; - for (const auto& cashflow : cashflows_) { - // the first coupon paying after d is the one we're after - if (!cashflow->hasOccurred(settlement, IncludeToday)) { - ext::shared_ptr coupon = ext::dynamic_pointer_cast(cashflow); - if (coupon != nullptr) - // !!! - return coupon->accruedAmount(settlement) / - notional(settlement) * 100.0; - else - return 0.0; - } - } - return 0.0; - } - - - void CallableFixedRateBond::setupArguments( - PricingEngine::arguments* args) const { - - CallableBond::setupArguments(args); + Bond::setupArguments(args); auto* arguments = dynamic_cast(args); @@ -441,6 +425,7 @@ namespace QuantLib { Date settlement = arguments->settlementDate; + arguments->faceAmount = faceAmount_; arguments->redemption = redemption()->amount(); arguments->redemptionDate = redemption()->date(); @@ -488,6 +473,60 @@ namespace QuantLib { } + Real CallableBond::accrued(Date settlement) const { + + if (settlement == Date()) settlement = settlementDate(); + + const bool IncludeToday = false; + for (const auto& cashflow : cashflows_) { + // the first coupon paying after d is the one we're after + if (!cashflow->hasOccurred(settlement, IncludeToday)) { + ext::shared_ptr coupon = ext::dynamic_pointer_cast(cashflow); + if (coupon != nullptr) + // !!! + return coupon->accruedAmount(settlement) / + notional(settlement) * 100.0; + else + return 0.0; + } + } + return 0.0; + } + + + CallableFixedRateBond::CallableFixedRateBond( + Natural settlementDays, + Real faceAmount, + const Schedule& schedule, + const std::vector& coupons, + const DayCounter& accrualDayCounter, + BusinessDayConvention paymentConvention, + Real redemption, + const Date& issueDate, + const CallabilitySchedule& putCallSchedule, + const Period& exCouponPeriod, + const Calendar& exCouponCalendar, + BusinessDayConvention exCouponConvention, + bool exCouponEndOfMonth) + : CallableBond(settlementDays, schedule.dates().back(), schedule.calendar(), + accrualDayCounter, faceAmount, issueDate, putCallSchedule) { + + frequency_ = schedule.tenor().frequency(); + + cashflows_ = + FixedRateLeg(schedule) + .withNotionals(faceAmount) + .withCouponRates(coupons, accrualDayCounter) + .withPaymentAdjustment(paymentConvention) + .withExCouponPeriod(exCouponPeriod, + exCouponCalendar, + exCouponConvention, + exCouponEndOfMonth); + + addRedemptionsToCashflows({redemption}); + } + + CallableZeroCouponBond::CallableZeroCouponBond( Natural settlementDays, Real faceAmount, @@ -498,17 +537,15 @@ namespace QuantLib { Real redemption, const Date& issueDate, const CallabilitySchedule& putCallSchedule) - : CallableFixedRateBond(settlementDays,faceAmount, - Schedule(issueDate, maturityDate, - Period(Once), - calendar, - paymentConvention, - paymentConvention, - DateGeneration::Backward, - false), - std::vector(1, 0.0), dayCounter, - paymentConvention, redemption, - issueDate, putCallSchedule) {} + : CallableBond(settlementDays, maturityDate, calendar, + dayCounter, faceAmount, issueDate, putCallSchedule) { + + frequency_ = Once; + + Date redemptionDate = calendar_.adjust(maturityDate_, + paymentConvention); + setSingleRedemption(faceAmount, redemption, redemptionDate); + } } diff --git a/ql/experimental/callablebonds/callablebond.hpp b/ql/experimental/callablebonds/callablebond.hpp index f8900cf25af..4bd35f55526 100644 --- a/ql/experimental/callablebonds/callablebond.hpp +++ b/ql/experimental/callablebonds/callablebond.hpp @@ -55,6 +55,7 @@ namespace QuantLib { class arguments; class results; class engine; + //! \name Inspectors //@{ //! return the bond's put/call schedule @@ -62,6 +63,7 @@ namespace QuantLib { return putCallSchedule_; } //@} + //! \name Calculations //@{ //! returns the Black implied forward yield volatility @@ -69,6 +71,20 @@ namespace QuantLib { Chapter 20, pg 536). Relevant only to European put/call schedules */ + Volatility impliedVolatility( + const Bond::Price& targetPrice, + const Handle& discountCurve, + Real accuracy, + Size maxEvaluations, + Volatility minVol, + Volatility maxVol) const; + + /*! \warning This version of the method takes an NPV as target, not a price. + + \deprecated Use the other overload. + Deprecated in version 1.28. + */ + QL_DEPRECATED Volatility impliedVolatility( Real targetValue, const Handle& discountCurve, @@ -124,47 +140,35 @@ namespace QuantLib { Real bump=2e-4); //@} + void setupArguments(PricingEngine::arguments* args) const override; + protected: CallableBond(Natural settlementDays, - const Schedule& schedule, + const Date& maturityDate, + const Calendar& calendar, DayCounter paymentDayCounter, + Real faceAmount, const Date& issueDate = Date(), CallabilitySchedule putCallSchedule = CallabilitySchedule()); DayCounter paymentDayCounter_; Frequency frequency_; CallabilitySchedule putCallSchedule_; - //! must be set by derived classes for impliedVolatility() to work - mutable ext::shared_ptr blackEngine_; - //! Black fwd yield volatility quote handle to internal blackEngine_ - mutable RelinkableHandle blackVolQuote_; - //! Black fwd yield volatility quote handle to internal blackEngine_ - mutable RelinkableHandle blackDiscountCurve_; - //! helper class for Black implied volatility calculation + Real faceAmount_; + // helper class for Black implied volatility calculation class ImpliedVolHelper; - friend class ImpliedVolHelper; - class ImpliedVolHelper { - public: - ImpliedVolHelper(const CallableBond& bond, - Real targetValue); - Real operator()(Volatility x) const; - private: - ext::shared_ptr engine_; - Real targetValue_; - ext::shared_ptr vol_; - const Instrument::results* results_; - }; - //! Helper class for option adjusted spread calculations + // helper class for option adjusted spread calculations class NPVSpreadHelper; - friend class NPVSpreadHelper; - class NPVSpreadHelper { - public: - explicit NPVSpreadHelper(CallableBond& bond); - Real operator()(Spread x) const; - private: - CallableBond& bond_; - const Instrument::results* results_; - }; + + private: + /* Used internally. + same as Bond::accruedAmount() but with enable early + payments true. Forces accrued to be calculated in a + consistent way for future put/ call dates, which can be + problematic in lattice engines when option dates are also + coupon dates. + */ + Real accrued(Date settlement) const; }; class CallableBond::arguments : public Bond::arguments { @@ -172,6 +176,7 @@ namespace QuantLib { arguments() = default; std::vector couponDates; std::vector couponAmounts; + Real faceAmount; //! redemption = face amount * redemption / 100. Real redemption; Date redemptionDate; @@ -212,28 +217,14 @@ namespace QuantLib { const Schedule& schedule, const std::vector& coupons, const DayCounter& accrualDayCounter, - BusinessDayConvention paymentConvention - = Following, + BusinessDayConvention paymentConvention = Following, Real redemption = 100.0, const Date& issueDate = Date(), - const CallabilitySchedule& putCallSchedule - = CallabilitySchedule(), + const CallabilitySchedule& putCallSchedule = {}, const Period& exCouponPeriod = Period(), const Calendar& exCouponCalendar = Calendar(), BusinessDayConvention exCouponConvention = Unadjusted, bool exCouponEndOfMonth = false); - - void setupArguments(PricingEngine::arguments* args) const override; - - private: - //! accrued interest used internally, where includeToday = false - /*! same as Bond::accruedAmount() but with enable early - payments true. Forces accrued to be calculated in a - consistent way for future put/ call dates, which can be - problematic in lattice engines when option dates are also - coupon dates. - */ - Real accrued(Date settlement) const; }; //! callable/puttable zero coupon bond @@ -241,19 +232,17 @@ namespace QuantLib { \ingroup instruments */ - class CallableZeroCouponBond : public CallableFixedRateBond { + class CallableZeroCouponBond : public CallableBond { public: CallableZeroCouponBond(Natural settlementDays, Real faceAmount, const Calendar& calendar, const Date& maturityDate, const DayCounter& dayCounter, - BusinessDayConvention paymentConvention - = Following, + BusinessDayConvention paymentConvention = Following, Real redemption = 100.0, const Date& issueDate = Date(), - const CallabilitySchedule& putCallSchedule - = CallabilitySchedule()); + const CallabilitySchedule& putCallSchedule = {}); }; } diff --git a/ql/experimental/callablebonds/discretizedcallablefixedratebond.cpp b/ql/experimental/callablebonds/discretizedcallablefixedratebond.cpp index f9d84513d00..d71fe9077f5 100644 --- a/ql/experimental/callablebonds/discretizedcallablefixedratebond.cpp +++ b/ql/experimental/callablebonds/discretizedcallablefixedratebond.cpp @@ -94,6 +94,7 @@ namespace QuantLib { } } + adjustedCallabilityPrices_[i] *= arguments_.faceAmount / 100.0; callabilityTimes_[i] = callabilityTime; } } diff --git a/test-suite/callablebonds.cpp b/test-suite/callablebonds.cpp index 88faaaf4943..7c3dc8d9ac1 100644 --- a/test-suite/callablebonds.cpp +++ b/test-suite/callablebonds.cpp @@ -22,6 +22,7 @@ #include "utilities.hpp" #include #include +#include #include #include #include @@ -88,8 +89,7 @@ namespace { dayCounter = Actual365Fixed(); rollingConvention = ModifiedFollowing; - today = Date::todaysDate(); - Settings::instance().evaluationDate() = today; + today = Settings::instance().evaluationDate(); settlement = calendar.advance(today,2,Days); } }; @@ -524,7 +524,7 @@ void CallableBondTest::testCached() { double tolerance = 1.0e-8; double storedPrice1 = 110.60975477; - CallableFixedRateBond bond1(3, 100.0, schedule, + CallableFixedRateBond bond1(3, 10000.0, schedule, coupons, Thirty360(Thirty360::BondBasis), vars.rollingConvention, 100.0, vars.issueDate(), @@ -539,7 +539,7 @@ void CallableBondTest::testCached() { << " expected: " << storedPrice1); double storedPrice2 = 115.16559362; - CallableFixedRateBond bond2(3, 100.0, schedule, + CallableFixedRateBond bond2(3, 10000.0, schedule, coupons, Thirty360(Thirty360::BondBasis), vars.rollingConvention, 100.0, vars.issueDate(), @@ -554,7 +554,7 @@ void CallableBondTest::testCached() { << " expected: " << storedPrice2); double storedPrice3 = 110.97509625; - CallableFixedRateBond bond3(3, 100.0, schedule, + CallableFixedRateBond bond3(3, 10000.0, schedule, coupons, Thirty360(Thirty360::BondBasis), vars.rollingConvention, 100.0, vars.issueDate(), @@ -674,6 +674,143 @@ void CallableBondTest::testSnappingExerciseDate2ClosestCouponDate() { } } + +void CallableBondTest::testBlackEngine() { + + BOOST_TEST_MESSAGE("Testing Black engine for European callable bonds..."); + + Globals vars; + + vars.today = Date(20, September, 2022); + Settings::instance().evaluationDate() = vars.today; + vars.settlement = vars.calendar.advance(vars.today, 3, Days); + + vars.termStructure.linkTo(vars.makeFlatCurve(0.03)); + + CallabilitySchedule callabilities = { + ext::make_shared( + Bond::Price(100.0, Bond::Price::Clean), + Callability::Call, + vars.calendar.advance(vars.issueDate(),4,Years)) + }; + + CallableZeroCouponBond bond(3, 10000.0, vars.calendar, + vars.maturityDate(), Thirty360(Thirty360::BondBasis), + vars.rollingConvention, 100.0, + vars.issueDate(), callabilities); + + bond.setPricingEngine(ext::make_shared( + Handle(ext::make_shared(0.3)), vars.termStructure)); + + Real expected = 74.52915084; + Real calculated = bond.cleanPrice(); + + if (std::fabs(calculated - expected) > 1.0e-4) + BOOST_ERROR( + "failed to reproduce cached price:\n" + << std::setprecision(5) + << " calculated NPV: " << calculated << "\n" + << " expected: " << expected << "\n" + << " difference: " << calculated - expected); +} + + +void CallableBondTest::testImpliedVol() { + + BOOST_TEST_MESSAGE("Testing implied-volatility calculation for callable bonds..."); + + Globals vars; + + vars.termStructure.linkTo(vars.makeFlatCurve(0.03)); + + Schedule schedule = + MakeSchedule() + .from(vars.issueDate()) + .to(vars.maturityDate()) + .withCalendar(vars.calendar) + .withFrequency(Semiannual) + .withConvention(vars.rollingConvention) + .withRule(DateGeneration::Backward); + + std::vector coupons = { 0.01 }; + + CallabilitySchedule callabilities = { + ext::make_shared( + Bond::Price(100.0, Bond::Price::Clean), + Callability::Call, + schedule.at(8)) + }; + + CallableFixedRateBond bond(3, 10000.0, schedule, + coupons, Thirty360(Thirty360::BondBasis), + vars.rollingConvention, + 100.0, vars.issueDate(), + callabilities); + + auto targetPrice = Bond::Price(78.50, Bond::Price::Dirty); + Real volatility = bond.impliedVolatility(targetPrice, + vars.termStructure, + 1e-8, // accuracy + 200, // max evaluations + 1e-4, // min vol + 1.0); // max vol + + bond.setPricingEngine(ext::make_shared( + Handle(ext::make_shared(volatility)), vars.termStructure)); + + if (std::fabs(bond.dirtyPrice() - targetPrice.amount()) > 1.0e-4) + BOOST_ERROR( + "failed to reproduce target dirty price with implied volatility:\n" + << std::setprecision(5) + << " calculated price: " << bond.dirtyPrice() << "\n" + << " expected: " << targetPrice.amount() << "\n" + << " difference: " << bond.dirtyPrice() - targetPrice.amount()); + + targetPrice = Bond::Price(78.50, Bond::Price::Clean); + volatility = bond.impliedVolatility(targetPrice, + vars.termStructure, + 1e-8, // accuracy + 200, // max evaluations + 1e-4, // min vol + 1.0); // max vol + + bond.setPricingEngine(ext::make_shared( + Handle(ext::make_shared(volatility)), vars.termStructure)); + + if (std::fabs(bond.cleanPrice() - targetPrice.amount()) > 1.0e-4) + BOOST_ERROR( + "failed to reproduce target clean price with implied volatility:\n" + << std::setprecision(5) + << " calculated price: " << bond.cleanPrice() << "\n" + << " expected: " << targetPrice.amount() << "\n" + << " difference: " << bond.cleanPrice() - targetPrice.amount()); + + + QL_DEPRECATED_DISABLE_WARNING + + Real targetNPV = 7850.0; + volatility = bond.impliedVolatility(targetNPV, + vars.termStructure, + 1e-8, // accuracy + 200, // max evaluations + 1e-4, // min vol + 1.0); // max vol + + QL_DEPRECATED_ENABLE_WARNING + + bond.setPricingEngine(ext::make_shared( + Handle(ext::make_shared(volatility)), vars.termStructure)); + + if (std::fabs(bond.NPV() - targetNPV) > 1.0e-4) + BOOST_ERROR( + "failed to reproduce target NPV with implied volatility:\n" + << std::setprecision(5) + << " calculated NPV: " << bond.NPV() << "\n" + << " expected: " << targetNPV << "\n" + << " difference: " << bond.NPV() - targetNPV); +} + + test_suite* CallableBondTest::suite() { auto* suite = BOOST_TEST_SUITE("Callable-bond tests"); suite->add(QUANTLIB_TEST_CASE(&CallableBondTest::testConsistency)); @@ -682,5 +819,7 @@ test_suite* CallableBondTest::suite() { suite->add(QUANTLIB_TEST_CASE(&CallableBondTest::testDegenerate)); suite->add(QUANTLIB_TEST_CASE(&CallableBondTest::testCached)); suite->add(QUANTLIB_TEST_CASE(&CallableBondTest::testSnappingExerciseDate2ClosestCouponDate)); + suite->add(QUANTLIB_TEST_CASE(&CallableBondTest::testBlackEngine)); + suite->add(QUANTLIB_TEST_CASE(&CallableBondTest::testImpliedVol)); return suite; } diff --git a/test-suite/callablebonds.hpp b/test-suite/callablebonds.hpp index 45c4e7b541a..ed55fed9eed 100644 --- a/test-suite/callablebonds.hpp +++ b/test-suite/callablebonds.hpp @@ -31,6 +31,8 @@ class CallableBondTest { static void testDegenerate(); static void testCached(); static void testSnappingExerciseDate2ClosestCouponDate(); + static void testBlackEngine(); + static void testImpliedVol(); static boost::unit_test_framework::test_suite* suite(); };