[go: up one dir, main page]

1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 Copyright (C) 2022 Marcin Rybacki
4
5 This file is part of QuantLib, a free-software/open-source library
6 for financial quantitative analysts and developers - http://quantlib.org/
7
8 QuantLib is free software: you can redistribute it and/or modify it
9 under the terms of the QuantLib license. You should have received a
10 copy of the license along with this program; if not, please email
11 <quantlib-dev@lists.sf.net>. The license is also available online at
12 <http://quantlib.org/license.shtml>.
13
14 This program is distributed in the hope that it will be useful, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19#include "bondforward.hpp"
20#include "utilities.hpp"
21#include <ql/time/calendars/target.hpp>
22#include <ql/time/daycounters/actualactual.hpp>
23#include <ql/instruments/bondforward.hpp>
24#include <ql/instruments/bonds/fixedratebond.hpp>
25#include <ql/pricingengines/bond/discountingbondengine.hpp>
26
27using namespace QuantLib;
28using namespace boost::unit_test_framework;
29
30namespace bond_forward_test {
31
32 struct CommonVars {
33 // common data
34 Date today;
35 RelinkableHandle<YieldTermStructure> curveHandle;
36
37 // setup
38 CommonVars() {
39 today = Date(7, March, 2022);
40 Settings::instance().evaluationDate() = today;
41
42 curveHandle.linkTo(h: flatRate(today, forward: 0.0004977, dc: Actual365Fixed()));
43 }
44 };
45
46 ext::shared_ptr<Bond> buildBond(const Date &issue,
47 const Date &maturity,
48 Rate cpn) {
49 Schedule sch(issue, maturity, Period(Annual), TARGET(), Following, Following,
50 DateGeneration::Backward, false);
51
52 return ext::make_shared<FixedRateBond>(args: 2, args: 1.e5, args&: sch, args: std::vector<Rate>(1, cpn),
53 args: ActualActual(ActualActual::ISDA));
54 }
55
56 ext::shared_ptr<BondForward> buildBondForward(const ext::shared_ptr<Bond>& underlying,
57 const Handle<YieldTermStructure> &handle,
58 const Date& delivery,
59 Position::Type type) {
60 auto valueDt = handle->referenceDate();
61 return ext::make_shared<BondForward>(args&: valueDt, args: delivery, args&: type, args: 0.0, args: 2,
62 args: ActualActual(ActualActual::ISDA),
63 args: TARGET(), args: Following, args: underlying, args: handle, args: handle);
64 }
65}
66
67void BondForwardTest::testFuturesPriceReplication() {
68 BOOST_TEST_MESSAGE("Testing futures price replication...");
69
70 using namespace bond_forward_test;
71
72 CommonVars vars;
73
74 Real tolerance = 1.0e-2;
75
76 Date issue(15, August, 2015);
77 Date maturity(15, August, 2046);
78 Rate cpn = 0.025;
79
80 auto bnd = buildBond(issue, maturity, cpn);
81 auto pricer = ext::make_shared<DiscountingBondEngine>(args&: vars.curveHandle);
82 bnd->setPricingEngine(pricer);
83
84 Date delivery(10, March, 2022);
85 Real conversionFactor = 0.76871;
86 auto bndFwd = buildBondForward(underlying: bnd, handle: vars.curveHandle, delivery, type: Position::Long);
87
88 auto futuresPrice = bndFwd->cleanForwardPrice() / conversionFactor;
89 auto expectedFuturesPrice = 207.47;
90
91 if (std::fabs(x: futuresPrice - expectedFuturesPrice) > tolerance)
92 BOOST_ERROR("unable to replicate bond futures price\n"
93 << std::setprecision(5) << " calculated: " << futuresPrice << "\n"
94 << " expected: " << expectedFuturesPrice << "\n");
95}
96
97void BondForwardTest::testCleanForwardPriceReplication() {
98 BOOST_TEST_MESSAGE("Testing clean forward price replication...");
99
100 using namespace bond_forward_test;
101
102 CommonVars vars;
103
104 Real tolerance = 1.0e-2;
105
106 Date issue(15, August, 2015);
107 Date maturity(15, August, 2046);
108 Rate cpn = 0.025;
109
110 auto bnd = buildBond(issue, maturity, cpn);
111 auto pricer = ext::make_shared<DiscountingBondEngine>(args&: vars.curveHandle);
112 bnd->setPricingEngine(pricer);
113
114 Date delivery(10, March, 2022);
115 auto bndFwd = buildBondForward(underlying: bnd, handle: vars.curveHandle, delivery, type: Position::Long);
116
117 auto fwdCleanPrice = bndFwd->cleanForwardPrice();
118 auto expectedFwdCleanPrice = bndFwd->forwardValue() - bnd->accruedAmount(d: delivery);
119
120 if (std::fabs(x: fwdCleanPrice - expectedFwdCleanPrice) > tolerance)
121 BOOST_ERROR("unable to replicate clean forward price\n"
122 << std::setprecision(5) << " calculated: " << fwdCleanPrice << "\n"
123 << " expected: " << expectedFwdCleanPrice << "\n");
124}
125
126void BondForwardTest::testThatForwardValueIsEqualToSpotValueIfNoIncome() {
127 BOOST_TEST_MESSAGE(
128 "Testing that forward value is equal to spot value if no income...");
129
130 using namespace bond_forward_test;
131
132 CommonVars vars;
133
134 Real tolerance = 1.0e-2;
135
136 Date issue(15, August, 2015);
137 Date maturity(15, August, 2046);
138 Rate cpn = 0.025;
139
140 auto bnd = buildBond(issue, maturity, cpn);
141 auto pricer = ext::make_shared<DiscountingBondEngine>(args&: vars.curveHandle);
142 bnd->setPricingEngine(pricer);
143
144 Date delivery(10, March, 2022);
145 auto bndFwd = buildBondForward(underlying: bnd, handle: vars.curveHandle, delivery, type: Position::Long);
146
147 auto bndFwdValue = bndFwd->forwardValue();
148 auto underlyingDirtyPrice = bnd->dirtyPrice();
149
150 if (std::fabs(x: bndFwdValue - underlyingDirtyPrice) > tolerance)
151 BOOST_ERROR("unable to match the dirty price \n"
152 << std::setprecision(5) << " bond forward: " << bndFwdValue << "\n"
153 << " underlying bond: " << underlyingDirtyPrice << "\n");
154}
155
156test_suite* BondForwardTest::suite() {
157 auto* suite = BOOST_TEST_SUITE("Bond forward tests");
158
159 suite->add(QUANTLIB_TEST_CASE(&BondForwardTest::testFuturesPriceReplication));
160 suite->add(QUANTLIB_TEST_CASE(&BondForwardTest::testCleanForwardPriceReplication));
161 suite->add(QUANTLIB_TEST_CASE(
162 &BondForwardTest::testThatForwardValueIsEqualToSpotValueIfNoIncome));
163 return suite;
164}

source code of quantlib/test-suite/bondforward.cpp