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() { 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"); BasketPayoff basket_payoff = arguments_.payoff as BasketPayoff; MinBasketPayoff min_basket = arguments_.payoff as MinBasketPayoff; MaxBasketPayoff max_basket = arguments_.payoff as MaxBasketPayoff; Utils.QL_REQUIRE(min_basket != null || max_basket != null, () => "unknown basket type"); PlainVanillaPayoff payoff = basket_payoff.basePayoff() as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); double strike = payoff.strike(); double variance1 = process1_.blackVolatility().link.blackVariance(exercise.lastDate(), strike); double variance2 = process2_.blackVolatility().link.blackVariance(exercise.lastDate(), strike); double riskFreeDiscount = process1_.riskFreeRate().link.discount(exercise.lastDate()); // cannot handle non zero dividends, so don't believe this... double dividendDiscount1 = process1_.dividendYield().link.discount(exercise.lastDate()); double dividendDiscount2 = process2_.dividendYield().link.discount(exercise.lastDate()); double forward1 = process1_.stateVariable().link.value() * dividendDiscount1 / riskFreeDiscount; double forward2 = process2_.stateVariable().link.value() * dividendDiscount2 / riskFreeDiscount; if (max_basket != null) { switch (payoff.optionType()) { // euro call on a two asset max basket case Option.Type.Call: results_.value = euroTwoAssetMaxBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho_); break; // euro put on a two asset max basket case Option.Type.Put: results_.value = strike * riskFreeDiscount - euroTwoAssetMaxBasketCall(forward1, forward2, 0.0, riskFreeDiscount, variance1, variance2, rho_) + euroTwoAssetMaxBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho_); break; default: Utils.QL_FAIL("unknown option type"); break; } } else if (min_basket != null) { switch (payoff.optionType()) { // euro call on a two asset min basket case Option.Type.Call: results_.value = euroTwoAssetMinBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho_); break; // euro put on a two asset min basket case Option.Type.Put: results_.value = strike * riskFreeDiscount - euroTwoAssetMinBasketCall(forward1, forward2, 0.0, riskFreeDiscount, variance1, variance2, rho_) + euroTwoAssetMinBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho_); break; default: Utils.QL_FAIL("unknown option type"); break; } } else { Utils.QL_FAIL("unknown type"); } }
public override void calculate() { // First: tests on types Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European Option"); PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "not a plain-vanilla payoff"); // forward values - futures, so b=0 double forward1 = process1_.stateVariable().link.value(); double forward2 = process2_.stateVariable().link.value(); Date exerciseDate = arguments_.exercise.lastDate(); // Volatilities double sigma1 = process1_.blackVolatility().link.blackVol(exerciseDate, forward1); double sigma2 = process2_.blackVolatility().link.blackVol(exerciseDate, forward2); double riskFreeDiscount = process1_.riskFreeRate().link.discount(exerciseDate); double strike = payoff.strike(); // Unique F (forward) value for pricing double F = forward1 / (forward2 + strike); // Its volatility double sigma = Math.Sqrt(Math.Pow(sigma1, 2) + Math.Pow((sigma2 * (forward2 / (forward2 + strike))), 2) - 2 * rho_.link.value() * sigma1 * sigma2 * (forward2 / (forward2 + strike))); // Day counter and Dates handling variables DayCounter rfdc = process1_.riskFreeRate().link.dayCounter(); double t = rfdc.yearFraction(process1_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); // Black-Scholes solution values double d1 = (Math.Log(F) + 0.5 * Math.Pow(sigma, 2) * t) / (sigma * Math.Sqrt(t)); double d2 = d1 - sigma * Math.Sqrt(t); NormalDistribution pdf = new NormalDistribution(); CumulativeNormalDistribution cum = new CumulativeNormalDistribution(); double Nd1 = cum.value(d1); double Nd2 = cum.value(d2); double NMd1 = cum.value(-d1); double NMd2 = cum.value(-d2); Option.Type optionType = payoff.optionType(); if (optionType == Option.Type.Call) { results_.value = riskFreeDiscount * (F * Nd1 - Nd2) * (forward2 + strike); } else { results_.value = riskFreeDiscount * (NMd2 - F * NMd1) * (forward2 + strike); } double?callValue = optionType == Option.Type.Call ? results_.value : riskFreeDiscount * (F * Nd1 - Nd2) * (forward2 + strike); results_.theta = (Math.Log(riskFreeDiscount) / t) * callValue + riskFreeDiscount * (forward1 * sigma) / (2 * Math.Sqrt(t)) * pdf.value(d1); }
public override void calculate() { Utils.QL_REQUIRE(arguments_.averageType == Average.Type.Geometric, () => "not a geometric average option"); Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European option"); Utils.QL_REQUIRE(arguments_.runningAccumulator > 0.0, () => "positive running product required: " + arguments_.runningAccumulator + "not allowed"); double runningLog = Math.Log(arguments_.runningAccumulator.GetValueOrDefault()); int? pastFixings = arguments_.pastFixings; Utils.QL_REQUIRE(pastFixings == 0, () => "past fixings currently not managed"); PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); List <double> fixingTimes = new List <double>(); for (int i = 0; i < arguments_.fixingDates.Count; i++) { if (arguments_.fixingDates[i] >= arguments_.fixingDates[0]) { double t = voldc.yearFraction(arguments_.fixingDates[0], arguments_.fixingDates[i]); fixingTimes.Add(t); } } int remainingFixings = fixingTimes.Count; int numberOfFixings = pastFixings.GetValueOrDefault() + remainingFixings; double N = (double)(numberOfFixings); double pastWeight = pastFixings.GetValueOrDefault() / N; double futureWeight = 1.0 - pastWeight; double timeSum = 0; fixingTimes.ForEach((ii, vv) => timeSum += fixingTimes[ii]); double residualTime = rfdc.yearFraction(arguments_.fixingDates[pastFixings.GetValueOrDefault()], arguments_.exercise.lastDate()); double underlying = process_.stateVariable().link.value(); Utils.QL_REQUIRE(underlying > 0.0, () => "positive underlying value required"); double volatility = process_.blackVolatility().link.blackVol(arguments_.exercise.lastDate(), underlying); Date exDate = arguments_.exercise.lastDate(); double dividendRate = process_.dividendYield().link.zeroRate(exDate, divdc, Compounding.Continuous, Frequency.NoFrequency).value(); double riskFreeRate = process_.riskFreeRate().link.zeroRate(exDate, rfdc, Compounding.Continuous, Frequency.NoFrequency).value(); double nu = riskFreeRate - dividendRate - 0.5 * volatility * volatility; double temp = 0.0; for (int i = pastFixings.GetValueOrDefault() + 1; i < numberOfFixings; i++) { temp += fixingTimes[i - pastFixings.GetValueOrDefault() - 1] * (N - i); } double variance = volatility * volatility / N / N * (timeSum + 2.0 * temp); double covarianceTerm = volatility * volatility / N * timeSum; double sigmaSum_2 = variance + volatility * volatility * residualTime - 2.0 * covarianceTerm; int M = (pastFixings.GetValueOrDefault() == 0 ? 1 : pastFixings.GetValueOrDefault()); double runningLogAverage = runningLog / M; double muG = pastWeight * runningLogAverage + futureWeight * Math.Log(underlying) + nu * timeSum / N; CumulativeNormalDistribution f = new CumulativeNormalDistribution(); double y1 = (Math.Log(underlying) + (riskFreeRate - dividendRate) * residualTime - muG - variance / 2.0 + sigmaSum_2 / 2.0) / Math.Sqrt(sigmaSum_2); double y2 = y1 - Math.Sqrt(sigmaSum_2); switch (payoff.optionType()) { case Option.Type.Call: results_.value = underlying * Math.Exp(-dividendRate * residualTime) * f.value(y1) - Math.Exp(muG + variance / 2.0 - riskFreeRate * residualTime) * f.value(y2); break; case Option.Type.Put: results_.value = -underlying *Math.Exp(-dividendRate *residualTime) * f.value(-y1) + Math.Exp(muG + variance / 2.0 - riskFreeRate * residualTime) * f.value(-y2); break; default: Utils.QL_FAIL("invalid option type"); break; } }
public override void calculate() { PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); Utils.QL_REQUIRE(payoff.strike() > 0.0, () => "strike must be positive"); double K = payoff.strike(); double S = process_.x0(); Utils.QL_REQUIRE(S >= 0.0, () => "negative or null underlying given"); Utils.QL_REQUIRE(!triggered(S), () => "barrier touched"); DoubleBarrier.Type barrierType = arguments_.barrierType; Utils.QL_REQUIRE(barrierType == DoubleBarrier.Type.KnockOut || barrierType == DoubleBarrier.Type.KnockIn, () => "only KnockIn and KnockOut options supported"); double L = arguments_.barrier_lo.GetValueOrDefault(); double H = arguments_.barrier_hi.GetValueOrDefault(); double K_up = Math.Min(H, K); double K_down = Math.Max(L, K); double T = residualTime(); double rd = riskFreeRate(); double dd = riskFreeDiscount(); double rf = dividendYield(); double df = dividendDiscount(); double vol = volatility(); double mu = rd - rf - vol * vol / 2.0; double sgn = mu > 0 ? 1.0 : (mu < 0 ? -1.0 : 0.0); //rebate double R_L = arguments_.rebate.GetValueOrDefault(); double R_H = arguments_.rebate.GetValueOrDefault(); //european option EuropeanOption europeanOption = new EuropeanOption(payoff, arguments_.exercise); IPricingEngine analyticEuropeanEngine = new AnalyticEuropeanEngine(process_); europeanOption.setPricingEngine(analyticEuropeanEngine); double european = europeanOption.NPV(); double barrierOut = 0; double rebateIn = 0; for (int n = -series_; n < series_; n++) { double d1 = D(S / H * Math.Pow(L / H, 2.0 * n), vol * vol + mu, vol, T); double d2 = d1 - vol * Math.Sqrt(T); double g1 = D(H / S * Math.Pow(L / H, 2.0 * n - 1.0), vol * vol + mu, vol, T); double g2 = g1 - vol * Math.Sqrt(T); double h1 = D(S / H * Math.Pow(L / H, 2.0 * n - 1.0), vol * vol + mu, vol, T); double h2 = h1 - vol * Math.Sqrt(T); double k1 = D(L / S * Math.Pow(L / H, 2.0 * n - 1.0), vol * vol + mu, vol, T); double k2 = k1 - vol * Math.Sqrt(T); double d1_down = D(S / K_down * Math.Pow(L / H, 2.0 * n), vol * vol + mu, vol, T); double d2_down = d1_down - vol * Math.Sqrt(T); double d1_up = D(S / K_up * Math.Pow(L / H, 2.0 * n), vol * vol + mu, vol, T); double d2_up = d1_up - vol * Math.Sqrt(T); double k1_down = D((H * H) / (K_down * S) * Math.Pow(L / H, 2.0 * n), vol * vol + mu, vol, T); double k2_down = k1_down - vol * Math.Sqrt(T); double k1_up = D((H * H) / (K_up * S) * Math.Pow(L / H, 2.0 * n), vol * vol + mu, vol, T); double k2_up = k1_up - vol * Math.Sqrt(T); if (payoff.optionType() == Option.Type.Call) { barrierOut += Math.Pow(L / H, 2.0 * n * mu / (vol * vol)) * (df * S * Math.Pow(L / H, 2.0 * n) * (f_.value(d1_down) - f_.value(d1)) - dd * K * (f_.value(d2_down) - f_.value(d2)) - df * Math.Pow(L / H, 2.0 * n) * H * H / S * Math.Pow(H / S, 2.0 * mu / (vol * vol)) * (f_.value(k1_down) - f_.value(k1)) + dd * K * Math.Pow(H / S, 2.0 * mu / (vol * vol)) * (f_.value(k2_down) - f_.value(k2))); } else if (payoff.optionType() == Option.Type.Put) { barrierOut += Math.Pow(L / H, 2.0 * n * mu / (vol * vol)) * (dd * K * (f_.value(h2) - f_.value(d2_up)) - df * S * Math.Pow(L / H, 2.0 * n) * (f_.value(h1) - f_.value(d1_up)) - dd * K * Math.Pow(H / S, 2.0 * mu / (vol * vol)) * (f_.value(g2) - f_.value(k2_up)) + df * Math.Pow(L / H, 2.0 * n) * H * H / S * Math.Pow(H / S, 2.0 * mu / (vol * vol)) * (f_.value(g1) - f_.value(k1_up))); } else { Utils.QL_FAIL("option type not recognized"); } double v1 = D(H / S * Math.Pow(H / L, 2.0 * n), -mu, vol, T); double v2 = D(H / S * Math.Pow(H / L, 2.0 * n), mu, vol, T); double v3 = D(S / L * Math.Pow(H / L, 2.0 * n), -mu, vol, T); double v4 = D(S / L * Math.Pow(H / L, 2.0 * n), mu, vol, T); rebateIn += dd * R_H * sgn * (Math.Pow(L / H, 2.0 * n * mu / (vol * vol)) * f_.value(sgn * v1) - Math.Pow(H / S, 2.0 * mu / (vol * vol)) * f_.value(-sgn * v2)) + dd * R_L * sgn * (Math.Pow(L / S, 2.0 * mu / (vol * vol)) * f_.value(-sgn * v3) - Math.Pow(H / L, 2.0 * n * mu / (vol * vol)) * f_.value(sgn * v4)); } //rebate paid at maturity if (barrierType == DoubleBarrier.Type.KnockOut) { results_.value = barrierOut; } else { results_.value = european - barrierOut; } results_.additionalResults["vanilla"] = european; results_.additionalResults["barrierOut"] = barrierOut; results_.additionalResults["barrierIn"] = european - barrierOut; }
public override void calculate() { Utils.QL_REQUIRE(arguments_.averageType == Average.Type.Geometric, () => "not a geometric average option"); Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European Option"); Date exercise = arguments_.exercise.lastDate(); PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "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(); Utils.QL_REQUIRE(spot > 0.0, () => "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 (Exception /*Error*/) { results_.theta = null; } }
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(); Utils.QL_REQUIRE(s0 > 0.0, () => "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; Utils.QL_REQUIRE(payoff != null, () => "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 = FastActivator <T> .Create().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()); Utils.QL_REQUIRE(va2.size() == 3, () => "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()); Utils.QL_REQUIRE(va.size() == 2, () => "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()); }
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"); double strike = underlying * moneyness.strike(); StrikedTypePayoff payoff = new PlainVanillaPayoff(moneyness.optionType(), strike); 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 weight = process_.dividendYield().link.discount(resetDates[i - 1]); double discount = 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 = underlying * qDiscount / discount; double variance = process_.blackVolatility().link.blackForwardVariance(resetDates[i - 1], resetDates[i], strike); BlackCalculator black = new BlackCalculator(payoff, forward, Math.Sqrt(variance), discount); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); results_.value += weight * black.value(); results_.delta += weight * (black.delta(underlying) + moneyness.strike() * discount * black.beta()); results_.gamma += 0.0; results_.theta += process_.dividendYield().link.forwardRate( resetDates[i - 1], resetDates[i], rfdc, Compounding.Continuous, Frequency.NoFrequency).value() * weight * black.value(); double dt = rfdc.yearFraction(resetDates[i - 1], resetDates[i]); results_.rho += weight * black.rho(dt); double t = divdc.yearFraction(process_.dividendYield().link.referenceDate(), resetDates[i - 1]); dt = divdc.yearFraction(resetDates[i - 1], resetDates[i]); results_.dividendRho += weight * (black.dividendRho(dt) - t * black.value()); dt = voldc.yearFraction(resetDates[i - 1], resetDates[i]); results_.vega += weight * black.vega(dt); } }
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); } }
public override void calculate() { // this is an european option pricer Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European option"); // plain vanilla PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-striked payoff given"); double v0 = model_.link.v0(); double spotPrice = model_.link.s0(); Utils.QL_REQUIRE(spotPrice > 0.0, () => "negative or null underlying given"); double strike = payoff.strike(); double term = model_.link.riskFreeRate().link.dayCounter().yearFraction( model_.link.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); double riskFreeDiscount = model_.link.riskFreeRate().link.discount(arguments_.exercise.lastDate()); double dividendDiscount = model_.link.dividendYield().link.discount(arguments_.exercise.lastDate()); //average values TimeGrid timeGrid = model_.link.timeGrid(); int n = timeGrid.size() - 1; double kappaAvg = 0.0, thetaAvg = 0.0, sigmaAvg = 0.0, rhoAvg = 0.0; for (int i = 1; i <= n; ++i) { double t = 0.5 * (timeGrid[i - 1] + timeGrid[i]); kappaAvg += model_.link.kappa(t); thetaAvg += model_.link.theta(t); sigmaAvg += model_.link.sigma(t); rhoAvg += model_.link.rho(t); } kappaAvg /= n; thetaAvg /= n; sigmaAvg /= n; rhoAvg /= n; double c_inf = Math.Min(10.0, Math.Max(0.0001, Math.Sqrt(1.0 - Math.Pow(rhoAvg, 2)) / sigmaAvg)) * (v0 + kappaAvg * thetaAvg * term); double p1 = integration_.calculate(c_inf, new Fj_Helper(model_, term, strike, 1).value) / Const.M_PI; double p2 = integration_.calculate(c_inf, new Fj_Helper(model_, term, strike, 2).value) / Const.M_PI; switch (payoff.optionType()) { case Option.Type.Call: results_.value = spotPrice * dividendDiscount * (p1 + 0.5) - strike * riskFreeDiscount * (p2 + 0.5); break; case Option.Type.Put: results_.value = spotPrice * dividendDiscount * (p1 - 0.5) - strike * riskFreeDiscount * (p2 - 0.5); break; default: Utils.QL_FAIL("unknown option type"); break; } }
public override void calculate() { PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); Utils.QL_REQUIRE(payoff.strike() > 0.0, () => "strike must be positive"); double strike = payoff.strike(); double spot = process_.x0(); Utils.QL_REQUIRE(spot >= 0.0, () => "negative or null underlying given"); Utils.QL_REQUIRE(!triggered(spot), () => "barrier touched"); Barrier.Type barrierType = arguments_.barrierType; switch (payoff.optionType()) { case Option.Type.Call: switch (barrierType) { case Barrier.Type.DownIn: if (strike >= barrier()) { results_.value = C(1, 1) + E(1); } else { results_.value = A(1) - B(1) + D(1, 1) + E(1); } break; case Barrier.Type.UpIn: if (strike >= barrier()) { results_.value = A(1) + E(-1); } else { results_.value = B(1) - C(-1, 1) + D(-1, 1) + E(-1); } break; case Barrier.Type.DownOut: if (strike >= barrier()) { results_.value = A(1) - C(1, 1) + F(1); } else { results_.value = B(1) - D(1, 1) + F(1); } break; case Barrier.Type.UpOut: if (strike >= barrier()) { results_.value = F(-1); } else { results_.value = A(1) - B(1) + C(-1, 1) - D(-1, 1) + F(-1); } break; } break; case Option.Type.Put: switch (barrierType) { case Barrier.Type.DownIn: if (strike >= barrier()) { results_.value = B(-1) - C(1, -1) + D(1, -1) + E(1); } else { results_.value = A(-1) + E(1); } break; case Barrier.Type.UpIn: if (strike >= barrier()) { results_.value = A(-1) - B(-1) + D(-1, -1) + E(-1); } else { results_.value = C(-1, -1) + E(-1); } break; case Barrier.Type.DownOut: if (strike >= barrier()) { results_.value = A(-1) - B(-1) + C(1, -1) - D(1, -1) + F(1); } else { results_.value = F(1); } break; case Barrier.Type.UpOut: if (strike >= barrier()) { results_.value = B(-1) - D(-1, -1) + F(-1); } else { results_.value = A(-1) - C(-1, -1) + F(-1); } break; } break; default: Utils.QL_FAIL("unknown type"); break; } }
public override void calculate() { Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "this engine handles only european options"); PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); double strike = payoff.strike(); Utils.QL_REQUIRE(strike > 0.0, () => "strike must be positive"); double spot = underlying(); Utils.QL_REQUIRE(spot >= 0.0, () => "negative or null underlying given"); Utils.QL_REQUIRE(!triggered(spot), () => "barrier(s) already touched"); DoubleBarrier.Type barrierType = arguments_.barrierType; if (triggered(spot)) { if (barrierType == DoubleBarrier.Type.KnockIn) { results_.value = vanillaEquivalent(); // knocked in } else { results_.value = 0.0; // knocked out } } else { switch (payoff.optionType()) { case Option.Type.Call: switch (barrierType) { case DoubleBarrier.Type.KnockIn: results_.value = callKI(); break; case DoubleBarrier.Type.KnockOut: results_.value = callKO(); break; case DoubleBarrier.Type.KIKO: case DoubleBarrier.Type.KOKI: Utils.QL_FAIL("unsupported double-barrier type: " + barrierType); break; default: Utils.QL_FAIL("unknown double-barrier type: " + barrierType); break; } break; case Option.Type.Put: switch (barrierType) { case DoubleBarrier.Type.KnockIn: results_.value = putKI(); break; case DoubleBarrier.Type.KnockOut: results_.value = putKO(); break; case DoubleBarrier.Type.KIKO: case DoubleBarrier.Type.KOKI: Utils.QL_FAIL("unsupported double-barrier type: " + barrierType); break; default: Utils.QL_FAIL("unknown double-barrier type: " + barrierType); break; } break; default: Utils.QL_FAIL("unknown type"); break; } } }