TODO removeNoticeDatesAfterLastAccrualStart is only there for backwards compatibility, we should remove it at some point and handle such notice dates in the callling code as appropriate. If the exercise style is American the flag is set to false always internally.
141 {
142
143
144
145 if (optionData.style() == "American")
146 removeNoticeDatesAfterLastAccrualStart = false;
147
148
149
150
151
152
153 Date lastAccrualStartDate = Date::minDate();
154 for (auto const& l : legs) {
155 for (auto const& c : l) {
156 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(c))
157 lastAccrualStartDate = std::max(lastAccrualStartDate, cpn->accrualStartDate());
158 }
159 }
160
161
162
163 Period noticePeriod = optionData.noticePeriod().empty() ? 0 * Days :
parsePeriod(optionData.noticePeriod());
164 Calendar noticeCal =
165 optionData.noticeCalendar().empty() ? NullCalendar() :
parseCalendar(optionData.noticeCalendar());
166 BusinessDayConvention noticeBdc =
168
169
170
171 std::vector<QuantLib::Date> sortedExerciseDates;
172 if (optionData.exerciseDatesSchedule().hasData()) {
173 Schedule schedule =
makeSchedule(optionData.exerciseDatesSchedule());
174 sortedExerciseDates = schedule.dates();
175 } else {
176
177 for (auto const& d : optionData.exerciseDates())
178 sortedExerciseDates.push_back(
parseDate(d));
179 }
180 std::sort(sortedExerciseDates.begin(), sortedExerciseDates.end());
181
182
183
184 QL_REQUIRE(optionData.style() != "American" || sortedExerciseDates.size() == 2,
185 "ExerciseBuilder: expected 2 exercise dates for style 'American', got " << sortedExerciseDates.size());
186
187
188
189 std::vector<bool> isExerciseDateAlive(sortedExerciseDates.size(), false);
190
191 Date today = Settings::instance().evaluationDate();
192
193 for (Size i = 0; i < sortedExerciseDates.size(); i++) {
194 Date noticeDate = noticeCal.advance(sortedExerciseDates[i], -noticePeriod, noticeBdc);
195
196 if (optionData.style() == "American" && i == 0) {
197 noticeDate = std::max(today + 1, noticeDate);
198 sortedExerciseDates[0] = std::max(today + 1, sortedExerciseDates[0]);
199 }
200 if (noticeDate > today && (noticeDate <= lastAccrualStartDate || !removeNoticeDatesAfterLastAccrualStart)) {
201 isExerciseDateAlive[i] = true;
204 DLOG(
"Got notice date " << QuantLib::io::iso_date(noticeDate) <<
" using notice period " << noticePeriod
205 << ", convention " << noticeBdc << ", calendar " << noticeCal.name()
207 }
208 if (noticeDate > lastAccrualStartDate && removeNoticeDatesAfterLastAccrualStart)
210 << sortedExerciseDates[i] << ") after last accrual start date "
211 << ore ::data::to_string(lastAccrualStartDate));
212 }
213
214
215
217 if (optionData.style() == "European") {
218 QL_REQUIRE(
exerciseDates_.size() == 1,
"Got 'European' option style, but "
220 << " exercise dates. Should the style be 'Bermudan'?");
222 } else if (optionData.style() == "Bermudan" || optionData.style().empty()) {
223
225 } else if (optionData.style() == "American") {
226 QL_REQUIRE(
noticeDates_.size() == 2,
"ExerciseBuilder: internal error, style is american but got "
227 <<
noticeDates_.size() <<
" notice dates, expected 2.");
229 optionData.payoffAtExpiry());
230 } else {
231 QL_FAIL("ExerciseBuilder: style '"
232 << optionData.style() << "' not recognized. Expected one of 'European', 'Bermudan', 'American'");
233 }
234 }
235
236
237
238 if (optionData.exerciseData()) {
239 Date d = optionData.exerciseData()->date();
240 Real p = optionData.exerciseData()->price();
241 auto nextDate = std::lower_bound(sortedExerciseDates.begin(), sortedExerciseDates.end(), d);
242 if (nextDate != sortedExerciseDates.end()) {
245
246 exerciseDate_ = optionData.style() ==
"American" ? d : *nextDate;
248 if (optionData.settlement() == "Cash") {
249 Date cashSettlementDate = d;
250 if (optionData.paymentData()) {
251 if (optionData.paymentData()->rulesBased()) {
252 cashSettlementDate = optionData.paymentData()->calendar().advance(
253 d, optionData.paymentData()->lag(), Days, optionData.paymentData()->convention());
254 } else {
255 auto const& dates = optionData.paymentData()->dates();
256 auto nextDate = std::lower_bound(dates.begin(), dates.end(), d);
257 if (nextDate != dates.end())
258 cashSettlementDate = *nextDate;
259 }
260 }
261 if (p != Null<Real>())
262 cashSettlement_ = QuantLib::ext::make_shared<QuantLib::SimpleCashFlow>(p, cashSettlementDate);
263 DLOG(
"Option is cash settled, amount " << p <<
" paid on " << cashSettlementDate);
264 }
265 }
266 }
267
268
269
270 if (!optionData.exerciseFees().empty()) {
271
272 QL_REQUIRE(optionData.style() != "American" || optionData.exerciseFees().size() == 1,
273 "ExerciseBuilder: for style 'American' at most one exercise fee is allowed");
274
275
276
277 std::vector<Date> exDatesPlusInf(sortedExerciseDates);
278 exDatesPlusInf.push_back(Date::maxDate());
280 optionData.exerciseFeeDates(), exDatesPlusInf, 0.0);
281
282
283
284 for (auto& r : allRebates)
285 r = -r;
286
287 vector<string> feeType = buildScheduledVectorNormalised<string>(
288 optionData.exerciseFeeTypes(), optionData.exerciseFeeDates(), exDatesPlusInf, "");
289
290
291
292 for (Size i = 0; i < allRebates.size(); ++i) {
293
294
295
296 if (feeType[i].empty())
297 feeType[i] = "Absolute";
298
299 if (feeType[i] == "Percentage") {
300
301
302
303 std::set<std::pair<Date, Real>> notionals;
304 for (auto const& l : legs) {
305 for (auto const& c : l) {
306 if (auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(c)) {
307 if (cpn->accrualStartDate() >= sortedExerciseDates[i])
308 notionals.insert(std::make_pair(cpn->accrualStartDate(), cpn->nominal()));
309 }
310 }
311 }
312
313 if (notionals.empty())
314 allRebates[i] = 0.0;
315 else {
316 Real feeNotional = notionals.begin()->second;
317 DLOG(
"Convert percentage rebate "
318 << allRebates[i] << " to absolute rebate " << allRebates[i] * feeNotional << " using nominal "
319 << feeNotional << " for exercise date " << QuantLib::io::iso_date(sortedExerciseDates[i]));
320 allRebates[i] *= feeNotional;
321 }
322
323 } else {
324 QL_REQUIRE(feeType[i] == "Absolute", "fee type must be Absolute or Relative");
325 }
326 }
327
328
329
330 Period feeSettlPeriod = optionData.exerciseFeeSettlementPeriod().empty()
331 ? 0 * Days
332 :
parsePeriod(optionData.exerciseFeeSettlementPeriod());
333
334 Calendar feeSettlCal = optionData.exerciseFeeSettlementCalendar().empty()
335 ? NullCalendar()
337
338 BusinessDayConvention feeSettlBdc =
339 optionData.exerciseFeeSettlementConvention().empty()
340 ? Unadjusted
342
343
344
346 feeSettlement_ = QuantLib::ext::make_shared<QuantLib::SimpleCashFlow>(
348 DLOG(
"Settlement fee for exercised option is " <<
feeSettlement_->amount() <<
" paid on "
350 }
351
352
353
355 vector<double> rebates;
356 for (Size i = 0; i < sortedExerciseDates.size(); ++i) {
357 if (isExerciseDateAlive[i])
358 rebates.push_back(allRebates[i]);
359 }
360 if (optionData.style() == "American") {
361
362 exercise_ = QuantLib::ext::make_shared<QuantExt::RebatedExercise>(*
exercise_, rebates.front(), feeSettlPeriod,
363 feeSettlCal, feeSettlBdc);
364 auto dbgEx = QuantLib::ext::static_pointer_cast<QuantExt::RebatedExercise>(
exercise_);
365 DLOG(
"Got rebate " << dbgEx->rebate(0) <<
" for American exercise with fee settle period "
366 << feeSettlPeriod << ", cal " << feeSettlCal << ", bdc " << feeSettlBdc);
367 } else {
369 feeSettlPeriod, feeSettlCal, feeSettlBdc);
370 auto dbgEx = QuantLib::ext::static_pointer_cast<QuantExt::RebatedExercise>(
exercise_);
372 DLOG(
"Got rebate " << dbgEx->rebate(i) <<
" with payment date "
373 << QuantLib::io::iso_date(dbgEx->rebatePaymentDate(i))
374 <<
" (exercise date=" << QuantLib::io::iso_date(
exerciseDates_[i])
375 << ") using rebate settl period " << feeSettlPeriod << ", calendar "
376 << feeSettlCal << ", convention " << feeSettlBdc);
377 }
378 }
379 }
380 }
381}
QuantLib::Date exerciseDate_
QuantLib::ext::shared_ptr< QuantLib::Exercise > exercise_
std::vector< QuantLib::Date > noticeDates_
QuantLib::ext::shared_ptr< QuantLib::CashFlow > feeSettlement_
QuantLib::ext::shared_ptr< QuantLib::CashFlow > cashSettlement_
std::vector< QuantLib::Date > exerciseDates_
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
BusinessDayConvention parseBusinessDayConvention(const string &s)
Convert text to QuantLib::BusinessDayConvention.
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
#define DLOG(text)
Logging Macro (Level = Debug)
#define WLOG(text)
Logging Macro (Level = Warning)
vector< T > buildScheduledVectorNormalised(const vector< T > &values, const vector< string > &dates, const Schedule &schedule, const T &defaultValue, const bool checkAllValuesAppearInResult=false)
std::string to_string(const LocationInfo &l)
Schedule makeSchedule(const ScheduleDates &data)