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() { if (!(arguments_.exercise.type() == Exercise.Type.European)) { throw new Exception("not an European Option"); } StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; if (payoff == null) { throw new Exception("not an European Option"); } 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 drift = Math.Log(dividendDiscount / riskFreeDiscount) - 0.5 * variance; Integrand f = new Integrand(arguments_.payoff, process_.stateVariable().link.value(), drift, variance); SegmentIntegral integrator = new SegmentIntegral(5000); double infinity = 10.0 * Math.Sqrt(variance); results_.value = process_.riskFreeRate().link.discount(arguments_.exercise.lastDate()) / Math.Sqrt(2.0 * Math.PI * variance) * integrator.value(f.value, drift - infinity, drift + infinity); }
//! default theta calculation for Black-Scholes options public static double blackScholesTheta(GeneralizedBlackScholesProcess p, double value, double delta, double gamma) { double u = p.stateVariable().currentLink().value(); double r = p.riskFreeRate().currentLink().zeroRate(0.0, Compounding.Continuous).rate(); double q = p.dividendYield().currentLink().zeroRate(0.0, Compounding.Continuous).rate(); double v = p.localVolatility().currentLink().localVol(0.0, u, false); return r *value -(r-q)*u *delta - 0.5 *v *v *u *u *gamma; }
//! default theta calculation for Black-Scholes options public static double blackScholesTheta(GeneralizedBlackScholesProcess p, double value, double delta, double gamma) { double u = p.stateVariable().currentLink().value(); double r = p.riskFreeRate().currentLink().zeroRate(0.0, Compounding.Continuous).rate(); double q = p.dividendYield().currentLink().zeroRate(0.0, Compounding.Continuous).rate(); double v = p.localVolatility().currentLink().localVol(0.0, u, false); return(r * value - (r - q) * u * delta - 0.5 * v * v * u * u * gamma); }
public static GeneralizedBlackScholesProcess clone(GeneralizedBlackScholesProcess process, SimpleQuote volQuote) { Handle<Quote> stateVariable = process.stateVariable(); Handle<YieldTermStructure> dividendYield = process.dividendYield(); Handle<YieldTermStructure> riskFreeRate = process.riskFreeRate(); Handle<BlackVolTermStructure> blackVol = process.blackVolatility(); var volatility = new Handle<BlackVolTermStructure>(new BlackConstantVol(blackVol.link.referenceDate(), blackVol.link.calendar(), new Handle<Quote>(volQuote), blackVol.link.dayCounter())); return new GeneralizedBlackScholesProcess(stateVariable, dividendYield, riskFreeRate, volatility); }
public static GeneralizedBlackScholesProcess clone(GeneralizedBlackScholesProcess process, SimpleQuote volQuote) { Handle <Quote> stateVariable = process.stateVariable(); Handle <YieldTermStructure> dividendYield = process.dividendYield(); Handle <YieldTermStructure> riskFreeRate = process.riskFreeRate(); Handle <BlackVolTermStructure> blackVol = process.blackVolatility(); var volatility = new Handle <BlackVolTermStructure>(new BlackConstantVol(blackVol.link.referenceDate(), blackVol.link.calendar(), new Handle <Quote>(volQuote), blackVol.link.dayCounter())); return(new GeneralizedBlackScholesProcess(stateVariable, dividendYield, riskFreeRate, volatility)); }
public FdmBlackScholesOp(FdmMesher mesher, GeneralizedBlackScholesProcess bsProcess, double strike, bool localVol = false, double?illegalLocalVolOverwrite = null, int direction = 0) { mesher_ = mesher; rTS_ = bsProcess.riskFreeRate().currentLink(); qTS_ = bsProcess.dividendYield().currentLink(); volTS_ = bsProcess.blackVolatility().currentLink(); localVol_ = (localVol) ? bsProcess.localVolatility().currentLink() : null; x_ = (localVol) ? new Vector(Vector.Exp(mesher.locations(direction))) : null; dxMap_ = new FirstDerivativeOp(direction, mesher); dxxMap_ = new SecondDerivativeOp(direction, mesher); mapT_ = new TripleBandLinearOp(direction, mesher); strike_ = strike; illegalLocalVolOverwrite_ = illegalLocalVolOverwrite; direction_ = direction; }
public override void calculate() { DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); Calendar volcal = process_.blackVolatility().link.calendar(); double s0 = process_.stateVariable().link.value(); if (!(s0 > 0.0)) { throw new ApplicationException("negative or null underlying given"); } double v = process_.blackVolatility().link.blackVol(arguments_.exercise.lastDate(), s0); Date maturityDate = arguments_.exercise.lastDate(); double r = process_.riskFreeRate().link.zeroRate(maturityDate, rfdc, Compounding.Continuous, Frequency.NoFrequency).rate(); double q = process_.dividendYield().link.zeroRate(maturityDate, divdc, Compounding.Continuous, Frequency.NoFrequency).rate(); Date referenceDate = process_.riskFreeRate().link.referenceDate(); // binomial trees with constant coefficient var flatRiskFree = new Handle <YieldTermStructure>(new FlatForward(referenceDate, r, rfdc)); var flatDividends = new Handle <YieldTermStructure>(new FlatForward(referenceDate, q, divdc)); var flatVol = new Handle <BlackVolTermStructure>(new BlackConstantVol(referenceDate, volcal, v, voldc)); PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; if (payoff == null) { throw new ApplicationException("non-plain payoff given"); } double maturity = rfdc.yearFraction(referenceDate, maturityDate); StochasticProcess1D bs = new GeneralizedBlackScholesProcess(process_.stateVariable(), flatDividends, flatRiskFree, flatVol); TimeGrid grid = new TimeGrid(maturity, timeSteps_); T tree = new T().factory(bs, maturity, timeSteps_, payoff.strike()); BlackScholesLattice <T> lattice = new BlackScholesLattice <T>(tree, r, maturity, timeSteps_); DiscretizedVanillaOption option = new DiscretizedVanillaOption(arguments_, process_, grid); option.initialize(lattice, maturity); // Partial derivatives calculated from various points in the // binomial tree (Odegaard) // Rollback to third-last step, and get underlying price (s2) & // option values (p2) at this point option.rollback(grid[2]); Vector va2 = new Vector(option.values()); if (!(va2.size() == 3)) { throw new ApplicationException("Expect 3 nodes in grid at second step"); } double p2h = va2[2]; // high-price double s2 = lattice.underlying(2, 2); // high price // Rollback to second-last step, and get option value (p1) at // this point option.rollback(grid[1]); Vector va = new Vector(option.values()); if (!(va.size() == 2)) { throw new ApplicationException("Expect 2 nodes in grid at first step"); } double p1 = va[1]; // Finally, rollback to t=0 option.rollback(0.0); double p0 = option.presentValue(); double s1 = lattice.underlying(1, 1); // Calculate partial derivatives double delta0 = (p1 - p0) / (s1 - s0); // dp/ds double delta1 = (p2h - p1) / (s2 - s1); // dp/ds // Store results results_.value = p0; results_.delta = delta0; results_.gamma = 2.0 * (delta1 - delta0) / (s2 - s0); //d(delta)/ds results_.theta = Utils.blackScholesTheta(process_, results_.value.GetValueOrDefault(), results_.delta.GetValueOrDefault(), results_.gamma.GetValueOrDefault()); }
private double dividendYield() { return(process_.dividendYield().link.zeroRate(residualTime(), Compounding.Continuous, Frequency.NoFrequency).value()); }
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() { 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() { 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 FdmBlackScholesMesher(int size, GeneralizedBlackScholesProcess process, double maturity, double strike, double?xMinConstraint = null, double?xMaxConstraint = null, double eps = 0.0001, double scaleFactor = 1.5, Pair <double?, double?> cPoint = null, DividendSchedule dividendSchedule = null, FdmQuantoHelper fdmQuantoHelper = null, double spotAdjustment = 0.0) : base(size) { double S = process.x0(); Utils.QL_REQUIRE(S > 0.0, () => "negative or null underlying given"); dividendSchedule = dividendSchedule == null ? new DividendSchedule() : dividendSchedule; List <pair_double> intermediateSteps = new List <pair_double>(); for (int i = 0; i < dividendSchedule.Count && process.time(dividendSchedule[i].date()) <= maturity; ++i) { intermediateSteps.Add( new pair_double( process.time(dividendSchedule[i].date()), dividendSchedule[i].amount() )); } int intermediateTimeSteps = (int)Math.Max(2, 24.0 * maturity); for (int i = 0; i < intermediateTimeSteps; ++i) { intermediateSteps.Add( new pair_double((i + 1) * (maturity / intermediateTimeSteps), 0.0)); } intermediateSteps.Sort(); Handle <YieldTermStructure> rTS = process.riskFreeRate(); Handle <YieldTermStructure> qTS = fdmQuantoHelper != null ? new Handle <YieldTermStructure>( new QuantoTermStructure(process.dividendYield(), process.riskFreeRate(), new Handle <YieldTermStructure>(fdmQuantoHelper.foreignTermStructure()), process.blackVolatility(), strike, new Handle <BlackVolTermStructure>(fdmQuantoHelper.fxVolatilityTermStructure()), fdmQuantoHelper.exchRateATMlevel(), fdmQuantoHelper.equityFxCorrelation())) : process.dividendYield(); double lastDivTime = 0.0; double fwd = S + spotAdjustment; double mi = fwd, ma = fwd; for (int i = 0; i < intermediateSteps.Count; ++i) { double divTime = intermediateSteps[i].first; double divAmount = intermediateSteps[i].second; fwd = fwd / rTS.currentLink().discount(divTime) * rTS.currentLink().discount(lastDivTime) * qTS.currentLink().discount(divTime) / qTS.currentLink().discount(lastDivTime); mi = Math.Min(mi, fwd); ma = Math.Max(ma, fwd); fwd -= divAmount; mi = Math.Min(mi, fwd); ma = Math.Max(ma, fwd); lastDivTime = divTime; } // Set the grid boundaries double normInvEps = new InverseCumulativeNormal().value(1 - eps); double sigmaSqrtT = process.blackVolatility().currentLink().blackVol(maturity, strike) * Math.Sqrt(maturity); double?xMin = Math.Log(mi) - sigmaSqrtT * normInvEps * scaleFactor; double?xMax = Math.Log(ma) + sigmaSqrtT * normInvEps * scaleFactor; if (xMinConstraint != null) { xMin = xMinConstraint; } if (xMaxConstraint != null) { xMax = xMaxConstraint; } Fdm1dMesher helper; if (cPoint != null && cPoint.first != null && Math.Log(cPoint.first.Value) >= xMin && Math.Log(cPoint.first.Value) <= xMax) { helper = new Concentrating1dMesher(xMin.Value, xMax.Value, size, new Pair <double?, double?>(Math.Log(cPoint.first.Value), cPoint.second)); } else { helper = new Uniform1dMesher(xMin.Value, xMax.Value, size); } locations_ = helper.locations(); for (int i = 0; i < locations_.Count; ++i) { dplus_[i] = helper.dplus(i); dminus_[i] = helper.dminus(i); } }