public static flatVol ( Date today, |
||
today | Date | |
vol | ||
dc | DayCounter | |
return | QLNet.BlackVolTermStructure |
// setup public CommonVars() { calendar = new TARGET(); today = calendar.adjust(Date.Today); Settings.Instance.setEvaluationDate(today); dayCounter = new Actual360(); frequency = Frequency.Annual; settlementDays = 3; issueDate = calendar.advance(today, 2, TimeUnit.Days); maturityDate = calendar.advance(issueDate, 10, TimeUnit.Years); // reset to avoid inconsistencies as the schedule is backwards issueDate = calendar.advance(maturityDate, -10, TimeUnit.Years); underlying.linkTo(new SimpleQuote(50.0)); dividendYield.linkTo(Utilities.flatRate(today, 0.02, dayCounter)); riskFreeRate.linkTo(Utilities.flatRate(today, 0.05, dayCounter)); volatility.linkTo(Utilities.flatVol(today, 0.15, dayCounter)); process = new BlackScholesMertonProcess(underlying, dividendYield, riskFreeRate, volatility); creditSpread.linkTo(new SimpleQuote(0.005)); // it fails with 1000000 // faceAmount = 1000000.0; faceAmount = 100.0; redemption = 100.0; conversionRatio = redemption / underlying.link.value(); }
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) }; 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 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 void testCashOrNothingEuropeanValues() { // Testing European cash-or-nothing digital option DigitalOptionData[] values = { // "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 - pag 88 // type, strike, spot, q, r, t, vol, value, tol new DigitalOptionData(Option.Type.Put, 80.00, 100.0, 0.06, 0.06, 0.75, 0.35, 2.6710, 1e-4, true) }; 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++) { StrikedTypePayoff payoff = new CashOrNothingPayoff(values[i].type, values[i].strike, 10.0); 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); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new AnalyticEuropeanEngine(stochProcess); VanillaOption opt = new VanillaOption(payoff, exercise); opt.setPricingEngine(engine); double calculated = opt.NPV(); double error = Math.Abs(calculated - values[i].result); if (error > values[i].tol) { REPORT_FAILURE("value", payoff, exercise, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].result, calculated, error, values[i].tol, values[i].knockin); } } }
public void testValues() { // Testing Cliquet option values Date today = Date.Today; DayCounter dc = new Actual360(); SimpleQuote spot = new SimpleQuote(60.0); SimpleQuote qRate = new SimpleQuote(0.04); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.08); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.30); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); BlackScholesMertonProcess process = new BlackScholesMertonProcess( new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new AnalyticCliquetEngine(process); List <Date> reset = new List <Date>(); reset.Add(today + 90); Date maturity = today + 360; Option.Type type = Option.Type.Call; double moneyness = 1.1; PercentageStrikePayoff payoff = new PercentageStrikePayoff(type, moneyness); EuropeanExercise exercise = new EuropeanExercise(maturity); CliquetOption option = new CliquetOption(payoff, exercise, reset); option.setPricingEngine(engine); double calculated = option.NPV(); double expected = 4.4064; // Haug, p.37 double error = Math.Abs(calculated - expected); double tolerance = 1e-4; if (error > tolerance) { REPORT_FAILURE("value", payoff, exercise, spot.value(), qRate.value(), rRate.value(), today, vol.value(), expected, calculated, error, tolerance); } }
public void testFdValues() { //("Testing finite-difference engine for American options..."); 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 = 8.0e-2; for (int i = 0; i < juValues.Length; i++) { StrikedTypePayoff payoff = new PlainVanillaPayoff(juValues[i].type, juValues[i].strike); Date exDate = today + Convert.ToInt32(juValues[i].t * 360 + 0.5); Exercise exercise = new AmericanExercise(today, exDate); spot.setValue(juValues[i].s); qRate.setValue(juValues[i].q); rRate.setValue(juValues[i].r); vol.setValue(juValues[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 FDAmericanEngine(stochProcess, 100, 100); VanillaOption option = new VanillaOption(payoff, exercise); option.setPricingEngine(engine); double calculated = option.NPV(); double error = Math.Abs(calculated - juValues[i].result); if (error > tolerance) { REPORT_FAILURE("value", payoff, exercise, juValues[i].s, juValues[i].q, juValues[i].r, today, juValues[i].v, juValues[i].result, 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 testCashAtHitOrNothingAmericanValues() { // Testing American cash-(at-hit)-or-nothing digital option DigitalOptionData[] values = { // type, strike, spot, q, r, t, vol, value, tol // "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 - pag 95, case 1,2 new DigitalOptionData(Option.Type.Put, 100.00, 105.00, 0.00, 0.10, 0.5, 0.20, 9.7264, 1e-4, true), new DigitalOptionData(Option.Type.Call, 100.00, 95.00, 0.00, 0.10, 0.5, 0.20, 11.6553, 1e-4, true), // the following cases are not taken from a reference paper or book // in the money options (guaranteed immediate payoff) new DigitalOptionData(Option.Type.Call, 100.00, 105.00, 0.00, 0.10, 0.5, 0.20, 15.0000, 1e-16, true), new DigitalOptionData(Option.Type.Put, 100.00, 95.00, 0.00, 0.10, 0.5, 0.20, 15.0000, 1e-16, true), // non null dividend (cross-tested with MC simulation) new DigitalOptionData(Option.Type.Put, 100.00, 105.00, 0.20, 0.10, 0.5, 0.20, 12.2715, 1e-4, true), new DigitalOptionData(Option.Type.Call, 100.00, 95.00, 0.20, 0.10, 0.5, 0.20, 8.9109, 1e-4, true), new DigitalOptionData(Option.Type.Call, 100.00, 105.00, 0.20, 0.10, 0.5, 0.20, 15.0000, 1e-16, true), new DigitalOptionData(Option.Type.Put, 100.00, 95.00, 0.20, 0.10, 0.5, 0.20, 15.0000, 1e-16, true) }; 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++) { StrikedTypePayoff payoff = new CashOrNothingPayoff(values[i].type, values[i].strike, 15.00); Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise amExercise = 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 AnalyticDigitalAmericanEngine(stochProcess); VanillaOption opt = new VanillaOption(payoff, amExercise); opt.setPricingEngine(engine); double calculated = opt.NPV(); double error = Math.Abs(calculated - values[i].result); if (error > values[i].tol) { REPORT_FAILURE("value", payoff, amExercise, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].result, calculated, error, values[i].tol, values[i].knockin); } } }
public void testFdGreeks <Engine>() where Engine : IFDEngine, new() { using (SavedSettings backup = new SavedSettings()) { 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(); } } } } } } } } }
private void testOptionGreeks(ForwardVanillaEngine.GetOriginalEngine getEngine) { 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["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 }; Frequency[] frequencies = { Frequency.Semiannual, Frequency.Quarterly, }; double[] vols = { 0.11, 0.50, 1.20 }; DayCounter dc = new Actual360(); Date today = Date.Today; Settings.setEvaluationDate(today); SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(qRate, dc)); SimpleQuote rRate = new SimpleQuote(0.0); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(rRate, dc)); SimpleQuote vol = new SimpleQuote(0.0); Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(vol, dc)); BlackScholesMertonProcess process = new BlackScholesMertonProcess(new Handle <Quote>(spot), qTS, rTS, volTS); for (int i = 0; i < types.Length; i++) { for (int j = 0; j < moneyness.Length; j++) { for (int k = 0; k < lengths.Length; k++) { for (int kk = 0; kk < frequencies.Length; kk++) { EuropeanExercise maturity = new EuropeanExercise(today + new Period(lengths[k], TimeUnit.Years)); PercentageStrikePayoff payoff = new PercentageStrikePayoff(types[i], moneyness[j]); List <Date> reset = new List <Date>(); for (Date d = today + new Period(frequencies[kk]); d < maturity.lastDate(); d += new Period(frequencies[kk])) { reset.Add(d); } IPricingEngine engine = getEngine(process); CliquetOption option = new CliquetOption(payoff, maturity, reset); option.setPricingEngine(engine); for (int l = 0; l < underlyings.Length; l++) { for (int m = 0; m < qRates.Length; m++) { for (int n = 0; n < rRates.Length; n++) { for (int p = 0; p < vols.Length; p++) { double u = underlyings[l]; double q = qRates[m], r = rRates[n]; double v = vols[p]; spot.setValue(u); qRate.setValue(q); rRate.setValue(r); vol.setValue(v); double value = option.NPV(); calculated["delta"] = option.delta(); calculated["gamma"] = option.gamma(); calculated["theta"] = option.theta(); calculated["rho"] = option.rho(); calculated["divRho"] = option.dividendRho(); calculated["vega"] = option.vega(); if (value > spot.value() * 1.0e-5) { // perturb spot and get delta and gamma double du = u * 1.0e-4; spot.setValue(u + du); double value_p = option.NPV(), delta_p = option.delta(); spot.setValue(u - du); double value_m = option.NPV(), delta_m = option.delta(); spot.setValue(u); expected["delta"] = (value_p - value_m) / (2 * du); expected["gamma"] = (delta_p - delta_m) / (2 * du); // perturb rates and get rho and dividend rho double dr = r * 1.0e-4; rRate.setValue(r + dr); value_p = option.NPV(); rRate.setValue(r - dr); value_m = option.NPV(); rRate.setValue(r); expected["rho"] = (value_p - value_m) / (2 * dr); double dq = q * 1.0e-4; qRate.setValue(q + dq); value_p = option.NPV(); qRate.setValue(q - dq); value_m = option.NPV(); qRate.setValue(q); expected["divRho"] = (value_p - value_m) / (2 * dq); // perturb volatility and get vega double dv = v * 1.0e-4; vol.setValue(v + dv); value_p = option.NPV(); vol.setValue(v - dv); value_m = option.NPV(); vol.setValue(v); expected["vega"] = (value_p - value_m) / (2 * dv); // perturb date and get theta double dT = dc.yearFraction(today - 1, today + 1); Settings.setEvaluationDate(today - 1); value_m = option.NPV(); Settings.setEvaluationDate(today + 1); value_p = option.NPV(); Settings.setEvaluationDate(today); expected["theta"] = (value_p - value_m) / dT; // compare foreach (var 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, maturity, u, q, r, today, v, expct, calcl, error, tol); } } } } } } } } } } } }
public void testAssetOrNothingHaugValues() { // Testing asset-or-nothing barrier options against Haug's values BinaryOptionData[] values = { /* The data below are from * "Option pricing formulas 2nd Ed.", E.G. Haug, McGraw-Hill 2007 pag. 180 - cases 15,16,19,20,23,24,27,28 * Note: * q is the dividend rate, while the book gives b, the cost of carry (q=r-b) */ // barrierType, barrier, cash, type, strike, spot, q, r, t, vol, value, tol new BinaryOptionData(Barrier.Type.DownIn, 100.00, 0.00, Option.Type.Call, 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 37.2782, 1e-4), new BinaryOptionData(Barrier.Type.DownIn, 100.00, 0.00, Option.Type.Call, 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 45.8530, 1e-4), new BinaryOptionData(Barrier.Type.UpIn, 100.00, 0.00, Option.Type.Call, 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 44.5294, 1e-4), new BinaryOptionData(Barrier.Type.UpIn, 100.00, 0.00, Option.Type.Call, 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 54.9262, 1e-4), // 19,20 new BinaryOptionData(Barrier.Type.DownIn, 100.00, 0.00, Option.Type.Put, 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 27.5644, 1e-4), new BinaryOptionData(Barrier.Type.DownIn, 100.00, 0.00, Option.Type.Put, 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 18.9896, 1e-4), // following value is wrong in book. new BinaryOptionData(Barrier.Type.UpIn, 100.00, 0.00, Option.Type.Put, 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 33.1723, 1e-4), new BinaryOptionData(Barrier.Type.UpIn, 100.00, 0.00, Option.Type.Put, 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 22.7755, 1e-4), // 23,24 new BinaryOptionData(Barrier.Type.DownOut, 100.00, 0.00, Option.Type.Call, 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 39.9391, 1e-4), new BinaryOptionData(Barrier.Type.DownOut, 100.00, 0.00, Option.Type.Call, 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 40.1574, 1e-4), new BinaryOptionData(Barrier.Type.UpOut, 100.00, 0.00, Option.Type.Call, 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0000, 1e-4), new BinaryOptionData(Barrier.Type.UpOut, 100.00, 0.00, Option.Type.Call, 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.2676, 1e-4), // 27,28 new BinaryOptionData(Barrier.Type.DownOut, 100.00, 0.00, Option.Type.Put, 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.2183, 1e-4), new BinaryOptionData(Barrier.Type.DownOut, 100.00, 0.00, Option.Type.Put, 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0000, 1e-4), new BinaryOptionData(Barrier.Type.UpOut, 100.00, 0.00, Option.Type.Put, 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 17.2983, 1e-4), new BinaryOptionData(Barrier.Type.UpOut, 100.00, 0.00, Option.Type.Put, 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 17.0306, 1e-4), }; DayCounter dc = new Actual360(); Date today = Date.Today; SimpleQuote spot = new SimpleQuote(100.0); SimpleQuote qRate = new SimpleQuote(0.04); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.01); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.25); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); for (int i = 0; i < values.Length; i++) { StrikedTypePayoff payoff = new AssetOrNothingPayoff(values[i].type, values[i].strike); Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise amExercise = new AmericanExercise(today, exDate, true); 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 AnalyticBinaryBarrierEngine(stochProcess); BarrierOption opt = new BarrierOption(values[i].barrierType, values[i].barrier, 0, payoff, amExercise); opt.setPricingEngine(engine); double calculated = opt.NPV(); double error = Math.Abs(calculated - values[i].result); if (error > values[i].tol) { REPORT_FAILURE("value", payoff, amExercise, values[i].barrierType, values[i].barrier, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].result, calculated, error, values[i].tol); } } }
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); } } } } } } } } } }
public void testBSMOperatorConsistency() { //("Testing consistency of BSM operators..."); Vector grid = new Vector(10); double price = 20.0; double factor = 1.1; for (int i = 0; i < grid.size(); i++) { grid[i] = price; price *= factor; } double dx = Math.Log(factor); double r = 0.05; double q = 0.01; double sigma = 0.5; BSMOperator refer = new BSMOperator(grid.size(), dx, r, q, sigma); DayCounter dc = new Actual360(); Date today = Date.Today; Date exercise = today + new Period(2, TimeUnit.Years); double residualTime = dc.yearFraction(today, exercise); SimpleQuote spot = new SimpleQuote(0.0); YieldTermStructure qTS = Utilities.flatRate(today, q, dc); YieldTermStructure rTS = Utilities.flatRate(today, r, dc); BlackVolTermStructure volTS = Utilities.flatVol(today, sigma, dc); GeneralizedBlackScholesProcess stochProcess = new GeneralizedBlackScholesProcess( new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); BSMOperator op1 = new BSMOperator(grid, stochProcess, residualTime); PdeOperator <PdeBSM> op2 = new PdeOperator <PdeBSM>(grid, stochProcess, residualTime); double tolerance = 1.0e-6; Vector lderror = refer.lowerDiagonal() - op1.lowerDiagonal(); Vector derror = refer.diagonal() - op1.diagonal(); Vector uderror = refer.upperDiagonal() - op1.upperDiagonal(); for (int i = 2; i < grid.size() - 2; i++) { if (Math.Abs(lderror[i]) > tolerance || Math.Abs(derror[i]) > tolerance || Math.Abs(uderror[i]) > tolerance) { Assert.Fail("inconsistency between BSM operators:\n" + i + " row:\n" + "expected: " + refer.lowerDiagonal()[i] + ", " + refer.diagonal()[i] + ", " + refer.upperDiagonal()[i] + "\n" + "calculated: " + op1.lowerDiagonal()[i] + ", " + op1.diagonal()[i] + ", " + op1.upperDiagonal()[i]); } } lderror = refer.lowerDiagonal() - op2.lowerDiagonal(); derror = refer.diagonal() - op2.diagonal(); uderror = refer.upperDiagonal() - op2.upperDiagonal(); for (int i = 2; i < grid.size() - 2; i++) { if (Math.Abs(lderror[i]) > tolerance || Math.Abs(derror[i]) > tolerance || Math.Abs(uderror[i]) > tolerance) { Assert.Fail("inconsistency between BSM operators:\n" + i + " row:\n" + "expected: " + refer.lowerDiagonal()[i] + ", " + refer.diagonal()[i] + ", " + refer.upperDiagonal()[i] + "\n" + "calculated: " + op2.lowerDiagonal()[i] + ", " + op2.diagonal()[i] + ", " + op2.upperDiagonal()[i]); } } }
public void testCashAtHitOrNothingAmericanGreeks() { // Testing American cash-(at-hit)-or-nothing digital option greeks SavedSettings backup = new SavedSettings(); SortedDictionary <string, double> calculated = new SortedDictionary <string, double>(); SortedDictionary <string, double> expected = new SortedDictionary <string, double>(); SortedDictionary <string, double> tolerance = new SortedDictionary <string, double>(); // std::map<std::string,Real> calculated, expected, tolerance; tolerance["delta"] = 5.0e-5; tolerance["gamma"] = 5.0e-5; tolerance["rho"] = 5.0e-5; Option.Type[] types = { QLNet.Option.Type.Call, QLNet.Option.Type.Put }; double[] strikes = { 50.0, 99.5, 100.5, 150.0 }; double cashPayoff = 100.0; double[] underlyings = { 100 }; double[] qRates = { 0.04, 0.05, 0.06 }; double[] rRates = { 0.01, 0.05, 0.15 }; double[] vols = { 0.11, 0.5, 1.2 }; 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)); // there is no cycling on different residual times Date exDate = today + 360; Exercise exercise = new EuropeanExercise(exDate); Exercise amExercise = new AmericanExercise(today, exDate, false); Exercise[] exercises = { exercise, amExercise }; BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), qTS, rTS, volTS); IPricingEngine euroEngine = new AnalyticEuropeanEngine(stochProcess); IPricingEngine amEngine = new AnalyticDigitalAmericanEngine(stochProcess); IPricingEngine[] engines = { euroEngine, amEngine }; bool knockin = true; for (int j = 0; j < engines.Length; j++) { for (int i1 = 0; i1 < types.Length; i1++) { for (int i6 = 0; i6 < strikes.Length; i6++) { StrikedTypePayoff payoff = new CashOrNothingPayoff(types[i1], strikes[i6], cashPayoff); VanillaOption opt = new VanillaOption(payoff, exercises[j]); opt.setPricingEngine(engines[j]); for (int i2 = 0; i2 < underlyings.Length; i2++) { for (int i4 = 0; i4 < qRates.Length; i4++) { for (int i3 = 0; i3 < rRates.Length; i3++) { for (int i7 = 0; i7 < vols.Length; i7++) { // test data double u = underlyings[i2]; double q = qRates[i4]; double r = rRates[i3]; double v = vols[i7]; spot.setValue(u); qRate.setValue(q); rRate.setValue(r); vol.setValue(v); // theta, dividend rho and vega are not available for // digital option with american exercise. Greeks of // digital options with european payoff are tested // in the europeanoption.cpp test double value = opt.NPV(); calculated["delta"] = opt.delta(); calculated["gamma"] = opt.gamma(); calculated["rho"] = opt.rho(); if (value > 1.0e-6) { // perturb spot and get delta and gamma double du = u * 1.0e-4; spot.setValue(u + du); double value_p = opt.NPV(), delta_p = opt.delta(); spot.setValue(u - du); double value_m = opt.NPV(), delta_m = opt.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 = opt.NPV(); rRate.setValue(r - dr); value_m = opt.NPV(); rRate.setValue(r); expected["rho"] = (value_p - value_m) / (2 * dr); // check //std::map<std::string,Real>::iterator it; foreach (var it in calculated) { string greek = it.Key; double expct = expected [greek], calcl = calculated[greek], tol = tolerance [greek]; double error = Utilities.relativeError(expct, calcl, value); if (error > tol) { REPORT_FAILURE(greek, payoff, exercise, u, q, r, today, v, expct, calcl, error, tol, knockin); } } } } } } } } } } }
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); } } }
public void testMonteCarloLookback() { double tolerance = 0.1; DayCounter dc = new Actual360(); Date today = Date.Today; double strike = 90; double t = 1; double t1 = 0.25; Date exDate = today + Convert.ToInt32(t * 360 + 0.5); Exercise exercise = new EuropeanExercise(exDate); 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); spot.setValue(100); qRate.setValue(0); rRate.setValue(0.06); vol.setValue(0.1); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess( new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); Option.Type[] types = new Option.Type[] { Option.Type.Call, Option.Type.Put }; for (int i = 0; i < types.Length; i++) { Option.Type type = types[i]; StrikedTypePayoff payoff = new PlainVanillaPayoff(type, strike); /** * Partial Fixed * **/ Date lookbackStart = today + Convert.ToInt32(t1 * 360 + 0.5); ContinuousPartialFixedLookbackOption partialFixedLookback = new ContinuousPartialFixedLookbackOption(lookbackStart, payoff, exercise); IPricingEngine engine = new AnalyticContinuousPartialFixedLookbackEngine(stochProcess); partialFixedLookback.setPricingEngine(engine); double analytical = partialFixedLookback.NPV(); IPricingEngine mcpartialfixedengine = new MakeMCLookbackEngine <ContinuousPartialFixedLookbackOption.Arguments, ContinuousPartialFixedLookbackOption.Results, PseudoRandom, Statistics>(stochProcess) .withSteps(2000) .withAntitheticVariate() .withSeed(1) .withAbsoluteTolerance(tolerance) .value(); partialFixedLookback.setPricingEngine(mcpartialfixedengine); double monteCarlo = partialFixedLookback.NPV(); double diff = Math.Abs(analytical - monteCarlo); if (diff > tolerance) { REPORT_FAILURE_MC("Partial Fixed", type, analytical, monteCarlo, tolerance); } /** * Fixed * **/ double minMax = 100; ContinuousFixedLookbackOption fixedLookback = new ContinuousFixedLookbackOption(minMax, payoff, exercise); IPricingEngine analyticalfixedengine = new AnalyticContinuousFixedLookbackEngine(stochProcess); fixedLookback.setPricingEngine(analyticalfixedengine); analytical = fixedLookback.NPV(); IPricingEngine mcfixedengine = new MakeMCLookbackEngine <ContinuousFixedLookbackOption.Arguments, ContinuousFixedLookbackOption.Results, PseudoRandom, Statistics>(stochProcess) .withSteps(2000) .withAntitheticVariate() .withSeed(1) .withAbsoluteTolerance(tolerance) .value(); fixedLookback.setPricingEngine(mcfixedengine); monteCarlo = fixedLookback.NPV(); diff = Math.Abs(analytical - monteCarlo); if (diff > tolerance) { REPORT_FAILURE_MC("Fixed", type, analytical, monteCarlo, tolerance); } /** * Partial Floating * **/ double lambda = 1; Date lookbackEnd = today + Convert.ToInt32(t1 * 360 + 0.5); FloatingTypePayoff floatingPayoff = new FloatingTypePayoff(type); ContinuousPartialFloatingLookbackOption partialFloating = new ContinuousPartialFloatingLookbackOption(minMax, lambda, lookbackEnd, floatingPayoff, exercise); IPricingEngine analyticalpartialFloatingengine = new AnalyticContinuousPartialFloatingLookbackEngine(stochProcess); partialFloating.setPricingEngine(analyticalpartialFloatingengine); analytical = partialFloating.NPV(); IPricingEngine mcpartialfloatingengine = new MakeMCLookbackEngine <ContinuousPartialFloatingLookbackOption.Arguments, ContinuousPartialFloatingLookbackOption.Results, PseudoRandom, Statistics> (stochProcess) .withSteps(2000) .withAntitheticVariate() .withSeed(1) .withAbsoluteTolerance(tolerance) .value(); partialFloating.setPricingEngine(mcpartialfloatingengine); monteCarlo = partialFloating.NPV(); diff = Math.Abs(analytical - monteCarlo); if (diff > tolerance) { REPORT_FAILURE_MC("Partial Floating", type, analytical, monteCarlo, tolerance); } /** * Floating * **/ ContinuousFloatingLookbackOption floating = new ContinuousFloatingLookbackOption(minMax, floatingPayoff, exercise); IPricingEngine analyticalFloatingengine = new AnalyticContinuousFloatingLookbackEngine(stochProcess); floating.setPricingEngine(analyticalFloatingengine); analytical = floating.NPV(); IPricingEngine mcfloatingengine = new MakeMCLookbackEngine <ContinuousFloatingLookbackOption.Arguments, ContinuousFloatingLookbackOption.Results, PseudoRandom, Statistics> (stochProcess) .withSteps(2000) .withAntitheticVariate() .withSeed(1) .withAbsoluteTolerance(tolerance) .value(); floating.setPricingEngine(mcfloatingengine); monteCarlo = floating.NPV(); diff = Math.Abs(analytical - monteCarlo); if (diff > tolerance) { REPORT_FAILURE_MC("Floating", type, analytical, monteCarlo, tolerance); } } }
public void testAnalyticContinuousPartialFloatingLookback() { // Testing analytic continuous partial floating-strike lookback options..."); LookbackOptionData[] values = { // data from "Option Pricing Formulas, Second Edition", Haug, 2006, pg.146 //type, strike, minmax, s, q, r, t, v, l, t1, result, tol new LookbackOptionData(Option.Type.Call, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.25, 8.6524, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.5, 9.2128, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.75, 9.5567, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.25, 10.5751, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.5, 11.2601, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.75, 11.6804, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.25, 13.3402, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.5, 14.5121, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.75, 15.314, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.25, 16.3047, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.5, 17.737, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.75, 18.7171, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.25, 17.9831, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.5, 19.6618, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.75, 20.8493, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.25, 21.9793, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.5, 24.0311, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.75, 25.4825, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.25, 2.7189, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.5, 3.4639, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.75, 4.1912, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.25, 3.3231, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.5, 4.2336, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.75, 5.1226, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.25, 7.9153, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.5, 9.5825, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.75, 11.0362, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.25, 9.6743, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.5, 11.7119, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.75, 13.4887, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.25, 13.4719, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.5, 16.1495, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.75, 18.4071, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.25, 16.4657, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.5, 19.7383, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.75, 22.4976, 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); FloatingTypePayoff payoff = new FloatingTypePayoff(values[i].type); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess( new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new AnalyticContinuousPartialFloatingLookbackEngine(stochProcess); Date lookbackEnd = today + Convert.ToInt32(values[i].t1 * 360 + 0.5); ContinuousPartialFloatingLookbackOption option = new ContinuousPartialFloatingLookbackOption( values[i].minmax, values[i].l, lookbackEnd, 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_FLOATING("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); } } }
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); } } }
public void testAnalyticContinuousFloatingLookback() { // Testing analytic continuous floating-strike lookback options LookbackOptionData[] values = { // data from "Option Pricing Formulas", Haug, 1998, pg.61-62 new LookbackOptionData(Option.Type.Call, 0, 100, 120.0, 0.06, 0.10, 0.50, 0.30, 0, 0, 25.3533, 1.0e-4), // data from "Connecting discrete and continuous path-dependent options", // Broadie, Glasserman & Kou, 1999, pg.70-74 new LookbackOptionData(Option.Type.Call, 0, 100, 100.0, 0.00, 0.05, 1.00, 0.30, 0, 0, 23.7884, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 100, 100.0, 0.00, 0.05, 0.20, 0.30, 0, 0, 10.7190, 1.0e-4), new LookbackOptionData(Option.Type.Call, 0, 100, 110.0, 0.00, 0.05, 0.20, 0.30, 0, 0, 14.4597, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 100, 100.0, 0.00, 0.10, 0.50, 0.30, 0, 0, 15.3526, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 110, 100.0, 0.00, 0.10, 0.50, 0.30, 0, 0, 16.8468, 1.0e-4), new LookbackOptionData(Option.Type.Put, 0, 120, 100.0, 0.00, 0.10, 0.50, 0.30, 0, 0, 21.0645, 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); FloatingTypePayoff payoff = new FloatingTypePayoff(values[i].type); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess( new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new AnalyticContinuousFloatingLookbackEngine(stochProcess); ContinuousFloatingLookbackOption option = new ContinuousFloatingLookbackOption(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_FLOATING("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); } } }
public void testCashAtExpiryOrNothingAmericanValues() { // Testing American cash-(at-expiry)-or-nothing digital option DigitalOptionData[] values = { // type, strike, spot, q, r, t, vol, value, tol // "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 - pag 95, case 5,6,9,10 new DigitalOptionData(Option.Type.Put, 100.00, 105.00, 0.00, 0.10, 0.5, 0.20, 9.3604, 1e-4, true), new DigitalOptionData(Option.Type.Call, 100.00, 95.00, 0.00, 0.10, 0.5, 0.20, 11.2223, 1e-4, true), new DigitalOptionData(Option.Type.Put, 100.00, 105.00, 0.00, 0.10, 0.5, 0.20, 4.9081, 1e-4, false), new DigitalOptionData(Option.Type.Call, 100.00, 95.00, 0.00, 0.10, 0.5, 0.20, 3.0461, 1e-4, false), // in the money options (guaranteed discounted payoff) new DigitalOptionData(Option.Type.Call, 100.00, 105.00, 0.00, 0.10, 0.5, 0.20, 15.0000 * Math.Exp(-0.05), 1e-12, true), new DigitalOptionData(Option.Type.Put, 100.00, 95.00, 0.00, 0.10, 0.5, 0.20, 15.0000 * Math.Exp(-0.05), 1e-12, true), // out of bonds case new DigitalOptionData(Option.Type.Call, 2.37, 2.33, 0.07, 0.43, 0.19, 0.005, 0.0000, 1e-4, false), }; DayCounter dc = new Actual360(); Date today = Date.Today; SimpleQuote spot = new SimpleQuote(100.0); SimpleQuote qRate = new SimpleQuote(0.04); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.01); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.25); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); for (int i = 0; i < values.Length; i++) { StrikedTypePayoff payoff = new CashOrNothingPayoff(values[i].type, values[i].strike, 15.0); Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise amExercise = new AmericanExercise(today, exDate, true); 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; if (values[i].knockin) { engine = new AnalyticDigitalAmericanEngine(stochProcess); } else { engine = new AnalyticDigitalAmericanKOEngine(stochProcess); } VanillaOption opt = new VanillaOption(payoff, amExercise); opt.setPricingEngine(engine); double calculated = opt.NPV(); double error = Math.Abs(calculated - values[i].result); if (error > values[i].tol) { REPORT_FAILURE("value", payoff, amExercise, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].result, calculated, error, values[i].tol, values[i].knockin); } } }
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 testAssetAtExpiryOrNothingAmericanValues() { // Testing American asset-(at-expiry)-or-nothing digital option DigitalOptionData[] values = { // type, strike, spot, q, r, t, vol, value, tol // "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 - pag 95, case 7,8,11,12 new DigitalOptionData(Option.Type.Put, 100.00, 105.00, 0.00, 0.10, 0.5, 0.20, 64.8426, 1e-04, true), new DigitalOptionData(Option.Type.Call, 100.00, 95.00, 0.00, 0.10, 0.5, 0.20, 77.7017, 1e-04, true), new DigitalOptionData(Option.Type.Put, 100.00, 105.00, 0.00, 0.10, 0.5, 0.20, 40.1574, 1e-04, false), new DigitalOptionData(Option.Type.Call, 100.00, 95.00, 0.00, 0.10, 0.5, 0.20, 17.2983, 1e-04, false), // data from Haug VBA code results new DigitalOptionData(Option.Type.Put, 100.00, 105.00, 0.01, 0.10, 0.5, 0.20, 65.5291, 1e-04, true), new DigitalOptionData(Option.Type.Call, 100.00, 95.00, 0.01, 0.10, 0.5, 0.20, 76.5951, 1e-04, true), // in the money options (guaranteed discounted payoff = forward * riskFreeDiscount // = spot * dividendDiscount) new DigitalOptionData(Option.Type.Call, 100.00, 105.00, 0.00, 0.10, 0.5, 0.20, 105.0000, 1e-12, true), new DigitalOptionData(Option.Type.Put, 100.00, 95.00, 0.00, 0.10, 0.5, 0.20, 95.0000, 1e-12, true), new DigitalOptionData(Option.Type.Call, 100.00, 105.00, 0.01, 0.10, 0.5, 0.20, 105.0000 * Math.Exp(-0.005), 1e-12, true), new DigitalOptionData(Option.Type.Put, 100.00, 95.00, 0.01, 0.10, 0.5, 0.20, 95.0000 * Math.Exp(-0.005), 1e-12, true) }; DayCounter dc = new Actual360(); Date today = Date.Today; SimpleQuote spot = new SimpleQuote(100.0); SimpleQuote qRate = new SimpleQuote(0.04); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.01); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.25); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); for (int i = 0; i < values.Length; i++) { StrikedTypePayoff payoff = new AssetOrNothingPayoff(values[i].type, values[i].strike); Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise amExercise = new AmericanExercise(today, exDate, true); 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; if (values[i].knockin) { engine = new AnalyticDigitalAmericanEngine(stochProcess); } else { engine = new AnalyticDigitalAmericanKOEngine(stochProcess); } VanillaOption opt = new VanillaOption(payoff, amExercise); opt.setPricingEngine(engine); double calculated = opt.NPV(); double error = Math.Abs(calculated - values[i].result); if (error > values[i].tol) { REPORT_FAILURE("value", payoff, amExercise, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].result, calculated, error, values[i].tol, values[i].knockin); } } }
public void testPathGenerator() { //"Testing 1-D path generation against cached values..."); //SavedSettings backup; Settings.setEvaluationDate(new Date(26, 4, 2005)); Handle <Quote> x0 = new Handle <Quote> (new SimpleQuote(100.0)); Handle <YieldTermStructure> r = new Handle <YieldTermStructure> (Utilities.flatRate(0.05, new Actual360())); Handle <YieldTermStructure> q = new Handle <YieldTermStructure> (Utilities.flatRate(0.02, new Actual360())); Handle <BlackVolTermStructure> sigma = new Handle <BlackVolTermStructure>(Utilities.flatVol(0.20, new Actual360())); // commented values must be used when Halley's correction is enabled testSingle(new BlackScholesMertonProcess(x0, q, r, sigma), "Black-Scholes", false, 26.13784357783, 467.2928561411); // 26.13784357783, 467.2928562519); //Error make the borwnian bridge test first testSingle(new BlackScholesMertonProcess(x0, q, r, sigma), "Black-Scholes", true, 60.28215549393, 202.6143139999); // 60.28215551021, 202.6143139437); testSingle(new GeometricBrownianMotionProcess(100.0, 0.03, 0.20), "geometric Brownian", false, 27.62223714065, 483.6026514084); // 27.62223714065, 483.602651493); testSingle(new OrnsteinUhlenbeckProcess(0.1, 0.20), "Ornstein-Uhlenbeck", false, -0.8372003433557, 0.8372003433557); testSingle(new SquareRootProcess(0.1, 0.1, 0.20, 10.0), "square-root", false, 1.70608664108, 6.024200546031); }
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 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 testCashOrNothingHaugValues() { // Testing cash-or-nothing barrier options against Haug's values BinaryOptionData[] values = { /* The data below are from * "Option pricing formulas 2nd Ed.", E.G. Haug, McGraw-Hill 2007 pag. 180 - cases 13,14,17,18,21,22,25,26 * Note: * q is the dividend rate, while the book gives b, the cost of carry (q=r-b) */ // barrierType, barrier, cash, type, strike, spot, q, r, t, vol, value, tol new BinaryOptionData(Barrier.Type.DownIn, 100.00, 15.00, Option.Type.Call, 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 4.9289, 1e-4), new BinaryOptionData(Barrier.Type.DownIn, 100.00, 15.00, Option.Type.Call, 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 6.2150, 1e-4), // following value is wrong in book. new BinaryOptionData(Barrier.Type.UpIn, 100.00, 15.00, Option.Type.Call, 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 5.8926, 1e-4), new BinaryOptionData(Barrier.Type.UpIn, 100.00, 15.00, Option.Type.Call, 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 7.4519, 1e-4), // 17,18 new BinaryOptionData(Barrier.Type.DownIn, 100.00, 15.00, Option.Type.Put, 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 4.4314, 1e-4), new BinaryOptionData(Barrier.Type.DownIn, 100.00, 15.00, Option.Type.Put, 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 3.1454, 1e-4), new BinaryOptionData(Barrier.Type.UpIn, 100.00, 15.00, Option.Type.Put, 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 5.3297, 1e-4), new BinaryOptionData(Barrier.Type.UpIn, 100.00, 15.00, Option.Type.Put, 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 3.7704, 1e-4), // 21,22 new BinaryOptionData(Barrier.Type.DownOut, 100.00, 15.00, Option.Type.Call, 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 4.8758, 1e-4), new BinaryOptionData(Barrier.Type.DownOut, 100.00, 15.00, Option.Type.Call, 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 4.9081, 1e-4), new BinaryOptionData(Barrier.Type.UpOut, 100.00, 15.00, Option.Type.Call, 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0000, 1e-4), new BinaryOptionData(Barrier.Type.UpOut, 100.00, 15.00, Option.Type.Call, 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 0.0407, 1e-4), // 25,26 new BinaryOptionData(Barrier.Type.DownOut, 100.00, 15.00, Option.Type.Put, 102.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0323, 1e-4), new BinaryOptionData(Barrier.Type.DownOut, 100.00, 15.00, Option.Type.Put, 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 0.0000, 1e-4), new BinaryOptionData(Barrier.Type.UpOut, 100.00, 15.00, Option.Type.Put, 102.00, 95.00, 0.00, 0.10, 0.5, 0.20, 3.0461, 1e-4), new BinaryOptionData(Barrier.Type.UpOut, 100.00, 15.00, Option.Type.Put, 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 3.0054, 1e-4), // other values calculated with book vba new BinaryOptionData(Barrier.Type.UpIn, 100.00, 15.00, Option.Type.Call, 102.00, 95.00, -0.14, 0.10, 0.5, 0.20, 8.6806, 1e-4), new BinaryOptionData(Barrier.Type.UpIn, 100.00, 15.00, Option.Type.Call, 102.00, 95.00, 0.03, 0.10, 0.5, 0.20, 5.3112, 1e-4), // degenerate conditions (barrier touched) new BinaryOptionData(Barrier.Type.DownIn, 100.00, 15.00, Option.Type.Call, 98.00, 95.00, 0.00, 0.10, 0.5, 0.20, 7.4926, 1e-4), new BinaryOptionData(Barrier.Type.UpIn, 100.00, 15.00, Option.Type.Call, 98.00, 105.00, 0.00, 0.10, 0.5, 0.20, 11.1231, 1e-4), // 17,18 new BinaryOptionData(Barrier.Type.DownIn, 100.00, 15.00, Option.Type.Put, 102.00, 98.00, 0.00, 0.10, 0.5, 0.20, 7.1344, 1e-4), new BinaryOptionData(Barrier.Type.UpIn, 100.00, 15.00, Option.Type.Put, 102.00, 101.00, 0.00, 0.10, 0.5, 0.20, 5.9299, 1e-4), // 21,22 new BinaryOptionData(Barrier.Type.DownOut, 100.00, 15.00, Option.Type.Call, 98.00, 99.00, 0.00, 0.10, 0.5, 0.20, 0.0000, 1e-4), new BinaryOptionData(Barrier.Type.UpOut, 100.00, 15.00, Option.Type.Call, 98.00, 101.00, 0.00, 0.10, 0.5, 0.20, 0.0000, 1e-4), // 25,26 new BinaryOptionData(Barrier.Type.DownOut, 100.00, 15.00, Option.Type.Put, 98.00, 99.00, 0.00, 0.10, 0.5, 0.20, 0.0000, 1e-4), new BinaryOptionData(Barrier.Type.UpOut, 100.00, 15.00, Option.Type.Put, 98.00, 101.00, 0.00, 0.10, 0.5, 0.20, 0.0000, 1e-4), }; DayCounter dc = new Actual360(); Date today = Date.Today; SimpleQuote spot = new SimpleQuote(100.0); SimpleQuote qRate = new SimpleQuote(0.04); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.01); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.25); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); for (int i = 0; i < values.Length; i++) { StrikedTypePayoff payoff = new CashOrNothingPayoff(values[i].type, values[i].strike, values[i].cash); Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise amExercise = new AmericanExercise(today, exDate, true); 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 AnalyticBinaryBarrierEngine(stochProcess); BarrierOption opt = new BarrierOption(values[i].barrierType, values[i].barrier, 0, payoff, amExercise); opt.setPricingEngine(engine); double calculated = opt.NPV(); double error = Math.Abs(calculated - values[i].result); if (error > values[i].tol) { REPORT_FAILURE("value", payoff, amExercise, values[i].barrierType, values[i].barrier, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].result, calculated, error, values[i].tol); } } }
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); //} } }
public void testCrankNicolsonWithDamping() { SavedSettings backup = new SavedSettings(); DayCounter dc = new Actual360(); Date today = Date.Today; SimpleQuote spot = new SimpleQuote(100.0); YieldTermStructure qTS = Utilities.flatRate(today, 0.06, dc); YieldTermStructure rTS = Utilities.flatRate(today, 0.06, dc); BlackVolTermStructure volTS = Utilities.flatVol(today, 0.35, dc); StrikedTypePayoff payoff = new CashOrNothingPayoff(Option.Type.Put, 100, 10.0); double maturity = 0.75; Date exDate = today + Convert.ToInt32(maturity * 360 + 0.5); Exercise exercise = new EuropeanExercise(exDate); BlackScholesMertonProcess process = new BlackScholesMertonProcess(new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new AnalyticEuropeanEngine(process); VanillaOption opt = new VanillaOption(payoff, exercise); opt.setPricingEngine(engine); double expectedPV = opt.NPV(); double expectedGamma = opt.gamma(); // fd pricing using implicit damping steps and Crank Nicolson int csSteps = 25, dampingSteps = 3, xGrid = 400; List <int> dim = new InitializedList <int>(1, xGrid); FdmLinearOpLayout layout = new FdmLinearOpLayout(dim); Fdm1dMesher equityMesher = new FdmBlackScholesMesher( dim[0], process, maturity, payoff.strike(), null, null, 0.0001, 1.5, new Pair <double?, double?>(payoff.strike(), 0.01)); FdmMesher mesher = new FdmMesherComposite(equityMesher); FdmBlackScholesOp map = new FdmBlackScholesOp(mesher, process, payoff.strike()); FdmInnerValueCalculator calculator = new FdmLogInnerValue(payoff, mesher, 0); object rhs = new Vector(layout.size()); Vector x = new Vector(layout.size()); FdmLinearOpIterator endIter = layout.end(); for (FdmLinearOpIterator iter = layout.begin(); iter != endIter; ++iter) { (rhs as Vector)[iter.index()] = calculator.avgInnerValue(iter, maturity); x[iter.index()] = mesher.location(iter, 0); } FdmBackwardSolver solver = new FdmBackwardSolver(map, new FdmBoundaryConditionSet(), new FdmStepConditionComposite(), new FdmSchemeDesc().Douglas()); solver.rollback(ref rhs, maturity, 0.0, csSteps, dampingSteps); MonotonicCubicNaturalSpline spline = new MonotonicCubicNaturalSpline(x, x.Count, rhs as Vector); double s = spot.value(); double calculatedPV = spline.value(Math.Log(s)); double calculatedGamma = (spline.secondDerivative(Math.Log(s)) - spline.derivative(Math.Log(s))) / (s * s); double relTol = 2e-3; if (Math.Abs(calculatedPV - expectedPV) > relTol * expectedPV) { QAssert.Fail("Error calculating the PV of the digital option" + "\n rel. tolerance: " + relTol + "\n expected: " + expectedPV + "\n calculated: " + calculatedPV); } if (Math.Abs(calculatedGamma - expectedGamma) > relTol * expectedGamma) { QAssert.Fail("Error calculating the Gamma of the digital option" + "\n rel. tolerance: " + relTol + "\n expected: " + expectedGamma + "\n calculated: " + calculatedGamma); } }
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), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.00, 0.00, 0.50, 0.15, 4.22949) }; 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 testMultiPathGenerator() { //("Testing n-D path generation against cached values..."); //SavedSettings backup; Settings.setEvaluationDate(new Date(26, 4, 2005)); Handle <Quote> x0 = new Handle <Quote> (new SimpleQuote(100.0)); Handle <YieldTermStructure> r = new Handle <YieldTermStructure> (Utilities.flatRate(0.05, new Actual360())); Handle <YieldTermStructure> q = new Handle <YieldTermStructure> (Utilities.flatRate(0.02, new Actual360())); Handle <BlackVolTermStructure> sigma = new Handle <BlackVolTermStructure> (Utilities.flatVol(0.20, new Actual360())); Matrix correlation = new Matrix(3, 3); correlation[0, 0] = 1.0; correlation[0, 1] = 0.9; correlation[0, 2] = 0.7; correlation[1, 0] = 0.9; correlation[1, 1] = 1.0; correlation[1, 2] = 0.4; correlation[2, 0] = 0.7; correlation[2, 1] = 0.4; correlation[2, 2] = 1.0; List <StochasticProcess1D> processes = new List <StochasticProcess1D>(3); StochasticProcess process; processes.Add(new BlackScholesMertonProcess(x0, q, r, sigma)); processes.Add(new BlackScholesMertonProcess(x0, q, r, sigma)); processes.Add(new BlackScholesMertonProcess(x0, q, r, sigma)); process = new StochasticProcessArray(processes, correlation); // commented values must be used when Halley's correction is enabled double[] result1 = { 188.2235868185, 270.6713069569, 113.0431145652 }; // Real result1[] = { // 188.2235869273, // 270.6713071508, // 113.0431145652 }; double[] result1a = { 64.89105742957, 45.12494404804, 108.0475146914 }; // Real result1a[] = { // 64.89105739157, // 45.12494401537, // 108.0475146914 }; testMultiple(process, "Black-Scholes", result1, result1a); processes[0] = new GeometricBrownianMotionProcess(100.0, 0.03, 0.20); processes[1] = new GeometricBrownianMotionProcess(100.0, 0.03, 0.20); processes[2] = new GeometricBrownianMotionProcess(100.0, 0.03, 0.20); process = new StochasticProcessArray(processes, correlation); double[] result2 = { 174.8266131680, 237.2692443633, 119.1168555440 }; // Real result2[] = { // 174.8266132344, // 237.2692444869, // 119.1168555605 }; double[] result2a = { 57.69082393020, 38.50016862915, 116.4056510107 }; // Real result2a[] = { // 57.69082387657, // 38.50016858691, // 116.4056510107 }; testMultiple(process, "geometric Brownian", result2, result2a); processes[0] = new OrnsteinUhlenbeckProcess(0.1, 0.20); processes[1] = new OrnsteinUhlenbeckProcess(0.1, 0.20); processes[2] = new OrnsteinUhlenbeckProcess(0.1, 0.20); process = new StochasticProcessArray(processes, correlation); double[] result3 = { 0.2942058437284, 0.5525006418386, 0.02650931054575 }; double[] result3a = { -0.2942058437284, -0.5525006418386, -0.02650931054575 }; testMultiple(process, "Ornstein-Uhlenbeck", result3, result3a); processes[0] = new SquareRootProcess(0.1, 0.1, 0.20, 10.0); processes[1] = new SquareRootProcess(0.1, 0.1, 0.20, 10.0); processes[2] = new SquareRootProcess(0.1, 0.1, 0.20, 10.0); process = new StochasticProcessArray(processes, correlation); double[] result4 = { 4.279510844897, 4.943783503533, 3.590930385958 }; double[] result4a = { 2.763967737724, 2.226487196647, 3.503859264341 }; testMultiple(process, "square-root", result4, result4a); }
// Reference pg. 253 - Hull - Options, Futures, and Other Derivatives 5th ed // Exercise 12.8 // Doesn't quite work. Need to deal with date conventions 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); } }