| 242 | | } |
|---|
| 243 | | } |
|---|
| 244 | | } |
|---|
| 245 | | |
|---|
| | 243 | } |
|---|
| | 244 | } |
|---|
| | 245 | |
|---|
| | 246 | [TestMethod()] |
|---|
| | 247 | public void testConsistency() |
|---|
| | 248 | { |
|---|
| | 249 | CommonVars vars = new CommonVars(); |
|---|
| | 250 | |
|---|
| | 251 | int[] lengths = { 1, 2, 3, 5, 7, 10, 15, 20 }; |
|---|
| | 252 | double[] cap_rates = { 0.03, 0.04, 0.05, 0.06, 0.07 }; |
|---|
| | 253 | double[] floor_rates = { 0.03, 0.04, 0.05, 0.06, 0.07 }; |
|---|
| | 254 | double[] vols = { 0.01, 0.05, 0.10, 0.15, 0.20 }; |
|---|
| | 255 | |
|---|
| | 256 | Date startDate = vars.termStructure.link.referenceDate(); |
|---|
| | 257 | |
|---|
| | 258 | for (int i=0; i<lengths.Length; i++) { |
|---|
| | 259 | for (int j=0; j<cap_rates.Length; j++) { |
|---|
| | 260 | for (int k=0; k<floor_rates.Length; k++) { |
|---|
| | 261 | for (int l=0; l<vols.Length; l++) { |
|---|
| | 262 | |
|---|
| | 263 | List<CashFlow> leg = vars.makeLeg(startDate,lengths[i]); |
|---|
| | 264 | Instrument cap = vars.makeCapFloor(CapFloorType.Cap,leg, |
|---|
| | 265 | cap_rates[j],vols[l]); |
|---|
| | 266 | Instrument floor = vars.makeCapFloor(CapFloorType.Floor,leg, |
|---|
| | 267 | floor_rates[k],vols[l]); |
|---|
| | 268 | Collar collar = new Collar(leg,new InitializedList<double>(1,cap_rates[j]), |
|---|
| | 269 | new InitializedList<double>(1,floor_rates[k])); |
|---|
| | 270 | collar.setPricingEngine(vars.makeEngine(vols[l])); |
|---|
| | 271 | |
|---|
| | 272 | if (Math.Abs((cap.NPV()-floor.NPV())-collar.NPV()) > 1e-10) { |
|---|
| | 273 | Assert.Fail( |
|---|
| | 274 | "inconsistency between cap, floor and collar:\n" |
|---|
| | 275 | + " length: " + lengths[i] + " years\n" |
|---|
| | 276 | + " volatility: " + vols[l] + "\n" |
|---|
| | 277 | + " cap value: " + cap.NPV() |
|---|
| | 278 | + " at strike: " + cap_rates[j] + "\n" |
|---|
| | 279 | + " floor value: " + floor.NPV() |
|---|
| | 280 | + " at strike: " + floor_rates[k] + "\n" |
|---|
| | 281 | + " collar value: " + collar.NPV()); |
|---|
| | 282 | } |
|---|
| | 283 | } |
|---|
| | 284 | } |
|---|
| | 285 | } |
|---|
| | 286 | } |
|---|
| | 287 | } |
|---|
| | 288 | |
|---|
| | 289 | [TestMethod()] |
|---|
| | 290 | public void testParity() |
|---|
| | 291 | { |
|---|
| | 292 | CommonVars vars = new CommonVars(); |
|---|
| | 293 | |
|---|
| | 294 | int[] lengths = { 1, 2, 3, 5, 7, 10, 15, 20 }; |
|---|
| | 295 | double[] strikes = { 0.0, 0.03, 0.04, 0.05, 0.06, 0.07 }; |
|---|
| | 296 | double[] vols = { 0.01, 0.05, 0.10, 0.15, 0.20 }; |
|---|
| | 297 | |
|---|
| | 298 | Date startDate = vars.termStructure.link.referenceDate(); |
|---|
| | 299 | |
|---|
| | 300 | for (int i=0; i<lengths.Length; i++) { |
|---|
| | 301 | for (int j=0; j<strikes.Length; j++) { |
|---|
| | 302 | for (int k=0; k<vols.Length; k++) { |
|---|
| | 303 | |
|---|
| | 304 | List<CashFlow> leg = vars.makeLeg(startDate,lengths[i]); |
|---|
| | 305 | Instrument cap = vars.makeCapFloor(CapFloorType.Cap,leg,strikes[j],vols[k]); |
|---|
| | 306 | Instrument floor = vars.makeCapFloor(CapFloorType.Floor,leg,strikes[j],vols[k]); |
|---|
| | 307 | Date maturity = vars.calendar.advance(startDate,lengths[i],TimeUnit.Years,vars.convention); |
|---|
| | 308 | Schedule schedule = new Schedule(startDate,maturity, |
|---|
| | 309 | new Period(vars.frequency),vars.calendar, |
|---|
| | 310 | vars.convention,vars.convention, |
|---|
| | 311 | DateGeneration.Rule.Forward,false); |
|---|
| | 312 | VanillaSwap swap = new VanillaSwap(VanillaSwap.Type.Payer, vars.nominals[0], |
|---|
| | 313 | schedule, strikes[j], vars.index.dayCounter(), |
|---|
| | 314 | schedule, vars.index, 0.0, |
|---|
| | 315 | vars.index.dayCounter()); |
|---|
| | 316 | swap.setPricingEngine((IPricingEngine)new DiscountingSwapEngine(vars.termStructure)); |
|---|
| | 317 | // FLOATING_POINT_EXCEPTION |
|---|
| | 318 | if (Math.Abs((cap.NPV()-floor.NPV()) - swap.NPV()) > 1.0e-10) |
|---|
| | 319 | { |
|---|
| | 320 | Assert.Fail( |
|---|
| | 321 | "put/call parity violated:\n" |
|---|
| | 322 | + " length: " + lengths[i] + " years\n" |
|---|
| | 323 | + " volatility: " + vols[k] + "\n" |
|---|
| | 324 | + " strike: " + strikes[j] + "\n" |
|---|
| | 325 | + " cap value: " + cap.NPV() + "\n" |
|---|
| | 326 | + " floor value: " + floor.NPV() + "\n" |
|---|
| | 327 | + " swap value: " + swap.NPV()); |
|---|
| | 328 | } |
|---|
| | 329 | } |
|---|
| | 330 | } |
|---|
| | 331 | } |
|---|
| | 332 | } |
|---|
| | 333 | |
|---|
| | 334 | [TestMethod()] |
|---|
| | 335 | public void testATMRate() |
|---|
| | 336 | { |
|---|
| | 337 | CommonVars vars = new CommonVars(); |
|---|
| | 338 | |
|---|
| | 339 | int[] lengths = { 1, 2, 3, 5, 7, 10, 15, 20 }; |
|---|
| | 340 | double[] strikes = { 0.0, 0.03, 0.04, 0.05, 0.06, 0.07 }; |
|---|
| | 341 | double[] vols = { 0.01, 0.05, 0.10, 0.15, 0.20 }; |
|---|
| | 342 | |
|---|
| | 343 | Date startDate = vars.termStructure.link.referenceDate(); |
|---|
| | 344 | |
|---|
| | 345 | for (int i=0; i<lengths.Length; i++) |
|---|
| | 346 | { |
|---|
| | 347 | List<CashFlow> leg = vars.makeLeg(startDate,lengths[i]); |
|---|
| | 348 | Date maturity = vars.calendar.advance(startDate,lengths[i],TimeUnit.Years,vars.convention); |
|---|
| | 349 | Schedule schedule = new Schedule(startDate,maturity, |
|---|
| | 350 | new Period(vars.frequency),vars.calendar, |
|---|
| | 351 | vars.convention,vars.convention, |
|---|
| | 352 | DateGeneration.Rule.Forward,false); |
|---|
| | 353 | |
|---|
| | 354 | for (int j=0; j<strikes.Length; j++) |
|---|
| | 355 | { |
|---|
| | 356 | for (int k=0; k<vols.Length; k++) |
|---|
| | 357 | { |
|---|
| | 358 | |
|---|
| | 359 | CapFloor cap = vars.makeCapFloor(CapFloorType.Cap, leg, strikes[j],vols[k]); |
|---|
| | 360 | CapFloor floor = vars.makeCapFloor(CapFloorType.Floor, leg, strikes[j],vols[k]); |
|---|
| | 361 | double capATMRate = cap.atmRate(vars.termStructure); |
|---|
| | 362 | double floorATMRate = floor.atmRate(vars.termStructure); |
|---|
| | 363 | |
|---|
| | 364 | if (!checkAbsError(floorATMRate, capATMRate, 1.0e-10)) |
|---|
| | 365 | Assert.Fail( |
|---|
| | 366 | "Cap ATM Rate and floor ATM Rate should be equal :\n" |
|---|
| | 367 | + " length: " + lengths[i] + " years\n" |
|---|
| | 368 | + " volatility: " + vols[k] + "\n" |
|---|
| | 369 | + " strike: " + strikes[j] + "\n" |
|---|
| | 370 | + " cap ATM rate: " + capATMRate + "\n" |
|---|
| | 371 | + " floor ATM rate:" + floorATMRate + "\n" |
|---|
| | 372 | + " relative Error:" |
|---|
| | 373 | + Utilities.relativeError(capATMRate, floorATMRate,capATMRate)*100 + "%" ); |
|---|
| | 374 | VanillaSwap swap = new VanillaSwap(VanillaSwap.Type.Payer, vars.nominals[0], |
|---|
| | 375 | schedule, floorATMRate, |
|---|
| | 376 | vars.index.dayCounter(), |
|---|
| | 377 | schedule, vars.index, 0.0, |
|---|
| | 378 | vars.index.dayCounter()); |
|---|
| | 379 | swap.setPricingEngine((IPricingEngine)( |
|---|
| | 380 | new DiscountingSwapEngine(vars.termStructure))); |
|---|
| | 381 | double swapNPV = swap.NPV(); |
|---|
| | 382 | if (!checkAbsError(swapNPV, 0, 1.0e-10)) |
|---|
| | 383 | Assert.Fail( |
|---|
| | 384 | "the NPV of a Swap struck at ATM rate " |
|---|
| | 385 | + "should be equal to 0:\n" |
|---|
| | 386 | + " length: " + lengths[i] + " years\n" |
|---|
| | 387 | + " volatility: " + vols[k] + "\n" |
|---|
| | 388 | + " ATM rate: " + floorATMRate + "\n" |
|---|
| | 389 | + " swap NPV: " + swapNPV); |
|---|
| | 390 | |
|---|
| | 391 | } |
|---|
| | 392 | } |
|---|
| | 393 | } |
|---|
| | 394 | } |
|---|
| | 395 | |
|---|
| | 396 | [TestMethod()] |
|---|
| | 397 | public void testImpliedVolatility() |
|---|
| | 398 | { |
|---|
| | 399 | CommonVars vars = new CommonVars(); |
|---|
| | 400 | |
|---|
| | 401 | int maxEvaluations = 100; |
|---|
| | 402 | double tolerance = 1.0e-6; |
|---|
| | 403 | |
|---|
| | 404 | CapFloorType[] types = { CapFloorType.Cap, CapFloorType.Floor }; |
|---|
| | 405 | double[] strikes = { 0.02, 0.03, 0.04 }; |
|---|
| | 406 | int[] lengths = { 1, 5, 10 }; |
|---|
| | 407 | |
|---|
| | 408 | // test data |
|---|
| | 409 | double[] rRates = { 0.02, 0.03, 0.04 }; |
|---|
| | 410 | double[] vols = { 0.01, 0.20, 0.30, 0.70, 0.90 }; |
|---|
| | 411 | |
|---|
| | 412 | for (int k=0; k<lengths.Length; k++) |
|---|
| | 413 | { |
|---|
| | 414 | List<CashFlow> leg = vars.makeLeg(vars.settlement, lengths[k]); |
|---|
| | 415 | |
|---|
| | 416 | for (int i=0; i<types.Length; i++) |
|---|
| | 417 | { |
|---|
| | 418 | for (int j=0; j<strikes.Length; j++) |
|---|
| | 419 | { |
|---|
| | 420 | CapFloor capfloor = vars.makeCapFloor(types[i], leg, strikes[j], 0.0); |
|---|
| | 421 | |
|---|
| | 422 | for (int n=0; n<rRates.Length; n++) |
|---|
| | 423 | { |
|---|
| | 424 | for (int m=0; m<vols.Length; m++) |
|---|
| | 425 | { |
|---|
| | 426 | double r = rRates[n]; |
|---|
| | 427 | double v = vols[m]; |
|---|
| | 428 | vars.termStructure.linkTo(Utilities.flatRate(vars.settlement,r,new Actual360())); |
|---|
| | 429 | capfloor.setPricingEngine(vars.makeEngine(v)); |
|---|
| | 430 | |
|---|
| | 431 | double value = capfloor.NPV(); |
|---|
| | 432 | double implVol = 0.0; |
|---|
| | 433 | |
|---|
| | 434 | try |
|---|
| | 435 | { |
|---|
| | 436 | implVol = capfloor.impliedVolatility(value, |
|---|
| | 437 | vars.termStructure, |
|---|
| | 438 | 0.10, |
|---|
| | 439 | tolerance, |
|---|
| | 440 | maxEvaluations); |
|---|
| | 441 | } |
|---|
| | 442 | catch (Exception e) |
|---|
| | 443 | { |
|---|
| | 444 | Assert.Fail(typeToString(types[i]) + |
|---|
| | 445 | " strike: " + strikes[j] + |
|---|
| | 446 | " risk-free: " + r + |
|---|
| | 447 | " length: " + lengths[k] + "Y" + |
|---|
| | 448 | " volatility: " + v + e.Message); |
|---|
| | 449 | } |
|---|
| | 450 | if (Math.Abs(implVol-v) > tolerance) |
|---|
| | 451 | { |
|---|
| | 452 | // the difference might not matter |
|---|
| | 453 | capfloor.setPricingEngine(vars.makeEngine(implVol)); |
|---|
| | 454 | double value2 = capfloor.NPV(); |
|---|
| | 455 | if (Math.Abs(value-value2) > tolerance) |
|---|
| | 456 | { |
|---|
| | 457 | Assert.Fail( |
|---|
| | 458 | typeToString(types[i]) + ":" |
|---|
| | 459 | + " strike: " |
|---|
| | 460 | + strikes[j] + "\n" |
|---|
| | 461 | + " risk-free rate: " |
|---|
| | 462 | + r + "\n" |
|---|
| | 463 | + " length: " |
|---|
| | 464 | + lengths[k] + " years\n\n" |
|---|
| | 465 | + " original volatility: " |
|---|
| | 466 | + v + "\n" |
|---|
| | 467 | + " price: " |
|---|
| | 468 | + value + "\n" |
|---|
| | 469 | + " implied volatility: " |
|---|
| | 470 | + implVol + "\n" |
|---|
| | 471 | + " corresponding price: " + value2); |
|---|
| | 472 | } |
|---|
| | 473 | } |
|---|
| | 474 | } |
|---|
| | 475 | } |
|---|
| | 476 | } |
|---|
| | 477 | } |
|---|
| | 478 | } |
|---|
| | 479 | } |
|---|
| | 480 | |
|---|
| | 481 | [TestMethod()] |
|---|
| | 482 | public void testCachedValue() |
|---|
| | 483 | { |
|---|
| | 484 | CommonVars vars = new CommonVars(); |
|---|
| | 485 | |
|---|
| | 486 | Date cachedToday = new Date(14, Month.March, 2002), |
|---|
| | 487 | cachedSettlement = new Date(18, Month.March, 2002); |
|---|
| | 488 | Settings.setEvaluationDate(cachedToday); |
|---|
| | 489 | vars.termStructure.linkTo(Utilities.flatRate(cachedSettlement, 0.05, new Actual360())); |
|---|
| | 490 | Date startDate = vars.termStructure.link.referenceDate(); |
|---|
| | 491 | List<CashFlow> leg = vars.makeLeg(startDate,20); |
|---|
| | 492 | Instrument cap = vars.makeCapFloor(CapFloorType.Cap,leg,0.07,0.20); |
|---|
| | 493 | Instrument floor = vars.makeCapFloor(CapFloorType.Floor,leg,0.03,0.20); |
|---|
| | 494 | |
|---|
| | 495 | // par coupon price |
|---|
| | 496 | double cachedCapNPV = 6.87570026732, |
|---|
| | 497 | cachedFloorNPV = 2.65812927959; |
|---|
| | 498 | |
|---|
| | 499 | // index fixing price |
|---|
| | 500 | //Real cachedCapNPV = 6.87630307745, |
|---|
| | 501 | // cachedFloorNPV = 2.65796764715; |
|---|
| | 502 | |
|---|
| | 503 | // test Black cap price against cached value |
|---|
| | 504 | if (Math.Abs(cap.NPV()-cachedCapNPV) > 1.0e-11) |
|---|
| | 505 | Assert.Fail("failed to reproduce cached cap value:\n" |
|---|
| | 506 | + " calculated: " + cap.NPV() + "\n" |
|---|
| | 507 | + " expected: " + cachedCapNPV); |
|---|
| | 508 | |
|---|
| | 509 | // test Black floor price against cached value |
|---|
| | 510 | if (Math.Abs(floor.NPV()-cachedFloorNPV) > 1.0e-11) |
|---|
| | 511 | Assert.Fail("failed to reproduce cached floor value:\n" |
|---|
| | 512 | + " calculated: " + floor.NPV() + "\n" |
|---|
| | 513 | + " expected: " + cachedFloorNPV); |
|---|
| | 514 | } |
|---|