| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2008 Ferdinando Ametrano |
| 5 | Copyright (C) 2007, 2008 Laurent Hoffmann |
| 6 | Copyright (C) 2015, 2016 Michael von den Driesch |
| 7 | |
| 8 | This file is part of QuantLib, a free-software/open-source library |
| 9 | for financial quantitative analysts and developers - http://quantlib.org/ |
| 10 | |
| 11 | QuantLib is free software: you can redistribute it and/or modify it |
| 12 | under the terms of the QuantLib license. You should have received a |
| 13 | copy of the license along with this program; if not, please email |
| 14 | <quantlib-dev@lists.sf.net>. The license is also available online at |
| 15 | <http://quantlib.org/license.shtml>. |
| 16 | |
| 17 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 18 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 19 | FOR A PARTICULAR PURPOSE. See the license for more details. |
| 20 | */ |
| 21 | |
| 22 | #include "optionletstripper.hpp" |
| 23 | #include "utilities.hpp" |
| 24 | #include <ql/termstructures/volatility/optionlet/optionletstripper1.hpp> |
| 25 | #include <ql/termstructures/volatility/optionlet/optionletstripper2.hpp> |
| 26 | #include <ql/termstructures/volatility/optionlet/strippedoptionletadapter.hpp> |
| 27 | #include <ql/termstructures/volatility/capfloor/constantcapfloortermvol.hpp> |
| 28 | #include <ql/termstructures/volatility/capfloor/capfloortermvolcurve.hpp> |
| 29 | #include <ql/termstructures/yield/flatforward.hpp> |
| 30 | #include <ql/termstructures/yield/zerocurve.hpp> |
| 31 | #include <ql/time/calendars/target.hpp> |
| 32 | #include <ql/indexes/ibor/euribor.hpp> |
| 33 | #include <ql/pricingengines/capfloor/blackcapfloorengine.hpp> |
| 34 | #include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp> |
| 35 | #include <ql/instruments/makecapfloor.hpp> |
| 36 | #include <ql/quotes/simplequote.hpp> |
| 37 | #include <algorithm> |
| 38 | #include <iterator> |
| 39 | |
| 40 | using namespace QuantLib; |
| 41 | using namespace boost::unit_test_framework; |
| 42 | |
| 43 | namespace optionlet_stripper_test { |
| 44 | |
| 45 | struct CommonVars { |
| 46 | // global data |
| 47 | Calendar calendar; |
| 48 | DayCounter dayCounter; |
| 49 | |
| 50 | RelinkableHandle<YieldTermStructure> yieldTermStructure; |
| 51 | RelinkableHandle< YieldTermStructure > discountingYTS; |
| 52 | RelinkableHandle< YieldTermStructure > forwardingYTS; |
| 53 | |
| 54 | std::vector<Rate> strikes; |
| 55 | std::vector<Period> optionTenors; |
| 56 | Matrix termV; |
| 57 | std::vector<Rate> atmTermV; |
| 58 | std::vector<Handle<Quote> > atmTermVolHandle; |
| 59 | |
| 60 | Handle<CapFloorTermVolCurve> capFloorVolCurve; |
| 61 | Handle<CapFloorTermVolCurve> flatTermVolCurve; |
| 62 | |
| 63 | ext::shared_ptr<CapFloorTermVolSurface> capFloorVolSurface; |
| 64 | ext::shared_ptr<CapFloorTermVolSurface> flatTermVolSurface; |
| 65 | ext::shared_ptr< CapFloorTermVolSurface > capFloorVolRealSurface; |
| 66 | |
| 67 | Real accuracy; |
| 68 | Real tolerance; |
| 69 | |
| 70 | CommonVars() { |
| 71 | accuracy = 1.0e-6; |
| 72 | tolerance = 2.5e-8; |
| 73 | } |
| 74 | |
| 75 | void setTermStructure() { |
| 76 | |
| 77 | calendar = TARGET(); |
| 78 | dayCounter = Actual365Fixed(); |
| 79 | |
| 80 | Rate flatFwdRate = 0.04; |
| 81 | yieldTermStructure.linkTo( |
| 82 | h: ext::make_shared<FlatForward>(args: 0, |
| 83 | args&: calendar, |
| 84 | args&: flatFwdRate, |
| 85 | args&: dayCounter)); |
| 86 | } |
| 87 | |
| 88 | void setRealTermStructure() { |
| 89 | |
| 90 | calendar = TARGET(); |
| 91 | dayCounter = Actual365Fixed(); |
| 92 | |
| 93 | std::vector< int > datesTmp = { |
| 94 | 42124, 42129, 42143, 42221, 42254, 42282, 42313, 42345, |
| 95 | 42374, 42405, 42465, 42495, 42587, 42681, 42772, 42860, 43227, |
| 96 | 43956, 44321, 44686, 45051, 45418, 45782, 46147, 46512, 47609, |
| 97 | 49436, 51263, 53087, 56739, 60392 |
| 98 | }; |
| 99 | |
| 100 | std::vector< Date > dates; |
| 101 | dates.reserve(n: datesTmp.size()); |
| 102 | for (int& it : datesTmp) |
| 103 | dates.emplace_back(args&: it); |
| 104 | |
| 105 | std::vector< Rate > rates = { |
| 106 | -0.00292, -0.00292, -0.001441, -0.00117, -0.001204, |
| 107 | -0.001212, -0.001223, -0.001236, -0.001221, -0.001238, |
| 108 | -0.001262, -0.00125, -0.001256, -0.001233, -0.00118, -0.001108, |
| 109 | -0.000619, 0.000833, 0.001617, 0.002414, 0.003183, 0.003883, |
| 110 | 0.004514, 0.005074, 0.005606, 0.006856, 0.00813, 0.008709, |
| 111 | 0.009136, 0.009601, 0.009384 |
| 112 | }; |
| 113 | |
| 114 | discountingYTS.linkTo( |
| 115 | h: ext::make_shared< InterpolatedZeroCurve< Linear > >( |
| 116 | args&: dates, args&: rates, |
| 117 | args&: dayCounter, args&: calendar)); |
| 118 | |
| 119 | datesTmp.clear(); |
| 120 | dates.clear(); |
| 121 | rates.clear(); |
| 122 | |
| 123 | datesTmp = { |
| 124 | 42124, 42313, 42436, 42556, 42618, 42800, 42830, 42860, |
| 125 | 43227, 43591, 43956, 44321, 44686, 45051, 45418, 45782, 46147, |
| 126 | 46512, 46878, 47245, 47609, 47973, 48339, 48704, 49069, 49436, |
| 127 | 49800, 50165, 50530, 50895, 51263, 51627, 51991, 52356, 52722, |
| 128 | 53087, 54913, 56739, 60392, 64045 |
| 129 | }; |
| 130 | |
| 131 | for (int& it : datesTmp) |
| 132 | dates.emplace_back(args&: it); |
| 133 | |
| 134 | rates = { |
| 135 | 0.000649, 0.000649, 0.000684, 0.000717, 0.000745, 0.000872, |
| 136 | 0.000905, 0.000954, 0.001532, 0.002319, 0.003147, 0.003949, |
| 137 | 0.004743, 0.00551, 0.006198, 0.006798, 0.007339, 0.007832, |
| 138 | 0.008242, 0.008614, 0.008935, 0.009205, 0.009443, 0.009651, |
| 139 | 0.009818, 0.009952, 0.010054, 0.010146, 0.010206, 0.010266, |
| 140 | 0.010315, 0.010365, 0.010416, 0.010468, 0.010519, 0.010571, |
| 141 | 0.010757, 0.010806, 0.010423, 0.010217 |
| 142 | }; |
| 143 | |
| 144 | forwardingYTS.linkTo( |
| 145 | h: ext::make_shared< InterpolatedZeroCurve< Linear > >( |
| 146 | args&: dates, args&: rates, args&: dayCounter, args&: calendar)); |
| 147 | } |
| 148 | |
| 149 | void setFlatTermVolCurve() { |
| 150 | |
| 151 | setTermStructure(); |
| 152 | |
| 153 | optionTenors.resize(new_size: 10); |
| 154 | for (Size i = 0; i < optionTenors.size(); ++i) |
| 155 | optionTenors[i] = Period(i + 1, Years); |
| 156 | |
| 157 | Volatility flatVol = .18; |
| 158 | |
| 159 | std::vector<Handle<Quote> > curveVHandle(optionTenors.size()); |
| 160 | for (Size i=0; i<optionTenors.size(); ++i) |
| 161 | curveVHandle[i] = Handle<Quote>(ext::shared_ptr<Quote>(new |
| 162 | SimpleQuote(flatVol))); |
| 163 | |
| 164 | flatTermVolCurve = Handle<CapFloorTermVolCurve>( |
| 165 | ext::make_shared<CapFloorTermVolCurve>(args: 0, args&: calendar, args: Following, args&: optionTenors, |
| 166 | args&: curveVHandle, args&: dayCounter)); |
| 167 | |
| 168 | } |
| 169 | |
| 170 | void setFlatTermVolSurface() { |
| 171 | |
| 172 | setTermStructure(); |
| 173 | |
| 174 | optionTenors.resize(new_size: 10); |
| 175 | for (Size i = 0; i < optionTenors.size(); ++i) |
| 176 | optionTenors[i] = Period(i + 1, Years); |
| 177 | |
| 178 | strikes.resize(new_size: 10); |
| 179 | for (Size j = 0; j < strikes.size(); ++j) |
| 180 | strikes[j] = Real(j + 1) / 100.0; |
| 181 | |
| 182 | Volatility flatVol = .18; |
| 183 | termV = Matrix(optionTenors.size(), strikes.size(), flatVol); |
| 184 | flatTermVolSurface = ext::make_shared<CapFloorTermVolSurface>(args: 0, args&: calendar, args: Following, |
| 185 | args&: optionTenors, args&: strikes, |
| 186 | args&: termV, args&: dayCounter); |
| 187 | } |
| 188 | |
| 189 | |
| 190 | void setCapFloorTermVolCurve() { |
| 191 | |
| 192 | setTermStructure(); |
| 193 | |
| 194 | //atm cap volatility curve |
| 195 | optionTenors = { |
| 196 | {1, Years}, |
| 197 | {18, Months}, |
| 198 | {2, Years}, |
| 199 | {3, Years}, |
| 200 | {4, Years}, |
| 201 | {5, Years}, |
| 202 | {6, Years}, |
| 203 | {7, Years}, |
| 204 | {8, Years}, |
| 205 | {9, Years}, |
| 206 | {10, Years}, |
| 207 | {12, Years}, |
| 208 | {15, Years}, |
| 209 | {20, Years}, |
| 210 | {25, Years}, |
| 211 | {30, Years} |
| 212 | }; |
| 213 | |
| 214 | //atm capfloor vols from mkt vol matrix using flat yield curve |
| 215 | atmTermV = { |
| 216 | 0.090304, |
| 217 | 0.12180, |
| 218 | 0.13077, |
| 219 | 0.14832, |
| 220 | 0.15570, |
| 221 | 0.15816, |
| 222 | 0.15932, |
| 223 | 0.16035, |
| 224 | 0.15951, |
| 225 | 0.15855, |
| 226 | 0.15754, |
| 227 | 0.15459, |
| 228 | 0.15163, |
| 229 | 0.14575, |
| 230 | 0.14175, |
| 231 | 0.13889 |
| 232 | }; |
| 233 | |
| 234 | atmTermVolHandle.resize(new_size: optionTenors.size()); |
| 235 | for (Size i=0; i<optionTenors.size(); ++i) { |
| 236 | atmTermVolHandle[i] = Handle<Quote>(ext::shared_ptr<Quote>(new |
| 237 | SimpleQuote(atmTermV[i]))); |
| 238 | } |
| 239 | |
| 240 | capFloorVolCurve = Handle<CapFloorTermVolCurve>( |
| 241 | ext::make_shared<CapFloorTermVolCurve>(args: 0, args&: calendar, args: Following, |
| 242 | args&: optionTenors, args&: atmTermVolHandle, |
| 243 | args&: dayCounter)); |
| 244 | |
| 245 | } |
| 246 | |
| 247 | void setCapFloorTermVolSurface() { |
| 248 | |
| 249 | setTermStructure(); |
| 250 | |
| 251 | //cap volatility smile matrix |
| 252 | optionTenors = { |
| 253 | {1, Years}, |
| 254 | {18, Months}, |
| 255 | {2, Years}, |
| 256 | {3, Years}, |
| 257 | {4, Years}, |
| 258 | {5, Years}, |
| 259 | {6, Years}, |
| 260 | {7, Years}, |
| 261 | {8, Years}, |
| 262 | {9, Years}, |
| 263 | {10, Years}, |
| 264 | {12, Years}, |
| 265 | {15, Years}, |
| 266 | {20, Years}, |
| 267 | {25, Years}, |
| 268 | {30, Years} |
| 269 | }; |
| 270 | |
| 271 | strikes = { |
| 272 | 0.015, |
| 273 | 0.0175, |
| 274 | 0.02, |
| 275 | 0.0225, |
| 276 | 0.025, |
| 277 | 0.03, |
| 278 | 0.035, |
| 279 | 0.04, |
| 280 | 0.05, |
| 281 | 0.06, |
| 282 | 0.07, |
| 283 | 0.08, |
| 284 | 0.1 |
| 285 | }; |
| 286 | |
| 287 | termV = Matrix(optionTenors.size(), strikes.size()); |
| 288 | termV[0][0]=0.287; termV[0][1]=0.274; termV[0][2]=0.256; termV[0][3]=0.245; termV[0][4]=0.227; termV[0][5]=0.148; termV[0][6]=0.096; termV[0][7]=0.09; termV[0][8]=0.11; termV[0][9]=0.139; termV[0][10]=0.166; termV[0][11]=0.19; termV[0][12]=0.214; |
| 289 | termV[1][0]=0.303; termV[1][1]=0.258; termV[1][2]=0.22; termV[1][3]=0.203; termV[1][4]=0.19; termV[1][5]=0.153; termV[1][6]=0.126; termV[1][7]=0.118; termV[1][8]=0.147; termV[1][9]=0.165; termV[1][10]=0.18; termV[1][11]=0.192; termV[1][12]=0.212; |
| 290 | termV[2][0]=0.303; termV[2][1]=0.257; termV[2][2]=0.216; termV[2][3]=0.196; termV[2][4]=0.182; termV[2][5]=0.154; termV[2][6]=0.134; termV[2][7]=0.127; termV[2][8]=0.149; termV[2][9]=0.166; termV[2][10]=0.18; termV[2][11]=0.192; termV[2][12]=0.212; |
| 291 | termV[3][0]=0.305; termV[3][1]=0.266; termV[3][2]=0.226; termV[3][3]=0.203; termV[3][4]=0.19; termV[3][5]=0.167; termV[3][6]=0.151; termV[3][7]=0.144; termV[3][8]=0.16; termV[3][9]=0.172; termV[3][10]=0.183; termV[3][11]=0.193; termV[3][12]=0.209; |
| 292 | termV[4][0]=0.294; termV[4][1]=0.261; termV[4][2]=0.216; termV[4][3]=0.201; termV[4][4]=0.19; termV[4][5]=0.171; termV[4][6]=0.158; termV[4][7]=0.151; termV[4][8]=0.163; termV[4][9]=0.172; termV[4][10]=0.181; termV[4][11]=0.188; termV[4][12]=0.201; |
| 293 | termV[5][0]=0.276; termV[5][1]=0.248; termV[5][2]=0.212; termV[5][3]=0.199; termV[5][4]=0.189; termV[5][5]=0.172; termV[5][6]=0.16; termV[5][7]=0.155; termV[5][8]=0.162; termV[5][9]=0.17; termV[5][10]=0.177; termV[5][11]=0.183; termV[5][12]=0.195; |
| 294 | termV[6][0]=0.26; termV[6][1]=0.237; termV[6][2]=0.21; termV[6][3]=0.198; termV[6][4]=0.188; termV[6][5]=0.172; termV[6][6]=0.161; termV[6][7]=0.156; termV[6][8]=0.161; termV[6][9]=0.167; termV[6][10]=0.173; termV[6][11]=0.179; termV[6][12]=0.19; |
| 295 | termV[7][0]=0.25; termV[7][1]=0.231; termV[7][2]=0.208; termV[7][3]=0.196; termV[7][4]=0.187; termV[7][5]=0.172; termV[7][6]=0.162; termV[7][7]=0.156; termV[7][8]=0.16; termV[7][9]=0.165; termV[7][10]=0.17; termV[7][11]=0.175; termV[7][12]=0.185; |
| 296 | termV[8][0]=0.244; termV[8][1]=0.226; termV[8][2]=0.206; termV[8][3]=0.195; termV[8][4]=0.186; termV[8][5]=0.171; termV[8][6]=0.161; termV[8][7]=0.156; termV[8][8]=0.158; termV[8][9]=0.162; termV[8][10]=0.166; termV[8][11]=0.171; termV[8][12]=0.18; |
| 297 | termV[9][0]=0.239; termV[9][1]=0.222; termV[9][2]=0.204; termV[9][3]=0.193; termV[9][4]=0.185; termV[9][5]=0.17; termV[9][6]=0.16; termV[9][7]=0.155; termV[9][8]=0.156; termV[9][9]=0.159; termV[9][10]=0.163; termV[9][11]=0.168; termV[9][12]=0.177; |
| 298 | termV[10][0]=0.235; termV[10][1]=0.219; termV[10][2]=0.202; termV[10][3]=0.192; termV[10][4]=0.183; termV[10][5]=0.169; termV[10][6]=0.159; termV[10][7]=0.154; termV[10][8]=0.154; termV[10][9]=0.156; termV[10][10]=0.16; termV[10][11]=0.164; termV[10][12]=0.173; |
| 299 | termV[11][0]=0.227; termV[11][1]=0.212; termV[11][2]=0.197; termV[11][3]=0.187; termV[11][4]=0.179; termV[11][5]=0.166; termV[11][6]=0.156; termV[11][7]=0.151; termV[11][8]=0.149; termV[11][9]=0.15; termV[11][10]=0.153; termV[11][11]=0.157; termV[11][12]=0.165; |
| 300 | termV[12][0]=0.22; termV[12][1]=0.206; termV[12][2]=0.192; termV[12][3]=0.183; termV[12][4]=0.175; termV[12][5]=0.162; termV[12][6]=0.153; termV[12][7]=0.147; termV[12][8]=0.144; termV[12][9]=0.144; termV[12][10]=0.147; termV[12][11]=0.151; termV[12][12]=0.158; |
| 301 | termV[13][0]=0.211; termV[13][1]=0.197; termV[13][2]=0.185; termV[13][3]=0.176; termV[13][4]=0.168; termV[13][5]=0.156; termV[13][6]=0.147; termV[13][7]=0.142; termV[13][8]=0.138; termV[13][9]=0.138; termV[13][10]=0.14; termV[13][11]=0.144; termV[13][12]=0.151; |
| 302 | termV[14][0]=0.204; termV[14][1]=0.192; termV[14][2]=0.18; termV[14][3]=0.171; termV[14][4]=0.164; termV[14][5]=0.152; termV[14][6]=0.143; termV[14][7]=0.138; termV[14][8]=0.134; termV[14][9]=0.134; termV[14][10]=0.137; termV[14][11]=0.14; termV[14][12]=0.148; |
| 303 | termV[15][0]=0.2; termV[15][1]=0.187; termV[15][2]=0.176; termV[15][3]=0.167; termV[15][4]=0.16; termV[15][5]=0.148; termV[15][6]=0.14; termV[15][7]=0.135; termV[15][8]=0.131; termV[15][9]=0.132; termV[15][10]=0.135; termV[15][11]=0.139; termV[15][12]=0.146; |
| 304 | |
| 305 | capFloorVolSurface = ext::make_shared<CapFloorTermVolSurface>(args: 0, args&: calendar, args: Following, |
| 306 | args&: optionTenors, args&: strikes, |
| 307 | args&: termV, args&: dayCounter); |
| 308 | } |
| 309 | |
| 310 | void setRealCapFloorTermVolSurface() { |
| 311 | |
| 312 | setRealTermStructure(); |
| 313 | |
| 314 | // cap volatility smile matrix |
| 315 | optionTenors = { |
| 316 | {1, Years}, |
| 317 | {18, Months}, |
| 318 | {2, Years}, |
| 319 | {3, Years}, |
| 320 | {4, Years}, |
| 321 | {5, Years}, |
| 322 | {6, Years}, |
| 323 | {7, Years}, |
| 324 | {8, Years}, |
| 325 | {9, Years}, |
| 326 | {10, Years}, |
| 327 | {12, Years}, |
| 328 | {15, Years}, |
| 329 | {20, Years}, |
| 330 | {25, Years}, |
| 331 | {30, Years} |
| 332 | }; |
| 333 | // 16 |
| 334 | |
| 335 | strikes = { |
| 336 | -0.005, |
| 337 | -0.0025, |
| 338 | -0.00125, |
| 339 | 0.0, |
| 340 | 0.00125, |
| 341 | 0.0025, |
| 342 | 0.005, |
| 343 | 0.01, |
| 344 | 0.015, |
| 345 | 0.02, |
| 346 | 0.03, |
| 347 | 0.05, |
| 348 | 0.1 |
| 349 | }; |
| 350 | // 13 |
| 351 | |
| 352 | std::vector< Real > rawVols = { |
| 353 | 0.49, 0.39, 0.34, 0.31, 0.34, 0.37, 0.50, 0.75, 0.99, 1.21, 1.64, 2.44, 4.29, |
| 354 | 0.44, 0.36, 0.33, 0.31, 0.33, 0.35,0.45, 0.65, 0.83, 1.00, 1.32, 1.93, 3.30, |
| 355 | 0.40, 0.35, 0.33,0.31, 0.33, 0.34, 0.41, 0.55, 0.69, 0.82, 1.08, 1.56, 2.68, |
| 356 | 0.42, 0.39, 0.38, 0.37, 0.38, 0.39, 0.43, 0.54, 0.64, 0.74,0.94, 1.31, 2.18, |
| 357 | 0.46, 0.43, 0.42, 0.41, 0.42, 0.43, 0.47,0.56, 0.66, 0.75, 0.93, 1.28, 2.07, |
| 358 | 0.49, 0.47, 0.46, 0.45,0.46, 0.47, 0.51, 0.59, 0.68, 0.76, 0.93, 1.25, 1.99, |
| 359 | 0.51, 0.49, 0.49, 0.48, 0.49, 0.50, 0.54, 0.62, 0.70, 0.78, 0.94,1.24, 1.94, |
| 360 | 0.52, 0.51, 0.51, 0.51, 0.52, 0.53, 0.56, 0.63,0.71, 0.79, 0.94, 1.23, 1.89, |
| 361 | 0.53, 0.52, 0.52, 0.52, 0.53,0.54, 0.57, 0.65, 0.72, 0.79, 0.94, 1.21, 1.83, |
| 362 | 0.55, 0.54, 0.54, 0.54, 0.55, 0.56, 0.59, 0.66, 0.72, 0.79, 0.91, 1.15,1.71, |
| 363 | 0.56, 0.56, 0.56, 0.56, 0.57, 0.58, 0.61, 0.67, 0.72,0.78, 0.89, 1.09, 1.59, |
| 364 | 0.59, 0.58, 0.58, 0.59, 0.59, 0.60,0.63, 0.68, 0.73, 0.78, 0.86, 1.03, 1.45, |
| 365 | 0.61, 0.61, 0.61,0.61, 0.62, 0.62, 0.64, 0.69, 0.73, 0.77, 0.85, 1.02, 1.44, |
| 366 | 0.62, 0.62, 0.63, 0.63, 0.64, 0.64, 0.65, 0.69, 0.72, 0.76,0.82, 0.96, 1.32, |
| 367 | 0.62, 0.63, 0.63, 0.63, 0.65, 0.66, 0.66,0.68, 0.72, 0.74, 0.80, 0.93, 1.25, |
| 368 | 0.62, 0.62, 0.62, 0.62,0.66, 0.67, 0.67, 0.67, 0.72, 0.72, 0.78, 0.90, 1.25 |
| 369 | }; |
| 370 | |
| 371 | termV = Matrix(optionTenors.size(), strikes.size()); |
| 372 | std::copy(first: rawVols.begin(), last: rawVols.end(), result: termV.begin()); |
| 373 | termV /= 100; |
| 374 | |
| 375 | capFloorVolRealSurface = |
| 376 | ext::make_shared< CapFloorTermVolSurface >( |
| 377 | args: 0, args&: calendar, args: Following, |
| 378 | args&: optionTenors, args&: strikes, args&: termV, |
| 379 | args&: dayCounter); |
| 380 | } |
| 381 | }; |
| 382 | } |
| 383 | |
| 384 | void OptionletStripperTest::testFlatTermVolatilityStripping1() { |
| 385 | |
| 386 | BOOST_TEST_MESSAGE( |
| 387 | "Testing forward/forward vol stripping from flat term vol " |
| 388 | "surface using OptionletStripper1 class..." ); |
| 389 | |
| 390 | using namespace optionlet_stripper_test; |
| 391 | |
| 392 | CommonVars vars; |
| 393 | Settings::instance().evaluationDate() = Date(28, October, 2013); |
| 394 | |
| 395 | vars.setFlatTermVolSurface(); |
| 396 | |
| 397 | ext::shared_ptr<IborIndex> iborIndex(new Euribor6M(vars.yieldTermStructure)); |
| 398 | |
| 399 | ext::shared_ptr<OptionletStripper> optionletStripper1(new |
| 400 | OptionletStripper1(vars.flatTermVolSurface, |
| 401 | iborIndex, |
| 402 | Null<Rate>(), |
| 403 | vars.accuracy)); |
| 404 | |
| 405 | ext::shared_ptr<StrippedOptionletAdapter> strippedOptionletAdapter(new |
| 406 | StrippedOptionletAdapter(optionletStripper1)); |
| 407 | |
| 408 | Handle<OptionletVolatilityStructure> vol(strippedOptionletAdapter); |
| 409 | |
| 410 | vol->enableExtrapolation(); |
| 411 | |
| 412 | ext::shared_ptr<BlackCapFloorEngine> strippedVolEngine(new |
| 413 | BlackCapFloorEngine(vars.yieldTermStructure, |
| 414 | vol)); |
| 415 | |
| 416 | ext::shared_ptr<CapFloor> cap; |
| 417 | for (Size tenorIndex=0; tenorIndex<vars.optionTenors.size(); ++tenorIndex) { |
| 418 | for (Size strikeIndex=0; strikeIndex<vars.strikes.size(); ++strikeIndex) { |
| 419 | cap = MakeCapFloor(CapFloor::Cap, |
| 420 | vars.optionTenors[tenorIndex], |
| 421 | iborIndex, |
| 422 | vars.strikes[strikeIndex], |
| 423 | 0*Days) |
| 424 | .withPricingEngine(engine: strippedVolEngine); |
| 425 | |
| 426 | Real priceFromStrippedVolatility = cap->NPV(); |
| 427 | |
| 428 | ext::shared_ptr<PricingEngine> blackCapFloorEngineConstantVolatility(new |
| 429 | BlackCapFloorEngine(vars.yieldTermStructure, |
| 430 | vars.termV[tenorIndex][strikeIndex])); |
| 431 | |
| 432 | cap->setPricingEngine(blackCapFloorEngineConstantVolatility); |
| 433 | Real priceFromConstantVolatility = cap->NPV(); |
| 434 | |
| 435 | Real error = std::fabs(x: priceFromStrippedVolatility - priceFromConstantVolatility); |
| 436 | if (error>vars.tolerance) |
| 437 | BOOST_FAIL("\noption tenor: " << vars.optionTenors[tenorIndex] << |
| 438 | "\nstrike: " << io::rate(vars.strikes[strikeIndex]) << |
| 439 | "\nstripped vol price: " << io::rate(priceFromStrippedVolatility) << |
| 440 | "\nconstant vol price: " << io::rate(priceFromConstantVolatility) << |
| 441 | "\nerror: " << io::rate(error) << |
| 442 | "\ntolerance: " << io::rate(vars.tolerance)); |
| 443 | } |
| 444 | } |
| 445 | } |
| 446 | |
| 447 | void OptionletStripperTest::testTermVolatilityStripping1() { |
| 448 | |
| 449 | BOOST_TEST_MESSAGE( |
| 450 | "Testing forward/forward vol stripping from non-flat term " |
| 451 | "vol surface using OptionletStripper1 class..." ); |
| 452 | |
| 453 | using namespace optionlet_stripper_test; |
| 454 | |
| 455 | CommonVars vars; |
| 456 | Settings::instance().evaluationDate() = Date(28, October, 2013); |
| 457 | |
| 458 | vars.setCapFloorTermVolSurface(); |
| 459 | |
| 460 | ext::shared_ptr<IborIndex> iborIndex(new Euribor6M(vars.yieldTermStructure)); |
| 461 | |
| 462 | ext::shared_ptr<OptionletStripper> optionletStripper1(new |
| 463 | OptionletStripper1(vars.capFloorVolSurface, |
| 464 | iborIndex, |
| 465 | Null<Rate>(), |
| 466 | vars.accuracy)); |
| 467 | |
| 468 | ext::shared_ptr<StrippedOptionletAdapter> strippedOptionletAdapter = |
| 469 | ext::make_shared<StrippedOptionletAdapter>(args&: optionletStripper1); |
| 470 | |
| 471 | Handle<OptionletVolatilityStructure> vol(strippedOptionletAdapter); |
| 472 | |
| 473 | vol->enableExtrapolation(); |
| 474 | |
| 475 | ext::shared_ptr<BlackCapFloorEngine> strippedVolEngine(new |
| 476 | BlackCapFloorEngine(vars.yieldTermStructure, |
| 477 | vol)); |
| 478 | |
| 479 | ext::shared_ptr<CapFloor> cap; |
| 480 | for (Size tenorIndex=0; tenorIndex<vars.optionTenors.size(); ++tenorIndex) { |
| 481 | for (Size strikeIndex=0; strikeIndex<vars.strikes.size(); ++strikeIndex) { |
| 482 | cap = MakeCapFloor(CapFloor::Cap, |
| 483 | vars.optionTenors[tenorIndex], |
| 484 | iborIndex, |
| 485 | vars.strikes[strikeIndex], |
| 486 | 0*Days) |
| 487 | .withPricingEngine(engine: strippedVolEngine); |
| 488 | |
| 489 | Real priceFromStrippedVolatility = cap->NPV(); |
| 490 | |
| 491 | ext::shared_ptr<PricingEngine> blackCapFloorEngineConstantVolatility(new |
| 492 | BlackCapFloorEngine(vars.yieldTermStructure, |
| 493 | vars.termV[tenorIndex][strikeIndex])); |
| 494 | |
| 495 | cap->setPricingEngine(blackCapFloorEngineConstantVolatility); |
| 496 | Real priceFromConstantVolatility = cap->NPV(); |
| 497 | |
| 498 | Real error = std::fabs(x: priceFromStrippedVolatility - priceFromConstantVolatility); |
| 499 | if (error>vars.tolerance) |
| 500 | BOOST_FAIL("\noption tenor: " << vars.optionTenors[tenorIndex] << |
| 501 | "\nstrike: " << io::rate(vars.strikes[strikeIndex]) << |
| 502 | "\nstripped vol price: " << io::rate(priceFromStrippedVolatility) << |
| 503 | "\nconstant vol price: " << io::rate(priceFromConstantVolatility) << |
| 504 | "\nerror: " << io::rate(error) << |
| 505 | "\ntolerance: " << io::rate(vars.tolerance)); |
| 506 | } |
| 507 | } |
| 508 | } |
| 509 | |
| 510 | void OptionletStripperTest::testTermVolatilityStrippingNormalVol() { |
| 511 | |
| 512 | BOOST_TEST_MESSAGE( |
| 513 | "Testing forward/forward vol stripping from non-flat normal vol term " |
| 514 | "vol surface for normal vol setup using OptionletStripper1 class..." ); |
| 515 | |
| 516 | using namespace optionlet_stripper_test; |
| 517 | |
| 518 | CommonVars vars; |
| 519 | Settings::instance().evaluationDate() = Date(30, April, 2015); |
| 520 | |
| 521 | vars.setRealCapFloorTermVolSurface(); |
| 522 | |
| 523 | ext::shared_ptr< IborIndex > iborIndex(new Euribor6M(vars.forwardingYTS)); |
| 524 | |
| 525 | ext::shared_ptr< OptionletStripper > optionletStripper1( |
| 526 | new OptionletStripper1(vars.capFloorVolRealSurface, iborIndex, |
| 527 | Null< Rate >(), vars.accuracy, 100, |
| 528 | vars.discountingYTS, Normal)); |
| 529 | |
| 530 | ext::shared_ptr< StrippedOptionletAdapter > strippedOptionletAdapter = |
| 531 | ext::make_shared< StrippedOptionletAdapter >( |
| 532 | args&: optionletStripper1); |
| 533 | |
| 534 | Handle< OptionletVolatilityStructure > vol(strippedOptionletAdapter); |
| 535 | |
| 536 | vol->enableExtrapolation(); |
| 537 | |
| 538 | ext::shared_ptr< BachelierCapFloorEngine > strippedVolEngine( |
| 539 | new BachelierCapFloorEngine(vars.discountingYTS, vol)); |
| 540 | |
| 541 | ext::shared_ptr< CapFloor > cap; |
| 542 | for (Size tenorIndex = 0; tenorIndex < vars.optionTenors.size(); |
| 543 | ++tenorIndex) { |
| 544 | for (Size strikeIndex = 0; strikeIndex < vars.strikes.size(); |
| 545 | ++strikeIndex) { |
| 546 | cap = MakeCapFloor(CapFloor::Cap, vars.optionTenors[tenorIndex], |
| 547 | iborIndex, vars.strikes[strikeIndex], |
| 548 | 0 * Days).withPricingEngine(engine: strippedVolEngine); |
| 549 | |
| 550 | Real priceFromStrippedVolatility = cap->NPV(); |
| 551 | |
| 552 | ext::shared_ptr< PricingEngine > |
| 553 | bachelierCapFloorEngineConstantVolatility( |
| 554 | new BachelierCapFloorEngine( |
| 555 | vars.discountingYTS, |
| 556 | vars.termV[tenorIndex][strikeIndex])); |
| 557 | |
| 558 | cap->setPricingEngine(bachelierCapFloorEngineConstantVolatility); |
| 559 | Real priceFromConstantVolatility = cap->NPV(); |
| 560 | |
| 561 | Real error = std::fabs(x: priceFromStrippedVolatility - |
| 562 | priceFromConstantVolatility); |
| 563 | if (error > vars.tolerance) |
| 564 | BOOST_FAIL( |
| 565 | "\noption tenor: " |
| 566 | << vars.optionTenors[tenorIndex] << "\nstrike: " |
| 567 | << io::rate(vars.strikes[strikeIndex]) |
| 568 | << "\nstripped vol price: " |
| 569 | << io::rate(priceFromStrippedVolatility) |
| 570 | << "\nconstant vol price: " |
| 571 | << io::rate(priceFromConstantVolatility) |
| 572 | << "\nerror: " << io::rate(error) |
| 573 | << "\ntolerance: " << io::rate(vars.tolerance)); |
| 574 | } |
| 575 | } |
| 576 | } |
| 577 | |
| 578 | void OptionletStripperTest::testTermVolatilityStrippingShiftedLogNormalVol() { |
| 579 | |
| 580 | BOOST_TEST_MESSAGE( |
| 581 | "Testing forward/forward vol stripping from non-flat normal vol term " |
| 582 | "vol surface for normal vol setup using OptionletStripper1 class..." ); |
| 583 | |
| 584 | using namespace optionlet_stripper_test; |
| 585 | |
| 586 | CommonVars vars; |
| 587 | Real shift = 0.03; |
| 588 | Settings::instance().evaluationDate() = Date(30, April, 2015); |
| 589 | |
| 590 | vars.setRealCapFloorTermVolSurface(); |
| 591 | |
| 592 | ext::shared_ptr< IborIndex > iborIndex(new Euribor6M(vars.forwardingYTS)); |
| 593 | |
| 594 | ext::shared_ptr< OptionletStripper > optionletStripper1( |
| 595 | new OptionletStripper1(vars.capFloorVolRealSurface, iborIndex, |
| 596 | Null< Rate >(), vars.accuracy, 100, |
| 597 | vars.discountingYTS, ShiftedLognormal, shift, |
| 598 | true)); |
| 599 | |
| 600 | ext::shared_ptr< StrippedOptionletAdapter > strippedOptionletAdapter = |
| 601 | ext::make_shared< StrippedOptionletAdapter >( |
| 602 | args&: optionletStripper1); |
| 603 | |
| 604 | Handle< OptionletVolatilityStructure > vol(strippedOptionletAdapter); |
| 605 | |
| 606 | vol->enableExtrapolation(); |
| 607 | |
| 608 | ext::shared_ptr< BlackCapFloorEngine > strippedVolEngine( |
| 609 | new BlackCapFloorEngine(vars.discountingYTS, vol)); |
| 610 | |
| 611 | ext::shared_ptr< CapFloor > cap; |
| 612 | for (Size strikeIndex = 0; strikeIndex < vars.strikes.size(); |
| 613 | ++strikeIndex) { |
| 614 | for (Size tenorIndex = 0; tenorIndex < vars.optionTenors.size(); |
| 615 | ++tenorIndex) { |
| 616 | cap = MakeCapFloor(CapFloor::Cap, vars.optionTenors[tenorIndex], |
| 617 | iborIndex, vars.strikes[strikeIndex], |
| 618 | 0 * Days).withPricingEngine(engine: strippedVolEngine); |
| 619 | |
| 620 | Real priceFromStrippedVolatility = cap->NPV(); |
| 621 | |
| 622 | ext::shared_ptr< PricingEngine > |
| 623 | blackCapFloorEngineConstantVolatility(new BlackCapFloorEngine( |
| 624 | vars.discountingYTS, vars.termV[tenorIndex][strikeIndex], |
| 625 | vars.capFloorVolRealSurface->dayCounter(), shift)); |
| 626 | |
| 627 | cap->setPricingEngine(blackCapFloorEngineConstantVolatility); |
| 628 | Real priceFromConstantVolatility = cap->NPV(); |
| 629 | |
| 630 | Real error = std::fabs(x: priceFromStrippedVolatility - |
| 631 | priceFromConstantVolatility); |
| 632 | if (error > vars.tolerance) |
| 633 | BOOST_FAIL( |
| 634 | "\noption tenor: " |
| 635 | << vars.optionTenors[tenorIndex] << "\nstrike: " |
| 636 | << io::rate(vars.strikes[strikeIndex]) |
| 637 | << "\nstripped vol price: " |
| 638 | << io::rate(priceFromStrippedVolatility) |
| 639 | << "\nconstant vol price: " |
| 640 | << io::rate(priceFromConstantVolatility) |
| 641 | << "\nerror: " << io::rate(error) |
| 642 | << "\ntolerance: " << io::rate(vars.tolerance)); |
| 643 | } |
| 644 | } |
| 645 | } |
| 646 | |
| 647 | void OptionletStripperTest::testFlatTermVolatilityStripping2() { |
| 648 | |
| 649 | BOOST_TEST_MESSAGE( |
| 650 | "Testing forward/forward vol stripping from flat term vol " |
| 651 | "surface using OptionletStripper2 class..." ); |
| 652 | |
| 653 | using namespace optionlet_stripper_test; |
| 654 | |
| 655 | CommonVars vars; |
| 656 | Settings::instance().evaluationDate() = Date(28, October, 2013); |
| 657 | |
| 658 | vars.setFlatTermVolCurve(); |
| 659 | vars.setFlatTermVolSurface(); |
| 660 | |
| 661 | ext::shared_ptr<IborIndex> iborIndex(new Euribor6M(vars.yieldTermStructure)); |
| 662 | |
| 663 | // optionletstripper1 |
| 664 | ext::shared_ptr<OptionletStripper1> optionletStripper1(new |
| 665 | OptionletStripper1(vars.flatTermVolSurface, |
| 666 | iborIndex, |
| 667 | Null<Rate>(), |
| 668 | vars.accuracy)); |
| 669 | |
| 670 | ext::shared_ptr<StrippedOptionletAdapter> strippedOptionletAdapter1(new |
| 671 | StrippedOptionletAdapter(optionletStripper1)); |
| 672 | |
| 673 | Handle<OptionletVolatilityStructure> vol1(strippedOptionletAdapter1); |
| 674 | |
| 675 | vol1->enableExtrapolation(); |
| 676 | |
| 677 | // optionletstripper2 |
| 678 | ext::shared_ptr<OptionletStripper> optionletStripper2(new |
| 679 | OptionletStripper2(optionletStripper1, vars.flatTermVolCurve)); |
| 680 | |
| 681 | ext::shared_ptr<StrippedOptionletAdapter> strippedOptionletAdapter2(new |
| 682 | StrippedOptionletAdapter(optionletStripper2)); |
| 683 | |
| 684 | Handle<OptionletVolatilityStructure> vol2(strippedOptionletAdapter2); |
| 685 | |
| 686 | vol2->enableExtrapolation(); |
| 687 | |
| 688 | // consistency check: diff(stripped vol1-stripped vol2) |
| 689 | for (Size strikeIndex=0; strikeIndex<vars.strikes.size(); ++strikeIndex) { |
| 690 | for (Size tenorIndex=0; tenorIndex<vars.optionTenors.size(); ++tenorIndex) { |
| 691 | |
| 692 | Volatility strippedVol1 = vol1->volatility(optionTenor: vars.optionTenors[tenorIndex], |
| 693 | strike: vars.strikes[strikeIndex], extrapolate: true); |
| 694 | |
| 695 | Volatility strippedVol2 = vol2->volatility(optionTenor: vars.optionTenors[tenorIndex], |
| 696 | strike: vars.strikes[strikeIndex], extrapolate: true); |
| 697 | |
| 698 | // vol from flat vol surface (for comparison only) |
| 699 | Volatility flatVol = vars.flatTermVolSurface->volatility(optT: vars.optionTenors[tenorIndex], |
| 700 | strike: vars.strikes[strikeIndex], extrap: true); |
| 701 | |
| 702 | Real error = std::fabs(x: strippedVol1-strippedVol2); |
| 703 | if (error>vars.tolerance) |
| 704 | BOOST_FAIL("\noption tenor: " << vars.optionTenors[tenorIndex] << |
| 705 | "\nstrike: " << io::rate(vars.strikes[strikeIndex]) << |
| 706 | "\nstripped vol1: " << io::rate(strippedVol1) << |
| 707 | "\nstripped vol2: " << io::rate(strippedVol2) << |
| 708 | "\nflat vol: " << io::rate(flatVol) << |
| 709 | "\nerror: " << io::rate(error) << |
| 710 | "\ntolerance: " << io::rate(vars.tolerance)); |
| 711 | } |
| 712 | } |
| 713 | |
| 714 | } |
| 715 | |
| 716 | void OptionletStripperTest::testTermVolatilityStripping2() { |
| 717 | |
| 718 | BOOST_TEST_MESSAGE( |
| 719 | "Testing forward/forward vol stripping from non-flat term vol " |
| 720 | "surface using OptionletStripper2 class..." ); |
| 721 | |
| 722 | using namespace optionlet_stripper_test; |
| 723 | |
| 724 | CommonVars vars; |
| 725 | Settings::instance().evaluationDate() = Date(30, April, 2015); |
| 726 | |
| 727 | vars.setCapFloorTermVolCurve(); |
| 728 | vars.setCapFloorTermVolSurface(); |
| 729 | |
| 730 | ext::shared_ptr<IborIndex> iborIndex(new Euribor6M(vars.yieldTermStructure)); |
| 731 | |
| 732 | // optionletstripper1 |
| 733 | ext::shared_ptr<OptionletStripper1> optionletStripper1(new |
| 734 | OptionletStripper1(vars.capFloorVolSurface, |
| 735 | iborIndex, |
| 736 | Null<Rate>(), |
| 737 | vars.accuracy)); |
| 738 | |
| 739 | ext::shared_ptr<StrippedOptionletAdapter> strippedOptionletAdapter1 = |
| 740 | ext::make_shared<StrippedOptionletAdapter>(args&: optionletStripper1); |
| 741 | |
| 742 | Handle<OptionletVolatilityStructure> vol1(strippedOptionletAdapter1); |
| 743 | vol1->enableExtrapolation(); |
| 744 | |
| 745 | // optionletstripper2 |
| 746 | ext::shared_ptr<OptionletStripper> optionletStripper2(new |
| 747 | OptionletStripper2(optionletStripper1, |
| 748 | vars.capFloorVolCurve)); |
| 749 | |
| 750 | ext::shared_ptr<StrippedOptionletAdapter> strippedOptionletAdapter2(new |
| 751 | StrippedOptionletAdapter(optionletStripper2)); |
| 752 | |
| 753 | Handle<OptionletVolatilityStructure> vol2(strippedOptionletAdapter2); |
| 754 | vol2->enableExtrapolation(); |
| 755 | |
| 756 | // consistency check: diff(stripped vol1-stripped vol2) |
| 757 | for (Size strikeIndex=0; strikeIndex<vars.strikes.size(); ++strikeIndex) { |
| 758 | for (Size tenorIndex=0; tenorIndex<vars.optionTenors.size(); ++tenorIndex) { |
| 759 | |
| 760 | Volatility strippedVol1 = vol1->volatility(optionTenor: vars.optionTenors[tenorIndex], |
| 761 | strike: vars.strikes[strikeIndex], extrapolate: true); |
| 762 | |
| 763 | Volatility strippedVol2 = vol2->volatility(optionTenor: vars.optionTenors[tenorIndex], |
| 764 | strike: vars.strikes[strikeIndex], extrapolate: true); |
| 765 | |
| 766 | // vol from flat vol surface (for comparison only) |
| 767 | Volatility flatVol = vars.capFloorVolSurface->volatility(optT: vars.optionTenors[tenorIndex], |
| 768 | strike: vars.strikes[strikeIndex], extrap: true); |
| 769 | |
| 770 | Real error = std::fabs(x: strippedVol1-strippedVol2); |
| 771 | if (error>vars.tolerance) |
| 772 | BOOST_FAIL("\noption tenor: " << vars.optionTenors[tenorIndex] << |
| 773 | "\nstrike: " << io::rate(vars.strikes[strikeIndex]) << |
| 774 | "\nstripped vol1: " << io::rate(strippedVol1) << |
| 775 | "\nstripped vol2: " << io::rate(strippedVol2) << |
| 776 | "\nflat vol: " << io::rate(flatVol) << |
| 777 | "\nerror: " << io::rate(error) << |
| 778 | "\ntolerance: " << io::rate(vars.tolerance)); |
| 779 | } |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | void OptionletStripperTest::testSwitchStrike() { |
| 784 | BOOST_TEST_MESSAGE("Testing switch strike level and recalibration of level " |
| 785 | "in case of curve relinking..." ); |
| 786 | |
| 787 | using namespace optionlet_stripper_test; |
| 788 | |
| 789 | bool usingAtParCoupons = IborCoupon::Settings::instance().usingAtParCoupons(); |
| 790 | |
| 791 | CommonVars vars; |
| 792 | Settings::instance().evaluationDate() = Date(28, October, 2013); |
| 793 | vars.setCapFloorTermVolSurface(); |
| 794 | |
| 795 | RelinkableHandle< YieldTermStructure > yieldTermStructure; |
| 796 | yieldTermStructure.linkTo(h: ext::make_shared< FlatForward >( |
| 797 | args: 0, args&: vars.calendar, args: 0.03, args&: vars.dayCounter)); |
| 798 | |
| 799 | ext::shared_ptr< IborIndex > iborIndex(new Euribor6M(yieldTermStructure)); |
| 800 | |
| 801 | ext::shared_ptr< OptionletStripper1 > optionletStripper1( |
| 802 | new OptionletStripper1(vars.capFloorVolSurface, iborIndex, |
| 803 | Null< Rate >(), vars.accuracy)); |
| 804 | |
| 805 | Real expected = usingAtParCoupons ? 0.02981223 : 0.02981258; |
| 806 | |
| 807 | Real error = std::fabs(x: optionletStripper1->switchStrike() - expected); |
| 808 | if (error > vars.tolerance) |
| 809 | BOOST_FAIL("\nSwitchstrike not correctly computed: " |
| 810 | << "\nexpected switch strike: " << io::rate(expected) |
| 811 | << "\ncomputed switch strike: " |
| 812 | << io::rate(optionletStripper1->switchStrike()) |
| 813 | << "\nerror: " << io::rate(error) |
| 814 | << "\ntolerance: " << io::rate(vars.tolerance)); |
| 815 | |
| 816 | yieldTermStructure.linkTo(h: ext::make_shared< FlatForward >( |
| 817 | args: 0, args&: vars.calendar, args: 0.05, args&: vars.dayCounter)); |
| 818 | |
| 819 | expected = usingAtParCoupons ? 0.0499371 : 0.0499381; |
| 820 | |
| 821 | error = std::fabs(x: optionletStripper1->switchStrike() - expected); |
| 822 | if (error > vars.tolerance) |
| 823 | BOOST_FAIL("\nSwitchstrike not correctly computed: " |
| 824 | << "\nexpected switch strike: " << io::rate(expected) |
| 825 | << "\ncomputed switch strike: " |
| 826 | << io::rate(optionletStripper1->switchStrike()) |
| 827 | << "\nerror: " << io::rate(error) |
| 828 | << "\ntolerance: " << io::rate(vars.tolerance)); |
| 829 | } |
| 830 | |
| 831 | test_suite* OptionletStripperTest::suite() { |
| 832 | auto* suite = BOOST_TEST_SUITE("OptionletStripper Tests" ); |
| 833 | suite->add(QUANTLIB_TEST_CASE(&OptionletStripperTest::testFlatTermVolatilityStripping1)); |
| 834 | suite->add(QUANTLIB_TEST_CASE(&OptionletStripperTest::testTermVolatilityStripping1)); |
| 835 | suite->add(QUANTLIB_TEST_CASE(&OptionletStripperTest::testFlatTermVolatilityStripping2)); |
| 836 | suite->add(QUANTLIB_TEST_CASE(&OptionletStripperTest::testTermVolatilityStripping2)); |
| 837 | suite->add(QUANTLIB_TEST_CASE(&OptionletStripperTest::testSwitchStrike)); |
| 838 | suite->add(QUANTLIB_TEST_CASE(&OptionletStripperTest::testTermVolatilityStrippingNormalVol)); |
| 839 | suite->add(QUANTLIB_TEST_CASE(&OptionletStripperTest::testTermVolatilityStrippingShiftedLogNormalVol)); |
| 840 | |
| 841 | return suite; |
| 842 | } |
| 843 | |