25#include <boost/lexical_cast.hpp>
32 static const std::string accumulator01_script =
33 " REQUIRE SIZE(FixingDates) == SIZE(SettlementDates);\n"
34 " REQUIRE KnockOutType == 3 OR KnockOutType == 4;\n"
35 " NUMBER Payoff, fix, d, r, Alive, currentNotional, Factor, ThisPayout, Fixing[SIZE(FixingDates)], dailyMult;\n"
38 " FOR d IN (1, SIZE(FixingDates), 1) DO\n"
39 " fix = Underlying(FixingDates[d]);\n"
42 " IF DailyFixingAmount == 1 THEN\n"
44 " dailyMult = days(DailyFixingAmountDayCounter, StartDate, FixingDates[d]);\n"
46 " dailyMult = days(DailyFixingAmountDayCounter, FixingDates[d-1], FixingDates[d]);\n"
50 " IF AmericanKO == 1 THEN\n"
51 " IF KnockOutType == 4 THEN\n"
52 " IF FixingDates[d] >= StartDate THEN\n"
53 " IF d == 1 OR FixingDates[d-1] <= StartDate THEN\n"
54 " Alive = Alive * (1 - ABOVEPROB(Underlying, StartDate, FixingDates[d], KnockOutLevel));\n"
56 " Alive = Alive * (1 - ABOVEPROB(Underlying, FixingDates[d-1], FixingDates[d], KnockOutLevel));\n"
60 " IF FixingDates[d] >= StartDate THEN\n"
61 " IF d == 1 OR FixingDates[d-1] <= StartDate THEN\n"
62 " Alive = Alive * (1 - BELOWPROB(Underlying, StartDate, FixingDates[d], KnockOutLevel));\n"
64 " Alive = Alive * (1 - BELOWPROB(Underlying, FixingDates[d-1], FixingDates[d], KnockOutLevel));\n"
69 " IF {KnockOutType == 4 AND fix >= KnockOutLevel} OR\n"
70 " {KnockOutType == 3 AND fix <= KnockOutLevel} THEN\n"
75 " IF d <= GuaranteedFixings THEN\n"
81 " FOR r IN (1, SIZE(RangeUpperBounds), 1) DO\n"
82 " IF fix > RangeLowerBounds[r] AND fix <= RangeUpperBounds[r] THEN\n"
83 " IF NakedOption == 1 THEN\n"
84 " ThisPayout = abs(RangeLeverages[r]) * FixingAmount * dailyMult * max(0, OptionType * (fix - Strike[r])) * Factor;\n"
86 " ThisPayout = RangeLeverages[r] * FixingAmount * dailyMult * (fix - Strike[r]) * Factor;\n"
88 " IF d > GuaranteedFixings OR ThisPayout >= 0 THEN\n"
89 " Payoff = Payoff + LOGPAY(ThisPayout, FixingDates[d], SettlementDates[d], PayCcy);\n"
94 " value = LongShort * Payoff;\n"
95 " currentNotional = FixingAmount * dailyMult * Strike[1];";
98 static const std::string accumulator02_script =
99 " REQUIRE SIZE(ObservationDates) == SIZE(KnockOutSettlementDates);\n"
100 " REQUIRE SIZE(ObservationPeriodEndDates) == SIZE(SettlementDates);\n"
101 " REQUIRE SIZE(RangeUpperBounds) == SIZE(RangeLowerBounds);\n"
102 " REQUIRE SIZE(RangeUpperBounds) == SIZE(RangeLeverages);\n"
103 " REQUIRE ObservationPeriodEndDates[SIZE(ObservationPeriodEndDates)] >= ObservationDates[SIZE(ObservationDates)];\n"
104 " NUMBER Payoff, fix, d, dd, KnockedOut, currentNotional, Days[SIZE(RangeUpperBounds)], knockOutDays, Fixing[SIZE(ObservationPeriodEndDates)];\n"
105 " NUMBER currentPeriod, r, ThisPayout;\n"
106 " currentPeriod = 1;\n"
107 " FOR d IN (1, SIZE(ObservationDates), 1) DO\n"
108 " fix = Underlying(ObservationDates[d]);\n"
110 " knockOutDays = max(DATEINDEX(GuaranteedPeriodEndDate, ObservationDates, GT) - 1 - d, 0);\n"
112 " IF KnockedOut == 0 THEN\n"
113 " IF {KnockOutType == 4 AND fix >= KnockOutLevel} OR\n"
114 " {KnockOutType == 3 AND fix <= KnockOutLevel} THEN\n"
116 " Days[DefaultRange] = Days[DefaultRange] + knockOutDays;\n"
117 " FOR r IN (1, SIZE(RangeUpperBounds), 1) DO\n"
118 " IF NakedOption == 1 THEN\n"
119 " ThisPayout = LongShort * FixingAmount * abs(RangeLeverages[r]) * Days[r] * max(0, OptionType * (fix - Strike) );\n"
121 " ThisPayout = LongShort * FixingAmount * RangeLeverages[r] * Days[r] * ( fix - Strike );\n"
123 " value = value + PAY( ThisPayout, ObservationDates[d], KnockOutSettlementDates[d], PayCcy );\n"
128 " IF KnockedOut == 0 THEN\n"
129 " FOR r IN (1, SIZE(RangeUpperBounds), 1) DO\n"
130 " IF fix > RangeLowerBounds[r] AND fix <= RangeUpperBounds[r] THEN\n"
131 " Days[r] = Days[r] + 1;\n"
134 " IF ObservationDates[d] >= ObservationPeriodEndDates[currentPeriod] THEN\n"
135 " FOR r IN (1, SIZE(RangeUpperBounds), 1) DO\n"
136 " IF NakedOption == 1 THEN\n"
137 " ThisPayout = LongShort * FixingAmount * abs(RangeLeverages[r]) * Days[r] * max(0, OptionType * (fix - Strike) );\n"
139 " ThisPayout = LongShort * FixingAmount * RangeLeverages[r] * Days[r] * ( fix - Strike );\n"
141 " value = value + LOGPAY( ThisPayout, ObservationDates[d], SettlementDates[currentPeriod], PayCcy );\n"
145 " IF ObservationDates[d] >= ObservationPeriodEndDates[currentPeriod] THEN\n"
146 " Fixing[currentPeriod] = fix;\n"
147 " currentPeriod = currentPeriod + 1;\n"
148 " FOR r IN (1, SIZE(RangeUpperBounds), 1) DO\n"
153 " currentNotional = FixingAmount * Strike;";
156 static const std::string accumulator02_script_fd =
157 " REQUIRE SIZE(ObservationDates) == SIZE(KnockOutSettlementDates);\n"
158 " REQUIRE SIZE(ObservationPeriodEndDates) == SIZE(SettlementDates);\n"
159 " REQUIRE SIZE(RangeUpperBounds) == SIZE(RangeLowerBounds);\n"
160 " REQUIRE SIZE(RangeUpperBounds) == SIZE(RangeLeverages);\n"
161 " REQUIRE ObservationPeriodEndDates[SIZE(ObservationPeriodEndDates)] >= ObservationDates[SIZE(ObservationDates)];\n"
163 " NUMBER currentPeriod, referencePayout, fix, d, r, dd, currentNotional, Fixing[SIZE(ObservationPeriodEndDates)], ThisPayout;\n"
165 " currentPeriod = DATEINDEX(ObservationDates[SIZE(ObservationDates)], ObservationPeriodEndDates, GEQ);\n"
166 " IF NakedOption == 1 THEN\n"
167 " ThisPayout = max(0, OptionType * (Underlying(ObservationDates[SIZE(ObservationDates)]) - Strike) );\n"
169 " ThisPayout = Underlying(ObservationDates[SIZE(ObservationDates)]) - Strike;\n"
171 " referencePayout = PAY( LongShort * FixingAmount * ThisPayout, ObservationDates[SIZE(ObservationDates)],\n"
172 " SettlementDates[currentPeriod], PayCcy );\n"
173 " value = 0 * referencePayout;\n"
175 " FOR d IN (SIZE(ObservationDates), 1, -1) DO\n"
177 " IF ObservationDates[d] >= TODAY THEN\n"
178 " value = NPV(value, ObservationDates[d]);\n"
179 " referencePayout = NPV(referencePayout, ObservationDates[d]);\n"
181 " value = NPV(value, TODAY);\n"
182 " referencePayout = NPV(referencePayout, TODAY);\n"
185 " fix = Underlying(ObservationDates[d]);\n"
186 " IF NakedOption == 1 THEN\n"
187 " ThisPayout = LongShort * FixingAmount * max(0, OptionType * (fix - Strike));\n"
189 " ThisPayout = LongShort * FixingAmount * (fix - Strike);\n"
192 " IF d > 1 AND currentPeriod > 0 AND ObservationDates[d-1] < ObservationPeriodEndDates[currentPeriod] THEN\n"
193 " referencePayout = PAY( ThisPayout, ObservationDates[d], SettlementDates[currentPeriod], PayCcy );\n"
194 " Fixing[currentPeriod] = fix;\n"
195 " currentPeriod = currentPeriod - 1;\n"
198 " IF {KnockOutType == 4 AND fix >= KnockOutLevel} OR\n"
199 " {KnockOutType == 3 AND fix <= KnockOutLevel} THEN\n"
200 " IF NakedOption == 1 THEN\n"
201 " ThisPayout = ThisPayout * abs(RangeLeverages[DefaultRange]);\n"
203 " ThisPayout = ThisPayout * RangeLeverages[DefaultRange];\n"
205 " referencePayout = PAY( ThisPayout, ObservationDates[d], KnockOutSettlementDates[d], PayCcy );\n"
206 " value = referencePayout * max(DATEINDEX(GuaranteedPeriodEndDate, ObservationDates, GT) - 1 - d, 0);\n"
208 " FOR r IN (1, SIZE(RangeUpperBounds), 1) DO\n"
209 " IF fix > RangeLowerBounds[r] AND fix <= RangeUpperBounds[r] THEN\n"
210 " IF NakedOption == 1 THEN\n"
211 " value = value + abs(RangeLeverages[r]) * referencePayout;\n"
213 " value = value + RangeLeverages[r] * referencePayout;\n"
219 " currentNotional = FixingAmount * Strike;";
230 enum class AccumulatorScript { Accumulator01, Accumulator02 };
231 AccumulatorScript scriptToUse;
233 scriptToUse = AccumulatorScript::Accumulator01;
234 DLOG(
"building scripted trade wrapper using (internal) script Accumulator01");
236 scriptToUse = AccumulatorScript::Accumulator02;
237 DLOG(
"building scripted trade wrapper using (internal) script Accumulator02");
240 Real leverageMultiplier;
242 leverageMultiplier = 1.0;
244 leverageMultiplier = -1.0;
246 QL_FAIL(
"invalid payoff type, expected Accumulator or Decumulator");
253 QL_REQUIRE(scriptToUse == AccumulatorScript::Accumulator01 || globalStrike != Null<Real>(),
254 "For accumulator type 02 a global strike must be given");
256 std::vector<std::string> rangeUpperBounds, rangeLowerBounds, rangeLeverages, rangeStrikes;
258 rangeLowerBounds.push_back(
259 boost::lexical_cast<std::string>(r.from() == Null<Real>() ? -QL_MAX_REAL : r.from()));
260 rangeUpperBounds.push_back(boost::lexical_cast<std::string>(r.to() == Null<Real>() ? QL_MAX_REAL : r.to()));
261 rangeLeverages.push_back(
262 boost::lexical_cast<std::string>(leverageMultiplier * (r.leverage() == Null<Real>() ? 1.0 : r.leverage())));
263 if (scriptToUse == AccumulatorScript::Accumulator01) {
264 if (r.strike() != Null<Real>()) {
265 rangeStrikes.push_back(boost::lexical_cast<std::string>(r.strike()));
266 }
else if (r.strikeAdjustment() != Null<Real>() && globalStrike != Null<Real>()) {
267 rangeStrikes.push_back(boost::lexical_cast<std::string>(globalStrike + r.strikeAdjustment()));
268 }
else if (globalStrike != Null<Real>()) {
269 rangeStrikes.push_back(boost::lexical_cast<std::string>(globalStrike));
272 "insufficient strike information: either a global strike or a range-specific strike must be given");
277 bool initPositive =
false;
278 for (Size i = 0; i < rangeLeverages.size(); i++) {
279 Real rl =
parseReal(rangeLeverages.at(i));
281 initPositive = rl >= 0.0;
283 bool nextPositive = rl >= 0.0;
284 QL_REQUIRE(nextPositive == initPositive,
"Range leverages must all have the same sign.");
289 numbers_.emplace_back(
"Number",
"OptionType", initPositive ?
"1" :
"-1");
291 numbers_.emplace_back(
"Number",
"RangeLowerBounds", rangeLowerBounds);
292 numbers_.emplace_back(
"Number",
"RangeUpperBounds", rangeUpperBounds);
293 numbers_.emplace_back(
"Number",
"RangeLeverages", rangeLeverages);
294 if (scriptToUse == AccumulatorScript::Accumulator02) {
295 numbers_.emplace_back(
"Number",
"DefaultRange",
"1");
299 numbers_.emplace_back(
"Number",
"LongShort",
303 if (scriptToUse == AccumulatorScript::Accumulator01) {
304 numbers_.emplace_back(
"Number",
"Strike", rangeStrikes);
306 numbers_.emplace_back(
"Number",
"Strike", boost::lexical_cast<std::string>(globalStrike));
309 if (scriptToUse == AccumulatorScript::Accumulator01) {
320 events_.emplace_back(
"KnockOutSettlementDates",
"ObservationDates",
328 events_.emplace_back(
"SettlementDates",
"ObservationPeriodEndDates",
335 std::string knockOutLevel = boost::lexical_cast<std::string>(QL_MAX_REAL), knockOutType =
"4",
336 guaranteedFixings =
"0";
337 bool barrierSet =
false;
338 bool americanKO =
false;
340 QL_REQUIRE(b.style().empty() || b.style() ==
"European" || b.style() ==
"American",
341 "expected barrier style American or European, got " << b.style());
342 QL_REQUIRE(b.style() !=
"European" || scriptToUse == AccumulatorScript::Accumulator01,
343 "European barrier style not allowed if PricingDates are given (Accumulator02 script variant)");
344 if (b.type() ==
"UpAndOut" && !b.levels().empty()) {
346 knockOutLevel = boost::lexical_cast<std::string>(b.levels().front().value());
347 QL_REQUIRE(!barrierSet,
"multiple barrier definitions");
349 americanKO = !(b.style() ==
"European");
350 }
else if (b.type() ==
"DownAndOut" && !b.levels().empty()) {
352 knockOutLevel = boost::lexical_cast<std::string>(b.levels().front().value());
353 QL_REQUIRE(!barrierSet,
"multiple barrier definitions");
355 americanKO = !(b.style() ==
"European");
356 }
else if (b.type() ==
"FixingFloor" && !b.levels().empty()) {
357 guaranteedFixings = boost::lexical_cast<std::string>(b.levels().front().value());
359 QL_FAIL(
"invalid barrier definition, expected UpAndOut, DownAndOut, FixingFloor (with exactly one level)");
362 numbers_.emplace_back(
"Number",
"KnockOutLevel", knockOutLevel);
363 numbers_.emplace_back(
"Number",
"KnockOutType", knockOutType);
365 if (scriptToUse == AccumulatorScript::Accumulator01) {
367 "For american knock out or when using a daily fixing amount StartDate must be given.");
369 numbers_.emplace_back(
"Number",
"AmericanKO", americanKO ?
"1" :
"-1");
370 numbers_.emplace_back(
"Number",
"GuaranteedFixings", guaranteedFixings);
372 daycounters_.emplace_back(
"Daycounter",
"DailyFixingAmountDayCounter",
"ACT/ACT.ISDA");
377 QL_REQUIRE(gf <= pd.size(),
378 "guaranteed fixings (" << gf <<
") > pricing dates schedule size (" << pd.size() <<
")");
379 Date gpend = gf == 0 ? Date::minDate() : pd.date(gf - 1);
385 productTag_ = scriptToUse == AccumulatorScript::Accumulator01 ?
"SingleAssetOptionCG({AssetClass})"
386 :
"SingleAssetOptionBwd({AssetClass})";
390 if (scriptToUse == AccumulatorScript::Accumulator01) {
392 accumulator01_script,
"value",
393 {{
"currentNotional",
"currentNotional"},
394 {
"notionalCurrency",
"PayCcy"},
396 {
"Fixing",
"Fixing"}},
400 accumulator02_script,
"value",
401 {{
"currentNotional",
"currentNotional"},
402 {
"notionalCurrency",
"PayCcy"},
403 {
"KnockedOut",
"KnockedOut"},
404 {
"Fixing",
"Fixing"}},
407 accumulator02_script_fd,
"value",
408 {{
"currentNotional",
"currentNotional"}, {
"notionalCurrency",
"PayCcy"}, {
"Fixing",
"Fixing"}}, {}, {},
422 std::string assetClass = boost::any_cast<std::string>(
additionalData_[
"isdaAssetClass"]);
423 if (assetClass ==
"Equity") {
425 additionalData_[
"isdaSubProduct"] = string(
"Price Return Basic Performance");
426 }
else if (assetClass ==
"Commodity") {
429 additionalData_[
"isdaSubProduct"] = string(
"Price Return Basic Performance");
430 }
else if (assetClass ==
"Foreign Exchange") {
434 WLOG(
"ISDA taxonomy incomplete for trade " <<
id());
445 QL_REQUIRE(dataNode,
tradeType() +
"Data node not found");
456 underlyingBuilder.
fromXML(tmp);
471 QL_REQUIRE(rangeBoundsNode,
"No RangeBounds node");
473 for (
auto const& n : rangeBounds) {
478 QL_REQUIRE(barriersNode,
"No Barriers node");
480 for (
auto const& n : barriers) {
accumulator wrapper for scripted trade
std::string settlementConvention_
ScheduleData settlementDates_
void setIsdaTaxonomyFields() override
void fromXML(XMLNode *node) override
QuantLib::ext::shared_ptr< Underlying > underlying_
XMLNode * toXML(XMLDocument &doc) const override
std::string settlementLag_
ScheduleData observationDates_
ScheduleData pricingDates_
std::vector< BarrierData > barriers_
std::string fixingAmount_
std::string settlementCalendar_
std::vector< RangeBound > rangeBounds_
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
Serializable obejct holding barrier data.
const string & payoffType() const
const string & longShort() const
virtual void fromXML(XMLNode *node) override
virtual XMLNode * toXML(XMLDocument &doc) const override
Serializable obejct holding range bound data.
bool hasData() const
Check if has any dates/rules/derived schedules.
virtual void fromXML(XMLNode *node) override
virtual XMLNode * toXML(XMLDocument &doc) const override
std::vector< ScriptedTradeValueTypeData > daycounters_
std::vector< ScriptedTradeEventData > events_
std::vector< ScriptedTradeValueTypeData > currencies_
std::vector< ScriptedTradeValueTypeData > indices_
virtual void setIsdaTaxonomyFields()
std::vector< ScriptedTradeValueTypeData > numbers_
std::map< std::string, ScriptedTradeScriptData > script_
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
virtual void fromXML(XMLNode *node) override
virtual XMLNode * toXML(XMLDocument &doc) const override
const string & tradeType() const
std::map< std::string, boost::any > additionalData_
XMLNode * toXML(XMLDocument &doc) const
void setCurrency(const std::string ¤cy)
void fromXML(XMLNode *node, const bool isRequired=true, const bool allowYieldStrike=false)
QuantLib::Real value() const
const QuantLib::ext::shared_ptr< Underlying > & underlying()
void fromXML(XMLNode *node) override
Small XML Document wrapper class.
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
static vector< XMLNode * > getChildrenNodes(XMLNode *node, const string &name)
Returns all the children with a given name.
static string getChildValue(XMLNode *node, const string &name, bool mandatory=false, const string &defaultValue=string())
static bool getChildValueAsBool(XMLNode *node, const string &name, bool mandatory=false, bool defaultValue=true)
static XMLNode * getChildNode(XMLNode *n, const string &name="")
static void setNodeName(XMLDocument &doc, XMLNode *node, const string &name)
static XMLNode * addChild(XMLDocument &doc, XMLNode *n, const string &name)
static void appendNode(XMLNode *parent, XMLNode *child)
Position::Type parsePositionType(const std::string &s)
Convert text to QuantLib::Position::Type.
Real parseReal(const string &s)
Convert text to Real.
Integer parseInteger(const string &s)
Convert text to QuantLib::Integer.
#define DLOG(text)
Logging Macro (Level = Debug)
#define WLOG(text)
Logging Macro (Level = Warning)
QL_DEPRECATED_ENABLE_WARNING std::string scriptedIndexName(const QuantLib::ext::shared_ptr< Underlying > &underlying)
std::string to_string(const LocationInfo &l)
Schedule makeSchedule(const ScheduleDates &data)
Serializable Credit Default Swap.
Map text representations to QuantLib/QuantExt types.
string conversion utilities