Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
equitycoupon.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2018 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
21
22#include <ql/utilities/vectors.hpp>
23#include <ql/time/calendars/jointcalendar.hpp>
24#include <boost/algorithm/string/case_conv.hpp>
25
26using namespace QuantLib;
27
28namespace QuantExt {
29
30std::ostream& operator<<(std::ostream& out, EquityReturnType t) {
31 switch (t) {
33 return out << "Price";
35 return out << "Total";
37 return out << "Absolute";
39 return out << "Dividend";
40 default:
41 QL_FAIL("unknown EquityReturnType(" << int(t) << ")");
42 }
43}
44
45EquityReturnType parseEquityReturnType(const std::string& str) {
46 if (boost::algorithm::to_upper_copy(str) == "PRICE")
48 else if (boost::algorithm::to_upper_copy(str) == "TOTAL")
50 else if (boost::algorithm::to_upper_copy(str) == "ABSOLUTE")
52 else if (boost::algorithm::to_upper_copy(str) == "DIVIDEND")
54 QL_FAIL("Invalid EquityReturnType " << str);
55}
56
57EquityCoupon::EquityCoupon(const Date& paymentDate, Real nominal, const Date& startDate, const Date& endDate,
58 Natural fixingDays, const QuantLib::ext::shared_ptr<QuantExt::EquityIndex2>& equityCurve,
59 const DayCounter& dayCounter, EquityReturnType returnType, Real dividendFactor,
60 bool notionalReset, Real initialPrice, Real quantity, const Date& fixingStartDate,
61 const Date& fixingEndDate, const Date& refPeriodStart, const Date& refPeriodEnd,
62 const Date& exCouponDate, const QuantLib::ext::shared_ptr<FxIndex>& fxIndex,
63 const bool initialPriceIsInTargetCcy, Real legInitialNotional, const Date& legFixingDate)
64 : Coupon(paymentDate, nominal, startDate, endDate, refPeriodStart, refPeriodEnd, exCouponDate),
65 fixingDays_(fixingDays), equityCurve_(equityCurve), dayCounter_(dayCounter), returnType_(returnType),
66 dividendFactor_(dividendFactor), notionalReset_(notionalReset), initialPrice_(initialPrice),
67 initialPriceIsInTargetCcy_(initialPriceIsInTargetCcy), quantity_(quantity), fixingStartDate_(fixingStartDate),
68 fixingEndDate_(fixingEndDate), fxIndex_(fxIndex), legInitialNotional_(legInitialNotional),
69 legFixingDate_(legFixingDate) {
70 QL_REQUIRE(dividendFactor_ > 0.0, "Dividend factor should not be negative. It is expected to be between 0 and 1.");
71 QL_REQUIRE(equityCurve_, "Equity underlying an equity swap coupon cannot be empty.");
72
73 // set up fixing calendar as combined eq / fx calendar
74 Calendar eqCalendar = NullCalendar();
75 Calendar fxCalendar = NullCalendar();
76 if (!equityCurve_->fixingCalendar().empty()) {
77 eqCalendar = equityCurve_->fixingCalendar();
78 }
79 if (fxIndex_ && !fxIndex_->fixingCalendar().empty()) {
80 fxCalendar = fxIndex_->fixingCalendar();
81 }
82 Calendar fixingCalendar = JointCalendar(eqCalendar, fxCalendar);
83
84 // If a fixing start / end date is provided, use these
85 // else adjust the start/endDate by the FixingDays - defaulted to 0
86 if (fixingStartDate_ == Date())
88 fixingCalendar.advance(startDate, -static_cast<Integer>(fixingDays_), Days, Preceding);
89
90 if (fixingEndDate_ == Date())
92 fixingCalendar.advance(endDate, -static_cast<Integer>(fixingDays_), Days, Preceding);
93
94 registerWith(equityCurve_);
95 registerWith(fxIndex_);
96 registerWith(Settings::instance().evaluationDate());
97
98 // QL_REQUIRE(!notionalReset_ || quantity_ != Null<Real>(), "EquityCoupon: quantity required if notional resets");
99 QL_REQUIRE(notionalReset_ || nominal_ != Null<Real>(),
100 "EquityCoupon: notional required if notional does not reset");
101}
102
103void EquityCoupon::setPricer(const QuantLib::ext::shared_ptr<EquityCouponPricer>& pricer) {
104 if (pricer_)
105 unregisterWith(pricer_);
106 pricer_ = pricer;
107 if (pricer_)
108 registerWith(pricer_);
109 update();
110}
111
113 if (notionalReset_ && quantity_ == Null<Real>()) {
114 QL_REQUIRE(legInitialNotional_ != Null<Real>() && legFixingDate_ != Date(),
115 "leg initial notional and fixing date required to compute the missing quantity in case of a resetting equity leg");
116 quantity_ = legInitialNotional_ / equityCurve_->fixing(legFixingDate_, false, false);
117 }
118 return quantity_;
119}
120
122 // use quantity for dividend swaps, this ensures notional resetting is not relevant
123 // swaplet rate returns the absolute dividend to match
125 return quantity();
126 else if(notionalReset_) {
127 Real mult = (initialPrice_ == 0) ? 1 : initialPrice();
128 return mult * (initialPriceIsInTargetCcy_ ? 1.0 : fxRate()) * quantity();
129 } else {
130 return nominal_;
131 }
132}
133
135 // fxRate applied if equity underlying currency differs from leg
136 return fxIndex_ ? fxIndex_->fixing(fixingStartDate_) : 1.0;
137}
138
140 if (initialPrice_ != Null<Real>())
141 return initialPrice_;
142 else
143 return equityCurve_->fixing(fixingStartDate(), false, false);
144}
145
147
148Real EquityCoupon::accruedAmount(const Date& d) const {
149 if (d <= accrualStartDate_ || d > paymentDate_) {
150 return 0.0;
151 } else {
152 Time fullPeriod = dayCounter().yearFraction(accrualStartDate_, accrualEndDate_, refPeriodStart_, refPeriodEnd_);
153 Time thisPeriod =
154 dayCounter().yearFraction(accrualStartDate_, std::min(d, accrualEndDate_), refPeriodStart_, refPeriodEnd_);
155 return nominal() * rate() * thisPeriod / fullPeriod;
156 }
157}
158
159Rate EquityCoupon::rate() const {
160 QL_REQUIRE(pricer_, "pricer not set");
161 // we know it is the correct type because checkPricerImpl checks on setting
162 // in general pricer_ will be a derived class, as will *this on calling
163 pricer_->initialize(*this);
164 return pricer_->swapletRate();
165}
166
167std::vector<Date> EquityCoupon::fixingDates() const {
168 std::vector<Date> fixingDates;
169
170 fixingDates.push_back(fixingStartDate_);
171 fixingDates.push_back(fixingEndDate_);
172 // We may need a fixing date at the leg start date if it a notionalReset and the quantity is null
173 // Quantity is null if no initial price was given for the Swap
174 if (notionalReset_ && quantity_ == Null<Real>())
175 fixingDates.push_back(legFixingDate_);
176 return fixingDates;
177};
178
179EquityLeg::EquityLeg(const Schedule& schedule, const QuantLib::ext::shared_ptr<QuantExt::EquityIndex2>& equityCurve,
180 const QuantLib::ext::shared_ptr<FxIndex>& fxIndex)
181 : schedule_(schedule), equityCurve_(equityCurve), fxIndex_(fxIndex), paymentLag_(0), paymentAdjustment_(Following),
182 paymentCalendar_(Calendar()), returnType_(EquityReturnType::Total), initialPrice_(Null<Real>()),
183 initialPriceIsInTargetCcy_(false), dividendFactor_(1.0), fixingDays_(0), notionalReset_(false),
184 quantity_(Null<Real>()) {}
185
187 notionals_ = std::vector<Real>(1, notional);
188 return *this;
189}
190
191EquityLeg& EquityLeg::withNotionals(const std::vector<Real>& notionals) {
192 notionals_ = notionals;
193 return *this;
194}
195
196EquityLeg& EquityLeg::withPaymentDayCounter(const DayCounter& dayCounter) {
197 paymentDayCounter_ = dayCounter;
198 return *this;
199}
200
201EquityLeg& EquityLeg::withPaymentAdjustment(BusinessDayConvention convention) {
202 paymentAdjustment_ = convention;
203 return *this;
204}
205
207 paymentLag_ = paymentLag;
208 return *this;
209}
210
211EquityLeg& EquityLeg::withPaymentCalendar(const Calendar& calendar) {
212 paymentCalendar_ = calendar;
213 return *this;
214}
215
217 returnType_ = returnType;
218 return *this;
219}
220
222 dividendFactor_ = dividendFactor;
223 return *this;
224}
225
227 initialPrice_ = initialPrice;
228 return *this;
229}
230
231EquityLeg& EquityLeg::withInitialPriceIsInTargetCcy(bool initialPriceIsInTargetCcy) {
232 initialPriceIsInTargetCcy_ = initialPriceIsInTargetCcy;
233 return *this;
234}
235
237 fixingDays_ = fixingDays;
238 return *this;
239}
240
241EquityLeg& EquityLeg::withValuationSchedule(const Schedule& valuationSchedule) {
242 valuationSchedule_ = valuationSchedule;
243 return *this;
244}
245
247 notionalReset_ = notionalReset;
248 return *this;
249}
250
252 quantity_ = quantity;
253 return *this;
254}
255
256EquityLeg::operator Leg() const {
257
258 Leg cashflows;
259 Date startDate;
260 Date endDate;
261 Date paymentDate;
262
263 Calendar calendar;
264 if (!paymentCalendar_.empty()) {
265 calendar = paymentCalendar_;
266 } else {
267 calendar = schedule_.calendar();
268 }
269
270 Size numPeriods = schedule_.size() - 1;
271
272 if (valuationSchedule_.size() > 0) {
273 QL_REQUIRE(valuationSchedule_.size() == schedule_.size(),
274 "mismatch in valuationSchedule (" << valuationSchedule_.size() << ") and scheduleData ("
275 << schedule_.size() << ") sizes");
276 }
277
278 Date legFixingDate = Date();
279 if (valuationSchedule_.size() > 0)
280 legFixingDate = valuationSchedule_.dates().front();
281 else if (schedule_.size() > 0)
282 legFixingDate = equityCurve_->fixingCalendar().advance(schedule_.dates().front(),
283 -static_cast<Integer>(fixingDays_), Days, Preceding);
284 else
285 QL_FAIL("Cannot build equity leg, neither schedule nor valuation schedule are defined");
286
287 Real quantity = Null<Real>(), notional = Null<Real>(), legInitialNotional = Null<Real>();
288 if (notionalReset_) {
289 // We need a quantity in each coupon in this case
290 if (quantity_ != Null<Real>()) {
291 quantity = quantity_;
292 QL_REQUIRE(notionals_.empty(), "EquityLeg: notional and quantity are given at the same time");
293 } else {
294 // If we have a notional, but no quantity is given:
295 // a) Compute quantity from notional and initial price
296 // b) If initial price is missing, leave quantity undefined here:
297 // The coupon will compute it from legInitialNotional=notional and legFixingDate
298 QL_REQUIRE(!notionals_.empty(), "EquityLeg: can not compute qunantity, since no notional is given");
299 QL_REQUIRE(fxIndex_ == nullptr || initialPriceIsInTargetCcy_,
300 "EquityLeg: can not compute quantity from nominal when fx conversion is required");
301 notional = notionals_.front();
302 legInitialNotional = notional;
303 if (initialPrice_ != Null<Real>())
304 quantity = (initialPrice_ == 0) ? notional : notional / initialPrice_;
305 }
306 } else {
307 if (!notionals_.empty()) {
308 QL_REQUIRE(quantity_ == Null<Real>(), "EquityLeg: notional and quantity are given at the same time");
309 // notional is determined below in the loop over the periods
310 legInitialNotional = notionals_.front();
311 } else {
312 QL_REQUIRE(initialPrice_ != Null<Real>(), "EquityLeg: can not compute notional, since no intialPrice is given");
313 QL_REQUIRE(quantity_ != Null<Real>(), "EquityLeg: can not compute notional, since no quantity is given");
314 QL_REQUIRE(fxIndex_ == nullptr || initialPriceIsInTargetCcy_,
315 "EquityLeg: can not compute notional from quantity when fx conversion is required");
316 notional = (initialPrice_ == 0) ? quantity_ : quantity_ * initialPrice_;
317 }
318 }
319
320 for (Size i = 0; i < numPeriods; ++i) {
321 startDate = schedule_.date(i);
322 endDate = schedule_.date(i + 1);
323 paymentDate = calendar.advance(endDate, paymentLag_, Days, paymentAdjustment_);
324
325 Date fixingStartDate = Date();
326 Date fixingEndDate = Date();
327 if (valuationSchedule_.size() > 0) {
328 fixingStartDate = valuationSchedule_.date(i);
329 fixingEndDate = valuationSchedule_.date(i + 1);
330 }
331
332 Real initialPrice = (i == 0) ? initialPrice_ : Null<Real>();
333 bool initialPriceIsInTargetCcy = initialPrice != Null<Real>() ? initialPriceIsInTargetCcy_ : false;
334 if (!notionalReset_ && !notionals_.empty()) {
335 notional = detail::get(notionals_, i, 0.0);
336 }
337
338 QuantLib::ext::shared_ptr<EquityCoupon> cashflow(new EquityCoupon(
339 paymentDate, notional, startDate, endDate, fixingDays_, equityCurve_, paymentDayCounter_, returnType_,
340 dividendFactor_, notionalReset_, initialPrice, quantity, fixingStartDate, fixingEndDate, Date(), Date(),
341 Date(), fxIndex_, initialPriceIsInTargetCcy, legInitialNotional, legFixingDate));
342
343 QuantLib::ext::shared_ptr<EquityCouponPricer> pricer(new EquityCouponPricer);
344 cashflow->setPricer(pricer);
345
346 cashflows.push_back(cashflow);
347 }
348 return cashflows;
349}
350
351} // namespace QuantExt
QuantLib::ext::shared_ptr< QuantExt::EquityIndex2 > equityCurve_
void setPricer(const QuantLib::ext::shared_ptr< EquityCouponPricer > &)
QuantLib::ext::shared_ptr< EquityCouponPricer > pricer() const
QuantLib::ext::shared_ptr< FxIndex > fxIndex_
std::vector< Date > fixingDates() const
return both fixing dates
void update() override
bool initialPriceIsInTargetCcy() const
initial price is in target ccy (if applicable, i.e. if fxIndex != null, otherwise ignored)
Rate rate() const override
Real fxRate() const
FX conversion rate (or 1.0 if not applicable)
Real nominal() const override
EquityReturnType returnType_
EquityCoupon(const Date &paymentDate, Real nominal, const Date &startDate, const Date &endDate, Natural fixingDays, const QuantLib::ext::shared_ptr< QuantExt::EquityIndex2 > &equityCurve, const DayCounter &dayCounter, EquityReturnType returnType, Real dividendFactor=1.0, bool notionalReset=false, Real initialPrice=Null< Real >(), Real quantity=Null< Real >(), const Date &fixingStartDate=Date(), const Date &fixingEndDate=Date(), const Date &refPeriodStart=Date(), const Date &refPeriodEnd=Date(), const Date &exCouponDate=Date(), const QuantLib::ext::shared_ptr< FxIndex > &fxIndex=nullptr, const bool initialPriceIsInTargetCcy=false, Real legInitialNotional=Null< Real >(), const Date &legFixingDate=Date())
DayCounter dayCounter() const override
Real accruedAmount(const Date &) const override
QuantLib::ext::shared_ptr< EquityCouponPricer > pricer_
Real quantity() const
Number of equity shares held.
Date fixingStartDate() const
The date at which the starting equity price is fixed.
Real initialPrice() const
initial price
Pricer for equity coupons.
helper class building a sequence of equity coupons
EquityLeg & withInitialPriceIsInTargetCcy(bool)
BusinessDayConvention paymentAdjustment_
EquityLeg & withDividendFactor(Real)
EquityLeg & withPaymentCalendar(const Calendar &calendar)
EquityLeg & withNotional(Real notional)
EquityLeg & withFixingDays(Natural)
EquityLeg(const Schedule &schedule, const QuantLib::ext::shared_ptr< QuantExt::EquityIndex2 > &equityCurve, const QuantLib::ext::shared_ptr< FxIndex > &fxIndex=nullptr)
std::vector< Real > notionals_
EquityReturnType returnType_
EquityLeg & withNotionalReset(bool)
EquityLeg & withQuantity(Real)
EquityLeg & withNotionals(const std::vector< Real > &notionals)
EquityLeg & withInitialPrice(Real)
EquityLeg & withPaymentDayCounter(const DayCounter &dayCounter)
EquityLeg & withValuationSchedule(const Schedule &valuationSchedule)
EquityLeg & withPaymentAdjustment(BusinessDayConvention convention)
EquityLeg & withPaymentLag(Natural paymentLag)
EquityLeg & withReturnType(EquityReturnType)
DayCounter paymentDayCounter_
coupon paying the return on an equity
Pricer for equity coupons.
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
EquityReturnType parseEquityReturnType(const std::string &str)