final double /*@DiscountFactor*/ dividendDiscount = process.dividendYield().currentLink().discount(ex.lastDate());
final double /*@DiscountFactor*/ riskFreeDiscount = process.riskFreeRate().currentLink().discount(ex.lastDate());
final double /*@Real*/ spot = process.stateVariable().currentLink().value();
QL.require(spot > 0.0, "negative or null underlying given"); // TODO: message
final double /*@Real*/ forwardPrice = spot * dividendDiscount / riskFreeDiscount;
final BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.sqrt(variance), riskFreeDiscount);
if (dividendDiscount>=1.0 && payoff.optionType()==Option.Type.Call) {
// early exercise never optimal
r.value = black.value();
greeks.delta = black.delta(spot);
moreGreeks.deltaForward = black.deltaForward();
moreGreeks.elasticity = black.elasticity(spot);
greeks.gamma = black.gamma(spot);
final DayCounter rfdc = process.riskFreeRate().currentLink().dayCounter();
final DayCounter divdc = process.dividendYield().currentLink().dayCounter();
final DayCounter voldc = process.blackVolatility().currentLink().dayCounter();
double /*@Time*/ t = rfdc.yearFraction(process.riskFreeRate().currentLink().referenceDate(), a.exercise.lastDate());
greeks.rho = black.rho(t);
t = divdc.yearFraction(process.dividendYield().currentLink().referenceDate(), a.exercise.lastDate());
greeks.dividendRho = black.dividendRho(t);
t = voldc.yearFraction(process.blackVolatility().currentLink().referenceDate(), a.exercise.lastDate());
greeks.vega = black.vega(t);
greeks.theta = black.theta(spot, t);
moreGreeks.thetaPerDay = black.thetaPerDay(spot, t);
moreGreeks.strikeSensitivity = black.strikeSensitivity();
moreGreeks.itmCashProbability = black.itmCashProbability();
} else {
// early exercise can be optimal
final CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution();
final double /*@Real*/ tolerance = 1e-6;
final double /*@Real*/ Sk = criticalPrice(payoff, riskFreeDiscount, dividendDiscount, variance, tolerance);
final double /*@Real*/ forwardSk = Sk * dividendDiscount / riskFreeDiscount;
final double /*@Real*/ d1 = (Math.log(forwardSk/payoff.strike()) + 0.5*variance)/Math.sqrt(variance);
final double /*@Real*/ n = 2.0*Math.log(dividendDiscount/riskFreeDiscount)/variance;
final double /*@Real*/ K = -2.0*Math.log(riskFreeDiscount)/(variance*(1.0-riskFreeDiscount));
double /*@Real*/ Q, a;
switch (payoff.optionType()) {
case 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.op(d1));
if (spot<Sk)
r.value = black.value() + a * Math.pow((spot/Sk), Q);
else
r.value = spot - payoff.strike();
break;
case 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.op(-d1));
if (spot>Sk)
r.value = black.value() + a * Math.pow((spot/Sk), Q);
else
r.value = payoff.strike() - spot;
break;
default:
throw new LibraryException(UNKNOWN_OPTION_TYPE); // QA:[RG]::verified