Beispiel #1
0
        public double GetVolForDeltaStrike(double deltaStrike, double maturity, double forward)
        {
            if (StrikeType == StrikeType.ForwardDelta)
            {
                var interpForStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble,
                                                                          _interpolators.Select(x => x.Interpolate(deltaStrike)).ToArray(),
                                                                          TimeInterpolatorType);
                return(interpForStrike.Interpolate(maturity));
            }
            else
            {
                var fwd = forward;
                var cp  = deltaStrike < 0 ? OptionType.Put : OptionType.Call;
                Func <double, double> testFunc = (absK =>
                {
                    var interpForStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble,
                                                                              _interpolators.Select(x => x.Interpolate(absK)).ToArray(),
                                                                              TimeInterpolatorType);
                    var vol = interpForStrike.Interpolate(maturity);
                    var deltaK = BlackFunctions.BlackDelta(fwd, absK, 0, maturity, vol, cp);
                    return(deltaK - System.Math.Abs(deltaStrike));
                });

                var solvedStrike          = Qwack.Math.Solvers.Brent.BrentsMethodSolve(testFunc, 0.000000001, 10 * fwd, 1e-8);
                var interpForSolvedStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble,
                                                                                _interpolators.Select(x => x.Interpolate(solvedStrike)).ToArray(),
                                                                                TimeInterpolatorType);
                return(interpForSolvedStrike.Interpolate(maturity));
            }
        }
Beispiel #2
0
        public static double Delta(double forward, double knownAverage, double sigma, double K, double tAvgStart, double tExpiry, double riskFree, OptionType callPut)
        {
            var tau = Max(0, tAvgStart);
            var M   = 2 * (Exp(sigma * sigma * tExpiry) - Exp(sigma * sigma * tau) * (1 + sigma * sigma * (tExpiry - tau)));

            M /= Pow(sigma, 4.0) * (tExpiry - tau) * (tExpiry - tau);

            var sigma_a = Sqrt(Log(M) / tExpiry);

            K = AsianUtils.AdjustedStrike(K, knownAverage, tExpiry, tAvgStart);

            if (K <= 0)
            {
                if (callPut == OptionType.P)
                {
                    return(0);
                }
                var t2     = tExpiry - tAvgStart;
                var expAvg = knownAverage * (t2 - tExpiry) / t2 + forward * tExpiry / t2;
                var df     = Exp(-riskFree * tExpiry);
                return(df * (expAvg - K));
            }

            var delta = BlackFunctions.BlackDelta(forward, K, riskFree, tExpiry, sigma_a, callPut);

            if (tAvgStart < 0)
            {
                delta *= tExpiry / (tExpiry - tAvgStart);
            }
            return(delta);
        }
Beispiel #3
0
        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));
        }
Beispiel #4
0
        public void GridVolSurfaceAbsolute()
        {
            //flat surface
            var origin     = new DateTime(2017, 02, 07);
            var strikes    = new double[] { 1, 2 };
            var maturities = new DateTime[] { new DateTime(2017, 04, 06), new DateTime(2017, 06, 07) };
            var vols       = new double[][]
            {
                new double[] { 0.32, 0.32 },
                new double[] { 0.32, 0.32 }
            };
            var surface = new Qwack.Options.VolSurfaces.GridVolSurface(
                origin, strikes, maturities, vols,
                Core.Basic.StrikeType.Absolute,
                Math.Interpolation.Interpolator1DType.Linear,
                Math.Interpolation.Interpolator1DType.Linear,
                Dates.DayCountBasis.Act_365F);

            var fwd = 1.5;
            Func <double, double> fwdCurve = (t => { return(fwd); });

            Assert.Equal(vols[0][0], surface.GetVolForAbsoluteStrike(999, origin.AddDays(33), fwd), 12);
            Assert.Equal(vols[0][0], surface.GetVolForDeltaStrike(-0.3, origin.AddDays(303), fwd), 12);
            Assert.Equal(vols[0][0], surface.GetVolForAbsoluteStrike(4, 0.777, fwd), 12);
            Assert.Equal(vols[0][0], surface.GetVolForDeltaStrike(0.9, 0.123, fwd), 12);

            //with some shape
            vols = new double[][]
            {
                new double[] { 0.16, 0.32 },
                new double[] { 0.32, 0.64 }
            };

            surface = new Qwack.Options.VolSurfaces.GridVolSurface(
                origin, strikes, maturities, vols,
                Core.Basic.StrikeType.Absolute,
                Math.Interpolation.Interpolator1DType.Linear,
                Math.Interpolation.Interpolator1DType.Linear,
                Dates.DayCountBasis.Act_365F);

            Assert.Equal(vols[0].Average(), surface.GetVolForAbsoluteStrike(1.5, maturities[0], fwd), 12);
            var midPoint = maturities[0].AddDays(((maturities[1] - maturities[0]).TotalDays / 2.0));

            Assert.Equal(vols.Select(x => x.Average()).Average(), surface.GetVolForAbsoluteStrike(1.5, midPoint, fwd), 12);

            //test delta-space function
            var vol          = surface.GetVolForAbsoluteStrike(1.75, maturities[0].AddDays(15), fwd);
            var deltaK       = BlackFunctions.BlackDelta(1.5, 1.75, 0, surface.ExpiriesDouble[0] + 15.0 / 365.0, vol, OptionType.C);
            var volForDeltaK = surface.GetVolForDeltaStrike(deltaK, maturities[0].AddDays(15), fwd);

            Assert.Equal(vol, volForDeltaK, 8);
        }
