public static IInterpolator1D GenerateCompositeSmileBasic(this IVolSurface surface, IVolSurface fxSurface, int numSamples, DateTime expiry, double fwdAsset, double fwdFx, double correlation, bool deltaStrikeOutput = false) { var deltaKa = fwdAsset * 0.00001; var deltaKfx = fwdFx * 0.00001; var t = surface.OriginDate.CalculateYearFraction(expiry, DayCountBasis.Act365F); var atmFx = fxSurface.GetVolForAbsoluteStrike(fwdFx, t, fwdFx); var atmA = surface.GetVolForAbsoluteStrike(fwdAsset, t, fwdAsset); var compoFwd = fwdAsset * fwdFx; var atmCompo = Sqrt(atmFx * atmFx + atmA * atmA + 2.0 * correlation * atmA * atmFx); var lowK = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(compoFwd, -0.0001, 0, t, atmCompo); var hiK = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(compoFwd, -0.9999, 0, t, atmCompo); var lowKA = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwdAsset, -0.0001, 0, t, atmA); var hiKA = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwdAsset, -0.9999, 0, t, atmA); var x = new double[numSamples]; var y = new double[numSamples]; var k = lowK; var kStep = (hiK - lowK) / numSamples; var kStepA = (hiKA - lowKA) / numSamples; var kStepFx = (hiK / hiKA - lowK / lowKA) / numSamples; for (var i = 0; i < x.Length; i++) { x[i] = k; var kA = lowKA; var totalP = 0.0; for (var j = 0; j < numSamples; j++) { var kFx = k / kA; var volFx = fxSurface.GetVolForAbsoluteStrike(kFx, t, fwdFx); var volA = surface.GetVolForAbsoluteStrike(kA, t, fwdAsset); var volC = Sqrt(volFx * volFx + volA * volA + 2.0 * correlation * volA * volFx); var fxBucketLow = kFx - deltaKfx / 2.0; var fxBucketHi = kFx + deltaKfx / 2.0; var assetBucketLow = kA - deltaKa / 2.0; var assetBucketHi = kA + deltaKa / 2.0; var weight = 1.0; y[i] += weight * volC; totalP += weight; kA += kStepA; } y[i] /= totalP; k += kStep; } if (deltaStrikeOutput) { x = x.Select((q, ix) => - BlackFunctions.BlackDelta(compoFwd, q, 0.0, t, y[ix], OptionType.P)).ToArray(); } return(InterpolatorFactory.GetInterpolator(x, y, Interpolator1DType.LinearFlatExtrap)); }
public static double InverseCDFex(this IVolSurface surface, double t, double fwd, double p) { var deltaK = fwd * 1e-10; var lowGuess = fwd / 2; var highGuess = fwd * 2; var targetFunc = new Func <double, double>(k => { var volM = surface.GetVolForAbsoluteStrike(k - deltaK, t, fwd); var volP = surface.GetVolForAbsoluteStrike(k + deltaK, t, fwd); var pvM = BlackFunctions.BlackPV(fwd, k - deltaK, 0.0, t, volM, OptionType.P); var pvP = BlackFunctions.BlackPV(fwd, k + deltaK, 0.0, t, volP, OptionType.P); var digi = (pvP - pvM) / (2 * deltaK); //var digi = BlackFunctions.BlackDigitalPV(fwd, k, 0, t, surface.GetVolForAbsoluteStrike(k, t, fwd), OptionType.P); return(p - digi); }); var breakCount = 0; while (targetFunc(lowGuess) < 0) { // highGuess = lowGuess*2.0; lowGuess /= 2.0; breakCount++; if (breakCount == 10) { return(lowGuess); } } breakCount = 0; while (targetFunc(highGuess) > 0) { //lowGuess = highGuess/2.0; highGuess *= 2.0; breakCount++; if (breakCount == 10) { return(highGuess); } } var b = Math.Solvers.Brent.BrentsMethodSolve(targetFunc, lowGuess, highGuess, 1e-8); //var b = Math.Solvers.Newton1D.MethodSolve2(targetFunc, fwd, 1e-6, 1000, fwd * 0.00001); if (double.IsInfinity(b) || double.IsNaN(b)) { throw new Exception("Invalid strike found"); } //if (b==lowGuess || b==highGuess) // throw new Exception("Strike outside of bounds"); return(b); }
public static IInterpolator1D GeneratePDF(this IVolSurface surface, int numSamples, DateTime expiry, double fwd) { var deltaK = fwd * 0.0001; var t = surface.OriginDate.CalculateYearFraction(expiry, DayCountBasis.Act365F); var lowStrikeVol = surface.GetVolForDeltaStrike(0.0001, t, fwd); var lowStrike = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.0001, 0, t, lowStrikeVol); var hiStrikeVol = surface.GetVolForDeltaStrike(0.9999, t, fwd); var hiStrike = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.9999, 0, t, hiStrikeVol); var x = new double[numSamples + 2]; var y = new double[numSamples + 2]; var k = lowStrike; var kStep = (hiStrike - lowStrike) / numSamples; for (var i = 0; i < x.Length; i++) { if (i == 0) { x[0] = lowStrike / 2.0; y[0] = 0; continue; } if (i == x.Length - 1) { x[i] = k * 2; y[i] = 0; continue; } var volLow = surface.GetVolForAbsoluteStrike(k - deltaK, t, fwd); var callLow = BlackFunctions.BlackPV(fwd, k - deltaK, 0, t, volLow, OptionType.C); var volMid = surface.GetVolForAbsoluteStrike(k, t, fwd); var callMid = BlackFunctions.BlackPV(fwd, k, 0, t, volMid, OptionType.C); var volHi = surface.GetVolForAbsoluteStrike(k + deltaK, t, fwd); var callHi = BlackFunctions.BlackPV(fwd, k + deltaK, 0, t, volHi, OptionType.C); var digitalLo = (callLow - callMid) / deltaK; var digitalHi = (callMid - callHi) / deltaK; y[i] = (digitalLo - digitalHi) / deltaK; x[i] = k; k += kStep; } var firstPass = InterpolatorFactory.GetInterpolator(x, y, Interpolator1DType.LinearFlatExtrap); var totalY = ((IIntegrableInterpolator)firstPass).DefiniteIntegral(x.First(), x.Last()); y = y.Select(v => v / totalY).ToArray(); return(InterpolatorFactory.GetInterpolator(x, y, Interpolator1DType.LinearFlatExtrap)); }
public static double[][] ComputeLocalVarianceOnGridFromCalls(this IVolSurface VanillaSurface, double[][] strikes, double[] timeSteps, Func <double, double> forwardFunc) { var numberOfTimesteps = timeSteps.Length; var deltaK = 0.001 * forwardFunc(timeSteps[0]); var lvGrid = new double[numberOfTimesteps - 1][]; var fwds = timeSteps.Select(t => forwardFunc(t)).ToArray(); //ParallelUtils.Instance.For(1, numberOfTimesteps, 1, it => for (var it = 1; it < numberOfTimesteps; it++) { var T = timeSteps[it]; var T1 = timeSteps[it - 1]; var fwd = fwds[it]; var fwdtm1 = fwds[it - 1]; var rmq = Log(fwd / fwdtm1) / (T - T1); var numberOfStrikes = strikes[it - 1].Length; var cInterp = VanillaSurface.GeneratePremiumInterpolator(numberOfStrikes * 2, T, fwd, OptionType.C); lvGrid[it - 1] = new double[numberOfStrikes]; if (numberOfStrikes > 1) { for (var ik = 0; ik < numberOfStrikes; ik++) { var K = strikes[it][ik]; var V = VanillaSurface.GetVolForAbsoluteStrike(K, T, fwd); var C = BlackFunctions.BlackPV(fwd, K, 0.0, T, V, OptionType.C); var Vtm1 = VanillaSurface.GetVolForAbsoluteStrike(K, T1, fwdtm1); //var dcdt = -BlackFunctions.BlackTheta(fwd, K, 0.0, T, V, OptionType.C); var dcdt = -(BlackFunctions.BlackPV(fwdtm1, K, 0.0, T1, Vtm1, OptionType.C) - C) / (T - T1); var dcdk = cInterp.FirstDerivative(K); var d2cdk2 = cInterp.SecondDerivative(K); var localVariance = d2cdk2 == 0 ? V * V : (dcdt - rmq * (C - K * dcdk)) / (0.5 * K * K * d2cdk2); lvGrid[it - 1][ik] = localVariance; } } else { var K = strikes[it][0]; var V = VanillaSurface.GetVolForAbsoluteStrike(K, T, fwd); lvGrid[it - 1][0] = V * V; } }//, false).Wait(); return(lvGrid); }
public static IInterpolator1D GeneratePremiumInterpolator(this IVolSurface surface, int numSamples, double t, double fwd, OptionType cp) { var lowStrikeVol = surface.GetVolForDeltaStrike(0.001, t, fwd); var lowStrike = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.001, 0, t, lowStrikeVol); var hiStrikeVol = surface.GetVolForDeltaStrike(0.999, t, fwd); var hiStrike = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.999, 0, t, hiStrikeVol); var x = new double[numSamples + 2]; var y = new double[numSamples + 2]; var k = lowStrike; var kStep = (hiStrike - lowStrike) / (numSamples + 1.0); var vol = surface.GetVolForAbsoluteStrike(k / 10, t, fwd); var call = BlackFunctions.BlackPV(fwd, k / 10, 0, t, vol, cp); y[0] = call; x[0] = k / 10; for (var i = 0; i < x.Length - 1; i++) { vol = surface.GetVolForAbsoluteStrike(k, t, fwd); call = BlackFunctions.BlackPV(fwd, k, 0, t, vol, cp); y[i + 1] = call; x[i + 1] = k; k += kStep; } vol = surface.GetVolForAbsoluteStrike(k * 10, t, fwd); call = BlackFunctions.BlackPV(fwd, k * 10, 0, t, vol, cp); y[x.Length - 1] = call; x[x.Length - 1] = k * 10; return(InterpolatorFactory.GetInterpolator(x, y, Interpolator1DType.MonotoneCubicSpline)); }
public static double StrikeForPV(double targetPV, double forward, double knownAverage, IVolSurface volSurface, DateTime evalDate, DateTime avgStartDate, DateTime avgEndDate, double riskFree, OptionType callPut) { var minStrike = forward / 100.0; var maxStrike = forward * 100.0; var volDate = avgStartDate.Average(avgEndDate); Func <double, double> testFunc = (absK => { var vol = volSurface.GetVolForAbsoluteStrike(absK, volDate, forward); var pv = PV(forward, knownAverage, vol, absK, evalDate, avgStartDate, avgEndDate, riskFree, callPut); return(targetPV - pv); }); var solvedStrike = Math.Solvers.Brent.BrentsMethodSolve(testFunc, minStrike, maxStrike, 1e-8); return(solvedStrike); }
public static double StrikeForPV(double targetPV, double[] forwards, DateTime[] fixingDates, IVolSurface volSurface, DateTime evalDate, DateTime payDate, double riskFree, OptionType callPut) { var minStrike = forwards.Min() / 100.0; var maxStrike = forwards.Max() * 100.0; var ixMin = Array.BinarySearch(fixingDates, evalDate); if (ixMin < 0) { ixMin = ~ixMin; } Func <double, double> testFunc = (absK => { var vols = fixingDates.Select((d, ix) => ix >= ixMin ? volSurface.GetVolForAbsoluteStrike(absK, d, forwards[ix]) : 0.0).ToArray(); var pv = PV(forwards, fixingDates, evalDate, payDate, vols, absK, riskFree, callPut); return(targetPV - pv); }); var solvedStrike = Math.Solvers.Brent.BrentsMethodSolve(testFunc, minStrike, maxStrike, 1e-8); return(solvedStrike); }
public static double[][] ComputeLocalVarianceOnGrid(this IVolSurface VanillaSurface, double[][] strikes, double[] timeSteps, Func <double, double> forwardFunc) { var numberOfTimesteps = timeSteps.Length; var deltaK = 0.001 * forwardFunc(timeSteps[0]); var lvGrid = new double[numberOfTimesteps - 1][]; var Ss = new double[numberOfTimesteps]; for (var i = 0; i < Ss.Length; i++) { Ss[i] = forwardFunc(timeSteps[i]); } for (var it = 1; it < numberOfTimesteps; it++) { var numberOfStrikes = strikes[it - 1].Length; lvGrid[it - 1] = new double[numberOfStrikes]; double K, V, S, Td, dwdT, localVariance, TdSq, T, T1; double V_t1, V_Kp2, V_Km2, K_tm1, St; double y, yPlus, yPlus2, yMinus, yMinus2, dwdY, dwdY_p, dwdY_m, d2wd2Y, w, w_t1, w_kPlus2, w_kMinus2; //, Y1, Y2; T = timeSteps[it]; T1 = timeSteps[it - 1]; Td = T - T1; TdSq = Sqrt(Td); S = Ss[it]; St = Ss[it - 1]; var fwd = forwardFunc(T); for (var ik = 0; ik < numberOfStrikes; ik++) { K = strikes[it][ik]; K_tm1 = K * St / S; y = Log(K / S); yPlus = Log((K + deltaK) / S); yPlus2 = Log((K + deltaK * 2) / S); yMinus = Log((K - deltaK) / S); yMinus2 = Log((K - deltaK * 2) / S); //Y1 = Log(Sqrt(K * K + 2 * deltaK * K) / S); //Y2 = Log(Sqrt(K * K - 2 * deltaK * K) / S); V = VanillaSurface.GetVolForAbsoluteStrike(K, T, fwd); w = V * V * T; V_t1 = VanillaSurface.GetVolForAbsoluteStrike(K_tm1, T1, fwd); w_t1 = V_t1 * V_t1 * T1; V_Kp2 = VanillaSurface.GetVolForAbsoluteStrike(K + deltaK * 2, T, fwd); w_kPlus2 = V_Kp2 * V_Kp2 * T; V_Km2 = VanillaSurface.GetVolForAbsoluteStrike(K - deltaK * 2, T, fwd); w_kMinus2 = V_Km2 * V_Km2 * T; dwdT = (w - w_t1) / Td; dwdY_m = (w - w_kMinus2) / (y - yMinus2); dwdY_p = (w_kPlus2 - w) / (yPlus2 - y); dwdY = (dwdY_m + dwdY_p) / 2; //d2wd2Y = (dwdY_p - dwdY_m) / (Y1 - Y2); d2wd2Y = (dwdY_p - dwdY_m) / (yPlus - yMinus); localVariance = dwdT / (1 - y / w * dwdY + 0.25 * (-0.25 - 1 / w + (y * y / (w))) * dwdY * dwdY + 0.5 * d2wd2Y); lvGrid[it - 1][ik] = localVariance; } } return(lvGrid); }