Coverage Report

Created: 2026-06-08 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/pricingengines/capfloor/bacheliercapfloorengine.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2014, 2015 Michael von den Driesch
5
 Copyright (C) 2019 Wojciech Ĺšlusarski
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/pricingengines/blackformula.hpp>
22
#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
23
#include <ql/termstructures/volatility/optionlet/constantoptionletvol.hpp>
24
#include <ql/termstructures/volatility/optionlet/strippedoptionletadapter.hpp>
25
#include <ql/termstructures/yieldtermstructure.hpp>
26
#include <ql/time/calendars/nullcalendar.hpp>
27
#include <utility>
28
29
namespace QuantLib {
30
31
    BachelierCapFloorEngine::BachelierCapFloorEngine(Handle<YieldTermStructure> discountCurve,
32
                                                     Volatility v,
33
                                                     const DayCounter& dc)
34
0
    : discountCurve_(std::move(discountCurve)),
35
0
      vol_(ext::shared_ptr<OptionletVolatilityStructure>(
36
0
          new ConstantOptionletVolatility(0, NullCalendar(), Following, v, dc))) {
37
0
        registerWith(discountCurve_);
38
0
    }
39
40
    BachelierCapFloorEngine::BachelierCapFloorEngine(Handle<YieldTermStructure> discountCurve,
41
                                                     const Handle<Quote>& v,
42
                                                     const DayCounter& dc)
43
0
    : discountCurve_(std::move(discountCurve)),
44
0
      vol_(ext::shared_ptr<OptionletVolatilityStructure>(
45
0
          new ConstantOptionletVolatility(0, NullCalendar(), Following, v, dc))) {
46
0
        registerWith(discountCurve_);
47
0
        registerWith(vol_);
48
0
    }
49
50
    BachelierCapFloorEngine::BachelierCapFloorEngine(
51
        Handle<YieldTermStructure> discountCurve, Handle<OptionletVolatilityStructure> volatility)
52
0
    : discountCurve_(std::move(discountCurve)), vol_(std::move(volatility)) {
53
0
        QL_REQUIRE(vol_->volatilityType() == Normal,
54
0
                   "BachelierCapFloorEngine should only be used for vol "
55
0
                   "surfaces stripped with normal model. Options were stripped "
56
0
                   "with model "
57
0
                       << vol_->volatilityType());
58
0
        registerWith(discountCurve_);
59
0
        registerWith(vol_);
60
0
    }
61
62
0
    void BachelierCapFloorEngine::calculate() const {
63
0
        Real value = 0.0;
64
0
        Real vega = 0.0;
65
0
        Size optionlets = arguments_.startDates.size();
66
0
        std::vector<Real> values(optionlets, 0.0);
67
0
        std::vector<Real> deltas(optionlets, 0.0);
68
0
        std::vector<Real> vegas(optionlets, 0.0);
69
0
        std::vector<Real> stdDevs(optionlets, 0.0);
70
0
        std::vector<DiscountFactor> discountFactors(optionlets, 0.0);
71
0
        CapFloor::Type type = arguments_.type;
72
0
        Date today = vol_->referenceDate();
73
0
        Date settlement = discountCurve_->referenceDate();
74
75
0
        for (Size i=0; i<optionlets; ++i) {
76
0
            Date paymentDate = arguments_.endDates[i];
77
            // handling of settlementDate, npvDate and includeSettlementFlows
78
            // should be implemented.
79
            // For the time being just discard expired caplets
80
0
            if (paymentDate > settlement) {
81
0
                DiscountFactor d = discountCurve_->discount(paymentDate);
82
0
                discountFactors[i] = d;
83
0
                Real accrualFactor = arguments_.nominals[i] *
84
0
                                   arguments_.gearings[i] *
85
0
                                   arguments_.accrualTimes[i];
86
0
                Real discountedAccrual = d * accrualFactor;
87
88
0
                Rate forward = arguments_.forwards[i];
89
90
0
                Date fixingDate = arguments_.fixingDates[i];
91
0
                Time sqrtTime = 0.0;
92
0
                if (fixingDate > today)
93
0
                    sqrtTime = std::sqrt(vol_->timeFromReference(fixingDate));
94
95
0
                if (type == CapFloor::Cap || type == CapFloor::Collar) {
96
0
                    Rate strike = arguments_.capRates[i];
97
0
                    if (sqrtTime>0.0) {
98
0
                        stdDevs[i] = std::sqrt(vol_->blackVariance(fixingDate,
99
0
                                                                   strike));
100
0
                        vegas[i] = bachelierBlackFormulaStdDevDerivative(strike,
101
0
                            forward, stdDevs[i], discountedAccrual) * sqrtTime;
102
0
                        deltas[i] = bachelierBlackFormulaAssetItmProbability(Option::Call,
103
0
                            strike, forward, stdDevs[i]);
104
0
                    }
105
                    // include caplets with past fixing date
106
0
                    values[i] = bachelierBlackFormula(Option::Call,
107
0
                        strike, forward, stdDevs[i], discountedAccrual);
108
0
                }
109
0
                if (type == CapFloor::Floor || type == CapFloor::Collar) {
110
0
                    Rate strike = arguments_.floorRates[i];
111
0
                    Real floorletVega = 0.0;
112
0
                    Real floorletDelta = 0.0;
113
0
                    if (sqrtTime>0.0) {
114
0
                        stdDevs[i] = std::sqrt(vol_->blackVariance(fixingDate,
115
0
                                                                   strike));
116
0
                        floorletVega = bachelierBlackFormulaStdDevDerivative(strike,
117
0
                            forward, stdDevs[i], discountedAccrual) * sqrtTime;
118
0
                        floorletDelta = Integer(Option::Put) * bachelierBlackFormulaAssetItmProbability(
119
0
                                                        Option::Put, strike, forward, 
120
0
                                                        stdDevs[i]);
121
0
                    }
122
0
                    Real floorlet = bachelierBlackFormula(Option::Put,
123
0
                        strike, forward, stdDevs[i], discountedAccrual);
124
0
                    if (type == CapFloor::Floor) {
125
0
                        values[i] = floorlet;
126
0
                        vegas[i] = floorletVega;
127
0
                        deltas[i] = floorletDelta;
128
0
                    } else {
129
                        // a collar is long a cap and short a floor
130
0
                        values[i] -= floorlet;
131
0
                        vegas[i] -= floorletVega;
132
0
                        deltas[i] -= floorletDelta;
133
0
                    }
134
0
                }
135
0
                value += values[i];
136
0
                vega += vegas[i];
137
0
            }
138
0
        }
139
0
        results_.value = value;
140
0
        results_.additionalResults["vega"] = vega;
141
142
0
        results_.additionalResults["optionletsPrice"] = values;
143
0
        results_.additionalResults["optionletsVega"] = vegas;
144
0
        results_.additionalResults["optionletsDelta"] = deltas;
145
0
        results_.additionalResults["optionletsDiscountFactor"] = discountFactors;
146
0
        results_.additionalResults["optionletsAtmForward"] = arguments_.forwards;
147
0
        if (type != CapFloor::Collar)
148
0
            results_.additionalResults["optionletsStdDev"] = stdDevs;
149
0
    }
150
151
}