Beispiel #1
0
        public void ImpliedVolFacts()
        {
            var t   = 1.0;
            var k   = 120;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.05;
            var cp  = OptionType.P;

            var PV         = BlackFunctions.BlackPV(f, k, rf, t, vol, cp);
            var impliedVol = BlackFunctions.BlackImpliedVol(f, k, rf, t, PV, cp);

            Assert.Equal(vol, impliedVol, 10);
        }
Beispiel #2
0
        public void CanRunPV()
        {
            var sut         = GetSut();
            var zar         = TestProviderHelper.CurrencyProvider["ZAR"];
            var pvCube      = sut.PV(zar);
            var expiryDates = sut.Portfolio.Instruments
                              .Select(x => x as AsianOption)
                              .OrderBy(x => x.AverageEndDate)
                              .Select(x => x.AverageEndDate);
            var expiryTimes  = expiryDates.Select(d => sut.OriginDate.CalculateYearFraction(d, DayCountBasis.Act365F)).ToArray();
            var pvs          = pvCube.GetAllRows().Select(x => x.Value).ToArray();
            var vols         = pvs.Select((x, ix) => BlackFunctions.BlackImpliedVol(1400, 1400, 0.0, expiryTimes[ix], x, OptionType.C)).ToArray();
            var volsCl       = expiryDates.Select(d => sut.VanillaModel.GetVolSurface("CL").GetVolForDeltaStrike(0.5, d, 100)).ToArray();
            var volsFx       = expiryDates.Select(d => sut.VanillaModel.GetVolSurface("USD/ZAR").GetVolForDeltaStrike(0.5, d, 100)).ToArray();
            var expectedVols = volsCl.Select((x, ix) => System.Math.Sqrt(x * x + volsFx[ix] * volsFx[ix] + 2 * _correls[ix + 1] * volsFx[ix] * x)).ToArray();

            for (var i = 0; i < expiryTimes.Length; i++)
            {
                Assert.Equal(expectedVols[i], vols[i], 1);
            }
        }
