protected double digitalPriceWithoutSmile(double strike, double initialValue, double expiry, double deflator) { double lambdaS = smilesOnExpiry_.volatility(strike); double lambdaT = smilesOnPayment_.volatility(strike); List <double> lambdaU = lambdasOverPeriod(expiry, lambdaS, lambdaT); double variance = startTime_ * lambdaU[0] * lambdaU[0] + (expiry - startTime_) * lambdaU[1] * lambdaU[1]; double lambdaSATM = smilesOnExpiry_.volatility(initialValue); double lambdaTATM = smilesOnPayment_.volatility(initialValue); //drift of Lognormal process (of Libor) "a_U()" nel paper List <double> muU = driftsOverPeriod(expiry, lambdaSATM, lambdaTATM, correlation_); double adjustment = (startTime_ * muU[0] + (expiry - startTime_) * muU[1]); double d2 = (Math.Log(initialValue / strike) + adjustment - 0.5 * variance) / Math.Sqrt(variance); CumulativeNormalDistribution phi = new CumulativeNormalDistribution(); double result = deflator * phi.value(d2); Utils.QL_REQUIRE(result > 0.0, () => "RangeAccrualPricerByBgm::digitalPriceWithoutSmile: result< 0. Result:" + result); Utils.QL_REQUIRE(result / deflator <= 1.0, () => "RangeAccrualPricerByBgm::digitalPriceWithoutSmile: result/deflator > 1. Ratio: " + result / deflator + " result: " + result + " deflator: " + deflator); return(result); }
/*! Black style formula when forward is normal rather than * log-normal. This is essentially the model of Bachelier. * * \warning Bachelier model needs absolute volatility, not * percentage volatility. Standard deviation is * absoluteVolatility*sqrt(timeToMaturity) */ public static double bachelierBlackFormula(Option.Type optionType, double strike, double forward, double stdDev, double discount = 1.0) { QL_REQUIRE(stdDev >= 0.0, () => "stdDev (" + stdDev + ") must be non-negative"); QL_REQUIRE(discount > 0.0, () => "discount (" + discount + ") must be positive"); double d = (forward - strike) * (int)optionType, h = d / stdDev; if (stdDev.IsEqual(0.0)) { return(discount * Math.Max(d, 0.0)); } CumulativeNormalDistribution phi = new CumulativeNormalDistribution(); double result = discount * (stdDev * phi.derivative(h) + d * phi.value(h)); QL_REQUIRE(result >= 0.0, () => "negative value (" + result + ") for " + stdDev + " stdDev, " + optionType + " option, " + strike + " strike , " + forward + " forward"); return(result); }
public WulinYongDoubleBarrierEngine(GeneralizedBlackScholesProcess process, int series = 5) { process_ = process; series_ = series; f_ = new CumulativeNormalDistribution(); process_.registerWith(update); }
//! 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); }
public LognormalCmsSpreadPricer( CmsCouponPricer cmsPricer1, CmsCouponPricer cmsPricer2, Handle <Quote> correlation, Handle <YieldTermStructure> couponDiscountCurve = null, int integrationPoints = 16, VolatilityType volatilityType = VolatilityType.None, double?shift1 = null, double?shift2 = null) : base(correlation) { correlation_.registerWith(update); cmsPricer1_ = cmsPricer1; cmsPricer2_ = cmsPricer2; couponDiscountCurve_ = couponDiscountCurve; if (couponDiscountCurve_ != null && !couponDiscountCurve_.empty()) { couponDiscountCurve_.registerWith(update); } cmsPricer1_.registerWith(update); cmsPricer2_.registerWith(update); Utils.QL_REQUIRE(integrationPoints >= 4, () => "at least 4 integration points should be used (" + integrationPoints + ")"); integrator_ = new GaussHermiteIntegration(integrationPoints); cnd_ = new CumulativeNormalDistribution(0.0, 1.0); if (volatilityType == VolatilityType.None) { Utils.QL_REQUIRE(shift1 == null && shift2 == null, () => "if volatility type is inherited, no shifts should be specified"); inheritedVolatilityType_ = true; volType_ = cmsPricer1.swaptionVolatility().currentLink().volatilityType(); } else { shift1_ = shift1 == null ? 0.0 : shift1.Value; shift2_ = shift2 == null ? 0.0 : shift2.Value; inheritedVolatilityType_ = false; volType_ = volatilityType; } }
public BlackImpliedStdDevHelper(Option.Type optionType, double strike, double forward, double undiscountedBlackPrice, double displacement = 0.0) { halfOptionType_ = 0.5 * (int)optionType; signedStrike_ = (int)optionType * (strike + displacement); signedForward_ = (int)optionType * (forward + displacement); undiscountedBlackPrice_ = undiscountedBlackPrice; N_ = new CumulativeNormalDistribution(); checkParameters(strike, forward, displacement); Utils.QL_REQUIRE(undiscountedBlackPrice >= 0.0, () => "undiscounted Black price (" + undiscountedBlackPrice + ") must be non-negative"); signedMoneyness_ = (int)optionType * Math.Log((forward + displacement) / (strike + displacement)); }
public double nD2(double strike) // n(d2) { double d2_ = 0.0; double n_d2_ = 0.0; // n(d2) if (stdDev_ >= Const.QL_EPSILON) { if (strike > 0) { d2_ = Math.Log(forward_ / strike) / stdDev_ - 0.5 * stdDev_; CumulativeNormalDistribution f = new CumulativeNormalDistribution(); n_d2_ = f.derivative(d2_); } } return(n_d2_); }
public double value(double x) { CumulativeNormalDistribution phi = new CumulativeNormalDistribution(); double temp = (x - mux_) / sigmax_; double txy = Math.Sqrt(1.0 - rhoxy_ * rhoxy_); Vector lambda = new Vector(size_); int i; for (i = 0; i < size_; i++) { double tau = (i == 0 ? t_[0] - T_ : t_[i] - t_[i - 1]); double c = (i == size_ - 1 ? (1.0 + rate_ * tau) : rate_ * tau); lambda[i] = c * A_[i] * Math.Exp(-Ba_[i] * x); } SolvingFunction function = new SolvingFunction(lambda, Bb_); Brent s1d = new Brent(); s1d.setMaxEvaluations(1000); double yb = s1d.solve(function, 1e-6, 0.00, -100.0, 100.0); double h1 = (yb - muy_) / (sigmay_ * txy) - rhoxy_ * (x - mux_) / (sigmax_ * txy); double value = phi.value(-w_ * h1); for (i = 0; i < size_; i++) { double h2 = h1 + Bb_[i] * sigmay_ *Math.Sqrt(1.0 - rhoxy_ *rhoxy_); double kappa = -Bb_[i] * (muy_ - 0.5 * txy * txy * sigmay_ * sigmay_ * Bb_[i] + rhoxy_ * sigmay_ * (x - mux_) / sigmax_); value -= lambda[i] * Math.Exp(kappa) * phi.value(-w_ * h2); } return(Math.Exp(-0.5 * temp * temp) * value / (sigmax_ * Math.Sqrt(2.0 * QLCore.Const.M_PI))); }
/*! Black 1976 formula * \warning instead of volatility it uses standard deviation, * i.e. volatility*sqrt(timeToMaturity) */ public static double blackFormula(Option.Type optionType, double strike, double forward, double stdDev, double discount = 1.0, double displacement = 0.0) { checkParameters(strike, forward, displacement); Utils.QL_REQUIRE(stdDev >= 0.0, () => "stdDev (" + stdDev + ") must be non-negative"); Utils.QL_REQUIRE(discount > 0.0, () => "discount (" + discount + ") must be positive"); if (stdDev.IsEqual(0.0)) { return(Math.Max((forward - strike) * (int)optionType, 0.0) * discount); } forward = forward + displacement; strike = strike + displacement; // since displacement is non-negative strike==0 iff displacement==0 // so returning forward*discount is OK if (strike.IsEqual(0.0)) { return(optionType == Option.Type.Call ? forward * discount : 0.0); } double d1 = Math.Log(forward / strike) / stdDev + 0.5 * stdDev; double d2 = d1 - stdDev; CumulativeNormalDistribution phi = new CumulativeNormalDistribution(); double nd1 = phi.value((int)optionType * d1); double nd2 = phi.value((int)optionType * d2); double result = discount * (int)optionType * (forward * nd1 - strike * nd2); Utils.QL_REQUIRE(result >= 0.0, () => "negative value (" + result + ") for " + stdDev + " stdDev, " + optionType + " option, " + strike + " strike , " + forward + " forward"); return(result); }
public double cumD2(double strike) // N(d2) or N(-d2) { double d2_ = 0.0; double cum_d2_pos_ = 1.0; // N(d2) double cum_d2_neg_ = 0.0; // N(-d2) CumulativeNormalDistribution f = new CumulativeNormalDistribution(); if (stdDev_ >= Const.QL_EPSILON) { if (strike > 0) { d2_ = Math.Log(forward_ / strike) / stdDev_ - 0.5 * stdDev_; return(f.value(phi_ * d2_)); } } else { if (forward_ < strike) { cum_d2_pos_ = 0.0; cum_d2_neg_ = 1.0; } else if (forward_.IsEqual(strike)) { d2_ = -0.5 * stdDev_; return(f.value(phi_ * d2_)); } } if (phi_ > 0) { // if Call return(cum_d2_pos_); } else { return(cum_d2_neg_); } }
// calculate the value of euro min basket call private double euroTwoAssetMinBasketCall(double forward1, double forward2, double strike, double riskFreeDiscount, double variance1, double variance2, double rho) { double stdDev1 = Math.Sqrt(variance1); double stdDev2 = Math.Sqrt(variance2); double variance = variance1 + variance2 - 2 * rho * stdDev1 * stdDev2; double stdDev = Math.Sqrt(variance); double modRho1 = (rho * stdDev2 - stdDev1) / stdDev; double modRho2 = (rho * stdDev1 - stdDev2) / stdDev; double D1 = (Math.Log(forward1 / forward2) + 0.5 * variance) / stdDev; double alfa, beta, gamma; if (strike.IsNotEqual(0.0)) { BivariateCumulativeNormalDistribution bivCNorm = new BivariateCumulativeNormalDistribution(rho); BivariateCumulativeNormalDistribution bivCNormMod2 = new BivariateCumulativeNormalDistribution(modRho2); BivariateCumulativeNormalDistribution bivCNormMod1 = new BivariateCumulativeNormalDistribution(modRho1); double D1_1 = (Math.Log(forward1 / strike) + 0.5 * variance1) / stdDev1; double D1_2 = (Math.Log(forward2 / strike) + 0.5 * variance2) / stdDev2; alfa = bivCNormMod1.value(D1_1, -D1); beta = bivCNormMod2.value(D1_2, D1 - stdDev); gamma = bivCNorm.value(D1_1 - stdDev1, D1_2 - stdDev2); } else { CumulativeNormalDistribution cum = new CumulativeNormalDistribution(); alfa = cum.value(-D1); beta = cum.value(D1 - stdDev); gamma = 1.0; } return(riskFreeDiscount * (forward1 * alfa + forward2 * beta - strike * gamma)); }
/*! Black 1976 probability of being in the money (in the bond martingale measure), i.e. N(d2). * It is a risk-neutral probability, not the real world one. * \warning instead of volatility it uses standard deviation, i.e. volatility*sqrt(timeToMaturity) */ public static double blackFormulaCashItmProbability(Option.Type optionType, double strike, double forward, double stdDev, double displacement = 0.0) { checkParameters(strike, forward, displacement); if (stdDev.IsEqual(0.0)) { return(forward * (int)optionType > strike * (int)optionType ? 1.0 : 0.0); } forward = forward + displacement; strike = strike + displacement; if (strike.IsEqual(0.0)) { return(optionType == Option.Type.Call ? 1.0 : 0.0); } double d2 = Math.Log(forward / strike) / stdDev - 0.5 * stdDev; CumulativeNormalDistribution phi = new CumulativeNormalDistribution(); return(phi.value((int)optionType * d2)); }
//Hagan, 3.5b, 3.5c public override double optionletPrice(Option.Type optionType, double effectiveStrike) { double variance = swaptionVolatility().link.blackVariance(fixingDate_, swapTenor_, swapRateValue_); double firstDerivativeOfGAtForwardValue = gFunction_.firstDerivative(swapRateValue_); double price = 0; double CK = vanillaOptionPricer_.value(effectiveStrike, optionType, annuity_); price += (discount_ / annuity_) * CK; double sqrtSigma2T = Math.Sqrt(variance); double lnRoverK = Math.Log(swapRateValue_ / effectiveStrike); double d32 = (lnRoverK + 1.5 * variance) / sqrtSigma2T; double d12 = (lnRoverK + .5 * variance) / sqrtSigma2T; double dminus12 = (lnRoverK - .5 * variance) / sqrtSigma2T; CumulativeNormalDistribution cumulativeOfNormal = new CumulativeNormalDistribution(); double N32 = cumulativeOfNormal.value(((int)optionType) * d32); double N12 = cumulativeOfNormal.value(((int)optionType) * d12); double Nminus12 = cumulativeOfNormal.value(((int)optionType) * dminus12); price += ((int)optionType) * firstDerivativeOfGAtForwardValue * annuity_ * swapRateValue_ * (swapRateValue_ * Math.Exp(variance) * N32 - (swapRateValue_ + effectiveStrike) * N12 + effectiveStrike * Nminus12); price *= coupon_.accrualPeriod(); return(price); }
public AmericanPayoffAtHit(double spot, double discount, double dividendDiscount, double variance, StrikedTypePayoff payoff) { spot_ = spot; discount_ = discount; dividendDiscount_ = dividendDiscount; variance_ = variance; Utils.QL_REQUIRE(spot_ > 0.0, () => "positive spot value required"); Utils.QL_REQUIRE(discount_ > 0.0, () => "positive discount required"); Utils.QL_REQUIRE(dividendDiscount_ > 0.0, () => "positive dividend discount required"); Utils.QL_REQUIRE(variance_ >= 0.0, () => "negative variance not allowed"); stdDev_ = Math.Sqrt(variance_); Option.Type type = payoff.optionType(); strike_ = payoff.strike(); log_H_S_ = Math.Log(strike_ / spot_); double n_d1; double n_d2; double cum_d1_; double cum_d2_; if (variance_ >= Const.QL_EPSILON) { if (discount_.IsEqual(0.0) && dividendDiscount_.IsEqual(0.0)) { mu_ = -0.5; lambda_ = 0.5; } else if (discount_.IsEqual(0.0)) { Utils.QL_FAIL("null discount not handled yet"); } else { mu_ = Math.Log(dividendDiscount_ / discount_) / variance_ - 0.5; lambda_ = Math.Sqrt(mu_ * mu_ - 2.0 * Math.Log(discount_) / variance_); } D1_ = log_H_S_ / stdDev_ + lambda_ * stdDev_; D2_ = D1_ - 2.0 * lambda_ * stdDev_; CumulativeNormalDistribution f = new CumulativeNormalDistribution(); cum_d1_ = f.value(D1_); cum_d2_ = f.value(D2_); n_d1 = f.derivative(D1_); n_d2 = f.derivative(D2_); } else { // not tested yet mu_ = Math.Log(dividendDiscount_ / discount_) / variance_ - 0.5; lambda_ = Math.Sqrt(mu_ * mu_ - 2.0 * Math.Log(discount_) / variance_); if (log_H_S_ > 0) { cum_d1_ = 1.0; cum_d2_ = 1.0; } else { cum_d1_ = 0.0; cum_d2_ = 0.0; } n_d1 = 0.0; n_d2 = 0.0; } switch (type) { // up-and-in cash-(at-hit)-or-nothing option // a.k.a. american call with cash-or-nothing payoff case Option.Type.Call: if (strike_ > spot_) { alpha_ = 1.0 - cum_d1_; // N(-d1) DalphaDd1_ = -n_d1; // -n( d1) beta_ = 1.0 - cum_d2_; // N(-d2) DbetaDd2_ = -n_d2; // -n( d2) } else { alpha_ = 0.5; DalphaDd1_ = 0.0; beta_ = 0.5; DbetaDd2_ = 0.0; } break; // down-and-in cash-(at-hit)-or-nothing option // a.k.a. american put with cash-or-nothing payoff case Option.Type.Put: if (strike_ < spot_) { alpha_ = cum_d1_; // N(d1) DalphaDd1_ = n_d1; // n(d1) beta_ = cum_d2_; // N(d2) DbetaDd2_ = n_d2; // n(d2) } else { alpha_ = 0.5; DalphaDd1_ = 0.0; beta_ = 0.5; DbetaDd2_ = 0.0; } break; default: Utils.QL_FAIL("invalid option type"); break; } muPlusLambda_ = mu_ + lambda_; muMinusLambda_ = mu_ - lambda_; inTheMoney_ = (type == Option.Type.Call && strike_ < spot_) || (type == Option.Type.Put && strike_ > spot_); if (inTheMoney_) { forward_ = 1.0; X_ = 1.0; } else { forward_ = Math.Pow(strike_ / spot_, muPlusLambda_); X_ = Math.Pow(strike_ / spot_, muMinusLambda_); } // Binary Cash-Or-Nothing payoff? CashOrNothingPayoff coo = payoff as CashOrNothingPayoff; if (coo != null) { K_ = coo.cashPayoff(); } // Binary Asset-Or-Nothing payoff? AssetOrNothingPayoff aoo = payoff as AssetOrNothingPayoff; if (aoo != null) { if (inTheMoney_) { K_ = spot_; } else { K_ = aoo.strike(); } } }
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()); }
//! gaussian-assumption Shortfall (observations below target) public double gaussianShortfall(double target) { CumulativeNormalDistribution gIntegral = new CumulativeNormalDistribution(this.mean(), this.standardDeviation()); return(gIntegral.value(target)); }
public BlackCalculator(StrikedTypePayoff payoff, double forward, double stdDev, double discount) { strike_ = payoff.strike(); forward_ = forward; stdDev_ = stdDev; discount_ = discount; variance_ = stdDev * stdDev; Utils.QL_REQUIRE(forward > 0.0, () => "positive forward value required: " + forward + " not allowed"); Utils.QL_REQUIRE(stdDev >= 0.0, () => "non-negative standard deviation required: " + stdDev + " not allowed"); Utils.QL_REQUIRE(discount > 0.0, () => "positive discount required: " + discount + " not allowed"); if (stdDev_ >= Const.QL_EPSILON) { if (strike_.IsEqual(0.0)) { n_d1_ = 0.0; n_d2_ = 0.0; cum_d1_ = 1.0; cum_d2_ = 1.0; } else { D1_ = Math.Log(forward / strike_) / stdDev_ + 0.5 * stdDev_; D2_ = D1_ - stdDev_; CumulativeNormalDistribution f = new CumulativeNormalDistribution(); cum_d1_ = f.value(D1_); cum_d2_ = f.value(D2_); n_d1_ = f.derivative(D1_); n_d2_ = f.derivative(D2_); } } else { if (forward > strike_) { cum_d1_ = 1.0; cum_d2_ = 1.0; } else { cum_d1_ = 0.0; cum_d2_ = 0.0; } n_d1_ = 0.0; n_d2_ = 0.0; } X_ = strike_; DXDstrike_ = 1.0; // the following one will probably disappear as soon as // super-share will be properly handled DXDs_ = 0.0; // this part is always executed. // in case of plain-vanilla payoffs, it is also the only part // which is executed. switch (payoff.optionType()) { case Option.Type.Call: alpha_ = cum_d1_; // N(d1) DalphaDd1_ = n_d1_; // n(d1) beta_ = -cum_d2_; // -N(d2) DbetaDd2_ = -n_d2_; // -n(d2) break; case Option.Type.Put: alpha_ = -1.0 + cum_d1_; // -N(-d1) DalphaDd1_ = n_d1_; // n( d1) beta_ = 1.0 - cum_d2_; // N(-d2) DbetaDd2_ = -n_d2_; // -n( d2) break; default: Utils.QL_FAIL("invalid option type"); break; } // now dispatch on type. Calculator calc = new Calculator(this); payoff.accept(calc); }
public double payoffAtExpiry(double spot, double variance, double discount) { double dividendDiscount = process_.dividendYield().link.discount(exercise_.lastDate()); Utils.QL_REQUIRE(spot > 0.0, () => "positive spot value required"); Utils.QL_REQUIRE(discount > 0.0, () => "positive discount required"); Utils.QL_REQUIRE(dividendDiscount > 0.0, () => "positive dividend discount required"); Utils.QL_REQUIRE(variance >= 0.0, () => "negative variance not allowed"); Option.Type type = payoff_.optionType(); double strike = payoff_.strike(); double? barrier = arguments_.barrier; Utils.QL_REQUIRE(barrier > 0.0, () => "positive barrier value required"); Barrier.Type barrierType = arguments_.barrierType; double stdDev = Math.Sqrt(variance); double mu = Math.Log(dividendDiscount / discount) / variance - 0.5; double K = 0; // binary cash-or-nothing payoff? CashOrNothingPayoff coo = payoff_ as CashOrNothingPayoff; if (coo != null) { K = coo.cashPayoff(); } // binary asset-or-nothing payoff? AssetOrNothingPayoff aoo = payoff_ as AssetOrNothingPayoff; if (aoo != null) { mu += 1.0; K = spot * dividendDiscount / discount; // forward } double log_S_X = Math.Log(spot / strike); double log_S_H = Math.Log(spot / barrier.GetValueOrDefault()); double log_H_S = Math.Log(barrier.GetValueOrDefault() / spot); double log_H2_SX = Math.Log(barrier.GetValueOrDefault() * barrier.GetValueOrDefault() / (spot * strike)); double H_S_2mu = Math.Pow(barrier.GetValueOrDefault() / spot, 2 * mu); double eta = (barrierType == Barrier.Type.DownIn || barrierType == Barrier.Type.DownOut ? 1.0 : -1.0); double phi = (type == Option.Type.Call ? 1.0 : -1.0); double x1, x2, y1, y2; double cum_x1, cum_x2, cum_y1, cum_y2; if (variance >= Const.QL_EPSILON) { // we calculate using mu*stddev instead of (mu+1)*stddev // because cash-or-nothing don't need it. asset-or-nothing // mu is really mu+1 x1 = phi * (log_S_X / stdDev + mu * stdDev); x2 = phi * (log_S_H / stdDev + mu * stdDev); y1 = eta * (log_H2_SX / stdDev + mu * stdDev); y2 = eta * (log_H_S / stdDev + mu * stdDev); CumulativeNormalDistribution f = new CumulativeNormalDistribution(); cum_x1 = f.value(x1); cum_x2 = f.value(x2); cum_y1 = f.value(y1); cum_y2 = f.value(y2); } else { if (log_S_X > 0) { cum_x1 = 1.0; } else { cum_x1 = 0.0; } if (log_S_H > 0) { cum_x2 = 1.0; } else { cum_x2 = 0.0; } if (log_H2_SX > 0) { cum_y1 = 1.0; } else { cum_y1 = 0.0; } if (log_H_S > 0) { cum_y2 = 1.0; } else { cum_y2 = 0.0; } } double alpha = 0; switch (barrierType) { case Barrier.Type.DownIn: if (type == Option.Type.Call) { // down-in and call if (strike >= barrier) { // B3 (eta=1, phi=1) alpha = H_S_2mu * cum_y1; } else { // B1-B2+B4 (eta=1, phi=1) alpha = cum_x1 - cum_x2 + H_S_2mu * cum_y2; } } else { // down-in and put if (strike >= barrier) { // B2-B3+B4 (eta=1, phi=-1) alpha = cum_x2 + H_S_2mu * (-cum_y1 + cum_y2); } else { // B1 (eta=1, phi=-1) alpha = cum_x1; } } break; case Barrier.Type.UpIn: if (type == Option.Type.Call) { // up-in and call if (strike >= barrier) { // B1 (eta=-1, phi=1) alpha = cum_x1; } else { // B2-B3+B4 (eta=-1, phi=1) alpha = cum_x2 + H_S_2mu * (-cum_y1 + cum_y2); } } else { // up-in and put if (strike >= barrier) { // B1-B2+B4 (eta=-1, phi=-1) alpha = cum_x1 - cum_x2 + H_S_2mu * cum_y2; } else { // B3 (eta=-1, phi=-1) alpha = H_S_2mu * cum_y1; } } break; case Barrier.Type.DownOut: if (type == Option.Type.Call) { // down-out and call if (strike >= barrier) { // B1-B3 (eta=1, phi=1) alpha = cum_x1 - H_S_2mu * cum_y1; } else { // B2-B4 (eta=1, phi=1) alpha = cum_x2 - H_S_2mu * cum_y2; } } else { // down-out and put if (strike >= barrier) { // B1-B2+B3-B4 (eta=1, phi=-1) alpha = cum_x1 - cum_x2 + H_S_2mu * (cum_y1 - cum_y2); } else { // always 0 alpha = 0; } } break; case Barrier.Type.UpOut: if (type == Option.Type.Call) { // up-out and call if (strike >= barrier) { // always 0 alpha = 0; } else { // B1-B2+B3-B4 (eta=-1, phi=1) alpha = cum_x1 - cum_x2 + H_S_2mu * (cum_y1 - cum_y2); } } else { // up-out and put if (strike >= barrier) { // B2-B4 (eta=-1, phi=-1) alpha = cum_x2 - H_S_2mu * cum_y2; } else { // B1-B3 (eta=-1, phi=-1) alpha = cum_x1 - H_S_2mu * cum_y1; } } break; default: Utils.QL_FAIL("invalid barrier type"); break; } return(discount * K * alpha); }
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 Vector evolve(double t0, Vector x0, double dt, Vector dw) { Vector retVal = new Vector(2); double vol, vol2, mu, nu, dy; double sdt = Math.Sqrt(dt); double sqrhov = Math.Sqrt(1.0 - rho_ * rho_); switch (discretization_) { // For the definition of PartialTruncation, FullTruncation // and Reflection see Lord, R., R. Koekkoek and D. van Dijk (2006), // "A Comparison of biased simulation schemes for // stochastic volatility models", // Working Paper, Tinbergen Institute case Discretization.PartialTruncation: vol = (x0[1] > 0.0) ? Math.Sqrt(x0[1]) : 0.0; vol2 = sigma_ * vol; mu = riskFreeRate_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value() - dividendYield_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value() - 0.5 * vol * vol; nu = kappa_ * (theta_ - x0[1]); retVal[0] = x0[0] * Math.Exp(mu * dt + vol * dw[0] * sdt); retVal[1] = x0[1] + nu * dt + vol2 * sdt * (rho_ * dw[0] + sqrhov * dw[1]); break; case Discretization.FullTruncation: vol = (x0[1] > 0.0) ? Math.Sqrt(x0[1]) : 0.0; vol2 = sigma_ * vol; mu = riskFreeRate_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value() - dividendYield_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value() - 0.5 * vol * vol; nu = kappa_ * (theta_ - vol * vol); retVal[0] = x0[0] * Math.Exp(mu * dt + vol * dw[0] * sdt); retVal[1] = x0[1] + nu * dt + vol2 * sdt * (rho_ * dw[0] + sqrhov * dw[1]); break; case Discretization.Reflection: vol = Math.Sqrt(Math.Abs(x0[1])); vol2 = sigma_ * vol; mu = riskFreeRate_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value() - dividendYield_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value() - 0.5 * vol * vol; nu = kappa_ * (theta_ - vol * vol); retVal[0] = x0[0] * Math.Exp(mu * dt + vol * dw[0] * sdt); retVal[1] = vol * vol + nu * dt + vol2 * sdt * (rho_ * dw[0] + sqrhov * dw[1]); break; case Discretization.NonCentralChiSquareVariance: // use Alan Lewis trick to decorrelate the equity and the variance // process by using y(t)=x(t)-\frac{rho}{sigma}\nu(t) // and Ito's Lemma. Then use exact sampling for the variance // process. For further details please read the Wilmott thread // "QuantLib code is very high quality" vol = (x0[1] > 0.0) ? Math.Sqrt(x0[1]) : 0.0; mu = riskFreeRate_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value() - dividendYield_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value() - 0.5 * vol * vol; retVal[1] = varianceDistribution(x0[1], dw[1], dt); dy = (mu - rho_ / sigma_ * kappa_ * (theta_ - vol * vol)) * dt + vol * sqrhov * dw[0] * sdt; retVal[0] = x0[0] * Math.Exp(dy + rho_ / sigma_ * (retVal[1] - x0[1])); break; case Discretization.QuadraticExponential: case Discretization.QuadraticExponentialMartingale: { // for details of the quadratic exponential discretization scheme // see Leif Andersen, // Efficient Simulation of the Heston Stochastic Volatility Model double ex = Math.Exp(-kappa_ * dt); double m = theta_ + (x0[1] - theta_) * ex; double s2 = x0[1] * sigma_ * sigma_ * ex / kappa_ * (1 - ex) + theta_ * sigma_ * sigma_ / (2 * kappa_) * (1 - ex) * (1 - ex); double psi = s2 / (m * m); double g1 = 0.5; double g2 = 0.5; double k0 = -rho_ * kappa_ * theta_ * dt / sigma_; double k1 = g1 * dt * (kappa_ * rho_ / sigma_ - 0.5) - rho_ / sigma_; double k2 = g2 * dt * (kappa_ * rho_ / sigma_ - 0.5) + rho_ / sigma_; double k3 = g1 * dt * (1 - rho_ * rho_); double k4 = g2 * dt * (1 - rho_ * rho_); double A = k2 + 0.5 * k4; if (psi < 1.5) { double b2 = 2 / psi - 1 + Math.Sqrt(2 / psi * (2 / psi - 1)); double b = Math.Sqrt(b2); double a = m / (1 + b2); if (discretization_ == Discretization.QuadraticExponentialMartingale) { // martingale correction Utils.QL_REQUIRE(A < 1 / (2 * a), () => "illegal value"); k0 = -A * b2 * a / (1 - 2 * A * a) + 0.5 * Math.Log(1 - 2 * A * a) - (k1 + 0.5 * k3) * x0[1]; } retVal[1] = a * (b + dw[1]) * (b + dw[1]); } else { double p = (psi - 1) / (psi + 1); double beta = (1 - p) / m; double u = new CumulativeNormalDistribution().value(dw[1]); if (discretization_ == Discretization.QuadraticExponentialMartingale) { // martingale correction Utils.QL_REQUIRE(A < beta, () => "illegal value"); k0 = -Math.Log(p + beta * (1 - p) / (beta - A)) - (k1 + 0.5 * k3) * x0[1]; } retVal[1] = ((u <= p) ? 0.0 : Math.Log((1 - p) / (1 - u)) / beta); } mu = riskFreeRate_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value() - dividendYield_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value(); retVal[0] = x0[0] * Math.Exp(mu * dt + k0 + k1 * x0[1] + k2 * retVal[1] + Math.Sqrt(k3 * x0[1] + k4 * retVal[1]) * dw[0]); } break; case Discretization.BroadieKayaExactSchemeLobatto: case Discretization.BroadieKayaExactSchemeLaguerre: case Discretization.BroadieKayaExactSchemeTrapezoidal: { double nu_0 = x0[1]; double nu_t = varianceDistribution(nu_0, dw[1], dt); double x = Math.Min(1.0 - Const.QL_EPSILON, Math.Max(0.0, new CumulativeNormalDistribution().value(dw[2]))); cdf_nu_ds_minus_x f = new cdf_nu_ds_minus_x(x, this, nu_0, nu_t, dt, discretization_); double vds = new Brent().solve(f, 1e-5, theta_ * dt, 0.1 * theta_ * dt); double vdw = (nu_t - nu_0 - kappa_ * theta_ * dt + kappa_ * vds) / sigma_; mu = (riskFreeRate_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value() - dividendYield_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value()) * dt - 0.5 * vds + rho_ * vdw; double sig = Math.Sqrt((1 - rho_ * rho_) * vds); double s = x0[0] * Math.Exp(mu + sig * dw[0]); retVal[0] = s; retVal[1] = nu_t; } break; default: Utils.QL_FAIL("unknown discretization schema"); break; } return(retVal); }
// function public double value(double a, double b) { CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution(); double CumNormDistA = cumNormalDist.value(a); double CumNormDistB = cumNormalDist.value(b); double MaxCumNormDistAB = Math.Max(CumNormDistA, CumNormDistB); double MinCumNormDistAB = Math.Min(CumNormDistA, CumNormDistB); if (1.0 - MaxCumNormDistAB < 1e-15) { return(MinCumNormDistAB); } if (MinCumNormDistAB < 1e-15) { return(MinCumNormDistAB); } double a1 = a / Math.Sqrt(2.0 * (1.0 - rho2_)); double b1 = b / Math.Sqrt(2.0 * (1.0 - rho2_)); double result = -1.0; if (a <= 0.0 && b <= 0 && rho_ <= 0) { double sum = 0.0; for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { sum += x_[i] * x_[j] * Math.Exp(a1 * (2.0 * y_[i] - a1) + b1 * (2.0 * y_[j] - b1) + 2.0 * rho_ * (y_[i] - a1) * (y_[j] - b1)); } } result = Math.Sqrt(1.0 - rho2_) / Const.M_PI * sum; } else if (a <= 0 && b >= 0 && rho_ >= 0) { BivariateCumulativeNormalDistributionDr78 bivCumNormalDist = new BivariateCumulativeNormalDistributionDr78(-rho_); result = CumNormDistA - bivCumNormalDist.value(a, -b); } else if (a >= 0.0 && b <= 0.0 && rho_ >= 0.0) { BivariateCumulativeNormalDistributionDr78 bivCumNormalDist = new BivariateCumulativeNormalDistributionDr78(-rho_); result = CumNormDistB - bivCumNormalDist.value(-a, b); } else if (a >= 0.0 && b >= 0.0 && rho_ <= 0.0) { result = CumNormDistA + CumNormDistB - 1.0 + (this.value(-a, -b)); } else if (a * b * rho_ > 0.0) { double rho1 = (rho_ * a - b) * (a > 0.0 ? 1.0 : -1.0) / Math.Sqrt(a * a - 2.0 * rho_ * a * b + b * b); BivariateCumulativeNormalDistributionDr78 bivCumNormalDist = new BivariateCumulativeNormalDistributionDr78(rho1); double rho2 = (rho_ * b - a) * (b > 0.0 ? 1.0 : -1.0) / Math.Sqrt(a * a - 2.0 * rho_ * a * b + b * b); BivariateCumulativeNormalDistributionDr78 CBND2 = new BivariateCumulativeNormalDistributionDr78(rho2); double delta = (1.0 - (a > 0.0 ? 1.0 : -1.0) * (b > 0.0 ? 1.0 : -1.0)) / 4.0; result = bivCumNormalDist.value(a, 0.0) + CBND2.value(b, 0.0) - delta; } else { Utils.QL_FAIL("case not handled"); } return(result); }
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_.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" }
// critical commodity price public static double criticalPrice(StrikedTypePayoff payoff, double riskFreeDiscount, double dividendDiscount, double variance, double tolerance) { // Calculation of seed value, Si double n = 2.0 * Math.Log(dividendDiscount / riskFreeDiscount) / (variance); double m = -2.0 * Math.Log(riskFreeDiscount) / (variance); double bT = Math.Log(dividendDiscount / riskFreeDiscount); double qu, Su, h, Si = 0; switch (payoff.optionType()) { case Option.Type.Call: qu = (-(n - 1.0) + Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * m)) / 2.0; Su = payoff.strike() / (1.0 - 1.0 / qu); h = -(bT + 2.0 * Math.Sqrt(variance)) * payoff.strike() / (Su - payoff.strike()); Si = payoff.strike() + (Su - payoff.strike()) * (1.0 - Math.Exp(h)); break; case Option.Type.Put: qu = (-(n - 1.0) - Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * m)) / 2.0; Su = payoff.strike() / (1.0 - 1.0 / qu); h = (bT - 2.0 * Math.Sqrt(variance)) * payoff.strike() / (payoff.strike() - Su); Si = Su + (payoff.strike() - Su) * Math.Exp(h); break; default: Utils.QL_FAIL("unknown option type"); break; } // Newton Raphson algorithm for finding critical price Si double Q, LHS, RHS, bi; double forwardSi = Si * dividendDiscount / riskFreeDiscount; double d1 = (Math.Log(forwardSi / payoff.strike()) + 0.5 * variance) / Math.Sqrt(variance); CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution(); double K = (!Utils.close(riskFreeDiscount, 1.0, 1000)) ? -2.0 * Math.Log(riskFreeDiscount) / (variance * (1.0 - riskFreeDiscount)) : 2.0 / variance; double temp = Utils.blackFormula(payoff.optionType(), payoff.strike(), forwardSi, Math.Sqrt(variance)) * riskFreeDiscount; switch (payoff.optionType()) { case Option.Type.Call: Q = (-(n - 1.0) + Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4 * K)) / 2; LHS = Si - payoff.strike(); RHS = temp + (1 - dividendDiscount * cumNormalDist.value(d1)) * Si / Q; bi = dividendDiscount * cumNormalDist.value(d1) * (1 - 1 / Q) + (1 - dividendDiscount * cumNormalDist.derivative(d1) / Math.Sqrt(variance)) / Q; while (Math.Abs(LHS - RHS) / payoff.strike() > tolerance) { Si = (payoff.strike() + RHS - bi * Si) / (1 - bi); forwardSi = Si * dividendDiscount / riskFreeDiscount; d1 = (Math.Log(forwardSi / payoff.strike()) + 0.5 * variance) / Math.Sqrt(variance); LHS = Si - payoff.strike(); double temp2 = Utils.blackFormula(payoff.optionType(), payoff.strike(), forwardSi, Math.Sqrt(variance)) * riskFreeDiscount; RHS = temp2 + (1 - dividendDiscount * cumNormalDist.value(d1)) * Si / Q; bi = dividendDiscount * cumNormalDist.value(d1) * (1 - 1 / Q) + (1 - dividendDiscount * cumNormalDist.derivative(d1) / Math.Sqrt(variance)) / Q; } break; case Option.Type.Put: Q = (-(n - 1.0) - Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4 * K)) / 2; LHS = payoff.strike() - Si; RHS = temp - (1 - dividendDiscount * cumNormalDist.value(-d1)) * Si / Q; bi = -dividendDiscount *cumNormalDist.value(-d1) * (1 - 1 / Q) - (1 + dividendDiscount * cumNormalDist.derivative(-d1) / Math.Sqrt(variance)) / Q; while (Math.Abs(LHS - RHS) / payoff.strike() > tolerance) { Si = (payoff.strike() - RHS + bi * Si) / (1 + bi); forwardSi = Si * dividendDiscount / riskFreeDiscount; d1 = (Math.Log(forwardSi / payoff.strike()) + 0.5 * variance) / Math.Sqrt(variance); LHS = payoff.strike() - Si; double temp2 = Utils.blackFormula(payoff.optionType(), payoff.strike(), forwardSi, Math.Sqrt(variance)) * riskFreeDiscount; RHS = temp2 - (1 - dividendDiscount * cumNormalDist.value(-d1)) * Si / Q; bi = -dividendDiscount *cumNormalDist.value(-d1) * (1 - 1 / Q) - (1 + dividendDiscount * cumNormalDist.derivative(-d1) / Math.Sqrt(variance)) / Q; } break; default: Utils.QL_FAIL("unknown option type"); break; } return(Si); }
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(); double tolerance = 1e-6; double Sk = criticalPrice(payoff, riskFreeDiscount, dividendDiscount, variance, tolerance); double forwardSk = Sk * dividendDiscount / riskFreeDiscount; double d1 = (Math.Log(forwardSk / payoff.strike()) + 0.5 * variance) / Math.Sqrt(variance); double n = 2.0 * Math.Log(dividendDiscount / riskFreeDiscount) / variance; double K = (!Utils.close(riskFreeDiscount, 1.0, 1000)) ? -2.0 * Math.Log(riskFreeDiscount) / (variance * (1.0 - riskFreeDiscount)) : 2.0 / variance; double Q, a; switch (payoff.optionType()) { case Option.Type.Call: Q = (-(n - 1.0) + Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * K)) / 2.0; a = (Sk / Q) * (1.0 - dividendDiscount * cumNormalDist.value(d1)); if (spot < Sk) { results_.value = black.value() + a * Math.Pow((spot / Sk), Q); } else { results_.value = spot - payoff.strike(); } break; case Option.Type.Put: Q = (-(n - 1.0) - Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * K)) / 2.0; a = -(Sk / Q) * (1.0 - dividendDiscount * cumNormalDist.value(-d1)); if (spot > Sk) { results_.value = black.value() + a * Math.Pow((spot / Sk), Q); } else { results_.value = payoff.strike() - spot; } break; default: Utils.QL_FAIL("unknown option type"); break; } } // end of "early exercise can be optimal" }
public AmericanPayoffAtExpiry(double spot, double discount, double dividendDiscount, double variance, StrikedTypePayoff payoff, bool knock_in = true) { spot_ = spot; discount_ = discount; dividendDiscount_ = dividendDiscount; variance_ = variance; knock_in_ = knock_in; Utils.QL_REQUIRE(spot_ > 0.0, () => "positive spot value required"); Utils.QL_REQUIRE(discount_ > 0.0, () => "positive discount required"); Utils.QL_REQUIRE(dividendDiscount_ > 0.0, () => "positive dividend discount required"); Utils.QL_REQUIRE(variance_ >= 0.0, () => "negative variance not allowed"); stdDev_ = Math.Sqrt(variance_); Option.Type type = payoff.optionType(); strike_ = payoff.strike(); forward_ = spot_ * dividendDiscount_ / discount_; mu_ = Math.Log(dividendDiscount_ / discount_) / variance_ - 0.5; // binary cash-or-nothing payoff? CashOrNothingPayoff coo = payoff as CashOrNothingPayoff; if (coo != null) { K_ = coo.cashPayoff(); } // binary asset-or-nothing payoff? AssetOrNothingPayoff aoo = payoff as AssetOrNothingPayoff; if (aoo != null) { K_ = forward_; mu_ += 1.0; } log_H_S_ = Math.Log(strike_ / spot_); double log_S_H_ = Math.Log(spot_ / strike_); double eta = 0.0; double phi = 0.0; switch (type) { case Option.Type.Call: if (knock_in_) { // up-and-in cash-(at-expiry)-or-nothing option // a.k.a. american call with cash-or-nothing payoff eta = -1.0; phi = 1.0; } else { // up-and-out cash-(at-expiry)-or-nothing option eta = -1.0; phi = -1.0; } break; case Option.Type.Put: if (knock_in_) { // down-and-in cash-(at-expiry)-or-nothing option // a.k.a. american put with cash-or-nothing payoff eta = 1.0; phi = -1.0; } else { // down-and-out cash-(at-expiry)-or-nothing option eta = 1.0; phi = 1.0; } break; default: Utils.QL_FAIL("invalid option type"); break; } if (variance_ >= Const.QL_EPSILON) { D1_ = phi * (log_S_H_ / stdDev_ + mu_ * stdDev_); D2_ = eta * (log_H_S_ / stdDev_ + mu_ * stdDev_); CumulativeNormalDistribution f = new CumulativeNormalDistribution(); cum_d1_ = f.value(D1_); cum_d2_ = f.value(D2_); n_d1_ = f.derivative(D1_); n_d2_ = f.derivative(D2_); } else { if (log_S_H_ * phi > 0) { cum_d1_ = 1.0; } else { cum_d1_ = 0.0; } if (log_H_S_ * eta > 0) { cum_d2_ = 1.0; } else { cum_d2_ = 0.0; } n_d1_ = 0.0; n_d2_ = 0.0; } switch (type) { case Option.Type.Call: if (strike_ <= spot_) { if (knock_in_) { // up-and-in cash-(at-expiry)-or-nothing option // a.k.a. american call with cash-or-nothing payoff cum_d1_ = 0.5; cum_d2_ = 0.5; } else { // up-and-out cash-(at-expiry)-or-nothing option // already knocked out cum_d1_ = 0.0; cum_d2_ = 0.0; } n_d1_ = 0.0; n_d2_ = 0.0; } break; case Option.Type.Put: if (strike_ >= spot_) { if (knock_in_) { // down-and-in cash-(at-expiry)-or-nothing option // a.k.a. american put with cash-or-nothing payoff cum_d1_ = 0.5; cum_d2_ = 0.5; } else { // down-and-out cash-(at-expiry)-or-nothing option // already knocked out cum_d1_ = 0.0; cum_d2_ = 0.0; } n_d1_ = 0.0; n_d2_ = 0.0; } break; default: Utils.QL_FAIL("invalid option type"); break; } inTheMoney_ = (type == Option.Type.Call && strike_ < spot_) || (type == Option.Type.Put && strike_ > spot_); if (inTheMoney_) { X_ = 1.0; Y_ = 1.0; } else { X_ = 1.0; if (cum_d2_.IsEqual(0.0)) { Y_ = 0.0; // check needed on some extreme cases } else { Y_ = Math.Pow((strike_ / spot_), (2.0 * mu_)); } } if (!knock_in_) { Y_ *= -1.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_.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; } }