public void LVMC_PathsGenerated() { var origin = DateTime.Now.Date; using var engine = new PathEngine(2.IntPow(IsCoverageOnly ? 6 : 12)) { Parallelize = false }; engine.AddPathProcess(new Random.MersenneTwister.MersenneTwister64() { UseNormalInverse = true, UseAnthithetic = true }); var tenorsStr = new[] { "1m", "2m", "3m", "6m" }; var tenors = tenorsStr.Select(x => new Frequency(x)); var expiries = tenors.Select(t => origin.AddPeriod(RollType.F, new Calendar(), t)).ToArray(); var deltaKs = new[] { 0.1, 0.25, 0.5, 0.75, 0.9 }; var smileVols = new[] { 0.32, 0.3, 0.29, 0.3, 0.32 }; var vols = Enumerable.Repeat(smileVols, expiries.Length).ToArray(); var tExp = (origin.AddMonths(6) - origin).TotalDays / 365.0; var volSurface = new GridVolSurface(origin, deltaKs, expiries, vols, StrikeType.ForwardDelta, Interpolator1DType.GaussianKernel, Interpolator1DType.LinearInVariance, DayCountBasis.Act365F); var fwdCurve = new Func <double, double>(t => { return(900 + 100 * t / tExp); }); var asset = new LVSingleAsset ( startDate: origin, expiryDate: origin.AddMonths(6), volSurface: volSurface, forwardCurve: fwdCurve, nTimeSteps: IsCoverageOnly ? 3 : 100, name: "TestAsset" ); engine.AddPathProcess(asset); var payoff = new EuropeanPut("TestAsset", 900, origin.AddMonths(6)); var payoff2 = new EuropeanCall("TestAsset", 0, origin.AddMonths(6)); engine.AddPathProcess(payoff); engine.AddPathProcess(payoff2); engine.SetupFeatures(); engine.RunProcess(); var pv = payoff.AverageResult; var blackVol = volSurface.GetVolForAbsoluteStrike(900, origin.AddMonths(6), fwdCurve(tExp)); var blackPv = BlackFunctions.BlackPV(fwdCurve(tExp), 900, 0, tExp, blackVol, OptionType.P); if (!IsCoverageOnly) { Assert.True(System.Math.Abs(blackPv / pv - 1.0) < 0.02); var fwd = payoff2.AverageResult; Assert.True(System.Math.Abs(fwdCurve(tExp) / fwd - 1.0) < 0.005); } }
public void LVMC_PathsGenerated() { var origin = DateTime.Now.Date; var engine = new PathEngine(2.IntPow(17)); //engine.AddPathProcess(new Random.MersenneTwister.MersenneTwister64() //{ // UseNormalInverse = true, // UseAnthithetic = false //}); engine.AddPathProcess(new Random.Sobol.SobolShiftedPathGenerator(new Random.Sobol.SobolDirectionNumbers(s_directionNumbers), 0) { UseNormalInverse = true }); var tenorsStr = new[] { "1m", "2m", "3m", "6m", "9m", "1y" }; var tenors = tenorsStr.Select(x => new Frequency(x)); var expiries = tenors.Select(t => origin.AddPeriod(RollType.F, new Calendar(), t)).ToArray(); var deltaKs = new[] { -0.1, -0.25, -0.5, -0.75, -0.9 }; var smileVols = new[] { 0.32, 0.3, 0.29, 0.3, 0.32 }; var vols = Enumerable.Repeat(smileVols, expiries.Length).ToArray(); var volSurface = new GridVolSurface(origin, deltaKs, expiries, vols, Core.Basic.StrikeType.ForwardDelta, Interpolator1DType.LinearFlatExtrap, Interpolator1DType.LinearInVariance, DayCountBasis.Act365F); var fwdCurve = new Func <double, double>(t => { return(900 + 100 * t); }); var asset = new LVSingleAsset ( startDate: origin, expiryDate: origin.AddYears(1), volSurface: volSurface, forwardCurve: fwdCurve, nTimeSteps: 365, name: "TestAsset" ); engine.AddPathProcess(asset); var payoff = new EuropeanPut("TestAsset", 900, origin.AddYears(1)); var payoff2 = new EuropeanCall("TestAsset", 0, origin.AddYears(1)); engine.AddPathProcess(payoff); engine.AddPathProcess(payoff2); engine.SetupFeatures(); engine.RunProcess(); var pv = payoff.AverageResult; var blackVol = volSurface.GetVolForAbsoluteStrike(900, origin.AddYears(1), fwdCurve(1.0)); var blackPv = BlackFunctions.BlackPV(1000, 900, 0, 1, blackVol, OptionType.P); Assert.Equal(blackPv, pv, 0); var fwd = payoff2.AverageResult; Assert.True(System.Math.Abs(fwdCurve(1) / fwd - 1.0) < 0.001); //var output = new OutputPathsToImage(engine,2000,1000); }
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); } }