public void testFdEuropeanValues() { // Testing finite-difference dividend European option values... SavedSettings backup = new SavedSettings(); double tolerance = 1.0e-2; int gridPoints = 300; int timeSteps = 40; Option.Type[] types = { Option.Type.Call, Option.Type.Put }; double[] strikes = { 50.0, 99.5, 100.0, 100.5, 150.0 }; double[] underlyings = { 100.0 }; // Rate qRates[] = { 0.00, 0.10, 0.30 }; // Analytic dividend may not be handling q correctly double[] qRates = { 0.00 }; double[] rRates = { 0.01, 0.05, 0.15 }; int[] lengths = { 1, 2 }; double[] vols = { 0.05, 0.20, 0.40 }; 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)); for (int i = 0; i < types.Length; i++) { for (int j = 0; j < strikes.Length; j++) { for (int k = 0; k < lengths.Length; k++) { Date exDate = today + new Period(lengths[k], TimeUnit.Years); Exercise exercise = new EuropeanExercise(exDate); List <Date> dividendDates = new List <Date>(); List <double> dividends = new List <double>(); for (Date d = today + new Period(3, TimeUnit.Months); d < exercise.lastDate(); d += new Period(6, TimeUnit.Months)) { dividendDates.Add(d); dividends.Add(5.0); } StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], strikes[j]); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), qTS, rTS, volTS); IPricingEngine engine = new FDDividendEuropeanEngine(stochProcess, timeSteps, gridPoints); IPricingEngine ref_engine = new AnalyticDividendEuropeanEngine(stochProcess); DividendVanillaOption option = new DividendVanillaOption(payoff, exercise, dividendDates, dividends); option.setPricingEngine(engine); DividendVanillaOption ref_option = new DividendVanillaOption(payoff, exercise, dividendDates, dividends); ref_option.setPricingEngine(ref_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); // FLOATING_POINT_EXCEPTION double calculated = option.NPV(); if (calculated > spot.value() * 1.0e-5) { double expected = ref_option.NPV(); double error = Math.Abs(calculated - expected); if (error > tolerance) { REPORT_FAILURE("value", payoff, exercise, u, q, r, today, v, expected, calculated, error, tolerance); } } } } } } } } } }
static void Main(string[] args) { DateTime startTime = DateTime.Now; Option.Type optionType = Option.Type.Put; double underlyingPrice = 36; double strikePrice = 40; double dividendYield = 0.0; double riskFreeRate = 0.06; double volatility = 0.2; Date todaysDate = new Date(15, Month.May, 1998); Settings.instance().setEvaluationDate(todaysDate); Date settlementDate = new Date(17, Month.May, 1998); Date maturityDate = new Date(17, Month.May, 1999); Calendar calendar = new TARGET(); DateVector exerciseDates = new DateVector(4); for (int i = 1; i <= 4; i++) { Period forwardPeriod = new Period(3 * i, TimeUnit.Months); Date forwardDate = settlementDate.Add(forwardPeriod); exerciseDates.Add(forwardDate); } EuropeanExercise europeanExercise = new EuropeanExercise(maturityDate); BermudanExercise bermudanExercise = new BermudanExercise(exerciseDates); AmericanExercise americanExercise = new AmericanExercise(settlementDate, maturityDate); // bootstrap the yield/dividend/vol curves and create a // BlackScholesMerton stochastic process DayCounter dayCounter = new Actual365Fixed(); YieldTermStructureHandle flatRateTSH = new YieldTermStructureHandle( new FlatForward(settlementDate, riskFreeRate, dayCounter)); YieldTermStructureHandle flatDividendTSH = new YieldTermStructureHandle( new FlatForward(settlementDate, dividendYield, dayCounter)); BlackVolTermStructureHandle flatVolTSH = new BlackVolTermStructureHandle( new BlackConstantVol(settlementDate, calendar, volatility, dayCounter)); QuoteHandle underlyingQuoteH = new QuoteHandle(new SimpleQuote(underlyingPrice)); BlackScholesMertonProcess stochasticProcess = new BlackScholesMertonProcess(underlyingQuoteH, flatDividendTSH, flatRateTSH, flatVolTSH); PlainVanillaPayoff payoff = new PlainVanillaPayoff(optionType, strikePrice); // options VanillaOption europeanOption = new VanillaOption(payoff, europeanExercise); VanillaOption bermudanOption = new VanillaOption(payoff, bermudanExercise); VanillaOption americanOption = new VanillaOption(payoff, americanExercise); // report the parameters we are using ReportParameters(optionType, underlyingPrice, strikePrice, dividendYield, riskFreeRate, volatility, maturityDate); // write out the column headings ReportHeadings(); #region Analytic Formulas // Black-Scholes for European try { europeanOption.setPricingEngine( new AnalyticEuropeanEngine(stochasticProcess)); ReportResults("Black-Scholes", europeanOption.NPV(), null, null); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Barone-Adesi and Whaley approximation for American try { americanOption.setPricingEngine( new BaroneAdesiWhaleyApproximationEngine(stochasticProcess)); ReportResults("Barone-Adesi/Whaley", null, null, americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Bjerksund and Stensland approximation for American try { americanOption.setPricingEngine( new BjerksundStenslandApproximationEngine(stochasticProcess)); ReportResults("Bjerksund/Stensland", null, null, americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Integral try { europeanOption.setPricingEngine( new IntegralEngine(stochasticProcess)); ReportResults("Integral", europeanOption.NPV(), null, null); } catch (Exception e) { Console.WriteLine(e.ToString()); } uint timeSteps = 801; // Finite differences try { europeanOption.setPricingEngine( new FdBlackScholesVanillaEngine(stochasticProcess, timeSteps, timeSteps - 1)); bermudanOption.setPricingEngine( new FdBlackScholesVanillaEngine(stochasticProcess, timeSteps, timeSteps - 1)); americanOption.setPricingEngine( new FdBlackScholesVanillaEngine(stochasticProcess, timeSteps, timeSteps - 1)); ReportResults("Finite differences", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } //Variance Gamma try { VarianceGammaProcess vgProcess = new VarianceGammaProcess(underlyingQuoteH, flatDividendTSH, flatRateTSH, volatility, 0.01, 0.0 ); europeanOption.setPricingEngine( new VarianceGammaEngine(vgProcess)); ReportResults("Variance-Gamma", europeanOption.NPV(), null, null); } catch (Exception e) { Console.WriteLine(e.ToString()); } #endregion Analytic Formulas #region Binomial Methods // Binomial Jarrow-Rudd try { europeanOption.setPricingEngine( new BinomialJRVanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine( new BinomialJRVanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine( new BinomialJRVanillaEngine(stochasticProcess, timeSteps)); ReportResults("Binomial Jarrow-Rudd", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Binomial Cox-Ross-Rubinstein try { europeanOption.setPricingEngine( new BinomialCRRVanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine( new BinomialCRRVanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine( new BinomialCRRVanillaEngine(stochasticProcess, timeSteps)); ReportResults("Binomial Cox-Ross-Rubinstein", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Additive Equiprobabilities try { europeanOption.setPricingEngine( new BinomialEQPVanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine( new BinomialEQPVanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine( new BinomialEQPVanillaEngine(stochasticProcess, timeSteps)); ReportResults("Additive Equiprobabilities", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Binomial Trigeorgis try { europeanOption.setPricingEngine( new BinomialTrigeorgisVanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine( new BinomialTrigeorgisVanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine( new BinomialTrigeorgisVanillaEngine(stochasticProcess, timeSteps)); ReportResults("Binomial Trigeorgis", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Binomial Tian try { europeanOption.setPricingEngine( new BinomialTianVanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine( new BinomialTianVanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine( new BinomialTianVanillaEngine(stochasticProcess, timeSteps)); ReportResults("Binomial Tian", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Binomial Leisen-Reimer try { europeanOption.setPricingEngine( new BinomialLRVanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine( new BinomialLRVanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine( new BinomialLRVanillaEngine(stochasticProcess, timeSteps)); ReportResults("Binomial Leisen-Reimer", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Binomial Joshi try { europeanOption.setPricingEngine( new BinomialJ4VanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine( new BinomialJ4VanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine( new BinomialJ4VanillaEngine(stochasticProcess, timeSteps)); ReportResults("Binomial Joshi", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } #endregion Binomial Methods #region Monte Carlo Methods // quantlib appears to use max numeric (int and real) values to test for 'null' (or rather 'default') values // MC (crude) try { int mcTimeSteps = 1; int timeStepsPerYear = int.MaxValue; bool brownianBridge = false; bool antitheticVariate = false; int requiredSamples = int.MaxValue; double requiredTolerance = 0.02; int maxSamples = int.MaxValue; int seed = 42; europeanOption.setPricingEngine( new MCPREuropeanEngine(stochasticProcess, mcTimeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed)); ReportResults("MC (crude)", europeanOption.NPV(), null, null); } catch (Exception e) { Console.WriteLine(e.ToString()); } // MC (Sobol) try { int mcTimeSteps = 1; int timeStepsPerYear = int.MaxValue; bool brownianBridge = false; bool antitheticVariate = false; int requiredSamples = 32768; // 2^15 double requiredTolerance = double.MaxValue; int maxSamples = int.MaxValue; int seed = 0; europeanOption.setPricingEngine( new MCLDEuropeanEngine(stochasticProcess, mcTimeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed)); ReportResults("MC (Sobol)", europeanOption.NPV(), null, null); } catch (Exception e) { Console.WriteLine(e.ToString()); } #endregion Monte Carlo Methods DateTime endTime = DateTime.Now; TimeSpan delta = endTime - startTime; Console.WriteLine(); Console.WriteLine("Run completed in {0} s", delta.TotalSeconds); Console.WriteLine(); }
void testEuropeanKnownValue() { // Testing dividend European option values with known value... SavedSettings backup = new SavedSettings(); double tolerance = 1.0e-2; double expected = 3.67; 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)); Date exDate = today + new Period(6, TimeUnit.Months); Exercise exercise = new EuropeanExercise(exDate); List <Date> dividendDates = new List <Date>(); List <double> dividends = new List <double>(); dividendDates.Add(today + new Period(2, TimeUnit.Months)); dividends.Add(0.50); dividendDates.Add(today + new Period(5, TimeUnit.Months)); dividends.Add(0.50); StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Call, 40.0); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), qTS, rTS, volTS); IPricingEngine engine = new AnalyticDividendEuropeanEngine(stochProcess); DividendVanillaOption option = new DividendVanillaOption(payoff, exercise, dividendDates, dividends); option.setPricingEngine(engine); double u = 40.0; double q = 0.0, r = 0.09; double v = 0.30; spot.setValue(u); qRate.setValue(q); rRate.setValue(r); vol.setValue(v); double calculated = option.NPV(); double error = Math.Abs(calculated - expected); if (error > tolerance) { REPORT_FAILURE("value start limit", payoff, exercise, u, q, r, today, v, expected, calculated, error, tolerance); } }
public void testEuropeanGreeks() { // Testing dividend European option greeks... SavedSettings backup = new SavedSettings(); Dictionary <string, double> calculated = new Dictionary <string, double>(), expected = new Dictionary <string, double>(), tolerance = new Dictionary <string, double>(); tolerance["delta"] = 1.0e-5; tolerance["gamma"] = 1.0e-5; tolerance["theta"] = 1.0e-5; tolerance["rho"] = 1.0e-5; tolerance["vega"] = 1.0e-5; Option.Type[] types = { Option.Type.Call, Option.Type.Put }; double[] strikes = { 50.0, 99.5, 100.0, 100.5, 150.0 }; double[] underlyings = { 100.0 }; double[] qRates = { 0.00, 0.10, 0.30 }; double[] rRates = { 0.01, 0.05, 0.15 }; int[] lengths = { 1, 2 }; double[] vols = { 0.05, 0.20, 0.40 }; 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)); for (int i = 0; i < types.Length; i++) { for (int j = 0; j < strikes.Length; j++) { for (int k = 0; k < lengths.Length; k++) { Date exDate = today + new Period(lengths[k], TimeUnit.Years); Exercise exercise = new EuropeanExercise(exDate); List <Date> dividendDates = new List <Date>(); List <double> dividends = new List <double>(); for (Date d = today + new Period(3, TimeUnit.Months); d < exercise.lastDate(); d += new Period(6, TimeUnit.Months)) { dividendDates.Add(d); dividends.Add(5.0); } StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], strikes[j]); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), qTS, rTS, volTS); IPricingEngine engine = new AnalyticDividendEuropeanEngine(stochProcess); DividendVanillaOption option = new DividendVanillaOption(payoff, exercise, dividendDates, dividends); 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["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 risk-free rate and get 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); // 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> it in calculated) { string greek = it.Key; double expct = expected [greek], calcl = calculated[greek], tol = tolerance [greek]; double error = Utilities.relativeError(expct, calcl, u); if (error > tol) { REPORT_FAILURE(greek, payoff, exercise, u, q, r, today, v, expct, calcl, error, tol); } } } } } } } } } } }
public void testBsmHullWhiteEngine() { // Testing European option pricing for a BSM process with one-factor Hull-White model DayCounter dc = new Actual365Fixed(); Date today = Date.Today; Date maturity = today + new Period(20, TimeUnit.Years); Settings.setEvaluationDate(today); Handle <Quote> spot = new Handle <Quote>(new SimpleQuote(100.0)); SimpleQuote qRate = new SimpleQuote(0.04); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, qRate, dc)); SimpleQuote rRate = new SimpleQuote(0.0525); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, rRate, dc)); SimpleQuote vol = new SimpleQuote(0.25); Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(today, vol, dc)); // FLOATING_POINT_EXCEPTION HullWhite hullWhiteModel = new HullWhite(new Handle <YieldTermStructure>(rTS), 0.00883, 0.00526); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(spot, qTS, rTS, volTS); Exercise exercise = new EuropeanExercise(maturity); double fwd = spot.link.value() * qTS.link.discount(maturity) / rTS.link.discount(maturity); StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Call, fwd); EuropeanOption option = new EuropeanOption(payoff, exercise); double tol = 1e-8; double[] corr = { -0.75, -0.25, 0.0, 0.25, 0.75 }; double[] expectedVol = { 0.217064577, 0.243995801, 0.256402830, 0.268236596, 0.290461343 }; for (int i = 0; i < corr.Length; ++i) { IPricingEngine bsmhwEngine = new AnalyticBSMHullWhiteEngine(corr[i], stochProcess, hullWhiteModel); option.setPricingEngine(bsmhwEngine); double npv = option.NPV(); Handle <BlackVolTermStructure> compVolTS = new Handle <BlackVolTermStructure>( Utilities.flatVol(today, expectedVol[i], dc)); BlackScholesMertonProcess bsProcess = new BlackScholesMertonProcess(spot, qTS, rTS, compVolTS); IPricingEngine bsEngine = new AnalyticEuropeanEngine(bsProcess); EuropeanOption comp = new EuropeanOption(payoff, exercise); comp.setPricingEngine(bsEngine); double impliedVol = comp.impliedVolatility(npv, bsProcess, 1e-10, 100); if (Math.Abs(impliedVol - expectedVol[i]) > tol) { QAssert.Fail("Failed to reproduce implied volatility" + "\n calculated: " + impliedVol + "\n expected : " + expectedVol[i]); } if (Math.Abs((comp.NPV() - npv) / npv) > tol) { QAssert.Fail("Failed to reproduce NPV" + "\n calculated: " + npv + "\n expected : " + comp.NPV()); } if (Math.Abs(comp.delta() - option.delta()) > tol) { QAssert.Fail("Failed to reproduce NPV" + "\n calculated: " + npv + "\n expected : " + comp.NPV()); } if (Math.Abs((comp.gamma() - option.gamma()) / npv) > tol) { QAssert.Fail("Failed to reproduce NPV" + "\n calculated: " + npv + "\n expected : " + comp.NPV()); } if (Math.Abs((comp.theta() - option.theta()) / npv) > tol) { QAssert.Fail("Failed to reproduce NPV" + "\n calculated: " + npv + "\n expected : " + comp.NPV()); } if (Math.Abs((comp.vega() - option.vega()) / npv) > tol) { QAssert.Fail("Failed to reproduce NPV" + "\n calculated: " + npv + "\n expected : " + comp.NPV()); } } }
public void testDiscretizationError() { // Testing the discretization error of the Heston Hull-White process DayCounter dc = new Actual360(); Date today = Date.Today; Settings.setEvaluationDate(today); // construct a strange yield curve to check drifts and discounting // of the joint stochastic process List <Date> dates = new List <Date>(); List <double> times = new List <double>(); List <double> rates = new List <double>(), divRates = new List <double>(); for (int i = 0; i <= 31; ++i) { dates.Add(today + new Period(i, TimeUnit.Years)); // FLOATING_POINT_EXCEPTION rates.Add(0.04 + 0.0001 * Math.Exp(Math.Sin(i))); divRates.Add(0.04 + 0.0001 * Math.Exp(Math.Sin(i))); times.Add(dc.yearFraction(today, dates.Last())); } Date maturity = today + new Period(10, TimeUnit.Years); double v = 0.25; Handle <Quote> s0 = new Handle <Quote>(new SimpleQuote(100)); SimpleQuote vol = new SimpleQuote(v); Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(today, vol, dc)); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, rates, dc)); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, divRates, dc)); BlackScholesMertonProcess bsmProcess = new BlackScholesMertonProcess(s0, qTS, rTS, volTS); HestonProcess hestonProcess = new HestonProcess(rTS, qTS, s0, v * v, 1, v * v, 1e-6, -0.4); HullWhiteForwardProcess hwProcess = new HullWhiteForwardProcess(rTS, 0.01, 0.01); hwProcess.setForwardMeasureTime(20.1472222222222222); double tol = 0.05; double[] corr = { -0.85, 0.5 }; double[] strike = { 50, 100, 125 }; for (int i = 0; i < corr.Length; ++i) { for (int j = 0; j < strike.Length; ++j) { StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Put, strike[j]); Exercise exercise = new EuropeanExercise(maturity); VanillaOption optionBsmHW = new VanillaOption(payoff, exercise); HullWhite hwModel = new HullWhite(rTS, hwProcess.a(), hwProcess.sigma()); optionBsmHW.setPricingEngine(new AnalyticBSMHullWhiteEngine(corr[i], bsmProcess, hwModel)); double expected = optionBsmHW.NPV(); VanillaOption optionHestonHW = new VanillaOption(payoff, exercise); HybridHestonHullWhiteProcess jointProcess = new HybridHestonHullWhiteProcess(hestonProcess, hwProcess, corr[i]); optionHestonHW.setPricingEngine( new MakeMCHestonHullWhiteEngine <PseudoRandom, Statistics>(jointProcess) .withSteps(1) .withAntitheticVariate() .withAbsoluteTolerance(tol) .withSeed(42).getAsPricingEngine()); double calculated = optionHestonHW.NPV(); double error = optionHestonHW.errorEstimate(); if ((Math.Abs(calculated - expected) > 3 * error && Math.Abs(calculated - expected) > 1e-5)) { QAssert.Fail("Failed to reproduce discretization error" + "\n corr: " + corr[i] + "\n strike: " + strike[j] + "\n calculated: " + calculated + "\n error: " + error + "\n expected: " + expected); } } } }
/// <summary> /// Evaluates the specified option contract to compute a theoretical price, IV and greeks /// </summary> /// <param name="security">The option security object</param> /// <param name="slice">The current data slice. This can be used to access other information /// available to the algorithm</param> /// <param name="contract">The option contract to evaluate</param> /// <returns>An instance of <see cref="OptionPriceModelResult"/> containing the theoretical /// price of the specified option contract</returns> public OptionPriceModelResult Evaluate(Security security, Slice slice, OptionContract contract) { try { // setting up option pricing parameters var calendar = new UnitedStates(); var dayCounter = new Actual365Fixed(); var optionSecurity = (Option)security; var settlementDate = contract.Time.Date.AddDays(Option.DefaultSettlementDays); var maturityDate = contract.Expiry.Date.AddDays(Option.DefaultSettlementDays); var underlyingQuoteValue = new SimpleQuote((double)optionSecurity.Underlying.Price); var dividendYieldValue = new SimpleQuote(_dividendYieldEstimator.Estimate(security, slice, contract)); var dividendYield = new Handle <YieldTermStructure>(new FlatForward(0, calendar, dividendYieldValue, dayCounter)); var riskFreeRateValue = new SimpleQuote(_riskFreeRateEstimator.Estimate(security, slice, contract)); var riskFreeRate = new Handle <YieldTermStructure>(new FlatForward(0, calendar, riskFreeRateValue, dayCounter)); var underlyingVolValue = new SimpleQuote(_underlyingVolEstimator.Estimate(security, slice, contract)); var underlyingVol = new Handle <BlackVolTermStructure>(new BlackConstantVol(0, calendar, new Handle <Quote>(underlyingVolValue), dayCounter)); // preparing stochastic process and payoff functions var stochasticProcess = new BlackScholesMertonProcess(new Handle <Quote>(underlyingQuoteValue), dividendYield, riskFreeRate, underlyingVol); var payoff = new PlainVanillaPayoff(contract.Right == OptionRight.Call ? QLNet.Option.Type.Call : QLNet.Option.Type.Put, (double)contract.Strike); // creating option QL object var option = contract.Symbol.ID.OptionStyle == OptionStyle.American ? new VanillaOption(payoff, new AmericanExercise(settlementDate, maturityDate)) : new VanillaOption(payoff, new EuropeanExercise(maturityDate)); Settings.setEvaluationDate(settlementDate); // preparing pricing engine QL object option.setPricingEngine(_pricingEngineFunc(contract.Symbol, stochasticProcess)); // running calculations var npv = EvaluateOption(option); // function extracts QL greeks catching exception if greek is not generated by the pricing engine and reevaluates option to get numerical estimate of the seisitivity Func <Func <double>, Func <double>, decimal> tryGetGreekOrReevaluate = (greek, reevalFunc) => { try { return((decimal)greek()); } catch (Exception) { return(EnableGreekApproximation ? (decimal)reevalFunc() : 0.0m); } }; // function extracts QL greeks catching exception if greek is not generated by the pricing engine Func <Func <double>, decimal> tryGetGreek = greek => tryGetGreekOrReevaluate(greek, () => 0.0); // function extracts QL IV catching exception if IV is not generated by the pricing engine Func <decimal> tryGetImpliedVol = () => { try { return((decimal)option.impliedVolatility((double)optionSecurity.Price, stochasticProcess)); } catch (Exception err) { Log.Debug("tryGetImpliedVol() error: " + err.Message); return(0m); } }; Func <Tuple <decimal, decimal> > evalDeltaGamma = () => { try { return(Tuple.Create((decimal)option.delta(), (decimal)option.gamma())); } catch (Exception) { if (EnableGreekApproximation) { var step = 0.01; var initial = underlyingQuoteValue.value(); underlyingQuoteValue.setValue(initial - step); var npvMinus = EvaluateOption(option); underlyingQuoteValue.setValue(initial + step); var npvPlus = EvaluateOption(option); underlyingQuoteValue.setValue(initial); return(Tuple.Create((decimal)((npvPlus - npvMinus) / (2 * step)), (decimal)((npvPlus - 2 * npv + npvMinus) / (step * step)))); } else { return(Tuple.Create(0.0m, 0.0m)); } } }; Func <double> reevalVega = () => { var step = 0.001; var initial = underlyingVolValue.value(); underlyingVolValue.setValue(initial + step); var npvPlus = EvaluateOption(option); underlyingVolValue.setValue(initial); return((npvPlus - npv) / step); }; Func <double> reevalTheta = () => { var step = 1.0 / 365.0; Settings.setEvaluationDate(settlementDate.AddDays(-1)); var npvMinus = EvaluateOption(option); Settings.setEvaluationDate(settlementDate); return((npv - npvMinus) / step); }; Func <double> reevalRho = () => { var step = 0.001; var initial = riskFreeRateValue.value(); riskFreeRateValue.setValue(initial + step); var npvPlus = EvaluateOption(option); riskFreeRateValue.setValue(initial); return((npvPlus - npv) / step); }; // producing output with lazy calculations of IV and greeks return(new OptionPriceModelResult((decimal)npv, tryGetImpliedVol, () => new Greeks(evalDeltaGamma, () => tryGetGreekOrReevaluate(() => option.vega(), reevalVega), () => tryGetGreekOrReevaluate(() => option.theta(), reevalTheta), () => tryGetGreekOrReevaluate(() => option.rho(), reevalRho), () => tryGetGreek(() => option.elasticity())))); } catch (Exception err) { Log.Debug("QLOptionPriceModel.Evaluate() error: " + err.Message); return(new OptionPriceModelResult(0m, new Greeks())); } }
public void testPutCallParity() { // Testing put-call parity for deltas // Test for put call parity between put and call deltas. SavedSettings backup = new SavedSettings(); /* The data below are from * "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 * pag 11-16 */ EuropeanOptionData[] values = { // pag 2-8 // type, strike, spot, q, r, t, vol, value, tol new EuropeanOptionData(Option.Type.Call, 65.00, 60.00, 0.00, 0.08, 0.25, 0.30, 2.1334, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 95.00, 100.00, 0.05, 0.10, 0.50, 0.20, 2.4648, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 19.00, 19.00, 0.10, 0.10, 0.75, 0.28, 1.7011, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 19.00, 19.00, 0.10, 0.10, 0.75, 0.28, 1.7011, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 1.60, 1.56, 0.08, 0.06, 0.50, 0.12, 0.0291, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 70.00, 75.00, 0.05, 0.10, 0.50, 0.35, 4.0870, 1.0e-4), // pag 24 new EuropeanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.15, 0.0205, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.15, 1.8734, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.15, 9.9413, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.25, 0.3150, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.25, 3.1217, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.25, 10.3556, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.35, 0.9474, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.35, 4.3693, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.35, 11.1381, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.15, 0.8069, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.15, 4.0232, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.15, 10.5769, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.25, 2.7026, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.25, 6.6997, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.25, 12.7857, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.35, 4.9329, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.35, 9.3679, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.35, 15.3086, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.15, 9.9210, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.15, 1.8734, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.15, 0.0408, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.25, 10.2155, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.25, 3.1217, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.25, 0.4551, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.35, 10.8479, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.35, 4.3693, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.35, 1.2376, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.15, 10.3192, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.15, 4.0232, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.15, 1.0646, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.25, 12.2149, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.25, 6.6997, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.25, 3.2734, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.35, 14.4452, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.35, 9.3679, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.35, 5.7963, 1.0e-4), // pag 27 new EuropeanOptionData(Option.Type.Call, 40.00, 42.00, 0.08, 0.04, 0.75, 0.35, 5.0975, 1.0e-4) }; DayCounter dc = new Actual360(); Calendar calendar = new TARGET(); Date today = Date.Today; double discFor = 0.0; double discDom = 0.0; double implVol = 0.0; double deltaCall = 0.0; double deltaPut = 0.0; double expectedDiff = 0.0; double calculatedDiff = 0.0; double error = 0.0; double forward = 0.0; SimpleQuote spotQuote = new SimpleQuote(0.0); SimpleQuote qQuote = new SimpleQuote(0.0); Handle <Quote> qHandle = new Handle <Quote>(qQuote); YieldTermStructure qTS = new FlatForward(today, qHandle, dc); SimpleQuote rQuote = new SimpleQuote(0.0); Handle <Quote> rHandle = new Handle <Quote>(qQuote); YieldTermStructure rTS = new FlatForward(today, rHandle, dc); SimpleQuote volQuote = new SimpleQuote(0.0); Handle <Quote> volHandle = new Handle <Quote>(volQuote); BlackVolTermStructure volTS = new BlackConstantVol(today, calendar, volHandle, dc); StrikedTypePayoff payoff; Date exDate; Exercise exercise; double tolerance = 1.0e-10; for (int i = 0; i < values.Length; ++i) { payoff = new PlainVanillaPayoff(Option.Type.Call, values[i].strike); exDate = today + timeToDays(values[i].t); exercise = new EuropeanExercise(exDate); spotQuote.setValue(values[i].s); volQuote.setValue(values[i].v); rQuote.setValue(values[i].r); qQuote.setValue(values[i].q); discDom = rTS.discount(exDate); discFor = qTS.discount(exDate); implVol = Math.Sqrt(volTS.blackVariance(exDate, 0.0)); forward = spotQuote.value() * discFor / discDom; BlackDeltaCalculator myCalc = new BlackDeltaCalculator(Option.Type.Call, DeltaVolQuote.DeltaType.Spot, spotQuote.value(), discDom, discFor, implVol); deltaCall = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Put); deltaPut = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Call); expectedDiff = discFor; calculatedDiff = deltaCall - deltaPut; error = Math.Abs(expectedDiff - calculatedDiff); if (error > tolerance) { QAssert.Fail("\n Put-call parity failed for spot delta. \n" + "Calculated Call Delta: " + deltaCall + "\n" + "Calculated Put Delta: " + deltaPut + "\n" + "Expected Difference: " + expectedDiff + "\n" + "Calculated Difference: " + calculatedDiff); } myCalc.setDeltaType(DeltaVolQuote.DeltaType.Fwd); deltaCall = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Put); deltaPut = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Call); expectedDiff = 1.0; calculatedDiff = deltaCall - deltaPut; error = Math.Abs(expectedDiff - calculatedDiff); if (error > tolerance) { QAssert.Fail("\n Put-call parity failed for forward delta. \n" + "Calculated Call Delta: " + deltaCall + "\n" + "Calculated Put Delta: " + deltaPut + "\n" + "Expected Difference: " + expectedDiff + "\n" + "Calculated Difference: " + calculatedDiff); } myCalc.setDeltaType(DeltaVolQuote.DeltaType.PaSpot); deltaCall = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Put); deltaPut = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Call); expectedDiff = discFor * values[i].strike / forward; calculatedDiff = deltaCall - deltaPut; error = Math.Abs(expectedDiff - calculatedDiff); if (error > tolerance) { QAssert.Fail("\n Put-call parity failed for premium-adjusted spot delta. \n" + "Calculated Call Delta: " + deltaCall + "\n" + "Calculated Put Delta: " + deltaPut + "\n" + "Expected Difference: " + expectedDiff + "\n" + "Calculated Difference: " + calculatedDiff); } myCalc.setDeltaType(DeltaVolQuote.DeltaType.PaFwd); deltaCall = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Put); deltaPut = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Call); expectedDiff = values[i].strike / forward; calculatedDiff = deltaCall - deltaPut; error = Math.Abs(expectedDiff - calculatedDiff); if (error > tolerance) { QAssert.Fail("\n Put-call parity failed for premium-adjusted forward delta. \n" + "Calculated Call Delta: " + deltaCall + "\n" + "Calculated Put Delta: " + deltaPut + "\n" + "Expected Difference: " + expectedDiff + "\n" + "Calculated Difference: " + calculatedDiff); } } }
public void testFdGreeks <Engine>() where Engine : IFDEngine, new() { //SavedSettings backup; Dictionary <string, double> calculated = new Dictionary <string, double>(), expected = new Dictionary <string, double>(), tolerance = new Dictionary <string, double>(); tolerance.Add("delta", 7.0e-4); tolerance.Add("gamma", 2.0e-4); //tolerance["theta"] = 1.0e-4; Option.Type[] types = new Option.Type[] { Option.Type.Call, Option.Type.Put }; double[] strikes = { 50.0, 99.5, 100.0, 100.5, 150.0 }; double[] underlyings = { 100.0 }; double[] qRates = { 0.04, 0.05, 0.06 }; double[] rRates = { 0.01, 0.05, 0.15 }; int[] years = { 1, 2 }; double[] vols = { 0.11, 0.50, 1.20 }; Date today = Date.Today; Settings.setEvaluationDate(today); DayCounter dc = new Actual360(); SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.0); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.0); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); for (int i = 0; i < types.Length; i++) { for (int j = 0; j < strikes.Length; j++) { for (int k = 0; k < years.Length; k++) { Date exDate = today + new Period(years[k], TimeUnit.Years); Exercise exercise = new AmericanExercise(today, exDate); StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], strikes[j]); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new Engine().factory(stochProcess); VanillaOption option = new VanillaOption(payoff, exercise); 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.Add("delta", option.delta()); calculated.Add("gamma", option.gamma()); //calculated["theta"] = option.theta(); 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.Add("delta", (value_p - value_m) / (2 * du)); expected.Add("gamma", (delta_p - delta_m) / (2 * du)); /* * // perturb date and get theta * Time dT = dc.yearFraction(today-1, today+1); * Settings::instance().setEvaluationDate(today-1); * value_m = option.NPV(); * Settings::instance().setEvaluationDate(today+1); * value_p = option.NPV(); * Settings::instance().setEvaluationDate(today); * expected["theta"] = (value_p - value_m)/dT; */ // compare foreach (string greek in calculated.Keys) { double expct = expected [greek], calcl = calculated[greek], tol = tolerance [greek]; double error = Utilities.relativeError(expct, calcl, u); if (error > tol) { REPORT_FAILURE(greek, payoff, exercise, u, q, r, today, v, expct, calcl, error, tol); } } } calculated.Clear(); expected.Clear(); } } } } } } } }
void testEngineConsistency(EngineType engine, int binomialSteps, int samples, Dictionary <string, double> tolerance, bool testGreeks) { //QL_TEST_START_TIMING Dictionary <string, double> calculated = new Dictionary <string, double>(), expected = new Dictionary <string, double>(); // test options Option.Type[] types = { Option.Type.Call, Option.Type.Put }; double[] strikes = { 75.0, 100.0, 125.0 }; int[] lengths = { 1 }; // test data double[] underlyings = { 100.0 }; double[] qRates = { 0.00, 0.05 }; double[] rRates = { 0.01, 0.05, 0.15 }; double[] vols = { 0.11, 0.50, 1.20 }; DayCounter dc = new Actual360(); Date today = Date.Today; SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote vol = new SimpleQuote(0.0); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); SimpleQuote qRate = new SimpleQuote(0.0); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.0); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); for (int i = 0; i < types.Length; i++) { for (int j = 0; j < strikes.Length; j++) { for (int k = 0; k < lengths.Length; k++) { Date exDate = today + lengths[k] * 360; Exercise exercise = new EuropeanExercise(exDate); StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], strikes[j]); // reference option VanillaOption refOption = makeOption(payoff, exercise, spot, qTS, rTS, volTS, EngineType.Analytic, 0, 0); // option to check VanillaOption option = makeOption(payoff, exercise, spot, qTS, rTS, volTS, engine, binomialSteps, samples); 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); expected.Clear(); calculated.Clear(); // FLOATING_POINT_EXCEPTION expected.Add("value", refOption.NPV()); calculated.Add("value", option.NPV()); if (testGreeks && option.NPV() > spot.value() * 1.0e-5) { expected.Add("delta", refOption.delta()); expected.Add("gamma", refOption.gamma()); expected.Add("theta", refOption.theta()); calculated.Add("delta", option.delta()); calculated.Add("gamma", option.gamma()); calculated.Add("theta", option.theta()); } foreach (string greek in calculated.Keys) { double expct = expected[greek], calcl = calculated[greek], tol = tolerance[greek]; double error = Utilities.relativeError(expct, calcl, u); if (error > tol) { REPORT_FAILURE(greek, payoff, exercise, u, q, r, today, v, expct, calcl, error, tol); } } } } } } } } } }
public void testBaroneAdesiWhaleyValues() { // ("Testing Barone-Adesi and Whaley approximation for American options..."); /* The data below are from * "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 pag 24 * * The following values were replicated only up to the second digit * by the VB code provided by Haug, which was used as base for the * C++ implementation * */ AmericanOptionData[] values = { new AmericanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.15, 0.0206), new AmericanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.15, 1.8771), new AmericanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.15, 10.0089), new AmericanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.25, 0.3159), new AmericanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.25, 3.1280), new AmericanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.25, 10.3919), new AmericanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.35, 0.9495), new AmericanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.35, 4.3777), new AmericanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.35, 11.1679), new AmericanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.15, 0.8208), new AmericanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.15, 4.0842), new AmericanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.15, 10.8087), new AmericanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.25, 2.7437), new AmericanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.25, 6.8015), new AmericanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.25, 13.0170), new AmericanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.35, 5.0063), new AmericanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.35, 9.5106), new AmericanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.35, 15.5689), new AmericanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.15, 10.0000), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.15, 1.8770), new AmericanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.15, 0.0410), new AmericanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.25, 10.2533), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.25, 3.1277), new AmericanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.25, 0.4562), new AmericanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.35, 10.8787), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.35, 4.3777), new AmericanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.35, 1.2402), new AmericanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.15, 10.5595), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.15, 4.0842), new AmericanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.15, 1.0822), new AmericanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.25, 12.4419), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.25, 6.8014), new AmericanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.25, 3.3226), new AmericanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.35, 14.6945), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.35, 9.5104), new AmericanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.35, 5.8823) }; Date today = Date.Today; DayCounter dc = new Actual360(); SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.0); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.0); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); double tolerance = 3.0e-3; for (int i = 0; i < values.Length; i++) { StrikedTypePayoff payoff = new PlainVanillaPayoff(values[i].type, values[i].strike); Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise exercise = new AmericanExercise(today, exDate); spot.setValue(values[i].s); qRate.setValue(values[i].q); rRate.setValue(values[i].r); vol.setValue(values[i].v); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new BaroneAdesiWhaleyApproximationEngine(stochProcess); VanillaOption option = new VanillaOption(payoff, exercise); option.setPricingEngine(engine); double calculated = option.NPV(); double error = Math.Abs(calculated - values[i].result); if (error > tolerance) { REPORT_FAILURE("value", payoff, exercise, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].result, calculated, error, tolerance); } } }
public void testBjerksundStenslandValues() { // ("Testing Bjerksund and Stensland approximation for American options..."); AmericanOptionData[] values = new AmericanOptionData[] { // type, strike, spot, q, r, t, vol, value, tol // from "Option pricing formulas", Haug, McGraw-Hill 1998, pag 27 new AmericanOptionData(Option.Type.Call, 40.00, 42.00, 0.08, 0.04, 0.75, 0.35, 5.2704), // from "Option pricing formulas", Haug, McGraw-Hill 1998, VBA code new AmericanOptionData(Option.Type.Put, 40.00, 36.00, 0.00, 0.06, 1.00, 0.20, 4.4531), // ATM option with very small volatility, reference value taken from R new AmericanOptionData(Option.Type.Call, 100, 100, 0.05, 0.05, 1.0, 0.0021, 0.08032314), // ATM option with very small volatility, // reference value taken from Barone-Adesi and Whaley Approximation new AmericanOptionData(Option.Type.Call, 100, 100, 0.05, 0.05, 1.0, 0.0001, 0.003860656), new AmericanOptionData(Option.Type.Call, 100, 99.99, 0.05, 0.05, 1.0, 0.0001, 0.00081), // ITM option with a very small volatility new AmericanOptionData(Option.Type.Call, 100, 110, 0.05, 0.05, 1.0, 0.0001, 10.0), new AmericanOptionData(Option.Type.Put, 110, 100, 0.05, 0.05, 1.0, 0.0001, 10.0), // ATM option with a very large volatility new AmericanOptionData(Option.Type.Put, 100, 110, 0.05, 0.05, 1.0, 10, 94.89543) }; Date today = Date.Today; DayCounter dc = new Actual360(); SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.0); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.0); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); double tolerance = 5.0e-5; for (int i = 0; i < values.Length; i++) { StrikedTypePayoff payoff = new PlainVanillaPayoff(values[i].type, values[i].strike); Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise exercise = new AmericanExercise(today, exDate); spot.setValue(values[i].s); qRate.setValue(values[i].q); rRate.setValue(values[i].r); vol.setValue(values[i].v); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new BjerksundStenslandApproximationEngine(stochProcess); VanillaOption option = new VanillaOption(payoff, exercise); option.setPricingEngine(engine); double calculated = option.NPV(); double error = Math.Abs(calculated - values[i].result); if (error > tolerance) { REPORT_FAILURE("value", payoff, exercise, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].result, calculated, error, tolerance); } } }
public override void calculate() { DayCounter rfdc = process_.riskFreeRate().link.dayCounter(); DayCounter divdc = process_.dividendYield().link.dayCounter(); DayCounter voldc = process_.blackVolatility().link.dayCounter(); Calendar volcal = process_.blackVolatility().link.calendar(); double s0 = process_.x0(); Utils.QL_REQUIRE(s0 > 0.0, () => "negative or null underlying"); double v = process_.blackVolatility().link.blackVol(arguments_.exercise.lastDate(), s0); Date maturityDate = arguments_.exercise.lastDate(); double riskFreeRate = process_.riskFreeRate().link.zeroRate(maturityDate, rfdc, Compounding.Continuous, Frequency.NoFrequency).value(); double q = process_.dividendYield().link.zeroRate(maturityDate, divdc, Compounding.Continuous, Frequency.NoFrequency).value(); Date referenceDate = process_.riskFreeRate().link.referenceDate(); // substract dividends ConvertibleBond.option.Arguments args = arguments_ as ConvertibleBond.option.Arguments; for (int i = 0; i < args.dividends.Count; i++) { if (args.dividends[i].date() >= referenceDate) { s0 -= args.dividends[i].amount() * process_.riskFreeRate().link.discount(args.dividends[i].date()); } Utils.QL_REQUIRE(s0 > 0.0, () => "negative value after substracting dividends"); } // binomial trees with constant coefficient Handle <Quote> underlying = new Handle <Quote>(new SimpleQuote(s0)); Handle <YieldTermStructure> flatRiskFree = new Handle <YieldTermStructure>(new FlatForward(referenceDate, riskFreeRate, rfdc)); Handle <YieldTermStructure> flatDividends = new Handle <YieldTermStructure>(new FlatForward(referenceDate, q, divdc)); Handle <BlackVolTermStructure> flatVol = new Handle <BlackVolTermStructure>(new BlackConstantVol(referenceDate, volcal, v, voldc)); PlainVanillaPayoff payoff = args.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => " non-plain payoff given "); double maturity = rfdc.yearFraction(args.settlementDate, maturityDate); GeneralizedBlackScholesProcess bs = new GeneralizedBlackScholesProcess(underlying, flatDividends, flatRiskFree, flatVol); T Tree = new T().factory(bs, maturity, timeSteps_, payoff.strike()); double creditSpread = args.creditSpread.link.value(); TsiveriotisFernandesLattice <T> lattice = new TsiveriotisFernandesLattice <T>(Tree, riskFreeRate, maturity, timeSteps_, creditSpread, v, q); DiscretizedConvertible convertible = new DiscretizedConvertible(args, bs, new TimeGrid(maturity, timeSteps_)); convertible.initialize(lattice, maturity); convertible.rollback(0.0); results_.value = convertible.presentValue(); Utils.QL_REQUIRE(results_.value < double.MaxValue, () => "floating-point overflow on tree grid"); }
static void Main(string[] args) { DateTime timer = DateTime.Now; //////////////// DATES ////////////////////////////////////////////// Calendar calendar = new TARGET(); Date todaysDate = new Date(15, Month.January, 2017); Date settlementDate = new Date(todaysDate); Settings.setEvaluationDate(todaysDate); DayCounter dayCounter = new Actual365Fixed(); //////////////// MARKET ////////////////////////////////////////////// // Spot double underlying = 4468.17; Handle <Quote> underlyingH = new Handle <Quote>(new SimpleQuote(underlying)); // riskfree double riskFreeRate = 0.035; Handle <YieldTermStructure> flatTermStructure = new Handle <YieldTermStructure>(new FlatForward(settlementDate, riskFreeRate, dayCounter)); // dividend double dividendYield = 0.0; double fixedDiv = 5.0; Handle <YieldTermStructure> flatDividendTS = new Handle <YieldTermStructure>(new FlatForward(settlementDate, dividendYield, dayCounter)); Handle <YieldTermStructure> FixedDivTermStructure = new Handle <YieldTermStructure>(new FixedForward(settlementDate, fixedDiv, underlying, dayCounter)); // vol surface Date StartDateVol = settlementDate + new Period(1, TimeUnit.Months); List <int> maturityInDays = new InitializedList <int>() { 0, 13, 41, 90, 165, 256, 345, 524, 703 }; List <Date> datesVol = new InitializedList <Date>(); for (int d = 1; d < maturityInDays.Count; d++) { datesVol.Add(calendar.advance(settlementDate, new Period(maturityInDays[d], TimeUnit.Days))); } List <double> strikes = new InitializedList <double>() { 3400, 3600, 3800, 4000, 4200, 4400, 4500, 4600, 4800, 5000, 5200, 5400, 5600 }; Matrix blackVolMatrix = new Matrix(maturityInDays.Count - 1, strikes.Count, 0.2); var vols = new InitializedList <double>() { 0.6625, 0.4875, 0.4204, 0.3667, 0.3431, 0.3267, 0.3121, 0.3121, 0.6007, 0.4543, 0.3967, 0.3511, 0.3279, 0.3154, 0.2984, 0.2921, 0.5084, 0.4221, 0.3718, 0.3327, 0.3155, 0.3027, 0.2919, 0.2889, 0.4541, 0.3869, 0.3492, 0.3149, 0.2963, 0.2926, 0.2819, 0.2800, 0.4060, 0.3607, 0.3330, 0.2999, 0.2887, 0.2811, 0.2751, 0.2775, 0.3726, 0.3396, 0.3108, 0.2781, 0.2788, 0.2722, 0.2661, 0.2686, 0.3550, 0.3277, 0.3012, 0.2781, 0.2781, 0.2661, 0.2661, 0.2681, 0.3428, 0.3209, 0.2958, 0.2740, 0.2688, 0.2627, 0.2580, 0.2620, 0.3302, 0.3062, 0.2799, 0.2631, 0.2573, 0.2533, 0.2504, 0.2544, 0.3343, 0.2959, 0.2705, 0.2540, 0.2504, 0.2464, 0.2448, 0.2462, 0.3460, 0.2845, 0.2624, 0.2463, 0.2425, 0.2385, 0.2373, 0.2422, 0.3857, 0.2860, 0.2578, 0.2399, 0.2357, 0.2327, 0.2312, 0.2351, 0.3976, 0.2860, 0.2607, 0.2356, 0.2297, 0.2268, 0.2241, 0.2320 }; for (int i = 0; i < vols.Count; i++) { int testraw = (int)(i % (datesVol.Count)); int testcol = (int)(i / (datesVol.Count)); blackVolMatrix[testraw, testcol] = vols[i]; } BlackVarianceSurface mySurface = new BlackVarianceSurface(settlementDate, calendar, datesVol, strikes, Matrix.transpose(blackVolMatrix), dayCounter); Handle <BlackVolTermStructure> mySurfaceH = new Handle <BlackVolTermStructure>(mySurface); //////////////// CALIBRATION ////////////////////////////////////////////// Period helperPeriod = new Period(); //helpers List <CalibrationHelper> calibrationHelpers = new List <CalibrationHelper>(); for (int k = 0; k < strikes.Count; k++) { for (int d = 0; d < datesVol.Count; d++) { helperPeriod = new Period(datesVol[d] - settlementDate, TimeUnit.Days); calibrationHelpers.Add(new HestonModelHelper(helperPeriod, calendar, underlying, strikes[k], new Handle <Quote>(new SimpleQuote(blackVolMatrix[d, k])), flatTermStructure, flatDividendTS, CalibrationHelper.CalibrationErrorType.ImpliedVolError)); } } // starting data double v0 = 0.1; double kappa = 1.0; double theta = 0.1; double sigma = 0.5; double rho = -0.5; // model HestonProcess hestonProcess = new HestonProcess(flatTermStructure, flatDividendTS, underlyingH, v0, kappa, theta, sigma, rho); HestonModel hestonmodel = new HestonModel(hestonProcess); AnalyticHestonEngine analyticHestonEngine = new AnalyticHestonEngine(hestonmodel); foreach (HestonModelHelper hmh in calibrationHelpers) { hmh.setPricingEngine(analyticHestonEngine); } // optimization double tolerance = 1.0e-8; LevenbergMarquardt optimizationmethod = new LevenbergMarquardt(tolerance, tolerance, tolerance); hestonmodel.calibrate(calibrationHelpers, optimizationmethod, new EndCriteria(400, 40, tolerance, tolerance, tolerance)); double error = 0.0; List <double> errorList = new InitializedList <double>(); //////////////// CALIBRATION RESULTS ////////////////////////////////////////////// Console.WriteLine("Calbration :"); Console.WriteLine("-----------"); foreach (HestonModelHelper hmh in calibrationHelpers) { error += Math.Abs(hmh.calibrationError()); errorList.Add(Math.Abs(hmh.calibrationError())); } Vector hestonParameters = hestonmodel.parameters(); Console.WriteLine("v0 = {0:0.00%}", hestonParameters[4]); Console.WriteLine("kappa = {0:0.00%}", hestonParameters[1]); Console.WriteLine("theta = {0:0.00%}", hestonParameters[0]); Console.WriteLine("sigma = {0:0.00%}", hestonParameters[2]); Console.WriteLine("rho = {0:0.00%}", hestonParameters[3]); Console.WriteLine(); Console.WriteLine("Total error = {0:0.0000}", error); Console.WriteLine("Mean error = {0:0.0000%}", error / (errorList.Count - 1)); Console.WriteLine(); int StepsPerYear = 52; double absoluteTolerance = 80.0; ulong mcSeed = 42; // MC Heston process HestonProcess calibratedHestonProcess = new HestonProcess(flatTermStructure, flatDividendTS, underlyingH, hestonParameters[4], hestonParameters[1], hestonParameters[0], hestonParameters[2], hestonParameters[3]); // BS process GeneralizedBlackScholesProcessTolerance bsmProcess = new GeneralizedBlackScholesProcessTolerance(underlyingH, FixedDivTermStructure, flatTermStructure, mySurfaceH); //////////////// ENGINES ///////////////////////////////////////////////// IPricingEngine mcHestonEngine = new MakeMCEuropeanHestonEngine <PseudoRandom, Statistics>(calibratedHestonProcess) .withStepsPerYear(StepsPerYear) .withAbsoluteTolerance(absoluteTolerance) .withSeed(mcSeed) .getAsPricingEngine(); double absoluteTolerance2 = 1.0; IPricingEngine mcGenHestonEngineTestbs = new MakeMCGenericScriptInstrument <PseudoRandom>(bsmProcess) .withStepsPerYear(StepsPerYear) .withAbsoluteTolerance(absoluteTolerance2) .withSeed(mcSeed) .value(); IPricingEngine mcGenHestonEngineTestbs2 = new MakeMCGenericScriptInstrument <PseudoRandom>(calibratedHestonProcess) .withStepsPerYear(StepsPerYear) .withAbsoluteTolerance(absoluteTolerance2) .withSeed(mcSeed) .value(); //////////////// PRICING ////////////////////////////////////////////// Console.WriteLine("Pricing Vanilla:"); Console.WriteLine("---------------"); Date maturity = new Date(17, Month.May, 2019); Exercise europeanExercise = new EuropeanExercise(maturity); Option.Type type = Option.Type.Call; double strike = underlying; StrikedTypePayoff payoff = new PlainVanillaPayoff(type, strike); VanillaOption europeanOption = new VanillaOption(payoff, europeanExercise); // heston europeanOption.setPricingEngine(analyticHestonEngine); Console.Write("Heston pricing = {0:0.0000}", europeanOption.NPV()); Console.WriteLine(" -> {0:0.0000%}", europeanOption.NPV() / underlying); // Mc heston europeanOption.setPricingEngine(mcHestonEngine); Console.Write("HestMC pricing = {0:0.0000}", europeanOption.NPV()); Console.Write(" -> {0:0.0000%}", europeanOption.NPV() / underlying); Console.WriteLine(" tolerance {0:0.0} / {1:0.00%}", absoluteTolerance, absoluteTolerance / underlying); // analytic bs europeanOption.setPricingEngine(new AnalyticEuropeanEngine(bsmProcess)); Console.Write("BS pricing = {0:0.0000}", europeanOption.NPV()); Console.WriteLine(" -> {0:0.0000%}", europeanOption.NPV() / underlying); Console.WriteLine(); //////////////// AUTOCALL HESTON ////////////////////////////////////////////// List <Date> fixingdates = new InitializedList <Date>(); double coupon = 0.05; double barrierlvl = 0.6; for (int i = 1; i <= 4; i++) { fixingdates.Add(settlementDate + new Period(i, TimeUnit.Years)); } ScriptGenericAutocall myGenericAutocallHTTEst = new ScriptGenericAutocall(fixingdates, coupon, barrierlvl, underlying); myGenericAutocallHTTEst.setPricingEngine(mcGenHestonEngineTestbs); Console.WriteLine("Pricing Autocall BS :"); Console.WriteLine("---------------------"); Console.WriteLine("test = {0:0.0000}", myGenericAutocallHTTEst.NPV()); Console.WriteLine("Err = {0:0.0000%}", myGenericAutocallHTTEst.errorEstimate() / myGenericAutocallHTTEst.NPV()); Console.WriteLine("Samples = {0}", myGenericAutocallHTTEst.samples()); Console.Write("\n"); for (int i = 0; i < 4; i++) { Console.WriteLine("ProbaCall {1} = {0:0.0000%}", myGenericAutocallHTTEst.inspout("ProbaCall " + i), i + 1); } Console.WriteLine("ProbaMid = {0:0.0000%}", myGenericAutocallHTTEst.inspout("ProbaMid")); Console.WriteLine("probaDown = {0:0.0000%}", myGenericAutocallHTTEst.inspout("ProbaDown")); Console.WriteLine("test = {0:0.0000%}", myGenericAutocallHTTEst.inspout("ProbaDown")); Console.WriteLine("AvgDown/Proba = {0:0.0000%}", myGenericAutocallHTTEst.inspout("AvgDown") / myGenericAutocallHTTEst.inspout("ProbaDown")); Console.Write("\n"); myGenericAutocallHTTEst.setPricingEngine(mcGenHestonEngineTestbs2); Console.WriteLine("Pricing Autocall Heston:"); Console.WriteLine("------------------------"); Console.WriteLine("test = {0:0.0000}", myGenericAutocallHTTEst.NPV()); Console.WriteLine("Err = {0:0.0000%}", myGenericAutocallHTTEst.errorEstimate() / myGenericAutocallHTTEst.NPV()); Console.WriteLine("Samples = {0}", myGenericAutocallHTTEst.samples()); Console.Write("\n"); for (int i = 0; i < 4; i++) { Console.WriteLine("ProbaCall {1} = {0:0.0000%}", myGenericAutocallHTTEst.inspout("ProbaCall " + i), i + 1); } Console.WriteLine("ProbaMid = {0:0.0000%}", myGenericAutocallHTTEst.inspout("ProbaMid")); Console.WriteLine("probaDown = {0:0.0000%}", myGenericAutocallHTTEst.inspout("ProbaDown")); Console.WriteLine("test = {0:0.0000%}", myGenericAutocallHTTEst.inspout("ProbaDown")); Console.WriteLine("AvgDown/Proba = {0:0.0000%}", myGenericAutocallHTTEst.inspout("AvgDown") / myGenericAutocallHTTEst.inspout("ProbaDown")); Console.Write("\n"); //////////////// END TEST ////////////////////////////////////////////// Console.WriteLine(); Console.WriteLine(" \nRun completed in {0}", DateTime.Now - timer); Console.WriteLine(); Console.Write("Press any key to continue ..."); Console.ReadKey(); }
private void testForwardGreeks(Type engine_type) { Dictionary <String, double> calculated = new Dictionary <string, double>(), expected = new Dictionary <string, double>(), tolerance = new Dictionary <string, double>(); 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[] moneyness = { 0.9, 1.0, 1.1 }; double[] underlyings = { 100.0 }; double[] qRates = { 0.04, 0.05, 0.06 }; double[] rRates = { 0.01, 0.05, 0.15 }; int[] lengths = { 1, 2 }; int[] startMonths = { 6, 9 }; 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 stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), qTS, rTS, volTS); IPricingEngine engine = engine_type == typeof(ForwardVanillaEngine) ? new ForwardVanillaEngine(stochProcess, process => new AnalyticEuropeanEngine(process)) : new ForwardPerformanceVanillaEngine(stochProcess, process => new AnalyticEuropeanEngine(process)); for (int i = 0; i < types.Length; i++) { for (int j = 0; j < moneyness.Length; j++) { for (int k = 0; k < lengths.Length; k++) { for (int h = 0; h < startMonths.Length; h++) { Date exDate = today + new Period(lengths[k], TimeUnit.Years); Exercise exercise = new EuropeanExercise(exDate); Date reset = today + new Period(startMonths[h], TimeUnit.Months); StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], 0.0); ForwardVanillaOption option = new ForwardVanillaOption(moneyness[j], reset, payoff, exercise); 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 //std::map<std::string,double>::iterator it; foreach (KeyValuePair <string, double> it in calculated) { String greek = it.Key; double expct = expected [greek], calcl = calculated[greek], tol = tolerance [greek]; double error = Utilities.relativeError(expct, calcl, u); if (error > tol) { REPORT_FAILURE(greek, payoff, exercise, u, q, r, today, v, moneyness[j], reset, expct, calcl, error, tol); } } } } } } } } } } } }
public void testDeltaPriceConsistency() { // Testing premium-adjusted delta price consistency // This function tests for price consistencies with the standard // Black Scholes calculator, since premium adjusted deltas can be calculated // from spot deltas by adding/subtracting the premium. SavedSettings backup = new SavedSettings(); // actually, value and tol won't be needed for testing EuropeanOptionData[] values = { // type, strike, spot, rd, rf, t, vol, value, tol new EuropeanOptionData(Option.Type.Call, 0.9123, 1.2212, 0.0231, 0.0000, 0.25, 0.301, 0.0, 0.0), new EuropeanOptionData(Option.Type.Call, 0.9234, 1.2212, 0.0231, 0.0000, 0.35, 0.111, 0.0, 0.0), new EuropeanOptionData(Option.Type.Call, 0.9783, 1.2212, 0.0231, 0.0000, 0.45, 0.071, 0.0, 0.0), new EuropeanOptionData(Option.Type.Call, 1.0000, 1.2212, 0.0231, 0.0000, 0.55, 0.082, 0.0, 0.0), new EuropeanOptionData(Option.Type.Call, 1.1230, 1.2212, 0.0231, 0.0000, 0.65, 0.012, 0.0, 0.0), new EuropeanOptionData(Option.Type.Call, 1.2212, 1.2212, 0.0231, 0.0000, 0.75, 0.129, 0.0, 0.0), new EuropeanOptionData(Option.Type.Call, 1.3212, 1.2212, 0.0231, 0.0000, 0.85, 0.034, 0.0, 0.0), new EuropeanOptionData(Option.Type.Call, 1.3923, 1.2212, 0.0131, 0.2344, 0.95, 0.001, 0.0, 0.0), new EuropeanOptionData(Option.Type.Call, 1.3455, 1.2212, 0.0000, 0.0000, 1.00, 0.127, 0.0, 0.0), new EuropeanOptionData(Option.Type.Put, 0.9123, 1.2212, 0.0231, 0.0000, 0.25, 0.301, 0.0, 0.0), new EuropeanOptionData(Option.Type.Put, 0.9234, 1.2212, 0.0231, 0.0000, 0.35, 0.111, 0.0, 0.0), new EuropeanOptionData(Option.Type.Put, 0.9783, 1.2212, 0.0231, 0.0000, 0.45, 0.071, 0.0, 0.0), new EuropeanOptionData(Option.Type.Put, 1.0000, 1.2212, 0.0231, 0.0000, 0.55, 0.082, 0.0, 0.0), new EuropeanOptionData(Option.Type.Put, 1.1230, 1.2212, 0.0231, 0.0000, 0.65, 0.012, 0.0, 0.0), new EuropeanOptionData(Option.Type.Put, 1.2212, 1.2212, 0.0231, 0.0000, 0.75, 0.129, 0.0, 0.0), new EuropeanOptionData(Option.Type.Put, 1.3212, 1.2212, 0.0231, 0.0000, 0.85, 0.034, 0.0, 0.0), new EuropeanOptionData(Option.Type.Put, 1.3923, 1.2212, 0.0131, 0.2344, 0.95, 0.001, 0.0, 0.0), new EuropeanOptionData(Option.Type.Put, 1.3455, 1.2212, 0.0000, 0.0000, 1.00, 0.127, 0.0, 0.0), // extreme case: zero vol new EuropeanOptionData(Option.Type.Put, 1.3455, 1.2212, 0.0000, 0.0000, 0.50, 0.000, 0.0, 0.0), // extreme case: zero strike new EuropeanOptionData(Option.Type.Put, 0.0000, 1.2212, 0.0000, 0.0000, 1.50, 0.133, 0.0, 0.0), // extreme case: zero strike+zero vol new EuropeanOptionData(Option.Type.Put, 0.0000, 1.2212, 0.0000, 0.0000, 1.00, 0.133, 0.0, 0.0), }; DayCounter dc = new Actual360(); Calendar calendar = new TARGET(); Date today = Date.Today; // Start setup of market data double discFor = 0.0; double discDom = 0.0; double implVol = 0.0; double expectedVal = 0.0; double calculatedVal = 0.0; double error = 0.0; SimpleQuote spotQuote = new SimpleQuote(0.0); Handle <Quote> spotHandle = new Handle <Quote>(spotQuote); SimpleQuote qQuote = new SimpleQuote(0.0); Handle <Quote> qHandle = new Handle <Quote>(qQuote); YieldTermStructure qTS = new FlatForward(today, qHandle, dc); SimpleQuote rQuote = new SimpleQuote(0.0); Handle <Quote> rHandle = new Handle <Quote>(qQuote); YieldTermStructure rTS = new FlatForward(today, rHandle, dc); SimpleQuote volQuote = new SimpleQuote(0.0); Handle <Quote> volHandle = new Handle <Quote>(volQuote); BlackVolTermStructure volTS = new BlackConstantVol(today, calendar, volHandle, dc); BlackScholesMertonProcess stochProcess; IPricingEngine engine; StrikedTypePayoff payoff; Date exDate; Exercise exercise; // Setup of market data finished double tolerance = 1.0e-10; for (int i = 0; i < values.Length; ++i) { payoff = new PlainVanillaPayoff(values[i].type, values[i].strike); exDate = today + timeToDays(values[i].t); exercise = new EuropeanExercise(exDate); spotQuote.setValue(values[i].s); volQuote.setValue(values[i].v); rQuote.setValue(values[i].r); qQuote.setValue(values[i].q); discDom = rTS.discount(exDate); discFor = qTS.discount(exDate); implVol = Math.Sqrt(volTS.blackVariance(exDate, 0.0)); BlackDeltaCalculator myCalc = new BlackDeltaCalculator(values[i].type, DeltaVolQuote.DeltaType.PaSpot, spotQuote.value(), discDom, discFor, implVol); stochProcess = new BlackScholesMertonProcess(spotHandle, new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); engine = new AnalyticEuropeanEngine(stochProcess); EuropeanOption option = new EuropeanOption(payoff, exercise); option.setPricingEngine(engine); calculatedVal = myCalc.deltaFromStrike(values[i].strike); expectedVal = option.delta() - option.NPV() / spotQuote.value(); error = Math.Abs(expectedVal - calculatedVal); if (error > tolerance) { QAssert.Fail("\n Premium-adjusted spot delta test failed. \n" + "Calculated Delta: " + calculatedVal + "\n" + "Expected Value: " + expectedVal + "\n" + "Error: " + error); } myCalc.setDeltaType(DeltaVolQuote.DeltaType.PaFwd); calculatedVal = myCalc.deltaFromStrike(values[i].strike); expectedVal = expectedVal / discFor; // Premium adjusted Fwd Delta is PA spot without discount error = Math.Abs(expectedVal - calculatedVal); if (error > tolerance) { QAssert.Fail("\n Premium-adjusted forward delta test failed. \n" + "Calculated Delta: " + calculatedVal + "\n" + "Expected Value: " + expectedVal + "\n" + "Error: " + error); } // Test consistency with BlackScholes Calculator for Spot Delta myCalc.setDeltaType(DeltaVolQuote.DeltaType.Spot); calculatedVal = myCalc.deltaFromStrike(values[i].strike); expectedVal = option.delta(); error = Math.Abs(calculatedVal - expectedVal); if (error > tolerance) { QAssert.Fail("\n spot delta in BlackDeltaCalculator differs from delta in BlackScholesCalculator. \n" + "Calculated Value: " + calculatedVal + "\n" + "Expected Value: " + expectedVal + "\n" + "Error: " + error); } } }
public void testGreeksInitialization() { // Testing forward option greeks initialization DayCounter dc = new Actual360(); SavedSettings backup = new SavedSettings(); Date today = Date.Today; Settings.setEvaluationDate(today); SimpleQuote spot = new SimpleQuote(100.0); SimpleQuote qRate = new SimpleQuote(0.04); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(qRate, dc)); SimpleQuote rRate = new SimpleQuote(0.01); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(rRate, dc)); SimpleQuote vol = new SimpleQuote(0.11); Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(vol, dc)); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), qTS, rTS, volTS); IPricingEngine engine = new ForwardVanillaEngine(stochProcess, process => new TestBinomialEngine(process)); Date exDate = today + new Period(1, TimeUnit.Years); Exercise exercise = new EuropeanExercise(exDate); Date reset = today + new Period(6, TimeUnit.Months); StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Call, 0.0); ForwardVanillaOption option = new ForwardVanillaOption(0.9, reset, payoff, exercise); option.setPricingEngine(engine); IPricingEngine ctrlengine = new TestBinomialEngine(stochProcess); VanillaOption ctrloption = new VanillaOption(payoff, exercise); ctrloption.setPricingEngine(ctrlengine); double?delta = 0; try { delta = ctrloption.delta(); } catch (Exception) { // if normal option can't calculate delta, // nor should forward try { delta = option.delta(); } catch (Exception) { delta = null; } Utils.QL_REQUIRE(delta == null, () => "Forward delta invalid"); } double?rho = 0; try { rho = ctrloption.rho(); } catch (Exception) { // if normal option can't calculate rho, // nor should forward try { rho = option.rho(); } catch (Exception) { rho = null; } Utils.QL_REQUIRE(rho == null, () => "Forward rho invalid"); } double?divRho = 0; try { divRho = ctrloption.dividendRho(); } catch (Exception) { // if normal option can't calculate divRho, // nor should forward try { divRho = option.dividendRho(); } catch (Exception) { divRho = null; } Utils.QL_REQUIRE(divRho == null, () => "Forward dividendRho invalid"); } double?vega = 0; try { vega = ctrloption.vega(); } catch (Exception) { // if normal option can't calculate vega, // nor should forward try { vega = option.vega(); } catch (Exception) { vega = null; } Utils.QL_REQUIRE(vega == null, () => "Forward vega invalid"); } }
public void testCompareBsmHWandHestonHW() { // Comparing European option pricing for a BSM process with one-factor Hull-White model DayCounter dc = new Actual365Fixed(); Date today = Date.Today; Settings.setEvaluationDate(today); Handle <Quote> spot = new Handle <Quote>(new SimpleQuote(100.0)); List <Date> dates = new List <Date>(); List <double> rates = new List <double>(), divRates = new List <double>(); for (int i = 0; i <= 40; ++i) { dates.Add(today + new Period(i, TimeUnit.Years)); // FLOATING_POINT_EXCEPTION rates.Add(0.01 + 0.0002 * Math.Exp(Math.Sin(i / 4.0))); divRates.Add(0.02 + 0.0001 * Math.Exp(Math.Sin(i / 5.0))); } Handle <Quote> s0 = new Handle <Quote>(new SimpleQuote(100)); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>( new InterpolatedZeroCurve <Linear>(dates, rates, dc)); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>( new InterpolatedZeroCurve <Linear>(dates, divRates, dc)); SimpleQuote vol = new SimpleQuote(0.25); Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(today, vol, dc)); BlackScholesMertonProcess bsmProcess = new BlackScholesMertonProcess(spot, qTS, rTS, volTS); HestonProcess hestonProcess = new HestonProcess(rTS, qTS, spot, vol.value() * vol.value(), 1.0, vol.value() * vol.value(), 1e-4, 0.0); HestonModel hestonModel = new HestonModel(hestonProcess); HullWhite hullWhiteModel = new HullWhite(new Handle <YieldTermStructure>(rTS), 0.01, 0.01); IPricingEngine bsmhwEngine = new AnalyticBSMHullWhiteEngine(0.0, bsmProcess, hullWhiteModel); IPricingEngine hestonHwEngine = new AnalyticHestonHullWhiteEngine(hestonModel, hullWhiteModel, 128); double tol = 1e-5; double[] strike = { 0.25, 0.5, 0.75, 0.8, 0.9, 1.0, 1.1, 1.2, 1.5, 2.0, 4.0 }; int[] maturity = { 1, 2, 3, 5, 10, 15, 20, 25, 30 }; Option.Type[] types = { Option.Type.Put, Option.Type.Call }; for (int i = 0; i < types.Length; ++i) { for (int j = 0; j < strike.Length; ++j) { for (int l = 0; l < maturity.Length; ++l) { Date maturityDate = today + new Period(maturity[l], TimeUnit.Years); Exercise exercise = new EuropeanExercise(maturityDate); double fwd = strike[j] * spot.link.value() * qTS.link.discount(maturityDate) / rTS.link.discount(maturityDate); StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], fwd); EuropeanOption option = new EuropeanOption(payoff, exercise); option.setPricingEngine(bsmhwEngine); double calculated = option.NPV(); option.setPricingEngine(hestonHwEngine); double expected = option.NPV(); if (Math.Abs(calculated - expected) > calculated * tol && Math.Abs(calculated - expected) > tol) { QAssert.Fail("Failed to reproduce npvs" + "\n calculated: " + calculated + "\n expected : " + expected + "\n strike : " + strike[j] + "\n maturity : " + maturity[l] + "\n type : " + ((types[i] == QLNet.Option.Type.Put) ? "Put" : "Call")); } } } } }
public void testValues() { // Testing forward option values... /* The data below are from * "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 */ ForwardOptionData[] values = { // type, moneyness, spot, div, rate,start, t, vol, result, tol // "Option pricing formulas", pag. 37 new ForwardOptionData(Option.Type.Call, 1.1, 60.0, 0.04, 0.08, 0.25, 1.0, 0.30, 4.4064, 1.0e-4), // "Option pricing formulas", VBA code new ForwardOptionData(Option.Type.Put, 1.1, 60.0, 0.04, 0.08, 0.25, 1.0, 0.30, 8.2971, 1.0e-4) }; DayCounter dc = new Actual360(); Date today = Date.Today; SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, qRate, dc)); SimpleQuote rRate = new SimpleQuote(0.0); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, rRate, dc)); SimpleQuote vol = new SimpleQuote(0.0); Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(today, vol, dc)); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new ForwardVanillaEngine(stochProcess, process => new AnalyticEuropeanEngine(process)); // AnalyticEuropeanEngine for (int i = 0; i < values.Length; i++) { StrikedTypePayoff payoff = new PlainVanillaPayoff(values[i].type, 0.0); Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise exercise = new EuropeanExercise(exDate); Date reset = today + Convert.ToInt32(values[i].start * 360 + 0.5); spot.setValue(values[i].s); qRate.setValue(values[i].q); rRate.setValue(values[i].r); vol.setValue(values[i].v); ForwardVanillaOption option = new ForwardVanillaOption(values[i].moneyness, reset, payoff, exercise); option.setPricingEngine(engine); double calculated = option.NPV(); double error = Math.Abs(calculated - values[i].result); double tolerance = 1e-4; if (error > tolerance) { REPORT_FAILURE("value", payoff, exercise, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].moneyness, reset, values[i].result, calculated, error, tolerance); } } }
public void testAnalyticHestonHullWhitePricing() { // Testing analytic Heston Hull-White option pricing DayCounter dc = new Actual360(); Date today = Date.Today; Settings.setEvaluationDate(today); // construct a strange yield curve to check drifts and discounting // of the joint stochastic process List <Date> dates = new List <Date>(); List <double> times = new List <double>(); List <double> rates = new List <double>(), divRates = new List <double>(); for (int i = 0; i <= 40; ++i) { dates.Add(today + new Period(i, TimeUnit.Years)); // FLOATING_POINT_EXCEPTION rates.Add(0.03 + 0.0001 * Math.Exp(Math.Sin(i / 4.0))); divRates.Add(0.02 + 0.0002 * Math.Exp(Math.Sin(i / 3.0))); times.Add(dc.yearFraction(today, dates.Last())); } Date maturity = today + new Period(5, TimeUnit.Years); Handle <Quote> s0 = new Handle <Quote>(new SimpleQuote(100)); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, rates, dc)); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, divRates, dc)); HestonProcess hestonProcess = new HestonProcess(rTS, qTS, s0, 0.08, 1.5, 0.0625, 0.5, -0.8); HestonModel hestonModel = new HestonModel(hestonProcess); HullWhiteForwardProcess hwFwdProcess = new HullWhiteForwardProcess(rTS, 0.01, 0.01); hwFwdProcess.setForwardMeasureTime(dc.yearFraction(today, maturity)); HullWhite hullWhiteModel = new HullWhite(rTS, hwFwdProcess.a(), hwFwdProcess.sigma()); double tol = 0.002; double[] strike = { 80, 120 }; Option.Type[] types = { Option.Type.Put, Option.Type.Call }; for (int i = 0; i < types.Length; ++i) { for (int j = 0; j < strike.Length; ++j) { HybridHestonHullWhiteProcess jointProcess = new HybridHestonHullWhiteProcess(hestonProcess, hwFwdProcess, 0.0, HybridHestonHullWhiteProcess.Discretization.Euler); StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], strike[j]); Exercise exercise = new EuropeanExercise(maturity); VanillaOption optionHestonHW = new VanillaOption(payoff, exercise); optionHestonHW.setPricingEngine(new MakeMCHestonHullWhiteEngine <PseudoRandom, Statistics>(jointProcess) .withSteps(1) .withAntitheticVariate() .withControlVariate() .withAbsoluteTolerance(tol) .withSeed(42).getAsPricingEngine()); VanillaOption optionPureHeston = new VanillaOption(payoff, exercise); optionPureHeston.setPricingEngine(new AnalyticHestonHullWhiteEngine(hestonModel, hullWhiteModel, 128)); double calculated = optionHestonHW.NPV(); double error = optionHestonHW.errorEstimate(); double expected = optionPureHeston.NPV(); if (Math.Abs(calculated - expected) > 3 * error && Math.Abs(calculated - expected) > tol) { QAssert.Fail("Failed to reproduce hw heston vanilla prices" + "\n strike: " + strike[j] + "\n calculated: " + calculated + "\n error: " + error + "\n expected: " + expected); } } } }
public void testEuropeanHaugValues() { // Testing double barrier european options against Haug's values Exercise.Type european = Exercise.Type.European; NewBarrierOptionData[] values = { /* The data below are from * "The complete guide to option pricing formulas 2nd Ed",E.G. Haug, McGraw-Hill, p.156 and following. * * Note: * The book uses b instead of q (q=r-b) */ // BarrierType, barr.lo, barr.hi, type, exercise,strk, s, q, r, t, v, result, tol new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 50.0, 150.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 4.3515, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 50.0, 150.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 6.1644, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 50.0, 150.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 7.0373, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 50.0, 150.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 6.9853, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 50.0, 150.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 7.9336, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 50.0, 150.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 6.5088, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 60.0, 140.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 4.3505, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 60.0, 140.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 5.8500, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 60.0, 140.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 5.7726, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 60.0, 140.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 6.8082, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 60.0, 140.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 6.3383, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 60.0, 140.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 4.3841, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 70.0, 130.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 4.3139, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 70.0, 130.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 4.8293, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 70.0, 130.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 3.7765, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 70.0, 130.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 5.9697, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 70.0, 130.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 4.0004, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 70.0, 130.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 2.2563, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 80.0, 120.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 3.7516, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 80.0, 120.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 2.6387, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 80.0, 120.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 1.4903, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 80.0, 120.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 3.5805, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 80.0, 120.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 1.5098, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 80.0, 120.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 0.5635, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 90.0, 110.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 1.2055, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 90.0, 110.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 0.3098, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 90.0, 110.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 0.0477, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 90.0, 110.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 0.5537, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 90.0, 110.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 0.0441, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 90.0, 110.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 0.0011, 1.0e-4), // BarrierType, barr.lo, barr.hi, type, exercise,strk, s, q, r, t, v, result, tol new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 50.0, 150.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 1.8825, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 50.0, 150.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 3.7855, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 50.0, 150.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 5.7191, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 50.0, 150.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 2.1374, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 50.0, 150.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 4.7033, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 50.0, 150.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 7.1683, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 60.0, 140.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 1.8825, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 60.0, 140.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 3.7845, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 60.0, 140.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 5.6060, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 60.0, 140.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 2.1374, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 60.0, 140.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 4.6236, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 60.0, 140.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 6.1062, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 70.0, 130.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 1.8825, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 70.0, 130.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 3.7014, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 70.0, 130.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 4.6472, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 70.0, 130.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 2.1325, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 70.0, 130.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 3.8944, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 70.0, 130.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 3.5868, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 80.0, 120.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 1.8600, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 80.0, 120.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 2.6866, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 80.0, 120.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 2.0719, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 80.0, 120.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 1.8883, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 80.0, 120.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 1.7851, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 80.0, 120.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 0.8244, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 90.0, 110.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 0.9473, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 90.0, 110.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 0.3449, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 90.0, 110.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 0.0578, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 90.0, 110.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 0.4555, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 90.0, 110.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 0.0491, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockOut, 90.0, 110.0, Option.Type.Put, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 0.0013, 1.0e-4), // BarrierType, barr.lo, barr.hi, type, strk, s, q, r, t, v, result, tol new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 50.0, 150.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 0.0000, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 50.0, 150.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 0.0900, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 50.0, 150.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 1.1537, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 50.0, 150.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 0.0292, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 50.0, 150.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 1.6487, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 50.0, 150.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 5.7321, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 60.0, 140.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 0.0010, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 60.0, 140.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 0.4045, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 60.0, 140.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 2.4184, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 60.0, 140.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 0.2062, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 60.0, 140.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 3.2439, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 60.0, 140.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 7.8569, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 70.0, 130.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 0.0376, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 70.0, 130.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 1.4252, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 70.0, 130.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 4.4145, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 70.0, 130.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 1.0447, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 70.0, 130.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 5.5818, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 70.0, 130.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 9.9846, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 80.0, 120.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 0.5999, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 80.0, 120.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 3.6158, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 80.0, 120.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 6.7007, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 80.0, 120.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 3.4340, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 80.0, 120.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 8.0724, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 80.0, 120.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 11.6774, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 90.0, 110.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.15, 3.1460, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 90.0, 110.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.25, 5.9447, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 90.0, 110.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.25, 0.35, 8.1432, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 90.0, 110.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.15, 6.4608, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 90.0, 110.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.25, 9.5382, 1.0e-4), new NewBarrierOptionData(DoubleBarrier.Type.KnockIn, 90.0, 110.0, Option.Type.Call, european, 100, 100.0, 0.0, 0.1, 0.50, 0.35, 12.2398, 1.0e-4), }; DayCounter dc = new Actual360(); Date today = Date.Today; SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.0); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.0); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); for (int i = 0; i < values.Length; i++) { Date exDate = today + (int)(values[i].t * 360 + 0.5); Exercise exercise = new EuropeanExercise(exDate); spot.setValue(values[i].s); qRate.setValue(values[i].q); rRate.setValue(values[i].r); vol.setValue(values[i].v); StrikedTypePayoff payoff = new PlainVanillaPayoff(values[i].type, values[i].strike); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess( new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); DoubleBarrierOption opt = new DoubleBarrierOption(values[i].barrierType, values[i].barrierlo, values[i].barrierhi, 0, // no rebate payoff, exercise); // Ikeda/Kunitomo engine IPricingEngine engine = new AnalyticDoubleBarrierEngine(stochProcess); opt.setPricingEngine(engine); double calculated = opt.NPV(); double expected = values[i].result; double error = Math.Abs(calculated - expected); if (error > values[i].tol) { REPORT_FAILURE("Ikeda/Kunitomo value", values[i].barrierType, values[i].barrierlo, values[i].barrierhi, payoff, exercise, values[i].s, values[i].q, values[i].r, today, values[i].v, expected, calculated, error, values[i].tol); } // Wulin Suo/Yong Wang engine engine = new WulinYongDoubleBarrierEngine(stochProcess); opt.setPricingEngine(engine); calculated = opt.NPV(); expected = values[i].result; error = Math.Abs(calculated - expected); if (error > values[i].tol) { REPORT_FAILURE("Wulin/Yong value", values[i].barrierType, values[i].barrierlo, values[i].barrierhi, payoff, exercise, values[i].s, values[i].q, values[i].r, today, values[i].v, expected, calculated, error, values[i].tol); } } }
public void testH1HWPricingEngine() { /* * Example taken from Lech Aleksander Grzelak, * Equity and Foreign Exchange Hybrid Models for Pricing Long-Maturity * Financial Derivatives, * http://repository.tudelft.nl/assets/uuid:a8e1a007-bd89-481a-aee3-0e22f15ade6b/PhDThesis_main.pdf */ Date today = new Date(15, Month.July, 2012); Settings.setEvaluationDate(today); Date exerciseDate = new Date(13, Month.July, 2022); DayCounter dc = new Actual365Fixed(); Exercise exercise = new EuropeanExercise(exerciseDate); Handle <Quote> s0 = new Handle <Quote>(new SimpleQuote(100.0)); double r = 0.02; double q = 0.00; double v0 = 0.05; double theta = 0.05; double kappa_v = 0.3; double[] sigma_v = { 0.3, 0.6 }; double rho_sv = -0.30; double rho_sr = 0.6; double kappa_r = 0.01; double sigma_r = 0.01; Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, r, dc)); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, q, dc)); Handle <BlackVolTermStructure> flatVolTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(today, 0.20, dc)); GeneralizedBlackScholesProcess bsProcess = new GeneralizedBlackScholesProcess(s0, qTS, rTS, flatVolTS); HullWhiteProcess hwProcess = new HullWhiteProcess(rTS, kappa_r, sigma_r); HullWhite hullWhiteModel = new HullWhite(new Handle <YieldTermStructure>(rTS), kappa_r, sigma_r); double tol = 0.0001; double[] strikes = { 40, 80, 100, 120, 180 }; double[][] expected = { new double[] { 0.267503, 0.235742, 0.228223, 0.223461, 0.217855 }, new double[] { 0.263626, 0.211625, 0.199907, 0.193502, 0.190025 } }; for (int j = 0; j < sigma_v.Length; ++j) { HestonProcess hestonProcess = new HestonProcess(rTS, qTS, s0, v0, kappa_v, theta, sigma_v[j], rho_sv); HestonModel hestonModel = new HestonModel(hestonProcess); for (int i = 0; i < strikes.Length; ++i) { StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Call, strikes[i]); VanillaOption option = new VanillaOption(payoff, exercise); IPricingEngine analyticH1HWEngine = new AnalyticH1HWEngine(hestonModel, hullWhiteModel, rho_sr, 144); option.setPricingEngine(analyticH1HWEngine); double impliedH1HW = option.impliedVolatility(option.NPV(), bsProcess); if (Math.Abs(expected[j][i] - impliedH1HW) > tol) { QAssert.Fail("Failed to reproduce H1HW implied volatility" + "\n expected : " + expected[j][i] + "\n calculated : " + impliedH1HW + "\n tol : " + tol + "\n strike : " + strikes[i] + "\n sigma : " + sigma_v[j]); } } } }
public void testVannaVolgaDoubleBarrierValues() { // Testing double-barrier FX options against Vanna/Volga values SavedSettings backup = new SavedSettings(); DoubleBarrierFxOptionData[] values = { // BarrierType, barr.1, barr.2, rebate, type, strike, s, q, r, t, vol25Put, volAtm,vol25Call, vol, result, tol new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.1, 1.5, 0.0, Option.Type.Call, 1.13321, 1.30265, 0.0003541, 0.0033871, 1.0, 0.10087, 0.08925, 0.08463, 0.11638, 0.14413, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.1, 1.5, 0.0, Option.Type.Call, 1.22687, 1.30265, 0.0003541, 0.0033871, 1.0, 0.10087, 0.08925, 0.08463, 0.10088, 0.07456, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.1, 1.5, 0.0, Option.Type.Call, 1.31179, 1.30265, 0.0003541, 0.0033871, 1.0, 0.10087, 0.08925, 0.08463, 0.08925, 0.02710, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.1, 1.5, 0.0, Option.Type.Call, 1.38843, 1.30265, 0.0003541, 0.0033871, 1.0, 0.10087, 0.08925, 0.08463, 0.08463, 0.00569, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.1, 1.5, 0.0, Option.Type.Call, 1.46047, 1.30265, 0.0003541, 0.0033871, 1.0, 0.10087, 0.08925, 0.08463, 0.08412, 0.00013, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.1, 1.5, 0.0, Option.Type.Put, 1.13321, 1.30265, 0.0003541, 0.0033871, 1.0, 0.10087, 0.08925, 0.08463, 0.11638, 0.00017, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.1, 1.5, 0.0, Option.Type.Put, 1.22687, 1.30265, 0.0003541, 0.0033871, 1.0, 0.10087, 0.08925, 0.08463, 0.10088, 0.00353, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.1, 1.5, 0.0, Option.Type.Put, 1.31179, 1.30265, 0.0003541, 0.0033871, 1.0, 0.10087, 0.08925, 0.08463, 0.08925, 0.02221, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.1, 1.5, 0.0, Option.Type.Put, 1.38843, 1.30265, 0.0003541, 0.0033871, 1.0, 0.10087, 0.08925, 0.08463, 0.08463, 0.06049, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.1, 1.5, 0.0, Option.Type.Put, 1.46047, 1.30265, 0.0003541, 0.0033871, 1.0, 0.10087, 0.08925, 0.08463, 0.08412, 0.11103, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.0, 1.6, 0.0, Option.Type.Call, 1.06145, 1.30265, 0.0009418, 0.0039788, 2.0, 0.10891, 0.09525, 0.09197, 0.12511, 0.19981, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.0, 1.6, 0.0, Option.Type.Call, 1.19545, 1.30265, 0.0009418, 0.0039788, 2.0, 0.10891, 0.09525, 0.09197, 0.10890, 0.10389, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.0, 1.6, 0.0, Option.Type.Call, 1.32238, 1.30265, 0.0009418, 0.0039788, 2.0, 0.10891, 0.09525, 0.09197, 0.09444, 0.03555, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.0, 1.6, 0.0, Option.Type.Call, 1.44298, 1.30265, 0.0009418, 0.0039788, 2.0, 0.10891, 0.09525, 0.09197, 0.09197, 0.00634, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.0, 1.6, 0.0, Option.Type.Call, 1.56345, 1.30265, 0.0009418, 0.0039788, 2.0, 0.10891, 0.09525, 0.09197, 0.09261, 0.00000, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.0, 1.6, 0.0, Option.Type.Put, 1.06145, 1.30265, 0.0009418, 0.0039788, 2.0, 0.10891, 0.09525, 0.09197, 0.12511, 0.00000, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.0, 1.6, 0.0, Option.Type.Put, 1.19545, 1.30265, 0.0009418, 0.0039788, 2.0, 0.10891, 0.09525, 0.09197, 0.10890, 0.00436, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.0, 1.6, 0.0, Option.Type.Put, 1.32238, 1.30265, 0.0009418, 0.0039788, 2.0, 0.10891, 0.09525, 0.09197, 0.09444, 0.03173, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.0, 1.6, 0.0, Option.Type.Put, 1.44298, 1.30265, 0.0009418, 0.0039788, 2.0, 0.10891, 0.09525, 0.09197, 0.09197, 0.09346, 1.0e-4), new DoubleBarrierFxOptionData(DoubleBarrier.Type.KnockOut, 1.0, 1.6, 0.0, Option.Type.Put, 1.56345, 1.30265, 0.0009418, 0.0039788, 2.0, 0.10891, 0.09525, 0.09197, 0.09261, 0.17704, 1.0e-4), }; DayCounter dc = new Actual360(); Date today = new Date(05, Month.Mar, 2013); Settings.setEvaluationDate(today); SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.0); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol25Put = new SimpleQuote(0.0); SimpleQuote volAtm = new SimpleQuote(0.0); SimpleQuote vol25Call = new SimpleQuote(0.0); for (int i = 0; i < values.Length; i++) { for (int j = 0; j <= 1; j++) { DoubleBarrier.Type barrierType = (DoubleBarrier.Type)j; spot.setValue(values[i].s); qRate.setValue(values[i].q); rRate.setValue(values[i].r); vol25Put.setValue(values[i].vol25Put); volAtm.setValue(values[i].volAtm); vol25Call.setValue(values[i].vol25Call); StrikedTypePayoff payoff = new PlainVanillaPayoff(values[i].type, values[i].strike); Date exDate = today + (int)(values[i].t * 365 + 0.5); Exercise exercise = new EuropeanExercise(exDate); Handle <DeltaVolQuote> volAtmQuote = new Handle <DeltaVolQuote>( new DeltaVolQuote(new Handle <Quote>(volAtm), DeltaVolQuote.DeltaType.Fwd, values[i].t, DeltaVolQuote.AtmType.AtmDeltaNeutral)); //always delta neutral atm Handle <DeltaVolQuote> vol25PutQuote = new Handle <DeltaVolQuote>(new DeltaVolQuote(-0.25, new Handle <Quote>(vol25Put), values[i].t, DeltaVolQuote.DeltaType.Fwd)); Handle <DeltaVolQuote> vol25CallQuote = new Handle <DeltaVolQuote>(new DeltaVolQuote(0.25, new Handle <Quote>(vol25Call), values[i].t, DeltaVolQuote.DeltaType.Fwd)); DoubleBarrierOption doubleBarrierOption = new DoubleBarrierOption(barrierType, values[i].barrier1, values[i].barrier2, values[i].rebate, payoff, exercise); double bsVanillaPrice = Utils.blackFormula(values[i].type, values[i].strike, spot.value() * qTS.discount(values[i].t) / rTS.discount(values[i].t), values[i].v * Math.Sqrt(values[i].t), rTS.discount(values[i].t)); IPricingEngine vannaVolgaEngine; vannaVolgaEngine = new VannaVolgaDoubleBarrierEngine(volAtmQuote, vol25PutQuote, vol25CallQuote, new Handle <Quote>(spot), new Handle <YieldTermStructure>(rTS), new Handle <YieldTermStructure>(qTS), (process, series) => new WulinYongDoubleBarrierEngine(process, series), true, bsVanillaPrice); doubleBarrierOption.setPricingEngine(vannaVolgaEngine); double expected = 0; if (barrierType == DoubleBarrier.Type.KnockOut) { expected = values[i].result; } else if (barrierType == DoubleBarrier.Type.KnockIn) { expected = (bsVanillaPrice - values[i].result); } double calculated = doubleBarrierOption.NPV(); double error = Math.Abs(calculated - expected); if (error > values[i].tol) { REPORT_FAILURE_VANNAVOLGA("value", values[i].barrierType, values[i].barrier1, values[i].barrier2, values[i].rebate, payoff, exercise, values[i].s, values[i].q, values[i].r, today, values[i].vol25Put, values[i].volAtm, values[i].vol25Call, values[i].v, expected, calculated, error, values[i].tol); } vannaVolgaEngine = new VannaVolgaDoubleBarrierEngine(volAtmQuote, vol25PutQuote, vol25CallQuote, new Handle <Quote>(spot), new Handle <YieldTermStructure>(rTS), new Handle <YieldTermStructure>(qTS), (process, series) => new AnalyticDoubleBarrierEngine(process, series), true, bsVanillaPrice); doubleBarrierOption.setPricingEngine(vannaVolgaEngine); calculated = doubleBarrierOption.NPV(); error = Math.Abs(calculated - expected); double maxtol = 5.0e-3; // different engines have somewhat different results if (error > maxtol) { REPORT_FAILURE_VANNAVOLGA("value", values[i].barrierType, values[i].barrier1, values[i].barrier2, values[i].rebate, payoff, exercise, values[i].s, values[i].q, values[i].r, today, values[i].vol25Put, values[i].volAtm, values[i].vol25Call, values[i].v, expected, calculated, error, values[i].tol); } } } }
public void testEuropeanStartLimit() { // Testing dividend European option with a dividend on today's date... SavedSettings backup = new SavedSettings(); double tolerance = 1.0e-5; double dividendValue = 10.0; Option.Type[] types = { Option.Type.Call, Option.Type.Put }; double[] strikes = { 50.0, 99.5, 100.0, 100.5, 150.0 }; double[] underlyings = { 100.0 }; double[] qRates = { 0.00, 0.10, 0.30 }; double[] rRates = { 0.01, 0.05, 0.15 }; int[] lengths = { 1, 2 }; double[] vols = { 0.05, 0.20, 0.70 }; 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)); for (int i = 0; i < types.Length; i++) { for (int j = 0; j < strikes.Length; j++) { for (int k = 0; k < lengths.Length; k++) { Date exDate = today + new Period(lengths[k], TimeUnit.Years); Exercise exercise = new EuropeanExercise(exDate); List <Date> dividendDates = new List <Date>(); List <double> dividends = new List <double>(); dividendDates.Add(today); dividends.Add(dividendValue); StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], strikes[j]); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), qTS, rTS, volTS); IPricingEngine engine = new AnalyticDividendEuropeanEngine(stochProcess); IPricingEngine ref_engine = new AnalyticEuropeanEngine(stochProcess); DividendVanillaOption option = new DividendVanillaOption(payoff, exercise, dividendDates, dividends); option.setPricingEngine(engine); VanillaOption ref_option = new VanillaOption(payoff, exercise); ref_option.setPricingEngine(ref_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 calculated = option.NPV(); spot.setValue(u - dividendValue); double expected = ref_option.NPV(); double error = Math.Abs(calculated - expected); if (error > tolerance) { REPORT_FAILURE("value", payoff, exercise, u, q, r, today, v, expected, calculated, error, tolerance); } } } } } } } } }
public void testEuroTwoValues() { // Testing two-asset European basket options... /* * Data from: * Excel spreadsheet www.maths.ox.ac.uk/~firth/computing/excel.shtml * and * "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 pag 56-58 * European two asset max basket options */ BasketOptionTwoData[] values = { // basketType, optionType, strike, s1, s2, q1, q2, r, t, v1, v2, rho, result, tol // data from http://www.maths.ox.ac.uk/~firth/computing/excel.shtml new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.90, 10.898, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.70, 8.483, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.50, 6.844, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.30, 5.531, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.10, 4.413, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.50, 0.70, 0.00, 4.981, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.50, 0.30, 0.00, 4.159, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.50, 0.10, 0.00, 2.597, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.50, 0.10, 0.50, 4.030, 1.0e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.90, 17.565, 1.0e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.70, 19.980, 1.0e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.50, 21.619, 1.0e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.30, 22.932, 1.0e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Call, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.10, 24.049, 1.1e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Call, 100.0, 80.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.30, 16.508, 1.0e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Call, 100.0, 80.0, 80.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.30, 8.049, 1.0e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Call, 100.0, 80.0, 120.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.30, 30.141, 1.0e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Call, 100.0, 120.0, 120.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.30, 42.889, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.90, 11.369, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.70, 12.856, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.50, 13.890, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.30, 14.741, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.10, 15.485, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 0.50, 0.30, 0.30, 0.10, 11.893, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 0.25, 0.30, 0.30, 0.10, 8.881, 1.0e-3), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 2.00, 0.30, 0.30, 0.10, 19.268, 1.0e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.90, 7.339, 1.0e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.70, 5.853, 1.0e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.50, 4.818, 1.0e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.30, 3.967, 1.1e-3), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Put, 100.0, 100.0, 100.0, 0.00, 0.00, 0.05, 1.00, 0.30, 0.30, 0.10, 3.223, 1.0e-3), // basketType, optionType, strike, s1, s2, q1, q2, r, t, v1, v2, rho, result, tol // data from "Option pricing formulas" VB code + spreadsheet new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Call, 98.0, 100.0, 105.0, 0.00, 0.00, 0.05, 0.50, 0.11, 0.16, 0.63, 4.8177, 1.0e-4), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Call, 98.0, 100.0, 105.0, 0.00, 0.00, 0.05, 0.50, 0.11, 0.16, 0.63, 11.6323, 1.0e-4), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Put, 98.0, 100.0, 105.0, 0.00, 0.00, 0.05, 0.50, 0.11, 0.16, 0.63, 2.0376, 1.0e-4), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Put, 98.0, 100.0, 105.0, 0.00, 0.00, 0.05, 0.50, 0.11, 0.16, 0.63, 0.5731, 1.0e-4), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Call, 98.0, 100.0, 105.0, 0.06, 0.09, 0.05, 0.50, 0.11, 0.16, 0.63, 2.9340, 1.0e-4), new BasketOptionTwoData(BasketType.MinBasket, Option.Type.Put, 98.0, 100.0, 105.0, 0.06, 0.09, 0.05, 0.50, 0.11, 0.16, 0.63, 3.5224, 1.0e-4), // data from "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 pag 58 new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Call, 98.0, 100.0, 105.0, 0.06, 0.09, 0.05, 0.50, 0.11, 0.16, 0.63, 8.0701, 1.0e-4), new BasketOptionTwoData(BasketType.MaxBasket, Option.Type.Put, 98.0, 100.0, 105.0, 0.06, 0.09, 0.05, 0.50, 0.11, 0.16, 0.63, 1.2181, 1.0e-4), /* "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 pag 59-60 * Kirk approx. for a european spread option on two futures*/ new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.1, 0.20, 0.20, -0.5, 4.7530, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.1, 0.20, 0.20, 0.0, 3.7970, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.1, 0.20, 0.20, 0.5, 2.5537, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.1, 0.25, 0.20, -0.5, 5.4275, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.1, 0.25, 0.20, 0.0, 4.3712, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.1, 0.25, 0.20, 0.5, 3.0086, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.1, 0.20, 0.25, -0.5, 5.4061, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.1, 0.20, 0.25, 0.0, 4.3451, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.1, 0.20, 0.25, 0.5, 2.9723, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.5, 0.20, 0.20, -0.5, 10.7517, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.5, 0.20, 0.20, 0.0, 8.7020, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.5, 0.20, 0.20, 0.5, 6.0257, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.5, 0.25, 0.20, -0.5, 12.1941, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.5, 0.25, 0.20, 0.0, 9.9340, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.5, 0.25, 0.20, 0.5, 7.0067, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.5, 0.20, 0.25, -0.5, 12.1483, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.5, 0.20, 0.25, 0.0, 9.8780, 1.0e-3), new BasketOptionTwoData(BasketType.SpreadBasket, Option.Type.Call, 3.0, 122.0, 120.0, 0.0, 0.0, 0.10, 0.5, 0.20, 0.25, 0.5, 6.9284, 1.0e-3) }; DayCounter dc = new Actual360(); Date today = Date.Today; SimpleQuote spot1 = new SimpleQuote(0.0); SimpleQuote spot2 = new SimpleQuote(0.0); SimpleQuote qRate1 = new SimpleQuote(0.0); YieldTermStructure qTS1 = Utilities.flatRate(today, qRate1, dc); SimpleQuote qRate2 = new SimpleQuote(0.0); YieldTermStructure qTS2 = Utilities.flatRate(today, qRate2, dc); SimpleQuote rRate = new SimpleQuote(0.0); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol1 = new SimpleQuote(0.0); BlackVolTermStructure volTS1 = Utilities.flatVol(today, vol1, dc); SimpleQuote vol2 = new SimpleQuote(0.0); BlackVolTermStructure volTS2 = Utilities.flatVol(today, vol2, dc); //double mcRelativeErrorTolerance = 0.01; //double fdRelativeErrorTolerance = 0.01; for (int i = 0; i < values.Length; i++) { PlainVanillaPayoff payoff = new PlainVanillaPayoff(values[i].type, values[i].strike); Date exDate = today + (int)(values[i].t * 360 + 0.5); Exercise exercise = new EuropeanExercise(exDate); spot1.setValue(values[i].s1); spot2.setValue(values[i].s2); qRate1.setValue(values[i].q1); qRate2.setValue(values[i].q2); rRate.setValue(values[i].r); vol1.setValue(values[i].v1); vol2.setValue(values[i].v2); IPricingEngine analyticEngine = null; GeneralizedBlackScholesProcess p1 = null, p2 = null; switch (values[i].basketType) { case BasketType.MaxBasket: case BasketType.MinBasket: p1 = new BlackScholesMertonProcess(new Handle <Quote>(spot1), new Handle <YieldTermStructure>(qTS1), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS1)); p2 = new BlackScholesMertonProcess(new Handle <Quote>(spot2), new Handle <YieldTermStructure>(qTS2), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS2)); analyticEngine = new StulzEngine(p1, p2, values[i].rho); break; case BasketType.SpreadBasket: p1 = new BlackProcess(new Handle <Quote>(spot1), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS1)); p2 = new BlackProcess(new Handle <Quote>(spot2), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS2)); analyticEngine = new KirkEngine((BlackProcess)p1, (BlackProcess)p2, values[i].rho); break; default: Utils.QL_FAIL("unknown basket type"); break; } List <StochasticProcess1D> procs = new List <StochasticProcess1D> { p1, p2 }; Matrix correlationMatrix = new Matrix(2, 2, values[i].rho); for (int j = 0; j < 2; j++) { correlationMatrix[j, j] = 1.0; } StochasticProcessArray process = new StochasticProcessArray(procs, correlationMatrix); //IPricingEngine mcEngine = MakeMCEuropeanBasketEngine<PseudoRandom, Statistics>(process) // .withStepsPerYear(1) // .withSamples(10000) // .withSeed(42); //IPricingEngine fdEngine = new Fd2dBlackScholesVanillaEngine(p1, p2, values[i].rho, 50, 50, 15); BasketOption basketOption = new BasketOption(basketTypeToPayoff(values[i].basketType, payoff), exercise); // analytic engine basketOption.setPricingEngine(analyticEngine); double calculated = basketOption.NPV(); double expected = values[i].result; double error = Math.Abs(calculated - expected); if (error > values[i].tol) { REPORT_FAILURE_2("value", values[i].basketType, payoff, exercise, values[i].s1, values[i].s2, values[i].q1, values[i].q2, values[i].r, today, values[i].v1, values[i].v2, values[i].rho, values[i].result, calculated, error, values[i].tol); } // // fd engine // basketOption.setPricingEngine(fdEngine); // calculated = basketOption.NPV(); // double relError = relativeError(calculated, expected, expected); // if (relError > mcRelativeErrorTolerance ) // { // REPORT_FAILURE_2("FD value", values[i].basketType, payoff, // exercise, values[i].s1, values[i].s2, // values[i].q1, values[i].q2, values[i].r, // today, values[i].v1, values[i].v2, values[i].rho, // values[i].result, calculated, relError, // fdRelativeErrorTolerance); // } //// mc engine //basketOption.setPricingEngine(mcEngine); //calculated = basketOption.NPV(); //relError = relativeError(calculated, expected, values[i].s1); //if (relError > mcRelativeErrorTolerance ) //{ // REPORT_FAILURE_2("MC value", values[i].basketType, payoff, // exercise, values[i].s1, values[i].s2, // values[i].q1, values[i].q2, values[i].r, // today, values[i].v1, values[i].v2, values[i].rho, // values[i].result, calculated, relError, // mcRelativeErrorTolerance); //} } }
void testFdGreeks <Engine>(Date today, Exercise exercise) where Engine : IFDEngine, new() { Dictionary <string, double> calculated = new Dictionary <string, double>(), expected = new Dictionary <string, double>(), tolerance = new Dictionary <string, double>(); tolerance.Add("delta", 5.0e-3); tolerance.Add("gamma", 7.0e-3); // tolerance["theta"] = 1.0e-2; Option.Type[] types = { Option.Type.Call, Option.Type.Put }; double[] strikes = { 50.0, 99.5, 100.0, 100.5, 150.0 }; double[] underlyings = { 100.0 }; double[] qRates = { 0.00, 0.10, 0.20 }; double[] rRates = { 0.01, 0.05, 0.15 }; double[] vols = { 0.05, 0.20, 0.50 }; DayCounter dc = new Actual360(); 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)); for (int i = 0; i < types.Length; i++) { for (int j = 0; j < strikes.Length; j++) { List <Date> dividendDates = new List <Date>(); List <double> dividends = new List <double>(); for (Date d = today + new Period(3, TimeUnit.Months); d < exercise.lastDate(); d += new Period(6, TimeUnit.Months)) { dividendDates.Add(d); dividends.Add(5.0); } StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], strikes[j]); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), qTS, rTS, volTS); IPricingEngine engine = new Engine().factory(stochProcess); DividendVanillaOption option = new DividendVanillaOption(payoff, exercise, dividendDates, dividends); 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); // FLOATING_POINT_EXCEPTION double value = option.NPV(); calculated["delta"] = option.delta(); calculated["gamma"] = option.gamma(); // calculated["theta"] = option.theta(); 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 date and get theta /* * Time dT = dc.yearFraction(today-1, today+1); * Settings::instance().evaluationDate() = today-1; * value_m = option.NPV(); * Settings::instance().evaluationDate() = today+1; * value_p = option.NPV(); * Settings::instance().evaluationDate() = today; * expected["theta"] = (value_p - value_m)/dT; */ // compare foreach (string greek in calculated.Keys) { double expct = expected[greek], calcl = calculated[greek], tol = tolerance[greek]; double error = Utilities.relativeError(expct, calcl, u); if (error > tol) { REPORT_FAILURE(greek, payoff, exercise, u, q, r, today, v, expct, calcl, error, tol); } } } } } } } } } }
public void testAnalyticContinuousFixedLookback() { // Testing analytic continuous fixed-strike lookback options LookbackOptionData[] values = { // data from "Option Pricing Formulas", Haug, 1998, pg.63-64 //type, strike, minmax, s, q, r, t, v, l, t1, result, tol new LookbackOptionData(Option.Type.Call, 95, 100, 100.0, 0.00, 0.10, 0.50, 0.10, 0, 0, 13.2687, 1.0e-4), new LookbackOptionData(Option.Type.Call, 95, 100, 100.0, 0.00, 0.10, 0.50, 0.20, 0, 0, 18.9263, 1.0e-4), new LookbackOptionData(Option.Type.Call, 95, 100, 100.0, 0.00, 0.10, 0.50, 0.30, 0, 0, 24.9857, 1.0e-4), new LookbackOptionData(Option.Type.Call, 100, 100, 100.0, 0.00, 0.10, 0.50, 0.10, 0, 0, 8.5126, 1.0e-4), new LookbackOptionData(Option.Type.Call, 100, 100, 100.0, 0.00, 0.10, 0.50, 0.20, 0, 0, 14.1702, 1.0e-4), new LookbackOptionData(Option.Type.Call, 100, 100, 100.0, 0.00, 0.10, 0.50, 0.30, 0, 0, 20.2296, 1.0e-4), new LookbackOptionData(Option.Type.Call, 105, 100, 100.0, 0.00, 0.10, 0.50, 0.10, 0, 0, 4.3908, 1.0e-4), new LookbackOptionData(Option.Type.Call, 105, 100, 100.0, 0.00, 0.10, 0.50, 0.20, 0, 0, 9.8905, 1.0e-4), new LookbackOptionData(Option.Type.Call, 105, 100, 100.0, 0.00, 0.10, 0.50, 0.30, 0, 0, 15.8512, 1.0e-4), new LookbackOptionData(Option.Type.Call, 95, 100, 100.0, 0.00, 0.10, 1.00, 0.10, 0, 0, 18.3241, 1.0e-4), new LookbackOptionData(Option.Type.Call, 95, 100, 100.0, 0.00, 0.10, 1.00, 0.20, 0, 0, 26.0731, 1.0e-4), new LookbackOptionData(Option.Type.Call, 95, 100, 100.0, 0.00, 0.10, 1.00, 0.30, 0, 0, 34.7116, 1.0e-4), new LookbackOptionData(Option.Type.Call, 100, 100, 100.0, 0.00, 0.10, 1.00, 0.10, 0, 0, 13.8000, 1.0e-4), new LookbackOptionData(Option.Type.Call, 100, 100, 100.0, 0.00, 0.10, 1.00, 0.20, 0, 0, 21.5489, 1.0e-4), new LookbackOptionData(Option.Type.Call, 100, 100, 100.0, 0.00, 0.10, 1.00, 0.30, 0, 0, 30.1874, 1.0e-4), new LookbackOptionData(Option.Type.Call, 105, 100, 100.0, 0.00, 0.10, 1.00, 0.10, 0, 0, 9.5445, 1.0e-4), new LookbackOptionData(Option.Type.Call, 105, 100, 100.0, 0.00, 0.10, 1.00, 0.20, 0, 0, 17.2965, 1.0e-4), new LookbackOptionData(Option.Type.Call, 105, 100, 100.0, 0.00, 0.10, 1.00, 0.30, 0, 0, 25.9002, 1.0e-4), new LookbackOptionData(Option.Type.Put, 95, 100, 100.0, 0.00, 0.10, 0.50, 0.10, 0, 0, 0.6899, 1.0e-4), new LookbackOptionData(Option.Type.Put, 95, 100, 100.0, 0.00, 0.10, 0.50, 0.20, 0, 0, 4.4448, 1.0e-4), new LookbackOptionData(Option.Type.Put, 95, 100, 100.0, 0.00, 0.10, 0.50, 0.30, 0, 0, 8.9213, 1.0e-4), new LookbackOptionData(Option.Type.Put, 100, 100, 100.0, 0.00, 0.10, 0.50, 0.10, 0, 0, 3.3917, 1.0e-4), new LookbackOptionData(Option.Type.Put, 100, 100, 100.0, 0.00, 0.10, 0.50, 0.20, 0, 0, 8.3177, 1.0e-4), new LookbackOptionData(Option.Type.Put, 100, 100, 100.0, 0.00, 0.10, 0.50, 0.30, 0, 0, 13.1579, 1.0e-4), new LookbackOptionData(Option.Type.Put, 105, 100, 100.0, 0.00, 0.10, 0.50, 0.10, 0, 0, 8.1478, 1.0e-4), new LookbackOptionData(Option.Type.Put, 105, 100, 100.0, 0.00, 0.10, 0.50, 0.20, 0, 0, 13.0739, 1.0e-4), new LookbackOptionData(Option.Type.Put, 105, 100, 100.0, 0.00, 0.10, 0.50, 0.30, 0, 0, 17.9140, 1.0e-4), new LookbackOptionData(Option.Type.Put, 95, 100, 100.0, 0.00, 0.10, 1.00, 0.10, 0, 0, 1.0534, 1.0e-4), new LookbackOptionData(Option.Type.Put, 95, 100, 100.0, 0.00, 0.10, 1.00, 0.20, 0, 0, 6.2813, 1.0e-4), new LookbackOptionData(Option.Type.Put, 95, 100, 100.0, 0.00, 0.10, 1.00, 0.30, 0, 0, 12.2376, 1.0e-4), new LookbackOptionData(Option.Type.Put, 100, 100, 100.0, 0.00, 0.10, 1.00, 0.10, 0, 0, 3.8079, 1.0e-4), new LookbackOptionData(Option.Type.Put, 100, 100, 100.0, 0.00, 0.10, 1.00, 0.20, 0, 0, 10.1294, 1.0e-4), new LookbackOptionData(Option.Type.Put, 100, 100, 100.0, 0.00, 0.10, 1.00, 0.30, 0, 0, 16.3889, 1.0e-4), new LookbackOptionData(Option.Type.Put, 105, 100, 100.0, 0.00, 0.10, 1.00, 0.10, 0, 0, 8.3321, 1.0e-4), new LookbackOptionData(Option.Type.Put, 105, 100, 100.0, 0.00, 0.10, 1.00, 0.20, 0, 0, 14.6536, 1.0e-4), new LookbackOptionData(Option.Type.Put, 105, 100, 100.0, 0.00, 0.10, 1.00, 0.30, 0, 0, 20.9130, 1.0e-4) }; DayCounter dc = new Actual360(); Date today = Date.Today; SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.0); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.0); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); for (int i = 0; i < values.Length; i++) { Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise exercise = new EuropeanExercise(exDate); spot.setValue(values[i].s); qRate.setValue(values[i].q); rRate.setValue(values[i].r); vol.setValue(values[i].v); StrikedTypePayoff payoff = new PlainVanillaPayoff(values[i].type, values[i].strike); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess( new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new AnalyticContinuousFixedLookbackEngine(stochProcess); ContinuousFixedLookbackOption option = new ContinuousFixedLookbackOption(values[i].minmax, payoff, exercise); option.setPricingEngine(engine); double calculated = option.NPV(); double expected = values[i].result; double error = Math.Abs(calculated - expected); if (error > values[i].tol) { REPORT_FAILURE_FIXED("value", values[i].minmax, payoff, exercise, values[i].s, values[i].q, values[i].r, today, values[i].v, expected, calculated, error, values[i].tol); } } }
static void Main(string[] args) { DateTime timer = DateTime.Now; // set up dates Calendar calendar = new TARGET(); Date todaysDate = new Date(15, Month.May, 1998); Date settlementDate = new Date(17, Month.May, 1998); Settings.setEvaluationDate(todaysDate); // our options Option.Type type = Option.Type.Put; double underlying = 36; double strike = 40; double dividendYield = 0.00; double riskFreeRate = 0.06; double volatility = 0.20; Date maturity = new Date(17, Month.May, 1999); DayCounter dayCounter = new Actual365Fixed(); Console.WriteLine("Option type = " + type); Console.WriteLine("Maturity = " + maturity); Console.WriteLine("Underlying price = " + underlying); Console.WriteLine("Strike = " + strike); Console.WriteLine("Risk-free interest rate = {0:0.000000%}", riskFreeRate); Console.WriteLine("Dividend yield = {0:0.000000%}", dividendYield); Console.WriteLine("Volatility = {0:0.000000%}", volatility); Console.Write("\n"); string method; Console.Write("\n"); // write column headings int[] widths = new int[] { 35, 14, 14, 14 }; Console.Write("{0,-" + widths[0] + "}", "Method"); Console.Write("{0,-" + widths[1] + "}", "European"); Console.Write("{0,-" + widths[2] + "}", "Bermudan"); Console.WriteLine("{0,-" + widths[3] + "}", "American"); List <Date> exerciseDates = new List <Date>();; for (int i = 1; i <= 4; i++) { exerciseDates.Add(settlementDate + new Period(3 * i, TimeUnit.Months)); } Exercise europeanExercise = new EuropeanExercise(maturity); Exercise bermudanExercise = new BermudanExercise(exerciseDates); Exercise americanExercise = new AmericanExercise(settlementDate, maturity); Handle <Quote> underlyingH = new Handle <Quote>(new SimpleQuote(underlying)); // bootstrap the yield/dividend/vol curves var flatTermStructure = new Handle <YieldTermStructure>(new FlatForward(settlementDate, riskFreeRate, dayCounter)); var flatDividendTS = new Handle <YieldTermStructure>(new FlatForward(settlementDate, dividendYield, dayCounter)); var flatVolTS = new Handle <BlackVolTermStructure>(new BlackConstantVol(settlementDate, calendar, volatility, dayCounter)); StrikedTypePayoff payoff = new PlainVanillaPayoff(type, strike); var bsmProcess = new BlackScholesMertonProcess(underlyingH, flatDividendTS, flatTermStructure, flatVolTS); // options VanillaOption europeanOption = new VanillaOption(payoff, europeanExercise); VanillaOption bermudanOption = new VanillaOption(payoff, bermudanExercise); VanillaOption americanOption = new VanillaOption(payoff, americanExercise); // Analytic formulas: // Black-Scholes for European method = "Black-Scholes"; europeanOption.setPricingEngine(new AnalyticEuropeanEngine(bsmProcess)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + "}", "N/A"); Console.WriteLine("{0,-" + widths[3] + "}", "N/A"); // Barone-Adesi and Whaley approximation for American method = "Barone-Adesi/Whaley"; americanOption.setPricingEngine(new BaroneAdesiWhaleyApproximationEngine(bsmProcess)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + "}", "N/A"); Console.Write("{0,-" + widths[2] + "}", "N/A"); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Bjerksund and Stensland approximation for American method = "Bjerksund/Stensland"; americanOption.setPricingEngine(new BjerksundStenslandApproximationEngine(bsmProcess)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + "}", "N/A"); Console.Write("{0,-" + widths[2] + "}", "N/A"); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Integral method = "Integral"; europeanOption.setPricingEngine(new IntegralEngine(bsmProcess)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + "}", "N/A"); Console.WriteLine("{0,-" + widths[3] + "}", "N/A"); // Finite differences int timeSteps = 801; method = "Finite differences"; europeanOption.setPricingEngine(new FDEuropeanEngine(bsmProcess, timeSteps, timeSteps - 1)); bermudanOption.setPricingEngine(new FDBermudanEngine(bsmProcess, timeSteps, timeSteps - 1)); americanOption.setPricingEngine(new FDAmericanEngine(bsmProcess, timeSteps, timeSteps - 1)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Binomial method: Jarrow-Rudd method = "Binomial Jarrow-Rudd"; europeanOption.setPricingEngine(new BinomialVanillaEngine <JarrowRudd>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <JarrowRudd>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <JarrowRudd>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); method = "Binomial Cox-Ross-Rubinstein"; europeanOption.setPricingEngine(new BinomialVanillaEngine <CoxRossRubinstein>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <CoxRossRubinstein>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <CoxRossRubinstein>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Binomial method: Additive equiprobabilities method = "Additive equiprobabilities"; europeanOption.setPricingEngine(new BinomialVanillaEngine <AdditiveEQPBinomialTree>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <AdditiveEQPBinomialTree>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <AdditiveEQPBinomialTree>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Binomial method: Binomial Trigeorgis method = "Binomial Trigeorgis"; europeanOption.setPricingEngine(new BinomialVanillaEngine <Trigeorgis>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <Trigeorgis>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <Trigeorgis>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Binomial method: Binomial Tian method = "Binomial Tian"; europeanOption.setPricingEngine(new BinomialVanillaEngine <Tian>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <Tian>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <Tian>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Binomial method: Binomial Leisen-Reimer method = "Binomial Leisen-Reimer"; europeanOption.setPricingEngine(new BinomialVanillaEngine <LeisenReimer>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <LeisenReimer>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <LeisenReimer>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Binomial method: Binomial Joshi method = "Binomial Joshi"; europeanOption.setPricingEngine(new BinomialVanillaEngine <Joshi4>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <Joshi4>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <Joshi4>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Monte Carlo Method: MC (crude) timeSteps = 1; method = "MC (crude)"; ulong mcSeed = 42; IPricingEngine mcengine1 = new MakeMCEuropeanEngine <PseudoRandom>(bsmProcess) .withSteps(timeSteps) .withAbsoluteTolerance(0.02) .withSeed(mcSeed) .value(); europeanOption.setPricingEngine(mcengine1); // Real errorEstimate = europeanOption.errorEstimate(); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", "N/A"); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", "N/A"); // Monte Carlo Method: QMC (Sobol) method = "QMC (Sobol)"; int nSamples = 32768; // 2^15 IPricingEngine mcengine2 = new MakeMCEuropeanEngine <LowDiscrepancy>(bsmProcess) .withSteps(timeSteps) .withSamples(nSamples) .value(); europeanOption.setPricingEngine(mcengine2); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", "N/A"); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", "N/A"); // Monte Carlo Method: MC (Longstaff Schwartz) method = "MC (Longstaff Schwartz)"; IPricingEngine mcengine3 = new MakeMCAmericanEngine <PseudoRandom>(bsmProcess) .withSteps(100) .withAntitheticVariate() .withCalibrationSamples(4096) .withAbsoluteTolerance(0.02) .withSeed(mcSeed) .value(); americanOption.setPricingEngine(mcengine3); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", "N/A"); Console.Write("{0,-" + widths[2] + ":0.000000}", "N/A"); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // End test Console.WriteLine(" \nRun completed in {0}", DateTime.Now - timer); Console.WriteLine(); Console.Write("Press any key to continue ..."); Console.ReadKey(); }
public void testAnalyticContinuousPartialFixedLookback() { // Testing analytic continuous fixed-strike lookback options LookbackOptionData[] values = { // data from "Option Pricing Formulas, Second Edition", Haug, 2006, pg.148 //type, strike, minmax, s, q, r, t, v, l, t1, result, tol new LookbackOptionData(Option.Type.Call, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.25, 20.2845, 1.0e-4), new LookbackOptionData(Option.Type.Call, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.5, 19.6239, 1.0e-4), new LookbackOptionData(Option.Type.Call, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.75, 18.6244, 1.0e-4), new LookbackOptionData(Option.Type.Call, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.25, 4.0432, 1.0e-4), new LookbackOptionData(Option.Type.Call, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.5, 3.958, 1.0e-4), new LookbackOptionData(Option.Type.Call, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.75, 3.7015, 1.0e-4), new LookbackOptionData(Option.Type.Call, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.25, 27.5385, 1.0e-4), new LookbackOptionData(Option.Type.Call, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.5, 25.8126, 1.0e-4), new LookbackOptionData(Option.Type.Call, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.75, 23.4957, 1.0e-4), new LookbackOptionData(Option.Type.Call, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.25, 11.4895, 1.0e-4), new LookbackOptionData(Option.Type.Call, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.5, 10.8995, 1.0e-4), new LookbackOptionData(Option.Type.Call, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.75, 9.8244, 1.0e-4), new LookbackOptionData(Option.Type.Call, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.25, 35.4578, 1.0e-4), new LookbackOptionData(Option.Type.Call, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.5, 32.7172, 1.0e-4), new LookbackOptionData(Option.Type.Call, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.75, 29.1473, 1.0e-4), new LookbackOptionData(Option.Type.Call, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.25, 19.725, 1.0e-4), new LookbackOptionData(Option.Type.Call, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.5, 18.4025, 1.0e-4), new LookbackOptionData(Option.Type.Call, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.75, 16.2976, 1.0e-4), new LookbackOptionData(Option.Type.Put, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.25, 0.4973, 1.0e-4), new LookbackOptionData(Option.Type.Put, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.5, 0.4632, 1.0e-4), new LookbackOptionData(Option.Type.Put, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.75, 0.3863, 1.0e-4), new LookbackOptionData(Option.Type.Put, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.25, 12.6978, 1.0e-4), new LookbackOptionData(Option.Type.Put, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.5, 10.9492, 1.0e-4), new LookbackOptionData(Option.Type.Put, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.75, 9.1555, 1.0e-4), new LookbackOptionData(Option.Type.Put, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.25, 4.5863, 1.0e-4), new LookbackOptionData(Option.Type.Put, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.5, 4.1925, 1.0e-4), new LookbackOptionData(Option.Type.Put, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.75, 3.5831, 1.0e-4), new LookbackOptionData(Option.Type.Put, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.25, 19.0255, 1.0e-4), new LookbackOptionData(Option.Type.Put, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.5, 16.9433, 1.0e-4), new LookbackOptionData(Option.Type.Put, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.75, 14.6505, 1.0e-4), new LookbackOptionData(Option.Type.Put, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.25, 9.9348, 1.0e-4), new LookbackOptionData(Option.Type.Put, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.5, 9.1111, 1.0e-4), new LookbackOptionData(Option.Type.Put, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.75, 7.9267, 1.0e-4), new LookbackOptionData(Option.Type.Put, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.25, 25.2112, 1.0e-4), new LookbackOptionData(Option.Type.Put, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.5, 22.8217, 1.0e-4), new LookbackOptionData(Option.Type.Put, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.75, 20.0566, 1.0e-4) }; DayCounter dc = new Actual360(); Date today = Date.Today; SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.0); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.0); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); for (int i = 0; i < values.Length; i++) { Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise exercise = new EuropeanExercise(exDate); spot.setValue(values[i].s); qRate.setValue(values[i].q); rRate.setValue(values[i].r); vol.setValue(values[i].v); StrikedTypePayoff payoff = new PlainVanillaPayoff(values[i].type, values[i].strike); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess( new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new AnalyticContinuousPartialFixedLookbackEngine(stochProcess); Date lookbackStart = today + Convert.ToInt32(values[i].t1 * 360 + 0.5); ContinuousPartialFixedLookbackOption option = new ContinuousPartialFixedLookbackOption(lookbackStart, payoff, exercise); option.setPricingEngine(engine); double calculated = option.NPV(); double expected = values[i].result; double error = Math.Abs(calculated - expected); if (error > values[i].tol) { REPORT_FAILURE_FIXED("value", values[i].minmax, payoff, exercise, values[i].s, values[i].q, values[i].r, today, values[i].v, expected, calculated, error, values[i].tol); } } }
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(PlainVanillaPayoff obj) { return((obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr); }
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(PlainVanillaPayoff obj) { return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; }