Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
fdconvertiblebondevents.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2021 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
19
20#include <ql/cashflows/coupon.hpp>
21#include <ql/math/interpolations/bilinearinterpolation.hpp>
22#include <ql/time/calendars/jointcalendar.hpp>
23#include <ql/time/daycounters/actualactual.hpp>
24#include <ql/timegrid.hpp>
25
26namespace QuantExt {
27
28namespace {
29std::string getDateStr(const Date& d) {
30 std::ostringstream os;
31 os << QuantLib::io::iso_date(d);
32 return os.str();
33}
34} // namespace
35
36FdConvertibleBondEvents::FdConvertibleBondEvents(const Date& today, const DayCounter& dc, const Real N0,
37 const QuantLib::ext::shared_ptr<QuantExt::EquityIndex2>& equity,
38 const QuantLib::ext::shared_ptr<FxIndex>& fxConversion)
39 : today_(today), dc_(dc), N0_(N0), equity_(equity), fxConversion_(fxConversion) {}
40
41Real FdConvertibleBondEvents::time(const Date& d) const { return dc_.yearFraction(today_, d); }
42
43void FdConvertibleBondEvents::registerBondCashflow(const QuantLib::ext::shared_ptr<CashFlow>& c) {
44 if (c->date() > today_) {
45 registeredBondCashflows_.push_back(c);
46 times_.insert(time(c->date()));
47 }
48}
49
51 registeredCallData_.push_back(c);
52 if (c.exerciseDate > today_) {
53 times_.insert(time(c.exerciseDate));
54 }
55}
56
59}
60
62 registeredPutData_.push_back(c);
63 if (c.exerciseDate > today_) {
64 times_.insert(time(c.exerciseDate));
65 }
66}
67
70 if (c.fromDate > today_) {
71 times_.insert(time(c.fromDate));
72 }
73}
74
76 registeredConversionData_.push_back(c);
77 if (c.exerciseDate > today_) {
78 times_.insert(time(c.exerciseDate));
79 }
80}
81
84 if (c.exerciseDate > today_) {
85 times_.insert(time(c.exerciseDate));
86 }
87}
88
91 if (c.resetDate > today_) {
92 times_.insert(time(c.resetDate));
93 }
94}
95
98 if (c.protectionDate > today_) {
99 times_.insert(time(c.protectionDate));
100 }
101}
102
103const std::set<Real>& FdConvertibleBondEvents::times() const { return times_; }
104
106 const std::vector<ConvertibleBond2::CallabilityData>& data) const {
107 Date result = Date::maxDate();
108 for (auto const& x : data) {
109 if (x.exerciseDate > d)
110 result = std::min(result, x.exerciseDate);
111 }
112 if (result == Date::maxDate())
113 return Null<Date>();
114 return result;
115}
116
118 Date result = Date::maxDate();
119 for (auto const& x : registeredConversionData_) {
120 if (x.exerciseDate > d)
121 result = std::min(result, x.exerciseDate);
122 }
123 if (result == Date::maxDate())
124 return Null<Date>();
125 return result;
126}
127
129 lastRedemptionDate_ = Date::minDate();
130 for (auto const& c : registeredBondCashflows_) {
131 if (QuantLib::ext::dynamic_pointer_cast<Coupon>(c) == nullptr)
132 lastRedemptionDate_ = std::max(lastRedemptionDate_, c->date());
133 }
134 for (auto const& d : registeredBondCashflows_) {
135 bool isRedemption = QuantLib::ext::dynamic_pointer_cast<Coupon>(d) == nullptr;
136 Size index = grid_.index(time(d->date()));
137 hasBondCashflow_[index] = true;
138 associatedDate_[index] = d->date();
139 if (isRedemption && d->date() == lastRedemptionDate_)
140 bondFinalRedemption_[index] += d->amount();
141 else
142 bondCashflow_[index] += d->amount();
143 }
144}
145
146void FdConvertibleBondEvents::processExerciseData(const std::vector<ConvertibleBond2::CallabilityData>& sourceData,
147 std::vector<bool>& targetFlags, std::vector<CallData>& targetData) {
148 for (auto const& c : sourceData) {
149 if (c.exerciseDate <= today_ && c.exerciseType == ConvertibleBond2::CallabilityData::ExerciseType::OnThisDate)
150 continue;
151 Size indexStart = grid_.index(time(std::max(c.exerciseDate, today_)));
152 Size indexEnd;
153 associatedDate_[indexStart] = std::max(c.exerciseDate, today_);
155 indexEnd = indexStart;
157 Date nextDate = nextExerciseDate(c.exerciseDate, sourceData);
158 QL_REQUIRE(nextDate != Null<Date>(),
159 "FdConvertibleBondEvents::processExerciseData(): internal error: did not find a next exercise "
160 "date after "
161 << c.exerciseDate
162 << ", the last exercise date should not have exercise type FromThisDateOn");
163 if (nextDate <= today_)
164 continue;
165 indexEnd = grid_.index(time(nextDate)) - 1;
166 } else {
167 QL_FAIL("FdConvertibleBondEvents: internal error, exercise type not "
168 "recognized");
169 }
170 for (Size i = indexStart; i <= indexEnd; ++i) {
171 targetFlags[i] = true;
172 targetData[i] = CallData{c.price, c.priceType, c.includeAccrual,
173 c.isSoft, c.softTriggerRatio, std::function<Real(Real, Real)>()};
174 }
175 }
176}
177
180
181 // init and checks
182
183 auto const& stockPrices = (*registeredMakeWholeData_.crIncreaseData).stockPrices;
184 auto const& effDates = (*registeredMakeWholeData_.crIncreaseData).effectiveDates;
185 auto const& crInc = (*registeredMakeWholeData_.crIncreaseData).crIncrease;
186 QL_REQUIRE(stockPrices.size() >= 2,
187 "FdConvertibleBondEvents::processMakeWholeData(): at least two stock prices required (cr increase)");
188 QL_REQUIRE(
189 effDates.size() >= 2,
190 "FdConvertibleBondEvents::processMakeWholeData(): at least two effective dates required (cr increase)");
191 QL_REQUIRE(effDates.size() == crInc.size(), "FdConvertibleBondEvents::processMakeWholeData(): effective dates ("
192 << effDates.size() << ") must match cr increase rows ("
193 << crInc.size() << ") (cr increase)");
194 for (auto const& c : crInc) {
195 QL_REQUIRE(c.size() == stockPrices.size(),
196 "FdConvertibleBondEvents::processMakeWholeData(): stock prices size ("
197 << stockPrices.size() << ") must match cr increase coluns (" << c.size() << ")");
198 }
199
200 // build interpolation
201
202 mw_cr_inc_x_ = Array(stockPrices.begin(), stockPrices.end());
203 mw_cr_inc_y_ = Array(effDates.size());
204 mw_cr_inc_z_ = Matrix(mw_cr_inc_y_.size(), mw_cr_inc_x_.size());
205
206 for (Size i = 0; i < effDates.size(); ++i) {
207 mw_cr_inc_y_[i] = time(effDates[i]);
208 for (Size j = 0; j < stockPrices.size(); ++j) {
209 mw_cr_inc_z_(i, j) = crInc[i][j];
210 }
211 }
212
213 auto interpolation = QuantLib::ext::make_shared<BilinearInterpolation>(
214 mw_cr_inc_x_.begin(), mw_cr_inc_x_.end(), mw_cr_inc_y_.begin(), mw_cr_inc_y_.end(), mw_cr_inc_z_);
215
216 // init cap (infty if not given)
217
218 Real cap = QL_MAX_REAL;
219 if ((*registeredMakeWholeData_.crIncreaseData).cap != Null<Real>())
221
222 // set effetive mw cr functions on the call data
223
224 for (Size i = 0; i < grid_.size(); ++i) {
225 if (hasCall_[i]) {
226 Real t = grid_[i];
227 callData_[i].mwCr = [interpolation, t, cap](const Real S, const Real cr) {
228 if ((S < interpolation->xMin() && !close_enough(S, interpolation->xMin())) ||
229 (S > interpolation->xMax() && !close_enough(S, interpolation->xMax())) ||
230 (t < interpolation->yMin() && !close_enough(t, interpolation->yMin())) ||
231 (t > interpolation->yMax() && !close_enough(t, interpolation->yMax()))) {
232 return cr;
233 } else {
234 Real tmp = interpolation->operator()(S, t);
235 // apply cap, but if initial cr was already greater than result, keep that
236 return std::max(cr, std::min(cr + tmp, cap));
237 }
238 };
239 }
240 }
241 }
242}
243
245
246 // set the initial conversion ratio
247
248 std::set<std::pair<Date, Real>> crSchedule;
249 for (const auto& d : registeredConversionRatioData_) {
250 crSchedule.insert(std::make_pair(d.fromDate, d.conversionRatio));
251 }
252 if (!crSchedule.empty()) {
253 initialConversionRatio_ = crSchedule.begin()->second;
254 }
256 additionalResults_["historicEvents.initialConversionRatio"] = initialConversionRatio_;
257
258 // collect all relevant conversion events
259 // - cd: cr reset event
260 // - dd: dp event (with cr adjustment or pass-through)
261 // - vd: voluntary conversion (with coco possibly)
262 // - newCr: cr changed (or initially set to) specific value
263
264 struct AdjEvent {
268 Real newCr = Null<Real>(); // set cr to specific value on event date
269 };
270
271 std::map<Date, AdjEvent> adjEvents;
272
273 for (auto& d : registeredConversionResetData_) {
274 adjEvents[d.resetDate].cd = &d;
275 }
276
277 for (auto& d : registeredDividendProtectionData_) {
278 adjEvents[d.protectionDate].dd = &d;
279 }
280
281 for (auto& d : registeredConversionRatioData_) {
282 adjEvents[d.fromDate].newCr = d.conversionRatio;
283 }
284
285 for (auto& d : registeredConversionData_) {
286 adjEvents[d.exerciseDate].vd = &d;
287 }
288
289 // step through the events and process them (historical and future events)
290
291 Real currentCr = initialConversionRatio_;
292 Size lastDividendProtectionTimeIndex = Null<Size>();
293 bool haveStochasticCr = false;
294
295 for (auto const& event : adjEvents) {
296
297 if (event.second.cd != nullptr) {
298 const ConvertibleBond2::ConversionResetData& c = *event.second.cd;
299 if (c.resetDate <= today_) {
300
301 // historical conversion ratio reset event
302
303 Real S = equity_->fixing(equity_->fixingCalendar().adjust(c.resetDate, Preceding));
304 Real fx = 1.0;
305 if (fxConversion_ != nullptr) {
306 fx = fxConversion_->fixing(JointCalendar(equity_->fixingCalendar(), fxConversion_->fixingCalendar())
307 .adjust(c.resetDate, Preceding));
308 }
310 ? currentCr
312 Real referenceCP = N0_ / cr;
313 additionalResults_["historicEvents.crReset_" + getDateStr(event.first) + "_S"] = S;
314 additionalResults_["historicEvents.crReset_" + getDateStr(event.first) + "_threshold"] = c.threshold;
315 additionalResults_["historicEvents.crReset_" + getDateStr(event.first) + "_referenceCP"] = referenceCP;
316 additionalResults_["historicEvents.crReset_" + getDateStr(event.first) + "_gearing"] = c.gearing;
317 additionalResults_["historicEvents.crReset_" + getDateStr(event.first) + "_floor"] = c.floor;
318 additionalResults_["historicEvents.crReset_" + getDateStr(event.first) + "_globalFloor"] =
319 c.globalFloor * fx;
320 additionalResults_["historicEvents.crReset_" + getDateStr(event.first) + "_fxConversion"] = fx;
321 additionalResults_["historicEvents.crReset_" + getDateStr(event.first) + "_currentCr"] = currentCr;
322 if (!close_enough(cr, 0.0) && S < c.threshold * referenceCP) {
323 Real adjustedConversionRatio = QL_MAX_REAL;
324 if (!close_enough(c.gearing, 0.0)) {
325 adjustedConversionRatio = std::min(adjustedConversionRatio, N0_ / (c.gearing * S));
326 }
327 if (!close_enough(c.floor, 0.0)) {
328 adjustedConversionRatio = std::min(adjustedConversionRatio, N0_ / (c.floor * referenceCP));
329 }
330 if (!close_enough(c.globalFloor * fx, 0.0)) {
331 adjustedConversionRatio = std::min(adjustedConversionRatio, N0_ / (c.globalFloor * fx));
332 }
333 adjustedConversionRatio = std::max(
334 currentCr, adjustedConversionRatio != QL_MAX_REAL ? adjustedConversionRatio : -QL_MAX_REAL);
335 currentCr = std::max(currentCr, adjustedConversionRatio);
336 }
337 additionalResults_["historicEvents.crReset_" + getDateStr(event.first) + "_adjustedCr"] = currentCr;
338 } else {
339
340 // future conversion ratio reset event
341
342 Size index = grid_.index(time(event.first));
343 associatedDate_[index] = event.first;
344 hasConversionReset_[index] = true;
345 const ConvertibleBond2::ConversionResetData& c = *event.second.cd;
346 conversionResetData_[index].resetActive = true;
347 conversionResetData_[index].reference = c.referenceType;
348 conversionResetData_[index].gearing = c.gearing;
349 conversionResetData_[index].floor = c.floor;
350 conversionResetData_[index].globalFloor = c.globalFloor * currentFxConversion_[index];
351 conversionResetData_[index].threshold = c.threshold;
352 for (Size i = index + 1; i < grid_.size(); ++i) {
354 }
355 haveStochasticCr = true;
356 }
357 }
358
359 if (event.second.dd != nullptr) {
360 const ConvertibleBond2::DividendProtectionData& c = *event.second.dd;
361 if (c.protectionDate <= today_) {
366
367 // historic dividend protection event with conversion ratio adjustment
368
369 Real fx = 1.0;
370 if (fxConversion_ != nullptr) {
371 fx = fxConversion_->fixing(
372 JointCalendar(equity_->fixingCalendar(), fxConversion_->fixingCalendar())
373 .adjust(c.protectionDate, Preceding));
374 }
375 Real S = equity_->fixing(equity_->fixingCalendar().adjust(c.protectionDate, Preceding));
376 Real D = equity_->dividendsBetweenDates(c.startDate, c.protectionDate);
377 Real H = c.threshold * fx;
378
379 additionalResults_["historicEvents.crReset_DP_" + getDateStr(event.first) + "_div_" +
380 getDateStr(c.startDate) + "_" + getDateStr(c.protectionDate)] = D;
381 additionalResults_["historicEvents.crReset_DP_" + getDateStr(event.first) + "_S"] = S;
382 additionalResults_["historicEvents.crReset_DP_" + getDateStr(event.first) + "_threshold"] = H;
383 additionalResults_["historicEvents.crReset_DP_" + getDateStr(event.first) + "_fxConversion"] = fx;
384 additionalResults_["historicEvents.crReset_DP_" + getDateStr(event.first) + "_currentCr"] =
385 currentCr;
386
389 bool absolute =
391 Real d = absolute ? D : D / S;
392 Real C =
394 ? std::max(d - H, 0.0)
395 : d - H;
396 currentCr *= absolute ? S / std::max(S - C, 1E-4) : (1.0 + C);
397 } else {
398 Real f = std::max(S - H, 0.0) / std::max(S - D, 1E-4);
400 f = std::max(f, 1.0);
401 currentCr *= f;
402 }
403
404 additionalResults_["historicEvents.crReset_DP_" + getDateStr(event.first) + "_adjustedCr"] =
405 currentCr;
406 }
407 } else {
408
413
414 // future dividend protection event with conversion ratio adjustment
415
416 Size index = grid_.index(time(event.first));
417 associatedDate_[index] = event.first;
418 hasConversionReset_[index] = true;
419 const ConvertibleBond2::DividendProtectionData& c = *event.second.dd;
420 conversionResetData_[index].divProtActive = true;
421 conversionResetData_[index].adjustmentStyle = c.adjustmentStyle;
422 conversionResetData_[index].dividendType = c.dividendType;
423 conversionResetData_[index].divThreshold = c.threshold * currentFxConversion_[index];
424 if (lastDividendProtectionTimeIndex == Null<Size>()) {
425 conversionResetData_[index].lastDividendProtectionTimeIndex = 0;
426 conversionResetData_[index].accruedHistoricalDividends =
427 equity_->dividendsBetweenDates(c.startDate, today_);
428 additionalResults_["historicEvents.accruedDividends_" + getDateStr(c.startDate) + "_" +
429 getDateStr(today_)] = conversionResetData_[index].accruedHistoricalDividends;
430 } else {
431 conversionResetData_[index].lastDividendProtectionTimeIndex = lastDividendProtectionTimeIndex;
432 conversionResetData_[index].accruedHistoricalDividends = 0.0;
433 }
434 lastDividendProtectionTimeIndex = index;
435 for (Size i = index + 1; i < grid_.size(); ++i) {
437 }
438 haveStochasticCr = true;
439
440 } else {
441
442 // future dividend pass through event
443 Size index = grid_.index(time(c.protectionDate));
445 hasDividendPassThrough_[index] = true;
446 dividendPassThroughData_[index].adjustmentStyle = c.adjustmentStyle;
447 dividendPassThroughData_[index].dividendType = c.dividendType;
448 dividendPassThroughData_[index].divThreshold = c.threshold;
449 if (lastDividendProtectionTimeIndex == Null<Size>()) {
450 dividendPassThroughData_[index].lastDividendProtectionTimeIndex = 0;
451 dividendPassThroughData_[index].accruedHistoricalDividends =
452 equity_->dividendsBetweenDates(c.startDate, today_);
453 additionalResults_["historicEvents.accruedDividends_" + getDateStr(c.startDate) + "_" +
454 getDateStr(today_)] =
455 dividendPassThroughData_[index].accruedHistoricalDividends;
456 } else {
457 dividendPassThroughData_[index].lastDividendProtectionTimeIndex =
458 lastDividendProtectionTimeIndex;
459 dividendPassThroughData_[index].accruedHistoricalDividends = 0.0;
460 }
461 lastDividendProtectionTimeIndex = index;
462 }
463 }
464 }
465
466 if (event.second.vd != nullptr) {
467
468 // voluntary conversion data (possibly with coco)
469
470 const ConvertibleBond2::ConversionData& vd = *event.second.vd;
471
472 Date nextConvDate = nextConversionDate(vd.exerciseDate);
473 if (nextConvDate > today_) {
474 bool conversionIsProhibited = false;
475 Size indexStart = grid_.index(time(std::max(vd.exerciseDate, today_)));
476 Size indexEnd;
477 associatedDate_[indexStart] = std::max(vd.exerciseDate, today_);
479 indexEnd = indexStart;
481 QL_REQUIRE(nextConvDate != Null<Date>(),
482 "FdConvertibleBondEvents::processConversionData(): internal error: did not find a next "
483 "conversion "
484 "date after "
485 << vd.exerciseDate
486 << ", the last conversione date should not have exercise type FromThisDateOn");
487 indexEnd = grid_.index(time(nextConvDate));
488 // check whether conversion is prohibited for the future due to past coco condition check
490 vd.exerciseDate <= today_) {
491 Real S = equity_->fixing(equity_->fixingCalendar().adjust(vd.exerciseDate, Preceding));
492 conversionIsProhibited = S * currentCr <= vd.cocoBarrier;
493 additionalResults_["historicEvents.coco_" + getDateStr(vd.exerciseDate) + "_S"] = S;
494 additionalResults_["historicEvents.coco_" + getDateStr(vd.exerciseDate) + "_currentCr"] =
495 currentCr;
496 additionalResults_["historicEvents.coco_" + getDateStr(vd.exerciseDate) + "_cocoBarrier"] =
497 vd.cocoBarrier;
498 additionalResults_["historicEvents.coco_" + getDateStr(vd.exerciseDate) + "_triggered"] =
499 !conversionIsProhibited;
500 }
501 } else {
502 QL_FAIL("FdConvertibleBondEvents: internal error, exercise type not recognized");
503 }
504 // update the grid info
505 for (Size i = indexStart; i <= indexEnd; ++i) {
506 // if we have set conversion information already and the current is the last conversion
507 // date, we do not modify the exisiting information, because then the current date is the
508 // end date of the last american period.
509 if (nextConvDate == Null<Date>() && hasConversionInfoSet_[i])
510 continue;
511 // otherwise mark the index as set
512 hasConversionInfoSet_[i] = true;
513 // if the conversion is not allowed, do not set the hasConversion flag
514 if (conversionIsProhibited)
515 continue;
516 // if the conversion is allowed, set the conversion flag ...
517 hasConversion_[i] = true;
518 // ... and the conversion data
520 // a start of period coco check should only be done, if the date is > today, otherwise it's
521 // done already
523 hasContingentConversion_[i] = true;
524 } else if (vd.exerciseDate > today_ &&
526 hasContingentConversion_[i] = true;
527 hasNoConversionPlane_[i] = i > indexStart;
528 }
529 }
530 }
531 }
532
533 if (event.second.newCr != Null<Real>()) {
534 // update current cr
535 currentCr = event.second.newCr;
536 if (event.first >= today_) {
537 associatedDate_[grid_.index(time(event.first))] = event.first;
538 if (haveStochasticCr) {
539 // we know event.first >= today if haveStochasticCr is true
540 Size index = grid_.index(time(event.first));
541 hasConversionReset_[index] = true;
542 conversionResetData_[index].resetToSpecificValue = true;
543 conversionResetData_[index].newCr = currentCr;
544 }
545 }
546 }
547
548 // update current cr on grid
549
550 for (Size i = grid_.index(time(std::max(event.first, today_))); i < grid_.size(); ++i) {
551 currentConversionRatio_[i] = currentCr;
552 }
553
554 } // for adjEvents
555
556} // processConversionData()
557
559 for (auto const& d : registeredMandatoryConversionData_) {
560 if (d.exerciseDate <= today_)
561 continue;
562 Size index = grid_.index(time(d.exerciseDate));
563 associatedDate_[index] = d.exerciseDate;
564 hasMandatoryConversion_[index] = true;
565 mandatoryConversionData_[index] = {d.pepsUpperBarrier * currentFxConversion_[index],
566 d.pepsLowerBarrier * currentFxConversion_[index], d.pepsUpperConversionRatio,
567 d.pepsLowerConversionRatio};
568 }
569}
570
571void FdConvertibleBondEvents::finalise(const TimeGrid& grid) {
572 QL_REQUIRE(!finalised_, "FdConvertibleBondEvents: internal error, events already finalised");
573 finalised_ = true;
574 grid_ = grid;
575
576 hasBondCashflow_.resize(grid.size(), false);
577 hasCall_.resize(grid.size(), false);
578 hasPut_.resize(grid.size(), false);
579 hasConversion_.resize(grid.size(), false);
580 hasMandatoryConversion_.resize(grid.size(), false);
581 hasContingentConversion_.resize(grid.size(), false);
582 hasConversionInfoSet_.resize(grid.size(), false);
583 hasNoConversionPlane_.resize(grid.size(), false);
584 hasConversionReset_.resize(grid.size(), false);
585 hasDividendPassThrough_.resize(grid.size(), false);
586
587 bondCashflow_.resize(grid.size(), 0.0);
588 bondFinalRedemption_.resize(grid.size(), 0.0);
589 callData_.resize(grid.size());
590 putData_.resize(grid.size());
591 conversionData_.resize(grid.size());
592 conversionResetData_.resize(grid.size());
593 dividendPassThroughData_.resize(grid.size());
594 mandatoryConversionData_.resize(grid.size());
595
596 stochasticConversionRatio_.resize(grid.size(), false);
597 currentConversionRatio_.resize(grid.size(), 0.0);
598 currentFxConversion_.resize(grid.size(), 1.0);
599 associatedDate_.resize(grid.size(), Null<Date>());
600
602
603 // fill fx conversion rate on grid
604
605 if (fxConversion_ != nullptr) {
606 auto source = fxConversion_->sourceCurve();
607 auto target = fxConversion_->targetCurve();
608 Real spot = fxConversion_->fixing(today_);
609 for (Size i = 0; i < grid_.size(); ++i) {
610 Real fx = spot * source->discount(grid_[i]) / target->discount(grid_[i]);
611 currentFxConversion_[i] = fx;
612 }
613 }
614
615 // process data
616
623
624 // checks
625
626 Size lastRedemptionIndex = grid_.index(time(lastRedemptionDate_));
627 for (Size k = lastRedemptionIndex + 1; k < grid_.size(); ++k) {
628 QL_REQUIRE(!hasConversion(k) && !hasMandatoryConversion(k),
629 "FdConvertibleBondEvents: conversion right after last bond redemption flow not allowed");
630 }
631}
632
633bool FdConvertibleBondEvents::hasBondCashflow(const Size i) const { return hasBondCashflow_.at(i); }
634bool FdConvertibleBondEvents::hasCall(const Size i) const { return hasCall_.at(i); }
635bool FdConvertibleBondEvents::hasPut(const Size i) const { return hasPut_.at(i); }
636bool FdConvertibleBondEvents::hasConversion(const Size i) const { return hasConversion_.at(i); }
640bool FdConvertibleBondEvents::hasConversionReset(const Size i) const { return hasConversionReset_.at(i); }
642
643Real FdConvertibleBondEvents::getBondCashflow(const Size i) const { return bondCashflow_.at(i); }
645
647 return callData_.at(i);
648}
649
651 return putData_.at(i);
652}
653
655 return conversionData_.at(i);
656}
657
660 return mandatoryConversionData_.at(i);
661}
662
665 return conversionResetData_.at(i);
666}
667
670 return dividendPassThroughData_.at(i);
671}
672
674 return stochasticConversionRatio_.at(i);
675}
676
680Date FdConvertibleBondEvents::getAssociatedDate(const Size i) const { return associatedDate_.at(i); }
681
682} // namespace QuantExt
bool hasDividendPassThrough(const Size i) const
std::vector< ConvertibleBond2::CallabilityData > registeredCallData_
bool hasMandatoryConversion(const Size i) const
bool hasContingentConversion(const Size i) const
const std::set< Real > & times() const
Date nextConversionDate(const Date &d) const
void registerMandatoryConversion(const ConvertibleBond2::MandatoryConversionData &c)
void registerBondCashflow(const QuantLib::ext::shared_ptr< CashFlow > &c)
Date nextExerciseDate(const Date &d, const std::vector< ConvertibleBond2::CallabilityData > &data) const
QuantLib::ext::shared_ptr< FxIndex > fxConversion_
void registerConversionReset(const ConvertibleBond2::ConversionResetData &c)
std::vector< QuantLib::ext::shared_ptr< CashFlow > > registeredBondCashflows_
std::vector< ConvertibleBond2::MandatoryConversionData > registeredMandatoryConversionData_
std::map< std::string, boost::any > additionalResults_
std::vector< ConvertibleBond2::ConversionRatioData > registeredConversionRatioData_
std::vector< DividendPassThroughData > dividendPassThroughData_
const ConversionResetData & getConversionResetData(const Size i) const
std::vector< ConversionData > conversionData_
void registerPut(const ConvertibleBond2::CallabilityData &c)
Real getCurrentFxConversion(const Size i) const
void registerConversion(const ConvertibleBond2::ConversionData &c)
const MandatoryConversionData & getMandatoryConversionData(const Size i) const
void registerConversionRatio(const ConvertibleBond2::ConversionRatioData &c)
FdConvertibleBondEvents(const Date &today, const DayCounter &dc, const Real N0, const QuantLib::ext::shared_ptr< QuantExt::EquityIndex2 > &equity, const QuantLib::ext::shared_ptr< FxIndex > &fxConversion)
std::vector< ConvertibleBond2::ConversionData > registeredConversionData_
std::vector< ConvertibleBond2::CallabilityData > registeredPutData_
std::vector< MandatoryConversionData > mandatoryConversionData_
void registerCall(const ConvertibleBond2::CallabilityData &c)
std::vector< ConversionResetData > conversionResetData_
QuantLib::ext::shared_ptr< QuantExt::EquityIndex2 > equity_
void processExerciseData(const std::vector< ConvertibleBond2::CallabilityData > &sourceData, std::vector< bool > &targetFlags, std::vector< CallData > &targetData)
Real getCurrentConversionRatio(const Size i) const
std::vector< ConvertibleBond2::ConversionResetData > registeredConversionResetData_
const ConversionData & getConversionData(const Size i) const
std::vector< ConvertibleBond2::DividendProtectionData > registeredDividendProtectionData_
const DividendPassThroughData & getDividendPassThroughData(const Size i) const
const CallData & getCallData(const Size i) const
ConvertibleBond2::MakeWholeData registeredMakeWholeData_
Real getBondFinalRedemption(const Size i) const
const CallData & getPutData(const Size i) const
bool hasStochasticConversionRatio(const Size i) const
void registerDividendProtection(const ConvertibleBond2::DividendProtectionData &c)
void registerMakeWhole(const ConvertibleBond2::MakeWholeData &c)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
boost::optional< CrIncreaseData > crIncreaseData