Skip to content

Commit 82c61b1

Browse files
committed
QPR-11988 fxbarrieroption and double barrier option with delegate for AMC
1 parent bcbfefa commit 82c61b1

3 files changed

Lines changed: 169 additions & 28 deletions

File tree

OREData/ored/portfolio/barrieroption.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,20 @@ void BarrierOption::build(const QuantLib::ext::shared_ptr<EngineFactory>& engine
7474
Real rebate = barrier_.rebate() / tradeMultiplier();
7575
QL_REQUIRE(rebate >= 0, "rebate must be non-negative");
7676

77-
77+
auto delegatingBldr = getDelegatingBuilder(engineFactory);
78+
if (delegatingBldr) {
79+
delegatingTrade_ = delegatingBldr->build(this, engineFactory);
80+
instrument_ = delegatingTrade_->instrument();
81+
maturity_ = delegatingTrade_->maturity();
82+
npvCurrency_ = delegatingTrade_->npvCurrency();
83+
additionalData_ = delegatingTrade_->additionalData();
84+
requiredFixings_ = delegatingTrade_->requiredFixings();
85+
setSensitivityTemplate(delegatingTrade_->sensitivityTemplate());
86+
addProductModelEngine(delegatingTrade_->productModelEngine());
87+
// notional and notional currency are defined in overriden methods!
88+
return;
89+
}
90+
7891
// set the maturity
7992
maturity_ = std::max(option_.premiumData().latestPremiumDate(), payDate);
8093
maturityType_ = maturity_ == payDate ? "Pay Date" : "Option's Latest Premium Date";
@@ -186,6 +199,13 @@ void BarrierOption::build(const QuantLib::ext::shared_ptr<EngineFactory>& engine
186199
tradeCurrency(), discountCurve, engineFactory, engineFactory->configuration(MarketContext::pricing));
187200
}
188201

202+
QuantLib::Real BarrierOption::notional() const {
203+
return delegatingTrade_ != nullptr ? delegatingTrade_->notional() : Trade::notional();
204+
}
205+
206+
string BarrierOption::notionalCurrency() const {
207+
return delegatingTrade_ != nullptr ? delegatingTrade_->notionalCurrency() : Trade::notionalCurrency();
208+
}
189209

