Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Public Member Functions | Private Attributes | List of all members
ExerciseBuilder Class Reference

#include <ored/portfolio/optiondata.hpp>

+ Collaboration diagram for ExerciseBuilder:

Public Member Functions

 ExerciseBuilder (const OptionData &optionData, const std::vector< QuantLib::Leg > legs, bool removeNoticeDatesAfterLastAccrualStart=true)
 
QuantLib::ext::shared_ptr< QuantLib::Exercise > exercise () const
 
const std::vector< QuantLib::Date > & exerciseDates () const
 
const std::vector< QuantLib::Date > & noticeDates () const
 
bool isExercised () const
 
const QuantLib::Date & exerciseDate () const
 
QuantLib::ext::shared_ptr< QuantLib::CashFlow > cashSettlement () const
 
QuantLib::ext::shared_ptr< QuantLib::CashFlow > feeSettlement () const
 

Private Attributes

QuantLib::ext::shared_ptr< QuantLib::Exercise > exercise_
 
std::vector< QuantLib::Date > exerciseDates_
 
std::vector< QuantLib::Date > noticeDates_
 
bool isExercised_ = false
 
QuantLib::Date exerciseDate_
 
QuantLib::ext::shared_ptr< QuantLib::CashFlow > cashSettlement_
 
QuantLib::ext::shared_ptr< QuantLib::CashFlow > feeSettlement_
 
Size exerciseDateIndex_ = QuantLib::Null<QuantLib::Size>()
 

Detailed Description

Definition at line 148 of file optiondata.hpp.

Constructor & Destructor Documentation

◆ ExerciseBuilder()

ExerciseBuilder ( const OptionData optionData,
const std::vector< QuantLib::Leg >  legs,
bool  removeNoticeDatesAfterLastAccrualStart = true 
)

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.

Definition at line 140 of file optiondata.cpp.

