public void SABRSurfaceRRBF() { //flat surface var origin = new DateTime(2017, 02, 07); var expiry = origin.AddYears(1); var t = (expiry - origin).TotalDays / 365.0; var fwd = 1.5; var vol = 0.32; var rr = new[] { new[] { 0.02, 0.03 } }; var bf = new[] { new[] { 0.005, 0.007 } }; Func <double, double> fwdCurve = (tt => { return(fwd); }); var surface = new SabrVolSurface(origin, new[] { vol }, new[] { expiry }, new[] { 0.25, 0.1 }, rr, bf, new[] { 100.0 }, WingQuoteType.Arithmatic, AtmVolType.ZeroDeltaStraddle, Math.Interpolation.Interpolator1DType.Linear); var gSurface = new RiskyFlySurface(origin, new[] { vol }, new[] { expiry }, new[] { 0.25, 0.1 }, rr, bf, new[] { 100.0 }, WingQuoteType.Arithmatic, AtmVolType.ZeroDeltaStraddle, Math.Interpolation.Interpolator1DType.Linear, Math.Interpolation.Interpolator1DType.Linear); var atmK = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, 0.5, 0.0, t, vol); Assert.Equal(vol, surface.GetVolForAbsoluteStrike(atmK, expiry, fwd), 2); var v25c = gSurface.GetVolForDeltaStrike(0.75, expiry, fwd); var v25p = gSurface.GetVolForDeltaStrike(0.25, expiry, fwd); var k25c = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, 0.25, 0.0, t, v25c); var k25p = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.25, 0.0, t, v25p); var t25c = surface.GetVolForAbsoluteStrike(k25c, expiry, fwd); var t25p = surface.GetVolForAbsoluteStrike(k25p, expiry, fwd); Assert.Equal(rr[0][0], t25c - t25p, 2); }
public double GetVolForAbsoluteStrike(double strike, double maturity, double forward) { if (StrikeType == StrikeType.Absolute) { var interpForStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble, _interpolators.Select(x => x.Interpolate(strike)).ToArray(), TimeInterpolatorType); return(interpForStrike.Interpolate(maturity)); } else { var fwd = forward; var cp = strike < 0 ? OptionType.Put : OptionType.Call; Func <double, double> testFunc = (deltaK => { var interpForStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble, _interpolators.Select(x => x.Interpolate(deltaK)).ToArray(), TimeInterpolatorType); var vol = interpForStrike.Interpolate(maturity); var absK = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, deltaK, 0, maturity, vol); return(absK - strike); }); var solvedStrike = Qwack.Math.Solvers.Brent.BrentsMethodSolve(testFunc, 0.000000001, 0.999999999, 1e-8); var interpForSolvedStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble, _interpolators.Select(x => x.Interpolate(solvedStrike)).ToArray(), TimeInterpolatorType); return(interpForSolvedStrike.Interpolate(maturity)); } }
private double GetDeltaStrikeForAbs(double fwd, double strike, double maturity) { var cp = strike < 0 ? OptionType.Put : OptionType.Call; Func <double, double> testFunc = (deltaK => { var interpForStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble, ExpiriesDouble.Select(e => GetVolForDeltaStrike(deltaK, e, fwd)).ToArray(), TimeInterpolatorType); var vol2 = interpForStrike.Interpolate(maturity); var absK = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, deltaK, 0, maturity, vol2); return(absK - strike); }); var solvedStrike = -Math.Solvers.Brent.BrentsMethodSolve(testFunc, -0.99999999999, -0.00000000001, 1e-8); if (solvedStrike == 0.00000000001 || solvedStrike == 0.99999999999) //out of bounds { var upperK = testFunc(-0.00000000001); var lowerK = testFunc(-0.99999999999); if (Abs(upperK - fwd) < Abs(lowerK - fwd)) { solvedStrike = 0.00000000001; } else { solvedStrike = 0.99999999999; } } return(solvedStrike); }
public static IInterpolator1D GenerateCDF2(this IVolSurface surface, int numSamples, DateTime expiry, double fwd, bool returnInverse = false, double strikeScale = 1.0, bool logStrikes = false) { var premInterp = GeneratePremiumInterpolator(surface, numSamples, expiry, fwd, OptionType.P); var t = surface.OriginDate.CalculateYearFraction(expiry, DayCountBasis.Act365F); var x = new double[numSamples]; var y = new double[numSamples]; var deltaKLow = 0.0000000001; var deltaKHi = 0.9999999999; var kStepD = (deltaKHi - deltaKLow) / numSamples; for (var i = 0; i < x.Length; i++) { var deltaKNew = -deltaKLow - i * kStepD; var newStrikeVol = surface.GetVolForDeltaStrike(-deltaKNew, t, fwd); var k = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, deltaKNew, 0, t, newStrikeVol); var digital = premInterp.FirstDerivative(k); y[i] = digital; x[i] = k * strikeScale; if (logStrikes) { x[i] = Log(x[i]); } } return(returnInverse ? InterpolatorFactory.GetInterpolator(y, x, Interpolator1DType.MonotoneCubicSpline) : InterpolatorFactory.GetInterpolator(x, y, Interpolator1DType.MonotoneCubicSpline)); }
public double GetVolForAbsoluteStrike(double strike, double maturity, double forward) { var key = $"{strike:f6}~{maturity:f3}~{forward:f6}"; if (_allowCaching && _absVolCache.TryGetValue(key, out var vol)) { return(vol); } if (StrikeType == StrikeType.Absolute) { var interpForStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble, _interpolators.Select(x => x.Interpolate(strike)).ToArray(), TimeInterpolatorType); vol = interpForStrike.Interpolate(maturity); } else { var fwd = forward; var cp = strike < 0 ? OptionType.Put : OptionType.Call; Func <double, double> testFunc = (deltaK => { var dkModified = FlatDeltaSmileInExtreme ? Min(1.0 - FlatDeltaPoint, Max(deltaK, FlatDeltaPoint)) : deltaK; var interpForStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble, _interpolators.Select(x => x.Interpolate(-dkModified)).ToArray(), TimeInterpolatorType); var vol2 = interpForStrike.Interpolate(maturity); var absK = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, deltaK, 0, maturity, vol2); return(absK - strike); }); var hiK = FlatDeltaSmileInExtreme ? 1.0 - FlatDeltaPoint : 0.999999999; var loK = FlatDeltaSmileInExtreme ? FlatDeltaPoint : 0.000000001; var solvedStrike = -Math.Solvers.Brent.BrentsMethodSolve(testFunc, -hiK, -loK, 1e-12); if (solvedStrike == loK || solvedStrike == hiK) //out of bounds { var upperK = testFunc(-loK); var lowerK = testFunc(-hiK); if (Abs(upperK - fwd) < Abs(lowerK - fwd)) { solvedStrike = loK; } else { solvedStrike = hiK; } } var interpForSolvedStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble, _interpolators.Select(x => x.Interpolate(solvedStrike)).ToArray(), TimeInterpolatorType); vol = interpForSolvedStrike.Interpolate(maturity); } if (_allowCaching) { _absVolCache[key] = vol; } return(vol); }
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 IInterpolator1D GenerateCDF(this IVolSurface surface, int numSamples, DateTime expiry, double fwd, bool returnInverse = false) { var deltaKLow = 0.0000001; var deltaKHi = 0.9999999; var kStepD = (deltaKHi - deltaKLow) / (numSamples + 3); var deltaKBump = deltaKLow / 10; var t = surface.OriginDate.CalculateYearFraction(expiry, DayCountBasis.Act365F); var x = new double[numSamples + 2]; var y = new double[numSamples + 2]; for (var i = 0; i < x.Length; i++) { var deltaKNew = deltaKLow + i * kStepD; var mStrike = deltaKNew - deltaKBump / 2; var pStrike = deltaKNew + deltaKBump / 2; var mStrikeVol = surface.GetVolForDeltaStrike(mStrike, t, fwd); var mk = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -mStrike, 0, t, mStrikeVol); var pStrikeVol = surface.GetVolForDeltaStrike(pStrike, t, fwd); var pk = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -pStrike, 0, t, pStrikeVol); if (i == 0) { x[0] = mk / 2.0; y[0] = 0; continue; } if (i == x.Length - 1) { x[i] = pk * 2; y[i] = 1; continue; } var dkAbs = (pk - mk); var pPut = BlackFunctions.BlackPV(fwd, pk, 0, t, pStrikeVol, OptionType.P); var mPut = BlackFunctions.BlackPV(fwd, mk, 0, t, mStrikeVol, OptionType.P); var digital = (pPut - mPut) / dkAbs; y[i] = digital; x[i] = (mk + pk) / 2.0; } return(returnInverse ? InterpolatorFactory.GetInterpolator(y, x, Interpolator1DType.LinearFlatExtrap) : InterpolatorFactory.GetInterpolator(x, y, Interpolator1DType.LinearFlatExtrap)); }
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 void DeltaStrikeMappingFacts() { var t = 1.0; var k = 100; var f = 100; var vol = 0.32; var rf = 0.0; var cp = OptionType.P; //black forward delta matches var delta = BlackFunctions.BlackDelta(f, k, rf, t, vol, cp); var absolute = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(f, delta, rf, t, vol); Assert.Equal(k, absolute, 10); }
public static IInterpolator1D GenerateCDF(this IInterpolator1D smile, int numSamples, double t, double fwd) { var deltaK = fwd * 0.0001; var atmVol = smile.Interpolate(fwd); var lowStrike = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.0001, 0, t, atmVol); var hiStrike = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.9999, 0, t, atmVol); 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] = k / 2.0; y[0] = 0; continue; } if (i == x.Length - 1) { x[i] = k * 2; y[i] = 1; continue; } var volLow = smile.Interpolate(k - deltaK / 2.0); var putLow = BlackFunctions.BlackPV(fwd, k - deltaK / 2.0, 0, t, volLow, OptionType.P); var volHi = smile.Interpolate(k + deltaK / 2.0); var putHi = BlackFunctions.BlackPV(fwd, k + deltaK / 2.0, 0, t, volHi, OptionType.P); var digital = (putHi - putLow) / deltaK; y[i] = digital; x[i] = k; k += kStep; } return(InterpolatorFactory.GetInterpolator(x, y, Interpolator1DType.Linear)); }
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 void SolveSmileMarket() { var valDate = new DateTime(2018, 07, 28); var expDate = valDate.AddDays(365); var tExp = (expDate - valDate).TotalDays / 365.0; var fwd = 1000; double[] strikes = { 0.25, 0.5, 0.75 }; var atmConstraint = new ATMStraddleConstraint { ATMVolType = AtmVolType.ZeroDeltaStraddle, MarketVol = 0.32 }; var smile25d = new RRBFConstraint { Delta = 0.25, FlyVol = 0.01, RisykVol = 0.02, WingQuoteType = WingQuoteType.Market }; var s = new AssetSmileSolver(); if (IsCoverageOnly) { s.Tollerance = 1; } var smile = s.Solve(atmConstraint, new[] { smile25d }, valDate, expDate, fwd, strikes, Interpolator1DType.Linear); if (!IsCoverageOnly) { Assert.Equal(atmConstraint.MarketVol, smile[1], 8); } var surface = new GridVolSurface(valDate, strikes, new[] { expDate }, new[] { smile }, StrikeType.ForwardDelta, Interpolator1DType.Linear, Interpolator1DType.Linear, DayCountBasis.Act365F); //reprice market RR structrure off smile, premium must match var marketVolC = atmConstraint.MarketVol + smile25d.FlyVol + 0.5 * smile25d.RisykVol; var marketVolP = atmConstraint.MarketVol + smile25d.FlyVol - 0.5 * smile25d.RisykVol; var marketKC25 = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, 0.25, 0, tExp, marketVolC); var marketKP25 = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.25, 0, tExp, marketVolP); var marketC25FV = BlackFunctions.BlackPV(fwd, marketKC25, 0, tExp, marketVolC, OptionType.C); var marketP25FV = BlackFunctions.BlackPV(fwd, marketKP25, 0, tExp, marketVolP, OptionType.P); var marketRR = marketC25FV - marketP25FV; var volC25d = surface.GetVolForAbsoluteStrike(marketKC25, expDate, fwd); var volP25d = surface.GetVolForAbsoluteStrike(marketKP25, expDate, fwd); var call25FV = BlackFunctions.BlackPV(fwd, marketKC25, 0, tExp, volC25d, OptionType.C); var put25FV = BlackFunctions.BlackPV(fwd, marketKP25, 0, tExp, volP25d, OptionType.P); var smileRR = call25FV - put25FV; if (!IsCoverageOnly) { Assert.Equal(marketRR, smileRR, 8); } //reprice market BF structrure off smile, premium must match var marketVolBF = atmConstraint.MarketVol + smile25d.FlyVol; var marketKBFC25 = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, 0.25, 0, tExp, marketVolBF); var marketKBFP25 = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.25, 0, tExp, marketVolBF); var marketBFC25FV = BlackFunctions.BlackPV(fwd, marketKBFC25, 0, tExp, marketVolBF, OptionType.C); var marketBFP25FV = BlackFunctions.BlackPV(fwd, marketKBFP25, 0, tExp, marketVolBF, OptionType.P); var marketBF = marketBFC25FV + marketBFP25FV; var volCBF25d = surface.GetVolForAbsoluteStrike(marketKBFC25, expDate, fwd); var volPBF25d = surface.GetVolForAbsoluteStrike(marketKBFP25, expDate, fwd); var callBF25FV = BlackFunctions.BlackPV(fwd, marketKBFC25, 0, tExp, volCBF25d, OptionType.C); var putBF25FV = BlackFunctions.BlackPV(fwd, marketKBFP25, 0, tExp, volPBF25d, OptionType.P); var smileBF = callBF25FV + putBF25FV; if (!IsCoverageOnly) { Assert.Equal(marketBF, smileBF, 8); } }
public static IInterpolator1D GenerateCompositeSmileB(this IVolSurface surface, IVolSurface fxSurface, int numSamples, DateTime expiry, double fwdAsset, double fwdFx, double correlation, bool strikesInDeltaSpace = false) { var t = surface.OriginDate.CalculateYearFraction(expiry, DayCountBasis.Act365F); var fxInv = new InverseFxSurface("fxInv", fxSurface as IATMVolSurface, null); var atmFx = fxSurface.GetVolForDeltaStrike(0.5, t, fwdFx); var atmA = surface.GetVolForDeltaStrike(0.5, t, fwdAsset); var compoFwd = fwdAsset * fwdFx; var atmCompo = Sqrt(atmFx * atmFx + atmA * atmA + 2.0 * correlation * atmA * atmFx); var lowK = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(compoFwd, -0.01, 0, t, atmCompo); var hiK = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(compoFwd, -0.99, 0, t, atmCompo); //var cdfInvFx = fxSurface.GenerateCDF2(numSamples * 10, expiry, fwdFx, true); //var cdfInvAsset = surface.GenerateCDF2(numSamples * 10, expiry, fwdAsset, true); //var yFx = new Func<double, double>(z => cdfInvFx.Interpolate(Statistics.NormSDist(z))); //var yAsset = new Func<double, double>(z => cdfInvAsset.Interpolate(Statistics.NormSDist(z))); var fxCDFCache = new Dictionary <double, double>(); var assetCDFCache = new Dictionary <double, double>(); var yFx = new Func <double, double>(z => { if (fxCDFCache.TryGetValue(z, out var K)) { return(K); } K = fxInv.InverseCDF(expiry, 1.0 / fwdFx, Statistics.NormSDist(z)); fxCDFCache.Add(z, K); return(K); }); var yAsset = new Func <double, double>(z => { if (assetCDFCache.TryGetValue(z, out var K)) { return(K); } K = surface.InverseCDF(expiry, fwdAsset, Statistics.NormSDist(z)); assetCDFCache.Add(z, K); return(K); }); //var fxCDFCache = new Dictionary<double, double>(); //var assetCDFCache = new Dictionary<double, double>(); //var putFx = fxInv.GeneratePremiumInterpolator(numSamples * 10, expiry, 1.0/fwdFx, OptionType.P); //var putAsset = surface.GeneratePremiumInterpolator(numSamples * 10, expiry, fwdAsset, OptionType.P); //var yFx = new Func<double, double>(z => //{ // if (fxCDFCache.TryGetValue(z, out var K)) return K; // K = InverseCDF(putFx, t, 1.0/fwdFx, Statistics.NormSDist(z)); // fxCDFCache.Add(z, K); // return K; //}); //var yAsset = new Func<double, double>(z => //{ // if (assetCDFCache.TryGetValue(z, out var K)) return K; // K = InverseCDF(putAsset, t, fwdAsset, Statistics.NormSDist(z)); // var kl = assetCDFCache.Keys.ToList(); // var closerIx = kl.BinarySearch(z); // var keyIx = ~closerIx; // if (closerIx < 0 && z < 0 && kl.Count > keyIx) // { // if (assetCDFCache[kl[keyIx]] < K) // K = assetCDFCache[kl[keyIx]]; // } // assetCDFCache.Add(z, K); // return K; //}); var payoff = new Func <double, double, double, double>((z1, z2, kQ) => Max(kQ * yFx(z2) - yAsset(z1), 0)); var integrand = new Func <double, double, double, double>((z1, z2, kQ) => payoff(z1, z2, kQ) * BivariateNormal.PDF(z1, z2, -correlation)); var kStep = (hiK - lowK) / numSamples; var ks = Enumerable.Range(0, numSamples).Select(kk => lowK + kk * kStep).ToArray(); var premiums = new double[ks.Length]; var vols = new double[ks.Length]; for (var i = 0; i < ks.Length; i++) { var ik = new Func <double, double, double>((z1, z2) => integrand(z1, z2, ks[i])); var pk = Integration.TwoDimensionalGaussLegendre(ik, -5, 5, -5, 5, 16); //var pk = Integration.TwoDimensionalSimpsons(ik, -5, 5, -5, 5, 100); pk *= fwdFx; var volK = BlackFunctions.BlackImpliedVol(compoFwd, ks[i], 0.0, t, pk, OptionType.P); vols[i] = volK; premiums[i] = pk; } if (strikesInDeltaSpace) { ks = ks.Select((ak, ix) => - BlackFunctions.BlackDelta(compoFwd, ak, 0.0, t, vols[ix], OptionType.P)).ToArray(); } return(InterpolatorFactory.GetInterpolator(ks, vols, Interpolator1DType.CubicSpline)); }
public static IInterpolator1D GenerateCompositeSmile(this IVolSurface surface, IVolSurface fxSurface, int numSamples, DateTime expiry, double fwdAsset, double fwdFx, double rho, bool strikesInDeltaSpace = false) { var t = surface.OriginDate.CalculateYearFraction(expiry, DayCountBasis.Act365F); var atmFx = fxSurface.GetVolForDeltaStrike(0.5, t, fwdFx); var atmA = surface.GetVolForDeltaStrike(0.5, t, fwdAsset); var compoFwd = fwdAsset / fwdFx; var atmCompo = Sqrt(atmFx * atmFx + atmA * atmA + 2.0 * rho * atmA * atmFx); var lowK = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(compoFwd, -0.01, 0, t, atmCompo); var hiK = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(compoFwd, -0.99, 0, t, atmCompo); var nuA = Sqrt(t) * atmA; var nuFx = Sqrt(t) * atmFx; var cdfFx = new Func <double, double>(k => fxSurface.CDF(expiry, fwdFx, Exp(k))); var cdfA = new Func <double, double>(k => surface.CDF(expiry, fwdAsset, Exp(k))); var fxCDFCache = new Dictionary <double, double>(); var assetCDFCache = new Dictionary <double, double>(); var yFx = new Func <double, double>(z => { if (fxCDFCache.TryGetValue(z, out var K)) { return(K); } K = Log(fxSurface.InverseCDF(expiry, fwdFx, Statistics.NormSDist(z))); fxCDFCache.Add(z, K); return(K); }); var yA = new Func <double, double>(z => { if (assetCDFCache.TryGetValue(z, out var K)) { return(K); } K = Log(surface.InverseCDF(expiry, fwdAsset, Statistics.NormSDist(z))); assetCDFCache.Add(z, K); return(K); }); //var zfxS = new Func<double, double, double>((zA, K) => Statistics.NormInv(Max(1e-18, Min(1.0 - 1e-18, cdfFx(yA(zA) - Log(K)))))); //var zAs = new Func<double, double, double>((zFx, K) => Statistics.NormInv(Max(1e-18, Min(1.0 - 1e-18, cdfA(yFx(zFx) + Log(K)))))); var zfxS = new Func <double, double, double>((zA, K) => Statistics.NormInv(cdfFx(yA(zA) - Log(K)))); var zAs = new Func <double, double, double>((zFx, K) => Statistics.NormInv(cdfA(yFx(zFx) + Log(K)))); var d = -1.0; var p2 = 1.0 / Sqrt(2.0 * PI); //var I1 = new Func<double, double, double>((zA, K) => //p2*Exp(yA(zA) - (nuA * zA - nuA * nuA / 2)) * Statistics.NormSDist(d * (zfxS(zA, K) - rho * zA) / Sqrt(1 - rho * rho)) * Exp(-(zA - nuA) * (zA - nuA) / 2.0) // ); //var I2 = new Func<double, double, double>((zFx, K) => //p2*Exp(yFx(zFx) - (nuFx * zFx - nuFx * nuFx / 2)) * Statistics.NormSDist(-d * (zAs(zFx, K) - rho * zFx) / Sqrt(1 - rho * rho)) * Exp(-(zFx - nuFx) * (zFx - nuFx) / 2.0) // ); var I1 = new Func <double, double, double>((zA, K) => p2 * Exp(yA(zA)) * Statistics.NormSDist(d * (zfxS(zA, K) - rho * zA) / Sqrt(1 - rho * rho)) * Exp(-(zA * zA) / 2.0) ); var I2 = new Func <double, double, double>((zFx, K) => p2 * Exp(yFx(zFx)) * Statistics.NormSDist(-d * (zAs(zFx, K) - rho * zFx) / Sqrt(1 - rho * rho)) * Exp(-(zFx * zFx) / 2.0) ); var kStep = (hiK - lowK) / numSamples; var ks = Enumerable.Range(0, numSamples).Select(kk => lowK + kk * kStep).ToArray(); var premiums = new double[ks.Length]; var vols = new double[ks.Length]; for (var i = 0; i < ks.Length; i++) { var I1k = new Func <double, double>(z => I1(z, ks[i])); var I2k = new Func <double, double>(z => I2(z, ks[i])); //var i1 = Integration.GaussLegendre(I1k, -5, 5, 16); //var i2 = Integration.GaussLegendre(I2k, -5, 5, 16); var i1 = Integration.SimpsonsRule(I1k, -5, 5, numSamples); var i2 = Integration.SimpsonsRule(I2k, -5, 5, numSamples); var pk = d * (i1 - ks[i] * i2); pk /= fwdFx; var volK = BlackFunctions.BlackImpliedVol(compoFwd, ks[i], 0.0, t, pk, OptionType.P); vols[i] = volK; premiums[i] = pk; } if (strikesInDeltaSpace) { ks = ks.Select((ak, ix) => - BlackFunctions.BlackDelta(compoFwd, ak, 0.0, t, vols[ix], OptionType.P)).ToArray(); } return(InterpolatorFactory.GetInterpolator(ks, vols, Interpolator1DType.CubicSpline)); }