protected SwaptionVolatilityDiscrete(List <Period> optionTenors, List <Period> swapTenors, int settlementDays, Calendar cal, BusinessDayConvention bdc, DayCounter dc) : base(settlementDays, cal, bdc, dc) { nOptionTenors_ = optionTenors.Count; optionTenors_ = optionTenors; optionDates_ = new InitializedList <Date>(nOptionTenors_); optionTimes_ = new InitializedList <double>(nOptionTenors_); optionDatesAsReal_ = new InitializedList <double>(nOptionTenors_); nSwapTenors_ = swapTenors.Count; swapTenors_ = swapTenors; swapLengths_ = new InitializedList <double>(nSwapTenors_); checkOptionTenors(); initializeOptionDatesAndTimes(); checkSwapTenors(); initializeSwapLengths(); optionInterpolator_ = new LinearInterpolation(optionTimes_, optionTimes_.Count, optionDatesAsReal_); optionInterpolator_.update(); optionInterpolator_.enableExtrapolation(); evaluationDate_ = Settings.Instance.evaluationDate(); Settings.Instance.registerWith(update); }
protected SwaptionVolatilityDiscrete(List <Date> optionDates, List <Period> swapTenors, Date referenceDate, Calendar cal, BusinessDayConvention bdc, DayCounter dc) : base(referenceDate, cal, bdc, dc) { nOptionTenors_ = optionDates.Count; optionTenors_ = new InitializedList <Period>(nOptionTenors_); optionDates_ = optionDates; optionTimes_ = new InitializedList <double>(nOptionTenors_); optionDatesAsReal_ = new InitializedList <double>(nOptionTenors_); nSwapTenors_ = swapTenors.Count; swapTenors_ = swapTenors; swapLengths_ = new InitializedList <double>(nSwapTenors_); checkOptionDates(); initializeOptionTimes(); checkSwapTenors(); initializeSwapLengths(); optionInterpolator_ = new LinearInterpolation(optionTimes_, optionTimes_.Count, optionDatesAsReal_); optionInterpolator_.update(); optionInterpolator_.enableExtrapolation(); }
public override void calculate() { double sigmaShift_vega = 0.001; double sigmaShift_volga = 0.0001; double spotShift_delta = 0.0001 * spotFX_.link.value(); double sigmaShift_vanna = 0.0001; Utils.QL_REQUIRE(arguments_.barrierType == DoubleBarrier.Type.KnockIn || arguments_.barrierType == DoubleBarrier.Type.KnockOut, () => "Only same type barrier supported"); Handle <Quote> x0Quote = new Handle <Quote>(new SimpleQuote(spotFX_.link.value())); Handle <Quote> atmVolQuote = new Handle <Quote>(new SimpleQuote(atmVol_.link.value())); BlackVolTermStructure blackVolTS = new BlackConstantVol(Settings.Instance.evaluationDate(), new NullCalendar(), atmVolQuote, new Actual365Fixed()); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(x0Quote, foreignTS_, domesticTS_, new Handle <BlackVolTermStructure>(blackVolTS)); IPricingEngine engineBS = getOriginalEngine_(stochProcess, series_); BlackDeltaCalculator blackDeltaCalculatorAtm = new BlackDeltaCalculator( Option.Type.Call, atmVol_.link.deltaType(), x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_)); double atmStrike = blackDeltaCalculatorAtm.atmStrike(atmVol_.link.atmType()); double call25Vol = vol25Call_.link.value(); double put25Vol = vol25Put_.link.value(); BlackDeltaCalculator blackDeltaCalculatorPut25 = new BlackDeltaCalculator( Option.Type.Put, vol25Put_.link.deltaType(), x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), put25Vol * Math.Sqrt(T_)); double put25Strike = blackDeltaCalculatorPut25.strikeFromDelta(-0.25); BlackDeltaCalculator blackDeltaCalculatorCall25 = new BlackDeltaCalculator( Option.Type.Call, vol25Call_.link.deltaType(), x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), call25Vol * Math.Sqrt(T_)); double call25Strike = blackDeltaCalculatorCall25.strikeFromDelta(0.25); //here use vanna volga interpolated smile to price vanilla List <double> strikes = new List <double>(); List <double> vols = new List <double>(); strikes.Add(put25Strike); vols.Add(put25Vol); strikes.Add(atmStrike); vols.Add(atmVol_.link.value()); strikes.Add(call25Strike); vols.Add(call25Vol); VannaVolga vannaVolga = new VannaVolga(x0Quote.link.value(), foreignTS_.link.discount(T_), foreignTS_.link.discount(T_), T_); Interpolation interpolation = vannaVolga.interpolate(strikes, strikes.Count, vols); interpolation.enableExtrapolation(); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; Utils.QL_REQUIRE(payoff != null, () => "invalid payoff"); double strikeVol = interpolation.value(payoff.strike()); // Vanilla option price double vanillaOption = Utils.blackFormula(payoff.optionType(), payoff.strike(), x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), strikeVol * Math.Sqrt(T_), domesticTS_.link.discount(T_)); //already out if ((x0Quote.link.value() > arguments_.barrier_hi || x0Quote.link.value() < arguments_.barrier_lo) && arguments_.barrierType == DoubleBarrier.Type.KnockOut) { results_.value = 0.0; results_.additionalResults["VanillaPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } //already in else if ((x0Quote.link.value() > arguments_.barrier_hi || x0Quote.link.value() < arguments_.barrier_lo) && arguments_.barrierType == DoubleBarrier.Type.KnockIn) { results_.value = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["VanillaPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } else { //set up BS barrier option pricing //only calculate out barrier option price // in barrier price = vanilla - out barrier DoubleBarrierOption doubleBarrierOption = new DoubleBarrierOption( DoubleBarrier.Type.KnockOut, arguments_.barrier_lo.GetValueOrDefault(), arguments_.barrier_hi.GetValueOrDefault(), arguments_.rebate.GetValueOrDefault(), payoff, arguments_.exercise); doubleBarrierOption.setPricingEngine(engineBS); //BS price double priceBS = doubleBarrierOption.NPV(); double priceAtmCallBS = Utils.blackFormula(Option.Type.Call, atmStrike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25CallBS = Utils.blackFormula(Option.Type.Call, call25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25PutBS = Utils.blackFormula(Option.Type.Put, put25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); //market price double priceAtmCallMkt = Utils.blackFormula(Option.Type.Call, atmStrike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25CallMkt = Utils.blackFormula(Option.Type.Call, call25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), call25Vol * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25PutMkt = Utils.blackFormula(Option.Type.Put, put25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), put25Vol * Math.Sqrt(T_), domesticTS_.link.discount(T_)); //Analytical Black Scholes formula NormalDistribution norm = new NormalDistribution(); double d1atm = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / atmStrike) + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_)); double vegaAtm_Analytical = x0Quote.link.value() * norm.value(d1atm) * Math.Sqrt(T_) * foreignTS_.link.discount(T_); double vannaAtm_Analytical = vegaAtm_Analytical / x0Quote.link.value() * (1.0 - d1atm / (atmVolQuote.link.value() * Math.Sqrt(T_))); double volgaAtm_Analytical = vegaAtm_Analytical * d1atm * (d1atm - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value(); double d125call = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / call25Strike) + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_)); double vega25Call_Analytical = x0Quote.link.value() * norm.value(d125call) * Math.Sqrt(T_) * foreignTS_.link.discount(T_); double vanna25Call_Analytical = vega25Call_Analytical / x0Quote.link.value() * (1.0 - d125call / (atmVolQuote.link.value() * Math.Sqrt(T_))); double volga25Call_Analytical = vega25Call_Analytical * d125call * (d125call - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value(); double d125Put = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / put25Strike) + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_)); double vega25Put_Analytical = x0Quote.link.value() * norm.value(d125Put) * Math.Sqrt(T_) * foreignTS_.link.discount(T_); double vanna25Put_Analytical = vega25Put_Analytical / x0Quote.link.value() * (1.0 - d125Put / (atmVolQuote.link.value() * Math.Sqrt(T_))); double volga25Put_Analytical = vega25Put_Analytical * d125Put * (d125Put - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value(); //BS vega ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vega); doubleBarrierOption.recalculate(); double vegaBarBS = (doubleBarrierOption.NPV() - priceBS) / sigmaShift_vega; ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_vega);//setback //BS volga //vegaBar2 //base NPV ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_volga); doubleBarrierOption.recalculate(); double priceBS2 = doubleBarrierOption.NPV(); //shifted npv ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vega); doubleBarrierOption.recalculate(); double vegaBarBS2 = (doubleBarrierOption.NPV() - priceBS2) / sigmaShift_vega; double volgaBarBS = (vegaBarBS2 - vegaBarBS) / sigmaShift_volga; ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_volga - sigmaShift_vega);//setback //BS Delta //base delta ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//shift forth doubleBarrierOption.recalculate(); double priceBS_delta1 = doubleBarrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() - 2 * spotShift_delta);//shift back doubleBarrierOption.recalculate(); double priceBS_delta2 = doubleBarrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//set back double deltaBar1 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta); //shifted vanna ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vanna); //shift sigma //shifted delta ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta); //shift forth doubleBarrierOption.recalculate(); priceBS_delta1 = doubleBarrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() - 2 * spotShift_delta);//shift back doubleBarrierOption.recalculate(); priceBS_delta2 = doubleBarrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//set back double deltaBar2 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta); double vannaBarBS = (deltaBar2 - deltaBar1) / sigmaShift_vanna; ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_vanna);//set back //Matrix Matrix A = new Matrix(3, 3, 0.0); //analytical A[0, 0] = vegaAtm_Analytical; A[0, 1] = vega25Call_Analytical; A[0, 2] = vega25Put_Analytical; A[1, 0] = vannaAtm_Analytical; A[1, 1] = vanna25Call_Analytical; A[1, 2] = vanna25Put_Analytical; A[2, 0] = volgaAtm_Analytical; A[2, 1] = volga25Call_Analytical; A[2, 2] = volga25Put_Analytical; Vector b = new Vector(3, 0.0); b[0] = vegaBarBS; b[1] = vannaBarBS; b[2] = volgaBarBS; Vector q = Matrix.inverse(A) * b; double H = arguments_.barrier_hi.GetValueOrDefault(); double L = arguments_.barrier_lo.GetValueOrDefault(); double theta_tilt_minus = ((domesticTS_.link.zeroRate(T_, Compounding.Continuous).value() - foreignTS_.link.zeroRate(T_, Compounding.Continuous).value()) / atmVol_.link.value() - atmVol_.link.value() / 2.0) * Math.Sqrt(T_); double h = 1.0 / atmVol_.link.value() * Math.Log(H / x0Quote.link.value()) / Math.Sqrt(T_); double l = 1.0 / atmVol_.link.value() * Math.Log(L / x0Quote.link.value()) / Math.Sqrt(T_); CumulativeNormalDistribution cnd = new CumulativeNormalDistribution(); double doubleNoTouch = 0.0; for (int j = -series_; j < series_; j++) { double e_minus = 2 * j * (h - l) - theta_tilt_minus; doubleNoTouch += Math.Exp(-2.0 * j * theta_tilt_minus * (h - l)) * (cnd.value(h + e_minus) - cnd.value(l + e_minus)) - Math.Exp(-2.0 * j * theta_tilt_minus * (h - l) + 2.0 * theta_tilt_minus * h) * (cnd.value(h - 2.0 * h + e_minus) - cnd.value(l - 2.0 * h + e_minus)); } double p_survival = doubleNoTouch; double lambda = p_survival; double adjust = q[0] * (priceAtmCallMkt - priceAtmCallBS) + q[1] * (price25CallMkt - price25CallBS) + q[2] * (price25PutMkt - price25PutBS); double outPrice = priceBS + lambda * adjust; // double inPrice; //adapt Vanilla delta if (adaptVanDelta_ == true) { outPrice += lambda * (bsPriceWithSmile_ - vanillaOption); //capfloored by (0, vanilla) outPrice = Math.Max(0.0, Math.Min(bsPriceWithSmile_, outPrice)); inPrice = bsPriceWithSmile_ - outPrice; } else { //capfloored by (0, vanilla) outPrice = Math.Max(0.0, Math.Min(vanillaOption, outPrice)); inPrice = vanillaOption - outPrice; } if (arguments_.barrierType == DoubleBarrier.Type.KnockOut) { results_.value = outPrice; } else { results_.value = inPrice; } results_.additionalResults["VanillaPrice"] = vanillaOption; results_.additionalResults["BarrierInPrice"] = inPrice; results_.additionalResults["BarrierOutPrice"] = outPrice; results_.additionalResults["lambda"] = lambda; } }
public override void calculate() { Utils.QL_REQUIRE(arguments_.barrierType == Barrier.Type.UpIn || arguments_.barrierType == Barrier.Type.UpOut || arguments_.barrierType == Barrier.Type.DownIn || arguments_.barrierType == Barrier.Type.DownOut, () => "Invalid barrier type"); double sigmaShift_vega = 0.0001; double sigmaShift_volga = 0.0001; double spotShift_delta = 0.0001 * spotFX_.link.value(); double sigmaShift_vanna = 0.0001; Handle <Quote> x0Quote = new Handle <Quote>(new SimpleQuote(spotFX_.link.value())); //used for shift Handle <Quote> atmVolQuote = new Handle <Quote>(new SimpleQuote(atmVol_.link.value())); //used for shift BlackVolTermStructure blackVolTS = new BlackConstantVol(Settings.Instance.evaluationDate(), new NullCalendar(), atmVolQuote, new Actual365Fixed()); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(x0Quote, foreignTS_, domesticTS_, new Handle <BlackVolTermStructure>(blackVolTS)); IPricingEngine engineBS = new AnalyticBarrierEngine(stochProcess); BlackDeltaCalculator blackDeltaCalculatorAtm = new BlackDeltaCalculator( Option.Type.Call, atmVol_.link.deltaType(), x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_)); double atmStrike = blackDeltaCalculatorAtm.atmStrike(atmVol_.link.atmType()); double call25Vol = vol25Call_.link.value(); double put25Vol = vol25Put_.link.value(); BlackDeltaCalculator blackDeltaCalculatorPut25 = new BlackDeltaCalculator( Option.Type.Put, vol25Put_.link.deltaType(), x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), put25Vol * Math.Sqrt(T_)); double put25Strike = blackDeltaCalculatorPut25.strikeFromDelta(-0.25); BlackDeltaCalculator blackDeltaCalculatorCall25 = new BlackDeltaCalculator( Option.Type.Call, vol25Call_.link.deltaType(), x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), call25Vol * Math.Sqrt(T_)); double call25Strike = blackDeltaCalculatorCall25.strikeFromDelta(0.25); //here use vanna volga interpolated smile to price vanilla List <double> strikes = new List <double>(); List <double> vols = new List <double>(); strikes.Add(put25Strike); vols.Add(put25Vol); strikes.Add(atmStrike); vols.Add(atmVol_.link.value()); strikes.Add(call25Strike); vols.Add(call25Vol); VannaVolga vannaVolga = new VannaVolga(x0Quote.link.value(), domesticTS_.link.discount(T_), foreignTS_.link.discount(T_), T_); Interpolation interpolation = vannaVolga.interpolate(strikes, strikes.Count, vols); interpolation.enableExtrapolation(); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; double strikeVol = interpolation.value(payoff.strike()); // Vanilla option price double vanillaOption = Utils.blackFormula(payoff.optionType(), payoff.strike(), x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), strikeVol * Math.Sqrt(T_), domesticTS_.link.discount(T_)); //spot > barrier up&out 0 if (x0Quote.link.value() >= arguments_.barrier && arguments_.barrierType == Barrier.Type.UpOut) { results_.value = 0.0; results_.additionalResults["VanillaPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } //spot > barrier up&in vanilla else if (x0Quote.link.value() >= arguments_.barrier && arguments_.barrierType == Barrier.Type.UpIn) { results_.value = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["VanillaPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } //spot < barrier down&out 0 else if (x0Quote.link.value() <= arguments_.barrier && arguments_.barrierType == Barrier.Type.DownOut) { results_.value = 0.0; results_.additionalResults["VanillaPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } //spot < barrier down&in vanilla else if (x0Quote.link.value() <= arguments_.barrier && arguments_.barrierType == Barrier.Type.DownIn) { results_.value = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["VanillaPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierInPrice"] = adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption; results_.additionalResults["BarrierOutPrice"] = 0.0; } else { //set up BS barrier option pricing //only calculate out barrier option price // in barrier price = vanilla - out barrier Barrier.Type barrierType; if (arguments_.barrierType == Barrier.Type.UpOut) { barrierType = arguments_.barrierType; } else if (arguments_.barrierType == Barrier.Type.UpIn) { barrierType = Barrier.Type.UpOut; } else if (arguments_.barrierType == Barrier.Type.DownOut) { barrierType = arguments_.barrierType; } else { barrierType = Barrier.Type.DownOut; } BarrierOption barrierOption = new BarrierOption(barrierType, arguments_.barrier.GetValueOrDefault(), arguments_.rebate.GetValueOrDefault(), (StrikedTypePayoff)arguments_.payoff, arguments_.exercise); barrierOption.setPricingEngine(engineBS); //BS price with atm vol double priceBS = barrierOption.NPV(); double priceAtmCallBS = Utils.blackFormula(Option.Type.Call, atmStrike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25CallBS = Utils.blackFormula(Option.Type.Call, call25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25PutBS = Utils.blackFormula(Option.Type.Put, put25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); //market price double priceAtmCallMkt = Utils.blackFormula(Option.Type.Call, atmStrike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), atmVol_.link.value() * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25CallMkt = Utils.blackFormula(Option.Type.Call, call25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), call25Vol * Math.Sqrt(T_), domesticTS_.link.discount(T_)); double price25PutMkt = Utils.blackFormula(Option.Type.Put, put25Strike, x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_), put25Vol * Math.Sqrt(T_), domesticTS_.link.discount(T_)); //Analytical Black Scholes formula for vanilla option NormalDistribution norm = new NormalDistribution(); double d1atm = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / atmStrike) + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_)); double vegaAtm_Analytical = x0Quote.link.value() * norm.value(d1atm) * Math.Sqrt(T_) * foreignTS_.link.discount(T_); double vannaAtm_Analytical = vegaAtm_Analytical / x0Quote.link.value() * (1.0 - d1atm / (atmVolQuote.link.value() * Math.Sqrt(T_))); double volgaAtm_Analytical = vegaAtm_Analytical * d1atm * (d1atm - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value(); double d125call = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / call25Strike) + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_)); double vega25Call_Analytical = x0Quote.link.value() * norm.value(d125call) * Math.Sqrt(T_) * foreignTS_.link.discount(T_); double vanna25Call_Analytical = vega25Call_Analytical / x0Quote.link.value() * (1.0 - d125call / (atmVolQuote.link.value() * Math.Sqrt(T_))); double volga25Call_Analytical = vega25Call_Analytical * d125call * (d125call - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value(); double d125Put = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / put25Strike) + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_)); double vega25Put_Analytical = x0Quote.link.value() * norm.value(d125Put) * Math.Sqrt(T_) * foreignTS_.link.discount(T_); double vanna25Put_Analytical = vega25Put_Analytical / x0Quote.link.value() * (1.0 - d125Put / (atmVolQuote.link.value() * Math.Sqrt(T_))); double volga25Put_Analytical = vega25Put_Analytical * d125Put * (d125Put - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value(); //BS vega ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vega); barrierOption.recalculate(); double vegaBarBS = (barrierOption.NPV() - priceBS) / sigmaShift_vega; ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_vega);//setback //BS volga //vegaBar2 //base NPV ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_volga); barrierOption.recalculate(); double priceBS2 = barrierOption.NPV(); //shifted npv ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vega); barrierOption.recalculate(); double vegaBarBS2 = (barrierOption.NPV() - priceBS2) / sigmaShift_vega; double volgaBarBS = (vegaBarBS2 - vegaBarBS) / sigmaShift_volga; ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_volga - sigmaShift_vega);//setback //BS Delta //base delta ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//shift forth barrierOption.recalculate(); double priceBS_delta1 = barrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() - 2 * spotShift_delta);//shift back barrierOption.recalculate(); double priceBS_delta2 = barrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//set back double deltaBar1 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta); //shifted delta ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vanna); //shift sigma ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta); //shift forth barrierOption.recalculate(); priceBS_delta1 = barrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() - 2 * spotShift_delta);//shift back barrierOption.recalculate(); priceBS_delta2 = barrierOption.NPV(); ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//set back double deltaBar2 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta); double vannaBarBS = (deltaBar2 - deltaBar1) / sigmaShift_vanna; ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_vanna);//set back //Matrix Matrix A = new Matrix(3, 3, 0.0); //analytical A[0, 0] = vegaAtm_Analytical; A[0, 1] = vega25Call_Analytical; A[0, 2] = vega25Put_Analytical; A[1, 0] = vannaAtm_Analytical; A[1, 1] = vanna25Call_Analytical; A[1, 2] = vanna25Put_Analytical; A[2, 0] = volgaAtm_Analytical; A[2, 1] = volga25Call_Analytical; A[2, 2] = volga25Put_Analytical; Vector b = new Vector(3, 0.0); b[0] = vegaBarBS; b[1] = vannaBarBS; b[2] = volgaBarBS; //Vector q = inverse(A) * b; TODO implements transpose Vector q = Matrix.inverse(A) * b; //touch probability CumulativeNormalDistribution cnd = new CumulativeNormalDistribution(); double mu = domesticTS_.link.zeroRate(T_, Compounding.Continuous).value() - foreignTS_.link.zeroRate(T_, Compounding.Continuous).value() - Math.Pow(atmVol_.link.value(), 2.0) / 2.0; double h2 = (Math.Log(arguments_.barrier.GetValueOrDefault() / x0Quote.link.value()) + mu * T_) / (atmVol_.link.value() * Math.Sqrt(T_)); double h2Prime = (Math.Log(x0Quote.link.value() / arguments_.barrier.GetValueOrDefault()) + mu * T_) / (atmVol_.link.value() * Math.Sqrt(T_)); double probTouch = 0.0; if (arguments_.barrierType == Barrier.Type.UpIn || arguments_.barrierType == Barrier.Type.UpOut) { probTouch = cnd.value(h2Prime) + Math.Pow(arguments_.barrier.GetValueOrDefault() / x0Quote.link.value(), 2.0 * mu / Math.Pow(atmVol_.link.value(), 2.0)) * cnd.value(-h2); } else { probTouch = cnd.value(-h2Prime) + Math.Pow(arguments_.barrier.GetValueOrDefault() / x0Quote.link.value(), 2.0 * mu / Math.Pow(atmVol_.link.value(), 2.0)) * cnd.value(h2); } double p_survival = 1.0 - probTouch; double lambda = p_survival; double adjust = q[0] * (priceAtmCallMkt - priceAtmCallBS) + q[1] * (price25CallMkt - price25CallBS) + q[2] * (price25PutMkt - price25PutBS); double outPrice = priceBS + lambda * adjust; // double inPrice; //adapt Vanilla delta if (adaptVanDelta_ == true) { outPrice += lambda * (bsPriceWithSmile_ - vanillaOption); //capfloored by (0, vanilla) outPrice = Math.Max(0.0, Math.Min(bsPriceWithSmile_, outPrice)); inPrice = bsPriceWithSmile_ - outPrice; } else { //capfloored by (0, vanilla) outPrice = Math.Max(0.0, Math.Min(vanillaOption, outPrice)); inPrice = vanillaOption - outPrice; } if (arguments_.barrierType == Barrier.Type.DownOut || arguments_.barrierType == Barrier.Type.UpOut) { results_.value = outPrice; } else { results_.value = inPrice; } results_.additionalResults["VanillaPrice"] = vanillaOption; results_.additionalResults["BarrierInPrice"] = inPrice; results_.additionalResults["BarrierOutPrice"] = outPrice; results_.additionalResults["lambda"] = lambda; } }