Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extended yield curves by cheapesttodeliver #228

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 88 additions & 2 deletions OREData/ored/configuration/yieldcurveconfig.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
Copyright (C) 2016 Quaternion Risk Management Ltd
Copyright (C) 2023 Oleg Kulkov
Copyright (C) 2023,2024 Oleg Kulkov
All rights reserved.

This file is part of ORE, a free-software/open-source library
Expand Down Expand Up @@ -72,6 +72,8 @@ YieldCurveSegment::Type parseYieldCurveSegment(const string& s) {
return YieldCurveSegment::Type::DiscountRatio;
else if (iequals(s, "FittedBond"))
return YieldCurveSegment::Type::FittedBond;
else if (iequals(s, "Cheapest To Deliver"))
return YieldCurveSegment::Type::CheapestToDeliver;
else if (iequals(s, "Yield Plus Default"))
return YieldCurveSegment::Type::YieldPlusDefault;
else if (iequals(s, "Weighted Average"))
Expand All @@ -93,6 +95,7 @@ class SegmentIDGetter : public AcyclicVisitor,
public Visitor<DiscountRatioYieldCurveSegment>,
public Visitor<FittedBondYieldCurveSegment>,
public Visitor<WeightedAverageYieldCurveSegment>,
public Visitor<CheapestToDeliverCurveSegment>,
public Visitor<YieldPlusDefaultYieldCurveSegment>,
public Visitor<BondYieldShiftedYieldCurveSegment>,
public Visitor<IborFallbackCurveSegment> {
Expand All @@ -112,6 +115,7 @@ class SegmentIDGetter : public AcyclicVisitor,
void visit(WeightedAverageYieldCurveSegment& s) override;
void visit(YieldPlusDefaultYieldCurveSegment& s) override;
void visit(BondYieldShiftedYieldCurveSegment& s) override;
void visit(CheapestToDeliverCurveSegment& s) override;
void visit(IborFallbackCurveSegment& s) override;

private:
Expand Down Expand Up @@ -193,6 +197,15 @@ void SegmentIDGetter::visit(BondYieldShiftedYieldCurveSegment& s) {
requiredCurveIds_[CurveSpec::CurveType::Yield].insert(s.referenceCurveID());
}

void SegmentIDGetter::visit(CheapestToDeliverCurveSegment& s) {
for (auto const& c : s.ctdCurves()) {
string cname = c.second;
if (curveID_ != cname && !cname.empty()) {
requiredCurveIds_[CurveSpec::CurveType::Yield].insert(cname);
}
}
}

void SegmentIDGetter::visit(WeightedAverageYieldCurveSegment& s) {
string aCurveID1 = s.referenceCurveID1();
string aCurveID2 = s.referenceCurveID2();
Expand Down Expand Up @@ -281,6 +294,8 @@ void YieldCurveConfig::fromXML(XMLNode* node) {
segment.reset(new BondYieldShiftedYieldCurveSegment());
} else if (childName == "WeightedAverage") {
segment.reset(new WeightedAverageYieldCurveSegment());
} else if (childName == "CheapestToDeliver") {
segment.reset(new CheapestToDeliverCurveSegment());
} else if (childName == "YieldPlusDefault") {
segment.reset(new YieldPlusDefaultYieldCurveSegment());
} else if(childName == "IborFallback"){
Expand Down Expand Up @@ -399,7 +414,8 @@ void YieldCurveSegment::fromXML(XMLNode* node) {
{"WeightedAverage", {"Weighted Average"}},
{"DiscountRatio", {"Discount Ratio"}},
{"IborFallback", {"Ibor Fallback"}},
{"BondYieldShifted", {"Bond Yield Shifted"}}
{"BondYieldShifted", {"Bond Yield Shifted"}},
{"CheapestToDeliver", {"Cheapest To Deliver"}}
};

std::list<std::string> validTypes = validSegmentTypes.at(name);
Expand Down Expand Up @@ -820,6 +836,76 @@ void YieldPlusDefaultYieldCurveSegment::accept(AcyclicVisitor& v) {
YieldCurveSegment::accept(v);
}

void CheapestToDeliverCurveSegment::fromXML(XMLNode* node) {
XMLUtils::checkNode(node, "CheapestToDeliver");
YieldCurveSegment::fromXML(node);
vector<string> ctdCurveCcys;
vector<string> ctdCurves = XMLUtils::getChildrenValuesWithAttributes(
node, "CollateralCurves", "CollateralCurve", "currency", ctdCurveCcys, true);
for (Size i = 0; i < ctdCurveCcys.size(); ++i) {
ctdCurves_[ctdCurveCcys[i]] = ctdCurves[i];
}

vector<string> ctSpreadsCcy;
vector<string> ctdSpreads = XMLUtils::getChildrenValuesWithAttributes(
node, "CollateralSpreads", "CollateralSpread", "currency", ctSpreadsCcy, true);

//collect information on the schedule for the final curve grid
XMLNode* pillarGenNode = XMLUtils::getChildNode(node, "PillarsGeneration");
rule_ = parseBool(XMLUtils::getChildValue(pillarGenNode, "Rule", false, "false"));

if (rule_) {

vector<string> grid;
vector<string> tenors = XMLUtils::getChildrenValuesWithAttributes(
pillarGenNode, "Periods", "Period", "maxTenor", grid, true);
for (Size i = 0; i < grid.size(); ++i) {
Period tmpMaxPeriod = parsePeriod(grid[i]);
Period tmpTenor = parsePeriod(tenors[i]);
if (tmpMaxPeriod > tmpTenor) {
periods_.push_back(std::make_pair(tmpMaxPeriod,tmpTenor));
} else {
ALOG("Period " << tmpMaxPeriod << " must not be longer than maximum tenor " << tmpTenor);
}
}
} else {
vector<std::string> pillars = XMLUtils::getChildrenValuesAsStrings(pillarGenNode, "Pillars", true);
pillars_ = parseVectorOfValues<Period>(pillars, &parsePeriod);

}

for (Size i = 0; i < ctdSpreads.size(); ++i) {
ctdSpreads_[ctSpreadsCcy[i]] = parseReal(ctdSpreads[i]);
auto it = ctdCurves_.find(ctSpreadsCcy[i]);
if (it == ctdCurves_.end()) {
QL_FAIL("CTD spread for currency " << ctdCurveCcys[i] << " is not defined in Spreads.");
}
}

QL_REQUIRE(ctdCurves_.size() == ctdSpreads_.size(), "size of ctd spreads " << ctdSpreads_.size() << " does not correspond to size of ctd curves " << ctdCurves_.size());

}

XMLNode* CheapestToDeliverCurveSegment::toXML(XMLDocument& doc) {
XMLNode* node = YieldCurveSegment::toXML(doc);
XMLUtils::setNodeName(doc, node, "CheapestToDeliver");
//XMLUtils::addChild(doc, node, "CCBasisIncluded", ccBasisIncluded_);
XMLUtils::addChild(doc, node, "CollateralCurves");
XMLUtils::addChild(doc, node, "CollateralSpreads");
XMLUtils::addGenericChildAsList(doc, node, "Pillars", pillars_);
XMLUtils::addChild(doc, node, "Conventions");
XMLUtils::addChild(doc, node, "PillarsGeneration");
return node;
}

void CheapestToDeliverCurveSegment::accept(AcyclicVisitor& v) {
Visitor<CheapestToDeliverCurveSegment>* v1 = dynamic_cast<Visitor<CheapestToDeliverCurveSegment>*>(&v);
if (v1 != 0)
v1->visit(*this);
else
YieldCurveSegment::accept(v);
}

IborFallbackCurveSegment::IborFallbackCurveSegment(const string& typeID, const string& iborIndex,
const string& rfrCurve, const boost::optional<string>& rfrIndex,
const boost::optional<Real>& spread)
Expand Down
55 changes: 53 additions & 2 deletions OREData/ored/configuration/yieldcurveconfig.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
Copyright (C) 2016 Quaternion Risk Management Ltd
Copyright (C) 2023 Oleg Kulkov
Copyright (C) 2023,2024 Oleg Kulkov
All rights reserved.

This file is part of ORE, a free-software/open-source library
Expand Down Expand Up @@ -79,7 +79,8 @@ class YieldCurveSegment : public XMLSerializable {
WeightedAverage,
YieldPlusDefault,
IborFallback,
BondYieldShifted
BondYieldShifted,
CheapestToDeliver
};
//! Default destructor
virtual ~YieldCurveSegment() {}
Expand Down Expand Up @@ -517,6 +518,56 @@ class DiscountRatioYieldCurveSegment : public YieldCurveSegment {
std::string denominatorCurveCurrency_;
};

//! Cheapest To Deliver curve segment
/*! Used to configure a QuantExt::Type::CheapestToDeliver.
\ingroup configuration
*/
class CheapestToDeliverCurveSegment : public YieldCurveSegment {
public:
//! \name Constructors/Destructors
//@{
//! Default constructor
CheapestToDeliverCurveSegment() {}
//! Detailed constructor
CheapestToDeliverCurveSegment(const std::string& typeId,
const bool ccBasisIncluded,
const map<std::string, std::string>& ctdCurvesId,
const std::vector<Period> pillars,
const vector<Real>& ctdSpreads);
//@}

//! \name Serialisation
//@{
virtual void fromXML(XMLNode* node) override;
virtual XMLNode* toXML(XMLDocument& doc) override;
//@}

//! \name Inspectors
//@{
//const bool CCBasisIncluded() const { return ccBasisIncluded_; }
const map<std::string, std::string>& ctdCurves() const { return ctdCurves_; }
const std::vector<Period> pillars() { return pillars_; }
const map<std::string, Real>& ctdSpreads() const { return ctdSpreads_; }
const bool getRule() { return rule_; }
const std::vector<pair<Period, Period>> getPeriods() { return periods_; }

//@}

//! \name Visitability
//@{
void accept(QuantLib::AcyclicVisitor& v) override;
//@}

private:
//bool ccBasisIncluded_;
map<std::string, std::string> ctdCurves_;
std::vector<pair<Period, Period>> periods_;
std::vector<Period> pillars_;
map<std::string, Real> ctdSpreads_;
bool rule_;

};

//! FittedBond yield curve segment
/*!
A bond segment is used to build a yield curve from liquid bond quotes.
Expand Down
Loading