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"); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; Utils.QL_REQUIRE(payoff != null, () => "non-striked 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 forwardPrice = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); if (dividendDiscount >= 1.0 && payoff.optionType() == Option.Type.Call) { // early exercise never optimal 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 CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution(); NormalDistribution normalDist = new NormalDistribution(); double tolerance = 1e-6; double Sk = BaroneAdesiWhaleyApproximationEngine.criticalPrice(payoff, riskFreeDiscount, dividendDiscount, variance, tolerance); double forwardSk = Sk * dividendDiscount / riskFreeDiscount; double alpha = -2.0 * Math.Log(riskFreeDiscount) / (variance); double beta = 2.0 * Math.Log(dividendDiscount / riskFreeDiscount) / (variance); double h = 1 - riskFreeDiscount; double phi = 0; switch (payoff.optionType()) { case Option.Type.Call: phi = 1; break; case Option.Type.Put: phi = -1; break; default: Utils.QL_FAIL("invalid option type"); break; } //it can throw: to be fixed // FLOATING_POINT_EXCEPTION double temp_root = Math.Sqrt((beta - 1) * (beta - 1) + (4 * alpha) / h); double lambda = (-(beta - 1) + phi * temp_root) / 2; double lambda_prime = -phi * alpha / (h * h * temp_root); double black_Sk = Utils.blackFormula(payoff.optionType(), payoff.strike(), forwardSk, Math.Sqrt(variance)) * riskFreeDiscount; double hA = phi * (Sk - payoff.strike()) - black_Sk; double d1_Sk = (Math.Log(forwardSk / payoff.strike()) + 0.5 * variance) / Math.Sqrt(variance); double d2_Sk = d1_Sk - Math.Sqrt(variance); double part1 = forwardSk * normalDist.value(d1_Sk) / (alpha * Math.Sqrt(variance)); double part2 = -phi *forwardSk *cumNormalDist.value(phi *d1_Sk) * Math.Log(dividendDiscount) / Math.Log(riskFreeDiscount); double part3 = +phi *payoff.strike() * cumNormalDist.value(phi * d2_Sk); double V_E_h = part1 + part2 + part3; double b = (1 - h) * alpha * lambda_prime / (2 * (2 * lambda + beta - 1)); double c = -((1 - h) * alpha / (2 * lambda + beta - 1)) * (V_E_h / (hA) + 1 / h + lambda_prime / (2 * lambda + beta - 1)); double temp_spot_ratio = Math.Log(spot / Sk); double chi = temp_spot_ratio * (b * temp_spot_ratio + c); if (phi * (Sk - spot) > 0) { results_.value = black.value() + hA * Math.Pow((spot / Sk), lambda) / (1 - chi); } else { results_.value = phi * (spot - payoff.strike()); } double temp_chi_prime = (2 * b / spot) * Math.Log(spot / Sk); double chi_prime = temp_chi_prime + c / spot; double chi_double_prime = 2 * b / (spot * spot) - temp_chi_prime / spot - c / (spot * spot); results_.delta = phi * dividendDiscount * cumNormalDist.value(phi * d1_Sk) + (lambda / (spot * (1 - chi)) + chi_prime / ((1 - chi) * (1 - chi))) * (phi * (Sk - payoff.strike()) - black_Sk) * Math.Pow((spot / Sk), lambda); results_.gamma = phi * dividendDiscount * normalDist.value(phi * d1_Sk) / (spot * Math.Sqrt(variance)) + (2 * lambda * chi_prime / (spot * (1 - chi) * (1 - chi)) + 2 * chi_prime * chi_prime / ((1 - chi) * (1 - chi) * (1 - chi)) + chi_double_prime / ((1 - chi) * (1 - chi)) + lambda * (1 - lambda) / (spot * spot * (1 - chi))) * (phi * (Sk - payoff.strike()) - black_Sk) * Math.Pow((spot / Sk), lambda); } // end of "early exercise can be optimal" }
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() { 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"); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; Utils.QL_REQUIRE(payoff != null, () => "non-striked 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 forwardPrice = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); if (dividendDiscount >= 1.0 && payoff.optionType() == Option.Type.Call) { // early exercise never optimal 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 CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution(); double tolerance = 1e-6; double Sk = criticalPrice(payoff, riskFreeDiscount, dividendDiscount, variance, tolerance); double forwardSk = Sk * dividendDiscount / riskFreeDiscount; double d1 = (Math.Log(forwardSk / payoff.strike()) + 0.5 * variance) / Math.Sqrt(variance); double n = 2.0 * Math.Log(dividendDiscount / riskFreeDiscount) / variance; double K = (!Utils.close(riskFreeDiscount, 1.0, 1000)) ? -2.0 * Math.Log(riskFreeDiscount) / (variance * (1.0 - riskFreeDiscount)) : 2.0 / variance; double Q, a; switch (payoff.optionType()) { case Option.Type.Call: Q = (-(n - 1.0) + Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * K)) / 2.0; a = (Sk / Q) * (1.0 - dividendDiscount * cumNormalDist.value(d1)); if (spot < Sk) { results_.value = black.value() + a * Math.Pow((spot / Sk), Q); } else { results_.value = spot - payoff.strike(); } break; case Option.Type.Put: Q = (-(n - 1.0) - Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * K)) / 2.0; a = -(Sk / Q) * (1.0 - dividendDiscount * cumNormalDist.value(-d1)); if (spot > Sk) { results_.value = black.value() + a * Math.Pow((spot / Sk), Q); } else { results_.value = payoff.strike() - spot; } break; default: Utils.QL_FAIL("unknown option type"); break; } } // end of "early exercise can be optimal" }
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); } }