public virtual DeformedSurface localVolatilityFromPrice(Surface callPriceSurface, double spot, System.Func <double, double> interestRate, System.Func <double, double> dividendRate) { System.Func <DoublesPair, ValueDerivatives> func = (DoublesPair x) => { double t = x.First; double k = x.Second; double r = interestRate(t); double q = dividendRate(t); double price = callPriceSurface.zValue(t, k); DoubleArray priceSensi = callPriceSurface.zValueParameterSensitivity(t, k).Sensitivity; double divT = FIRST_DERIV.differentiate(u => callPriceSurface.zValue(u, k)).apply(t); DoubleArray divTSensi = FIRST_DERIV_SENSI.differentiate(u => callPriceSurface.zValueParameterSensitivity(u.get(0), k).Sensitivity).apply(DoubleArray.of(t)).column(0); double divK = FIRST_DERIV.differentiate(l => callPriceSurface.zValue(t, l)).apply(k); DoubleArray divKSensi = FIRST_DERIV_SENSI.differentiate(l => callPriceSurface.zValueParameterSensitivity(t, l.get(0)).Sensitivity).apply(DoubleArray.of(k)).column(0); double divK2 = SECOND_DERIV.differentiate(l => callPriceSurface.zValue(t, l)).apply(k); DoubleArray divK2Sensi = SECOND_DERIV_SENSI.differentiateNoCross(l => callPriceSurface.zValueParameterSensitivity(t, l.get(0)).Sensitivity).apply(DoubleArray.of(k)).column(0); double var = 2d * (divT + q * price + (r - q) * k * divK) / (k * k * divK2); if (var < 0d) { throw new System.ArgumentException("Negative variance"); } double localVol = Math.Sqrt(var); double factor = 1d / (localVol * k * k * divK2); DoubleArray localVolSensi = divTSensi.multipliedBy(factor).plus(divKSensi.multipliedBy((r - q) * k * factor)).plus(priceSensi.multipliedBy(q * factor)).plus(divK2Sensi.multipliedBy(-0.5 * localVol / divK2)); return(ValueDerivatives.of(localVol, localVolSensi)); }; SurfaceMetadata metadata = DefaultSurfaceMetadata.builder().xValueType(ValueType.YEAR_FRACTION).yValueType(ValueType.STRIKE).zValueType(ValueType.LOCAL_VOLATILITY).surfaceName(SurfaceName.of("localVol_" + callPriceSurface.Name)).build(); return(DeformedSurface.of(metadata, callPriceSurface, func)); }
public virtual DeformedSurface localVolatilityFromImpliedVolatility(Surface impliedVolatilitySurface, double spot, System.Func <double, double> interestRate, System.Func <double, double> dividendRate) { System.Func <DoublesPair, ValueDerivatives> func = (DoublesPair x) => { double t = x.First; double k = x.Second; double r = interestRate(t); double q = dividendRate(t); double vol = impliedVolatilitySurface.zValue(t, k); DoubleArray volSensi = impliedVolatilitySurface.zValueParameterSensitivity(t, k).Sensitivity; double divT = FIRST_DERIV.differentiate(u => impliedVolatilitySurface.zValue(u, k)).apply(t); DoubleArray divTSensi = FIRST_DERIV_SENSI.differentiate(u => impliedVolatilitySurface.zValueParameterSensitivity(u.get(0), k).Sensitivity).apply(DoubleArray.of(t)).column(0); double localVol; DoubleArray localVolSensi = DoubleArray.of(); if (k < SMALL) { localVol = Math.Sqrt(vol * vol + 2 * vol * t * (divT)); localVolSensi = volSensi.multipliedBy((vol + t * divT) / localVol).plus(divTSensi.multipliedBy(vol * t / localVol)); } else { double divK = FIRST_DERIV.differentiate(l => impliedVolatilitySurface.zValue(t, l)).apply(k); DoubleArray divKSensi = FIRST_DERIV_SENSI.differentiate(l => impliedVolatilitySurface.zValueParameterSensitivity(t, l.get(0)).Sensitivity).apply(DoubleArray.of(k)).column(0); double divK2 = SECOND_DERIV.differentiate(l => impliedVolatilitySurface.zValue(t, l)).apply(k); DoubleArray divK2Sensi = SECOND_DERIV_SENSI.differentiateNoCross(l => impliedVolatilitySurface.zValueParameterSensitivity(t, l.get(0)).Sensitivity).apply(DoubleArray.of(k)).column(0); double rq = r - q; double h1 = (Math.Log(spot / k) + (rq + 0.5 * vol * vol) * t) / vol; double h2 = h1 - vol * t; double den = 1d + 2d * h1 * k * divK + k * k * (h1 * h2 * divK * divK + t * vol * divK2); double var = (vol * vol + 2d * vol * t * (divT + k * rq * divK)) / den; if (var < 0d) { throw new System.ArgumentException("Negative variance"); } localVol = Math.Sqrt(var); localVolSensi = volSensi.multipliedBy(localVol * k * h2 * divK * (1d + 0.5 * k * h2 * divK) / vol / den + 0.5 * localVol * Math.Pow(k * h1 * divK, 2) / vol / den + (vol + divT * t + rq * t * k * divK) / (localVol * den) - 0.5 * divK2 * localVol * k * k * t / den).plus(divKSensi.multipliedBy((vol * t * rq * k / localVol - localVol * k * h1 * (1d + k * h2 * divK)) / den)).plus(divTSensi.multipliedBy(vol * t / (localVol * den))).plus(divK2Sensi.multipliedBy(-0.5 * vol * localVol * k * k * t / den)); } return(ValueDerivatives.of(localVol, localVolSensi)); }; SurfaceMetadata metadata = DefaultSurfaceMetadata.builder().xValueType(ValueType.YEAR_FRACTION).yValueType(ValueType.STRIKE).zValueType(ValueType.LOCAL_VOLATILITY).surfaceName(SurfaceName.of("localVol_" + impliedVolatilitySurface.Name)).build(); return(DeformedSurface.of(metadata, impliedVolatilitySurface, func)); }
public virtual void flatVolTest() { double constantVol = 0.15; ConstantSurface impliedVolSurface = ConstantSurface.of("impliedVol", constantVol); System.Func <double, double> zeroRate = (double?x) => { return(0.05d); }; System.Func <double, double> zeroRate1 = (double?x) => { return(0.02d); }; double[] strikes = new double[] { 90d, 100d, 115d }; foreach (double strike in strikes) { foreach (double time in TEST_TIMES) { DeformedSurface localVolSurface = CALC.localVolatilityFromImpliedVolatility(impliedVolSurface, 100d, zeroRate, zeroRate1); assertEquals(localVolSurface.zValue(time, strike), constantVol); } } }