• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lballabio / QuantLib / 22867607730

09 Mar 2026 06:03PM UTC coverage: 74.215% (-0.04%) from 74.259%
22867607730

Pull #2368

github

web-flow
Merge a06826527 into ec72e5100
Pull Request #2368: Fx options utils - `BlackVolatilitySurfaceDelta` class

113 of 177 new or added lines in 5 files covered. (63.84%)

135 existing lines in 17 files now uncovered.

57833 of 77926 relevant lines covered (74.22%)

8716353.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

54.44
/ql/termstructures/volatility/equityfx/blackvoltermstructure.cpp
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2

3
/*
4
 Copyright (C) 2002, 2003 Ferdinando Ametrano
5
 Copyright (C) 2026 Paolo D'Elia
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/termstructures/volatility/equityfx/blackvoltermstructure.hpp>
22
#include <array>
23
#include <ql/math/comparison.hpp>
24
#include <ql/math/interpolations/linearinterpolation.hpp>
25

26
namespace QuantLib {
27

28
    namespace detail {
NEW
29
        Real linearExtrapolation(const double t, const std::array<double, 2>& times, const std::array<double, 2>& variances) {
×
NEW
30
            QL_REQUIRE(t > times[1], "t must be greater than times[1]");
×
NEW
31
            QL_REQUIRE(times[1] > times[0], "times must be sorted");
×
NEW
32
            QL_REQUIRE(variances[1] >= variances[0], "variances must be non-decreasing");
×
33
            std::array<double, 2> vols;
NEW
34
            vols[0] = close_enough(times[0], 0.0) ? 0.0 : std::sqrt(variances[0] / times[0]);
×
NEW
35
            vols[1] = close_enough(times[1], 0.0) ? 0.0 : std::sqrt(variances[1] / times[1]);
×
NEW
36
            LinearInterpolation interpolation(times.begin(), times.end(), vols.begin());
×
NEW
37
            return std::max(interpolation(t, true), 0.0);
×
38
        }
39

40
        template <typename F>
41
        Real timeExtrapolationBlackVarianceFlat(const Time t, const Real strike, const std::vector<double>& times,
42
                                                const F& varianceSurface) {
43
            return std::max(varianceSurface(times.back(), strike, true), 0.0) / times.back() * t;
44
        }
45

46
        Real timeExtrapolationBlackVarianceFlat(const Time t, const std::vector<double>& times,
1,023✔
47
                                                        const Interpolation& varianceCurve) {
48
            return std::max(varianceCurve(times.back(), true), 0.0) / times.back() * t;
1,023✔
49
        }
50

51
        template <typename F>
52
        Real timeExtrapolationBlackVarianceLinear(const Time t, const Real strike, const std::vector<double>& times,
53
                                                        const F& varianceSurface) {
54
            Size ind1 = times.size() - 2;
55
            Size ind2 = times.size() - 1;
56
            std::array<Real, 2> xs{times[ind1], times[ind2]};
57
            std::array<Real, 2> variances;
58
            variances[0] = varianceSurface(xs[0], strike, true);
59
            variances[1] = varianceSurface(xs[1], strike, true);
60
            Real v = detail::linearExtrapolation(t, xs, variances);
61
            return v * v * t;
62
        }
63

NEW
64
        Real timeExtrapolationBlackVarianceLinear(const Time t, const std::vector<double>& times,
×
65
                                                                const Interpolation& varianceCurve) {
66
            Size ind1 = times.size() - 2;
67
            Size ind2 = times.size() - 1;
NEW
68
            std::array<Real, 2> xs{times[ind1], times[ind2]};
×
69
            std::array<Real, 2> variances;
NEW
70
            variances[0] = varianceCurve(xs[0], true);
×
NEW
71
            variances[1] = varianceCurve(xs[1], true);
×
NEW
72
            Real v = detail::linearExtrapolation(t, xs, variances);
×
NEW
73
            return v * v * t;
×
74
        }
75
    }
76

77
    // BlackVolTimeExtrapolation implementation
78
    template<class F>
79
    Real BlackVolTimeExtrapolation::extrapolate(Type type, const Time t, const Real strike, const std::vector<Time>& times, const F& varianceSurface) {
80
        switch (type) {
81
            case FlatVolatility:
82
                return detail::timeExtrapolationBlackVarianceFlat(t, times, strike, varianceSurface);
83
            case UseInterpolatorVariance:
84
                return std::max(varianceSurface(t, strike, true), 0.0);
85
            case LinearVariance: 
86
                return detail::timeExtrapolationBlackVarianceLinear(t, strike, times, varianceSurface);
87
            default:
88
                QL_FAIL("unknown extrapolation type");
89
        }
90
    }
91

92
    Real BlackVolTimeExtrapolation::extrapolate(Type type, const Time t, const std::vector<Time>& times, const Interpolation& varianceCurve) {
1,023✔
93
        switch (type) {
1,023✔
94
            case FlatVolatility:
1,023✔
95
                return detail::timeExtrapolationBlackVarianceFlat(t, times, varianceCurve);
1,023✔
NEW
96
            case UseInterpolatorVariance:
×
NEW
97
                return std::max(varianceCurve(t, true), 0.0);
×
98
            case LinearVariance: {
NEW
99
                QL_REQUIRE(times.size() >= 2, "at least two times required for volatility extrapolation");
×
NEW
100
                return detail::timeExtrapolationBlackVarianceLinear(t, times, varianceCurve);
×
101
            }
NEW
102
            default:
×
NEW
103
                QL_FAIL("unknown extrapolation type");
×
104
        }
105
    }
106

107
    BlackVolTermStructure::BlackVolTermStructure(BusinessDayConvention bdc,
20✔
108
                                                 const DayCounter& dc)
×
109
    : VolatilityTermStructure(bdc, dc) {}
20✔
110

111
    BlackVolTermStructure::BlackVolTermStructure(const Date& refDate,
355,108✔
112
                                                 const Calendar& cal,
113
                                                 BusinessDayConvention bdc,
114
                                                 const DayCounter& dc)
×
115
    : VolatilityTermStructure(refDate, cal, bdc, dc) {}
355,108✔
116

117
    BlackVolTermStructure::BlackVolTermStructure(Natural settlDays,
257✔
118
                                                 const Calendar& cal,
119
                                                 BusinessDayConvention bdc,
120
                                                 const DayCounter& dc)
×
121
    : VolatilityTermStructure(settlDays, cal, bdc, dc) {}
257✔
122

123
    Volatility BlackVolTermStructure::blackForwardVol(const Date& date1,
×
124
                                                      const Date& date2,
125
                                                      Real strike,
126
                                                      bool extrapolate) const {
127
        // (redundant) date-based checks
128
        QL_REQUIRE(date1 <= date2,
×
129
                   date1 << " later than " << date2);
130
        checkRange(date2, extrapolate);
×
131

132
        // using the time implementation
133
        Time time1 = timeFromReference(date1);
×
134
        Time time2 = timeFromReference(date2);
×
135
        return blackForwardVol(time1, time2, strike, extrapolate);
×
136
    }
137

138
    Volatility BlackVolTermStructure::blackForwardVol(Time time1,
8,589,359✔
139
                                                      Time time2,
140
                                                      Real strike,
141
                                                      bool extrapolate) const {
142
        QL_REQUIRE(time1 <= time2,
8,589,359✔
143
                   time1 << " later than " << time2);
144
        checkRange(time2, extrapolate);
8,589,359✔
145
        checkStrike(strike, extrapolate);
8,589,359✔
146
        if (time2==time1) {
8,589,359✔
147
            if (time1==0.0) {
159,850✔
148
                Time epsilon = 1.0e-5;
149
                Real var = blackVarianceImpl(epsilon, strike);
×
150
                return std::sqrt(var/epsilon);
×
151
            } else {
152
                Time epsilon = std::min<Time>(1.0e-5, time1);
159,850✔
153
                Real var1 = blackVarianceImpl(time1-epsilon, strike);
159,850✔
154
                Real var2 = blackVarianceImpl(time1+epsilon, strike);
159,850✔
155
                QL_ENSURE(var2>=var1,
159,850✔
156
                          "variances must be non-decreasing");
157
                return std::sqrt((var2-var1)/(2*epsilon));
159,850✔
158
            }
159
        } else {
160
            Real var1 = blackVarianceImpl(time1, strike);
8,429,509✔
161
            Real var2 = blackVarianceImpl(time2, strike);
8,429,509✔
162
            QL_ENSURE(var2 >= var1,
8,429,509✔
163
                      "variances must be non-decreasing");
164
            return std::sqrt((var2-var1)/(time2-time1));
8,429,509✔
165
        }
166
    }
167

168
    Real BlackVolTermStructure::blackForwardVariance(const Date& date1,
49,893✔
169
                                                     const Date& date2,
170
                                                     Real strike,
171
                                                     bool extrapolate)
172
                                                                      const {
173
        // (redundant) date-based checks
174
        QL_REQUIRE(date1 <= date2,
49,893✔
175
                   date1 << " later than " << date2);
176
        checkRange(date2, extrapolate);
49,893✔
177

178
        // using the time implementation
179
        Time time1 = timeFromReference(date1);
49,893✔
180
        Time time2 = timeFromReference(date2);
49,893✔
181
        return blackForwardVariance(time1, time2, strike, extrapolate);
49,893✔
182
    }
183

184
    Real BlackVolTermStructure::blackForwardVariance(Time time1,
962,886✔
185
                                                     Time time2,
186
                                                     Real strike,
187
                                                     bool extrapolate) const {
188
        QL_REQUIRE(time1 <= time2,
962,886✔
189
                   time1 << " later than " << time2);
190
        checkRange(time2, extrapolate);
962,886✔
191
        checkStrike(strike, extrapolate);
962,886✔
192
        Real v1 = blackVarianceImpl(time1, strike);
962,886✔
193
        Real v2 = blackVarianceImpl(time2, strike);
962,886✔
194
        QL_ENSURE(v2 >= v1,
962,886✔
195
                  "variances must be non-decreasing");
196
        return v2-v1;
962,886✔
197
    }
198

199
    BlackVolatilityTermStructure::BlackVolatilityTermStructure(
×
200
                                                    BusinessDayConvention bdc,
201
                                                    const DayCounter& dc)
×
202
    : BlackVolTermStructure(bdc, dc) {}
×
203

204
    BlackVolatilityTermStructure::BlackVolatilityTermStructure(
310,218✔
205
                                                    const Date& refDate,
206
                                                    const Calendar& cal,
207
                                                    BusinessDayConvention bdc,
208
                                                    const DayCounter& dc)
×
209
    : BlackVolTermStructure(refDate, cal, bdc, dc) {}
310,218✔
210

211
    BlackVolatilityTermStructure::BlackVolatilityTermStructure(
257✔
212
                                                    Natural settlementDays,
213
                                                    const Calendar& cal,
214
                                                    BusinessDayConvention bdc,
215
                                                    const DayCounter& dc)
×
216
    : BlackVolTermStructure(settlementDays, cal, bdc, dc) {}
257✔
217

218
    BlackVarianceTermStructure::BlackVarianceTermStructure(
20✔
219
                                                    BusinessDayConvention bdc,
220
                                                    const DayCounter& dc)
×
221
    : BlackVolTermStructure(bdc, dc) {}
20✔
222

223
    BlackVarianceTermStructure::BlackVarianceTermStructure(
43,538✔
224
                                                    const Date& refDate,
225
                                                    const Calendar& cal,
226
                                                    BusinessDayConvention bdc,
227
                                                    const DayCounter& dc)
×
228
    : BlackVolTermStructure(refDate, cal, bdc, dc) {}
43,538✔
229

230
    BlackVarianceTermStructure::BlackVarianceTermStructure(
×
231
                                                    Natural settlementDays,
232
                                                    const Calendar& cal,
233
                                                    BusinessDayConvention bdc,
234
                                                    const DayCounter& dc)
×
235
    : BlackVolTermStructure(settlementDays, cal, bdc, dc) {}
×
236

237
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc