48 const Envelope& envelope,
const OptionData& optionData, Real quantity, Real strike,
const string& currency,
50 const string& paymentCalendar,
const string& paymentLag,
const string& paymentConvention,
51 const string& pricingCalendar,
const string& paymentDate, Real gearing, Spread spread,
53 QuantLib::Natural futureMonthOffset, QuantLib::Natural deliveryRollDays,
bool includePeriodEnd,
54 const BarrierData& barrierData,
const std::string& fxIndex)
55 : Trade(
"CommodityAveragePriceOption", envelope), optionData_(optionData), barrierData_(barrierData),
56 quantity_(quantity), strike_(strike), currency_(currency), name_(
name), priceType_(priceType),
57 startDate_(startDate), endDate_(endDate), paymentCalendar_(paymentCalendar), paymentLag_(paymentLag),
58 paymentConvention_(paymentConvention), pricingCalendar_(pricingCalendar), paymentDate_(paymentDate),
59 gearing_(gearing),
spread_(spread), commodityQuantityFrequency_(commodityQuantityFrequency),
60 commodityPayRelativeTo_(commodityPayRelativeTo), futureMonthOffset_(futureMonthOffset),
61 deliveryRollDays_(deliveryRollDays), includePeriodEnd_(includePeriodEnd), fxIndex_(fxIndex), allAveraging_(false) {}
63void CommodityAveragePriceOption::build(
const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
68 DLOG(
"CommodityAveragePriceOption::build() called for trade " <<
id());
71 additionalData_[
"isdaAssetClass"] = std::string(
"Commodity");
72 additionalData_[
"isdaBaseProduct"] = std::string(
"Option");
73 additionalData_[
"isdaSubProduct"] = std::string(
"Price Return Basic Performance");
75 additionalData_[
"isdaTransaction"] = std::string();
77 QL_REQUIRE(gearing_ > 0.0,
"Gearing (" << gearing_ <<
") should be positive.");
78 QL_REQUIRE(
spread_ < strike_ || QuantLib::close_enough(
spread_, strike_),
79 "Spread (" <<
spread_ <<
") should be less than strike (" << strike_ <<
").");
82 additionalData_[
"strike"] = strike_;
83 additionalData_[
"strikeCurrency"] = currency_;
87 notionalCurrency_ = npvCurrency_ = currency_;
92 if (!optionData_.exerciseDates().empty()) {
93 QL_REQUIRE(optionData_.exerciseDates().size() == 1,
"Commodity average price option must be European");
94 exDate =
parseDate(optionData_.exerciseDates().front());
98 QuantLib::ext::shared_ptr<EngineBuilder> builder = engineFactory->builder(
99 barrierData_.initialized() ?
"CommodityAveragePriceBarrierOption" :
"CommodityAveragePriceOption");
100 string configuration = builder->configuration(MarketContext::pricing);
104 Leg leg = buildLeg(engineFactory, configuration);
108 buildStandardOption(engineFactory, leg, exDate);
110 buildApo(engineFactory, leg, exDate, builder);
114 legs_.push_back(leg);
115 legPayers_.push_back(
false);
116 legCurrencies_.push_back(currency_);
119std::map<AssetClass, std::set<std::string>> CommodityAveragePriceOption::underlyingIndices(
120 const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceDataManager)
const {
121 return {{AssetClass::COM, std::set<std::string>({name_})}};
124void CommodityAveragePriceOption::fromXML(
XMLNode* node) {
126 Trade::fromXML(node);
128 XMLNode* apoNode = XMLUtils::getChildNode(node,
"CommodityAveragePriceOptionData");
129 QL_REQUIRE(apoNode,
"No CommodityAveragePriceOptionData Node");
131 optionData_.fromXML(XMLUtils::getChildNode(apoNode,
"OptionData"));
132 if(
auto b = XMLUtils::getChildNode(apoNode,
"BarrierData")) {
133 barrierData_.fromXML(b);
135 name_ = XMLUtils::getChildValue(apoNode,
"Name",
true);
136 currency_ = XMLUtils::getChildValue(apoNode,
"Currency",
true);
137 quantity_ = XMLUtils::getChildValueAsDouble(apoNode,
"Quantity",
true);
138 strike_ = XMLUtils::getChildValueAsDouble(apoNode,
"Strike",
true);
140 startDate_ = XMLUtils::getChildValue(apoNode,
"StartDate",
true);
141 endDate_ = XMLUtils::getChildValue(apoNode,
"EndDate",
true);
142 paymentCalendar_ = XMLUtils::getChildValue(apoNode,
"PaymentCalendar",
true);
143 paymentLag_ = XMLUtils::getChildValue(apoNode,
"PaymentLag",
true);
144 paymentConvention_ = XMLUtils::getChildValue(apoNode,
"PaymentConvention",
true);
145 pricingCalendar_ = XMLUtils::getChildValue(apoNode,
"PricingCalendar",
true);
147 paymentDate_ = XMLUtils::getChildValue(apoNode,
"PaymentDate",
false);
150 if (
XMLNode* n = XMLUtils::getChildNode(apoNode,
"Gearing")) {
151 gearing_ =
parseReal(XMLUtils::getNodeValue(n));
154 spread_ = XMLUtils::getChildValueAsDouble(apoNode,
"Spread",
false);
156 commodityQuantityFrequency_ = CommodityQuantityFrequency::PerCalculationPeriod;
157 if (
XMLNode* n = XMLUtils::getChildNode(apoNode,
"CommodityQuantityFrequency")) {
161 commodityPayRelativeTo_ = CommodityPayRelativeTo::CalculationPeriodEndDate;
162 if (
XMLNode* n = XMLUtils::getChildNode(apoNode,
"CommodityPayRelativeTo")) {
166 futureMonthOffset_ = XMLUtils::getChildValueAsInt(apoNode,
"FutureMonthOffset",
false);
167 deliveryRollDays_ = XMLUtils::getChildValueAsInt(apoNode,
"DeliveryRollDays",
false);
169 includePeriodEnd_ =
true;
170 if (
XMLNode* n = XMLUtils::getChildNode(apoNode,
"IncludePeriodEnd")) {
171 includePeriodEnd_ =
parseBool(XMLUtils::getNodeValue(n));
174 if (
XMLNode* n = XMLUtils::getChildNode(apoNode,
"FXIndex")){
175 fxIndex_ = XMLUtils::getNodeValue(n);
181 XMLNode* node = Trade::toXML(doc);
184 XMLUtils::appendNode(node, apoNode);
186 XMLUtils::appendNode(apoNode, optionData_.toXML(doc));
187 if (barrierData_.initialized())
188 XMLUtils::appendNode(apoNode, barrierData_.toXML(doc));
189 XMLUtils::addChild(doc, apoNode,
"Name", name_);
190 XMLUtils::addChild(doc, apoNode,
"Currency", currency_);
191 XMLUtils::addChild(doc, apoNode,
"Quantity",
quantity_);
192 XMLUtils::addChild(doc, apoNode,
"Strike", strike_);
193 XMLUtils::addChild(doc, apoNode,
"PriceType",
to_string(priceType_));
194 XMLUtils::addChild(doc, apoNode,
"StartDate", startDate_);
195 XMLUtils::addChild(doc, apoNode,
"EndDate", endDate_);
196 XMLUtils::addChild(doc, apoNode,
"PaymentCalendar", paymentCalendar_);
197 XMLUtils::addChild(doc, apoNode,
"PaymentLag", paymentLag_);
198 XMLUtils::addChild(doc, apoNode,
"PaymentConvention", paymentConvention_);
199 XMLUtils::addChild(doc, apoNode,
"PricingCalendar", pricingCalendar_);
200 XMLUtils::addChild(doc, apoNode,
"PaymentDate", paymentDate_);
201 XMLUtils::addChild(doc, apoNode,
"Gearing", gearing_);
202 XMLUtils::addChild(doc, apoNode,
"Spread",
spread_);
203 XMLUtils::addChild(doc, apoNode,
"CommodityQuantityFrequency",
to_string(commodityQuantityFrequency_));
204 XMLUtils::addChild(doc, apoNode,
"CommodityPayRelativeTo",
to_string(commodityPayRelativeTo_));
205 XMLUtils::addChild(doc, apoNode,
"FutureMonthOffset",
static_cast<int>(futureMonthOffset_));
206 XMLUtils::addChild(doc, apoNode,
"DeliveryRollDays",
static_cast<int>(deliveryRollDays_));
207 XMLUtils::addChild(doc, apoNode,
"IncludePeriodEnd", includePeriodEnd_);
209 XMLUtils::addChild(doc, apoNode,
"FXIndex",
fxIndex_);
215Leg CommodityAveragePriceOption::buildLeg(
const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory,
216 const string& configuration) {
223 vector<string> quantityDates;
225 vector<string> spreadDates;
226 vector<Real> gearings{gearing_};
227 vector<string> gearingDates;
229 bool isAveraged =
true;
230 bool isInArrears =
false;
231 QuantLib::ext::shared_ptr<CommodityFloatingLegData> commLegData = QuantLib::ext::make_shared<CommodityFloatingLegData>(
232 name_, priceType_, quantities, quantityDates, commodityQuantityFrequency_, commodityPayRelativeTo_, spreads,
233 spreadDates, gearings, gearingDates, CommodityPricingDateRule::FutureExpiryDate, pricingCalendar_, 0,
234 pricingDates, isAveraged, isInArrears, futureMonthOffset_, deliveryRollDays_, includePeriodEnd_,
true,
235 QuantLib::Null<QuantLib::Natural>(),
true,
"", QuantLib::Null<QuantLib::Natural>(),
false, QuantLib::Null<QuantLib::Natural>(),
239 vector<string> paymentDates = paymentDate_.empty() ? vector<string>() : vector<string>(1, paymentDate_);
240 LegData legData(commLegData,
true, currency_, scheduleData,
"", vector<Real>(), vector<string>(),
241 paymentConvention_,
false,
false,
false,
true,
"", 0,
"", vector<AmortizationData>(),
242 paymentLag_,
"", paymentCalendar_, paymentDates);
245 auto legBuilder = engineFactory->legBuilder(legData.
legType());
246 QuantLib::ext::shared_ptr<CommodityFloatingLegBuilder> cflb =
247 QuantLib::ext::dynamic_pointer_cast<CommodityFloatingLegBuilder>(legBuilder);
248 QL_REQUIRE(cflb,
"Expected a CommodityFloatingLegBuilder for leg type " << legData.
legType());
249 Leg leg = cflb->buildLeg(legData, engineFactory, requiredFixings_, configuration);
250 allAveraging_ = cflb->allAveraging();
255void CommodityAveragePriceOption::buildStandardOption(
const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory,
256 const Leg& leg, Date exerciseDate) {
258 QL_REQUIRE(!barrierData_.initialized(),
"Commodity APO: standard option does not support barriers");
261 QL_REQUIRE(leg.size() == 1,
"Single flow expected but found " << leg.size());
262 auto flow = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedCashFlow>(leg[0]);
263 QL_REQUIRE(flow,
"Expected a cashflow of type CommodityIndexedCashFlow");
266 if (exerciseDate != Date()) {
267 QL_REQUIRE(exerciseDate >= flow->pricingDate(),
268 "Exercise date, " << io::iso_date(exerciseDate) <<
", should be on or after the pricing date, "
269 << io::iso_date(flow->pricingDate()));
270 DLOG(
"buildStandardOption: explicit exercise date given for APO " << io::iso_date(exerciseDate) <<
".");
272 exerciseDate = flow->pricingDate();
273 optionData_.setExerciseDates({
to_string(exerciseDate)});
274 DLOG(
"buildStandardOption: set exercise date on APO to cashflow's pricing date " << io::iso_date(exerciseDate)
277 DLOG(
"buildStandardOption: pricing date on APO is " << io::iso_date(flow->pricingDate()) <<
".");
280 if (!optionData_.automaticExercise()) {
281 optionData_.setAutomaticExercise(
true);
282 DLOG(
"buildStandardOption: setting automatic exercise to true on APO.");
285 if (!optionData_.paymentData()) {
286 QL_REQUIRE(exerciseDate <= flow->date(),
"Exercise date, " << io::iso_date(exerciseDate)
287 <<
", should be on or before payment date, "
288 << io::iso_date(flow->date()));
289 string strDate =
to_string(flow->date());
291 DLOG(
"buildStandardOption: setting payment date to " << strDate <<
" on APO.");
293 DLOG(
"buildStandardOption: using explicitly provided payment data on APO.");
298 Real effectiveQuantity = gearing_ *
quantity_;
300 flow->index()->isFuturesIndex(), flow->pricingDate());
301 commOption.
build(engineFactory);
307void CommodityAveragePriceOption::buildApo(
const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory,
const Leg& leg,
308 Date exerciseDate,
const QuantLib::ext::shared_ptr<EngineBuilder>& builder) {
311 QL_REQUIRE(leg.size() == 1,
"Single flow expected but found " << leg.size());
312 auto apoFlow = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(leg[0]);
313 QL_REQUIRE(apoFlow,
"Expected a cashflow of type CommodityIndexedAverageCashFlow");
316 maturity_ = std::max(optionData_.premiumData().latestPremiumDate(), apoFlow->date());
318 Date lastApoFixingDate = apoFlow->indices().rbegin()->first;
321 if (exerciseDate != Date()) {
322 QL_REQUIRE(exerciseDate >= lastApoFixingDate,
"Exercise date, "
323 << io::iso_date(exerciseDate)
324 <<
", should be on or after the last APO fixing date, "
325 << io::iso_date(lastApoFixingDate));
326 DLOG(
"buildApo: explicit exercise date given for APO " << io::iso_date(exerciseDate) <<
".");
328 exerciseDate = lastApoFixingDate;
329 string strDate =
to_string(lastApoFixingDate);
330 optionData_.setExerciseDates({strDate});
331 DLOG(
"buildApo: set exercise date on APO to cashflow's last pricing date " << io::iso_date(lastApoFixingDate)
334 DLOG(
"buildApo: pricing date on APO is " << io::iso_date(lastApoFixingDate) <<
".");
336 QL_REQUIRE(exerciseDate <= apoFlow->date(),
"Exercise date, " << io::iso_date(exerciseDate)
337 <<
", should be on or before payment date, "
338 << io::iso_date(apoFlow->date()));
341 QuantLib::ext::shared_ptr<FxIndex>
fxIndex;
344 QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(leg[0])->index()->priceCurve()->currency();
346 QL_REQUIRE(npvCurrency_ == underlyingCcy.code() || npvCurrency_ == currency_,
"Commodity cross-currency APO: inconsistent currencies in trade.");
349 if (npvCurrency_ != underlyingCcy.code()) {
351 engineFactory->configuration(MarketContext::pricing));
352 for (
auto cf : leg) {
353 auto cacf = QuantLib::ext::dynamic_pointer_cast<CommodityIndexedAverageCashFlow>(cf);
354 for (
auto kv : cacf->indices()) {
355 if (!
fxIndex->fixingCalendar().isBusinessDay(
359 Date adjustedFixingDate =
fxIndex->fixingCalendar().adjust(kv.first, Preceding);
360 requiredFixings_.addFixingDate(adjustedFixingDate,
fxIndex_);
363 requiredFixings_.addFixingDate(kv.first,
fxIndex_);
374 if (barrierData_.initialized()) {
375 QL_REQUIRE(barrierData_.levels().size() == 1,
"Commodity APO: Expected exactly one barrier level.");
378 if (!barrierData_.style().empty()) {
381 "Commodity APO: Expected 'European' or 'American' as barrier style");
386 QuantLib::ext::shared_ptr<QuantLib::Exercise> exercise = QuantLib::ext::make_shared<EuropeanExercise>(exerciseDate);
387 auto apo = QuantLib::ext::make_shared<QuantExt::CommodityAveragePriceOption>(
388 apoFlow, exercise, apoFlow->periodQuantity(), strike_,
parseOptionType(optionData_.callPut()),
393 auto engineBuilder = QuantLib::ext::dynamic_pointer_cast<CommodityApoBaseEngineBuilder>(builder);
394 QuantLib::ext::shared_ptr<PricingEngine> engine = engineBuilder->engine(ccy, name_,
id(), apo);
395 apo->setPricingEngine(engine);
396 setSensitivityTemplate(*engineBuilder);
400 Real multiplier = positionType == Position::Long ? 1.0 : -1.0;
403 vector<QuantLib::ext::shared_ptr<Instrument>> additionalInstruments;
404 vector<Real> additionalMultipliers;
405 addPremiums(additionalInstruments, additionalMultipliers, multiplier, optionData_.premiumData(),
406 positionType == Position::Long ? -1.0 : 1.0, ccy, engineFactory,
407 engineBuilder->configuration(MarketContext::pricing));
410 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(apo, multiplier, additionalInstruments, additionalMultipliers);
Engine builder for commodity average price options.
const boost::shared_ptr< FxIndex > & fxIndex() const
Barrier::Type barrierType() const
Real barrierLevel() const
Exercise::Type barrierStyle() const
boost::shared_ptr< FxIndex > fxIndex_
Real effectiveStrike() const
CommodityAveragePriceOption()
Commodity option trade representation.
void build(const QuantLib::ext::shared_ptr< EngineFactory > &engineFactory) override
Build underlying instrument and link pricing engine.
Serializable object holding leg data.
const string & legType() const
Serializable schedule data.
Serializable object holding schedule Dates data.
const std::string & sensitivityTemplate() const
const Date & maturity() const
const QuantLib::ext::shared_ptr< InstrumentWrapper > & instrument() const
Small XML Document wrapper class.
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Commodity Average Price Option data model and serialization.
Commodity fixed and floating leg builders.
Commodity option representation.
Logic for calculating required fixing dates on legs.
Exercise::Type parseExerciseType(const std::string &s)
Convert text to QuantLib::Exercise::Type.
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Position::Type parsePositionType(const std::string &s)
Convert text to QuantLib::Position::Type.
bool parseBool(const string &s)
Convert text to bool.
Barrier::Type parseBarrierType(const std::string &s)
Convert std::string to QuantLib::BarrierType.
Real parseReal(const string &s)
Convert text to Real.
Option::Type parseOptionType(const std::string &s)
Convert text to QuantLib::Option::Type.
Classes and functions for log message handling.
#define DLOG(text)
Logging Macro (Level = Debug)
market data related utilties
CommodityQuantityFrequency
set< Date > pricingDates(const Date &s, const Date &e, const Calendar &pricingCalendar, bool excludeStart, bool includeEnd, bool useBusinessDays)
CommodityPriceType parseCommodityPriceType(const string &s)
CommodityPayRelativeTo parseCommodityPayRelativeTo(const string &s)
void reset(const ASTNodePtr root)
std::string to_string(const LocationInfo &l)
CommodityQuantityFrequency parseCommodityQuantityFrequency(const string &s)
Convert text to QuantExt::CommodityQuantityFrequency.
QuantLib::ext::shared_ptr< QuantExt::FxIndex > buildFxIndex(const string &fxIndex, const string &domestic, const string &foreign, const QuantLib::ext::shared_ptr< Market > &market, const string &configuration, bool useXbsCurves)
Serializable Credit Default Swap.
Wrapper for option instruments, tracks whether option has been exercised or not.
Map text representations to QuantLib/QuantExt types.
trade schedule data model and serialization
string conversion utilities