141 {
142
143 // for american style exercise, never remove notice dates after last accrual start
144
145 if (optionData.style() == "American")
146 removeNoticeDatesAfterLastAccrualStart = false;
147
148 // only keep a) future exercise dates and b) exercise dates that exercise into a whole
149 // accrual period of the underlying; TODO handle exercises into broken periods?
150
151 // determine last accrual start date present in the underlying legs
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 // get notice period, calendar, bdc
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 =
167 optionData.noticeConvention().empty() ? Unadjusted : parseBusinessDayConvention(optionData.noticeConvention());
168
169 // build vector of sorted exercise dates
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 // For backward compatibility
177 for (auto const& d : optionData.exerciseDates())
178 sortedExerciseDates.push_back(parseDate(d));
179 }
180 std::sort(sortedExerciseDates.begin(), sortedExerciseDates.end());
181
182 // check that we have exactly two exercise dates for american style
183
184 QL_REQUIRE(optionData.style() != "American" || sortedExerciseDates.size() == 2,
185 "ExerciseBuilder: expected 2 exercise dates for style 'American', got " << sortedExerciseDates.size());
186
187 // build vector of alive exercise dates and corresponding notice dates
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 // keep two alive notice dates always for american style exercise
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;
202 noticeDates_.push_back(noticeDate);
203 exerciseDates_.push_back(sortedExerciseDates[i]);
204 DLOG("Got notice date " << QuantLib::io::iso_date(noticeDate) << " using notice period " << noticePeriod
205 << ", convention " << noticeBdc << ", calendar " << noticeCal.name()
206 << " from exercise date " << exerciseDates_.back());
207 }
208 if (noticeDate > lastAccrualStartDate && removeNoticeDatesAfterLastAccrualStart)
209 WLOG("Remove notice date " << ore::data::to_string(noticeDate) << " (exercise date "
210 << sortedExerciseDates[i] << ") after last accrual start date "
211 << ore ::data::to_string(lastAccrualStartDate));
212 }
213
214 // build exercise instance if we have alive notice dates
215
216 if (!noticeDates_.empty()) {
217 if (optionData.style() == "European") {
218 QL_REQUIRE(exerciseDates_.size() == 1, "Got 'European' option style, but "
219 << exerciseDates_.size()
220 << " exercise dates. Should the style be 'Bermudan'?");
221 exercise_ = QuantLib::ext::make_shared<EuropeanExercise>(noticeDates_.back());
222 } else if (optionData.style() == "Bermudan" || optionData.style().empty()) {
223 // Note: empty exercise style defaults to Bermudan for backwards compatibility
224 exercise_ = QuantLib::ext::make_shared<BermudanExercise>(noticeDates_);
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.");
228 exercise_ = QuantLib::ext::make_shared<AmericanExercise>(noticeDates_.front(), noticeDates_.back(),
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 // check if the exercise right was executed and if so set cash settlement amount
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()) {
243 isExercised_ = true;
244 exerciseDateIndex_ = std::distance(sortedExerciseDates.begin(), nextDate);
245 // Note: we set the exercise date to the notification date here
246 exerciseDate_ = optionData.style() == "American" ? d : *nextDate;
247 DLOG("Option is exercised, exercise date = " << exerciseDate_);
248 if (optionData.settlement() == "Cash") {
249 Date cashSettlementDate = d; // default to exercise date
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 // build fee and rebated exercise instance, if any fees are present
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 // build an exercise date "schedule" by adding the maximum possible date at the end
276
277 std::vector<Date> exDatesPlusInf(sortedExerciseDates);
278 exDatesPlusInf.push_back(Date::maxDate());
279 vector<double> allRebates = buildScheduledVectorNormalised(optionData.exerciseFees(),
280 optionData.exerciseFeeDates(), exDatesPlusInf, 0.0);
281
282 // flip the sign of the fee to get a rebate
283
284 for (auto& r : allRebates)
285 r = -r;
286
287 vector<string> feeType = buildScheduledVectorNormalised<string>(
288 optionData.exerciseFeeTypes(), optionData.exerciseFeeDates(), exDatesPlusInf, "");
289
290 // convert relative to absolute fees if required
291
292 for (Size i = 0; i < allRebates.size(); ++i) {
293
294 // default to Absolute
295
296 if (feeType[i].empty())
297 feeType[i] = "Absolute";
298
299 if (feeType[i] == "Percentage") {
300
301 // get next coupon after exercise to determine relevant notional
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; // notional is zero
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; // multiply percentage fee by relevant notional
321 }
322
323 } else {
324 QL_REQUIRE(feeType[i] == "Absolute", "fee type must be Absolute or Relative");
325 }
326 }
327
328 // set fee settlement conventions
329
330 Period feeSettlPeriod = optionData.exerciseFeeSettlementPeriod().empty()
331 ? 0 * Days
332 : parsePeriod(optionData.exerciseFeeSettlementPeriod());
333
334 Calendar feeSettlCal = optionData.exerciseFeeSettlementCalendar().empty()
335 ? NullCalendar()
336 : parseCalendar(optionData.exerciseFeeSettlementCalendar());
337
338 BusinessDayConvention feeSettlBdc =
339 optionData.exerciseFeeSettlementConvention().empty()
340 ? Unadjusted
341 : parseBusinessDayConvention(optionData.exerciseFeeSettlementConvention());
342
343 // set fee settlement amount if option is exercised
344
345 if (isExercised_) {
346 feeSettlement_ = QuantLib::ext::make_shared<QuantLib::SimpleCashFlow>(
347 -allRebates[exerciseDateIndex_], feeSettlCal.advance(exerciseDate_, feeSettlPeriod, feeSettlBdc));
348 DLOG("Settlement fee for exercised option is " << feeSettlement_->amount() << " paid on "
349 << feeSettlement_->date() << ".");
350 }
351
352 // update exercise instance with rebate information
353
354 if (exercise_ != nullptr) {
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 // Note: we compute the settl date relative to notification, not exercise here
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 {
368 exercise_ = QuantLib::ext::make_shared<QuantExt::RebatedExercise>(*exercise_, exerciseDates_, rebates,
369 feeSettlPeriod, feeSettlCal, feeSettlBdc);
370 auto dbgEx = QuantLib::ext::static_pointer_cast<QuantExt::RebatedExercise>(exercise_);
371 for (Size i = 0; i < exerciseDates_.size(); ++i) {
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 } // if exercise fees are given
381} // ExerciseBuilder()
QuantLib::Date exerciseDate_
Definition: optiondata.hpp:179
QuantLib::ext::shared_ptr< QuantLib::Exercise > exercise_
Definition: optiondata.hpp:173
std::vector< QuantLib::Date > noticeDates_
Definition: optiondata.hpp:176
QuantLib::ext::shared_ptr< QuantLib::CashFlow > feeSettlement_
Definition: optiondata.hpp:181
QuantLib::ext::shared_ptr< QuantLib::CashFlow > cashSettlement_
Definition: optiondata.hpp:180
std::vector< QuantLib::Date > exerciseDates_
Definition: optiondata.hpp:175
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Definition: parsers.cpp:157
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Definition: parsers.cpp:51
BusinessDayConvention parseBusinessDayConvention(const string &s)
Convert text to QuantLib::BusinessDayConvention.
Definition: parsers.cpp:173
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
Definition: parsers.cpp:171
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define WLOG(text)
Logging Macro (Level = Warning)
Definition: log.hpp:550
vector< T > buildScheduledVectorNormalised(const vector< T > &values, const vector< string > &dates, const Schedule &schedule, const T &defaultValue, const bool checkAllValuesAppearInResult=false)
Definition: legdata.hpp:1139
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
Schedule makeSchedule(const ScheduleDates &data)
Definition: schedule.cpp:263
+ Here is the call graph for this function:

Member Function Documentation

◆ exercise()

QuantLib::ext::shared_ptr< QuantLib::Exercise > exercise ( ) const

Definition at line 157 of file optiondata.hpp.

157{ return exercise_; }
+ Here is the caller graph for this function:

◆ exerciseDates()

const std::vector< QuantLib::Date > & exerciseDates ( ) const

Definition at line 159 of file optiondata.hpp.

159{ return exerciseDates_; }

◆ noticeDates()

const std::vector< QuantLib::Date > & noticeDates ( ) const

Definition at line 161 of file optiondata.hpp.

161{ return noticeDates_; }

◆ isExercised()

bool isExercised ( ) const

Definition at line 164 of file optiondata.hpp.

164{ return isExercised_; }

◆ exerciseDate()

const QuantLib::Date & exerciseDate ( ) const

Definition at line 166 of file optiondata.hpp.

166{ return exerciseDate_; }

◆ cashSettlement()

QuantLib::ext::shared_ptr< QuantLib::CashFlow > cashSettlement ( ) const

Definition at line 168 of file optiondata.hpp.

168{ return cashSettlement_; }

◆ feeSettlement()

QuantLib::ext::shared_ptr< QuantLib::CashFlow > feeSettlement ( ) const

Definition at line 170 of file optiondata.hpp.

170{ return feeSettlement_; }

Member Data Documentation

◆ exercise_

QuantLib::ext::shared_ptr<QuantLib::Exercise> exercise_
private

Definition at line 173 of file optiondata.hpp.

◆ exerciseDates_

std::vector<QuantLib::Date> exerciseDates_
private

Definition at line 175 of file optiondata.hpp.

◆ noticeDates_

std::vector<QuantLib::Date> noticeDates_
private

Definition at line 176 of file optiondata.hpp.

◆ isExercised_

bool isExercised_ = false
private

Definition at line 178 of file optiondata.hpp.

◆ exerciseDate_

QuantLib::Date exerciseDate_
private

Definition at line 179 of file optiondata.hpp.

◆ cashSettlement_

QuantLib::ext::shared_ptr<QuantLib::CashFlow> cashSettlement_
private

Definition at line 180 of file optiondata.hpp.

◆ feeSettlement_

QuantLib::ext::shared_ptr<QuantLib::CashFlow> feeSettlement_
private

Definition at line 181 of file optiondata.hpp.

◆ exerciseDateIndex_

Size exerciseDateIndex_ = QuantLib::Null<QuantLib::Size>()
private

Definition at line 184 of file optiondata.hpp.