Coverage Report

Created: 2026-01-25 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/pricingengines/exotic/analyticcomplexchooserengine.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2014 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/analyticcomplexchooserengine.hpp>
22
#include <ql/math/distributions/bivariatenormaldistribution.hpp>
23
#include <utility>
24
25
using std::pow;
26
using std::log;
27
using std::exp;
28
using std::sqrt;
29
30
namespace QuantLib {
31
32
    AnalyticComplexChooserEngine::AnalyticComplexChooserEngine(
33
        ext::shared_ptr<GeneralizedBlackScholesProcess> process)
34
0
    : process_(std::move(process)) {
35
0
        registerWith(process_);
36
0
    }
37
38
0
    void AnalyticComplexChooserEngine::calculate() const {
39
0
        Real S = process_->x0();
40
0
        Real b;
41
0
        Real v;
42
0
        Real Xc = arguments_.strikeCall;
43
0
        Real Xp = arguments_.strikePut;
44
0
        Time T = choosingTime();
45
0
        Time Tc = callMaturity() - T;
46
0
        Time Tp = putMaturity() - T;
47
48
0
        Real i = criticalValue();
49
50
0
        b = riskFreeRate(T) - dividendYield(T);
51
0
        v = volatility(T);
52
0
        Real d1 = (log(S / i) + (b + pow(v, 2) / 2)*T) / (v*sqrt(T));
53
0
        Real d2 = d1 - v*sqrt(T);
54
55
0
        b = riskFreeRate(T + Tc) - dividendYield(T + Tc);
56
0
        v = volatility(Tc);
57
0
        Real y1 = (log(S / Xc) + (b + pow(v, 2) / 2)*Tc) / (v*sqrt(Tc));
58
59
0
        b = riskFreeRate(T + Tp) - dividendYield(T + Tp);
60
0
        v = volatility(Tp);
61
0
        Real y2 = (log(S / Xp) + (b + pow(v, 2) / 2)*Tp) / (v*sqrt(Tp));
62
63
0
        Real rho1 = sqrt(T / Tc);
64
0
        Real rho2 = sqrt(T / Tp);
65
0
        b = riskFreeRate(T + Tc) - dividendYield(T + Tc);
66
0
        Real r = riskFreeRate(T + Tc);
67
0
        Real ComplexChooser = S * exp((b - r)*Tc) *  BivariateCumulativeNormalDistributionDr78(rho1)(d1, y1)
68
0
            - Xc * exp(-r*Tc)*BivariateCumulativeNormalDistributionDr78(rho1)(d2, y1 - v * sqrt(Tc)) ;
69
0
        b = riskFreeRate(T + Tp) - dividendYield(T + Tp);
70
0
        r = riskFreeRate(T + Tp);
71
0
        ComplexChooser -= S * exp((b - r)*Tp) * BivariateCumulativeNormalDistributionDr78(rho2)(-d1, -y2);
72
0
        ComplexChooser += Xp * exp(-r*Tp) * BivariateCumulativeNormalDistributionDr78(rho2)(-d2, -y2 + v * sqrt(Tp));
73
74
0
        results_.value = ComplexChooser;
75
0
    }
76
77
    BlackScholesCalculator AnalyticComplexChooserEngine::bsCalculator(
78
0
                                   Real spot, Option::Type optionType) const {
79
0
        Real vol;
80
0
        DiscountFactor growth;
81
0
        DiscountFactor discount;
82
0
        Time T = choosingTime();
83
84
        // payoff
85
0
        ext::shared_ptr<PlainVanillaPayoff > vanillaPayoff;
86
0
        if (optionType == Option::Call){
87
            //TC-T
88
0
            Time t=callMaturity()-2*T;
89
0
            vanillaPayoff = ext::make_shared<PlainVanillaPayoff>(
90
0
                                          Option::Call, strike(Option::Call));
91
            //QuantLib requires sigma * sqrt(t) rather than just sigma/volatility
92
0
            vol = volatility(t) * std::sqrt(t);
93
0
            growth = dividendDiscount(t);
94
0
            discount = riskFreeDiscount(t);
95
0
        } else{
96
0
            Time t=putMaturity()-2*T;
97
0
            vanillaPayoff = ext::make_shared<PlainVanillaPayoff>(
98
0
                                            Option::Put, strike(Option::Put));
99
0
            vol = volatility(t) * std::sqrt(t);
100
0
            growth = dividendDiscount(t);
101
0
            discount = riskFreeDiscount(t);
102
0
        }
103
104
0
        BlackScholesCalculator bs(vanillaPayoff, spot, growth, vol, discount);
105
0
        return bs;
106
0
    }
107
108
0
    Real AnalyticComplexChooserEngine::criticalValue() const{
109
0
        Real Sv = process_->x0();
110
111
0
        BlackScholesCalculator bs=bsCalculator(Sv,Option::Call);
112
0
        Real ci = bs.value();
113
0
        Real dc = bs.delta();
114
115
0
        bs=bsCalculator(Sv,Option::Put);
116
0
        Real Pi = bs.value();
117
0
        Real dp = bs.delta();
118
119
0
        Real yi = ci - Pi;
120
0
        Real di = dc - dp;
121
0
        Real epsilon = 0.001;
122
123
        //Newton-Raphson process
124
0
        while (std::fabs(yi) > epsilon){
125
0
            Sv = Sv - yi / di;
126
127
0
            bs=bsCalculator(Sv,Option::Call);
128
0
            ci = bs.value();
129
0
            dc = bs.delta();
130
131
0
            bs=bsCalculator(Sv,Option::Put);
132
0
            Pi = bs.value();
133
0
            dp = bs.delta();
134
135
0
            yi = ci - Pi;
136
0
            di = dc - dp;
137
0
        }
138
0
        return Sv;
139
0
    }
140
141
142
0
    Real AnalyticComplexChooserEngine::strike(Option::Type optionType) const {
143
0
        if (optionType == Option::Call)
144
0
            return arguments_.strikeCall;
145
0
        else
146
0
            return arguments_.strikePut;
147
0
    }
148
149
0
    Time AnalyticComplexChooserEngine::choosingTime() const {
150
0
        return process_->time(arguments_.choosingDate);
151
0
    }
152
153
0
    Time AnalyticComplexChooserEngine::putMaturity() const {
154
0
        return process_->time(arguments_.exercisePut->lastDate());
155
0
    }
156
157
0
    Time AnalyticComplexChooserEngine::callMaturity() const {
158
0
        return process_->time(arguments_.exerciseCall->lastDate());
159
0
    }
160
161
0
    Volatility AnalyticComplexChooserEngine::volatility(Time t) const {
162
0
        return process_->blackVolatility()->blackVol(t, arguments_.strikeCall);
163
0
    }
164
165
0
    Rate AnalyticComplexChooserEngine::dividendYield(Time t) const {
166
0
        return process_->dividendYield()->zeroRate(t, Continuous, NoFrequency);
167
0
    }
168
169
0
    DiscountFactor AnalyticComplexChooserEngine::dividendDiscount(Time t) const {
170
0
        return process_->dividendYield()->discount(t);
171
0
    }
172
173
0
    Rate AnalyticComplexChooserEngine::riskFreeRate(Time t) const {
174
0
        return process_->riskFreeRate()->zeroRate(t, Continuous, NoFrequency);
175
0
    }
176
177
0
    DiscountFactor AnalyticComplexChooserEngine::riskFreeDiscount(Time t) const {
178
0
        return process_->riskFreeRate()->discount(t);
179
0
    }
180
181
}