Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
vanillaoption.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2018 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*/
18
19/*! \file ored/portfolio/vanillaoption.hpp
20\brief vanilla option representation
21\ingroup tradedata
22*/
23
28#include <ql/instruments/vanillaoption.hpp>
29#include <ql/instruments/quantovanillaoption.hpp>
32
33using namespace QuantLib;
35
36namespace ore {
37namespace data {
38
39void VanillaOptionTrade::build(const QuantLib::ext::shared_ptr<ore::data::EngineFactory>& engineFactory) {
41
42 QL_REQUIRE(tradeActions().empty(), "TradeActions not supported for VanillaOption");
43
44 // If underlying currency is empty, then set to payment currency by default.
45 // If non-empty, then check if the currencies are different for a Quanto payoff
46 Currency ccy = parseCurrencyWithMinors(currency_);
47 Currency underlyingCurrency = underlyingCurrency_.empty() ? ccy : underlyingCurrency_;
48 bool sameCcy = underlyingCurrency == ccy;
49
50 if (strike_.currency().empty())
51 strike_.setCurrency(ccy.code());
52
53 // Payoff
54 Option::Type type = parseOptionType(option_.callPut());
55 QuantLib::ext::shared_ptr<StrikedTypePayoff> payoff(new PlainVanillaPayoff(type, strike_.value()));
56 QuantLib::Exercise::Type exerciseType = parseExerciseType(option_.style());
57 QL_REQUIRE(option_.exerciseDates().size() == 1, "Invalid number of exercise dates");
59 // Set the maturity date equal to the expiry date. It may get updated below if option is cash settled with
60 // payment after expiry.
62 // Exercise
63 QuantLib::ext::shared_ptr<Exercise> exercise;
64 switch (exerciseType) {
65 case QuantLib::Exercise::Type::European: {
66 exercise = QuantLib::ext::make_shared<EuropeanExercise>(expiryDate_);
67 break;
68 }
69 case QuantLib::Exercise::Type::American: {
70 exercise = QuantLib::ext::make_shared<AmericanExercise>(expiryDate_, option_.payoffAtExpiry());
71 break;
72 }
73 default:
74 QL_FAIL("Option Style " << option_.style() << " is not supported");
75 }
76 // Create the instrument and then populate the name for the engine builder.
77 QuantLib::ext::shared_ptr<Instrument> vanilla;
78 string tradeTypeBuilder = tradeType_;
79 Settlement::Type settlementType = parseSettlementType(option_.settlement());
80
81 // For Quanto, check for European and Cash, except for an FX underlying
82 if (!sameCcy) {
83 QL_REQUIRE(exerciseType == Exercise::Type::European, "Option exercise must be European for a Quanto payoff.");
84 if (settlementType == Settlement::Type::Physical) {
86 "Physically settled Quanto options are allowed only for an FX underlying.");
87 }
88 }
89
90 if (exerciseType == Exercise::European && settlementType == Settlement::Cash) {
91 // We have a European cash settled option.
92
93 // Get the payment date.
94 const boost::optional<OptionPaymentData>& opd = option_.paymentData();
96 if (opd) {
97 if (opd->rulesBased()) {
98 const Calendar& cal = opd->calendar();
99 QL_REQUIRE(cal != Calendar(), "Need a non-empty calendar for rules based payment date.");
100 paymentDate = cal.advance(expiryDate_, opd->lag(), Days, opd->convention());
101 } else {
102 const vector<Date>& dates = opd->dates();
103 QL_REQUIRE(dates.size() == 1, "Need exactly one payment date for cash settled European option.");
104 paymentDate = dates[0];
105 }
106 QL_REQUIRE(paymentDate >= expiryDate_, "Payment date must be greater than or equal to expiry date.");
107 }
108
109 if (paymentDate > expiryDate_) {
110 QL_REQUIRE(sameCcy, "Payment date must equal expiry date for a Quanto payoff. Trade: " << id() << ".");
111
112 // Build a QuantExt::CashSettledEuropeanOption if payment date is strictly greater than expiry.
113
114 // Has the option been marked as exercised
115 const boost::optional<OptionExerciseData>& oed = option_.exerciseData();
116 bool exercised = false;
117 Real exercisePrice = Null<Real>();
118 if (oed) {
119 QL_REQUIRE(oed->date() == expiryDate_, "The supplied exercise date ("
120 << io::iso_date(oed->date())
121 << ") should equal the option's expiry date ("
122 << io::iso_date(expiryDate_) << ").");
123 exercised = true;
124 exercisePrice = oed->price();
125 }
126
127 // If automatic exercise, we will need an index fixing on the expiry date.
129 QL_REQUIRE(index_, "Option trade " << id() << " has automatic exercise so we need a valid index.");
130 // If index name has not been populated, use logic here to populate it from the index object.
131 string indexName = indexName_;
132 if (indexName.empty()) {
133 indexName = index_->name();
135 indexName = "EQ-" + indexName;
136 }
138 }
139
140 // Build the instrument
141 LOG("Build CashSettledEuropeanOption for trade " << id());
142 vanilla = QuantLib::ext::make_shared<CashSettledEuropeanOption>(
143 type, strike_.value(), expiryDate_, paymentDate, option_.isAutomaticExercise(), index_, exercised, exercisePrice);
144
145 // Allow for a separate pricing engine that takes care of payment on a date after expiry. Do this by
146 // appending 'EuropeanCS' to the trade type.
147 tradeTypeBuilder = tradeType_ + "EuropeanCS";
148
149 // Update the maturity date.
151
152 } else {
153 if (forwardDate_ == QuantLib::Date()) {
154 // If payment date is not greater than expiry, build QuantLib::VanillaOption.
155 if (sameCcy) {
156 LOG("Build VanillaOption for trade " << id());
157 vanilla = QuantLib::ext::make_shared<QuantLib::VanillaOption>(payoff, exercise);
158 }
159 else {
160 LOG("Build QuantoVanillaOption for trade " << id());
161 vanilla = QuantLib::ext::make_shared<QuantLib::QuantoVanillaOption>(payoff, exercise);
163 tradeTypeBuilder = "QuantoEquityOption";
165 tradeTypeBuilder = "QuantoCommodityOption";
166 else
167 QL_FAIL("Option Quanto payoff not supported for " << assetClassUnderlying_ << " class.");
168 }
169 } else {
170 LOG("Build VanillaForwardOption for trade " << id());
171 QL_REQUIRE(sameCcy, "Quanto payoff is not currently supported for Forward Options: Trade " << id());
172 vanilla = QuantLib::ext::make_shared<QuantExt::VanillaForwardOption>(payoff, exercise, forwardDate_);
174 tradeTypeBuilder = tradeType_ + "Forward";
175 }
176 }
177
178 } else {
179 if (forwardDate_ == QuantLib::Date()) {
180 // If not European or not cash settled, build QuantLib::VanillaOption.
181 if (sameCcy) {
182 LOG("Build VanillaOption for trade " << id());
183 vanilla = QuantLib::ext::make_shared<QuantLib::VanillaOption>(payoff, exercise);
184 } else {
185 LOG("Build QuantoVanillaOption for trade " << id());
186 vanilla = QuantLib::ext::make_shared<QuantLib::QuantoVanillaOption>(payoff, exercise);
187 }
188 } else {
189 QL_REQUIRE(exerciseType == QuantLib::Exercise::Type::European, "Only European Forward Options currently supported");
190 LOG("Built VanillaForwardOption for trade " << id());
191 vanilla = QuantLib::ext::make_shared<QuantExt::VanillaForwardOption>(payoff, exercise, forwardDate_, paymentDate_);
193 tradeTypeBuilder = tradeType_ + "Forward";
194 }
195
196 // If the tradeTypeBuilder has not been modified yet..
197 if (tradeTypeBuilder == tradeType_) {
198 if (sameCcy)
199 tradeTypeBuilder = tradeType_ + (exerciseType == QuantLib::Exercise::Type::European ? "" : "American");
200 else
201 tradeTypeBuilder = "QuantoFxOption";
202 }
203 }
204 LOG("tradeTypeBuilder set to " << tradeTypeBuilder << " for trade " << id());
205
206 // Generally we need to set the pricing engine here even if the option is expired at build time, since the valuation date
207 // might change after build, and we get errors for the edge case valuation date = expiry date for European options.
208 string configuration = Market::defaultConfiguration;
209 QuantLib::ext::shared_ptr<EngineBuilder> builder = engineFactory->builder(tradeTypeBuilder);
210 QL_REQUIRE(builder, "No builder found for " << tradeTypeBuilder);
211
212 if (sameCcy) {
213 QuantLib::ext::shared_ptr<VanillaOptionEngineBuilder> vanillaOptionBuilder =
214 QuantLib::ext::dynamic_pointer_cast<VanillaOptionEngineBuilder>(builder);
215 QL_REQUIRE(vanillaOptionBuilder != nullptr, "No engine builder found for trade type " << tradeTypeBuilder);
216
217 if (forwardDate_ != Date()) {
218 vanilla->setPricingEngine(vanillaOptionBuilder->engine(assetName_, ccy, expiryDate_, false));
219 } else {
220 vanilla->setPricingEngine(vanillaOptionBuilder->engine(assetName_, ccy, expiryDate_, true));
221 }
222 setSensitivityTemplate(*vanillaOptionBuilder);
223
224 configuration = vanillaOptionBuilder->configuration(MarketContext::pricing);
225 } else {
226 QuantLib::ext::shared_ptr<QuantoVanillaOptionEngineBuilder> quantoVanillaOptionBuilder =
227 QuantLib::ext::dynamic_pointer_cast<QuantoVanillaOptionEngineBuilder>(builder);
228 QL_REQUIRE(quantoVanillaOptionBuilder != nullptr, "No (Quanto) engine builder found for trade type "
229 << tradeTypeBuilder);
230
231 vanilla->setPricingEngine(quantoVanillaOptionBuilder->engine(assetName_, underlyingCurrency, ccy, expiryDate_));
232 setSensitivityTemplate(*quantoVanillaOptionBuilder);
233
234 configuration = quantoVanillaOptionBuilder->configuration(MarketContext::pricing);
235 }
236
237 Position::Type positionType = parsePositionType(option_.longShort());
238 Real bsInd = (positionType == QuantLib::Position::Long ? 1.0 : -1.0);
239 Real mult = quantity_ * bsInd;
240
241 std::vector<QuantLib::ext::shared_ptr<Instrument>> additionalInstruments;
242 std::vector<Real> additionalMultipliers;
243 maturity_ = std::max(maturity_, addPremiums(additionalInstruments, additionalMultipliers, mult,
244 option_.premiumData(), -bsInd, ccy, engineFactory, configuration));
245
246 instrument_ = QuantLib::ext::shared_ptr<InstrumentWrapper>(
247 new VanillaInstrument(vanilla, mult, additionalInstruments, additionalMultipliers));
248}
249
251 Currency ccy = parseCurrencyWithMinors(currency_);
252 npvCurrency_ = ccy.code();
253
254 // Notional - we really need todays spot to get the correct notional.
255 // But rather than having it move around we use strike * quantity
257 // the following is correct for vanilla (sameCcy = true) and quanto (sameCcy = false)
258 notionalCurrency_ = ccy.code();
259}
260} // namespace data
261} // namespace ore
Abstract engine builders for European and American Options.
static const string defaultConfiguration
Default configuration label.
Definition: market.hpp:296
const string & callPut() const
Definition: optiondata.hpp:71
const string & longShort() const
Definition: optiondata.hpp:70
const string & style() const
Definition: optiondata.hpp:74
const string & settlement() const
Definition: optiondata.hpp:81
const bool & payoffAtExpiry() const
Definition: optiondata.hpp:75
bool isAutomaticExercise() const
Automatic exercise assumed false if not explicitly provided.
Definition: optiondata.hpp:117
const boost::optional< OptionPaymentData > & paymentData() const
Definition: optiondata.hpp:93
const boost::optional< OptionExerciseData > & exerciseData() const
Definition: optiondata.hpp:92
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)
TradeActions & tradeActions()
Set the trade actions.
Definition: trade.hpp:126
string npvCurrency_
Definition: trade.hpp:201
QuantLib::Real notional_
Definition: trade.hpp:202
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
string tradeType_
Definition: trade.hpp:196
RequiredFixings requiredFixings_
Definition: trade.hpp:223
QuantLib::ext::shared_ptr< InstrumentWrapper > instrument_
Definition: trade.hpp:197
string notionalCurrency_
Definition: trade.hpp:203
void setCurrency(const std::string &currency)
std::string currency()
QuantLib::Real value() const
Vanilla Instrument Wrapper.
QuantLib::Date expiryDate_
Store the option expiry date.
QuantLib::Date paymentDate_
Store the (optional) payment date.
const QuantLib::Date paymentDate() const
QuantLib::Date forwardDate_
Store the (optional) forward date.
std::string indexName_
Hold the external index name if needed e.g. in the case of an FX index.
QuantLib::ext::shared_ptr< QuantLib::Index > index_
An index is needed if the option is to be automatically exercised on expiry.
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Build QuantLib/QuantExt instrument, link pricing engine.
Exercise::Type parseExerciseType(const std::string &s)
Convert text to QuantLib::Exercise::Type.
Definition: parsers.cpp:466
Currency parseCurrencyWithMinors(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:310
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Definition: parsers.cpp:51
Position::Type parsePositionType(const std::string &s)
Convert text to QuantLib::Position::Type.
Definition: parsers.cpp:404
Settlement::Type parseSettlementType(const std::string &s)
Convert text to QuantLib::Settlement::Type.
Definition: parsers.cpp:434
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 LOG(text)
Logging Macro (Level = Notice)
Definition: log.hpp:552
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Abstract engine builder for Quanto European Options.
vanilla option representation