public override void calculate() { AmericanExercise ex = arguments_.exercise as AmericanExercise; Utils.QL_REQUIRE(ex != null, () => "non-American exercise given"); Utils.QL_REQUIRE(ex.payoffAtExpiry(), () => "payoff must be at expiry"); Utils.QL_REQUIRE(ex.dates()[0] <= process_.blackVolatility().link.referenceDate(), () => "American option with window exercise not handled yet"); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; Utils.QL_REQUIRE(payoff != null, () => "non-striked payoff given"); double spot = process_.stateVariable().link.value(); Utils.QL_REQUIRE(spot > 0.0, () => "negative or null underlying given"); double variance = process_.blackVolatility().link.blackVariance(ex.lastDate(), payoff.strike()); double?barrier = arguments_.barrier; Utils.QL_REQUIRE(barrier > 0.0, () => "positive barrier value required"); Barrier.Type barrierType = arguments_.barrierType; // KO degenerate cases if ((barrierType == Barrier.Type.DownOut && spot <= barrier) || (barrierType == Barrier.Type.UpOut && spot >= barrier)) { // knocked out, no value results_.value = 0; results_.delta = 0; results_.gamma = 0; results_.vega = 0; results_.theta = 0; results_.rho = 0; results_.dividendRho = 0; return; } // KI degenerate cases if ((barrierType == Barrier.Type.DownIn && spot <= barrier) || (barrierType == Barrier.Type.UpIn && spot >= barrier)) { // knocked in - is a digital european Exercise exercise = new EuropeanExercise(arguments_.exercise.lastDate()); IPricingEngine engine = new AnalyticEuropeanEngine(process_); VanillaOption opt = new VanillaOption(payoff, exercise); opt.setPricingEngine(engine); results_.value = opt.NPV(); results_.delta = opt.delta(); results_.gamma = opt.gamma(); results_.vega = opt.vega(); results_.theta = opt.theta(); results_.rho = opt.rho(); results_.dividendRho = opt.dividendRho(); return; } double riskFreeDiscount = process_.riskFreeRate().link.discount(ex.lastDate()); AnalyticBinaryBarrierEngine_helper helper = new AnalyticBinaryBarrierEngine_helper( process_, payoff, ex, arguments_); results_.value = helper.payoffAtExpiry(spot, variance, riskFreeDiscount); }
public override void calculate() { if (arguments_.barrierType == DoubleBarrier.Type.KIKO || arguments_.barrierType == DoubleBarrier.Type.KOKI) { AmericanExercise ex = arguments_.exercise as AmericanExercise; Utils.QL_REQUIRE(ex != null, () => "KIKO/KOKI options must have American exercise"); Utils.QL_REQUIRE(ex.dates()[0] <= process_.blackVolatility().currentLink().referenceDate(), () => "American option with window exercise not handled yet"); } else { EuropeanExercise ex = arguments_.exercise as EuropeanExercise; Utils.QL_REQUIRE(ex != null, () => "non-European exercise given"); } CashOrNothingPayoff payoff = arguments_.payoff as CashOrNothingPayoff; Utils.QL_REQUIRE(payoff != null, () => "a cash-or-nothing payoff must be given"); double spot = process_.stateVariable().currentLink().value(); Utils.QL_REQUIRE(spot > 0.0, () => "negative or null underlying given"); double variance = process_.blackVolatility().currentLink().blackVariance( arguments_.exercise.lastDate(), payoff.strike()); double barrier_lo = arguments_.barrier_lo.Value; double barrier_hi = arguments_.barrier_hi.Value; DoubleBarrier.Type barrierType = arguments_.barrierType; Utils.QL_REQUIRE(barrier_lo > 0.0, () => "positive low barrier value required"); Utils.QL_REQUIRE(barrier_hi > 0.0, () => "positive high barrier value required"); Utils.QL_REQUIRE(barrier_lo < barrier_hi, () => "barrier_lo must be < barrier_hi"); Utils.QL_REQUIRE(barrierType == DoubleBarrier.Type.KnockIn || barrierType == DoubleBarrier.Type.KnockOut || barrierType == DoubleBarrier.Type.KIKO || barrierType == DoubleBarrier.Type.KOKI, () => "Unsupported barrier type"); // degenerate cases switch (barrierType) { case DoubleBarrier.Type.KnockOut: if (spot <= barrier_lo || spot >= barrier_hi) { // knocked out, no value results_.value = 0; results_.delta = 0; results_.gamma = 0; results_.vega = 0; results_.rho = 0; return; } break; case DoubleBarrier.Type.KnockIn: if (spot <= barrier_lo || spot >= barrier_hi) { // knocked in - pays results_.value = payoff.cashPayoff(); results_.delta = 0; results_.gamma = 0; results_.vega = 0; results_.rho = 0; return; } break; case DoubleBarrier.Type.KIKO: if (spot >= barrier_hi) { // knocked out, no value results_.value = 0; results_.delta = 0; results_.gamma = 0; results_.vega = 0; results_.rho = 0; return; } else if (spot <= barrier_lo) { // knocked in, pays results_.value = payoff.cashPayoff(); results_.delta = 0; results_.gamma = 0; results_.vega = 0; results_.rho = 0; return; } break; case DoubleBarrier.Type.KOKI: if (spot <= barrier_lo) { // knocked out, no value results_.value = 0; results_.delta = 0; results_.gamma = 0; results_.vega = 0; results_.rho = 0; return; } else if (spot >= barrier_hi) { // knocked in, pays results_.value = payoff.cashPayoff(); results_.delta = 0; results_.gamma = 0; results_.vega = 0; results_.rho = 0; return; } break; } AnalyticDoubleBarrierBinaryEngineHelper helper = new AnalyticDoubleBarrierBinaryEngineHelper(process_, payoff, arguments_); switch (barrierType) { case DoubleBarrier.Type.KnockOut: case DoubleBarrier.Type.KnockIn: results_.value = helper.payoffAtExpiry(spot, variance, barrierType); break; case DoubleBarrier.Type.KIKO: case DoubleBarrier.Type.KOKI: results_.value = helper.payoffKIKO(spot, variance, barrierType); break; default: results_.value = null; break; } }
public override void calculate() { Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.American, () => "not an American Option"); AmericanExercise ex = arguments_.exercise as AmericanExercise; Utils.QL_REQUIRE(ex != null, () => "non-American exercise given"); Utils.QL_REQUIRE(!ex.payoffAtExpiry(), () => "payoff at expiry not handled"); PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); double variance = process_.blackVolatility().link.blackVariance(ex.lastDate(), payoff.strike()); double dividendDiscount = process_.dividendYield().link.discount(ex.lastDate()); double riskFreeDiscount = process_.riskFreeRate().link.discount(ex.lastDate()); double spot = process_.stateVariable().link.value(); Utils.QL_REQUIRE(spot > 0.0, () => "negative or null underlying given"); double strike = payoff.strike(); if (payoff.optionType() == Option.Type.Put) { // use put-call simmetry Utils.swap <double>(ref spot, ref strike); Utils.swap <double>(ref riskFreeDiscount, ref dividendDiscount); payoff = new PlainVanillaPayoff(Option.Type.Call, strike); } if (dividendDiscount >= 1.0) { // early exercise is never optimal - use Black formula double forwardPrice = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); results_.value = black.value(); results_.delta = black.delta(spot); results_.deltaForward = black.deltaForward(); results_.elasticity = black.elasticity(spot); results_.gamma = black.gamma(spot); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); double t = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); results_.rho = black.rho(t); t = divdc.yearFraction(process_.dividendYield().link.referenceDate(), arguments_.exercise.lastDate()); results_.dividendRho = black.dividendRho(t); t = voldc.yearFraction(process_.blackVolatility().link.referenceDate(), arguments_.exercise.lastDate()); results_.vega = black.vega(t); results_.theta = black.theta(spot, t); results_.thetaPerDay = black.thetaPerDay(spot, t); results_.strikeSensitivity = black.strikeSensitivity(); results_.itmCashProbability = black.itmCashProbability(); } else { // early exercise can be optimal - use approximation results_.value = americanCallApproximation(spot, strike, riskFreeDiscount, dividendDiscount, variance); } }