Skip to content

Instantly share code, notes, and snippets.

@tomwhoiscontrary
Last active June 11, 2018 16:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomwhoiscontrary/38ee94f22e1a13428fa1d8d5868317ca to your computer and use it in GitHub Desktop.
Save tomwhoiscontrary/38ee94f22e1a13428fa1d8d5868317ca to your computer and use it in GitHub Desktop.
Trying to make a curve by blending futures and swaps - see https://imgur.com/a/viMGk6J
#include <iostream>
#include <vector>
#include <boost/make_shared.hpp>
#include <ql/indexes/ibor/euribor.hpp>
#include <ql/math/interpolations/convexmonotoneinterpolation.hpp>
#include <ql/termstructures/yield/piecewiseyieldcurve.hpp>
#include <ql/termstructures/yield/ratehelpers.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <ql/time/daycounters/thirty360.hpp>
#include <ql/types.hpp>
using QuantLib::Actual360;
using QuantLib::BusinessDayConvention;
using QuantLib::ConvexMonotone;
using QuantLib::Date;
using QuantLib::DayCounter;
using QuantLib::Euribor;
using QuantLib::ForwardRate;
using QuantLib::Frequency;
using QuantLib::FuturesRateHelper;
using QuantLib::Handle;
using QuantLib::IborIndex;
using QuantLib::InterpolatedForwardCurve;
using QuantLib::Month;
using QuantLib::Period;
using QuantLib::PiecewiseYieldCurve;
using QuantLib::Quote;
using QuantLib::RateHelper;
using QuantLib::Real;
using QuantLib::Settings;
using QuantLib::SwapRateHelper;
using QuantLib::Thirty360;
using QuantLib::TimeUnit;
using QuantLib::YieldTermStructure;
Month mth(unsigned int monthNumber) { return static_cast<Month>(monthNumber); }
class NeutralConvexMonotone : public ConvexMonotone {
public:
NeutralConvexMonotone(Real quadraticity = 0.3, Real monotonicity = 0.7, bool forcePositive = false)
: ConvexMonotone(quadraticity, monotonicity, forcePositive) {}
};
/*
In the shell:
blend > blend.csv
gnuplot
In gnuplot:
set datafile separator ","
set key autotitle columnhead
set xdata time
set timefmt '%Y-%m-%d'
set format x '%Y-%m-%d'
plot "tmp/blend.csv" using 1:2 with lines, \
"tmp/blend.csv" using 1:3 with lines, \
"tmp/blend.csv" using 1:4 with lines
*/
int main(int argc, char** argv) {
Period blendGap = Period(argc > 1 ? std::stoi(argv[1]) : 0, TimeUnit::Months);
Date today = Date(11, Month::June, 2018);
Settings::instance().evaluationDate() = today;
DayCounter curveDayCounter = Actual360();
// DISCOUNTING CURVE
std::map<Date, double> discountingCurveNodes =
{{Date(11, mth(6), 2018), -0.00355584210537288}, {Date(20, mth(6), 2018), -0.00355584210537288},
{Date(13, mth(7), 2018), -0.00354847355117201}, {Date(13, mth(9), 2018), -0.00347811838208307},
{Date(13, mth(12), 2018), -0.0035046853117356}, {Date(13, mth(3), 2019), -0.00359597753246646},
{Date(13, mth(6), 2019), -0.00336488221017208}, {Date(13, mth(9), 2019), -0.00283900803118639},
{Date(13, mth(12), 2019), -0.00226724478999542}, {Date(13, mth(3), 2020), -0.00126475754837997},
{Date(15, mth(6), 2020), -4.04082576520525E-4}, {Date(14, mth(6), 2021), 0.00193549877791657},
{Date(13, mth(6), 2022), 0.00541515029337548}, {Date(13, mth(6), 2023), 0.00834596171638419},
{Date(13, mth(6), 2024), 0.0105506898899723}, {Date(13, mth(6), 2025), 0.0127289184432656},
{Date(15, mth(6), 2026), 0.0146859781244856}, {Date(14, mth(6), 2027), 0.0160839038520465},
{Date(13, mth(6), 2028), 0.0171039813057311}, {Date(13, mth(6), 2033), 0.0190037548450844},
{Date(14, mth(6), 2038), 0.0188138594071705}, {Date(15, mth(6), 2043), 0.0162799102512021},
{Date(15, mth(6), 2048), 0.0147848694988243}, {Date(13, mth(6), 2058), 0.0140576373915513},
{Date(13, mth(6), 2068), 0.0120862578337932}};
std::vector<Date> discountingCurveDates;
std::vector<double> discountingCurveRates;
for (std::pair<const Date, double> dateAndRate : discountingCurveNodes) {
discountingCurveDates.push_back(dateAndRate.first);
discountingCurveRates.push_back(dateAndRate.second);
}
boost::shared_ptr<YieldTermStructure> discountingCurve =
boost::make_shared<InterpolatedForwardCurve<NeutralConvexMonotone>>(discountingCurveDates,
discountingCurveRates,
curveDayCounter,
NeutralConvexMonotone());
Handle<YieldTermStructure> discountingCurveHandle(discountingCurve);
// FORWARD CURVES
Date blendDate = today + Period(2, TimeUnit::Years);
boost::shared_ptr<IborIndex> index =
boost::make_shared<Euribor>(Period(3, TimeUnit::Months), Handle<YieldTermStructure>());
std::vector<boost::shared_ptr<RateHelper>> futuresCurveHelpers;
std::vector<boost::shared_ptr<RateHelper>> swapsCurveHelpers;
std::vector<boost::shared_ptr<RateHelper>> blendedCurveHelpers;
// FUTURES
std::map<Date, std::pair<double, double>> futurePrices =
{{Date(18, mth(6), 2018), {100.317582176, 6.51945329655112E-7}}, // these are fixing days, not IMM days
{Date(17, mth(9), 2018), {100.29345811, 1.66050681924613E-5}},
{Date(17, mth(12), 2018), {100.26624303, 2.88897080688881E-5}},
{Date(18, mth(3), 2019), {100.242551023, 2.91980601922156E-5}},
{Date(17, mth(6), 2019), {100.184444922, 2.66410283674007E-5}},
{Date(16, mth(9), 2019), {100.109963131, 2.89883148058077E-5}},
{Date(16, mth(12), 2019), {100.017761623, 2.9163458982448E-5}},
{Date(16, mth(3), 2020), {99.926027142, 2.93675715979597E-5}},
{Date(15, mth(6), 2020), {99.832434961, 3.35612290842065E-5}},
{Date(14, mth(9), 2020), {99.730683903, 4.63643156983516E-5}},
{Date(14, mth(12), 2020), {99.629396539, 7.17604585175422E-5}},
{Date(15, mth(3), 2021), {99.528613094, 1.14210609420962E-4}},
{Date(14, mth(6), 2021), {99.429723314, 1.77795812011371E-4}},
{Date(13, mth(9), 2021), {99.331323697, 2.66221336240436E-4}},
{Date(13, mth(12), 2021), {99.239333333, 3.83024162904268E-4}},
{Date(14, mth(3), 2022), {99.155351288, 5.30713049104974E-4}},
{Date(13, mth(6), 2022), {99.0725, 7.09806526725978E-4}},
{Date(19, mth(9), 2022), {98.9875, 8.04726635393628E-4}},
{Date(19, mth(12), 2022), {98.905, 8.98671630673705E-4}},
{Date(13, mth(3), 2023), {98.835, 9.90531253531082E-4}},
{Date(19, mth(6), 2023), {98.76, 0.00110267836537918}},
{Date(18, mth(9), 2023), {98.6875, 0.00121159386949753}},
{Date(18, mth(12), 2023), {98.62, 0.0013262637001}},
{Date(18, mth(3), 2024), {98.545, 0.00144677326172775}}};
for (std::pair<const Date, std::pair<double, double>> dateAndPriceAndConvexity : futurePrices) {
Date expiryDate = dateAndPriceAndConvexity.first;
double price = dateAndPriceAndConvexity.second.first;
double convexity = dateAndPriceAndConvexity.second.second;
Date valueDate = index->valueDate(expiryDate);
boost::shared_ptr<FuturesRateHelper> futureHelper =
boost::make_shared<FuturesRateHelper>(price, valueDate, index, convexity);
futuresCurveHelpers.push_back(futureHelper);
if (futureHelper->pillarDate() < blendDate - blendGap) {
blendedCurveHelpers.push_back(futureHelper);
}
}
// SWAPS
std::map<Period, double> swapRates = {{Period(1, TimeUnit::Years), -0.002873},
{Period(2, TimeUnit::Years), -0.001762},
{Period(3, TimeUnit::Years), -1.45E-4},
{Period(4, TimeUnit::Years), 0.001585},
{Period(5, TimeUnit::Years), 0.0032175},
{Period(6, TimeUnit::Years), 0.004705},
{Period(7, TimeUnit::Years), 0.0060575},
{Period(8, TimeUnit::Years), 0.007295},
{Period(9, TimeUnit::Years), 0.008395},
{Period(10, TimeUnit::Years), 0.0093975},
{Period(12, TimeUnit::Years), 0.0110675},
{Period(15, TimeUnit::Years), 0.012865},
{Period(20, TimeUnit::Years), 0.014465},
{Period(25, TimeUnit::Years), 0.014995},
{Period(30, TimeUnit::Years), 0.015135},
{Period(40, TimeUnit::Years), 0.01509},
{Period(50, TimeUnit::Years), 0.0148}};
for (std::pair<const Period, double> tenorAndRate : swapRates) {
Period tenor = tenorAndRate.first;
double rate = tenorAndRate.second;
boost::shared_ptr<SwapRateHelper> swapHelper =
boost::make_shared<SwapRateHelper>(rate,
tenor,
index->fixingCalendar(),
Frequency::Annual,
BusinessDayConvention::ModifiedFollowing,
Thirty360(),
index,
Handle<Quote>(),
Period(0, TimeUnit::Days),
discountingCurveHandle);
swapsCurveHelpers.push_back(swapHelper);
if (swapHelper->pillarDate() >= blendDate) {
blendedCurveHelpers.push_back(swapHelper);
}
}
boost::shared_ptr<PiecewiseYieldCurve<ForwardRate, NeutralConvexMonotone>> futuresCurve =
boost::make_shared<PiecewiseYieldCurve<ForwardRate, NeutralConvexMonotone>>(today,
futuresCurveHelpers,
curveDayCounter);
boost::shared_ptr<PiecewiseYieldCurve<ForwardRate, NeutralConvexMonotone>> swapsCurve =
boost::make_shared<PiecewiseYieldCurve<ForwardRate, NeutralConvexMonotone>>(today,
swapsCurveHelpers,
curveDayCounter);
boost::shared_ptr<PiecewiseYieldCurve<ForwardRate, NeutralConvexMonotone>> blendedCurve =
boost::make_shared<PiecewiseYieldCurve<ForwardRate, NeutralConvexMonotone>>(today,
blendedCurveHelpers,
curveDayCounter);
// READOUT
std::cout << "date, futures, swaps, blended" << '\n';
for (Date date = futuresCurve->dates().front(); date <= futuresCurve->maxDate();
date = index->fixingCalendar().advance(date, 1, TimeUnit::Days)) {
std::cout << QuantLib::io::iso_date(date) //
<< ", " << futuresCurve->forwardRate(date, date, curveDayCounter, QuantLib::Compounding::Simple).rate() //
<< ", " << swapsCurve->forwardRate(date, date, curveDayCounter, QuantLib::Compounding::Simple).rate() //
<< ", " << blendedCurve->forwardRate(date, date, curveDayCounter, QuantLib::Compounding::Simple).rate() //
<< '\n';
}
}
mkdir -p blend_output
for g in 0 3 6 9 12
do
./blend $g >blend_output/blend.$g.csv
cat >blend_output/blend.$g.gnuplot <<-EOF
set datafile separator ","
set key autotitle columnhead
set xdata time
set timefmt '%Y-%m-%d'
set format x '%Y-%m-%d'
set terminal png size 800,600 enhanced font "Helvetica,9"
set output 'blend_output/blend.$g.png'
plot "blend_output/blend.$g.csv" using 1:2 with lines, \
"blend_output/blend.$g.csv" using 1:3 with lines, \
"blend_output/blend.$g.csv" using 1:4 with lines
EOF
gnuplot blend_output/blend.$g.gnuplot
done
ls -1 blend_output/*.png
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment