Coverage Report

Created: 2025-12-08 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/pricingengines/exotic/analyticeuropeanmargrabeengine.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2010 Master IMAFA - Polytech'Nice Sophia - Université de Nice Sophia Antipolis
5
6
 This file is part of QuantLib, a free-software/open-source library
7
 for financial quantitative analysts and developers - http://quantlib.org/
8
9
 QuantLib is free software: you can redistribute it and/or modify it
10
 under the terms of the QuantLib license.  You should have received a
11
 copy of the license along with this program; if not, please email
12
 <quantlib-dev@lists.sf.net>. The license is also available online at
13
 <https://www.quantlib.org/license.shtml>.
14
15
 This program is distributed in the hope that it will be useful, but WITHOUT
16
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 FOR A PARTICULAR PURPOSE.  See the license for more details.
18
*/
19
20
#include <ql/exercise.hpp>
21
#include <ql/pricingengines/exotic/analyticeuropeanmargrabeengine.hpp>
22
#include <ql/instruments/payoffs.hpp>
23
#include <ql/math/distributions/normaldistribution.hpp>
24
#include <utility>
25
26
namespace QuantLib {
27
28
    AnalyticEuropeanMargrabeEngine::AnalyticEuropeanMargrabeEngine(
29
        ext::shared_ptr<GeneralizedBlackScholesProcess> process1,
30
        ext::shared_ptr<GeneralizedBlackScholesProcess> process2,
31
        Real correlation)
32
0
    : process1_(std::move(process1)), process2_(std::move(process2)), rho_(correlation) {
33
0
        registerWith(process1_);
34
0
        registerWith(process2_);
35
0
    }
36
37
0
    void AnalyticEuropeanMargrabeEngine::calculate() const {
38
39
0
        QL_REQUIRE(arguments_.exercise->type() == Exercise::European,
40
0
                   "not an European Option");
41
42
0
        ext::shared_ptr<EuropeanExercise> exercise =
43
0
            ext::dynamic_pointer_cast<EuropeanExercise>(arguments_.exercise);
44
0
        QL_REQUIRE(exercise, "not an European Option");
45
46
0
        ext::shared_ptr<NullPayoff> payoff =
47
0
            ext::dynamic_pointer_cast<NullPayoff>(arguments_.payoff);
48
0
        QL_REQUIRE(payoff, "non a Null Payoff type");
49
50
0
        Integer quantity1 = arguments_.Q1;
51
0
        Integer quantity2 = arguments_.Q2;
52
53
0
        Real s1  = process1_->stateVariable()->value();
54
0
        Real s2  = process2_->stateVariable()->value();
55
56
0
        Real variance1 = process1_->blackVolatility()->blackVariance(
57
0
                                                exercise->lastDate(), s1);
58
0
        Real variance2 = process2_->blackVolatility()->blackVariance(
59
0
                                                exercise->lastDate(), s2);
60
61
0
        DiscountFactor riskFreeDiscount =
62
0
            process1_->riskFreeRate()->discount(exercise->lastDate());
63
64
0
        DiscountFactor dividendDiscount1 =
65
0
            process1_->dividendYield()->discount(exercise->lastDate());
66
0
        DiscountFactor dividendDiscount2 =
67
0
            process2_->dividendYield()->discount(exercise->lastDate());
68
69
0
        Real forward1 = process1_->stateVariable()->value() *
70
0
            dividendDiscount1 / riskFreeDiscount;
71
0
        Real forward2 = process2_->stateVariable()->value() *
72
0
            dividendDiscount2 / riskFreeDiscount;
73
74
0
        Real stdDev1 = std::sqrt(variance1);
75
0
        Real stdDev2 = std::sqrt(variance2);
76
0
        Real variance = variance1 + variance2 - 2*rho_*stdDev1*stdDev2;
77
0
        Real stdDev = std::sqrt(variance);
78
0
        Real d1 = (std::log((quantity1*forward1)/(quantity2*forward2))
79
0
                   + 0.5*variance) / stdDev;
80
0
        Real d2 = d1 - stdDev;
81
0
        Real Nd1, Nd2, nd1, nd2;
82
0
        CumulativeNormalDistribution cum;
83
0
        NormalDistribution norm;
84
0
        Nd1 = cum(d1);
85
0
        Nd2 = cum(d2);
86
0
        nd1 = norm(d1);
87
0
        nd2 = norm(d2);
88
0
        DayCounter rfdc  = process1_->riskFreeRate()->dayCounter();
89
0
        Time t = rfdc.yearFraction(process1_->riskFreeRate()->referenceDate(),
90
0
                                  arguments_.exercise->lastDate());
91
0
        Real sqt = std::sqrt(t);
92
0
        Real q1  = -std::log(dividendDiscount1)/(sqt*sqt);
93
0
        Real q2  = -std::log(dividendDiscount2)/(sqt*sqt);
94
95
0
        results_.value =
96
0
            riskFreeDiscount * (quantity1*forward1*Nd1 - quantity2*forward2*Nd2);
97
98
        // Greeks
99
0
        results_.delta1 = riskFreeDiscount*(quantity1*forward1*Nd1)/s1;
100
0
        results_.delta2 = -riskFreeDiscount*(quantity2*forward2*Nd2)/s2;
101
0
        results_.gamma1 = (riskFreeDiscount*(quantity1*forward1*nd1)/s1)/(quantity1*s1*stdDev);
102
0
        results_.gamma2 = (-riskFreeDiscount*(quantity2*forward2*nd2)/s2)/(-quantity2*s2*stdDev);
103
0
        Real vega       = riskFreeDiscount*(quantity1*forward1*nd1)*sqt;
104
0
        results_.theta  = -((stdDev*vega/sqt)/(2*t)-(q1*quantity1*s1*results_.delta1)-(q2*quantity2*s2*results_.delta2));
105
0
        results_.rho    = 0.0;
106
0
    }
107
108
}