190210
void BarrierOption::fromXML(XMLNode* node) {
191211
Trade::fromXML(node);
@@ -223,6 +243,19 @@ std::string FxOptionWithBarrier::indexFixingName() {
223243
return fxIndexStr_;
224244
}
225245

246+
QuantLib::ext::shared_ptr<DelegatingEngineBuilder>
247+
FxOptionWithBarrier::getDelegatingBuilder(const QuantLib::ext::shared_ptr<EngineFactory>& ef) {
248+
QuantLib::ext::shared_ptr<FxBarrierOptionScriptedEngineBuilder> fxEuropeanBarrierOptionBuilder;
249+
try {
250+
fxEuropeanBarrierOptionBuilder =
251+
QuantLib::ext::dynamic_pointer_cast<FxBarrierOptionScriptedEngineBuilder>(ef->builder("FxBarrierOption"));
252+
DLOG("FxEuropeanBarrierOptionScriptedEngineBuilder found for trade " << tradeType_);
253+
} catch (...) {
254+
// no delegating builder found
255+
}
256+
return fxEuropeanBarrierOptionBuilder;
257+
}
258+
226259
void FxOptionWithBarrier::build(const QuantLib::ext::shared_ptr<ore::data::EngineFactory>& ef) {
227260

228261
// ISDA taxonomy

OREData/ored/portfolio/barrieroption.hpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ class BarrierOption : virtual public ore::data::Trade {
5151
//! Build QuantLib/QuantExt instrument, link pricing engine
5252
void build(const QuantLib::ext::shared_ptr<ore::data::EngineFactory>&) override;
5353

54+
QuantLib::Real notional() const override;
55+
string notionalCurrency() const override;
56+
5457
//! check validity of barriers
5558
virtual void checkBarriers() = 0;
5659

@@ -60,7 +63,7 @@ class BarrierOption : virtual public ore::data::Trade {
6063
virtual QuantLib::ext::shared_ptr<QuantLib::Index> getHighIndex() const { return nullptr; }
6164

6265
// strike of underlying option
63-
virtual const QuantLib::Real strike() = 0;
66+
virtual const QuantLib::Real strike() const = 0;
6467

6568
virtual QuantLib::Real tradeMultiplier() = 0;
6669
virtual Currency tradeCurrency() = 0;
@@ -70,6 +73,11 @@ class BarrierOption : virtual public ore::data::Trade {
7073
virtual QuantLib::ext::shared_ptr<QuantLib::PricingEngine>
7174
barrierPricingEngine(const QuantLib::ext::shared_ptr<EngineFactory>& ef, const QuantLib::Date& expiryDate,
7275
const QuantLib::Date& paymentDate) = 0;
76+
77+
virtual QuantLib::ext::shared_ptr<ore::data::DelegatingEngineBuilder>
78+
getDelegatingBuilder(const QuantLib::ext::shared_ptr<EngineFactory>& ef) {
79+
return nullptr;
80+
}
7381
virtual const QuantLib::Handle<QuantLib::Quote>& spotQuote() = 0;
7482

7583
virtual void additionalFromXml(ore::data::XMLNode* node) = 0;
@@ -81,6 +89,7 @@ class BarrierOption : virtual public ore::data::Trade {
8189
const BarrierData& barrier() const { return barrier_; }
8290
const QuantLib::Date& startDate() const { return startDate_; }
8391
const QuantLib::Calendar& calendar() const { return calendar_; }
92+
const std::string& calendarStr() const { return calendarStr_; }
8493
//@}
8594

8695
//! \name Serialisation
@@ -94,7 +103,7 @@ class BarrierOption : virtual public ore::data::Trade {
94103
BarrierData barrier_;
95104
QuantLib::Date startDate_;
96105
QuantLib::Calendar calendar_;
97-
106+
QuantLib::ext::shared_ptr<Trade> delegatingTrade_;
98107
protected:
99108
std::string calendarStr_;
100109
};
@@ -124,6 +133,7 @@ class FxOptionWithBarrier : public FxSingleAssetDerivative, public BarrierOption
124133
//@}
125134

126135
void build(const QuantLib::ext::shared_ptr<ore::data::EngineFactory>& ef) override;
136+
127137
//! \name Serialisation
128138
//@{
129139
void additionalFromXml(ore::data::XMLNode* node) override;
@@ -133,11 +143,14 @@ class FxOptionWithBarrier : public FxSingleAssetDerivative, public BarrierOption
133143
QuantLib::ext::shared_ptr<QuantLib::Index> getIndex() const override { return QuantLib::ext::dynamic_pointer_cast<Index>(fxIndex_); }
134144
QuantLib::ext::shared_ptr<QuantLib::Index> getLowIndex() const override { return QuantLib::ext::dynamic_pointer_cast<Index>(fxIndexLows_); }
135145
QuantLib::ext::shared_ptr<QuantLib::Index> getHighIndex() const override { return QuantLib::ext::dynamic_pointer_cast<Index>(fxIndexHighs_); }
136-
const QuantLib::Real strike() override { return soldAmount_ / boughtAmount_; }
146+
QuantLib::ext::shared_ptr<DelegatingEngineBuilder> getDelegatingBuilder(const QuantLib::ext::shared_ptr<EngineFactory>& ef) override;
147+
const QuantLib::Real strike() const override { return soldAmount_ / boughtAmount_; }
137148
QuantLib::Real tradeMultiplier() override { return boughtAmount_; }
138149
Currency tradeCurrency() override { return parseCurrency(soldCurrency_); }
139150
const QuantLib::Handle<QuantLib::Quote>& spotQuote() override { return spotQuote_; }
140151
std::string indexFixingName() override;
152+
const std::string& fxIndex() const { return fxIndexStr_; }
153+
141154

142155
void fromXML(ore::data::XMLNode* node) override { BarrierOption::fromXML(node); }
143156
ore::data::XMLNode* toXML(ore::data::XMLDocument& doc) const override { return BarrierOption::toXML(doc); }
@@ -152,6 +165,7 @@ class FxOptionWithBarrier : public FxSingleAssetDerivative, public BarrierOption
152165
QuantLib::Handle<QuantLib::Quote> spotQuote_;
153166
QuantLib::Real boughtAmount_;
154167
QuantLib::Real soldAmount_;
168+
155169
};
156170

157171

@@ -187,7 +201,7 @@ class EquityOptionWithBarrier : public EquitySingleAssetDerivative, public Barri
187201
//@}
188202

189203
QuantLib::ext::shared_ptr<QuantLib::Index> getIndex() const override { return QuantLib::ext::dynamic_pointer_cast<Index>(eqIndex_); }
190-
const QuantLib::Real strike() override { return tradeStrike_.value(); }
204+
const QuantLib::Real strike() const override { return tradeStrike_.value(); }
191205
QuantLib::Real tradeMultiplier() override { return quantity_; }
192206
Currency tradeCurrency() override { return currency_; }
193207
const QuantLib::Handle<QuantLib::Quote>& spotQuote() override { return eqIndex_->equitySpot(); }

OREData/ored/portfolio/builders/fxbarrieroption.cpp

Lines changed: 117 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,42 +14,118 @@
1414
FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
1515
*/
1616

17+
#include <ored/portfolio/barrieroption.hpp>
1718
#include <ored/portfolio/builders/fxbarrieroption.hpp>
1819
#include <ored/portfolio/fxkikobarrieroption.hpp>
1920
#include <ored/portfolio/genericbarrieroption.hpp>
21+
#include <ored/utilities/parsers.hpp>
2022
namespace ore {
2123
namespace data {
2224

25+
struct GenericBarrierOptionData {
26+
QuantLib::ext::shared_ptr<Underlying> underlying;
27+
OptionData optionData;
28+
std::vector<BarrierData> barriers;
29+
ScheduleData barrierMonitoringDates;
30+
BarrierData transatlanticBarrier;
31+
std::string payCurrency;
32+
std::string settlementDate;
33+
std::string quantity;
34+
std::string strike;
35+
std::string amount;
36+
std::string kikoType;
37+
};
2338

24-
QuantLib::ext::shared_ptr<ore::data::Trade> FxBarrierOptionScriptedEngineBuilder::build(const Trade* trade, const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory){
25-
auto fxKiKoBarrierOption = dynamic_cast<const ore::data::FxKIKOBarrierOption*>(trade);
39+
GenericBarrierOptionData parseFxBarrierOption(const ore::data::FxOptionWithBarrier* fxBarrierOption) {
40+
QL_REQUIRE(fxBarrierOption != nullptr, "FxBarrierOptionScriptedEngineBuilder: internal error, could not "
41+
"cast to ore::data::FxOptionWithBarrier. Contact dev.");
42+
GenericBarrierOptionData data;
43+
std::string indexName = fxBarrierOption->fxIndex().empty()
44+
? "GENERIC-" + fxBarrierOption->boughtCurrency() + "-" + fxBarrierOption->soldCurrency()
45+
: fxBarrierOption->fxIndex().substr(3);
46+
data.underlying = QuantLib::ext::make_shared<FXUnderlying>("FX", indexName, 1.0);
47+
data.optionData = fxBarrierOption->option();
48+
// Barrier
49+
const auto& barrier = fxBarrierOption->barrier();
50+
if (barrier.levels().size() == 1){
51+
data.barriers.push_back(barrier);
52+
} else if (barrier.levels().size() == 2){
53+
auto doubleBarrierType = parseDoubleBarrierType(barrier.type());
54+
string lowBarrierType = doubleBarrierType == DoubleBarrier::KIKO || doubleBarrierType == DoubleBarrier::KnockIn
55+
? "DownAndIn"
56+
: "DownAndOut";
57+
string highBarrierType = doubleBarrierType == DoubleBarrier::KIKO || doubleBarrierType == DoubleBarrier::KnockOut
58+
? "UpAndOut"
59+
: "UpAndIn";
2660

27-
QL_REQUIRE(fxKiKoBarrierOption != nullptr,
28-
"FxKIKOBarrierOptionScriptedEngineBuilder: internal error, could not "
29-
"cast to ore::data::FxKIKOBarrierOption. Contact dev.");
61+
BarrierData lowBarrier(lowBarrierType, {barrier.levels().front().value()}, barrier.rebate(), {barrier.levels().front()},
62+
barrier.style(), barrier.strictComparison(), barrier.overrideTriggered());
63+
BarrierData highBarrier(highBarrierType, {barrier.levels().back().value()}, barrier.rebate(),
64+
{barrier.levels().back()}, barrier.style(), barrier.strictComparison(),
65+
barrier.overrideTriggered());
66+
data.barriers.push_back(lowBarrier);
67+
data.barriers.push_back(highBarrier);
68+
} else {
69+
QL_FAIL("FxBarrierOptionScriptedEngineBuilder: only single and double barriers are supported. Please check trade xml.");
70+
}
71+
72+
std::string startDate = to_string(fxBarrierOption->startDate());
73+
std::string exerciseDate = data.optionData.exerciseDates().front();
74+
75+
ScheduleRules rule(startDate, exerciseDate, "1D", fxBarrierOption->calendarStr(), "Following", "Unadjusted",
76+
"Backward");
77+
data.barrierMonitoringDates = ScheduleData(rule);
78+
//! Empty transatlantic
79+
data.transatlanticBarrier = BarrierData();
80+
data.payCurrency = fxBarrierOption->soldCurrency();
81+
Date expiryDate = parseDate(exerciseDate);
82+
Date paymentDate = expiryDate;
83+
const QuantLib::ext::optional<OptionPaymentData>& opd = data.optionData.paymentData();
84+
if (opd) {
85+
if (opd->rulesBased()) {
86+
const Calendar& cal = opd->calendar();
87+
QL_REQUIRE(cal != Calendar(), "Need a non-empty calendar for rules based payment date.");
88+
paymentDate = cal.advance(expiryDate, opd->lag(), Days, opd->convention());
89+
} else {
90+
const vector<Date>& dates = opd->dates();
91+
QL_REQUIRE(dates.size() == 1, "Need exactly one payment date for cash settled European option.");
92+
paymentDate = dates[0];
93+
}
94+
QL_REQUIRE(paymentDate >= expiryDate, "Payment date must be greater than or equal to expiry date.");
95+
}
96+
data.settlementDate = to_string(paymentDate);
97+
data.quantity = to_string(fxBarrierOption->boughtAmount());
98+
data.strike = to_string(fxBarrierOption->strike());
99+
data.amount = "";
100+
data.kikoType = "KoAlways";
101+
return data;
102+
}
103+
104+
GenericBarrierOptionData parseFxKIKOBarrierOptionData(const ore::data::FxKIKOBarrierOption* fxKiKoBarrierOption) {
105+
QL_REQUIRE(fxKiKoBarrierOption != nullptr, "FxKIKOBarrierOptionScriptedEngineBuilder: internal error, could not "
106+
"cast to ore::data::FxKIKOBarrierOption. Contact dev.");
107+
GenericBarrierOptionData data;
30108
std::string indexName =
31109
fxKiKoBarrierOption->fxIndex().empty()
32110
? "GENERIC-" + fxKiKoBarrierOption->boughtCurrency() + "-" + fxKiKoBarrierOption->soldCurrency()
33111
: fxKiKoBarrierOption->fxIndex().substr(3);
34-
QuantLib::ext::shared_ptr<Underlying> underlying = QuantLib::ext::make_shared<FXUnderlying>("FX", indexName, 1.0);
35-
36-
const auto& optionData = fxKiKoBarrierOption->option();
112+
data.underlying = QuantLib::ext::make_shared<FXUnderlying>("FX", indexName, 1.0);
113+
data.optionData = fxKiKoBarrierOption->option();
37114

38115
std::string startDate = fxKiKoBarrierOption->startDate();
39-
std::string exerciseDate = optionData.exerciseDates().front();
40-
41-
ScheduleRules rule(startDate, exerciseDate, "1D", fxKiKoBarrierOption->calendar(), "Following", "Unadjusted", "Backward");
42-
ScheduleData barrierMonitoringDates(rule);
43-
44-
//! Empty transatlantic barrier
45-
auto transatlanticBarrier = BarrierData();
46-
//! Observation date for schedule monitoring dates only at the end
116+
std::string exerciseDate = data.optionData.exerciseDates().front();
47117

48-
std::string domesticCurrency = fxKiKoBarrierOption->soldCurrency();
118+
ScheduleRules rule(startDate, exerciseDate, "1D", fxKiKoBarrierOption->calendar(), "Following", "Unadjusted",
119+
"Backward");
120+
data.barrierMonitoringDates = ScheduleData(rule);
49121

122+
//! Empty transatlantic
123+
data.barriers = fxKiKoBarrierOption->barriers();
124+
data.transatlanticBarrier = BarrierData();
125+
data.payCurrency = fxKiKoBarrierOption->soldCurrency();
50126
Date expiryDate = parseDate(exerciseDate);
51127
Date paymentDate = expiryDate;
52-
const QuantLib::ext::optional<OptionPaymentData>& opd = optionData.paymentData();
128+
const QuantLib::ext::optional<OptionPaymentData>& opd = data.optionData.paymentData();
53129
if (opd) {
54130
if (opd->rulesBased()) {
55131
const Calendar& cal = opd->calendar();
@@ -62,12 +138,30 @@ QuantLib::ext::shared_ptr<ore::data::Trade> FxBarrierOptionScriptedEngineBuilder
62138
}
63139
QL_REQUIRE(paymentDate >= expiryDate, "Payment date must be greater than or equal to expiry date.");
64140
}
65-
auto qty = fxKiKoBarrierOption->boughtAmount();
66-
auto strike = fxKiKoBarrierOption->strike();
67-
std::vector<BarrierData> barriers = fxKiKoBarrierOption->barriers();
141+
data.settlementDate = to_string(paymentDate);
142+
data.quantity = to_string(fxKiKoBarrierOption->boughtAmount());
143+
data.strike = to_string(fxKiKoBarrierOption->strike());
144+
data.amount = "";
145+
data.kikoType = "KoAlways";
146+
return data;
147+
}
148+
149+
QuantLib::ext::shared_ptr<ore::data::Trade>
150+
FxBarrierOptionScriptedEngineBuilder::build(const Trade* trade,
151+
const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory) {
152+
GenericBarrierOptionData data;
153+
if (auto fxKiKoBarrierOption = dynamic_cast<const ore::data::FxKIKOBarrierOption*>(trade);
154+
fxKiKoBarrierOption != nullptr) {
155+
data = parseFxKIKOBarrierOptionData(fxKiKoBarrierOption);
156+
} else if (auto fxBarrierOption = dynamic_cast<const ore::data::FxOptionWithBarrier*>(trade);
157+
fxBarrierOption != nullptr) {
158+
data = parseFxBarrierOption(fxBarrierOption);
159+
} else {
160+
QL_FAIL("FxKIKOBarrierOptionScriptedEngineBuilder::build(): trade is not a FxKIKOBarrierOption");
161+
}
68162
auto barrierOption = QuantLib::ext::make_shared<GenericBarrierOption>(
69-
underlying, optionData, barriers, barrierMonitoringDates, transatlanticBarrier, domesticCurrency,
70-
to_string(paymentDate), to_string(qty), to_string(strike), "", "KoAlways");
163+
data.underlying, data.optionData, data.barriers, data.barrierMonitoringDates, data.transatlanticBarrier,
164+
data.payCurrency, data.settlementDate, data.quantity, data.strike, data.amount, data.kikoType);
71165

72166
barrierOption->build(engineFactory);
73167
return barrierOption;

0 commit comments

Comments
 (0)