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 cdf_nu_ds_minus_x(double _x0, HestonProcess _process, double _nu_0, double _nu_t, double _dt, Discretization _discretization) { cdf_nu = new cdf_nu_ds(_process, _nu_0, _nu_t, _dt, _discretization); x0 = _x0; }