public static double bachelierBlackFormula( Option.Type optionType, double strike, double forward, double stdDev, double discount) { if ( stdDev < 0.0 ) throw new ArgumentException( "stdDev (" + stdDev + ") must be non-negative" ); if ( discount <= 0.0 ) throw new ArgumentException( "discount (" + discount + ") must be positive" ); double d = ( forward - strike ) * (int)optionType; double h = d / stdDev; if ( stdDev == 0.0 ) return discount * Math.Max( d, 0.0 ); CumulativeNormalDistribution phi = new CumulativeNormalDistribution(); double result = discount * ( stdDev * phi.derivative( h ) + d * phi.value( h ) ); if ( !( result >= 0.0 ) ) throw new ApplicationException( "negative value (" + result + ") for " + stdDev + " stdDev, " + optionType + " option, " + strike + " strike , " + forward + " forward" ); return result; }
public static double blackFormulaStdDevDerivative(double strike, double forward, double stdDev, double discount, double displacement) { checkParameters(strike, forward, displacement); if (stdDev < 0.0) { throw new ArgumentException("stdDev (" + stdDev + ") must be non-negative"); } if (discount <= 0.0) { throw new ArgumentException("discount (" + discount + ") must be positive"); } forward = forward + displacement; strike = strike + displacement; if (stdDev == 0.0 || strike == 0) { return(0.0); } double d1 = Math.Log(forward / strike) / stdDev + .5 * stdDev; CumulativeNormalDistribution phi = new CumulativeNormalDistribution(); return(discount * forward * phi.derivative(d1)); }
public static double blackFormula( Option.Type optionType, double strike, double forward, double stdDev, double discount, double displacement ) { checkParameters( strike, forward, displacement ); if ( !( stdDev >= 0.0 ) ) throw new ApplicationException( "stdDev (" + stdDev + ") must be non-negative" ); if ( !( discount > 0.0 ) ) throw new ApplicationException( "discount (" + discount + ") must be positive" ); if ( stdDev == 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 == 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 ); if ( !( result >= 0.0 ) ) throw new ApplicationException( "negative value (" + result + ") for " + stdDev + " stdDev, " + optionType + " option, " + strike + " strike , " + forward + " forward" ); return result; }
public GaussianKernel(double average, double sigma) { nd_ = new NormalDistribution(average,sigma); cnd_ = new CumulativeNormalDistribution(average,sigma); // normFact is \sqrt{2*\pi}. normFact_ = Const.M_SQRT2*Const.M_SQRTPI; }
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); }
public static double bachelierBlackFormula(Option.Type optionType, double strike, double forward, double stdDev, double discount) { if (stdDev < 0.0) { throw new ArgumentException("stdDev (" + stdDev + ") must be non-negative"); } if (discount <= 0.0) { throw new ArgumentException("discount (" + discount + ") must be positive"); } double d = (forward - strike) * (int)optionType; double h = d / stdDev; if (stdDev == 0.0) { return(discount * Math.Max(d, 0.0)); } CumulativeNormalDistribution phi = new CumulativeNormalDistribution(); double result = discount * (stdDev * phi.derivative(h) + d * phi.value(h)); if (!(result >= 0.0)) { throw new ApplicationException("negative value (" + result + ") for " + stdDev + " stdDev, " + optionType + " option, " + strike + " strike , " + forward + " forward"); } 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 freeArbSVI(List <double> strikes, List <double> times, double spot, Handle <YieldTermStructure> riskFreeTS, Handle <YieldTermStructure> dividendTS, Matrix crudeVolSurface, double lambda) { // sanity check to add : check date + strikes vs vol surface strikes_ = strikes; times_ = times; spot_ = spot; riskFreeTS_ = riskFreeTS; dividendTS_ = dividendTS; fwMoneynessGrid_ = new Matrix(times_.Count, strikes_.Count, 0.0); totalVariance_ = new Matrix(times_.Count, strikes_.Count, 0.0); X_ = new Matrix(times_.Count, 2 * strikes_.Count + 2, 0.0); crudeVolSurface_ = new Matrix(crudeVolSurface); cumulNormdiv_ = new CumulativeNormalDistribution(); lambda_ = lambda; g_i_ = new List <CubicInterpolation>(); A_ = new Matrix(2 * strikes_.Count + 2, strikes_.Count, 0.0);; B_ = new Matrix(2 * strikes_.Count + 2, 2 * strikes_.Count + 1, 0.0);; strikesSpline_ = new InitializedList <List <double> >(); }
public AnalyticDoubleBarrierEngine(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 * \f[ \frac{\sum w_i (min(0, x_i-target))^2 }{\sum w_i}. \f] * * 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 static double blackFormula(Option.Type optionType, double strike, double forward, double stdDev, double discount, double displacement) { checkParameters(strike, forward, displacement); if (!(stdDev >= 0.0)) { throw new ApplicationException("stdDev (" + stdDev + ") must be non-negative"); } if (!(discount > 0.0)) { throw new ApplicationException("discount (" + discount + ") must be positive"); } if (stdDev == 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 == 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); if (!(result >= 0.0)) { throw new ApplicationException("negative value (" + result + ") for " + stdDev + " stdDev, " + optionType + " option, " + strike + " strike , " + forward + " forward"); } return(result); }
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 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 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_); //verifier le polymorphisme 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 * QLNet.Const.M_PI))); }
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_ == strike) { d2_ = -0.5 * stdDev_; return(f.value(phi_ * d2_)); } } if (phi_ > 0) { // if Call return(cum_d2_pos_); } else { return(cum_d2_neg_); } }
public double cumD1(double strike) // N(d1) or N(-d1) { double d1_ = 0.0; double cum_d1_pos_ = 1.0; // N(d1) double cum_d1_neg_ = 0.0; // N(-d1) CumulativeNormalDistribution f = new CumulativeNormalDistribution(); if (stdDev_ >= Const.QL_EPSILON) { if (strike > 0) { d1_ = Math.Log(forward_ / strike) / stdDev_ + 0.5 * stdDev_; return(f.value(phi_ * d1_)); } } else { if (forward_ < strike) { cum_d1_pos_ = 0.0; cum_d1_neg_ = 1.0; } else if (forward_.IsEqual(strike)) { d1_ = 0.5 * stdDev_; return(f.value(phi_ * d1_)); } } if (phi_ > 0) { // if Call return(cum_d1_pos_); } else { return(cum_d1_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 != 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 protected override double optionletPrice(Option.Type optionType, double strike) { double variance = swaptionVolatility().link.blackVariance(fixingDate_, swapTenor_, swapRateValue_); double firstDerivativeOfGAtForwardValue = gFunction_.firstDerivative(swapRateValue_); double price = 0; double CK = vanillaOptionPricer_.value(strike, optionType, annuity_); price += (discount_ / annuity_) * CK; double sqrtSigma2T = Math.Sqrt(variance); double lnRoverK = Math.Log(swapRateValue_ / strike); 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_ + strike) * N12 + strike * Nminus12); price *= coupon_.accrualPeriod(); return price; }
//Hagan, 3.5b, 3.5c protected override double optionletPrice(Option.Type optionType, double strike) { double variance = swaptionVolatility().link.blackVariance(fixingDate_, swapTenor_, swapRateValue_); double firstDerivativeOfGAtForwardValue = gFunction_.firstDerivative(swapRateValue_); double price = 0; double CK = vanillaOptionPricer_.value(strike, optionType, annuity_); price += (discount_ / annuity_) * CK; double sqrtSigma2T = Math.Sqrt(variance); double lnRoverK = Math.Log(swapRateValue_ / strike); 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_ + strike) * N12 + strike * Nminus12); price *= coupon_.accrualPeriod(); return(price); }
// public BlackCalculator(StrikedTypePayoff payoff, double forward, double stdDev, double discount = 1.0) { public BlackCalculator(StrikedTypePayoff payoff, double forward, double stdDev, double discount) { strike_ = payoff.strike(); forward_ = forward; stdDev_ = stdDev; discount_ = discount; variance_ = stdDev*stdDev; if (!(forward>0.0)) throw new ApplicationException("positive forward value required: " + forward + " not allowed"); if (!(stdDev>=0.0)) throw new ApplicationException("non-negative standard deviation required: " + stdDev + " not allowed"); if (!(discount>0.0)) throw new ApplicationException("positive discount required: " + discount + " not allowed"); if (stdDev_>=Const.QL_EPSILON) { if (strike_==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: throw new ArgumentException("invalid option type"); } // now dispatch on type. Calculator calc = new Calculator(this); payoff.accept(calc); }
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 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); }
// critical commodity price //public static double criticalPrice(StrikedTypePayoff payoff, double riskFreeDiscount, double dividendDiscount, // double variance, double tolerance = 1e-6); 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; 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: throw new ArgumentException("unknown option type"); } // 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 = (riskFreeDiscount != 1.0 ? -2.0 * Math.Log(riskFreeDiscount) / (variance * (1.0 - riskFreeDiscount)) : 0.0); 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: throw new ArgumentException("unknown option type"); } return(Si); }
// critical commodity price //public static double criticalPrice(StrikedTypePayoff payoff, double riskFreeDiscount, double dividendDiscount, // double variance, double tolerance = 1e-6); 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; 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: throw new ArgumentException("unknown option type"); } // 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 = (riskFreeDiscount!=1.0 ? -2.0*Math.Log(riskFreeDiscount)/ (variance*(1.0-riskFreeDiscount)) : 0.0); 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: throw new ArgumentException("unknown option type"); } return Si; }
// public BlackCalculator(StrikedTypePayoff payoff, double forward, double stdDev, double discount = 1.0) { public BlackCalculator(StrikedTypePayoff payoff, double forward, double stdDev, double discount) { strike_ = payoff.strike(); forward_ = forward; stdDev_ = stdDev; discount_ = discount; variance_ = stdDev * stdDev; if (!(forward > 0.0)) { throw new ApplicationException("positive forward value required: " + forward + " not allowed"); } if (!(stdDev >= 0.0)) { throw new ApplicationException("non-negative standard deviation required: " + stdDev + " not allowed"); } if (!(discount > 0.0)) { throw new ApplicationException("positive discount required: " + discount + " not allowed"); } if (stdDev_ >= Const.QL_Epsilon) { if (strike_ == 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: throw new ArgumentException("invalid option type"); } // now dispatch on type. Calculator calc = new Calculator(this); payoff.accept(calc); }
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 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_); //verifier le polymorphisme 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*QLNet.Const.M_PI)); }
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"); */ if(!(arguments_.exercise.type() == Exercise.Type.European)) throw new ApplicationException("not an European Option"); double runningLog; int pastFixings; if (arguments_.averageType == Average.Type.Geometric) { if(!(arguments_.runningAccumulator>0.0)) throw new ApplicationException("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 = (PlainVanillaPayoff)(arguments_.payoff); if (payoff == null) throw new ApplicationException("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 InitializedList<double>(arguments_.fixingDates.Count()); 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 = std::accumulate(fixingTimes.begin(), fixingTimes.end(), 0.0);*/ 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(); if(!(s > 0.0)) throw new ApplicationException("positive underlying value required"); int M = (pastFixings == 0 ? 1 : pastFixings); double muG = pastWeight * runningLog/M + futureWeight * Math.Log(s) + nu*timeSum/N; double forwardPrice = Math.Exp(muG + variance / 2.0); double riskFreeDiscount = process_.riskFreeRate().link.discount( arguments_.exercise.lastDate()); BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); results_.value = black.value(); results_.delta = futureWeight*black.delta(forwardPrice)*forwardPrice/s; results_.gamma = forwardPrice*futureWeight/(s*s) *( black.gamma(forwardPrice)*futureWeight*forwardPrice - pastWeight*black.delta(forwardPrice) ); double Nx_1, nx_1; CumulativeNormalDistribution CND = new CumulativeNormalDistribution(); NormalDistribution ND = new NormalDistribution(); if (sigG > Const.QL_Epsilon) { double x_1 = (muG-Math.Log(payoff.strike())+variance)/sigG; Nx_1 = CND.value(x_1); nx_1 = ND.value( x_1); } else { Nx_1 = (muG > Math.Log(payoff.strike()) ? 1.0 : 0.0); nx_1 = 0.0; } results_.vega = forwardPrice * riskFreeDiscount * ( (dmuG_dsig + sigG * dsigG_dsig)*Nx_1 + nx_1*dsigG_dsig ); if (payoff.optionType() == Option.Type.Put) results_.vega -= riskFreeDiscount * forwardPrice * (dmuG_dsig + sigG * dsigG_dsig); double tRho = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); results_.rho = black.rho(tRho)*timeSum/(N*tRho) - (tRho-timeSum/N)*results_.value; double tDiv = divdc.yearFraction( process_.dividendYield().link.referenceDate(), arguments_.exercise.lastDate()); results_.dividendRho = black.dividendRho(tDiv)*timeSum /(N*tDiv); results_.strikeSensitivity = black.strikeSensitivity(); results_.theta = Utils.blackScholesTheta(process_, results_.value.GetValueOrDefault(), results_.delta.GetValueOrDefault(), results_.gamma.GetValueOrDefault()); }
public override void calculate() { if (!(arguments_.exercise.type() == Exercise.Type.American)) throw new ApplicationException("not an American Option"); AmericanExercise ex = arguments_.exercise as AmericanExercise; if (ex == null) throw new ApplicationException("non-American exercise given"); if (ex.payoffAtExpiry()) throw new ApplicationException("payoff at expiry not handled"); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; if (payoff == null) throw new ApplicationException("non-striked payoff given"); double variance = process_.blackVolatility().link.blackVariance(ex.lastDate(), payoff.strike()); double dividendDiscount = process_.dividendYield().link.discount(ex.lastDate()); double riskFreeDiscount = process_.riskFreeRate().link.discount(ex.lastDate()); double spot = process_.stateVariable().link.value(); if (!(spot > 0.0)) throw new ApplicationException("negative or null underlying given"); double forwardPrice = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); if (dividendDiscount>=1.0 && payoff.optionType()==Option.Type.Call) { // early exercise never optimal results_.value = black.value(); results_.delta = black.delta(spot); results_.deltaForward = black.deltaForward(); results_.elasticity = black.elasticity(spot); results_.gamma = black.gamma(spot); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); double t = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); results_.rho = black.rho(t); t = divdc.yearFraction(process_.dividendYield().link.referenceDate(), arguments_.exercise.lastDate()); results_.dividendRho = black.dividendRho(t); t = voldc.yearFraction(process_.blackVolatility().link.referenceDate(), arguments_.exercise.lastDate()); results_.vega = black.vega(t); results_.theta = black.theta(spot, t); results_.thetaPerDay = black.thetaPerDay(spot, t); results_.strikeSensitivity = black.strikeSensitivity(); results_.itmCashProbability = black.itmCashProbability(); } else { // early exercise can be optimal CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution(); 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; switch (payoff.optionType()) { case Option.Type.Call: phi = 1; break; case Option.Type.Put: phi = -1; break; default: throw new ArgumentException("invalid option type"); } //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() { // 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() { if (!(arguments_.exercise.type() == Exercise.Type.American)) throw new ApplicationException("not an American Option"); AmericanExercise ex = arguments_.exercise as AmericanExercise; if (ex == null) throw new ApplicationException("non-American exercise given"); if(ex.payoffAtExpiry()) throw new ApplicationException("payoff at expiry not handled"); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; if (payoff == null) throw new ApplicationException("non-striked payoff given"); double variance = process_.blackVolatility().link.blackVariance(ex.lastDate(), payoff.strike()); double dividendDiscount = process_.dividendYield().link.discount(ex.lastDate()); double riskFreeDiscount = process_.riskFreeRate().link.discount(ex.lastDate()); double spot = process_.stateVariable().link.value(); if (!(spot > 0.0)) throw new ApplicationException("negative or null underlying given"); double forwardPrice = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); if (dividendDiscount>=1.0 && payoff.optionType()==Option.Type.Call) { // early exercise never optimal results_.value = black.value(); results_.delta = black.delta(spot); results_.deltaForward = black.deltaForward(); results_.elasticity = black.elasticity(spot); results_.gamma = black.gamma(spot); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); double t = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); results_.rho = black.rho(t); t = divdc.yearFraction(process_.dividendYield().link.referenceDate(), arguments_.exercise.lastDate()); results_.dividendRho = black.dividendRho(t); t = voldc.yearFraction(process_.blackVolatility().link.referenceDate(), arguments_.exercise.lastDate()); results_.vega = black.vega(t); results_.theta = black.theta(spot, t); results_.thetaPerDay = black.thetaPerDay(spot, t); results_.strikeSensitivity = black.strikeSensitivity(); results_.itmCashProbability = black.itmCashProbability(); } else { // early exercise can be optimal CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution(); double tolerance = 1e-6; double Sk = criticalPrice(payoff, riskFreeDiscount, dividendDiscount, variance, tolerance); double forwardSk = Sk * dividendDiscount / riskFreeDiscount; double d1 = (Math.Log(forwardSk/payoff.strike()) + 0.5*variance) /Math.Sqrt(variance); double n = 2.0*Math.Log(dividendDiscount/riskFreeDiscount)/variance; double K = -2.0*Math.Log(riskFreeDiscount)/ (variance*(1.0-riskFreeDiscount)); double Q, a; switch (payoff.optionType()) { case Option.Type.Call: Q = (-(n-1.0) + Math.Sqrt(((n-1.0)*(n-1.0))+4.0*K))/2.0; a = (Sk/Q) * (1.0 - dividendDiscount * cumNormalDist.value(d1)); if (spot<Sk) { results_.value = black.value() + a * Math.Pow((spot/Sk), Q); } else { results_.value = spot - payoff.strike(); } break; case Option.Type.Put: Q = (-(n-1.0) - Math.Sqrt(((n-1.0)*(n-1.0))+4.0*K))/2.0; a = -(Sk/Q) * (1.0 - dividendDiscount * cumNormalDist.value(-d1)); if (spot>Sk) { results_.value = black.value() + a * Math.Pow((spot / Sk), Q); } else { results_.value = payoff.strike() - spot; } break; default: throw new ApplicationException("unknown option type"); } } // end of "early exercise can be optimal" }
public override void calculate() { if (!(arguments_.exercise.type() == Exercise.Type.American)) { throw new ApplicationException("not an American Option"); } AmericanExercise ex = arguments_.exercise as AmericanExercise; if (ex == null) { throw new ApplicationException("non-American exercise given"); } if (ex.payoffAtExpiry()) { throw new ApplicationException("payoff at expiry not handled"); } StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; if (payoff == null) { throw new ApplicationException("non-striked payoff given"); } double variance = process_.blackVolatility().link.blackVariance(ex.lastDate(), payoff.strike()); double dividendDiscount = process_.dividendYield().link.discount(ex.lastDate()); double riskFreeDiscount = process_.riskFreeRate().link.discount(ex.lastDate()); double spot = process_.stateVariable().link.value(); if (!(spot > 0.0)) { throw new ApplicationException("negative or null underlying given"); } double forwardPrice = spot * dividendDiscount / riskFreeDiscount; BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount); if (dividendDiscount >= 1.0 && payoff.optionType() == Option.Type.Call) { // early exercise never optimal results_.value = black.value(); results_.delta = black.delta(spot); results_.deltaForward = black.deltaForward(); results_.elasticity = black.elasticity(spot); results_.gamma = black.gamma(spot); DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); double t = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate()); results_.rho = black.rho(t); t = divdc.yearFraction(process_.dividendYield().link.referenceDate(), arguments_.exercise.lastDate()); results_.dividendRho = black.dividendRho(t); t = voldc.yearFraction(process_.blackVolatility().link.referenceDate(), arguments_.exercise.lastDate()); results_.vega = black.vega(t); results_.theta = black.theta(spot, t); results_.thetaPerDay = black.thetaPerDay(spot, t); results_.strikeSensitivity = black.strikeSensitivity(); results_.itmCashProbability = black.itmCashProbability(); } else { // early exercise can be optimal CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution(); double tolerance = 1e-6; double Sk = criticalPrice(payoff, riskFreeDiscount, dividendDiscount, variance, tolerance); double forwardSk = Sk * dividendDiscount / riskFreeDiscount; double d1 = (Math.Log(forwardSk / payoff.strike()) + 0.5 * variance) / Math.Sqrt(variance); double n = 2.0 * Math.Log(dividendDiscount / riskFreeDiscount) / variance; double K = -2.0 * Math.Log(riskFreeDiscount) / (variance * (1.0 - riskFreeDiscount)); double Q, a; switch (payoff.optionType()) { case Option.Type.Call: Q = (-(n - 1.0) + Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * K)) / 2.0; a = (Sk / Q) * (1.0 - dividendDiscount * cumNormalDist.value(d1)); if (spot < Sk) { results_.value = black.value() + a * Math.Pow((spot / Sk), Q); } else { results_.value = spot - payoff.strike(); } break; case Option.Type.Put: Q = (-(n - 1.0) - Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * K)) / 2.0; a = -(Sk / Q) * (1.0 - dividendDiscount * cumNormalDist.value(-d1)); if (spot > Sk) { results_.value = black.value() + a * Math.Pow((spot / Sk), Q); } else { results_.value = payoff.strike() - spot; } break; default: throw new ApplicationException("unknown option type"); } } // end of "early exercise can be optimal" }
//private double DXDstrike_; public AmericanPayoffAtExpiry(double spot, double discount, double dividendDiscount, double variance, StrikedTypePayoff payoff) { spot_ = spot; discount_ = discount; dividendDiscount_ = dividendDiscount; variance_ = variance; if (!(spot_ > 0.0)) { throw new ApplicationException("positive spot_ value required"); } forward_ = spot_ * dividendDiscount_ / discount_; if (!(discount_ > 0.0)) { throw new ApplicationException("positive discount required"); } if (!(dividendDiscount_ > 0.0)) { throw new ApplicationException("positive dividend discount_ required"); } if (!(variance_ >= 0.0)) { throw new ApplicationException("negative variance_ not allowed"); } stdDev_ = Math.Sqrt(variance_); Option.Type type = payoff.optionType(); strike_ = payoff.strike(); mu_ = Math.Log(dividendDiscount_ / discount_) / variance_ - 0.5; // binary cash-or-nothing payoff? CashOrNothingPayoff coo = payoff as CashOrNothingPayoff; if (coo != null) { K_ = coo.cashPayoff(); //DKDstrike_ = 0.0; } // binary asset-or-nothing payoff? AssetOrNothingPayoff aoo = payoff as AssetOrNothingPayoff; if (aoo != null) { K_ = forward_; //DKDstrike_ = 0.0; mu_ += 1.0; } log_H_S_ = Math.Log(strike_ / spot_); double n_d1; double n_d2; double cum_d1_; double cum_d2_; if (variance_ >= Const.QL_Epsilon) { D1_ = log_H_S_ / stdDev_ + mu_ * stdDev_; D2_ = D1_ - 2.0 * 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_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_d2_; // N(-d2) DalphaDd1_ = -n_d2; // -n( d2) beta_ = 1.0 - cum_d1_; // N(-d1) DbetaDd2_ = -n_d1; // -n( d1) } 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_d2_; // N(d2) DalphaDd1_ = n_d2; // n(d2) beta_ = cum_d1_; // N(d1) DbetaDd2_ = n_d1; // n(d1) } else { alpha_ = 0.5; DalphaDd1_ = 0.0; beta_ = 0.5; DbetaDd2_ = 0.0; } break; default: throw new ApplicationException("invalid option type"); } inTheMoney_ = (type == Option.Type.Call && strike_ < spot_) || (type == Option.Type.Put && strike_ > spot_); if (inTheMoney_) { Y_ = 1.0; X_ = 1.0; //DYDstrike_ = 0.0; //DXDstrike_ = 0.0; } else { Y_ = 1.0; X_ = Math.Pow((double)(strike_ / spot_), (double)(2.0 * mu_)); // DXDstrike_ = ......; } }
public void testOperatorConsistency() { //("Testing differential operators..."); NormalDistribution normal = new NormalDistribution(average, sigma); CumulativeNormalDistribution cum = new CumulativeNormalDistribution(average, sigma); double xMin = average - 4 * sigma, xMax = average + 4 * sigma; int N = 10001; double h = (xMax - xMin) / (N - 1); Vector x = new Vector(N), y = new Vector(N), yi = new Vector(N), yd = new Vector(N), temp = new Vector(N), diff = new Vector(N); for (int i = 0; i < N; i++) x[i] = xMin + h * i; for (int i = 0; i < x.Count; i++) y[i] = normal.value(x[i]); for (int i = 0; i < x.Count; i++) yi[i] = cum.value(x[i]); for (int i = 0; i < x.size(); i++) yd[i] = normal.derivative(x[i]); // define the differential operators DZero D = new DZero(N, h); DPlusDMinus D2 = new DPlusDMinus(N, h); // check that the derivative of cum is Gaussian temp = D.applyTo(yi); for (int i = 0; i < y.Count; i++) diff[i] = y[i] - temp[i]; double e = Utilities.norm(diff, diff.size(), h); if (e > 1.0e-6) { Assert.Fail("norm of 1st derivative of cum minus Gaussian: " + e + "\ntolerance exceeded"); } // check that the second derivative of cum is normal.derivative temp = D2.applyTo(yi); for (int i = 0; i < yd.Count; i++) diff[i] = yd[i] - temp[i]; e = Utilities.norm(diff, diff.size(), h); if (e > 1.0e-4) { Assert.Fail("norm of 2nd derivative of cum minus Gaussian derivative: " + e + "\ntolerance exceeded"); } }
public AmericanPayoffAtHit(double spot, double discount, double dividendDiscount, double variance, StrikedTypePayoff payoff) { spot_ = spot; discount_ = discount; dividendDiscount_ = dividendDiscount; variance_ = variance; if (!(spot_ > 0.0)) throw new ApplicationException("positive spot value required"); if (!(discount_ > 0.0)) throw new ApplicationException("positive discount required"); if (!(dividendDiscount_ > 0.0)) throw new ApplicationException("positive dividend discount required"); if (!(variance_ >= 0.0)) throw new ApplicationException("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_ == 0.0 && dividendDiscount_ == 0.0) { mu_ = -0.5; lambda_ = 0.5; } else if (discount_ == 0.0) { throw new ApplicationException("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: throw new ApplicationException("invalid option type"); } 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; DXDstrike_ = 0.0; } else { forward_ = Math.Pow(strike_ / spot_, muPlusLambda_); X_ = Math.Pow(strike_ / spot_, muMinusLambda_); // DXDstrike_ = ......; } // Binary Cash-Or-Nothing payoff? CashOrNothingPayoff coo = payoff as CashOrNothingPayoff; if (coo != null) { K_ = coo.cashPayoff(); DKDstrike_ = 0.0; } // Binary Asset-Or-Nothing payoff? AssetOrNothingPayoff aoo = payoff as AssetOrNothingPayoff; if (aoo != null) { if (inTheMoney_) { K_ = spot_; DKDstrike_ = 0.0; } else { K_ = aoo.strike(); DKDstrike_ = 1.0; } } }
//private double DXDstrike_; public AmericanPayoffAtExpiry(double spot, double discount, double dividendDiscount, double variance, StrikedTypePayoff payoff) { spot_ = spot; discount_ = discount; dividendDiscount_ = dividendDiscount; variance_ = variance; if (!(spot_ > 0.0)) throw new ApplicationException("positive spot_ value required"); forward_ = spot_ * dividendDiscount_ / discount_; if (!(discount_ > 0.0)) throw new ApplicationException("positive discount required"); if (!(dividendDiscount_ > 0.0)) throw new ApplicationException("positive dividend discount_ required"); if (!(variance_ >= 0.0)) throw new ApplicationException("negative variance_ not allowed"); stdDev_ = Math.Sqrt(variance_); Option.Type type = payoff.optionType(); strike_ = payoff.strike(); mu_ = Math.Log(dividendDiscount_ / discount_) / variance_ - 0.5; // binary cash-or-nothing payoff? CashOrNothingPayoff coo = payoff as CashOrNothingPayoff; if (coo != null) { K_ = coo.cashPayoff(); //DKDstrike_ = 0.0; } // binary asset-or-nothing payoff? AssetOrNothingPayoff aoo = payoff as AssetOrNothingPayoff; if (aoo != null) { K_ = forward_; //DKDstrike_ = 0.0; mu_ += 1.0; } log_H_S_ = Math.Log(strike_ / spot_); double n_d1; double n_d2; double cum_d1_; double cum_d2_; if (variance_ >= Const.QL_EPSILON) { D1_ = log_H_S_ / stdDev_ + mu_ * stdDev_; D2_ = D1_ - 2.0 * 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_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_d2_; // N(-d2) DalphaDd1_ = -n_d2; // -n( d2) beta_ = 1.0 - cum_d1_; // N(-d1) DbetaDd2_ = -n_d1; // -n( d1) } 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_d2_; // N(d2) DalphaDd1_ = n_d2; // n(d2) beta_ = cum_d1_; // N(d1) DbetaDd2_ = n_d1; // n(d1) } else { alpha_ = 0.5; DalphaDd1_ = 0.0; beta_ = 0.5; DbetaDd2_ = 0.0; } break; default: throw new ApplicationException("invalid option type"); } inTheMoney_ = (type == Option.Type.Call && strike_ < spot_) || (type == Option.Type.Put && strike_ > spot_); if (inTheMoney_) { Y_ = 1.0; X_ = 1.0; //DYDstrike_ = 0.0; //DXDstrike_ = 0.0; } else { Y_ = 1.0; X_ = Math.Pow((double)(strike_ / spot_), (double)(2.0 * mu_)); // DXDstrike_ = ......; } }
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 f = new cdf_nu_ds(this, nu_0, nu_t, dt, discretization_); double vds = new Brent().solve(f, 1e-5, -x, theta_ * dt, 0.1 * theta_ * dt); //double vds = new Brent().solve( boost::lambda::bind(&cdf_nu_ds, *this, boost::lambda::_1, // nu_0, nu_t, dt, discretization_)-x, // 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); }
public static double blackFormulaStdDevDerivative( double strike, double forward, double stdDev, double discount, double displacement) { checkParameters( strike, forward, displacement ); if ( stdDev < 0.0 ) throw new ArgumentException( "stdDev (" + stdDev + ") must be non-negative" ); if ( discount <= 0.0 ) throw new ArgumentException( "discount (" + discount + ") must be positive" ); forward = forward + displacement; strike = strike + displacement; if ( stdDev == 0.0 || strike == 0) return 0.0; double d1 = Math.Log( forward / strike ) / stdDev + .5 * stdDev; CumulativeNormalDistribution phi = new CumulativeNormalDistribution(); return discount * forward * phi.derivative( d1 ); }
public override void calculate() { 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.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()); //vannila 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 barrierTyp; if (arguments_.barrierType == Barrier.Type.UpOut) { barrierTyp = arguments_.barrierType; } else if (arguments_.barrierType == Barrier.Type.UpIn) { barrierTyp = Barrier.Type.UpOut; } else if (arguments_.barrierType == Barrier.Type.DownOut) { barrierTyp = arguments_.barrierType; } else { barrierTyp = Barrier.Type.DownOut; } BarrierOption barrierOption = new BarrierOption(barrierTyp, 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; } }
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_ == 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; } }