Coverage Report

Created: 2026-01-25 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/experimental/basismodels/tenoroptionletvts.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
Copyright (C) 2018 Sebastian Schlenkrich
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
/*! \file tenoroptionletvts.cpp
21
    \brief caplet volatility term structure based on volatility transformation
22
*/
23
24
#include <ql/exercise.hpp>
25
#include <ql/experimental/basismodels/tenoroptionletvts.hpp>
26
#include <ql/indexes/iborindex.hpp>
27
#include <ql/math/rounding.hpp>
28
#include <ql/pricingengines/swap/discountingswapengine.hpp>
29
#include <ql/time/dategenerationrule.hpp>
30
#include <ql/time/schedule.hpp>
31
#include <utility>
32
33
34
namespace QuantLib {
35
36
    TenorOptionletVTS::TenorOptionletVTS(const Handle<OptionletVolatilityStructure>& baseVTS,
37
                                         ext::shared_ptr<IborIndex> baseIndex,
38
                                         ext::shared_ptr<IborIndex> targIndex,
39
                                         ext::shared_ptr<CorrelationStructure> correlation)
40
0
    : OptionletVolatilityStructure(baseVTS->referenceDate(),
41
0
                                   baseVTS->calendar(),
42
0
                                   baseVTS->businessDayConvention(),
43
0
                                   baseVTS->dayCounter()),
44
0
      baseVTS_(baseVTS), baseIndex_(std::move(baseIndex)), targIndex_(std::move(targIndex)),
45
0
      correlation_(std::move(correlation)) {
46
0
        QL_REQUIRE(baseIndex_->tenor().frequency() % targIndex_->tenor().frequency() == 0,
47
0
                   "Base index frequency must be a multiple of target tenor frequency");
48
0
    }
Unexecuted instantiation: QuantLib::TenorOptionletVTS::TenorOptionletVTS(QuantLib::Handle<QuantLib::OptionletVolatilityStructure> const&, boost::shared_ptr<QuantLib::IborIndex>, boost::shared_ptr<QuantLib::IborIndex>, boost::shared_ptr<QuantLib::TenorOptionletVTS::CorrelationStructure>)
Unexecuted instantiation: QuantLib::TenorOptionletVTS::TenorOptionletVTS(QuantLib::Handle<QuantLib::OptionletVolatilityStructure> const&, boost::shared_ptr<QuantLib::IborIndex>, boost::shared_ptr<QuantLib::IborIndex>, boost::shared_ptr<QuantLib::TenorOptionletVTS::CorrelationStructure>)
49
50
51
    TenorOptionletVTS::TenorOptionletSmileSection::TenorOptionletSmileSection(
52
        const TenorOptionletVTS& volTS, const Time optionTime)
53
0
    : SmileSection(optionTime, volTS.baseVTS_->dayCounter(), Normal, 0.0),
54
0
      correlation_(volTS.correlation_) {
55
        // we assume that long (target) tenor is a multiple of short (base) tenor
56
        // first we need the long tenor start and end date
57
0
        Real oneDayAsYear =
58
0
            volTS.dayCounter().yearFraction(volTS.referenceDate(), volTS.referenceDate() + 1);
59
0
        Date exerciseDate =
60
0
            volTS.referenceDate() + ((BigInteger)ClosestRounding(0)(optionTime / oneDayAsYear));
61
0
        Date effectiveDate = volTS.baseIndex_->fixingCalendar().advance(
62
0
            exerciseDate, volTS.baseIndex_->fixingDays() * Days);
63
0
        Date maturityDate = volTS.baseIndex_->fixingCalendar().advance(
64
0
            effectiveDate, volTS.targIndex_->tenor(), Unadjusted, false);
65
        // now we can set up the short tenor schedule
66
0
        Schedule baseFloatSchedule(effectiveDate, maturityDate, volTS.baseIndex_->tenor(),
67
0
                                   volTS.baseIndex_->fixingCalendar(), ModifiedFollowing,
68
0
                                   Unadjusted, DateGeneration::Backward, false);
69
        // set up scalar attributes
70
0
        fraRateTarg_ = volTS.targIndex_->fixing(exerciseDate);
71
0
        Time yfTarg = volTS.targIndex_->dayCounter().yearFraction(effectiveDate, maturityDate);
72
0
        for (Size k = 0; k < baseFloatSchedule.dates().size() - 1; ++k) {
73
0
            Date startDate = baseFloatSchedule.dates()[k];
74
0
            Date fixingDate = volTS.baseIndex_->fixingCalendar().advance(
75
0
                startDate, (-1 * volTS.baseIndex_->fixingDays()) * Days);
76
0
            Time yearFrac = volTS.baseIndex_->dayCounter().yearFraction(
77
0
                baseFloatSchedule.dates()[k], baseFloatSchedule.dates()[k + 1]);
78
            // set up vector attributes
79
0
            baseSmileSection_.push_back(volTS.baseVTS_->smileSection(fixingDate, true));
80
0
            startTimeBase_.push_back(
81
0
                volTS.dayCounter().yearFraction(volTS.referenceDate(), startDate));
82
0
            fraRateBase_.push_back(volTS.baseIndex_->fixing(fixingDate));
83
0
            v_.push_back(yearFrac / yfTarg * (1.0 + yfTarg * fraRateTarg_) /
84
0
                         (1.0 + yearFrac * fraRateBase_[k]));
85
0
        }
86
0
    }
Unexecuted instantiation: QuantLib::TenorOptionletVTS::TenorOptionletSmileSection::TenorOptionletSmileSection(QuantLib::TenorOptionletVTS const&, double)
Unexecuted instantiation: QuantLib::TenorOptionletVTS::TenorOptionletSmileSection::TenorOptionletSmileSection(QuantLib::TenorOptionletVTS const&, double)
87
88
0
    Volatility TenorOptionletVTS::TenorOptionletSmileSection::volatilityImpl(Rate strike) const {
89
0
        Real sum_v = 0.0;
90
0
        for (Real k : v_)
91
0
            sum_v += k;
92
0
        std::vector<Real> volBase(v_.size());
93
0
        for (Size k = 0; k < fraRateBase_.size(); ++k) {
94
0
            Real strike_k = (strike - (fraRateTarg_ - sum_v * fraRateBase_[k])) / sum_v;
95
0
            volBase[k] = baseSmileSection_[k]->volatility(strike_k, Normal, 0.0);
96
0
        }
97
0
        Real var = 0.0;
98
0
        for (Size i = 0; i < volBase.size(); ++i) {
99
0
            var += v_[i] * v_[i] * volBase[i] * volBase[i];
100
0
            for (Size j = i + 1; j < volBase.size(); ++j) {
101
0
                Real corr = (*correlation_)(startTimeBase_[i], startTimeBase_[j]);
102
0
                var += 2.0 * corr * v_[i] * v_[j] * volBase[i] * volBase[j];
103
0
            }
104
0
        }
105
0
        Real vol = sqrt(var);
106
0
        return vol;
107
0
    }
108
109
110
}