Beispiel #5
0
        public void DeltaGammaFacts()
        {
            var t   = 1.0;
            var k   = 100;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.05;
            var cp  = OptionType.P;

            //delta closely matches numerical estimate
            var PV1      = BlackFunctions.BlackPV(f + 0.000005, k, rf, t, vol, cp);
            var PV2      = BlackFunctions.BlackPV(f - 0.000005, k, rf, t, vol, cp);
            var deltaEst = (PV1 - PV2) / 0.00001;
            var delta    = BlackFunctions.BlackDelta(f, k, rf, t, vol, cp);

            Assert.Equal(deltaEst, delta, 6);

            //all else the same, more time for OTM option == more delta
            k = 150;
            var deltaNear = BlackFunctions.BlackDelta(f, k, rf, t, vol, cp);
            var deltaFar  = BlackFunctions.BlackDelta(f, k, rf, t * 2, vol, cp);

            Assert.True(deltaFar > deltaNear);

            //put-call parity
            var deltaCall = BlackFunctions.BlackDelta(f, k, rf, t, vol, OptionType.C);
            var deltaPut  = BlackFunctions.BlackDelta(f, k, rf, t, vol, OptionType.P);

            var syntheticFwdDelta = deltaCall - deltaPut;

            Assert.Equal(System.Math.Exp(-rf * t), syntheticFwdDelta, 10);

            //gamma closely matches numerical estimate
            var delta1   = BlackFunctions.BlackDelta(f + 0.000005, k, rf, t, vol, cp);
            var delta2   = BlackFunctions.BlackDelta(f - 0.000005, k, rf, t, vol, cp);
            var gammaEst = (delta1 - delta2) / 0.00001;
            var gamma    = BlackFunctions.BlackGamma(f, k, rf, t, vol);

            Assert.Equal(gammaEst, gamma, 6);

            //cases for zero delta / gamma
            delta = BlackFunctions.BlackDelta(f, 0, rf, t, vol, OptionType.P);
            Assert.Equal(0, delta, 8);
            delta = BlackFunctions.BlackDelta(f, 1e6, rf, t, vol, OptionType.C);
            Assert.Equal(0, delta, 8);
            gamma = BlackFunctions.BlackGamma(f, 0, rf, t, vol);
            Assert.Equal(0, gamma, 8);
            gamma = BlackFunctions.BlackGamma(f, 1e6, rf, t, vol);
            Assert.Equal(0, gamma, 8);
        }
Beispiel #6
0
        public double GetVolForDeltaStrike(double deltaStrike, double maturity, double forward)
        {
            var cp = deltaStrike < 0 ? OptionType.Put : OptionType.Call;
            Func <double, double> testFunc = (absK =>
            {
                var volTest = GetVolForAbsoluteStrike(absK, maturity, forward);
                var deltaK = BlackFunctions.BlackDelta(forward, absK, 0, maturity, volTest, cp);
                return(deltaK - System.Math.Abs(deltaStrike));
            });

            var solvedStrike = Math.Solvers.Brent.BrentsMethodSolve(testFunc, 0.000000001, 10 * forward, 1e-8);

            return(GetVolForAbsoluteStrike(solvedStrike, maturity, forward));
        }
Beispiel #7
0
        private double GetAbsStrikeForDelta(double fwd, double deltaStrike, double maturity)
        {
            var cp = deltaStrike < 0 ? OptionType.Put : OptionType.Call;
            Func <double, double> testFunc = (absK =>
            {
                var interpForStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble, ExpiriesDouble.Select(e => GetVolForAbsoluteStrike(absK, e, fwd)).ToArray(), TimeInterpolatorType);
                var vol2 = interpForStrike.Interpolate(maturity);
                var deltaK = BlackFunctions.BlackDelta(fwd, absK, 0, maturity, vol2, cp);
                return(deltaK - deltaStrike);
            });

            var solvedStrike = Math.Solvers.Brent.BrentsMethodSolve(testFunc, 0.000000001, 50 * fwd, 1e-8);

            return(solvedStrike);
        }
