223 {
224
225
226
229
230 enum class AccumulatorScript { Accumulator01, Accumulator02 };
231 AccumulatorScript scriptToUse;
233 scriptToUse = AccumulatorScript::Accumulator01;
234 DLOG(
"building scripted trade wrapper using (internal) script Accumulator01");
235 } else {
236 scriptToUse = AccumulatorScript::Accumulator02;
237 DLOG(
"building scripted trade wrapper using (internal) script Accumulator02");
238 }
239
240 Real leverageMultiplier;
242 leverageMultiplier = 1.0;
244 leverageMultiplier = -1.0;
245 else {
246 QL_FAIL("invalid payoff type, expected Accumulator or Decumulator");
247 }
248
252
253 QL_REQUIRE(scriptToUse == AccumulatorScript::Accumulator01 || globalStrike != Null<Real>(),
254 "For accumulator type 02 a global strike must be given");
255
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));
270 } else {
271 QL_FAIL(
272 "insufficient strike information: either a global strike or a range-specific strike must be given");
273 }
274 }
275 }
276
277 bool initPositive = false;
278 for (Size i = 0; i < rangeLeverages.size(); i++) {
279 Real rl =
parseReal(rangeLeverages.at(i));
280 if (i == 0)
281 initPositive = rl >= 0.0;
282 else {
283 bool nextPositive = rl >= 0.0;
284 QL_REQUIRE(nextPositive == initPositive, "Range leverages must all have the same sign.");
285 }
286 }
289 numbers_.emplace_back(
"Number",
"OptionType", initPositive ?
"1" :
"-1");
290
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");
296 }
297
299 numbers_.emplace_back(
"Number",
"LongShort",
301
303 if (scriptToUse == AccumulatorScript::Accumulator01) {
304 numbers_.emplace_back(
"Number",
"Strike", rangeStrikes);
305 } else {
306 numbers_.emplace_back(
"Number",
"Strike", boost::lexical_cast<std::string>(globalStrike));
307 }
308
309 if (scriptToUse == AccumulatorScript::Accumulator01) {
313 } else {
317 }
318 } else {
320 events_.emplace_back(
"KnockOutSettlementDates",
"ObservationDates",
327 } else {
328 events_.emplace_back(
"SettlementDates",
"ObservationPeriodEndDates",
332 }
333 }
334
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()) {
345 knockOutType = "4";
346 knockOutLevel = boost::lexical_cast<std::string>(b.levels().front().value());
347 QL_REQUIRE(!barrierSet, "multiple barrier definitions");
348 barrierSet = true;
349 americanKO = !(b.style() == "European");
350 } else if (b.type() == "DownAndOut" && !b.levels().empty()) {
351 knockOutType = "3";
352 knockOutLevel = boost::lexical_cast<std::string>(b.levels().front().value());
353 QL_REQUIRE(!barrierSet, "multiple barrier definitions");
354 barrierSet = true;
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());
358 } else
359 QL_FAIL("invalid barrier definition, expected UpAndOut, DownAndOut, FixingFloor (with exactly one level)");
360 }
361
362 numbers_.emplace_back(
"Number",
"KnockOutLevel", knockOutLevel);
363 numbers_.emplace_back(
"Number",
"KnockOutType", knockOutType);
364
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");
373
374 } else {
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);
381 }
382
383
384
385 productTag_ = scriptToUse == AccumulatorScript::Accumulator01 ?
"SingleAssetOptionCG({AssetClass})"
386 : "SingleAssetOptionBwd({AssetClass})";
387
388
389
390 if (scriptToUse == AccumulatorScript::Accumulator01) {
391 script_[
""] = ScriptedTradeScriptData(
392 accumulator01_script, "value",
393 {{"currentNotional", "currentNotional"},
394 {"notionalCurrency", "PayCcy"},
395 {"Alive", "Alive"},
396 {"Fixing", "Fixing"}},
397 {}, {}, {ScriptedTradeScriptData::CalibrationData("Underlying", {"Strike", "KnockOutLevel"})});
398 } else {
399 script_[
""] = ScriptedTradeScriptData(
400 accumulator02_script, "value",
401 {{"currentNotional", "currentNotional"},
402 {"notionalCurrency", "PayCcy"},
403 {"KnockedOut", "KnockedOut"},
404 {"Fixing", "Fixing"}},
405 {}, {}, {ScriptedTradeScriptData::CalibrationData("Underlying", {"Strike", "KnockOutLevel"})});
406 script_[
"FD"] = ScriptedTradeScriptData(
407 accumulator02_script_fd, "value",
408 {{"currentNotional", "currentNotional"}, {"notionalCurrency", "PayCcy"}, {"Fixing", "Fixing"}}, {}, {},
409 {ScriptedTradeScriptData::CalibrationData("Underlying", {"Strike", "KnockOutLevel"})});
410 }
411
412
413
415}
const string & payoffType() const
const string & longShort() const
bool hasData() const
Check if has any dates/rules/derived schedules.
std::vector< ScriptedTradeValueTypeData > daycounters_
std::vector< ScriptedTradeEventData > events_
std::vector< ScriptedTradeValueTypeData > currencies_
std::vector< ScriptedTradeValueTypeData > numbers_
std::map< std::string, ScriptedTradeScriptData > script_
void build(const QuantLib::ext::shared_ptr< EngineFactory > &) override
void setCurrency(const std::string ¤cy)
QuantLib::Real value() const
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)
std::string to_string(const LocationInfo &l)
Schedule makeSchedule(const ScheduleDates &data)