43 {
44
45 Date today = Settings::instance().evaluationDate();
46
47
48
49 QL_REQUIRE(referenceDate >= today, "RepresentativeFxOptionMatcher: referenceDate ("
50 << referenceDate << ") must be < today (" << today << ")");
51 QL_REQUIRE(isPayer.size() == underlying.size(), "RepresentativeFxOptionMatcher: isPayer ("
52 << isPayer.size() << ") does not match underlying ("
53 << underlying.size() << ")");
54 QL_REQUIRE(currencies.size() == underlying.size(), "RepresentativeFxOptionMatcher: currencies ("
55 << currencies.size() << ") does not match underlying ("
56 << underlying.size() << ")");
57
58
59
60 auto fxScenarioValue = QuantLib::ext::make_shared<SimpleQuote>(fxSpot->value());
61 Handle<Quote> fxSpotScen(fxScenarioValue);
62
63
64
65 auto m = [](Real x) { return 1.0 / x; };
66 Handle<Quote> fxSpotScenInv(QuantLib::ext::make_shared<DerivedQuote<decltype(m)>>(fxSpotScen, m));
67
68
69
70 Leg forCfs, domCfs;
71
72 for (Size i = 0; i < underlying.size(); ++i) {
73
74
75 QL_REQUIRE(currencies[i] == forCcy || currencies[i] == domCcy,
76 "RepresentativeFxOptionMatcher: currency at index "
77 << i << " (" << currencies[i] << ") does not match forCcy (" << forCcy << ") or domCcy ("
78 << domCcy << ")");
79
80 for (auto const& c : underlying[i]) {
81
82
83
84 if (c->date() < referenceDate || (c->date() == referenceDate && !includeRefDateFlows))
85 continue;
86
87
88
89 QL_REQUIRE(!QuantLib::ext::dynamic_pointer_cast<IndexedCoupon>(c),
90 "RepresentativeFxOptionMatcher: Indexed Coupons are not supported");
91
92 QuantLib::ext::shared_ptr<CashFlow> res;
93
94 if (auto fxlinked = QuantLib::ext::dynamic_pointer_cast<FXLinked>(c)) {
95
96
97
98 std::set<std::string> ccys = {fxlinked->fxIndex()->sourceCurrency().code(),
99 fxlinked->fxIndex()->targetCurrency().code()};
100 QL_REQUIRE((ccys == std::set<std::string>{forCcy, domCcy}),
101 "RepresentativeFxOptionMatcher: FXLinked coupon ccys "
102 << fxlinked->fxIndex()->sourceCurrency().code() << ", "
103 << fxlinked->fxIndex()->targetCurrency().code()
104 << " do noth match currencies to be matched (" << forCcy << ", " << domCcy << ")");
105
106 res = QuantLib::ext::dynamic_pointer_cast<CashFlow>(fxlinked->clone(fxlinked->fxIndex()->clone(
107 fxlinked->fxIndex()->sourceCurrency().code() == forCcy ? fxSpotScen : fxSpotScenInv,
108 fxlinked->fxIndex()->sourceCurve(), fxlinked->fxIndex()->targetCurve())));
109 QL_REQUIRE(res, "RepresentativeFxOptionMatcher: internal error, cloned fx linked cashflow could not be "
110 "cast to CashFlow");
111
112 } else {
113
114
115
116 res = QuantLib::ext::make_shared<SimpleCashFlow>(c->amount(), c->date());
117 }
118
119
120
121 res = QuantLib::ext::make_shared<ScaledCashFlow>(isPayer[i] ? -1.0 : 1.0, res);
122
123 if (currencies[i] == forCcy)
124 forCfs.push_back(res);
125 else
126 domCfs.push_back(res);
127 }
128 }
129
130
131
132 Real npv =
133 CashFlows::npv(forCfs, **forCurve, false) * fxSpotScen->value() + CashFlows::npv(domCfs, **domCurve, false);
134
135
136
137 constexpr Real relShift = 0.01;
138
139 Real baseFx = fxScenarioValue->value();
140
141 fxScenarioValue->setValue(baseFx * (1.0 + relShift));
142 Real npv_up =
143 CashFlows::npv(forCfs, **forCurve, false) * fxSpotScen->value() + CashFlows::npv(domCfs, **domCurve, false);
144
145 fxScenarioValue->setValue(baseFx * (1.0 - relShift));
146 Real npv_down =
147 CashFlows::npv(forCfs, **forCurve, false) * fxSpotScen->value() + CashFlows::npv(domCfs, **domCurve, false);
148
149 Real fxDelta = (npv_up - npv_down) / (2.0 * baseFx * relShift);
150
151
152
153
154 amount1_ = fxDelta / forCurve->discount(referenceDate);
155 amount2_ = (npv - fxDelta * baseFx) / domCurve->discount(referenceDate);
158}