Beispiel #8
0
        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);
        }
Beispiel #9
0
        public double GetVolForDeltaStrike(double deltaStrike, double maturity, double forward)
        {
            if (deltaStrike > 1.0 || deltaStrike < -1.0)
            {
                throw new ArgumentOutOfRangeException($"Delta strike must be in range -1.0 < x < 1.0 - value was {deltaStrike}");
            }

            var key = $"{deltaStrike:f6}~{maturity:f3}~{forward:f6}";

            if (_allowCaching && _deltaVolCache.TryGetValue(key, out var vol))
            {
                return(vol);
            }

            if (StrikeType == StrikeType.ForwardDelta)
            {
                var interpForStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble,
                                                                          _interpolators.Select(x => x.Interpolate(deltaStrike)).ToArray(),
                                                                          TimeInterpolatorType);
                vol = interpForStrike.Interpolate(maturity);
            }
            else
            {
                var fwd = forward;
                var cp  = deltaStrike < 0 ? OptionType.Put : OptionType.Call;
                Func <double, double> testFunc = (absK =>
                {
                    var interpForStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble,
                                                                              _interpolators.Select(x => x.Interpolate(absK)).ToArray(),
                                                                              TimeInterpolatorType);
                    var vol2 = interpForStrike.Interpolate(maturity);
                    var deltaK = BlackFunctions.BlackDelta(fwd, absK, 0, maturity, vol2, cp);
                    return(deltaK - Abs(deltaStrike));
                });

                var solvedStrike          = Math.Solvers.Brent.BrentsMethodSolve(testFunc, 0.000000001, 10 * fwd, 1e-8);
                var interpForSolvedStrike = InterpolatorFactory.GetInterpolator(ExpiriesDouble,
                                                                                _interpolators.Select(x => x.Interpolate(solvedStrike)).ToArray(),
                                                                                TimeInterpolatorType);
                vol = interpForSolvedStrike.Interpolate(maturity);
            }

            if (_allowCaching)
            {
                _deltaVolCache[key] = vol;
            }
            return(vol);
        }
Beispiel #10
0
        public static double GetVolForDeltaStrike(double deltaStrike, double maturity, double forward, Func <double, double> GetVolForAbsoluteStrike)
        {
            var fwd = forward;
            var cp  = OptionType.Put;

            Func <double, double> testFunc = (absK =>
            {
                var vol = GetVolForAbsoluteStrike(absK);
                var deltaK = System.Math.Abs(BlackFunctions.BlackDelta(fwd, absK, 0, maturity, vol, cp));
                return(deltaK - System.Math.Abs(deltaStrike));
            });

            var solvedStrike = Math.Solvers.Brent.BrentsMethodSolve(testFunc, fwd / 10, 50 * fwd, 1e-8);

            return(GetVolForAbsoluteStrike(solvedStrike));
        }
Beispiel #11
0
        public static Dictionary <DateTime, IInterpolator1D> ToDeltaSmiles(this List <ListedOptionSettlementRecord> optionSettlements, Dictionary <string, double> futuresPrices, SmileOrderOfPrecedence orderOfP = SmileOrderOfPrecedence.UseOTM)
        {
            var output = new Dictionary <DateTime, IInterpolator1D>();

            var byExpiry = optionSettlements.GroupBy(r => r.ExpiryDate).OrderBy(o => o.Key);

            foreach (var expGroup in byExpiry)
            {
                var expiry = expGroup.Key;
                if (expGroup.Select(x => x.UnderlyingFuturesCode).Distinct().Count() != 1)
                {
                    throw new Exception($"Inconsistent underlying contracts for expiry {expiry}");
                }

                if (expGroup.Select(x => x.ValDate).Distinct().Count() != 1)
                {
                    throw new Exception($"Inconsistent value dates for expiry {expiry}");
                }

                var t = expGroup.First().ValDate.CalculateYearFraction(expiry, DayCountBasis.ACT365F);


                var futCode = expGroup.First().UnderlyingFuturesCode;
                if (!futuresPrices.TryGetValue(futCode, out var fut))
                {
                    throw new Exception($"No future price found for contract {futCode}");
                }

                var filtered = new List <SmilePoint>();
                var byStrike = expGroup.GroupBy(e => e.Strike).OrderBy(o => o.Key);
                foreach (var strikeGrp in byStrike)
                {
                    var k = strikeGrp.Key;
                    if (strikeGrp.Count() > 2)
                    {
                        throw new Exception($"Did not expect more than two options at strike {k}");
                    }
                    switch (orderOfP)
                    {
                    case SmileOrderOfPrecedence.Average:
                    {
                        var v = strikeGrp.Average(s => s.ImpliedVol);
                        filtered.Add(
                            new SmilePoint
                            {
                                Strike     = -BlackFunctions.BlackDelta(fut, k, 0.0, t, v, OptionType.P),
                                ImpliedVol = v
                            });
                        break;
                    }

                    case SmileOrderOfPrecedence.UseOTM:
                    {
                        ListedOptionSettlementRecord r;
                        if (fut > k)
                        {
                            r = strikeGrp.Where(sg => sg.CallPut == OptionType.P).SingleOrDefault();
                        }
                        else
                        {
                            r = strikeGrp.Where(sg => sg.CallPut == OptionType.C).SingleOrDefault();
                        }

                        if (r != null && r.ImpliedVol > 1e-8)
                        {
                            filtered.Add(
                                new SmilePoint
                                {
                                    Strike     = -BlackFunctions.BlackDelta(fut, k, 0.0, t, r.ImpliedVol, OptionType.P),
                                    ImpliedVol = r.ImpliedVol
                                });
                        }
                        break;
                    }
                    }
                }

                //now we have filtered to a single vol per-strike...
                if (filtered.Any())
                {
                    output[expiry] = InterpolatorFactory.GetInterpolator(filtered.Select(f => f.Strike).ToArray(), filtered.Select(f => f.ImpliedVol).ToArray(), Interpolator1DType.CubicSpline);
                }
            }
            return(output);
        }
