1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4Copyright (C) 2018 Sebastian Schlenkrich
5
6This file is part of QuantLib, a free-software/open-source library
7for financial quantitative analysts and developers - http://quantlib.org/
8
9QuantLib is free software: you can redistribute it and/or modify it
10under the terms of the QuantLib license. You should have received a
11copy 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<http://quantlib.org/license.shtml>.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR 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
34namespace 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 : OptionletVolatilityStructure(baseVTS->referenceDate(),
41 baseVTS->calendar(),
42 baseVTS->businessDayConvention(),
43 baseVTS->dayCounter()),
44 baseVTS_(baseVTS), baseIndex_(std::move(baseIndex)), targIndex_(std::move(targIndex)),
45 correlation_(std::move(correlation)) {
46 QL_REQUIRE(baseIndex_->tenor().frequency() % targIndex_->tenor().frequency() == 0,
47 "Base index frequency must be a multiple of target tenor frequency");
48 }
49
50
51 TenorOptionletVTS::TenorOptionletSmileSection::TenorOptionletSmileSection(
52 const TenorOptionletVTS& volTS, const Time optionTime)
53 : SmileSection(optionTime, volTS.baseVTS_->dayCounter(), Normal, 0.0),
54 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 Real oneDayAsYear =
58 volTS.dayCounter().yearFraction(d1: volTS.referenceDate(), d2: volTS.referenceDate() + 1);
59 Date exerciseDate =
60 volTS.referenceDate() + ((BigInteger)ClosestRounding(0)(optionTime / oneDayAsYear));
61 Date effectiveDate = volTS.baseIndex_->fixingCalendar().advance(
62 date: exerciseDate, period: volTS.baseIndex_->fixingDays() * Days);
63 Date maturityDate = volTS.baseIndex_->fixingCalendar().advance(
64 date: effectiveDate, period: volTS.targIndex_->tenor(), convention: Unadjusted, endOfMonth: false);
65 // now we can set up the short tenor schedule
66 Schedule baseFloatSchedule(effectiveDate, maturityDate, volTS.baseIndex_->tenor(),
67 volTS.baseIndex_->fixingCalendar(), ModifiedFollowing,
68 Unadjusted, DateGeneration::Backward, false);
69 // set up scalar attributes
70 fraRateTarg_ = volTS.targIndex_->fixing(fixingDate: exerciseDate);
71 Time yfTarg = volTS.targIndex_->dayCounter().yearFraction(d1: effectiveDate, d2: maturityDate);
72 for (Size k = 0; k < baseFloatSchedule.dates().size() - 1; ++k) {
73 Date startDate = baseFloatSchedule.dates()[k];
74 Date fixingDate = volTS.baseIndex_->fixingCalendar().advance(
75 date: startDate, period: (-1 * volTS.baseIndex_->fixingDays()) * Days);
76 Time yearFrac = volTS.baseIndex_->dayCounter().yearFraction(
77 d1: baseFloatSchedule.dates()[k], d2: baseFloatSchedule.dates()[k + 1]);
78 // set up vector attributes
79 baseSmileSection_.push_back(x: volTS.baseVTS_->smileSection(optionDate: fixingDate, extrapolate: true));
80 startTimeBase_.push_back(
81 x: volTS.dayCounter().yearFraction(d1: volTS.referenceDate(), d2: startDate));
82 fraRateBase_.push_back(x: volTS.baseIndex_->fixing(fixingDate));
83 v_.push_back(x: yearFrac / yfTarg * (1.0 + yfTarg * fraRateTarg_) /
84 (1.0 + yearFrac * fraRateBase_[k]));
85 }
86 }
87
88 Volatility TenorOptionletVTS::TenorOptionletSmileSection::volatilityImpl(Rate strike) const {
89 Real sum_v = 0.0;
90 for (Real k : v_)
91 sum_v += k;
92 std::vector<Real> volBase(v_.size());
93 for (Size k = 0; k < fraRateBase_.size(); ++k) {
94 Real strike_k = (strike - (fraRateTarg_ - sum_v * fraRateBase_[k])) / sum_v;
95 volBase[k] = baseSmileSection_[k]->volatility(strike: strike_k, type: Normal, shift: 0.0);
96 }
97 Real var = 0.0;
98 for (Size i = 0; i < volBase.size(); ++i) {
99 var += v_[i] * v_[i] * volBase[i] * volBase[i];
100 for (Size j = i + 1; j < volBase.size(); ++j) {
101 Real corr = (*correlation_)(startTimeBase_[i], startTimeBase_[j]);
102 var += 2.0 * corr * v_[i] * v_[j] * volBase[i] * volBase[j];
103 }
104 }
105 Real vol = sqrt(x: var);
106 return vol;
107 }
108
109
110}
111

source code of quantlib/ql/experimental/basismodels/tenoroptionletvts.cpp