Beispiel #3
0
        public static void ImplyVols(List <ListedOptionSettlementRecord> optionSettlements, Dictionary <string, double> futuresPrices, IIrCurve discountCurve = null)
        {
            var o = new Dictionary <DateTime, IInterpolator1D>();

            foreach (var s in optionSettlements)
            {
                if (!futuresPrices.TryGetValue(s.UnderlyingFuturesCode, out var fut))
                {
                    throw new Exception($"No future price found for contract {s}");
                }

                var t = s.ValDate.CalculateYearFraction(s.ExpiryDate, DayCountBasis.ACT365F);
                var r = 0.0;

                if (s.MarginType == OptionMarginingType.Regular)
                {
                    if (discountCurve == null)
                    {
                        throw new Exception("To strip vols from options with regular margining, a discount curve must be supplied");
                    }
                    r = discountCurve.GetRate(s.ExpiryDate);
                }

                if (s.ExerciseType == OptionExerciseType.European || s.MarginType == OptionMarginingType.FuturesStyle)
                {
                    s.ImpliedVol = BlackFunctions.BlackImpliedVol(fut, s.Strike, r, t, s.PV, s.CallPut);
                }
                else if (s.ExerciseType == OptionExerciseType.American)
                {
                    s.ImpliedVol = BinomialTree.AmericanFuturesOptionImpliedVol(fut, s.Strike, r, t, s.PV, s.CallPut);
                }
                else
                {
                    throw new Exception("Unable to handle option in stripper");
                }
            }
        }
        public void CompositeSmimleFacts_LocalVol()
        {
            var origin        = new DateTime(2017, 02, 07);
            var expiry        = origin.AddMonths(2);
            var tExp          = origin.CalculateYearFraction(expiry, DayCountBasis.Act365F);
            var fwdCurveAsset = new Func <double, double>(t => { return(100); });
            var fwdCurveFx    = new Func <double, double>(t => { return(15); });
            var volAsset      = 0.32;
            var volFx         = 0.16;
            var correl        = 0.25;

            var surfaceAsset = new RiskyFlySurface(origin, new[] { volAsset }, new[] { expiry }, new[] { 0.25, 0.1 }, new[] { new[] { 0.02, 0.03 } }, new[] { new[] { 0.005, 0.007 } }, new[] { 100.0 }, WingQuoteType.Arithmatic, AtmVolType.ZeroDeltaStraddle, Interpolator1DType.GaussianKernel, Interpolator1DType.Linear)
            {
                FlatDeltaSmileInExtreme = true
            };
            var surfaceFx = new RiskyFlySurface(origin, new[] { volFx }, new[] { expiry }, new[] { 0.25, 0.1 }, new[] { new[] { 0.015, 0.025 } }, new[] { new[] { 0.005, 0.007 } }, new[] { 0.1 }, WingQuoteType.Arithmatic, AtmVolType.ZeroDeltaStraddle, Interpolator1DType.GaussianKernel, Interpolator1DType.Linear)
            {
                FlatDeltaSmileInExtreme = true
            };



            //var surfaceAsset = new SabrVolSurface(origin, new[] { volAsset }, new[] { expiry }, new[] { 0.25, 0.1 }, new[] { new[] { 0.02, 0.03 } }, new[] { new[] { 0.005, 0.007 } }, new[] { 100.0 }, WingQuoteType.Arithmatic, AtmVolType.ZeroDeltaStraddle, Interpolator1DType.Linear);
            //var surfaceFx = new SabrVolSurface(origin, new[] { volFx }, new[] { expiry }, new[] { 0.25, 0.1 }, new[] { new[] { 0.015, 0.025 } }, new[] { new[] { 0.005, 0.007 } }, new[] { 0.1 }, WingQuoteType.Arithmatic, AtmVolType.ZeroDeltaStraddle, Interpolator1DType.Linear);

            //var surfaceAsset = new SVIVolSurface(origin, new[] { volAsset }, new[] { expiry }, new[] { 0.25, 0.1 }, new[] { new[] { 0.02, 0.03 } }, new[] { new[] { 0.005, 0.007 } }, new[] { 100.0 }, WingQuoteType.Arithmatic, AtmVolType.ZeroDeltaStraddle, Interpolator1DType.Linear);
            //var surfaceFx = new SVIVolSurface(origin, new[] { volFx }, new[] { expiry }, new[] { 0.25, 0.1 }, new[] { new[] { 0.015, 0.025 } }, new[] { new[] { 0.005, 0.007 } }, new[] { 0.1 }, WingQuoteType.Arithmatic, AtmVolType.ZeroDeltaStraddle, Interpolator1DType.Linear);

            var invFx        = new InverseFxSurface("inv", surfaceFx, TestProviderHelper.CurrencyProvider);
            var surfaceCompo = surfaceAsset.GenerateCompositeSmile(invFx, 200, expiry, 100, 1.0 / 15, correl);

            //setup MC
            using var engine = new PathEngine(2.IntPow(IsCoverageOnly?5:15));
            engine.AddPathProcess(
                new Qwack.Random.MersenneTwister.MersenneTwister64
            {
                UseNormalInverse = true
            });


            var correlMatrix = new double[][]
            {
                new double[] { 1.0, correl },
                new double[] { correl, 1.0 },
            };

            engine.AddPathProcess(new Cholesky(correlMatrix));


            var asset1 = new TurboSkewSingleAsset
                         (
                startDate: origin,
                expiryDate: expiry,
                volSurface: surfaceAsset,
                forwardCurve: fwdCurveAsset,
                nTimeSteps: 1,
                name: "Asset"
                         );

            var asset2 = new TurboSkewSingleAsset
                         (
                startDate: origin,
                expiryDate: expiry,
                volSurface: surfaceFx,
                forwardCurve: fwdCurveFx,
                nTimeSteps: 1,
                name: "USD/ZAR"
                         );

            engine.AddPathProcess(asset1);
            engine.AddPathProcess(asset2);

            var strike  = 1500;
            var product = new EuropeanOption
            {
                AssetId          = "Asset",
                CallPut          = OptionType.C,
                ExpiryDate       = expiry,
                PaymentCurrency  = TestProviderHelper.CurrencyProvider["ZAR"],
                PaymentDate      = expiry,
                Notional         = 1.0,
                SpotLag          = new Frequency("0b"),
                Strike           = strike,
                FxConversionType = FxConversionType.ConvertThenAverage
            };
            var productAsset = new EuropeanOption
            {
                AssetId          = "Asset",
                CallPut          = OptionType.C,
                ExpiryDate       = expiry,
                PaymentCurrency  = TestProviderHelper.CurrencyProvider["USD"],
                PaymentDate      = expiry,
                Notional         = 1.0,
                SpotLag          = new Frequency("0b"),
                Strike           = 100,
                FxConversionType = FxConversionType.None
            };
            var productFx = new EuropeanOption
            {
                AssetId          = "USD/ZAR",
                CallPut          = OptionType.C,
                ExpiryDate       = expiry,
                PaymentCurrency  = TestProviderHelper.CurrencyProvider["ZAR"],
                PaymentDate      = expiry,
                Notional         = 1.0,
                SpotLag          = new Frequency("0b"),
                Strike           = 0.1,
                FxConversionType = FxConversionType.None
            };
            var pathProduct      = new AssetPathPayoff(product, TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider, TestProviderHelper.CurrencyProvider["ZAR"]);
            var pathProductAsset = new AssetPathPayoff(productAsset, TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider, TestProviderHelper.CurrencyProvider["ZAR"]);
            var pathProductFx    = new AssetPathPayoff(productFx, TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider, TestProviderHelper.CurrencyProvider["ZAR"]);

            engine.AddPathProcess(pathProduct);
            engine.AddPathProcess(pathProductAsset);
            engine.AddPathProcess(pathProductFx);

            engine.SetupFeatures();
            engine.RunProcess();
            var q              = pathProduct.ResultsByPath;
            var qq             = q.Average();
            var productIv      = BlackFunctions.BlackImpliedVol(1500, strike, 0.0, tExp, pathProduct.AverageResult, OptionType.C);
            var productAssetIv = BlackFunctions.BlackImpliedVol(100, 100, 0.0, tExp, pathProductAsset.AverageResult, OptionType.C);
            var productFxIv    = BlackFunctions.BlackImpliedVol(10, 10, 0.0, tExp, pathProductFx.AverageResult, OptionType.C);

            Assert.True(Abs(productIv - surfaceCompo.Interpolate(strike)) < 0.01);
        }
Beispiel #5
0
 public void BlackImpliedVol_Facts()
 {
     Assert.Equal("Could not parse call or put flag - blah", BlackFunctions.BlackImpliedVol(1.0, 1.0, 1, 1, 1, "blah"));
     Assert.Equal(1e-9, BlackFunctions.BlackImpliedVol(0.1, 1.0, 0.5, 0.0, 0.0, "C"));
 }
Beispiel #6
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 #7
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));
        }