Beispiel #12
0
        public static double Delta(double forward, double knownAverage, double sigma, double K, DateTime evalDate, DateTime avgStartDate, DateTime avgEndDate, double riskFree, OptionType callPut, Calendar fixingCalendar)
        {
            //Build Vector of Observation Dates
            var resetDates = avgStartDate.BusinessDaysInPeriod(avgEndDate, fixingCalendar);
            var RT         = resetDates.Count;

            if (avgEndDate == evalDate)
            {
                if (callPut == OptionType.C)
                {
                    return(knownAverage > K ? 1.0 / RT : 0);
                }
                else
                {
                    return(knownAverage < K ? -1.0 / RT : 0);
                }
            }
            else if (avgEndDate < evalDate)
            {
                return(0);
            }
            else if (avgStartDate == avgEndDate)
            {
                var t = (avgEndDate - evalDate).TotalDays / 365.0;
                return(BlackFunctions.BlackDelta(forward, K, riskFree, t, sigma, callPut));
            }


            //Initialise Variables
            double E1 = 0.0, E2 = 0.0, E3 = 0.0, E4 = 0.0, E5 = 0.0;
            double DeltaTPrime = 0.0, DeltaT = 0.0;

            var FBar = forward;
            var tvs  = resetDates.Count(x => x < evalDate);

            if (tvs > 0)
            {
                DeltaTPrime = (resetDates[tvs + 1] - resetDates[tvs]).TotalDays / 365.0;
            }
            else
            {
                DeltaTPrime = ((resetDates[0] - evalDate).TotalDays) / 365.0;
            }

            if (tvs + 2 >= RT)
            {
                DeltaT = DeltaTPrime;
            }
            else
            {
                DeltaT = (avgEndDate - resetDates[tvs + 1]).TotalDays / 365.0 / (RT - tvs);
            }

            var Ak = knownAverage * tvs / RT;

            E5 = 2 * Ak * FBar * (RT - tvs) / RT + (Ak * Ak);

            if (tvs < RT)
            {
                E4  = (1 + Exp(sigma * sigma * DeltaT)) * (Exp((RT - tvs) * sigma * sigma * DeltaT) - 1);
                E4 += 2 * (RT - tvs) * (1 - Exp(sigma * sigma * DeltaT));
                E4 /= (Pow(Exp(sigma * sigma * DeltaT) - 1, 2));
            }
            else
            {
                E4 = 1;
            }


            E3 = Pow(FBar / RT, 2) * Exp(sigma * sigma * DeltaTPrime);
            E2 = E3 * E4 + E5;
            E1 = FBar * (RT - tvs) / RT + Ak;

            var EA = Ak + FBar * (RT - tvs) / RT;

            var b  = Log(E2) - 2 * Log(E1);
            var d1 = (Log(EA / K) + 0.5 * b) / Sqrt(b);

            var df = Exp(-riskFree * (avgEndDate - evalDate).TotalDays / 365.0);

            return(df * ((callPut == OptionType.Put) ? Math.Statistics.NormSDist(d1) - 1 : Math.Statistics.NormSDist(d1)));
        }
Beispiel #13
0
 public void BlackDelta_Facts()
 {
     Assert.Equal("Could not parse call or put flag - blah", BlackFunctions.BlackDelta(1.0, 1.0, 1, 1, 1, "blah"));
     Assert.Equal(0.0, BlackFunctions.BlackDelta(0.1, 1.0, 0.5, 0.0, 0.0, "C"));
 }
Beispiel #14
0
        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));
        }
Beispiel #15
0
        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));
        }