Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
asianoption.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 Skandinaviska Enskilda Banken AB (publ)
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 ORE is free software: you can redistribute it and/or modify it
8 under the terms of the Modified BSD License. You should have received a
9 copy of the license along with this program.
10 The license is also available online at <http://opensourcerisk.org>
11 This program is distributed on the basis that it will form a useful
12 contribution to risk analytics and model standardisation, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
15 */
16
17/*! \file ored/portfolio/asianoption.hpp
18\brief Asian option representation
19\ingroup tradedata
20*/
21
28#include <ql/errors.hpp>
29#include <ql/instruments/asianoption.hpp>
30#include <ql/instruments/averagetype.hpp>
31
32namespace ore {
33namespace data {
34
35void AsianOption::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
36
37 // ISDA taxonomy
38 if (underlying_->type() == "Equity") {
39 additionalData_["isdaAssetClass"] = string("Equity");
40 additionalData_["isdaBaseProduct"] = string("Option");
41 additionalData_["isdaSubProduct"] = string("Price Return Basic Performance");
42 } else if (underlying_->type() == "FX") {
43 additionalData_["isdaAssetClass"] = string("Foreign Exchange");
44 additionalData_["isdaBaseProduct"] = string("Vanilla Option");
45 additionalData_["isdaSubProduct"] = string("");
46 } else if (underlying_->type() == "Commodity") {
47 // guessing that Commodities are treated like Equity
48 additionalData_["isdaAssetClass"] = string("Commodity");
49 additionalData_["isdaBaseProduct"] = string("Option");
50 additionalData_["isdaSubProduct"] = string("Price Return Basic Performance");
51 }
52 else {
53 WLOG("ISDA taxonomy not set for trade " << id());
54 }
55 additionalData_["isdaTransaction"] = string("");
56
57 Currency payCcy = parseCurrency(currency_);
58
59 QL_REQUIRE(tradeActions().empty(), "TradeActions not supported for AsianOption");
60
61 Option::Type type = parseOptionType(option_.callPut());
62 QL_REQUIRE(option_.exerciseDates().size() == 1, "Expected exactly one exercise date");
63 Date expiryDate = parseDate(option_.exerciseDates().front());
64
65 string tradeTypeBuilder = tradeType_;
66
67 // Add Arithmetic/Geometric
68
69 if (option_.payoffType2() == "Arithmetic" || option_.payoffType2().empty())
70 tradeTypeBuilder += "Arithmetic";
71 else if (option_.payoffType2() == "Geometric")
72 tradeTypeBuilder += "Geometric";
73 else {
74 QL_FAIL("payoff type 2 must be 'Arithmetic' or 'Geometric'");
75 }
76
77 // Add Price/Strike
78
79 if (option_.payoffType() == "Asian")
80 tradeTypeBuilder += "Price";
81 else if (option_.payoffType() == "AverageStrike")
82 tradeTypeBuilder += "Strike";
83 else {
84 QL_FAIL("payoff type must be 'Asian' or 'AverageStrike'");
85 }
86
87 QuantLib::ext::shared_ptr<EngineBuilder> builder = engineFactory->builder(tradeTypeBuilder);
88 QL_REQUIRE(builder, "No builder found for " << tradeTypeBuilder);
89
90 // check for delegating engine builder
91
92 if (auto db = QuantLib::ext::dynamic_pointer_cast<DelegatingEngineBuilder>(builder)) {
93
94 // let the delegating builder build the trade and link the results to this trade
95
96 delegatingBuilderTrade_ = db->build(this, engineFactory);
97
100 npvCurrency_ = delegatingBuilderTrade_->npvCurrency();
101 additionalData_ = delegatingBuilderTrade_->additionalData();
102 requiredFixings_ = delegatingBuilderTrade_->requiredFixings();
103 setSensitivityTemplate(delegatingBuilderTrade_->sensitivityTemplate());
104
105 // notional and notional currency are defined in overriden methods!
106
107 return;
108 }
109
110 // we do not have a delegating engine builder
111
112 QuantLib::ext::shared_ptr<AsianOptionEngineBuilder> asianOptionBuilder =
113 QuantLib::ext::dynamic_pointer_cast<AsianOptionEngineBuilder>(builder);
114
115 QL_REQUIRE(asianOptionBuilder, "engine builder is not an AsianOption engine builder" << tradeTypeBuilder);
116
117 std::string processType = asianOptionBuilder->processType();
118 QL_REQUIRE(!processType.empty(), "ProcessType must be configured, this is unexpected");
119
120 QuantLib::ext::shared_ptr<StrikedTypePayoff> payoff(new PlainVanillaPayoff(type, tradeStrike_.value()));
121
122 auto index = parseIndex(indexName());
123
124 if (auto fxIndex = QuantLib::ext::dynamic_pointer_cast<QuantExt::FxIndex>(index)) {
125 QL_REQUIRE(fxIndex->targetCurrency() == payCcy,
126 "FX domestic ccy " << fxIndex->targetCurrency() << " must match pay ccy " << payCcy);
127 assetName_ = fxIndex->sourceCurrency().code();
128 } else if (auto eqIndex = QuantLib::ext::dynamic_pointer_cast<QuantExt::EquityIndex2>(index)) {
129 // FIXME for EQ and COMM indices check whether EQ, COMM ccy = payCcy (in the engine builders probably)
130 assetName_ = eqIndex->name();
131 } else if (auto commIndex = QuantLib::ext::dynamic_pointer_cast<QuantExt::CommodityIndex>(index)) {
132 assetName_ = commIndex->underlyingName();
133 }
134
135 // FIXME the engine should handle the historical part of the averaging as well!
136 QuantLib::ext::shared_ptr<QuantLib::Instrument> asian;
137 auto exercise = QuantLib::ext::make_shared<QuantLib::EuropeanExercise>(expiryDate);
138 if (processType == "Discrete") {
139 QuantLib::Date today = engineFactory->market()->asofDate();
140 Real runningAccumulator = option_.payoffType2() == "Geometric" ? 1.0 : 0.0;
141 Size pastFixings = 0;
142 Schedule observationSchedule = makeSchedule(observationDates_);
143 std::vector<QuantLib::Date> observationDates = observationSchedule.dates();
144
145 // Sort for the engine's sake. Not needed - instrument also sorts...?
146 std::sort(observationDates.begin(), observationDates.end());
147
148 for (QuantLib::Date observationDate : observationDates) {
149 // TODO: Verify. Should today be read too? a enforcesTodaysHistoricFixings() be used?
150 if (observationDate < today ||
151 (observationDate == today && Settings::instance().enforcesTodaysHistoricFixings())) {
152 // FIXME all observation dates lead to a required fixing
153 requiredFixings_.addFixingDate(observationDate, indexName());
154 Real fixingValue = index->fixing(observationDate);
155 if (option_.payoffType2() == "Geometric") {
156 runningAccumulator *= fixingValue;
157 } else if (option_.payoffType2() == "Arithmetic") {
158 runningAccumulator += fixingValue;
159 }
160 ++pastFixings;
161 }
162 }
163 asian = QuantLib::ext::make_shared<QuantLib::DiscreteAveragingAsianOption>(
164 option_.payoffType2() == "Geometric" ? QuantLib::Average::Type::Geometric
165 : QuantLib::Average::Type::Arithmetic,
166 runningAccumulator, pastFixings, observationDates, payoff, exercise);
167 } else if (processType == "Continuous") {
168 // FIXME how is the accumulated average handled in this case?
169 asian = QuantLib::ext::make_shared<QuantLib::ContinuousAveragingAsianOption>(option_.payoffType2() == "Geometric"
170 ? QuantLib::Average::Type::Geometric
171 : QuantLib::Average::Type::Arithmetic,
172 payoff, exercise);
173 } else {
174 QL_FAIL("unexpected ProcessType, valid options are Discrete/Continuous");
175 }
176
177 // Only try to set an engine on the option instrument if it is not expired. This avoids errors in
178 // engine builders that rely on the expiry date being in the future.
179 string configuration = asianOptionBuilder->configuration(MarketContext::pricing);
180 if (!asian->isExpired()) {
181 asian->setPricingEngine(asianOptionBuilder->engine(assetName_, payCcy, expiryDate));
182 setSensitivityTemplate(*asianOptionBuilder);
183 } else {
184 DLOG("No engine attached for option on trade " << id() << " with expiry date " << io::iso_date(expiryDate)
185 << " because it is expired.");
186 }
187
188 Position::Type positionType = parsePositionType(option_.longShort());
189 Real bsInd = (positionType == QuantLib::Position::Long ? 1.0 : -1.0);
190 Real mult = quantity_ * bsInd;
191
192 std::vector<QuantLib::ext::shared_ptr<Instrument>> additionalInstruments;
193 std::vector<Real> additionalMultipliers;
194 maturity_ = expiryDate;
195 maturity_ =
196 std::max(maturity_, addPremiums(additionalInstruments, additionalMultipliers, mult, option_.premiumData(),
197 positionType == QuantLib::Position::Long ? -1.0 : 1.0, payCcy, engineFactory,
198 configuration));
199
200 instrument_ = QuantLib::ext::make_shared<VanillaInstrument>(asian, mult, additionalInstruments, additionalMultipliers);
201
205}
206
208 Trade::fromXML(node);
209 XMLNode* n = XMLUtils::getChildNode(node, tradeType() + "Data");
210 QL_REQUIRE(n, "No " + tradeType_ + "Data node found.");
211
212 quantity_ = XMLUtils::getChildValueAsDouble(n, "Quantity", true);
213
215
216 currency_ = XMLUtils::getChildValue(n, "Currency", false);
217
218 XMLNode* tmp = XMLUtils::getChildNode(n, "Underlying");
219 if (!tmp)
220 tmp = XMLUtils::getChildNode(n, "Name");
221 UnderlyingBuilder underlyingBuilder;
222 underlyingBuilder.fromXML(tmp);
223 underlying_ = underlyingBuilder.underlying();
224
225 option_.fromXML(XMLUtils::getChildNode(n, "OptionData"));
226
227 settlementDate_ = parseDate(XMLUtils::getChildValue(n, "Settlement", false));
228
229 observationDates_.fromXML(XMLUtils::getChildNode(n, "ObservationDates"));
230}
231
233 XMLNode* node = Trade::toXML(doc);
234 XMLNode* n = doc.allocNode(tradeType() + "Data");
235 XMLUtils::appendNode(node, n);
236 XMLUtils::addChild(doc, n, "Quantity", quantity_);
238 XMLUtils::addChild(doc, n, "Currency", currency_);
239 XMLUtils::appendNode(n, underlying_->toXML(doc));
241 if (settlementDate_ != Null<Date>())
243 auto tmp = observationDates_.toXML(doc);
244 XMLUtils::setNodeName(doc, tmp, "ObservationDates");
245 XMLUtils::appendNode(n, tmp);
246 return node;
247}
248
249std::map<AssetClass, std::set<std::string>>
250AsianOption::underlyingIndices(const QuantLib::ext::shared_ptr<ReferenceDataManager>& referenceDataManager) const {
251 std::map<AssetClass, std::set<std::string>> result;
252 if (isEquityIndex(indexName())) {
253 result[AssetClass::EQ].insert(indexName());
254 } else if (isFxIndex(indexName_)) {
255 result[AssetClass::FX].insert(indexName());
256 } else if (isCommodityIndex(indexName_)) {
257 result[AssetClass::COM].insert(indexName());
258 }
259 return result;
260}
261
263 if (!indexName_.empty())
264 return;
265 if (underlying_->type() == "Equity") {
266 indexName_ = "EQ-" + underlying_->name();
267 } else if (underlying_->type() == "FX") {
268 indexName_ = "FX-" + underlying_->name();
269 } else if (underlying_->type() == "Commodity") {
270 QuantLib::ext::shared_ptr<CommodityUnderlying> comUnderlying =
271 QuantLib::ext::dynamic_pointer_cast<CommodityUnderlying>(underlying_);
272 std::string tmp = "COMM-" + comUnderlying->name();
273 if (comUnderlying->priceType().empty() || comUnderlying->priceType() == "Spot") {
274 indexName_ = tmp;
275 } else if (comUnderlying->priceType() == "FutureSettlement") {
276 auto conventions = InstrumentConventions::instance().conventions();
277 QL_REQUIRE(conventions->has(comUnderlying->name()),
278 "future settlement requires conventions for commodity '" << comUnderlying->name() << "'");
279 auto convention =
280 QuantLib::ext::dynamic_pointer_cast<CommodityFutureConvention>(conventions->get(comUnderlying->name()));
281 Size futureMonthsOffset =
282 comUnderlying->futureMonthOffset() == Null<Size>() ? 0 : comUnderlying->futureMonthOffset();
283 Size deliveryRollDays =
284 comUnderlying->deliveryRollDays() == Null<Size>() ? 0 : comUnderlying->deliveryRollDays();
285 Calendar cal = parseCalendar(comUnderlying->deliveryRollCalendar());
286 ConventionsBasedFutureExpiry expiryCalculator(*convention);
287 QL_REQUIRE(option_.exerciseDates().size() == 1, "expected exactly one exercise date");
288 Date expiryDate = parseDate(option_.exerciseDates().front());
289 Date adjustedObsDate =
290 deliveryRollDays != 0 ? cal.advance(expiryDate, deliveryRollDays * Days) : expiryDate;
291 auto tmp = parseCommodityIndex(comUnderlying->name(), false, Handle<QuantExt::PriceTermStructure>(),
292 convention->calendar(), true);
293 tmp = tmp->clone(expiryCalculator.nextExpiry(true, adjustedObsDate, futureMonthsOffset));
294 indexName_ = tmp->name();
295 } else {
296 QL_FAIL("underlying price type '" << comUnderlying->priceType() << "' for commodity underlying '"
297 << comUnderlying->name() << "' not handled.");
298 }
299 } else if (underlying_->type() == "Basic") {
300 indexName_ = underlying_->name();
301 } else {
302 QL_FAIL("invalid underlying type: " << underlying_->type());
303 }
304}
305
306QuantLib::Real AsianOption::notional() const {
307 return delegatingBuilderTrade_ != nullptr ? delegatingBuilderTrade_->notional() : Trade::notional();
308}
309
311 return delegatingBuilderTrade_ != nullptr ? delegatingBuilderTrade_->notionalCurrency() : Trade::notionalCurrency();
312}
313
314} // namespace data
315} // namespace ore
Asian option representation.
Abstract engine builders for European Asian Options.
QuantLib::ext::shared_ptr< Trade > delegatingBuilderTrade_
Definition: asianoption.hpp:97
std::map< AssetClass, std::set< std::string > > underlyingIndices(const QuantLib::ext::shared_ptr< ReferenceDataManager > &referenceDataManager=nullptr) const override
QuantLib::Real notional() const override
Trade interface.
virtual void fromXML(XMLNode *node) override
QuantLib::ext::shared_ptr< Underlying > underlying_
Definition: asianoption.hpp:91
virtual XMLNode * toXML(XMLDocument &doc) const override
TradeStrike tradeStrike_
Definition: asianoption.hpp:88
ScheduleData observationDates_
Definition: asianoption.hpp:90
const string & indexName() const
Definition: asianoption.hpp:73
string notionalCurrency() const override
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Build QuantLib/QuantExt instrument, link pricing engine.
Definition: asianoption.cpp:35
void populateIndexName() const
const ScheduleData & observationDates() const
Definition: asianoption.hpp:70
Perform date calculations for future contracts based on conventions.
QuantLib::Date nextExpiry(bool includeExpiry=true, const QuantLib::Date &referenceDate=QuantLib::Date(), QuantLib::Natural offset=0, bool forOption=false) override
const string & callPut() const
Definition: optiondata.hpp:71
const string & payoffType2() const
Definition: optiondata.hpp:73
const string & payoffType() const
Definition: optiondata.hpp:72
const string & longShort() const
Definition: optiondata.hpp:70
virtual void fromXML(XMLNode *node) override
Definition: optiondata.cpp:32
virtual XMLNode * toXML(XMLDocument &doc) const override
Definition: optiondata.cpp:86
const PremiumData & premiumData() const
Definition: optiondata.hpp:83
const vector< string > & exerciseDates() const
Definition: optiondata.hpp:76
void addFixingDate(const QuantLib::Date &fixingDate, const std::string &indexName, const QuantLib::Date &payDate=Date::maxDate(), const bool alwaysAddIfPaysOnSettlement=false, const bool mandatoryFixing=true)
const vector< ScheduleDates > & dates() const
Definition: schedule.hpp:228
virtual void fromXML(XMLNode *node) override
Definition: schedule.cpp:179
virtual XMLNode * toXML(XMLDocument &doc) const override
Definition: schedule.cpp:198
TradeActions & tradeActions()
Set the trade actions.
Definition: trade.hpp:126
string npvCurrency_
Definition: trade.hpp:201
QuantLib::Real notional_
Definition: trade.hpp:202
virtual void fromXML(XMLNode *node) override
Definition: trade.cpp:34
Date addPremiums(std::vector< QuantLib::ext::shared_ptr< Instrument > > &instruments, std::vector< Real > &multipliers, const Real tradeMultiplier, const PremiumData &premiumData, const Real premiumMultiplier, const Currency &tradeCurrency, const QuantLib::ext::shared_ptr< EngineFactory > &factory, const string &configuration)
Definition: trade.cpp:58
void setSensitivityTemplate(const EngineBuilder &builder)
Definition: trade.cpp:295
virtual XMLNode * toXML(XMLDocument &doc) const override
Definition: trade.cpp:46
string tradeType_
Definition: trade.hpp:196
RequiredFixings requiredFixings_
Definition: trade.hpp:223
virtual QuantLib::Real notional() const
Return the current notional in npvCurrency. See individual sub-classes for the precise definition.
Definition: trade.hpp:153
QuantLib::ext::shared_ptr< InstrumentWrapper > instrument_
Definition: trade.hpp:197
string notionalCurrency_
Definition: trade.hpp:203
virtual string notionalCurrency() const
Definition: trade.hpp:155
const string & tradeType() const
Definition: trade.hpp:133
std::map< std::string, boost::any > additionalData_
Definition: trade.hpp:224
XMLNode * toXML(XMLDocument &doc) const
Definition: tradestrike.cpp:86
void fromXML(XMLNode *node, const bool isRequired=true, const bool allowYieldStrike=false)
Definition: tradestrike.cpp:50
QuantLib::Real value() const
const QuantLib::ext::shared_ptr< Underlying > & underlying()
Definition: underlying.hpp:266
void fromXML(XMLNode *node) override
Definition: underlying.cpp:305
Small XML Document wrapper class.
Definition: xmlutils.hpp:65
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Definition: xmlutils.cpp:132
static Real getChildValueAsDouble(XMLNode *node, const string &name, bool mandatory=false, double defaultValue=0.0)
Definition: xmlutils.cpp:286
static string getChildValue(XMLNode *node, const string &name, bool mandatory=false, const string &defaultValue=string())
Definition: xmlutils.cpp:277
static XMLNode * getChildNode(XMLNode *n, const string &name="")
Definition: xmlutils.cpp:387
static void setNodeName(XMLDocument &doc, XMLNode *node, const string &name)
Definition: xmlutils.cpp:478
static XMLNode * addChild(XMLDocument &doc, XMLNode *n, const string &name)
Definition: xmlutils.cpp:181
static void appendNode(XMLNode *parent, XMLNode *child)
Definition: xmlutils.cpp:406
Base class for classes that perform date calculations for future contracts.
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Definition: parsers.cpp:157
bool isEquityIndex(const string &indexName)
Return true if the indexName is that of an EquityIndex, otherwise false.
bool isCommodityIndex(const string &indexName)
Return true if the indexName is that of an CommodityIndex, otherwise false.
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Definition: parsers.cpp:51
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:290
Position::Type parsePositionType(const std::string &s)
Convert text to QuantLib::Position::Type.
Definition: parsers.cpp:404
QuantLib::ext::shared_ptr< Index > parseIndex(const string &s)
Convert std::string to QuantLib::Index.
Option::Type parseOptionType(const std::string &s)
Convert text to QuantLib::Option::Type.
Definition: parsers.cpp:481
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define WLOG(text)
Logging Macro (Level = Warning)
Definition: log.hpp:550
bool isFxIndex(const std::string &indexName)
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
QuantLib::ext::shared_ptr< QuantExt::CommodityIndex > parseCommodityIndex(const string &name, bool hasPrefix, const Handle< PriceTermStructure > &ts, const Calendar &cal, const bool enforceFutureIndex)
Schedule makeSchedule(const ScheduleDates &data)
Definition: schedule.cpp:263
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Wrapper for option instruments, tracks whether option has been exercised or not.
trade schedule data model and serialization