241 {
242
243
244
248
249
250
251 for (Size i = 0; i <
stepTimes_.size(); ++i) {
253 "DefaultableEquityJumpDiffusionModel: creditCurve implies zero survival probability at t = "
255 << ", this can not be handled. Check the credit curve / security spread provided in the market "
256 "data. If this happens during a spread imply, the target price might not be attainable even "
257 "for high spreads.");
258 }
259
261
262
263
264 Real accumulatedVariance = 0.0;
265
266 for (Size i = 0; i <
stepTimes_.size(); ++i) {
267
268
269
273
274
275
276 Real impliedModelVol = 0.0;
279
280 if (!adjustEquityVolatility) {
281
282
283
284 impliedModelVol = volatility->blackVol(
stepTimes_[i], forward);
285
286 } else {
287
288
289
290 Real marketPrice = blackFormula(Option::Call, forward, forward,
291 std::sqrt(volatility->blackVariance(
stepTimes_[i], forward)),
293
294
295
296
297
298
299
300
303 : forward;
304 try {
305 impliedModelVol =
306 blackFormulaImpliedStdDev(Option::Call, forward, adjustedForward, marketPrice,
310 } catch (...) {
311 }
312 }
313
314 impliedModelVol = std::max(1E-4, impliedModelVol);
315
316
317
319 i == 0
320 ? impliedModelVol
321 : std::sqrt(std::max(impliedModelVol * impliedModelVol *
stepTimes_[i] - accumulatedVariance, 0.0) /
323
325 }
326
327 } else {
328
329
330
331
332
333
334
335 Real spot =
equity()->equitySpot()->value();
336 Real logSpot = std::log(spot);
337
338 if (
mesher_ ==
nullptr || !staticMesher) {
339
341 std::max<Size>(std::lround(timeStepsPerYear *
stepTimes_.back() + 0.5), 1));
342
343 Real mi = spot;
344 Real ma = spot;
345
346 Real forward = spot;
347 for (Size i = 1; i < tmpGrid.size(); ++i) {
348 forward =
equity()->equitySpot()->value() *
equity()->equityDividendCurve()->discount(tmpGrid[i]) /
349 equity()->equityForecastCurve()->discount(tmpGrid[i]) /
351 mi = std::min(mi, forward);
352 ma = std::max(ma, forward);
353 }
354
355 Real sigmaSqrtT = std::max(1E-4, std::sqrt(volatility->blackVariance(tmpGrid.back(), forward)));
356 Real normInvEps = InverseCumulativeNormal()(1.0 - mesherEpsilon);
357 Real xMin = std::log(mi) - sigmaSqrtT * normInvEps * mesherScaling;
358 Real xMax = std::log(ma) + sigmaSqrtT * normInvEps * mesherScaling;
359
360 if (mesherConcentration != Null<Real>()) {
361 mesher_ = QuantLib::ext::make_shared<Concentrating1dMesher>(xMin, xMax, stateGridPoints,
362 std::make_pair(logSpot, mesherConcentration), true);
363 } else {
364 mesher_ = QuantLib::ext::make_shared<Uniform1dMesher>(xMin, xMax, stateGridPoints);
365 }
366 }
367
368
369
370 auto fdmOp = QuantLib::ext::make_shared<FdmDefaultableEquityJumpDiffusionFokkerPlanckOp>(
371 stepTimes_.back(), QuantLib::ext::make_shared<FdmMesherComposite>(
mesher_), shared_from_this());
372
373
374
375
376
377
378
379 auto solver = QuantLib::ext::make_shared<FdmBackwardSolver>(
380 fdmOp, std::vector<QuantLib::ext::shared_ptr<BoundaryCondition<FdmLinearOp>>>(), nullptr, FdmSchemeDesc::Douglas());
381
382 constexpr Size dampingSteps = 5;
383
384
385
386
387
389
390 for (Size i = 0; i <
mesher_->size(); ++i) {
391 if (i > 0) {
392 dy[i] += 0.5 *
mesher_->dminus(i);
393 } else {
394 dy[i] += 0.5 *
mesher_->dminus(i + 1);
395 }
396 if (i < mesher_->size() - 1) {
397 dy[i] += 0.5 *
mesher_->dplus(i);
398 } else {
399 dy[i] += 0.5 *
mesher_->dplus(i - 1);
400 }
401 }
402
403 for (Size i = 0; i <
mesher_->size(); ++i) {
404 if (i > 0) {
407 Real alpha = (
mesher_->location(i) - logSpot) /
mesher_->dplus(i - 1);
408 p[i - 1] = alpha / dy[i - 1];
409 p[i] = (1 - alpha) / dy[i];
410 }
411 }
412 }
413
414 Array guess(2);
415
416
417 constexpr Real thresholdSuccessfulOptimization = 1E-5;
418 Real lastOptimizationError = 0.0;
419 Array lastValidGuess;
420
421 for (Size i = 0; i <
stepTimes_.size(); ++i) {
422
423
424
429 Real marketEquityOption = blackFormula(Option::Call, forward, forward,
430 std::sqrt(volatility->blackVariance(
stepTimes_[i], forward)),
432
433 struct TargetFunction : public QuantLib::CostFunction {
434 static Real hToOpt(const Real x) { return std::log(std::max(x, 1E-6)); }
435 static Real sigmaToOpt(const Real x) { return std::log(std::max(x, 1E-6)); }
436 static Real hToReal(const Real x) { return std::exp(x); }
437 static Real sigmaToReal(const Real x) { return std::exp(x); }
438
439 enum class Mode { h0, sigma, both };
440 TargetFunction(const Mode mode, const Real strike, const Real marketEquityOption,
441 const Real marketDefaultableBond, Real& h0, Real& sigma, const Array& p,
442 const std::vector<Real>& locations, const Array& dy,
443 const QuantLib::ext::shared_ptr<FdmBackwardSolver>& solver, const Real t_from, const Real t_to,
444 const Size
steps,
const Size dampingSteps)
445 : mode_(mode), strike_(strike), marketEquityOption_(marketEquityOption),
446 marketDefaultableBond_(marketDefaultableBond), h0_(h0), sigma_(sigma), locations_(locations),
447 dy_(dy), p_(p), solver_(solver), t_from_(t_from), t_to_(t_to), steps_(
steps),
448 dampingSteps_(dampingSteps) {}
449
450 Array values(const Array& x) const override {
451
452
453
454
455 if (mode_ == Mode::h0) {
456 h0_ = hToReal(x[0]);
457 } else if (mode_ == Mode::sigma) {
458 sigma_ = sigmaToReal(x[0]);
459 } else {
460 h0_ = hToReal(x[0]);
461 sigma_ = sigmaToReal(x[1]);
462 }
463
464
465
466 Array pTmp = p_;
467 solver_->rollback(pTmp, t_from_, t_to_, steps_, dampingSteps_);
468
469
470
471 Real defaultableBond = 0.0;
472 if (mode_ == Mode::h0 || mode_ == Mode::both) {
473 for (Size i = 0; i < pTmp.size(); ++i) {
474 defaultableBond += pTmp[i] * dy_[i];
475 }
476 }
477
478 Real equityOption = 0.0;
479 if (mode_ == Mode::sigma || mode_ == Mode::both) {
480 bool knockInLocation = true;
481 for (Size i = 0; i < pTmp.size(); ++i) {
482 if (locations_[i] > std::log(strike_) && !
close_enough(locations_[i], std::log(strike_))) {
483 Real dy = dy_[i];
484 if (knockInLocation) {
485
486 dy = (locations_[i] - std::log(strike_));
487 if (i < pTmp.size())
488 dy += 0.5 * (locations_[i + 1] - locations_[i]);
489 else
490 dy += 0.5 * (locations_[i] - locations_[i - 1]);
491 knockInLocation = false;
492 }
493 equityOption += pTmp[i] * dy * (std::exp(locations_[i]) - strike_);
494 }
495 }
496 }
497
498 if (mode_ == Mode::h0) {
499 Array result(1);
500 result[0] = (defaultableBond - marketDefaultableBond_) / marketDefaultableBond_;
501 return result;
502 } else if (mode_ == Mode::sigma) {
503 Array result(1);
504 result[0] = (equityOption - marketEquityOption_) / marketEquityOption_;
505 return result;
506 } else {
507 Array result(2);
508 result[0] = (defaultableBond - marketDefaultableBond_) / marketDefaultableBond_;
509 result[1] = (equityOption - marketEquityOption_) / marketEquityOption_;
510 return result;
511 }
512 }
513 const Mode mode_;
514 const Real strike_, marketEquityOption_, marketDefaultableBond_;
515 Real &h0_, &sigma_;
516 const std::vector<Real>& locations_;
517 const Array &dy_, &p_;
518 const QuantLib::ext::shared_ptr<FdmBackwardSolver> solver_;
519 const Real t_from_, t_to_;
520 const Size steps_, dampingSteps_;
521 };
522
523
524
525
526
527
528 if (i == 0) {
532 guess[1] = std::sqrt(volatility->blackVariance(
stepTimes_[i], forward) /
534 lastValidGuess = guess;
535 } else {
536 if (lastOptimizationError < thresholdSuccessfulOptimization) {
537 guess[0] =
h0_[i - 1];
539 lastValidGuess = guess;
540 } else {
541 guess = lastValidGuess;
542 }
543 }
544
545
546
547 guess[0] = TargetFunction::hToOpt(guess[0]);
548 guess[1] = TargetFunction::sigmaToOpt(guess[1]);
549
550
551
554 Size
steps =
static_cast<Size
>(0.5 + (t_from - t_to) *
static_cast<Real
>(timeStepsPerYear));
555
556 NoConstraint noConstraint;
557 LevenbergMarquardt lm;
558 EndCriteria endCriteria(100, 10, 1E-8, 1E-8, 1E-8);
560
561 TargetFunction targetFunction(TargetFunction::Mode::both, forward, marketEquityOption,
563 solver, t_from, t_to,
steps, i == 0 ? dampingSteps : 0);
564 Problem problem(targetFunction, noConstraint, guess);
565 lm.minimize(problem, endCriteria);
566 h0_[i] = TargetFunction::hToReal(problem.currentValue()[0]);
567 sigma_[i] = TargetFunction::sigmaToReal(problem.currentValue()[1]);
568 lastOptimizationError = problem.functionValue();
569 } else {
570
571 TargetFunction target_function_h0(TargetFunction::Mode::h0, forward, marketEquityOption,
573 solver, t_from, t_to,
steps, i == 0 ? dampingSteps : 0);
574 TargetFunction target_function_sigma(TargetFunction::Mode::sigma, forward, marketEquityOption,
576 dy, solver, t_from, t_to,
steps, i == 0 ? dampingSteps : 0);
577 Real current0 = guess[0], current1 = guess[1];
578 Real delta0 = 0.0, delta1 = 0.0;
579 Real functionValue0 = 0.0, functionValue1 = 0.0;
580 Size iteration = 0;
581 constexpr Real tol_x = 1E-8;
582 do {
583
584 Problem p0(target_function_h0, noConstraint, Array(1, current0));
585 lm.minimize(p0, endCriteria);
586 h0_[i] = TargetFunction::hToReal(p0.currentValue()[0]);
587 delta0 = std::abs(
h0_[i] - TargetFunction::hToReal(current0));
588 current0 = p0.currentValue()[0];
589 functionValue0 = p0.functionValue();
590
591 Problem p1(target_function_sigma, noConstraint, Array(1, current1));
592 lm.minimize(p1, endCriteria);
593 sigma_[i] = TargetFunction::sigmaToReal(p1.currentValue()[0]);
594 delta1 = std::abs(
sigma_[i] - TargetFunction::sigmaToReal(current1));
595 current1 = p1.currentValue()[0];
596 functionValue1 = p1.functionValue();
597 } while (iteration++ < 50 && (delta0 > tol_x || delta1 > tol_x));
598 lastOptimizationError =
599 std::sqrt(0.5 * (functionValue0 * functionValue0 + functionValue1 * functionValue1));
600 }
601
602
603
604 solver->rollback(
p, t_from, t_to,
steps, i == 0 ? dampingSteps : 0);
605
606 }
607 }
608}
QuantLib::ext::shared_ptr< Fdm1dMesher > mesher_
std::vector< Size > steps