Coverage Report

Created: 2025-09-04 07:11

/src/quantlib/ql/pricingengines/capfloor/blackcapfloorengine.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2007 Ferdinando Ametrano
5
 Copyright (C) 2001, 2002, 2003 Sadruddin Rejeb
6
 Copyright (C) 2006, 2007 StatPro Italia srl
7
 Copyright (C) 2015 Michael von den Driesch
8
 Copyright (C) 2019 Wojciech Ĺšlusarski
9
10
 This file is part of QuantLib, a free-software/open-source library
11
 for financial quantitative analysts and developers - http://quantlib.org/
12
13
 QuantLib is free software: you can redistribute it and/or modify it
14
 under the terms of the QuantLib license.  You should have received a
15
 copy of the license along with this program; if not, please email
16
 <quantlib-dev@lists.sf.net>. The license is also available online at
17
 <https://www.quantlib.org/license.shtml>.
18
19
 This program is distributed in the hope that it will be useful, but WITHOUT
20
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21
 FOR A PARTICULAR PURPOSE.  See the license for more details.
22
*/
23
24
#include <ql/pricingengines/blackformula.hpp>
25
#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
26
#include <ql/termstructures/volatility/optionlet/constantoptionletvol.hpp>
27
#include <ql/termstructures/yieldtermstructure.hpp>
28
#include <ql/time/calendars/nullcalendar.hpp>
29
#include <utility>
30
31
namespace QuantLib {
32
33
    BlackCapFloorEngine::BlackCapFloorEngine(Handle<YieldTermStructure> discountCurve,
34
                                             Volatility v,
35
                                             const DayCounter& dc,
36
                                             Real displacement)
37
0
    : discountCurve_(std::move(discountCurve)),
38
0
      vol_(ext::shared_ptr<OptionletVolatilityStructure>(
39
0
          new ConstantOptionletVolatility(0, NullCalendar(), Following, v, dc))),
40
0
      displacement_(displacement) {
41
0
        registerWith(discountCurve_);
42
0
    }
43
44
    BlackCapFloorEngine::BlackCapFloorEngine(Handle<YieldTermStructure> discountCurve,
45
                                             const Handle<Quote>& v,
46
                                             const DayCounter& dc,
47
                                             Real displacement)
48
0
    : discountCurve_(std::move(discountCurve)),
49
0
      vol_(ext::shared_ptr<OptionletVolatilityStructure>(
50
0
          new ConstantOptionletVolatility(0, NullCalendar(), Following, v, dc))),
51
0
      displacement_(displacement) {
52
0
        registerWith(discountCurve_);
53
0
        registerWith(vol_);
54
0
    }
55
56
    BlackCapFloorEngine::BlackCapFloorEngine(Handle<YieldTermStructure> discountCurve,
57
                                             Handle<OptionletVolatilityStructure> volatility,
58
                                             Real displacement)
59
0
    : discountCurve_(std::move(discountCurve)), vol_(std::move(volatility)) {
60
0
        QL_REQUIRE(
61
0
            vol_->volatilityType() == ShiftedLognormal,
62
0
            "BlackCapFloorEngine should only be used for vol surfaces stripped "
63
0
            "with shifted log normal model. Options were stripped with model "
64
0
                << vol_->volatilityType());
65
0
        if (displacement != Null< Real >()) {
66
0
            displacement_ = displacement;
67
0
            QL_REQUIRE(vol_->displacement() == displacement_,
68
0
                       "Displacement used for stripping and provided for "
69
0
                       "pricing differ. Model displacement was : "
70
0
                           << vol_->displacement());
71
0
        } else
72
0
            displacement_ = vol_->displacement();
73
0
        registerWith(discountCurve_);
74
0
        registerWith(vol_);
75
0
    }
76
77
0
    void BlackCapFloorEngine::calculate() const {
78
0
        Real value = 0.0;
79
0
        Real vega = 0.0;
80
0
        Size optionlets = arguments_.startDates.size();
81
0
        std::vector<Real> values(optionlets, 0.0);
82
0
        std::vector<Real> deltas(optionlets, 0.0);
83
0
        std::vector<Real> vegas(optionlets, 0.0);
84
0
        std::vector<Real> stdDevs(optionlets, 0.0);
85
0
        std::vector<DiscountFactor> discountFactors(optionlets, 0.0);
86
0
        CapFloor::Type type = arguments_.type;
87
0
        Date today = vol_->referenceDate();
88
0
        Date settlement = discountCurve_->referenceDate();
89
90
0
        for (Size i=0; i<optionlets; ++i) {
91
0
            Date paymentDate = arguments_.endDates[i];
92
            // handling of settlementDate, npvDate and includeSettlementFlows
93
            // should be implemented.
94
            // For the time being just discard expired caplets
95
0
            if (paymentDate > settlement) {
96
0
                DiscountFactor d = discountCurve_->discount(paymentDate);
97
0
                discountFactors[i] = d;
98
0
                Real accrualFactor = arguments_.nominals[i] *
99
0
                                   arguments_.gearings[i] *
100
0
                                   arguments_.accrualTimes[i];
101
0
                Real discountedAccrual = d * accrualFactor;
102
0
                Rate forward = arguments_.forwards[i];
103
104
0
                Date fixingDate = arguments_.fixingDates[i];
105
0
                Time sqrtTime = 0.0;
106
0
                if (fixingDate > today)
107
0
                    sqrtTime = std::sqrt(vol_->timeFromReference(fixingDate));
108
109
0
                if (type == CapFloor::Cap || type == CapFloor::Collar) {
110
0
                    Rate strike = arguments_.capRates[i];
111
0
                    if (sqrtTime>0.0) {
112
0
                        stdDevs[i] = std::sqrt(vol_->blackVariance(fixingDate,
113
0
                                                                   strike));
114
0
                        vegas[i] = blackFormulaStdDevDerivative(strike,
115
0
                            forward, stdDevs[i], discountedAccrual, displacement_) 
116
0
                            * sqrtTime;
117
0
                        deltas[i] = blackFormulaAssetItmProbability(Option::Call,
118
0
                            strike, forward, stdDevs[i], displacement_);
119
0
                    }
120
                    // include caplets with past fixing date
121
0
                    values[i] = blackFormula(Option::Call,
122
0
                        strike, forward, stdDevs[i], discountedAccrual, 
123
0
                        displacement_);
124
0
                }
125
0
                if (type == CapFloor::Floor || type == CapFloor::Collar) {
126
0
                    Rate strike = arguments_.floorRates[i];
127
0
                    Real floorletVega = 0.0;
128
0
                    Real floorletDelta = 0.0;
129
0
                    if (sqrtTime>0.0) {
130
0
                        stdDevs[i] = std::sqrt(vol_->blackVariance(fixingDate,
131
0
                                                                   strike));
132
0
                        floorletVega = blackFormulaStdDevDerivative(strike,
133
0
                            forward, stdDevs[i], discountedAccrual, displacement_) 
134
0
                            * sqrtTime;
135
0
                        floorletDelta = Integer(Option::Put) * blackFormulaAssetItmProbability(
136
0
                                                        Option::Put, strike, forward, 
137
0
                                                        stdDevs[i], displacement_);
138
0
                    }
139
0
                    Real floorlet = blackFormula(Option::Put,
140
0
                        strike, forward, stdDevs[i], discountedAccrual, displacement_);
141
0
                    if (type == CapFloor::Floor) {
142
0
                        values[i] = floorlet;
143
0
                        vegas[i] = floorletVega;
144
0
                        deltas[i] = floorletDelta;
145
0
                    } else {
146
                        // a collar is long a cap and short a floor
147
0
                        values[i] -= floorlet;
148
0
                        vegas[i] -= floorletVega;
149
0
                        deltas[i] -= floorletDelta; 
150
0
                    }
151
0
                }
152
0
                value += values[i];
153
0
                vega += vegas[i];
154
0
            }
155
0
        }
156
0
        results_.value = value;
157
0
        results_.additionalResults["vega"] = vega;
158
159
0
        results_.additionalResults["optionletsPrice"] = values;
160
0
        results_.additionalResults["optionletsVega"] = vegas;
161
0
        results_.additionalResults["optionletsDelta"] = deltas;
162
0
        results_.additionalResults["optionletsDiscountFactor"] = discountFactors;
163
0
        results_.additionalResults["optionletsAtmForward"] = arguments_.forwards;
164
0
        if (type != CapFloor::Collar)
165
0
            results_.additionalResults["optionletsStdDev"] = stdDevs;
166
0
    }
167
168
}