// calculate the value of euro max basket call private double euroTwoAssetMaxBasketCall(double forward1, double forward2, double strike, double riskFreeDiscount, double variance1, double variance2, double rho) { StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Call, strike); double black1 = Utils.blackFormula(payoff.optionType(), payoff.strike(), forward1, Math.Sqrt(variance1)) * riskFreeDiscount; double black2 = Utils.blackFormula(payoff.optionType(), payoff.strike(), forward2, Math.Sqrt(variance2)) * riskFreeDiscount; return(black1 + black2 - euroTwoAssetMinBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho)); }
public override void calculate() { PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "Non-plain payoff given"); Utils.QL_REQUIRE(process_.x0() > 0.0, () => "negative or null underlying"); switch (payoff.optionType()) { case Option.Type.Call: Utils.QL_REQUIRE(payoff.strike() >= 0.0, () => "Strike must be positive or null"); results_.value = A(1); break; case Option.Type.Put: Utils.QL_REQUIRE(payoff.strike() > 0.0, () => "Strike must be positive"); results_.value = A(-1); break; default: Utils.QL_FAIL("Unknown type"); break; } }
public static double bachelierBlackFormula(PlainVanillaPayoff payoff, double forward, double stdDev, double discount = 1.0) { return(bachelierBlackFormula(payoff.optionType(), payoff.strike(), forward, stdDev, discount)); }
public static double blackFormulaCashItmProbability(PlainVanillaPayoff payoff, double forward, double stdDev, double displacement = 0.0) { return(blackFormulaCashItmProbability(payoff.optionType(), payoff.strike(), forward, stdDev, displacement)); }
public static double blackFormulaImpliedStdDevApproximation(PlainVanillaPayoff payoff, double forward, double blackPrice, double discount, double displacement) { return(blackFormulaImpliedStdDevApproximation(payoff.optionType(), payoff.strike(), forward, blackPrice, discount, displacement)); }
public override void calculate() { // cache lookup for precalculated results for (int i = 0; i < cachedArgs2results_.Count; ++i) { if (cachedArgs2results_[i].first.exercise.type() == arguments_.exercise.type() && cachedArgs2results_[i].first.exercise.dates() == arguments_.exercise.dates()) { PlainVanillaPayoff p1 = arguments_.payoff as PlainVanillaPayoff; PlainVanillaPayoff p2 = cachedArgs2results_[i].first.payoff as PlainVanillaPayoff; if (p1 != null && p1.strike() == p2.strike() && p1.optionType() == p2.optionType()) { Utils.QL_REQUIRE(arguments_.cashFlow.empty(), () => "multiple strikes engine does " + "not work with discrete dividends"); results_ = cachedArgs2results_[i].second; return; } } } HestonProcess process = model_.currentLink().process(); FdmHestonSolver solver = new FdmHestonSolver( new Handle <HestonProcess>(process), getSolverDesc(1.5), schemeDesc_, new Handle <FdmQuantoHelper>(), leverageFct_); double v0 = process.v0(); double spot = process.s0().currentLink().value(); results_.value = solver.valueAt(spot, v0); results_.delta = solver.deltaAt(spot, v0); results_.gamma = solver.gammaAt(spot, v0); results_.theta = solver.thetaAt(spot, v0); cachedArgs2results_ = new InitializedList <Pair <DividendVanillaOption.Arguments, DividendVanillaOption.Results> >(strikes_.Count); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; for (int i = 0; i < strikes_.Count; ++i) { cachedArgs2results_[i] = new Pair <DividendVanillaOption.Arguments, OneAssetOption.Results>(new DividendVanillaOption.Arguments(), new OneAssetOption.Results()); cachedArgs2results_[i].first.exercise = arguments_.exercise; cachedArgs2results_[i].first.payoff = new PlainVanillaPayoff(payoff.optionType(), strikes_[i]); double d = payoff.strike() / strikes_[i]; cachedArgs2results_[i].second.value = solver.valueAt(spot * d, v0) / d; cachedArgs2results_[i].second.delta = solver.deltaAt(spot * d, v0); cachedArgs2results_[i].second.gamma = solver.gammaAt(spot * d, v0) * d; cachedArgs2results_[i].second.theta = solver.thetaAt(spot * d, v0) / d; } }
public static double blackFormulaImpliedStdDev(PlainVanillaPayoff payoff, double forward, double blackPrice, double discount, double displacement, double guess, double accuracy, int maxIterations = 100) { return(blackFormulaImpliedStdDev(payoff.optionType(), payoff.strike(), forward, blackPrice, discount, displacement, guess, accuracy, maxIterations)); }
protected override PathPricer <IPath> pathPricer() { PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); GeneralizedBlackScholesProcess process = process_ as GeneralizedBlackScholesProcess; Utils.QL_REQUIRE(process != null, () => "Black-Scholes process required"); return(new EuropeanPathPricer(payoff.optionType(), payoff.strike(), process.riskFreeRate().link.discount(timeGrid().Last()))); }
protected PathPricer <IPath> mc_looback_path_pricer(ContinuousFixedLookbackOption.Arguments args, GeneralizedBlackScholesProcess process, double discount) { PlainVanillaPayoff payoff = args.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); return(new LookbackFixedPathPricer(payoff.optionType(), payoff.strike(), discount)); }
protected override PathPricer <IPath> pathPricer() { PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); TimeGrid grid = timeGrid(); List <double> discounts = new InitializedList <double>(grid.size()); for (int i = 0; i < grid.size(); i++) { discounts[i] = process_.riskFreeRate().currentLink().discount(grid[i]); } // do this with template parameters? if (isBiased_) { return(new BiasedBarrierPathPricer(arguments_.barrierType, arguments_.barrier, arguments_.rebate, payoff.optionType(), payoff.strike(), discounts)); } else { IRNG sequenceGen = new RandomSequenceGenerator <MersenneTwisterUniformRng>(grid.size() - 1, 5); return(new BarrierPathPricer(arguments_.barrierType, arguments_.barrier, arguments_.rebate, payoff.optionType(), payoff.strike(), discounts, process_, sequenceGen)); } }
protected override double blackVolImpl(double t, double strike) { HestonProcess process = hestonModel_.link.process(); double df = process.riskFreeRate().link.discount(t, true); double div = process.dividendYield().link.discount(t, true); double spotPrice = process.s0().link.value(); double fwd = spotPrice * process.dividendYield().link.discount(t, true) / process.riskFreeRate().link.discount(t, true); var payoff = new PlainVanillaPayoff(fwd > strike ? Option.Type.Put : Option.Type.Call, strike); double kappa = hestonModel_.link.kappa(); double theta = hestonModel_.link.theta(); double rho = hestonModel_.link.rho(); double sigma = hestonModel_.link.sigma(); double v0 = hestonModel_.link.v0(); AnalyticHestonEngine.ComplexLogFormula cpxLogFormula = AnalyticHestonEngine.ComplexLogFormula.Gatheral; AnalyticHestonEngine hestonEnginePtr = null; double?npv = null; int evaluations = 0; AnalyticHestonEngine.doCalculation( df, div, spotPrice, strike, t, kappa, theta, sigma, v0, rho, payoff, integration_, cpxLogFormula, hestonEnginePtr, ref npv, ref evaluations); if (npv <= 0.0) { return(Math.Sqrt(theta)); } Brent solver = new Brent(); solver.setMaxEvaluations(10000); double guess = Math.Sqrt(theta); double accuracy = Const.QL_EPSILON; var f = new ImpliedVolHelper(payoff.optionType(), strike, fwd, t, df, npv.Value); return(solver.solve(f, accuracy, guess, 0.01)); }
// conversion to pricing engine protected override PathPricer <IPath> pathPricer() { PlainVanillaPayoff payoff = (PlainVanillaPayoff)(this.arguments_.payoff); Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); EuropeanExercise exercise = (EuropeanExercise)this.arguments_.exercise; Utils.QL_REQUIRE(exercise != null, () => "wrong exercise given"); return((PathPricer <IPath>) new GeometricAPOPathPricer( payoff.optionType(), payoff.strike(), this.process_.riskFreeRate().link.discount( this.timeGrid().Last()), this.arguments_.runningAccumulator.GetValueOrDefault(), this.arguments_.pastFixings.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"); 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 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() { 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() { 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() { /* 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() { // 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() { 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() { 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_.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() { 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; } } }