Skip to content

Commit

Permalink
#1986: Fixed pricing in partanalyticbarengine, code refactoring, test…
Browse files Browse the repository at this point in the history
… fixed
  • Loading branch information
paolodelia99 committed Jan 26, 2025
1 parent 98b6a9b commit 829c38f
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<StrikedTypePayoff> call_payoff =
ext::make_shared<PlainVanillaPayoff>(opt_type, call_strike);
auto createSymmetricCallOpt = [&]() -> PartialTimeBarrierOption {
Real spotSq = spot * spot;
Real callStrike = spotSq / payoff->strike();
Option::Type optType = Option::Call;
ext::shared_ptr<StrikedTypePayoff> callPayoff =
ext::make_shared<PlainVanillaPayoff>(optType, callStrike);
ext::shared_ptr<Exercise> 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<GeneralizedBlackScholesProcess> callProcess =
ext::make_shared<GeneralizedBlackScholesProcess>(
process_->stateVariable(),
rRate,
qYield,
process_->blackVolatility()
);
ext::shared_ptr<PricingEngine> engine =
ext::make_shared<AnalyticPartialTimeBarrierOptionEngine>(process_);
ext::make_shared<AnalyticPartialTimeBarrierOptionEngine>(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()) {
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion ql/pricingengines/barrier/analyticbarrierengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ namespace QuantLib {
return (1 + mu()) * stdDeviation();
}

Real AnalyticBarrierEngine::costOfCarry() const {
DiscountFactor AnalyticBarrierEngine::costOfCarry() const {
return dividendDiscount() / riskFreeDiscount();
}

Expand Down
2 changes: 1 addition & 1 deletion ql/pricingengines/barrier/analyticbarrierengine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
123 changes: 67 additions & 56 deletions test-suite/partialtimebarrieroption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,30 +157,30 @@ BOOST_AUTO_TEST_CASE(testAnalyticEnginePutOption) {
ext::make_shared<AnalyticPartialTimeBarrierOptionEngine>(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) {
Expand Down Expand Up @@ -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[] = {
Expand All @@ -241,50 +241,61 @@ BOOST_AUTO_TEST_CASE(testPutCallSymmetry) {
Date maturity = today + 360;
ext::shared_ptr<Exercise> exercise =
ext::make_shared<EuropeanExercise>(maturity);
Real r = 0.01;
Real rebate = 0.0;
Real spotPrice = 100.0;

ext::shared_ptr<SimpleQuote> spot = ext::make_shared<SimpleQuote>();
ext::shared_ptr<SimpleQuote> qRate = ext::make_shared<SimpleQuote>(0.0);
ext::shared_ptr<SimpleQuote> rRate = ext::make_shared<SimpleQuote>(0.01);
ext::shared_ptr<SimpleQuote> rRate = ext::make_shared<SimpleQuote>(r);
ext::shared_ptr<SimpleQuote> vol = ext::make_shared<SimpleQuote>(0.25);

Handle<Quote> underlying(spot);
Handle<YieldTermStructure> dividendTS(flatRate(today, qRate, dc));
Handle<YieldTermStructure> riskFreeTS(flatRate(today, rRate, dc));
Handle<YieldTermStructure> dividendTSCall(flatRate(today, qRate, dc));
Handle<YieldTermStructure> riskFreeTSCall(flatRate(today, rRate, dc));
Handle<YieldTermStructure> dividendTSPut(flatRate(today, rRate, dc));
Handle<YieldTermStructure> riskFreeTSPut(flatRate(today, qRate, dc));
Handle<BlackVolTermStructure> blackVolTS(flatVol(today, vol, dc));

const ext::shared_ptr<BlackScholesMertonProcess> process =
const ext::shared_ptr<BlackScholesMertonProcess> callProcess =
ext::make_shared<BlackScholesMertonProcess>(underlying,
dividendTS,
riskFreeTS,
dividendTSCall,
riskFreeTSCall,
blackVolTS);
ext::shared_ptr<PricingEngine> engine =
ext::make_shared<AnalyticPartialTimeBarrierOptionEngine>(process);
const ext::shared_ptr<BlackScholesMertonProcess> putProcess =
ext::make_shared<BlackScholesMertonProcess>(underlying,
dividendTSPut,
riskFreeTSPut,
blackVolTS);
ext::shared_ptr<PricingEngine> callEngine =
ext::make_shared<AnalyticPartialTimeBarrierOptionEngine>(callProcess);
ext::shared_ptr<PricingEngine> putEngine =
ext::make_shared<AnalyticPartialTimeBarrierOptionEngine>(putProcess);

for (auto& i : cases) {
Date coverEventDate = today + i.days;
ext::shared_ptr<StrikedTypePayoff> put_payoff =
ext::make_shared<PlainVanillaPayoff>(Option::Put, i.put_strike);
ext::shared_ptr<StrikedTypePayoff> call_payoff =
ext::make_shared<PlainVanillaPayoff>(Option::Call, i.call_strike);
PartialTimeBarrierOption put_option(i.put_type,
ext::shared_ptr<StrikedTypePayoff> putPayoff =
ext::make_shared<PlainVanillaPayoff>(Option::Put, i.putStrike);
ext::shared_ptr<StrikedTypePayoff> callPayoff =
ext::make_shared<PlainVanillaPayoff>(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 "
Expand Down

0 comments on commit 829c38f

Please sign in to comment.