// function public double value(double z) { z = (z - average_) / sigma_; double result = 0.5 * (1.0 + erf(z * Const.M_SQRT_2)); if (result <= 1e-8) //todo: investigate the threshold level { // Asymptotic expansion for very negative z following (26.2.12) // on page 408 in M. Abramowitz and A. Stegun, // Pocketbook of Mathematical Functions, ISBN 3-87144818-4. double sum = 1.0, zsqr = z * z, i = 1.0, g = 1.0, x, y, a = double.MaxValue, lasta; do { lasta = a; x = (4.0 * i - 3.0) / zsqr; y = x * ((4.0 * i - 1) / zsqr); a = g * (x - y); sum -= a; g *= y; ++i; a = Math.Abs(a); }while (lasta > a && a >= Math.Abs(sum * Const.QL_EPSILON)); result = -gaussian_.value(z) / z * sum; } return(result); }
private double vega(double k) { double d1 = (Math.Log(fwd_ / k) + 0.5 * Math.Pow(atmVol_, 2.0) * T_) / (atmVol_ * Math.Sqrt(T_)); NormalDistribution norm = new NormalDistribution(); return(spot_ * dDiscount_ * Math.Sqrt(T_) * norm.value(d1)); }
//! gaussian-assumption Average Shortfall (averaged shortfallness) public double gaussianAverageShortfall(double target) { double m = this.mean(); double std = this.standardDeviation(); CumulativeNormalDistribution gIntegral = new CumulativeNormalDistribution(m, std); NormalDistribution g = new NormalDistribution(m, std); return((target - m) + std * std * g.value(target) / gIntegral.value(target)); }
protected double smileCorrection(double strike, double forward, double expiry, double deflator) { double previousStrike = strike - eps_ / 2; double nextStrike = strike + eps_ / 2; double derSmileS = (smilesOnExpiry_.volatility(nextStrike) - smilesOnExpiry_.volatility(previousStrike)) / eps_; double derSmileT = (smilesOnPayment_.volatility(nextStrike) - smilesOnPayment_.volatility(previousStrike)) / eps_; double lambdaS = smilesOnExpiry_.volatility(strike); double lambdaT = smilesOnPayment_.volatility(strike); double derLambdaDerK = derLambdaDerLambdaS(expiry) * derSmileS + derLambdaDerLambdaT(expiry) * derSmileT; double lambdaSATM = smilesOnExpiry_.volatility(forward); double lambdaTATM = smilesOnPayment_.volatility(forward); List <double> lambdasOverPeriodU = lambdasOverPeriod(expiry, lambdaS, lambdaT); //drift of Lognormal process (of Libor) "a_U()" nel paper List <double> muU = driftsOverPeriod(expiry, lambdaSATM, lambdaTATM, correlation_); double variance = Math.Max(startTime_, 0.0) * lambdasOverPeriodU[0] * lambdasOverPeriodU[0] + Math.Min(expiry - startTime_, expiry) * lambdasOverPeriodU[1] * lambdasOverPeriodU[1]; double forwardAdjustment = Math.Exp(Math.Max(startTime_, 0.0) * muU[0] + Math.Min(expiry - startTime_, expiry) * muU[1]); double forwardAdjusted = forward * forwardAdjustment; double d1 = (Math.Log(forwardAdjusted / strike) + 0.5 * variance) / Math.Sqrt(variance); double sqrtOfTimeToExpiry = (Math.Max(startTime_, 0.0) * lambdasOverPeriodU[0] + Math.Min(expiry - startTime_, expiry) * lambdasOverPeriodU[1]) * (1.0 / Math.Sqrt(variance)); CumulativeNormalDistribution phi = new CumulativeNormalDistribution(); NormalDistribution psi = new NormalDistribution(); double result = -forwardAdjusted *psi.value(d1) * sqrtOfTimeToExpiry * derLambdaDerK; result *= deflator; Utils.QL_REQUIRE(Math.Abs(result / deflator) <= 1.0 + Math.Pow(eps_, .2), () => "RangeAccrualPricerByBgm::smileCorrection: abs(result/deflator) > 1. Ratio: " + result / deflator + " result: " + result + " deflator: " + deflator); return(result); }
/*! returns the variance of observations below target * See Dembo, Freeman "The Rules Of Risk", Wiley (2001) */ public double gaussianRegret(double target) { double m = this.mean(); double std = this.standardDeviation(); double variance = std * std; CumulativeNormalDistribution gIntegral = new CumulativeNormalDistribution(m, std); NormalDistribution g = new NormalDistribution(m, std); double firstTerm = variance + m * m - 2.0 * target * m + target * target; double alfa = gIntegral.value(target); double secondTerm = m - target; double beta = variance * g.value(target); double result = alfa * firstTerm - beta * secondTerm; return(result / alfa); }
//! gaussian-assumption Expected Shortfall at a given percentile /*! Assuming a gaussian distribution it * returns the expected loss in case that the loss exceeded * a VaR threshold, * * that is the average of observations below the * given percentile \f$ p \f$. * Also know as conditional value-at-risk. * * See Artzner, Delbaen, Eber and Heath, * "Coherent measures of risk", Mathematical Finance 9 (1999) */ public double gaussianExpectedShortfall(double percentile) { Utils.QL_REQUIRE(percentile < 1.0 && percentile >= 0.9, () => "percentile (" + percentile + ") out of range [0.9, 1)"); double m = this.mean(); double std = this.standardDeviation(); InverseCumulativeNormal gInverse = new InverseCumulativeNormal(m, std); double var = gInverse.value(1.0 - percentile); NormalDistribution g = new NormalDistribution(m, std); double result = m - std * std * g.value(var) / (1.0 - percentile); // expectedShortfall must be a loss // this means that it has to be MIN(result, 0.0) // expectedShortfall must also be a positive quantity, so -MIN(*) return(-Math.Min(result, 0.0)); }
public override void calculate() { double sigmaShift_vega = 0.001; double sigmaShift_volga = 0.0001; double spotShift_delta = 0.0001 * spotFX_.link.value(); double sigmaShift_vanna = 0.0001; Utils.QL_REQUIRE(arguments_.barrierType == DoubleBarrier.Type.KnockIn || arguments_.barrierType == DoubleBarrier.Type.KnockOut, () => "Only same type barrier supported"); Handle <Quote> x0Quote = new Handle <Quote>(new SimpleQuote(spotFX_.link.value())); Handle <Quote> atmVolQuote = new Handle <Quote>(new SimpleQuote(atmVol_.link.value())); BlackVolTermStructure blackVolTS = new BlackConstantVol(Settings.Instance.evaluationDate(), new NullCalendar(), atmVolQuote, new Actual365Fixed()); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(x0Quote, foreignTS_, domesticTS_, new Handle <BlackVolTermStructure>(blackVolTS)); IPricingEngine engineBS = getOriginalEngine_(stochProcess, series_); BlackDeltaCalculator blackDeltaCalculatorAtm = new BlackDeltaCalculator( Option.Type.Call, atmVol_.link.deltaType(), x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_)); double atmStrike = blackDeltaCalculatorAtm.atmStrike(atmVol_.link.atmType()); double call25Vol = vol25Call_.link.value(); double put25Vol = vol25Put_.link.value(); BlackDeltaCalculator blackDeltaCalculatorPut25 = new BlackDeltaCalculator( Option.Type.Put, vol25Put_.link.deltaType(), x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), put25Vol * Math.Sqrt(T_)); double put25Strike = blackDeltaCalculatorPut25.strikeFromDelta(-0.25); BlackDeltaCalculator blackDeltaCalculatorCall25 = new BlackDeltaCalculator( Option.Type.Call, vol25Call_.link.deltaType(), x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), call25Vol * Math.Sqrt(T_)); double call25Strike = blackDeltaCalculatorCall25.strikeFromDelta(0.25); //here use vanna volga interpolated smile to price vanilla List <double> strikes = new List <double>(); List <double> vols = new List <double>(); strikes.Add(put25Strike); vols.Add(put25Vol); strikes.Add(atmStrike); vols.Add(atmVol_.link.value()); strikes.Add(call25Strike); vols.Add(call25Vol); VannaVolga vannaVolga = new VannaVolga(x0Quote.link.value(), foreignTS_.link.discount(T_), foreignTS_.link.discount(T_), T_); Interpolation interpolation = vannaVolga.interpolate(strikes, strikes.Count, vols); interpolation.enableExtrapolation(); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; Utils.QL_REQUIRE(payoff != null, () => "invalid payoff"); double strikeVol = interpolation.value(payoff.strike()); // Vanilla option price double vanillaOption = Utils.blackFormula(payoff.optionType(), payoff.strike(), x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), strikeVol * Math.Sqrt(T_), domesticTS_.link.discount(T_)); //already out if ((x0Quote.link.value() > arguments_.barrier_hi || x0Quote.link.value() < arguments_.barrier_lo) && arguments_.barrierType == DoubleBarrier.Type.KnockOut) { results_.value = 0.0; results_.additionalResults["VanillaPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } //already in else if ((x0Quote.link.value() > arguments_.barrier_hi || x0Quote.link.value() < arguments_.barrier_lo) && arguments_.barrierType == DoubleBarrier.Type.KnockIn) { results_.value = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["VanillaPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } else { //set up BS barrier option pricing //only calculate out barrier option price // in barrier price = vanilla - out barrier DoubleBarrierOption doubleBarrierOption = new DoubleBarrierOption( DoubleBarrier.Type.KnockOut, arguments_.barrier_lo.GetValueOrDefault(), arguments_.barrier_hi.GetValueOrDefault(), arguments_.rebate.GetValueOrDefault(), payoff, arguments_.exercise); doubleBarrierOption.setPricingEngine(engineBS); //BS price double priceBS = doubleBarrierOption.NPV(); double priceAtmCallBS = Utils.blackFormula(Option.Type.Call, atmStrike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25CallBS = Utils.blackFormula(Option.Type.Call, call25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25PutBS = Utils.blackFormula(Option.Type.Put, put25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); //market price double priceAtmCallMkt = Utils.blackFormula(Option.Type.Call, atmStrike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25CallMkt = Utils.blackFormula(Option.Type.Call, call25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), call25Vol * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25PutMkt = Utils.blackFormula(Option.Type.Put, put25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), put25Vol * Math.Sqrt(T_), domesticTS_.link.discount(T_)); //Analytical Black Scholes formula NormalDistribution norm = new NormalDistribution(); double d1atm = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / atmStrike) + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_)); double vegaAtm_Analytical = x0Quote.link.value() * norm.value(d1atm) * Math.Sqrt(T_) * foreignTS_.link.discount(T_); double vannaAtm_Analytical = vegaAtm_Analytical / x0Quote.link.value() * (1.0 - d1atm / (atmVolQuote.link.value() * Math.Sqrt(T_))); double volgaAtm_Analytical = vegaAtm_Analytical * d1atm * (d1atm - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value(); double d125call = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / call25Strike) + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_)); double vega25Call_Analytical = x0Quote.link.value() * norm.value(d125call) * Math.Sqrt(T_) * foreignTS_.link.discount(T_); double vanna25Call_Analytical = vega25Call_Analytical / x0Quote.link.value() * (1.0 - d125call / (atmVolQuote.link.value() * Math.Sqrt(T_))); double volga25Call_Analytical = vega25Call_Analytical * d125call * (d125call - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value(); double d125Put = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / put25Strike) + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_)); double vega25Put_Analytical = x0Quote.link.value() * norm.value(d125Put) * Math.Sqrt(T_) * foreignTS_.link.discount(T_); double vanna25Put_Analytical = vega25Put_Analytical / x0Quote.link.value() * (1.0 - d125Put / (atmVolQuote.link.value() * Math.Sqrt(T_))); double volga25Put_Analytical = vega25Put_Analytical * d125Put * (d125Put - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value(); //BS vega ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vega); doubleBarrierOption.recalculate(); double vegaBarBS = (doubleBarrierOption.NPV() - priceBS) / sigmaShift_vega; ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_vega);//setback //BS volga //vegaBar2 //base NPV ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_volga); doubleBarrierOption.recalculate(); double priceBS2 = doubleBarrierOption.NPV(); //shifted npv ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vega); doubleBarrierOption.recalculate(); double vegaBarBS2 = (doubleBarrierOption.NPV() - priceBS2) / sigmaShift_vega; double volgaBarBS = (vegaBarBS2 - vegaBarBS) / sigmaShift_volga; ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_volga - sigmaShift_vega);//setback //BS Delta //base delta ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//shift forth doubleBarrierOption.recalculate(); double priceBS_delta1 = doubleBarrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() - 2 * spotShift_delta);//shift back doubleBarrierOption.recalculate(); double priceBS_delta2 = doubleBarrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//set back double deltaBar1 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta); //shifted vanna ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vanna); //shift sigma //shifted delta ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta); //shift forth doubleBarrierOption.recalculate(); priceBS_delta1 = doubleBarrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() - 2 * spotShift_delta);//shift back doubleBarrierOption.recalculate(); priceBS_delta2 = doubleBarrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//set back double deltaBar2 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta); double vannaBarBS = (deltaBar2 - deltaBar1) / sigmaShift_vanna; ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_vanna);//set back //Matrix Matrix A = new Matrix(3, 3, 0.0); //analytical A[0, 0] = vegaAtm_Analytical; A[0, 1] = vega25Call_Analytical; A[0, 2] = vega25Put_Analytical; A[1, 0] = vannaAtm_Analytical; A[1, 1] = vanna25Call_Analytical; A[1, 2] = vanna25Put_Analytical; A[2, 0] = volgaAtm_Analytical; A[2, 1] = volga25Call_Analytical; A[2, 2] = volga25Put_Analytical; Vector b = new Vector(3, 0.0); b[0] = vegaBarBS; b[1] = vannaBarBS; b[2] = volgaBarBS; Vector q = Matrix.inverse(A) * b; double H = arguments_.barrier_hi.GetValueOrDefault(); double L = arguments_.barrier_lo.GetValueOrDefault(); double theta_tilt_minus = ((domesticTS_.link.zeroRate(T_, Compounding.Continuous).value() - foreignTS_.link.zeroRate(T_, Compounding.Continuous).value()) / atmVol_.link.value() - atmVol_.link.value() / 2.0) * Math.Sqrt(T_); double h = 1.0 / atmVol_.link.value() * Math.Log(H / x0Quote.link.value()) / Math.Sqrt(T_); double l = 1.0 / atmVol_.link.value() * Math.Log(L / x0Quote.link.value()) / Math.Sqrt(T_); CumulativeNormalDistribution cnd = new CumulativeNormalDistribution(); double doubleNoTouch = 0.0; for (int j = -series_; j < series_; j++) { double e_minus = 2 * j * (h - l) - theta_tilt_minus; doubleNoTouch += Math.Exp(-2.0 * j * theta_tilt_minus * (h - l)) * (cnd.value(h + e_minus) - cnd.value(l + e_minus)) - Math.Exp(-2.0 * j * theta_tilt_minus * (h - l) + 2.0 * theta_tilt_minus * h) * (cnd.value(h - 2.0 * h + e_minus) - cnd.value(l - 2.0 * h + e_minus)); } double p_survival = doubleNoTouch; double lambda = p_survival; double adjust = q[0] * (priceAtmCallMkt - priceAtmCallBS) + q[1] * (price25CallMkt - price25CallBS) + q[2] * (price25PutMkt - price25PutBS); double outPrice = priceBS + lambda * adjust; // double inPrice; //adapt Vanilla delta if (adaptVanDelta_ == true) { outPrice += lambda * (bsPriceWithSmile_ - vanillaOption); //capfloored by (0, vanilla) outPrice = Math.Max(0.0, Math.Min(bsPriceWithSmile_, outPrice)); inPrice = bsPriceWithSmile_ - outPrice; } else { //capfloored by (0, vanilla) outPrice = Math.Max(0.0, Math.Min(vanillaOption, outPrice)); inPrice = vanillaOption - outPrice; } if (arguments_.barrierType == DoubleBarrier.Type.KnockOut) { results_.value = outPrice; } else { results_.value = inPrice; } results_.additionalResults["VanillaPrice"] = vanillaOption; results_.additionalResults["BarrierInPrice"] = inPrice; results_.additionalResults["BarrierOutPrice"] = outPrice; results_.additionalResults["lambda"] = lambda; } }
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() { /* 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() { Utils.QL_REQUIRE(arguments_.barrierType == Barrier.Type.UpIn || arguments_.barrierType == Barrier.Type.UpOut || arguments_.barrierType == Barrier.Type.DownIn || arguments_.barrierType == Barrier.Type.DownOut, () => "Invalid barrier type"); double sigmaShift_vega = 0.0001; double sigmaShift_volga = 0.0001; double spotShift_delta = 0.0001 * spotFX_.link.value(); double sigmaShift_vanna = 0.0001; Handle <Quote> x0Quote = new Handle <Quote>(new SimpleQuote(spotFX_.link.value())); //used for shift Handle <Quote> atmVolQuote = new Handle <Quote>(new SimpleQuote(atmVol_.link.value())); //used for shift BlackVolTermStructure blackVolTS = new BlackConstantVol(Settings.Instance.evaluationDate(), new NullCalendar(), atmVolQuote, new Actual365Fixed()); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(x0Quote, foreignTS_, domesticTS_, new Handle <BlackVolTermStructure>(blackVolTS)); IPricingEngine engineBS = new AnalyticBarrierEngine(stochProcess); BlackDeltaCalculator blackDeltaCalculatorAtm = new BlackDeltaCalculator( Option.Type.Call, atmVol_.link.deltaType(), x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_)); double atmStrike = blackDeltaCalculatorAtm.atmStrike(atmVol_.link.atmType()); double call25Vol = vol25Call_.link.value(); double put25Vol = vol25Put_.link.value(); BlackDeltaCalculator blackDeltaCalculatorPut25 = new BlackDeltaCalculator( Option.Type.Put, vol25Put_.link.deltaType(), x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), put25Vol * Math.Sqrt(T_)); double put25Strike = blackDeltaCalculatorPut25.strikeFromDelta(-0.25); BlackDeltaCalculator blackDeltaCalculatorCall25 = new BlackDeltaCalculator( Option.Type.Call, vol25Call_.link.deltaType(), x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), call25Vol * Math.Sqrt(T_)); double call25Strike = blackDeltaCalculatorCall25.strikeFromDelta(0.25); //here use vanna volga interpolated smile to price vanilla List <double> strikes = new List <double>(); List <double> vols = new List <double>(); strikes.Add(put25Strike); vols.Add(put25Vol); strikes.Add(atmStrike); vols.Add(atmVol_.link.value()); strikes.Add(call25Strike); vols.Add(call25Vol); VannaVolga vannaVolga = new VannaVolga(x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), T_); Interpolation interpolation = vannaVolga.interpolate(strikes, strikes.Count, vols); interpolation.enableExtrapolation(); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; double strikeVol = interpolation.value(payoff.strike()); // Vanilla option price double vanillaOption = Utils.blackFormula(payoff.optionType(), payoff.strike(), x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), strikeVol * Math.Sqrt(T_), domesticTS_.link.discount(T_)); //spot > barrier up&out 0 if (x0Quote.link.value() >= arguments_.barrier && arguments_.barrierType == Barrier.Type.UpOut) { results_.value = 0.0; results_.additionalResults["VanillaPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } //spot > barrier up&in vanilla else if (x0Quote.link.value() >= arguments_.barrier && arguments_.barrierType == Barrier.Type.UpIn) { results_.value = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["VanillaPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } //spot < barrier down&out 0 else if (x0Quote.link.value() <= arguments_.barrier && arguments_.barrierType == Barrier.Type.DownOut) { results_.value = 0.0; results_.additionalResults["VanillaPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } //spot < barrier down&in vanilla else if (x0Quote.link.value() <= arguments_.barrier && arguments_.barrierType == Barrier.Type.DownIn) { results_.value = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["VanillaPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } else { //set up BS barrier option pricing //only calculate out barrier option price // in barrier price = vanilla - out barrier Barrier.Type barrierType; if (arguments_.barrierType == Barrier.Type.UpOut) { barrierType = arguments_.barrierType; } else if (arguments_.barrierType == Barrier.Type.UpIn) { barrierType = Barrier.Type.UpOut; } else if (arguments_.barrierType == Barrier.Type.DownOut) { barrierType = arguments_.barrierType; } else { barrierType = Barrier.Type.DownOut; } BarrierOption barrierOption = new BarrierOption(barrierType, arguments_.barrier.GetValueOrDefault(), arguments_.rebate.GetValueOrDefault(), (StrikedTypePayoff)arguments_.payoff, arguments_.exercise); barrierOption.setPricingEngine(engineBS); //BS price with atm vol double priceBS = barrierOption.NPV(); double priceAtmCallBS = Utils.blackFormula(Option.Type.Call, atmStrike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25CallBS = Utils.blackFormula(Option.Type.Call, call25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25PutBS = Utils.blackFormula(Option.Type.Put, put25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); //market price double priceAtmCallMkt = Utils.blackFormula(Option.Type.Call, atmStrike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25CallMkt = Utils.blackFormula(Option.Type.Call, call25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), call25Vol * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25PutMkt = Utils.blackFormula(Option.Type.Put, put25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), put25Vol * Math.Sqrt(T_), domesticTS_.link.discount(T_)); //Analytical Black Scholes formula for vanilla option NormalDistribution norm = new NormalDistribution(); double d1atm = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / atmStrike) + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_)); double vegaAtm_Analytical = x0Quote.link.value() * norm.value(d1atm) * Math.Sqrt(T_) * foreignTS_.link.discount(T_); double vannaAtm_Analytical = vegaAtm_Analytical / x0Quote.link.value() * (1.0 - d1atm / (atmVolQuote.link.value() * Math.Sqrt(T_))); double volgaAtm_Analytical = vegaAtm_Analytical * d1atm * (d1atm - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value(); double d125call = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / call25Strike) + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_)); double vega25Call_Analytical = x0Quote.link.value() * norm.value(d125call) * Math.Sqrt(T_) * foreignTS_.link.discount(T_); double vanna25Call_Analytical = vega25Call_Analytical / x0Quote.link.value() * (1.0 - d125call / (atmVolQuote.link.value() * Math.Sqrt(T_))); double volga25Call_Analytical = vega25Call_Analytical * d125call * (d125call - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value(); double d125Put = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / put25Strike) + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_)); double vega25Put_Analytical = x0Quote.link.value() * norm.value(d125Put) * Math.Sqrt(T_) * foreignTS_.link.discount(T_); double vanna25Put_Analytical = vega25Put_Analytical / x0Quote.link.value() * (1.0 - d125Put / (atmVolQuote.link.value() * Math.Sqrt(T_))); double volga25Put_Analytical = vega25Put_Analytical * d125Put * (d125Put - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value(); //BS vega ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vega); barrierOption.recalculate(); double vegaBarBS = (barrierOption.NPV() - priceBS) / sigmaShift_vega; ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_vega);//setback //BS volga //vegaBar2 //base NPV ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_volga); barrierOption.recalculate(); double priceBS2 = barrierOption.NPV(); //shifted npv ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vega); barrierOption.recalculate(); double vegaBarBS2 = (barrierOption.NPV() - priceBS2) / sigmaShift_vega; double volgaBarBS = (vegaBarBS2 - vegaBarBS) / sigmaShift_volga; ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_volga - sigmaShift_vega);//setback //BS Delta //base delta ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//shift forth barrierOption.recalculate(); double priceBS_delta1 = barrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() - 2 * spotShift_delta);//shift back barrierOption.recalculate(); double priceBS_delta2 = barrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//set back double deltaBar1 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta); //shifted delta ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vanna); //shift sigma ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta); //shift forth barrierOption.recalculate(); priceBS_delta1 = barrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() - 2 * spotShift_delta);//shift back barrierOption.recalculate(); priceBS_delta2 = barrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//set back double deltaBar2 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta); double vannaBarBS = (deltaBar2 - deltaBar1) / sigmaShift_vanna; ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_vanna);//set back //Matrix Matrix A = new Matrix(3, 3, 0.0); //analytical A[0, 0] = vegaAtm_Analytical; A[0, 1] = vega25Call_Analytical; A[0, 2] = vega25Put_Analytical; A[1, 0] = vannaAtm_Analytical; A[1, 1] = vanna25Call_Analytical; A[1, 2] = vanna25Put_Analytical; A[2, 0] = volgaAtm_Analytical; A[2, 1] = volga25Call_Analytical; A[2, 2] = volga25Put_Analytical; Vector b = new Vector(3, 0.0); b[0] = vegaBarBS; b[1] = vannaBarBS; b[2] = volgaBarBS; //Vector q = inverse(A) * b; TODO implements transpose Vector q = Matrix.inverse(A) * b; //touch probability CumulativeNormalDistribution cnd = new CumulativeNormalDistribution(); double mu = domesticTS_.link.zeroRate(T_, Compounding.Continuous).value() - foreignTS_.link.zeroRate(T_, Compounding.Continuous).value() - Math.Pow(atmVol_.link.value(), 2.0) / 2.0; double h2 = (Math.Log(arguments_.barrier.GetValueOrDefault() / x0Quote.link.value()) + mu * T_) / (atmVol_.link.value() * Math.Sqrt(T_)); double h2Prime = (Math.Log(x0Quote.link.value() / arguments_.barrier.GetValueOrDefault()) + mu * T_) / (atmVol_.link.value() * Math.Sqrt(T_)); double probTouch = 0.0; if (arguments_.barrierType == Barrier.Type.UpIn || arguments_.barrierType == Barrier.Type.UpOut) { probTouch = cnd.value(h2Prime) + Math.Pow(arguments_.barrier.GetValueOrDefault() / x0Quote.link.value(), 2.0 * mu / Math.Pow(atmVol_.link.value(), 2.0)) * cnd.value(-h2); } else { probTouch = cnd.value(-h2Prime) + Math.Pow(arguments_.barrier.GetValueOrDefault() / x0Quote.link.value(), 2.0 * mu / Math.Pow(atmVol_.link.value(), 2.0)) * cnd.value(h2); } double p_survival = 1.0 - probTouch; double lambda = p_survival; double adjust = q[0] * (priceAtmCallMkt - priceAtmCallBS) + q[1] * (price25CallMkt - price25CallBS) + q[2] * (price25PutMkt - price25PutBS); double outPrice = priceBS + lambda * adjust; // double inPrice; //adapt Vanilla delta if (adaptVanDelta_ == true) { outPrice += lambda * (bsPriceWithSmile_ - vanillaOption); //capfloored by (0, vanilla) outPrice = Math.Max(0.0, Math.Min(bsPriceWithSmile_, outPrice)); inPrice = bsPriceWithSmile_ - outPrice; } else { //capfloored by (0, vanilla) outPrice = Math.Max(0.0, Math.Min(vanillaOption, outPrice)); inPrice = vanillaOption - outPrice; } if (arguments_.barrierType == Barrier.Type.DownOut || arguments_.barrierType == Barrier.Type.UpOut) { results_.value = outPrice; } else { results_.value = inPrice; } results_.additionalResults["VanillaPrice"] = vanillaOption; results_.additionalResults["BarrierInPrice"] = inPrice; results_.additionalResults["BarrierOutPrice"] = outPrice; results_.additionalResults["lambda"] = lambda; } }