From 829c38f1c563815a00ff22976f7d426a506b1e3e Mon Sep 17 00:00:00 2001 From: paolodelia99 Date: Sun, 26 Jan 2025 14:45:39 +0100 Subject: [PATCH] #1986: Fixed pricing in partanalyticbarengine, code refactoring, test fixed --- ...analyticpartialtimebarrieroptionengine.cpp | 51 +++++--- .../barrier/analyticbarrierengine.cpp | 2 +- .../barrier/analyticbarrierengine.hpp | 2 +- test-suite/partialtimebarrieroption.cpp | 123 ++++++++++-------- 4 files changed, 99 insertions(+), 79 deletions(-) diff --git a/ql/experimental/exoticoptions/analyticpartialtimebarrieroptionengine.cpp b/ql/experimental/exoticoptions/analyticpartialtimebarrieroptionengine.cpp index 94ce093615..09abb3695d 100644 --- a/ql/experimental/exoticoptions/analyticpartialtimebarrieroptionengine.cpp +++ b/ql/experimental/exoticoptions/analyticpartialtimebarrieroptionengine.cpp @@ -44,39 +44,48 @@ namespace QuantLib { PartialBarrier::Type barrierType = arguments_.barrierType; PartialBarrier::Range barrierRange = arguments_.barrierRange; - auto get_symmetric_opt_type = [](Barrier::Type barrier_type) -> Barrier::Type { - if (barrier_type == Barrier::UpIn) return Barrier::DownIn; - if (barrier_type == Barrier::DownIn) return Barrier::UpIn; - if (barrier_type == Barrier::UpOut) return Barrier::DownOut; + auto getSymmetricBarrierType = [](Barrier::Type barrierType) -> Barrier::Type { + if (barrierType == Barrier::UpIn) return Barrier::DownIn; + if (barrierType == Barrier::DownIn) return Barrier::UpIn; + if (barrierType == Barrier::UpOut) return Barrier::DownOut; return Barrier::UpOut; }; - auto create_symmetric_call_opt = [&]() -> PartialTimeBarrierOption { - Real spot_sq = spot * spot; - Real call_strike = spot_sq / payoff->strike(); - Option::Type opt_type = Option::Call; - ext::shared_ptr call_payoff = - ext::make_shared(opt_type, call_strike); + auto createSymmetricCallOpt = [&]() -> PartialTimeBarrierOption { + Real spotSq = spot * spot; + Real callStrike = spotSq / payoff->strike(); + Option::Type optType = Option::Call; + ext::shared_ptr callPayoff = + ext::make_shared(optType, callStrike); ext::shared_ptr exercise = arguments_.exercise; - Real call_barrier = spot_sq / arguments_.barrier; + Real callBarrier = spotSq / arguments_.barrier; Real rebate = arguments_.barrier; Date coverEventDate = arguments_.coverEventDate; - PartialTimeBarrierOption symmetric_call_option( - get_symmetric_opt_type(barrierType), + PartialTimeBarrierOption symmetricCallOption( + getSymmetricBarrierType(barrierType), arguments_.barrierRange, - call_barrier, rebate, + callBarrier, rebate, coverEventDate, - call_payoff, exercise + callPayoff, exercise ); - return symmetric_call_option; + return symmetricCallOption; }; - auto price_symmetric_call_opt = [&](PartialTimeBarrierOption option) -> Real { + auto priceSymmetricCallOpt = [&](PartialTimeBarrierOption option) -> Real { + auto rRate = process_->riskFreeRate(); + auto qYield = process_->dividendYield(); + const ext::shared_ptr callProcess = + ext::make_shared( + process_->stateVariable(), + rRate, + qYield, + process_->blackVolatility() + ); ext::shared_ptr engine = - ext::make_shared(process_); + ext::make_shared(callProcess); option.setPricingEngine(engine); - Real call_value = option.NPV(); - return payoff->strike() / spot * call_value; + Real callValue = option.NPV(); + return payoff->strike() / spot * callValue; }; switch (payoff->optionType()) { @@ -144,7 +153,7 @@ namespace QuantLib { break; case Option::Put: - results_.value = price_symmetric_call_opt(create_symmetric_call_opt()); + results_.value = priceSymmetricCallOpt(createSymmetricCallOpt()); break; default: diff --git a/ql/pricingengines/barrier/analyticbarrierengine.cpp b/ql/pricingengines/barrier/analyticbarrierengine.cpp index 4cfa181131..cec381de79 100644 --- a/ql/pricingengines/barrier/analyticbarrierengine.cpp +++ b/ql/pricingengines/barrier/analyticbarrierengine.cpp @@ -178,7 +178,7 @@ namespace QuantLib { return (1 + mu()) * stdDeviation(); } - Real AnalyticBarrierEngine::costOfCarry() const { + DiscountFactor AnalyticBarrierEngine::costOfCarry() const { return dividendDiscount() / riskFreeDiscount(); } diff --git a/ql/pricingengines/barrier/analyticbarrierengine.hpp b/ql/pricingengines/barrier/analyticbarrierengine.hpp index 17d560f141..56c958515c 100644 --- a/ql/pricingengines/barrier/analyticbarrierengine.hpp +++ b/ql/pricingengines/barrier/analyticbarrierengine.hpp @@ -64,7 +64,7 @@ namespace QuantLib { DiscountFactor dividendDiscount() const; Rate mu() const; Real muSigma() const; - Real costOfCarry() const; + DiscountFactor costOfCarry() const; Real A(Real phi) const; Real B(Real phi) const; Real C(Real eta, Real phi) const; diff --git a/test-suite/partialtimebarrieroption.cpp b/test-suite/partialtimebarrieroption.cpp index 113fec71ce..c3248eae0e 100644 --- a/test-suite/partialtimebarrieroption.cpp +++ b/test-suite/partialtimebarrieroption.cpp @@ -157,30 +157,30 @@ BOOST_AUTO_TEST_CASE(testAnalyticEnginePutOption) { ext::make_shared(process); TestCase cases[] = { - { 95.0, 90.0, 1, 5.4163 }, - { 95.0, 95.0, 1, 6.6971 }, - { 90.0, 95.0, 1, 12.5330 }, - { 99.0, 90.0, 1, 1.3410 }, - - { 95.0, 90.0, 90, 8.1702 }, - { 95.0, 95.0, 90, 10.1678 }, - { 90.0, 95.0, 90, 14.0314 }, - { 99.0, 90.0, 90, 6.0043 }, - - { 95.0, 90.0, 180, 9.7281 }, - { 95.0, 95.0, 180, 12.2367 }, - { 90.0, 95.0, 180, 15.5553 }, - { 99.0, 90.0, 180, 7.8045 }, - - { 95.0, 90.0, 270, 10.6227 }, - { 95.0, 95.0, 270, 13.5662 }, - { 90.0, 95.0, 270, 16.6170 }, - { 99.0, 90.0, 270, 8.8133 }, - - { 95.0, 90.0, 359, 10.9186 }, - { 95.0, 95.0, 359, 14.2270 }, - { 90.0, 95.0, 359, 17.1611 }, - { 99.0, 90.0, 359, 9.1440 } + { 95.0, 90.0, 1, 1.5551 }, + { 95.0, 95.0, 1, 2.0589 }, + { 90.0, 95.0, 1, 4.4512 }, + { 99.0, 90.0, 1, 0.3404 }, + + { 95.0, 90.0, 90, 2.4181 }, + { 95.0, 95.0, 90, 3.2257 }, + { 90.0, 95.0, 90, 5.0624 }, + { 99.0, 90.0, 90, 1.5992 }, + + { 95.0, 90.0, 180, 3.0021 }, + { 95.0, 95.0, 180, 4.0617 }, + { 90.0, 95.0, 180, 5.7960 }, + { 99.0, 90.0, 180, 2.1903 }, + + { 95.0, 90.0, 270, 3.4194 }, + { 95.0, 95.0, 270, 4.7362 }, + { 90.0, 95.0, 270, 6.4370 }, + { 99.0, 90.0, 270, 2.6025 }, + + { 95.0, 90.0, 359, 3.5965 }, + { 95.0, 95.0, 359, 5.1865 }, + { 90.0, 95.0, 359, 6.8782 }, + { 99.0, 90.0, 359, 2.7759 } }; for (auto& i : cases) { @@ -214,13 +214,13 @@ BOOST_AUTO_TEST_CASE(testPutCallSymmetry) { Date today = Settings::instance().evaluationDate(); struct PutCallSymmetryTestCase { - Real call_strike; - Real call_barrier; - PartialBarrier::Type call_type; - Real put_strike; - Real put_barrier; + Real callStrike; + Real callBarrier; + Barrier::Type callType; + Real putStrike; + Real putBarrier; Integer days; - PartialBarrier::Type put_type; + Barrier::Type putType; }; PutCallSymmetryTestCase cases[] = { @@ -241,50 +241,61 @@ BOOST_AUTO_TEST_CASE(testPutCallSymmetry) { Date maturity = today + 360; ext::shared_ptr exercise = ext::make_shared(maturity); + Real r = 0.01; Real rebate = 0.0; + Real spotPrice = 100.0; ext::shared_ptr spot = ext::make_shared(); ext::shared_ptr qRate = ext::make_shared(0.0); - ext::shared_ptr rRate = ext::make_shared(0.01); + ext::shared_ptr rRate = ext::make_shared(r); ext::shared_ptr vol = ext::make_shared(0.25); Handle underlying(spot); - Handle dividendTS(flatRate(today, qRate, dc)); - Handle riskFreeTS(flatRate(today, rRate, dc)); + Handle dividendTSCall(flatRate(today, qRate, dc)); + Handle riskFreeTSCall(flatRate(today, rRate, dc)); + Handle dividendTSPut(flatRate(today, rRate, dc)); + Handle riskFreeTSPut(flatRate(today, qRate, dc)); Handle blackVolTS(flatVol(today, vol, dc)); - const ext::shared_ptr process = + const ext::shared_ptr callProcess = ext::make_shared(underlying, - dividendTS, - riskFreeTS, + dividendTSCall, + riskFreeTSCall, blackVolTS); - ext::shared_ptr engine = - ext::make_shared(process); + const ext::shared_ptr putProcess = + ext::make_shared(underlying, + dividendTSPut, + riskFreeTSPut, + blackVolTS); + ext::shared_ptr callEngine = + ext::make_shared(callProcess); + ext::shared_ptr putEngine = + ext::make_shared(putProcess); for (auto& i : cases) { Date coverEventDate = today + i.days; - ext::shared_ptr put_payoff = - ext::make_shared(Option::Put, i.put_strike); - ext::shared_ptr call_payoff = - ext::make_shared(Option::Call, i.call_strike); - PartialTimeBarrierOption put_option(i.put_type, + ext::shared_ptr putPayoff = + ext::make_shared(Option::Put, i.putStrike); + ext::shared_ptr callPayoff = + ext::make_shared(Option::Call, i.callStrike); + PartialTimeBarrierOption putOption(i.putType, PartialBarrier::EndB1, - i.put_barrier, rebate, + i.putBarrier, rebate, coverEventDate, - put_payoff, exercise); - put_option.setPricingEngine(engine); - PartialTimeBarrierOption call_option(i.call_type, + putPayoff, exercise); + putOption.setPricingEngine(putEngine); + PartialTimeBarrierOption callOption(i.callType, PartialBarrier::EndB1, - i.call_barrier, rebate, + i.callBarrier, rebate, coverEventDate, - call_payoff, exercise); - call_option.setPricingEngine(engine); - - spot->setValue(100.0); - Real put_value = put_option.NPV(); - Real call_value = call_option.NPV(); - Real call_amount = (i.put_strike / 100.0); - Real error = std::fabs(put_value - call_amount * call_value); + callPayoff, exercise); + callOption.setPricingEngine(callEngine); + + spot->setValue(spotPrice); + Real putValue = putOption.NPV(); + Real callValue = callOption.NPV(); + Real callAmount = (i.putStrike / spotPrice); + Real error = std::fabs(putValue - callAmount * callValue); Real tolerance = 1e-4; if (error > tolerance) BOOST_ERROR("Failed to reproduce the put-call symmetry for the partial-time barrier options "