public void SolveSmileSimple() { var valDate = new DateTime(2018, 07, 28); var expDate = valDate.AddDays(365); 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.Arithmatic }; 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); Assert.Equal(smile25d.RisykVol, smile[2] - smile[0], 8); Assert.Equal(smile25d.FlyVol, (smile[2] + smile[0]) / 2 - atmConstraint.MarketVol, 8); } }
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 RiskyFlySurface(DateTime originDate, double[] ATMVols, DateTime[] expiries, double[] wingDeltas, double[][] riskies, double[][] flies, double[] fwds, WingQuoteType wingQuoteType, AtmVolType atmVolType, Interpolator1DType strikeInterpType, Interpolator1DType timeInterpType, string[] pillarLabels = null) { if (pillarLabels == null) { PillarLabels = expiries.Select(x => x.ToString("yyyy-MM-dd")).ToArray(); } else { PillarLabels = pillarLabels; } if (ATMVols.Length != expiries.Length || expiries.Length != riskies.Length || riskies.Length != flies.Length) { throw new Exception("Inputs do not have consistent time dimensions"); } if (wingDeltas.Length != riskies[0].Length || riskies[0].Length != flies[0].Length) { throw new Exception("Inputs do not have consistent strike dimensions"); } var strikes = new double[2 * wingDeltas.Length + 1]; var vols = new double[expiries.Length][]; if (riskies.SelectMany(x => x).All(x => x == 0) && flies.SelectMany(x => x).All(x => x == 0)) { for (var s = 0; s < wingDeltas.Length; s++) { strikes[s] = wingDeltas[wingDeltas.Length - 1 - s]; strikes[strikes.Length - 1 - s] = 1.0 - wingDeltas[wingDeltas.Length - 1 - s]; } for (var t = 0; t < expiries.Length; t++) { vols[t] = Enumerable.Repeat(ATMVols[t], strikes.Length).ToArray(); } } else { var atmConstraints = ATMVols.Select(a => new ATMStraddleConstraint { ATMVolType = atmVolType, MarketVol = a }).ToArray(); var needsFlip = wingDeltas.First() > wingDeltas.Last(); if (needsFlip) { for (var s = 0; s < wingDeltas.Length; s++) { strikes[s] = wingDeltas[wingDeltas.Length - 1 - s]; strikes[strikes.Length - 1 - s] = 1.0 - wingDeltas[wingDeltas.Length - 1 - s]; } } else { for (var s = 0; s < wingDeltas.Length; s++) { strikes[s] = wingDeltas[s]; strikes[strikes.Length - 1 - s] = 1.0 - wingDeltas[s]; } } strikes[wingDeltas.Length] = 0.5; var wingConstraints = new RRBFConstraint[expiries.Length][]; var f = new AssetSmileSolver(); if (needsFlip) { for (var i = 0; i < wingConstraints.Length; i++) { var offset = wingDeltas.Length - 1; wingConstraints[i] = new RRBFConstraint[wingDeltas.Length]; for (var j = 0; j < wingConstraints[i].Length; j++) { wingConstraints[i][j] = new RRBFConstraint { Delta = wingDeltas[offset - j], FlyVol = flies[i][offset - j], RisykVol = riskies[i][offset - j], WingQuoteType = wingQuoteType, }; } vols[i] = f.Solve(atmConstraints[i], wingConstraints[i], originDate, expiries[i], fwds[i], strikes, strikeInterpType); } } else { for (var i = 0; i < wingConstraints.Length; i++) { wingConstraints[i] = new RRBFConstraint[wingDeltas.Length]; for (var j = 0; j < wingConstraints[i].Length; j++) { wingConstraints[i][j] = new RRBFConstraint { Delta = wingDeltas[j], FlyVol = flies[i][j], RisykVol = riskies[i][j], WingQuoteType = wingQuoteType, }; } vols[i] = f.Solve(atmConstraints[i], wingConstraints[i], originDate, expiries[i], fwds[i], strikes, strikeInterpType); } } } StrikeInterpolatorType = strikeInterpType; TimeInterpolatorType = timeInterpType; Expiries = expiries; ExpiriesDouble = expiries.Select(x => x.ToOADate()).ToArray(); Strikes = strikes; StrikeType = StrikeType.ForwardDelta; ATMs = ATMVols; Riskies = riskies; Flies = flies; WingDeltas = wingDeltas; Forwards = fwds; WingQuoteType = wingQuoteType; AtmVolType = atmVolType; base.Build(originDate, strikes, expiries, vols); }