public override void calculate() { if (arguments_.exercise.type() != Exercise.Type.European) { throw new ApplicationException("not an European option"); } StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; if (payoff == null) { throw new ApplicationException("non-striked payoff given"); } double variance = process_.blackVolatility().link.blackVariance(arguments_.exercise.lastDate(), payoff.strike()); double dividendDiscount = process_.dividendYield().link.discount(arguments_.exercise.lastDate()); double riskFreeDiscount = process_.riskFreeRate().link.discount(arguments_.exercise.lastDate()); double spot = process_.stateVariable().link.value(); if (!(spot > 0.0)) { throw new ApplicationException("negative or null underlying given"); } 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); try { results_.theta = black.theta(spot, t); results_.thetaPerDay = black.thetaPerDay(spot, t); } catch { results_.theta = null; results_.thetaPerDay = null; } results_.strikeSensitivity = black.strikeSensitivity(); results_.itmCashProbability = black.itmCashProbability(); }
private double vanillaEquivalent() { // Call KI equates to vanilla - callKO StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; double forwardPrice = underlying() * dividendDiscount() / riskFreeDiscount(); BlackCalculator black = new BlackCalculator(payoff, forwardPrice, stdDeviation(), riskFreeDiscount()); double vanilla = black.value(); if (vanilla < 0.0) { vanilla = 0.0; } return(vanilla); }
public override void calculate() { if(arguments_.exercise.type() != Exercise.Type.European) throw new ApplicationException("not an European option"); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; if (payoff == null) throw new ApplicationException("non-striked payoff given"); double variance = process_.blackVolatility().link.blackVariance(arguments_.exercise.lastDate(), payoff.strike()); double dividendDiscount = process_.dividendYield().link.discount(arguments_.exercise.lastDate()); double riskFreeDiscount = process_.riskFreeRate().link.discount(arguments_.exercise.lastDate()); double spot = process_.stateVariable().link.value(); if (!(spot > 0.0)) throw new ApplicationException("negative or null underlying given"); 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); try { results_.theta = black.theta(spot, t); results_.thetaPerDay = black.thetaPerDay(spot, t); } catch { results_.theta = null; results_.thetaPerDay = null; } results_.strikeSensitivity = black.strikeSensitivity(); results_.itmCashProbability = black.itmCashProbability(); }
public override void calculate() { Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European Option"); EuropeanExercise exercise = arguments_.exercise as EuropeanExercise; Utils.QL_REQUIRE(exercise != null, () => "not an European Option"); SpreadBasketPayoff spreadPayoff = arguments_.payoff as SpreadBasketPayoff; Utils.QL_REQUIRE(spreadPayoff != null, () => " spread payoff expected"); PlainVanillaPayoff payoff = spreadPayoff.basePayoff() as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); double strike = payoff.strike(); double f1 = process1_.stateVariable().link.value(); double f2 = process2_.stateVariable().link.value(); // use atm vols double variance1 = process1_.blackVolatility().link.blackVariance(exercise.lastDate(), f1); double variance2 = process2_.blackVolatility().link.blackVariance(exercise.lastDate(), f2); double riskFreeDiscount = process1_.riskFreeRate().link.discount(exercise.lastDate()); Func <double, double> Square = x => x * x; double f = f1 / (f2 + strike); double v = Math.Sqrt(variance1 + variance2 * Square(f2 / (f2 + strike)) - 2 * rho_ * Math.Sqrt(variance1 * variance2) * (f2 / (f2 + strike))); BlackCalculator black = new BlackCalculator(new PlainVanillaPayoff(payoff.optionType(), 1.0), f, v, riskFreeDiscount); results_.value = (f2 + strike) * black.value(); }
public Calculator(BlackCalculator black) { black_ = black; }
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(IPricingEngineResults r) { OneAssetOption.Results results = r as OneAssetOption.Results; setGridLimits(); initializeInitialCondition(); initializeOperator(); initializeBoundaryConditions(); initializeStepCondition(); List <IOperator> operatorSet = new List <IOperator>(); List <Vector> arraySet = new List <Vector>(); BoundaryConditionSet bcSet = new BoundaryConditionSet(); StepConditionSet <Vector> conditionSet = new StepConditionSet <Vector>(); prices_ = (SampledCurve)intrinsicValues_.Clone(); controlPrices_ = (SampledCurve)intrinsicValues_.Clone(); controlOperator_ = (TridiagonalOperator)finiteDifferenceOperator_.Clone(); controlBCs_[0] = BCs_[0]; controlBCs_[1] = BCs_[1]; operatorSet.Add(finiteDifferenceOperator_); operatorSet.Add(controlOperator_); arraySet.Add(prices_.values()); arraySet.Add(controlPrices_.values()); bcSet.Add(BCs_); bcSet.Add(controlBCs_); conditionSet.Add(stepCondition_); conditionSet.Add(new NullCondition <Vector>()); var model = new FiniteDifferenceModel <ParallelEvolver <CrankNicolson <TridiagonalOperator> > >(operatorSet, bcSet); object temp = arraySet; model.rollback(ref temp, getResidualTime(), 0.0, timeSteps_, conditionSet); arraySet = (List <Vector>)temp; prices_.setValues(arraySet[0]); controlPrices_.setValues(arraySet[1]); StrikedTypePayoff striked_payoff = payoff_ as StrikedTypePayoff; Utils.QL_REQUIRE(striked_payoff != null, () => "non-striked payoff given"); double variance = process_.blackVolatility().link.blackVariance(exerciseDate_, striked_payoff.strike()); double dividendDiscount = process_.dividendYield().link.discount(exerciseDate_); double riskFreeDiscount = process_.riskFreeRate().link.discount(exerciseDate_); double spot = process_.stateVariable().link.value(); double forwardPrice = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black = new BlackCalculator(striked_payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); results.value = prices_.valueAtCenter() - controlPrices_.valueAtCenter() + black.value(); results.delta = prices_.firstDerivativeAtCenter() - controlPrices_.firstDerivativeAtCenter() + black.delta(spot); results.gamma = prices_.secondDerivativeAtCenter() - controlPrices_.secondDerivativeAtCenter() + black.gamma(spot); results.additionalResults["priceCurve"] = prices_; }
public override void calculate() { if (!(arguments_.exercise.type() == Exercise.Type.American)) { throw new ApplicationException("not an American Option"); } AmericanExercise ex = arguments_.exercise as AmericanExercise; if (ex == null) { throw new ApplicationException("non-American exercise given"); } if (ex.payoffAtExpiry()) { throw new ApplicationException("payoff at expiry not handled"); } StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; if (payoff == null) { throw new ApplicationException("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(); if (!(spot > 0.0)) { throw new ApplicationException("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 = -2.0 * Math.Log(riskFreeDiscount) / (variance * (1.0 - riskFreeDiscount)); 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: throw new ApplicationException("unknown option type"); } } // end of "early exercise can be optimal" }
public override void calculate(IPricingEngineResults r) { OneAssetOption.Results results = r as OneAssetOption.Results; setGridLimits(); initializeInitialCondition(); initializeOperator(); initializeBoundaryConditions(); initializeStepCondition(); // typedef StandardSystemFiniteDifferenceModel model_type; List<IOperator> operatorSet = new List<IOperator>(); List<Vector> arraySet = new List<Vector>(); BoundaryConditionSet bcSet = new BoundaryConditionSet(); StepConditionSet<Vector> conditionSet = new StepConditionSet<Vector>(); prices_ = (SampledCurve)intrinsicValues_.Clone(); controlPrices_ = (SampledCurve)intrinsicValues_.Clone(); controlOperator_ = (TridiagonalOperator)finiteDifferenceOperator_.Clone(); controlBCs_[0] = BCs_[0]; controlBCs_[1] = BCs_[1]; operatorSet.Add(finiteDifferenceOperator_); operatorSet.Add(controlOperator_); arraySet.Add(prices_.values()); arraySet.Add(controlPrices_.values()); bcSet.Add(BCs_); bcSet.Add(controlBCs_); conditionSet.Add(stepCondition_); conditionSet.Add(new NullCondition<Vector>()); var model = new FiniteDifferenceModel<ParallelEvolver<CrankNicolson<TridiagonalOperator>>>(operatorSet, bcSet); object temp = arraySet; model.rollback(ref temp, getResidualTime(), 0.0, timeSteps_, conditionSet); arraySet = (List<Vector>)temp; prices_.setValues(arraySet[0]); controlPrices_.setValues(arraySet[1]); StrikedTypePayoff striked_payoff = payoff_ as StrikedTypePayoff; if (striked_payoff == null) throw new ApplicationException("non-striked payoff given"); double variance = process_.blackVolatility().link.blackVariance(exerciseDate_, striked_payoff.strike()); double dividendDiscount = process_.dividendYield().link.discount(exerciseDate_); double riskFreeDiscount = process_.riskFreeRate().link.discount(exerciseDate_); double spot = process_.stateVariable().link.value(); double forwardPrice = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black = new BlackCalculator(striked_payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); results.value = prices_.valueAtCenter() - controlPrices_.valueAtCenter() + black.value(); results.delta = prices_.firstDerivativeAtCenter() - controlPrices_.firstDerivativeAtCenter() + black.delta(spot); results.gamma = prices_.secondDerivativeAtCenter() - controlPrices_.secondDerivativeAtCenter() + black.gamma(spot); results.additionalResults.Add("priceCurve", prices_); }
public override void calculate() { if (!(arguments_.exercise.type() == Exercise.Type.American)) throw new ApplicationException("not an American Option"); AmericanExercise ex = arguments_.exercise as AmericanExercise; if (ex == null) throw new ApplicationException("non-American exercise given"); if(ex.payoffAtExpiry()) throw new ApplicationException("payoff at expiry not handled"); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; if (payoff == null) throw new ApplicationException("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(); if (!(spot > 0.0)) throw new ApplicationException("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 = -2.0*Math.Log(riskFreeDiscount)/ (variance*(1.0-riskFreeDiscount)); 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: throw new ApplicationException("unknown option type"); } } // end of "early exercise can be optimal" }
public override void calculate() { if (!(arguments_.exercise.type() == Exercise.Type.American)) { throw new ApplicationException("not an American Option"); } AmericanExercise ex = arguments_.exercise as AmericanExercise; if (ex == null) { throw new ApplicationException("non-American exercise given"); } if (ex.payoffAtExpiry()) { throw new ApplicationException("payoff at expiry not handled"); } PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; if (payoff == null) { throw new ApplicationException("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(); if (!(spot > 0.0)) { throw new ApplicationException("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); } }
public override void calculate() { Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European option"); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; Utils.QL_REQUIRE(payoff != null, () => "non-striked payoff given"); Date settlementDate = process_.riskFreeRate().link.referenceDate(); double riskless = 0.0; int i; for (i = 0; i < arguments_.cashFlow.Count; i++) { if (arguments_.cashFlow[i].date() >= settlementDate) { riskless += arguments_.cashFlow[i].amount() * process_.riskFreeRate().link.discount(arguments_.cashFlow[i].date()); } } double spot = process_.stateVariable().link.value() - riskless; Utils.QL_REQUIRE(spot > 0.0, () => "negative or null underlying after subtracting dividends"); double dividendDiscount = process_.dividendYield().link.discount(arguments_.exercise.lastDate()); double riskFreeDiscount = process_.riskFreeRate().link.discount(arguments_.exercise.lastDate()); double forwardPrice = spot * dividendDiscount / riskFreeDiscount; double variance = process_.blackVolatility().link.blackVariance(arguments_.exercise.lastDate(), payoff.strike()); BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); results_.value = black.value(); results_.delta = black.delta(spot); results_.gamma = black.gamma(spot); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); double t = voldc.yearFraction(process_.blackVolatility().link.referenceDate(), arguments_.exercise.lastDate()); results_.vega = black.vega(t); double delta_theta = 0.0, delta_rho = 0.0; for (i = 0; i < arguments_.cashFlow.Count; i++) { Date d = arguments_.cashFlow[i].date(); if (d >= settlementDate) { delta_theta -= arguments_.cashFlow[i].amount() * process_.riskFreeRate().link.zeroRate(d, rfdc, Compounding.Continuous, Frequency.Annual).value() * process_.riskFreeRate().link.discount(d); double t1 = process_.time(d); delta_rho += arguments_.cashFlow[i].amount() * t1 * process_.riskFreeRate().link.discount(t1); } } t = process_.time(arguments_.exercise.lastDate()); try { results_.theta = black.theta(spot, t) + delta_theta * black.delta(spot); } catch (Exception) { results_.theta = null; } results_.rho = black.rho(t) + delta_rho * black.delta(spot); }
public override void calculate() { /* this engine cannot really check for the averageType==Geometric since it can be used as control variate for the Arithmetic version QL_REQUIRE(arguments_.averageType == Average::Geometric, "not a geometric average option"); */ if(!(arguments_.exercise.type() == Exercise.Type.European)) throw new ApplicationException("not an European Option"); double runningLog; int pastFixings; if (arguments_.averageType == Average.Type.Geometric) { if(!(arguments_.runningAccumulator>0.0)) throw new ApplicationException("positive running product required: " + arguments_.runningAccumulator + " not allowed"); runningLog = Math.Log(arguments_.runningAccumulator.GetValueOrDefault()); pastFixings = arguments_.pastFixings.GetValueOrDefault(); } else { // it is being used as control variate runningLog = 1.0; pastFixings = 0; } PlainVanillaPayoff payoff = (PlainVanillaPayoff)(arguments_.payoff); if (payoff == null) throw new ApplicationException("non-plain payoff given"); Date referenceDate = process_.riskFreeRate().link.referenceDate(); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); List<double> fixingTimes = new InitializedList<double>(arguments_.fixingDates.Count()); int i; for (i=0; i<arguments_.fixingDates.Count(); i++) { if (arguments_.fixingDates[i]>=referenceDate) { double t = voldc.yearFraction(referenceDate, arguments_.fixingDates[i]); fixingTimes.Add(t); } } int remainingFixings = fixingTimes.Count(); int numberOfFixings = pastFixings + remainingFixings; double N = numberOfFixings; double pastWeight = pastFixings/N; double futureWeight = 1.0-pastWeight; /*double timeSum = std::accumulate(fixingTimes.begin(), fixingTimes.end(), 0.0);*/ double timeSum = 0; fixingTimes.ForEach((ii, vv) => timeSum += fixingTimes[ii]); double vola = process_.blackVolatility().link.blackVol( arguments_.exercise.lastDate(), payoff.strike()); double temp = 0.0; for (i=pastFixings+1; i<numberOfFixings; i++) temp += fixingTimes[i-pastFixings-1]*(N-i); double variance = vola*vola /N/N * (timeSum+ 2.0*temp); double dsigG_dsig = Math.Sqrt((timeSum + 2.0*temp))/N; double sigG = vola * dsigG_dsig; double dmuG_dsig = -(vola * timeSum)/N; Date exDate = arguments_.exercise.lastDate(); double dividendRate = process_.dividendYield().link. zeroRate(exDate, divdc, Compounding.Continuous, Frequency.NoFrequency).rate(); double riskFreeRate = process_.riskFreeRate().link. zeroRate(exDate, rfdc, Compounding.Continuous, Frequency.NoFrequency).rate(); double nu = riskFreeRate - dividendRate - 0.5*vola*vola; double s = process_.stateVariable().link.value(); if(!(s > 0.0)) throw new ApplicationException("positive underlying value required"); int M = (pastFixings == 0 ? 1 : pastFixings); double muG = pastWeight * runningLog/M + futureWeight * Math.Log(s) + nu*timeSum/N; double forwardPrice = Math.Exp(muG + variance / 2.0); double riskFreeDiscount = process_.riskFreeRate().link.discount( arguments_.exercise.lastDate()); BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); results_.value = black.value(); results_.delta = futureWeight*black.delta(forwardPrice)*forwardPrice/s; results_.gamma = forwardPrice*futureWeight/(s*s) *( black.gamma(forwardPrice)*futureWeight*forwardPrice - pastWeight*black.delta(forwardPrice) ); double Nx_1, nx_1; CumulativeNormalDistribution CND = new CumulativeNormalDistribution(); NormalDistribution ND = new NormalDistribution(); if (sigG > Const.QL_Epsilon) { double x_1 = (muG-Math.Log(payoff.strike())+variance)/sigG; Nx_1 = CND.value(x_1); nx_1 = ND.value( x_1); } else { Nx_1 = (muG > Math.Log(payoff.strike()) ? 1.0 : 0.0); nx_1 = 0.0; } results_.vega = forwardPrice * riskFreeDiscount * ( (dmuG_dsig + sigG * dsigG_dsig)*Nx_1 + nx_1*dsigG_dsig ); if (payoff.optionType() == Option.Type.Put) results_.vega -= riskFreeDiscount * forwardPrice * (dmuG_dsig + sigG * dsigG_dsig); double tRho = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); results_.rho = black.rho(tRho)*timeSum/(N*tRho) - (tRho-timeSum/N)*results_.value; double tDiv = divdc.yearFraction( process_.dividendYield().link.referenceDate(), arguments_.exercise.lastDate()); results_.dividendRho = black.dividendRho(tDiv)*timeSum /(N*tDiv); results_.strikeSensitivity = black.strikeSensitivity(); results_.theta = Utils.blackScholesTheta(process_, results_.value.GetValueOrDefault(), results_.delta.GetValueOrDefault(), results_.gamma.GetValueOrDefault()); }
public override void calculate() { Utils.QL_REQUIRE(arguments_.accruedCoupon == null && arguments_.lastFixing == null, () => "this engine cannot price options already started"); Utils.QL_REQUIRE(arguments_.localCap == null && arguments_.localFloor == null && arguments_.globalCap == null && arguments_.globalFloor == null, () => "this engine cannot price capped/floored options"); Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European option"); PercentageStrikePayoff moneyness = arguments_.payoff as PercentageStrikePayoff; Utils.QL_REQUIRE(moneyness != null, () => "wrong payoff given"); List <Date> resetDates = arguments_.resetDates; resetDates.Add(arguments_.exercise.lastDate()); double underlying = process_.stateVariable().link.value(); Utils.QL_REQUIRE(underlying > 0.0, () => "negative or null underlying"); StrikedTypePayoff payoff = new PlainVanillaPayoff(moneyness.optionType(), 1.0); results_.value = 0.0; results_.delta = results_.gamma = 0.0; results_.theta = 0.0; results_.rho = results_.dividendRho = 0.0; results_.vega = 0.0; for (int i = 1; i < resetDates.Count; i++) { double discount = process_.riskFreeRate().link.discount(resetDates[i - 1]); double rDiscount = process_.riskFreeRate().link.discount(resetDates[i]) / process_.riskFreeRate().link.discount(resetDates[i - 1]); double qDiscount = process_.dividendYield().link.discount(resetDates[i]) / process_.dividendYield().link.discount(resetDates[i - 1]); double forward = (1.0 / moneyness.strike()) * qDiscount / rDiscount; double variance = process_.blackVolatility().link.blackForwardVariance( resetDates[i - 1], resetDates[i], underlying * moneyness.strike()); BlackCalculator black = new BlackCalculator(payoff, forward, Math.Sqrt(variance), rDiscount); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); results_.value += discount * moneyness.strike() * black.value(); results_.delta += 0.0; results_.gamma += 0.0; results_.theta += process_.riskFreeRate().link.forwardRate( resetDates[i - 1], resetDates[i], rfdc, Compounding.Continuous, Frequency.NoFrequency).value() * discount * moneyness.strike() * black.value(); double dt = rfdc.yearFraction(resetDates[i - 1], resetDates[i]); double t = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), resetDates[i - 1]); results_.rho += discount * moneyness.strike() * (black.rho(dt) - t * black.value()); dt = divdc.yearFraction(resetDates[i - 1], resetDates[i]); results_.dividendRho += discount * moneyness.strike() * black.dividendRho(dt); dt = voldc.yearFraction(resetDates[i - 1], resetDates[i]); results_.vega += discount * moneyness.strike() * black.vega(dt); } }
public override void calculate() { Utils.QL_REQUIRE( arguments_.exercise.type() == Exercise.Type.European, () => "not an European option" ); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; Utils.QL_REQUIRE( payoff != null, () => "non-striked payoff given" ); Date settlementDate = process_.riskFreeRate().link.referenceDate(); double riskless = 0.0; int i; for (i=0; i<arguments_.cashFlow.Count; i++) if (arguments_.cashFlow[i].date() >= settlementDate) riskless += arguments_.cashFlow[i].amount() * process_.riskFreeRate().link.discount(arguments_.cashFlow[i].date()); double spot = process_.stateVariable().link.value() - riskless; Utils.QL_REQUIRE( spot > 0.0, () => "negative or null underlying after subtracting dividends" ); double dividendDiscount = process_.dividendYield().link.discount(arguments_.exercise.lastDate()); double riskFreeDiscount = process_.riskFreeRate().link.discount(arguments_.exercise.lastDate()); double forwardPrice = spot * dividendDiscount / riskFreeDiscount; double variance = process_.blackVolatility().link.blackVariance( arguments_.exercise.lastDate(), payoff.strike()); BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); results_.value = black.value(); results_.delta = black.delta(spot); results_.gamma = black.gamma(spot); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); double t = voldc.yearFraction( process_.blackVolatility().link.referenceDate(),arguments_.exercise.lastDate()); results_.vega = black.vega(t); double delta_theta = 0.0, delta_rho = 0.0; for (i = 0; i < arguments_.cashFlow.Count; i++) { Date d = arguments_.cashFlow[i].date(); if (d >= settlementDate) { delta_theta -= arguments_.cashFlow[i].amount() * process_.riskFreeRate().link.zeroRate(d,rfdc,Compounding.Continuous,Frequency.Annual).value() * process_.riskFreeRate().link.discount(d); double t1 = process_.time(d); delta_rho += arguments_.cashFlow[i].amount() * t1 * process_.riskFreeRate().link.discount(t1); } } t = process_.time(arguments_.exercise.lastDate()); try { results_.theta = black.theta(spot, t) + delta_theta * black.delta(spot); } catch (Exception) { results_.theta = null; } results_.rho = black.rho(t) + delta_rho * black.delta(spot); }
public override void calculate() { if (arguments_.averageType != Average.Type.Geometric) { throw new ApplicationException("not a geometric average option"); } if (arguments_.exercise.type() != Exercise.Type.European) { throw new ApplicationException("not an European Option"); } Date exercise = arguments_.exercise.lastDate(); PlainVanillaPayoff payoff = (PlainVanillaPayoff)(arguments_.payoff); if (payoff == null) { throw new ApplicationException("non-plain payoff given"); } double volatility = process_.blackVolatility().link.blackVol(exercise, payoff.strike()); double variance = process_.blackVolatility().link.blackVariance(exercise, payoff.strike()); double riskFreeDiscount = process_.riskFreeRate().link.discount(exercise); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); double dividendYield = 0.5 * ( process_.riskFreeRate().link.zeroRate(exercise, rfdc, Compounding.Continuous, Frequency.NoFrequency).rate() + process_.dividendYield().link.zeroRate(exercise, divdc, Compounding.Continuous, Frequency.NoFrequency).rate() + volatility * volatility / 6.0); double t_q = divdc.yearFraction( process_.dividendYield().link.referenceDate(), exercise); double dividendDiscount = Math.Exp(-dividendYield * t_q); double spot = process_.stateVariable().link.value(); if (!(spot > 0.0)) { throw new ApplicationException("negative or null underlying"); } double forward = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black = new BlackCalculator(payoff, forward, Math.Sqrt(variance / 3.0), riskFreeDiscount); results_.value = black.value(); results_.delta = black.delta(spot); results_.gamma = black.gamma(spot); results_.dividendRho = black.dividendRho(t_q) / 2.0; double t_r = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); results_.rho = black.rho(t_r) + 0.5 * black.dividendRho(t_q); double t_v = voldc.yearFraction( process_.blackVolatility().link.referenceDate(), arguments_.exercise.lastDate()); results_.vega = black.vega(t_v) / Math.Sqrt(3.0) + black.dividendRho(t_q) * volatility / 6.0; try{ results_.theta = black.theta(spot, t_v); } catch (System.Exception /*Error*/) { results_.theta = double.NaN; //Null<Real>(); } }
public override void calculate() { if (!(arguments_.exercise.type() == Exercise.Type.American)) throw new ApplicationException("not an American Option"); AmericanExercise ex = arguments_.exercise as AmericanExercise; if (ex == null) throw new ApplicationException("non-American exercise given"); if (ex.payoffAtExpiry()) throw new ApplicationException("payoff at expiry not handled"); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; if (payoff == null) throw new ApplicationException("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(); if (!(spot > 0.0)) throw new ApplicationException("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; switch (payoff.optionType()) { case Option.Type.Call: phi = 1; break; case Option.Type.Put: phi = -1; break; default: throw new ArgumentException("invalid option type"); } //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() { /* this engine cannot really check for the averageType==Geometric * since it can be used as control variate for the Arithmetic version * QL_REQUIRE(arguments_.averageType == Average::Geometric,"not a geometric average option") */ Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European Option"); double runningLog; int pastFixings; if (arguments_.averageType == Average.Type.Geometric) { Utils.QL_REQUIRE(arguments_.runningAccumulator > 0.0, () => "positive running product required: " + arguments_.runningAccumulator + " not allowed"); runningLog = Math.Log(arguments_.runningAccumulator.GetValueOrDefault()); pastFixings = arguments_.pastFixings.GetValueOrDefault(); } else { // it is being used as control variate runningLog = 1.0; pastFixings = 0; } PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); Date referenceDate = process_.riskFreeRate().link.referenceDate(); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); List <double> fixingTimes = new List <double>(); int i; for (i = 0; i < arguments_.fixingDates.Count; i++) { if (arguments_.fixingDates[i] >= referenceDate) { double t = voldc.yearFraction(referenceDate, arguments_.fixingDates[i]); fixingTimes.Add(t); } } int remainingFixings = fixingTimes.Count; int numberOfFixings = pastFixings + remainingFixings; double N = numberOfFixings; double pastWeight = pastFixings / N; double futureWeight = 1.0 - pastWeight; double timeSum = 0; fixingTimes.ForEach((ii, vv) => timeSum += fixingTimes[ii]); double vola = process_.blackVolatility().link.blackVol(arguments_.exercise.lastDate(), payoff.strike()); double temp = 0.0; for (i = pastFixings + 1; i < numberOfFixings; i++) { temp += fixingTimes[i - pastFixings - 1] * (N - i); } double variance = vola * vola / N / N * (timeSum + 2.0 * temp); double dsigG_dsig = Math.Sqrt((timeSum + 2.0 * temp)) / N; double sigG = vola * dsigG_dsig; double dmuG_dsig = -(vola * timeSum) / N; Date exDate = arguments_.exercise.lastDate(); double dividendRate = process_.dividendYield().link. zeroRate(exDate, divdc, Compounding.Continuous, Frequency.NoFrequency).rate(); double riskFreeRate = process_.riskFreeRate().link. zeroRate(exDate, rfdc, Compounding.Continuous, Frequency.NoFrequency).rate(); double nu = riskFreeRate - dividendRate - 0.5 * vola * vola; double s = process_.stateVariable().link.value(); Utils.QL_REQUIRE(s > 0.0, () => "positive underlying value required"); int M = (pastFixings == 0 ? 1 : pastFixings); double muG = pastWeight * runningLog / M + futureWeight * Math.Log(s) + nu * timeSum / N; double forwardPrice = Math.Exp(muG + variance / 2.0); double riskFreeDiscount = process_.riskFreeRate().link.discount(arguments_.exercise.lastDate()); BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); results_.value = black.value(); results_.delta = futureWeight * black.delta(forwardPrice) * forwardPrice / s; results_.gamma = forwardPrice * futureWeight / (s * s) * (black.gamma(forwardPrice) * futureWeight * forwardPrice - pastWeight * black.delta(forwardPrice)); double Nx_1, nx_1; CumulativeNormalDistribution CND = new CumulativeNormalDistribution(); NormalDistribution ND = new NormalDistribution(); if (sigG > Const.QL_EPSILON) { double x_1 = (muG - Math.Log(payoff.strike()) + variance) / sigG; Nx_1 = CND.value(x_1); nx_1 = ND.value(x_1); } else { Nx_1 = (muG > Math.Log(payoff.strike()) ? 1.0 : 0.0); nx_1 = 0.0; } results_.vega = forwardPrice * riskFreeDiscount * ((dmuG_dsig + sigG * dsigG_dsig) * Nx_1 + nx_1 * dsigG_dsig); if (payoff.optionType() == Option.Type.Put) { results_.vega -= riskFreeDiscount * forwardPrice * (dmuG_dsig + sigG * dsigG_dsig); } double tRho = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); results_.rho = black.rho(tRho) * timeSum / (N * tRho) - (tRho - timeSum / N) * results_.value; double tDiv = divdc.yearFraction( process_.dividendYield().link.referenceDate(), arguments_.exercise.lastDate()); results_.dividendRho = black.dividendRho(tDiv) * timeSum / (N * tDiv); results_.strikeSensitivity = black.strikeSensitivity(); results_.theta = Utils.blackScholesTheta(process_, results_.value.GetValueOrDefault(), results_.delta.GetValueOrDefault(), results_.gamma.GetValueOrDefault()); }
public override void calculate() { if (arguments_.averageType != Average.Type.Geometric) throw new ApplicationException("not a geometric average option"); if (arguments_.exercise.type() != Exercise.Type.European) throw new ApplicationException("not an European Option"); Date exercise = arguments_.exercise.lastDate(); PlainVanillaPayoff payoff = (PlainVanillaPayoff)(arguments_.payoff); if (payoff == null) throw new ApplicationException("non-plain payoff given"); double volatility = process_.blackVolatility().link.blackVol(exercise, payoff.strike()); double variance = process_.blackVolatility().link.blackVariance(exercise, payoff.strike()); double riskFreeDiscount = process_.riskFreeRate().link.discount(exercise); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); double dividendYield = 0.5 * ( process_.riskFreeRate().link.zeroRate(exercise, rfdc, Compounding.Continuous, Frequency.NoFrequency).rate() + process_.dividendYield().link.zeroRate(exercise, divdc, Compounding.Continuous, Frequency.NoFrequency).rate() + volatility * volatility / 6.0); double t_q = divdc.yearFraction( process_.dividendYield().link.referenceDate(), exercise); double dividendDiscount = Math.Exp(-dividendYield * t_q); double spot = process_.stateVariable().link.value(); if (!(spot > 0.0)) throw new ApplicationException("negative or null underlying"); double forward = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black = new BlackCalculator(payoff, forward, Math.Sqrt(variance / 3.0), riskFreeDiscount); results_.value = black.value(); results_.delta = black.delta(spot); results_.gamma = black.gamma(spot); results_.dividendRho = black.dividendRho(t_q) / 2.0; double t_r = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); results_.rho = black.rho(t_r) + 0.5 * black.dividendRho(t_q); double t_v = voldc.yearFraction( process_.blackVolatility().link.referenceDate(), arguments_.exercise.lastDate()); results_.vega = black.vega(t_v) / Math.Sqrt(3.0) + black.dividendRho(t_q) * volatility / 6.0; try{ results_.theta = black.theta(spot, t_v); } catch (System.Exception /*Error*/){ results_.theta = double.NaN; //Null<Real>(); } }
public override void calculate() { if (!(arguments_.exercise.type() == Exercise.Type.American)) throw new ApplicationException("not an American Option"); AmericanExercise ex = arguments_.exercise as AmericanExercise; if(ex == null) throw new ApplicationException("non-American exercise given"); if(ex.payoffAtExpiry()) throw new ApplicationException("payoff at expiry not handled"); PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; if(payoff == null) throw new ApplicationException("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(); if (!(spot > 0.0)) throw new ApplicationException("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); } }