Coverage Report

Created: 2025-10-14 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/pricingengines/lookback/analyticcontinuouspartialfloatinglookback.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2006 Warren Chou
5
 Copyright (C) 2007 StatPro Italia srl
6
7
 This file is part of QuantLib, a free-software/open-source library
8
 for financial quantitative analysts and developers - http://quantlib.org/
9
10
 QuantLib is free software: you can redistribute it and/or modify it
11
 under the terms of the QuantLib license.  You should have received a
12
 copy of the license along with this program; if not, please email
13
 <quantlib-dev@lists.sf.net>. The license is also available online at
14
 <https://www.quantlib.org/license.shtml>.
15
16
 This program is distributed in the hope that it will be useful, but WITHOUT
17
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
 FOR A PARTICULAR PURPOSE.  See the license for more details.
19
*/
20
21
#include <ql/exercise.hpp>
22
#include <ql/pricingengines/lookback/analyticcontinuouspartialfloatinglookback.hpp>
23
#include <utility>
24
25
namespace QuantLib {
26
27
    AnalyticContinuousPartialFloatingLookbackEngine::
28
        AnalyticContinuousPartialFloatingLookbackEngine(
29
            ext::shared_ptr<GeneralizedBlackScholesProcess> process)
30
0
    : process_(std::move(process)) {
31
0
        registerWith(process_);
32
0
    }
33
34
0
    void AnalyticContinuousPartialFloatingLookbackEngine::calculate() const {
35
36
0
        ext::shared_ptr<FloatingTypePayoff> payoff =
37
0
            ext::dynamic_pointer_cast<FloatingTypePayoff>(arguments_.payoff);
38
0
        QL_REQUIRE(payoff, "Non-floating payoff given");
39
40
0
        QL_REQUIRE(process_->x0() > 0.0, "negative or null underlying");
41
42
0
        switch (payoff->optionType()) {
43
0
          case Option::Call:
44
0
            results_.value = A(1);
45
0
            break;
46
0
          case Option::Put:
47
0
            results_.value = A(-1);
48
0
            break;
49
0
          default:
50
0
            QL_FAIL("Unknown type");
51
0
        }
52
0
    }
53
54
0
    Real AnalyticContinuousPartialFloatingLookbackEngine::underlying() const {
55
0
        return process_->x0();
56
0
    }
57
58
0
    Time AnalyticContinuousPartialFloatingLookbackEngine::residualTime() const {
59
0
        return process_->time(arguments_.exercise->lastDate());
60
0
    }
61
62
0
    Volatility AnalyticContinuousPartialFloatingLookbackEngine::volatility() const {
63
0
        return process_->blackVolatility()->blackVol(residualTime(), minmax());
64
0
    }
65
66
0
    Real AnalyticContinuousPartialFloatingLookbackEngine::stdDeviation() const {
67
0
        return volatility() * std::sqrt(residualTime());
68
0
    }
69
70
0
    Rate AnalyticContinuousPartialFloatingLookbackEngine::riskFreeRate() const {
71
0
        return process_->riskFreeRate()->zeroRate(residualTime(), Continuous,
72
0
                                                  NoFrequency);
73
0
    }
74
75
    DiscountFactor AnalyticContinuousPartialFloatingLookbackEngine::riskFreeDiscount()
76
0
                                 const {
77
0
        return process_->riskFreeRate()->discount(residualTime());
78
0
    }
79
80
0
    Rate AnalyticContinuousPartialFloatingLookbackEngine::dividendYield() const {
81
0
        return process_->dividendYield()->zeroRate(residualTime(),
82
0
                                                   Continuous, NoFrequency);
83
0
    }
84
85
    DiscountFactor AnalyticContinuousPartialFloatingLookbackEngine::dividendDiscount()
86
0
                                 const {
87
0
        return process_->dividendYield()->discount(residualTime());
88
0
    }
89
90
0
    Real AnalyticContinuousPartialFloatingLookbackEngine::minmax() const {
91
0
        return arguments_.minmax;
92
0
    }
93
94
0
    Real AnalyticContinuousPartialFloatingLookbackEngine::lambda() const {
95
0
        return arguments_.lambda;
96
0
    }
97
98
0
    Time AnalyticContinuousPartialFloatingLookbackEngine::lookbackPeriodEndTime() const {
99
0
        return process_->time(arguments_.lookbackPeriodEnd);
100
0
    }
101
102
103
0
    Real AnalyticContinuousPartialFloatingLookbackEngine::A(Real eta) const {
104
0
        bool fullLookbackPeriod = lookbackPeriodEndTime() == residualTime();
105
0
        Real carry = riskFreeRate() - dividendYield();
106
0
        Volatility vol = volatility();
107
0
        Real x = 2.0*carry/(vol*vol);
108
0
        Real s = underlying()/minmax();
109
110
0
        Real ls = std::log(s);
111
0
        Real d1 = ls/stdDeviation() + 0.5*(x+1.0)*stdDeviation();
112
0
        Real d2 = d1 - stdDeviation();
113
114
0
        Real e1 = 0, e2 = 0;
115
0
        if (!fullLookbackPeriod)
116
0
        {
117
0
            e1 = (carry + vol * vol / 2) * (residualTime() - lookbackPeriodEndTime()) / (vol * std::sqrt(residualTime() - lookbackPeriodEndTime()));
118
0
            e2 = e1 - vol * std::sqrt(residualTime() - lookbackPeriodEndTime());
119
0
        }
120
121
0
        Real f1 = (ls + (carry + vol * vol / 2) * lookbackPeriodEndTime()) / (vol * std::sqrt(lookbackPeriodEndTime()));
122
0
        Real f2 = f1 - vol * std::sqrt(lookbackPeriodEndTime());
123
124
0
        Real l1 = std::log(lambda()) / vol;
125
0
        Real g1 = l1 / std::sqrt(residualTime());
126
127
0
        Real n1 = f_(eta*(d1 - g1));
128
0
        Real n2 = f_(eta*(d2 - g1));
129
130
0
        BivariateCumulativeNormalDistributionWe04DP cnbn1(1), cnbn2(0), cnbn3(-1);
131
0
        if (!fullLookbackPeriod) {
132
0
            cnbn1 = BivariateCumulativeNormalDistributionWe04DP (std::sqrt(lookbackPeriodEndTime() / residualTime()));
133
0
            cnbn2 = BivariateCumulativeNormalDistributionWe04DP (-std::sqrt(1 - lookbackPeriodEndTime() / residualTime()));
134
0
            cnbn3 = BivariateCumulativeNormalDistributionWe04DP (-std::sqrt(lookbackPeriodEndTime() / residualTime()));
135
0
        }
136
137
0
        Real n3 = cnbn1(eta*(-f1+2.0* carry * std::sqrt(lookbackPeriodEndTime()) / vol), eta*(-d1+x*stdDeviation()-g1));
138
0
        Real n4 = 0, n5 = 0, n6 = 0, n7 = 0;
139
0
        if (!fullLookbackPeriod)
140
0
        {
141
0
            Real g2 = l1 / std::sqrt(residualTime() - lookbackPeriodEndTime());
142
0
            n4 = cnbn2(-eta*(d1+g1), eta*(e1 + g2));
143
0
            n5 = cnbn2(-eta*(d1-g1), eta*(e1 - g2));
144
0
            n6 = cnbn3(eta*-f2, eta*(d2 - g1));
145
0
            n7 = f_(eta*(e2 - g2));
146
0
        }
147
0
        else
148
0
        {
149
0
            n4 = f_(-eta*(d1+g1));
150
0
        }
151
152
0
        Real n8 = f_(-eta*f1);
153
0
        Real pow_s = std::pow(s, -x);
154
0
        Real pow_l = std::pow(lambda(), x);
155
156
0
        if (!fullLookbackPeriod)
157
0
        {
158
0
            return eta*(underlying() * dividendDiscount() * n1 -
159
0
                        lambda() * minmax() * riskFreeDiscount() * n2 + 
160
0
                        underlying() * riskFreeDiscount() * lambda() / x *
161
0
                        (pow_s * n3 - dividendDiscount() / riskFreeDiscount() * pow_l * n4)
162
0
                        + underlying() * dividendDiscount() * n5 + 
163
0
                        riskFreeDiscount() * lambda() * minmax() * n6 -
164
0
                        std::exp(-carry * (residualTime() - lookbackPeriodEndTime())) * 
165
0
                        dividendDiscount() * (1 + 0.5 * vol * vol / carry) * lambda() * 
166
0
                        underlying() * n7 * n8);
167
0
        }
168
0
        else
169
0
        {
170
            //Simpler calculation
171
0
            return eta*(underlying() * dividendDiscount() * n1 -
172
0
                        lambda() * minmax() * riskFreeDiscount() * n2 + 
173
0
                        underlying() * riskFreeDiscount() * lambda() / x *
174
0
                        (pow_s * n3 - dividendDiscount() / riskFreeDiscount() * pow_l * n4));
175
0
        }
176
0
    }
177
}
178