Coverage Report

Created: 2025-10-14 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/experimental/inflation/cpicapfloortermpricesurface.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2010 Chris Kenyon
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
21
#include <ql/experimental/inflation/cpicapfloortermpricesurface.hpp>
22
#include <utility>
23
24
25
namespace QuantLib {
26
27
    CPICapFloorTermPriceSurface::CPICapFloorTermPriceSurface(
28
        Real nominal,
29
        Real baseRate, // avoids an uncontrolled crash if index has no TS
30
        const Period& observationLag,
31
        const Calendar& cal, // calendar in index may not be useful
32
        const BusinessDayConvention& bdc,
33
        const DayCounter& dc,
34
        ext::shared_ptr<ZeroInflationIndex>  zii,
35
        CPI::InterpolationType interpolationType,
36
        Handle<YieldTermStructure> yts,
37
        const std::vector<Rate>& cStrikes,
38
        const std::vector<Rate>& fStrikes,
39
        const std::vector<Period>& cfMaturities,
40
        const Matrix& cPrice,
41
        const Matrix& fPrice)
42
0
    : TermStructure(0, cal, dc),
43
0
      zii_(std::move(zii)), interpolationType_(interpolationType), nominalTS_(std::move(yts)),
44
0
      cStrikes_(cStrikes), fStrikes_(fStrikes), cfMaturities_(cfMaturities),
45
0
      cPrice_(cPrice), fPrice_(fPrice), nominal_(nominal), bdc_(bdc),
46
0
      observationLag_(observationLag), baseRate_(baseRate) {
47
48
        // does the index have a TS?
49
0
        QL_REQUIRE(!zii_->zeroInflationTermStructure().empty(), "ZITS missing from index");
50
0
        QL_REQUIRE(!nominalTS_.empty(), "nominal TS missing");
51
              
52
        // data consistency checking, enough data?
53
0
        QL_REQUIRE(fStrikes_.size() > 1, "not enough floor strikes");
54
0
        QL_REQUIRE(cStrikes_.size() > 1, "not enough cap strikes");
55
0
        QL_REQUIRE(cfMaturities_.size() > 1, "not enough maturities");
56
0
        QL_REQUIRE(fStrikes_.size() == fPrice.rows(),
57
0
                   "floor strikes vs floor price rows not equal");
58
0
        QL_REQUIRE(cStrikes_.size() == cPrice.rows(),
59
0
                   "cap strikes vs cap price rows not equal");
60
0
        QL_REQUIRE(cfMaturities_.size() == fPrice.columns(),
61
0
                   "maturities vs floor price columns not equal");
62
0
        QL_REQUIRE(cfMaturities_.size() == cPrice.columns(),
63
0
                   "maturities vs cap price columns not equal");
64
65
        // data has correct properties (positive, monotonic)?
66
0
        for(Size j = 0; j <cfMaturities_.size(); j++) {
67
0
            QL_REQUIRE( cfMaturities[j] > Period(0,Days), "non-positive maturities");
68
0
            if(j>0) {
69
0
                QL_REQUIRE( cfMaturities[j] > cfMaturities[j-1],
70
0
                            "non-increasing maturities");
71
0
            }
72
0
            for(Size i = 0; i <fPrice_.rows(); i++) {
73
0
                QL_REQUIRE( fPrice_[i][j] > 0.0,
74
0
                            "non-positive floor price: " << fPrice_[i][j] );
75
0
                if(i>0) {
76
0
                    QL_REQUIRE( fPrice_[i][j] >= fPrice_[i-1][j],
77
0
                                "non-increasing floor prices");
78
0
                }
79
0
            }
80
0
            for(Size i = 0; i <cPrice_.rows(); i++) {
81
0
                QL_REQUIRE( cPrice_[i][j] > 0.0,
82
0
                            "non-positive cap price: " << cPrice_[i][j] );
83
0
                if(i>0) {
84
0
                    QL_REQUIRE( cPrice_[i][j] <= cPrice_[i-1][j],
85
0
                                "non-decreasing cap prices: " 
86
0
                               << cPrice_[i][j] << " then " << cPrice_[i-1][j]);
87
0
                }
88
0
            }
89
0
        }
90
91
92
        // Get the set of strikes, noting that repeats, overlaps are
93
        // expected between caps and floors but that no overlap in the
94
        // output is allowed so no repeats or overlaps are used
95
0
        cfStrikes_ = std::vector<Rate>();
96
0
        for(Size i = 0; i <fStrikes_.size(); i++)
97
0
            cfStrikes_.push_back( fStrikes[i] );
98
0
        Real eps = 0.0000001;
99
0
        Rate maxFstrike = fStrikes_.back();
100
0
        for(Size i = 0; i < cStrikes_.size(); i++) {
101
0
            Rate k = cStrikes[i];
102
0
            if (k > maxFstrike + eps) cfStrikes_.push_back(k);
103
0
        }
104
105
        // final consistency checking
106
0
        QL_REQUIRE(cfStrikes_.size() > 2, "overall not enough strikes");
107
0
        for (Size i = 1; i < cfStrikes_.size(); i++)
108
0
            QL_REQUIRE( cfStrikes_[i] > cfStrikes_[i-1],
109
0
                        "cfStrikes not increasing");
110
0
    }
111
112
0
    Rate CPICapFloorTermPriceSurface::atmRate(Date maturity) const {
113
0
        Real F0 = CPI::laggedFixing(zii_, referenceDate(), observationLag_, interpolationType_);
114
0
        Real F1 = CPI::laggedFixing(zii_, maturity, observationLag_, interpolationType_);
115
116
0
        Time T = inflationYearFraction(
117
0
            zii_->frequency(),
118
0
            detail::CPI::isInterpolated(interpolationType_), dayCounter(),
119
0
            referenceDate() - observationLag_, maturity - observationLag_);
120
121
0
        return T > 0.0 ? std::pow(F1 / F0, 1 / T) - 1.0 : baseRate();
122
0
    }
123
124
    Date CPICapFloorTermPriceSurface::cpiOptionDateFromTenor(const Period& p) const
125
0
    {
126
0
        return calendar().adjust(referenceDate() + p, businessDayConvention());
127
0
    }
128
129
    
130
0
    Real CPICapFloorTermPriceSurface::price(const Period &d, Rate k) const {
131
0
        return this->price(cpiOptionDateFromTenor(d), k);
132
0
    }
133
    
134
135
0
    Real CPICapFloorTermPriceSurface::capPrice(const Period &d, Rate k) const {
136
0
        return this->capPrice(cpiOptionDateFromTenor(d), k);
137
0
    }
138
    
139
140
0
    Real CPICapFloorTermPriceSurface::floorPrice(const Period &d, Rate k) const {
141
0
        return this->floorPrice(cpiOptionDateFromTenor(d), k);
142
0
    }
143
144
}
145