Skip to content

Commit 1daf6d0

Browse files
committed
Merge branch 'feature/QPR-13668' into 'master'
QPR-13668 add discount_curve to fx forward Closes QPR-13668 See merge request qs/oreplus!3082
2 parents 1bb2f4e + a28ea5f commit 1daf6d0

10 files changed

Lines changed: 58 additions & 28 deletions

File tree

OREData/ored/portfolio/builders/deltagammaengines.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,16 @@ class FxForwardEngineBuilderDeltaGamma : public FxForwardEngineBuilderBase {
170170
: FxForwardEngineBuilderBase("DiscountedCashflows", "DiscountingFxForwardEngineDeltaGamma") {}
171171

172172
protected:
173-
virtual QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy) override {
173+
virtual QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy,
174+
const std::string& discountCurve) override {
174175

175176
std::vector<Time> bucketTimes = parseListOfValues<Time>(engineParameter("BucketTimes"), &parseReal);
176177
bool computeDelta = parseBool(engineParameter("ComputeDelta"));
177178
bool computeGamma = parseBool(engineParameter("ComputeGamma"));
178179
bool linearInZero = parseBool(engineParameter("LinearInZero", {}, false, "True")); // FIXME: Add to pricing engine parameters?
179180
bool applySimmExemptions = parseBool(engineParameter("ApplySimmExemptions", {}, false, "false"));
180181

181-
string pair = keyImpl(forCcy, domCcy);
182+
string pair = forCcy.code() + domCcy.code();
182183
Handle<YieldTermStructure> domCcyCurve =
183184
market_->discountCurve(domCcy.code(), configuration(MarketContext::pricing));
184185
Handle<YieldTermStructure> forCcyCurve =

OREData/ored/portfolio/builders/fxforward.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ using namespace QuantLib;
2929
using namespace QuantExt;
3030

3131
QuantLib::ext::shared_ptr<PricingEngine> CamAmcFxForwardEngineBuilder::engineImpl(const Currency& forCcy,
32-
const Currency& domCcy) {
32+
const Currency& domCcy,
33+
const std::string& discountCurve) {
3334

3435
QL_REQUIRE(domCcy != forCcy, "CamAmcFxForwardEngineBuilder: domCcy = forCcy = " << domCcy.code());
3536

@@ -76,7 +77,8 @@ QuantLib::ext::shared_ptr<PricingEngine> CamAmcFxForwardEngineBuilder::engineImp
7677
}
7778

7879
QuantLib::ext::shared_ptr<PricingEngine> AmcCgFxForwardEngineBuilder::engineImpl(const Currency& forCcy,
79-
const Currency& domCcy) {
80+
const Currency& domCcy,
81+
const std::string& discountCurve) {
8082

8183
QL_REQUIRE(domCcy != forCcy, "AmcCgFxForwardEngineBuilder: domCcy = forCcy = " << domCcy.code());
8284

OREData/ored/portfolio/builders/fxforward.hpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@
2323

2424
#pragma once
2525

26-
#include <boost/make_shared.hpp>
2726
#include <ored/portfolio/builders/cachingenginebuilder.hpp>
2827
#include <ored/portfolio/enginefactory.hpp>
28+
#include <ored/utilities/marketdata.hpp>
29+
2930
#include <qle/pricingengines/discountingfxforwardengine.hpp>
3031

32+
#include <boost/make_shared.hpp>
33+
3134
namespace ore {
3235
namespace data {
3336

@@ -36,15 +39,17 @@ namespace data {
3639
\ingroup builders
3740
*/
3841
class FxForwardEngineBuilderBase
39-
: public CachingPricingEngineBuilder<string, const Currency&, const Currency&> {
42+
: public CachingPricingEngineBuilder<string, const Currency&, const Currency&, const string&> {
4043
public:
4144
FxForwardEngineBuilderBase(const std::string& model, const std::string& engine)
4245
: CachingEngineBuilder(model, engine, {"FxForward"}) {}
4346

4447
protected:
45-
string keyImpl(const Currency& forCcy, const Currency& domCcy) override {
46-
return forCcy.code() + domCcy.code();
48+
string keyImpl(const Currency& forCcy, const Currency& domCcy, const std::string& discountCurve) override {
49+
return forCcy.code() + domCcy.code() + "_" + discountCurve;
4750
}
51+
52+
std::string discountCurve_;
4853
};
4954

5055
//! Engine Builder for FX Forwards
@@ -56,12 +61,16 @@ class FxForwardEngineBuilder : public FxForwardEngineBuilderBase {
5661
FxForwardEngineBuilder() : FxForwardEngineBuilderBase("DiscountedCashflows", "DiscountingFxForwardEngine") {}
5762

5863
protected:
59-
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy) override {
60-
string pair = keyImpl(forCcy, domCcy);
64+
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy,
65+
const std::string& discountCurve) override {
66+
std::cout << "getting curve '" << discountCurve << "'" << std::endl;
67+
string pair = forCcy.code() + domCcy.code();
6168
return QuantLib::ext::make_shared<QuantExt::DiscountingFxForwardEngine>(
6269
domCcy, market_->discountCurve(domCcy.code(), configuration(MarketContext::pricing)), forCcy,
6370
market_->discountCurve(forCcy.code(), configuration(MarketContext::pricing)),
64-
market_->fxRate(pair, configuration(MarketContext::pricing)));
71+
market_->fxRate(pair, configuration(MarketContext::pricing)), QuantLib::Date(), QuantLib::Date(),
72+
discountCurve.empty() ? Handle<YieldTermStructure>{}
73+
: indexOrYieldCurve(market_, discountCurve, configuration(MarketContext::pricing)));
6574
}
6675
};
6776

@@ -75,7 +84,8 @@ class CamAmcFxForwardEngineBuilder : public FxForwardEngineBuilderBase {
7584
stickyCloseOutDates_(stickyCloseOutDates) {}
7685

7786
protected:
78-
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy) override;
87+
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy,
88+
const std::string& discountCurve) override;
7989

8090
private:
8191
const QuantLib::ext::shared_ptr<QuantExt::CrossAssetModel> cam_;
@@ -93,7 +103,8 @@ class AmcCgFxForwardEngineBuilder : public FxForwardEngineBuilderBase {
93103
}
94104

95105
protected:
96-
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy) override;
106+
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy,
107+
const std::string& discountCurve) override;
97108

98109
private:
99110
const QuantLib::ext::shared_ptr<ore::data::ModelCG> modelCg_;

OREData/ored/portfolio/fxforward.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ void FxForward::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFact
156156
instrument_.reset(new VanillaInstrument(instrument));
157157

158158
// set pricing engine
159-
instrument_->qlInstrument()->setPricingEngine(fxBuilder->engine(boughtCcy, soldCcy));
159+
instrument_->qlInstrument()->setPricingEngine(
160+
fxBuilder->engine(boughtCcy, soldCcy, envelope().additionalField("discount_curve", false)));
160161
setSensitivityTemplate(*fxBuilder);
161162
addProductModelEngine(*fxBuilder);
162163

OREData/ored/portfolio/fxoption.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ void FxOption::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFacto
178178
QuantLib::ext::shared_ptr<EngineBuilder> builder = engineFactory->builder("FxForward");
179179
auto fxBuilder = QuantLib::ext::dynamic_pointer_cast<FxForwardEngineBuilderBase>(builder);
180180
QL_REQUIRE(fxBuilder, "FxOption::build(): internal error: could not cast to FxForwardEngineBuilderBase");
181-
qlinstr->setPricingEngine(fxBuilder->engine(boughtCcy,soldCcy));
181+
qlinstr->setPricingEngine(fxBuilder->engine(boughtCcy, soldCcy, {}));
182182
auto configuration = fxBuilder->configuration(MarketContext::pricing);
183183

184184
maturity_ = expiryDate_;

OREData/ored/portfolio/fxswap.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ void FxSwap::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory
6363
QL_REQUIRE(builder, "No builder found for " << tradeType_);
6464
QuantLib::ext::shared_ptr<FxForwardEngineBuilderBase> fxBuilder =
6565
QuantLib::ext::dynamic_pointer_cast<FxForwardEngineBuilderBase>(builder);
66-
instNear_->setPricingEngine(fxBuilder->engine(nearSoldCcy, nearBoughtCcy));
66+
instNear_->setPricingEngine(fxBuilder->engine(nearSoldCcy, nearBoughtCcy, {}));
6767
setSensitivityTemplate(*fxBuilder);
6868
addProductModelEngine(*fxBuilder);
6969
instFar_.reset(
7070
new QuantExt::FxForward(farBoughtAmount_, nearSoldCcy, farSoldAmount_, nearBoughtCcy, farDate, false));
71-
instFar_->setPricingEngine(fxBuilder->engine(nearSoldCcy, nearBoughtCcy));
71+
instFar_->setPricingEngine(fxBuilder->engine(nearSoldCcy, nearBoughtCcy, {}));
7272

7373
DLOG("FxSwap::build(): Near NPV = " << instNear_->NPV());
7474
DLOG("FxSwap::build(): Far NPV = " << instFar_->NPV());

OREData/ored/utilities/marketdata.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <qle/indexes/fxindex.hpp>
3232

3333
#include <ql/time/imm.hpp>
34+
#include <ql/termstructures/yield/flatforward.hpp>
3435

3536
#include <boost/algorithm/string.hpp>
3637

@@ -72,6 +73,10 @@ Handle<YieldTermStructure> xccyYieldCurve(const QuantLib::ext::shared_ptr<Market
7273

7374
Handle<YieldTermStructure> indexOrYieldCurve(const QuantLib::ext::shared_ptr<Market>& market, const std::string& name,
7475
const std::string& configuration) {
76+
if (name == "NULLCURVE") {
77+
return Handle<YieldTermStructure>(
78+
QuantLib::ext::make_shared<QuantLib::FlatForward>(0, NullCalendar(), 0.0, Actual365Fixed()));
79+
}
7580
try {
7681
return market->iborIndex(name, configuration)->forwardingTermStructure();
7782
} catch (...) {
@@ -84,7 +89,6 @@ Handle<YieldTermStructure> indexOrYieldCurve(const QuantLib::ext::shared_ptr<Mar
8489
<< "' or default configuration.");
8590
}
8691

87-
8892
std::string indexTrancheSpecificCreditCurveName(const std::string& creditCurveId, const double assumedRecoveryRate){
8993
std::ostringstream oss;
9094
oss << "__CDO_" << creditCurveId << "_&_REC_" << std::fixed << std::setprecision(2) << assumedRecoveryRate << "_&_";

OREData/ored/utilities/marketdata.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ QuantLib::Handle<QuantLib::YieldTermStructure>
5656
xccyYieldCurve(const QuantLib::ext::shared_ptr<Market>& market, const std::string& ccyCode, bool& outXccyExists,
5757
const std::string& configuration = Market::defaultConfiguration);
5858

59-
/*! Get a yield curve by name, where name can refer to an index or a yield curve name */
59+
/*! Get a yield curve by name, where name can refer to an index or a yield curve name
60+
If the name is set to the special string "NULLCURVE" a curve with constant rate 0 is returned. */
6061
QuantLib::Handle<QuantLib::YieldTermStructure>
6162
indexOrYieldCurve(const QuantLib::ext::shared_ptr<Market>& market, const std::string& name,
6263
const std::string& configuration = Market::defaultConfiguration);

QuantExt/qle/pricingengines/discountingfxforwardengine.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,17 @@
2323

2424
namespace QuantExt {
2525

26-
DiscountingFxForwardEngine::DiscountingFxForwardEngine(const Currency& ccy1,
27-
const Handle<YieldTermStructure>& currency1Discountcurve,
28-
const Currency& ccy2,
29-
const Handle<YieldTermStructure>& currency2Discountcurve,
30-
const Handle<Quote>& spotFX, const Date& settlementDate,
31-
const Date& npvDate)
26+
DiscountingFxForwardEngine::DiscountingFxForwardEngine(
27+
const Currency& ccy1, const Handle<YieldTermStructure>& currency1Discountcurve, const Currency& ccy2,
28+
const Handle<YieldTermStructure>& currency2Discountcurve, const Handle<Quote>& spotFX, const Date& settlementDate,
29+
const Date& npvDate, const Handle<YieldTermStructure>& discountCurve)
3230
: ccy1_(ccy1), currency1Discountcurve_(currency1Discountcurve), ccy2_(ccy2),
3331
currency2Discountcurve_(currency2Discountcurve), spotFX_(spotFX), settlementDate_(settlementDate),
34-
npvDate_(npvDate) {
32+
npvDate_(npvDate), discountCurve_(discountCurve) {
3533
registerWith(currency1Discountcurve_);
3634
registerWith(currency2Discountcurve_);
3735
registerWith(spotFX_);
36+
registerWith(discountCurve_);
3837
}
3938

4039
void DiscountingFxForwardEngine::calculate() const {
@@ -148,7 +147,12 @@ void DiscountingFxForwardEngine::calculate() const {
148147
}
149148
results_.additionalResults["cashFlowResults"] = cashFlowResults;
150149

151-
results_.value = (tmpPayCurrency1 ? -1.0 : 1.0) * discFar / discNear * (tmpNominal1 / fx1 - tmpNominal2 / fx2);
150+
Real applicableDiscountFactor = discFar / discNear;
151+
if (!discountCurve_.empty()) {
152+
applicableDiscountFactor = discountCurve_->discount(arguments_.payDate) / discountCurve_->discount(npvDate);
153+
}
154+
155+
results_.value = (tmpPayCurrency1 ? -1.0 : 1.0) * applicableDiscountFactor * (tmpNominal1 / fx1 - tmpNominal2 / fx2);
152156

153157
results_.npv = Money(settleCcy, results_.value);
154158

@@ -159,6 +163,9 @@ void DiscountingFxForwardEngine::calculate() const {
159163
results_.additionalResults["discountFactor[2]"] = disc2far;
160164
results_.additionalResults["legNPV[1]"] = (tmpPayCurrency1 ? -1.0 : 1.0) * discFar / discNear * tmpNominal1 / fx1;
161165
results_.additionalResults["legNPV[2]"] = (tmpPayCurrency1 ? -1.0 : 1.0) * discFar / discNear * (-tmpNominal2 / fx2);
166+
if(!discountCurve_.empty()) {
167+
results_.additionalResults["applicableDiscountFactor"] = applicableDiscountFactor;
168+
}
162169

163170
// set notional
164171
if (arguments_.isPhysicallySettled) {

QuantExt/qle/pricingengines/discountingfxforwardengine.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,13 @@ class DiscountingFxForwardEngine : public FxForward::engine {
5959
\param npvDate
6060
Discount to this date. If not given the npv date
6161
is set to the evaluation date
62+
\param discountCurve
63+
optional discounting curve (in npv currency)
6264
*/
6365
DiscountingFxForwardEngine(const Currency& ccy1, const Handle<YieldTermStructure>& currency1Discountcurve,
6466
const Currency& ccy2, const Handle<YieldTermStructure>& currency2Discountcurve,
6567
const Handle<Quote>& spotFX, const Date& settlementDate = Date(),
66-
const Date& npvDate = Date());
68+
const Date& npvDate = Date(), const Handle<YieldTermStructure>& discountCurve = {});
6769

6870
void calculate() const override;
6971

@@ -85,6 +87,7 @@ class DiscountingFxForwardEngine : public FxForward::engine {
8587
QuantLib::ext::optional<bool> includeSettlementDateFlows_;
8688
Date settlementDate_;
8789
Date npvDate_;
90+
Handle<YieldTermStructure> discountCurve_;
8891
};
8992
} // namespace QuantExt
9093

0 commit comments

Comments
 (0)