public override void calculate() { Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European Option"); EuropeanExercise exercise = arguments_.exercise as EuropeanExercise; Utils.QL_REQUIRE(exercise != null, () => "not an European Option"); SpreadBasketPayoff spreadPayoff = arguments_.payoff as SpreadBasketPayoff; Utils.QL_REQUIRE(spreadPayoff != null, () => " spread payoff expected"); PlainVanillaPayoff payoff = spreadPayoff.basePayoff() as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); double strike = payoff.strike(); double f1 = process1_.stateVariable().link.value(); double f2 = process2_.stateVariable().link.value(); // use atm vols double variance1 = process1_.blackVolatility().link.blackVariance(exercise.lastDate(), f1); double variance2 = process2_.blackVolatility().link.blackVariance(exercise.lastDate(), f2); double riskFreeDiscount = process1_.riskFreeRate().link.discount(exercise.lastDate()); Func <double, double> Square = x => x * x; double f = f1 / (f2 + strike); double v = Math.Sqrt(variance1 + variance2 * Square(f2 / (f2 + strike)) - 2 * rho_ * Math.Sqrt(variance1 * variance2) * (f2 / (f2 + strike))); BlackCalculator black = new BlackCalculator(new PlainVanillaPayoff(payoff.optionType(), 1.0), f, v, riskFreeDiscount); results_.value = (f2 + strike) * black.value(); }
public override void calculate() { Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European Option"); EuropeanExercise exercise = arguments_.exercise as EuropeanExercise; Utils.QL_REQUIRE(exercise != null, () => "not an European Option"); BasketPayoff basket_payoff = arguments_.payoff as BasketPayoff; MinBasketPayoff min_basket = arguments_.payoff as MinBasketPayoff; MaxBasketPayoff max_basket = arguments_.payoff as MaxBasketPayoff; Utils.QL_REQUIRE(min_basket != null || max_basket != null, () => "unknown basket type"); PlainVanillaPayoff payoff = basket_payoff.basePayoff() as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given"); double strike = payoff.strike(); double variance1 = process1_.blackVolatility().link.blackVariance(exercise.lastDate(), strike); double variance2 = process2_.blackVolatility().link.blackVariance(exercise.lastDate(), strike); double riskFreeDiscount = process1_.riskFreeRate().link.discount(exercise.lastDate()); // cannot handle non zero dividends, so don't believe this... double dividendDiscount1 = process1_.dividendYield().link.discount(exercise.lastDate()); double dividendDiscount2 = process2_.dividendYield().link.discount(exercise.lastDate()); double forward1 = process1_.stateVariable().link.value() * dividendDiscount1 / riskFreeDiscount; double forward2 = process2_.stateVariable().link.value() * dividendDiscount2 / riskFreeDiscount; if (max_basket != null) { switch (payoff.optionType()) { // euro call on a two asset max basket case Option.Type.Call: results_.value = euroTwoAssetMaxBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho_); break; // euro put on a two asset max basket case Option.Type.Put: results_.value = strike * riskFreeDiscount - euroTwoAssetMaxBasketCall(forward1, forward2, 0.0, riskFreeDiscount, variance1, variance2, rho_) + euroTwoAssetMaxBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho_); break; default: Utils.QL_FAIL("unknown option type"); break; } } else if (min_basket != null) { switch (payoff.optionType()) { // euro call on a two asset min basket case Option.Type.Call: results_.value = euroTwoAssetMinBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho_); break; // euro put on a two asset min basket case Option.Type.Put: results_.value = strike * riskFreeDiscount - euroTwoAssetMinBasketCall(forward1, forward2, 0.0, riskFreeDiscount, variance1, variance2, rho_) + euroTwoAssetMinBasketCall(forward1, forward2, strike, riskFreeDiscount, variance1, variance2, rho_); break; default: Utils.QL_FAIL("unknown option type"); break; } } else { Utils.QL_FAIL("unknown type"); } }
public void testAnalyticDiscreteGeometricAveragePriceGreeks() { //BOOST_MESSAGE("Testing discrete-averaging geometric Asian greeks..."); //SavedSettings backup; Dictionary<string,double> calculated, expected, tolerance; calculated = new Dictionary<string, double>(6); expected = new Dictionary<string, double>(6); tolerance = new Dictionary<string, double>(6); tolerance["delta"] = 1.0e-5; tolerance["gamma"] = 1.0e-5; tolerance["theta"] = 1.0e-5; tolerance["rho"] = 1.0e-5; tolerance["divRho"] = 1.0e-5; tolerance["vega"] = 1.0e-5; Option.Type[] types = { Option.Type.Call, Option.Type.Put }; double[] underlyings = { 100.0 }; double[] strikes = { 90.0, 100.0, 110.0 }; double[] qRates = { 0.04, 0.05, 0.06 }; double[] rRates = { 0.01, 0.05, 0.15 }; int[] lengths = { 1, 2 }; double[] vols = { 0.11, 0.50, 1.20 }; DayCounter dc = new Actual360(); Date today = Date.Today; Settings.setEvaluationDate(today); SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); Handle<YieldTermStructure> qTS = new Handle<YieldTermStructure> (Utilities.flatRate(qRate, dc)); SimpleQuote rRate = new SimpleQuote(0.0); Handle<YieldTermStructure> rTS = new Handle<YieldTermStructure> (Utilities.flatRate(rRate, dc)); SimpleQuote vol = new SimpleQuote(0.0); Handle<BlackVolTermStructure> volTS = new Handle<BlackVolTermStructure> (Utilities.flatVol(vol, dc)); BlackScholesMertonProcess process = new BlackScholesMertonProcess(new Handle<Quote>(spot), qTS, rTS, volTS); for (int i=0; i<types.Length ; i++) { for (int j=0; j<strikes.Length ; j++) { for (int k=0; k<lengths.Length ; k++) { EuropeanExercise maturity = new EuropeanExercise( today + new Period(lengths[k],TimeUnit.Years)); PlainVanillaPayoff payoff = new PlainVanillaPayoff(types[i], strikes[j]); double runningAverage = 120; int pastFixings = 1; List<Date> fixingDates = new List<Date>(); for (Date d = today + new Period(3, TimeUnit.Months); d <= maturity.lastDate(); d += new Period(3, TimeUnit.Months)) fixingDates.Add(d); IPricingEngine engine = new AnalyticDiscreteGeometricAveragePriceAsianEngine(process); DiscreteAveragingAsianOption option = new DiscreteAveragingAsianOption(Average.Type.Geometric, runningAverage, pastFixings, fixingDates, payoff, maturity); option.setPricingEngine(engine); for (int l=0; l<underlyings.Length ; l++) { for (int m=0; m<qRates.Length ; m++) { for (int n=0; n<rRates.Length ; n++) { for (int p=0; p<vols.Length ; p++) { double u = underlyings[l]; double q = qRates[m], r = rRates[n]; double v = vols[p]; spot.setValue(u); qRate.setValue(q); rRate.setValue(r); vol.setValue(v); double value = option.NPV(); calculated["delta"] = option.delta(); calculated["gamma"] = option.gamma(); calculated["theta"] = option.theta(); calculated["rho"] = option.rho(); calculated["divRho"] = option.dividendRho(); calculated["vega"] = option.vega(); if (value > spot.value()*1.0e-5) { // perturb spot and get delta and gamma double du = u*1.0e-4; spot.setValue(u+du); double value_p = option.NPV(), delta_p = option.delta(); spot.setValue(u-du); double value_m = option.NPV(), delta_m = option.delta(); spot.setValue(u); expected["delta"] = (value_p - value_m)/(2*du); expected["gamma"] = (delta_p - delta_m)/(2*du); // perturb rates and get rho and dividend rho double dr = r*1.0e-4; rRate.setValue(r+dr); value_p = option.NPV(); rRate.setValue(r-dr); value_m = option.NPV(); rRate.setValue(r); expected["rho"] = (value_p - value_m)/(2*dr); double dq = q*1.0e-4; qRate.setValue(q+dq); value_p = option.NPV(); qRate.setValue(q-dq); value_m = option.NPV(); qRate.setValue(q); expected["divRho"] = (value_p - value_m)/(2*dq); // perturb volatility and get vega double dv = v*1.0e-4; vol.setValue(v+dv); value_p = option.NPV(); vol.setValue(v-dv); value_m = option.NPV(); vol.setValue(v); expected["vega"] = (value_p - value_m)/(2*dv); // perturb date and get theta double dT = dc.yearFraction(today-1, today+1); Settings.setEvaluationDate(today-1); value_m = option.NPV(); Settings.setEvaluationDate(today+1); value_p = option.NPV(); Settings.setEvaluationDate(today); expected["theta"] = (value_p - value_m)/dT; // compare foreach (KeyValuePair<string, double> kvp in calculated){ string greek = kvp.Key; double expct = expected[greek], calcl = calculated[greek], tol = tolerance [greek]; double error =Utilities.relativeError(expct,calcl,u); if (error>tol) { REPORT_FAILURE(greek, Average.Type.Geometric, runningAverage, pastFixings, new List<Date>(), payoff, maturity, u, q, r, today, v, expct, calcl, tol); } } } } } } } } } } }